Skip to content

Commit

Permalink
fix units CCUS discipline and improved graphs
Browse files Browse the repository at this point in the history
  • Loading branch information
perrotcap committed Oct 12, 2024
1 parent 42e5edf commit f849c89
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 107 deletions.
128 changes: 62 additions & 66 deletions energy_models/core/ccus/ccus.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {}
Expand All @@ -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({
Expand All @@ -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 = {
Expand All @@ -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)
Expand Down
61 changes: 27 additions & 34 deletions energy_models/core/ccus/ccus_disc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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},

Expand Down Expand Up @@ -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'))
Expand Down Expand Up @@ -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
Expand All @@ -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)

Expand All @@ -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(),
Expand All @@ -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,
Expand All @@ -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
Expand Down
Binary file modified energy_models/tests/jacobian_pkls/jacobian_CCUS_disc.pkl
Binary file not shown.
Loading

0 comments on commit f849c89

Please sign in to comment.