diff --git a/energy_models/core/ccus/ccus.py b/energy_models/core/ccus/ccus.py index 4db58eb1..d0f6c87e 100644 --- a/energy_models/core/ccus/ccus.py +++ b/energy_models/core/ccus/ccus.py @@ -51,8 +51,6 @@ def __init__(self, name): self.year_end = None self.years = None self.co2_for_food = None - self.scaling_factor_energy_production = None - self.scaling_factor_energy_consumption = None self.inputs_dict = {} self.outputs_dict = {} @@ -65,44 +63,43 @@ def configure_parameters(self, inputs_dict): self.year_start = inputs_dict[GlossaryEnergy.YearStart] self.year_end = inputs_dict[GlossaryEnergy.YearEnd] self.years = np.arange(self.year_start, self.year_end + 1) - self.scaling_factor_energy_production = inputs_dict['scaling_factor_energy_production'] - self.scaling_factor_energy_consumption = inputs_dict['scaling_factor_energy_consumption'] self.co2_for_food = pd.DataFrame({GlossaryEnergy.Years: self.years, f'{GlossaryEnergy.carbon_capture} for food (Mt)': 0.0}) self.ccs_list = [GlossaryEnergy.carbon_capture, GlossaryEnergy.carbon_storage] def compute_carbon_storage_capacity(self): - total_carbon_storage_by_invest = self.inputs_dict[f"{GlossaryEnergy.carbon_storage}.{GlossaryEnergy.EnergyProductionValue}"][ GlossaryEnergy.carbon_storage].values * self.scaling_factor_energy_production + total_carbon_storage_by_invest_mt = self.inputs_dict[f"{GlossaryEnergy.carbon_storage}.{GlossaryEnergy.EnergyProductionValue}"][ GlossaryEnergy.carbon_storage].values - self.outputs_dict['carbon_storage_capacity'] = pd.DataFrame({ + self.outputs_dict['carbon_storage_capacity (Gt)'] = pd.DataFrame({ GlossaryEnergy.Years: self.years, - 'carbon_storage_capacity': total_carbon_storage_by_invest + 'carbon_storage_capacity (Gt)': total_carbon_storage_by_invest_mt / 1e3 }) def compute_co2_emissions(self): self.compute_carbon_storage_capacity() - carbon_capture_from_energy_mix = self.inputs_dict['carbon_capture_from_energy_mix'][f'{GlossaryEnergy.carbon_capture} from energy mix (Gt)'].values - co2_emissions_needed_by_energy_mix = self.inputs_dict['co2_emissions_needed_by_energy_mix'][f'{GlossaryEnergy.carbon_capture} needed by energy mix (Gt)'].values - co2_for_food = self.co2_for_food[f'{GlossaryEnergy.carbon_capture} for food (Mt)'].values - - carbon_capture_prod = self.inputs_dict[f"{GlossaryEnergy.carbon_capture}.{GlossaryEnergy.EnergyProductionValue}"][GlossaryEnergy.carbon_capture].values - carbon_storage_prod = self.inputs_dict[f"{GlossaryEnergy.carbon_storage}.{GlossaryEnergy.EnergyProductionValue}"][GlossaryEnergy.carbon_storage].values - - carbon_capture_to_be_stored, carbon_storage_limited_by_capture_gt, carbon_storage, carbon_capture_from_cc_technos = compute_carbon_storage_limited_by_capture_gt( - carbon_capture_prod=carbon_capture_prod, - carbon_storage_prod=carbon_storage_prod, - carbon_capture_from_energy_mix=carbon_capture_from_energy_mix, - co2_emissions_needed_by_energy_mix=co2_emissions_needed_by_energy_mix, - co2_for_food=co2_for_food, - scaling_factor_energy_production=self.scaling_factor_energy_production + carbon_capture_from_energy_mix_gt = self.inputs_dict['carbon_capture_from_energy_mix'][f'{GlossaryEnergy.carbon_capture} from energy mix (Gt)'].values + co2_emissions_needed_by_energy_mix_gt = self.inputs_dict['co2_emissions_needed_by_energy_mix'][f'{GlossaryEnergy.carbon_capture} needed by energy mix (Gt)'].values + co2_for_food_mt = self.co2_for_food[f'{GlossaryEnergy.carbon_capture} for food (Mt)'].values + + # production of CCS technos are in Mt + carbon_capture_prod_mt = self.inputs_dict[f"{GlossaryEnergy.carbon_capture}.{GlossaryEnergy.EnergyProductionValue}"][GlossaryEnergy.carbon_capture].values + carbon_storage_prod_mt = self.inputs_dict[f"{GlossaryEnergy.carbon_storage}.{GlossaryEnergy.EnergyProductionValue}"][GlossaryEnergy.carbon_storage].values + + # Outputs are in Gt (for automatic differentiation purpose : coupling output var is in Gt) + carbon_capture_to_be_stored_gt, carbon_storage_limited_by_capture_gt, carbon_storage_gt, carbon_capture_from_cc_technos_gt = compute_carbon_storage_limited_by_capture_gt( + carbon_capture_prod_mt=carbon_capture_prod_mt, + carbon_storage_prod_mt=carbon_storage_prod_mt, + carbon_capture_from_energy_mix_gt=carbon_capture_from_energy_mix_gt, + co2_emissions_needed_by_energy_mix_gt=co2_emissions_needed_by_energy_mix_gt, + co2_for_food_mt=co2_for_food_mt ) self.outputs_dict['co2_emissions_ccus'] = pd.DataFrame({ GlossaryEnergy.Years: self.years, - f'{GlossaryEnergy.carbon_storage} ({GlossaryEnergy.mass_unit})': carbon_storage, - f'{GlossaryEnergy.carbon_capture} to be stored (Mt)': carbon_capture_to_be_stored, - f'{GlossaryEnergy.carbon_capture} ({GlossaryEnergy.mass_unit}) from CC technos': carbon_capture_from_cc_technos, - f'{GlossaryEnergy.carbon_storage} Limited by capture (Mt)': carbon_storage_limited_by_capture_gt / 1e3, + f'{GlossaryEnergy.carbon_storage} ({GlossaryEnergy.mass_unit})': carbon_storage_gt * 1e3, + f'{GlossaryEnergy.carbon_capture} to be stored (Mt)': carbon_capture_to_be_stored_gt * 1e3, + f'{GlossaryEnergy.carbon_capture} ({GlossaryEnergy.mass_unit}) from CC technos': carbon_capture_from_cc_technos_gt * 1e3, + f'{GlossaryEnergy.carbon_storage} Limited by capture (Mt)': carbon_storage_limited_by_capture_gt * 1e3, }) self.outputs_dict['co2_emissions_ccus_Gt'] = pd.DataFrame({ @@ -126,21 +123,21 @@ def compute_CCS_price(self): }) def grad_co2_emissions_ccus_Gt(self): - carbon_capture_from_energy_mix = self.inputs_dict['carbon_capture_from_energy_mix'][f'{GlossaryEnergy.carbon_capture} from energy mix (Gt)'].values - co2_emissions_needed_by_energy_mix = self.inputs_dict['co2_emissions_needed_by_energy_mix'][f'{GlossaryEnergy.carbon_capture} needed by energy mix (Gt)'].values - co2_for_food = self.co2_for_food[f'{GlossaryEnergy.carbon_capture} for food (Mt)'].values + carbon_capture_from_energy_mix_gt = self.inputs_dict['carbon_capture_from_energy_mix'][f'{GlossaryEnergy.carbon_capture} from energy mix (Gt)'].values + co2_emissions_needed_by_energy_mix_gt = self.inputs_dict['co2_emissions_needed_by_energy_mix'][f'{GlossaryEnergy.carbon_capture} needed by energy mix (Gt)'].values + co2_for_food_mt = self.co2_for_food[f'{GlossaryEnergy.carbon_capture} for food (Mt)'].values - carbon_capture_prod = self.inputs_dict[f"{GlossaryEnergy.carbon_capture}.{GlossaryEnergy.EnergyProductionValue}"][GlossaryEnergy.carbon_capture].values - carbon_storage_prod = self.inputs_dict[f"{GlossaryEnergy.carbon_storage}.{GlossaryEnergy.EnergyProductionValue}"][GlossaryEnergy.carbon_storage].values + # production of CCS technos are in Mt + carbon_capture_prod_mt = self.inputs_dict[f"{GlossaryEnergy.carbon_capture}.{GlossaryEnergy.EnergyProductionValue}"][GlossaryEnergy.carbon_capture].values + carbon_storage_prod_mt = self.inputs_dict[f"{GlossaryEnergy.carbon_storage}.{GlossaryEnergy.EnergyProductionValue}"][GlossaryEnergy.carbon_storage].values jac_carbon_capture_from_cc_prod, jac_carbon_capture_from_cs_prod, jac_carbon_capture_from_energy_mix, jac_co2_emissions_needed_by_energy_mix =\ compute_carbon_storage_limited_by_capture_gt_der( - carbon_capture_prod=carbon_capture_prod, - carbon_storage_prod=carbon_storage_prod, - carbon_capture_from_energy_mix=carbon_capture_from_energy_mix, - co2_emissions_needed_by_energy_mix=co2_emissions_needed_by_energy_mix, - co2_for_food=co2_for_food, - scaling_factor_energy_production=self.scaling_factor_energy_production + carbon_capture_prod_mt=carbon_capture_prod_mt, + carbon_storage_prod_mt=carbon_storage_prod_mt, + carbon_capture_from_energy_mix_gt=carbon_capture_from_energy_mix_gt, + co2_emissions_needed_by_energy_mix_gt=co2_emissions_needed_by_energy_mix_gt, + co2_for_food_mt=co2_for_food_mt, ) # input_name : (column_name, grad value) out = { @@ -154,43 +151,42 @@ def grad_co2_emissions_ccus_Gt(self): def compute_carbon_storage_limited_by_capture_gt( - carbon_capture_prod: np.ndarray, - carbon_storage_prod: np.ndarray, - carbon_capture_from_energy_mix: np.ndarray, - co2_emissions_needed_by_energy_mix: np.ndarray, - co2_for_food: np.ndarray, - scaling_factor_energy_production: float + carbon_capture_prod_mt: np.ndarray, + carbon_storage_prod_mt: np.ndarray, + carbon_capture_from_energy_mix_gt: np.ndarray, + co2_emissions_needed_by_energy_mix_gt: np.ndarray, + co2_for_food_mt: np.ndarray, ): - '''The carbon stored by invest is limited by the carbon to stored''' - - carbon_storage = carbon_storage_prod * scaling_factor_energy_production - carbon_capture_from_cc_technos = carbon_capture_prod * scaling_factor_energy_production - - carbon_capture_to_be_stored = ( - carbon_capture_from_cc_technos + - carbon_capture_from_energy_mix * 1e3 - - co2_emissions_needed_by_energy_mix * 1e3 - - co2_for_food + ''' + The carbon stored by invest is limited by the carbon to stored + All outputs are in Gt because the output coupling variable is in Gt + ''' + + carbon_capture_from_cc_technos_mt = carbon_capture_prod_mt + + carbon_capture_to_be_stored_mt = ( + carbon_capture_from_cc_technos_mt + + carbon_capture_from_energy_mix_gt * 1e3 - + 0 * co2_emissions_needed_by_energy_mix_gt * 1e3 - + 0 * co2_for_food_mt ) - carbon_storage_limited_by_capture_mt = np.minimum(carbon_capture_to_be_stored, carbon_storage) - return carbon_capture_to_be_stored, carbon_storage_limited_by_capture_mt / 1e3, carbon_storage, carbon_capture_from_cc_technos + carbon_storage_limited_by_capture_mt = np.minimum(carbon_capture_to_be_stored_mt, carbon_storage_prod_mt) + return carbon_capture_to_be_stored_mt / 1e3, carbon_storage_limited_by_capture_mt / 1e3, carbon_storage_prod_mt / 1e3, carbon_capture_from_cc_technos_mt / 1e3 def compute_carbon_storage_limited_by_capture_gt_der( - carbon_capture_prod: np.ndarray, - carbon_storage_prod: np.ndarray, - carbon_capture_from_energy_mix: np.ndarray, - co2_emissions_needed_by_energy_mix: np.ndarray, - co2_for_food: np.ndarray, - scaling_factor_energy_production: float + carbon_capture_prod_mt: np.ndarray, + carbon_storage_prod_mt: np.ndarray, + carbon_capture_from_energy_mix_gt: np.ndarray, + co2_emissions_needed_by_energy_mix_gt: np.ndarray, + co2_for_food_mt: np.ndarray, ): - args = (carbon_capture_prod, - carbon_storage_prod, - carbon_capture_from_energy_mix, - co2_emissions_needed_by_energy_mix, - co2_for_food, - scaling_factor_energy_production,) + args = (carbon_capture_prod_mt, + carbon_storage_prod_mt, + carbon_capture_from_energy_mix_gt, + co2_emissions_needed_by_energy_mix_gt, + co2_for_food_mt,) jac_carbon_capture_from_cc_prod = jacobian(lambda *args: compute_carbon_storage_limited_by_capture_gt(*args)[1], 0) jac_carbon_capture_from_cs_prod = jacobian(lambda *args: compute_carbon_storage_limited_by_capture_gt(*args)[1], 1) diff --git a/energy_models/core/ccus/ccus_disc.py b/energy_models/core/ccus/ccus_disc.py index 87ebfa1d..2307bde5 100644 --- a/energy_models/core/ccus/ccus_disc.py +++ b/energy_models/core/ccus/ccus_disc.py @@ -55,10 +55,6 @@ class CCUS_Discipline(SoSWrapp): 'unit': 'year', 'visibility': 'Shared', 'namespace': 'ns_public', 'range': [2000,2300]}, 'alpha': {'type': 'float', 'range': [0., 1.], 'default': 0.5, 'unit': '-', 'visibility': SoSWrapp.SHARED_VISIBILITY, 'namespace': 'ns_energy_study'}, - 'scaling_factor_energy_production': {'type': 'float', 'default': 1e3, 'unit': '-', 'user_level': 2, - 'visibility': SoSWrapp.SHARED_VISIBILITY, 'namespace': 'ns_public'}, - 'scaling_factor_energy_consumption': {'type': 'float', 'default': 1e3, 'unit': '-', 'user_level': 2, - 'visibility': SoSWrapp.SHARED_VISIBILITY, 'namespace': 'ns_public'}, 'carbonstorage_limit': {'type': 'float', 'default': 12e6, 'unit': 'Mt', 'user_level': 2,}, 'carbonstorage_constraint_ref': {'type': 'float', 'default': 12e6, 'unit': 'Mt', 'user_level': 2}, 'co2_emissions_needed_by_energy_mix': {'type': 'dataframe', 'unit': 'Gt', @@ -79,7 +75,7 @@ class CCUS_Discipline(SoSWrapp): DESC_OUT = { 'co2_emissions_ccus': {'type': 'dataframe', 'unit': 'Mt'}, - 'carbon_storage_capacity': {'type': 'dataframe', 'unit': 'Mt'}, + 'carbon_storage_capacity (Gt)': {'type': 'dataframe', 'unit': 'Mt'}, 'co2_emissions_ccus_Gt': {'type': 'dataframe', 'unit': 'Gt', 'visibility': SoSWrapp.SHARED_VISIBILITY, 'namespace': GlossaryEnergy.NS_CCS}, @@ -171,7 +167,7 @@ def compute_sos_jacobian(self): def get_chart_filter_list(self): chart_filters = [] - chart_list = ['CCS price', 'CO2 storage limited by capture', 'CO2 emissions captured, used and to store'] + chart_list = ['CCS price', 'CO2 storage limited by capture'] chart_filters.append(ChartFilter( 'Charts', chart_list, chart_list, 'charts')) @@ -211,29 +207,28 @@ def get_post_processing_list(self, filters=None): if new_chart is not None: instanciated_charts.append(new_chart) - if 'CO2 emissions captured, used and to store' in charts: - new_chart = self.get_chart_co2_to_store() - if new_chart is not None: - instanciated_charts.append(new_chart) - return instanciated_charts def get_chart_CCS_price(self): ccs_prices = self.get_sosdisc_outputs('CCS_price') - years = list(ccs_prices[GlossaryEnergy.Years].values) + chart_name = 'CCS price' + new_chart = TwoAxesInstanciatedChart(GlossaryEnergy.Years, '[$/tCO2]', chart_name=chart_name, stacked_bar=True) + visible_line = True - chart_name = 'CCS price over time' + carbon_capture_price = self.get_sosdisc_inputs(f'{GlossaryEnergy.carbon_capture}.{GlossaryEnergy.StreamPricesValue}')[ + GlossaryEnergy.carbon_capture].values + carbon_storage_price = self.get_sosdisc_inputs(f'{GlossaryEnergy.carbon_storage}.{GlossaryEnergy.StreamPricesValue}')[ + GlossaryEnergy.carbon_storage].values - new_chart = TwoAxesInstanciatedChart('Years', 'CCS price ($/tCO2)', - chart_name=chart_name) + new_series = InstanciatedSeries(years, carbon_capture_price, 'Capture', 'bar', visible_line) + new_chart.series.append(new_series) - visible_line = True + new_series = InstanciatedSeries(years, carbon_storage_price, 'Storage', 'bar', visible_line) + new_chart.series.append(new_series) - # add CCS price serie - new_series = InstanciatedSeries( - years, ccs_prices['ccs_price_per_tCO2'].values.tolist(), 'CCS price', 'lines', visible_line) + new_series = InstanciatedSeries(years, ccs_prices['ccs_price_per_tCO2'].values.tolist(), 'CCS price', 'lines', visible_line) new_chart.series.append(new_series) return new_chart @@ -245,10 +240,8 @@ def get_chart_co2_to_store(self): chart_name = 'CO2 emissions captured, used and to store' co2_emissions = self.get_sosdisc_outputs('co2_emissions_ccus') co2_for_food = self.get_sosdisc_inputs('co2_for_food') - carbon_capture_from_energy_mix = self.get_sosdisc_inputs( - 'carbon_capture_from_energy_mix') - co2_emissions_needed_by_energy_mix = self.get_sosdisc_inputs( - 'co2_emissions_needed_by_energy_mix') + carbon_capture_from_energy_mix = self.get_sosdisc_inputs('carbon_capture_from_energy_mix') + co2_emissions_needed_by_energy_mix = self.get_sosdisc_inputs('co2_emissions_needed_by_energy_mix') new_chart = TwoAxesInstanciatedChart(GlossaryEnergy.Years, 'CO2 emissions (Gt)', chart_name=chart_name, stacked_bar=True) @@ -259,7 +252,7 @@ def get_chart_co2_to_store(self): (co2_emissions[f'{GlossaryEnergy.carbon_capture} ({GlossaryEnergy.mass_unit}) from CC technos'].values / 1.0e3).tolist(), 'CO2 captured from CC technos', 'bar') new_chart.add_series(serie) - + """ serie = InstanciatedSeries( x_serie_1, carbon_capture_from_energy_mix[f'{GlossaryEnergy.carbon_capture} from energy mix (Gt)'].values.tolist(), @@ -276,6 +269,7 @@ def get_chart_co2_to_store(self): x_serie_1, (-co2_for_food[f'{GlossaryEnergy.carbon_capture} for food (Mt)'].values / 1.0e3).tolist(), f'{GlossaryEnergy.carbon_capture} used for food', 'bar') new_chart.add_series(serie) + """ serie = InstanciatedSeries( x_serie_1, @@ -288,27 +282,26 @@ def get_chart_co2_limited_storage(self): ''' Plot a graph to understand storage ''' - chart_name = 'CO2 emissions storage limited by CO2 to store' + chart_name = 'CO2 capture management' co2_emissions = self.get_sosdisc_outputs('co2_emissions_ccus') - carbon_storage_by_invest = self.get_sosdisc_outputs('carbon_storage_capacity') - new_chart = TwoAxesInstanciatedChart(GlossaryEnergy.Years, 'CO2 emissions (Gt)', - chart_name=chart_name) + carbon_storage_capacity = self.get_sosdisc_outputs('carbon_storage_capacity (Gt)') + new_chart = TwoAxesInstanciatedChart(GlossaryEnergy.Years, '[Gt]', chart_name=chart_name, stacked_bar=True) - x_serie_1 = co2_emissions[GlossaryEnergy.Years].values.tolist() + years = co2_emissions[GlossaryEnergy.Years].values.tolist() serie = InstanciatedSeries( - x_serie_1, + years, (co2_emissions[f'{GlossaryEnergy.carbon_capture} to be stored (Mt)'].values / 1.0e3).tolist(), 'CO2 captured to store') new_chart.add_series(serie) serie = InstanciatedSeries( - x_serie_1, - (carbon_storage_by_invest['carbon_storage_capacity'] / 1.0e3).tolist(), 'CO2 storage capacity') + years, + (carbon_storage_capacity['carbon_storage_capacity (Gt)']).tolist(), 'CO2 storage capacity') new_chart.add_series(serie) serie = InstanciatedSeries( - x_serie_1, + years, (co2_emissions[f'{GlossaryEnergy.carbon_storage} Limited by capture (Mt)'].values / 1.0e3).tolist(), - 'CO2 captured and stored') + 'CO2 captured and stored', 'bar') new_chart.add_series(serie) return new_chart diff --git a/energy_models/tests/jacobian_pkls/jacobian_CCUS_disc.pkl b/energy_models/tests/jacobian_pkls/jacobian_CCUS_disc.pkl index 7dec4b14..020b42f7 100644 Binary files a/energy_models/tests/jacobian_pkls/jacobian_CCUS_disc.pkl and b/energy_models/tests/jacobian_pkls/jacobian_CCUS_disc.pkl differ diff --git a/energy_models/tests/l0_test_compute_ccus_disc.py b/energy_models/tests/l0_test_compute_ccus_disc.py index 90f227e7..982ab31a 100644 --- a/energy_models/tests/l0_test_compute_ccus_disc.py +++ b/energy_models/tests/l0_test_compute_ccus_disc.py @@ -114,9 +114,6 @@ def test_01_CCUS_discipline(self): f'{self.name}.{GlossaryEnergy.YearStart}': self.year_start, f'{self.name}.{GlossaryEnergy.YearEnd}': self.year_end, f'{self.name}.{GlossaryEnergy.energy_list}': self.energy_list, - f'{self.name}.{GlossaryEnergy.ccs_list}': [GlossaryEnergy.carbon_capture, GlossaryEnergy.carbon_storage], - f'{self.name}.scaling_factor_energy_production': self.scaling_factor_energy_production, - f'{self.name}.scaling_factor_energy_consumption': self.scaling_factor_energy_consumption, f'{self.name}.{GlossaryEnergy.StreamProductionDetailedValue}': self.energy_production_detailed, } for energy in self.energy_list: @@ -146,10 +143,9 @@ def test_01_CCUS_discipline(self): f'{self.name}.{self.model_name}')[0] filters = disc.get_chart_filter_list() graph_list = disc.get_post_processing_list(filters) - - -# for graph in graph_list: -# graph.to_plotly().show() + for graph in graph_list: + #graph.to_plotly().show() + pass if '__main__' == __name__: