diff --git a/hypatia/analysis/postprocessing.py b/hypatia/analysis/postprocessing.py index 5318233..066844b 100644 --- a/hypatia/analysis/postprocessing.py +++ b/hypatia/analysis/postprocessing.py @@ -40,11 +40,25 @@ "lines_decomisioning_cost": {"index": "years", "var": "results.cost_decom_line"}, } - import pandas as pd import numpy as np +from hypatia.utility.utility import get_emission_types import os +def get_result_map(glob_mapping): + result_map = RESULT_MAP.copy() + emission_types = get_emission_types(glob_mapping) + for emission_type in emission_types: + result_map["{}_emission_cost".format(emission_type)] = { + "index": "years", + "var": "results.emission_cost_by_type['{}']".format(emission_type) + } + result_map["{}_emission".format(emission_type)] = { + "index": "years", + "var": "results.emission_by_type['{}']".format(emission_type) + } + return result_map + def dict_to_csv(Dict, path): """Writes nested dicts to csv""" @@ -80,7 +94,7 @@ def set_DataFrame( vars_frames = {} - for item, info in RESULT_MAP.items(): + for item, info in get_result_map(glob_mapping).items(): try: var = eval(info["var"]) except (KeyError, AttributeError): diff --git a/hypatia/backend/Build.py b/hypatia/backend/Build.py index 59d2a93..a91ed38 100644 --- a/hypatia/backend/Build.py +++ b/hypatia/backend/Build.py @@ -18,6 +18,7 @@ annual_activity, get_regions_with_storage, storage_max_flow, + get_emission_types, ) from hypatia.backend.constraints.ConstraintList import CONSTRAINTS import logging @@ -38,8 +39,8 @@ "totalcapacity", "cost_fix_tax", "cost_fix_sub", - "emission_cost", - "CO2_equivalent", + "emission_cost_by_type", + "emission_by_type", "demand", ] @@ -186,10 +187,10 @@ def _set_regional_objective_planning(self): self.inv_allregions += self.vars.cost_inv_fvalue[reg][ctgry] if ctgry != "Transmission" and ctgry != "Storage": - - totalcost_regional += cp.sum( - self.vars.emission_cost[reg][ctgry], axis=1 - ) + for emission_type in get_emission_types(self.model_data.settings.global_settings): + totalcost_regional += cp.sum( + self.vars.emission_cost_by_region[reg][emission_type][ctgry], axis=1 + ) discount_factor = ( 1 + self.model_data.regional_parameters[reg]["discount_rate"]["Annual Discount Rate"].values @@ -222,10 +223,10 @@ def _set_regional_objective_operation(self): ) if ctgry != "Transmission" and ctgry != "Storage": - - totalcost_regional += cp.sum( - self.vars.emission_cost[reg][ctgry], axis=1 - ) + for emission_type in get_emission_types(self.model_data.settings.global_settings): + totalcost_regional += cp.sum( + self.vars.emission_cost_by_region[reg][emission_type][ctgry], axis=1 + ) self.totalcost_allregions += totalcost_regional diff --git a/hypatia/backend/ModelSettings.py b/hypatia/backend/ModelSettings.py index 39bdf21..002441d 100644 --- a/hypatia/backend/ModelSettings.py +++ b/hypatia/backend/ModelSettings.py @@ -25,7 +25,10 @@ check_carrier_type, check_years_mode_consistency, ) -from hypatia.utility.utility import create_technology_columns +from hypatia.utility.utility import ( + create_technology_columns, + get_emission_types, +) from hypatia.backend.constraints.ConstraintList import CONSTRAINTS class ModelSettings: @@ -374,6 +377,18 @@ def regional_parameters_template(self) -> Optional[Dict]: additional_level=("Taxes or Subsidies", ["Tax", "Sub"]) ) + specific_emission_indexer = create_technology_columns( + self.technologies[reg], + ignored_tech_categories=["Demand"], + additional_level=("Emission Type", get_emission_types(self.global_settings)) + ) + + carbon_tax_indexer = create_technology_columns( + self.technologies[reg], + ignored_tech_categories=["Demand", "Storage", "Transmission"], + additional_level=("Emission Type", get_emission_types(self.global_settings)) + ) + regional_parameters_template[reg] = { "tech_fixed_cost": { "sheet_name": "F_OM", @@ -403,13 +418,13 @@ def regional_parameters_template(self) -> Optional[Dict]: "sheet_name": "Specific_emission", "value": 0, "index": pd.Index(self.years, name="Years"), - "columns": indexer_reg_drop2, + "columns": specific_emission_indexer, }, - "carbon_tax": { - "sheet_name": "Carbon_tax", + "emission_tax": { + "sheet_name": "Emission_tax", "value": 0, "index": pd.Index(self.years, name="Years"), - "columns": indexer_reg_drop2, + "columns": carbon_tax_indexer, }, "fix_taxsub": { "sheet_name": "Fix_taxsub", diff --git a/hypatia/backend/ModelVariables.py b/hypatia/backend/ModelVariables.py index b3d114e..af09145 100644 --- a/hypatia/backend/ModelVariables.py +++ b/hypatia/backend/ModelVariables.py @@ -3,6 +3,7 @@ import cvxpy as cp import itertools as it from hypatia.utility.utility import * +from collections import defaultdict class ModelVariables(): def __init__(self, model_data: ModelData): @@ -157,8 +158,6 @@ def _calc_variable_planning(self): self.decommissioned_capacity = {} self.cost_decom = {} self.cost_variable = {} - self.CO2_equivalent = {} - self.emission_cost = {} self.production_annual = {} for reg in self.model_data.settings.regions: @@ -597,31 +596,37 @@ def _balance_(self): def _calc_emission_variables(self): - self.CO2_equivalent = {} - self.emission_cost = {} - self.regional_emission = {} + self.emission_by_region = {} + self.emission_cost_by_region = {} + self.emission_by_type = {} + self.emission_cost_by_type = {} + for reg in self.model_data.settings.regions: - CO2_equivalent_regional = {} - emission_cost_regional = {} - emission = np.zeros( - (len(self.model_data.settings.years) * len(self.model_data.settings.time_steps), 1) - ) + emissions_regional = defaultdict(dict) + emission_cost_regional = defaultdict(dict) for key in self.model_data.settings.technologies[reg].keys(): if key == "Demand" or key == "Transmission" or key == "Storage": continue - CO2_equivalent_regional[key] = cp.multiply( - self.production_annual[reg][key], - self.model_data.regional_parameters[reg]["specific_emission"].loc[:, key], - ) - - emission_cost_regional[key] = cp.multiply( - CO2_equivalent_regional[key], - self.model_data.regional_parameters[reg]["carbon_tax"].loc[:, key], - ) - - emission += cp.sum(CO2_equivalent_regional[key], axis=1) + for emission_type in get_emission_types(self.model_data.settings.global_settings): + emissions_regional[emission_type][key] = cp.multiply( + self.production_annual[reg][key], + self.model_data.regional_parameters[reg]["specific_emission"][emission_type].loc[:, key], + ) - self.CO2_equivalent[reg] = CO2_equivalent_regional - self.emission_cost[reg] = emission_cost_regional - self.regional_emission[reg] = emission + emission_cost_regional[emission_type][key] = cp.multiply( + emissions_regional[emission_type][key], + self.model_data.regional_parameters[reg]["emission_tax"][emission_type].loc[:, key], + ) + self.emission_by_region[reg] = emissions_regional + self.emission_cost_by_region[reg] = emission_cost_regional + + self.emission_by_type = self.flip_keys(self.emission_by_region) + self.emission_cost_by_type = self.flip_keys(self.emission_cost_by_region) + + def flip_keys(self, d): + result = defaultdict(dict) + for key, value in d.items(): + for k, v in value.items(): + result[k][key] = v + return result diff --git a/hypatia/backend/constraints/EmissionCapGlobal.py b/hypatia/backend/constraints/EmissionCapGlobal.py index f126eac..f025e4d 100644 --- a/hypatia/backend/constraints/EmissionCapGlobal.py +++ b/hypatia/backend/constraints/EmissionCapGlobal.py @@ -3,9 +3,10 @@ ModelMode, TopologyType ) -import numpy as np +from hypatia.utility.utility import get_emission_types import pandas as pd - +import numpy as np +import cvxpy as cp """ Defines the CO2 emission cap across all regions @@ -14,20 +15,23 @@ class EmissionCapGlobal(Constraint): TOPOLOGY_TYPES = [TopologyType.MultiNode] def _check(self): - assert hasattr(self.variables, "regional_emission"), "regional_emission must be defined" + assert hasattr(self.variables, "emission_by_region"), "emission_by_region must be defined" def rules(self): - global_emission = np.zeros( - (len(self.model_data.settings.years) * len(self.model_data.settings.time_steps), 1) - ) - for reg in self.model_data.settings.regions: - global_emission += self.variables.regional_emission[reg] - - global_emission_cap = self.model_data.global_parameters[ - "global_emission_cap_annual" - ].values - global_emission_cap.shape = global_emission.shape - return [global_emission_cap - global_emission >= 0] + rules = [] + for emission_type in get_emission_types(self.model_data.settings.global_settings): + global_emission = np.zeros( + (len(self.model_data.settings.years) * len(self.model_data.settings.time_steps), 1) + ) + for reg in self.model_data.settings.regions: + for key, value in self.variables.emission_by_region[reg][emission_type].items(): + global_emission += cp.sum(value, axis=1) + global_emission_cap = self.model_data.global_parameters[ + "global_emission_cap_annual" + ]["{} Global Emission Cap".format(emission_type)].values + global_emission_cap.shape = global_emission.shape + rules.append(global_emission_cap - global_emission >= 0) + return rules def _required_global_parameters(settings): return { @@ -35,6 +39,10 @@ def _required_global_parameters(settings): "sheet_name": "Glob_emission_cap_annual", "value": 1e30, "index": pd.Index(settings.years, name="Years"), - "columns": pd.Index(["Global Emission Cap"]), + "columns": pd.Index( + [emission_type + " Global Emission Cap" for emission_type in get_emission_types( + settings.global_settings + )] + ), }, } diff --git a/hypatia/backend/constraints/EmissionCapRegional.py b/hypatia/backend/constraints/EmissionCapRegional.py index 9271d1f..702e5f7 100644 --- a/hypatia/backend/constraints/EmissionCapRegional.py +++ b/hypatia/backend/constraints/EmissionCapRegional.py @@ -3,25 +3,32 @@ ModelMode, TopologyType ) +from hypatia.utility.utility import get_emission_types import pandas as pd +import numpy as np +import cvxpy as cp """ Defines the CO2 emission cap within each region """ class EmissionCapRegional(Constraint): def _check(self): - assert hasattr(self.variables, "regional_emission"), "regional_emission must be defined" - assert hasattr(self.variables, "CO2_equivalent"), "regional_emission must be defined" + assert hasattr(self.variables, "emission_by_region"), "emission_by_region must be defined" def rules(self): rules = [] - for reg in self.model_data.settings.regions: - for key, value in self.variables.CO2_equivalent[reg].items(): - emission_cap = self.model_data.regional_parameters[reg]["emission_cap_annual"].values - emission_cap.shape = self.variables.regional_emission[reg].shape - - rules.append(emission_cap - self.variables.regional_emission[reg] >= 0) - + for emission_type in get_emission_types(self.model_data.settings.global_settings): + for reg in self.model_data.settings.regions: + regional_emission = np.zeros( + (len(self.model_data.settings.years) * len(self.model_data.settings.time_steps), 1) + ) + for key, value in self.variables.emission_by_region[reg][emission_type].items(): + regional_emission += cp.sum(value, axis=1) + emission_cap = self.model_data.regional_parameters[reg]["emission_cap_annual"][ + "{} Global Cap".format(emission_type) + ].values + emission_cap.shape = regional_emission.shape + rules.append(emission_cap - regional_emission >= 0) return rules def _required_regional_parameters(settings): @@ -32,7 +39,11 @@ def _required_regional_parameters(settings): "sheet_name": "Emission_cap_annual", "value": 1e10, "index": pd.Index(settings.years, name="Years"), - "columns": pd.Index(["Emission Cap"]), + "columns": pd.Index( + [emission_type + " Global Cap" for emission_type in get_emission_types( + settings.global_settings + )] + ), }, } diff --git a/hypatia/backend/tests/EmissionTests.py b/hypatia/backend/tests/EmissionTests.py new file mode 100644 index 0000000..cafbc51 --- /dev/null +++ b/hypatia/backend/tests/EmissionTests.py @@ -0,0 +1,1435 @@ +import pandas as pd +import numpy as np +import unittest +import copy +from hypatia.backend.ModelSettings import ModelSettings +from hypatia.backend.ModelData import ModelData +from hypatia.utility.constants import ModelMode +from hypatia.backend.Build import BuildModel +from hypatia.backend.tests.TestSettings import ( + SingleNodeOperationEmissionTestSettings, + SingleNodePlanningEmissionTestSettings, + MultiNodeOperationEmissionTestSettings, + MultiNodePlanningEmissionTestSettings +) +import hypatia.error_log.Exceptions as hypatiaException + +''' +Test how hypatia models emissions by checking that: + 1. The model correctly tracks the emission generate by the model solution + 2. The model correctly considers emission cost to find the optimal solution + 3. The model correctly considers emission caps (global and regional) to find the optimal solution +We check these feature in the 4 main scenarios: + 1. Single region, planning + 2. Single region, operation + 3. Multi region, planning + 4. Multi region, operation +''' + +class TestEmissionsSingleRegionOperation(unittest.TestCase): + ''' + The scenario used in the test is: + + ------------------------------ Reg1 -------------------------------- + Elec_import -> Elec | + |-> Elec Demand + |-> Elec | + NG_extr -> NG -> NG_ref -> NG_prod -> NG_chp| + |-> Heat | + |-> Heat Demand + Heat_import -> Heat | + ''' + + def __init__(self, *args, **kwargs): + super(TestEmissionsSingleRegionOperation, self).__init__(*args, **kwargs) + + # Checks the model correctly tracks the emission generate by the model solution + def test_emission_tracking(self): + example_settings = SingleNodeOperationEmissionTestSettings() + settings = ModelSettings( + ModelMode.Operation, + example_settings.global_settings, + example_settings.regional_settings, + ) + + regional_parameters = settings.default_regional_parameters + + # Increment demand for heat and elec to 1 for each timestamp + regional_parameters["reg1"]["demand"] += 1 + + # Increment the residual capacity for all techs + regional_parameters["reg1"]["tech_residual_cap"] += 8760 + + # Increment the cost for Heat_import and Elec_import so it is better to fulfil the demand using NG + regional_parameters["reg1"]["tech_var_cost"].loc[:, (slice(None), ["Elec_import", "Heat_import"])] += 1 + + # Increment the Emission production for the NG transformation technologies + regional_parameters["reg1"]["specific_emission"].loc[:,(["CO2"], slice(None), ["NG_ref","NG_chp"])] += 1 + regional_parameters["reg1"]["specific_emission"].loc[:,(["NOx"], slice(None), ["NG_chp"])] += 2 + + model_data = ModelData( + settings, + settings.default_global_parameters, + settings.default_trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + # Check that we used NG to produced heat and elec and did not import them + suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["NG_extr"].values, decimals=10) + )) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["Elec_import"].values, decimals=10), + )) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["Heat_import"].values, decimals=10), + )) + + # Check how many emission were produced + expected = np.array([[0.0, 0.0, 0.0]]) + self.assertTrue(np.array_equal(expected, results.emission_by_type["CO2"]["reg1"]["Supply"].value)) + self.assertTrue(np.array_equal(expected, results.emission_by_type["NOx"]["reg1"]["Supply"].value)) + self.assertEqual( + results.emission_by_type["CO2"]["reg1"]["Conversion"].value, + [[8760]] + ) + self.assertEqual( + results.emission_by_type["CO2"]["reg1"]["Conversion_plus"].value, + [[8760]] + ) + self.assertEqual( + results.emission_by_type["NOx"]["reg1"]["Conversion"].value, + [[0]] + ) + self.assertEqual( + results.emission_by_type["NOx"]["reg1"]["Conversion_plus"].value, + [[17520]] + ) + + # Checks the model correctly considers emission cost to find the optimal solution + def test_emission_cost_function(self): + example_settings = SingleNodeOperationEmissionTestSettings() + settings = ModelSettings( + ModelMode.Operation, + example_settings.global_settings, + example_settings.regional_settings, + ) + + regional_parameters = settings.default_regional_parameters + + # Increment demand for heat and elec to 1 for each timestamp + regional_parameters["reg1"]["demand"] += 1 + + # Increment the residual capacity for all techs + regional_parameters["reg1"]["tech_residual_cap"] += 8760 + + # Increment the cost for importing heat and elec so it is better to produce them using NG + regional_parameters["reg1"]["tech_var_cost"].loc[:, (slice(None), ["Elec_import", "Heat_import"])] += 1 + + # Increment the Emission production for the NG transformation technologies + regional_parameters["reg1"]["specific_emission"].loc[:,(["CO2"], slice(None), ["NG_ref","NG_chp"])] += 1 + regional_parameters["reg1"]["specific_emission"].loc[:,(["NOx"], slice(None), ["NG_chp"])] += 2 + + model_data = ModelData( + settings, + settings.default_global_parameters, + settings.default_trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + # Check that we used NG to produced heat and elec and did not import them + suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["NG_extr"].values, decimals=10) + )) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["Elec_import"].values, decimals=10), + )) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["Heat_import"].values, decimals=10), + )) + + # Now increase the emission taxes and show that decide to import heat and elect + # if the emission taxes makes it more expensive to generate them using NG + regional_parameters["reg1"]["emission_tax"] += 1 + model_data = ModelData( + settings, + settings.default_global_parameters, + settings.default_trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + # Check that we did not import heat and elec and produced them using NG + suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["NG_extr"].values, decimals=10) + )) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["Elec_import"].values, decimals=10) + )) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["Heat_import"].values, decimals=10) + )) + + # Checks the model correctly considers regional emission caps to find the optimal solution + def test_emission_cap(self): + example_settings = SingleNodeOperationEmissionTestSettings() + settings = ModelSettings( + ModelMode.Operation, + example_settings.global_settings, + example_settings.regional_settings, + ) + + regional_parameters = settings.default_regional_parameters + + # Increment demand of heat and elec to 1 for each timestamp + regional_parameters["reg1"]["demand"] += 1 + + # Increment the residual capacity for all techs in the NG value chain + regional_parameters["reg1"]["tech_residual_cap"] += 8760 + + # Increment the cost for importing heat and elec so it is better to produce them using NG + regional_parameters["reg1"]["tech_var_cost"].loc[:, (slice(None), ["Elec_import", "Heat_import"])] += 1 + + # Increment the emission production per tech + regional_parameters["reg1"]["specific_emission"].loc[:,(["CO2"], slice(None), ["NG_ref","NG_chp"])] += 1 + regional_parameters["reg1"]["specific_emission"].loc[:,(["NOx"], slice(None), ["NG_chp"])] += 2 + + model_data = ModelData( + settings, + settings.default_global_parameters, + settings.default_trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + # Check that we used NG to produced heat and elec and did not import them + suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["NG_extr"].values, decimals=10) + )) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["Elec_import"].values, decimals=10), + )) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["Heat_import"].values, decimals=10), + )) + + # Now introduce an emission cap equal to 0 and show that we pick to import heat and elec + # even if they are more expensive + regional_parameters["reg1"]["emission_cap_annual"].values[:] = 0 + + model_data = ModelData( + settings, + settings.default_global_parameters, + settings.default_trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + # Check that we did not import heat and elec and produced them using NG + suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["NG_extr"].values, decimals=10) + )) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["Elec_import"].values, decimals=10) + )) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["Heat_import"].values, decimals=10) + )) + +class TestEmissionsSingleRegionPlanning(unittest.TestCase): + ''' + The scenario used in the test is: + + Reg1 + Elec_import -> Elec | + |-> Elec Demand + |-> Elec | + NG_extr -> NG -> NG_ref -> NG_prod -> NG_chp| + |-> Heat | + |-> Heat Demand + Heat_import -> Heat | + ''' + + def __init__(self, *args, **kwargs): + super(TestEmissionsSingleRegionPlanning, self).__init__(*args, **kwargs) + + # Checks the model correctly tracks the emission generate by the model solution + def test_emission_tracking(self): + example_settings = SingleNodePlanningEmissionTestSettings() + settings = ModelSettings( + ModelMode.Planning, + example_settings.global_settings, + example_settings.regional_settings, + ) + + regional_parameters = settings.default_regional_parameters + + # Increment demand of heat and elec to 1 for each timestamp + regional_parameters["reg1"]["demand"] += 1 + + # Increment the cost for importing heat and elec so it is better to produce them using NG + regional_parameters["reg1"]["tech_var_cost"].loc[:, (slice(None), ["Elec_import", "Heat_import"])] += 1 + + # Increment the emission production per tech + regional_parameters["reg1"]["specific_emission"].loc[:,(["CO2"], slice(None), ["NG_ref","NG_chp"])] += 1 + regional_parameters["reg1"]["specific_emission"].loc[:,(["NOx"], slice(None), ["NG_chp"])] += 2 + + model_data = ModelData( + settings, + settings.default_global_parameters, + settings.default_trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + # Check that we used NG to produced heat and elec and did not import them + suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["NG_extr"].values, decimals=10) + )) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["Elec_import"].values, decimals=10), + )) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["Heat_import"].values, decimals=10), + )) + + # # Check how many emission were produced + expected = np.array([[0.0, 0.0, 0.0]] * len(settings.years)) + self.assertTrue( + np.array_equal( + np.array([[0.0, 0.0, 0.0]] * len(settings.years)), + results.emission_by_type["CO2"]["reg1"]["Supply"].value + ) + ) + self.assertTrue( + np.array_equal( + np.array([[0.0, 0.0, 0.0]] * len(settings.years)), + results.emission_by_type["NOx"]["reg1"]["Supply"].value + ) + ) + self.assertTrue( + np.array_equal( + np.array([[1]] * len(settings.years)), + results.emission_by_type["CO2"]["reg1"]["Conversion"].value + ) + ) + self.assertTrue( + np.array_equal( + np.array([[0]] * len(settings.years)), + results.emission_by_type["NOx"]["reg1"]["Conversion"].value, + ) + ) + self.assertTrue( + np.array_equal( + np.array([[1]] * len(settings.years)), + results.emission_by_type["CO2"]["reg1"]["Conversion_plus"].value, + ) + ) + self.assertTrue( + np.array_equal( + np.array([[2]] * len(settings.years)), + results.emission_by_type["NOx"]["reg1"]["Conversion_plus"].value, + ) + ) + + # Checks the model correctly considers emission cost to find the optimal solution + def test_emission_cost_function(self): + example_settings = SingleNodePlanningEmissionTestSettings() + settings = ModelSettings( + ModelMode.Planning, + example_settings.global_settings, + example_settings.regional_settings, + ) + + regional_parameters = settings.default_regional_parameters + + # Increment demand of heat and elec to 1 for each timestamp + regional_parameters["reg1"]["demand"] += 1 + + # Increment the cost for importing heat and elec so it is better to produce them using NG + regional_parameters["reg1"]["tech_var_cost"].loc[:, (slice(None), ["Elec_import", "Heat_import"])] += 1 + + # Increment the emission production per tech + regional_parameters["reg1"]["specific_emission"].loc[:,(["CO2"], slice(None), ["NG_ref","NG_chp"])] += 1 + regional_parameters["reg1"]["specific_emission"].loc[:,(["NOx"], slice(None), ["NG_chp"])] += 2 + + model_data = ModelData( + settings, + settings.default_global_parameters, + settings.default_trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + # Check that we used NG to produced heat and elec and did not import them + suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["NG_extr"].values, decimals=10) + )) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["Elec_import"].values, decimals=10), + )) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["Heat_import"].values, decimals=10), + )) + + # Now increase the emission taxes and show that decide to import heat and elect + # if the emission taxes makes it more expensive to generate them using NG + regional_parameters["reg1"]["emission_tax"] += 1 + model_data = ModelData( + settings, + settings.default_global_parameters, + settings.default_trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + # Check that imported heat and elec and did not produced them using NG + suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["NG_extr"].values, decimals=10) + )) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["Elec_import"].values, decimals=10), + )) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["Heat_import"].values, decimals=10), + )) + + # Checks the model correctly considers regional emission caps to find the optimal solution + def test_emission_cap(self): + example_settings = SingleNodePlanningEmissionTestSettings() + settings = ModelSettings( + ModelMode.Planning, + example_settings.global_settings, + example_settings.regional_settings, + ) + + regional_parameters = settings.default_regional_parameters + + # Increment demand of heat and elec to 1 for each timestamp + regional_parameters["reg1"]["demand"] += 1 + + # Increment the cost for importing heat and elec so it is better to produce them using NG + regional_parameters["reg1"]["tech_var_cost"].loc[:, (slice(None), ["Elec_import", "Heat_import"])] += 1 + + # Increment the emission production per tech + regional_parameters["reg1"]["specific_emission"].loc[:,(["CO2"], slice(None), ["NG_ref","NG_chp"])] += 1 + regional_parameters["reg1"]["specific_emission"].loc[:,(["NOx"], slice(None), ["NG_chp"])] += 2 + + model_data = ModelData( + settings, + settings.default_global_parameters, + settings.default_trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + # Check that we used NG to produced heat and elec and did not import them + suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["NG_extr"].values, decimals=10) + )) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["Elec_import"].values, decimals=10), + )) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["Heat_import"].values, decimals=10), + )) + + # Now introduce an emission cap equal to 0 and show that we pick to import heat and elec + # even if they are more expensive + regional_parameters["reg1"]["emission_cap_annual"].values[:] = 0 + model_data = ModelData( + settings, + settings.default_global_parameters, + settings.default_trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + # Check that imported heat and elec and did not produced them using NG + suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["NG_extr"].values, decimals=10) + )) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["Elec_import"].values, decimals=10), + )) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(suppy_technology_use["Heat_import"].values, decimals=10), + )) + +class TestEmissionsMultiRegionOperation(unittest.TestCase): + ''' + The scenario used in the test is: + + --------- Reg1 --------- ---------------- reg2 ----------------- + Elec_import-> Elec | + |-> Elec Demand + | -> Elec | + NG_extr -> NG -> NG_ref -> NG_prod -> NG_chp| + | -> Heat | + |-> Heat Demand + Heat_import-> Heat | + ''' + + def __init__(self, *args, **kwargs): + super(TestEmissionsMultiRegionOperation, self).__init__(*args, **kwargs) + + # Checks the model correctly tracks the emission generate by the model solution + def test_emission_tracking(self): + example_settings = MultiNodeOperationEmissionTestSettings() + settings = ModelSettings( + ModelMode.Operation, + example_settings.global_settings, + example_settings.regional_settings, + ) + global_parameters = settings.default_global_parameters + regional_parameters = settings.default_regional_parameters + trade_parameters = settings.default_trade_parameters + + # Increment demand of heat and elec to 1 for each timestamp + regional_parameters["reg1"]["demand"] += 1 + + # Increment the residual capacity for all techs in the NG value chain + regional_parameters["reg1"]["tech_residual_cap"] += 8760 + regional_parameters["reg2"]["tech_residual_cap"] += 8760 + + # Increment the cost for importing heat and elec so it is better to produce them using NG + regional_parameters["reg1"]["tech_var_cost"].loc[:, (slice(None), ["Elec_import", "Heat_import"])] += 1 + + # Increment the emission production per tech + regional_parameters["reg1"]["specific_emission"].loc[:,(["CO2"], slice(None), ["NG_chp"])] += 1 + regional_parameters["reg1"]["specific_emission"].loc[:,(["NOx"], slice(None), ["NG_chp"])] += 2 + regional_parameters["reg2"]["specific_emission"].loc[:,(["CO2"], slice(None), ["NG_ref"])] += 1 + + # Set a line capacity + trade_parameters["line_residual_cap"].loc[:, (slice(None), ["NG_prod"])] = 8760 + + model_data = ModelData( + settings, + global_parameters, + trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + # Check that we used NG from reg2 and did not use the imports techs in reg1 + reg1_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Elec_import"].values, decimals=10), + )) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Heat_import"].values, decimals=10), + )) + reg2_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg2"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg2"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(reg2_suppy_technology_use["NG_extr"].values, decimals=10), + )) + + # Check how many emission were produced + self.assertTrue( + np.array_equal( + np.array([[8760]]), + results.emission_by_type["CO2"]["reg1"]["Conversion_plus"].value + ) + ) + self.assertTrue( + np.array_equal( + np.array([[17520]]), + results.emission_by_type["NOx"]["reg1"]["Conversion_plus"].value + ) + ) + self.assertTrue( + np.array_equal( + np.array([[8760]]), + results.emission_by_type["CO2"]["reg2"]["Conversion"].value + ) + ) + self.assertTrue( + np.array_equal( + np.array([[0]]), + results.emission_by_type["NOx"]["reg2"]["Conversion"].value + ) + ) + + # Checks the model correctly considers emission cost to find the optimal solution + def test_emission_cost_function(self): + example_settings = MultiNodeOperationEmissionTestSettings() + settings = ModelSettings( + ModelMode.Operation, + example_settings.global_settings, + example_settings.regional_settings, + ) + global_parameters = settings.default_global_parameters + regional_parameters = settings.default_regional_parameters + trade_parameters = settings.default_trade_parameters + + # Increment demand of heat and elec to 1 for each timestamp + regional_parameters["reg1"]["demand"] += 1 + + # Increment the residual capacity for all techs in the NG value chain + regional_parameters["reg1"]["tech_residual_cap"] += 8760 + regional_parameters["reg2"]["tech_residual_cap"] += 8760 + + # Increment the cost for importing heat and elec so it is better to produce them using NG + regional_parameters["reg1"]["tech_var_cost"].loc[:, (slice(None), ["Elec_import", "Heat_import"])] += 1 + + # Increment the emission production per tech + regional_parameters["reg1"]["specific_emission"].loc[:,(["CO2"], slice(None), ["NG_chp"])] += 1 + regional_parameters["reg1"]["specific_emission"].loc[:,(["NOx"], slice(None), ["NG_chp"])] += 2 + regional_parameters["reg2"]["specific_emission"].loc[:,(["CO2"], slice(None), ["NG_ref"])] += 1 + + # Set a line capacity + trade_parameters["line_residual_cap"].loc[:, (slice(None), ["NG_prod"])] = 8760 + + model_data = ModelData( + settings, + global_parameters, + trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + # Check that we used NG from reg2 and did not use the imports techs in reg1 + reg1_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Elec_import"].values, decimals=10), + )) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Heat_import"].values, decimals=10), + )) + reg2_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg2"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg2"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(reg2_suppy_technology_use["NG_extr"].values, decimals=10), + )) + + # Now increase the emission taxes one of the regions and check that now it is better + # to use the import techs in region 2 + regional_parameters["reg2"]["emission_tax"] += 3 + model_data = ModelData( + settings, + global_parameters, + trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + reg1_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Elec_import"].values, decimals=10), + )) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Heat_import"].values, decimals=10), + )) + reg2_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg2"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg2"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(reg2_suppy_technology_use["NG_extr"].values, decimals=10), + )) + + # Checks the model correctly considers regional emission caps to find the optimal solution + def test_emission_regional_emission_cap(self): + example_settings = MultiNodeOperationEmissionTestSettings() + settings = ModelSettings( + ModelMode.Operation, + example_settings.global_settings, + example_settings.regional_settings, + ) + global_parameters = settings.default_global_parameters + regional_parameters = settings.default_regional_parameters + trade_parameters = settings.default_trade_parameters + + # Increment demand of heat and elec to 1 for each timestamp + regional_parameters["reg1"]["demand"] += 1 + + # Increment the residual capacity for all techs in the NG value chain + regional_parameters["reg1"]["tech_residual_cap"] += 8760 + regional_parameters["reg2"]["tech_residual_cap"] += 8760 + + # Increment the cost for importing heat and elec so it is better to produce them using NG + regional_parameters["reg1"]["tech_var_cost"].loc[:, (slice(None), ["Elec_import", "Heat_import"])] += 1 + + # Increment the emission production per tech + regional_parameters["reg1"]["specific_emission"].loc[:,(["CO2"], slice(None), ["NG_chp"])] += 1 + regional_parameters["reg1"]["specific_emission"].loc[:,(["NOx"], slice(None), ["NG_chp"])] += 2 + regional_parameters["reg2"]["specific_emission"].loc[:,(["CO2"], slice(None), ["NG_ref"])] += 1 + + # Set a line capacity + trade_parameters["line_residual_cap"].loc[:, (slice(None), ["NG_prod"])] = 8760 + + model_data = ModelData( + settings, + global_parameters, + trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + # Check that we used NG from reg2 and did not use the imports techs in reg1 + reg1_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Elec_import"].values, decimals=10), + )) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Heat_import"].values, decimals=10), + )) + reg2_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg2"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg2"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(reg2_suppy_technology_use["NG_extr"].values, decimals=10), + )) + + # Now introduce an emission cap equal to 0 and show that we pick to import heat and elec + # even if they are more expensive + regional_parameters["reg2"]["emission_cap_annual"].values[:] = 0 + model_data = ModelData( + settings, + global_parameters, + trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + reg1_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Elec_import"].values, decimals=10), + )) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Heat_import"].values, decimals=10), + )) + reg2_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg2"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg2"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(reg2_suppy_technology_use["NG_extr"].values, decimals=10), + )) + + # Checks the model correctly considers global emission caps to find the optimal solution + def test_emission_global_emission_cap(self): + example_settings = MultiNodeOperationEmissionTestSettings() + settings = ModelSettings( + ModelMode.Operation, + example_settings.global_settings, + example_settings.regional_settings, + ) + global_parameters = settings.default_global_parameters + regional_parameters = settings.default_regional_parameters + trade_parameters = settings.default_trade_parameters + + # Increment demand of heat and elec to 1 for each timestamp + regional_parameters["reg1"]["demand"] += 1 + + # Increment the residual capacity for all techs in the NG value chain + regional_parameters["reg1"]["tech_residual_cap"] += 8760 + regional_parameters["reg2"]["tech_residual_cap"] += 8760 + + # Increment the cost for importing heat and elec so it is better to produce them using NG + regional_parameters["reg1"]["tech_var_cost"].loc[:, (slice(None), ["Elec_import", "Heat_import"])] += 1 + + # Increment the emission production per tech + regional_parameters["reg1"]["specific_emission"].loc[:,(["CO2"], slice(None), ["NG_chp"])] += 1 + regional_parameters["reg1"]["specific_emission"].loc[:,(["NOx"], slice(None), ["NG_chp"])] += 2 + regional_parameters["reg2"]["specific_emission"].loc[:,(["CO2"], slice(None), ["NG_ref"])] += 1 + + # Set a line capacity + trade_parameters["line_residual_cap"].loc[:, (slice(None), ["NG_prod"])] = 8760 + + model_data = ModelData( + settings, + global_parameters, + trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + # Check that we used NG from reg2 and did not use the imports techs in reg1 + reg1_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Elec_import"].values, decimals=10), + )) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Heat_import"].values, decimals=10), + )) + reg2_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg2"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg2"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(reg2_suppy_technology_use["NG_extr"].values, decimals=10), + )) + + # Now introduce an emission cap equal to 0 and show that we pick to import heat and elec + # even if they are more expensive + global_parameters["global_emission_cap_annual"].values[:] = 0 + model_data = ModelData( + settings, + global_parameters, + trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + reg1_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Elec_import"].values, decimals=10), + )) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Heat_import"].values, decimals=10), + )) + reg2_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg2"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg2"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(reg2_suppy_technology_use["NG_extr"].values, decimals=10), + )) + +class TestEmissionsMultiRegionPlanning(unittest.TestCase): + ''' + The scenario used in the test is: + + --------- Reg1 --------- -------------- reg2 ----------------- + Elec_import-> Elec | + |-> Elec Demand + | -> Elec | + NG_extr -> NG -> NG_ref -> NG_prod -> NG_chp| + | -> Heat | + |-> Heat Demand + Heat_import-> Heat | + ''' + + def __init__(self, *args, **kwargs): + super(TestEmissionsMultiRegionPlanning, self).__init__(*args, **kwargs) + + # Checks the model correctly tracks the emission generate by the model solution + def test_emission_tracking(self): + example_settings = MultiNodePlanningEmissionTestSettings() + settings = ModelSettings( + ModelMode.Planning, + example_settings.global_settings, + example_settings.regional_settings, + ) + global_parameters = settings.default_global_parameters + regional_parameters = settings.default_regional_parameters + trade_parameters = settings.default_trade_parameters + + # Increment demand of heat and elec to 1 for each timestamp + regional_parameters["reg1"]["demand"] += 1 + + # Increment the cost for importing heat and elec so it is better to produce them using NG + regional_parameters["reg1"]["tech_var_cost"].loc[:, (slice(None), ["Elec_import", "Heat_import"])] += 1 + + # Increment the emission production per tech + regional_parameters["reg1"]["specific_emission"].loc[:,(["CO2"], slice(None), ["NG_chp"])] += 1 + regional_parameters["reg1"]["specific_emission"].loc[:,(["NOx"], slice(None), ["NG_chp"])] += 2 + regional_parameters["reg2"]["specific_emission"].loc[:,(["CO2"], slice(None), ["NG_ref"])] += 1 + + model_data = ModelData( + settings, + global_parameters, + trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + # Check that we used NG from reg2 and did not use the imports techs in reg1 + reg1_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Elec_import"].values, decimals=10), + )) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Heat_import"].values, decimals=10), + )) + reg2_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg2"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg2"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(reg2_suppy_technology_use["NG_extr"].values, decimals=10), + )) + + # Check how many emission were produced + self.assertTrue( + np.array_equal( + np.array([[1]] * len(settings.years)), + results.emission_by_type["CO2"]["reg1"]["Conversion_plus"].value + ) + ) + self.assertTrue( + np.array_equal( + np.array([[2]] * len(settings.years)), + results.emission_by_type["NOx"]["reg1"]["Conversion_plus"].value + ) + ) + self.assertTrue( + np.array_equal( + np.array([[1]] * len(settings.years)), + results.emission_by_type["CO2"]["reg2"]["Conversion"].value + ) + ) + self.assertTrue( + np.array_equal( + np.array([[0]] * len(settings.years)), + results.emission_by_type["NOx"]["reg2"]["Conversion"].value + ) + ) + + # Checks the model correctly considers emission cost to find the optimal solution + def test_emission_cost_function(self): + example_settings = MultiNodeOperationEmissionTestSettings() + settings = ModelSettings( + ModelMode.Planning, + example_settings.global_settings, + example_settings.regional_settings, + ) + global_parameters = settings.default_global_parameters + regional_parameters = settings.default_regional_parameters + trade_parameters = settings.default_trade_parameters + + # Increment demand of heat and elec to 1 for each timestamp + regional_parameters["reg1"]["demand"] += 1 + + # Increment the cost for importing heat and elec so it is better to produce them using NG + regional_parameters["reg1"]["tech_var_cost"].loc[:, (slice(None), ["Elec_import", "Heat_import"])] += 1 + + # Increment the emission production per tech + regional_parameters["reg1"]["specific_emission"].loc[:,(["CO2"], slice(None), ["NG_chp"])] += 1 + regional_parameters["reg1"]["specific_emission"].loc[:,(["NOx"], slice(None), ["NG_chp"])] += 2 + regional_parameters["reg2"]["specific_emission"].loc[:,(["CO2"], slice(None), ["NG_ref"])] += 1 + + model_data = ModelData( + settings, + global_parameters, + trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + # Check that we used NG from reg2 and did not use the imports techs in reg1 + reg1_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Elec_import"].values, decimals=10), + )) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Heat_import"].values, decimals=10), + )) + reg2_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg2"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg2"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(reg2_suppy_technology_use["NG_extr"].values, decimals=10), + )) + + # Now increase the emission taxes one of the regions and check that now it is better + # to use the import techs in region 2 + regional_parameters["reg2"]["emission_tax"] += 3 + model_data = ModelData( + settings, + global_parameters, + trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + reg1_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Elec_import"].values, decimals=10), + )) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Heat_import"].values, decimals=10), + )) + reg2_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg2"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg2"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(reg2_suppy_technology_use["NG_extr"].values, decimals=10), + )) + + # Checks the model correctly considers regional emission caps to find the optimal solution + def test_emission_regional_emission_cap(self): + example_settings = MultiNodeOperationEmissionTestSettings() + settings = ModelSettings( + ModelMode.Planning, + example_settings.global_settings, + example_settings.regional_settings, + ) + global_parameters = settings.default_global_parameters + regional_parameters = settings.default_regional_parameters + trade_parameters = settings.default_trade_parameters + + # Increment demand of heat and elec to 1 for each timestamp + regional_parameters["reg1"]["demand"] += 1 + + # Increment the cost for importing heat and elec so it is better to produce them using NG + regional_parameters["reg1"]["tech_var_cost"].loc[:, (slice(None), ["Elec_import", "Heat_import"])] += 1 + + # Increment the emission production per tech + regional_parameters["reg1"]["specific_emission"].loc[:,(["CO2"], slice(None), ["NG_chp"])] += 1 + regional_parameters["reg1"]["specific_emission"].loc[:,(["NOx"], slice(None), ["NG_chp"])] += 2 + regional_parameters["reg2"]["specific_emission"].loc[:,(["CO2"], slice(None), ["NG_ref"])] += 1 + + model_data = ModelData( + settings, + global_parameters, + trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + # Check that we used NG from reg2 and did not use the imports techs in reg1 + reg1_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Elec_import"].values, decimals=10), + )) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Heat_import"].values, decimals=10), + )) + reg2_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg2"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg2"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(reg2_suppy_technology_use["NG_extr"].values, decimals=10), + )) + + # Now introduce an emission cap equal to 0 and show that we pick to import heat and elec + # even if they are more expensive + regional_parameters["reg2"]["emission_cap_annual"].values[:] = 0 + model_data = ModelData( + settings, + global_parameters, + trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + reg1_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Elec_import"].values, decimals=10), + )) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Heat_import"].values, decimals=10), + )) + reg2_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg2"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg2"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(reg2_suppy_technology_use["NG_extr"].values, decimals=10), + )) + + # Checks the model correctly considers global emission caps to find the optimal solution + def test_emission_global_emission_cap(self): + example_settings = MultiNodeOperationEmissionTestSettings() + settings = ModelSettings( + ModelMode.Planning, + example_settings.global_settings, + example_settings.regional_settings, + ) + global_parameters = settings.default_global_parameters + regional_parameters = settings.default_regional_parameters + trade_parameters = settings.default_trade_parameters + + # Increment demand of heat and elec to 1 for each timestamp + regional_parameters["reg1"]["demand"] += 1 + + # Increment the cost for importing heat and elec so it is better to produce them using NG + regional_parameters["reg1"]["tech_var_cost"].loc[:, (slice(None), ["Elec_import", "Heat_import"])] += 1 + + # Increment the emission production per tech + regional_parameters["reg1"]["specific_emission"].loc[:,(["CO2"], slice(None), ["NG_chp"])] += 1 + regional_parameters["reg1"]["specific_emission"].loc[:,(["NOx"], slice(None), ["NG_chp"])] += 2 + regional_parameters["reg2"]["specific_emission"].loc[:,(["CO2"], slice(None), ["NG_ref"])] += 1 + + model_data = ModelData( + settings, + global_parameters, + trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + # Check that we used NG from reg2 and did not use the imports techs in reg1 + reg1_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Elec_import"].values, decimals=10), + )) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Heat_import"].values, decimals=10), + )) + reg2_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg2"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg2"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(reg2_suppy_technology_use["NG_extr"].values, decimals=10), + )) + + # Now introduce an emission cap equal to 0 and show that we pick to import heat and elec + # even if they are more expensive + global_parameters["global_emission_cap_annual"].values[:] = 0 + model_data = ModelData( + settings, + global_parameters, + trade_parameters, + regional_parameters + ) + model = BuildModel(model_data=model_data) + results = model._solve(verbosity=False, solver="SCIPY") + + reg1_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg1"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg1"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Elec_import"].values, decimals=10), + )) + self.assertTrue(np.array_equal( + np.ones(len(settings.years) * len(settings.time_steps)), + np.around(reg1_suppy_technology_use["Heat_import"].values, decimals=10), + )) + reg2_suppy_technology_use = pd.DataFrame( + data=results.technology_prod["reg2"]["Supply"].value, + index=pd.MultiIndex.from_product( + [settings.years, settings.time_steps], + names=["Years", "Timesteps"], + ), + columns=settings.technologies["reg2"]["Supply"], + ) + self.assertTrue(np.array_equal( + np.zeros(len(settings.years) * len(settings.time_steps)), + np.around(reg2_suppy_technology_use["NG_extr"].values, decimals=10), + )) diff --git a/hypatia/backend/tests/ModelDataTests.py b/hypatia/backend/tests/ModelDataTests.py index dada468..18a2885 100644 --- a/hypatia/backend/tests/ModelDataTests.py +++ b/hypatia/backend/tests/ModelDataTests.py @@ -5,7 +5,7 @@ from hypatia.backend.ModelSettings import ModelSettings from hypatia.backend.ModelData import ModelData from hypatia.utility.constants import ModelMode -from hypatia.examples.ExampleSettings import ( +from hypatia.backend.tests.TestSettings import ( Utopia2PlanningSingleNodeDN, Utopia2OperationMultiNode ) diff --git a/hypatia/backend/tests/ModelSettingsTests.py b/hypatia/backend/tests/ModelSettingsTests.py index f6a3ce7..52184a5 100644 --- a/hypatia/backend/tests/ModelSettingsTests.py +++ b/hypatia/backend/tests/ModelSettingsTests.py @@ -4,7 +4,7 @@ import copy from hypatia.backend.ModelSettings import ModelSettings from hypatia.utility.constants import ModelMode -from hypatia.examples.ExampleSettings import ( +from hypatia.backend.tests.TestSettings import ( Utopia2PlanningSingleNodeDN, Utopia2OperationMultiNode ) diff --git a/hypatia/backend/tests/TestSettings.py b/hypatia/backend/tests/TestSettings.py new file mode 100644 index 0000000..075c83b --- /dev/null +++ b/hypatia/backend/tests/TestSettings.py @@ -0,0 +1,760 @@ +import pandas as pd +import numpy as np +from abc import abstractmethod + +class ExampleSettings: + @property + @abstractmethod + def global_settings(self): + pass + + @property + @abstractmethod + def regional_settings(self): + pass + +''' +Basic example settings +''' +class Utopia2PlanningSingleNodeDN(ExampleSettings): + @property + def global_settings(self): + return { + "Regions": pd.DataFrame( + np.array([['reg1', 'Utopia']]), + columns=["Region", "Region_name"], + ), + "Years": pd.DataFrame( + np.array([ + ['Y0', '2020'], + ['Y1', '2021'], + ['Y2', '2022'], + ['Y3', '2023'], + ['Y4', '2024'], + ['Y5', '2025'], + ['Y6', '2026'], + ['Y7', '2027'], + ['Y8', '2028'], + ['Y9', '2029'], + ['Y10', '2030'], + ]), + columns=["Year", "Year_name"], + ), + "Technologies_glob": pd.DataFrame( + np.array([ + ['Oil_extr', 'Oil Resource Extraction', "Supply", "extraction units", "Kilo barrels of oil"], + ['Oil_refine', 'Oil Refinery', "Conversion", "kb/d", "Kilo barrels of oil"], + ['Oil_PP', 'Oil Power Plant', "Conversion", "GW", "GWh"], + ['Hydro_PP', 'Hydro Power Plant', "Supply", "GW", "GWh"], + ['Elec_transmission', 'Electricity Transmission', 'Transmission', "GW", "GWh"], + ['Diesel_pipeline', 'Diesel Pipeline', "Transmission", "kb/y", "Kilo barrels of oil"], + ['EV', 'Electric Vehicles Demand', "Demand", "GWh", "GWh"], + ['ICEV', 'ICE Vehicles Demand', "Demand", "GWh", "GWh"], + ['HH_elec_demand', 'Household Elecricity Demand', "Demand", "-", "-"], + ['Other_elec_demand', 'Other Electricity Demand', "Demand", "-", "-"], + ]), + columns=["Technology", "Tech_name", "Tech_category", "Tech_cap_unit", "Tech_act_unit"] + ), + "Carriers_glob": pd.DataFrame( + np.array([ + ['Raw_oil', 'Raw Oil', "Resource", "Kilo barrels of oil"], + ['Oil', 'Oil', "Intermediate", "Kilo barrels of oil"], + ['Elec', 'Electricity', "Intermediate", "GWh"], + ['Oil_final', 'Final Diesel Oil', "Demand", "Kilo barrels of oil"], + ['Elec_final', 'Final Electricity', "Demand", "GWh"], + ]), + columns=["Carrier", "Carr_name", "Carr_type", "Carr_unit"] + ), + "Timesteps": pd.DataFrame( + np.array([ + ['WD_days', 'Weekdays-days', '0.357534246575342'], + ['WD_nights', 'Weekdays-nights', '0.357534246575342'], + ['WE_days', 'Weekends-days', '0.142465753424658'], + ['WE_nights', 'Weekends-nights', '0.142465753424658'], + + ]), + columns=["Timeslice", "Timeslice_name", "Timeslice_fraction"] + ), + + "Emissions": pd.DataFrame( + np.array([['CO2', 'CO2 emissions', 'ton']]), + columns=["Emission", "Emission_name", "Emission_unit"], + ), + } + + @property + def regional_settings(self): + return { + 'reg1': { + "Technologies": pd.DataFrame( + np.array([ + ['Oil_extr', 'Oil Resource Extraction', "Supply"], + ['Oil_refine', 'Oil Refinery', "Conversion"], + ['Oil_PP', 'Oil Power Plant', "Conversion"], + ['Hydro_PP', 'Hydro Power Plant', "Supply"], + ['Elec_transmission', 'Electricity Transmission', 'Transmission'], + ['Diesel_pipeline', 'Diesel Pipeline', "Transmission"], + ['EV', 'Electric Vehicles Demand', "Demand"], + ['ICEV', 'ICE Vehicles Demand', "Demand"], + ['HH_elec_demand', 'Household Elecricity Demand', "Demand"], + ['Other_elec_demand', 'Other Electricity Demand', "Demand"], + ]), + columns=["Technology", "Tech_name", "Tech_category"] + ), + "Carriers": pd.DataFrame( + np.array([ + ['Raw_Oil', 'Raw Oil', "Resource"], + ['Oil', 'Oil', "Intermediate"], + ['Elec', 'Electricity', "Intermediate"], + ['Oil_final', 'Final Diesel Oil', "Demand"], + ['Elec_final', 'Final Electricity', "Demand"], + ]), + columns=["Carrier", "Carr_name", "Carr_type"] + ), + "Carrier_input": pd.DataFrame( + np.array([ + ['Oil_refine', 'Raw_Oil'], + ['Oil_PP', 'Oil'], + ['Elec_transmission', 'Elec'], + ['Diesel_pipeline', 'Oil'], + ['EV', 'Elec_final'], + ['ICEV', 'Oil_final'], + ['HH_elec_demand', 'Elec_final'], + ['Other_elec_demand', 'Elec_final'], + ]), + columns=["Technology", "Carrier_in"] + ), + "Carrier_output": pd.DataFrame( + np.array([ + ['Oil_extr', 'Raw_Oil'], + ['Oil_refine', 'Oil'], + ['Oil_PP', 'Elec'], + ['Hydro_PP', 'Elec'], + ['Elec_transmission', 'Elec_final'], + ['Diesel_pipeline', 'Oil_final'], + ]), + columns=["Technology", "Carrier_out"] + ), + } + } + +class Utopia2OperationMultiNode(ExampleSettings): + @property + def global_settings(self): + global_settings = { + "Regions": pd.DataFrame( + np.array([ + ['reg1', 'Utopia_north'], + ['reg2', 'Utopia_south'] + ]), + columns=["Region", "Region_name"], + ), + "Years": pd.DataFrame( + np.array([ + ['Y0', '2021'], + ]), + columns=["Year", "Year_name"], + ), + "Technologies_glob": pd.DataFrame( + np.array([ + ["NG_extraction", "Natural Gas Extraction", "Supply", "number of extraction units", "BOE"], + ["CHP_PP", "Combined Heat and Power", "Conversion_plus", "MW", "MWh"], + ["Geo_PP", "Geothermal Power Plant", "Supply", "MW", "MWh"], + ["Solar_PP", "Solar Power Plant", "Supply", "MW", "MWh"], + ["Wind_PP", "Wind Power Plant", "Supply", "MW", "MWh"], + ["Hydro_PP", "Hydro Power Plant", "Supply", "MW", "MWh"], + ["Elec_transmission", "Electricity Transmission", "Transmission", "MW", "MWh"], + ["Gas_pipeline", "Natural Gas Pipeline", "Transmission", "MW", "MWh"], + ["Boiler", "Gas Boiler", "Conversion", "number of boilers", "MWh"], + ["Elec_demand", "Final Electricity Demand", "Demand", "-", "-"], + ["Heat_demand", "Final Heat Demand", "Demand", "-", "-"], + ]), + columns=["Technology", "Tech_name", "Tech_category", "Tech_cap_unit", "Tech_act_unit"] + ), + "Carriers_glob": pd.DataFrame( + np.array([ + ['NG', 'Fuel Natural Gas', "Resource", "BOE"], + ['Elec', 'Electricity', "Intermediate", "MWh"], + ['Elec_final', 'Final Electricity', "Demand", "MWh"], + ['Gas_final', 'Final Gas', "Intermediate", "BOE"], + ['Heat', 'Heat', "Demand", "MWh"], + ]), + columns=["Carrier", "Carr_name", "Carr_type", "Carr_unit"] + ), + "Emissions": pd.DataFrame( + np.array([['CO2', 'CO2 emissions', 'ton']]), + columns=["Emission", "Emission_name", "Emission_unit"], + ), + } + + time_slices = [] + for i in range(1, 8761): + time_slices.append([str(i), "h"+str(i), 0.000114155251141553]) + + global_settings["Timesteps"] = pd.DataFrame( + np.array(time_slices), + columns=["Timeslice", "Timeslice_name", "Timeslice_fraction"] + ) + + return global_settings + + @property + def regional_settings(self): + return { + 'reg1': { + "Technologies": pd.DataFrame( + np.array([ + ["NG_extraction", "Natural Gas Extraction", "Supply"], + ["CHP_PP", "Combined Heat and Power", "Conversion_plus"], + ["Wind_PP", "Wind Power Plant", "Supply"], + ["Hydro_PP", "Hydro Power Plant", "Supply"], + ["Elec_transmission", "Electricity Transmission", "Transmission"], + ["Gas_pipeline", "Natural Gas Pipeline", "Transmission"], + ["Boiler", "Gas Boiler", "Conversion"], + ["Elec_demand", "Final Electricity", "Demand"], + ["Heat_demand", "Final Heat", "Demand"], + ]), + columns=["Technology", "Tech_name", "Tech_category"] + ), + "Carriers": pd.DataFrame( + np.array([ + ['NG', 'Fuel Natural Gas', "Resource"], + ['Elec', 'Electricity', "Intermediate"], + ['Elec_final', 'Final Electricity', "Demand"], + ['Gas_final', 'Final Gas', "Intermediate"], + ['Heat', 'Heat', "Demand"], + ]), + columns=["Carrier", "Carr_name", "Carr_type"] + ), + "Carrier_input": pd.DataFrame( + np.array([ + ['CHP_PP', 'NG'], + ['Elec_transmission', 'Elec'], + ['Gas_pipeline', 'NG'], + ['Boiler', 'Gas_final'], + ['Elec_demand', 'Elec_final'], + ['Heat_demand', 'Heat'], + ]), + columns=["Technology", "Carrier_in"] + ), + "Carrier_output": pd.DataFrame( + np.array([ + ['NG_extraction', 'NG'], + ['CHP_PP', 'Elec'], + ['CHP_PP', 'Heat'], + ['Wind_PP', 'Elec'], + ['Hydro_PP', 'Elec'], + ['Elec_transmission', 'Elec_final'], + ['Gas_pipeline', 'Gas_final'], + ['Boiler', 'Heat'], + + ]), + columns=["Technology", "Carrier_out"] + ), + }, + 'reg2': { + "Technologies": pd.DataFrame( + np.array([ + ["Geo_PP", "Geothermal Power Plant", "Supply"], + ["Solar_PP", "Solar Power Plant", "Supply"], + ["Elec_transmission", "Electricity Transmission", "Transmission"], + ["Elec_demand", "Final Electricity", "Demand"], + ]), + columns=["Technology", "Tech_name", "Tech_category"] + ), + "Carriers": pd.DataFrame( + np.array([ + ['Elec', 'Electricity', "Intermediate"], + ['Elec_final', 'Final Electricity', "Demand"], + ]), + columns=["Carrier", "Carr_name", "Carr_type"] + ), + "Carrier_input": pd.DataFrame( + np.array([ + ['Elec_transmission', 'Elec'], + ['Elec_demand', 'Elec_final'], + ]), + columns=["Technology", "Carrier_in"] + ), + "Carrier_output": pd.DataFrame( + np.array([ + ['Geo_PP', 'Elec'], + ['Solar_PP', 'Elec'], + ['Elec_transmission', 'Elec_final'], + ]), + columns=["Technology", "Carrier_out"] + ), + } + } + +''' +Emission Tests settings +''' +class SingleNodeOperationEmissionTestSettings(ExampleSettings): + @property + def global_settings(self): + global_settings = { + "Regions": pd.DataFrame( + np.array([['reg1', 'Utopia']]), + columns=["Region", "Region_name"], + ), + "Years": pd.DataFrame( + np.array([ + ['Y0', '2020'], + ]), + columns=["Year", "Year_name"], + ), + "Technologies_glob": pd.DataFrame( + np.array([ + ['NG_extr', 'NG extraction', "Supply", "GW", "GWh"], + ['Elec_import', 'Import of Electricity', "Supply", "GW", "GWh"], + ['Heat_import', 'Import of Heat', "Supply", "GW", "GWh"], + ['NG_ref', 'NG refinery', "Conversion", "GW", "GWh"], + ['NG_chp', 'NG combined heat and power', "Conversion_plus", "GWh", "GWh"], + ['Elec_demand', 'Electricty demand', "Demand", "GWh", "GWh"], + ['Heat_demand', 'Heat demand', "Demand", "GWh", "GWh"], + ]), + columns=["Technology", "Tech_name", "Tech_category", "Tech_cap_unit", "Tech_act_unit"] + ), + "Carriers_glob": pd.DataFrame( + np.array([ + ['NG', 'NG', "Resource", "GWh"], + ['NG_prod', 'NG prod', "Intermediate", "GWh"], + ['Elec', 'Electricity', "Demand", "GWh"], + ['Heat', 'Electricity', "Demand", "GWh"], + ]), + columns=["Carrier", "Carr_name", "Carr_type", "Carr_unit"] + ), + "Emissions": pd.DataFrame( + np.array([ + ['CO2', 'CO2 emissions', 'ton'], + ['NOx', 'NOx emissions', 'ton'] + ]), + columns=["Emission", "Emission_name", "Emission_unit"], + ), + } + + time_slices = [] + for i in range(1, 8761): + time_slices.append([str(i), "h"+str(i), 0.000114155251141553]) + + global_settings["Timesteps"] = pd.DataFrame( + np.array(time_slices), + columns=["Timeslice", "Timeslice_name", "Timeslice_fraction"] + ) + + return global_settings + + @property + def regional_settings(self): + return { + 'reg1': { + "Technologies": pd.DataFrame( + np.array([ + ['NG_extr', 'NG extraction', "Supply"], + ['Elec_import', 'Import of Electricity', "Supply"], + ['Heat_import', 'Import of Heat', "Supply"], + ['NG_ref', 'NG refinery', "Conversion"], + ['NG_chp', 'NG combined heat and power', "Conversion_plus"], + ['Elec_demand', 'Electricty demand', "Demand"], + ['Heat_demand', 'Heat demand', "Demand"], + ]), + columns=["Technology", "Tech_name", "Tech_category"] + ), + "Carriers": pd.DataFrame( + np.array([ + ['NG', 'NG', "Resource"], + ['NG_prod', 'NG prod', "Intermediate"], + ['Elec', 'Electricity', "Demand"], + ['Heat', 'Electricity', "Demand"], + ]), + columns=["Carrier", "Carr_name", "Carr_type"] + ), + "Carrier_input": pd.DataFrame( + np.array([ + ['NG_ref', 'NG'], + ['NG_chp', 'NG_prod'], + ['Elec_demand', 'Elec'], + ['Elec_demand', 'Heat'], + ]), + columns=["Technology", "Carrier_in"] + ), + "Carrier_output": pd.DataFrame( + np.array([ + ['NG_extr', 'NG'], + ['NG_ref', 'NG_prod'], + ['NG_chp', 'Elec'], + ['NG_chp', 'Heat'], + ['Elec_import', 'Elec'], + ['Heat_import', 'Heat'], + ]), + columns=["Technology", "Carrier_out"] + ), + } + } + +class SingleNodePlanningEmissionTestSettings(ExampleSettings): + @property + def global_settings(self): + global_settings = { + "Regions": pd.DataFrame( + np.array([['reg1', 'Utopia']]), + columns=["Region", "Region_name"], + ), + "Years": pd.DataFrame( + np.array([ + ['Y0', '2020'], + ['Y1', '2020'], + ['Y2', '2020'], + ['Y3', '2020'], + ['Y4', '2020'], + ['Y5', '2020'], + ]), + columns=["Year", "Year_name"], + ), + "Technologies_glob": pd.DataFrame( + np.array([ + ['NG_extr', 'NG extraction', "Supply", "GW", "GWh"], + ['Elec_import', 'Import of Electricity', "Supply", "GW", "GWh"], + ['Heat_import', 'Import of Heat', "Supply", "GW", "GWh"], + ['NG_ref', 'NG refinery', "Conversion", "GW", "GWh"], + ['NG_chp', 'NG combined heat and power', "Conversion_plus", "GWh", "GWh"], + ['Elec_demand', 'Electricty demand', "Demand", "GWh", "GWh"], + ['Heat_demand', 'Heat demand', "Demand", "GWh", "GWh"], + ]), + columns=["Technology", "Tech_name", "Tech_category", "Tech_cap_unit", "Tech_act_unit"] + ), + "Carriers_glob": pd.DataFrame( + np.array([ + ['NG', 'NG', "Resource", "GWh"], + ['NG_prod', 'NG prod', "Intermediate", "GWh"], + ['Elec', 'Electricity', "Demand", "GWh"], + ['Heat', 'Electricity', "Demand", "GWh"], + ]), + columns=["Carrier", "Carr_name", "Carr_type", "Carr_unit"] + ), + "Emissions": pd.DataFrame( + np.array([ + ['CO2', 'CO2 emissions', 'ton'], + ['NOx', 'NOx emissions', 'ton'] + ]), + columns=["Emission", "Emission_name", "Emission_unit"], + ), + } + + global_settings["Timesteps"] = pd.DataFrame( + np.array([["0", "Annual", 1]]), + columns=["Timeslice", "Timeslice_name", "Timeslice_fraction"] + ) + + return global_settings + + @property + def regional_settings(self): + return { + 'reg1': { + "Technologies": pd.DataFrame( + np.array([ + ['NG_extr', 'NG extraction', "Supply"], + ['Elec_import', 'Import of Electricity', "Supply"], + ['Heat_import', 'Import of Heat', "Supply"], + ['NG_ref', 'NG refinery', "Conversion"], + ['NG_chp', 'NG combined heat and power', "Conversion_plus"], + ['Elec_demand', 'Electricty demand', "Demand"], + ['Heat_demand', 'Heat demand', "Demand"], + ]), + columns=["Technology", "Tech_name", "Tech_category"] + ), + "Carriers": pd.DataFrame( + np.array([ + ['NG', 'NG', "Resource"], + ['NG_prod', 'NG prod', "Intermediate"], + ['Elec', 'Electricity', "Demand"], + ['Heat', 'Electricity', "Demand"], + ]), + columns=["Carrier", "Carr_name", "Carr_type"] + ), + "Carrier_input": pd.DataFrame( + np.array([ + ['NG_ref', 'NG'], + ['NG_chp', 'NG_prod'], + ['Elec_demand', 'Elec'], + ['Elec_demand', 'Heat'], + ]), + columns=["Technology", "Carrier_in"] + ), + "Carrier_output": pd.DataFrame( + np.array([ + ['NG_extr', 'NG'], + ['NG_ref', 'NG_prod'], + ['NG_chp', 'Elec'], + ['NG_chp', 'Heat'], + ['Elec_import', 'Elec'], + ['Heat_import', 'Heat'], + ]), + columns=["Technology", "Carrier_out"] + ), + } + } + +class MultiNodeOperationEmissionTestSettings(ExampleSettings): + @property + def global_settings(self): + global_settings = { + "Regions": pd.DataFrame( + np.array([ + ['reg1', 'Utopia1'], + ['reg2', 'Utopia2'], + ]), + columns=["Region", "Region_name"], + ), + "Years": pd.DataFrame( + np.array([ + ['Y0', '2020'], + ]), + columns=["Year", "Year_name"], + ), + "Technologies_glob": pd.DataFrame( + np.array([ + ['NG_extr', 'NG extraction', "Supply", "GW", "GWh"], + ['Elec_import', 'Import of Electricity', "Supply", "GW", "GWh"], + ['Heat_import', 'Import of Heat', "Supply", "GW", "GWh"], + ['NG_ref', 'NG refinery', "Conversion", "GW", "GWh"], + ['NG_chp', 'NG combined heat and power', "Conversion_plus", "GWh", "GWh"], + ['Elec_demand', 'Electricty demand', "Demand", "GWh", "GWh"], + ['Heat_demand', 'Heat demand', "Demand", "GWh", "GWh"], + ]), + columns=["Technology", "Tech_name", "Tech_category", "Tech_cap_unit", "Tech_act_unit"] + ), + "Carriers_glob": pd.DataFrame( + np.array([ + ['NG', 'NG', "Resource", "GWh"], + ['NG_prod', 'NG prod', "Intermediate", "GWh"], + ['Elec', 'Electricity', "Demand", "GWh"], + ['Heat', 'Electricity', "Demand", "GWh"], + ]), + columns=["Carrier", "Carr_name", "Carr_type", "Carr_unit"] + ), + "Emissions": pd.DataFrame( + np.array([ + ['CO2', 'CO2 emissions', 'ton'], + ['NOx', 'NOx emissions', 'ton'] + ]), + columns=["Emission", "Emission_name", "Emission_unit"], + ), + } + + time_slices = [] + for i in range(1, 8761): + time_slices.append([str(i), "h"+str(i), 0.000114155251141553]) + + global_settings["Timesteps"] = pd.DataFrame( + np.array(time_slices), + columns=["Timeslice", "Timeslice_name", "Timeslice_fraction"] + ) + + return global_settings + + @property + def regional_settings(self): + return { + 'reg1': { + "Technologies": pd.DataFrame( + np.array([ + ['Elec_import', 'Import of Electricity', "Supply"], + ['Heat_import', 'Import of Heat', "Supply"], + ['NG_chp', 'NG combined heat and power', "Conversion_plus"], + ['Elec_demand', 'Electricty demand', "Demand"], + ['Heat_demand', 'Heat demand', "Demand"], + ['placeholder_demand', 'place_holder', "Demand"], + ]), + columns=["Technology", "Tech_name", "Tech_category"] + ), + "Carriers": pd.DataFrame( + np.array([ + ['NG_prod', 'NG prod', "Intermediate"], + ['Elec', 'Electricity', "Demand"], + ['Heat', 'Electricity', "Demand"], + ]), + columns=["Carrier", "Carr_name", "Carr_type"] + ), + "Carrier_input": pd.DataFrame( + np.array([ + ['NG_chp', 'NG_prod'], + ['Elec_demand', 'Elec'], + ['Elec_demand', 'Heat'], + ]), + columns=["Technology", "Carrier_in"] + ), + "Carrier_output": pd.DataFrame( + np.array([ + ['NG_chp', 'Elec'], + ['NG_chp', 'Heat'], + ['Elec_import', 'Elec'], + ['Heat_import', 'Heat'], + ]), + columns=["Technology", "Carrier_out"] + ), + }, + 'reg2': { + "Technologies": pd.DataFrame( + np.array([ + ['NG_extr', 'NG extraction', "Supply"], + ['NG_ref', 'NG refinery', "Conversion"], + ['placeholder_demand', 'place_holder', "Demand"], + ]), + columns=["Technology", "Tech_name", "Tech_category"] + ), + "Carriers": pd.DataFrame( + np.array([ + ['NG', 'NG', "Resource"], + ['NG_prod', 'NG prod', "Intermediate"], + ]), + columns=["Carrier", "Carr_name", "Carr_type"] + ), + "Carrier_input": pd.DataFrame( + np.array([ + ['NG_ref', 'NG'], + ]), + columns=["Technology", "Carrier_in"] + ), + "Carrier_output": pd.DataFrame( + np.array([ + ['NG_extr', 'NG'], + ['NG_ref', 'NG_prod'], + ]), + columns=["Technology", "Carrier_out"] + ), + } + } + +class MultiNodePlanningEmissionTestSettings(ExampleSettings): + @property + def global_settings(self): + global_settings = { + "Regions": pd.DataFrame( + np.array([ + ['reg1', 'Utopia1'], + ['reg2', 'Utopia2'], + ]), + columns=["Region", "Region_name"], + ), + "Years": pd.DataFrame( + np.array([ + ['Y0', '2020'], + ['Y1', '2020'], + ['Y2', '2020'], + ['Y3', '2020'], + ['Y4', '2020'], + ['Y5', '2020'], ]), + columns=["Year", "Year_name"], + ), + "Technologies_glob": pd.DataFrame( + np.array([ + ['NG_extr', 'NG extraction', "Supply", "GW", "GWh"], + ['Elec_import', 'Import of Electricity', "Supply", "GW", "GWh"], + ['Heat_import', 'Import of Heat', "Supply", "GW", "GWh"], + ['NG_ref', 'NG refinery', "Conversion", "GW", "GWh"], + ['NG_chp', 'NG combined heat and power', "Conversion_plus", "GWh", "GWh"], + ['Elec_demand', 'Electricty demand', "Demand", "GWh", "GWh"], + ['Heat_demand', 'Heat demand', "Demand", "GWh", "GWh"], + ]), + columns=["Technology", "Tech_name", "Tech_category", "Tech_cap_unit", "Tech_act_unit"] + ), + "Carriers_glob": pd.DataFrame( + np.array([ + ['NG', 'NG', "Resource", "GWh"], + ['NG_prod', 'NG prod', "Intermediate", "GWh"], + ['Elec', 'Electricity', "Demand", "GWh"], + ['Heat', 'Electricity', "Demand", "GWh"], + ]), + columns=["Carrier", "Carr_name", "Carr_type", "Carr_unit"] + ), + "Emissions": pd.DataFrame( + np.array([ + ['CO2', 'CO2 emissions', 'ton'], + ['NOx', 'NOx emissions', 'ton'] + ]), + columns=["Emission", "Emission_name", "Emission_unit"], + ), + } + + global_settings["Timesteps"] = pd.DataFrame( + np.array([["0", "Annual", 1]]), + columns=["Timeslice", "Timeslice_name", "Timeslice_fraction"] + ) + + return global_settings + + @property + def regional_settings(self): + return { + 'reg1': { + "Technologies": pd.DataFrame( + np.array([ + ['Elec_import', 'Import of Electricity', "Supply"], + ['Heat_import', 'Import of Heat', "Supply"], + ['NG_chp', 'NG combined heat and power', "Conversion_plus"], + ['Elec_demand', 'Electricty demand', "Demand"], + ['Heat_demand', 'Heat demand', "Demand"], + ['placeholder_demand', 'place_holder', "Demand"], + ]), + columns=["Technology", "Tech_name", "Tech_category"] + ), + "Carriers": pd.DataFrame( + np.array([ + ['NG_prod', 'NG prod', "Intermediate"], + ['Elec', 'Electricity', "Demand"], + ['Heat', 'Electricity', "Demand"], + ]), + columns=["Carrier", "Carr_name", "Carr_type"] + ), + "Carrier_input": pd.DataFrame( + np.array([ + ['NG_chp', 'NG_prod'], + ['Elec_demand', 'Elec'], + ['Elec_demand', 'Heat'], + ]), + columns=["Technology", "Carrier_in"] + ), + "Carrier_output": pd.DataFrame( + np.array([ + ['NG_chp', 'Elec'], + ['NG_chp', 'Heat'], + ['Elec_import', 'Elec'], + ['Heat_import', 'Heat'], + ]), + columns=["Technology", "Carrier_out"] + ), + }, + 'reg2': { + "Technologies": pd.DataFrame( + np.array([ + ['NG_extr', 'NG extraction', "Supply"], + ['NG_ref', 'NG refinery', "Conversion"], + ['placeholder_demand', 'place_holder', "Demand"], + ]), + columns=["Technology", "Tech_name", "Tech_category"] + ), + "Carriers": pd.DataFrame( + np.array([ + ['NG', 'NG', "Resource"], + ['NG_prod', 'NG prod', "Intermediate"], + ]), + columns=["Carrier", "Carr_name", "Carr_type"] + ), + "Carrier_input": pd.DataFrame( + np.array([ + ['NG_ref', 'NG'], + ]), + columns=["Technology", "Carrier_in"] + ), + "Carrier_output": pd.DataFrame( + np.array([ + ['NG_extr', 'NG'], + ['NG_ref', 'NG_prod'], + ]), + columns=["Technology", "Carrier_out"] + ), + } + } diff --git a/hypatia/examples/ExampleSettings.py b/hypatia/examples/ExampleSettings.py deleted file mode 100644 index 464094b..0000000 --- a/hypatia/examples/ExampleSettings.py +++ /dev/null @@ -1,285 +0,0 @@ -import pandas as pd -import numpy as np -from abc import abstractmethod - -class ExampleSettings: - @property - @abstractmethod - def global_settings(self): - pass - - @property - @abstractmethod - def regional_settings(self): - pass - -class Utopia2PlanningSingleNodeDN(ExampleSettings): - @property - def global_settings(self): - return { - "Regions": pd.DataFrame( - np.array([['reg1', 'Utopia']]), - columns=["Region", "Region_name"], - ), - "Years": pd.DataFrame( - np.array([ - ['Y0', '2020'], - ['Y1', '2021'], - ['Y2', '2022'], - ['Y3', '2023'], - ['Y4', '2024'], - ['Y5', '2025'], - ['Y6', '2026'], - ['Y7', '2027'], - ['Y8', '2028'], - ['Y9', '2029'], - ['Y10', '2030'], - ]), - columns=["Year", "Year_name"], - ), - "Technologies_glob": pd.DataFrame( - np.array([ - ['Oil_extr', 'Oil Resource Extraction', "Supply", "extraction units", "Kilo barrels of oil"], - ['Oil_refine', 'Oil Refinery', "Conversion", "kb/d", "Kilo barrels of oil"], - ['Oil_PP', 'Oil Power Plant', "Conversion", "GW", "GWh"], - ['Hydro_PP', 'Hydro Power Plant', "Supply", "GW", "GWh"], - ['Elec_transmission', 'Electricity Transmission', 'Transmission', "GW", "GWh"], - ['Diesel_pipeline', 'Diesel Pipeline', "Transmission", "kb/y", "Kilo barrels of oil"], - ['EV', 'Electric Vehicles Demand', "Demand", "GWh", "GWh"], - ['ICEV', 'ICE Vehicles Demand', "Demand", "GWh", "GWh"], - ['HH_elec_demand', 'Household Elecricity Demand', "Demand", "-", "-"], - ['Other_elec_demand', 'Other Electricity Demand', "Demand", "-", "-"], - ]), - columns=["Technology", "Tech_name", "Tech_category", "Tech_cap_unit", "Tech_act_unit"] - ), - "Carriers_glob": pd.DataFrame( - np.array([ - ['Raw_oil', 'Raw Oil', "Resource", "Kilo barrels of oil"], - ['Oil', 'Oil', "Intermediate", "Kilo barrels of oil"], - ['Elec', 'Electricity', "Intermediate", "GWh"], - ['Oil_final', 'Final Diesel Oil', "Demand", "Kilo barrels of oil"], - ['Elec_final', 'Final Electricity', "Demand", "GWh"], - ]), - columns=["Carrier", "Carr_name", "Carr_type", "Carr_unit"] - ), - "Timesteps": pd.DataFrame( - np.array([ - ['WD_days', 'Weekdays-days', '0.357534246575342'], - ['WD_nights', 'Weekdays-nights', '0.357534246575342'], - ['WE_days', 'Weekends-days', '0.142465753424658'], - ['WE_nights', 'Weekends-nights', '0.142465753424658'], - - ]), - columns=["Timeslice", "Timeslice_name", "Timeslice_fraction"] - ), - - "Emissions": pd.DataFrame( - np.array([['CO2', 'CO2 emissions', 'ton']]), - columns=["Emission", "Emission_name", "Emission_unit"], - ), - } - - @property - def regional_settings(self): - return { - 'reg1': { - "Technologies": pd.DataFrame( - np.array([ - ['Oil_extr', 'Oil Resource Extraction', "Supply"], - ['Oil_refine', 'Oil Refinery', "Conversion"], - ['Oil_PP', 'Oil Power Plant', "Conversion"], - ['Hydro_PP', 'Hydro Power Plant', "Supply"], - ['Elec_transmission', 'Electricity Transmission', 'Transmission'], - ['Diesel_pipeline', 'Diesel Pipeline', "Transmission"], - ['EV', 'Electric Vehicles Demand', "Demand"], - ['ICEV', 'ICE Vehicles Demand', "Demand"], - ['HH_elec_demand', 'Household Elecricity Demand', "Demand"], - ['Other_elec_demand', 'Other Electricity Demand', "Demand"], - ]), - columns=["Technology", "Tech_name", "Tech_category"] - ), - "Carriers": pd.DataFrame( - np.array([ - ['Raw_Oil', 'Raw Oil', "Resource"], - ['Oil', 'Oil', "Intermediate"], - ['Elec', 'Electricity', "Intermediate"], - ['Oil_final', 'Final Diesel Oil', "Demand"], - ['Elec_final', 'Final Electricity', "Demand"], - ]), - columns=["Carrier", "Carr_name", "Carr_type"] - ), - "Carrier_input": pd.DataFrame( - np.array([ - ['Oil_refine', 'Raw_Oil'], - ['Oil_PP', 'Oil'], - ['Elec_transmission', 'Elec'], - ['Diesel_pipeline', 'Oil'], - ['EV', 'Elec_final'], - ['ICEV', 'Oil_final'], - ['HH_elec_demand', 'Elec_final'], - ['Other_elec_demand', 'Elec_final'], - ]), - columns=["Technology", "Carrier_in"] - ), - "Carrier_output": pd.DataFrame( - np.array([ - ['Oil_extr', 'Raw_Oil'], - ['Oil_refine', 'Oil'], - ['Oil_PP', 'Elec'], - ['Hydro_PP', 'Elec'], - ['Elec_transmission', 'Elec_final'], - ['Diesel_pipeline', 'Oil_final'], - ]), - columns=["Technology", "Carrier_out"] - ), - } - } - -class Utopia2OperationMultiNode(ExampleSettings): - @property - def global_settings(self): - global_settings = { - "Regions": pd.DataFrame( - np.array([ - ['reg1', 'Utopia_north'], - ['reg2', 'Utopia_south'] - ]), - columns=["Region", "Region_name"], - ), - "Years": pd.DataFrame( - np.array([ - ['Y0', '2021'], - ]), - columns=["Year", "Year_name"], - ), - "Technologies_glob": pd.DataFrame( - np.array([ - ["NG_extraction", "Natural Gas Extraction", "Supply", "number of extraction units", "BOE"], - ["CHP_PP", "Combined Heat and Power", "Conversion_plus", "MW", "MWh"], - ["Geo_PP", "Geothermal Power Plant", "Supply", "MW", "MWh"], - ["Solar_PP", "Solar Power Plant", "Supply", "MW", "MWh"], - ["Wind_PP", "Wind Power Plant", "Supply", "MW", "MWh"], - ["Hydro_PP", "Hydro Power Plant", "Supply", "MW", "MWh"], - ["Elec_transmission", "Electricity Transmission", "Transmission", "MW", "MWh"], - ["Gas_pipeline", "Natural Gas Pipeline", "Transmission", "MW", "MWh"], - ["Boiler", "Gas Boiler", "Conversion", "number of boilers", "MWh"], - ["Elec_demand", "Final Electricity Demand", "Demand", "-", "-"], - ["Heat_demand", "Final Heat Demand", "Demand", "-", "-"], - ]), - columns=["Technology", "Tech_name", "Tech_category", "Tech_cap_unit", "Tech_act_unit"] - ), - "Carriers_glob": pd.DataFrame( - np.array([ - ['NG', 'Fuel Natural Gas', "Resource", "BOE"], - ['Elec', 'Electricity', "Intermediate", "MWh"], - ['Elec_final', 'Final Electricity', "Demand", "MWh"], - ['Gas_final', 'Final Gas', "Intermediate", "BOE"], - ['Heat', 'Heat', "Demand", "MWh"], - ]), - columns=["Carrier", "Carr_name", "Carr_type", "Carr_unit"] - ), - "Emissions": pd.DataFrame( - np.array([['CO2', 'CO2 emissions', 'ton']]), - columns=["Emission", "Emission_name", "Emission_unit"], - ), - } - - time_slices = [] - for i in range(1, 8761): - time_slices.append([str(i), "h"+str(i), 0.000114155251141553]) - - global_settings["Timesteps"] = pd.DataFrame( - np.array(time_slices), - columns=["Timeslice", "Timeslice_name", "Timeslice_fraction"] - ) - - return global_settings - - @property - def regional_settings(self): - return { - 'reg1': { - "Technologies": pd.DataFrame( - np.array([ - ["NG_extraction", "Natural Gas Extraction", "Supply"], - ["CHP_PP", "Combined Heat and Power", "Conversion_plus"], - ["Wind_PP", "Wind Power Plant", "Supply"], - ["Hydro_PP", "Hydro Power Plant", "Supply"], - ["Elec_transmission", "Electricity Transmission", "Transmission"], - ["Gas_pipeline", "Natural Gas Pipeline", "Transmission"], - ["Boiler", "Gas Boiler", "Conversion"], - ["Elec_demand", "Final Electricity", "Demand"], - ["Heat_demand", "Final Heat", "Demand"], - ]), - columns=["Technology", "Tech_name", "Tech_category"] - ), - "Carriers": pd.DataFrame( - np.array([ - ['NG', 'Fuel Natural Gas', "Resource"], - ['Elec', 'Electricity', "Intermediate"], - ['Elec_final', 'Final Electricity', "Demand"], - ['Gas_final', 'Final Gas', "Intermediate"], - ['Heat', 'Heat', "Demand"], - ]), - columns=["Carrier", "Carr_name", "Carr_type"] - ), - "Carrier_input": pd.DataFrame( - np.array([ - ['CHP_PP', 'NG'], - ['Elec_transmission', 'Elec'], - ['Gas_pipeline', 'NG'], - ['Boiler', 'Gas_final'], - ['Elec_demand', 'Elec_final'], - ['Heat_demand', 'Heat'], - ]), - columns=["Technology", "Carrier_in"] - ), - "Carrier_output": pd.DataFrame( - np.array([ - ['NG_extraction', 'NG'], - ['CHP_PP', 'Elec'], - ['CHP_PP', 'Heat'], - ['Wind_PP', 'Elec'], - ['Hydro_PP', 'Elec'], - ['Elec_transmission', 'Elec_final'], - ['Gas_pipeline', 'Gas_final'], - ['Boiler', 'Heat'], - - ]), - columns=["Technology", "Carrier_out"] - ), - }, - 'reg2': { - "Technologies": pd.DataFrame( - np.array([ - ["Geo_PP", "Geothermal Power Plant", "Supply"], - ["Solar_PP", "Solar Power Plant", "Supply"], - ["Elec_transmission", "Electricity Transmission", "Transmission"], - ["Elec_demand", "Final Electricity", "Demand"], - ]), - columns=["Technology", "Tech_name", "Tech_category"] - ), - "Carriers": pd.DataFrame( - np.array([ - ['Elec', 'Electricity', "Intermediate"], - ['Elec_final', 'Final Electricity', "Demand"], - ]), - columns=["Carrier", "Carr_name", "Carr_type"] - ), - "Carrier_input": pd.DataFrame( - np.array([ - ['Elec_transmission', 'Elec'], - ['Elec_demand', 'Elec_final'], - ]), - columns=["Technology", "Carrier_in"] - ), - "Carrier_output": pd.DataFrame( - np.array([ - ['Geo_PP', 'Elec'], - ['Solar_PP', 'Elec'], - ['Elec_transmission', 'Elec_final'], - ]), - columns=["Technology", "Carrier_out"] - ), - } - } diff --git a/hypatia/utility/utility.py b/hypatia/utility/utility.py index 9cc78b3..964ad14 100644 --- a/hypatia/utility/utility.py +++ b/hypatia/utility/utility.py @@ -523,3 +523,7 @@ def create_technology_columns( ) return indexer + + +def get_emission_types(glob_settings): + return glob_settings["Emissions"]["Emission"].values