From 0f1db1d15defda2e3a5b88be3abd92fdb3c82b1c Mon Sep 17 00:00:00 2001 From: perrotcap Date: Tue, 22 Oct 2024 10:01:20 +0200 Subject: [PATCH 01/19] pushed script fitting renewable simple techno --- .../fitting/clean_energy_simple_techno.py | 136 ++++++++++++++++++ ...lean-energy-and-fossil-fuels-2015-2024.csv | 11 ++ 2 files changed, 147 insertions(+) create mode 100644 data_energy/fitting/clean_energy_simple_techno.py create mode 100644 data_energy/techno_invests/global-investment-in-clean-energy-and-fossil-fuels-2015-2024.csv diff --git a/data_energy/fitting/clean_energy_simple_techno.py b/data_energy/fitting/clean_energy_simple_techno.py new file mode 100644 index 00000000..2c153d48 --- /dev/null +++ b/data_energy/fitting/clean_energy_simple_techno.py @@ -0,0 +1,136 @@ +import numpy as np +import pandas as pd +from scipy.optimize import minimize +from sostrades_core.execution_engine.execution_engine import ExecutionEngine +from sostrades_core.tools.post_processing.charts.two_axes_instanciated_chart import ( + InstanciatedSeries, + TwoAxesInstanciatedChart, +) + +from energy_models.database_witness_energy import DatabaseWitnessEnergy +from energy_models.glossaryenergy import GlossaryEnergy +from energy_models.models.clean_energy.clean_energy_simple_techno.clean_energy_simple_techno_disc import ( + CleanEnergySimpleTechnoDiscipline, +) + +df_invest_historic = DatabaseWitnessEnergy.get_techno_invest_df(techno_name=GlossaryEnergy.CleanEnergySimpleTechno) +df_prod_historic = DatabaseWitnessEnergy.get_techno_prod(techno_name=GlossaryEnergy.CleanEnergySimpleTechno, year=2020)[1].value +ref_price_2023 = 70.76 # $/MWh +# data to run techno +construction_delay = GlossaryEnergy.TechnoConstructionDelayDict[GlossaryEnergy.CleanEnergySimpleTechno] +year_start_fitting = int(max(df_invest_historic['years'].min() + construction_delay, df_prod_historic['years'].min(), 2020)) +year_end_fitting = int(min(df_invest_historic['years'].max(), df_prod_historic['years'].max())) + +prod_values_historic = df_prod_historic.loc[(df_prod_historic['years'] >= year_start_fitting) & (df_prod_historic['years'] <= year_end_fitting)]['production'].values +years_fitting = list(np.arange(year_start_fitting, year_end_fitting + 1)) +invest_df = df_invest_historic.loc[(df_invest_historic['years'] >= year_start_fitting) & (df_invest_historic['years'] <= year_end_fitting)] +margin = pd.DataFrame({GlossaryEnergy.Years: years_fitting, GlossaryEnergy.MarginValue: 110}) +transport = pd.DataFrame({GlossaryEnergy.Years: years_fitting, 'transport': np.zeros(len(years_fitting))}) +co2_taxes = pd.DataFrame({GlossaryEnergy.Years: years_fitting, GlossaryEnergy.CO2Tax: np.linspace(14., 40., len(years_fitting))}) +stream_prices = pd.DataFrame({GlossaryEnergy.Years: years_fitting}) +resources_price = pd.DataFrame({GlossaryEnergy.Years: years_fitting}) +techno_dict_default = CleanEnergySimpleTechnoDiscipline.techno_infos_dict_default + +name = 'Test' +model_name = GlossaryEnergy.CleanEnergySimpleTechno +ee = ExecutionEngine(name) +ns_dict = {'ns_public': name, + 'ns_energy': name, + 'ns_energy_study': f'{name}', + 'ns_clean_energy': name, + 'ns_resource': name} +ee.ns_manager.add_ns_def(ns_dict) + +mod_path = 'energy_models.models.clean_energy.clean_energy_simple_techno.clean_energy_simple_techno_disc.CleanEnergySimpleTechnoDiscipline' +builder = ee.factory.get_builder_from_module( + model_name, mod_path) + +ee.factory.set_builders_to_coupling_builder(builder) + +ee.configure() +ee.display_treeview_nodes() + + + +def run_model(x: list, year_end: int = year_end_fitting): + techno_dict_default["Capex_init"] = x[0] + init_age_distrib_factor = x[1] + #techno_dict_default["learning_rate"] = x[2] + techno_dict_default["Opex_percentage"] = x[3] + techno_dict_default["WACC"] = x[4] + + inputs_dict = { + f'{name}.{GlossaryEnergy.YearStart}': year_start_fitting, + f'{name}.{GlossaryEnergy.YearEnd}': year_end, + f'{name}.{GlossaryEnergy.StreamPricesValue}': stream_prices, + f'{name}.{GlossaryEnergy.StreamsCO2EmissionsValue}': pd.DataFrame({GlossaryEnergy.Years: years_fitting}), + f'{name}.{model_name}.{GlossaryEnergy.InvestLevelValue}': invest_df, + f'{name}.{GlossaryEnergy.TransportMarginValue}': margin, + f'{name}.{GlossaryEnergy.CO2TaxesValue}': co2_taxes, + f'{name}.{GlossaryEnergy.TransportCostValue}': transport, + f'{name}.{GlossaryEnergy.ResourcesPriceValue}': resources_price, + f'{name}.{model_name}.{GlossaryEnergy.MarginValue}': margin, + f'{name}.{model_name}.{GlossaryEnergy.InitialPlantsAgeDistribFactor}': init_age_distrib_factor, + f'{name}.{model_name}.techno_infos_dict': techno_dict_default, + } + + ee.load_study_from_input_dict(inputs_dict) + + ee.execute() + + prod_df = ee.dm.get_value(ee.dm.get_all_namespaces_from_var_name(GlossaryEnergy.TechnoProductionValue)[0]) + prod_values_model = prod_df[f"{GlossaryEnergy.clean_energy} (TWh)"].values * 1000 + + price_df = ee.dm.get_value(ee.dm.get_all_namespaces_from_var_name(GlossaryEnergy.TechnoPricesValue)[0]) + + price_model_values = float((price_df.loc[price_df[GlossaryEnergy.Years] == 2023, f"{GlossaryEnergy.CleanEnergySimpleTechno}_wotaxes"]).values) + return prod_values_model, price_model_values + + +def fitting_renewable(x: list): + prod_values_model, price_model_values = run_model(x) + return (((prod_values_model - prod_values_historic)) ** 2).mean() + (price_model_values - ref_price_2023) ** 2 + + +# Initial guess for the variables +x0 = np.array([250., 1., 0.0, 0.2, 0.1]) +#x0 = np.array([743.8, 1.3, 0.06, 0.0, 0.06]) + +bounds = [(0, 10000), (0, 3.0), (0.01, 0.95), (0.001, 0.99), (0.0001, 0.3)] + +# Use minimize to find the minimum of the function +result = minimize(fitting_renewable, x0, bounds=bounds) + +prod_values_model, price_model_values = run_model(result.x) + +# Print the result +#print("Optimal solution:", result.x) +print("Function value at the optimum:", result.fun) + + +new_chart = TwoAxesInstanciatedChart('years', 'production (TWh)', + chart_name='Production : model vs historic') + + +serie = InstanciatedSeries(years_fitting, prod_values_model, 'model', 'lines') +new_chart.series.append(serie) + +serie = InstanciatedSeries(years_fitting, prod_values_historic, 'historic', 'lines') +new_chart.series.append(serie) + +new_chart.to_plotly().show() + +parameters = ["capex_init", "init_age_distrib_factor", "learning_rate", "opex_percentage", "wacc"] +opt_values = dict(zip(parameters, np.round(result.x, 2))) +for key, val in opt_values.items(): + print("Optimal", key, ":", val) + +capex_init, init_age_distrib_factor, learning_rate, opex_percentage, wacc = result.x + +disc = ee.dm.get_disciplines_with_name( + f'{name}.{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() + pass \ No newline at end of file diff --git a/data_energy/techno_invests/global-investment-in-clean-energy-and-fossil-fuels-2015-2024.csv b/data_energy/techno_invests/global-investment-in-clean-energy-and-fossil-fuels-2015-2024.csv new file mode 100644 index 00000000..e9fb810d --- /dev/null +++ b/data_energy/techno_invests/global-investment-in-clean-energy-and-fossil-fuels-2015-2024.csv @@ -0,0 +1,11 @@ +Renewable power;Grids and storage;Energy efficiency and end-use;Nuclear and other clean power;Low-emissions fuels +0;343;338;393;43;8 +0;349;350;457;44;8 +0;354;341;465;41;8 +0;379;335;451;40;8 +0;424;316;455;40;9 +0;446;313;436;46;9 +0;470;330;562;58;11 +0;605;365;655;65;17 +0;735;416;646;67;20 +0;771;452;669;80;31 \ No newline at end of file From 3a314cec5e7c0d063551fab95651eae6cc903ba1 Mon Sep 17 00:00:00 2001 From: perrotcap Date: Tue, 22 Oct 2024 11:17:38 +0200 Subject: [PATCH 02/19] recalibrated clean energy simple techno --- .../fitting/clean_energy_simple_techno.py | 16 ++++---- .../cleanenergysimpletechno.csv | 5 ++- .../cleanenergysimpletechno.csv | 21 +++++----- data_energy/techno_invests/sources.txt | 5 ++- .../anaerobicdigestion.csv | 2 +- .../autothermalreforming.csv | 2 +- .../biogasfired.csv | 2 +- .../biomassburyingfossilization.csv | 2 +- .../biomassfermentation.csv | 2 +- .../biomassfired.csv | 2 +- .../biomassgasification.csv | 2 +- .../chphighheat.csv | 2 +- .../techno_production_historic/chplowheat.csv | 2 +- .../chpmediumheat.csv | 2 +- .../cleanenergysimpletechno.csv | 36 +++++++++++++++++- .../co2hydrogenation.csv | 2 +- .../coalextraction.csv | 2 +- .../coalgasification.csv | 2 +- .../coelectrolysis.csv | 2 +- .../techno_production_historic/cropenergy.csv | 2 +- .../deepoceaninjection.csv | 2 +- .../deepsalineformation.csv | 2 +- .../depletedoilgas.csv | 2 +- .../direct_air_capture_aminescrubbing.csv | 2 +- ..._air_capture_calciumpotassiumscrubbing.csv | 2 +- .../electricboilerhighheat.csv | 2 +- .../electricboilerlowheat.csv | 2 +- .../electricboilermediumheat.csv | 2 +- .../electrolysis_awe.csv | 2 +- .../electrolysis_pem.csv | 2 +- .../electrolysis_soec.csv | 2 +- .../enhancedoilrecovery.csv | 2 +- .../fischertropsch.csv | 2 +- .../flue_gas_capture_calciumlooping.csv | 2 +- ...flue_gas_capture_chilledammoniaprocess.csv | 2 +- .../flue_gas_capture_co2membranes.csv | 2 +- .../flue_gas_capture_monoethanolamine.csv | 2 +- .../flue_gas_capture_piperazineprocess.csv | 2 +- ...ue_gas_capture_pressureswingadsorption.csv | 2 +- .../geologicmineralization.csv | 2 +- .../geothermalhighheat.csv | 2 +- .../geothermallowheat.csv | 2 +- .../geothermalmediumheat.csv | 2 +- .../heatpumphighheat.csv | 2 +- .../heatpumplowheat.csv | 2 +- .../heatpumpmediumheat.csv | 2 +- .../hefadecarboxylation.csv | 2 +- .../hefadeoxygenation.csv | 2 +- .../hydrogenliquefaction.csv | 2 +- .../managedwood.csv | 2 +- .../methanation.csv | 2 +- .../naturalgasboilerhighheat.csv | 2 +- .../naturalgasboilerlowheat.csv | 2 +- .../naturalgasboilermediumheat.csv | 2 +- .../pelletizing.csv | 2 +- .../plasmacracking.csv | 2 +- .../purecarbonsolidstorage.csv | 2 +- .../techno_production_historic/pyrolysis.csv | 2 +- .../techno_production_historic/refinery.csv | 2 +- .../reforestation.csv | 2 +- .../techno_production_historic/rwgs.csv | 2 +- .../techno_production_historic/smr.csv | 2 +- .../techno_production_historic/solarpv.csv | 2 +- .../solarthermal.csv | 2 +- .../transesterification.csv | 2 +- .../unmanagedwood.csv | 2 +- .../upgradingbiogas.csv | 2 +- .../watergasshift.csv | 2 +- .../windoffshore.csv | 2 +- .../windonshore.csv | 2 +- energy_models/core/ccus/ccus_disc.py | 1 - .../disciplines/carbon_capture_techno_disc.py | 4 +- .../disciplines/carbon_storage_techno_disc.py | 4 +- energy_models/core/techno_type/techno_disc.py | 19 +++++---- energy_models/core/techno_type/techno_type.py | 27 +++++++++---- energy_models/database_witness_energy.py | 19 ++++++++- energy_models/glossaryenergy.py | 12 +++++- .../clean_energy_simple_techno_disc.py | 8 ++-- .../water_gas_shift/water_gas_shift_disc.py | 2 +- .../electric_boiler_high_heat.py | 2 +- .../geothermal_high_heat.py | 2 +- .../heat_pump_high_heat.py | 2 +- .../natural_gas_boiler_high_heat.py | 2 +- .../electric_boiler_low_heat.py | 2 +- .../geothermal_low_heat.py | 2 +- .../heat_pump_low_heat/heat_pump_low_heat.py | 2 +- .../natural_gas_boiler_low_heat.py | 2 +- .../electric_boiler_medium_heat.py | 2 +- .../geothermal_medium_heat.py | 2 +- .../heat_pump_medium_heat.py | 2 +- .../natural_gas_boiler_medium_heat.py | 2 +- .../energy_mix_optim_sub_process/usecase.py | 4 +- ...gySimpleTechno_CleanEnergySimpleTechno.pkl | Bin 711 -> 1447 bytes ...nergySimpleTechno_construction_delay_0.pkl | Bin 651 -> 1395 bytes .../jacobian_electricity_cc_gas_turbine.pkl | Bin 2657 -> 2659 bytes .../jacobian_electricity_coal_gen.pkl | Bin 5227 -> 5231 bytes .../jacobian_electricity_gas_turbine.pkl | Bin 2619 -> 2652 bytes ...an_electricity_geothermal_high_heat_zz.pkl | Bin 2443 -> 2371 bytes .../jacobian_electricity_hydropower.pkl | Bin 1272 -> 1259 bytes .../jacobian_electricity_nuclear.pkl | Bin 2115 -> 2115 bytes .../jacobian_electricity_oil_gen.pkl | Bin 4729 -> 4805 bytes .../jacobian_electricity_solar_pv.pkl | Bin 3189 -> 3128 bytes .../jacobian_electricity_solar_thermal.pkl | Bin 3155 -> 3150 bytes .../jacobian_electricity_wind_off_shore.pkl | Bin 2750 -> 2716 bytes .../jacobian_electricity_wind_on_shore.pkl | Bin 2735 -> 2711 bytes .../jacobian_methane_fossil_gas.pkl | Bin 2392 -> 2396 bytes .../jacobian_ratio_FossilGas.pkl | Bin 2551 -> 2543 bytes .../jacobian_pkls/jacobian_ratio_Nuclear.pkl | Bin 2894 -> 2852 bytes ...est_compute_rnw_renewable_simple_techno.py | 1 - 109 files changed, 208 insertions(+), 130 deletions(-) diff --git a/data_energy/fitting/clean_energy_simple_techno.py b/data_energy/fitting/clean_energy_simple_techno.py index 2c153d48..700faf2a 100644 --- a/data_energy/fitting/clean_energy_simple_techno.py +++ b/data_energy/fitting/clean_energy_simple_techno.py @@ -13,12 +13,15 @@ CleanEnergySimpleTechnoDiscipline, ) +year_calibration = 2015 + + df_invest_historic = DatabaseWitnessEnergy.get_techno_invest_df(techno_name=GlossaryEnergy.CleanEnergySimpleTechno) df_prod_historic = DatabaseWitnessEnergy.get_techno_prod(techno_name=GlossaryEnergy.CleanEnergySimpleTechno, year=2020)[1].value ref_price_2023 = 70.76 # $/MWh # data to run techno construction_delay = GlossaryEnergy.TechnoConstructionDelayDict[GlossaryEnergy.CleanEnergySimpleTechno] -year_start_fitting = int(max(df_invest_historic['years'].min() + construction_delay, df_prod_historic['years'].min(), 2020)) +year_start_fitting = int(max(df_invest_historic['years'].min() + construction_delay, df_prod_historic['years'].min(), year_calibration)) year_end_fitting = int(min(df_invest_historic['years'].max(), df_prod_historic['years'].max())) prod_values_historic = df_prod_historic.loc[(df_prod_historic['years'] >= year_start_fitting) & (df_prod_historic['years'] <= year_end_fitting)]['production'].values @@ -26,7 +29,7 @@ invest_df = df_invest_historic.loc[(df_invest_historic['years'] >= year_start_fitting) & (df_invest_historic['years'] <= year_end_fitting)] margin = pd.DataFrame({GlossaryEnergy.Years: years_fitting, GlossaryEnergy.MarginValue: 110}) transport = pd.DataFrame({GlossaryEnergy.Years: years_fitting, 'transport': np.zeros(len(years_fitting))}) -co2_taxes = pd.DataFrame({GlossaryEnergy.Years: years_fitting, GlossaryEnergy.CO2Tax: np.linspace(14., 40., len(years_fitting))}) +co2_taxes = pd.DataFrame({GlossaryEnergy.Years: years_fitting, GlossaryEnergy.CO2Tax: np.linspace(0., 0., len(years_fitting))}) stream_prices = pd.DataFrame({GlossaryEnergy.Years: years_fitting}) resources_price = pd.DataFrame({GlossaryEnergy.Years: years_fitting}) techno_dict_default = CleanEnergySimpleTechnoDiscipline.techno_infos_dict_default @@ -51,17 +54,16 @@ ee.display_treeview_nodes() - -def run_model(x: list, year_end: int = year_end_fitting): +def run_model(x: list): techno_dict_default["Capex_init"] = x[0] init_age_distrib_factor = x[1] - #techno_dict_default["learning_rate"] = x[2] + techno_dict_default["learning_rate"] = x[2] techno_dict_default["Opex_percentage"] = x[3] techno_dict_default["WACC"] = x[4] inputs_dict = { f'{name}.{GlossaryEnergy.YearStart}': year_start_fitting, - f'{name}.{GlossaryEnergy.YearEnd}': year_end, + f'{name}.{GlossaryEnergy.YearEnd}': year_end_fitting, f'{name}.{GlossaryEnergy.StreamPricesValue}': stream_prices, f'{name}.{GlossaryEnergy.StreamsCO2EmissionsValue}': pd.DataFrame({GlossaryEnergy.Years: years_fitting}), f'{name}.{model_name}.{GlossaryEnergy.InvestLevelValue}': invest_df, @@ -96,7 +98,7 @@ def fitting_renewable(x: list): x0 = np.array([250., 1., 0.0, 0.2, 0.1]) #x0 = np.array([743.8, 1.3, 0.06, 0.0, 0.06]) -bounds = [(0, 10000), (0, 3.0), (0.01, 0.95), (0.001, 0.99), (0.0001, 0.3)] +bounds = [(0, 10000), (0, 1.1), (0.00, 0.), (0.001, 0.99), (0.0001, 0.3)] # Use minimize to find the minimum of the function result = minimize(fitting_renewable, x0, bounds=bounds) diff --git a/data_energy/techno_factories_age/cleanenergysimpletechno.csv b/data_energy/techno_factories_age/cleanenergysimpletechno.csv index c0496593..ee2b2878 100644 --- a/data_energy/techno_factories_age/cleanenergysimpletechno.csv +++ b/data_energy/techno_factories_age/cleanenergysimpletechno.csv @@ -1,4 +1,5 @@ years,growth_rate -2023,1.0 -2020,1.0 +2023,1.13 +2020,1.1 +2015,1.1 1991,1.0 diff --git a/data_energy/techno_invests/cleanenergysimpletechno.csv b/data_energy/techno_invests/cleanenergysimpletechno.csv index 095e7a6f..3c53df23 100644 --- a/data_energy/techno_invests/cleanenergysimpletechno.csv +++ b/data_energy/techno_invests/cleanenergysimpletechno.csv @@ -1,10 +1,11 @@ -years,invest -2015,394 -2016,401 -2017,403 -2018,427 -2019,473 -2020,500 -2021,539 -2022,687 -2023,822 +,Renewable power,Grids and storage,Energy efficiency and end-use,Nuclear and other clean power,Low-emissions fuels,invest,years +0,343,338,393,43,8,630.6,2015 +0,349,350,457,44,8,646.0,2016 +0,354,341,465,41,8,641.7,2017 +0,379,335,451,40,8,661.5,2018 +0,424,316,455,40,9,694.2,2019 +0,446,313,436,46,9,720.1,2020 +0,470,330,562,58,11,770.0,2021 +0,605,365,655,65,17,942.5,2022 +0,735,416,646,67,20,1113.2,2023 +0,771,452,669,80,31,1198.4,2024 diff --git a/data_energy/techno_invests/sources.txt b/data_energy/techno_invests/sources.txt index d36b6195..67e3ef13 100644 --- a/data_energy/techno_invests/sources.txt +++ b/data_energy/techno_invests/sources.txt @@ -1,5 +1,8 @@ FossilSimpleTechno : https://www.iea.org/reports/world-energy-investment-2023/overview-and-key-findings -RenewableSimpleTechno : https://www.iea.org/data-and-statistics/charts/global-investment-in-clean-energy-and-fossil-fuels-2015-2024 + +Clean energy simple techno : https://www.iea.org/data-and-statistics/charts/global-investment-in-clean-energy-and-fossil-fuels-2015-2024 +Clean energy invest = Renewable + Nuclear and others + Low emission fuels + 75% of grid invests because renewable need a lot of grid invest to work (https://www.rystadenergy.com/news/power-grids-investments-energy-transition-permitting-policies) + CarbonStorageTechno : https://www.iea.org/energy-system/carbon-capture-utilisation-and-storage/co2-capture-and-utilisation DirectAirCaptureTechno : https://www.iea.org/energy-system/carbon-capture-utilisation-and-storage/direct-air-capture SolarPV: [https://www.iea.org/energy-system/renewables/solar-pv,] diff --git a/data_energy/techno_production_historic/anaerobicdigestion.csv b/data_energy/techno_production_historic/anaerobicdigestion.csv index ab7d5937..19dcf15d 100644 --- a/data_energy/techno_production_historic/anaerobicdigestion.csv +++ b/data_energy/techno_production_historic/anaerobicdigestion.csv @@ -1,2 +1,2 @@ years,production,unit -2020,407.05,TWh +2019,407.05,TWh diff --git a/data_energy/techno_production_historic/autothermalreforming.csv b/data_energy/techno_production_historic/autothermalreforming.csv index 0e39a2e1..28dce373 100644 --- a/data_energy/techno_production_historic/autothermalreforming.csv +++ b/data_energy/techno_production_historic/autothermalreforming.csv @@ -1,2 +1,2 @@ years,production,unit -2020,0.0,TWh +2019,0.0,TWh diff --git a/data_energy/techno_production_historic/biogasfired.csv b/data_energy/techno_production_historic/biogasfired.csv index 53955e62..4b36b785 100644 --- a/data_energy/techno_production_historic/biogasfired.csv +++ b/data_energy/techno_production_historic/biogasfired.csv @@ -1,2 +1,2 @@ years,production,unit -2020,88.751,TWh +2019,88.751,TWh diff --git a/data_energy/techno_production_historic/biomassburyingfossilization.csv b/data_energy/techno_production_historic/biomassburyingfossilization.csv index d180e15d..7b337b0c 100644 --- a/data_energy/techno_production_historic/biomassburyingfossilization.csv +++ b/data_energy/techno_production_historic/biomassburyingfossilization.csv @@ -1,2 +1,2 @@ years,production,unit -2020,0.0,MtCO2 +2019,0.0,MtCO2 diff --git a/data_energy/techno_production_historic/biomassfermentation.csv b/data_energy/techno_production_historic/biomassfermentation.csv index ca24fee8..e7dd1350 100644 --- a/data_energy/techno_production_historic/biomassfermentation.csv +++ b/data_energy/techno_production_historic/biomassfermentation.csv @@ -1,2 +1,2 @@ years,production,unit -2020,649.9888347148141,TWh +2019,649.9888347148141,TWh diff --git a/data_energy/techno_production_historic/biomassfired.csv b/data_energy/techno_production_historic/biomassfired.csv index 60bcfe33..8fca606a 100644 --- a/data_energy/techno_production_historic/biomassfired.csv +++ b/data_energy/techno_production_historic/biomassfired.csv @@ -1,2 +1,2 @@ years,production,unit -2020,443.085,TWh +2019,443.085,TWh diff --git a/data_energy/techno_production_historic/biomassgasification.csv b/data_energy/techno_production_historic/biomassgasification.csv index 1545b730..3c8d1df3 100644 --- a/data_energy/techno_production_historic/biomassgasification.csv +++ b/data_energy/techno_production_historic/biomassgasification.csv @@ -1,2 +1,2 @@ years,production,unit -2020,19.728111000000002,TWh +2019,19.728111000000002,TWh diff --git a/data_energy/techno_production_historic/chphighheat.csv b/data_energy/techno_production_historic/chphighheat.csv index 3e51f872..1e15fb9a 100644 --- a/data_energy/techno_production_historic/chphighheat.csv +++ b/data_energy/techno_production_historic/chphighheat.csv @@ -1,2 +1,2 @@ years,production,unit -2020,43.97872340425532,TWh +2019,43.97872340425532,TWh diff --git a/data_energy/techno_production_historic/chplowheat.csv b/data_energy/techno_production_historic/chplowheat.csv index 78946ec8..a7595741 100644 --- a/data_energy/techno_production_historic/chplowheat.csv +++ b/data_energy/techno_production_historic/chplowheat.csv @@ -1,2 +1,2 @@ years,production,unit -2020,26.0,TWh +2019,26.0,TWh diff --git a/data_energy/techno_production_historic/chpmediumheat.csv b/data_energy/techno_production_historic/chpmediumheat.csv index 5f1ed54e..591ccf50 100644 --- a/data_energy/techno_production_historic/chpmediumheat.csv +++ b/data_energy/techno_production_historic/chpmediumheat.csv @@ -1,2 +1,2 @@ years,production,unit -2020,36.0,TWh +2019,36.0,TWh diff --git a/data_energy/techno_production_historic/cleanenergysimpletechno.csv b/data_energy/techno_production_historic/cleanenergysimpletechno.csv index d802ce38..300117db 100644 --- a/data_energy/techno_production_historic/cleanenergysimpletechno.csv +++ b/data_energy/techno_production_historic/cleanenergysimpletechno.csv @@ -1,3 +1,35 @@ years,production,unit -2020,31552.17,TWh -2023,36306.66,TWh +1990,18370.5875,TWh +1991,18873.95528,TWh +1992,19135.62528,TWh +1993,19486.54778,TWh +1994,19814.36028,TWh +1995,20365.18472,TWh +1996,20841.40972,TWh +1997,20918.9875,TWh +1998,21163.00139,TWh +1999,21622.88306,TWh +2000,21710.03944,TWh +2001,21717.85889,TWh +2002,22014.94472,TWh +2003,22208.18222,TWh +2004,22953.40194,TWh +2005,23430.80361,TWh +2006,23929.52528,TWh +2007,24004.7825,TWh +2008,24457.19194,TWh +2009,24592.80528,TWh +2010,25476.99056,TWh +2011,25200.32583,TWh +2012,25482.41556,TWh +2013,26268.14056,TWh +2014,26850.79611,TWh +2015,27282.17139,TWh +2016,28045.97389,TWh +2017,28835.65389,TWh +2018,29959.75861,TWh +2019,30840.28944,TWh +2020,31082.16167,TWh +2021,32428.30806,TWh +2022,32911.60028,TWh +2023,35741.32642,TWh diff --git a/data_energy/techno_production_historic/co2hydrogenation.csv b/data_energy/techno_production_historic/co2hydrogenation.csv index 1435dbcf..82ca4cd0 100644 --- a/data_energy/techno_production_historic/co2hydrogenation.csv +++ b/data_energy/techno_production_historic/co2hydrogenation.csv @@ -1,2 +1,2 @@ years,production,unit -2020,543.0,TWh +2019,543.0,TWh diff --git a/data_energy/techno_production_historic/coalextraction.csv b/data_energy/techno_production_historic/coalextraction.csv index 3b3bc258..1d9705b6 100644 --- a/data_energy/techno_production_historic/coalextraction.csv +++ b/data_energy/techno_production_historic/coalextraction.csv @@ -1,2 +1,2 @@ years,production,unit -2020,42799.22,TWh +2019,42799.22,TWh diff --git a/data_energy/techno_production_historic/coalgasification.csv b/data_energy/techno_production_historic/coalgasification.csv index 5e0a801d..d3f8d422 100644 --- a/data_energy/techno_production_historic/coalgasification.csv +++ b/data_energy/techno_production_historic/coalgasification.csv @@ -1,2 +1,2 @@ years,production,unit -2020,3023.294117647059,TWh +2019,3023.294117647059,TWh diff --git a/data_energy/techno_production_historic/coelectrolysis.csv b/data_energy/techno_production_historic/coelectrolysis.csv index 0e39a2e1..28dce373 100644 --- a/data_energy/techno_production_historic/coelectrolysis.csv +++ b/data_energy/techno_production_historic/coelectrolysis.csv @@ -1,2 +1,2 @@ years,production,unit -2020,0.0,TWh +2019,0.0,TWh diff --git a/data_energy/techno_production_historic/cropenergy.csv b/data_energy/techno_production_historic/cropenergy.csv index 7bef058a..78143497 100644 --- a/data_energy/techno_production_historic/cropenergy.csv +++ b/data_energy/techno_production_historic/cropenergy.csv @@ -1,2 +1,2 @@ years,production,unit -2020,292.62239999999997,TWh +2019,292.62239999999997,TWh diff --git a/data_energy/techno_production_historic/deepoceaninjection.csv b/data_energy/techno_production_historic/deepoceaninjection.csv index d180e15d..7b337b0c 100644 --- a/data_energy/techno_production_historic/deepoceaninjection.csv +++ b/data_energy/techno_production_historic/deepoceaninjection.csv @@ -1,2 +1,2 @@ years,production,unit -2020,0.0,MtCO2 +2019,0.0,MtCO2 diff --git a/data_energy/techno_production_historic/deepsalineformation.csv b/data_energy/techno_production_historic/deepsalineformation.csv index d180e15d..7b337b0c 100644 --- a/data_energy/techno_production_historic/deepsalineformation.csv +++ b/data_energy/techno_production_historic/deepsalineformation.csv @@ -1,2 +1,2 @@ years,production,unit -2020,0.0,MtCO2 +2019,0.0,MtCO2 diff --git a/data_energy/techno_production_historic/depletedoilgas.csv b/data_energy/techno_production_historic/depletedoilgas.csv index d180e15d..7b337b0c 100644 --- a/data_energy/techno_production_historic/depletedoilgas.csv +++ b/data_energy/techno_production_historic/depletedoilgas.csv @@ -1,2 +1,2 @@ years,production,unit -2020,0.0,MtCO2 +2019,0.0,MtCO2 diff --git a/data_energy/techno_production_historic/direct_air_capture_aminescrubbing.csv b/data_energy/techno_production_historic/direct_air_capture_aminescrubbing.csv index 210f7bfe..a3f2a9b4 100644 --- a/data_energy/techno_production_historic/direct_air_capture_aminescrubbing.csv +++ b/data_energy/techno_production_historic/direct_air_capture_aminescrubbing.csv @@ -1,2 +1,2 @@ years,production,unit -2020,0.005,MtCO2 +2019,0.005,MtCO2 diff --git a/data_energy/techno_production_historic/direct_air_capture_calciumpotassiumscrubbing.csv b/data_energy/techno_production_historic/direct_air_capture_calciumpotassiumscrubbing.csv index 210f7bfe..a3f2a9b4 100644 --- a/data_energy/techno_production_historic/direct_air_capture_calciumpotassiumscrubbing.csv +++ b/data_energy/techno_production_historic/direct_air_capture_calciumpotassiumscrubbing.csv @@ -1,2 +1,2 @@ years,production,unit -2020,0.005,MtCO2 +2019,0.005,MtCO2 diff --git a/data_energy/techno_production_historic/electricboilerhighheat.csv b/data_energy/techno_production_historic/electricboilerhighheat.csv index e4ad9308..aef2ab01 100644 --- a/data_energy/techno_production_historic/electricboilerhighheat.csv +++ b/data_energy/techno_production_historic/electricboilerhighheat.csv @@ -1,2 +1,2 @@ years,production,unit -2020,139.67,TWh +2019,139.67,TWh diff --git a/data_energy/techno_production_historic/electricboilerlowheat.csv b/data_energy/techno_production_historic/electricboilerlowheat.csv index 17b56d28..91255792 100644 --- a/data_energy/techno_production_historic/electricboilerlowheat.csv +++ b/data_energy/techno_production_historic/electricboilerlowheat.csv @@ -1,2 +1,2 @@ years,production,unit -2020,139.66,TWh +2019,139.66,TWh diff --git a/data_energy/techno_production_historic/electricboilermediumheat.csv b/data_energy/techno_production_historic/electricboilermediumheat.csv index e4ad9308..aef2ab01 100644 --- a/data_energy/techno_production_historic/electricboilermediumheat.csv +++ b/data_energy/techno_production_historic/electricboilermediumheat.csv @@ -1,2 +1,2 @@ years,production,unit -2020,139.67,TWh +2019,139.67,TWh diff --git a/data_energy/techno_production_historic/electrolysis_awe.csv b/data_energy/techno_production_historic/electrolysis_awe.csv index c026b2b2..1e3e8324 100644 --- a/data_energy/techno_production_historic/electrolysis_awe.csv +++ b/data_energy/techno_production_historic/electrolysis_awe.csv @@ -1,2 +1,2 @@ years,production,unit -2020,1.2000000000000002,TWh +2019,1.2000000000000002,TWh diff --git a/data_energy/techno_production_historic/electrolysis_pem.csv b/data_energy/techno_production_historic/electrolysis_pem.csv index 0246e148..f8191ece 100644 --- a/data_energy/techno_production_historic/electrolysis_pem.csv +++ b/data_energy/techno_production_historic/electrolysis_pem.csv @@ -1,2 +1,2 @@ years,production,unit -2020,0.4,TWh +2019,0.4,TWh diff --git a/data_energy/techno_production_historic/electrolysis_soec.csv b/data_energy/techno_production_historic/electrolysis_soec.csv index 0e39a2e1..28dce373 100644 --- a/data_energy/techno_production_historic/electrolysis_soec.csv +++ b/data_energy/techno_production_historic/electrolysis_soec.csv @@ -1,2 +1,2 @@ years,production,unit -2020,0.0,TWh +2019,0.0,TWh diff --git a/data_energy/techno_production_historic/enhancedoilrecovery.csv b/data_energy/techno_production_historic/enhancedoilrecovery.csv index d180e15d..7b337b0c 100644 --- a/data_energy/techno_production_historic/enhancedoilrecovery.csv +++ b/data_energy/techno_production_historic/enhancedoilrecovery.csv @@ -1,2 +1,2 @@ years,production,unit -2020,0.0,MtCO2 +2019,0.0,MtCO2 diff --git a/data_energy/techno_production_historic/fischertropsch.csv b/data_energy/techno_production_historic/fischertropsch.csv index ae516584..9e944d8a 100644 --- a/data_energy/techno_production_historic/fischertropsch.csv +++ b/data_energy/techno_production_historic/fischertropsch.csv @@ -1,2 +1,2 @@ years,production,unit -2020,309.6295,TWh +2019,309.6295,TWh diff --git a/data_energy/techno_production_historic/flue_gas_capture_calciumlooping.csv b/data_energy/techno_production_historic/flue_gas_capture_calciumlooping.csv index b842c224..ba8a9076 100644 --- a/data_energy/techno_production_historic/flue_gas_capture_calciumlooping.csv +++ b/data_energy/techno_production_historic/flue_gas_capture_calciumlooping.csv @@ -1,2 +1,2 @@ years,production,unit -2020,5.0,MtCO2 +2019,5.0,MtCO2 diff --git a/data_energy/techno_production_historic/flue_gas_capture_chilledammoniaprocess.csv b/data_energy/techno_production_historic/flue_gas_capture_chilledammoniaprocess.csv index b842c224..ba8a9076 100644 --- a/data_energy/techno_production_historic/flue_gas_capture_chilledammoniaprocess.csv +++ b/data_energy/techno_production_historic/flue_gas_capture_chilledammoniaprocess.csv @@ -1,2 +1,2 @@ years,production,unit -2020,5.0,MtCO2 +2019,5.0,MtCO2 diff --git a/data_energy/techno_production_historic/flue_gas_capture_co2membranes.csv b/data_energy/techno_production_historic/flue_gas_capture_co2membranes.csv index b842c224..ba8a9076 100644 --- a/data_energy/techno_production_historic/flue_gas_capture_co2membranes.csv +++ b/data_energy/techno_production_historic/flue_gas_capture_co2membranes.csv @@ -1,2 +1,2 @@ years,production,unit -2020,5.0,MtCO2 +2019,5.0,MtCO2 diff --git a/data_energy/techno_production_historic/flue_gas_capture_monoethanolamine.csv b/data_energy/techno_production_historic/flue_gas_capture_monoethanolamine.csv index 19f3eabc..7d9e00bd 100644 --- a/data_energy/techno_production_historic/flue_gas_capture_monoethanolamine.csv +++ b/data_energy/techno_production_historic/flue_gas_capture_monoethanolamine.csv @@ -1,2 +1,2 @@ years,production,unit -2020,15.0,MtCO2 +2019,15.0,MtCO2 diff --git a/data_energy/techno_production_historic/flue_gas_capture_piperazineprocess.csv b/data_energy/techno_production_historic/flue_gas_capture_piperazineprocess.csv index b842c224..ba8a9076 100644 --- a/data_energy/techno_production_historic/flue_gas_capture_piperazineprocess.csv +++ b/data_energy/techno_production_historic/flue_gas_capture_piperazineprocess.csv @@ -1,2 +1,2 @@ years,production,unit -2020,5.0,MtCO2 +2019,5.0,MtCO2 diff --git a/data_energy/techno_production_historic/flue_gas_capture_pressureswingadsorption.csv b/data_energy/techno_production_historic/flue_gas_capture_pressureswingadsorption.csv index b842c224..ba8a9076 100644 --- a/data_energy/techno_production_historic/flue_gas_capture_pressureswingadsorption.csv +++ b/data_energy/techno_production_historic/flue_gas_capture_pressureswingadsorption.csv @@ -1,2 +1,2 @@ years,production,unit -2020,5.0,MtCO2 +2019,5.0,MtCO2 diff --git a/data_energy/techno_production_historic/geologicmineralization.csv b/data_energy/techno_production_historic/geologicmineralization.csv index d180e15d..7b337b0c 100644 --- a/data_energy/techno_production_historic/geologicmineralization.csv +++ b/data_energy/techno_production_historic/geologicmineralization.csv @@ -1,2 +1,2 @@ years,production,unit -2020,0.0,MtCO2 +2019,0.0,MtCO2 diff --git a/data_energy/techno_production_historic/geothermalhighheat.csv b/data_energy/techno_production_historic/geothermalhighheat.csv index 839abfe9..04b16893 100644 --- a/data_energy/techno_production_historic/geothermalhighheat.csv +++ b/data_energy/techno_production_historic/geothermalhighheat.csv @@ -1,2 +1,2 @@ years,production,unit -2020,60833.333333333336,TWh +2019,60833.333333333336,TWh diff --git a/data_energy/techno_production_historic/geothermallowheat.csv b/data_energy/techno_production_historic/geothermallowheat.csv index 839abfe9..04b16893 100644 --- a/data_energy/techno_production_historic/geothermallowheat.csv +++ b/data_energy/techno_production_historic/geothermallowheat.csv @@ -1,2 +1,2 @@ years,production,unit -2020,60833.333333333336,TWh +2019,60833.333333333336,TWh diff --git a/data_energy/techno_production_historic/geothermalmediumheat.csv b/data_energy/techno_production_historic/geothermalmediumheat.csv index 839abfe9..04b16893 100644 --- a/data_energy/techno_production_historic/geothermalmediumheat.csv +++ b/data_energy/techno_production_historic/geothermalmediumheat.csv @@ -1,2 +1,2 @@ years,production,unit -2020,60833.333333333336,TWh +2019,60833.333333333336,TWh diff --git a/data_energy/techno_production_historic/heatpumphighheat.csv b/data_energy/techno_production_historic/heatpumphighheat.csv index e18deb4c..0990ce6c 100644 --- a/data_energy/techno_production_historic/heatpumphighheat.csv +++ b/data_energy/techno_production_historic/heatpumphighheat.csv @@ -1,2 +1,2 @@ years,production,unit -2020,2920.0,TWh +2019,2920.0,TWh diff --git a/data_energy/techno_production_historic/heatpumplowheat.csv b/data_energy/techno_production_historic/heatpumplowheat.csv index e18deb4c..0990ce6c 100644 --- a/data_energy/techno_production_historic/heatpumplowheat.csv +++ b/data_energy/techno_production_historic/heatpumplowheat.csv @@ -1,2 +1,2 @@ years,production,unit -2020,2920.0,TWh +2019,2920.0,TWh diff --git a/data_energy/techno_production_historic/heatpumpmediumheat.csv b/data_energy/techno_production_historic/heatpumpmediumheat.csv index e18deb4c..0990ce6c 100644 --- a/data_energy/techno_production_historic/heatpumpmediumheat.csv +++ b/data_energy/techno_production_historic/heatpumpmediumheat.csv @@ -1,2 +1,2 @@ years,production,unit -2020,2920.0,TWh +2019,2920.0,TWh diff --git a/data_energy/techno_production_historic/hefadecarboxylation.csv b/data_energy/techno_production_historic/hefadecarboxylation.csv index 159b7db8..d36990de 100644 --- a/data_energy/techno_production_historic/hefadecarboxylation.csv +++ b/data_energy/techno_production_historic/hefadecarboxylation.csv @@ -1,2 +1,2 @@ years,production,unit -2020,8.58,TWh +2019,8.58,TWh diff --git a/data_energy/techno_production_historic/hefadeoxygenation.csv b/data_energy/techno_production_historic/hefadeoxygenation.csv index 966efe91..2b4b22f0 100644 --- a/data_energy/techno_production_historic/hefadeoxygenation.csv +++ b/data_energy/techno_production_historic/hefadeoxygenation.csv @@ -1,2 +1,2 @@ years,production,unit -2020,77.22,TWh +2019,77.22,TWh diff --git a/data_energy/techno_production_historic/hydrogenliquefaction.csv b/data_energy/techno_production_historic/hydrogenliquefaction.csv index fc5849d8..79472695 100644 --- a/data_energy/techno_production_historic/hydrogenliquefaction.csv +++ b/data_energy/techno_production_historic/hydrogenliquefaction.csv @@ -1,2 +1,2 @@ years,production,unit -2020,2.331,TWh +2019,2.331,TWh diff --git a/data_energy/techno_production_historic/managedwood.csv b/data_energy/techno_production_historic/managedwood.csv index 419ee699..baecadb6 100644 --- a/data_energy/techno_production_historic/managedwood.csv +++ b/data_energy/techno_production_historic/managedwood.csv @@ -1,2 +1,2 @@ years,production,unit -2020,28850.625000000007,TWh +2019,28850.625000000007,TWh diff --git a/data_energy/techno_production_historic/methanation.csv b/data_energy/techno_production_historic/methanation.csv index 691983a4..fbde8780 100644 --- a/data_energy/techno_production_historic/methanation.csv +++ b/data_energy/techno_production_historic/methanation.csv @@ -1,2 +1,2 @@ years,production,unit -2020,0.047199379320000005,TWh +2019,0.047199379320000005,TWh diff --git a/data_energy/techno_production_historic/naturalgasboilerhighheat.csv b/data_energy/techno_production_historic/naturalgasboilerhighheat.csv index fc9f65e5..ac70a885 100644 --- a/data_energy/techno_production_historic/naturalgasboilerhighheat.csv +++ b/data_energy/techno_production_historic/naturalgasboilerhighheat.csv @@ -1,2 +1,2 @@ years,production,unit -2020,561.0,TWh +2019,561.0,TWh diff --git a/data_energy/techno_production_historic/naturalgasboilerlowheat.csv b/data_energy/techno_production_historic/naturalgasboilerlowheat.csv index fc9f65e5..ac70a885 100644 --- a/data_energy/techno_production_historic/naturalgasboilerlowheat.csv +++ b/data_energy/techno_production_historic/naturalgasboilerlowheat.csv @@ -1,2 +1,2 @@ years,production,unit -2020,561.0,TWh +2019,561.0,TWh diff --git a/data_energy/techno_production_historic/naturalgasboilermediumheat.csv b/data_energy/techno_production_historic/naturalgasboilermediumheat.csv index fc9f65e5..ac70a885 100644 --- a/data_energy/techno_production_historic/naturalgasboilermediumheat.csv +++ b/data_energy/techno_production_historic/naturalgasboilermediumheat.csv @@ -1,2 +1,2 @@ years,production,unit -2020,561.0,TWh +2019,561.0,TWh diff --git a/data_energy/techno_production_historic/pelletizing.csv b/data_energy/techno_production_historic/pelletizing.csv index 33160469..5e864ba4 100644 --- a/data_energy/techno_production_historic/pelletizing.csv +++ b/data_energy/techno_production_historic/pelletizing.csv @@ -1,2 +1,2 @@ years,production,unit -2020,217.04,TWh +2019,217.04,TWh diff --git a/data_energy/techno_production_historic/plasmacracking.csv b/data_energy/techno_production_historic/plasmacracking.csv index 0d077562..11af0fe1 100644 --- a/data_energy/techno_production_historic/plasmacracking.csv +++ b/data_energy/techno_production_historic/plasmacracking.csv @@ -1,2 +1,2 @@ years,production,unit -2020,1e-12,TWh +2019,1e-12,TWh diff --git a/data_energy/techno_production_historic/purecarbonsolidstorage.csv b/data_energy/techno_production_historic/purecarbonsolidstorage.csv index d180e15d..7b337b0c 100644 --- a/data_energy/techno_production_historic/purecarbonsolidstorage.csv +++ b/data_energy/techno_production_historic/purecarbonsolidstorage.csv @@ -1,2 +1,2 @@ years,production,unit -2020,0.0,MtCO2 +2019,0.0,MtCO2 diff --git a/data_energy/techno_production_historic/pyrolysis.csv b/data_energy/techno_production_historic/pyrolysis.csv index 0d077562..11af0fe1 100644 --- a/data_energy/techno_production_historic/pyrolysis.csv +++ b/data_energy/techno_production_historic/pyrolysis.csv @@ -1,2 +1,2 @@ years,production,unit -2020,1e-12,TWh +2019,1e-12,TWh diff --git a/data_energy/techno_production_historic/refinery.csv b/data_energy/techno_production_historic/refinery.csv index 134764de..ae443819 100644 --- a/data_energy/techno_production_historic/refinery.csv +++ b/data_energy/techno_production_historic/refinery.csv @@ -1,2 +1,2 @@ years,production,unit -2020,46986.11,TWh +2019,46986.11,TWh diff --git a/data_energy/techno_production_historic/reforestation.csv b/data_energy/techno_production_historic/reforestation.csv index 36025207..5a506807 100644 --- a/data_energy/techno_production_historic/reforestation.csv +++ b/data_energy/techno_production_historic/reforestation.csv @@ -1,2 +1,2 @@ years,production,unit -2020,0.,MtCO2 +2019,0.,MtCO2 diff --git a/data_energy/techno_production_historic/rwgs.csv b/data_energy/techno_production_historic/rwgs.csv index 0e39a2e1..28dce373 100644 --- a/data_energy/techno_production_historic/rwgs.csv +++ b/data_energy/techno_production_historic/rwgs.csv @@ -1,2 +1,2 @@ years,production,unit -2020,0.0,TWh +2019,0.0,TWh diff --git a/data_energy/techno_production_historic/smr.csv b/data_energy/techno_production_historic/smr.csv index aac1305c..d22f9b52 100644 --- a/data_energy/techno_production_historic/smr.csv +++ b/data_energy/techno_production_historic/smr.csv @@ -1,2 +1,2 @@ years,production,unit -2020,2980.79475,TWh +2019,2980.79475,TWh diff --git a/data_energy/techno_production_historic/solarpv.csv b/data_energy/techno_production_historic/solarpv.csv index 7b3f84fd..aa4bbc45 100644 --- a/data_energy/techno_production_historic/solarpv.csv +++ b/data_energy/techno_production_historic/solarpv.csv @@ -1,3 +1,3 @@ years,production,unit -2020,700.0,TWh +2019,700.0,TWh 2022,1300,TWh diff --git a/data_energy/techno_production_historic/solarthermal.csv b/data_energy/techno_production_historic/solarthermal.csv index 39a2e789..3f61cf0a 100644 --- a/data_energy/techno_production_historic/solarthermal.csv +++ b/data_energy/techno_production_historic/solarthermal.csv @@ -1,2 +1,2 @@ years,production,unit -2020,15.6,TWh +2019,15.6,TWh diff --git a/data_energy/techno_production_historic/transesterification.csv b/data_energy/techno_production_historic/transesterification.csv index 36988417..8c934c9c 100644 --- a/data_energy/techno_production_historic/transesterification.csv +++ b/data_energy/techno_production_historic/transesterification.csv @@ -1,2 +1,2 @@ years,production,unit -2020,339.27520000000004,TWh +2019,339.27520000000004,TWh diff --git a/data_energy/techno_production_historic/unmanagedwood.csv b/data_energy/techno_production_historic/unmanagedwood.csv index 0d2f2d37..77aaaf70 100644 --- a/data_energy/techno_production_historic/unmanagedwood.csv +++ b/data_energy/techno_production_historic/unmanagedwood.csv @@ -1,2 +1,2 @@ years,production,unit -2020,2332.4906250000004,TWh +2019,2332.4906250000004,TWh diff --git a/data_energy/techno_production_historic/upgradingbiogas.csv b/data_energy/techno_production_historic/upgradingbiogas.csv index 60de6038..391b3ffe 100644 --- a/data_energy/techno_production_historic/upgradingbiogas.csv +++ b/data_energy/techno_production_historic/upgradingbiogas.csv @@ -1,2 +1,2 @@ years,production,unit -2020,37.57071500000001,TWh +2019,37.57071500000001,TWh diff --git a/data_energy/techno_production_historic/watergasshift.csv b/data_energy/techno_production_historic/watergasshift.csv index 6fcb69a4..3c8eefd1 100644 --- a/data_energy/techno_production_historic/watergasshift.csv +++ b/data_energy/techno_production_historic/watergasshift.csv @@ -1,2 +1,2 @@ years,production,unit -2020,2284.38,TWh +2019,2284.38,TWh diff --git a/data_energy/techno_production_historic/windoffshore.csv b/data_energy/techno_production_historic/windoffshore.csv index 343f2f74..8b0ff956 100644 --- a/data_energy/techno_production_historic/windoffshore.csv +++ b/data_energy/techno_production_historic/windoffshore.csv @@ -1,2 +1,2 @@ years,production,unit -2020,89.0,TWh +2019,89.0,TWh diff --git a/data_energy/techno_production_historic/windonshore.csv b/data_energy/techno_production_historic/windonshore.csv index 7695ff3d..4b849545 100644 --- a/data_energy/techno_production_historic/windonshore.csv +++ b/data_energy/techno_production_historic/windonshore.csv @@ -1,2 +1,2 @@ years,production,unit -2020,1323.0,TWh +2019,1323.0,TWh diff --git a/energy_models/core/ccus/ccus_disc.py b/energy_models/core/ccus/ccus_disc.py index 06a8a10d..15f305a7 100644 --- a/energy_models/core/ccus/ccus_disc.py +++ b/energy_models/core/ccus/ccus_disc.py @@ -300,7 +300,6 @@ def get_chart_co2_limited_storage(self): (co2_emissions[f'{GlossaryEnergy.carbon_storage} Limited by capture (Mt)'].values / 1.0e3).tolist(), 'CO2 captured and stored', 'bar') new_chart.add_series(serie) - new_chart.to_plotly().show() return new_chart def get_chart_co2_emissions_sources(self): diff --git a/energy_models/core/techno_type/disciplines/carbon_capture_techno_disc.py b/energy_models/core/techno_type/disciplines/carbon_capture_techno_disc.py index d71723c4..c845ecbb 100644 --- a/energy_models/core/techno_type/disciplines/carbon_capture_techno_disc.py +++ b/energy_models/core/techno_type/disciplines/carbon_capture_techno_disc.py @@ -271,11 +271,11 @@ def get_chart_detailed_price_in_dollar_tCO2(self): new_chart.series.append(serie) - if 'energy_costs' in techno_detailed_prices: + if 'energy_and_resources_costs' in techno_detailed_prices: # energy_costs serie = InstanciatedSeries( techno_detailed_prices[GlossaryEnergy.Years].values.tolist(), - techno_detailed_prices['energy_costs'].values.tolist(), 'Energy costs', 'lines') + techno_detailed_prices['energy_and_resources_costs'].values.tolist(), 'Energy costs', 'lines') new_chart.series.append(serie) diff --git a/energy_models/core/techno_type/disciplines/carbon_storage_techno_disc.py b/energy_models/core/techno_type/disciplines/carbon_storage_techno_disc.py index 02704b57..dd27c36f 100644 --- a/energy_models/core/techno_type/disciplines/carbon_storage_techno_disc.py +++ b/energy_models/core/techno_type/disciplines/carbon_storage_techno_disc.py @@ -176,11 +176,11 @@ def get_chart_detailed_price_in_dollar_ton(self): new_chart.series.append(serie) - if 'energy_costs' in techno_detailed_prices: + if 'energy_and_resources_costs' in techno_detailed_prices: # energy_costs serie = InstanciatedSeries( techno_detailed_prices[GlossaryEnergy.Years].values.tolist(), - techno_detailed_prices['energy_costs'].values.tolist(), 'Energy costs', 'lines') + techno_detailed_prices['energy_and_resources_costs'].values.tolist(), 'Energy costs', 'lines') new_chart.series.append(serie) diff --git a/energy_models/core/techno_type/techno_disc.py b/energy_models/core/techno_type/techno_disc.py index 9043dc60..b6ed91d5 100644 --- a/energy_models/core/techno_type/techno_disc.py +++ b/energy_models/core/techno_type/techno_disc.py @@ -110,7 +110,8 @@ class TechnoDiscipline(SoSWrapp): }}, GlossaryEnergy.InstalledPower: GlossaryEnergy.InstalledPowerDf, GlossaryEnergy.TechnoCapitalValue: GlossaryEnergy.TechnoCapitalDf, - GlossaryEnergy.SpecificCostsForProductionValue: GlossaryEnergy.SpecificCostsForProduction + GlossaryEnergy.SpecificCostsForProductionValue: GlossaryEnergy.SpecificCostsForProduction, + GlossaryEnergy.InitialPlantsTechnoProductionValue: GlossaryEnergy.InitialPlantsTechnoProduction, } _maturity = 'Research' @@ -238,15 +239,16 @@ def update_default_values(self): self.update_default_value(GlossaryEnergy.LifetimeName, 'in', lifetime) if GlossaryEnergy.InitialPlantsAgeDistribFactor in self.get_data_in() and GlossaryEnergy.YearStart in self.get_data_in(): + initial_plant_age_distrib_factor = self.get_sosdisc_inputs(GlossaryEnergy.InitialPlantsAgeDistribFactor) year_start = self.get_sosdisc_inputs(GlossaryEnergy.YearStart) - if year_start is not None: + if year_start is not None and initial_plant_age_distrib_factor is None: initial_plant_age_distrib_factor, _ = DatabaseWitnessEnergy.get_techno_age_distrib_factor(self.techno_name, year=year_start) self.update_default_value(GlossaryEnergy.InitialPlantsAgeDistribFactor, 'in', initial_plant_age_distrib_factor) if 'initial_production' in self.get_data_in() and GlossaryEnergy.YearStart in self.get_data_in(): year_start = self.get_sosdisc_inputs(GlossaryEnergy.YearStart) if year_start is not None: - initial_production, _ = DatabaseWitnessEnergy.get_techno_prod(self.techno_name, year=year_start) + initial_production, _ = DatabaseWitnessEnergy.get_techno_prod(self.techno_name, year=year_start - 1) self.update_default_value('initial_production', 'in', initial_production) construction_delay = None @@ -326,6 +328,7 @@ def run(self): GlossaryEnergy.CostOfStreamsUsageValue: self.techno_model.cost_of_streams_usage, GlossaryEnergy.SpecificCostsForProductionValue: self.techno_model.specific_costs, 'initial_age_distrib': self.techno_model.initial_age_distrib, + GlossaryEnergy.InitialPlantsTechnoProductionValue: self.techno_model.initial_plants_historical_prod, } self.store_sos_outputs_values(outputs_dict) @@ -900,9 +903,9 @@ def get_chart_detailed_price_in_dollar_kwh(self): new_chart.series.append(serie) - if 'energy_costs' in techno_detailed_prices: + if 'energy_and_resources_costs' in techno_detailed_prices: # energy_costs - ec_price_mwh = techno_detailed_prices['energy_costs'].values + ec_price_mwh = techno_detailed_prices['energy_and_resources_costs'].values serie = InstanciatedSeries( techno_detailed_prices[GlossaryEnergy.Years].values.tolist(), ec_price_mwh.tolist(), 'Energy costs', 'bar') @@ -969,9 +972,9 @@ def get_chart_detailed_price_in_dollar_kg(self): techno_kg_price.tolist(), 'Factory', 'bar') new_chart.series.append(serie) - if 'energy_costs' in techno_detailed_prices: + if 'energy_and_resources_costs' in techno_detailed_prices: # energy_costs - techno_kg_price = techno_detailed_prices['energy_costs'].values * \ + techno_kg_price = techno_detailed_prices['energy_and_resources_costs'].values * \ data_fuel_dict['calorific_value'] serie = InstanciatedSeries( techno_detailed_prices[GlossaryEnergy.Years].values.tolist(), @@ -1139,7 +1142,7 @@ def get_chart_initial_production(self): initial_prod['cum energy (TWh)'] = initial_prod['energy (TWh)'].cumsum( ) study_production = self.get_sosdisc_outputs(GlossaryEnergy.TechnoDetailedProductionValue) - chart_name = f'{self.energy_name} World Production via {self.techno_name}
with 2020 factories distribution' + chart_name = f'{self.energy_name} World Production via {self.techno_name}
with {year_start} factories distribution' new_chart = TwoAxesInstanciatedChart(GlossaryEnergy.Years, f'{self.energy_name} production [TWh]', chart_name=chart_name.capitalize()) diff --git a/energy_models/core/techno_type/techno_type.py b/energy_models/core/techno_type/techno_type.py index db549820..420c106f 100644 --- a/energy_models/core/techno_type/techno_type.py +++ b/energy_models/core/techno_type/techno_type.py @@ -49,6 +49,7 @@ class TechnoType: min_value_invest = 1.e-12 def __init__(self, name): + self.initial_plants_historical_prod = None self.lifetime: int = 20 self.initial_age_distrib_distrib_factor: float = 1. self.construction_delay: int = 0 @@ -407,7 +408,7 @@ def compute_price(self): self.cost_details['transport'] = self.compute_transport() self.cost_details[self.name] = self.cost_details[f'{self.name}_factory'].values + self.cost_details['transport'].values + \ - self.cost_details['energy_costs'].values + self.cost_details['energy_and_resources_costs'].values # Add margin in % # self.cost_details[GlossaryEnergy.MarginValue] = self.cost_details[self.name] * self.margin.loc[self.margin[GlossaryEnergy.Years]<= self.cost_details[GlossaryEnergy.Years].max()][GlossaryEnergy.MarginValue].values / 100.0 @@ -436,7 +437,7 @@ def compute_price(self): # pylint: enable=no-member self.cost_details[f'{self.name}_amort'] = self.cost_details[f'{self.name}_factory_amort'].values + \ self.cost_details['transport'].values + \ - self.cost_details['energy_costs'].values + self.cost_details['energy_and_resources_costs'].values self.cost_details[f'{self.name}_amort'] *= self.margin.loc[self.margin[GlossaryEnergy.Years] <= self.cost_details[ GlossaryEnergy.Years].max()][ @@ -458,7 +459,7 @@ def compute_price(self): # Running OPEX in ($/MWh) self.cost_details['OPEX_Part'] = self.cost_details[f'Capex_{self.name}'].values * \ (self.techno_infos_dict['Opex_percentage']) + \ - self.cost_details['transport'].values + self.cost_details['energy_costs'].values + self.cost_details['transport'].values + self.cost_details['energy_and_resources_costs'].values # CO2 Tax in ($/MWh) self.cost_details['CO2Tax_Part'] = self.cost_details[self.name].values - \ self.cost_details[f'{self.name}_wotaxes'].values @@ -498,7 +499,7 @@ def compute_other_primary_energy_costs(self): self.compute_other_streams_needs() self.compute_cost_of_other_streams_usage() self.compute_specifif_costs_of_technos() - self.compute_sum_all_costs() + self.compute_energy_and_resources_costs() def is_invest_before_year(self, year): ''' @@ -1131,11 +1132,11 @@ def store_consumption_and_production_and_landuse_wo_ratios(self): self.consumption_woratio = copy(self.consumption_detailed) self.land_use_woratio = copy(self.land_use) - def compute_sum_all_costs(self): + def compute_energy_and_resources_costs(self): all_costs = self.cost_of_resources_usage[self.resources_used_for_production].values.sum(axis=1) + \ self.cost_of_streams_usage[self.streams_used_for_production].values.sum(axis=1) + \ self.specific_costs.drop(GlossaryEnergy.Years, axis=1).values.sum(axis=1) - self.cost_details['energy_costs'] = all_costs + self.cost_details['energy_and_resources_costs'] = all_costs def compute_co2_emissions_from_ressources_usage(self): """Computes the co2 emissions due to resources usage""" @@ -1162,6 +1163,7 @@ def compute(self, inputs_dict): # -- compute informations self.compute_initial_age_distribution() self.compute_price() + self.compute_initial_plants_historical_prod() self.compute_primary_energy_production() self.compute_land_use() self.compute_resource_consumption() @@ -1563,4 +1565,15 @@ def compute_initial_age_distribution(self): self.initial_age_distrib = pd.DataFrame({ "age": np.arange(1, self.lifetime), "distrib": distrib - }) \ No newline at end of file + }) + + def compute_initial_plants_historical_prod(self): + energy = self.initial_age_distrib['distrib'] / 100.0 * self.initial_production + + self.initial_plants_historical_prod = pd.DataFrame({ + GlossaryEnergy.Years: self.year_start - self.initial_age_distrib['age'], + f'energy ({self.product_unit})': energy, + }) + + self.initial_plants_historical_prod.sort_values(GlossaryEnergy.Years, inplace=True) + self.initial_plants_historical_prod[f'cum energy ({self.product_unit})'] = self.initial_plants_historical_prod[f'energy ({self.product_unit})'].cumsum() \ No newline at end of file diff --git a/energy_models/database_witness_energy.py b/energy_models/database_witness_energy.py index 585d2478..851b01ec 100644 --- a/energy_models/database_witness_energy.py +++ b/energy_models/database_witness_energy.py @@ -87,7 +87,7 @@ class DatabaseWitnessEnergy: invest_before_year_start_folder = join(Path(__file__).parents[1], "data_energy", "techno_invests") @classmethod - def get_techno_invest(cls, techno_name: str, year: int): + def get_techno_invest(cls, techno_name: str, year: int) -> float: name_formatted = techno_name.replace(".", "_") name_formatted = name_formatted.lower() path_to_csv = os.path.join(cls.invest_before_year_start_folder, name_formatted) + ".csv" @@ -103,6 +103,23 @@ def get_techno_invest(cls, techno_name: str, year: int): ) return heavy_collected_data.get_value_at_year(year=year) + @classmethod + def get_techno_invest_df(cls, techno_name: str) -> pd.DataFrame: + name_formatted = techno_name.replace(".", "_") + name_formatted = name_formatted.lower() + path_to_csv = os.path.join(cls.invest_before_year_start_folder, name_formatted) + ".csv" + heavy_collected_data = HeavyCollectedData( + value=path_to_csv, + description="", + unit="G$", + link="", + source="", + last_update_date=datetime.datetime.today(), + critical_at_year_start=True, + column_to_pick="invest" + ) + return heavy_collected_data.value + @classmethod def get_techno_invest_before_year_start(cls, techno_name: str, year_start: int, construction_delay: int, is_available_at_year: bool = False): name_formatted = techno_name.replace(".", "_") diff --git a/energy_models/glossaryenergy.py b/energy_models/glossaryenergy.py index 4489b42c..ccc1c260 100644 --- a/energy_models/glossaryenergy.py +++ b/energy_models/glossaryenergy.py @@ -338,10 +338,18 @@ class GlossaryEnergy(GlossaryWitnessCore): [1900, GlossaryWitnessCore.YearEndDefault], False, ), - GlossaryWitnessCore.UtilisationRatioValue: ("float", [0, 100], False), + GlossaryWitnessCore.UtilisationRatioValue: ("float", [0, 100.1], False), }, } + InitialPlantsTechnoProductionValue = "InitialPlantsTechnoProduction" + InitialPlantsTechnoProduction = { + "varname": InitialPlantsTechnoProductionValue, + "type": "dataframe", + "unit": "TWh", + "dynamic_dataframe_columns": True, + } + EnergyTypeCapitalDfValue = "energy_type_capital" EnergyTypeCapitalDf = { "var_name": EnergyTypeCapitalDfValue, @@ -1482,7 +1490,7 @@ def get_techno_detailed_price_df(cls, techno_name: str): f"Capex_{techno_name}": ("float", None, False), cls.InvestValue: ("float", None, False), "efficiency": ("float", None, False), - "energy_costs": ("float", None, False), + "energy_and_resources_costs": ("float", None, False), "transport": ("float", None, False), f"{techno_name}_factory": ("float", None, False), cls.MarginValue: ("float", None, False), diff --git a/energy_models/models/clean_energy/clean_energy_simple_techno/clean_energy_simple_techno_disc.py b/energy_models/models/clean_energy/clean_energy_simple_techno/clean_energy_simple_techno_disc.py index b618956a..655160bf 100644 --- a/energy_models/models/clean_energy/clean_energy_simple_techno/clean_energy_simple_techno_disc.py +++ b/energy_models/models/clean_energy/clean_energy_simple_techno/clean_energy_simple_techno_disc.py @@ -53,16 +53,16 @@ class CleanEnergySimpleTechnoDiscipline(CleanEnergyTechnoDiscipline): clean_energy_capital = 12.414 # trillion dollars techno_infos_dict_default = {'maturity': 0, - 'Opex_percentage': 0.12, + 'Opex_percentage': 0.06, 'WACC': 0.058, - 'learning_rate': 0.00, - 'Capex_init': 230.0, + 'learning_rate': 0.01, + 'Capex_init': 572., 'Capex_init_unit': '$/MWh', 'techno_evo_eff': 'no', 'efficiency': 1.0, 'CO2_from_production': 0.0, 'CO2_from_production_unit': 'kg/kg', - 'resource_price': 70.0, + 'resource_price': 10.0, 'resource_price_unit': '$/MWh'} techno_info_dict = techno_infos_dict_default diff --git a/energy_models/models/gaseous_hydrogen/water_gas_shift/water_gas_shift_disc.py b/energy_models/models/gaseous_hydrogen/water_gas_shift/water_gas_shift_disc.py index 9cab9bb4..de53890f 100644 --- a/energy_models/models/gaseous_hydrogen/water_gas_shift/water_gas_shift_disc.py +++ b/energy_models/models/gaseous_hydrogen/water_gas_shift/water_gas_shift_disc.py @@ -176,7 +176,7 @@ def compute_sos_jacobian(self): dwater_dsyngas_ratio / 100.0) self.set_partial_derivative_for_other_types( - (GlossaryEnergy.TechnoDetailedPricesValue, 'energy_costs'), ('syngas_ratio',), + (GlossaryEnergy.TechnoDetailedPricesValue, 'energy_and_resources_costs'), ('syngas_ratio',), (dsyngas_dsyngas_ratio + dwater_dsyngas_ratio) / 100.0) mol_H2 = (1.0 + syngas_ratio) / \ diff --git a/energy_models/models/heat/high/electric_boiler_high_heat/electric_boiler_high_heat.py b/energy_models/models/heat/high/electric_boiler_high_heat/electric_boiler_high_heat.py index 6360131d..0232e784 100644 --- a/energy_models/models/heat/high/electric_boiler_high_heat/electric_boiler_high_heat.py +++ b/energy_models/models/heat/high/electric_boiler_high_heat/electric_boiler_high_heat.py @@ -41,7 +41,7 @@ def configure_input(self, inputs_dict): def compute_heat_flux(self): land_rate = self.land_rate - self.heat_flux = land_rate / self.cost_details['energy_costs'].values + self.heat_flux = land_rate / self.cost_details['energy_and_resources_costs'].values self.heat_flux_distribution = pd.DataFrame({GlossaryEnergy.Years: self.cost_details[GlossaryEnergy.Years], 'heat_flux': self.heat_flux}) return self.heat_flux_distribution diff --git a/energy_models/models/heat/high/geothermal_high_heat/geothermal_high_heat.py b/energy_models/models/heat/high/geothermal_high_heat/geothermal_high_heat.py index cebc8d1d..44a64238 100644 --- a/energy_models/models/heat/high/geothermal_high_heat/geothermal_high_heat.py +++ b/energy_models/models/heat/high/geothermal_high_heat/geothermal_high_heat.py @@ -58,7 +58,7 @@ def configure_input(self, inputs_dict): def compute_heat_flux(self): land_rate = self.land_rate - self.heat_flux = land_rate / self.cost_details['energy_costs'].values + self.heat_flux = land_rate / self.cost_details['energy_and_resources_costs'].values self.heat_flux_distribution = pd.DataFrame({GlossaryEnergy.Years: self.cost_details[GlossaryEnergy.Years], 'heat_flux': self.heat_flux}) return self.heat_flux_distribution diff --git a/energy_models/models/heat/high/heat_pump_high_heat/heat_pump_high_heat.py b/energy_models/models/heat/high/heat_pump_high_heat/heat_pump_high_heat.py index 4c18e41f..cb792fb4 100644 --- a/energy_models/models/heat/high/heat_pump_high_heat/heat_pump_high_heat.py +++ b/energy_models/models/heat/high/heat_pump_high_heat/heat_pump_high_heat.py @@ -54,7 +54,7 @@ def configure_input(self, inputs_dict): def compute_heat_flux(self): land_rate = self.land_rate - self.heat_flux = land_rate / self.cost_details['energy_costs'].values + self.heat_flux = land_rate / self.cost_details['energy_and_resources_costs'].values self.heat_flux_distribution = pd.DataFrame({GlossaryEnergy.Years: self.cost_details[GlossaryEnergy.Years], 'heat_flux': self.heat_flux}) return self.heat_flux_distribution diff --git a/energy_models/models/heat/high/natural_gas_boiler_high_heat/natural_gas_boiler_high_heat.py b/energy_models/models/heat/high/natural_gas_boiler_high_heat/natural_gas_boiler_high_heat.py index ace5d72b..c25db254 100644 --- a/energy_models/models/heat/high/natural_gas_boiler_high_heat/natural_gas_boiler_high_heat.py +++ b/energy_models/models/heat/high/natural_gas_boiler_high_heat/natural_gas_boiler_high_heat.py @@ -75,7 +75,7 @@ def configure_input(self, inputs_dict): def compute_heat_flux(self): land_rate = self.land_rate - self.heat_flux = land_rate / self.cost_details['energy_costs'].values + self.heat_flux = land_rate / self.cost_details['energy_and_resources_costs'].values self.heat_flux_distribution = pd.DataFrame({GlossaryEnergy.Years: self.cost_details[GlossaryEnergy.Years], 'heat_flux': self.heat_flux}) return self.heat_flux_distribution diff --git a/energy_models/models/heat/low/electric_boiler_low_heat/electric_boiler_low_heat.py b/energy_models/models/heat/low/electric_boiler_low_heat/electric_boiler_low_heat.py index 8b6a3825..e79ae4f4 100644 --- a/energy_models/models/heat/low/electric_boiler_low_heat/electric_boiler_low_heat.py +++ b/energy_models/models/heat/low/electric_boiler_low_heat/electric_boiler_low_heat.py @@ -47,7 +47,7 @@ def configure_input(self, inputs_dict): def compute_heat_flux(self): land_rate = self.land_rate - self.heat_flux = land_rate / self.cost_details['energy_costs'].values + self.heat_flux = land_rate / self.cost_details['energy_and_resources_costs'].values self.heat_flux_distribution = pd.DataFrame({GlossaryEnergy.Years: self.cost_details[GlossaryEnergy.Years], 'heat_flux': self.heat_flux}) return self.heat_flux_distribution diff --git a/energy_models/models/heat/low/geothermal_low_heat/geothermal_low_heat.py b/energy_models/models/heat/low/geothermal_low_heat/geothermal_low_heat.py index 1860abe8..bae05328 100644 --- a/energy_models/models/heat/low/geothermal_low_heat/geothermal_low_heat.py +++ b/energy_models/models/heat/low/geothermal_low_heat/geothermal_low_heat.py @@ -68,7 +68,7 @@ def configure_input(self, inputs_dict): def compute_heat_flux(self): land_rate = self.land_rate - self.heat_flux = land_rate / self.cost_details['energy_costs'].values + self.heat_flux = land_rate / self.cost_details['energy_and_resources_costs'].values self.heat_flux_distribution = pd.DataFrame({GlossaryEnergy.Years: self.cost_details[GlossaryEnergy.Years], 'heat_flux': self.heat_flux}) return self.heat_flux_distribution diff --git a/energy_models/models/heat/low/heat_pump_low_heat/heat_pump_low_heat.py b/energy_models/models/heat/low/heat_pump_low_heat/heat_pump_low_heat.py index 91438ab6..976239e5 100644 --- a/energy_models/models/heat/low/heat_pump_low_heat/heat_pump_low_heat.py +++ b/energy_models/models/heat/low/heat_pump_low_heat/heat_pump_low_heat.py @@ -56,7 +56,7 @@ def configure_input(self, inputs_dict): def compute_heat_flux(self): land_rate = self.land_rate - self.heat_flux = land_rate / self.cost_details['energy_costs'].values + self.heat_flux = land_rate / self.cost_details['energy_and_resources_costs'].values self.heat_flux_distribution = pd.DataFrame({GlossaryEnergy.Years: self.cost_details[GlossaryEnergy.Years], 'heat_flux': self.heat_flux}) return self.heat_flux_distribution diff --git a/energy_models/models/heat/low/natural_gas_boiler_low_heat/natural_gas_boiler_low_heat.py b/energy_models/models/heat/low/natural_gas_boiler_low_heat/natural_gas_boiler_low_heat.py index d028b290..ffcb9831 100644 --- a/energy_models/models/heat/low/natural_gas_boiler_low_heat/natural_gas_boiler_low_heat.py +++ b/energy_models/models/heat/low/natural_gas_boiler_low_heat/natural_gas_boiler_low_heat.py @@ -75,7 +75,7 @@ def configure_input(self, inputs_dict): def compute_heat_flux(self): land_rate = self.land_rate - self.heat_flux = land_rate / self.cost_details['energy_costs'].values + self.heat_flux = land_rate / self.cost_details['energy_and_resources_costs'].values self.heat_flux_distribution = pd.DataFrame({GlossaryEnergy.Years: self.cost_details[GlossaryEnergy.Years], 'heat_flux': self.heat_flux}) return self.heat_flux_distribution diff --git a/energy_models/models/heat/medium/electric_boiler_medium_heat/electric_boiler_medium_heat.py b/energy_models/models/heat/medium/electric_boiler_medium_heat/electric_boiler_medium_heat.py index daa9774c..bfd17259 100644 --- a/energy_models/models/heat/medium/electric_boiler_medium_heat/electric_boiler_medium_heat.py +++ b/energy_models/models/heat/medium/electric_boiler_medium_heat/electric_boiler_medium_heat.py @@ -47,7 +47,7 @@ def configure_input(self, inputs_dict): def compute_heat_flux(self): land_rate = self.land_rate - self.heat_flux = land_rate / self.cost_details['energy_costs'].values + self.heat_flux = land_rate / self.cost_details['energy_and_resources_costs'].values self.heat_flux_distribution = pd.DataFrame({GlossaryEnergy.Years: self.cost_details[GlossaryEnergy.Years], 'heat_flux': self.heat_flux}) return self.heat_flux_distribution diff --git a/energy_models/models/heat/medium/geothermal_medium_heat/geothermal_medium_heat.py b/energy_models/models/heat/medium/geothermal_medium_heat/geothermal_medium_heat.py index 5013f2ff..bc05b8f6 100644 --- a/energy_models/models/heat/medium/geothermal_medium_heat/geothermal_medium_heat.py +++ b/energy_models/models/heat/medium/geothermal_medium_heat/geothermal_medium_heat.py @@ -68,7 +68,7 @@ def configure_input(self, inputs_dict): def compute_heat_flux(self): land_rate = self.land_rate - self.heat_flux = land_rate / self.cost_details['energy_costs'].values + self.heat_flux = land_rate / self.cost_details['energy_and_resources_costs'].values self.heat_flux_distribution = pd.DataFrame({GlossaryEnergy.Years: self.cost_details[GlossaryEnergy.Years], 'heat_flux': self.heat_flux}) return self.heat_flux_distribution diff --git a/energy_models/models/heat/medium/heat_pump_medium_heat/heat_pump_medium_heat.py b/energy_models/models/heat/medium/heat_pump_medium_heat/heat_pump_medium_heat.py index 4a5830ec..fb3058a3 100644 --- a/energy_models/models/heat/medium/heat_pump_medium_heat/heat_pump_medium_heat.py +++ b/energy_models/models/heat/medium/heat_pump_medium_heat/heat_pump_medium_heat.py @@ -55,7 +55,7 @@ def configure_input(self, inputs_dict): def compute_heat_flux(self): land_rate = self.land_rate - self.heat_flux = land_rate / self.cost_details['energy_costs'].values + self.heat_flux = land_rate / self.cost_details['energy_and_resources_costs'].values self.heat_flux_distribution = pd.DataFrame({GlossaryEnergy.Years: self.cost_details[GlossaryEnergy.Years], 'heat_flux': self.heat_flux}) return self.heat_flux_distribution diff --git a/energy_models/models/heat/medium/natural_gas_boiler_medium_heat/natural_gas_boiler_medium_heat.py b/energy_models/models/heat/medium/natural_gas_boiler_medium_heat/natural_gas_boiler_medium_heat.py index d50f4633..cb8d5547 100644 --- a/energy_models/models/heat/medium/natural_gas_boiler_medium_heat/natural_gas_boiler_medium_heat.py +++ b/energy_models/models/heat/medium/natural_gas_boiler_medium_heat/natural_gas_boiler_medium_heat.py @@ -76,7 +76,7 @@ def configure_input(self, inputs_dict): def compute_heat_flux(self): land_rate = self.land_rate - self.heat_flux = land_rate / self.cost_details['energy_costs'].values + self.heat_flux = land_rate / self.cost_details['energy_and_resources_costs'].values self.heat_flux_distribution = pd.DataFrame({GlossaryEnergy.Years: self.cost_details[GlossaryEnergy.Years], 'heat_flux': self.heat_flux}) return self.heat_flux_distribution diff --git a/energy_models/sos_processes/energy/MDA/energy_mix_optim_sub_process/usecase.py b/energy_models/sos_processes/energy/MDA/energy_mix_optim_sub_process/usecase.py index 23493bb0..88da6027 100644 --- a/energy_models/sos_processes/energy/MDA/energy_mix_optim_sub_process/usecase.py +++ b/energy_models/sos_processes/energy/MDA/energy_mix_optim_sub_process/usecase.py @@ -696,7 +696,7 @@ def setup_usecase(self, study_folder_path=None): f"{self.study_name}.{self.coupling_name}.FunctionsManager.function_df": func_df, f"{self.study_name}.{self.coupling_name}.GHGEmissions.{GlossaryEnergy.SectorListValue}": [], f"{self.study_name}.{self.coupling_name}.max_mda_iter": 200, - f"{self.study_name}.{self.coupling_name}.tolerance": 1e-8, + f"{self.study_name}.{self.coupling_name}.tolerance": 1e-10, f"{self.study_name}.{self.coupling_name}.sub_mda_class": "MDAGaussSeidel", } @@ -709,4 +709,4 @@ def setup_usecase(self, study_folder_path=None): if "__main__" == __name__: uc_cls = Study() - uc_cls.test() + uc_cls.test_jacobians_of_each_disc() diff --git a/energy_models/tests/jacobian_pkls/jacobian_CleanEnergySimpleTechno_CleanEnergySimpleTechno.pkl b/energy_models/tests/jacobian_pkls/jacobian_CleanEnergySimpleTechno_CleanEnergySimpleTechno.pkl index c48d1e3258ace8894c237fd7cc492eec82634e22..0b395731a04ba5a3e6da9fbe38dc7aecebf55550 100644 GIT binary patch literal 1447 zcmV;Y1z7q*T4*^jL0KkKS(Z@IEdV0YfB*mefB*mg-~Il--(SD~|Nn3GfB%2i*m?K- z-(UYb|LxEO-T(jq1qBgGJtlw}0000000003KmY&$0000000000&;S4c13-F@NI(Dp zXwU!vXwyIgCV&7M27nC%AOHqKLqHm615E%7O$I;)fuH~y88Qd}01X-d01X;w0A$br z13=ILpkx35$Y^K-O#o@20g0f<0MIl514AZ30004_KmY-wO#lp<003wj05lAM02vJp z0BN8NGypL)82}mvfBS@1Ye4MovhQM3doZ(Nzj5 zN=P}{qI{}IAY=5I-7CDgAh~VOqLx!#*zU%xu!M;&Wmk5#FV;GnXfz*Zy)9X8+4D50 z?}D>zMWh&vubX_jg82sbtHHr=eaikVlKs}`0;k%-RPC-*V4$M@{becS`OSjtpMh`L zrUBgexEfYNw8D2jrPXXT%JD^5I&Z_%ID`8(yo;yZdI)sPqtg4yR;B2-%oD|P7WV#G z4xvQzS!^mgSJ=XyqXfp{^jY~Y>EY2{MM{oGQENoi=ez`KeI)!^<4Wt-ytU${(_hxQ z%f1HXxG3B=B}$uj!ktoIx>Bv%k%xrWfBq-jJ~;BzD5ku1f1x(9_yh zSggNa<($9UHcJS5U54e(Cb&Gty_+(e@^^ioZ{m1W@%{H#q{(VH$DNe&8Fyuv*|;NN zUuxNUq%j)J!p3KH^%U37t!0)|4^P?K<+#2!PVaMbbitz&Jp)5Ydd0baBSgt{PmPpW z;bHoC7->o-u`ZSLcz5#!k{hL^!dtJCrUg5VkB4w$n+J^J-IKR*&7#uM($doTX_=E` zf`l66GI^?vg6cXbnAc}^n6>9+4CLN6$oBEElax%?>f|%J+8l~jS%sgeG3m3@CB~^2 z$@%g!RldG~7`A35BhafETiEkGKa-hG*vpj|#(7^6lwzvQS&YhyhAR5J3SE?4l!hl9N>HTeXN*_6ct>A0yJfX`K$SB+gQlMLDt(B;+@a>73ZN&+_XQNHtuY z(y3B{r3FfiKf17!5{a$eG@c7&^W!~8lI8{KxvER)N?_BX(9)Covfei`U;ejK-8`im zHvL7W)VkBT^^I#1a-3JLL!M7(d7spfwnLko-8rr0#1uh~+V(U|yLCG5TfeEkd0h4t zFqz+8^Fq{967{FM>fV*zbLla$-vx(+g$E9Xna1$??@d(Z03~<{A}|?UjK3yV)Rw2b zV<*`EgKw1SAf5C%69OPWaw3ux+*6X`^rsx)9<#Z|@+s6jH%6n!Yt3=a>o@)5fu!Ix zM0N>_%VK;@W{+~>+KgPf9LBsnZ2uZF=%k54QbS`zN{7vJE*5oOZq{dx)AkPkg>-8e z5^voGlkR1G;+#H*(XX7~`>8i>-J^0=_$^jOr`CJ#fyq3Y(pE~OD@B3(_RJR`?{aMW z&t{nlij}2XbD>0}H~005$3 zfB@JC00_VW+!T_4p(LpFO`#Q$QIsX`!_S zjD}4frkg3FO{u4p(x)eQgo!`ncu^UC=PklsN{Je!Mlz&=ZYoGeTBJzIs3gmTBEk`; z5)veyXT*zCRY*Pi2_STO`8TrJL$*&6hy>QK#<5*Q;cI7MLh5RZZqbAqp8PHzSv^4Vj-hL5YXXU<{Wv3>55 z5A7pg@ODN#3EM|<`bjG&Ye%`)&x=8b{wJJERp?6P0Z=BM`gOG*wG};#1$+YH- z#?uSut!YhZQ8R27QnEz8k++?(rp2NswX4vSr722LMOAg|GVEcqv82~*x$(1oo@T%B zHUAjCM!a-*5>J|{kzFzpB-`5RIDSi2tgBTPvQ)KFlJXAD=5o@?7ErRqi8Sdvp3f3< zsFMv^_9|qRS*zjbRZm#Lc!+-~1Y~g7>f)umN?%YHUROEl+?ntK!5*5wA20#@_RZ9Q> diff --git a/energy_models/tests/jacobian_pkls/jacobian_CleanEnergySimpleTechno_CleanEnergySimpleTechno_construction_delay_0.pkl b/energy_models/tests/jacobian_pkls/jacobian_CleanEnergySimpleTechno_CleanEnergySimpleTechno_construction_delay_0.pkl index ae485c6bbbb65c3f2de208fc7fa55381f834b298..5468066c08547b030afeb3cac834a95165cb676a 100644 GIT binary patch literal 1395 zcmb`7`#aMM0EefXwBvR##ZJf2M!BrHCTrE?_7$y}>ugz~O^J|Ok8nbvQEqK+liMiC zvstNw$HFCwuXrlTNB6M#zje|{bgv`(vUn01Oh>-l<_!x8Hxr@PidzpgFs9WNS*?6 zRAYnGZcw)OOQsxZy9=(aOoKu}J5Qc~YAYpK2Aa!NVDD<_YVULd$z_+_-+oT)N`itw zG?22~{~!MCemB?6zzvlQQMT+ZEM!$bm@B9hs}cmOI0H6|U}eqD)JZR7;dyFYIEq%?GHUPueAIJ)RXG4Z{X+Iw(6q_RSr*nfVEV`#)Olsj zRXclzg%a#oS65d11TZ9FX-5S$T`2Xs12MJx#(@+$&_LDYvWW@fziU}WgL_mH4wa1qJl*QaHf z$H+ni){hRErFg8x!zvf*#ZvD-D$+jY8(o?D9#V?UkJj{jNjn<}el=zLMX@`s9y0(h zZj?q$i+%k99t0m_bu2a{16Zkx8&c73PT&>^4JXd7Dr5*?MJBEuSCCZjsjW_620)|? zmr}^Tk5tsY^&xtZNPG-ZF#RY8cRpjZI#wsLE%LME33^J{J+-_rcNF6$BJk@IHc`Tl zVN)}chyx;IOvEt0CkTNb4Wj~!-ukF6yPqMfb`)R~%^nh+z?XYKRsO9VH= zqTf2VJ6cIsG@vc=m8+tq1b9Sv?o9RZ(w+(TKtzz_){tw~E)Mctrw_~P_CY^txg&zf z&7K?0s2`j8dHt|;fq@>LPSXyl)$|-QMwv3Ru055n*l~s#1R;>mAxV}D`V033cGo%= z0l_;!@PG@!)7?+v;HL$09a z1@6SWDoHEgf}8DMS??MI#y1AdoN^={WE;nwHJ9!kHaRkU;W#Ph5A)vsol_xv}IeXpTC=zjiX zn}H1?PR_dd314)m{AYAj1%JbdS7h>`h17AYRaL(qrlnBDJn+gnb-#M-L(Lu0+V!blY7TzgFQFVN#VH-I<@q za=4y)L=#p2n=h!-W8$*J=*#6E9(5*KhpEF~ngT4q77Q}&XnbAH zO(gXOk5kh~(UV81>KbSO2AUc&00D>q13-}}O&FRD0009( z000000B8UJHmX2|nKaPE)6p>v7y<((fsusKpaVvX)G&ckkR;UfA*0j{001<|0Avh? zj7>CR01s0oCaDP$ZXF>=F4AJINLM8!8096>gpwAi1eVtbs6sK3B#4uBoGPS)GN>UZ zlOscJoK^$xGRhCVY}u zPVt;B6{RXGI1dkGgAz($rVCD`O4VB{Zj@B+tHf`p-D|Ackvai&YWz2pVTm&6scgDO zDV5QFaA=RisO7?I}3*%vG^ozFaU#8dZ%bOzj>KZLJqgAZ%ty;Be!<5P{2pB>S zP)lEc6Z9y02cjXj+88~^D5Qz_v4$|53y$GOTF9O!h;&_)E?Q6#3uUGw!O3HK{1nYDd5IA0ZWz?CKKk;;`m&qF^ppv({D}%ZF?dJZO6$( zNP)0kKKKy0(ey>yB&o5C5X_)Jkd=mMMvORt!(k96p|Og{No-68kj4WB0-Dk7gWihv z0+l(?YLPcDCf&7o5|@Yt^nJD9&;uY65{}kjfq?`X*n=KZ5?LGWN~SxBiz(S=(|?t| lFEM2Qh;9(7+_6bB$Wc>uRI-31+)c;)UC9*TLP7rchoEtW8d(4U diff --git a/energy_models/tests/jacobian_pkls/jacobian_electricity_cc_gas_turbine.pkl b/energy_models/tests/jacobian_pkls/jacobian_electricity_cc_gas_turbine.pkl index 08ce8001bd8ac231af4b0887f7ce0324985d7337..f92ac9f982192ce721ef757fcc3ce11ac8a68ca7 100644 GIT binary patch literal 2659 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 diff --git a/energy_models/tests/jacobian_pkls/jacobian_electricity_coal_gen.pkl b/energy_models/tests/jacobian_pkls/jacobian_electricity_coal_gen.pkl index e0ee1c40509f9e8d638234b7a0305f5e55b2aa3d..8640eb5b84f337bbe5bd2f57cbd917fc1c24259c 100644 GIT binary patch literal 5231 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 literal 5227 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 lcNi1ri|8VfFf-WUV_7ji{7P>_CN;P!m|d{+Pf diff --git a/energy_models/tests/jacobian_pkls/jacobian_electricity_gas_turbine.pkl b/energy_models/tests/jacobian_pkls/jacobian_electricity_gas_turbine.pkl index 7f0e1d33f6a6f4c69bb3f6822d59e3f7cb5de38b..eccbf3f0348afb67616b2053454e314d9f98d73e 100644 GIT binary patch literal 2652 zcmZuyXHb)i68#_nLI{{Z5a~&1iJ~Nzj-dn-qyz~dNRuWgRVg;2LZ}7_nn+EQ4uYs4 zMFm8N(h&tk^uqcm9%p>rqitIshE__j+U>kPqmL0G5Cp00aR4AHs}Zb#ZIv5|>y+ zp%BRC8|9g${B(Bc1fVUa2f??*Ge z!F-^#oU!8s^Z|0ejr{7uk{EkP-}8UZMVI|fzXRld^fv?9{-dA+DdrUFNJ-hsiBr|N z;u*$-KYRmcLXnHc#T-3s#7>t z96tv?S0r~gMV*p{!1{yX!t)nU*>PkTt3gtbBm|WLsFrCM>~j<z0H!4pzzD#rDS$l`4><;B2v{c2z>*2r#t0;-k&F^~6zc>K zqJk@UPaiDxti{YY3(91~_Q8}68x#Lk0HC+LLcTiq3plwoxkK-Wm9%9*-L+KTUpq?E znapV%0xeS)9Wrb%pS$)nd(4Zw+M0FUia#I7BE<{jm`&SEEJ4IJkvAl)5Noh=+GSeB zAJeCfgl#9)u6k0x*g)GryIo|>pVOD{LO4k5wSxsPKwO&{?O2fg;7HHChZKCwzB=Q1 zo8SV|AwT;c`VCU4*mbM!pE2*b?QD0Pf~VV!UGdo;qYSuQztoX(0m)H(aU)wfIVuTA`H`QGGBCA9T%cl8zRAOfi`77?9l7KY!s*14+tr zoW{qM8_mpvH>R#RWu0O;8Xx**;&Wn#e7>DI9{aC1OB~DQV)6OxkOo4P!Qsz?X@MQW z9m(^RL`>T#gN5d|>bC;|(A|E=C>+(BR;o8Q4{R4U1i9miI=O)O-PyU$irUOX-)|Gr zyH1=*ZS+DHyFE>+Sk?1UDZhW~B3LBpnj`)r@6U?YP8nuNn(($b#Oqn@nC2@MQIok* zlehnI|BxP+o>^bhyaCT(Kc_$?2;5_X<~*qx+1auChr9zDuHz9Ii{he~U;&`=asr?_ zqs3n_5WC79wIx(ftgj(lW{oq_4cnzGnxZ20GKE>noq9&dE6&m+ZwR=l%$o@j7>#%c zFM~Kkl~c|ZV}etZYTn?Lh^m82)~mGp?lCDfU46)l#Roo*teqqw*Iwfu9sCq@dab># z&~;p_-c11yn#$@k^0NpOfTT>B{voww~KUDG0zQf9!>YG}^qHCPK=HQOU6SgbVH*-hc8f*2QXaPN|8b~63 zSEosOqr%7<2SNR`vSX~aL~b7C54Gfl9)!)MPU?GeUydzKxBRd>qdd6V|Nd(J+w2yf zTH&2NIh#NRa8K{B+3jAtg&M<3ixdl%kH2Jk8Aqz-6?nOqxN_<(7M_3QEfUsAWGTBO zlN>Zpa-yxSBbxD<FU!X22*A+Wl(3+=b+Zg9mpBvd%XZ~2v ztw)|6U^wbqkz=l#v)b9_br1BxH6KT)KwyMgD{0y?8i--T(j; zab<&?)SQ98@M?gD`niSyAomf62mlILzRZx_kK{;<(87+OFGO!$eT>NspU5TTyx!8# z^AWpO#v1#iG%)+Qv-Bs?(^QQPp%mgu3@*fYp+9iIiR?ot?UMQpWl5_sCma;XkhR=0+YT4*73~)ngS_)af8&2|5gUs&Kiu?1~E1*Gsr@ z5F_9A+Ar$#@XMpjvr|`{)@$d!@M57N+bs!~j_H}NMpZ!<{P1Ay}Cft%pqru~F`+C27(75qg#E<=S`{C>rNBPdJ zdC;E+jI}c+!=x!!HI$yO*~;OzS%@Zs4xm|SOvq9tBw#Q%@vYf3T8AcQd6w-oAnl1b zK&}`3e26VU=`=Jn6vYxoYd=Noj}PrYp#|au;cAaFCrdB8wrxF$TINZh1`|g4Be)dX z`gWD~Oj5c`EKsHEZwg6qY^C5Z+!~+K19s&ng!VS2^k%kpJ)>yak>fXI>Do5+#G+Z` zb#~s-z*IoJ<4I4b^L%wk0hV^>~m^+n1`1|M%t)e>8n39>gaj^t7Z z=D4=$U8vrIhTG+j$0hZ6)n4|<6+NN+J`Ak#1yNPsxZp)y+xp~bw^;u(XSy;M`Wa6W zNe}y$e8uH7cV7e@G4Xy=!X{Ma=|xG W&4XG(3^UX5Im=S0q^Jlgcl1BFUXr~4 literal 2619 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 literal 1272 zcmVPMiU{B0Kl0t05oW1XaK?*8Z-dV05kw( z15GjmKnNNc4Ge=ufM^XgXbm)BG8q6236mfLMutX!3?ZSTKn(x`Kn6fG(;zee6(taw zQ_^VBk&|kAOw<6#G{n$o&@=!V05kwJ27nm`L7>r~&;T?U8Z-f*27_u)oK!op2@-9Q z#oJV|MAy>PSw)hiS!I?=s<5$Ti!G8b%yyL8E&*~}z_k#uMf9RbnPP;KGEpOkQ6%RV zP>TW)k3@+i7&RqSgh3d}NgTZ?5k`uTNR0+}hRgH02@O^W3>+EEr9@n)e8aGd1SCm& zWstn`uShj!1EafT&TpUYIUAA>(%mwusU;~@RTO0R0vBW*HWx9DX)sXt?ebrw1FOd5 z08iaRej1?KHbH?^Wa9QR0wPSedhl$)nwqn;zZs@MvZ3ijF=|UaGK=^&UyV(>oVFjP zgyA3%H>PAd(j5KaXWP|-BL)o6S&EHj)z)9!qo-n zfV{*KdhH-YPkL_+^`giX3BFBe&L9H9^RM|BX)+mP=WgQjKp-j8RjWMfh7Pa2sGy4V z9W_H#a?=wN!PlM;6f9I=g2j%&AqlcyNLXMb#UEOLtdUm?nnNH)c|$e;K_;KW1oLE? zddJPMZt<)&N1A!YWX~9vuU@r!^_QuukWxTb2};MIp)|!25x~Pqh%vS+nqWqqQRuHO zTW2e$89K%TK-4QOmklCxcA?`=hXs+JgxK&uU@B&II?}6kQRS!;4#p&?ux72q!xvj$ z7dMm0jWLNb=F@I4$B%adaV0f2<;D2&z5xt;v)EfG{>E3Y_y?j)x_v72vHb=)rjh$PcTS!zVR8nD;1i4 z(6pZbY~=et4HkMKnTTipn$B0t3&@|V_Y(i{9VZu;X*=#jV)v+&^&4Y diff --git a/energy_models/tests/jacobian_pkls/jacobian_electricity_nuclear.pkl b/energy_models/tests/jacobian_pkls/jacobian_electricity_nuclear.pkl index 3ca139eab6c2f41ba1ea1e5128325f52918666ab..318e183c2f735a0ea377fb2abb56834e3f5b9518 100644 GIT binary patch literal 2115 zcmXYt2UJsO7RTR9LYfJLkfmfSNeB@E9ZDQPL|z~;gih!PQU;j>DaxP-3WQJ$Z3#^b zu1b?OU;!zPD1sD~fEkEL5zqlda8ML*9NCo(d-i|s_q+Eyx14*fwLj0?h~>xd@PnW$ zbwE&0%9cX^!|c|#?O36dZBt-tQSrK7AyX27knrfXwT4K?(AOQu>GV;M%ol}_@zu;x zh{q%2k!5gaCQ<-aX%p~x%{*it)5J{{_R#4PBO)x6zoeN*X7Zkt^Q$1fNXwm?RlLER zyih#JdYh-EB4><{krFK?k6)7pqN)?2Q8^P%0hcWa0D#An0Vdo=E+fMs01ky(LcGc- zEWdce(p7$~fj1gOMCM+0Lka*U008g^MKxFsfa2|sodDngif&5?AoCio%30++02pOe zL5k$><#0%b6OaK#Cms}qB5mvlWW1tUAs)ERZC|e7He`xHct!kwDaHV*9I7jJZ>zR^ zbjU-sK)k2na18}jEn|iyLfZ$yW#+SGxXet&USuY~Lqb~c5ydiirg5SKEQi6*41?h3Nq){d(4Em8q|&!R+*Qf!sRmHN;j(!!+T1fR~$5;GiQHvPsLF$4kZ2O?qp*8kevgv6!pXNe|<7r znjqXJl<2_z7}ynJK+?)_ln!i`E|Mj{(J9Q*(RO4b>MDD+z=J%=((i-m@7yQbH9m7-;Bv`bVXv`I1 zszxYun_o-80QW0Nv2VI>N983tcrT@dTLDt3H71!I`e0cJ#-YQ2+RU}vt})#@RFwBD zdnxKDV#xJr%Nf@(SJDuDSYz7T93n=-vQa{sr zWU?qI>E_(754D6LrV;J$M$h4C?N8S+wgT+EI2ti=;?l{N7qM*TeP(C683kIf^sq9d zB+U>nqC>_AfaLoLx_<`=3G)% zx4WwO+JT$%HtM1ntMfkjhYC}%D4mX9q>bxe zrmb?~!kl+C-6X!rpOLn344k0u=EArYoV^%(dHgcD`c=f^Y}=vU<x+v@c$JEAr01OblbKm80Yo z$8qa%1{}I1M|b zZ<-6eAKuPe#&g3rPbd{a90Um@^;RB!vAD{vpy{Obanlp%`ODGpv*+&|%0BG+mDF&h o(#0Nu*fE#dcB6gcq)cCIcJR&O#Q0Qj*T;CeM+OFgz)TAM3qYfU?EnA( literal 2115 zcmX|+3pmq@8^?dcX4sC+twe4u*KTGKk>9Q+CfDYgbkI=vhm%WXE-|9EX=0V;&Jc+r zDYuFhEhD6krAQR&;Qv?3U+4dS&i8q~@Avb(&-=X3`x1N^*5-I0Lk}NU%|Qnsur+yo zef^i$7bbqyjok2B^8cH^8d6wt0uVBO#X8{=&hpP@;VVrOj6_1C=(9Vq3;+wu$Vp)X zi6Ax@L8LH)!-@s+WpUF^@_2=lP2#6amNxvkQN>k zVmMBDSf>=!tj;P84`tio1>zT#73^g@8l;p4hyi8SFaZEdsjTV~h_Qg3b4Erkpd)~l z8&)Kyc~G1ghB-rhshbTA3pcCkGgTFevpB_^EKWKCg2isZ9R*~9j{-nJhI87+Vdi_} zj^bW8&Kd&%fK+y4Zv=n@wg3Q(2>#-K1QDSA$B<&d-vALHe~tbl+y=qRE-xAKD>FFN zeDv%de8n~}IE;;hSf?y%#AOC#j+r-pRx^mdtJo0QU(4r9#nwH3JfB)2)tWEi5(|5Te-I;jU;|lk=lV1Dl8At{U(< z&d1r2ey1owOAU!IW^Wk>0K$^6yV}|f2D4M_wfNc&7bxGG+*=;^fs)VXlsElRuf!HX zN(85F`_LcD`xrE?Z@cCZlh;nJLxfm+x*FN%`v2guGQ;>5Bw0BH=p^6wj8{eca})xF z*&288o0)Y*PI+SUuz$1W9RGf{dyPWeKNc6SR|Nu1yZ5z>`nFD;X?ytJ*IXAk$2@oIFQ&cc&%e+*;JA~ZX*!Wl7NhhzjcH`{<6bp$~ z_+VEj`0jRe0u)CW4K2_&Q#j99ePt@Q1jD#&o9`^l%I+I4dSC7)4p|qK2RM9Z1oA%{ zv@CLy$6wtucYxn%!ppoPL~QLR?PKa4XO@~x5ZtNJlVc`e+KD$%-!#)j2;@WMZ&UEz z{VE^(=u!iFKhwTioTB(lJea%qX$mv39S?3R-7$bj&8`zji4~ z|JFQL4@d{)p=bkFaJYlocT7nb7ylYsSkvx{uJ3Mc&=|Ek(wC(9bd}J9%#xqCU8_#< zixCEtC)75V(Q#SR^);dWhUjv}I2wn}n>HDw@JJ)y3Ps<&Ii&6rqSqMxAO%Ht8~g!4 zyVyif0F(BKjn-qH5ceI(l~bfX%GJ!$iWKX|x=pm4br_V{dG_&~8GcK&aeR+rSPnj$1VF}GPux+Bnmo5PVereQoS*a+LCng#;vW_k zFe=lwODTqD{%EoVwPvB#x^uaMi^wwO(&SVvaFmKluTFO&BW0@&sW;*0SJ;@0TI`wi z`J48)nuRtC4wlZ(X zyS6<{c1t8#lOG43=HD>Yzw*`%`N2^WJ|J<`*Mp73b2 zXt2h!+V!Bu$HQCP&;x@#}!SL!)y{jchB9Ucf~^ut7|7gvBQ>jT>>P0`uqQ~!1*`G z7{CmIrf)cBsRw*}@9@102A}G9HtYE>w$Fzsy=AxJ*{ksAhPzH2fZnFT@jxR+{M)nx z2+$0OfQwK)BRvIj^IZq2?7(Rp&Xv=QC2fiThjSvac!4-0I=&NBUB*eSILN0}a%)O3 z7S{&jAHb_B4mG8hipz>B-)dTIh6q%(;$PTJ=9Y!OpH+n$W6+ z@Qe?k|0@CBmQVlhBs>5=SmFWHCwKq}=uQRkS=b>*_Q*QNL!h$mC5mHo6!GCyQr3H8G#o?zY`21jtNhkco zU`-y_3_pF4YgS^KEc;rtXgd;wFjPbk&fI2vrlqbU)&m()LtrLY8Vi90)yqbb z>@K~OBeO8TvOpOuXl_<>?ipPeXwYG9+TxW}6q?JHM0HJEOXG3-fEwwBVG)@eJW{4H z2tQ^<6uBMAa)Q$z<53**=}D|H1R*S~SO8g|7;(}?zDqwz)`TeD7%mL%I@l>MCd(fk z!)3<^gmeZZgZz=KGDv`-AaWlmCIb;iO~i0;laY#OBvUULjbubN#0o}|f-?w!3>~K^ z1kOsWygLYnBgoBmD~&{Z;0Q4>*4N!H$e-BP+O%5{a6^WOJ;x1`qsi)y#SBJ+l+J?+TZXM4^f{E&ZwO&t zvf4>;5`A6XqyZ6{_m%tBQd-Q~lkB}53AP4jW_ee~t_Tep*MO#R}w#E`_L3UmB>R4b%;2aU{StLkr%8;o*_$NGYfhssu5VAK{@w_ukPtB{lj z1+AJ>LV5zhq=-C4S5()Dag)w#bc$Twk9R_Qi#c5)ag=;#$Mco4k9datya4w_GS#Ui zC8BIiF7MQFy93>2pK?^^9iLY@Udg)pi!-2}uyXlk)_hWCVWZ;8c9=BQeO$4xSf&c{ zWlBTEOgQ2S3gi1WxIvg+L%7W{bt@PMC5^!|KK*L#;qa&B_2hagF?z*U9KPJUl4KM> z5J_law>*QS%M@A1dm#EXBtpQ%JH{e*ntrZQ=mYJ(_vh#j!QKd)e`H4j$G zi2OXlMI6R=(U@|E$>C;3Gb7i>K}*6Y8*I5ikSR>y9oytlRGtdvP&ZE*`&;Ylz$`Si z#q{ZwG{WQA!Wqi27 z;ShkFmGOOb*Ky5VXm5)?e(dV}dSKAJ!T#hWo8@4__I~ev-Em7${H2S|S-s6R4Ur3L zt`EBLwY9a0!n@rF99bOdQeE4xUR%(Y#c}3Y{)*ROR_9)qd;$M>MMdKVybD!-q}fv= zVLF3|FjR503+XZL_EtMSbsk;_j0F$4g=9r zoe}_1jr6P9H?MnGpO~mg>r%A+i*X?*X8KP#XJ0R+hWpfs3W{O^Z`SGPdwH<-xEj^` zczoxIdLr8^3;w%YRP9$S&*1(WHHg7d&)rVNH-cLWtfWy1P}y1a%P>yQ<0ak};c z1H~&YcO;>z!CW(cbe_kBcFrt{PTCR2ai-m>`ER?psJX7OCGyi?tl$xGTeV|)CE$+> z_{bw$qW3ux#4LQu0ZU{aT#THZx*k(P7Bqsr7^TmJ;;xNm$86pfX8dyecll2P5G_Lf z_@A}w_7D1wfw+mKIk(RR3e}hRG5X1jeudK7W3%GOTyx-y@;cmsw)^p;EzkbEeVvFp zaX0XeQp8~vPm(1uS&ggacXSHheK5ynQ8h`*?oy)hYu1Z*gQX0Bp@Aym`>QIgsu1S5 zqWyP1f&&Ue#d3d^JwrS(_4^CpkpTo+n!!%a9oJy%SAMLjwVW7owf!% zjeAXW_;o&A*_^~S;?pJG$=Q_~V-8_NEGbmrKk!)bz6rMf@6a&EEbRqV#+Yl+eei01 z%BM9xMMwJ-mT&i0t=MEEL_+J+Kfe_dvvedxF@l%_1B1TFa{*Yn0K(}q<)(P$;OJ_$ z|AthBfv6^Bx4icPLg}##)80)i)I?7%SxoO1Vx^9lsjxo6o%xWjsiD18thuc$Bvo$M zCET^Lfw_`3$9yn<+U-O!GOGF0B_n3``fM5$Z~&a5vuWCXN}~z_SUPU^jqJ3=VnNM@ zXolc9avOcnn5mD$wpOxK^@lp!qn`#&^^SU2mUJF*`v@_Z3x~OwN57#`_^{m`#z&^O zx)tkjpSk&Nz~drOKPa^obU3w#7I~3n-v6qgM6AZ?Z^ui0Fn^YAPGP%bK^`mGO>;lf zIMY388-wwP z&;miqC|{d_;Aw#*?jk`FdM;z(@xnlh>|neV`=pCvIm)7XIrYgChv3e0HibOM`AjXg zT&%ks(bu;L^UHfKUvpdW@{)tP8)^OD_qOX+8Y^dey*(^sYl-D{j&2aq;wjzox%m^+T^ zB(@~)B&eZQY;2tOiY-0FhIU2u{9WeUV9k9RCnr>RkU{QFnYq_fCQ1K9mJF37`IRfhf7G@;Wt=c75fv%~aiX7Wz(TFWWr zd-7E7!!(&83N?m%Fwh~nz(L1*ei5J6jnLt+Wfw8Z#d!h$4l`qcvBdAHntiCH7)0WFFUZuz|*9qLhzk!T@9EH zCH!=E)mS(M{s|59{0w)ESa>LHQpB>@s|i-ohn|O7^BH%1zbWr$RP|o{`_qLq-^ClW zhAoHC%-|HR9u>Fk%PFI>UZZy06*rbB7v=B=24BL$<7Qj6em9D(M%daXO)?u_=4B+~ zoSR=frbWUW+5A7yH&^p#l<2jO%EXv`y~XF2;_pF%pI&3|0w3(Be8{l+75C-}WV)ml z<8WF(_u8kLDo+=@ozv2WG=%W?7B_l(wPo`%?y&x~t}45os?{GJo+WTXXYtTWfH$Spt2do_ z-D^Z`2=b(N^kgrOlYc#-srCQFJ_EZ03#XQ)7{+oFaGkKlh8{dM&GaqMheX2k;NuhR zR0GQp`AmQe@BW(vgG%m3nTh~#eIAu-5Ik<19 ztAA8Ke1n^Vxi}LF*9z^!^waBw9u(@yu3moN+whZhh|4phfrxIA+_p*?`Eq{oD?d#B z5h()r44t5eJNXl#y18dfl~>kyHn+y?MSLTR^%s{d&&)OemDns9UiwQMYKvm({2M25 zZOi>C5%=#m+A2S4Og(G6S2Y>-(5gXM(jXj>BtzNL-t{!5OsRCs4day23Yes-)|F2_ zg20k&R>wDNT)PeAkM9?BA$qW#hb$%{D&t+8?I+{B`=e%R4buB|!optHUd1mK{grxD zS^1eKrePxKW(1h`!ZJZm%B*xbiz0c~ktJV0&M`SMo@Rb&{GOkU4G8QxIBB-K1&?R; zz_P8QR69(Jr&dDOL)oiSzuO2@Y$SM&VcJ@}q^H9ePieVAoG zRNaL@@CuKoUaj_zTm(GfVV?0}ymLi|FwH;7c|=M@_5A+5@>LC?a{+oy;SoxQ3F_$U zbi|P_TfcDkuTjOxE!mV$_Ci;uht75%2W2m<*GR&;+s{UjvroAd%OG7R!~7Z#lq3Bg z9cqgCA8HLX$JiupybwP~r6)*_HwUfKguJyCT-&5Run1{2zR%rT16%rRWTDd_nG(!M zh$1@SqF=J$9z63Fue-{G22~n8>u;=iEVaU$-@ZXI@sgo?wBBH{FBh(gv*a&6d6^;I L9TP?8V6FZie0q}< literal 4729 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 W+e8LOj{iw$hSM(VUl*L{1pE*7ua=+y diff --git a/energy_models/tests/jacobian_pkls/jacobian_electricity_solar_pv.pkl b/energy_models/tests/jacobian_pkls/jacobian_electricity_solar_pv.pkl index f83edc8faced7e91a6eaf65e948cc94133688d28..22bdf67d9b75588c116417067b4d5b0f320bd389 100644 GIT binary patch literal 3128 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 SVIB2MXIh*@i^74A$o~eoB%K}r literal 3189 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 X8h&w5UPj>9bwK!wDc1R1_SXLa!lk=Q diff --git a/energy_models/tests/jacobian_pkls/jacobian_electricity_solar_thermal.pkl b/energy_models/tests/jacobian_pkls/jacobian_electricity_solar_thermal.pkl index f04719a055a334739a583809e65211cdcd64cfc9..d7cfee69fca08f103e4e21db4469479c345a7a05 100644 GIT binary patch literal 3150 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 diff --git a/energy_models/tests/jacobian_pkls/jacobian_electricity_wind_off_shore.pkl b/energy_models/tests/jacobian_pkls/jacobian_electricity_wind_off_shore.pkl index 6ba36fdb6d83c86aad61db9a91f08441af00471d..40a56ff651371586f79a1e9e4170115559d3ec85 100644 GIT binary patch delta 2623 zcmV-F3c&Tg6`U0gLRx4!F+o`-Q(60N<4lnbCx3qc29z~Y007p7n6##YNmJCGO*KDK zdXG@hdYL~|Wc3HBpc;CB8X6h^2k9UH41fS+&kHItQ(|IzCa0A0YI!w0r;&kA zQ`Gf6Pf@0N5;mHT(N9QwQaqz0I06KOxJ&6wm`tyw`G+SwJUQgylbkus4jkriQKS?f|FfPoqj%0KoAP)8W{0 z7b4TnHdD}`Meaj^hV^wp5X8+Q*(n;>QnpW6f1v1tq0$@4lY1YyB0Rz}Be=7G_%Ql4 zoDTFZ53WadzvVFDnG6J%RL=DJd~=XWoP$|+urYn=g6%MG)?}HfC5^2xU>g8C zrMBKH*E*os_M%KYqXCXGw>k+tt{SLJ$CD-QuKWA5f;Gj0E9i(6L`^)&?VC;ABmmP- zf5}m8tw%ysYvXho5<9upk{QO^;SGGhaa=mx-sAmbVWgbEICKcCKxDedz1F=ZKu z%=+Y+SJyPAHzE`7dP7Sf5#!T_*F9Zy#x~)p^fZGuaX7Qne3wlo)M!8 z5tS$SDFMmw!MKP)iUBwt0l>Bcf#cv2KsX!%v56=6v*8hJlIJl{bM52PkwO9^Yc$4& zox*63S~hjRaYor*eeiWwYkpU9#H`PlqKqjDjt+2Cw=)c9Zw2C-4?C2-itT=QfB!ra z8LZIP$3g+@Cq)%C_z>TiVr3X=Eq|eq0z-!0!pe#$y8^p-$p2!<+&Oz3fR-BdP3q+Q~g-f2QUi8T#;aVguaHIZ^^V{~r++IVLBGT-ofJTED(s zCxk<>IeH6)@XC=@1MG;FJPFV00j_&UjBs#5?i#SdhhQpT!GiGy3!t&&qz1TfkR0JFf4y=XCo+=B z+fjQ1)IB0U(Ozssc+~S_ep9LiL&`xgb;6qjspZWTFDqP!B5 z^VPnGzr2@%3|p&<(ctpx1zYZLq0*PR0|v&&2;$}%Bb5eFkSe}K)CG@zDMNJ@PIve0 z_Zw;(_r=VOmnmiO`~LV{!w6XdOX5t-gfZvO?ZJ z1X!X5@*%%=;?049$&6lNc$E}^GlCdZ5E1=>OvZ^}CRjPA9CH3$Fj}n5R9ZRA`^rt2 zqWpQ5Vb59zuT`3R16$T4XwOb9Bgj?ko;YwnQwpJPcEAjie_VtBC}M*z+Q-h^45OxX zV!O29#&1Kr%pWE7ta7WdP{=LDUS9N*IkD!ROv-Dbi0& z-I@9+vkF1=9qY~HRbBQf`_jK&=QGT@V~X?H?5X=a@8`qh&#G|CX$-3m7gOZ%?nG<0 z%!tuW{qF0fe}D&Hy%;lU0V&G9zE3_|&9P7uWf{91Xkimrd5Y{YjFC1^NS=b^AWC|c#ThHkX}LW~yU2=9TBYLrMhN~dGbH~78`tw}QylFjXJVmy>9TEiKyW#fv>D*w26RD*D zcB#Nc`q~TrX@mJ@z?U8&xCRs)Nd5DS<2AVEN?lZCG+>SiC1ZJQ_6IQ*e|JT}c5m*F z@Qv9hAvO1jQ#F^R)O4^O-K1votE>>H=Oad&c?Fb<6-Gvt6S_T-QodSsX?*ZxZY%(2%LP#t*G&Gq;Smn9u{2tybBg}=D_IMsGHnk+<-P`Ee zfAzZBCg#i>6F)O;i$NtpCaLt#bO{&YDlypXqfcyerOV4g37}=pZq@=5;v?kVz%M06 zN9XEc^zdN=8KJ?D0f2a5sq}TD!P8*Db9&I|WvI0?k7SzpE9wX$A_QuJ0v+mxQUPVs hJ%kF4D4G*biFm*p`116iGC$((NT&)C1s`qPX@JU;y-@%F delta 2692 zcmV-~3VZdO6}}Y?LRx4!F+o`-Q(4Ekfu4~LCx3kS00OmBumA^k7dwoSp`@v#+MlXv z>7fHAX*Qt9X`s^+OoLBC8UgBPG5|6LOhX7X8V9J*27np^Lq;Kp^$h@dO*ArO+KoL; zl+$H4pu&2FMn*u=i7`Mtlgel`2dEyP1Ju*h&;WWTq#IMz7}Od7F&Sj^*pEO1fC`U zO&BTZgw*hAo~Nn$rk|=drrIg$Y5FEp(2eSclqfOa1OX5tNhj$F5QQuv#6qI5Fe?#3 zMNq0hP^wfaDv26 zGV61lrPRU%5TF$UYs_ezGi5dQGFO!6L<-7zR6zj(LQ(8*^tC4!t|@<~(QHdPMDR?{ zwS5eNKwV{?zSlSD4nZDPk??f;AMF_-1HXG)n8bj${AYTWyL4=bWCEP^48crVKwfyj zpCkp_m+)2rFD7_*u4^J3U1$1wDrUF}ij3D*NrE`(=5X+NrJBQLU5T>QS;+V96CG#| z=Mn+#UQ%t5b0a4-Q^0?^a5|zgaH{aJb0Ca)ks>21D1JX=g)kPX6VbC<;-rn^Xm;@K<7AEjL3&f$u z7#$!vG#}x@4r6cG9g|1Wu+azRKey2YjEC@0{k=wHBenO;kaT|uW1e&e=OEoj{URgf z5llJEms(e4i&ch%wJKw;l0|@zIwHA;npOPLrF$ z-yQ{w&K+CDzR0Tqz5U2*+$w=6r&qTu z*FY}r^B7CG-sB|dUcQKGcrbnq;@BR*I{I_P$Ufh-rP@a!0tZE+!q;>JIYt@ZZ7Yt=Q$n;$EejdQfs6pqt7f(y z&5k#_sBAdQ>qI0g7coIJgz)r-@Hd$V4@vwA0wURqJ8YBVau=02qIPgpfQX z4n}EeMx<-9*J*$Vkb`FQ;0?UxW)ImrWj%Pgq}6}?B-_5Xeu^EVn}W574gEWc&It8z z@s=VOog!zAsZ-`?3w@kK4%yVwPhHHZ0M`)^cELwc(>$C*OU*-8LLoN#NE7!W8k!!Lyl2dz zdfsNRB2zo#caOV$_BX&-2qdqVXv`~NtYrD>P`n4g`p*FA-fm3j4`rYjlsy;2hkusC zJw1?IhqhrqFvy~l0qJl|iYVmkB8lNNV6%T;#)Bm(YYP&#hxyekw8=;naKnOE4@?Pb z5s~z#&k+uCWO(8WqsvtJAf(5;x(iuAaZ9)JAk0d~0ccMhObV!HlmO!mjxk0o3eit& zVF_u5jg&>ss#KODxIZnQ_YftOLkOs3pV-4XPIzN27Fg0)yOX%FLOqHjrHact2hrT`*7Pe|+z zP*m@208Jbt(}TSSEW~moQ*@Aog=2_icIlyz9983`VsJrIC0xouy8T5C2kU>%c3&F! z0hhvuP+qctFWMAS&mJZzn@i2$Aq28oe3c)pgcvgxgcSbkQ?)E&Kr%pW_KSrlwRmORxiPF2hCZ|ovOB4qHaHBM`*eN}R z2*pPmSG32xq(! z7!ur1@$W$A{9ZIL$c!dUmpdLAh9bZ?qLf{j2Sa$FlkigLKXriL%@zxBL~Jbs>g|0W zFzn(CdwCDTR?|{uZUayGYc>VU2Oz?Z7#tiJBgPmZL>Yv3tpOmANRmXL{F4zTFhl`| z1p{e6`|5i6t?vf!t1*8CY0sy+4@v4aF_;Ri&kT2SBX#JF__0(MI`c7MWW60fRPRzB zDRZ9n^I%$VC4bl9)j-D#x*CuRb(a+z>TDNg?N&lK2ZLe^6rz#V=2C?1Vruj1o`nZ>hU>=pY-rk^NZ2tS&kb;)m83I(6a?k&f&Tg8 zksn|SP~F-jB18mNiC?Q;6nO~~yIF+PJbnzrdSii10|`=Rya%@MziMls+2)UsqgWg- yY`B2}A|NA71OX1!YC0Ju`%@JVWU>3;#t5M?B5I()uvZM9{9VZu;X*?m<_385_rxjy diff --git a/energy_models/tests/jacobian_pkls/jacobian_electricity_wind_on_shore.pkl b/energy_models/tests/jacobian_pkls/jacobian_electricity_wind_on_shore.pkl index dd4519c7d355a7182cb5d9847707327ab2d451e1..f835efdd0c91094361a830c533c0e90259751143 100644 GIT binary patch literal 2711 zcmajYc|6k(0|)T$Y-S`T!|XxBD5)8ST(bj1W@`NiKUc`PB37(q&RT9IXL8QbjYvsz zU%7RXka89&rW`%5=lMO)@4x5ueE#{oKYxA5&VGj4Bqw!-(~qo$Qoyt=>i6II@Nez> zz5jzr*q<8!NO`Uh@UH^(*pF7Rl}jk|LYA*M6q-Kx4zSCMQ1mXb zaN7|ygO6F~(QPgz<4aEB3rIWwg!jk4)h`@18SQ>Vr&|{@P0~wjQ4BVtyRiEaUQbeE zh8=JccEW=w4#u7OP(PmtZ#$-;1|dsG1P5beEg&(XI>H$cGt99xwYCNXrGTW{tR^qP zg*${BQT(h4Atp==7zu~)t;K?nLPwrJh0Hhj)?S@45Ihhq#m^vH?fwqh+=Fb(fC!n9 zIMK_$I`na5B;ufziZ(__D*D#8!;9-XJsJ==iSw}7bJqk*4i&`t!LNKCsP zh>+8xLJ+DsFEsl}E17Xae1p#$Sv45w^2Z;o>543jQeX0UQ47s-h3BcqgebyGBMK#^ ztohK7fP0HJx6nQV?0puNlA)vOy9#W!+g&mbElV5<>mB^?UNCjRYjVub?(_Z5_kLUr zlcKK7I1e!{A;RBEY}b-%wQedlS-uQ4R&M(YrY_URH1%7Lnf3zaI;;ltPgT=j{(c&zWn z^#)Jdmzka)^vx~E0gqp7-YsAp4Tx4qT8t4hA<=3jIqo>&Owq;VL%&XU-Kg`vd~J=2 zOE?wG8~qAmxhlQsh4sFa-v4@|>B{c+HG3S6t=k1asN!IDOR0z4K~-X{nyEcx_I|}} zhsNl4{oLyPA$-L}H_5gGb7L2AGKUM%HxC>0XNC{(!wzc^pbCa?uWmSY&P;f`nyXz3 zExkN7^$OJWPzRs1WZQz+myR4rgGyxlk}EYYvMaG45X*Q=~X>V|-Whx{_F+AP(oAlDNkK4QRpK|7mwJv8jU5NqreVl$I zDJ%jA?&af7cxshK=sta3{ErNx&P+BJ>0hTZIpr0l2IgPeAd5A`xfHry3Y9q3m=e4* zgrWF@1?r!zsMTGu^<4L>#zRek=ln%_MyZF&~WTLGo_i&5VGWlhoOffvTH}d9M+xG}V7&pk+1c6NudxCTH>yT-_V zCaA40TPhbz#I)+mH`QOUo-m_#7nK;x-`L`Ri|BPqG5Ve--C}I~O5BgTymAh320;f_ z54~{~8PwR79SEv@gM)T-3B7xkyed5;rU`d?e9@IXB)}Tk*;tY|`^_8|zc&W9&k(pI z*{d0tn$4q!&(3BR$h{i`XJ|w*{YE9qTw>v}zQcKF326_5c0DEAp5awlfsIdse0*#H zBk$L4#;QP<&^!t{*E3}0Vt2nm zXl#=8WGrQ3)!_&!;;9jId`Jj~mYK-lhsW|3{0l2j3}v4I#DtSNJtTQsu78)#Oi`nYlFdKAqOgC`Yu=ue9_{JL_6y!>H0Lnz4Y^iT0^oQBi3TN5YZh|bBRM9VnJ)n zgwwgTaREyfzPb$>ppkbTs2l6J-;$3y^9epq`CT?F0y#zJb6B#tAaMHdsnbb)a>bm= zTLngH-gK{9U$2!l_Q{6s_e*-^@L9LeC86M_FFg}!f(AB}L=9jlC1>}Xd+|YG(nuG>4Sca4h?7*G}!g zj;nvYMaYZ4_SO*_64=wOkj46A)5rrkTfNxQaHimD&>pornx2e6*X=M<9}ej;3QjT^ zvvM>z6REiveCF3X`%afsxlgr)?R__^<5ukuGr8~#k`<14x#C-J1ajHnZj~ynhZuBv Wlazb>ff#kmwHq--rVP7RrTr5@KF*u~ literal 2735 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 B-k$&f diff --git a/energy_models/tests/jacobian_pkls/jacobian_methane_fossil_gas.pkl b/energy_models/tests/jacobian_pkls/jacobian_methane_fossil_gas.pkl index d6ad649f628cf129c3dccde73615a53360ee01c2..878dbe6bb7fc56be28bce491a8c3e0e74bd0c8af 100644 GIT binary patch literal 2396 zcmV-i38VHxT4*^jL0KkKS*UV}HUMrzfB*mg|NsC0|NsC0|NsC0|NnRY{`dd?|KI=b z|NsB~|Nr0z|9Jog?04UGiS{?y(06KeP2JtxGNn&FMH@(Zo=lPIdYXEShpCVQO#lp! zQJ@154FF;Q8U~trjQ|>GdV|!^4Kx9xMnJ-Po}kD8JwwzUpa1|cgC!cNw1Cr1l=6m; zL^3o3G>rfn8V9NA4FEI%dVtB04@3X}13(%8Z9o73Xc_?1O#lI=f$9JNbRX)V00009 zfB+@{022TJ001KZ00d%TF)#^$00004FaRbaOiU9Ri~tZdRX_bTRECWMKxhCB0gwOy z0Mks305WI*000000Lh_~Mu2E!0A$F~rXbTHrh%XU000AOk|;K#O&T=Grqsd!83RBx z14B&#pa1{>0004?G#U*6(;zYc01W^DGz~NW05oW50wpAw8Z|N+4GHRpsp@`{)Sgq* zC#rfHnHqYYko7ZDBSxDkr>H!m(mhX2BS)kf8!4kko~MccdYL^&njTZhih7<%^qVQB zo~E9V4GkWnJw{OVG#Y4nk16`3UU&YTfB-5!bb<(o`D>~bfUHoW6c`&oKrlgs$%7Dp zAc7f!2qI8oN??gWi6k*#NoHWcuq2eh5`z**Ab?DgMnXV{!Gr=v5X)8ste^}O&Qwqf zXedZm#>y6O{k261HcJHoU?c(%Q8W-GB%w-Cl%UXrAVLs?APFxT2(8f|K!6bdE)oR- zSb{CEBvQ;p$wU#lB8Xaw2p3Qh62TA_)x9nO5(tRUf&dqYAfUG~00a=T6h$hKs?9xik@Nf;LIrb4@1OrxB3i1PR zKvK zFFe%)tNp#(224x@l1U_zNhFfvSsk+TN<`Vx;mpEaI@lle@?Yzq#o?wp_G!j8i~5!D z{U#VAVKuq_G12-`t%FR&CSg?uaYgt4% z1VvB*7)X3mJfVk^z)&?--bPPZ()jr7 zKTmokTn$CZj@*bH2qAvG*Koi-%TO#RH5kis|9MB1JNRf62=)E~rNMGsj?F;Dn7!z0 z@_fON@RSY3fI{s4$_uob5Tu~-oc@e7ECY9z`3DF^@qq?`F%I#!PT+31w%LNMruvoET#fuf}ZNvx& z8niOzGEs3D(qqC=pptvAU*g|h2q0Xoi#qn+( znS-x66)mxh#)(48)X^M+2+F|hvQ)KbGa5UR?8#Ebi#n;~pcF(B7a@I8nidDa216JE zKnu{<)%Qd16D+$t``Cya$M=X*FZj!!RX`5PCWk}X)vKQ-Z{c@m8)uW4*I=fLajEeX z<@U;W9eMA_bmuf}&sKM*JamSz{lF%=AQnichwDHAQTPCybK)IIfNRb%#xIU3us8Q8 zkch3+0Z6JbBC04kUqzeF+XEyB+xqEzWdg#1t>}!6?r;wc5=<4%mCI)r$V$hL8~GJ$PxjcH#sO zH~U6i^HbsJTFMbw%8>q!dN6v5bhqVOez1E}^n7qy8d3aW1}!M>?Ex$wlZJ=ae<&RK zRCM}G6$7`%aCBVX3_tWW5Gevl2t_p77Pq%0?(S7?*`X1 zOGN1;fgvDB$s#+R=d;-5R1bHg<@#-En!SHr(dxxK;((q6iM}fk_rqz%j<&+bHkx?6 zkfBsI{vOKB%@9-2Soj?_@N?y_Knzq-6gF$hDH0-lA+IfqBVFz-EP(Ovd-x0YDn zv>73{cMR$Zx+VGyf|PXZ6P70~T)sYgW?(hQWK-<4zQS`F2_{E7(Q*k6K6qO5H5Y=vyzc5@dkV?O_S+WeP$<1t|$5 z6OFyMOeh#oaIIlsHJ%WR)&=xDxU2>?JC%x4YZR0Mha+OB-}5_6BT4Ag)n;Qnkdowp z9~uR`jETE~*3Rh;$|gh1+g=GOBHh5ZhR6S#NX-*KAOwe2R_@=IBbW%tQpsJ303ZqB O{}*yaI8czNa)~y-IY-z4 literal 2392 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 rwOoIV_o=ki!lhdp}~IuYwL2i literal 2551 zcmaiyc|6mPAICqNxv9;SM%tLG#4yP{+uUb{YB@8@_`1LPQm!#aq1GlxB01(r$=6ln zR*s^%MNzukM~)QbtMBje`~CZSzTfZH`}phqemvedl8=tM@i_(JIgN#fJ3#--z28jl zhx>2&FTdRPR`zXgI_BrzJAR`G9H2T8UZcxlaNw8T!Z`>40E7V!-~#}ld;pM)-R}T^ z3%f7>4t}R%d=sXyLMo;cGmvb6!0?DF#0q0L z81xPMAyex#44h1E$8d(Q9B$%9>Ex!v4Rb2Ja|jP(^R<&ZyLlASFw|HmhQpJ}*M`A> z3L#XMD2&BFCz-|9iRm6-%!?QWt;Y&Nmc1h4}%h~#cR%@-6M@!I)UdD!jTUui`x_tFz%HcqPW5QDC%Wsp+;PJ zyt_<)Qg05L&m%bU9$3;{8CGH#pYV8 zGtyE?@tIlUOtnXnAlX=mU~-3I%qLwVm+~w;*EQ+c5c%xre?Pb;6i3*A$2SG4PFlwX+m-Z}zh z_C)p0PRbW7FP^VSa1h5mw`v;*t#MEQeT%KqcbbEXuGHVxP^+#c3W(~x2nP_79pR{# zOJ#GrUp8i(zz~Vi(a~YXs-u+J4CRY*aqg3>s26>volHw5V<^So5#Cz<;a(FhB;DJm z^ul&(bWYwkR}Z&8Mgv2RMB)`Pg*$>H_Aw9$A7Akb7gaO}(y-I5topT;@m#TKG4bN= z+c*iCz1|W6*Nd&v17oKVGvD31j3&rU53om%J1Qg2pP>FItiCCWl#`X!>3*)$Bce8t zX`brk$$3?uc_uI@ZvX%H;olF&ztz5Q-Kg*c$vRdrvO4L6|6m(!;w^q>S5T3nu9x1N z{!P%SMXvm~f+Y2O^=5r;NmiIj^aGh}cd2coz^VGl<&E_FOWOLbG2pAexYbs^`ntq@ z1O*v~crD*w&SS28@GE_c3GjHxIdoPJVY|Gg0dnZcWwNqkxVMJp@t`eIDFV8?uvk{^%bUtMqrucdwxfyyBPJ7-(tFF6rRjkJhIV#{rV%;$>&T zqd%gwLT#KTwTQ1;^3q|vvMSDif>9wvpBgfmCt595U7Xi8Zhef#evkSq&%LQpZW`5! zNoJ-?2Di91)=-{AUo@t7_ilP;JRpT1V8y>t>G?>FP);<2n#=+Y+iD~|z4;%=ww>@Ccr!28qRkC+ z1HDOw;ao}(A?dYO@{Eg8ozUysH*jfg4u#I30LtfhG<1W^WRKmnhm37 zDbC*P{^N;rV}h3DZsUa%FS7lqeqo6j*xkI%w96S!$B~vFx}J~oAn3?27c?vl3q9(L z|9lf5Wc{M8&%t;T;%I){F~0Q)rKVSys7JvPQEQXu%C;kF#_WxK_I(692q>`^9mFJ{&fEmCN(jyIAffNp=k3?Iuh(+h7P31G1-4gL zkPwyt{sI`pjk{-G>Ud0CAOUypJ~#DQsuI)FN+5v|D*y_XL9+wS?DTe}Tp8OY^)>rm zuM@Lfsp$}kSCS-K5*k1XeEhWTb~TWR?^FW@2Mtt^)7c`)tCfTH*hUJufLOh zwLuB<*0m(Et$PD>2O>+Iia^G^{IKS7)~$~6ZIS%dHE`imN3WwS9SRQhNq#g z7R78%Nhj@=dnzxi>@jZcOC;H3n)Rf4_45*LU)fu60Oz4DW9#Tmr$ zKex9x%~oa*Q+fh(Eez_kRsAg(Dg^_0>BE-Qkx&JzB-hoeS)AWm!f6RvfbinfR^7l91WB9NJZw3jbF$58XJ zT8Q5`jD$vc3;%(UkKewJMg7`)pQGs#@WVB$_^58@)fz+gn5bcKuml)U{`Qlu ka7K%N<7=>J+W~Xv2uZC4Jbl!}XuFZA+)k%uy8bo(4@BU6M*si- diff --git a/energy_models/tests/jacobian_pkls/jacobian_ratio_Nuclear.pkl b/energy_models/tests/jacobian_pkls/jacobian_ratio_Nuclear.pkl index 55744edc5fce73a070e6b83b63be5d4da21b0566..54c7183b68696e37cd51bb14aaf23bb12df7cb6b 100644 GIT binary patch literal 2852 zcmY+6dpy(oAIHC2+stLm{hBsSF+w{dlA2+LiJ5Z03^BKe6rx6Q2^k%^=9XM0om`XL z@7JOs#X~2##fiuzgrCm&>-T(oKCjRF{d~V4AEGTo>$I-T37XAtc6|o$N$}3W|FLy2 zf7oyR=RO^Bu=h9jCjhXreI?mj{^;A|0VuAr{&moMqA><~u4Gk`?MiDiqxEEl7?$8S zOKbk1QL@OeoaC?gHOf1_B$}51wJ1SPxQF0MT~y0-rI{Jk=$O$=OKWfpI)38Rf(#&B#a5SoyIkb>P6CS^!57asvBj9s`d9 z`5nQ&hZL=v^;0)Q0R6&m$u`gd@jwix+e9=L*+3G&3seBU2)#a6?K5zH#o! zhyYE`3-Dwy7+i&(aYYfaLxkXS{JVYmHONbrL0q9sM2xaIm6{SMDybl0Tx4u*=~kGh z)H!~-d=VLyrlvBkv1A zIFU$1MxnWptYlV-9^9f7dDzpzd?+J9jI-zUnWvD`Dkf0{M#mF~U42Dtc45kJg@~}V zIlC>LKtQ9>a0vX02sV+))CDy{d0m9KK`0b-P}d|0V(w%OpQp= z)klK`F)TPzq7~kX0pFR9C?I3*!&#AfPGsgK6dufVnof};m-NU*MaYq(SW$4DRC6Qv z5fmDQ#4(Ds2kxe6jt9z9CsW6x;zYoGWKaec6eopPU%IDOx>P@zU%}t<5dp`tH7nL9 z&$E+nOA5DRoKv1;8=dR@S~WN@ZhG5Z@$6*c zm-r!f$>poYOQZSKUypv-J|ngfjI+1UKEo}2Lq)i@jSk`M3x7BgiK{ zQD5F8#edoUlarYtsgdQIH1lD?w?9qAUjJW6$t#;7RoLpA$u~su>eK0FJx^7hCNoQ! z_UB&uxAN_rgv{OP7yEo~jq_G$`zA^WV}F#ckscPP_Q8>V!WD6FXJ- zqi*z!MA+`^>f3+~zwEzEIJW}YE~CIZ`n-Jd*IFce;zb0mczSzEm=6sFX+I4kp%vw|CoXPBFQe$y>^Mj2xUF3>%P>So;KZdJS1nz0p+c#l-P zr*hbVjTpwM41klRw_L7Lc>OZIKLp)=wj1Ma?5HVR8?My*o(HdQLEq{3Z|6IkcwpK? zQi?0XOEkK34_U*ibf1eqF2 zxMjjsL~Fj0HW>Te%*&V39^n1Py52!^siT3osw%V^+~BAx9^d(H>n-36Z0dambK@;> zu|u5RCn2+&wx8X%r@VwR7?5|)X450xhAR_mszJh05*AGQ&b3~FfLJt2h-SQw`lTw? z2`P|(3kgFX{t_Y5H#r||xfi^QRA(@wqHU4qy^jvi<<-W#uvMpIj{+f}0t5fs?Hqh; z^^I(Tj1MdqGvJ-{(vXiM(#1PF*)|y$_OAMq%ggn*iB=6|bS=u>MX9>r-tI3-1=I54 z6qyuI5}@+);jw)1vRT7BCmdff=6l&@lPY5Gtpd46*ffR(zw^dH$;^Xuj)nrob%FMrFW^{KL1G1+ zZe}cBE+X75iJfy4w}K$7?(`uBCjq9QK0RfCV`v1w>s`}JxpjAz|>o7 zjX@exmHsewB`MxC%`e&cY1QP8TcT^z9G+LNEIAKdm$D7vpv#7^mZ>LrU};xs-`F(G z)>1Y;s=2~qs$^&v)FPQaXOCj4g;P_mER%Jg%|4T-i_3ad@l*Tnib=96S6ECMb+2eG;8^$ zz?78NHO9y>kK8&ga1v9=Ey16Jwxfiy{OUb_+K=qiJyK5yjh2U6!WiZGStobWeahr? zQ8vv1q9nO;6L_elhb%ZGn`0QEuxYf{ccBt~Z4VN+RONO1J;)-|r})}T*4YhuGKadl zC*ZQnAj(`zM)P-MY3D43k1E9t>mAd0^+rFXpOyoOb6i%>{KX~dd~2<&3G;!33vv#L z!CWnw=&1iW5|z!{Jm)tVvQ^oO=~KA{eU`QS8Pr5By1ne`ePd48<%iS&l$rc~zU)}n zt)p>nv;GsL%qyx3&1Ri9+2k^u1ht)JPYz{gnlP`*3@E$5fg#Q6+_WVZx literal 2894 zcmY+Gc|6mPAICpqn>of@nK{~KjwHv-6~4{PF`8Sc97|{@xyz_#Mu^OPl_rwZ7bzu0 zmXRan3|*umT2mq^$FCm0-#@?S1(m}d~k<9@u&jWEqM$jRqj)5vrljm}b#|@SWWLu5UdElH&LzNsK@^I?1OTyGIsl*u zf9#(H{tGIUO8}tRU=l@2ivThdXVLb`i`QtCQ1SR6KCP5+7%9u`IgAhL;qwUmAvVv* z5TtAb)eukTp=eYBy(hE=rs7o)rcNGOU@#w}=yW;(ZcPPH&}1$EfVcqaPYnJ~tSAux z;85n@17MwO-Ar7=t~Fh|GiGg?c71`7mgBhLz7SfOR#qxHy5YHEOGSa2rG^L_Zt9i4 z%90o8GHyU1baVwz9~u2qW69=PEgeMJn@eFt{VQ z0{}Z0WR>LN0zSHG$!ge4Zk~9!<6I;@4fB)Q zG03W`IuB1882iLnst;71v!SVHMy&mwmOkek|DLD7Z`(TkT=+XlYr8G)F>rDt?bY!2 zw)zia;%(@d+$aE|h9pxSxP9q66ue(`&LWxDag%}y+&#i{p6t9^gGH9CdSDxzRG%&m zSmwOeu4v#6@n5_GfZP`zc0T>6t{Wm?YXy?SWQD9Vlnn`xxs1!B z{GH1_j^~$AyU}#4O~`Icn;ny&CsLw@*6`W_wM0fv4YrK+ek0=B}}FlKNAW#erQW z6X(wc>sc_tOH!MS-}4@amJoWuWiI_-2yggwFH zEE)p7#0T5OZ|#&g&b2uQ>Op|QK8L(ig<;(;$^(qjLWJdUt6O*%r@$kz}h79MyH{Na6K79vwc7aelRGV(BH!N(P;`Rl6%&&mf?XlC!I+?Xs4z5t9s73i_s}%IG)lirtd8b8xdE5 z`*4Y@hKPjy=|%PH)uJ=SAj84il#3xvca^@#!BQ^vHgR0P^{a$Amez^+bp*CoKn@Ak zPrqW9ms{3`>b}*a&~mXAo0v!M?(olYfl5OojoGSB=DPbhuymPAoLNF<&3F#n zGa+eTILyVKay&qwyL%mHo*z4ycuQVU#6j#ujPeV!uj^+E^2-iSq?0Dlt!6msT8fk{ zQYHe@T@*poxq=Nk*0}XV9RssxHm%_I>ri5g)>_S4=NC!O&t}VM2h>?B1!wdxczAp9 zn#1g-`$G=RFD5GcxQ57im0o&S_e4r6ZSj+1yH`-K*3xqQj$Qdm=D3*F(}}GH724t- zC~}BM9>p#hB_U_>$lwRqE2%uVAnJ=qYqs8RTq7O0i;~`H;-{&le#3auD1Mm-r`N^?UnK9BshxaawiTG&*_{_I=s6J z|8t{TUl{#%B<6-xj~1geo^_OZ=l!E8jgZR;%L40r0c`@)RT~BH?gn=?kF?|UT`wbn mu91N41|I)T#+ Date: Tue, 22 Oct 2024 11:56:32 +0200 Subject: [PATCH 03/19] feat: fitting script of fossilsimpletechno parameters to reach historical data energy price --- .../fitting/fossil_energy_simple_techno.py | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 data_energy/fitting/fossil_energy_simple_techno.py diff --git a/data_energy/fitting/fossil_energy_simple_techno.py b/data_energy/fitting/fossil_energy_simple_techno.py new file mode 100644 index 00000000..2583b44f --- /dev/null +++ b/data_energy/fitting/fossil_energy_simple_techno.py @@ -0,0 +1,138 @@ +import numpy as np +import pandas as pd +from scipy.optimize import minimize +from sostrades_core.execution_engine.execution_engine import ExecutionEngine +from sostrades_core.tools.post_processing.charts.two_axes_instanciated_chart import ( + InstanciatedSeries, + TwoAxesInstanciatedChart, +) + +from energy_models.database_witness_energy import DatabaseWitnessEnergy +from energy_models.glossaryenergy import GlossaryEnergy +from energy_models.models.fossil.fossil_simple_techno.fossil_simple_techno_disc import ( + FossilSimpleTechnoDiscipline, +) + +construction_delay = GlossaryEnergy.TechnoConstructionDelayDict[GlossaryEnergy.FossilSimpleTechno] +df_invest_historic, heavy_collected_data = DatabaseWitnessEnergy.get_techno_invest_before_year_start(techno_name=GlossaryEnergy.FossilSimpleTechno, + year_start=2023, construction_delay=construction_delay) +df_prod_historic = DatabaseWitnessEnergy.get_techno_prod(techno_name=GlossaryEnergy.FossilSimpleTechno, year=2020)[1].value +#ToDo: with or without tax? +ref_price_2023 = 35. # $/MWh Source: chatgpt average of 30-40$/MWh +# data to run techno +year_start_fitting = int(max(df_invest_historic['years'].min() + construction_delay, df_prod_historic['years'].min(), 2020)) +year_end_fitting = int(min(df_invest_historic['years'].max(), df_prod_historic['years'].max())) + +prod_values_historic = df_prod_historic.loc[(df_prod_historic['years'] >= year_start_fitting) & (df_prod_historic['years'] <= year_end_fitting)]['production'].values +years_fitting = list(np.arange(year_start_fitting, year_end_fitting + 1)) +invest_df = df_invest_historic.loc[(df_invest_historic['years'] >= year_start_fitting) & (df_invest_historic['years'] <= year_end_fitting)] +margin = pd.DataFrame({GlossaryEnergy.Years: years_fitting, GlossaryEnergy.MarginValue: 110}) +transport = pd.DataFrame({GlossaryEnergy.Years: years_fitting, 'transport': np.zeros(len(years_fitting))}) +co2_taxes = pd.DataFrame({GlossaryEnergy.Years: years_fitting, GlossaryEnergy.CO2Tax: np.linspace(14., 40., len(years_fitting))}) +stream_prices = pd.DataFrame({GlossaryEnergy.Years: years_fitting}) +resources_price = pd.DataFrame({GlossaryEnergy.Years: years_fitting}) +techno_dict_default = FossilSimpleTechnoDiscipline.techno_infos_dict_default + +name = 'Test' +model_name = GlossaryEnergy.FossilSimpleTechno +ee = ExecutionEngine(name) +ns_dict = {'ns_public': name, + 'ns_energy': name, + 'ns_energy_study': f'{name}', + 'ns_fossil': name, + 'ns_resource': name} +ee.ns_manager.add_ns_def(ns_dict) + +mod_path = 'energy_models.models.fossil.fossil_simple_techno.fossil_simple_techno_disc.FossilSimpleTechnoDiscipline' +builder = ee.factory.get_builder_from_module( + model_name, mod_path) + +ee.factory.set_builders_to_coupling_builder(builder) + +ee.configure() +ee.display_treeview_nodes() + + + +def run_model(x: list, year_end: int = year_end_fitting): + techno_dict_default["Capex_init"] = x[0] + init_age_distrib_factor = x[1] + #techno_dict_default["learning_rate"] = x[2] + techno_dict_default["Opex_percentage"] = x[3] + techno_dict_default["WACC"] = x[4] + + inputs_dict = { + f'{name}.{GlossaryEnergy.YearStart}': year_start_fitting, + f'{name}.{GlossaryEnergy.YearEnd}': year_end, + f'{name}.{GlossaryEnergy.StreamPricesValue}': stream_prices, + f'{name}.{GlossaryEnergy.StreamsCO2EmissionsValue}': pd.DataFrame({GlossaryEnergy.Years: years_fitting}), + f'{name}.{model_name}.{GlossaryEnergy.InvestLevelValue}': invest_df, + f'{name}.{GlossaryEnergy.TransportMarginValue}': margin, + f'{name}.{GlossaryEnergy.CO2TaxesValue}': co2_taxes, + f'{name}.{GlossaryEnergy.TransportCostValue}': transport, + f'{name}.{GlossaryEnergy.ResourcesPriceValue}': resources_price, + f'{name}.{model_name}.{GlossaryEnergy.MarginValue}': margin, + f'{name}.{model_name}.{GlossaryEnergy.InitialPlantsAgeDistribFactor}': init_age_distrib_factor, + f'{name}.{model_name}.techno_infos_dict': techno_dict_default, + } + + ee.load_study_from_input_dict(inputs_dict) + + ee.execute() + + prod_df = ee.dm.get_value(ee.dm.get_all_namespaces_from_var_name(GlossaryEnergy.TechnoProductionValue)[0]) + prod_values_model = prod_df[f"{GlossaryEnergy.clean_energy} (TWh)"].values * 1000 + + price_df = ee.dm.get_value(ee.dm.get_all_namespaces_from_var_name(GlossaryEnergy.TechnoPricesValue)[0]) + + price_model_values = float((price_df.loc[price_df[GlossaryEnergy.Years] == 2023, f"{GlossaryEnergy.FossilSimpleTechno}_wotaxes"]).values) + return prod_values_model, price_model_values + + +def fitting_renewable(x: list): + prod_values_model, price_model_values = run_model(x) + return (((prod_values_model - prod_values_historic)) ** 2).mean() + (price_model_values - ref_price_2023) ** 2 + + +# Initial guess for the variables +x0 = np.array([250., 1., 0.0, 0.2, 0.1]) +#x0 = np.array([743.8, 1.3, 0.06, 0.0, 0.06]) + +bounds = [(0, 10000), (0, 3.0), (0.01, 0.95), (0.001, 0.99), (0.0001, 0.3)] + +# Use minimize to find the minimum of the function +result = minimize(fitting_renewable, x0, bounds=bounds) + +prod_values_model, price_model_values = run_model(result.x) + +# Print the result +#print("Optimal solution:", result.x) +print("Function value at the optimum:", result.fun) + + +new_chart = TwoAxesInstanciatedChart('years', 'production (TWh)', + chart_name='Production : model vs historic') + + +serie = InstanciatedSeries(years_fitting, prod_values_model, 'model', 'lines') +new_chart.series.append(serie) + +serie = InstanciatedSeries(years_fitting, prod_values_historic, 'historic', 'lines') +new_chart.series.append(serie) + +new_chart.to_plotly().show() + +parameters = ["capex_init", "init_age_distrib_factor", "learning_rate", "opex_percentage", "wacc"] +opt_values = dict(zip(parameters, np.round(result.x, 2))) +for key, val in opt_values.items(): + print("Optimal", key, ":", val) + +capex_init, init_age_distrib_factor, learning_rate, opex_percentage, wacc = result.x + +disc = ee.dm.get_disciplines_with_name( + f'{name}.{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() + pass \ No newline at end of file From a5da1715677ae092765b02bf603387ad939241c5 Mon Sep 17 00:00:00 2001 From: perrotcap Date: Tue, 22 Oct 2024 12:29:05 +0200 Subject: [PATCH 04/19] fixed some gradients --- energy_models/core/energy_mix/energy_mix_disc.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/energy_models/core/energy_mix/energy_mix_disc.py b/energy_models/core/energy_mix/energy_mix_disc.py index d2e81054..3284e866 100644 --- a/energy_models/core/energy_mix/energy_mix_disc.py +++ b/energy_models/core/energy_mix/energy_mix_disc.py @@ -543,7 +543,7 @@ def compute_sos_jacobian(self): ns_stream = self.get_ns_stream(stream) if stream in energies: - loss_percentage = inputs_dict[f'{ns_stream}.losses_percentage'] / 100.0 + loss_percentage = 0#inputs_dict[f'{ns_stream}.losses_percentage'] / 100.0 # fixme : loss percentage by energy was considered in gradient but not in compute method so for the moment its also disabled in gradient to fix gradients # To model raw to net percentage for witness coarse energies if stream in self.energy_model.raw_tonet_dict: loss_percentage += (1.0 - @@ -557,8 +557,7 @@ def compute_sos_jacobian(self): dtotal_prod_denergy_prod, inputs_dict['alpha'], outputs_dict[GlossaryEnergy.EnergyProductionValue], years) self.set_partial_derivative_for_other_types( - (GlossaryEnergy.EnergyProductionValue, - GlossaryEnergy.TotalProductionValue), + (GlossaryEnergy.EnergyProductionValue, GlossaryEnergy.TotalProductionValue), (f'{ns_stream}.{GlossaryEnergy.EnergyProductionValue}', stream), dtotal_prod_denergy_prod) target_production_constraint_ref = inputs_dict[GlossaryEnergy.TargetProductionConstraintRefValue] @@ -567,8 +566,7 @@ def compute_sos_jacobian(self): (f'{ns_stream}.{GlossaryEnergy.EnergyProductionValue}', stream), - dtotal_prod_denergy_prod * 1e3 / target_production_constraint_ref) self.set_partial_derivative_for_other_types( - (GlossaryEnergy.StreamProductionDetailedValue, - GlossaryEnergy.TotalProductionValue), + (GlossaryEnergy.StreamProductionDetailedValue, GlossaryEnergy.TotalProductionValue), (f'{ns_stream}.{GlossaryEnergy.EnergyProductionValue}', stream), dtotal_prod_denergy_prod * scaling_factor_energy_production) self.set_partial_derivative_for_other_types( From 30c798747dbd157ab0a828b72d729ca5b5beb051 Mon Sep 17 00:00:00 2001 From: perrotcap Date: Tue, 22 Oct 2024 12:38:01 +0200 Subject: [PATCH 05/19] fixed warning --- energy_models/core/techno_type/techno_type.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/energy_models/core/techno_type/techno_type.py b/energy_models/core/techno_type/techno_type.py index 420c106f..fec18ab8 100644 --- a/energy_models/core/techno_type/techno_type.py +++ b/energy_models/core/techno_type/techno_type.py @@ -1019,7 +1019,7 @@ def compute_prod_from_invest(self): GlossaryEnergy.InvestValue: invest_before_year_start, f'Capex_{self.name}': capex_year_start }) - invests_after_year_start_df = self.cost_details[[GlossaryEnergy.Years, GlossaryEnergy.InvestValue, f'Capex_{self.name}']] + invests_after_year_start_df = self.cost_details[[GlossaryEnergy.Years, GlossaryEnergy.InvestValue, f'Capex_{self.name}']].copy() prod_from_invests_df = pd.concat([invest_before_year_start_df, invests_after_year_start_df], ignore_index=True) if len(invest_before_year_start) > 0 else invests_after_year_start_df # Need prod_from invest in TWh we have M$ and $/MWh M$/($/MWh)= TWh From 8ce2695f472fbf6e4a7ec4681ff5d963bb194017 Mon Sep 17 00:00:00 2001 From: perrotcap Date: Tue, 22 Oct 2024 15:55:53 +0200 Subject: [PATCH 06/19] new technos dict midway --- energy_models/core/energy_mix/energy_mix.py | 1 + .../core/investments/independent_invest.py | 1 + .../energy_mix_optim_sub_process/usecase.py | 4 +-- .../techno_dict/data/techno_dict_test.json | 31 ++++++++++++------- .../techno_dict/data/techno_dicts.py | 2 +- .../techno_dict/techno_dict_builder.py | 25 ++++++++++++--- 6 files changed, 44 insertions(+), 20 deletions(-) diff --git a/energy_models/core/energy_mix/energy_mix.py b/energy_models/core/energy_mix/energy_mix.py index 457372b1..d0a5ea4c 100644 --- a/energy_models/core/energy_mix/energy_mix.py +++ b/energy_models/core/energy_mix/energy_mix.py @@ -984,6 +984,7 @@ def compute_grad_CO2_emissions(self): return dtot_CO2_emissions def compute_target_production_constraint(self, inputs_dict: dict): + """should be negative""" target_production_constraint_ref = inputs_dict[GlossaryEnergy.TargetProductionConstraintRefValue] target_energy_production = inputs_dict[GlossaryEnergy.TargetEnergyProductionValue][GlossaryEnergy.TargetEnergyProductionValue].values actual_production_twh = self.production[GlossaryEnergy.TotalProductionValue].values diff --git a/energy_models/core/investments/independent_invest.py b/energy_models/core/investments/independent_invest.py index 38b73c85..89443002 100644 --- a/energy_models/core/investments/independent_invest.py +++ b/energy_models/core/investments/independent_invest.py @@ -69,6 +69,7 @@ def compute_energy_investment_wo_tax(self, inputs_dict: dict): return energy_investment_wo_tax def compute_max_budget_constraint(self, energy_investment_wo_tax: np.ndarray, inputs_dict: dict): + """should be negative""" max_budget_constraint_ref = inputs_dict[GlossaryEnergy.MaxBudgetConstraintRefValue] max_budget = inputs_dict[GlossaryEnergy.MaxBudgetValue][GlossaryEnergy.MaxBudgetValue].values overspending = energy_investment_wo_tax[GlossaryEnergy.EnergyInvestmentsWoTaxValue].values * 1000 - max_budget diff --git a/energy_models/sos_processes/energy/MDA/energy_mix_optim_sub_process/usecase.py b/energy_models/sos_processes/energy/MDA/energy_mix_optim_sub_process/usecase.py index 88da6027..449512e2 100644 --- a/energy_models/sos_processes/energy/MDA/energy_mix_optim_sub_process/usecase.py +++ b/energy_models/sos_processes/energy/MDA/energy_mix_optim_sub_process/usecase.py @@ -410,8 +410,8 @@ def make_func_df(self): "variable": [GlossaryEnergy.CO2EmissionsObjectiveValue, GlossaryEnergy.TargetProductionConstraintValue, GlossaryEnergy.MaxBudgetConstraintValue,], "parent": ["objectives", "constraints", "constraints"], "ftype": [FunctionManagerDisc.OBJECTIVE, FunctionManagerDisc.INEQ_CONSTRAINT, FunctionManagerDisc.INEQ_CONSTRAINT] , - "weight": [1.0, 100.0, 100.0,], - FunctionManagerDisc.AGGR_TYPE: [FunctionManager.AGGR_TYPE_SUM, FunctionManager.AGGR_TYPE_SUM, FunctionManager.AGGR_TYPE_SUM,], + "weight": [1.0, 10.0, 10.0,], + FunctionManagerDisc.AGGR_TYPE: [FunctionManager.AGGR_TYPE_SUM, FunctionManager.INEQ_NEGATIVE_WHEN_SATIFIED_AND_SQUARE_IT, FunctionManager.INEQ_NEGATIVE_WHEN_SATIFIED_AND_SQUARE_IT,], "namespace": [GlossaryEnergy.NS_FUNCTIONS, GlossaryEnergy.NS_FUNCTIONS, GlossaryEnergy.NS_FUNCTIONS,] }) return func_df diff --git a/energy_models/sos_processes/techno_dict/data/techno_dict_test.json b/energy_models/sos_processes/techno_dict/data/techno_dict_test.json index e5dcf605..edabc6c7 100644 --- a/energy_models/sos_processes/techno_dict/data/techno_dict_test.json +++ b/energy_models/sos_processes/techno_dict/data/techno_dict_test.json @@ -1,12 +1,22 @@ { + "methane": { + "type": "energy", + "value": [ + "Methanation" + ] + }, "hydrogen.gaseous_hydrogen": { "type": "energy", "value": [ - "Electrolysis.SOEC", - "Electrolysis.PEM", "Electrolysis.AWE" ] }, + "hydrogen.liquid_hydrogen": { + "type": "energy", + "value": [ + "HydrogenLiquefaction" + ] + }, "fuel.hydrotreated_oil_fuel": { "type": "energy", "value": [ @@ -14,6 +24,12 @@ "HefaDeoxygenation" ] }, + "fuel.biodiesel": { + "type": "energy", + "value": [ + "Transesterification" + ] + }, "electricity": { "type": "energy", "value": [ @@ -29,11 +45,8 @@ "type": "CCUS", "value": [ "flue_gas_capture.CalciumLooping", - "flue_gas_capture.ChilledAmmoniaProcess", - "flue_gas_capture.CO2Membranes", "flue_gas_capture.MonoEthanolAmine", - "flue_gas_capture.PiperazineProcess", - "flue_gas_capture.PressureSwingAdsorption" + "flue_gas_capture.PiperazineProcess" ] }, "biomass_dry": { @@ -47,11 +60,5 @@ "value": [ "CarbonStorageTechno" ] - }, - "heat.hightemperatureheat": { - "type": "energy", - "value": [ - "ElectricBoilerHighHeat" - ] } } \ No newline at end of file diff --git a/energy_models/sos_processes/techno_dict/data/techno_dicts.py b/energy_models/sos_processes/techno_dict/data/techno_dicts.py index 63f8da53..0bb0bc6a 100644 --- a/energy_models/sos_processes/techno_dict/data/techno_dicts.py +++ b/energy_models/sos_processes/techno_dict/data/techno_dicts.py @@ -19,7 +19,7 @@ techno_dict_folder = dirname(__file__) filename = "techno_dict_2024-07-14 Jul01_24technos_12streams.json" -#filename = "techno_dict_test.json" +filename = "techno_dict_test.json" def load_dict(filename: str): filepath = join(techno_dict_folder, filename) with open(filepath, 'r') as json_file: diff --git a/energy_models/sos_processes/techno_dict/techno_dict_builder.py b/energy_models/sos_processes/techno_dict/techno_dict_builder.py index 57efb72f..6ec171b6 100644 --- a/energy_models/sos_processes/techno_dict/techno_dict_builder.py +++ b/energy_models/sos_processes/techno_dict/techno_dict_builder.py @@ -31,6 +31,8 @@ def techno_dict_builder(techno_infos: dict, initial_selection: list[str], minimal_techno_number: int = 0, minimal_stream_number: int = 0, technos_to_avoid: list[str] = [], + streams_to_avoid: list[str] = [], + streams_to_have: list[str] = [], max_carbon_storage_technos: int = 10): """ :param techno_infos: expect something like this : @@ -97,6 +99,11 @@ def techno_dict_builder(techno_infos: dict, initial_selection: list[str], M = 10000 bool_stream_produced_vars = pulp.LpVariable.dicts(name="IsStreamProduced", indices=all_streams, lowBound=0, upBound=1, cat=pulp.LpBinary) + for s in streams_to_avoid: + prob.addConstraint(constraint=bool_stream_produced_vars[s] == 0, name=f"AvoidedStreamProduced{s}") + for s in streams_to_have: + prob.addConstraint(constraint=bool_stream_produced_vars[s] == 1, name=f"StreamsToHave{s}") + # For each energy, add constraints to ensure that if it is consumed, it must be produced by some technology for tech in tech_vars: # Constraint to ensure each energy consumed must be produced @@ -121,7 +128,6 @@ def techno_dict_builder(techno_infos: dict, initial_selection: list[str], prob += pulp.lpSum([bool_stream_produced_vars[s] for s in all_streams]) >= minimal_stream_number - for stream in all_streams: techno_producing_stream = energy_to_producing_technos[stream] name = f"{stream}=" + 'or'.join(techno_producing_stream) @@ -217,12 +223,18 @@ def build_techno_infos(stream_used_by_technos: dict, stream_produced_by_techno: f"{GlossaryEnergy.flue_gas_capture}.{GlossaryEnergy.CO2Membranes}", # remove f"{GlossaryEnergy.flue_gas_capture}.{GlossaryEnergy.PressureSwingAdsorption}", # remove GlossaryEnergy.BiomassBuryingFossilization, -GlossaryEnergy.PureCarbonSolidStorage +GlossaryEnergy.PureCarbonSolidStorage, +GlossaryEnergy.FischerTropsch ] streams_to_avoid = [ GlossaryEnergy.hightemperatureheat_energyname, GlossaryEnergy.mediumtemperatureheat_energyname, -GlossaryEnergy.lowtemperatureheat_energyname +GlossaryEnergy.lowtemperatureheat_energyname, +GlossaryEnergy.syngas, +f'{GlossaryEnergy.fuel}.{GlossaryEnergy.liquid_fuel}', +] +streams_to_have = [ + GlossaryEnergy.carbon_capture, ] @@ -230,8 +242,11 @@ def build_techno_infos(stream_used_by_technos: dict, stream_produced_by_techno: sub_techno_dict, n_technos, n_streams = techno_dict_builder( techno_infos=techno_info_dict, initial_selection=inital_selection, - minimal_stream_number=7, - minimal_techno_number=20, + minimal_stream_number=8, + minimal_techno_number=17, + streams_to_avoid=streams_to_avoid, + streams_to_have=streams_to_have, + technos_to_avoid=technos_to_avoid, max_carbon_storage_technos=3) ts = datetime.now().strftime("%Y-%m-%d %h%M") From fed9eac28dbc65877205569ae99b6c1d646f60ed Mon Sep 17 00:00:00 2001 From: benherry Date: Tue, 22 Oct 2024 17:50:47 +0200 Subject: [PATCH 07/19] feat: calibrate fossil simple techno dict parameters to fit proper production and price at given invests --- .../fitting/fossil_energy_simple_techno.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/data_energy/fitting/fossil_energy_simple_techno.py b/data_energy/fitting/fossil_energy_simple_techno.py index 2583b44f..b1aa1c52 100644 --- a/data_energy/fitting/fossil_energy_simple_techno.py +++ b/data_energy/fitting/fossil_energy_simple_techno.py @@ -13,13 +13,12 @@ FossilSimpleTechnoDiscipline, ) -construction_delay = GlossaryEnergy.TechnoConstructionDelayDict[GlossaryEnergy.FossilSimpleTechno] -df_invest_historic, heavy_collected_data = DatabaseWitnessEnergy.get_techno_invest_before_year_start(techno_name=GlossaryEnergy.FossilSimpleTechno, - year_start=2023, construction_delay=construction_delay) +df_invest_historic = DatabaseWitnessEnergy.get_techno_invest_df(techno_name=GlossaryEnergy.FossilSimpleTechno) df_prod_historic = DatabaseWitnessEnergy.get_techno_prod(techno_name=GlossaryEnergy.FossilSimpleTechno, year=2020)[1].value #ToDo: with or without tax? -ref_price_2023 = 35. # $/MWh Source: chatgpt average of 30-40$/MWh +ref_price_2023 = 121.5 # $/MWh Source: chatgpt # data to run techno +construction_delay = GlossaryEnergy.TechnoConstructionDelayDict[GlossaryEnergy.FossilSimpleTechno] year_start_fitting = int(max(df_invest_historic['years'].min() + construction_delay, df_prod_historic['years'].min(), 2020)) year_end_fitting = int(min(df_invest_historic['years'].max(), df_prod_historic['years'].max())) @@ -28,7 +27,7 @@ invest_df = df_invest_historic.loc[(df_invest_historic['years'] >= year_start_fitting) & (df_invest_historic['years'] <= year_end_fitting)] margin = pd.DataFrame({GlossaryEnergy.Years: years_fitting, GlossaryEnergy.MarginValue: 110}) transport = pd.DataFrame({GlossaryEnergy.Years: years_fitting, 'transport': np.zeros(len(years_fitting))}) -co2_taxes = pd.DataFrame({GlossaryEnergy.Years: years_fitting, GlossaryEnergy.CO2Tax: np.linspace(14., 40., len(years_fitting))}) +co2_taxes = pd.DataFrame({GlossaryEnergy.Years: years_fitting, GlossaryEnergy.CO2Tax: np.linspace(0., 0., len(years_fitting))}) stream_prices = pd.DataFrame({GlossaryEnergy.Years: years_fitting}) resources_price = pd.DataFrame({GlossaryEnergy.Years: years_fitting}) techno_dict_default = FossilSimpleTechnoDiscipline.techno_infos_dict_default @@ -81,7 +80,7 @@ def run_model(x: list, year_end: int = year_end_fitting): ee.execute() prod_df = ee.dm.get_value(ee.dm.get_all_namespaces_from_var_name(GlossaryEnergy.TechnoProductionValue)[0]) - prod_values_model = prod_df[f"{GlossaryEnergy.clean_energy} (TWh)"].values * 1000 + prod_values_model = prod_df["fossil (TWh)"].values * 1000 price_df = ee.dm.get_value(ee.dm.get_all_namespaces_from_var_name(GlossaryEnergy.TechnoPricesValue)[0]) @@ -95,8 +94,8 @@ def fitting_renewable(x: list): # Initial guess for the variables -x0 = np.array([250., 1., 0.0, 0.2, 0.1]) -#x0 = np.array([743.8, 1.3, 0.06, 0.0, 0.06]) +x0 = np.array([100., 1., 0.0, 0.024, 0.058]) # [capex_init, init_age_distrib_factor, learnin_rate, Opex_fraction, WACC] +x0 = np.array([743.8, 1.3, 0.06, 0.0, 0.06]) bounds = [(0, 10000), (0, 3.0), (0.01, 0.95), (0.001, 0.99), (0.0001, 0.3)] From 2def3c7d5a80e811d408882961f656591d79d2f8 Mon Sep 17 00:00:00 2001 From: perrotcap Date: Wed, 23 Oct 2024 10:47:52 +0200 Subject: [PATCH 08/19] changed constraint format for energy mix optim --- energy_models/core/energy_mix/energy_mix.py | 5 ++++- energy_models/core/energy_mix/energy_mix_disc.py | 4 ++-- .../core/investments/disciplines/independent_invest_disc.py | 6 +++--- energy_models/core/investments/independent_invest.py | 6 +++++- .../energy/MDA/energy_mix_optim_sub_process/usecase.py | 2 +- .../energy/MDO/energy_mix_optim_process/process.py | 2 +- 6 files changed, 16 insertions(+), 9 deletions(-) diff --git a/energy_models/core/energy_mix/energy_mix.py b/energy_models/core/energy_mix/energy_mix.py index d0a5ea4c..28649b76 100644 --- a/energy_models/core/energy_mix/energy_mix.py +++ b/energy_models/core/energy_mix/energy_mix.py @@ -989,7 +989,10 @@ def compute_target_production_constraint(self, inputs_dict: dict): target_energy_production = inputs_dict[GlossaryEnergy.TargetEnergyProductionValue][GlossaryEnergy.TargetEnergyProductionValue].values actual_production_twh = self.production[GlossaryEnergy.TotalProductionValue].values missing_production = target_energy_production - actual_production_twh - self.target_production_constraint = missing_production / target_production_constraint_ref + self.target_production_constraint = pd.DataFrame({ + GlossaryEnergy.Years: self.years, + GlossaryEnergy.TargetProductionConstraintValue: missing_production / target_production_constraint_ref + }) def compute(self, inputs: dict, exp_min=True): self.configure_parameters_update(inputs) diff --git a/energy_models/core/energy_mix/energy_mix_disc.py b/energy_models/core/energy_mix/energy_mix_disc.py index 3284e866..4cb0f56d 100644 --- a/energy_models/core/energy_mix/energy_mix_disc.py +++ b/energy_models/core/energy_mix/energy_mix_disc.py @@ -562,7 +562,7 @@ def compute_sos_jacobian(self): dtotal_prod_denergy_prod) target_production_constraint_ref = inputs_dict[GlossaryEnergy.TargetProductionConstraintRefValue] self.set_partial_derivative_for_other_types( - (GlossaryEnergy.TargetProductionConstraintValue,), + (GlossaryEnergy.TargetProductionConstraintValue, GlossaryEnergy.TargetProductionConstraintValue), (f'{ns_stream}.{GlossaryEnergy.EnergyProductionValue}', stream), - dtotal_prod_denergy_prod * 1e3 / target_production_constraint_ref) self.set_partial_derivative_for_other_types( @@ -663,7 +663,7 @@ def compute_sos_jacobian(self): f'{stream} ({GlossaryEnergy.unit_dicts[stream]})'), scaling_factor_energy_consumption * dtotal_prod_denergy_cons / scaling_factor_energy_production) self.set_partial_derivative_for_other_types( - (GlossaryEnergy.TargetProductionConstraintValue,), ( + (GlossaryEnergy.TargetProductionConstraintValue, GlossaryEnergy.TargetProductionConstraintValue), ( f'{ns_stream_input}.{GlossaryEnergy.StreamConsumptionValue}', f'{stream} ({GlossaryEnergy.unit_dicts[stream]})'), - scaling_factor_energy_consumption * dtotal_prod_denergy_cons / scaling_factor_energy_production * 1e3 / target_production_constraint_ref) diff --git a/energy_models/core/investments/disciplines/independent_invest_disc.py b/energy_models/core/investments/disciplines/independent_invest_disc.py index 5e05fd84..35e55a9a 100644 --- a/energy_models/core/investments/disciplines/independent_invest_disc.py +++ b/energy_models/core/investments/disciplines/independent_invest_disc.py @@ -215,7 +215,7 @@ def compute_sos_jacobian(self): ones * 1e-3) self.set_partial_derivative_for_other_types( - (GlossaryEnergy.MaxBudgetConstraintValue,), + (GlossaryEnergy.MaxBudgetConstraintValue, GlossaryEnergy.MaxBudgetConstraintValue), (GlossaryEnergy.invest_mix, techno), identity / max_budget_constraint_ref) @@ -235,7 +235,7 @@ def compute_sos_jacobian(self): ones * 1e-3) self.set_partial_derivative_for_other_types( - (GlossaryEnergy.MaxBudgetConstraintValue,), + (GlossaryEnergy.MaxBudgetConstraintValue, GlossaryEnergy.MaxBudgetConstraintValue), (GlossaryEnergy.ForestInvestmentValue, GlossaryEnergy.ForestInvestmentValue), identity / max_budget_constraint_ref) @@ -253,7 +253,7 @@ def compute_sos_jacobian(self): ones * 1e-3) self.set_partial_derivative_for_other_types( - (GlossaryEnergy.MaxBudgetConstraintValue,), + (GlossaryEnergy.MaxBudgetConstraintValue, GlossaryEnergy.MaxBudgetConstraintValue), (techno, GlossaryEnergy.InvestmentsValue), identity / max_budget_constraint_ref) diff --git a/energy_models/core/investments/independent_invest.py b/energy_models/core/investments/independent_invest.py index 89443002..1a74f3da 100644 --- a/energy_models/core/investments/independent_invest.py +++ b/energy_models/core/investments/independent_invest.py @@ -73,4 +73,8 @@ def compute_max_budget_constraint(self, energy_investment_wo_tax: np.ndarray, in max_budget_constraint_ref = inputs_dict[GlossaryEnergy.MaxBudgetConstraintRefValue] max_budget = inputs_dict[GlossaryEnergy.MaxBudgetValue][GlossaryEnergy.MaxBudgetValue].values overspending = energy_investment_wo_tax[GlossaryEnergy.EnergyInvestmentsWoTaxValue].values * 1000 - max_budget - return overspending / max_budget_constraint_ref + max_budget_constraint_df = pd.DataFrame({ + GlossaryEnergy.Years: inputs_dict[GlossaryEnergy.invest_mix][GlossaryEnergy.Years], + GlossaryEnergy.MaxBudgetConstraintValue: overspending / max_budget_constraint_ref + }) + return max_budget_constraint_df diff --git a/energy_models/sos_processes/energy/MDA/energy_mix_optim_sub_process/usecase.py b/energy_models/sos_processes/energy/MDA/energy_mix_optim_sub_process/usecase.py index 449512e2..302ea147 100644 --- a/energy_models/sos_processes/energy/MDA/energy_mix_optim_sub_process/usecase.py +++ b/energy_models/sos_processes/energy/MDA/energy_mix_optim_sub_process/usecase.py @@ -410,7 +410,7 @@ def make_func_df(self): "variable": [GlossaryEnergy.CO2EmissionsObjectiveValue, GlossaryEnergy.TargetProductionConstraintValue, GlossaryEnergy.MaxBudgetConstraintValue,], "parent": ["objectives", "constraints", "constraints"], "ftype": [FunctionManagerDisc.OBJECTIVE, FunctionManagerDisc.INEQ_CONSTRAINT, FunctionManagerDisc.INEQ_CONSTRAINT] , - "weight": [1.0, 10.0, 10.0,], + "weight": [1.0, .0, 100.0,], FunctionManagerDisc.AGGR_TYPE: [FunctionManager.AGGR_TYPE_SUM, FunctionManager.INEQ_NEGATIVE_WHEN_SATIFIED_AND_SQUARE_IT, FunctionManager.INEQ_NEGATIVE_WHEN_SATIFIED_AND_SQUARE_IT,], "namespace": [GlossaryEnergy.NS_FUNCTIONS, GlossaryEnergy.NS_FUNCTIONS, GlossaryEnergy.NS_FUNCTIONS,] }) diff --git a/energy_models/sos_processes/energy/MDO/energy_mix_optim_process/process.py b/energy_models/sos_processes/energy/MDO/energy_mix_optim_process/process.py index 799851a3..98472519 100644 --- a/energy_models/sos_processes/energy/MDO/energy_mix_optim_process/process.py +++ b/energy_models/sos_processes/energy/MDO/energy_mix_optim_process/process.py @@ -21,7 +21,7 @@ class ProcessBuilder(BaseProcessBuilder): # ontology information _ontology_data = { 'label': 'Energy Mix Optim process - medium techno dict', - 'description': 'Techno dict with 12 streams and 24 technos', + 'description': '', 'category': '', 'version': '', } From 9c75ad41b53aba3a64e12bb0ad9af782c0081ab0 Mon Sep 17 00:00:00 2001 From: benherry Date: Wed, 23 Oct 2024 11:03:11 +0200 Subject: [PATCH 09/19] feat: added utilization ratio to be able to fit perfectly price and production. added header --- .../fitting/fossil_energy_simple_techno.py | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/data_energy/fitting/fossil_energy_simple_techno.py b/data_energy/fitting/fossil_energy_simple_techno.py index b1aa1c52..cf4154fc 100644 --- a/data_energy/fitting/fossil_energy_simple_techno.py +++ b/data_energy/fitting/fossil_energy_simple_techno.py @@ -1,3 +1,18 @@ +''' +Copyright 2024 Capgemini + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +''' import numpy as np import pandas as pd from scipy.optimize import minimize @@ -59,6 +74,8 @@ def run_model(x: list, year_end: int = year_end_fitting): #techno_dict_default["learning_rate"] = x[2] techno_dict_default["Opex_percentage"] = x[3] techno_dict_default["WACC"] = x[4] + utilisation_ratio = pd.DataFrame({GlossaryEnergy.Years: np.arange(year_start_fitting, year_end + 1), + GlossaryEnergy.UtilisationRatioValue: x[5:]}) inputs_dict = { f'{name}.{GlossaryEnergy.YearStart}': year_start_fitting, @@ -73,6 +90,7 @@ def run_model(x: list, year_end: int = year_end_fitting): f'{name}.{model_name}.{GlossaryEnergy.MarginValue}': margin, f'{name}.{model_name}.{GlossaryEnergy.InitialPlantsAgeDistribFactor}': init_age_distrib_factor, f'{name}.{model_name}.techno_infos_dict': techno_dict_default, + f'{name}.{model_name}.{GlossaryEnergy.UtilisationRatioValue}': utilisation_ratio, } ee.load_study_from_input_dict(inputs_dict) @@ -94,10 +112,11 @@ def fitting_renewable(x: list): # Initial guess for the variables -x0 = np.array([100., 1., 0.0, 0.024, 0.058]) # [capex_init, init_age_distrib_factor, learnin_rate, Opex_fraction, WACC] -x0 = np.array([743.8, 1.3, 0.06, 0.0, 0.06]) +years = np.arange(year_start_fitting, year_end_fitting + 1) +x0 = np.concatenate((np.array([100., 1., 0.0, 0.024, 0.058]), 100.0 * np.ones_like(years))) # [capex_init, init_age_distrib_factor, learnin_rate, Opex_fraction, WACC] +#x0 = np.array([743.8, 1.3, 0.06, 0.0, 0.06]) -bounds = [(0, 10000), (0, 3.0), (0.01, 0.95), (0.001, 0.99), (0.0001, 0.3)] +bounds = [(0, 10000), (0, 3.0), (0.01, 0.95), (0.001, 0.99), (0.0001, 0.3)] + len(years) * [(0.1, 100.0)] # Use minimize to find the minimum of the function result = minimize(fitting_renewable, x0, bounds=bounds) @@ -121,12 +140,13 @@ def fitting_renewable(x: list): new_chart.to_plotly().show() -parameters = ["capex_init", "init_age_distrib_factor", "learning_rate", "opex_percentage", "wacc"] +parameters = ["capex_init", "init_age_distrib_factor", "learning_rate", "opex_percentage", "wacc", "utilization_ratio"] opt_values = dict(zip(parameters, np.round(result.x, 2))) for key, val in opt_values.items(): print("Optimal", key, ":", val) -capex_init, init_age_distrib_factor, learning_rate, opex_percentage, wacc = result.x +capex_init, init_age_distrib_factor, learning_rate, opex_percentage, wacc = result.x[0:5] +utilization_ratio = result.x[5:] disc = ee.dm.get_disciplines_with_name( f'{name}.{model_name}')[0] From 472e67a7e0095c3467a2ff2000658496e1003707 Mon Sep 17 00:00:00 2001 From: perrotcap Date: Wed, 23 Oct 2024 14:55:30 +0200 Subject: [PATCH 10/19] improved sources citations --- data_energy/techno_invests/sources.txt | 2 +- data_energy/techno_production_historic/sources.txt | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/data_energy/techno_invests/sources.txt b/data_energy/techno_invests/sources.txt index 67e3ef13..53e2aeb4 100644 --- a/data_energy/techno_invests/sources.txt +++ b/data_energy/techno_invests/sources.txt @@ -1,7 +1,7 @@ FossilSimpleTechno : https://www.iea.org/reports/world-energy-investment-2023/overview-and-key-findings Clean energy simple techno : https://www.iea.org/data-and-statistics/charts/global-investment-in-clean-energy-and-fossil-fuels-2015-2024 -Clean energy invest = Renewable + Nuclear and others + Low emission fuels + 75% of grid invests because renewable need a lot of grid invest to work (https://www.rystadenergy.com/news/power-grids-investments-energy-transition-permitting-policies) +Clean energy invest = Renewable + Nuclear and others + Low emission fuels + 70% of grid invests because renewable need a lot of grid invest to work (https://www.rystadenergy.com/news/power-grids-investments-energy-transition-permitting-policies) CarbonStorageTechno : https://www.iea.org/energy-system/carbon-capture-utilisation-and-storage/co2-capture-and-utilisation DirectAirCaptureTechno : https://www.iea.org/energy-system/carbon-capture-utilisation-and-storage/direct-air-capture diff --git a/data_energy/techno_production_historic/sources.txt b/data_energy/techno_production_historic/sources.txt index a082af6b..ac146b10 100644 --- a/data_energy/techno_production_historic/sources.txt +++ b/data_energy/techno_production_historic/sources.txt @@ -9,4 +9,6 @@ Hydropower : https://www.statista.com/statistics/273273/world-electricity-genera GasTurbine : 25% of Natural gas electricity generation https://www.statista.com/statistics/273273/world-electricity-generation-by-energy-source/ CombinedGasCycleTurbine : 75% of Natural gas electricity generation https://www.statista.com/statistics/273273/world-electricity-generation-by-energy-source/ Geothermal : https://geothermal-energy-journal.springeropen.com/articles/10.1186/s40517-024-00290-w -CropEnergy : 1972, by reading this graph https://www.iea.org/reports/bioenergy-2#overview we get 3966Twh in 2022 (2361 from convetional crop and 1605 from short rotation) \ No newline at end of file +CropEnergy : 1972, by reading this graph https://www.iea.org/reports/bioenergy-2#overview we get 3966Twh in 2022 (2361 from convetional crop and 1605 from short rotation) + +Clean energy simple techno : \ No newline at end of file From d07f37b88d335729a821113bcf40546d0d0953de Mon Sep 17 00:00:00 2001 From: perrotcap Date: Wed, 23 Oct 2024 15:25:09 +0200 Subject: [PATCH 11/19] fixed headers --- data_energy/fitting/clean_energy_simple_techno.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/data_energy/fitting/clean_energy_simple_techno.py b/data_energy/fitting/clean_energy_simple_techno.py index 700faf2a..651e6975 100644 --- a/data_energy/fitting/clean_energy_simple_techno.py +++ b/data_energy/fitting/clean_energy_simple_techno.py @@ -1,3 +1,18 @@ +''' +Copyright 2024 Capgemini + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +''' import numpy as np import pandas as pd from scipy.optimize import minimize From e7501ee4ebd7616baba960f2838b9fa960807dd2 Mon Sep 17 00:00:00 2001 From: benherry Date: Wed, 23 Oct 2024 15:26:37 +0200 Subject: [PATCH 12/19] fix: update invest and production values based on Capgemini excel sheet "Data for coarse technos" --- .../techno_invests/fossilsimpletechno.csv | 20 ++--- data_energy/techno_invests/sources.txt | 3 +- .../fossilsimpletechno.csv | 79 ++++++++----------- 3 files changed, 47 insertions(+), 55 deletions(-) diff --git a/data_energy/techno_invests/fossilsimpletechno.csv b/data_energy/techno_invests/fossilsimpletechno.csv index 85a38792..516980a8 100644 --- a/data_energy/techno_invests/fossilsimpletechno.csv +++ b/data_energy/techno_invests/fossilsimpletechno.csv @@ -1,10 +1,10 @@ -years,invest -2015,1319 -2016,1105 -2017,1114 -2018,1109 -2019,1066 -2020,839 -2021,914 -2022,1002 -2023,1050 +years,invest +2015,1374 +2016,1145 +2017,1179 +2018,1170 +2019,1127 +2020,897 +2021,963 +2022,1036 +2023,1090 diff --git a/data_energy/techno_invests/sources.txt b/data_energy/techno_invests/sources.txt index 67e3ef13..6f468aaa 100644 --- a/data_energy/techno_invests/sources.txt +++ b/data_energy/techno_invests/sources.txt @@ -1,4 +1,5 @@ -FossilSimpleTechno : https://www.iea.org/reports/world-energy-investment-2023/overview-and-key-findings +FossilSimpleTechno : https://www.iea.org/data-and-statistics/charts/global-investment-in-clean-energy-and-fossil-fuels-2015-2024 +fossil simple techno invest = coal + gas + oil Clean energy simple techno : https://www.iea.org/data-and-statistics/charts/global-investment-in-clean-energy-and-fossil-fuels-2015-2024 Clean energy invest = Renewable + Nuclear and others + Low emission fuels + 75% of grid invests because renewable need a lot of grid invest to work (https://www.rystadenergy.com/news/power-grids-investments-energy-transition-permitting-policies) diff --git a/data_energy/techno_production_historic/fossilsimpletechno.csv b/data_energy/techno_production_historic/fossilsimpletechno.csv index 77472652..8560ebb7 100644 --- a/data_energy/techno_production_historic/fossilsimpletechno.csv +++ b/data_energy/techno_production_historic/fossilsimpletechno.csv @@ -1,44 +1,35 @@ -Entity,years,Coal production,Oil production,Gas production,unit,production -World,1981,21445.264,33858.336,14530.98,TWh,69834.58 -World,1982,22204.45,32518.732,14597.483,TWh,69320.66500000001 -World,1983,22137.21,32122.473,14728.052,TWh,68987.735 -World,1984,23105.666,32752.219,15988.612,TWh,71846.497 -World,1985,24276.508,32465.682,16410.338,TWh,73152.528 -World,1986,24852.295,34069.39,16834.781,TWh,75756.466 -World,1987,25321.266,34148.09,17678.572,TWh,77147.928 -World,1988,25936.305,35607.57,18457.764,TWh,80001.639 -World,1989,26394.465,36037.48,19096.992,TWh,81528.937 -World,1990,26344.957,36726.777,19697.164,TWh,82768.898 -World,1991,25628.818,36627.707,19959.688,TWh,82216.213 -World,1992,25572.447,37157.996,20018.34,TWh,82748.783 -World,1993,24802.262,37105.156,20246.295,TWh,82153.713 -World,1994,25371.623,37619.203,20542.516,TWh,83533.342 -World,1995,26146.615,38135.516,20883.5,TWh,85165.63100000001 -World,1996,26502.912,39151.24,21899.963,TWh,87554.115 -World,1997,26894.896,40023.2,21882.97,TWh,88801.06599999999 -World,1998,26412.506,41034.703,22462.334,TWh,89909.543 -World,1999,26421.326,40104.69,23102.75,TWh,89628.766 -World,2000,26812.184,41863.727,24007.166,TWh,92683.07699999999 -World,2001,27948.334,41854.504,24574.18,TWh,94377.01800000001 -World,2002,28287.195,41349.258,25147.336,TWh,94783.789 -World,2003,30361.137,43184.227,26046.873,TWh,99592.237 -World,2004,32984.98,45349.15,26932.35,TWh,105266.48000000001 -World,2005,35060.26,45735.105,27577.504,TWh,108372.869 -World,2006,36829.766,46140.66,28517.908,TWh,111488.334 -World,2007,38453.367,46042.082,29278.344,TWh,113773.79299999999 -World,2008,39718.855,46532.49,30349.936,TWh,116601.281 -World,2009,39704.535,45375.168,29408.73,TWh,114488.433 -World,2010,41834.363,46271.11,31501.742,TWh,119607.215 -World,2011,44789.605,46628.05,32575.154,TWh,123992.809 -World,2012,45253.773,47925.867,33265.63,TWh,126445.26999999999 -World,2013,45978.477,47984.973,33660.957,TWh,127624.407 -World,2014,45850.832,49109.766,34356.14,TWh,129316.738 -World,2015,44570.035,50752.184,35059.98,TWh,130382.19900000002 -World,2016,42189.684,50948.1,35076.668,TWh,128214.45199999999 -World,2017,43191.312,51020.32,36709.18,TWh,130920.812 -World,2018,45143.613,52221.39,38453.266,TWh,135818.269 -World,2019,45783.21,52188.836,39640.457,TWh,137612.503 -World,2020,43483.91,48708.867,38661.79,TWh,130854.56700000001 -World,2021,45202.457,49286.684,40437.043,TWh,134926.184 -World,2022,48358.03,51519.516,40486.11,TWh,140363.65600000002 -World,2023,49789.156,52432.227,40592.312,TWh,142813.695 +Entity,years,unit,production +World,1990,TWh,82793.46806 +World,1991,TWh,82936.90861 +World,1992,TWh,82756.93861 +World,1993,TWh,83442.45333 +World,1994,TWh,83909.28861 +World,1995,TWh,85971.84472 +World,1996,TWh,88145.4825 +World,1997,TWh,89179.28639 +World,1998,TWh,89465.70833 +World,1999,TWh,91444.55972 +World,2000,TWh,93822.62861 +World,2001,TWh,94776.65167 +World,2002,TWh,96919.55 +World,2003,TWh,101029.5506 +World,2004,TWh,105867.19 +World,2005,TWh,109070.5481 +World,2006,TWh,112404.6575 +World,2007,TWh,116029.2067 +World,2008,TWh,117157.3708 +World,2009,TWh,115546.1642 +World,2010,TWh,122588.6319 +World,2011,TWh,125257.3619 +World,2012,TWh,126692.7997 +World,2013,TWh,128144.5494 +World,2014,TWh,129481.3961 +World,2015,TWh,129263.3089 +World,2016,TWh,129960.4772 +World,2017,TWh,132681.4917 +World,2018,TWh,135365.7864 +World,2019,TWh,136480.9619 +World,2020,TWh,130071.7306 +World,2021,TWh,138229.7597 +World,2022,TWh,139917.3519 +World,2023,TWh,139500.1165 From bce89eab82cadba60344714e93a7a7230d1017d6 Mon Sep 17 00:00:00 2001 From: benherry Date: Wed, 23 Oct 2024 15:27:39 +0200 Subject: [PATCH 13/19] feat: impose utilisation ratio=100, learning rate = 0 and age_distrib=1 to simplify the problem --- .../fitting/fossil_energy_simple_techno.py | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/data_energy/fitting/fossil_energy_simple_techno.py b/data_energy/fitting/fossil_energy_simple_techno.py index cf4154fc..622dc76c 100644 --- a/data_energy/fitting/fossil_energy_simple_techno.py +++ b/data_energy/fitting/fossil_energy_simple_techno.py @@ -28,13 +28,14 @@ FossilSimpleTechnoDiscipline, ) +year_calibration = 2015 + df_invest_historic = DatabaseWitnessEnergy.get_techno_invest_df(techno_name=GlossaryEnergy.FossilSimpleTechno) df_prod_historic = DatabaseWitnessEnergy.get_techno_prod(techno_name=GlossaryEnergy.FossilSimpleTechno, year=2020)[1].value -#ToDo: with or without tax? -ref_price_2023 = 121.5 # $/MWh Source: chatgpt +ref_price_2023 = 121.5 # $/MWh Source: chatgpt LCOE without tax # data to run techno construction_delay = GlossaryEnergy.TechnoConstructionDelayDict[GlossaryEnergy.FossilSimpleTechno] -year_start_fitting = int(max(df_invest_historic['years'].min() + construction_delay, df_prod_historic['years'].min(), 2020)) +year_start_fitting = int(max(df_invest_historic['years'].min() + construction_delay, df_prod_historic['years'].min(), year_calibration)) year_end_fitting = int(min(df_invest_historic['years'].max(), df_prod_historic['years'].max())) prod_values_historic = df_prod_historic.loc[(df_prod_historic['years'] >= year_start_fitting) & (df_prod_historic['years'] <= year_end_fitting)]['production'].values @@ -71,10 +72,10 @@ def run_model(x: list, year_end: int = year_end_fitting): techno_dict_default["Capex_init"] = x[0] init_age_distrib_factor = x[1] - #techno_dict_default["learning_rate"] = x[2] + techno_dict_default["learning_rate"] = x[2] techno_dict_default["Opex_percentage"] = x[3] techno_dict_default["WACC"] = x[4] - utilisation_ratio = pd.DataFrame({GlossaryEnergy.Years: np.arange(year_start_fitting, year_end + 1), + utilisation_ratio = pd.DataFrame({GlossaryEnergy.Years: years_fitting, GlossaryEnergy.UtilisationRatioValue: x[5:]}) inputs_dict = { @@ -112,11 +113,11 @@ def fitting_renewable(x: list): # Initial guess for the variables -years = np.arange(year_start_fitting, year_end_fitting + 1) -x0 = np.concatenate((np.array([100., 1., 0.0, 0.024, 0.058]), 100.0 * np.ones_like(years))) # [capex_init, init_age_distrib_factor, learnin_rate, Opex_fraction, WACC] -#x0 = np.array([743.8, 1.3, 0.06, 0.0, 0.06]) +# [capex_init, init_age_distrib_factor, learnin_rate, Opex_fraction, WACC, utilization_ratio] +x0 = np.concatenate((np.array([200., 1., 0.0, 0.024, 0.058]), 100.0 * np.ones_like(years_fitting))) -bounds = [(0, 10000), (0, 3.0), (0.01, 0.95), (0.001, 0.99), (0.0001, 0.3)] + len(years) * [(0.1, 100.0)] +# can put different lower and upper bounds for utilization ratio if want to activate it +bounds = [(0, 10000), (1.0, 1.0), (0.0, 0.0), (0.001, 0.99), (0.0001, 0.3)] + len(years_fitting) * [(100.0, 100.0)] # Use minimize to find the minimum of the function result = minimize(fitting_renewable, x0, bounds=bounds) @@ -124,7 +125,6 @@ def fitting_renewable(x: list): prod_values_model, price_model_values = run_model(result.x) # Print the result -#print("Optimal solution:", result.x) print("Function value at the optimum:", result.fun) @@ -140,13 +140,13 @@ def fitting_renewable(x: list): new_chart.to_plotly().show() -parameters = ["capex_init", "init_age_distrib_factor", "learning_rate", "opex_percentage", "wacc", "utilization_ratio"] -opt_values = dict(zip(parameters, np.round(result.x, 2))) -for key, val in opt_values.items(): - print("Optimal", key, ":", val) - capex_init, init_age_distrib_factor, learning_rate, opex_percentage, wacc = result.x[0:5] utilization_ratio = result.x[5:] +parameters = ["capex_init", "init_age_distrib_factor", "learning_rate", "opex_percentage", "wacc"] +opt_values = dict(zip(parameters, np.round(result.x, 3))) +for key, val in opt_values.items(): + print("Optimal", key, ":", val) +print("Optimal utilization_ratio", utilization_ratio) disc = ee.dm.get_disciplines_with_name( f'{name}.{model_name}')[0] From 73fb0783eb768d854050678de9d5997fbcda91db Mon Sep 17 00:00:00 2001 From: benherry Date: Wed, 23 Oct 2024 15:28:33 +0200 Subject: [PATCH 14/19] feat: updated techno dict based on optimization results of fossil_energy_simple_techno.py --- .../fossil_simple_techno/fossil_simple_techno_disc.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/energy_models/models/fossil/fossil_simple_techno/fossil_simple_techno_disc.py b/energy_models/models/fossil/fossil_simple_techno/fossil_simple_techno_disc.py index f1548387..c96d5b04 100644 --- a/energy_models/models/fossil/fossil_simple_techno/fossil_simple_techno_disc.py +++ b/energy_models/models/fossil/fossil_simple_techno/fossil_simple_techno_disc.py @@ -69,16 +69,16 @@ class FossilSimpleTechnoDiscipline(FossilTechnoDiscipline): FossilGasDiscipline.techno_infos_dict_default['CO2_from_production'] * prod_methane) / prod_fossil techno_infos_dict_default = {'maturity': 0, - 'Opex_percentage': 0.024, - 'WACC': 0.058, + 'Opex_percentage': 0.299, + 'WACC': 0.0, 'learning_rate': 0.00, - 'Capex_init': 100., + 'Capex_init': 222.64, 'Capex_init_unit': '$/MWh', 'techno_evo_eff': 'no', 'efficiency': 1.0, 'CO2_from_production': co2_from_prod, 'CO2_from_production_unit': 'kg/kg', - 'resource_price': 75.0, + 'resource_price': 35.0, 'resource_price_unit': '$/MWh', 'CH4_venting_emission_factor': (21.9 + 7.2) / 50731., 'CH4_flaring_emission_factor': (1.4 + 6.9) / 50731., From 30909784dffb9183f248380e3010a37192f1f619 Mon Sep 17 00:00:00 2001 From: benherry Date: Wed, 23 Oct 2024 15:31:42 +0200 Subject: [PATCH 15/19] feat: added the values obtained in comment --- .../fitting/fossil_energy_simple_techno.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/data_energy/fitting/fossil_energy_simple_techno.py b/data_energy/fitting/fossil_energy_simple_techno.py index 622dc76c..a6c39a3b 100644 --- a/data_energy/fitting/fossil_energy_simple_techno.py +++ b/data_energy/fitting/fossil_energy_simple_techno.py @@ -154,4 +154,17 @@ def fitting_renewable(x: list): graph_list = disc.get_post_processing_list(filters) for graph in graph_list: graph.to_plotly().show() - pass \ No newline at end of file + pass + +""" +Results obtained: +Function value at the optimum: 16826745.79920797 +=> less than 6% error at max between model and historic production between 2015 and 2023 +=> no error on the price +Optimal capex_init : 222.638 +Optimal init_age_distrib_factor : 1.0 +Optimal learning_rate : 0.0 +Optimal opex_percentage : 0.299 +Optimal wacc : 0.0 +Optimal utilization_ratio [100. 100. 100. 100. 100. 100.] +""" \ No newline at end of file From 9920189f9eec87347478dce2b9fd1cec66a19fa3 Mon Sep 17 00:00:00 2001 From: benherry Date: Wed, 23 Oct 2024 17:26:01 +0200 Subject: [PATCH 16/19] fix: put non-zero wacc not to break tests --- data_energy/fitting/fossil_energy_simple_techno.py | 6 +++--- .../fossil_simple_techno/fossil_simple_techno_disc.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/data_energy/fitting/fossil_energy_simple_techno.py b/data_energy/fitting/fossil_energy_simple_techno.py index a6c39a3b..9588ae37 100644 --- a/data_energy/fitting/fossil_energy_simple_techno.py +++ b/data_energy/fitting/fossil_energy_simple_techno.py @@ -117,7 +117,7 @@ def fitting_renewable(x: list): x0 = np.concatenate((np.array([200., 1., 0.0, 0.024, 0.058]), 100.0 * np.ones_like(years_fitting))) # can put different lower and upper bounds for utilization ratio if want to activate it -bounds = [(0, 10000), (1.0, 1.0), (0.0, 0.0), (0.001, 0.99), (0.0001, 0.3)] + len(years_fitting) * [(100.0, 100.0)] +bounds = [(0, 10000), (1.0, 1.0), (0.0, 0.0), (0.001, 0.99), (0.058, 0.3)] + len(years_fitting) * [(100.0, 100.0)] # Use minimize to find the minimum of the function result = minimize(fitting_renewable, x0, bounds=bounds) @@ -164,7 +164,7 @@ def fitting_renewable(x: list): Optimal capex_init : 222.638 Optimal init_age_distrib_factor : 1.0 Optimal learning_rate : 0.0 -Optimal opex_percentage : 0.299 -Optimal wacc : 0.0 +Optimal opex_percentage : 0.262 +Optimal wacc : 0.058 Optimal utilization_ratio [100. 100. 100. 100. 100. 100.] """ \ No newline at end of file diff --git a/energy_models/models/fossil/fossil_simple_techno/fossil_simple_techno_disc.py b/energy_models/models/fossil/fossil_simple_techno/fossil_simple_techno_disc.py index c96d5b04..44cbfca0 100644 --- a/energy_models/models/fossil/fossil_simple_techno/fossil_simple_techno_disc.py +++ b/energy_models/models/fossil/fossil_simple_techno/fossil_simple_techno_disc.py @@ -70,7 +70,7 @@ class FossilSimpleTechnoDiscipline(FossilTechnoDiscipline): techno_infos_dict_default = {'maturity': 0, 'Opex_percentage': 0.299, - 'WACC': 0.0, + 'WACC': 0.058, 'learning_rate': 0.00, 'Capex_init': 222.64, 'Capex_init_unit': '$/MWh', From 730fd0f43d30dac91c79cb8b445f361b7c2c343a Mon Sep 17 00:00:00 2001 From: sostrades-dpeltre Date: Thu, 24 Oct 2024 00:39:45 +0200 Subject: [PATCH 17/19] update platform-version to v4.1.3 --- platform_version_required.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform_version_required.txt b/platform_version_required.txt index b572ab0b..8a58a0dc 100644 --- a/platform_version_required.txt +++ b/platform_version_required.txt @@ -1 +1 @@ -v4.1.2 \ No newline at end of file +v4.1.3 \ No newline at end of file From 548466cce36e256d734f5ed468363aa96f38c123 Mon Sep 17 00:00:00 2001 From: perrotcap Date: Thu, 24 Oct 2024 01:02:15 +0200 Subject: [PATCH 18/19] updated pickles to fix tests --- ...an_FossilSimpleTechno_FossilSimpleTechno.pkl | Bin 855 -> 859 bytes ..._FossilSimpleTechno_construction_delay_0.pkl | Bin 855 -> 859 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/energy_models/tests/jacobian_pkls/jacobian_FossilSimpleTechno_FossilSimpleTechno.pkl b/energy_models/tests/jacobian_pkls/jacobian_FossilSimpleTechno_FossilSimpleTechno.pkl index e48084062841cde0773848199620b29cf5170aee..ec4923d6c5b4a5b67e716c9b76b64988f84e7e77 100644 GIT binary patch literal 859 zcmV-h1El;yT4*^jL0KkKS;~knHUKa7fB*l#A7-cb6h+-1+(f_c|NcYdPatpr00c1N z5Ma>A00__nUI6vB0c;HwG$Txn4Af|7(?*P&AVWP05sD;003wJ z00x6bAOML-3TT<8lhqzgsv3HprkZ4WhL2Mxrl12r^#P&!qbH!71ppqIBsa2(ASuql z${i>}rVXB4PMFqrGUYw)KRZmQ(r~*$nv~!VSaBl{0 zGQnP|f$)YA8j)rp5%9B9NUK8|YjP zKS*i(p#*?h$Fa5%+6#cJ7}iu)R2P@w51~%UQmVkIhk&_Nhe8q))#;k1I3U~$#6d4K zh8ZTs1}38~FBxze7<@qxSh8_X6=0#Lcj{a1%15w~riwJ2lIZH12`Ur7T3)5qaZD43 zbE>80z2fs6b|lLmSxMWmu}5c0#X`k20m%3 zQ$JL&yf{xMGP4>Gym7P#P$3CeI4}oKG}O+w)#B(dgdqq*GEZ8AZjG3^*O~^2&`!F6 zio9AFaG2>)k6vWSlO{}-pvsgDG}-Q|`;ihwH@bp|3UmCSUd6-+MT7zb5WXq=?0AV+ zGD3Luso0$vs4(pri*%kIfD)1GG7P{7Kt-kl@4Ny8MnH=qry=N>nW8U+g66&C;kRIU zmeCRfb90YBkDBHm_Zoqt-&4bHDuFyE%x2XBk&q;iB0{ZjNM%G9n*h4VXcqtg literal 855 zcmV-d1E~B$T4*^jL0KkKS@CUl2LLb!|M&l|A7-cbG)0}C-$cLf|NckhP9SgrAmR|< z@XP=R&;woo4ueGoibw{Hi8iK(s5HPpG6o5!s2GexO$>pifHDDuW|KpJ000000004? z00000000026q-z$U`7bj0464lJs{HoFaRS0Xu@RCfiNQgLx2DP000000iXZ?00000 z009z^O%RFUO_XGKP-NPNs2{3m0QCTDkZnh)mS(>B1Tz zAt50lAT&Vc-S>aC76_9VD56})0|YK$B8BLnE$D!^SgOzkR}=tMM)Uzx0Oc@&Mg&GG z`t(%r+r`rn7;*ZWisc$o<7C=hXT1CdNS_)KwbT$1Ec;Jb2N^F#shS|B6RKoSNAoJ0x& zorm$yWCoc4zX3iKcQG77`Mg+OQrfts`NOU9DRI;dgR*vx*|vJcmRV(Xd1-C7+ikY? zkzk9!A&WHB=82cgeBtX3Wpy#(a}*8*Gsi4?8QI}j&@g8H_8kE#ALl~qjR7($$zpL+ zd~%BPa);D10)diZ;u$3@$ubK=gv+SLLKlY_pg7?OO2NN;Jq!juCAvIWtRV;t7%*gJ zdB_5~O64|pfe@I#5~9eW(m%XY?pOB`5us1_f>nt-{a@;s^_ZoL(${BiwF^6S>88B zW@-y#$S!N!(9uN$R?30^);5YVw9v0Y>3NY3TQbxLW`s>KEz&k37;xv|!|hrDFnlTo hDafF1nhFOql}pZ&fdBy$#y&Iu7ji{7P>}I$b_eFTZV>A00__nUI6vB0c;HwG$Txn4Af|7(?*P&AVWP05sD;003wJ z00x6bAOML-3TT<8lhqzgsv3HprkZ4WhL2Mxrl12r^#P&!qbH!71ppqIBsa2(ASuql z${i>}rVXB4PMFqrGUYw)KRZmQ(r~*$nv~!VSaBl{0 zGQnP|f$)YA8j)rp5%9B9NUK8|YjP zKS*i(p#*?h$Fa5%+6#cJ7}iu)R2P@w51~%UQmVkIhk&_Nhe8q))#;k1I3U~$#6d4K zh8ZTs1}38~FBxze7<@qxSh8_X6=0#Lcj{a1%15w~riwJ2lIZH12`Ur7T3)5qaZD43 zbE>80z2fs6b|lLmSxMWmu}5c0#X`k20m%3 zQ$JL&yf{xMGP4>Gym7P#P$3CeI4}oKG}O+w)#B(dgdqq*GEZ8AZjG3^*O~^2&`!F6 zio9AFaG2>)k6vWSlO{}-pvsgDG}-Q|`;ihwH@bp|3UmCSUd6-+MT7zb5WXq=?0AV+ zGD3Luso0$vs4(pri*%kIfD)1GG7P{7Kt-kl@4Ny8MnH=qry=N>nW8U+g66&C;kRIU zmeCRfb90YBkDBHm_Zoqt-&4bHDuFyE%x2XBk&q;iB0{ZjNM%G9n*h4VXcqtg literal 855 zcmV-d1E~B$T4*^jL0KkKS@CUl2LLb!|M&l|A7-cbG)0}C-$cLf|NckhP9SgrAmR|< z@XP=R&;woo4ueGoibw{Hi8iK(s5HPpG6o5!s2GexO$>pifHDDuW|KpJ000000004? z00000000026q-z$U`7bj0464lJs{HoFaRS0Xu@RCfiNQgLx2DP000000iXZ?00000 z009z^O%RFUO_XGKP-NPNs2{3m0QCTDkZnh)mS(>B1Tz zAt50lAT&Vc-S>aC76_9VD56})0|YK$B8BLnE$D!^SgOzkR}=tMM)Uzx0Oc@&Mg&GG z`t(%r+r`rn7;*ZWisc$o<7C=hXT1CdNS_)KwbT$1Ec;Jb2N^F#shS|B6RKoSNAoJ0x& zorm$yWCoc4zX3iKcQG77`Mg+OQrfts`NOU9DRI;dgR*vx*|vJcmRV(Xd1-C7+ikY? zkzk9!A&WHB=82cgeBtX3Wpy#(a}*8*Gsi4?8QI}j&@g8H_8kE#ALl~qjR7($$zpL+ zd~%BPa);D10)diZ;u$3@$ubK=gv+SLLKlY_pg7?OO2NN;Jq!juCAvIWtRV;t7%*gJ zdB_5~O64|pfe@I#5~9eW(m%XY?pOB`5us1_f>nt-{a@;s^_ZoL(${BiwF^6S>88B zW@-y#$S!N!(9uN$R?30^);5YVw9v0Y>3NY3TQbxLW`s>KEz&k37;xv|!|hrDFnlTo hDafF1nhFOql}pZ&fdBy$#y&Iu7ji{7P>}I$b_eFTZV> Date: Thu, 24 Oct 2024 12:15:40 +0200 Subject: [PATCH 19/19] added non use capital objective in energy mix --- .pre-commit-config.yaml | 53 +++++++++++++-- energy_models/core/energy_mix/energy_mix.py | 49 ++++++++----- .../core/energy_mix/energy_mix_disc.py | 68 +++++++++++-------- .../energy_mix_optim_sub_process/usecase.py | 21 +++--- 4 files changed, 129 insertions(+), 62 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b803e6b7..339dd803 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,10 +1,51 @@ ---- -ci: - autofix_commit_msg: "Chore: pre-commit autoupdate" - repos: - + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.5.0 + hooks: + - id: ruff + args: [ + --fix, + --preview, + --exit-non-zero-on-fix, + --config=ruff.toml, + ] - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v4.5.0 hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + exclude: LICENSES/headers + - id: check-yaml + # !reference is specific to gitlab + # !! prefix is specific to mkdocs + exclude: \.gitlab-ci.yml|mkdocs.yml - id: check-added-large-files + - id: check-json + - id: pretty-format-json + args: [ + --autofix, + --no-sort-keys, + ] + exclude: \.ipynb + - id: check-toml + - id: destroyed-symlinks + - id: check-symlinks + - repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.10.0 + hooks: + - id: rst-backticks + - id: rst-directive-colons + - id: rst-inline-touching-normal + - repo: https://github.com/kynan/nbstripout + rev: 0.7.1 + hooks: + - id: nbstripout + - repo: https://github.com/igorshubovych/markdownlint-cli + rev: v0.39.0 + hooks: + - id: markdownlint + args: [ + --fix, + --disable, + MD024, + ] diff --git a/energy_models/core/energy_mix/energy_mix.py b/energy_models/core/energy_mix/energy_mix.py index 28649b76..64db5c15 100644 --- a/energy_models/core/energy_mix/energy_mix.py +++ b/energy_models/core/energy_mix/energy_mix.py @@ -134,9 +134,10 @@ class EnergyMix(BaseStream): def __init__(self, name): ''' - Constructor + Constructor ''' super(EnergyMix, self).__init__(name) + self.non_use_capital_obj = None self.period_tol_power_non_use_capital_constraint = None self.non_use_capital_constraint_df = None self.target_production_constraint = None @@ -199,7 +200,7 @@ def __init__(self, name): def configure(self, inputs_dict): ''' - Configure method + Configure method ''' self.configure_parameters(inputs_dict) self.configure_parameters_update(inputs_dict) @@ -487,7 +488,7 @@ def compute_energy_production_uncut(self): def compute_net_prod_of_coarse_energies(self, energy, column_name): ''' Compute the net production for coarse energies which does not have energy consumption - We use a raw/net ratio to compute consumed energy production + We use a raw/net ratio to compute consumed energy production consu = raw-net = raw(1-1/ratio) ''' try: @@ -521,7 +522,7 @@ def compute_CO2_emissions_ratio(self): self.carbon_emissions_after_use[stream] = self.total_carbon_emissions[stream] + \ self.co2_emitted_by_energy[stream][GlossaryEnergy.CO2PerUse] else: - self.total_carbon_emissions[stream] = 0. # todo: fixme, Antoine: shouldnt we compute emissions for each stream, even ccs ones ? + self.total_carbon_emissions[stream] = 0. # todo: fixme, Antoine: shouldnt we compute emissions for each stream, even ccs ones ? def compute_CO2_emissions(self): ''' @@ -566,7 +567,7 @@ def compute_CO2_emissions(self): ''' CARBON CAPTURE needed by energy mix Total carbon capture needed by energy mix if a technology needs carbon_capture - Ex :Sabatier process or RWGS in FischerTropsch technology + Ex :Sabatier process or RWGS in FischerTropsch technology ''' energy_needing_carbon_capture = self.co2_consumption[[ col for col in self.co2_consumption if col.endswith(f'{GlossaryEnergy.carbon_capture} ({GlossaryEnergy.mass_unit})')]] @@ -748,7 +749,7 @@ def compute_syngas_prod_constraint(self): self.syngas_prod_constraint = np.zeros(len(self.years)) def compute_all_streams_demand_ratio(self): - '''! Computes the demand_ratio dataframe. + '''! Computes the demand_ratio dataframe. The ratio is calculated using the production and consumption WITHOUT the ratio applied The value of the ratio is capped to 100.0 ''' @@ -845,7 +846,7 @@ def compute_grad_CO2_emissions(self): # 0.0, self.production[f'production {energy} # ({self.energy_class_dict[energy].unit})'].values) - ''' CARBON STORAGE + ''' CARBON STORAGE Total carbon storage is production of carbon storage Solid carbon is gaseous equivalent in the production for solidcarbonstorage technology @@ -859,7 +860,7 @@ def compute_grad_CO2_emissions(self): # else: # self.total_co2_emissions[f'{GlossaryEnergy.carbon_storage} ({GlossaryEnergy.mass_unit})'] = 0.0 - ''' CARBON CAPTURE from CC technos + ''' CARBON CAPTURE from CC technos Total carbon capture = carbon captured from carboncapture stream + carbon captured from energies (can be negative if FischerTropsch needs carbon captured) @@ -896,7 +897,7 @@ def compute_grad_CO2_emissions(self): ''' CARBON CAPTURE needed by energy mix Total carbon capture needed by energy mix if a technology needs carbon_capture - Ex :Sabatier process or RWGS in FischerTropsch technology + Ex :Sabatier process or RWGS in FischerTropsch technology ''' energy_needing_carbon_capture = co2_consumption[[ col for col in co2_consumption if col.endswith(f'{GlossaryEnergy.carbon_capture} ({GlossaryEnergy.mass_unit})')]] @@ -913,9 +914,9 @@ def compute_grad_CO2_emissions(self): # self.total_co2_emissions[ # f'{GlossaryEnergy.carbon_capture} needed by energy mix (Mt)'] = 0.0 - ''' CO2 from energy mix - CO2 expelled by energy mix technologies during the process - i.e. for machinery or tractors + ''' CO2 from energy mix + CO2 expelled by energy mix technologies during the process + i.e. for machinery or tractors ''' energy_producing_co2 = co2_production[[ col for col in co2_production if col.endswith(f'{GlossaryEnergy.carbon_capture} ({GlossaryEnergy.mass_unit})')]] @@ -932,8 +933,8 @@ def compute_grad_CO2_emissions(self): # self.total_co2_emissions[ # f'{GlossaryEnergy.carbon_capture} from energy mix (Mt)'] = 0.0 - ''' CO2 removed by energy mix - CO2 removed by energy mix technologies during the process + ''' CO2 removed by energy mix + CO2 removed by energy mix technologies during the process i.e. biomass processes as managed wood or crop energy ''' energy_removing_co2 = co2_consumption[[ @@ -951,7 +952,7 @@ def compute_grad_CO2_emissions(self): # f'{GlossaryEnergy.carbon_capture} removed energy mix (Mt)'] = 0.0 ''' Total C02 from Flue gas - sum of all production of flue gas + sum of all production of flue gas it could be equal to carbon capture from CC technos if enough investment but not sure ''' # self.total_co2_emissions[f'Total {CarbonCapture.flue_gas_name} ({GlossaryEnergy.mass_unit})'] = self.co2_production[[ @@ -965,7 +966,7 @@ def compute_grad_CO2_emissions(self): f'Total {CarbonCapture.flue_gas_name} ({GlossaryEnergy.mass_unit}) vs {energy1}#{CarbonCapture.flue_gas_name} ({GlossaryEnergy.mass_unit})#prod'] = np.ones( len_years) ''' Carbon captured that needs to be stored - sum of the one from CC technos and the one directly captured + sum of the one from CC technos and the one directly captured we delete the one needed by energy mix and potentially later the CO2 for food ''' @@ -1007,6 +1008,7 @@ def compute(self, inputs: dict, exp_min=True): self.aggregate_land_use_required() self.compute_energy_capital() self.compute_non_use_energy_capital_constraint() + self.compute_non_use_energy_capital_objective() self.compute_total_prod_minus_min_prod_constraint() self.compute_constraint_solid_fuel_elec() self.compute_constraint_h2() @@ -1024,7 +1026,7 @@ def compute(self, inputs: dict, exp_min=True): def compute_energy_mean_price_objective(self): self.energy_mean_price_objective = np.array([ - self.energy_mean_price[GlossaryEnergy.EnergyPriceValue].mean() / self.energy_mean_price_objective_ref]) + self.energy_mean_price[GlossaryEnergy.EnergyPriceValue].mean() / self.energy_mean_price_objective_ref]) def d_energy_mean_price_obj_d_energy_mean_price(self, d_energy_mean_price): return np.mean(d_energy_mean_price, axis=0) / self.energy_mean_price_objective_ref @@ -1049,6 +1051,18 @@ def compute_non_use_energy_capital_constraint(self): GlossaryEnergy.ConstraintEnergyNonUseCapital: constraint }) + def compute_non_use_energy_capital_objective(self): + """to minimize""" + ratio_non_use_capital = self.energy_capital[GlossaryEnergy.NonUseCapital].values / self.energy_capital[GlossaryEnergy.Capital].values + self.non_use_capital_obj = np.array([ratio_non_use_capital.mean()]) + + def d_non_use_capital_obj_d_capital(self): + capital = self.energy_capital[GlossaryEnergy.Capital].values + non_use_capital = self.energy_capital[GlossaryEnergy.NonUseCapital].values + d_non_use_capital = 1 / capital / len(self.years) / 1e3 + d_capital = - non_use_capital / (capital ** 2) / len(self.years) / 1e3 + return d_non_use_capital, d_capital + def d_non_use_capital_constraint_d_capital(self): """ @@ -1062,6 +1076,7 @@ def d_non_use_capital_constraint_d_capital(self): d_capital = np.diag(- non_use_capital * period_tolerance / (capital ** 2) / self.ref_constraint_non_use_capital_energy / 1e3) return d_non_use_capital, d_capital + def update_new_gradient(grad_dict, key_dep_tuple_list, new_key): ''' Update new gradient which are dependent of old ones by simple sum or difference diff --git a/energy_models/core/energy_mix/energy_mix_disc.py b/energy_models/core/energy_mix/energy_mix_disc.py index 4cb0f56d..0d14ed6f 100644 --- a/energy_models/core/energy_mix/energy_mix_disc.py +++ b/energy_models/core/energy_mix/energy_mix_disc.py @@ -133,12 +133,12 @@ class Energy_Mix_Discipline(SoSWrapp): 'visibility': SoSWrapp.SHARED_VISIBILITY, 'namespace': 'ns_public'}, 'solid_fuel_elec_percentage': {'type': 'float', 'default': 0.75, 'unit': '-', 'user_level': 2}, - 'solid_fuel_elec_constraint_ref': {'type': 'float', 'default': 10000., 'unit': 'Twh', 'user_level': 2,}, - 'liquid_hydrogen_percentage': {'type': 'array', 'user_level': 2, 'unit': '%',}, - 'liquid_hydrogen_constraint_ref': {'type': 'float', 'default': 1000., 'unit': 'Twh', 'user_level': 2,}, - 'ref_constraint_non_use_capital_energy': {'type': 'float', 'default': 0.30, 'unit':'-','description': '0.30 means after 35 % of capital not used the constraint will explode'}, - 'tol_constraint_non_use_capital_energy': {'type': 'float', 'default': 0.05, 'unit':'-','description': '0.05 means constraint does not penalize lagrangian when non use capital is less than 5%'}, - 'period_tol_power_non_use_capital_constraint': {'type': 'float', 'default': 1.0, 'unit':'-','description': '0.05 means constraint does not penalize lagrangian when non use capital is less than 5%'}, + 'solid_fuel_elec_constraint_ref': {'type': 'float', 'default': 10000., 'unit': 'Twh', 'user_level': 2, }, + 'liquid_hydrogen_percentage': {'type': 'array', 'user_level': 2, 'unit': '%', }, + 'liquid_hydrogen_constraint_ref': {'type': 'float', 'default': 1000., 'unit': 'Twh', 'user_level': 2, }, + 'ref_constraint_non_use_capital_energy': {'type': 'float', 'default': 0.30, 'unit': '-', 'description': '0.30 means after 35 % of capital not used the constraint will explode'}, + 'tol_constraint_non_use_capital_energy': {'type': 'float', 'default': 0.05, 'unit': '-', 'description': '0.05 means constraint does not penalize lagrangian when non use capital is less than 5%'}, + 'period_tol_power_non_use_capital_constraint': {'type': 'float', 'default': 1.0, 'unit': '-', 'description': '0.05 means constraint does not penalize lagrangian when non use capital is less than 5%'}, 'syngas_prod_ref': {'type': 'float', 'default': 10000., 'unit': 'TWh', 'user_level': 2}, 'syngas_prod_constraint_limit': {'type': 'float', 'default': 10000., 'unit': 'TWh', 'user_level': 2}, 'ratio_ref': {'type': 'float', 'default': 500., 'unit': '-', 'user_level': 2}, @@ -196,6 +196,7 @@ class Energy_Mix_Discipline(SoSWrapp): GlossaryEnergy.EnergyCapitalDfValue: GlossaryEnergy.EnergyCapitalDf, GlossaryEnergy.EnergyMeanPriceObjectiveValue: GlossaryEnergy.EnergyMeanPriceObjective, GlossaryEnergy.ConstraintEnergyNonUseCapital: {'type': 'dataframe', 'unit': '-', 'visibility': ClimateEcoDiscipline.SHARED_VISIBILITY, 'namespace': GlossaryEnergy.NS_FUNCTIONS}, + GlossaryEnergy.ObjectiveEnergyNonUseCapital: {'type': 'array', 'unit': '-', 'visibility': ClimateEcoDiscipline.SHARED_VISIBILITY, 'namespace': GlossaryEnergy.NS_FUNCTIONS}, } energy_name = EnergyMix.name @@ -314,24 +315,24 @@ def setup_sos_disciplines(self): dynamic_inputs[f'{ccs_name}.{GlossaryEnergy.StreamConsumptionValue}'] = { 'type': 'dataframe', 'unit': 'PWh', 'visibility': SoSWrapp.SHARED_VISIBILITY, 'namespace': GlossaryEnergy.NS_CCS, - 'dynamic_dataframe_columns': True,} + 'dynamic_dataframe_columns': True, } dynamic_inputs[f'{ccs_name}.{GlossaryEnergy.StreamConsumptionWithoutRatioValue}'] = { 'type': 'dataframe', 'unit': 'PWh', 'visibility': SoSWrapp.SHARED_VISIBILITY, 'namespace': GlossaryEnergy.NS_CCS, - 'dynamic_dataframe_columns': True,} + 'dynamic_dataframe_columns': True, } dynamic_inputs[f'{ccs_name}.{GlossaryEnergy.EnergyProductionValue}'] = { 'type': 'dataframe', 'unit': 'PWh', 'visibility': SoSWrapp.SHARED_VISIBILITY, 'namespace': GlossaryEnergy.NS_CCS, - 'dynamic_dataframe_columns': True,} + 'dynamic_dataframe_columns': True, } dynamic_inputs[f'{ccs_name}.{GlossaryEnergy.StreamPricesValue}'] = { 'type': 'dataframe', 'unit': '$/MWh', 'visibility': SoSWrapp.SHARED_VISIBILITY, 'namespace': GlossaryEnergy.NS_CCS, - 'dynamic_dataframe_columns': True,} + 'dynamic_dataframe_columns': True, } dynamic_inputs[f'{ccs_name}.{GlossaryEnergy.LandUseRequiredValue}'] = { 'type': 'dataframe', 'unit': 'Gha', 'visibility': SoSWrapp.SHARED_VISIBILITY, 'namespace': GlossaryEnergy.NS_CCS, - 'dynamic_dataframe_columns': True,} + 'dynamic_dataframe_columns': True, } if GlossaryEnergy.energy_list in self.get_data_in() and GlossaryEnergy.ccs_list in self.get_data_in(): energy_list = self.get_sosdisc_inputs(GlossaryEnergy.energy_list) @@ -349,7 +350,7 @@ def setup_sos_disciplines(self): def update_default_with_years(self, inputs_dict): ''' - Update default variables knowing the year start and the year end + Update default variables knowing the year start and the year end ''' if GlossaryEnergy.YearStart in self.get_data_in(): year_start, year_end = self.get_sosdisc_inputs([GlossaryEnergy.YearStart, GlossaryEnergy.YearEnd]) @@ -379,7 +380,6 @@ def run(self): [(1. - alpha) * self.energy_model.production[GlossaryEnergy.TotalProductionValue][0] * delta_years / self.energy_model.production[GlossaryEnergy.TotalProductionValue].sum(), ]) - if EnergyMix.PRODUCTION in self.energy_model.stream_prices: self.energy_model.stream_prices.drop( columns=[EnergyMix.PRODUCTION], inplace=True) @@ -421,6 +421,7 @@ def run(self): GlossaryEnergy.TargetProductionConstraintValue: self.energy_model.target_production_constraint, GlossaryEnergy.EnergyMeanPriceObjectiveValue: self.energy_model.energy_mean_price_objective, GlossaryEnergy.ConstraintEnergyNonUseCapital: self.energy_model.non_use_capital_constraint_df, + GlossaryEnergy.ObjectiveEnergyNonUseCapital: self.energy_model.non_use_capital_obj, } primary_energy_percentage = inputs_dict['primary_energy_percentage'] @@ -514,6 +515,7 @@ def compute_sos_jacobian(self): # ---- Production / Consumption gradients----# # -------------------------------------------# d_non_use_capital, d_capital = self.energy_model.d_non_use_capital_constraint_d_capital() + d_non_use_capital_obj, d_capital_obj = self.energy_model.d_non_use_capital_obj_d_capital() for stream in inputs_dict[GlossaryEnergy.energy_list] + inputs_dict[GlossaryEnergy.ccs_list]: ns_stream = self.get_ns_stream(stream) self.set_partial_derivative_for_other_types( @@ -539,11 +541,23 @@ def compute_sos_jacobian(self): d_non_use_capital ) + self.set_partial_derivative_for_other_types( + (GlossaryEnergy.ObjectiveEnergyNonUseCapital,), + (f'{ns_stream}.{GlossaryEnergy.EnergyTypeCapitalDfValue}', GlossaryEnergy.Capital), + d_capital_obj + ) + + self.set_partial_derivative_for_other_types( + (GlossaryEnergy.ObjectiveEnergyNonUseCapital,), + (f'{ns_stream}.{GlossaryEnergy.EnergyTypeCapitalDfValue}', GlossaryEnergy.NonUseCapital), + d_non_use_capital_obj + ) + for stream in stream_list: ns_stream = self.get_ns_stream(stream) if stream in energies: - loss_percentage = 0#inputs_dict[f'{ns_stream}.losses_percentage'] / 100.0 # fixme : loss percentage by energy was considered in gradient but not in compute method so for the moment its also disabled in gradient to fix gradients + loss_percentage = 0 # inputs_dict[f'{ns_stream}.losses_percentage'] / 100.0 # fixme : loss percentage by energy was considered in gradient but not in compute method so for the moment its also disabled in gradient to fix gradients # To model raw to net percentage for witness coarse energies if stream in self.energy_model.raw_tonet_dict: loss_percentage += (1.0 - @@ -767,14 +781,14 @@ def compute_sos_jacobian(self): for stream_input in stream_list: ns_stream_input = self.get_ns_stream(stream_input) list_columns_energy_consumption = list(inputs_dict[f'{stream_input}.{GlossaryEnergy.StreamConsumptionValue}'].columns) - if f'{stream} ({GlossaryEnergy.unit_dicts[stream]})' in list_columns_energy_consumption:#F or stream_input == GlossaryEnergy.carbon_storage: + if f'{stream} ({GlossaryEnergy.unit_dicts[stream]})' in list_columns_energy_consumption: # F or stream_input == GlossaryEnergy.carbon_storage: self.set_partial_derivative_for_other_types( (GlossaryEnergy.StreamProductionDetailedValue, f'production {stream} ({GlossaryEnergy.unit_dicts[stream]})'), (f'{ns_stream_input}.{GlossaryEnergy.StreamConsumptionValue}', f'{stream} ({GlossaryEnergy.unit_dicts[stream]})'), -scaling_factor_energy_consumption * np.identity( - len(years)) / scaling_factor_energy_production * scaling_factor_energy_production* 0) + len(years)) / scaling_factor_energy_production * scaling_factor_energy_production * 0) - if f'{stream} ({GlossaryEnergy.unit_dicts[stream]})' in list_columns_energy_consumption:# or stream_input == GlossaryEnergy.carbon_capture: + if f'{stream} ({GlossaryEnergy.unit_dicts[stream]})' in list_columns_energy_consumption: # or stream_input == GlossaryEnergy.carbon_capture: self.set_partial_derivative_for_other_types( (GlossaryEnergy.StreamProductionDetailedValue, f'production {stream} ({GlossaryEnergy.unit_dicts[stream]})'), (f'{ns_stream_input}.{GlossaryEnergy.StreamConsumptionValue}', f'{stream} ({GlossaryEnergy.unit_dicts[stream]})'), @@ -1016,7 +1030,7 @@ def compute_sos_jacobian(self): if f'{stream} ({GlossaryEnergy.unit_dicts[stream]})' in list_columns_energy_consumption: self.set_partial_derivative_for_other_types( (GlossaryEnergy.AllStreamsDemandRatioValue, f'{stream}'), - (f'{ns_stream_input}.{GlossaryEnergy.StreamConsumptionWithoutRatioValue}',f'{stream} ({GlossaryEnergy.unit_dicts[stream]})'), + (f'{ns_stream_input}.{GlossaryEnergy.StreamConsumptionWithoutRatioValue}', f'{stream} ({GlossaryEnergy.unit_dicts[stream]})'), ddemand_ratio_denergy_cons) dobjective_dcons = np.matmul(dobjective_dratio_energy, ddemand_ratio_denergy_cons) self.set_partial_derivative_for_other_types( @@ -1085,7 +1099,7 @@ def set_gradient_for_co2_emissions(self, co2_variable, co2_emissions, co2_emissi def compute_dratio_objective(self, stream_ratios, ratio_ref, energy_list): ''' - Compute the ratio_objective with the gradient of stream_ratios vs any input and the ratio ojective value + Compute the ratio_objective with the gradient of stream_ratios vs any input and the ratio ojective value obj = smooth_maximum(100.0 - ratio_arrays, 3)/ratio_ref dobj/dratio = -dsmooth_max(100.0 - ratio_arrays, 3)/ratio_ref @@ -1164,8 +1178,8 @@ def compute_ddemand_ratio_denergy_production(self, energy, sub_production_dict, '''! Compute the gradient of the demand ratio vs energy production function : -the ratio is capped to one if energy_prod>energy_cons, hence the special condition. -the function is designed to be used even if no energy_input is specified (to get ddemand_ratio_denergy_prod gradient alone) - @param energy: string, name of the energy - @param sub_production_dict: dictionary with the raw production for all the energies + @param energy: string, name of the energy + @param sub_production_dict: dictionary with the raw production for all the energies @param sub_consumption_dict: dictionary with the raw consumption for all energies @param scaling_factor_production: float used to scale the energy production at input/output of the model @return ddemand_ratio_denergy_prod, ddemand_ratio_denergy_cons: numpy.arrays, shape=(len(years),len(years)) with the gradients @@ -1232,13 +1246,13 @@ def compute_dmean_price_dprod(self, energy, energies, mix_weight, energy_price_a production_energy_net_pos_consumable, production_detailed_df, cons=False): """ Function that returns the gradient of mean_price compared to energy_prod - Params: + Params: - energy: name of the energy derived by - energies: list of all the energies - mix_weight: dataframe of the energies ratio - - energy_price_after_tax: dataframe with values + - energy_price_after_tax: dataframe with values - production_energy_net_pos_consumable: dataframe with values - - production_detailed_df: dataframe with values + - production_detailed_df: dataframe with values Output: - dmean_price_dprod """ @@ -1862,7 +1876,7 @@ def get_chart_co2_limited_storage(self): def get_chart_co2_emissions_sources(self): ''' - Plot all CO2 emissions sources + Plot all CO2 emissions sources ''' chart_name = 'CO2 emissions sources' co2_emissions = self.get_sosdisc_outputs('co2_emissions') @@ -1903,7 +1917,7 @@ def get_chart_co2_emissions_sources(self): def get_chart_co2_needed_by_energy_mix(self): ''' - Plot all CO2 emissions sinks + Plot all CO2 emissions sinks ''' chart_name = 'CO2 emissions sinks' co2_emissions = self.get_sosdisc_outputs( @@ -1951,7 +1965,7 @@ def get_chart_stream_ratio(self): def get_chart_energy_mix_losses(self, energy_list): ''' - Plot chart on energy mix heat losses + Plot chart on energy mix heat losses ''' chart_name = 'Energy mix losses' diff --git a/energy_models/sos_processes/energy/MDA/energy_mix_optim_sub_process/usecase.py b/energy_models/sos_processes/energy/MDA/energy_mix_optim_sub_process/usecase.py index 302ea147..a95b402e 100644 --- a/energy_models/sos_processes/energy/MDA/energy_mix_optim_sub_process/usecase.py +++ b/energy_models/sos_processes/energy/MDA/energy_mix_optim_sub_process/usecase.py @@ -104,7 +104,6 @@ def __init__( self.bspline = bspline self.invest_discipline = INVEST_DISCIPLINE_OPTIONS[2] self.test_post_procs = False - def create_study_list(self): self.sub_study_dict = {} @@ -235,7 +234,7 @@ def setup_usecase_sub_study_list(self, merge_design_spaces=False): instanced_sub_studies = [] dspace_list = [] for sub_study_name, sub_study in self.sub_study_dict.items(): - instance_sub_study = None # initialize variable + instance_sub_study = None # initialize variable if self.techno_dict[sub_study_name][GlossaryEnergy.stream_type] == GlossaryEnergy.ccus_type: prefix_name = f"{GlossaryEnergy.ccus_type}" instance_sub_study = sub_study( @@ -320,7 +319,6 @@ def get_dvar_dscriptor(self): 'namespace_out': GlossaryEnergy.NS_WITNESS } - for ccs in self.ccs_list: ccs_wo_dot = ccs.replace('.', '_') for technology in self.dict_technos[ccs]: @@ -407,12 +405,12 @@ def make_dspace_utilisation_ratio(self) -> pd.DataFrame: def make_func_df(self): func_df = pd.DataFrame({ - "variable": [GlossaryEnergy.CO2EmissionsObjectiveValue, GlossaryEnergy.TargetProductionConstraintValue, GlossaryEnergy.MaxBudgetConstraintValue,], - "parent": ["objectives", "constraints", "constraints"], - "ftype": [FunctionManagerDisc.OBJECTIVE, FunctionManagerDisc.INEQ_CONSTRAINT, FunctionManagerDisc.INEQ_CONSTRAINT] , - "weight": [1.0, .0, 100.0,], - FunctionManagerDisc.AGGR_TYPE: [FunctionManager.AGGR_TYPE_SUM, FunctionManager.INEQ_NEGATIVE_WHEN_SATIFIED_AND_SQUARE_IT, FunctionManager.INEQ_NEGATIVE_WHEN_SATIFIED_AND_SQUARE_IT,], - "namespace": [GlossaryEnergy.NS_FUNCTIONS, GlossaryEnergy.NS_FUNCTIONS, GlossaryEnergy.NS_FUNCTIONS,] + "variable": [GlossaryEnergy.CO2EmissionsObjectiveValue, GlossaryEnergy.CO2EmissionsObjectiveValue, GlossaryEnergy.TargetProductionConstraintValue, GlossaryEnergy.MaxBudgetConstraintValue,], + "parent": ["objectives", "objectives", "constraints", "constraints"], + "ftype": [FunctionManagerDisc.OBJECTIVE, FunctionManagerDisc.OBJECTIVE, FunctionManagerDisc.INEQ_CONSTRAINT, FunctionManagerDisc.INEQ_CONSTRAINT], + "weight": [1.0, .0, .0, 100.0,], + FunctionManagerDisc.AGGR_TYPE: [FunctionManager.AGGR_TYPE_SUM, FunctionManager.AGGR_TYPE_SUM, FunctionManager.INEQ_NEGATIVE_WHEN_SATIFIED_AND_SQUARE_IT, FunctionManager.INEQ_NEGATIVE_WHEN_SATIFIED_AND_SQUARE_IT,], + "namespace": [GlossaryEnergy.NS_FUNCTIONS] * 4 }) return func_df @@ -580,7 +578,6 @@ def setup_usecase(self, study_folder_path=None): "indus_emissions": 0. }) - target_energy_prod = pd.DataFrame({ GlossaryEnergy.Years: self.years, GlossaryEnergy.TargetEnergyProductionValue: np.linspace(100. * 1.e3, 150. * 1e3, len(self.years)) @@ -596,7 +593,6 @@ def setup_usecase(self, study_folder_path=None): GlossaryEnergy.TotalProductionValue: 0. }) - values_dict = { f"{self.study_name}.{GlossaryEnergy.YearStart}": self.year_start, f"{self.study_name}.{GlossaryEnergy.YearEnd}": self.year_end, @@ -709,4 +705,5 @@ def setup_usecase(self, study_folder_path=None): if "__main__" == __name__: uc_cls = Study() - uc_cls.test_jacobians_of_each_disc() + uc_cls.load_data() + uc_cls.run()