From 705e4deb9b04140a460e7518c7c6599b5ecae6bf Mon Sep 17 00:00:00 2001 From: Will Usher Date: Fri, 27 Mar 2020 14:58:14 +0100 Subject: [PATCH] Added fixed operating costs and total capacity calculations --- scripts/prepare_cbc_short.sh | 2 +- src/otoole/results/calculate.py | 49 ++++++++++++++---- src/otoole/results/convert.py | 14 ++++-- tests/results/test_calculate.py | 89 ++++++++++++++++++++++++++------- 4 files changed, 122 insertions(+), 32 deletions(-) diff --git a/scripts/prepare_cbc_short.sh b/scripts/prepare_cbc_short.sh index a09e29b1..be634ba7 100644 --- a/scripts/prepare_cbc_short.sh +++ b/scripts/prepare_cbc_short.sh @@ -10,7 +10,7 @@ echo "end;" >> trimmed.txt # Omitting --check, which runs the solve using GLPK as well glpsol --wlp simplicity.lp -m ../osemosys/OSeMOSYS_GNU_MathProg/src/osemosys_short.txt -d trimmed.txt cbc simplicity.lp -dualpivot pesteep -psi 1.0 -pertv 52 -duals solve -solu simplicity.sol -otoole -vvv results cbc csv simplicity.sol output +otoole -vvv results cbc csv simplicity.sol output --input_data OSeMOSYS-simplicity-11a3a26/datapackage.json ls output > output_files ls results > result_file diff --git a/src/otoole/results/calculate.py b/src/otoole/results/calculate.py index baed45fd..b20e139c 100644 --- a/src/otoole/results/calculate.py +++ b/src/otoole/results/calculate.py @@ -41,18 +41,14 @@ def compute_annual_emissions( def compute_annual_fixed_operating_cost( - acc_new_capacity: pd.DataFrame, - residual_capacity: pd.DataFrame, - fixed_cost: pd.DataFrame, + total_capacity: pd.DataFrame, fixed_cost: pd.DataFrame, ) -> pd.DataFrame: """Compute AnnualFixedOperatingCost result Arguments --------- - acc_new_capacity: pd.DataFrame - Accumulated new capacity - residual_capacity: pd.DataFrame - Residual capacity + total_capacity: pd.DataFrame + Total annual capacity (new and residual) fixed_cost: pd.DataFrame Fixed cost @@ -64,9 +60,29 @@ def compute_annual_fixed_operating_cost( NewCapacity[r,t,yy]) + ResidualCapacity[r,t,y]) ~VALUE; """ - total_capacity = acc_new_capacity + residual_capacity - total_fixed_costs = fixed_cost * total_capacity - return total_fixed_costs[(total_fixed_costs != 0).all(1)] + total_fixed_costs = total_capacity.mul(fixed_cost, fill_value=0.0) + return total_fixed_costs[(total_fixed_costs != 0).all(1)].dropna() + + +def compute_total_capacity_annual(residual_capacity, acc_new_capacity): + """Compute TotalCapacityAnnual result + + Arguments + --------- + acc_new_capacity: pd.DataFrame + Accumulated new capacity + residual_capacity: pd.DataFrame + Residual capacity + + Notes + ----- + r~REGION, t~TECHNOLOGY, y~YEAR, + ResidualCapacity[r,t,y] + + (sum{yy in YEAR: y-yy < OperationalLife[r,t] && y-yy>=0} + NewCapacity[r,t,yy])~VALUE; + """ + total_capacity = residual_capacity.add(acc_new_capacity, fill_value=0.0) + return total_capacity[(total_capacity != 0).all(1)] def compute_accumulated_new_capacity( @@ -177,6 +193,7 @@ def calculate_result( new_capacity = results_data["NewCapacity"].copy() year = pd.Index(package["YEAR"]["VALUE"].to_list()) return compute_accumulated_new_capacity(operational_life, new_capacity, year) + elif parameter_name == "AnnualEmissions": emission_activity_ratio = package["EmissionActivityRatio"] yearsplit = package["YearSplit"] @@ -184,6 +201,18 @@ def calculate_result( return compute_annual_emissions( emission_activity_ratio, yearsplit, rate_of_activity ) + elif parameter_name == "TotalCapacityAnnual": + acc_new_capacity = calculate_result( + "AccumulatedNewCapacity", input_data, results_data + ) + residual_capacity = package["ResidualCapacity"] + return compute_total_capacity_annual(residual_capacity, acc_new_capacity) + elif parameter_name == "AnnualFixedOperatingCost": + total_capacity = calculate_result( + "TotalCapacityAnnual", input_data, results_data + ) + fixed_cost = package["FixedCost"] + return compute_annual_fixed_operating_cost(total_capacity, fixed_cost) elif parameter_name == "AnnualTechnologyEmission": emission_activity_ratio = package["EmissionActivityRatio"] yearsplit = package["YearSplit"] diff --git a/src/otoole/results/convert.py b/src/otoole/results/convert.py index 2cafa2a8..7e706b4b 100644 --- a/src/otoole/results/convert.py +++ b/src/otoole/results/convert.py @@ -226,10 +226,16 @@ def convert_dataframe_to_csv( ) -> Dict[str, pd.DataFrame]: """Convert from dataframe to csv + Converts a pandas DataFrame containing all CBC results to reformatted + dictionary of pandas DataFrames in long format ready to write out as + csv files + Arguments --------- data : pandas.DataFrame - input_data : str + CBC results stored in a dataframe + input_data : str, default=None + Path to the OSeMOSYS data file containing input data Example ------- @@ -285,14 +291,16 @@ def convert_dataframe_to_csv( ) df = calculate_result(name, input_data, results) if not df.empty: - results[name] = df.set_index(indices) + results[name] = df else: LOGGER.warning( "Calculation returned empty dataframe for parameter '%s'", name ) else: LOGGER.warning( - "No input data provided, unable to calculate parameter '%s'", name + "No OSeMOSYS data file provided, \ + unable to calculate parameter '%s'", + name, ) return results diff --git a/tests/results/test_calculate.py b/tests/results/test_calculate.py index 59e52349..fcbbfd73 100644 --- a/tests/results/test_calculate.py +++ b/tests/results/test_calculate.py @@ -9,6 +9,7 @@ compute_annual_fixed_operating_cost, compute_annual_technology_emission_by_mode, compute_annual_technology_emissions, + compute_total_capacity_annual, ) @@ -193,16 +194,16 @@ def accumulated_new_capacity(): @fixture -def fixed_cost(): +def residual_capacity(): data = pd.DataFrame( data=[ ["SIMPLICITY", "GAS_EXTRACTION", 2014, 1], ["SIMPLICITY", "GAS_EXTRACTION", 2015, 1], - ["SIMPLICITY", "GAS_EXTRACTION", 2016, 1], - ["SIMPLICITY", "GAS_EXTRACTION", 2017, 1], - ["SIMPLICITY", "DUMMY", 2014, 0.5], - ["SIMPLICITY", "DUMMY", 2015, 0.5], - ["SIMPLICITY", "DUMMY", 2016, 0.5], + ["SIMPLICITY", "GAS_EXTRACTION", 2016, 0], + ["SIMPLICITY", "GAS_EXTRACTION", 2017, 0], + ["SIMPLICITY", "DUMMY", 2014, 0.1], + ["SIMPLICITY", "DUMMY", 2015, 0.2], + ["SIMPLICITY", "DUMMY", 2016, 0.3], ], columns=["REGION", "TECHNOLOGY", "YEAR", "VALUE"], ).set_index(["REGION", "TECHNOLOGY", "YEAR"]) @@ -210,16 +211,33 @@ def fixed_cost(): @fixture -def residual_capacity(): +def total_capacity(): + data = pd.DataFrame( + data=[ + ["SIMPLICITY", "GAS_EXTRACTION", 2014, 2.3], + ["SIMPLICITY", "GAS_EXTRACTION", 2015, 2.3], + ["SIMPLICITY", "GAS_EXTRACTION", 2016, 1.6], + ["SIMPLICITY", "GAS_EXTRACTION", 2017, 1.6], + ["SIMPLICITY", "DUMMY", 2014, 1.0], + ["SIMPLICITY", "DUMMY", 2015, 1.1], + ["SIMPLICITY", "DUMMY", 2016, 1.2], + ], + columns=["REGION", "TECHNOLOGY", "YEAR", "VALUE"], + ).set_index(["REGION", "TECHNOLOGY", "YEAR"]) + return data + + +@fixture +def fixed_cost(): data = pd.DataFrame( data=[ ["SIMPLICITY", "GAS_EXTRACTION", 2014, 1], ["SIMPLICITY", "GAS_EXTRACTION", 2015, 1], - ["SIMPLICITY", "GAS_EXTRACTION", 2016, 0], - ["SIMPLICITY", "GAS_EXTRACTION", 2017, 0], - ["SIMPLICITY", "DUMMY", 2014, 0.1], - ["SIMPLICITY", "DUMMY", 2015, 0.2], - ["SIMPLICITY", "DUMMY", 2016, 0.3], + ["SIMPLICITY", "GAS_EXTRACTION", 2016, 1], + ["SIMPLICITY", "GAS_EXTRACTION", 2017, 1], + ["SIMPLICITY", "DUMMY", 2014, 0.5], + ["SIMPLICITY", "DUMMY", 2015, 0.5], + ["SIMPLICITY", "DUMMY", 2016, 0.5], ], columns=["REGION", "TECHNOLOGY", "YEAR", "VALUE"], ).set_index(["REGION", "TECHNOLOGY", "YEAR"]) @@ -478,10 +496,8 @@ class TestComputeFixedOperatingCost: 0.9 + 0.3 * 0.5 = 0.6 """ - def test_compute(self, accumulated_new_capacity, residual_capacity, fixed_cost): - actual = compute_annual_fixed_operating_cost( - accumulated_new_capacity, residual_capacity, fixed_cost - ) + def test_compute(self, total_capacity, fixed_cost): + actual = compute_annual_fixed_operating_cost(total_capacity, fixed_cost) expected = pd.DataFrame( data=[ ["SIMPLICITY", "GAS_EXTRACTION", 2014, 2.3], @@ -498,8 +514,45 @@ def test_compute(self, accumulated_new_capacity, residual_capacity, fixed_cost): assert_frame_equal(actual, expected) def test_compute_null(self): - actual = compute_annual_fixed_operating_cost( - pd.DataFrame(), pd.DataFrame(), pd.DataFrame() + actual = compute_annual_fixed_operating_cost(pd.DataFrame(), pd.DataFrame()) + expected = pd.DataFrame() + assert_frame_equal(actual, expected) + + +class TestComputeTotalAnnualCapacity: + """ + + Notes + ----- + 1.3 + 1.0 + 1.3 + 1.0 + 1.6 + 0.0 + 1.6 + 0.0 + 0.9 + 0.1 + 0.9 + 0.2 + 0.9 + 0.3 + """ + + def test_compute(self, accumulated_new_capacity, residual_capacity): + actual = compute_total_capacity_annual( + residual_capacity, accumulated_new_capacity ) + expected = pd.DataFrame( + data=[ + ["SIMPLICITY", "GAS_EXTRACTION", 2014, 2.3], + ["SIMPLICITY", "GAS_EXTRACTION", 2015, 2.3], + ["SIMPLICITY", "GAS_EXTRACTION", 2016, 1.6], + ["SIMPLICITY", "GAS_EXTRACTION", 2017, 1.6], + ["SIMPLICITY", "DUMMY", 2014, 1.0], + ["SIMPLICITY", "DUMMY", 2015, 1.1], + ["SIMPLICITY", "DUMMY", 2016, 1.2], + ], + columns=["REGION", "TECHNOLOGY", "YEAR", "VALUE"], + ).set_index(["REGION", "TECHNOLOGY", "YEAR"]) + + assert_frame_equal(actual, expected) + + def test_compute_null(self): + actual = compute_total_capacity_annual(pd.DataFrame(), pd.DataFrame()) expected = pd.DataFrame() assert_frame_equal(actual, expected)