diff --git a/climateeconomics/core/core_forest/forest.py b/climateeconomics/core/core_forest/forest.py new file mode 100644 index 000000000..217872dbe --- /dev/null +++ b/climateeconomics/core/core_forest/forest.py @@ -0,0 +1,413 @@ +''' +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 autograd.numpy as np +from energy_models.core.stream_type.energy_models.biomass_dry import BiomassDry +from energy_models.glossaryenergy import GlossaryEnergy + +from climateeconomics.core.tools.differentiable_model import DifferentiableModel +from climateeconomics.glossarycore import GlossaryCore + + +class ForestAutodiff(DifferentiableModel): + """ + Forest model class + basic for now, to evolve + + """ + + def compute(self): + """Computation methods""" + + self.initialize_years() + self.compute_yields() + self.compute_managed_wood_surface() + self.compute_reforestation_deforestation_surface() + self.compute_capital_loss() + self.compute_deforestation_biomass() + self.compute_managed_wood_production() + self.sumup_global_surface_data() + self.compute_global_CO2_production() + self.compute_biomass_dry_production() + + self.compute_land_use_required() + + + self.compute_forest_constraint_evolution() + self.compute_production_for_energy() + self.compute_price_in_d_per_mwh() + self.compute_carbon_emissions() + self.compute_carbon_consumption() + + self.compute_economical_output_and_damages() + self.rescale_techno_production_and_consumption() + self.compute_coupling_dfs() + + return self.get_dataframes() + + def compute_managed_wood_surface(self): + """ + compute managed wood delta and cumulative surface from investments + Will be recalculate with deforestation impact in compute_reforestation_deforestation_surface method + """ + construction_delay = self.inputs['params'][GlossaryCore.ConstructionDelay] + mw_cost = self.inputs['params']['managed_wood_price_per_ha'] + # managed wood from past invest. invest in G$ - surface in Gha. + mw_from_past_invest = self.inputs[f'managed_wood_invest_before_year_start:{GlossaryCore.InvestmentsValue}'] / mw_cost + # managed wood from actual invest + mw_from_invest = self.inputs[f'managed_wood_investment:{GlossaryCore.InvestmentsValue}'] / mw_cost + # concat all managed wood form invest + managed_wood_yearly_surface_variation = np.concatenate([mw_from_past_invest, mw_from_invest]) + # remove value that exceed year_end + managed_wood_yearly_surface_variation = managed_wood_yearly_surface_variation[:-construction_delay] + self.outputs['managed_wood_df:delta_surface'] = managed_wood_yearly_surface_variation + managed_wood_total_surface = self.inputs['managed_wood_initial_surface'] + np.cumsum(managed_wood_yearly_surface_variation) + self.outputs['managed_wood_df:cumulative_surface'] = managed_wood_total_surface + + def compute_managed_wood_production(self): + """ + compute data concerning managed wood : surface taken, production, CO2 absorbed, as delta and cumulative + """ + + # Biomass production part + # Gha * m3/ha + cubic_meter_production_wo_climate_change = (self.inputs['managed_wood_initial_surface'] * self.inputs['params']['actual_yield_year_start'] + + (self.outputs['managed_wood_df:cumulative_surface'] - self.inputs['managed_wood_initial_surface']) + * self.inputs['params']['managed_wood_yield_year_start']) + cubic_meter_production = (self.inputs['managed_wood_initial_surface'] * self.outputs['yields:actual'] + + (self.outputs['managed_wood_df:cumulative_surface'] - self.inputs['managed_wood_initial_surface']) + * self.outputs['yields:managed wood']) + wasted_production = cubic_meter_production_wo_climate_change - cubic_meter_production + self.outputs['managed_wood_df:delta_wood_production (Mt)'] = self.outputs['managed_wood_df:delta_surface'] * self.outputs['yields:managed wood'] * ( + 1 - self.inputs['params']['residues_density_percentage']) * self.inputs['params']['wood_density'] + # Gm3* kg/m3 => Mt + self.outputs['managed_wood_df:wood_production (Mt)'] = cubic_meter_production * (1 - self.inputs['params']['residues_density_percentage']) * self.inputs['params']['wood_density'] + self.outputs['managed_wood_df:wasted_wood_production (Mt)'] = wasted_production * (1 - self.inputs['params']['residues_density_percentage']) * self.inputs['params']['wood_density'] + + self.outputs['managed_wood_df:wood_production_for_energy (Mt)'] = self.outputs['managed_wood_df:wood_production (Mt)'] * self.inputs['params']['wood_percentage_for_energy'] + self.outputs['managed_wood_df:wood_production_for_industry (Mt)'] = self.outputs['managed_wood_df:wood_production (Mt)'] * (1 - self.inputs['params']['wood_percentage_for_energy']) + + self.outputs['managed_wood_df:delta_residues_production (Mt)'] = self.outputs['managed_wood_df:delta_surface'] * self.outputs['yields:managed wood'] * self.inputs['params']['residues_density_percentage'] * self.inputs['params']['residues_density'] + self.outputs['managed_wood_df:residues_production (Mt)'] = cubic_meter_production * self.inputs['params']['residues_density_percentage'] * self.inputs['params']['residues_density'] + self.outputs['managed_wood_df:wasted_residues_production (Mt)'] = wasted_production * self.inputs['params']['residues_density_percentage'] * self.inputs['params']['residues_density'] + self.outputs['managed_wood_df:residues_production_for_energy (Mt)'] = self.outputs['managed_wood_df:residues_production (Mt)'] * \ + self.inputs['params']['wood_percentage_for_energy'] + self.outputs['managed_wood_df:residues_production_for_industry (Mt)'] = self.outputs['managed_wood_df:residues_production (Mt)'] * \ + (1 - self.inputs['params']['wood_percentage_for_energy']) + + self.outputs['managed_wood_df:delta_biomass_production (Mt)'] = self.outputs['managed_wood_df:delta_wood_production (Mt)'] + \ + self.outputs['managed_wood_df:delta_residues_production (Mt)'] + self.outputs['managed_wood_df:biomass_production (Mt)'] = self.outputs['managed_wood_df:wood_production (Mt)'] + \ + self.outputs['managed_wood_df:residues_production (Mt)'] + + # CO2 part + self.outputs['managed_wood_df:delta_CO2_emitted'] = - \ + self.outputs['managed_wood_df:delta_surface'] * self.inputs['params']['CO2_per_ha'] / 1000 + # CO2 emitted is delta cumulate + self.outputs['managed_wood_df:CO2_emitted'] = - \ + (self.outputs['managed_wood_df:cumulative_surface'] - + self.inputs['managed_wood_initial_surface']) * self.inputs['params']['CO2_per_ha'] / 1000 + + def compute_reforestation_deforestation_surface(self): + """ + compute land use and due to reforestation et deforestation activities + CO2 is not computed here because surface limit need to be taken into account before. + """ + + # forest surface is in Gha, deforestation_surface is in Mha, + # deforested_surface is in Gha + self.outputs['forest_surface_detail_df:delta_deforestation_surface'] = - self.inputs[f'deforestation_investment:{GlossaryCore.InvestmentsValue}'] / self.inputs['params']['deforestation_cost_per_ha'] + + # forested surface + # invest in G$, coest_per_ha in $/ha --> Gha + self.outputs['forest_surface_detail_df:delta_reforestation_surface'] = self.inputs['reforestation_investment:reforestation_investment'] / self.inputs['params']['reforestation_cost_per_ha'] + + self.outputs['forest_surface_detail_df:deforestation_surface'] = np.cumsum(self.outputs['forest_surface_detail_df:delta_deforestation_surface']) + self.outputs['forest_surface_detail_df:reforestation_surface'] = np.cumsum(self.outputs['forest_surface_detail_df:delta_reforestation_surface']) + + delta_unmanaged_forest_surface = self.outputs['forest_surface_detail_df:delta_reforestation_surface'] + self.outputs['forest_surface_detail_df:delta_deforestation_surface'] + self.outputs['forest_surface_detail_df:unmanaged_forest'] = np.maximum(self.inputs['initial_unmanaged_forest_surface'] + np.cumsum(delta_unmanaged_forest_surface), 0) + + + def compute_deforestation_biomass(self): + """ + compute biomass produce by deforestation. It is a one time production. + We use actual yield because deforestation is done on actual forest not managed ones + """ + + deforestation_production_wo_climate_change = - self.outputs['forest_surface_detail_df:delta_deforestation_surface'] * self.inputs['params']['actual_yield_year_start'] * self.inputs['params']['wood_density'] + deforestation_production = - self.outputs['forest_surface_detail_df:delta_deforestation_surface'] * self.outputs['yields:unmanaged wood'] * self.inputs['params']['wood_density'] + self.outputs['biomass_dry_detail_df:deforestation (Mt)'] = deforestation_production + self.outputs['biomass_dry_detail_df:deforestation_wasted (Mt)'] = deforestation_production_wo_climate_change - deforestation_production + + self.outputs['biomass_dry_detail_df:deforestation_for_energy'] = self.outputs['biomass_dry_detail_df:deforestation (Mt)'] * \ + self.inputs['params']['wood_percentage_for_energy'] + self.outputs['biomass_dry_detail_df:deforestation_for_industry'] = self.outputs['biomass_dry_detail_df:deforestation (Mt)'] - \ + self.outputs['biomass_dry_detail_df:deforestation_for_energy'] + self.outputs['biomass_dry_detail_df:deforestation_price_per_ton'] = self.outputs['yields:unmanaged wood'] * self.inputs['params']['wood_density'] / self.inputs['params']['deforestation_cost_per_ha'] + self.outputs['biomass_dry_detail_df:deforestation_price_per_MWh'] = self.outputs['biomass_dry_detail_df:deforestation_price_per_ton'] / \ + self.inputs['params']['biomass_dry_calorific_value'] + + def sumup_global_surface_data(self): + """ + managed wood and unmanaged wood impact forest_surface_detail_df + """ + self.outputs['forest_surface_detail_df:delta_global_forest_surface'] = self.outputs['forest_surface_detail_df:delta_reforestation_surface'] + \ + self.outputs['forest_surface_detail_df:delta_deforestation_surface'] + self.outputs['forest_surface_detail_df:global_forest_surface'] = self.outputs['managed_wood_df:cumulative_surface'] + \ + self.outputs['forest_surface_detail_df:unmanaged_forest'] + \ + self.inputs['initial_protected_forest_surface'] + self.outputs['forest_surface_detail_df:protected_forest_surface'] = self.zeros_array + self.inputs['initial_protected_forest_surface'] + + def compute_global_CO2_production(self): + """ + compute the global CO2 production in Gt + """ + # in Gt of CO2 + + self.outputs[f'{GlossaryCore.CO2EmissionsDetailDfValue}:delta_CO2_emitted'] = -self.outputs['forest_surface_detail_df:delta_global_forest_surface'] * \ + self.inputs['params']['CO2_per_ha'] / 1000 + self.outputs[f'{GlossaryCore.CO2EmissionsDetailDfValue}:delta_CO2_deforestation'] = -self.outputs['forest_surface_detail_df:delta_deforestation_surface'] * \ + self.inputs['params']['CO2_per_ha'] / 1000 + self.outputs[f'{GlossaryCore.CO2EmissionsDetailDfValue}:delta_CO2_reforestation'] = -self.outputs['forest_surface_detail_df:delta_reforestation_surface'] * \ + self.inputs['params']['CO2_per_ha'] / 1000 + + # remove CO2 managed surface from global emission because CO2_per_ha + # from managed forest = 0 + self.outputs[f'{GlossaryCore.CO2EmissionsDetailDfValue}:CO2_deforestation'] = - self.outputs['forest_surface_detail_df:deforestation_surface'] * \ + self.inputs['params']['CO2_per_ha'] / 1000 + self.outputs[f'{GlossaryCore.CO2EmissionsDetailDfValue}:CO2_reforestation'] = -self.outputs['forest_surface_detail_df:reforestation_surface'] * \ + self.inputs['params']['CO2_per_ha'] / 1000 + self.outputs[f'{GlossaryCore.CO2EmissionsDetailDfValue}:initial_CO2_land_use_change'] = self.zeros_array + self.inputs['initial_co2_emissions'] + # global sum up + self.outputs[f'{GlossaryCore.CO2EmissionsDetailDfValue}:global_CO2_emitted'] = self.outputs[f'{GlossaryCore.CO2EmissionsDetailDfValue}:CO2_deforestation'] + \ + self.outputs[f'{GlossaryCore.CO2EmissionsDetailDfValue}:initial_CO2_land_use_change'] + self.outputs[f'{GlossaryCore.CO2EmissionsDetailDfValue}:global_CO2_captured'] = self.outputs[f'{GlossaryCore.CO2EmissionsDetailDfValue}:CO2_reforestation'] + self.outputs[f'{GlossaryCore.CO2EmissionsDetailDfValue}:emitted_CO2_evol_cumulative'] = self.outputs[f'{GlossaryCore.CO2EmissionsDetailDfValue}:global_CO2_emitted'] + \ + self.outputs[f'{GlossaryCore.CO2EmissionsDetailDfValue}:global_CO2_captured'] + + self.outputs[f'CO2_land_emission_df:{GlossaryCore.Years}'] = self.years + self.outputs['CO2_land_emission_df:emitted_CO2_evol_cumulative'] = self.outputs[f'{GlossaryCore.CO2EmissionsDetailDfValue}:emitted_CO2_evol_cumulative'] + + + + def compute_biomass_dry_production(self): + """ + compute total biomass dry prod + """ + + self.outputs['biomass_dry_detail_df:biomass_dry_for_energy (Mt)'] = self.outputs['managed_wood_df:wood_production_for_energy (Mt)'] + \ + self.outputs['managed_wood_df:residues_production_for_energy (Mt)'] + \ + self.outputs['biomass_dry_detail_df:deforestation_for_energy'] + + self.compute_price() + + self.managed_wood_part = self.outputs['managed_wood_df:biomass_production (Mt)'] / ( + self.outputs['managed_wood_df:biomass_production (Mt)'] + self.outputs['biomass_dry_detail_df:deforestation (Mt)']) + self.deforestation_part = self.outputs['biomass_dry_detail_df:deforestation (Mt)'] / ( + self.outputs['managed_wood_df:biomass_production (Mt)'] + self.outputs['biomass_dry_detail_df:deforestation (Mt)']) + + self.outputs['biomass_dry_detail_df:price_per_ton'] = self.outputs['biomass_dry_detail_df:managed_wood_price_per_ton'] * self.managed_wood_part + \ + self.outputs['biomass_dry_detail_df:deforestation_price_per_ton'] * \ + self.deforestation_part + + self.outputs['biomass_dry_detail_df:managed_wood_price_per_MWh'] = self.outputs['biomass_dry_detail_df:managed_wood_price_per_ton'] / \ + self.inputs['params']['biomass_dry_calorific_value'] + self.outputs['biomass_dry_detail_df:price_per_MWh'] = self.outputs['biomass_dry_detail_df:price_per_ton'] / \ + self.inputs['params']['biomass_dry_calorific_value'] + + def compute_price(self): + """ + compute price as in techno_type + """ + + crf = self.compute_capital_recovery_factor() + + self.outputs['biomass_dry_detail_df:managed_wood_transport ($/t)'] = self.inputs['transport_cost:transport'] + + # Factory cost including CAPEX OPEX + # $/ha * ha/m3 * m3/kg * 1000 = $/t + mean_density = (1 - self.inputs['params']['residues_density_percentage']) * self.inputs['params']['wood_density'] +\ + self.inputs['params']['residues_density_percentage'] * self.inputs['params']['residues_density'] + + self.outputs['biomass_dry_detail_df:managed_wood_capex ($/t)'] = \ + self.inputs['params']['managed_wood_price_per_ha'] * (crf + 0.045) / \ + self.outputs['yields:managed wood'] / mean_density * 1000 + + self.outputs['biomass_dry_detail_df:managed_wood_price_per_ton'] = \ + (self.outputs['biomass_dry_detail_df:managed_wood_capex ($/t)'] + + self.outputs['biomass_dry_detail_df:managed_wood_transport ($/t)']) * \ + self.inputs['margin:margin'] / 100.0 + + def compute_capital_recovery_factor(self): + """ + Compute annuity factor with the Weighted averaged cost of capital + and the lifetime of the selected solution + """ + wacc = self.inputs['params']['WACC'] + crf = (wacc * (1.0 + wacc) ** 100) / \ + ((1.0 + wacc) ** 100 - 1.0) + + return crf + + def compute_carbon_emissions(self): + ''' + Compute the carbon emissions from the technology taking into account + CO2 from production + CO2 from primary resources + ''' + # CO2 emissions + if 'CO2_from_production' not in self.inputs['params']: + self.outputs['CO2_emissions:production'] = self.zeros_array + self.get_theoretical_co2_prod(unit='kg/kWh') + elif self.inputs['params']['CO2_from_production'] == 0.0: + self.outputs['CO2_emissions:production'] = self.zeros_array + 0.0 + else: + if self.inputs['params']['CO2_from_production_unit'] == 'kg/kg': + self.outputs['CO2_emissions:production'] = self.zeros_array + self.inputs['params']['CO2_from_production'] / \ + self.inputs['params']['biomass_dry_high_calorific_value'] + elif self.inputs['params']['CO2_from_production_unit'] == 'kg/kWh': + self.outputs['CO2_emissions:production'] = self.zeros_array + self.inputs['params']['CO2_from_production'] + + # Add carbon emission from input energies (resources or other + # energies) + + co2_emissions_frominput_energies = self.compute_CO2_emissions_from_input_resources() + + # Add CO2 from production + C02 from input energies + self.outputs['CO2_emissions:Forest'] = self.outputs['CO2_emissions:production'] + \ + co2_emissions_frominput_energies + + def get_theoretical_co2_prod(self, unit='kg/kWh'): + ''' + Get the theoretical CO2 production for a given technology, + ''' + return 0.0 + + def compute_CO2_emissions_from_input_resources(self): + ''' + Need to take into account CO2 from electricity/fuel production + ''' + return 0.0 + + def compute_production_for_energy(self): + # techno production in TWh + self.outputs[f'techno_production:{BiomassDry.name} ({BiomassDry.unit})'] = (self.outputs['managed_wood_df:wood_production_for_energy (Mt)'] + + self.outputs['biomass_dry_detail_df:deforestation_for_energy']) * self.inputs['params']['biomass_dry_calorific_value'] + self.outputs['managed_wood_df:residues_production_for_energy (Mt)'] * self.inputs['params']['residue_calorific_value'] + + def compute_carbon_consumption(self): + # CO2 consumed + self.outputs[f'techno_consumption:{GlossaryEnergy.carbon_capture} (Mt)'] = \ + -self.inputs['params']['CO2_from_production'] / self.inputs['params']['biomass_dry_high_calorific_value'] * \ + self.outputs[f'techno_production:{BiomassDry.name} ({BiomassDry.unit})'] + + self.outputs[f'techno_consumption_woratio:{GlossaryEnergy.carbon_capture} (Mt)'] = \ + - self.inputs['params']['CO2_from_production'] / self.inputs['params']['biomass_dry_high_calorific_value'] * \ + self.outputs[f'techno_production:{BiomassDry.name} ({BiomassDry.unit})'] + + def compute_forest_constraint_evolution(self): + # compute forest constrain evolution: reforestation + deforestation + self.outputs['forest_surface_detail_df:forest_constraint_evolution'] = self.outputs['forest_surface_detail_df:reforestation_surface'] + \ + self.outputs['forest_surface_detail_df:deforestation_surface'] + + def compute_price_in_d_per_mwh(self): + # Prices in $/MWh + self.outputs['techno_prices:Forest'] = self.outputs['biomass_dry_detail_df:price_per_MWh'] + self.outputs['techno_prices:Forest_wotaxes'] = self.outputs['biomass_dry_detail_df:price_per_MWh'] + + def compute_economical_output_and_damages(self): + """ + Net output = Net production * Prices + Damages = Wasted production * Prices + Gross output = Net output + Damages + """ + # Forest net output breakdown + # Mt * $/ton = M$ -> so divide by 1e3 to get G$ + self.outputs[f'{GlossaryCore.EconomicsDetailDfValue}:{GlossaryCore.Years}'] = self.years + self.outputs[f'{GlossaryCore.EconomicsDetailDfValue}:Deforestation'] = self.outputs['biomass_dry_detail_df:deforestation (Mt)'] * self.outputs['biomass_dry_detail_df:deforestation_price_per_ton'] / 1e3 + self.outputs[f'{GlossaryCore.EconomicsDetailDfValue}:Managed wood'] = self.outputs['managed_wood_df:wood_production (Mt)'] * self.outputs['biomass_dry_detail_df:managed_wood_price_per_ton'] / 1e3 + self.outputs[f'{GlossaryCore.EconomicsDetailDfValue}:Residues'] = self.outputs['managed_wood_df:residues_production (Mt)'] * self.outputs['biomass_dry_detail_df:managed_wood_price_per_ton'] / 1e3 + + net_output = self.outputs[f'{GlossaryCore.EconomicsDetailDfValue}:Deforestation'] + \ + self.outputs[f'{GlossaryCore.EconomicsDetailDfValue}:Managed wood'] + \ + self.outputs[f'{GlossaryCore.EconomicsDetailDfValue}:Residues'] + + # Forest economical damages detail + # Mt * $/ton = M$ -> so divide by 1e3 to get G$ + self.outputs[f'{GlossaryCore.DamageDetailedDfValue}:{GlossaryCore.Years}'] = self.years + self.outputs[f'{GlossaryCore.DamageDetailedDfValue}:Deforestation'] = self.outputs['biomass_dry_detail_df:deforestation_wasted (Mt)'] * self.outputs['biomass_dry_detail_df:deforestation_price_per_ton'] / 1e3 + self.outputs[f'{GlossaryCore.DamageDetailedDfValue}:Managed wood'] = self.outputs['managed_wood_df:wasted_wood_production (Mt)'] * self.outputs['biomass_dry_detail_df:managed_wood_price_per_ton'] / 1e3 + self.outputs[f'{GlossaryCore.DamageDetailedDfValue}:Residues'] = self.outputs['managed_wood_df:wasted_residues_production (Mt)'] * self.outputs['biomass_dry_detail_df:managed_wood_price_per_ton'] / 1e3 + + total_damages = self.outputs[f'{GlossaryCore.DamageDetailedDfValue}:Deforestation'] + \ + self.outputs[f'{GlossaryCore.DamageDetailedDfValue}:Managed wood'] + \ + self.outputs[f'{GlossaryCore.DamageDetailedDfValue}:Residues'] + + # Forest economical output coupling variable + self.outputs[f"{GlossaryCore.Forest}.{GlossaryCore.ProductionDfValue}:{GlossaryCore.Years}"] = self.years + self.outputs[f"{GlossaryCore.Forest}.{GlossaryCore.ProductionDfValue}:{GlossaryCore.OutputNetOfDamage}"] = net_output + self.outputs[f"{GlossaryCore.Forest}.{GlossaryCore.ProductionDfValue}:{GlossaryCore.GrossOutput}"] = net_output + total_damages + + # Forest economical damages coupling variable + self.outputs[f"{GlossaryCore.Forest}.{GlossaryCore.DamageDfValue}:{GlossaryCore.Years}"] = self.years + self.outputs[f"{GlossaryCore.Forest}.{GlossaryCore.DamageDfValue}:{GlossaryCore.Damages}"] = total_damages + + + def compute_yields(self): + """yields are impact by climate change""" + self.outputs[f'yields:{GlossaryCore.Years}'] = self.years + self.outputs['yields:actual'] = self.inputs['params']['actual_yield_year_start'] * (1 - self.inputs[f'{GlossaryCore.CropProductivityReductionName}:{GlossaryCore.CropProductivityReductionName}'] / 100) + self.outputs['yields:managed wood'] = self.inputs['params']['managed_wood_yield_year_start'] * (1 - self.inputs[f'{GlossaryCore.CropProductivityReductionName}:{GlossaryCore.CropProductivityReductionName}'] / 100) + self.outputs['yields:unmanaged wood'] = self.inputs['params']['unmanaged_wood_yield_year_start'] * (1 - self.inputs[f'{GlossaryCore.CropProductivityReductionName}:{GlossaryCore.CropProductivityReductionName}'] / 100) + + def initialize_years(self): + self.years = np.arange(self.inputs[GlossaryCore.YearStart], self.inputs[GlossaryCore.YearEnd] + 1) + self.zeros_array = self.years * 0. + self.outputs[f'forest_surface_detail_df:{GlossaryCore.Years}'] = self.years + self.outputs[f'managed_wood_df:{GlossaryCore.Years}'] = self.years + self.outputs[f'biomass_dry_detail_df:{GlossaryCore.Years}'] = self.years + + # output dataframes: + self.outputs[f'techno_production:{GlossaryCore.Years}'] = self.years + self.outputs[f'techno_prices:{GlossaryCore.Years}'] = self.years + self.outputs[f'techno_consumption:{GlossaryCore.Years}'] = self.years + self.outputs[f'techno_consumption_woratio:{GlossaryCore.Years}'] = self.years + self.outputs[f'land_use_required:{GlossaryCore.Years}'] = self.years + self.outputs[f'CO2_emissions:{GlossaryCore.Years}'] = self.years + self.outputs[f'forest_lost_capital:{GlossaryCore.Years}'] = self.years + + def compute_land_use_required(self): + # compute land_use for energy + self.outputs[f'land_use_required:{GlossaryCore.Years}'] = self.years + self.outputs['land_use_required:Forest (Gha)'] = self.outputs['managed_wood_df:cumulative_surface'] + + def rescale_techno_production_and_consumption(self): + self.outputs[f'techno_production:{BiomassDry.name} ({BiomassDry.unit})'] /= self.inputs['scaling_factor_techno_production'] + self.outputs[f'techno_consumption:{GlossaryEnergy.carbon_capture} (Mt)'] /= self.inputs['scaling_factor_techno_consumption'] + self.outputs[f'techno_consumption_woratio:{GlossaryEnergy.carbon_capture} (Mt)'] /= self.inputs['scaling_factor_techno_consumption'] + + def compute_coupling_dfs(self): + self.outputs[f'biomass_dry_df:{GlossaryCore.Years}'] = self.years + self.outputs['biomass_dry_df:price_per_MWh'] = self.outputs['biomass_dry_detail_df:price_per_MWh'] + self.outputs['biomass_dry_df:biomass_dry_for_energy (Mt)'] = self.outputs['biomass_dry_detail_df:biomass_dry_for_energy (Mt)'] + + self.outputs[f'forest_surface_df:{GlossaryCore.Years}'] = self.years + self.outputs['forest_surface_df:global_forest_surface'] = self.outputs['forest_surface_detail_df:global_forest_surface'] + self.outputs['forest_surface_df:forest_constraint_evolution'] = self.outputs['forest_surface_detail_df:forest_constraint_evolution'] + + def compute_capital_loss(self): + self.outputs[f'forest_lost_capital:{GlossaryCore.Years}'] = self.years + self.outputs['forest_lost_capital:deforestation'] = - self.outputs['forest_surface_detail_df:delta_deforestation_surface'] * self.inputs['params']['reforestation_cost_per_ha'] + + diff --git a/climateeconomics/core/core_forest/forest_v1.py b/climateeconomics/core/core_forest/forest_v1.py deleted file mode 100644 index 59568b4ae..000000000 --- a/climateeconomics/core/core_forest/forest_v1.py +++ /dev/null @@ -1,193 +0,0 @@ -''' -Copyright 2022 Airbus SAS -Modifications on 2023/09/06-2023/11/03 Copyright 2023 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 climateeconomics.glossarycore import GlossaryCore - - -class Forest(): - """ - Forest pyworld3 class - basic for now, to evolve - - """ - YEAR_START = GlossaryCore.YearStart - YEAR_END = GlossaryCore.YearEnd - LIMIT_DEFORESTATION_SURFACE = 'limit_deforestation_surface' - DEFORESTATION_SURFACE = 'deforestation_surface' - CO2_PER_HA = 'CO2_per_ha' - INITIAL_CO2_EMISSIONS = 'initial_emissions' - REFORESTATION_INVESTMENT = 'forest_investment' - REFORESTATION_COST_PER_HA = 'reforestation_cost_per_ha' - - FOREST_SURFACE_DF = 'forest_surface_df' - FOREST_DETAIL_SURFACE_DF = 'forest_surface_detail_df' - CO2_EMITTED_FOREST_DF = GlossaryCore.insertGHGAgriLandEmissions.format(GlossaryCore.CO2) - CO2_EMITTED_DETAIL_DF = GlossaryCore.CO2EmissionsDetailDfValue - - def __init__(self, param): - """ - Constructor - """ - self.param = param - self.set_data() - self.create_dataframe() - - def set_data(self): - self.year_start = self.param[self.YEAR_START] - self.year_end = self.param[self.YEAR_END] - # deforestation limite - self.limit_deforestation_surface = self.param[self.LIMIT_DEFORESTATION_SURFACE] - # percentage of deforestation - self.deforestation_surface = self.param[self.DEFORESTATION_SURFACE] - # kg of CO2 not absorbed for 1 ha of forest deforested - self.CO2_per_ha = self.param[self.CO2_PER_HA] - # initial CO2 emissions - self.initial_emissions = self.param[self.INITIAL_CO2_EMISSIONS] - # forest data - self.forest_investment = self.param[self.REFORESTATION_INVESTMENT] - self.cost_per_ha = self.param[self.REFORESTATION_COST_PER_HA] - - def create_dataframe(self): - """ - Create the dataframe and fill it with values at year_start - """ - years = np.arange( - self.year_start, - self.year_end + 1) - self.years = years - self.forest_surface_df = pd.DataFrame() - self.CO2_emitted_df = pd.DataFrame() - - def compute(self, in_dict): - """ - Computation methods - """ - self.deforestation_surface = in_dict[self.DEFORESTATION_SURFACE] - self.year_start = in_dict[self.YEAR_START] - self.year_end = in_dict[self.YEAR_END] - self.forest_investment = in_dict[self.REFORESTATION_INVESTMENT] - self.cost_per_ha = in_dict[self.REFORESTATION_COST_PER_HA] - self.initial_emissions = self.param[self.INITIAL_CO2_EMISSIONS] - years = np.arange(self.year_start, self.year_end + 1) - self.limit_deforestation_surface = self.param[self.LIMIT_DEFORESTATION_SURFACE] - - self.forest_surface_df[GlossaryCore.Years] = years - # forest surface is in Gha, deforestation_surface is in Mha, - # deforested_surface is in Gha - self.forest_surface_df['deforested_surface'] = - \ - self.deforestation_surface['deforested_surface'].values / 1000 - - # forested surface - # invest in G$, coest_per_ha in $/ha --> Gha - self.forest_surface_df['forested_surface'] = self.forest_investment['forest_investment'].values / self.cost_per_ha - - # total - self.forest_surface_df['forest_surface_evol'] = self.forest_surface_df['forested_surface'] + \ - self.forest_surface_df['deforested_surface'] - - # cumulative values - self.forest_surface_df['forest_surface_evol_cumulative'] = np.cumsum( - self.forest_surface_df['forest_surface_evol']) - self.forest_surface_df['deforested_surface_cumulative'] = np.cumsum( - self.forest_surface_df['deforested_surface']) - self.forest_surface_df['forested_surface_cumulative'] = np.cumsum( - self.forest_surface_df['forested_surface']) - - # check limit of deforestation - for element in range(0, len(years)): - if self.forest_surface_df.loc[element, 'forest_surface_evol_cumulative'] < -self.limit_deforestation_surface / 1000: - self.forest_surface_df.loc[element, - 'forest_surface_evol'] = 0 - self.forest_surface_df.loc[element, 'deforested_surface'] = - \ - self.forest_surface_df.loc[element, 'forested_surface'] - self.forest_surface_df.loc[element, - 'forest_surface_evol_cumulative'] = -self.limit_deforestation_surface / 1000 - self.forest_surface_df.loc[element, - 'deforested_surface_cumulative'] = -self.forest_surface_df.loc[element, 'forested_surface_cumulative'] - self.limit_deforestation_surface / 1000 - - self.CO2_emitted_df[GlossaryCore.Years] = self.years - # in Gt of CO2 - self.CO2_emitted_df['emitted_CO2_evol'] = -self.forest_surface_df['forest_surface_evol'] * \ - self.CO2_per_ha / 1000 - self.CO2_emitted_df['emitted_CO2'] = -self.forest_surface_df['deforested_surface'] * \ - self.CO2_per_ha / 1000 - self.CO2_emitted_df['captured_CO2'] = -self.forest_surface_df['forested_surface'] * \ - self.CO2_per_ha / 1000 - - self.CO2_emitted_df['emitted_CO2_evol_cumulative'] = -self.forest_surface_df['forest_surface_evol_cumulative'] * \ - self.CO2_per_ha / 1000 + self.initial_emissions - self.CO2_emitted_df['emitted_CO2_cumulative'] = -self.forest_surface_df['deforested_surface_cumulative'] * \ - self.CO2_per_ha / 1000 + self.initial_emissions - self.CO2_emitted_df['captured_CO2_cumulative'] = -self.forest_surface_df['forested_surface_cumulative'] * \ - self.CO2_per_ha / 1000 - #To make forest_disc work with forest_model v1 & v2 - self.forest_surface_df['global_forest_surface']=np.zeros(len(years)) - - # Gradients - def d_deforestation_surface_d_deforestation_surface(self, ): - """ - Compute gradient of deforestation surface by deforestation_surface (design variable) - """ - number_of_values = (self.year_end - self.year_start + 1) - d_deforestation_surface_d_forests = np.identity(number_of_values) - for i in range(0, number_of_values): - if self.forest_surface_df.loc[i, 'forest_surface_evol_cumulative'] != -self.limit_deforestation_surface / 1000: - d_deforestation_surface_d_forests[i][i] = - 1 / 1000 - else: - d_deforestation_surface_d_forests[i][i] = 0 - - return d_deforestation_surface_d_forests - - def d_forestation_surface_d_invest(self, ): - """ - Compute gradient of deforestation surface by deforestation_surface (design variable) - """ - number_of_values = (self.year_end - self.year_start + 1) - d_forestation_surface_d_invest = np.identity(number_of_values) - for i in range(0, number_of_values): - if self.forest_surface_df.loc[i, 'forest_surface_evol_cumulative'] != -self.limit_deforestation_surface / 1000: - d_forestation_surface_d_invest[i][i] = 1 / self.cost_per_ha - else: - d_forestation_surface_d_invest[i][i] = 0 - - return d_forestation_surface_d_invest - - def d_cum(self, derivative): - """ - compute the gradient of a cumulative derivative - """ - number_of_values = (self.year_end - self.year_start + 1) - d_cum = np.identity(number_of_values) - for i in range(0, number_of_values): - d_cum[i] = derivative[i] - if derivative[i][i] != 0: - if i > 0: - d_cum[i] += d_cum[i - 1] - return d_cum - - def d_CO2_emitted(self, d_deforestation_surface): - """ - Compute gradient of non_captured_CO2 by deforestation surface - :param: d_deforestation_surface, derivative of deforestation surface - """ - - d_CO2_emitted = - d_deforestation_surface * self.CO2_per_ha / 1000 - - return d_CO2_emitted diff --git a/climateeconomics/core/core_forest/forest_v2.py b/climateeconomics/core/core_forest/forest_v2.py deleted file mode 100644 index 8ff7d50b5..000000000 --- a/climateeconomics/core/core_forest/forest_v2.py +++ /dev/null @@ -1,848 +0,0 @@ -''' -Copyright 2022 Airbus SAS -Modifications on 2023/06/21-2024/06/24 Copyright 2023 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 energy_models.core.stream_type.energy_models.biomass_dry import BiomassDry -from energy_models.glossaryenergy import GlossaryEnergy -from sostrades_optimization_plugins.tools.cst_manager.constraint_manager import ( - compute_func_with_exp_min, -) - -from climateeconomics.glossarycore import GlossaryCore - - -class Forest(): - """ - Forest model class - basic for now, to evolve - - """ - YEAR_START = GlossaryCore.YearStart - YEAR_END = GlossaryCore.YearEnd - CO2_PER_HA = 'CO2_per_ha' - INITIAL_CO2_EMISSIONS = 'initial_emissions' - REFORESTATION_INVESTMENT = 'forest_investment' - REFORESTATION_COST_PER_HA = 'reforestation_cost_per_ha' - WOOD_TECHNO_DICT = 'wood_techno_dict' - MW_INITIAL_PROD = 'managed_wood_initial_prod' - MW_INITIAL_SURFACE = 'managed_wood_initial_surface' - MW_INVEST_BEFORE_YEAR_START = 'managed_wood_invest_before_year_start' - MW_INVESTMENT = 'managed_wood_investment' - DEFORESTATION_INVESTMENT = 'deforestation_investment' - DEFORESTATION_COST_PER_HA = 'deforestation_cost_per_ha' - - TRANSPORT_COST = 'transport_cost' - MARGIN = 'margin' - UNMANAGED_FOREST = 'initial_unmanaged_forest_surface' - PROTECTED_FOREST = 'protected_forest_surface' - - FOREST_SURFACE_DF = 'forest_surface_df' - FOREST_DETAIL_SURFACE_DF = 'forest_surface_detail_df' - CO2_EMITTED_FOREST_DF = GlossaryCore.insertGHGAgriLandEmissions.format(GlossaryCore.CO2) - CO2_EMITTED_DETAIL_DF = GlossaryCore.CO2EmissionsDetailDfValue - MW_DF = 'managed_wood_df' - # UW_DF = 'unmanaged_wood_df' - BIOMASS_DRY_DETAIL_DF = 'biomass_dry_detail_df' - BIOMASS_DRY_DF = 'biomass_dry_df' - - def __init__(self, param): - """ - Constructor - """ - self.param = param - self.product_energy_unit = 'TWh' - self.mass_unit = 'Mt' - self.set_data() - self.create_dataframe() - self.counter = 0 - - def set_data(self): - """ - """ - self.year_start = self.param[self.YEAR_START] - self.year_end = self.param[self.YEAR_END] - years = np.arange( - self.year_start, - self.year_end + 1) - self.years = years - self.CO2_per_ha = self.param[self.CO2_PER_HA] - # initial CO2 emissions - self.initial_emissions = self.param[self.INITIAL_CO2_EMISSIONS] - # forest data - self.forest_investment = self.param[self.REFORESTATION_INVESTMENT] - self.cost_per_ha = self.param[self.REFORESTATION_COST_PER_HA] - self.deforest_invest = self.param[self.DEFORESTATION_INVESTMENT] - self.deforest_cost_per_ha = self.param[self.DEFORESTATION_COST_PER_HA] - self.techno_wood_info = self.param[self.WOOD_TECHNO_DICT] - self.managed_wood_initial_surface = self.param[self.MW_INITIAL_SURFACE] - self.managed_wood_invest_before_year_start = self.param[ - self.MW_INVEST_BEFORE_YEAR_START] - self.managed_wood_investment = self.param[self.MW_INVESTMENT] - self.transport = self.param[self.TRANSPORT_COST] - self.margin = self.param[self.MARGIN] - self.initial_unmanaged_forest_surface = self.param[self.UNMANAGED_FOREST] - self.protected_forest_surface = self.param[self.PROTECTED_FOREST] - self.scaling_factor_techno_consumption = self.param['scaling_factor_techno_consumption'] - self.scaling_factor_techno_production = self.param['scaling_factor_techno_production'] - - self.wood_density = self.techno_wood_info['wood_density'] - self.residues_density = self.techno_wood_info['residues_density'] - - self.residues_density_percentage = self.techno_wood_info['residues_density_percentage'] - - self.mean_density = self.wood_density * (1. - self.residues_density_percentage) + \ - self.residues_density * (1. - self.residues_density_percentage) - - self.actual_yield = self.techno_wood_info['actual_yield'] - self.managed_yield = self.techno_wood_info['managed_yield'] - self.unmanaged_yield = self.techno_wood_info['unmanaged_yield'] - self.wood_percentage_for_energy = self.techno_wood_info['wood_percentage_for_energy'] - - def create_dataframe(self): - """ - Create the dataframe and fill it with values at year_start - """ - years = np.arange( - self.year_start, - self.year_end + 1) - self.years = years - self.forest_surface_df = pd.DataFrame({GlossaryCore.Years: self.years}) - self.CO2_emitted_df = pd.DataFrame({GlossaryCore.Years: self.years}) - self.managed_wood_df = pd.DataFrame({GlossaryCore.Years: self.years}) - self.mw_from_invests = pd.DataFrame({GlossaryCore.Years: self.years}) - self.biomass_dry_df = pd.DataFrame({GlossaryCore.Years: self.years}) - self.ratio = pd.DataFrame({GlossaryCore.Years: self.years}) - - # output dataframes: - self.techno_production = pd.DataFrame({GlossaryCore.Years: self.years}) - self.techno_prices = pd.DataFrame({GlossaryCore.Years: self.years}) - self.techno_consumption = pd.DataFrame({GlossaryCore.Years: self.years}) - self.techno_consumption_woratio = pd.DataFrame({GlossaryCore.Years: self.years}) - self.land_use_required = pd.DataFrame({GlossaryCore.Years: self.years}) - self.CO2_emissions = pd.DataFrame({GlossaryCore.Years: self.years}) - self.forest_lost_capital = pd.DataFrame({GlossaryCore.Years: self.years}) - - def compute(self, in_dict): - """ - Computation methods - """ - self.biomass_dry_calorific_value = BiomassDry.data_energy_dict[ - 'calorific_value'] # kwh/kg - # kwh/kg - self.biomass_dry_high_calorific_value = BiomassDry.data_energy_dict[ - 'high_calorific_value'] - self.year_start = in_dict[self.YEAR_START] - self.year_end = in_dict[self.YEAR_END] - self.forest_investment = in_dict[self.REFORESTATION_INVESTMENT] - self.deforest_invest = in_dict[self.DEFORESTATION_INVESTMENT] - self.cost_per_ha = in_dict[self.REFORESTATION_COST_PER_HA] - self.initial_emissions = self.param[self.INITIAL_CO2_EMISSIONS] - self.years = np.arange( - self.year_start, self.year_end + 1) - self.managed_wood_investment = in_dict[self.MW_INVESTMENT] - - self.forest_surface_df['unmanaged_forest'] = self.initial_unmanaged_forest_surface - - self.forest_lost_capital['reforestation'] = 0 - self.forest_lost_capital['managed_wood'] = 0 - self.forest_lost_capital['deforestation'] = 0 - - # compute data of each contribution - self.compute_managed_wood_surface() - self.compute_reforestation_deforestation_surface() - self.compute_deforestation_biomass() - self.compute_managed_wood_production() - # sum up global surface data - self.sumup_global_surface_data() - # compute capital and lost capital - # sum up global CO2 data - self.compute_global_CO2_production() - - # compute biomass dry production - self.compute_biomass_dry_production() - - # compute outputs: - - # compute land_use for energy - self.land_use_required['Forest (Gha)'] = self.managed_wood_df['cumulative_surface'] - - # compute forest constrain evolution: reforestation + deforestation - self.forest_surface_df['forest_constraint_evolution'] = self.forest_surface_df['reforestation_surface'] + \ - self.forest_surface_df['deforestation_surface'] - - # techno production in TWh - self.techno_production[f'{BiomassDry.name} ({BiomassDry.unit})'] = (self.managed_wood_df[ - 'wood_production_for_energy (Mt)'] + \ - self.biomass_dry_df[ - 'deforestation_for_energy']) * self.biomass_dry_calorific_value + \ - self.managed_wood_df[ - 'residues_production_for_energy (Mt)'] * \ - self.techno_wood_info[ - 'residue_calorific_value'] - # price in $/MWh - self.techno_prices['Forest'] = self.biomass_dry_df['price_per_MWh'] - - if 'CO2_taxes_factory' in self.biomass_dry_df: - self.techno_prices['Forest_wotaxes'] = self.biomass_dry_df['price_per_MWh'] - \ - self.biomass_dry_df['CO2_taxes_factory'] - else: - self.techno_prices['Forest_wotaxes'] = self.biomass_dry_df['price_per_MWh'] - - # CO2 emissions - self.compute_carbon_emissions() - - # CO2 consumed - self.techno_consumption[f'{GlossaryEnergy.carbon_capture} ({self.mass_unit})'] = -self.techno_wood_info['CO2_from_production'] / \ - self.biomass_dry_high_calorific_value * \ - self.techno_production[ - f'{BiomassDry.name} ({BiomassDry.unit})'] - - self.techno_consumption_woratio[f'{GlossaryEnergy.carbon_capture} ({self.mass_unit})'] = -self.techno_wood_info[ - 'CO2_from_production'] / \ - self.biomass_dry_high_calorific_value * \ - self.techno_production[ - f'{BiomassDry.name} ({BiomassDry.unit})'] - - def compute_managed_wood_surface(self): - """ - compute managed wood delta and cumulative surface from investments - Will be recalculate with deforestation impact in compute_reforestation_deforestation_surface method - """ - construction_delay = self.techno_wood_info[GlossaryCore.ConstructionDelay] - mw_cost = self.techno_wood_info['managed_wood_price_per_ha'] - # managed wood from past invest. invest in G$ - surface in Gha. - mw_from_past_invest = self.managed_wood_invest_before_year_start[ - GlossaryCore.InvestmentsValue] / mw_cost - # managed wood from actual invest - mw_from_invest = self.managed_wood_investment[GlossaryCore.InvestmentsValue] / mw_cost - # concat all managed wood form invest - mw_added = pd.concat([mw_from_past_invest, mw_from_invest]).values - - # remove value that exceed year_end - for i in range(0, construction_delay): - mw_added = np.delete(mw_added, len(mw_added) - 1) - self.mw_from_invests['mw_surface'] = mw_added - self.managed_wood_df['delta_surface'] = mw_added - cumulative_mw = np.cumsum(mw_added) - self.managed_wood_df['cumulative_surface'] = cumulative_mw + \ - self.managed_wood_initial_surface - - def compute_managed_wood_production(self): - """ - compute data concerning managed wood : surface taken, production, CO2 absorbed, as delta and cumulative - """ - - # Biomass production part - # Gha * m3/ha - cubic_meter_production = (self.managed_wood_initial_surface * self.actual_yield + - (self.managed_wood_df['cumulative_surface'] - self.managed_wood_initial_surface) - * self.managed_yield) - self.managed_wood_df['delta_wood_production (Mt)'] = self.managed_wood_df[ - 'delta_surface'] * self.managed_yield * ( - 1 - self.residues_density_percentage) * self.wood_density - # Gm3* kg/m3 => Mt - self.managed_wood_df['wood_production (Mt)'] = cubic_meter_production * ( - 1 - self.residues_density_percentage) * self.wood_density - - self.managed_wood_df['wood_production_for_energy (Mt)'] = self.managed_wood_df['wood_production (Mt)'] * \ - self.wood_percentage_for_energy - self.managed_wood_df['wood_production_for_industry (Mt)'] = self.managed_wood_df['wood_production (Mt)'] * \ - (1 - self.wood_percentage_for_energy) - - self.managed_wood_df['delta_residues_production (Mt)'] = self.managed_wood_df[ - 'delta_surface'] * self.managed_yield * self.residues_density_percentage * self.residues_density - self.managed_wood_df[ - 'residues_production (Mt)'] = cubic_meter_production * self.residues_density_percentage * self.residues_density - self.managed_wood_df['residues_production_for_energy (Mt)'] = self.managed_wood_df['residues_production (Mt)'] * \ - self.wood_percentage_for_energy - self.managed_wood_df['residues_production_for_industry (Mt)'] = self.managed_wood_df[ - 'residues_production (Mt)'] * \ - (1 - self.wood_percentage_for_energy) - - self.managed_wood_df['delta_biomass_production (Mt)'] = self.managed_wood_df['delta_wood_production (Mt)'] + \ - self.managed_wood_df['delta_residues_production (Mt)'] - self.managed_wood_df['biomass_production (Mt)'] = self.managed_wood_df['wood_production (Mt)'] + \ - self.managed_wood_df['residues_production (Mt)'] - - # CO2 part - self.managed_wood_df['delta_CO2_emitted'] = - \ - self.managed_wood_df['delta_surface'] * self.CO2_per_ha / 1000 - # CO2 emitted is delta cumulate - self.managed_wood_df['CO2_emitted'] = - \ - (self.managed_wood_df['cumulative_surface'] - - self.managed_wood_initial_surface) * self.CO2_per_ha / 1000 - - def compute_reforestation_deforestation_surface(self): - """ - compute land use and due to reforestation et deforestation activities - CO2 is not computed here because surface limit need to be taken into account before. - """ - - # forest surface is in Gha, deforestation_surface is in Mha, - # deforested_surface is in Gha - self.forest_surface_df['delta_deforestation_surface'] = - \ - self.deforest_invest[ - GlossaryCore.InvestmentsValue].values / \ - self.deforest_cost_per_ha - - # forested surface - # invest in G$, coest_per_ha in $/ha --> Gha - self.forest_surface_df['delta_reforestation_surface'] = self.forest_investment[ - 'forest_investment'].values / self.cost_per_ha - - self.forest_surface_df['deforestation_surface'] = np.cumsum( - self.forest_surface_df['delta_deforestation_surface']) - self.forest_surface_df['reforestation_surface'] = np.cumsum( - self.forest_surface_df['delta_reforestation_surface']) - - deforested_unmanaged_surface = 0 - for i in range(0, len(self.years)): - # recompute unmanaged forest cumulated each year - if i == 0: - self.forest_surface_df.loc[i, 'unmanaged_forest'] = self.initial_unmanaged_forest_surface + \ - self.forest_surface_df.loc[ - i, 'delta_reforestation_surface'] + \ - self.forest_surface_df.loc[i, - 'delta_deforestation_surface'] - else: - self.forest_surface_df.loc[i, 'unmanaged_forest'] = self.forest_surface_df.loc[ - i - 1, 'unmanaged_forest'] + \ - self.forest_surface_df.loc[ - i, 'delta_reforestation_surface'] + \ - self.forest_surface_df.loc[i, - 'delta_deforestation_surface'] - # if unmanaged forest are empty, managed forest are removed - if self.forest_surface_df.loc[i, 'unmanaged_forest'] <= 0: - # remove managed wood - self.managed_wood_df.loc[i, - 'delta_surface'] += self.forest_surface_df.loc[i, 'unmanaged_forest'] - # compute reforestation lost capital - # in this loop all unmanaged forest + reforested forest has been deforested - # if i == 0, lost capital is the initial unmanaged + reforested surface - # else it is previous year unmanaged surface + reforested - # surface - if i == 0: - deforested_unmanaged_surface = self.initial_unmanaged_forest_surface + \ - self.forest_surface_df.loc[i, - 'delta_reforestation_surface'] - else: - deforested_unmanaged_surface = self.forest_surface_df.loc[i - 1, - 'unmanaged_forest'] + self.forest_surface_df.loc[i, 'delta_reforestation_surface'] - self.forest_lost_capital.loc[i, - 'reforestation'] = deforested_unmanaged_surface * self.cost_per_ha - - # lost capital of managed wood is what is deforested into - # managed forest - self.forest_lost_capital.loc[i, 'managed_wood'] = - self.forest_surface_df.loc[i, - 'unmanaged_forest'] * self.techno_wood_info['managed_wood_price_per_ha'] - # set unmanaged forest to 0 - self.forest_surface_df.loc[i, 'unmanaged_forest'] = 0 - else: - # reforestation lost capital equals deforestation - self.forest_lost_capital.loc[i, 'reforestation'] = - \ - self.forest_surface_df.loc[i, - 'delta_deforestation_surface'] * self.cost_per_ha - # recompute managed forest cumulated each year - if i > 0: - self.managed_wood_df.loc[i, 'cumulative_surface'] = self.managed_wood_df.loc[ - i - 1, 'cumulative_surface'] + \ - self.managed_wood_df.loc[i, 'delta_surface'] - - # if managed forest are empty, all is removed - if self.managed_wood_df.loc[i, 'cumulative_surface'] <= 0: - # the cumulative surface is the excedent surface deforested - # leading to lost capital - self.forest_lost_capital.loc[i, 'deforestation'] = - \ - self.managed_wood_df.loc[ - i, 'cumulative_surface'] * \ - self.deforest_cost_per_ha - - # lost capital of managed wood is what is left of managed wood - # + what have been invested in the i year - deforested_managed_surface = self.forest_surface_df.loc[ - i, 'delta_deforestation_surface'] + deforested_unmanaged_surface - \ - self.managed_wood_df.loc[i, 'cumulative_surface'] - self.forest_lost_capital.loc[i, 'managed_wood'] = - \ - deforested_managed_surface * \ - self.techno_wood_info['managed_wood_price_per_ha'] - - # delta is all the managed wood available - self.managed_wood_df.loc[i, 'delta_surface'] = - \ - self.managed_wood_df.loc[i - 1, 'cumulative_surface'] - self.managed_wood_df.loc[i, 'cumulative_surface'] = 0 - - # set a limit to deforestation at the forest that have been reforested because there is no other - # real_deforested surface = -delta_reforestation_surface + delta_mw_surface - # lost_capital = (delta_deforestation_surface - real_deforested) * deforestation_cost_per_ha - self.forest_surface_df.loc[i, 'delta_deforestation_surface'] = - self.forest_surface_df.loc[i, - 'delta_reforestation_surface'] + self.managed_wood_df.loc[i, 'delta_surface'] - - self.forest_surface_df['deforestation_surface'] = np.cumsum( - self.forest_surface_df['delta_deforestation_surface']) - self.managed_wood_df['cumulative_surface'] = compute_func_with_exp_min( - self.managed_wood_df['cumulative_surface'].values, 1e-15) - self.forest_surface_df['unmanaged_forest'] = compute_func_with_exp_min( - self.forest_surface_df['unmanaged_forest'].values, 1e-15) - - def compute_deforestation_biomass(self): - """ - compute biomass produce by deforestation. It is a one time production. - We use actual yield because deforestation is done on actual forest not managed ones - """ - - self.biomass_dry_df['deforestation (Mt)'] = -self.forest_surface_df[ - 'delta_deforestation_surface'] * self.unmanaged_yield * self.wood_density - self.biomass_dry_df['deforestation_for_energy'] = self.biomass_dry_df['deforestation (Mt)'] * \ - self.wood_percentage_for_energy - self.biomass_dry_df['deforestation_for_industry'] = self.biomass_dry_df['deforestation (Mt)'] - \ - self.biomass_dry_df['deforestation_for_energy'] - self.biomass_dry_df[ - 'deforestation_price_per_ton'] = self.unmanaged_yield * self.wood_density / self.deforest_cost_per_ha - self.biomass_dry_df['deforestation_price_per_MWh'] = self.biomass_dry_df['deforestation_price_per_ton'] / \ - self.biomass_dry_calorific_value - - def sumup_global_surface_data(self): - """ - managed wood and unmanaged wood impact forest_surface_df - """ - self.forest_surface_df['delta_global_forest_surface'] = self.forest_surface_df['delta_reforestation_surface'] + \ - self.forest_surface_df['delta_deforestation_surface'] - self.forest_surface_df['global_forest_surface'] = self.managed_wood_df['cumulative_surface'] + \ - self.forest_surface_df['unmanaged_forest'] + \ - self.protected_forest_surface - self.forest_surface_df['protected_forest_surface'] = self.protected_forest_surface - - def compute_global_CO2_production(self): - """ - compute the global CO2 production in Gt - """ - # in Gt of CO2 - - self.CO2_emitted_df['delta_CO2_emitted'] = -self.forest_surface_df['delta_global_forest_surface'] * \ - self.CO2_per_ha / 1000 - self.CO2_emitted_df['delta_CO2_deforestation'] = -self.forest_surface_df['delta_deforestation_surface'] * \ - self.CO2_per_ha / 1000 - self.CO2_emitted_df['delta_CO2_reforestation'] = -self.forest_surface_df['delta_reforestation_surface'] * \ - self.CO2_per_ha / 1000 - - # remove CO2 managed surface from global emission because CO2_per_ha - # from managed forest = 0 - self.CO2_emitted_df['CO2_deforestation'] = - self.forest_surface_df['deforestation_surface'] * \ - self.CO2_per_ha / 1000 - self.CO2_emitted_df['CO2_reforestation'] = -self.forest_surface_df['reforestation_surface'] * \ - self.CO2_per_ha / 1000 - self.CO2_emitted_df['initial_CO2_land_use_change'] = self.initial_emissions - # global sum up - self.CO2_emitted_df['global_CO2_emitted'] = self.CO2_emitted_df['CO2_deforestation'] + \ - self.CO2_emitted_df['initial_CO2_land_use_change'] - self.CO2_emitted_df['global_CO2_captured'] = self.CO2_emitted_df['CO2_reforestation'] - self.CO2_emitted_df['emitted_CO2_evol_cumulative'] = self.CO2_emitted_df['global_CO2_emitted'] + \ - self.CO2_emitted_df['global_CO2_captured'] - - def compute_biomass_dry_production(self): - """ - compute total biomass dry prod - """ - - self.biomass_dry_df['biomass_dry_for_energy (Mt)'] = self.managed_wood_df['wood_production_for_energy (Mt)'] + \ - self.managed_wood_df[ - 'residues_production_for_energy (Mt)'] + \ - self.biomass_dry_df['deforestation_for_energy'] - - self.compute_price() - - self.managed_wood_part = self.managed_wood_df['biomass_production (Mt)'] / ( - self.managed_wood_df['biomass_production (Mt)'] + self.biomass_dry_df['deforestation (Mt)']) - self.deforestation_part = self.biomass_dry_df['deforestation (Mt)'] / ( - self.managed_wood_df['biomass_production (Mt)'] + self.biomass_dry_df['deforestation (Mt)']) - - self.biomass_dry_df['price_per_ton'] = self.biomass_dry_df[ - 'managed_wood_price_per_ton'] * self.managed_wood_part + \ - self.biomass_dry_df['deforestation_price_per_ton'] * \ - self.deforestation_part - - self.biomass_dry_df['managed_wood_price_per_MWh'] = self.biomass_dry_df['managed_wood_price_per_ton'] / \ - self.biomass_dry_calorific_value - self.biomass_dry_df['price_per_MWh'] = self.biomass_dry_df['price_per_ton'] / \ - self.biomass_dry_calorific_value - - def compute_price(self): - """ - compute price as in techno_type - """ - - self.crf = self.compute_crf() - - self.biomass_dry_df['managed_wood_transport ($/t)'] = self.transport['transport'].values - - # Factory cost including CAPEX OPEX - # $/ha * ha/m3 * m3/kg * 1000 = $/t - self.biomass_dry_df['managed_wood_capex ($/t)'] = self.techno_wood_info['managed_wood_price_per_ha'] * \ - ( - self.crf + 0.045) / self.managed_yield / self.mean_density * 1000 - - self.biomass_dry_df['managed_wood_price_per_ton'] = ( - self.biomass_dry_df['managed_wood_capex ($/t)'].values + - self.biomass_dry_df['managed_wood_transport ($/t)']).values * \ - self.margin['margin'].values / 100.0 - - def compute_crf(self): - """ - Compute annuity factor with the Weighted averaged cost of capital - and the lifetime of the selected solution - """ - wacc = self.techno_wood_info['WACC'] - crf = (wacc * (1.0 + wacc) ** 100) / \ - ((1.0 + wacc) ** 100 - 1.0) - - return crf - - def compute_carbon_emissions(self): - ''' - Compute the carbon emissions from the technology taking into account - CO2 from production + CO2 from primary resources - ''' - if 'CO2_from_production' not in self.techno_wood_info: - self.CO2_emissions['production'] = self.get_theoretical_co2_prod( - unit='kg/kWh') - elif self.techno_wood_info['CO2_from_production'] == 0.0: - self.CO2_emissions['production'] = 0.0 - else: - if self.techno_wood_info['CO2_from_production_unit'] == 'kg/kg': - self.CO2_emissions['production'] = self.techno_wood_info['CO2_from_production'] / \ - self.biomass_dry_high_calorific_value - elif self.techno_wood_info['CO2_from_production_unit'] == 'kg/kWh': - self.CO2_emissions['production'] = self.techno_wood_info['CO2_from_production'] - - # Add carbon emission from input energies (resources or other - # energies) - - co2_emissions_frominput_energies = self.compute_CO2_emissions_from_input_resources( - ) - - # Add CO2 from production + C02 from input energies - self.CO2_emissions['Forest'] = self.CO2_emissions['production'] + \ - co2_emissions_frominput_energies - - def get_theoretical_co2_prod(self, unit='kg/kWh'): - ''' - Get the theoretical CO2 production for a given technology, - ''' - return 0.0 - - def compute_CO2_emissions_from_input_resources(self): - ''' - Need to take into account CO2 from electricity/fuel production - ''' - return 0.0 - - # Gradients - def compute_d_deforestation_surface_d_invest(self): - """ - - Compute gradient of deforestation surface by deforestation_invest (design variable) - """ - number_of_values = (self.year_end - self.year_start + 1) - d_deforestation_surface_d_forests = - \ - np.identity(number_of_values) / self.deforest_cost_per_ha - - return d_deforestation_surface_d_forests - - def compute_d_reforestation_surface_d_invest(self): - """ - - Compute gradient of reforestation surface by invest (design variable) - """ - number_of_values = (self.year_end - self.year_start + 1) - d_forestation_surface_d_invest = np.identity( - number_of_values) / self.cost_per_ha - - return d_forestation_surface_d_invest - - def compute_d_mw_surface_d_invest(self): - """ - compute gradient of managed_wood surface vs managed_wood_investment - """ - number_of_values = (self.year_end - self.year_start + 1) - result = np.identity(number_of_values) * 0.0 - construction_delay = self.techno_wood_info[GlossaryCore.ConstructionDelay] - for i in range(construction_delay, number_of_values): - result[i, i - construction_delay] = 1 / \ - self.techno_wood_info['managed_wood_price_per_ha'] - return result - - def compute_d_limit_surfaces_d_deforestation_invest(self, d_deforestation_surface_d_invest): - """ - Compute gradient of delta managed wood surface, delta deforestation surface, unmanaged wood cumulated surface, - mw lost capital, deforestation lost capital and reforestation lost capital vs deforestation invest - """ - number_of_values = (self.year_end - self.year_start + 1) - - d_delta_mw_surface_d_invest = np.zeros( - (number_of_values, number_of_values)) - d_delta_deforestation_surface_d_invest = d_deforestation_surface_d_invest - d_cum_umw_surface_d_invest = np.zeros( - (number_of_values, number_of_values)) - d_lc_deforestation_d_invest = np.zeros( - (number_of_values, number_of_values)) - d_lc_reforestation_d_invest = np.zeros( - (number_of_values, number_of_values)) - d_lc_mw_d_invest = np.zeros( - (number_of_values, number_of_values)) - - for i in range(0, len(self.years)): - if i == 0: - d_cum_umw_surface_d_invest[i] = d_delta_deforestation_surface_d_invest[i] - else: - d_cum_umw_surface_d_invest[i] = d_cum_umw_surface_d_invest[i - - 1] + d_delta_deforestation_surface_d_invest[ - i] - # if unmanaged forest are empty, managed forest are removed - if self.forest_surface_df.loc[i, 'unmanaged_forest'] <= 1e-10: - # remove managed wood - d_delta_mw_surface_d_invest[i] += d_cum_umw_surface_d_invest[i] - - if i == 0: - d_lc_reforestation_d_invest[i] = np.zeros(number_of_values) - else: - d_lc_reforestation_d_invest[i] = d_cum_umw_surface_d_invest[i - - 1] * self.cost_per_ha - - d_lc_mw_d_invest[i] = - d_cum_umw_surface_d_invest[i] * \ - self.techno_wood_info['managed_wood_price_per_ha'] - # set unmanaged forest to 0 - d_cum_umw_surface_d_invest[i] = np.zeros(number_of_values) - - else: - d_lc_reforestation_d_invest[i] = - \ - d_deforestation_surface_d_invest[i] * self.cost_per_ha - - # if managed forest are empty, all is removed - if self.managed_wood_df.loc[i, 'cumulative_surface'] <= 1e-10: - sum = self.d_cum(d_delta_mw_surface_d_invest) - # delta is all the managed wood available - d_lc_deforestation_d_invest[i] = - \ - sum[i] * self.deforest_cost_per_ha - d_lc_mw_d_invest[i] = - ( - d_deforestation_surface_d_invest[i] + d_lc_reforestation_d_invest[i] / self.cost_per_ha - - sum[i]) * \ - self.techno_wood_info['managed_wood_price_per_ha'] - - # d_delta_mw_surface_d_invest[i] = - \ - # compute_dfunc_with_exp_min( - # sum[i - 1], 1e-15).reshape(number_of_values) - d_delta_mw_surface_d_invest[i] = - \ - sum[i - 1] - d_delta_deforestation_surface_d_invest[i] = d_delta_mw_surface_d_invest[i] - - return d_cum_umw_surface_d_invest, d_delta_mw_surface_d_invest, d_delta_deforestation_surface_d_invest, d_lc_deforestation_d_invest, d_lc_reforestation_d_invest, d_lc_mw_d_invest - - def compute_d_limit_surfaces_d_reforestation_invest(self, d_reforestation_surface_d_invest): - """ - Compute gradient of delta managed wood surface, delta deforestation surface, unmanaged wood cumulated surface, - mw lost capital, deforestation lost capital and reforestation lost capital vs reforestation invest - """ - number_of_values = (self.year_end - self.year_start + 1) - - d_delta_mw_surface_d_invest = np.zeros( - (number_of_values, number_of_values)) - d_delta_deforestation_surface_d_invest = np.zeros( - (number_of_values, number_of_values)) - d_delta_reforestation_surface_d_invest = d_reforestation_surface_d_invest - d_cum_umw_surface_d_invest = np.zeros( - (number_of_values, number_of_values)) - d_lc_deforestation_d_invest = np.zeros( - (number_of_values, number_of_values)) - d_lc_reforestation_d_invest = np.zeros( - (number_of_values, number_of_values)) - d_lc_mw_d_invest = np.zeros( - (number_of_values, number_of_values)) - - for i in range(0, len(self.years)): - if i == 0: - d_cum_umw_surface_d_invest[i] = d_delta_reforestation_surface_d_invest[i] + \ - d_delta_deforestation_surface_d_invest[i] - else: - d_cum_umw_surface_d_invest[i] = d_cum_umw_surface_d_invest[i - 1] + \ - d_delta_reforestation_surface_d_invest[i] + \ - d_delta_deforestation_surface_d_invest[i] - # if unmanaged forest are empty, managed forest are removed - if self.forest_surface_df.loc[i, 'unmanaged_forest'] <= 1e-10: - # remove managed wood - d_delta_mw_surface_d_invest[i] += d_cum_umw_surface_d_invest[i] - - if i == 0: - d_lc_reforestation_d_invest[i] = d_reforestation_surface_d_invest[i] * \ - self.cost_per_ha - else: - d_lc_reforestation_d_invest[i] = ( - d_cum_umw_surface_d_invest[i - 1] + - d_reforestation_surface_d_invest[i]) * self.cost_per_ha - d_lc_mw_d_invest[i] = - d_cum_umw_surface_d_invest[i] * \ - self.techno_wood_info['managed_wood_price_per_ha'] - # set unmanaged forest to 0 - d_cum_umw_surface_d_invest[i] = np.zeros(number_of_values) - - else: - d_lc_reforestation_d_invest[i] = np.zeros(number_of_values) - # if managed forest are empty, all is removed - if self.managed_wood_df.loc[i, 'cumulative_surface'] <= 1e-10: - sum = self.d_cum(d_delta_mw_surface_d_invest) - # delta is all the managed wood available - d_lc_deforestation_d_invest[i] = - \ - sum[i] * self.deforest_cost_per_ha - d_lc_mw_d_invest[i] = - (d_lc_reforestation_d_invest[i] / self.cost_per_ha - - sum[i]) * self.techno_wood_info['managed_wood_price_per_ha'] - - d_delta_mw_surface_d_invest[i] = - \ - sum[i - 1] - d_delta_deforestation_surface_d_invest[i] = - \ - d_reforestation_surface_d_invest[i] + \ - d_delta_mw_surface_d_invest[i] - - return d_cum_umw_surface_d_invest, d_delta_mw_surface_d_invest, d_delta_deforestation_surface_d_invest, d_lc_deforestation_d_invest, d_lc_reforestation_d_invest, d_lc_mw_d_invest - - def compute_d_limit_surfaces_d_mw_invest(self, d_mw_surface_d_mw_invest): - """ - Compute gradient of delta managed wood surface, delta deforestation surface, unmanaged wood cumulated surface, - mw lost capital, deforestation lost capital and reforestation lost capital vs mw invest - """ - number_of_values = (self.year_end - self.year_start + 1) - - d_delta_mw_surface_d_invest = d_mw_surface_d_mw_invest - d_delta_deforestation_surface_d_invest = np.zeros( - (number_of_values, number_of_values)) - d_cum_umw_surface_d_invest = np.zeros( - (number_of_values, number_of_values)) - d_lc_deforestation_d_invest = np.zeros( - (number_of_values, number_of_values)) - d_lc_reforestation_d_invest = np.zeros( - (number_of_values, number_of_values)) - d_lc_mw_d_invest = np.zeros( - (number_of_values, number_of_values)) - - for i in range(0, number_of_values): - if self.forest_surface_df.loc[i, 'unmanaged_forest'] <= 1e-10: - if self.managed_wood_df.loc[i, 'cumulative_surface'] <= 1e-10: - sum = self.d_cum(d_delta_mw_surface_d_invest) - d_lc_deforestation_d_invest[i] = - \ - sum[i] * self.deforest_cost_per_ha - - # delta is all the managed wood available - d_delta_mw_surface_d_invest[i] = - sum[i - 1] - d_delta_deforestation_surface_d_invest[i] = d_delta_mw_surface_d_invest[i] - d_lc_mw_d_invest[i] = sum[i] * \ - self.techno_wood_info['managed_wood_price_per_ha'] - - return d_cum_umw_surface_d_invest, d_delta_mw_surface_d_invest, d_delta_deforestation_surface_d_invest, d_lc_deforestation_d_invest, d_lc_reforestation_d_invest, d_lc_mw_d_invest - - def compute_d_CO2_land_emission(self, d_forest_delta_surface): - """ - Compute gradient of CO2_land_emission by surface - :param: d_forest_delta_surface, derivative of forest constraint surface - d_CO2_emission = surface * constant --> d_surface is reused. - """ - - d_CO2_emission = - d_forest_delta_surface * self.CO2_per_ha / 1000 - - return d_CO2_emission - - def compute_d_techno_prod_d_invest(self, d_delta_mw_d_invest, d_delta_deforestation_d_invest): - """ - Compute gradient of techno prod by invest - :param: d_delta_mw_d_invest, derivative of managed wood surface vs invest - :param: d_delta_deforestation_d_invest, derivative of deforestation surface vs invest - """ - - # compute gradient of managed wood prod for energy - d_mw_prod_wood_for_nrj = self.d_cum( - d_delta_mw_d_invest * self.wood_density * self.managed_yield * (1 - self.residues_density_percentage)) - d_mw_prod_residue_for_nrj = self.d_cum( - d_delta_mw_d_invest * self.residues_density * self.managed_yield * self.residues_density_percentage) - - # compute gradient of deforestation production for nrj - d_deforestation_prod_for_nrj = -d_delta_deforestation_d_invest * self.wood_density * self.unmanaged_yield - - d_techno_prod_d_invest = ( - d_mw_prod_wood_for_nrj + d_deforestation_prod_for_nrj) * self.biomass_dry_calorific_value + \ - d_mw_prod_residue_for_nrj * self.techno_wood_info['residue_calorific_value'] - d_techno_prod_d_invest = d_techno_prod_d_invest * self.wood_percentage_for_energy - return d_techno_prod_d_invest - - def compute_d_techno_conso_d_invest(self, d_techno_prod_d_invest): - """ - Compute gradient of techno consumption by invest - :param: d_techno_prod_d_invest, derivative of techno_prod vs invest - """ - d_techno_conso_d_invest = -self.techno_wood_info['CO2_from_production'] / \ - self.biomass_dry_high_calorific_value * d_techno_prod_d_invest - - return d_techno_conso_d_invest - - def compute_d_techno_price_d_invest(self, d_delta_mw_d_invest, d_delta_deforestation_d_invest): - """ - Compute gradient of techno price by invest - :param: d_delta_mw_d_invest, derivative of managed wood surface vs invest - :param: d_delta_deforestation_d_invest, derivative of deforestation surface vs invest - """ - - d_mw_prod_wood_for_nrj = self.d_cum( - d_delta_mw_d_invest * self.wood_density * self.managed_yield) * (1 - self.residues_density_percentage) - d_mw_prod_residue_for_nrj = self.d_cum( - d_delta_mw_d_invest * self.residues_density * self.managed_yield) * self.residues_density_percentage - - # compute gradient of deforestation production for nrj - d_deforestation_prod = -d_delta_deforestation_d_invest * self.wood_density * self.unmanaged_yield - - # compute gradient of managed wood prod - d_mw_prod = d_mw_prod_wood_for_nrj + d_mw_prod_residue_for_nrj - - # derivative of mw_prod /(mw_prod + deforestation_prod) - # we get the transpose of the matrix to compute the right indexes - v = self.managed_wood_df['biomass_production (Mt)'].values + \ - self.biomass_dry_df['deforestation (Mt)'].values - v_prime = (d_mw_prod + d_deforestation_prod).T - v_square = v * v - u = self.managed_wood_df['biomass_production (Mt)'].values - u_prime = d_mw_prod.T - d_mw_price_per_ton = self.biomass_dry_df['managed_wood_price_per_ton'].values * ( - u_prime * v - v_prime * u) / v_square - - # derivative of deforestation_prod /(mw_prod + deforestation_prod) - u = self.biomass_dry_df['deforestation (Mt)'].values - u_prime = d_deforestation_prod.T - d_deforestation_price_per_ton = self.biomass_dry_df['deforestation_price_per_ton'].values * ( - u_prime * v - v_prime * u) / v_square - - d_price_per_ton = d_mw_price_per_ton.T + d_deforestation_price_per_ton.T - d_price_per_mwh = d_price_per_ton / self.biomass_dry_calorific_value - - return d_price_per_mwh - - def d_cum(self, derivative): - """ - - compute the gradient of a cumulative derivative - """ - number_of_values = (self.year_end - self.year_start + 1) - d_cum = np.identity(number_of_values) - for i in range(0, number_of_values): - d_cum[i] = derivative[i] - if i > 0: - d_cum[i] += d_cum[i - 1] - return d_cum diff --git a/climateeconomics/core/core_sectorization/agriculture_economy_model.py b/climateeconomics/core/core_sectorization/agriculture_economy_model.py index 068e3f5f1..fb0e8ddc3 100644 --- a/climateeconomics/core/core_sectorization/agriculture_economy_model.py +++ b/climateeconomics/core/core_sectorization/agriculture_economy_model.py @@ -264,4 +264,4 @@ def compute_food_type( }) - return outputs, price_breakdown_df + return outputs, price_breakdown_df \ No newline at end of file diff --git a/climateeconomics/core/core_witness/climateeco_discipline.py b/climateeconomics/core/core_witness/climateeco_discipline.py index e0fee81fc..faea86ddc 100644 --- a/climateeconomics/core/core_witness/climateeco_discipline.py +++ b/climateeconomics/core/core_witness/climateeco_discipline.py @@ -218,3 +218,13 @@ def check_ranges(self, data, ranges): # If the variable type is not supported, raise a TypeError else: raise TypeError(f"Unsupported type for variable '{key}'") + + def set_gradients_from_autodiff(self, gradients: dict[str: dict[str: dict[str: dict[str: np.ndarray]]]]): + for output_name in gradients: + for output_col in gradients[output_name]: + for input_name in gradients[output_name][output_col]: + for input_col, value in gradients[output_name][output_col][input_name].items(): + self.set_partial_derivative_for_other_types( + (output_name, output_col), + (input_name, input_col), + value) \ No newline at end of file diff --git a/climateeconomics/core/tools/ClimateEconomicsStudyManager.py b/climateeconomics/core/tools/ClimateEconomicsStudyManager.py index c97ae3bd9..85f236cea 100644 --- a/climateeconomics/core/tools/ClimateEconomicsStudyManager.py +++ b/climateeconomics/core/tools/ClimateEconomicsStudyManager.py @@ -239,3 +239,20 @@ def merge_design_spaces_dict(self, dspace_list: list[dict[str, Any]]) -> dict: dspace_out.update(dspace) dspace_out["dspace_size"] = dspace_size_out return dspace_out + + def load_data(self, from_path=None, from_input_dict=None, display_treeview=True, from_datasets_mapping=None): + parameter_changes = super().load_data(from_path=from_path, from_input_dict=from_input_dict, + display_treeview=display_treeview, + from_datasets_mapping=from_datasets_mapping) + lin_mode_dict = {} + linearization_mode_dict = self.add_auto_linearization_mode_rec(self.execution_engine.root_process, + lin_mode_dict) + lin_parameter_changes = self.execution_engine.load_study_from_input_dict(linearization_mode_dict) + parameter_changes.extend(lin_parameter_changes) + return parameter_changes + + def add_auto_linearization_mode_rec(self, disc, lin_mode_dict): + lin_mode_dict[f"{disc.get_disc_full_name()}.linearization_mode"] = 'auto' + for sub_disc in disc.proxy_disciplines: + lin_mode_dict = self.add_auto_linearization_mode_rec(sub_disc, lin_mode_dict) + return lin_mode_dict diff --git a/climateeconomics/core/tools/autodifferentiated_discipline.py b/climateeconomics/core/tools/autodifferentiated_discipline.py new file mode 100644 index 000000000..d3d5cd815 --- /dev/null +++ b/climateeconomics/core/tools/autodifferentiated_discipline.py @@ -0,0 +1,47 @@ +''' +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 logging +from typing import TYPE_CHECKING, Union + +from sostrades_core.execution_engine.sos_wrapp import SoSWrapp + +if TYPE_CHECKING: + from climateeconomics.core.tools.differentiable_model import DifferentiableModel + + +class AutodifferentiedDisc(SoSWrapp): + """Discipline which model is a DifferentiableModel""" + coupling_inputs = [] # inputs verified during jacobian test + coupling_outputs = [] # outputs verified during jacobian test + + def __init__(self, sos_name, logger: logging.Logger): + super().__init__(sos_name, logger) + self.model: Union[DifferentiableModel, None] = None + + def compute_sos_jacobian(self): + """ + Compute jacobian for each coupling variable + """ + + gradients = self.model.compute_jacobians_custom(outputs=self.coupling_outputs, inputs=self.coupling_inputs) + for output_name in gradients: + for output_col in gradients[output_name]: + for input_name in gradients[output_name][output_col]: + for input_col, value in gradients[output_name][output_col][input_name].items(): + self.set_partial_derivative_for_other_types( + (output_name, output_col), + (input_name, input_col), + value) diff --git a/climateeconomics/core/tools/differentiable_model.py b/climateeconomics/core/tools/differentiable_model.py index 2c6f2b79b..5ea3691aa 100644 --- a/climateeconomics/core/tools/differentiable_model.py +++ b/climateeconomics/core/tools/differentiable_model.py @@ -14,8 +14,8 @@ limitations under the License. ''' -import functools -import time +import functools # do not remove this grey imports otherwise pylint fails +import time # do not remove this grey imports otherwise pylint fails from contextlib import ContextDecorator, contextmanager from copy import deepcopy from statistics import mean @@ -28,6 +28,8 @@ from autograd.builtins import dict, isinstance from tqdm import tqdm +from climateeconomics.glossarycore import GlossaryCore + ArrayLike = Union[list[float], npt.NDArray[np.float64]] InputType = Union[float, int, ArrayLike, pd.DataFrame] OutputType = Union[float, ArrayLike] @@ -137,6 +139,8 @@ def __init__(self, flatten_dfs: bool = True): with keys as 'dataframe_name:column_name'. If False, DataFrames will be converted to dictionaries of arrays. """ + self.dataframes_outputs_colnames: dict[str: list[str]] = {} + self.dataframes_inputs_colnames: dict[str: list[str]] = {} self.inputs: dict[str, Union[float, np.ndarray, dict[str, np.ndarray]]] = {} self.outputs: dict[str, Union[float, np.ndarray, dict[str, np.ndarray]]] = {} self.flatten_dfs = flatten_dfs @@ -168,12 +172,14 @@ def set_inputs(self, inputs: dict[str, InputType]) -> None: ValueError: If an input array has more than 2 dimensions. """ + self.dataframes_inputs_colnames = {} for key, value in inputs.items(): if isinstance(value, pd.DataFrame): if not all(np.issubdtype(dtype, np.number) for dtype in value.dtypes): msg = f"DataFrame '{key}' contains non-numeric data, which is not supported." raise TypeError(msg) if self.flatten_dfs: + self.dataframes_inputs_colnames[key] = list(value.columns) for col in value.columns: self.inputs[f"{key}:{col}"] = value[col].to_numpy() else: @@ -240,7 +246,7 @@ def get_dataframes(self) -> dict[str, pd.DataFrame]: Dictionary of DataFrames reconstructed from outputs """ result = {} - + self.dataframes_outputs_colnames = {} if self.flatten_dfs: # Find all unique base names in flattened outputs base_names = set( @@ -250,6 +256,7 @@ def get_dataframes(self) -> dict[str, pd.DataFrame]: df = self.get_dataframe(base_name) if df is not None: result[base_name] = df + self.dataframes_outputs_colnames[base_name] = list(df.columns) # Check for dictionary outputs for key, value in self.outputs.items(): @@ -779,6 +786,37 @@ def f(x): "within_tolerance": within_tolerance, } + def compute_jacobians_custom(self, outputs: list[str], inputs: list[str]) -> dict[str: dict[str: dict[str: dict[str: np.ndarray]]]]: + """ + Returns a dictionnary 'gradients' containing gradients for SoSwrapp disciplines, with structure : + gradients[output df name][output column name][input df name][input column name] = value + """ + gradients = {} + all_inputs_paths = [] + for input_df_name in inputs: + all_inputs_paths.extend(self.get_df_input_dotpaths(input_df_name)) + all_inputs_paths = list(filter(lambda x: not (str(x).endswith(f':{GlossaryCore.Years}')), all_inputs_paths)) + for i, output in enumerate(outputs): + gradients[output] = {} + output_columns_paths = list(filter(lambda x: not (str(x).endswith(f':{GlossaryCore.Years}')), self.get_df_output_dotpaths(output))) + for output_path in output_columns_paths: + gradients_output_path = self.compute_partial_multiple(output_name=output_path, input_names=all_inputs_paths) + output_colname = output_path.split(f'{output}:')[1] + gradients[output][output_colname] = {} + for ip, value_grad in gradients_output_path.items(): + input_varname, input_varname_colname = ip.split(':') + if input_varname in gradients[output][output_colname]: + gradients[output][output_colname][input_varname][input_varname_colname] = value_grad + else: + gradients[output][output_colname][input_varname] = {input_varname_colname: value_grad} + + return gradients + def get_df_input_dotpaths(self, df_inputname: str) -> dict[str: list[str]]: + return [f'{df_inputname}:{colname}' for colname in self.dataframes_inputs_colnames[df_inputname]] + + def get_df_output_dotpaths(self, df_outputname: str) -> dict[str: list[str]]: + return [f'{df_outputname}:{colname}' for colname in self.dataframes_outputs_colnames[df_outputname]] + if __name__ == "__main__": @@ -842,7 +880,7 @@ def compute(self): y = self.inputs["data:feature2"] # Create flattened outputs - self.outputs["result:squared"] = x**2 + self.outputs["result:squared"] = x ** 2 self.outputs["result:sum"] = x + y self.outputs["other:value"] = x * y @@ -881,7 +919,7 @@ def compute(self): y = self.inputs["data"]["feature2"] # Create dictionary outputs - self.outputs["result:squared"] = x**2 + self.outputs["result:squared"] = x ** 2 self.outputs["result:sum"] = x + y self.outputs["single_value"] = ( x.mean() diff --git a/climateeconomics/core/tools/discipline_tester.py b/climateeconomics/core/tools/discipline_tester.py new file mode 100644 index 000000000..2a4500184 --- /dev/null +++ b/climateeconomics/core/tools/discipline_tester.py @@ -0,0 +1,89 @@ +''' +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. +''' + +from typing import List + +from sostrades_core.execution_engine.execution_engine import ExecutionEngine +from sostrades_core.tests.core.abstract_jacobian_unit_test import ( + AbstractJacobianUnittest, +) + + +def discipline_test_function(module_path: str, name: str, model_name: str, + inputs_dict: dict, namespaces_dict: dict, + jacobian_test: bool, coupling_inputs: List[str] = [], coupling_outputs: List[str] = [], + pickle_name: str = None, pickle_directory: str = None, + override_dump_jacobian: bool = False, + show_graphs: bool = True): + """ + Function to perform a discipline test, mimicking the behavior of the DisciplineTestTemplate class. + + :param module_path: Path to the module containing the discipline + :param model_name: Name of the model to be tested + :param coupling_inputs: List of coupling input variable names + :param coupling_outputs: List of coupling output variable names + :param jacobian_test: Flag indicating if Jacobian testing is required + :param name: Name of the execution engine + :param show_graphs: Flag to display graphs generated during post-processing + :param inputs_dict: Dictionary of input values for the study + :param namespaces_dict: Dictionary of namespaces definitions + """ + + # Initialize the execution engine + if jacobian_test and (pickle_name is None or pickle_directory is None): + raise ValueError("pickle_name and pickle_directory must be filled.") + ee = ExecutionEngine(name) + ee.ns_manager.add_ns_def(namespaces_dict) + + # Build and configure the discipline + builder = ee.factory.get_builder_from_module(model_name, module_path) + ee.factory.set_builders_to_coupling_builder(builder) + + ee.configure() + ee.display_treeview_nodes() + + # Load inputs and execute the study + ee.load_study_from_input_dict(inputs_dict) + ee.execute() + + # Retrieve discipline for post-processing + disc = ee.dm.get_disciplines_with_name(f'{name}.{model_name}')[0] + filter = disc.get_chart_filter_list() + graph_list = disc.get_post_processing_list(filter) + + # Show generated graphs + if show_graphs: + for graph in graph_list: + graph.to_plotly().show() + + # Perform Jacobian test if required + if jacobian_test: + def get_full_varnames(variables: list[str]): + return [ee.dm.get_all_namespaces_from_var_name(varname)[0] for varname in variables] + + disc_techno = ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline + jacobian_test_instnace = AbstractJacobianUnittest() + jacobian_test_instnace.override_dump_jacobian = override_dump_jacobian + jacobian_test_instnace.check_jacobian( + location=pickle_directory, + filename=pickle_name, + local_data=disc_techno.local_data, + discipline=disc_techno, + step=1e-15, + derr_approx='complex_step', + inputs=get_full_varnames(coupling_inputs), + outputs=get_full_varnames(coupling_outputs) + ) diff --git a/climateeconomics/data/GDP projections - SSP3 and from actual 2020 GDP.ipynb b/climateeconomics/data/GDP projections - SSP3 and from actual 2020 GDP.ipynb index 1d5951590..685b27c99 100644 --- a/climateeconomics/data/GDP projections - SSP3 and from actual 2020 GDP.ipynb +++ b/climateeconomics/data/GDP projections - SSP3 and from actual 2020 GDP.ipynb @@ -18,7 +18,7 @@ "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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", "text/plain": [ "
" ] @@ -28,21 +28,22 @@ } ], "source": [ + "import matplotlib.pyplot as plt\n", "import numpy as np\n", + "from scipy.interpolate import interp1d\n", + "\n", "years = np.arange(2020, 2101)\n", "x_years_ssp3 = list(np.arange(2020, 2101, 10))\n", "y_pop_ssp3 = np.array([7697.854, 8514.307, 9257.220, 9957.131, 10574.362, 11117.377, 11633.415, 12134.327, 12620.136])\n", "\n", - "from scipy.interpolate import interp1d\n", "f = interp1d(x_years_ssp3, y_pop_ssp3)\n", "\n", "population_interpolated_ssp3_values = f(years) \n", "\n", - "import matplotlib.pyplot as plt\n", "plt.title(\"SSP3 world population projection (billions)\")\n", "\n", - "plt.plot(years, population_interpolated_ssp3_values/1000, label=\"interpolation SSP3 scenario\", zorder=0);\n", - "plt.scatter(x_years_ssp3, y_pop_ssp3/1000, c=\"r\", zorder=1, label=\"SSP3 values\");\n", + "plt.plot(years, population_interpolated_ssp3_values/1000, label=\"interpolation SSP3 scenario\", zorder=0)\n", + "plt.scatter(x_years_ssp3, y_pop_ssp3/1000, c=\"r\", zorder=1, label=\"SSP3 values\")\n", "plt.legend();" ] }, @@ -62,7 +63,7 @@ "outputs": [ { "data": { - "image/png": "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xqJfv34YN24ckpKSMGfOHBgaGmLWrFnFPnbkyJFYtmwZRo0ahbCwMNSvXx8nTpzAvHnz0L17dwQEBBT62MmTJ+OPP/5Au3btMGXKFDRo0ABqtRrh4eE4cOAApk2bhubNm6Nt27YYMWIEvvjiC8TExKBnz56Qy+UICQmBkZERJk2aBCB3bpStW7di27ZtqF69OgwNDVG/fv0C9z1//nx06tQJ7du3x/Tp0yGTybB8+XJcuXIFW7ZsKbDXS1FWrlyJI0eOoEePHnB1dUVmZqamh0tRzwERERGVDSZBiIiIXoH//e9/+PPPP/Htt98iKioKWVlZcHR0REBAAGbNmoXatWsDALy8vDB16lQcOXIEf/75J+Li4iCVSuHt7Y0vvvgCU6dOhZ5e6Tpyenl5QSaT4fPPP0dMTAwAwN3dHW+++SY+/PBDmJubl6idefPmISgoCG+88QaSk5PRrFkzbN26FZ6ensU+1tDQEEePHsXHH3+MxYsXIy4uDtWqVcP06dMxZ86cfPWfTi4YGxvj+PHjWLBgAVavXo3Q0FAoFAq4uroiICBA0xMEANavX48mTZpgzZo1WL9+PRQKBerUqYOPPvpIU2fu3LmIiorCuHHjkJKSAjc3t0KXIvbz88ORI0cwZ84cjB49Gmq1Gg0bNsSuXbvQs2fPEj1vT2vUqBEOHDiAOXPmIDo6GiYmJqhXrx527dqFzp07l7o9IiIiKh2JeLpfKxEREdEzjh07hvbt2+O3337Lt7rLy2BlZYUxY8bgq6++eun7IiIioqqFPUGIiIioXLh06RL27t2LhIQEtGzZUtfhEBERUSXEJAgRERGVC++//z5u3LiB6dOno3///roOh4iIiCohDochIiIiIiIioiqBS+QSERERERERUZXAJAgRERERERERVQlMghARERERERFRlcAkCBERERERERFVCUyCEBEREREREVGVwCQIEREREREREVUJTIIQERERERERUZXAJAgRERERERERVQlMghARERERERFRlcAkCBERERERERFVCUyCEBEREREREVGVwCQIEREREREREVUJTIIQERERERERUZXAJAgRERERERERVQlMghARERERERFRlcAkCBERERERERFVCUyCEBEREREREVGVwCQIEREREREREVUJTIIQERERERERUZXAJEglcebMGfTr1w+urq6Qy+Wwt7dH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\n", + "image/png": "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xqJfv34YN24ckpKSMGfOHBgaGmLWrFnFPnbkyJFYtmwZRo0ahbCwMNSvXx8nTpzAvHnz0L17dwQEBBT62MmTJ+OPP/5Au3btMGXKFDRo0ABqtRrh4eE4cOAApk2bhubNm6Nt27YYMWIEvvjiC8TExKBnz56Qy+UICQmBkZERJk2aBCB3bpStW7di27ZtqF69OgwNDVG/fv0C9z1//nx06tQJ7du3x/Tp0yGTybB8+XJcuXIFW7ZsKbDXS1FWrlyJI0eOoEePHnB1dUVmZqamh0tRzwERERGVDSZBiIiIXoH//e9/+PPPP/Htt98iKioKWVlZcHR0REBAAGbNmoXatWsDALy8vDB16lQcOXIEf/75J+Li4iCVSuHt7Y0vvvgCU6dOhZ5e6Tpyenl5QSaT4fPPP0dMTAwAwN3dHW+++SY+/PBDmJubl6idefPmISgoCG+88QaSk5PRrFkzbN26FZ6ensU+1tDQEEePHsXHH3+MxYsXIy4uDtWqVcP06dMxZ86cfPWfTi4YGxvj+PHjWLBgAVavXo3Q0FAoFAq4uroiICBA0xMEANavX48mTZpgzZo1WL9+PRQKBerUqYOPPvpIU2fu3LmIiorCuHHjkJKSAjc3t0KXIvbz88ORI0cwZ84cjB49Gmq1Gg0bNsSuXbvQs2fPEj1vT2vUqBEOHDiAOXPmIDo6GiYmJqhXrx527dqFzp07l7o9IiIiKh2JeLpfKxEREdEzjh07hvbt2+O3337Lt7rLy2BlZYUxY8bgq6++eun7IiIioqqFPUGIiIioXLh06RL27t2LhIQEtGzZUtfhEBERUSXEJAgRERGVC++//z5u3LiB6dOno3///roOh4iIiCohDochIiIiIiIioiqBS+QSERERERERUZXAJAgRERERERERVQlMghARERERERFRlcAkCBERERERERFVCUyCEBEREREREVGVwCQIEREREREREVUJTIIQERERERERUZXAJAgRERERERERVQlMghARERERERFRlcAkCBERERERERFVCUyCEBEREREREVGVwCQIEREREREREVUJTIIQERERERERUZXAJAgRERERERERVQlMghARERERERFRlcAkCBERERERERFVCUyCEBEREREREVGVwCQIEREREREREVUJTIIQERERERERUZXAJEglcebMGfTr1w+urq6Qy+Wwt7dH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", "text/plain": [ "
" ] @@ -73,6 +74,7 @@ ], "source": [ "import pandas as pd\n", + "\n", "df_ssp3 = pd.DataFrame()\n", "\n", "inflation_2005_to_2020 = 1.33 # source: https://www.in2013dollars.com/us/inflation/2005?endYear=2020&amount=1\n", @@ -109,13 +111,13 @@ "fig, ax = plt.subplots(1,2, figsize=(11,5))\n", "fig.suptitle(\"SSP3 projections\")\n", "ax[0].set_title(\"SSP3 world GDP projection (Trillion US$)\")\n", - "ax[0].plot(years, df_ssp3[\"gross_output\"], label=\"SSP3 GDP projection (2005)\");\n", - "ax[0].plot(years, df_ssp3_adjusted_gdp[\"gross_output\"], label=\"SSP3 GDP projection (adjusted with actual 2020 GDP)\");\n", - "ax[0].legend();\n", + "ax[0].plot(years, df_ssp3[\"gross_output\"], label=\"SSP3 GDP projection (2005)\")\n", + "ax[0].plot(years, df_ssp3_adjusted_gdp[\"gross_output\"], label=\"SSP3 GDP projection (adjusted with actual 2020 GDP)\")\n", + "ax[0].legend()\n", "ax[1].set_title(\"SSP3 Per capita consumption\")\n", - "ax[1].plot(years, df_ssp3[\"pc_consumption\"], label=\"SSP3 GDP projection (2005)\");\n", - "ax[1].plot(years, df_ssp3_adjusted_gdp[\"pc_consumption\"], label=\"SSP3 GDP projection (adjusted with actual 2020 GDP)\");\n", - "ax[1].legend();\n", + "ax[1].plot(years, df_ssp3[\"pc_consumption\"], label=\"SSP3 GDP projection (2005)\")\n", + "ax[1].plot(years, df_ssp3_adjusted_gdp[\"pc_consumption\"], label=\"SSP3 GDP projection (adjusted with actual 2020 GDP)\")\n", + "ax[1].legend()\n", "fig.tight_layout();" ] } diff --git a/climateeconomics/data/GDP_data_preprocessing_model.ipynb b/climateeconomics/data/GDP_data_preprocessing_model.ipynb index 2fb53ad75..10b555622 100644 --- a/climateeconomics/data/GDP_data_preprocessing_model.ipynb +++ b/climateeconomics/data/GDP_data_preprocessing_model.ipynb @@ -7,9 +7,9 @@ "metadata": {}, "outputs": [], "source": [ - "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", "import numpy as np\n", - "import matplotlib.pyplot as plt " + "import pandas as pd" ] }, { @@ -18,10 +18,7 @@ "id": "c69c3ca9", "metadata": {}, "outputs": [], - "source": [ - "from pycountry_convert import country_alpha2_to_continent_code, country_name_to_country_alpha2\n", - "import pycountry" - ] + "source": [] }, { "cell_type": "markdown", @@ -2286,8 +2283,7 @@ "metadata": {}, "outputs": [], "source": [ - "from sklearn.linear_model import LinearRegression\n", - "from sklearn.preprocessing import StandardScaler" + "from sklearn.linear_model import LinearRegression" ] }, { @@ -2445,7 +2441,7 @@ "ax.legend()\n", "plt.xlabel('years')\n", "plt.ylabel('percentage GDP')\n", - "fig.suptitle(f'Estimated percentage of GDP per group', fontsize=20)\n" + "fig.suptitle('Estimated percentage of GDP per group', fontsize=20)\n" ] }, { diff --git a/climateeconomics/data/IEA_NZE_energy_final_consumption.csv b/climateeconomics/data/IEA_NZE_energy_final_consumption.csv deleted file mode 100644 index 25253f4f8..000000000 --- a/climateeconomics/data/IEA_NZE_energy_final_consumption.csv +++ /dev/null @@ -1,6 +0,0 @@ -years,Final Consumption -2010,101.93 -2020,107.63 -2030,103.16 -2040,95.12 -2050,90.54 diff --git a/climateeconomics/database/database_witness_core.py b/climateeconomics/database/database_witness_core.py index 09bb7f29a..7b990a976 100644 --- a/climateeconomics/database/database_witness_core.py +++ b/climateeconomics/database/database_witness_core.py @@ -588,8 +588,8 @@ class DatabaseWitnessCore: ) @classmethod - def get_forest_invest_before_year_start(cls, year_start: int, construction_delay: int, - is_available_at_year: bool = False): + def get_reforestation_invest_before_year_start(cls, year_start: int, construction_delay: int, + is_available_at_year: bool = False): path_to_csv = os.path.join(data_folder, "forest_invests") + ".csv" df = pd.read_csv(path_to_csv) diff --git a/climateeconomics/glossarycore.py b/climateeconomics/glossarycore.py index c105f54f0..c1de01f25 100644 --- a/climateeconomics/glossarycore.py +++ b/climateeconomics/glossarycore.py @@ -70,6 +70,7 @@ class GlossaryCore: "namespace": "ns_public", "range": [2000, 2300], } + Forest = "Forest" # todo in the futur: merge these 3 invest values InvestValue = "invest" InvestLevelValue = "invest_level" @@ -174,7 +175,7 @@ class GlossaryCore: FruitsAndVegetables = 'fruits and vegetables' Fish = "fish" OtherFood = "other" - + DefaultFoodTypesV2 = [ RedMeat, WhiteMeat, @@ -188,7 +189,7 @@ class GlossaryCore: SugarCane, OtherFood, ] - + FishDailyCal = "fish_calories_per_day" OtherDailyCal = "other_calories_per_day" @@ -207,6 +208,7 @@ class GlossaryCore: NS_ENERGY_MIX = "ns_energy_mix" NS_FUNCTIONS = "ns_functions" NS_CCS = "ns_ccs" + NS_AGRI = "ns_agriculture" NS_REGIONALIZED_POST_PROC = "ns_regionalized" NS_SECTORS_POST_PROC_EMISSIONS = "ns_sectors_postproc" NS_SECTORS_POST_PROC_GDP = "ns_sectors_postproc_gdp" @@ -614,11 +616,11 @@ class GlossaryCore: DamageFractionOutput: ("float", [0.0, 1.0], False), }, } - Damages = "Damages [G$]" + Damages = "Damages" DamageDfValue = "damage_df" - DamagesFromClimate = "Damages from climate [G$]" - DamagesFromProductivityLoss = "Damages from productivity loss [G$]" - EstimatedDamages = "Estimated damages [G$]" + DamagesFromClimate = "Damages from climate" + DamagesFromProductivityLoss = "Damages from productivity loss" + EstimatedDamages = "Estimated damages" DamageDf = { "var_name": DamageDfValue, "type": "dataframe", @@ -632,8 +634,18 @@ class GlossaryCore: }, } - EstimatedDamagesFromProductivityLoss = "Estimated damages from productivity loss (not applied) [G$]" - EstimatedDamagesFromClimate = "Estimated damages from climate (not applied) [G$]" + SubsectorDamagesDf = { + "type": "dataframe", + "visibility": "Shared", + "unit": "G$", + "dataframe_descriptor": { + Years: ("int", [1900, YearEndDefault], False), + Damages: ("float", [0, 1e30], False), + }, + } + + EstimatedDamagesFromProductivityLoss = "Estimated damages from productivity loss (not applied)" + EstimatedDamagesFromClimate = "Estimated damages from climate (not applied)" DamageDetailedDfValue = "damage_detailed_df" DamageDetailedDf = { "var_name": DamageDetailedDfValue, @@ -928,20 +940,6 @@ class GlossaryCore: }, } - FinalConsumptionValue = "Final Consumption" - EnergyFinalConsumptionName = "energy_final_consumption_df" - EnergyFinalConsumptionDf = { - "var_name": EnergyFinalConsumptionName, - "type": "dataframe", - "visibility": "Shared", - "unit": "PWh", - "namespace": NS_ENERGY_MIX, - "dataframe_descriptor": { - Years: ("int", [1900, YearEndDefault], False), - FinalConsumptionValue: ("float", [0, 1e30], False), - }, - } - EnergyProductionDetailedDf = { "var_name": EnergyProductionValue, "type": "dataframe", @@ -1223,6 +1221,22 @@ class GlossaryCore: OutputNetOfDamage: ("float", [0, 1e30], False), }, } + SubsectorProductionDf = { + "visibility": "Shared", + "type": "dataframe", + "unit": "G$", + "dataframe_descriptor": { + Years: ("int", [1900, YearEndDefault], False), + GrossOutput: ("float", [0, 1e30], False), + OutputNetOfDamage: ("float", [0, 1e30], False), + }, + } + + SubsectorProductionDetailedDf = { + "type": "dataframe", + "unit": "G$", + } + ConsumptionSectorBreakdown = { "type": "dataframe", "unit": "T$", @@ -1863,7 +1877,7 @@ class GlossaryCore: "var_name": FoodTypeNotProducedDueToClimateChangeName, "type": "dataframe", "unit": "Mt", - "description": "Dedicated production", # for energy production + "description": "Dedicated production", # for energy production } FoodTypeWasteByClimateDamagesName = "food_type_waste_by_climate_change" @@ -1976,19 +1990,19 @@ class GlossaryCore: # from capgemini sharepoint "default": { RedMeat: 2880, # https://www.fatsecret.com/calories-nutrition/generic/beef-cooked-ns-as-to-fat-eaten?portionid=50030&portionamount=100.000 - # https://www.fatsecret.com/calories-nutrition/generic/pork-cooked-ns-as-to-fat-eaten?portionid=50101&portionamount=100.000 - # https://www.fatsecret.com/calories-nutrition/generic/chicken-ns-as-to-skin-eaten + #https://www.fatsecret.com/calories-nutrition/generic/pork-cooked-ns-as-to-fat-eaten?portionid=50101&portionamount=100.000 + #https://www.fatsecret.com/calories-nutrition/generic/chicken-ns-as-to-skin-eaten WhiteMeat: (237 * 16.96 + 271 * 13.89) / (16.96 + 13.89) * 10, # weighted average for chicken and pork Milk: 650, # https://www.dudhsagardairy.coop/health-nutrition/nutritional-facts/#:~:text=The%20calorie%2Fenergy%20content%20of,fat)%20provides%2035kcals%20%2F100ml. Eggs: 1470, # https://www.fatsecret.com/calories-nutrition/usda/egg-(whole)?portionid=56523&portionamount=100.000 - Rice: 1350, # https://www.fatsecret.com/calories-nutrition/generic/rice-cooked?portionid=53182&portionamount=1000.000 - Maize: 960, # https://www.healthline.com/nutrition/foods/corn#:~:text=Here%20are%20the%20nutrition%20facts,Calories%3A%2096 - Cereals: 3670, # https://www.fatsecret.com/calories-nutrition/generic/cereal?portionid=53258&portionamount=100.000 + Rice: 1350, #https://www.fatsecret.com/calories-nutrition/generic/rice-cooked?portionid=53182&portionamount=1000.000 + Maize: 960, #https://www.healthline.com/nutrition/foods/corn#:~:text=Here%20are%20the%20nutrition%20facts,Calories%3A%2096 + Cereals: 3670, # https://www.fatsecret.com/calories-nutrition/generic/cereal?portionid=53258&portionamount=100.000 # 200 for vegetables https://www.fatsecret.co.in/calories-nutrition/generic/raw-vegetable?portionid=54903&portionamount=100.000&frc=True#:~:text=Nutritional%20Summary%3A&text=There%20are%2020%20calories%20in,%25%20carbs%2C%2016%25%20prot. # 580 for fruits https://www.fatsecret.co.in/calories-nutrition/generic/fruit?portionid=54046&portionamount=100.000&frc=True#:~:text=Nutritional%20Summary%3A&text=There%20are%2058%20calories%20in%20100%20grams%20of%20Fruit. FruitsAndVegetables: (580 * 86.40 + 147.04 * 200) / (86.40 + 147.04), Fish: 840, # https://www.fatsecret.com/calories-nutrition/generic/fish-raw?portionid=50616&portionamount=100.000 - SugarCane: 3750, # https://www.terrafreshfoods.com/products/sugar-cane#:~:text=in%20Latin%20America.-,Nutritional%20Value,and%20to%20increase%20our%20energy. + SugarCane: 3750, # https://www.terrafreshfoods.com/products/sugar-cane#:~:text=in%20Latin%20America.-,Nutritional%20Value,and%20to%20increase%20our%20energy. OtherFood: 2000, # assumed } } diff --git a/climateeconomics/sos_processes/iam/diet/diet_optim_process/_usecase_diet_optim.py b/climateeconomics/sos_processes/iam/diet/diet_optim_process/_usecase_diet_optim.py index f155ffa0b..b2c3611a0 100644 --- a/climateeconomics/sos_processes/iam/diet/diet_optim_process/_usecase_diet_optim.py +++ b/climateeconomics/sos_processes/iam/diet/diet_optim_process/_usecase_diet_optim.py @@ -122,7 +122,7 @@ def setup_usecase(self, study_folder_path=None): "n_processes": 32, "use_threading": False, "wait_time_between_fork": 0}, - f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.sub_mda_class': 'GSPureNewtonMDA', + f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.inner_mda_name': 'MDAGSNewton', f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.max_mda_iter': 50, f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.DesignVariables.{WRITE_XVECT}': False} diff --git a/climateeconomics/sos_processes/iam/diet/diet_optim_process/_usecase_witness_optim.py b/climateeconomics/sos_processes/iam/diet/diet_optim_process/_usecase_witness_optim.py index 304b6847f..3dc307f61 100644 --- a/climateeconomics/sos_processes/iam/diet/diet_optim_process/_usecase_witness_optim.py +++ b/climateeconomics/sos_processes/iam/diet/diet_optim_process/_usecase_witness_optim.py @@ -124,7 +124,7 @@ def setup_usecase(self, study_folder_path=None): "n_processes": 32, "use_threading": False, "wait_time_between_fork": 0}, - f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.sub_mda_class': 'GSPureNewtonMDA', + f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.inner_mda_name': 'MDAGSNewton', f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.max_mda_iter': 50, f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.DesignVariables.{WRITE_XVECT}': False} diff --git a/climateeconomics/sos_processes/iam/diet/diet_optim_sub_process/_usecase_diet_optim_sub.py b/climateeconomics/sos_processes/iam/diet/diet_optim_sub_process/_usecase_diet_optim_sub.py index 1539c1cfc..1f4399163 100644 --- a/climateeconomics/sos_processes/iam/diet/diet_optim_sub_process/_usecase_diet_optim_sub.py +++ b/climateeconomics/sos_processes/iam/diet/diet_optim_sub_process/_usecase_diet_optim_sub.py @@ -92,11 +92,11 @@ def setup_usecase(self, study_folder_path=None): design_var_descriptor = {} years = np.arange(self.year_start, self.year_end + 1) - dv_arrays_dict[f'{self.witness_uc.study_name}.forest_investment_array_mix'] = \ - dspace_df['forest_investment_array_mix']['value'] - design_var_descriptor['forest_investment_array_mix'] = {'out_name': 'forest_investment', + dv_arrays_dict[f'{self.witness_uc.study_name}.reforestation_investment_array_mix'] = \ + dspace_df['reforestation_investment_array_mix']['value'] + design_var_descriptor['reforestation_investment_array_mix'] = {'out_name': 'reforestation_investment', 'out_type': 'dataframe', - 'key': 'forest_investment', + 'key': 'reforestation_investment', 'index': years, 'index_name': GlossaryCore.Years, 'namespace_in': GlossaryCore.NS_WITNESS, @@ -183,7 +183,7 @@ def setup_usecase(self, study_folder_path=None): values_dict[ f'{self.study_name}.{self.coupling_name}.{self.designvariable_name}.design_var_descriptor'] = design_var_descriptor - values_dict[f'{self.study_name}.{self.coupling_name}.sub_mda_class'] = 'GSPureNewtonMDA' + values_dict[f'{self.study_name}.{self.coupling_name}.inner_mda_name'] = 'MDAGSNewton' # values_dict[f'{self.study_name}.{self.coupling_name}.warm_start'] = True values_dict[f'{self.study_name}.{self.coupling_name}.max_mda_iter'] = 50 values_dict[f'{self.study_name}.{self.coupling_name}.linearization_mode'] = 'adjoint' diff --git a/climateeconomics/sos_processes/iam/diet/diet_optim_sub_process/_usecase_witness_optim_sub.py b/climateeconomics/sos_processes/iam/diet/diet_optim_sub_process/_usecase_witness_optim_sub.py index e0667c238..b030f9c7a 100644 --- a/climateeconomics/sos_processes/iam/diet/diet_optim_sub_process/_usecase_witness_optim_sub.py +++ b/climateeconomics/sos_processes/iam/diet/diet_optim_sub_process/_usecase_witness_optim_sub.py @@ -91,11 +91,11 @@ def setup_usecase(self, study_folder_path=None): design_var_descriptor = {} years = np.arange(self.year_start, self.year_end + 1) - dv_arrays_dict[f'{self.witness_uc.study_name}.forest_investment_array_mix'] = \ - dspace_df['forest_investment_array_mix']['value'] - design_var_descriptor['forest_investment_array_mix'] = {'out_name': 'forest_investment', + dv_arrays_dict[f'{self.witness_uc.study_name}.reforestation_investment_array_mix'] = \ + dspace_df['reforestation_investment_array_mix']['value'] + design_var_descriptor['reforestation_investment_array_mix'] = {'out_name': 'reforestation_investment', 'out_type': 'dataframe', - 'key': 'forest_investment', + 'key': 'reforestation_investment', 'index': years, 'index_name': GlossaryCore.Years, 'namespace_in': GlossaryCore.NS_WITNESS, @@ -160,7 +160,7 @@ def setup_usecase(self, study_folder_path=None): values_dict[ f'{self.study_name}.{self.coupling_name}.{self.designvariable_name}.design_var_descriptor'] = design_var_descriptor - values_dict[f'{self.study_name}.{self.coupling_name}.sub_mda_class'] = 'GSPureNewtonMDA' + values_dict[f'{self.study_name}.{self.coupling_name}.inner_mda_name'] = 'MDAGSNewton' # values_dict[f'{self.study_name}.{self.coupling_name}.warm_start'] = True values_dict[f'{self.study_name}.{self.coupling_name}.max_mda_iter'] = 50 values_dict[f'{self.study_name}.{self.coupling_name}.linearization_mode'] = 'adjoint' diff --git a/climateeconomics/sos_processes/iam/diet/diet_process/process.py b/climateeconomics/sos_processes/iam/diet/diet_process/process.py index 0cd7b45c4..a71a51a44 100644 --- a/climateeconomics/sos_processes/iam/diet/diet_process/process.py +++ b/climateeconomics/sos_processes/iam/diet/diet_process/process.py @@ -36,17 +36,6 @@ class ProcessBuilder(BaseProcessBuilder): def get_builders(self): - ns_scatter = self.ee.study_name - - ns_dict = {GlossaryCore.NS_WITNESS: ns_scatter, - GlossaryCore.NS_ENERGY_MIX: ns_scatter, - 'ns_agriculture': ns_scatter, - GlossaryCore.NS_CCS: ns_scatter, - 'ns_energy': ns_scatter, - 'ns_forest': ns_scatter, - 'ns_invest': f'{self.ee.study_name}.InvestmentDistribution'} - - builder_list = [] @@ -69,11 +58,7 @@ def get_builders(self): self.ee.ns_manager.add_ns_def(ns_dict) - ''' - Add emissions disciplines - ''' - mods_dict = {AgricultureEmissionsDiscipline.name: 'climateeconomics.sos_wrapping.sos_wrapping_emissions.agriculture_emissions.agriculture_emissions_discipline.AgricultureEmissionsDiscipline', - } + mods_dict = {AgricultureEmissionsDiscipline.name: 'climateeconomics.sos_wrapping.sos_wrapping_emissions.agriculture_emissions.agriculture_emissions_discipline.AgricultureEmissionsDiscipline',} non_use_capital_list = self.create_builder_list( mods_dict, ns_dict=ns_dict) builder_list.extend(non_use_capital_list) diff --git a/climateeconomics/sos_processes/iam/diet/diet_process/usecase_diet.py b/climateeconomics/sos_processes/iam/diet/diet_process/usecase_diet.py index e28e15734..2d6423457 100644 --- a/climateeconomics/sos_processes/iam/diet/diet_process/usecase_diet.py +++ b/climateeconomics/sos_processes/iam/diet/diet_process/usecase_diet.py @@ -67,9 +67,9 @@ def setup_usecase(self, study_folder_path=None): nb_per = self.year_end - self.year_start + 1 years = arange(self.year_start, self.year_end + 1) - forest_invest = np.linspace(5.0, 8.0, len(years)) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, "forest_investment": forest_invest}) + reforestation_invest = np.linspace(5.0, 8.0, len(years)) + self.reforestation_investment_df = pd.DataFrame( + {GlossaryCore.Years: years, "reforestation_investment": reforestation_invest}) # private values economics operator model witness_input = {} @@ -78,7 +78,7 @@ def setup_usecase(self, study_folder_path=None): # Relax constraint for 15 first years - witness_input[f"{self.study_name}.{'InvestmentDistribution'}.forest_investment"] = self.forest_invest_df + witness_input[f"{self.study_name}.{'InvestmentDistribution'}.reforestation_investment"] = self.reforestation_investment_df # get population from csv file # get file from the data folder 3 folder up. global_data_dir = join(Path(__file__).parents[4], 'data') @@ -117,9 +117,9 @@ def setup_usecase(self, study_folder_path=None): self.CO2_tax = np.asarray([50.] * len(years)) - forest_invest = np.linspace(5.0, 8.0, len(years)) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, "forest_investment": forest_invest}) + reforestation_invest = np.linspace(5.0, 8.0, len(years)) + self.reforestation_investment_df = pd.DataFrame( + {GlossaryCore.Years: years, "reforestation_investment": reforestation_invest}) intermediate_point = 30 # CO2 taxes related inputs CO2_tax_efficiency = np.concatenate( @@ -157,7 +157,7 @@ def setup_usecase(self, study_folder_path=None): setup_data_list.append(witness_input) - self.func_df = pd.concat([self.setup_constraint_land_use(), self.setup_objectives()]) + self.func_df = self.setup_objectives() return setup_data_list @@ -190,64 +190,6 @@ def setup_objectives(self): return func_df - def setup_constraints(self): - func_df = pd.DataFrame( - columns=['variable', 'parent', 'ftype', 'weight', AGGR_TYPE]) - list_var = [] - list_parent = [] - list_ftype = [] - list_weight = [] - list_aggr_type = [] - list_ns = [] - - """ - list_var.append('non_use_capital_cons') - list_parent.append('invests_constraints') - list_ns.extend([GlossaryCore.NS_FUNCTIONS]) - list_ftype.append(INEQ_CONSTRAINT) - list_weight.append(-1.0) - list_aggr_type.append( - AGGR_TYPE_SMAX) - - func_df['variable'] = list_var - func_df['parent'] = list_parent - func_df['ftype'] = list_ftype - func_df['weight'] = list_weight - func_df[AGGR_TYPE] = list_aggr_type - func_df['namespace'] = list_ns - - """ - - return func_df - - - - def setup_constraint_land_use(self): - func_df = DataFrame( - columns=['variable', 'parent', 'ftype', 'weight', AGGR_TYPE]) - list_var = [] - list_parent = [] - list_ftype = [] - list_weight = [] - list_aggr_type = [] - list_ns = [] - list_var.extend( - ['land_demand_constraint', 'calories_per_day_constraint']) - list_parent.extend(['agriculture_constraint', 'agriculture_constraint']) - list_ftype.extend([INEQ_CONSTRAINT, INEQ_CONSTRAINT]) - list_weight.extend([-1.0, -3.0]) - list_aggr_type.extend( - [AGGR_TYPE_SUM, AGGR_TYPE_SUM]) - list_ns.extend([GlossaryCore.NS_FUNCTIONS, GlossaryCore.NS_FUNCTIONS]) - func_df['variable'] = list_var - func_df['parent'] = list_parent - func_df['ftype'] = list_ftype - func_df['weight'] = list_weight - func_df[AGGR_TYPE] = list_aggr_type - func_df['namespace'] = list_ns - - return func_df - if '__main__' == __name__: uc_cls = Study() diff --git a/climateeconomics/sos_processes/iam/witness/agriculture_mix_process/usecase.py b/climateeconomics/sos_processes/iam/witness/agriculture_mix_process/usecase.py index e1840bf51..0d3dfff6a 100644 --- a/climateeconomics/sos_processes/iam/witness/agriculture_mix_process/usecase.py +++ b/climateeconomics/sos_processes/iam/witness/agriculture_mix_process/usecase.py @@ -111,6 +111,12 @@ def setup_usecase(self, study_folder_path=None): GlossaryCore.Fish: 609.17, GlossaryCore.OtherFood: 3061.06, } + + crop_productivity_reduction = pd.DataFrame({ + GlossaryCore.Years: years, + GlossaryCore.CropProductivityReductionName: np.linspace(0., 4.5, len(years)) * 0, # fake + }) + red_meat_average_ca_daily_intake = default_kg_to_kcal[GlossaryCore.RedMeat] * diet_df_default[GlossaryCore.RedMeat].values[0] / 365 milk_eggs_average_ca_daily_intake = default_kg_to_kcal[GlossaryCore.Eggs] * diet_df_default[GlossaryCore.Eggs].values[0] / 365 + \ default_kg_to_kcal[GlossaryCore.Milk] * diet_df_default[GlossaryCore.Milk].values[0] / 365 @@ -163,8 +169,8 @@ def setup_usecase(self, study_folder_path=None): forest_invest = np.linspace(5, 8, year_range) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, "forest_investment": forest_invest}) + self.reforestation_investment_df = pd.DataFrame( + {GlossaryCore.Years: years, "reforestation_investment": forest_invest}) if 'CropEnergy' in self.techno_list: crop_invest = np.linspace(0.5, 0.25, year_range) @@ -233,13 +239,14 @@ def setup_usecase(self, study_folder_path=None): f'{self.study_name}.{energy_name}.{GlossaryEnergy.Crop}.{GlossaryCore.OtherDailyCal}': self.other_ca_per_day, f'{self.study_name}.{energy_name}.{GlossaryEnergy.Crop}.milk_and_eggs_calories_per_day': self.milk_and_eggs_calories_per_day, f'{self.study_name}.{energy_name}.{GlossaryEnergy.Crop}.crop_investment': self.crop_investment, - f'{self.study_name + self.additional_ns}.forest_investment': self.forest_invest_df, + f'{self.study_name + self.additional_ns}.reforestation_investment': self.reforestation_investment_df, f'{self.study_name}.{energy_name}.{GlossaryEnergy.Forest}.managed_wood_investment': self.mw_invest_df, f'{self.study_name}.{energy_name}.{GlossaryEnergy.Forest}.deforestation_investment': deforest_invest_df, f'{self.study_name}.{energy_name}.{GlossaryEnergy.Forest}.techno_capital': techno_capital, f'{self.study_name}.{energy_name}.{GlossaryEnergy.Crop}.techno_capital': techno_capital, f'{self.study_name}.{GlossaryCore.PopulationDfValue}': population_df, - f'{self.study_name}.{GlossaryCore.TemperatureDfValue}': temperature_df + f'{self.study_name}.{GlossaryCore.TemperatureDfValue}': temperature_df, + f'{self.study_name}.{GlossaryCore.CropProductivityReductionName}': crop_productivity_reduction, } red_meat_percentage_ctrl = np.linspace(600, 900, self.nb_poles) @@ -250,7 +257,7 @@ def setup_usecase(self, study_folder_path=None): other_calories_per_day_ctrl = np.linspace(900, 900, self.nb_poles) deforestation_investment_ctrl = np.linspace(10.0, 5.0, self.nb_poles) - forest_investment_array_mix = np.linspace(5.0, 8.0, self.nb_poles) + reforestation_investment_array_mix = np.linspace(5.0, 8.0, self.nb_poles) crop_investment_array_mix = np.linspace(1.0, 1.5, self.nb_poles) managed_wood_investment_array_mix = np.linspace( 2.0, 3.0, self.nb_poles) @@ -264,7 +271,7 @@ def setup_usecase(self, study_folder_path=None): 'vegetables_and_carbs_calories_per_day_ctrl'] = vegetables_and_carbs_calories_per_day_ctrl design_space_ctrl_dict['milk_and_eggs_calories_per_day_ctrl'] = milk_and_eggs_calories_per_day_ctrl design_space_ctrl_dict['deforestation_investment_ctrl'] = deforestation_investment_ctrl - design_space_ctrl_dict['forest_investment_array_mix'] = forest_investment_array_mix + design_space_ctrl_dict['reforestation_investment_array_mix'] = reforestation_investment_array_mix if 'CropEnergy' in self.techno_list: design_space_ctrl_dict['crop_investment_array_mix'] = crop_investment_array_mix @@ -304,8 +311,8 @@ def setup_design_space_ctrl_new(self): [0.0] * self.nb_poles, [100.0] * self.nb_poles, activated_elem=[True] * self.nb_poles) # ----------------------------------------- # Invests - update_dspace_dict_with(ddict, 'forest_investment_array_mix', - self.design_space_ctrl['forest_investment_array_mix'].values, + update_dspace_dict_with(ddict, 'reforestation_investment_array_mix', + self.design_space_ctrl['reforestation_investment_array_mix'].values, [1.0e-6] * self.nb_poles, [3000.0] * self.nb_poles, activated_elem=[True] * self.nb_poles) if 'CropEnergy' in self.techno_list: @@ -324,18 +331,4 @@ def setup_design_space_ctrl_new(self): if '__main__' == __name__: uc_cls = Study() - uc_cls.test() - """ - uc_cls.load_data() - uc_cls.run() - ppf = PostProcessingFactory() - for disc in uc_cls.execution_engine.root_process.proxy_disciplines: - filters = ppf.get_post_processing_filters_by_discipline( - disc) - graph_list = ppf.get_post_processing_by_discipline( - disc, filters, as_json=False) - - for graph in graph_list: - graph.to_plotly().show() - -""" + uc_cls.test() \ No newline at end of file diff --git a/climateeconomics/sos_processes/iam/witness/crop_2/process.py b/climateeconomics/sos_processes/iam/witness/crop_2/process.py index 9d1765dfd..104596f56 100644 --- a/climateeconomics/sos_processes/iam/witness/crop_2/process.py +++ b/climateeconomics/sos_processes/iam/witness/crop_2/process.py @@ -1,5 +1,6 @@ ''' Copyright 2024 Capgemini +Modifications on 2023/04/19-2024/06/24 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. @@ -14,6 +15,7 @@ limitations under the License. ''' + from energy_models.core.energy_process_builder import EnergyProcessBuilder from climateeconomics.glossarycore import GlossaryCore diff --git a/climateeconomics/sos_processes/iam/witness/sectorization/sectorization_process/usecase.py b/climateeconomics/sos_processes/iam/witness/sectorization/sectorization_process/usecase.py index 9687e8797..03a66c336 100644 --- a/climateeconomics/sos_processes/iam/witness/sectorization/sectorization_process/usecase.py +++ b/climateeconomics/sos_processes/iam/witness/sectorization/sectorization_process/usecase.py @@ -250,7 +250,7 @@ def setup_usecase(self, study_folder_path=None): f'{self.study_name}.tolerance': 1.0e-10, f'{self.study_name}.n_processes': 1, f'{self.study_name}.linearization_mode': 'adjoint', - f'{self.study_name}.sub_mda_class': 'MDAGaussSeidel'} + f'{self.study_name}.inner_mda_name': 'MDAGaussSeidel'} setup_data_list.append(numerical_values_dict) diff --git a/climateeconomics/sos_processes/iam/witness/sectorization/witness_sect_wo_energy/datacase_witness_wo_energy.py b/climateeconomics/sos_processes/iam/witness/sectorization/witness_sect_wo_energy/datacase_witness_wo_energy.py index d59afdb5d..66ab50bef 100644 --- a/climateeconomics/sos_processes/iam/witness/sectorization/witness_sect_wo_energy/datacase_witness_wo_energy.py +++ b/climateeconomics/sos_processes/iam/witness/sectorization/witness_sect_wo_energy/datacase_witness_wo_energy.py @@ -70,9 +70,9 @@ def setup_usecase(self, study_folder_path=None): nb_per = self.year_end - self.year_start + 1 years = arange(self.year_start, self.year_end + 1) - forest_invest = np.linspace(5.0, 8.0, len(years)) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, "forest_investment": forest_invest}) + reforestation_invest = np.linspace(5.0, 8.0, len(years)) + self.reforestation_investment_df = pd.DataFrame( + {GlossaryCore.Years: years, "reforestation_investment": reforestation_invest}) # private values economics operator pyworld3 witness_input = {} @@ -90,7 +90,7 @@ def setup_usecase(self, study_folder_path=None): witness_input[f"{self.study_name}.{'Damage.damage_constraint_factor'}"] = np.concatenate( (np.linspace(1.0, 1.0, 20), np.asarray([1] * (len(years) - 20)))) # witness_input[f"{self.study_name}.{}# '.Damage.damage_constraint_factor'}" = np.asarray([1] * len(years)) - witness_input[f"{self.study_name}.{'InvestmentDistribution'}.forest_investment"] = self.forest_invest_df + witness_input[f"{self.study_name}.{'InvestmentDistribution'}.reforestation_investment"] = self.reforestation_investment_df # get population from csv file # get file from the data folder 3 folder up. global_data_dir = join(Path(__file__).parents[5], 'data') @@ -171,9 +171,9 @@ def setup_usecase(self, study_folder_path=None): default_co2_efficiency = pd.DataFrame( {GlossaryCore.Years: years, GlossaryCore.CO2TaxEfficiencyValue: CO2_tax_efficiency}) - forest_invest = np.linspace(5.0, 8.0, len(years)) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, "forest_investment": forest_invest}) + reforestation_invest = np.linspace(5.0, 8.0, len(years)) + self.reforestation_investment_df = pd.DataFrame( + {GlossaryCore.Years: years, "reforestation_investment": reforestation_invest}) # -- load data from resource dc_resource = datacase_resource( diff --git a/climateeconomics/sos_processes/iam/witness/sectorization/witness_sect_wo_energy/process.py b/climateeconomics/sos_processes/iam/witness/sectorization/witness_sect_wo_energy/process.py index e7ac0bae1..7b5a3b061 100644 --- a/climateeconomics/sos_processes/iam/witness/sectorization/witness_sect_wo_energy/process.py +++ b/climateeconomics/sos_processes/iam/witness/sectorization/witness_sect_wo_energy/process.py @@ -75,9 +75,6 @@ def get_builders(self): self.ee.ns_manager.add_ns_def(ns_dict) - ''' - Add emissions disciplines - ''' mods_dict = { AgricultureEmissionsDiscipline.name: 'climateeconomics.sos_wrapping.sos_wrapping_emissions.agriculture_emissions.agriculture_emissions_discipline.AgricultureEmissionsDiscipline', } diff --git a/climateeconomics/sos_processes/iam/witness/sectorization/witness_sectorization/usecase_witness_coarse_sectorization.py b/climateeconomics/sos_processes/iam/witness/sectorization/witness_sectorization/usecase_witness_coarse_sectorization.py index 6e4359ff5..47eec3628 100644 --- a/climateeconomics/sos_processes/iam/witness/sectorization/witness_sectorization/usecase_witness_coarse_sectorization.py +++ b/climateeconomics/sos_processes/iam/witness/sectorization/witness_sectorization/usecase_witness_coarse_sectorization.py @@ -98,7 +98,7 @@ def setup_usecase(self, study_folder_path=None): f'{self.study_name}.tolerance': 1.0e-10, f'{self.study_name}.n_processes': 1, f'{self.study_name}.linearization_mode': 'adjoint', - f'{self.study_name}.sub_mda_class': 'GSPureNewtonMDA', + f'{self.study_name}.inner_mda_name': 'MDAGSNewton', f'{self.study_name}.cache_type': 'SimpleCache'} setup_data_list.append(numerical_values_dict) diff --git a/climateeconomics/sos_processes/iam/witness/sectorization/witness_sectorization_optim/usecase_witness_sectorization_optim.py b/climateeconomics/sos_processes/iam/witness/sectorization/witness_sectorization_optim/usecase_witness_sectorization_optim.py index e36204185..8332a5a60 100644 --- a/climateeconomics/sos_processes/iam/witness/sectorization/witness_sectorization_optim/usecase_witness_sectorization_optim.py +++ b/climateeconomics/sos_processes/iam/witness/sectorization/witness_sectorization_optim/usecase_witness_sectorization_optim.py @@ -34,7 +34,7 @@ class Study(ClimateEconomicsStudyManager): - def __init__(self, year_start=GlossaryCore.YearStartDefault, year_end=GlossaryCore.YearEndDefault, bspline=False, run_usecase=False, + def __init__(self, year_start=GlossaryCore.YearStartDefault, year_end=GlossaryCore.YearEndDefault, bspline=True, run_usecase=False, execution_engine=None, techno_dict=GlossaryEnergy.DEFAULT_COARSE_TECHNO_DICT,): super().__init__(__file__, run_usecase=run_usecase, execution_engine=execution_engine) self.year_start = year_start @@ -72,6 +72,7 @@ def setup_usecase(self, study_folder_path=None): f'{ns}.{self.optim_name}.max_iter': 1500, f'{ns}.warm_start': True, f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.warm_start': True, + f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.cache_type': 'SimpleCache', # SLSQP, NLOPT_SLSQP f'{ns}.{self.optim_name}.algo': "L-BFGS-B", f'{ns}.{self.optim_name}.formulation': 'DisciplinaryOpt', @@ -109,5 +110,4 @@ def setup_usecase(self, study_folder_path=None): if '__main__' == __name__: uc_cls = Study(run_usecase=True) - uc_cls.load_data() - uc_cls.run() \ No newline at end of file + uc_cls.test() \ No newline at end of file diff --git a/climateeconomics/sos_processes/iam/witness/sectorization/witness_sectorization_optim_sub_process/usecase.py b/climateeconomics/sos_processes/iam/witness/sectorization/witness_sectorization_optim_sub_process/usecase.py index 0960e64e7..3f29f0c75 100644 --- a/climateeconomics/sos_processes/iam/witness/sectorization/witness_sectorization_optim_sub_process/usecase.py +++ b/climateeconomics/sos_processes/iam/witness/sectorization/witness_sectorization_optim_sub_process/usecase.py @@ -45,7 +45,7 @@ class Study(ClimateEconomicsStudyManager): def __init__(self, year_start=GlossaryCore.YearStartDefault, year_end=GlossaryCore.YearEndDefault, - bspline=False, run_usecase=False, + bspline=True, run_usecase=False, execution_engine=None, techno_dict=GlossaryEnergy.DEFAULT_COARSE_TECHNO_DICT): super().__init__(__file__, run_usecase=run_usecase, execution_engine=execution_engine) self.dspace_size: int = 0 @@ -208,7 +208,7 @@ def setup_usecase(self, study_folder_path=None): values_dict = {} values_dict[f'{self.study_name}.epsilon0'] = 1.0 - values_dict[f'{self.study_name}.{self.coupling_name}.sub_mda_class'] = 'GSPureNewtonMDA' + values_dict[f'{self.study_name}.{self.coupling_name}.inner_mda_name'] = 'MDAGSNewton' values_dict[f'{self.study_name}.{self.coupling_name}.max_mda_iter'] = 50 values_dict[f'{self.study_name}.{self.coupling_name}.tolerance'] = 1e-10 values_dict[f'{self.study_name}.{self.coupling_name}.linearization_mode'] = 'adjoint' diff --git a/climateeconomics/sos_processes/iam/witness/sectorization/witness_sectorized_ms_optim_process/usecase.py b/climateeconomics/sos_processes/iam/witness/sectorization/witness_sectorized_ms_optim_process/usecase.py index ecdd66adc..6186f9440 100644 --- a/climateeconomics/sos_processes/iam/witness/sectorization/witness_sectorized_ms_optim_process/usecase.py +++ b/climateeconomics/sos_processes/iam/witness/sectorization/witness_sectorized_ms_optim_process/usecase.py @@ -31,7 +31,7 @@ class Study(ClimateEconomicsStudyManager): UC3 = "+ Damage, + Tax, No CCUS" UC4 = "+ Damage, + Tax, All technos" - def __init__(self, bspline=False, run_usecase=False, execution_engine=None): + def __init__(self, bspline=True, run_usecase=False, execution_engine=None): super().__init__(__file__, run_usecase=run_usecase, execution_engine=execution_engine) self.bspline = bspline self.data_dir = join(dirname(__file__), 'data') @@ -104,4 +104,3 @@ def setup_usecase(self, study_folder_path=None): if '__main__' == __name__: uc_cls = Study(run_usecase=True) uc_cls.test() - uc_cls.test() diff --git a/climateeconomics/sos_processes/iam/witness/witness/usecase_witness.py b/climateeconomics/sos_processes/iam/witness/witness/usecase_witness.py index f76ab2732..cc7b3adb4 100644 --- a/climateeconomics/sos_processes/iam/witness/witness/usecase_witness.py +++ b/climateeconomics/sos_processes/iam/witness/witness/usecase_witness.py @@ -19,7 +19,6 @@ import pstats from io import StringIO -import pandas as pd from energy_models.core.energy_process_builder import INVEST_DISCIPLINE_OPTIONS from energy_models.glossaryenergy import GlossaryEnergy from energy_models.sos_processes.energy.MDA.energy_process_v0_mda.usecase import ( @@ -74,19 +73,6 @@ def __init__(self, year_start=GlossaryCore.YearStartDefault, year_end=GlossaryCo self.sub_study_path_dict = self.dc_energy.sub_study_path_dict self.test_post_procs = False - def setup_constraint_land_use(self): - - func_df = pd.DataFrame({ - 'variable': ['land_demand_constraint'], - 'parent': ['agriculture_constraint'], - 'ftype': [INEQ_CONSTRAINT], - 'weight': [-1.0], - AGGR_TYPE: [AGGR_TYPE_SUM], - 'namespace': [GlossaryCore.NS_FUNCTIONS] - }) - - return func_df - def setup_usecase(self, study_folder_path=None): setup_data_list = [] @@ -112,14 +98,12 @@ def setup_usecase(self, study_folder_path=None): self.merge_design_spaces([dspace_energy, dc_witness.dspace]) - # constraint land use - land_use_df_constraint = self.setup_constraint_land_use() # WITNESS # setup objectives self.func_df = concat( [dc_witness.setup_objectives(), dc_witness.setup_constraints(), self.dc_energy.setup_constraints(), - self.dc_energy.setup_objectives(), land_use_df_constraint]) + self.dc_energy.setup_objectives()]) self.energy_list = self.dc_energy.energy_list self.ccs_list = self.dc_energy.ccs_list @@ -136,7 +120,7 @@ def setup_mda(self): f'{self.study_name}.tolerance': 1.0e-10, f'{self.study_name}.n_processes': 1, f'{self.study_name}.linearization_mode': 'adjoint', - f'{self.study_name}.sub_mda_class': 'GSPureNewtonMDA', + f'{self.study_name}.inner_mda_name': 'MDAGSNewton', f'{self.study_name}.cache_type': 'SimpleCache', } # f'{self.study_name}.gauss_seidel_execution': True} return numerical_values_dict diff --git a/climateeconomics/sos_processes/iam/witness/witness_coarse/usecase_witness_coarse_new.py b/climateeconomics/sos_processes/iam/witness/witness_coarse/usecase_witness_coarse_new.py index 365d547db..01e8ac571 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_coarse/usecase_witness_coarse_new.py +++ b/climateeconomics/sos_processes/iam/witness/witness_coarse/usecase_witness_coarse_new.py @@ -20,7 +20,6 @@ from energy_models.sos_processes.energy.MDA.energy_process_v0_mda.usecase import ( Study as datacase_energy, ) -from pandas import DataFrame from sostrades_optimization_plugins.models.func_manager.func_manager import ( FunctionManager, ) @@ -72,32 +71,6 @@ def __init__(self, year_start=GlossaryCore.YearStartDefault, year_end=GlossaryCo def setup_process(self): datacase_energy.setup_process(self) - def setup_constraint_land_use(self): - func_df = DataFrame( - columns=['variable', 'parent', 'ftype', 'weight', AGGR_TYPE]) - list_var = [] - list_parent = [] - list_ftype = [] - list_weight = [] - list_aggr_type = [] - list_ns = [] - list_var.extend( - ['land_demand_constraint_df']) - list_parent.extend([None]) - list_ftype.extend([INEQ_CONSTRAINT]) - list_weight.extend([-1.0]) - list_aggr_type.extend( - [AGGR_TYPE_SUM]) - list_ns.extend([GlossaryCore.NS_FUNCTIONS]) - func_df['variable'] = list_var - func_df['parent'] = list_parent - func_df['ftype'] = list_ftype - func_df['weight'] = list_weight - func_df[AGGR_TYPE] = list_aggr_type - func_df['namespace'] = list_ns - - return func_df - def setup_usecase(self, study_folder_path=None): setup_data_list = [] @@ -131,7 +104,7 @@ def setup_usecase(self, study_folder_path=None): f'{self.study_name}.tolerance': 1.0e-10, f'{self.study_name}.n_processes': 1, f'{self.study_name}.linearization_mode': 'adjoint', - f'{self.study_name}.sub_mda_class': 'GSPureNewtonMDA', + f'{self.study_name}.inner_mda_name': 'MDAGSNewton', f'{self.study_name}.cache_type': 'SimpleCache'} setup_data_list.append(numerical_values_dict) diff --git a/climateeconomics/sos_processes/iam/witness/witness_coarse_dev/usecase_witness_coarse_new.py b/climateeconomics/sos_processes/iam/witness/witness_coarse_dev/usecase_witness_coarse_new.py index cdab707ba..46a960331 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_coarse_dev/usecase_witness_coarse_new.py +++ b/climateeconomics/sos_processes/iam/witness/witness_coarse_dev/usecase_witness_coarse_new.py @@ -20,7 +20,6 @@ from energy_models.sos_processes.energy.MDA.energy_process_v0_mda.usecase import ( Study as datacase_energy, ) -from pandas import DataFrame from sostrades_optimization_plugins.models.func_manager.func_manager import ( FunctionManager, ) @@ -74,19 +73,6 @@ def __init__(self, year_start=GlossaryCore.YearStartDefault, year_end=GlossaryCo def setup_process(self): datacase_energy.setup_process(self) - def setup_constraint_land_use(self): - # Create the DataFrame - func_df = DataFrame({ - 'variable': ['land_demand_constraint_df'], - 'parent': [None], - 'ftype': [INEQ_CONSTRAINT], - 'weight': [-1.0], - AGGR_TYPE: [AGGR_TYPE_SUM], - 'namespace': [GlossaryCore.NS_FUNCTIONS] - }) - - return func_df - def setup_usecase(self, study_folder_path=None): setup_data_list = [] @@ -123,7 +109,7 @@ def setup_mda(self): f'{self.study_name}.tolerance': 1.0e-10, f'{self.study_name}.n_processes': 1, f'{self.study_name}.linearization_mode': 'adjoint', - f'{self.study_name}.sub_mda_class': 'GSPureNewtonMDA', + f'{self.study_name}.inner_mda_name': 'MDAGSNewton', f'{self.study_name}.cache_type': 'SimpleCache'} return numerical_values_dict diff --git a/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_grad_check_sub_process/demo_uq/_usecase.py b/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_grad_check_sub_process/demo_uq/_usecase.py new file mode 100644 index 000000000..a3052c631 --- /dev/null +++ b/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_grad_check_sub_process/demo_uq/_usecase.py @@ -0,0 +1,132 @@ +''' +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. +''' + +from typing import Any + +from numpy import array +from pandas import DataFrame +from sostrades_core.study_manager.study_manager import StudyManager + +from climateeconomics.sos_processes.iam.witness.witness_coarse_dev_grad_check_sub_process._usecase_2_fossil_only_damage_high_tax import ( + Study as WitnessStudy, +) + + +class Study(StudyManager): + """The UQ study on witness_coarse_float_invest.""" + + SAMPLE_GENERATOR_NAME = "SampleGenerator" + + UQ_NAME = "UncertaintyQuantification" + + def __init__(self) -> None: + super().__init__(__file__) + self.witness_study = WitnessStudy(run_usecase=True, execution_engine=self.ee) + + def setup_usecase(self) -> dict[str, Any]: + """Setup the usecase. + + Returns: + The usecase options. + """ + self.witness_study.study_name = f"{self.study_name}.DOE" + params_witness = self.witness_study.setup_usecase()[0] + params = { + f"{self.study_name}.DOE.{'.'.join(k.split('.')[2:])}": v + for k, v in params_witness.items() + } + + # Setup the uncertain inputs/outputs + dspace_dict = { + "variable": [f"{self.study_name}.Damage.tp_a3"], + "value": [array([3.0])], + "lower_bnd": [array([1.0])], + "upper_bnd": [array([6.0])], + "enable_variable": [True], + "activated_elem": [[True]], + } + design_space_uq = DataFrame(dspace_dict) + input_selection = { + "selected_input": [ + False, + False, + False, + False, + False, + False, + False, + True, + False, + ], + "full_name": [ + f"{self.study_name}.RenewableTechnoInfo.Opex_percentage", + f"{self.study_name}.RenewableTechnoInfo.Initial_capex", + f"{self.study_name}.RenewableTechnoInfo.Energy_costs", + f"{self.study_name}.FossilTechnoInfo.Opex_percentage", + f"{self.study_name}.FossilTechnoInfo.Initial_capex", + f"{self.study_name}.FossilTechnoInfo.Energy_costs", + f"{self.study_name}.FossilTechnoInfo.CO2_from_production", + f"{self.study_name}.Damage.tp_a3", + f"{self.study_name}.Temperature_change.init_temp_atmo", + ], + } + input_selection = DataFrame(input_selection) + output_selection = { + "selected_output": [True, True, True, True, True, True, True, True], + "full_name": [ + "WITNESS_Eval.Indicators.mean_energy_price_2100", + "WITNESS_Eval.Indicators.fossil_energy_price_2100", + "WITNESS_Eval.Indicators.renewable_energy_price_2100", + "WITNESS_Eval.Indicators.total_energy_production_2100", + "WITNESS_Eval.Indicators.fossil_energy_production_2100", + "WITNESS_Eval.Indicators.renewable_energy_production_2100", + "WITNESS_Eval.Indicators.world_net_product_2100", + "WITNESS_Eval.Indicators.temperature_rise_2100", + ], + } + output_selection = DataFrame(output_selection) + + # DOE sampling + sampling_params = { + "sampling_method": "doe_algo", + "sampling_algo": "PYDOE_FULLFACT", + "design_space": design_space_uq, + "algo_options": {"n_samples": 5}, + "eval_inputs": input_selection, + "sampling_generation_mode": "at_run_time", + } + params.update({ + f"{self.study_name}.{self.SAMPLE_GENERATOR_NAME}.{key}": value + for key, value in sampling_params.items() + }) + params[f"{self.study_name}.DOE.with_sample_generator"] = True + params[f"{self.study_name}.DOE.gather_outputs"] = output_selection + + # UQ + params.update( + { + f"{self.study_name}.{self.UQ_NAME}.eval_inputs": input_selection, + f"{self.study_name}.{self.UQ_NAME}.design_space": design_space_uq, + f"{self.study_name}.{self.UQ_NAME}.gather_outputs": output_selection, + } + ) + + return [params] + + +if "__main__" == __name__: + usecase = Study() + usecase.test() diff --git a/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_grad_check_sub_process/demo_uq/process.py b/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_grad_check_sub_process/demo_uq/process.py new file mode 100644 index 000000000..5dd8bd286 --- /dev/null +++ b/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_grad_check_sub_process/demo_uq/process.py @@ -0,0 +1,108 @@ +''' +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. +''' + +from __future__ import annotations + +from energy_models.core.energy_process_builder import INVEST_DISCIPLINE_OPTIONS +from energy_models.glossaryenergy import GlossaryEnergy +from energy_models.sos_processes.energy.MDA.energy_process_v0.usecase import ( + INVEST_DISC_NAME, +) +from energy_models.sos_processes.witness_sub_process_builder import ( + WITNESSSubProcessBuilder, +) + +from climateeconomics.sos_processes.iam.witness.witness_coarse_dev_grad_check_sub_process.demo_uq.usecase import ( + Study, +) + + +class ProcessBuilder(WITNESSSubProcessBuilder): + """Process builder for the UQ analysis on Witness coarse - Float invest.""" + + def get_builders(self): + coupling_name = "WITNESS_Eval" + extra_name = "WITNESS" + self.invest_discipline = INVEST_DISCIPLINE_OPTIONS[2] + self.techno_dict = GlossaryEnergy.DEFAULT_COARSE_TECHNO_DICT + + chain_builders = self.ee.factory.get_builder_from_process( + "climateeconomics.sos_processes.iam.witness", + "witness", + techno_dict=self.techno_dict, + invest_discipline=self.invest_discipline, + process_level=self.process_level, + use_resources_bool=False, + ) + + # Add Profile Builder discipline + mods_dict = { + "InvestmentsProfileBuilderDisc": "energy_models.core.investments.disciplines.investments_profile_builder_disc.InvestmentsProfileBuilderDisc", + } + + builder_invest_profile = self.create_builder_list( + mods_dict, associate_namespace=False + ) + chain_builders.extend(builder_invest_profile) + + self.ee.ns_manager.update_namespace_list_with_extra_ns( + extra_name, after_name=self.ee.study_name, clean_existing=True + ) + self.ee.factory.update_builder_list_with_extra_name( + extra_name, builder_list=chain_builders + ) + self.ee.ns_manager.update_namespace_list_with_extra_ns( + coupling_name, after_name=self.ee.study_name, clean_existing=True + ) + + # Add Indicators discipline + chain_builders.append( + self.ee.factory.get_builder_from_module( + "Indicators", + "climateeconomics.sos_wrapping.sos_wrapping_witness_coarse_for_sensitivity.witness_indicators.WitnessIndicators", + ) + ) + + ns_dict = { + # GlossaryCore.NS_FUNCTIONS: f"{self.ee.study_name}.{coupling_name}.{extra_name}", + #'ns_public': f'{self.ee.study_name}', + # "ns_optim": f"{self.ee.study_name}", + # GlossaryCore.NS_REFERENCE: f"{self.ee.study_name}.NormalizationReferences", + "ns_invest": f"{self.ee.study_name}.{coupling_name}.{extra_name}.{INVEST_DISC_NAME}", + # "ns_witness": f"{self.ee.study_name}.{coupling_name}.{extra_name}", + } + self.ee.ns_manager.add_ns_def(ns_dict) + + # create coupling builder + coupling_builder = self.ee.factory.create_builder_coupling(coupling_name) + coupling_builder.set_builder_info("cls_builder", chain_builders) + + # DOE builder + doe_builder = self.ee.factory.create_mono_instance_driver( + "DOE", coupling_builder + ) + + # UQ builder + uq_builder = self.ee.factory.add_uq_builder(Study.UQ_NAME) + + ns_dict = { + "ns_sample_generator": f"{self.ee.study_name}.{Study.SAMPLE_GENERATOR_NAME}", + "ns_evaluator": f"{self.ee.study_name}.DOE", + "ns_uncertainty_quantification": f"{self.ee.study_name}.{Study.UQ_NAME}", + } + self.ee.ns_manager.add_ns_def(ns_dict) + + return [doe_builder, uq_builder] diff --git a/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_ms_optim_ku_process/usecase_ms_ku.py b/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_ms_optim_ku_process/usecase_ms_ku.py index bfef25bd4..aa46983bf 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_ms_optim_ku_process/usecase_ms_ku.py +++ b/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_ms_optim_ku_process/usecase_ms_ku.py @@ -56,21 +56,17 @@ def setup_usecase(self, study_folder_path=None): scenario_df = pd.DataFrame({'selected_scenario': [True] * len(self.scenario_dict) ,'scenario_name': list(self.scenario_dict.keys())}) values_dict = { f'{self.study_name}.{self.scatter_scenario}.samples_df': scenario_df, - f'{self.study_name}.n_subcouplings_parallel': min(16, len(scenario_df.loc[scenario_df['selected_scenario']])) } for scenario_name, studyClass in self.scenario_dict.items(): scenarioUseCase = studyClass(execution_engine=self.execution_engine, year_start=self.year_start) scenarioUseCase.study_name = f'{self.study_name}.{self.scatter_scenario}.{scenario_name}' scenarioData = scenarioUseCase.setup_usecase() - scenarioDatadict = {} - for data in scenarioData: - scenarioDatadict.update(data) - values_dict.update(scenarioDatadict) + values_dict.update(scenarioData) values_dict.update({f"{self.study_name}.{self.scatter_scenario}.{scenario_name}.WITNESS_MDO.max_iter": 400 for scenario_name in self.scenario_dict.keys()}) values_dict.update( - {f"{self.study_name}.{self.scatter_scenario}.{scenario_name}.WITNESS_MDO.WITNESS_Eval.sub_mda_class": "MDAGaussSeidel" for scenario_name in + {f"{self.study_name}.{self.scatter_scenario}.{scenario_name}.WITNESS_MDO.WITNESS_Eval.inner_mda_name": "MDAGaussSeidel" for scenario_name in self.scenario_dict.keys()}) values_dict = self.update_dataframes_with_year_star(values_dict=values_dict, year_start=self.year_start) diff --git a/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_ms_optim_ku_process/usecase_ms_ku_tipping_point.py b/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_ms_optim_ku_process/usecase_ms_ku_tipping_point.py index b98832603..085e395fc 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_ms_optim_ku_process/usecase_ms_ku_tipping_point.py +++ b/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_ms_optim_ku_process/usecase_ms_ku_tipping_point.py @@ -36,7 +36,7 @@ class Study(ClimateEconomicsStudyManager): - def __init__(self, year_start=2023, filename=__file__, bspline=False, run_usecase=False, execution_engine=None): + def __init__(self, year_start=2023, filename=__file__, bspline=True, run_usecase=False, execution_engine=None): super().__init__(filename, run_usecase=run_usecase, execution_engine=execution_engine) self.bspline = bspline self.data_dir = join(dirname(__file__), 'data') @@ -77,14 +77,17 @@ def setup_usecase(self, study_folder_path=None): scenarioUseCase = studyClass(execution_engine=self.execution_engine, year_start=self.year_start) scenarioUseCase.study_name = f'{self.study_name}.{scatter_scenario}.{scenario_name}' scenarioData = scenarioUseCase.setup_usecase() - scenarioDatadict = {} - for data in scenarioData: - scenarioDatadict.update(data) - values_dict.update(scenarioDatadict) + if isinstance(scenarioData, list): + for dictionnary in scenarioData: + values_dict.update(dictionnary) + elif isinstance(scenarioData, dict): + values_dict.update(scenarioData) + else: + raise ValueError("Fixme") values_dict.update({f"{self.study_name}.{scatter_scenario}.{scenario_name}.WITNESS_MDO.max_iter": 100 for scenario_name in scenario_dict.keys()}) values_dict.update( - {f"{self.study_name}.{scatter_scenario}.{scenario_name}.WITNESS_MDO.WITNESS_Eval.sub_mda_class": "MDAGaussSeidel" for scenario_name in + {f"{self.study_name}.{scatter_scenario}.{scenario_name}.WITNESS_MDO.WITNESS_Eval.inner_mda_name": "MDAGaussSeidel" for scenario_name in scenario_dict.keys()}) # update the tipping point value values_dict.update({ diff --git a/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_ms_optim_process/usecase_ms_2023_with_nze.py b/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_ms_optim_process/usecase_ms_2023_with_nze.py new file mode 100644 index 000000000..144e185e9 --- /dev/null +++ b/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_ms_optim_process/usecase_ms_2023_with_nze.py @@ -0,0 +1,81 @@ +''' +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. + +''' +from os.path import dirname, join + +import pandas as pd + +from climateeconomics.core.tools.ClimateEconomicsStudyManager import ( + ClimateEconomicsStudyManager, +) +from climateeconomics.sos_processes.iam.witness.witness_coarse_dev_optim_process.usecase_3_no_ccs_damage_high_tax import ( + Study as Study3, +) +from climateeconomics.sos_processes.iam.witness.witness_coarse_dev_optim_process.usecase_4_all_in_damage_high_tax import ( + Study as Study4, +) +from climateeconomics.sos_processes.iam.witness.witness_coarse_dev_optim_process.usecase_2023_nze_2050 import ( + Study as StudyNZE, +) + + +class Study(ClimateEconomicsStudyManager): + NO_CCUS = "No CCUS" + ALL_TECHNOS = "All technos" + ALL_TECHNOS_NZE = "All technos NZE" + + def __init__(self, year_start=2023, bspline=False, run_usecase=False, execution_engine=None): + super().__init__(__file__, run_usecase=run_usecase, execution_engine=execution_engine) + self.bspline = bspline + self.data_dir = join(dirname(__file__), 'data') + self.year_start = year_start + + def setup_usecase(self, study_folder_path=None): + + scatter_scenario = 'optimization scenarios' + + scenario_dict = { + self.NO_CCUS: Study3, + self.ALL_TECHNOS: Study4, + self.ALL_TECHNOS_NZE: StudyNZE, + } + + # changing the tipping point + + scenario_df = pd.DataFrame({'selected_scenario': [True] * len(scenario_dict), 'scenario_name': list(scenario_dict.keys())}) + values_dict = { + f'{self.study_name}.{scatter_scenario}.samples_df': scenario_df, + f'{self.study_name}.n_subcouplings_parallel': min(16, len(scenario_df.loc[scenario_df['selected_scenario']])) + } + + for scenario_name, studyClass in scenario_dict.items(): + scenarioUseCase = studyClass(execution_engine=self.execution_engine, year_start=self.year_start) + scenarioUseCase.study_name = f'{self.study_name}.{scatter_scenario}.{scenario_name}' + scenarioData = scenarioUseCase.setup_usecase() + scenarioDatadict = {} + scenarioDatadict.update(scenarioData) + values_dict.update(scenarioDatadict) + + values_dict.update({f"{self.study_name}.{scatter_scenario}.{scenario_name}.WITNESS_MDO.max_iter": 400 for scenario_name in scenario_dict.keys()}) + # update the tipping point value + + values_dict = self.update_dataframes_with_year_star(values_dict=values_dict, year_start=self.year_start) + + return values_dict + + +if '__main__' == __name__: + uc_cls = Study(run_usecase=True) + uc_cls.test() diff --git a/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_ms_optim_process/usecase_ms_2_tipping_point_2023.py b/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_ms_optim_process/usecase_ms_2_tipping_point_2023.py index 450df87ad..b39b124d4 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_ms_optim_process/usecase_ms_2_tipping_point_2023.py +++ b/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_ms_optim_process/usecase_ms_2_tipping_point_2023.py @@ -96,7 +96,7 @@ def setup_usecase(self, study_folder_path=None): values_dict = self.update_dataframes_with_year_star(values_dict=values_dict, year_start=self.year_start) values_dict.update( - {f"{self.study_name}.{scatter_scenario}.{scenario_name}.WITNESS_MDO.max_iter": 2 for scenario_name in + {f"{self.study_name}.{scatter_scenario}.{scenario_name}.WITNESS_MDO.max_iter": 1 for scenario_name in scenario_dict.keys()}) return values_dict @@ -105,4 +105,4 @@ def setup_usecase(self, study_folder_path=None): if '__main__' == __name__: uc_cls = Study(run_usecase=True) uc_cls.load_data() - uc_cls.test() + uc_cls.run() diff --git a/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_ms_story_telling/usecase_witness_ms_mda_four_scenarios_2023.py b/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_ms_story_telling/usecase_witness_ms_mda_four_scenarios_2023.py index 1cd38d8b3..65d49c055 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_ms_story_telling/usecase_witness_ms_mda_four_scenarios_2023.py +++ b/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_ms_story_telling/usecase_witness_ms_mda_four_scenarios_2023.py @@ -55,5 +55,4 @@ def setup_usecase(self, study_folder_path=None): if '__main__' == __name__: uc_cls = Study(run_usecase=True) - uc_cls.load_data() - uc_cls.run() \ No newline at end of file + uc_cls.test() \ No newline at end of file diff --git a/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_optim_process/usecase_4_all_in_damage_high_tax.py b/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_optim_process/usecase_4_all_in_damage_high_tax.py index 7eb8513c4..56920e8f8 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_optim_process/usecase_4_all_in_damage_high_tax.py +++ b/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_optim_process/usecase_4_all_in_damage_high_tax.py @@ -103,4 +103,4 @@ def setup_usecase(self, study_folder_path=None): if '__main__' == __name__: uc_cls = Study(run_usecase=True) - uc_cls.test_jacobians_of_each_disc() + uc_cls.test() diff --git a/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_optim_process/usecase_witness_optim_invest_distrib.py b/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_optim_process/usecase_witness_optim_invest_distrib.py index d1601024a..b90fc866f 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_optim_process/usecase_witness_optim_invest_distrib.py +++ b/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_optim_process/usecase_witness_optim_invest_distrib.py @@ -14,7 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. ''' -import numpy as np from energy_models.core.energy_process_builder import INVEST_DISCIPLINE_OPTIONS from energy_models.glossaryenergy import GlossaryEnergy from sostrades_optimization_plugins.models.design_var.design_var_disc import ( @@ -51,7 +50,7 @@ class Study(ClimateEconomicsStudyManager): - def __init__(self, year_start=GlossaryCore.YearStartDefault, year_end=GlossaryCore.YearEndDefault, bspline=False, run_usecase=False, + def __init__(self, year_start=GlossaryCore.YearStartDefault, year_end=GlossaryCore.YearEndDefault, bspline=True, run_usecase=False, execution_engine=None, invest_discipline=INVEST_DISCIPLINE_OPTIONS[2], techno_dict=GlossaryEnergy.DEFAULT_COARSE_TECHNO_DICT, agri_techno_list=COARSE_AGRI_MIX_TECHNOLOGIES_LIST_FOR_OPT, @@ -106,7 +105,7 @@ def setup_usecase(self, study_folder_path=None): f'{ns}.{self.optim_name}.ineq_constraints': [], # optimization parameters: - f'{ns}.{self.optim_name}.max_iter': 1500, + f'{ns}.{self.optim_name}.max_iter': 2, f'{ns}.warm_start': True, f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.warm_start': True, # SLSQP, NLOPT_SLSQP @@ -140,7 +139,7 @@ def setup_usecase(self, study_folder_path=None): "n_processes": 32, "use_threading": False, "wait_time_between_fork": 0}, - f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.sub_mda_class': 'MDAGaussSeidel', + f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.inner_mda_name': 'MDAGaussSeidel', f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.max_mda_iter': 50, f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.cache_type': 'SimpleCache', f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.propagate_cache_to_children': True, @@ -157,13 +156,14 @@ def setup_usecase(self, study_folder_path=None): out = {} out.update(values_dict) + self.remove_all_variables_in_values_dict(out, shortvarname="inner_mda_name") out.update(optim_values_dict) out.update(ref_value_dict) dspace = out[f'{self.study_name}.{self.optim_name}.design_space'] list_design_var_to_clean = ['red_meat_calories_per_day_ctrl', 'white_meat_calories_per_day_ctrl', 'vegetables_and_carbs_calories_per_day_ctrl', - 'milk_and_eggs_calories_per_day_ctrl', 'forest_investment_array_mix', + 'milk_and_eggs_calories_per_day_ctrl', 'reforestation_investment_array_mix', 'deforestation_investment_ctrl'] # clean dspace @@ -184,16 +184,6 @@ def setup_usecase(self, study_folder_path=None): f'{self.study_name}.{self.optim_name}.{self.witness_uc.coupling_name}.DesignVariables.design_var_descriptor': updated_dvar_descriptor }) - - a = { - 'out_name': GlossaryCore.ShareNonEnergyInvestmentsValue, - 'out_type': "dataframe", - 'key': GlossaryCore.ShareNonEnergyInvestmentsValue, - 'index': np.arange(self.year_start, self.year_end + 1), - 'index_name': GlossaryCore.Years, - 'namespace_in': "", - 'namespace_out': GlossaryCore.NS_WITNESS, - } return out diff --git a/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_story_telling/usecase_7_witness_coarse_mda_gdp_model_w_damage_w_co2_tax.py b/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_story_telling/usecase_7_witness_coarse_mda_gdp_model_w_damage_w_co2_tax.py index fd907673a..3963a261d 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_story_telling/usecase_7_witness_coarse_mda_gdp_model_w_damage_w_co2_tax.py +++ b/climateeconomics/sos_processes/iam/witness/witness_coarse_dev_story_telling/usecase_7_witness_coarse_mda_gdp_model_w_damage_w_co2_tax.py @@ -44,7 +44,7 @@ def __init__(self, run_usecase=False, execution_engine=None, year_start=Glossary super().__init__(__file__, run_usecase=run_usecase, execution_engine=execution_engine) self.year_start = year_start self.year_end = year_end - self.test_post_procs = False + self.test_post_procs = True def setup_usecase(self, study_folder_path=None): witness_uc = usecase_witness_mda() diff --git a/climateeconomics/sos_processes/iam/witness/witness_coarse_optim_process/usecase_witness_optim_invest_distrib.py b/climateeconomics/sos_processes/iam/witness/witness_coarse_optim_process/usecase_witness_optim_invest_distrib.py index 619f84d69..7578578df 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_coarse_optim_process/usecase_witness_optim_invest_distrib.py +++ b/climateeconomics/sos_processes/iam/witness/witness_coarse_optim_process/usecase_witness_optim_invest_distrib.py @@ -127,7 +127,7 @@ def setup_usecase(self, study_folder_path=None): "n_processes": 32, "use_threading": False, "wait_time_between_fork": 0}, - f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.sub_mda_class': 'GSPureNewtonMDA', + f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.inner_mda_name': 'MDAGSNewton', f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.max_mda_iter': 50, } # f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.DesignVariables.{WRITE_XVECT}': # True} diff --git a/climateeconomics/sos_processes/iam/witness/witness_coarse_story_telling_mda_sensitivity/usecase_witness_mda_sensitivity.py b/climateeconomics/sos_processes/iam/witness/witness_coarse_story_telling_mda_sensitivity/_usecase_witness_mda_sensitivity.py similarity index 99% rename from climateeconomics/sos_processes/iam/witness/witness_coarse_story_telling_mda_sensitivity/usecase_witness_mda_sensitivity.py rename to climateeconomics/sos_processes/iam/witness/witness_coarse_story_telling_mda_sensitivity/_usecase_witness_mda_sensitivity.py index 7707bdecf..b2f4fbeb1 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_coarse_story_telling_mda_sensitivity/usecase_witness_mda_sensitivity.py +++ b/climateeconomics/sos_processes/iam/witness/witness_coarse_story_telling_mda_sensitivity/_usecase_witness_mda_sensitivity.py @@ -93,7 +93,7 @@ def setup_usecase(self, study_folder_path=None): for dict_data in uc.setup_usecase(): values_dict.update(dict_data) # NB: switch to MDAGaussSeidel so it won't crash with the l1 tests, GSNewton diverges. - values_dict[f'{self.study_name}.{self.driver_name}.{scenario}.sub_mda_class'] = 'MDAGaussSeidel' + values_dict[f'{self.study_name}.{self.driver_name}.{scenario}.inner_mda_name'] = 'MDANewtonRaphson' values_dict[f'{self.study_name}.{self.driver_name}.{scenario}.max_mda_iter'] = 2 return values_dict diff --git a/climateeconomics/sos_processes/iam/witness/witness_coarse_story_telling_optim_process/usecase_2_optim_story_telling.py b/climateeconomics/sos_processes/iam/witness/witness_coarse_story_telling_optim_process/usecase_2_optim_story_telling.py index 3115d74de..5d8d9dfee 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_coarse_story_telling_optim_process/usecase_2_optim_story_telling.py +++ b/climateeconomics/sos_processes/iam/witness/witness_coarse_story_telling_optim_process/usecase_2_optim_story_telling.py @@ -103,7 +103,7 @@ def setup_usecase(self, study_folder_path=None): "n_processes": 32, "use_threading": False, "wait_time_between_fork": 0}, - f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.sub_mda_class': 'GSPureNewtonMDA', + f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.inner_mda_name': 'MDAGSNewton', f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.max_mda_iter': 50, f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.cache_type': 'SimpleCache', f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.propagate_cache_to_children': True, diff --git a/climateeconomics/sos_processes/iam/witness/witness_coarse_story_telling_optim_process/usecase_2b_optim_story_telling.py b/climateeconomics/sos_processes/iam/witness/witness_coarse_story_telling_optim_process/usecase_2b_optim_story_telling.py index 7c3e32ead..ed68917d6 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_coarse_story_telling_optim_process/usecase_2b_optim_story_telling.py +++ b/climateeconomics/sos_processes/iam/witness/witness_coarse_story_telling_optim_process/usecase_2b_optim_story_telling.py @@ -62,7 +62,7 @@ def setup_usecase(self, study_folder_path=None): # design space WITNESS # optimization functions: - optim_values_dict = {f'{ns}.epsilon0': 1, + values_dict.update({f'{ns}.epsilon0': 1, f'{ns}.cache_type': 'SimpleCache', f'{ns}.{self.optim_name}.objective_name': FunctionManagerDisc.OBJECTIVE_LAGR, #GlossaryCore.UsableCapitalObjectiveName, f'{ns}.{self.optim_name}.eq_constraints': [], @@ -102,28 +102,25 @@ def setup_usecase(self, study_folder_path=None): "n_processes": 32, "use_threading": False, "wait_time_between_fork": 0}, - f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.sub_mda_class': 'GSPureNewtonMDA', + f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.inner_mda_name': 'MDAGSNewton', f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.max_mda_iter': 50, f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.cache_type': 'SimpleCache', f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.propagate_cache_to_children': True, - f'{self.witness_uc.witness_uc.study_name}.DesignVariables.is_val_level': False} - - data_witness = [] + f'{self.witness_uc.witness_uc.study_name}.DesignVariables.is_val_level': False}) # update assumptions dict and specific values for optimization - updated_data = {f'{self.study_name}.{self.optim_name}.{self.coupling_name}.{self.extra_name}.assumptions_dict': {'compute_gdp': True, + values_dict.update({f'{self.study_name}.{self.optim_name}.{self.coupling_name}.{self.extra_name}.assumptions_dict': {'compute_gdp': True, 'compute_climate_impact_on_gdp': True, 'activate_climate_effect_population': True, 'activate_pandemic_effects': True, - }} - data_witness.append(updated_data) + }}) - data_witness.append({ + values_dict.update({ f"{self.study_name}.{self.optim_name}.{self.coupling_name}.{self.extra_name}.ccs_price_percentage": 0.0, f"{self.study_name}.{self.optim_name}.{self.coupling_name}.{self.extra_name}.co2_damage_price_percentage": 100., }) - return [values_dict] + [optim_values_dict] + data_witness + return values_dict if '__main__' == __name__: diff --git a/climateeconomics/sos_processes/iam/witness/witness_coarse_story_telling_optim_process/usecase_4_optim_story_telling.py b/climateeconomics/sos_processes/iam/witness/witness_coarse_story_telling_optim_process/usecase_4_optim_story_telling.py index 2f0e35882..524aa0ed6 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_coarse_story_telling_optim_process/usecase_4_optim_story_telling.py +++ b/climateeconomics/sos_processes/iam/witness/witness_coarse_story_telling_optim_process/usecase_4_optim_story_telling.py @@ -63,7 +63,7 @@ def setup_usecase(self, study_folder_path=None): # design space WITNESS # optimization functions: - optim_values_dict = {f'{ns}.epsilon0': 1, + values_dict.update({f'{ns}.epsilon0': 1, f'{ns}.cache_type': 'SimpleCache', f'{ns}.{self.optim_name}.objective_name': FunctionManagerDisc.OBJECTIVE_LAGR, #GlossaryCore.UsableCapitalObjectiveName, f'{ns}.{self.optim_name}.eq_constraints': [], @@ -103,28 +103,26 @@ def setup_usecase(self, study_folder_path=None): "n_processes": 32, "use_threading": False, "wait_time_between_fork": 0}, - f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.sub_mda_class': 'GSPureNewtonMDA', + f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.inner_mda_name': 'MDAGSNewton', f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.max_mda_iter': 50, f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.cache_type': 'SimpleCache', f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.propagate_cache_to_children': True, - f'{self.witness_uc.witness_uc.study_name}.DesignVariables.is_val_level': False} + f'{self.witness_uc.witness_uc.study_name}.DesignVariables.is_val_level': False}) - data_witness = [] # update assumptions dict and specific values for optimization - updated_data = {f'{self.study_name}.{self.optim_name}.{self.coupling_name}.{self.extra_name}.assumptions_dict': {'compute_gdp': True, + values_dict.update({f'{self.study_name}.{self.optim_name}.{self.coupling_name}.{self.extra_name}.assumptions_dict': {'compute_gdp': True, 'compute_climate_impact_on_gdp': True, 'activate_climate_effect_population': True, 'activate_pandemic_effects': True, - }} - data_witness.append(updated_data) + }}) - data_witness.append({ + values_dict.update({ f"{self.study_name}.{self.optim_name}.{self.coupling_name}.{self.extra_name}.ccs_price_percentage": 0.0, f"{self.study_name}.{self.optim_name}.{self.coupling_name}.{self.extra_name}.co2_damage_price_percentage": 100., }) - return [values_dict] + [optim_values_dict] + data_witness + return values_dict if '__main__' == __name__: diff --git a/climateeconomics/sos_processes/iam/witness/witness_coarse_story_telling_optim_process/usecase_7_optim_story_telling.py b/climateeconomics/sos_processes/iam/witness/witness_coarse_story_telling_optim_process/usecase_7_optim_story_telling.py index 562830320..6b86512b2 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_coarse_story_telling_optim_process/usecase_7_optim_story_telling.py +++ b/climateeconomics/sos_processes/iam/witness/witness_coarse_story_telling_optim_process/usecase_7_optim_story_telling.py @@ -80,7 +80,7 @@ def setup_usecase(self, study_folder_path=None): # design space WITNESS # optimization functions: - optim_values_dict = {f'{ns}.epsilon0': 1, + values_dict.update({f'{ns}.epsilon0': 1, f'{ns}.cache_type': 'SimpleCache', f'{ns}.{self.optim_name}.objective_name': FunctionManagerDisc.OBJECTIVE_LAGR, #GlossaryCore.UsableCapitalObjectiveName, f'{ns}.{self.optim_name}.eq_constraints': [], @@ -120,15 +120,15 @@ def setup_usecase(self, study_folder_path=None): "n_processes": 32, "use_threading": False, "wait_time_between_fork": 0}, - f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.sub_mda_class': 'GSPureNewtonMDA', + f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.inner_mda_name': 'MDAGSNewton', f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.max_mda_iter': 50, f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.cache_type': None, f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.propagate_cache_to_children': True, - f'{self.witness_uc.witness_uc.study_name}.DesignVariables.is_val_level': False} + f'{self.witness_uc.witness_uc.study_name}.DesignVariables.is_val_level': False}) - return [values_dict] + [optim_values_dict] + return values_dict if '__main__' == __name__: diff --git a/climateeconomics/sos_processes/iam/witness/witness_dev/_usecase_witness.py b/climateeconomics/sos_processes/iam/witness/witness_dev/_usecase_witness.py index b9f318cd4..538884c6b 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_dev/_usecase_witness.py +++ b/climateeconomics/sos_processes/iam/witness/witness_dev/_usecase_witness.py @@ -1,6 +1,6 @@ ''' -Copyright 2022 Airbus SAS -Modifications on {} Copyright 2024 Capgemini +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 @@ -12,7 +12,6 @@ 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 pandas as pd @@ -60,32 +59,6 @@ def __init__(self, year_start=GlossaryCore.YearStartDefault, year_end=GlossaryCo invest_discipline=self.invest_discipline, techno_dict=techno_dict) self.sub_study_path_dict = self.dc_energy.sub_study_path_dict - def setup_constraint_land_use(self): - func_df = pd.DataFrame( - columns=['variable', 'parent', 'ftype', 'weight', AGGR_TYPE]) - list_var = [] - list_parent = [] - list_ftype = [] - list_weight = [] - list_aggr_type = [] - list_ns = [] - list_var.extend( - ['land_demand_constraint_df']) - list_parent.extend([None]) - list_ftype.extend([INEQ_CONSTRAINT]) - list_weight.extend([-1.0]) - list_aggr_type.extend( - [AGGR_TYPE_SUM]) - list_ns.extend([GlossaryCore.NS_FUNCTIONS]) - func_df['variable'] = list_var - func_df['parent'] = list_parent - func_df['ftype'] = list_ftype - func_df['weight'] = list_weight - func_df[AGGR_TYPE] = list_aggr_type - func_df['namespace'] = list_ns - - return func_df - def setup_usecase(self, study_folder_path=None): setup_data_list = [] @@ -109,14 +82,12 @@ def setup_usecase(self, study_folder_path=None): self.merge_design_spaces([dspace_energy, dc_witness.dspace]) - # constraint land use - land_use_df_constraint = self.setup_constraint_land_use() - + # WITNESS # setup objectives self.func_df = pd.concat( [dc_witness.setup_objectives(), dc_witness.setup_constraints(), self.dc_energy.setup_constraints(), - self.dc_energy.setup_objectives(), land_use_df_constraint]) + self.dc_energy.setup_objectives(), ]) self.energy_list = self.dc_energy.energy_list self.ccs_list = self.dc_energy.ccs_list @@ -128,7 +99,7 @@ def setup_usecase(self, study_folder_path=None): f'{self.study_name}.tolerance': 1.0e-10, f'{self.study_name}.n_processes': 1, f'{self.study_name}.linearization_mode': 'adjoint', - f'{self.study_name}.sub_mda_class': 'GSPureNewtonMDA', + f'{self.study_name}.inner_mda_name': 'MDAGSNewton', f'{self.study_name}.cache_type': 'SimpleCache'} setup_data_list.append(numerical_values_dict) diff --git a/climateeconomics/sos_processes/iam/witness/witness_dev/usecase_witness.py b/climateeconomics/sos_processes/iam/witness/witness_dev/usecase_witness.py index a1d68c32b..e78d4b901 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_dev/usecase_witness.py +++ b/climateeconomics/sos_processes/iam/witness/witness_dev/usecase_witness.py @@ -61,21 +61,6 @@ def __init__(self, year_start=GlossaryCore.YearStartDefault, year_end=GlossaryCo self.sub_study_path_dict = self.dc_energy.sub_study_path_dict self.test_post_procs = False - def setup_constraint_land_use(self): - - data = { - 'variable': ['land_demand_constraint_df'], - 'parent': [None], - 'ftype': [INEQ_CONSTRAINT], - 'weight': [-1.0], - AGGR_TYPE: [AGGR_TYPE_SUM], - 'namespace': [GlossaryCore.NS_FUNCTIONS] - } - - func_df = pd.DataFrame(data) - - return func_df - def setup_usecase(self, study_folder_path=None): setup_data_list = [] @@ -98,14 +83,11 @@ def setup_usecase(self, study_folder_path=None): self.merge_design_spaces([dspace_energy, dc_witness.dspace]) - # constraint land use - land_use_df_constraint = self.setup_constraint_land_use() - # WITNESS # setup objectives self.func_df = pd.concat( [dc_witness.setup_objectives(), dc_witness.setup_constraints(), self.dc_energy.setup_constraints(), - self.dc_energy.setup_objectives(), land_use_df_constraint]) + self.dc_energy.setup_objectives(), ]) self.energy_list = self.dc_energy.energy_list self.ccs_list = self.dc_energy.ccs_list @@ -117,7 +99,7 @@ def setup_usecase(self, study_folder_path=None): f'{self.study_name}.tolerance': 1.0e-10, f'{self.study_name}.n_processes': 1, f'{self.study_name}.linearization_mode': 'adjoint', - f'{self.study_name}.sub_mda_class': 'GSPureNewtonMDA', + f'{self.study_name}.inner_mda_name': 'MDAGSNewton', f'{self.study_name}.cache_type': 'SimpleCache'} setup_data_list.append(numerical_values_dict) diff --git a/climateeconomics/sos_processes/iam/witness/witness_dev_optim_process/usecase_witness_optim_invest_distrib.py b/climateeconomics/sos_processes/iam/witness/witness_dev_optim_process/usecase_witness_optim_invest_distrib.py index d67a0da99..8d9d34b83 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_dev_optim_process/usecase_witness_optim_invest_distrib.py +++ b/climateeconomics/sos_processes/iam/witness/witness_dev_optim_process/usecase_witness_optim_invest_distrib.py @@ -132,7 +132,7 @@ def setup_usecase(self, study_folder_path=None): "n_processes": 32, "use_threading": False, "wait_time_between_fork": 0}, - f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.sub_mda_class': 'GSPureNewtonMDA', + f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.inner_mda_name': 'MDAGSNewton', f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.max_mda_iter': 50, f'{self.witness_uc.witness_uc.study_name}.DesignVariables.is_val_level': False} # f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.DesignVariables.{WRITE_XVECT}': diff --git a/climateeconomics/sos_processes/iam/witness/witness_optim_process/data/forest_investment.csv b/climateeconomics/sos_processes/iam/witness/witness_optim_process/data/forest_investment.csv index 394d9ee9c..6a0ebcb2b 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_optim_process/data/forest_investment.csv +++ b/climateeconomics/sos_processes/iam/witness/witness_optim_process/data/forest_investment.csv @@ -1,4 +1,4 @@ -years,forest_investment +years,reforestation_investment 2020.0,10.639404859039153 2021.0,9.20283834835774 2022.0,7.924670408877128 diff --git a/climateeconomics/sos_processes/iam/witness/witness_optim_process/usecase_witness_optim.py b/climateeconomics/sos_processes/iam/witness/witness_optim_process/usecase_witness_optim.py index be3db9447..d5c7cffef 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_optim_process/usecase_witness_optim.py +++ b/climateeconomics/sos_processes/iam/witness/witness_optim_process/usecase_witness_optim.py @@ -129,7 +129,7 @@ def setup_usecase(self, study_folder_path=None): "n_processes": 32, "use_threading": False, "wait_time_between_fork": 0}, - f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.sub_mda_class': 'GSPureNewtonMDA', + f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.inner_mda_name': 'MDAGSNewton', f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.max_mda_iter': 50, f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.DesignVariables.{WRITE_XVECT}': False} diff --git a/climateeconomics/sos_processes/iam/witness/witness_optim_process_iea_nze/usecase_witness_optim_nze_eval.py b/climateeconomics/sos_processes/iam/witness/witness_optim_process_iea_nze/usecase_witness_optim_nze_eval.py index 8ea4fbea8..2162bf81a 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_optim_process_iea_nze/usecase_witness_optim_nze_eval.py +++ b/climateeconomics/sos_processes/iam/witness/witness_optim_process_iea_nze/usecase_witness_optim_nze_eval.py @@ -123,6 +123,7 @@ def setup_usecase(self, study_folder_path=None): f'{ns}.{self.optim_name}.eval_mode': True, f'{ns}.warm_start': True, f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.warm_start': True, + f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.cache_type': 'SimpleCache', # SLSQP, NLOPT_SLSQP f'{ns}.{self.optim_name}.algo': "L-BFGS-B", f'{ns}.{self.optim_name}.formulation': 'DisciplinaryOpt', @@ -152,7 +153,7 @@ def setup_usecase(self, study_folder_path=None): "n_processes": 32, "use_threading": False, "wait_time_between_fork": 0}, - f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.sub_mda_class': 'GSPureNewtonMDA', + f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.inner_mda_name': 'MDAGSNewton', f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.max_mda_iter': 50, f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.DesignVariables.{WRITE_XVECT}': False} @@ -160,7 +161,7 @@ def setup_usecase(self, study_folder_path=None): list_design_var_to_clean = ['red_meat_calories_per_day_ctrl', 'white_meat_calories_per_day_ctrl', 'vegetables_and_carbs_calories_per_day_ctrl', 'milk_and_eggs_calories_per_day_ctrl', - 'forest_investment_array_mix', 'crop_investment_array_mix'] + 'reforestation_investment_array_mix', 'crop_investment_array_mix'] diet_mortality_df = pd.read_csv(join(dirname(__file__), '../witness_optim_process/data', 'diet_mortality.csv')) # clean dspace @@ -209,9 +210,9 @@ def setup_usecase(self, study_folder_path=None): invest_mix_file = 'investment_mix.csv' invest_mix = pd.read_csv(join(dirname(__file__), '../witness_optim_process/data', invest_mix_file)) hydro_prod_IEA = pd.read_csv(join(dirname(__file__), '../../../../data', 'IEA_NZE_EnergyMix.electricity.Hydropower.techno_production.csv')) - models_path_abs = os.path.dirname(os.path.abspath(__file__)).split(os.sep + "models")[0] + models_path_abs = os.path.dirname(os.path.abspath(__file__)).split(os.sep + "witness-core")[0] df_prod_iea = pd.read_csv( - os.path.join(models_path_abs, 'models', 'witness-core', 'climateeconomics', 'data', + os.path.join(models_path_abs, 'witness-core', 'climateeconomics', 'data', 'IEA_NZE_EnergyMix.biogas.energy_production_detailed.csv')) forest_invest_file = 'forest_investment.csv' forest_invest = pd.read_csv(join(dirname(__file__), '../witness_optim_process/data', forest_invest_file)) @@ -226,10 +227,12 @@ def setup_usecase(self, study_folder_path=None): invest_before_year_start_hydropower = pd.DataFrame({GlossaryEnergy.Years: np.arange(self.year_start - GlossaryEnergy.TechnoConstructionDelayDict['Hydropower'], self.year_start), GlossaryEnergy.InvestValue: [0., 102.49276698, 98.17710767]}) invest_before_year_start_windonshore = pd.DataFrame({GlossaryEnergy.Years: np.arange(self.year_start - GlossaryEnergy.TechnoConstructionDelayDict['WindOnshore'], self.year_start), GlossaryEnergy.InvestValue: [0., 125.73068603, 125.73068603]}) invest_before_year_start_windoffshore = pd.DataFrame({GlossaryEnergy.Years: np.arange(self.year_start - GlossaryEnergy.TechnoConstructionDelayDict['WindOffshore'], self.year_start), GlossaryEnergy.InvestValue: [0., 34.26931397, 34.26931397]}) + invest_mix[f'{GlossaryEnergy.methane}.{GlossaryEnergy.Methanation}'] = 0. + invest_mix[f'{GlossaryEnergy.fuel}.{GlossaryEnergy.ethanol}.{GlossaryEnergy.BiomassFermentation}'] = 0. values_dict_updt.update({f'{ns}.{self.optim_name}.design_space': dspace_df, f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.{self.witness_uc.designvariable_name}.design_var_descriptor': updated_dvar_descriptor, f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.WITNESS.InvestmentDistribution.invest_mix': invest_mix, - f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.WITNESS.InvestmentDistribution.forest_investment': forest_invest, + f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.WITNESS.InvestmentDistribution.reforestation_investment': forest_invest, f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.WITNESS.AgricultureMix.Crop.crop_investment': crop_investment_df_NZE, f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.WITNESS.AgricultureMix.Forest.reforestation_cost_per_ha': 3800., f'{ns}.{self.optim_name}.{self.witness_uc.coupling_name}.WITNESS.Population.diet_mortality_param_df': diet_mortality_df, @@ -262,7 +265,6 @@ def setup_usecase(self, study_folder_path=None): population_df = create_df_from_csv("IEA_NZE_population.csv") temperature_df = create_df_from_csv("IEA_NZE_temp_atmo.csv") energy_production_df = create_df_from_csv("IEA_NZE_energy_production_brut.csv") - energy_consumption_df = create_df_from_csv("IEA_NZE_energy_final_consumption.csv") nuclear_production_df = create_df_from_csv("IEA_NZE_EnergyMix.electricity.Nuclear.techno_production.csv") hydro_production_df = create_df_from_csv("IEA_NZE_EnergyMix.electricity.Hydropower.techno_production.csv") solar_production_df = create_df_from_csv("IEA_NZE_EnergyMix.electricity.SolarPv.techno_production.csv") @@ -283,7 +285,6 @@ def setup_usecase(self, study_folder_path=None): f'{ns}.{IEA_DISC}.{GlossaryEnergy.EconomicsDfValue}': GDP_df, f'{ns}.{IEA_DISC}.{GlossaryEnergy.CO2TaxesValue}': CO2_tax_df, f'{ns}.{IEA_DISC}.{GlossaryEnergy.EnergyProductionValue}': energy_production_df, - f'{ns}.{IEA_DISC}.{GlossaryEnergy.EnergyFinalConsumptionName}': energy_consumption_df, f'{ns}.{IEA_DISC}.{GlossaryEnergy.TemperatureDfValue}': temperature_df, f'{ns}.{IEA_DISC}.{GlossaryEnergy.PopulationDfValue}': population_df, # energy production @@ -310,6 +311,7 @@ def setup_usecase(self, study_folder_path=None): uc_cls = Study() uc_cls.test() + ''' from sostrades_core.tools.post_processing.post_processing_factory import ( PostProcessingFactory, ) @@ -323,3 +325,4 @@ def setup_usecase(self, study_folder_path=None): graph_list = ppf.get_post_processing_by_namespace(uc_cls.ee, ns, filters, as_json=False) for graph in graph_list: graph.to_plotly().show() + ''' diff --git a/climateeconomics/sos_processes/iam/witness/witness_optim_story_telling_sub_process/usecase_witness_optim_sub.py b/climateeconomics/sos_processes/iam/witness/witness_optim_story_telling_sub_process/usecase_witness_optim_sub.py index 735493228..1d69b7af8 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_optim_story_telling_sub_process/usecase_witness_optim_sub.py +++ b/climateeconomics/sos_processes/iam/witness/witness_optim_story_telling_sub_process/usecase_witness_optim_sub.py @@ -155,8 +155,7 @@ def setup_usecase(self, study_folder_path=None): func_df = pd.DataFrame(data=func_dict) values_dict[f'{self.study_name}.{self.coupling_name}.{self.func_manager_name}.{FUNC_DF}'] = func_df - - values_dict[f'{self.study_name}.{self.coupling_name}.sub_mda_class'] = 'GSPureNewtonMDA' + values_dict[f'{self.study_name}.{self.coupling_name}.inner_mda_name'] = 'MDAGSNewton' # values_dict[f'{self.study_name}.{self.coupling_name}.warm_start'] = True values_dict[f'{self.study_name}.{self.coupling_name}.max_mda_iter'] = 50 values_dict[f'{self.study_name}.{self.coupling_name}.linearization_mode'] = 'adjoint' diff --git a/climateeconomics/sos_processes/iam/witness/witness_optim_sub_process/usecase_witness_optim_sub.py b/climateeconomics/sos_processes/iam/witness/witness_optim_sub_process/usecase_witness_optim_sub.py index 1ae3e9ef6..50734ad92 100644 --- a/climateeconomics/sos_processes/iam/witness/witness_optim_sub_process/usecase_witness_optim_sub.py +++ b/climateeconomics/sos_processes/iam/witness/witness_optim_sub_process/usecase_witness_optim_sub.py @@ -101,11 +101,11 @@ def setup_usecase(self, study_folder_path=None): - dv_arrays_dict[f'{self.witness_uc.study_name}.forest_investment_array_mix'] = \ - dspace_df['forest_investment_array_mix']['value'] - design_var_descriptor['forest_investment_array_mix'] = {'out_name': 'forest_investment', + dv_arrays_dict[f'{self.witness_uc.study_name}.reforestation_investment_array_mix'] = \ + dspace_df['reforestation_investment_array_mix']['value'] + design_var_descriptor['reforestation_investment_array_mix'] = {'out_name': 'reforestation_investment', 'out_type': 'dataframe', - 'key': 'forest_investment', + 'key': 'reforestation_investment', 'index': years, 'index_name': GlossaryCore.Years, 'namespace_in': GlossaryCore.NS_WITNESS, @@ -188,7 +188,6 @@ def setup_usecase(self, study_folder_path=None): func_df = self.witness_uc.func_df func_df = func_df[~func_df['variable'].isin(['non_use_capital_cons', 'forest_lost_capital_cons'])] - func_df.loc[func_df['variable'] == 'land_demand_constraint', 'weight'] = 0. func_df.loc[func_df['variable'] == 'calories_per_day_constraint', 'weight'] = 0. func_df.loc[func_df['variable'] == 'total_prod_minus_min_prod_constraint_df', 'weight'] = 0. @@ -201,7 +200,7 @@ def setup_usecase(self, study_folder_path=None): values_dict[ f'{self.study_name}.{self.coupling_name}.{self.designvariable_name}.design_var_descriptor'] = design_var_descriptor - values_dict[f'{self.study_name}.{self.coupling_name}.sub_mda_class'] = 'GSPureNewtonMDA' + values_dict[f'{self.study_name}.{self.coupling_name}.inner_mda_name'] = 'MDAGSNewton' # values_dict[f'{self.study_name}.{self.coupling_name}.warm_start'] = True values_dict[f'{self.study_name}.{self.coupling_name}.max_mda_iter'] = 50 values_dict[f'{self.study_name}.{self.coupling_name}.linearization_mode'] = 'adjoint' diff --git a/climateeconomics/sos_processes/iam/witness_wo_energy/datacase_witness_wo_energy.py b/climateeconomics/sos_processes/iam/witness_wo_energy/datacase_witness_wo_energy.py index f52ed150e..ca32509ed 100644 --- a/climateeconomics/sos_processes/iam/witness_wo_energy/datacase_witness_wo_energy.py +++ b/climateeconomics/sos_processes/iam/witness_wo_energy/datacase_witness_wo_energy.py @@ -68,9 +68,9 @@ def setup_usecase(self, study_folder_path=None): nb_per = self.year_end - self.year_start + 1 years = arange(self.year_start, self.year_end + 1) - forest_invest = np.linspace(5.0, 8.0, len(years)) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, "forest_investment": forest_invest}) + reforestation_invest = np.linspace(5.0, 8.0, len(years)) + self.reforestation_investment_df = pd.DataFrame( + {GlossaryCore.Years: years, "reforestation_investment": reforestation_invest}) # private values economics operator pyworld3 witness_input = {} @@ -88,7 +88,7 @@ def setup_usecase(self, study_folder_path=None): witness_input[f"{self.study_name}.{'Damage.damage_constraint_factor'}"] = np.concatenate( (np.linspace(1.0, 1.0, 20), np.asarray([1] * (len(years) - 20)))) # witness_input[f"{self.study_name}.{}# '.Damage.damage_constraint_factor'}" = np.asarray([1] * len(years)) - witness_input[f"{self.study_name}.{'InvestmentDistribution'}.forest_investment"] = self.forest_invest_df + witness_input[f"{self.study_name}.{'InvestmentDistribution'}.reforestation_investment"] = self.reforestation_investment_df # get population from csv file # get file from the data folder 3 folder up. global_data_dir = join(Path(__file__).parents[3], 'data') @@ -138,13 +138,12 @@ def setup_usecase(self, study_folder_path=None): 1935]}, index=[2017, 2018, 2019]) - CO2_emitted_land = pd.DataFrame() + CO2_emitted_land = pd.DataFrame({GlossaryCore.Years: years}) # GtCO2 emission_forest = np.linspace(0.04, 0.04, len(years)) cum_emission = np.cumsum(emission_forest) CO2_emitted_land['Crop'] = np.zeros(len(years)) CO2_emitted_land['Forest'] = cum_emission - witness_input[f"{self.study_name}.{GlossaryCore.insertGHGAgriLandEmissions.format(GlossaryCore.CO2)}"] = CO2_emitted_land self.CO2_tax = np.asarray([50.] * len(years)) @@ -159,9 +158,9 @@ def setup_usecase(self, study_folder_path=None): default_co2_efficiency = pd.DataFrame( {GlossaryCore.Years: years, GlossaryCore.CO2TaxEfficiencyValue: CO2_tax_efficiency}) - forest_invest = np.linspace(5.0, 8.0, len(years)) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, "forest_investment": forest_invest}) + reforestation_invest = np.linspace(5.0, 8.0, len(years)) + self.reforestation_investment_df = pd.DataFrame( + {GlossaryCore.Years: years, "reforestation_investment": reforestation_invest}) # -- load data from resource dc_resource = datacase_resource(self.year_start, self.year_end, main_study=False) diff --git a/climateeconomics/sos_processes/iam/witness_wo_energy/process.py b/climateeconomics/sos_processes/iam/witness_wo_energy/process.py index 6c513b3a7..d343f4b11 100644 --- a/climateeconomics/sos_processes/iam/witness_wo_energy/process.py +++ b/climateeconomics/sos_processes/iam/witness_wo_energy/process.py @@ -42,7 +42,7 @@ def get_builders(self): ns_dict = {GlossaryCore.NS_WITNESS: ns_scatter, GlossaryCore.NS_ENERGY_MIX: ns_scatter, 'ns_agriculture': ns_scatter, - GlossaryCore.NS_MACRO: ns_scatter, + GlossaryCore.NS_MACRO: f'{self.ee.study_name}.Macroeconomics', 'ns_forest': ns_scatter} mods_dict = { @@ -76,9 +76,6 @@ def get_builders(self): self.ee.ns_manager.add_ns_def(ns_dict) - ''' - Add emissions disciplines - ''' mods_dict = { GHGemissionsDiscipline.name: 'climateeconomics.sos_wrapping.sos_wrapping_emissions.ghgemissions.ghgemissions_discipline.GHGemissionsDiscipline', AgricultureEmissionsDiscipline.name: 'climateeconomics.sos_wrapping.sos_wrapping_emissions.agriculture_emissions.agriculture_emissions_discipline.AgricultureEmissionsDiscipline', diff --git a/climateeconomics/sos_processes/iam/witness_wo_energy_dev/datacase_witness_wo_energy.py b/climateeconomics/sos_processes/iam/witness_wo_energy_dev/datacase_witness_wo_energy.py index 1aec2659a..c1960bbcf 100644 --- a/climateeconomics/sos_processes/iam/witness_wo_energy_dev/datacase_witness_wo_energy.py +++ b/climateeconomics/sos_processes/iam/witness_wo_energy_dev/datacase_witness_wo_energy.py @@ -72,9 +72,9 @@ def setup_usecase(self, study_folder_path=None): nb_per = self.year_end - self.year_start + 1 years = arange(self.year_start, self.year_end + 1) - forest_invest = np.linspace(5.0, 8.0, len(years)) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, "forest_investment": forest_invest}) + reforestation_invest = np.linspace(5.0, 8.0, len(years)) + self.reforestation_investment_df = pd.DataFrame( + {GlossaryCore.Years: years, "reforestation_investment": reforestation_invest}) # private values economics operator pyworld3 witness_input = {} @@ -91,7 +91,7 @@ def setup_usecase(self, study_folder_path=None): # Relax constraint for 15 first years witness_input[f"{self.study_name}.{'Damage'}.{'damage_constraint_factor'}"] = np.concatenate( (np.linspace(1.0, 1.0, 20), np.asarray([1] * (len(years) - 20)))) - witness_input[f"{self.study_name}.{'InvestmentDistribution'}.{'forest_investment'}"] = self.forest_invest_df + witness_input[f"{self.study_name}.{'InvestmentDistribution'}.{'reforestation_investment'}"] = self.reforestation_investment_df # get population from csv file # get file from the data folder 3 folder up. global_data_dir = join(Path(__file__).parents[3], 'data') @@ -181,9 +181,9 @@ def setup_usecase(self, study_folder_path=None): default_co2_efficiency = pd.DataFrame( {GlossaryCore.Years: years, GlossaryCore.CO2TaxEfficiencyValue: CO2_tax_efficiency}) - forest_invest = np.linspace(5.0, 8.0, len(years)) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, "forest_investment": forest_invest}) + reforestation_invest = np.linspace(5.0, 8.0, len(years)) + self.reforestation_investment_df = pd.DataFrame( + {GlossaryCore.Years: years, "reforestation_investment": reforestation_invest}) # -- load data from resource dc_resource = datacase_resource( diff --git a/climateeconomics/sos_processes/iam/witness_wo_energy_dev/process.py b/climateeconomics/sos_processes/iam/witness_wo_energy_dev/process.py index 2fcb0f15e..529d4bbc2 100644 --- a/climateeconomics/sos_processes/iam/witness_wo_energy_dev/process.py +++ b/climateeconomics/sos_processes/iam/witness_wo_energy_dev/process.py @@ -41,7 +41,7 @@ def get_builders(self): ns_dict = {GlossaryCore.NS_WITNESS: self.ee.study_name, GlossaryCore.NS_ENERGY_MIX: self.ee.study_name, - GlossaryCore.NS_MACRO: self.ee.study_name, + GlossaryCore.NS_MACRO: f'{self.ee.study_name}.Macroeconomics', f'ns_{GlossaryCore.Households.lower()}_emissions':f"{self.ee.study_name}.{GHGemissionsDiscipline.name}.{GlossaryCore.EconomicSectors}.{GlossaryCore.Households}", 'ns_agriculture': self.ee.study_name, 'ns_dashboard': self.ee.study_name, @@ -76,9 +76,6 @@ def get_builders(self): self.ee.ns_manager.add_ns_def(ns_dict) - ''' - Add emissions disciplines - ''' mods_dict = {GHGemissionsDiscipline.name: 'climateeconomics.sos_wrapping.sos_wrapping_emissions.ghgemissions.ghgemissions_discipline.GHGemissionsDiscipline', AgricultureEmissionsDiscipline.name: 'climateeconomics.sos_wrapping.sos_wrapping_emissions.agriculture_emissions.agriculture_emissions_discipline.AgricultureEmissionsDiscipline', } diff --git a/climateeconomics/sos_processes/iam/witness_wo_energy_dev/usecase.py b/climateeconomics/sos_processes/iam/witness_wo_energy_dev/usecase.py deleted file mode 100644 index 49037088b..000000000 --- a/climateeconomics/sos_processes/iam/witness_wo_energy_dev/usecase.py +++ /dev/null @@ -1,414 +0,0 @@ -''' -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. - -''' -from os.path import dirname, join -from pathlib import Path - -import numpy as np -import pandas as pd -from numpy import arange, asarray -from pandas import DataFrame -from sostrades_core.study_manager.study_manager import StudyManager -from sostrades_optimization_plugins.models.func_manager.func_manager import ( - FunctionManager, -) -from sostrades_optimization_plugins.models.func_manager.func_manager_disc import ( - FunctionManagerDisc, -) - -from climateeconomics.database import DatabaseWitnessCore -from climateeconomics.glossarycore import GlossaryCore -from climateeconomics.sos_processes.iam.witness.agriculture_mix_process.usecase import ( - AGRI_MIX_TECHNOLOGIES_LIST_FOR_OPT, -) -from climateeconomics.sos_processes.iam.witness.agriculture_mix_process.usecase import ( - Study as datacase_agriculture_mix, -) -from climateeconomics.sos_processes.iam.witness.land_use_v2_process.usecase import ( - Study as datacase_landuse, -) -from climateeconomics.sos_processes.iam.witness.resources_process.usecase import ( - Study as datacase_resource, -) -from climateeconomics.sos_wrapping.sos_wrapping_agriculture.crop.crop_disc import ( - CropDiscipline, -) - -OBJECTIVE = FunctionManagerDisc.OBJECTIVE -INEQ_CONSTRAINT = FunctionManagerDisc.INEQ_CONSTRAINT -EQ_CONSTRAINT = FunctionManagerDisc.EQ_CONSTRAINT -AGGR_TYPE = FunctionManagerDisc.AGGR_TYPE -AGGR_TYPE_SMAX = FunctionManager.AGGR_TYPE_SMAX -AGGR_TYPE_SUM = FunctionManager.AGGR_TYPE_SUM -AGGR_TYPE_DELTA = FunctionManager.AGGR_TYPE_DELTA -AGGR_TYPE_LIN_TO_QUAD = FunctionManager.AGGR_TYPE_LIN_TO_QUAD - - -class Study(StudyManager): - def __init__(self, year_start=GlossaryCore.YearStartDefault, year_end=GlossaryCore.YearEndDefault, - agri_techno_list=AGRI_MIX_TECHNOLOGIES_LIST_FOR_OPT, execution_engine=None): - super().__init__(file_path=__file__, execution_engine=execution_engine) - self.study_name = 'usecase' - self.year_start = year_start - self.year_end = year_end - self.techno_dict = agri_techno_list - self.study_name_wo_extra_name = self.study_name - self.dspace = {} - self.dspace['dspace_size'] = 0 - - def setup_usecase(self, study_folder_path=None): - setup_data_list = [] - nb_per = self.year_end - self.year_start + 1 - years = arange(self.year_start, self.year_end + 1) - - forest_invest = np.linspace(5.0, 8.0, len(years)) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, "forest_investment": forest_invest}) - - # private values economics operator pyworld3 - witness_input = {} - witness_input[f"{self.study_name}.{GlossaryCore.YearStart}"] = self.year_start - witness_input[f"{self.study_name}.{GlossaryCore.YearEnd}"] = self.year_end - - - witness_input[f"{self.study_name}.{'Damage'}.{'tipping_point'}"] = True - witness_input[f"{self.study_name}.{'Macroeconomics'}.{GlossaryCore.DamageToProductivity}"] = True - witness_input[f"{self.study_name}.{GlossaryCore.FractionDamageToProductivityValue}"] = 0.30 - witness_input[f"{self.study_name}.{'init_rate_time_pref'}"] = .015 - witness_input[f"{self.study_name}.{'conso_elasticity'}"] = 1.45 - witness_input[f"{self.study_name}.{'init_gross_output'}"] = 130.187 - # Relax constraint for 15 first years - witness_input[f"{self.study_name}.{'Damage'}.{'damage_constraint_factor'}"] = np.concatenate( - (np.linspace(1.0, 1.0, 20), np.asarray([1] * (len(years) - 20)))) - witness_input[f"{self.study_name}.{'InvestmentDistribution'}.{'forest_investment'}"] = self.forest_invest_df - # get population from csv file - # get file from the data folder 3 folder up. - global_data_dir = join(Path(__file__).parents[3], 'data') - population_df = pd.read_csv( - join(global_data_dir, 'population_df.csv')) - #population_df.index = years - witness_input[self.study_name + f'.{GlossaryCore.PopulationDfValue}'] = population_df - working_age_population_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.Population1570: 6300}, index=years) - witness_input[f"{self.study_name}.{GlossaryCore.WorkingAgePopulationDfValue}"] = working_age_population_df - damage_fraction_initialisation = pd.DataFrame({ - GlossaryCore.Years: years, - GlossaryCore.DamageFractionOutput: np.linspace(0.001, 0.1, len(years)), - }) - witness_input[f'{self.study_name}.{GlossaryCore.DamageFractionDfValue}'] = damage_fraction_initialisation - share_non_energy_investment = DataFrame( - {GlossaryCore.Years: years, - GlossaryCore.ShareNonEnergyInvestmentsValue: asarray([27. - 0.3] * nb_per)}, - index=years) - - witness_input[f'{self.study_name}.{GlossaryCore.ShareNonEnergyInvestmentsValue}'] = share_non_energy_investment - - # deactive mortality due to undernutrition/overnutrition: - diet_mortality = pd.read_csv(join(global_data_dir, 'diet_mortality_param.csv')) - diet_mortality['undernutrition'] = 0. - diet_mortality['overnutrition'] = 0. - witness_input[f'{self.study_name}.Population.{GlossaryCore.DietMortalityParamDf["var_name"]}'] = diet_mortality - - witness_input[f'{self.study_name}.AgricultureMix.Crop.red_meat_calories_per_day'] = DataFrame( - {GlossaryCore.Years: years, - 'red_meat_calories_per_day': [CropDiscipline.red_meat_average_ca_daily_intake] * len(years)} - ) - - witness_input[f'{self.study_name}.AgricultureMix.Crop.white_meat_calories_per_day'] = DataFrame( - {GlossaryCore.Years: years, - 'white_meat_calories_per_day': [CropDiscipline.white_meat_average_ca_daily_intake] * len(years)} - ) - - witness_input[f'{self.study_name}.AgricultureMix.Crop.vegetables_and_carbs_calories_per_day'] = DataFrame( - {GlossaryCore.Years: years, - 'vegetables_and_carbs_calories_per_day': [CropDiscipline.vegetables_and_carbs_average_ca_daily_intake] * len(years)} - ) - - witness_input[f'{self.study_name}.AgricultureMix.Crop.milk_and_eggs_calories_per_day'] = DataFrame( - {GlossaryCore.Years: years, - 'milk_and_eggs_calories_per_day': [CropDiscipline.milk_eggs_average_ca_daily_intake] * len(years)} - ) - - data = arange(1.0, nb_per + 1.0, 1) - - df_eco = DataFrame({GlossaryCore.Years: years, - GlossaryCore.GrossOutput: data, - GlossaryCore.PerCapitaConsumption: data, - GlossaryCore.OutputNetOfDamage: data}, - index=arange(self.year_start, self.year_end + 1)) - - witness_input[self.study_name + f'.{GlossaryCore.EconomicsDfValue}'] = df_eco - - nrj_invest = arange(1000, nb_per + 1000, 1) - - df_energy_investment = DataFrame({GlossaryCore.Years: years, - GlossaryCore.EnergyInvestmentsValue: nrj_invest}, - index=arange(self.year_start, self.year_end + 1)) - - CO2_emitted_land = pd.DataFrame({GlossaryCore.Years: years}) - # GtCO2 - emission_forest = np.linspace(0.04, 0.04, len(years)) - cum_emission = np.cumsum(emission_forest) - CO2_emitted_land['Crop'] = np.zeros(len(years)) - CO2_emitted_land['Forest'] = cum_emission - - witness_input[f"{self.study_name}.{GlossaryCore.insertGHGAgriLandEmissions.format(GlossaryCore.CO2)}"] = CO2_emitted_land - - self.CO2_tax = np.asarray([50.] * len(years)) - - witness_input[f"{self.study_name}.{GlossaryCore.EnergyInvestmentsValue}"] = df_energy_investment - - intermediate_point = 30 - # CO2 taxes related inputs - CO2_tax_efficiency = np.concatenate( - (np.linspace(30, intermediate_point, 15), np.asarray([intermediate_point] * (len(years) - 15)))) - # CO2_tax_efficiency = 30.0 - default_co2_efficiency = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.CO2TaxEfficiencyValue: CO2_tax_efficiency}) - - forest_invest = np.linspace(5.0, 8.0, len(years)) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, "forest_investment": forest_invest}) - - # -- load data from resource - dc_resource = datacase_resource( - self.year_start, self.year_end, main_study=False) - dc_resource.study_name = self.study_name - - # -- load data from land use - dc_landuse = datacase_landuse( - self.year_start, self.year_end, name='.Land_Use_V2', extra_name='.EnergyMix') - dc_landuse.study_name = self.study_name - - # -- load data from agriculture - dc_agriculture_mix = datacase_agriculture_mix( - self.year_start, self.year_end, agri_techno_list=self.techno_dict) - dc_agriculture_mix.additional_ns = '.InvestmentDistribution' - dc_agriculture_mix.study_name = self.study_name - - resource_input_list = dc_resource.setup_usecase() - setup_data_list = setup_data_list + resource_input_list - - land_use_list = dc_landuse.setup_usecase() - setup_data_list = setup_data_list + land_use_list - - agriculture_list = dc_agriculture_mix.setup_usecase() - setup_data_list = setup_data_list + agriculture_list - self.dspace_size = dc_agriculture_mix.dspace.pop('dspace_size') - self.dspace.update(dc_agriculture_mix.dspace) - nb_poles = 8 - # WITNESS - # setup objectives - energy_investment_wo_tax = DataFrame( - {GlossaryCore.Years: years, - GlossaryCore.EnergyInvestmentsWoTaxValue: asarray([10.] * nb_per)}, - index=years) - - share_non_energy_investment = DataFrame( - {GlossaryCore.Years: years, - GlossaryCore.ShareNonEnergyInvestmentsValue: asarray([27. - 1.65] * nb_per)}, - index=years) - - share_residential_energy = pd.DataFrame( - {GlossaryCore.Years: years, - GlossaryCore.ShareSectorEnergy: DatabaseWitnessCore.EnergyshareResidentialYearStart.value}, ) - - witness_input[f'{self.study_name}.{GlossaryCore.ShareResidentialEnergyDfValue}'] = share_residential_energy - witness_input[f'{self.study_name}.{GlossaryCore.EnergyInvestmentsWoTaxValue}'] = energy_investment_wo_tax - witness_input[f'{self.study_name}.{GlossaryCore.ShareNonEnergyInvestmentsValue}'] = share_non_energy_investment - witness_input[f'{self.study_name}.Macroeconomics.{GlossaryCore.CO2TaxEfficiencyValue}'] = default_co2_efficiency - - witness_input[f'{self.study_name}.beta'] = 1.0 - - witness_input[f'{self.study_name}.init_rate_time_pref'] = 0.0 - - # ------------------ mda initialisation data - co2_emissions_Gt = pd.DataFrame({ - GlossaryCore.Years: years, - GlossaryCore.TotalCO2Emissions: 35., - }) - witness_input.update({ - f"{self.study_name}.EnergyMix.{GlossaryCore.CO2EmissionsGtValue}": co2_emissions_Gt, - }) - # ------------------ end mda initialisation - share_residential_energy = pd.DataFrame({ - GlossaryCore.Years: years, - GlossaryCore.ShareSectorEnergy: DatabaseWitnessCore.EnergyshareResidentialYearStart.value - }) - - for sector in GlossaryCore.SectorsPossibleValues: - witness_input[f'{self.study_name}.GHGEmissions.{GlossaryCore.EconomicSectors}.{sector}.{GlossaryCore.SectionNonEnergyEmissionGdpDfValue}'] = DatabaseWitnessCore.SectionsNonEnergyEmissionsDict.value[sector] - - GHG_total_energy_emissions = pd.DataFrame({GlossaryCore.Years: years, - GlossaryCore.TotalCO2Emissions: np.linspace(37., 10., len(years)), - GlossaryCore.TotalN2OEmissions: np.linspace(1.7e-3, 5.e-4, len(years)), - GlossaryCore.TotalCH4Emissions: np.linspace(0.17, 0.01, len(years))}) - witness_input[f'{self.study_name}.GHG_total_energy_emissions'] = GHG_total_energy_emissions - - witness_input[f'{self.study_name}.{GlossaryCore.ShareResidentialEnergyDfValue}'] = share_residential_energy - - global_data_dir = join(dirname(dirname(dirname(dirname(__file__)))), 'data') - weighted_average_percentage_per_sector_df = pd.read_csv( - join(global_data_dir, 'weighted_average_percentage_per_sector.csv')) - - subsector_share_dict = { - **{GlossaryCore.Years: np.arange(self.year_start, self.year_end + 1), }, - **dict(zip(weighted_average_percentage_per_sector_df.columns[1:], - weighted_average_percentage_per_sector_df.values[0, 1:])) - } - section_gdp_df = pd.DataFrame(subsector_share_dict) - witness_input[f'{self.study_name}.{GlossaryCore.SectionGdpPercentageDfValue}'] = section_gdp_df - - setup_data_list.append(witness_input) - - energy_prod = pd.DataFrame({ - GlossaryCore.Years: years, - GlossaryCore.TotalProductionValue: np.linspace(40, 60, len(years)) - }) - - energy_capital = pd.DataFrame({ - GlossaryCore.Years: years, - GlossaryCore.Capital: np.linspace(40, 60, len(years)) - }) - - energy_mean_price = pd.DataFrame({ - GlossaryCore.Years: years, - GlossaryCore.EnergyPriceValue: np.linspace(40, 60, len(years)) - }) - - ccs_price = pd.DataFrame({ - GlossaryCore.Years: years, - "ccs_price_per_tCO2": np.linspace(40, 60, len(years)) - }) - - forest_invest = pd.DataFrame({ - GlossaryCore.Years: years, - "forest_investment": np.linspace(40, 60, len(years)) - }) - - data_energy = { - f"{self.study_name}.{GlossaryCore.EnergyProductionValue}": energy_prod, - f"{self.study_name}.{GlossaryCore.EnergyCapitalDfValue}": energy_capital, - f"{self.study_name}.{GlossaryCore.EnergyMeanPriceValue}": energy_mean_price, - f"{self.study_name}.CCS_price": ccs_price, - f"{self.study_name}.forest_investment": forest_invest, - f"{self.study_name}.max_mda_iter": 70, - f"{self.study_name}.tolerance": 1e-12, - } - setup_data_list.append(data_energy) - - return setup_data_list - - def setup_objectives(self): - - data = { - 'variable': [ - GlossaryCore.ConstraintUpperBoundUsableCapital, - GlossaryCore.QuantityObjectiveValue, - GlossaryCore.UsableCapitalObjectiveName, - GlossaryCore.NetGdpGrowthRateObjectiveValue, - GlossaryCore.EnergyMeanPriceObjectiveValue, - GlossaryCore.DecreasingGdpIncrementsObjectiveValue, - GlossaryCore.ConstraintEnergyNonUseCapital - ], - 'parent': [ - 'constraint_usable_capital', - 'utility_objective', - 'invest_objective', - 'invest_objective', - 'invest_objective', - 'utility_objective', - 'constraint_energy_non_use_capital' - ], - 'ftype': [INEQ_CONSTRAINT] + [OBJECTIVE] * 5 + [INEQ_CONSTRAINT], - 'weight': [0.1, 1., 0., 0., 0., 5., .1], - AGGR_TYPE: [FunctionManager.INEQ_POSITIVE_WHEN_SATIFIED_AND_SQUARE_IT] + [AGGR_TYPE_SUM] * 5 + [FunctionManager.INEQ_NEGATIVE_WHEN_SATIFIED_AND_SQUARE_IT], - 'namespace': [GlossaryCore.NS_FUNCTIONS] * 7 - } - - func_df = DataFrame(data) - - return func_df - - def setup_constraints(self): - func_df = pd.DataFrame( - columns=['variable', 'parent', 'ftype', 'weight', AGGR_TYPE]) - list_var = [] - list_parent = [] - list_ftype = [] - list_weight = [] - list_aggr_type = [] - list_ns = [] - # ------------------------------------------------- - # CO2 ppm constraints - list_var.extend( - ['rockstrom_limit_constraint', 'minimum_ppm_constraint']) - list_parent.extend(['CO2 ppm', 'CO2 ppm']) - list_ns.extend([GlossaryCore.NS_FUNCTIONS, GlossaryCore.NS_FUNCTIONS]) - list_ftype.extend([INEQ_CONSTRAINT, INEQ_CONSTRAINT]) - list_weight.extend([0.0, -0.0]) - list_aggr_type.extend( - [AGGR_TYPE_SMAX, AGGR_TYPE_SMAX]) - - # ------------------------------------------------- - # calories_per_day_constraint - list_var.append('calories_per_day_constraint') - list_parent.append('agriculture_constraints') - list_ns.extend([GlossaryCore.NS_FUNCTIONS]) - list_ftype.append(INEQ_CONSTRAINT) - list_weight.append(-0.0) - list_aggr_type.append( - AGGR_TYPE_SMAX) - - - # ------------------------------------------------- - list_var.extend([GlossaryCore.ConstraintLowerBoundUsableCapital]) - list_parent.extend(['invests_constraints']) - list_ns.extend([GlossaryCore.NS_FUNCTIONS]) - list_ftype.extend([INEQ_CONSTRAINT]) - list_weight.extend([-0.0]) - list_aggr_type.extend([ - AGGR_TYPE_SMAX]) - - list_var.append('non_use_capital_cons') - list_parent.append('invests_constraints') - list_ns.extend([GlossaryCore.NS_FUNCTIONS]) - list_ftype.append(INEQ_CONSTRAINT) - list_weight.append(-0.0) - list_aggr_type.append( - AGGR_TYPE_SMAX) - - list_var.append('forest_lost_capital_cons') - list_parent.append('agriculture_constraint') - list_ns.extend([GlossaryCore.NS_FUNCTIONS]) - list_ftype.append(INEQ_CONSTRAINT) - list_weight.append(-0.0) - list_aggr_type.append( - AGGR_TYPE_SMAX) - - func_df['variable'] = list_var - func_df['parent'] = list_parent - func_df['ftype'] = list_ftype - func_df['weight'] = list_weight - func_df[AGGR_TYPE] = list_aggr_type - func_df['namespace'] = list_ns - - return func_df - - -if __name__ == "__main__": - uc = Study() - uc.load_data() - uc.run() diff --git a/climateeconomics/sos_wrapping/post_procs/compare_gradients.py b/climateeconomics/sos_wrapping/post_procs/compare_gradients.py index 20580bdcc..ce56b78d0 100644 --- a/climateeconomics/sos_wrapping/post_procs/compare_gradients.py +++ b/climateeconomics/sos_wrapping/post_procs/compare_gradients.py @@ -21,12 +21,15 @@ from os.path import dirname, join import numpy as np -from gemseo.utils.derivatives_approx import DisciplineJacApprox -from gemseo.utils.pkl_tools import dump_compressed_pickle, load_compressed_pickle +from gemseo.utils.derivatives.derivatives_approx import DisciplineJacApprox from sostrades_core.execution_engine.sos_mda_chain import ( SoSMDAChain, ) from sostrades_core.sos_processes.script_test_all_usecases import test_compare_dm +from sostrades_core.tools.pkl_converter.pkl_tools import ( + dump_compressed_pickle, + load_compressed_pickle, +) from sostrades_core.tools.post_processing.charts.chart_filter import ChartFilter from sostrades_core.tools.post_processing.charts.two_axes_instanciated_chart import ( InstanciatedSeries, @@ -42,7 +45,7 @@ an optim process, namely optimization and multi-scenario optimization processes The gradient is computed at the last iteration of the optimization problem Does not work for optim sub-processes, since mdo_disc._differentiated_inputs=[] -NB: the mdo_discipline is set to None in the execution engine when the graphs are updated => the gradients must be computed +NB: the discipline is set to None in the execution engine when the graphs are updated => the gradients must be computed at the end of a computation ''' @@ -51,25 +54,25 @@ def find_mdo_disc(execution_engine, scenario_name, class_to_check): ''' - recover for the scenario name the mdo_discipline at the lowest level that respects the class_to_check + recover for the scenario name the discipline at the lowest level that respects the class_to_check ex: for an optim process: - mdo_disc = execution_engine.root_process.proxy_disciplines[0].proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + mdo_disc = execution_engine.root_process.proxy_disciplines[0].proxy_disciplines[0].discipline_wrapp.discipline for a ms_optim_process: - mdo_disc = execution_engine.root_process.proxy_disciplines[1].proxy_disciplines[0].proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + mdo_disc = execution_engine.root_process.proxy_disciplines[1].proxy_disciplines[0].proxy_disciplines[0].discipline_wrapp.discipline this post-processing is assumed to be linked to the namespace ns_witness which value ends by .WITNESS. Therefore, the scenario name value = namespace value is something.WITNESS - The mdo_discipline should be in WITNESS_EVAL which is one step above witness + The discipline should be in WITNESS_EVAL which is one step above witness ''' scenario_name_trimmed = scenario_name[:scenario_name.rfind('.')] # remove the .WITNESS of the namespace value levels = [execution_engine.root_process] while levels: current_level = levels.pop(0) # Check if current level has the required attribute - if hasattr(current_level, 'mdo_discipline_wrapp'): - if hasattr(current_level.mdo_discipline_wrapp, 'mdo_discipline'): - mdo_disc = current_level.mdo_discipline_wrapp.mdo_discipline + if hasattr(current_level, 'discipline_wrapp'): + if hasattr(current_level.discipline_wrapp, 'discipline'): + mdo_disc = current_level.discipline_wrapp.discipline if isinstance(mdo_disc, class_to_check) and scenario_name_trimmed == mdo_disc.name: logging.debug(f"The object at {current_level} is an instance of {class_to_check.__name__}") return mdo_disc @@ -78,7 +81,7 @@ def find_mdo_disc(execution_engine, scenario_name, class_to_check): if hasattr(current_level, 'proxy_disciplines'): levels.extend(current_level.proxy_disciplines) - logging.debug(f"No instance of {class_to_check.__name__} found for mdo_discipline") + logging.debug(f"No instance of {class_to_check.__name__} found for discipline") return None diff --git a/climateeconomics/sos_wrapping/post_procs/compare_gradients_n_profiles.py b/climateeconomics/sos_wrapping/post_procs/compare_gradients_n_profiles.py index a1e0e2371..0b3bd5a98 100644 --- a/climateeconomics/sos_wrapping/post_procs/compare_gradients_n_profiles.py +++ b/climateeconomics/sos_wrapping/post_procs/compare_gradients_n_profiles.py @@ -21,9 +21,12 @@ import numpy as np from energy_models.glossaryenergy import GlossaryEnergy -from gemseo.utils.derivatives_approx import DisciplineJacApprox -from gemseo.utils.pkl_tools import dump_compressed_pickle, load_compressed_pickle +from gemseo.utils.derivatives.derivatives_approx import DisciplineJacApprox from sostrades_core.sos_processes.script_test_all_usecases import test_compare_dm +from sostrades_core.tools.pkl_converter.pkl_tools import ( + dump_compressed_pickle, + load_compressed_pickle, +) from sostrades_core.tools.post_processing.charts.chart_filter import ChartFilter from sostrades_core.tools.post_processing.charts.two_axes_instanciated_chart import ( InstanciatedSeries, @@ -91,14 +94,14 @@ def post_processings(execution_engine, scenario_name, chart_filters=None): #scen dm_data_dict_before = deepcopy(execution_engine.dm.get_data_dict_values()) n_profiles = get_scenario_value(execution_engine, 'n_profiles', scenario_name) - mdo_disc = execution_engine.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + mdo_disc = execution_engine.root_process.proxy_disciplines[0].discipline_wrapp.discipline inputs = [f'{scenario_name}.InvestmentsProfileBuilderDisc.coeff_{i}' for i in range(n_profiles)] outputs = [f"{scenario_name.split('.')[0]}.{OBJECTIVE_LAGR}"] # the objective lagrangian is one level above the scenario level # compute analytical gradient first at the converged point of the MDA mdo_disc.add_differentiated_inputs(inputs) mdo_disc.add_differentiated_outputs(outputs) logging.info('Post-processing: Computing analytical gradient') - grad_analytical = mdo_disc.linearize(force_no_exec=True) #can add data of the converged point (see sos_mdo_discipline.py) + grad_analytical = mdo_disc.linearize(force_no_exec=True) #can add data of the converged point (see sos_discipline.py) # computed approximated gradient in 2nd step so that the analytical gradient is not computed in X0 + h # warm start must have been removed so that mda and convergence criteria are the same for analytical and approximated gradients diff --git a/climateeconomics/sos_wrapping/post_procs/dashboard.py b/climateeconomics/sos_wrapping/post_procs/dashboard.py index 90f103273..bcfd57ddb 100644 --- a/climateeconomics/sos_wrapping/post_procs/dashboard.py +++ b/climateeconomics/sos_wrapping/post_procs/dashboard.py @@ -209,7 +209,7 @@ def post_processings(execution_engine, scenario_name, chart_filters=None): instanciated_charts) if 'gdp breakdown' in chart_list: - economics_df = get_scenario_value(execution_engine, GlossaryCore.EconomicsDetailDfValue, scenario_name) + economics_df = get_scenario_value(execution_engine, f'Macroeconomics.{GlossaryCore.EconomicsDetailDfValue}', scenario_name) compute_climate_impact_on_gdp = get_scenario_value(execution_engine, 'assumptions_dict', scenario_name)[ 'compute_climate_impact_on_gdp'] @@ -226,7 +226,7 @@ def post_processings(execution_engine, scenario_name, chart_filters=None): economics_df = complete_economics_df_for_sectorization(economics_df, df_non_energy_invest, df_energy_invest, df_consumption) else: - damage_df = get_scenario_value(execution_engine, GlossaryCore.DamageDetailedDfValue, scenario_name) + damage_df = get_scenario_value(execution_engine, f"Macroeconomics.{GlossaryCore.DamageDetailedDfValue}", scenario_name) new_chart = MacroEconomics.breakdown_gdp(economics_df, damage_df, compute_climate_impact_on_gdp, damages_to_productivity) instanciated_charts.append(new_chart) @@ -275,8 +275,8 @@ def post_processings(execution_engine, scenario_name, chart_filters=None): instanciated_charts.append(new_chart) if 'investment distribution' in chart_list: - forest_investment = get_scenario_value(execution_engine, GlossaryEnergy.ForestInvestmentValue, scenario_name) - years = forest_investment[GlossaryEnergy.Years] + reforestation_investment = get_scenario_value(execution_engine, GlossaryEnergy.ReforestationInvestmentValue, scenario_name) + years = reforestation_investment[GlossaryEnergy.Years] chart_name_energy = 'Distribution of investments on each energy ' @@ -643,7 +643,7 @@ def post_processings(execution_engine, scenario_name, chart_filters=None): if 'KPI5' in chart_list: # KPI5 is the return on investment, ie GDPVariation/Investment - economics_detailed_df = get_scenario_value(execution_engine, GlossaryCore.EconomicsDetailDfValue, scenario_name) + economics_detailed_df = get_scenario_value(execution_engine, f'Macroeconomics.{GlossaryCore.EconomicsDetailDfValue}', scenario_name) if sectorization: df_non_energy_invest = get_scenario_value(execution_engine, GlossaryCore.RedistributionInvestmentsDfValue, scenario_name) diff --git a/climateeconomics/sos_wrapping/post_procs/iea_data_preparation/iea_data_preparation_discipline.py b/climateeconomics/sos_wrapping/post_procs/iea_data_preparation/iea_data_preparation_discipline.py index 06742b874..4699fcae4 100644 --- a/climateeconomics/sos_wrapping/post_procs/iea_data_preparation/iea_data_preparation_discipline.py +++ b/climateeconomics/sos_wrapping/post_procs/iea_data_preparation/iea_data_preparation_discipline.py @@ -94,7 +94,6 @@ class IEADataPreparationDiscipline(SoSWrapp): population_dict_variable = Glossary.get_dynamic_variable(Glossary.PopulationDf) co2_tax_dict_value = Glossary.get_dynamic_variable(Glossary.CO2Taxes) energy_production_dict_value = Glossary.get_dynamic_variable(Glossary.EnergyProductionDf) - energy_consumption_dict_value = Glossary.get_dynamic_variable(Glossary.EnergyFinalConsumptionDf) temperature_dict_value = Glossary.get_dynamic_variable(Glossary.TemperatureDf) land_use_surface_dict_value = {'var_name': LandUseV2.LAND_SURFACE_DETAIL_DF, 'type': 'dataframe', 'unit': 'Gha', @@ -105,8 +104,7 @@ class IEADataPreparationDiscipline(SoSWrapp): } # list of dictionaries to update l_dict_to_update = [co2_emissions_dict_variable, gdp_dict_variable, population_dict_variable, co2_tax_dict_value, - energy_production_dict_value, energy_consumption_dict_value, temperature_dict_value, - land_use_surface_dict_value, + energy_production_dict_value, temperature_dict_value, land_use_surface_dict_value, ] # compute the updated dictionaries to be used in both desc_in and desc_out desc_in_updated, desc_out_updated = update_variable_name(l_dict_to_update, SUFFIX_VAR_INTERPOLATED) @@ -140,7 +138,7 @@ class IEADataPreparationDiscipline(SoSWrapp): # store list of input variables for later use variables_to_store = list(desc_in_updated.keys()) # create list of units by hand as very generic in Glossary TODO use glossary later - list_units = ['Gt', 'T$', 'millions of people', '$/tCO2Eq', 'TWh', 'TWh', '°C', 'Gha', 'TWh', 'TWh', 'TWh', 'TWh', 'TWh', 'TWh', 'TWh', 'TWh', 'TWh', '$/MWh', '$/MWh'] + list_units = ['Gt', 'T$', 'millions of people', '$/tCO2Eq', 'TWh', '°C', 'Gha', 'TWh', 'TWh', 'TWh', 'TWh', 'TWh', 'TWh', 'TWh','TWh','TWh', '$/MWh', '$/MWh'] variables_to_store_with_units = dict(zip(variables_to_store, list_units)) DESC_IN = { @@ -231,7 +229,7 @@ def get_post_processing_list(self, chart_filters=None): unit = self.preprocess_column_name_to_get_unit(col) # preprocess name of key to obtain unit of variable yaxis_title = unit - if yaxis_title is None: + if yaxis_title is None : yaxis_title = "Unit undefined" fig.update_layout( @@ -253,4 +251,4 @@ def preprocess_column_name_to_get_unit(col): """ match = re.search(r'\[(.*?)\]', col) # return found unit in column name - return f"[{match.group(1)}]" if match else None + return f"[{match.group(1)}]" if match else None \ No newline at end of file diff --git a/climateeconomics/sos_wrapping/post_procs/iea_nze_comparison/post_processing_iea_nze_comparison.py b/climateeconomics/sos_wrapping/post_procs/iea_nze_comparison/post_processing_iea_nze_comparison.py index 5435a9438..abbc599a6 100644 --- a/climateeconomics/sos_wrapping/post_procs/iea_nze_comparison/post_processing_iea_nze_comparison.py +++ b/climateeconomics/sos_wrapping/post_procs/iea_nze_comparison/post_processing_iea_nze_comparison.py @@ -44,9 +44,6 @@ WITNESS_RAW_ENERGY_DF_NAME = "EnergyMix.energy_production_brut" WITNESS_RAW_ENERGY_COL_NAME = f"{GlossaryCore.TotalProductionValue}" -WITNESS_NET_ENERGY_DF_NAME = f"EnergyMix.{GlossaryEnergy.EnergyProductionValue}" -WITNESS_NET_ENERGY_COL_NAME = f"{GlossaryCore.TotalProductionValue}" - def get_shared_value(execution_engine, short_name_var: str): """returns the value of a variables common to all scenarios""" @@ -356,16 +353,15 @@ def create_chart_comparing_WITNESS_and_IEA( instanciated_charts.append(new_chart) if "Energy_production" in chart_list: - # GDP net of damage vs raw energy for IEA, witness and historical data + # GDP vs energy for IEA, witness and historical data new_chart = TwoAxesInstanciatedChart( - "World's raw energy production (PWh)", - "World's GDP net of damage (T$)", + "World Raw energy production (PWh)", + "World GDP net of damage (T$)", chart_name="GDP vs energy production evolution", ) x_witness_df, _ = get_shared_value(execution_engine, WITNESS_RAW_ENERGY_DF_NAME) y_witness_df, _ = get_shared_value(execution_engine, "WITNESS.Macroeconomics.economics_detail_df") - x_witness = x_witness_df.loc[x_witness_df[GlossaryCore.Years] <= year_end][ - WITNESS_RAW_ENERGY_COL_NAME] / 1e3 # Value is in TWh, plot in PWh + x_witness = x_witness_df.loc[x_witness_df[GlossaryCore.Years] <= year_end][WITNESS_RAW_ENERGY_COL_NAME] / 1e3 # Value is in TWh, plot in PWh y_witness = y_witness_df.loc[y_witness_df[GlossaryCore.Years] <= year_end]["output_net_of_d"] new_series = InstanciatedSeries( x_witness.values.tolist(), @@ -379,22 +375,17 @@ def create_chart_comparing_WITNESS_and_IEA( f"{IEA_NAME}.{GlossaryEnergy.EnergyProductionValue}{SUFFIX_VAR_IEA}") y_iea_df, _ = get_shared_value(execution_engine, f"{IEA_NAME}.{GlossaryEnergy.EconomicsDfValue}{SUFFIX_VAR_IEA}") - # IEA Data is not always provided at the same years for different quantities - # => - # only keep the data for the common years for gdp and energy production. - # Witness data are provided for the same years - + # iea Data are not always provided at the same years for different quantities => only keep the data for the common + # years for gdp and energy production. Witness data are provided for the same years years_x = x_iea_df.loc[x_iea_df[GlossaryCore.Years] <= year_end][GlossaryCore.Years] years_y = y_iea_df.loc[y_iea_df[GlossaryCore.Years] <= year_end][GlossaryCore.Years] common_years = sorted(list(set(years_x).intersection(set(years_y)))) x_iea = x_iea_df.loc[x_iea_df[GlossaryCore.Years].isin(common_years)][GlossaryCore.TotalProductionValue] y_iea = y_iea_df.loc[y_iea_df[GlossaryCore.Years].isin(common_years)]["output_net_of_d"] - new_series = InstanciatedSeries( x_iea.values.tolist(), y_iea.values.tolist(), "IEA", display_type="scatter", - marker_symbol="square", text=common_years, ) new_chart.add_series(new_series) @@ -408,65 +399,6 @@ def create_chart_comparing_WITNESS_and_IEA( x_historical.values.tolist(), y_historical.values.tolist(), "Historical", display_type="scatter", - marker_symbol="triangle-up", - text=years_historical, - ) - new_chart.add_series(new_series) - instanciated_charts.append(new_chart) - - # GDP net of damage vs net energy for IEA, witness and historical data - new_chart = TwoAxesInstanciatedChart( - "World's net energy production (PWh)", - "World's GDP net of damage (T$)", - chart_name="GDP vs energy production evolution", - ) - x_witness_df, _ = get_shared_value(execution_engine, WITNESS_NET_ENERGY_DF_NAME) - y_witness_df, _ = get_shared_value(execution_engine, "WITNESS.Macroeconomics.economics_detail_df") - x_witness = x_witness_df.loc[x_witness_df[GlossaryCore.Years] <= year_end][ - WITNESS_NET_ENERGY_COL_NAME] - y_witness = y_witness_df.loc[y_witness_df[GlossaryCore.Years] <= year_end]["output_net_of_d"] - new_series = InstanciatedSeries( - x_witness.values.tolist(), - y_witness.values.tolist(), - "WITNESS", display_type="scatter", - text=y_witness_df.loc[y_witness_df[GlossaryCore.Years] <= year_end][GlossaryCore.Years].values.tolist(), - ) - new_chart.add_series(new_series) - - x_iea_df, _ = get_shared_value(execution_engine, - f"{IEA_NAME}.{GlossaryEnergy.EnergyFinalConsumptionName}{SUFFIX_VAR_IEA}") - y_iea_df, _ = get_shared_value(execution_engine, - f"{IEA_NAME}.{GlossaryEnergy.EconomicsDfValue}{SUFFIX_VAR_IEA}") - # IEA Data is not always provided at the same years for different quantities - # => - # only keep the data for the common years for gdp and energy production. - # Witness data are provided for the same years - - years_x = x_iea_df.loc[x_iea_df[GlossaryCore.Years] <= year_end][GlossaryCore.Years] - years_y = y_iea_df.loc[y_iea_df[GlossaryCore.Years] <= year_end][GlossaryCore.Years] - common_years = sorted(list(set(years_x).intersection(set(years_y)))) - x_iea = x_iea_df.loc[x_iea_df[GlossaryCore.Years].isin(common_years)][GlossaryCore.FinalConsumptionValue] - y_iea = y_iea_df.loc[y_iea_df[GlossaryCore.Years].isin(common_years)]["output_net_of_d"] - - new_series = InstanciatedSeries( - x_iea.values.tolist(), - y_iea.values.tolist(), - "IEA", display_type="scatter", - marker_symbol="square", - text=common_years, - ) - new_chart.add_series(new_series) - - df_historical_df = pd.read_csv( - join(Path(__file__).parents[3], "data", 'world_gdp_vs_net_energy_consumption.csv')) - years_historical = df_historical_df['years'].values.tolist() - x_historical = df_historical_df['Net energy consumption [PWh]'] - y_historical = df_historical_df['World GDP [T$]'] - new_series = InstanciatedSeries( - x_historical.values.tolist(), - y_historical.values.tolist(), - "Historical", display_type="scatter", - marker_symbol="triangle-up", text=years_historical, ) new_chart.add_series(new_series) diff --git a/climateeconomics/sos_wrapping/post_procs/regions.py b/climateeconomics/sos_wrapping/post_procs/regions.py index 3dc7f10f1..73ea790dc 100644 --- a/climateeconomics/sos_wrapping/post_procs/regions.py +++ b/climateeconomics/sos_wrapping/post_procs/regions.py @@ -112,7 +112,7 @@ def post_processings(execution_engine, scenario_name, chart_filters=None): if GlossaryCore.ChartGDPBiggestEconomies in chart_list: # get variable with total GDP per countries total_gdp_per_countries_df = get_scenario_value(execution_engine, GlossaryCore.GDPCountryDFName, scenario_name) - economics_df = get_scenario_value(execution_engine, GlossaryCore.EconomicsDetailDfValue, scenario_name) + economics_df = get_scenario_value(execution_engine, f'Macroeconomics.{GlossaryCore.EconomicsDetailDfValue}', scenario_name) # Take the year 2020 as a reference to determine the ten biggest countries in terms of GDP # Rank GDP in descending order to select the x countries with the biggest GDP # Find the name of the x biggest countries diff --git a/climateeconomics/sos_wrapping/post_procs/sectors/gdp_non_sectorized/economics_gdp.py b/climateeconomics/sos_wrapping/post_procs/sectors/gdp_non_sectorized/economics_gdp.py index ebde5ab8e..15f162942 100644 --- a/climateeconomics/sos_wrapping/post_procs/sectors/gdp_non_sectorized/economics_gdp.py +++ b/climateeconomics/sos_wrapping/post_procs/sectors/gdp_non_sectorized/economics_gdp.py @@ -54,7 +54,7 @@ def post_processings(execution_engine, scenario_name, chart_filters=None): if chart_filter.filter_key == 'Charts': chart_list = chart_filter.selected_values - total_gdp_macro = get_scenario_value(execution_engine, GlossaryCore.EconomicsDetailDfValue, scenario_name) + total_gdp_macro = get_scenario_value(execution_engine, f'Macroeconomics.{GlossaryCore.EconomicsDetailDfValue}', scenario_name) years = list(total_gdp_macro[GlossaryCore.Years].values) gdp_all_sectors = get_scenario_value(execution_engine, GlossaryCore.SectorGdpDfValue, scenario_name) diff --git a/climateeconomics/sos_wrapping/post_procs/witness_ms/post_processing_witness_coarse_mda.py b/climateeconomics/sos_wrapping/post_procs/witness_ms/post_processing_witness_coarse_mda.py index fdbe31c75..7905c636e 100644 --- a/climateeconomics/sos_wrapping/post_procs/witness_ms/post_processing_witness_coarse_mda.py +++ b/climateeconomics/sos_wrapping/post_procs/witness_ms/post_processing_witness_coarse_mda.py @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. ''' + import logging from math import floor from os.path import join @@ -38,6 +39,9 @@ from climateeconomics.sos_processes.iam.witness.witness_coarse_dev_ms_optim_process.usecase_ms_2_tipping_point_2023 import ( Study as usecase_ms_mdo_iamc, ) +from climateeconomics.sos_processes.iam.witness.witness_coarse_dev_ms_optim_process.usecase_ms_2023_with_nze import ( + Study as usecase_ms_mdo_with_nze, +) from climateeconomics.sos_processes.iam.witness.witness_coarse_dev_ms_story_telling.usecase_witness_ms_mda import ( Study as usecase_ms_mda, ) @@ -785,6 +789,10 @@ def get_scenario_comparison_chart(x_list, y_dict, chart_name, x_axis_name, y_axi usecase_ms_mdo_iamc.UC4_tp2: dict(color='#0047AB'), # Dark blue usecase_ms_mdo_iamc.UC_NZE_tp1: dict(color='#7FFF00'), # Light green usecase_ms_mdo_iamc.UC_NZE_tp2: dict(color='#2E8B57'), # Dark Green + + usecase_ms_mdo_with_nze.NO_CCUS: dict(color='#FFD633'), # Light Orange + usecase_ms_mdo_with_nze.ALL_TECHNOS: dict(color='#0047AB'), # Dark blue + usecase_ms_mdo_with_nze.ALL_TECHNOS_NZE: dict(color='#7FFF00'), # Light green } line_color = None diff --git a/climateeconomics/sos_wrapping/post_procs/witness_optim/post_processing_witness_full.py b/climateeconomics/sos_wrapping/post_procs/witness_optim/post_processing_witness_full.py index 1bdbed0c6..bb7f40053 100644 --- a/climateeconomics/sos_wrapping/post_procs/witness_optim/post_processing_witness_full.py +++ b/climateeconomics/sos_wrapping/post_procs/witness_optim/post_processing_witness_full.py @@ -328,9 +328,8 @@ def get_multilevel_df(execution_engine, namespace, columns=None): # no detailed CO2_emissions_df for Crop and Forest => CO2_from_other_consumption = 0 if 'Forest' in techno: data_fuel_dict = energy_disc.get_sosdisc_inputs('data_fuel_dict') - invest_techno = techno_disc.get_sosdisc_inputs('forest_investment')[ - 'forest_investment'].values * \ - techno_disc.get_sosdisc_inputs('scaling_factor_forest_investment') + invest_techno = techno_disc.get_sosdisc_inputs('reforestation_investment')[ + 'reforestation_investment'].values carbon_emissions = techno_disc.get_sosdisc_outputs( 'CO2_emissions') elif 'Crop' in techno: diff --git a/climateeconomics/sos_wrapping/sos_wrapping_agriculture/crop_2/crop_disc_2.py b/climateeconomics/sos_wrapping/sos_wrapping_agriculture/crop_2/crop_disc_2.py index 62654c256..e29863e43 100644 --- a/climateeconomics/sos_wrapping/sos_wrapping_agriculture/crop_2/crop_disc_2.py +++ b/climateeconomics/sos_wrapping/sos_wrapping_agriculture/crop_2/crop_disc_2.py @@ -543,10 +543,8 @@ def get_breakdown_charts_on_food_type(self, for col in list_food_types: dict_color = {'color': self.food_types_colors[col]} if col in self.food_types_colors else None kwargs = {'line': dict_color} if lines else {'marker': dict_color} - serie = df_all_food_types[col].values - if not(min(serie) == max(serie) == 0): - new_series = InstanciatedSeries(years, serie, str(col).capitalize(), 'bar' if not lines else "lines", True, **kwargs) - new_chart.add_series(new_series) + new_series = InstanciatedSeries(years, df_all_food_types[col], str(col).capitalize(), 'bar' if not lines else "lines", True, **kwargs) + new_chart.add_series(new_series) if df_total is not None and column_total is not None: new_series = InstanciatedSeries(years, df_total[column_total], 'Total', 'lines', True, line={'color': 'gray'}) @@ -594,7 +592,7 @@ def get_dict_bar_plot(self, post_proc_category: Union[None, str], note: Union[dict, None] = None): - new_chart = TwoAxesInstanciatedChart('', unit, stacked_bar=True, chart_name=charts_name) + new_chart = TwoAxesInstanciatedChart('', unit, stacked_bar=True, chart_name=charts_name, show_legend=False) for key, value in dict_values.items(): if key != GlossaryCore.Years: diff --git a/climateeconomics/sos_wrapping/sos_wrapping_agriculture/forest/documentation/forest_disc.markdown b/climateeconomics/sos_wrapping/sos_wrapping_agriculture/forest/documentation/forest_disc.markdown index f528a56c6..5be0ffb94 100644 --- a/climateeconomics/sos_wrapping/sos_wrapping_agriculture/forest/documentation/forest_disc.markdown +++ b/climateeconomics/sos_wrapping/sos_wrapping_agriculture/forest/documentation/forest_disc.markdown @@ -143,7 +143,7 @@ managed\_wood\_part = managed\_wood\_production / total\_biomass\_production ## CO2 emissions The land emissions can be computed with this formula: -$$CO2\_land\_emissions = Surface\_deforestation * CO2\_per\_ha - Surface\_reforestation * CO2\_per\_ha \\+ Surface\_managed\_wood * 0$$ +$$CO2\_land\_emissions = Surface\_deforestation * CO2\_per\_ha - Surface\_reforestation * CO2\_per\_ha + Surface\_managed\_wood * 0$$ The forest for energy emissions can be computed as following: $$CO2\_emissions = CO2(by\_use\_biomass) - CO2(captured\_from\_growing\_trees) => 0$$ diff --git a/climateeconomics/sos_wrapping/sos_wrapping_agriculture/forest/documentation/forest_disc.md b/climateeconomics/sos_wrapping/sos_wrapping_agriculture/forest/documentation/forest_disc.md index d3fed2ef1..2739eb676 100644 --- a/climateeconomics/sos_wrapping/sos_wrapping_agriculture/forest/documentation/forest_disc.md +++ b/climateeconomics/sos_wrapping/sos_wrapping_agriculture/forest/documentation/forest_disc.md @@ -142,7 +142,7 @@ managed\_wood\_part = managed\_wood\_production / total\_biomass\_production ## CO2 emissions The land emissions can be computed with this formula: -$$CO2\_land\_emissions = Surface\_deforestation * CO2\_per\_ha - Surface\_reforestation * CO2\_per\_ha \\+ Surface\_managed\_wood * 0$$ +$$CO2\_land\_emissions = Surface\_deforestation * CO2\_per\_ha - Surface\_reforestation * CO2\_per\_ha + Surface\_managed\_wood * 0$$ The forest for energy emissions can be computed as following: $$CO2\_emissions = CO2(by\_use\_biomass) - CO2(captured\_from\_growing\_trees) => 0$$ diff --git a/climateeconomics/sos_wrapping/sos_wrapping_agriculture/forest/forest_disc.py b/climateeconomics/sos_wrapping/sos_wrapping_agriculture/forest/forest_disc.py index 21fad3577..a6975e51c 100644 --- a/climateeconomics/sos_wrapping/sos_wrapping_agriculture/forest/forest_disc.py +++ b/climateeconomics/sos_wrapping/sos_wrapping_agriculture/forest/forest_disc.py @@ -14,25 +14,26 @@ See the License for the specific language governing permissions and limitations under the License. ''' -from copy import deepcopy from energy_models.core.stream_type.energy_models.biomass_dry import BiomassDry -from energy_models.glossaryenergy import GlossaryEnergy from sostrades_core.tools.post_processing.charts.chart_filter import ChartFilter from sostrades_core.tools.post_processing.charts.two_axes_instanciated_chart import ( InstanciatedSeries, TwoAxesInstanciatedChart, ) -from climateeconomics.core.core_forest.forest_v2 import Forest +from climateeconomics.core.core_forest.forest import ForestAutodiff from climateeconomics.core.core_witness.climateeco_discipline import ( ClimateEcoDiscipline, ) +from climateeconomics.core.tools.autodifferentiated_discipline import ( + AutodifferentiedDisc, +) from climateeconomics.database import DatabaseWitnessCore from climateeconomics.glossarycore import GlossaryCore -class ForestDiscipline(ClimateEcoDiscipline): +class ForestDiscipline(AutodifferentiedDisc): ''' Forest discipline ''' @@ -49,6 +50,25 @@ class ForestDiscipline(ClimateEcoDiscipline): 'icon': 'fas fa-tree fa-fw', 'version': 'Version 0', } + + coupling_inputs = [ + 'deforestation_investment', + 'reforestation_investment', + GlossaryCore.CropProductivityReductionName, + 'managed_wood_investment', + ] + coupling_outputs = [ + 'forest_surface_df', + 'land_use_required', + 'CO2_land_emission_df', + 'CO2_emissions', + 'techno_production', + 'techno_consumption', + GlossaryCore.TechnoConsumptionWithoutRatioValue, + 'techno_prices', + 'forest_lost_capital', + 'forest_lost_capital', + ] AGRI_CAPITAL_TECHNO_LIST = [] biomass_cal_val = BiomassDry.data_energy_dict[ 'calorific_value'] @@ -103,12 +123,17 @@ class ForestDiscipline(ClimateEcoDiscipline): 'Price_per_ha_unit': '$/ha', # in 2019, date of the paper 'residues_density_percentage': residue_percentage, 'wood_percentage_for_energy': wood_percentage_for_energy, - 'actual_yield': actual_yield, - 'managed_yield': managed_yield, - 'unmanaged_yield': unmanaged_yield, + 'actual_yield_year_start': actual_yield, + 'managed_wood_yield_year_start': managed_yield, + 'unmanaged_wood_yield_year_start': unmanaged_yield, 'yield_unit': 'm3/Ha', 'density_unit': 'm^3/ha', + 'deforestation_cost_per_ha': 8000, + 'reforestation_cost_per_ha': 13800, + 'CO2_per_ha': 4000, 'wood_density': wood_density, + 'biomass_dry_calorific_value': BiomassDry.data_energy_dict['calorific_value'], + 'biomass_dry_high_calorific_value': BiomassDry.data_energy_dict['high_calorific_value'], 'residues_density': residues_density, 'residue_calorific_value': 5.61, #4.356, 'residue_calorific_value_unit': 'kWh/kg', @@ -123,7 +148,7 @@ class ForestDiscipline(ClimateEcoDiscipline): # invest: 0.19 Mha are planted each year at 13047.328euro/ha, and 28% is # the share of wood (not residue) - invest_before_year_start = DatabaseWitnessCore.get_forest_invest_before_year_start(year_start=GlossaryCore.YearStartDefault, construction_delay=construction_delay)[0] + invest_before_year_start = DatabaseWitnessCore.get_reforestation_invest_before_year_start(year_start=GlossaryCore.YearStartDefault, construction_delay=construction_delay)[0] # protected forest are 21% of total forest # https://research.wri.org/gfr/forest-designation-indicators/protected-forests @@ -136,54 +161,50 @@ class ForestDiscipline(ClimateEcoDiscipline): DESC_IN = { GlossaryCore.YearStart: ClimateEcoDiscipline.YEAR_START_DESC_IN, GlossaryCore.YearEnd: GlossaryCore.YearEndVar, - Forest.DEFORESTATION_INVESTMENT: {'type': 'dataframe', 'unit': 'G$', + 'deforestation_investment': {'type': 'dataframe', 'unit': 'G$', 'dataframe_descriptor': {GlossaryCore.Years: ('float', None, False), GlossaryCore.InvestmentsValue: ( 'float', [0, 1e9], True)}, 'dataframe_edition_locked': False, 'visibility': ClimateEcoDiscipline.SHARED_VISIBILITY, 'namespace': 'ns_forest'}, - Forest.DEFORESTATION_COST_PER_HA: {'type': 'float', 'unit': '$/ha', 'default': 8000, + 'initial_co2_emissions': {'type': 'float', 'unit': 'GtCO2'}, + 'co2_per_ha': {'type': 'float', 'unit': 'kgCO2/ha/year', 'default': 4000, 'namespace': 'ns_forest'}, + 'reforestation_cost_per_ha': {'type': 'float', 'unit': '$/ha', 'default': 13800, 'namespace': 'ns_forest'}, - Forest.INITIAL_CO2_EMISSIONS: {'type': 'float', 'unit': 'GtCO2', - 'namespace': 'ns_forest', }, - Forest.CO2_PER_HA: {'type': 'float', 'unit': 'kgCO2/ha/year', 'default': 4000, - 'namespace': 'ns_forest'}, - Forest.REFORESTATION_COST_PER_HA: {'type': 'float', 'unit': '$/ha', 'default': 13800, - 'namespace': 'ns_forest'}, - Forest.REFORESTATION_INVESTMENT: {'type': 'dataframe', 'unit': 'G$', + 'reforestation_investment': {'type': 'dataframe', 'unit': 'G$', 'dataframe_descriptor': {GlossaryCore.Years: ('float', None, False), - 'forest_investment': ('float', [0, 1e9], True)}, + 'reforestation_investment': ('float', [0, 1e9], True)}, 'dataframe_edition_locked': False, 'visibility': ClimateEcoDiscipline.SHARED_VISIBILITY, 'namespace': 'ns_invest'}, - Forest.WOOD_TECHNO_DICT: {'type': 'dict', 'unit': '-', 'default': wood_techno_dict, + 'params': {'type': 'dict', 'unit': '-', 'default': wood_techno_dict, 'namespace': 'ns_forest'}, - Forest.MW_INITIAL_SURFACE: {'type': 'float', 'unit': 'Gha', 'default': wood_production_surface, + 'managed_wood_initial_surface': {'type': 'float', 'unit': 'Gha', 'default': wood_production_surface, 'namespace': 'ns_forest'}, - Forest.MW_INVEST_BEFORE_YEAR_START: {'type': 'dataframe', 'unit': 'G$', + 'managed_wood_invest_before_year_start': {'type': 'dataframe', 'unit': 'G$', 'dataframe_descriptor': {GlossaryCore.InvestmentsValue: ('float', [0, 1e9], True)}, 'dataframe_edition_locked': False, 'default': invest_before_year_start, 'namespace': 'ns_forest'}, - Forest.MW_INVESTMENT: {'type': 'dataframe', 'unit': 'G$', + 'managed_wood_investment': {'type': 'dataframe', 'unit': 'G$', 'dataframe_descriptor': {GlossaryCore.Years: ('float', None, False), GlossaryCore.InvestmentsValue: ('float', [0, 1e9], True)}, 'dataframe_edition_locked': False, 'visibility': ClimateEcoDiscipline.SHARED_VISIBILITY, 'namespace': 'ns_forest'}, - Forest.TRANSPORT_COST: {'type': 'dataframe', 'unit': '$/t', 'namespace': GlossaryCore.NS_WITNESS, + 'transport_cost': {'type': 'dataframe', 'unit': '$/t', 'namespace': GlossaryCore.NS_WITNESS, 'visibility': ClimateEcoDiscipline.SHARED_VISIBILITY, 'dataframe_descriptor': {GlossaryCore.Years: ('float', None, False), 'transport': ('float', [0, 1e9], True)}, 'dataframe_edition_locked': False}, - Forest.MARGIN: {'type': 'dataframe', 'unit': '%', 'namespace': GlossaryCore.NS_WITNESS, + 'margin': {'type': 'dataframe', 'unit': '%', 'namespace': GlossaryCore.NS_WITNESS, 'visibility': ClimateEcoDiscipline.SHARED_VISIBILITY, 'dataframe_descriptor': {GlossaryCore.Years: ('float', None, False), 'margin': ('float', [0, 1e9], True)}, 'dataframe_edition_locked': False}, - Forest.UNMANAGED_FOREST: {'type': 'float', 'unit': 'Gha', 'default': initial_unmanaged_forest_surface, + 'initial_unmanaged_forest_surface': {'type': 'float', 'unit': 'Gha', 'default': initial_unmanaged_forest_surface, 'namespace': 'ns_forest'}, - Forest.PROTECTED_FOREST: {'type': 'float', 'unit': 'Gha', 'default': initial_protected_forest_surface, + 'initial_protected_forest_surface': {'type': 'float', 'unit': 'Gha', 'default': initial_protected_forest_surface, 'namespace': 'ns_forest'}, 'scaling_factor_techno_consumption': {'type': 'float', 'default': 1e3, 'unit': '-', 'visibility': ClimateEcoDiscipline.SHARED_VISIBILITY, @@ -191,27 +212,31 @@ class ForestDiscipline(ClimateEcoDiscipline): 'scaling_factor_techno_production': {'type': 'float', 'default': 1e3, 'unit': '-', 'visibility': ClimateEcoDiscipline.SHARED_VISIBILITY, 'namespace': 'ns_public', 'user_level': 2}, - 'scaling_factor_forest_investment': {'type': 'float', 'default': 1e3, 'unit': '-', 'user_level': 2}, GlossaryCore.CheckRangeBeforeRunBoolName: GlossaryCore.CheckRangeBeforeRunBool, + GlossaryCore.CropProductivityReductionName: GlossaryCore.CropProductivityReductionDf } + subsector_production_df = GlossaryCore.get_dynamic_variable(GlossaryCore.SubsectorProductionDf) + subsector_production_df["namespace"] = GlossaryCore.NS_AGRI + damages_df = GlossaryCore.get_dynamic_variable(GlossaryCore.SubsectorDamagesDf) + damages_df["namespace"] = GlossaryCore.NS_AGRI DESC_OUT = { - Forest.CO2_EMITTED_DETAIL_DF: { + 'CO2_emissions_detail_df': { 'type': 'dataframe', 'unit': 'GtCO2', 'namespace': 'ns_forest'}, - Forest.FOREST_DETAIL_SURFACE_DF: { + 'forest_surface_detail_df': { 'type': 'dataframe', 'unit': 'Gha'}, - Forest.FOREST_SURFACE_DF: { + 'forest_surface_df': { 'type': 'dataframe', 'unit': 'Gha', 'visibility': ClimateEcoDiscipline.SHARED_VISIBILITY, 'namespace': GlossaryCore.NS_WITNESS}, 'CO2_land_emission_df': { 'type': 'dataframe', 'unit': 'GtCO2', 'visibility': ClimateEcoDiscipline.SHARED_VISIBILITY, 'namespace': 'ns_forest'}, - Forest.BIOMASS_DRY_DF: { + 'biomass_dry_df': { 'type': 'dataframe', 'unit': '-', 'visibility': ClimateEcoDiscipline.SHARED_VISIBILITY, 'namespace': GlossaryCore.NS_WITNESS}, - Forest.MW_DF: { + 'managed_wood_df': { 'type': 'dataframe', 'unit': 'Gha', 'namespace': 'ns_forest'}, - Forest.BIOMASS_DRY_DETAIL_DF: { + 'biomass_dry_detail_df': { 'type': 'dataframe', 'unit': '-', 'namespace': 'ns_forest'}, 'techno_production': { @@ -236,6 +261,11 @@ class ForestDiscipline(ClimateEcoDiscipline): 'forest_lost_capital': { 'type': 'dataframe', 'unit': 'G$', 'visibility': ClimateEcoDiscipline.SHARED_VISIBILITY, 'namespace': 'ns_forest'}, + 'yields': {'type': 'dataframe', 'unit': 'm^3/Ha', 'description': 'evolution of yields. Yields are affected by temperature change'}, + f"{GlossaryCore.Forest}.{GlossaryCore.ProductionDfValue}": subsector_production_df, + f"{GlossaryCore.Forest}.{GlossaryCore.DamageDfValue}": damages_df, + GlossaryCore.DamageDetailedDfValue: {'type': 'dataframe', 'unit': 'G$', 'description': 'Economical damages details.'}, + GlossaryCore.EconomicsDetailDfValue: {'type': 'dataframe', 'unit': 'G$', 'description': 'Net economical output details.'}, } FOREST_CHARTS = 'Forest chart' @@ -248,280 +278,18 @@ def update_default_values(self): if disc_in is not None and GlossaryCore.YearStart in disc_in: year_start = self.get_sosdisc_inputs(GlossaryCore.YearStart) if year_start is not None: - self.update_default_value(Forest.INITIAL_CO2_EMISSIONS, 'in', DatabaseWitnessCore.ForestEmissions.get_value_at_year(year_start)) + self.update_default_value('initial_co2_emissions', 'in', DatabaseWitnessCore.ForestEmissions.get_value_at_year(year_start)) def init_execution(self): - param = self.get_sosdisc_inputs() - - self.forest_model = Forest(param) + self.model = ForestAutodiff() def run(self): - # -- get inputs - # inputs = list(self.DESC_IN.keys()) - # inp_dict = self.get_sosdisc_inputs(inputs, in_dict=True) - - # -- compute - inputs_dict = self.get_sosdisc_inputs() - - self.forest_model.compute(inputs_dict) - # Scale production TWh -> PWh - techno_production = self.forest_model.techno_production[[ - GlossaryCore.Years, 'biomass_dry (TWh)']] - for column in techno_production.columns: - if column == GlossaryCore.Years: - continue - techno_production[column] = techno_production[column].values / \ - inputs_dict['scaling_factor_techno_production'] - # Scale production Mt -> Gt - techno_consumption = deepcopy(self.forest_model.techno_consumption) - techno_consumption_woratio = deepcopy( - self.forest_model.techno_consumption_woratio) - for column in techno_consumption.columns: - if column == GlossaryCore.Years: - continue - techno_consumption[column] = techno_consumption[column].values / \ - inputs_dict['scaling_factor_techno_consumption'] - techno_consumption_woratio[column] = techno_consumption_woratio[column].values / \ - inputs_dict['scaling_factor_techno_consumption'] - - outputs_dict = { - Forest.CO2_EMITTED_DETAIL_DF: self.forest_model.CO2_emitted_df, - Forest.FOREST_DETAIL_SURFACE_DF: self.forest_model.forest_surface_df, - Forest.FOREST_SURFACE_DF: self.forest_model.forest_surface_df[ - [GlossaryCore.Years, 'global_forest_surface', 'forest_constraint_evolution']], - 'CO2_land_emission_df': self.forest_model.CO2_emitted_df[ - [GlossaryCore.Years, 'emitted_CO2_evol_cumulative']], - 'managed_wood_df': self.forest_model.managed_wood_df, - 'biomass_dry_detail_df': self.forest_model.biomass_dry_df, - 'biomass_dry_df': self.forest_model.biomass_dry_df[ - [GlossaryCore.Years, 'price_per_MWh', 'biomass_dry_for_energy (Mt)']], - - 'techno_production': techno_production, - 'techno_prices': self.forest_model.techno_prices, - 'techno_consumption': techno_consumption, - 'techno_consumption_woratio': techno_consumption_woratio, - 'land_use_required': self.forest_model.land_use_required, - 'CO2_emissions': self.forest_model.CO2_emissions, - 'forest_lost_capital': self.forest_model.forest_lost_capital - } - - self.store_sos_outputs_values(outputs_dict) - - def compute_sos_jacobian(self): - """ - Compute jacobian for each coupling variable - """ - inputs_dict = self.get_sosdisc_inputs() - self.forest_model.compute(inputs_dict) - wood_techno_dict = inputs_dict['wood_techno_dict'] - scaling_factor_techno_production = inputs_dict['scaling_factor_techno_production'] - scaling_factor_techno_consumption = inputs_dict['scaling_factor_techno_consumption'] - - # gradient for surface vs invest - d_deforestation_surface_d_invest = self.forest_model.compute_d_deforestation_surface_d_invest() - d_reforestation_surface_d_invest = self.forest_model.compute_d_reforestation_surface_d_invest() - d_mw_surface_d_invest = self.forest_model.compute_d_mw_surface_d_invest() - - # compute the gradient of the surfaces vs invest at the limit - d_cum_umw_d_deforestation_invest, d_delta_mw_d_deforestation_invest, d_delta_deforestation_d_deforestation_invest, d_lc_deforestation_d_deforestation_invest, d_lc_reforestation_d_deforestation_invest, d_lc_mw_d_deforestation_invest = self.forest_model.compute_d_limit_surfaces_d_deforestation_invest( - d_deforestation_surface_d_invest) - d_cum_umw_d_reforestation_invest, d_delta_mw_d_reforestation_invest, d_delta_deforestation_d_reforestation_invest, d_lc_deforestation_d_reforestation_invest, d_lc_reforestation_d_reforestation_invest, d_lc_mw_d_reforestation_invest = self.forest_model.compute_d_limit_surfaces_d_reforestation_invest( - d_reforestation_surface_d_invest) - d_cum_umw_d_mw_invest, d_delta_mw_d_mw_invest, d_delta_deforestation_d_mw_invest, d_lc_deforestation_d_mw_invest, d_lc_reforestation_d_mw_invest, d_lc_mw_d_mw_invest = self.forest_model.compute_d_limit_surfaces_d_mw_invest( - d_mw_surface_d_invest) - - # compute cumulated surfaces vs deforestation invest - d_cum_mw_surface_d_deforestation_invest = self.forest_model.d_cum( - d_delta_mw_d_deforestation_invest) - d_cum_deforestation_surface_d_deforestation_invest = self.forest_model.d_cum( - d_delta_deforestation_d_deforestation_invest) - # compute cumulated surfaces vs reforestation invest - d_cum_mw_surface_d_reforestation_invest = self.forest_model.d_cum( - d_delta_mw_d_reforestation_invest) - d_cum_deforestation_surface_d_reforestation_invest = self.forest_model.d_cum( - d_delta_deforestation_d_reforestation_invest) - d_cum_reforestation_surface_d_reforestation_invest = self.forest_model.d_cum( - d_reforestation_surface_d_invest) - - # compute cumulated surfaces vs mw invest - d_cum_mw_surface_d_mw_invest = self.forest_model.d_cum( - d_delta_mw_d_mw_invest) - d_cum_deforestation_surface_d_mw_invest = self.forest_model.d_cum( - d_delta_deforestation_d_mw_invest) - # compute gradient global forest surface vs invest: global_surface = - # cum_mw_surface + cum_deforestation_surface - self.set_partial_derivative_for_other_types((Forest.FOREST_SURFACE_DF, 'global_forest_surface'), ( - Forest.DEFORESTATION_INVESTMENT, GlossaryCore.InvestmentsValue), - d_cum_mw_surface_d_deforestation_invest + d_cum_umw_d_deforestation_invest) - self.set_partial_derivative_for_other_types((Forest.FOREST_SURFACE_DF, 'global_forest_surface'), ( - Forest.REFORESTATION_INVESTMENT, 'forest_investment'), - d_cum_mw_surface_d_reforestation_invest + d_cum_umw_d_reforestation_invest) - self.set_partial_derivative_for_other_types((Forest.FOREST_SURFACE_DF, 'global_forest_surface'), ( - 'managed_wood_investment', GlossaryCore.InvestmentsValue), - d_cum_mw_surface_d_mw_invest + d_cum_umw_d_mw_invest) - - # compute gradient constraint surface vs invest. Comstraint surface = cum_deforestation_surface + cum_reforestation_surface - # forest constraint surface vs deforestation invest - d_forest_constraint_d_deforestation_invest = d_cum_deforestation_surface_d_deforestation_invest - self.set_partial_derivative_for_other_types((Forest.FOREST_SURFACE_DF, 'forest_constraint_evolution'), ( - Forest.DEFORESTATION_INVESTMENT, GlossaryCore.InvestmentsValue), d_forest_constraint_d_deforestation_invest) - - # forest constraint surface vs reforestation invest - d_forest_constraint_d_reforestation_invest = d_cum_deforestation_surface_d_reforestation_invest + \ - d_cum_reforestation_surface_d_reforestation_invest - self.set_partial_derivative_for_other_types((Forest.FOREST_SURFACE_DF, 'forest_constraint_evolution'), ( - Forest.REFORESTATION_INVESTMENT, 'forest_investment'), d_forest_constraint_d_reforestation_invest) - - # forest constraint surface vs mw invest - d_forest_constraint_d_mw_invest = d_cum_deforestation_surface_d_mw_invest - self.set_partial_derivative_for_other_types((Forest.FOREST_SURFACE_DF, 'forest_constraint_evolution'), ( - 'managed_wood_investment', GlossaryCore.InvestmentsValue), d_forest_constraint_d_mw_invest) - - # compute gradient land use required vs invest, land use required = - # cum_mw_surface - self.set_partial_derivative_for_other_types(('land_use_required', 'Forest (Gha)'), ( - Forest.DEFORESTATION_INVESTMENT, GlossaryCore.InvestmentsValue), d_cum_mw_surface_d_deforestation_invest) - self.set_partial_derivative_for_other_types(('land_use_required', 'Forest (Gha)'), ( - Forest.REFORESTATION_INVESTMENT, 'forest_investment'), d_cum_mw_surface_d_reforestation_invest) - self.set_partial_derivative_for_other_types(('land_use_required', 'Forest (Gha)'), ( - 'managed_wood_investment', GlossaryCore.InvestmentsValue), d_cum_mw_surface_d_mw_invest) - - # compute gradient CO2_land_emission vs invest - d_CO2_land_emission_d_deforestation_invest = self.forest_model.compute_d_CO2_land_emission( - d_forest_constraint_d_deforestation_invest) - self.set_partial_derivative_for_other_types(('CO2_land_emission_df', 'emitted_CO2_evol_cumulative'), ( - Forest.DEFORESTATION_INVESTMENT, GlossaryCore.InvestmentsValue), d_CO2_land_emission_d_deforestation_invest) - - d_CO2_land_emission_d_reforestation_invest = self.forest_model.compute_d_CO2_land_emission( - d_forest_constraint_d_reforestation_invest) - self.set_partial_derivative_for_other_types(('CO2_land_emission_df', 'emitted_CO2_evol_cumulative'), ( - Forest.REFORESTATION_INVESTMENT, 'forest_investment'), d_CO2_land_emission_d_reforestation_invest) - - d_CO2_land_emission_d_mw_invest = self.forest_model.compute_d_CO2_land_emission( - d_forest_constraint_d_mw_invest) - self.set_partial_derivative_for_other_types(('CO2_land_emission_df', 'emitted_CO2_evol_cumulative'), ( - 'managed_wood_investment', GlossaryCore.InvestmentsValue), d_CO2_land_emission_d_mw_invest) - - # compute gradient of techno production vs invest - d_techno_prod_d_deforestation_invest = self.forest_model.compute_d_techno_prod_d_invest( - d_delta_mw_d_deforestation_invest, d_delta_deforestation_d_deforestation_invest) - d_techno_prod_d_reforestation_invest = self.forest_model.compute_d_techno_prod_d_invest( - d_delta_mw_d_reforestation_invest, d_delta_deforestation_d_reforestation_invest) - d_techno_prod_d_mw_invest = self.forest_model.compute_d_techno_prod_d_invest( - d_delta_mw_d_mw_invest, d_delta_deforestation_d_mw_invest) - - self.set_partial_derivative_for_other_types(('techno_production', 'biomass_dry (TWh)'), - (Forest.DEFORESTATION_INVESTMENT, GlossaryCore.InvestmentsValue), - d_techno_prod_d_deforestation_invest / scaling_factor_techno_production) - self.set_partial_derivative_for_other_types(('techno_production', 'biomass_dry (TWh)'), - (Forest.REFORESTATION_INVESTMENT, 'forest_investment'), - d_techno_prod_d_reforestation_invest / scaling_factor_techno_production) - self.set_partial_derivative_for_other_types(('techno_production', 'biomass_dry (TWh)'), - ('managed_wood_investment', GlossaryCore.InvestmentsValue), - d_techno_prod_d_mw_invest / scaling_factor_techno_production) - - # compute gradient of techno consumption vs invest - d_techno_conso_d_deforestation_invest = self.forest_model.compute_d_techno_conso_d_invest( - d_techno_prod_d_deforestation_invest) - d_techno_conso_d_reforestation_invest = self.forest_model.compute_d_techno_conso_d_invest( - d_techno_prod_d_reforestation_invest) - d_techno_conso_d_mw_invest = self.forest_model.compute_d_techno_conso_d_invest( - d_techno_prod_d_mw_invest) - - self.set_partial_derivative_for_other_types( - ('techno_consumption', f'{GlossaryEnergy.carbon_capture} ({self.forest_model.mass_unit})'), - (Forest.DEFORESTATION_INVESTMENT, GlossaryCore.InvestmentsValue), - d_techno_conso_d_deforestation_invest / scaling_factor_techno_consumption) - self.set_partial_derivative_for_other_types( - ('techno_consumption', f'{GlossaryEnergy.carbon_capture} ({self.forest_model.mass_unit})'), - (Forest.REFORESTATION_INVESTMENT, 'forest_investment'), - d_techno_conso_d_reforestation_invest / scaling_factor_techno_consumption) - self.set_partial_derivative_for_other_types( - ('techno_consumption', f'{GlossaryEnergy.carbon_capture} ({self.forest_model.mass_unit})'), - ('managed_wood_investment', GlossaryCore.InvestmentsValue), - d_techno_conso_d_mw_invest / scaling_factor_techno_consumption) - - # gradient of techno consumption wo ratio (same as techno_consumption - # here) - self.set_partial_derivative_for_other_types( - ('techno_consumption_woratio', f'{GlossaryEnergy.carbon_capture} ({self.forest_model.mass_unit})'), - (Forest.DEFORESTATION_INVESTMENT, GlossaryCore.InvestmentsValue), - d_techno_conso_d_deforestation_invest / scaling_factor_techno_consumption) - self.set_partial_derivative_for_other_types( - ('techno_consumption_woratio', f'{GlossaryEnergy.carbon_capture} ({self.forest_model.mass_unit})'), - (Forest.REFORESTATION_INVESTMENT, 'forest_investment'), - d_techno_conso_d_reforestation_invest / scaling_factor_techno_consumption) - self.set_partial_derivative_for_other_types( - ('techno_consumption_woratio', f'{GlossaryEnergy.carbon_capture} ({self.forest_model.mass_unit})'), - ('managed_wood_investment', GlossaryCore.InvestmentsValue), - d_techno_conso_d_mw_invest / scaling_factor_techno_consumption) - - # compute gradient of techno prices vs invest - d_techno_price_d_deforestation_invest = self.forest_model.compute_d_techno_price_d_invest( - d_delta_mw_d_deforestation_invest, d_delta_deforestation_d_deforestation_invest) - d_techno_price_d_reforestation_invest = self.forest_model.compute_d_techno_price_d_invest( - d_delta_mw_d_reforestation_invest, d_delta_deforestation_d_reforestation_invest) - d_techno_price_d_mw_invest = self.forest_model.compute_d_techno_price_d_invest( - d_delta_mw_d_mw_invest, d_delta_deforestation_d_mw_invest) - - self.set_partial_derivative_for_other_types(('techno_prices', 'Forest'), - (Forest.DEFORESTATION_INVESTMENT, GlossaryCore.InvestmentsValue), - d_techno_price_d_deforestation_invest) - self.set_partial_derivative_for_other_types(('techno_prices', 'Forest'), - (Forest.REFORESTATION_INVESTMENT, 'forest_investment'), - d_techno_price_d_reforestation_invest) - self.set_partial_derivative_for_other_types(('techno_prices', 'Forest'), - ('managed_wood_investment', GlossaryCore.InvestmentsValue), - d_techno_price_d_mw_invest) - - # gradient of techno prices wo ratio (same as techno_price here) - self.set_partial_derivative_for_other_types(('techno_prices', 'Forest_wotaxes'), - (Forest.DEFORESTATION_INVESTMENT, GlossaryCore.InvestmentsValue), - d_techno_price_d_deforestation_invest) - self.set_partial_derivative_for_other_types(('techno_prices', 'Forest_wotaxes'), - (Forest.REFORESTATION_INVESTMENT, 'forest_investment'), - d_techno_price_d_reforestation_invest) - self.set_partial_derivative_for_other_types(('techno_prices', 'Forest_wotaxes'), - ('managed_wood_investment', GlossaryCore.InvestmentsValue), - d_techno_price_d_mw_invest) - - # gradient lost capital vs reforestation investment - - self.set_partial_derivative_for_other_types(('forest_lost_capital', 'reforestation'), - (Forest.REFORESTATION_INVESTMENT, 'forest_investment'), - d_lc_reforestation_d_reforestation_invest) - self.set_partial_derivative_for_other_types(('forest_lost_capital', 'deforestation'), - (Forest.REFORESTATION_INVESTMENT, 'forest_investment'), - d_lc_deforestation_d_reforestation_invest) - self.set_partial_derivative_for_other_types(('forest_lost_capital', 'managed_wood'), - (Forest.REFORESTATION_INVESTMENT, 'forest_investment'), - d_lc_mw_d_reforestation_invest) - - # gradient lost capital vs deforestation investment - - self.set_partial_derivative_for_other_types(('forest_lost_capital', 'reforestation'), - (Forest.DEFORESTATION_INVESTMENT, GlossaryCore.InvestmentsValue), - d_lc_reforestation_d_deforestation_invest) - self.set_partial_derivative_for_other_types(('forest_lost_capital', 'deforestation'), - (Forest.DEFORESTATION_INVESTMENT, GlossaryCore.InvestmentsValue), - d_lc_deforestation_d_deforestation_invest) - self.set_partial_derivative_for_other_types(('forest_lost_capital', 'managed_wood'), - (Forest.DEFORESTATION_INVESTMENT, GlossaryCore.InvestmentsValue), - d_lc_mw_d_deforestation_invest) - - # gradient lost capital vs managed wood investment - - self.set_partial_derivative_for_other_types(('forest_lost_capital', 'reforestation'), - ('managed_wood_investment', GlossaryCore.InvestmentsValue), - d_lc_reforestation_d_mw_invest) - self.set_partial_derivative_for_other_types(('forest_lost_capital', 'deforestation'), - ('managed_wood_investment', GlossaryCore.InvestmentsValue), - d_lc_deforestation_d_mw_invest) - self.set_partial_derivative_for_other_types(('forest_lost_capital', 'managed_wood'), - ('managed_wood_investment', GlossaryCore.InvestmentsValue), - d_lc_mw_d_mw_invest) + inputs = self.get_sosdisc_inputs() + self.model.set_inputs(inputs) + outputs = self.model.compute() + self.store_sos_outputs_values(outputs) def get_chart_filter_list(self): @@ -530,7 +298,14 @@ def get_chart_filter_list(self): chart_filters = [] - chart_list = [ForestDiscipline.FOREST_CHARTS] + chart_list = [ForestDiscipline.FOREST_CHARTS, + "Economical output", + "Investments", + "Surface", + "Biomass price" + "Emissions", + "Capital", + "Damages"] # First filter to deal with the view : program or actor chart_filters.append(ChartFilter( @@ -552,106 +327,131 @@ def get_post_processing_list(self, chart_filters=None): if chart_filter.filter_key == 'charts': chart_list = chart_filter.selected_values - forest_surface_df = self.get_sosdisc_outputs( - Forest.FOREST_DETAIL_SURFACE_DF) + forest_surface_df = self.get_sosdisc_outputs('forest_surface_detail_df') + managed_wood_df = self.get_sosdisc_outputs('managed_wood_df') + years = managed_wood_df[GlossaryCore.Years] + if "Economical output" in chart_list: + economical_output_df = self.get_sosdisc_outputs(f"{GlossaryCore.Forest}.{GlossaryCore.ProductionDfValue}") + economical_damages_df = self.get_sosdisc_outputs(f"{GlossaryCore.Forest}.{GlossaryCore.DamageDfValue}") + new_chart = TwoAxesInstanciatedChart(GlossaryCore.Years, GlossaryCore.SubsectorProductionDf['unit'], chart_name='Economical output of forestry', stacked_bar=True) - managed_wood_df = self.get_sosdisc_outputs( - 'managed_wood_df') + for col in economical_output_df.columns: + if col != GlossaryCore.Years: + new_chart.add_series(InstanciatedSeries(years, economical_output_df[col], pimp_string(col), "lines")) - if ForestDiscipline.FOREST_CHARTS in chart_list: - years = forest_surface_df[GlossaryCore.Years].values.tolist() - # values are *1000 to convert from Gha to Mha - delta_reforestation = forest_surface_df['delta_reforestation_surface'].values * 1000 - reforestation = forest_surface_df['reforestation_surface'].values * 1000 + new_chart.add_series(InstanciatedSeries(years, -economical_damages_df[GlossaryCore.Damages], "Damages", "bar")) + new_chart.post_processing_section_name = "Economical output" + instanciated_charts.append(new_chart) + + if "Economical output" in chart_list: + economical_detail = self.get_sosdisc_outputs(GlossaryCore.EconomicsDetailDfValue) + economical_output_df = self.get_sosdisc_outputs(f"{GlossaryCore.Forest}.{GlossaryCore.ProductionDfValue}") + new_chart = TwoAxesInstanciatedChart(GlossaryCore.Years, GlossaryCore.SubsectorProductionDf['unit'], chart_name='Net economical output breakdown', stacked_bar=True) + + for col in economical_detail.columns: + if col != GlossaryCore.Years: + new_chart.add_series(InstanciatedSeries(years, economical_detail[col], pimp_string(col), "bar")) + + new_chart.add_series(InstanciatedSeries(years, economical_output_df[GlossaryCore.OutputNetOfDamage], "Total", "lines")) + new_chart.post_processing_section_name = "Economical output" + instanciated_charts.append(new_chart) + + if "Damages" in chart_list: + economical_damages_df = self.get_sosdisc_outputs(f"{GlossaryCore.Forest}.{GlossaryCore.DamageDfValue}") + economical_damages_df_detailed = self.get_sosdisc_outputs(GlossaryCore.DamageDetailedDfValue) + new_chart = TwoAxesInstanciatedChart(GlossaryCore.Years, GlossaryCore.SubsectorDamagesDf['unit'], chart_name='Economical damages', stacked_bar=True) + + for col in economical_damages_df_detailed.columns: + if col != GlossaryCore.Years: + new_chart.add_series(InstanciatedSeries(years, economical_damages_df_detailed[col], pimp_string(col), "bar")) + + new_chart.add_series(InstanciatedSeries(years, economical_damages_df[GlossaryCore.Damages], "Total", "lines")) + new_chart.post_processing_section_name = "Damages" + instanciated_charts.append(new_chart) + + if "Damages" in chart_list: + yields_df = self.get_sosdisc_outputs('yields') + new_chart = TwoAxesInstanciatedChart(GlossaryCore.Years, 'm^3/Ha', chart_name='Yields') + + for col in yields_df.columns: + if col != GlossaryCore.Years: + new_chart.add_series(InstanciatedSeries(years, yields_df[col], pimp_string(col), "lines")) - delta_deforestation = forest_surface_df['delta_deforestation_surface'].values * 1000 - deforestation = forest_surface_df['deforestation_surface'].values * 1000 + new_chart.post_processing_section_name = "Damages" + instanciated_charts.append(new_chart) - delta_managed_wood_surface = managed_wood_df['delta_surface'].values * 1000 - managed_wood_surface = managed_wood_df['cumulative_surface'].values * 1000 + if "Damages" in chart_list: + crop_productivity_reduction = self.get_sosdisc_inputs(GlossaryCore.CropProductivityReductionName) + new_chart = TwoAxesInstanciatedChart(GlossaryCore.Years, GlossaryCore.CropProductivityReductionDf['unit'], chart_name='Yields variation due to climate change') - delta_global = forest_surface_df['delta_global_forest_surface'].values * 1000 - global_surface = forest_surface_df['global_forest_surface'].values * 1000 + for col in crop_productivity_reduction.columns: + if col != GlossaryCore.Years: + new_chart.add_series(InstanciatedSeries(years, - crop_productivity_reduction[col], pimp_string(col), "lines")) - unmanaged_forest = forest_surface_df['unmanaged_forest'].values * 1000 - protected_forest = forest_surface_df['protected_forest_surface'].values * 1000 + new_chart.post_processing_section_name = "Damages" + instanciated_charts.append(new_chart) - # invests graph - forest_investment_df = self.get_sosdisc_inputs('forest_investment') - managed_wood_investment_df = self.get_sosdisc_inputs( - 'managed_wood_investment') - deforestation_investment_df = self.get_sosdisc_inputs( - 'deforestation_investment') + if "Investments" in chart_list: + reforestation_investment_df = self.get_sosdisc_inputs('reforestation_investment') + managed_wood_investment_df = self.get_sosdisc_inputs('managed_wood_investment') + deforestation_investment_df = self.get_sosdisc_inputs('deforestation_investment') new_chart = TwoAxesInstanciatedChart(GlossaryCore.Years, 'Investments [G$]', chart_name='Investments in forests activities', stacked_bar=True) - forest_investment = forest_investment_df['forest_investment'] - managed_wood_investment = managed_wood_investment_df[GlossaryCore.InvestmentsValue] - deforestation_investment = deforestation_investment_df[GlossaryCore.InvestmentsValue] - - forest_investment_series = InstanciatedSeries( - years, forest_investment.tolist(), 'Reforestation invests', InstanciatedSeries.BAR_DISPLAY) - managed_wood_investment_series = InstanciatedSeries( - years, managed_wood_investment.tolist(), 'Managed wood invests', InstanciatedSeries.BAR_DISPLAY) - deforestation_investment_series = InstanciatedSeries( - years, deforestation_investment.tolist(), 'Deforestation invests', InstanciatedSeries.BAR_DISPLAY) - - # new_chart.add_series(total_capital_series) - new_chart.add_series(deforestation_investment_series) - new_chart.add_series(forest_investment_series) - new_chart.add_series(managed_wood_investment_series) + new_chart.add_series(InstanciatedSeries(years, reforestation_investment_df['reforestation_investment'], 'Reforestation invests', 'bar')) + new_chart.add_series(InstanciatedSeries(years, managed_wood_investment_df[GlossaryCore.InvestmentsValue], 'Managed wood invests', 'bar')) + new_chart.add_series(InstanciatedSeries(years, deforestation_investment_df[GlossaryCore.InvestmentsValue], 'Deforestation invests', 'bar')) + new_chart.post_processing_section_name = "Investments & Capital" instanciated_charts.append(new_chart) + if "Surface" in chart_list: + # values are *1000 to convert from Gha to Mha + delta_reforestation = forest_surface_df['delta_reforestation_surface'].values * 1e3 + reforestation = forest_surface_df['reforestation_surface'].values * 1e3 + + delta_deforestation = forest_surface_df['delta_deforestation_surface'].values * 1e3 + deforestation = forest_surface_df['deforestation_surface'].values * 1e3 + + delta_managed_wood_surface = managed_wood_df['delta_surface'].values * 1e3 + managed_wood_surface = managed_wood_df['cumulative_surface'].values * 1e3 + + delta_global = forest_surface_df['delta_global_forest_surface'].values * 1e3 + global_surface = forest_surface_df['global_forest_surface'].values * 1e3 + + unmanaged_forest = forest_surface_df['unmanaged_forest'].values * 1e3 + protected_forest = forest_surface_df['protected_forest_surface'].values * 1e3 + # forest evolution year by year chart new_chart = TwoAxesInstanciatedChart(GlossaryCore.Years, - 'Yearly delta of forest surface evolution [Mha / year]', + 'Yearly delta of forest surface evolution [Mha]', chart_name='Yearly delta of forest surface evolution', stacked_bar=True) - deforested_series = InstanciatedSeries( - years, delta_deforestation.tolist(), 'Deforestation', 'bar') - forested_series = InstanciatedSeries( - years, delta_reforestation.tolist(), 'Reforestation', 'bar') - total_series = InstanciatedSeries( - years, delta_global.tolist(), 'Global forest surface', InstanciatedSeries.LINES_DISPLAY) - managed_wood_series = InstanciatedSeries( - years, delta_managed_wood_surface.tolist(), 'Managed wood', 'bar') - - new_chart.add_series(deforested_series) - new_chart.add_series(total_series) - new_chart.add_series(forested_series) + new_chart.add_series(InstanciatedSeries(years, delta_deforestation.tolist(), 'Deforestation', 'bar')) + new_chart.add_series(InstanciatedSeries(years, delta_managed_wood_surface.tolist(), 'Managed wood', 'bar')) + new_chart.add_series(InstanciatedSeries(years, delta_global.tolist(), 'Global forest surface', InstanciatedSeries.LINES_DISPLAY)) + new_chart.add_series(InstanciatedSeries(years, delta_reforestation.tolist(), 'Reforestation', 'bar')) + new_chart.post_processing_section_name = "Surface" instanciated_charts.append(new_chart) # forest cumulative evolution chart new_chart = TwoAxesInstanciatedChart(GlossaryCore.Years, 'Forest surface evolution [Mha]', chart_name='Global forest surface evolution', stacked_bar=True) - deforested_series = InstanciatedSeries( - years, deforestation.tolist(), 'Deforested surface', 'bar') - forested_series = InstanciatedSeries( - years, reforestation.tolist(), 'Reforested surface', 'bar') - total_series = InstanciatedSeries( - years, global_surface.tolist(), 'Forest surface evolution', InstanciatedSeries.LINES_DISPLAY) - managed_wood_series = InstanciatedSeries( - years, managed_wood_surface.tolist(), 'Managed wood', 'bar') - unmanaged_forest_series = InstanciatedSeries( - years, unmanaged_forest.tolist(), 'Unmanaged forest', 'bar') - protected_forest_series = InstanciatedSeries( - years, protected_forest.tolist(), 'Protected forest', 'bar') - - new_chart.add_series(unmanaged_forest_series) - new_chart.add_series(total_series) - new_chart.add_series(managed_wood_series) - new_chart.add_series(protected_forest_series) + new_chart.add_series(InstanciatedSeries(years, deforestation.tolist(), 'Deforested surface', 'bar')) + new_chart.add_series(InstanciatedSeries(years, reforestation.tolist(), 'Reforested surface', 'bar')) + new_chart.add_series(InstanciatedSeries(years, global_surface.tolist(), 'Forest surface evolution', InstanciatedSeries.LINES_DISPLAY)) + new_chart.add_series(InstanciatedSeries(years, managed_wood_surface.tolist(), 'Managed wood', 'bar')) + new_chart.add_series(InstanciatedSeries(years, unmanaged_forest.tolist(), 'Unmanaged forest', 'bar')) + new_chart.add_series(InstanciatedSeries(years, protected_forest.tolist(), 'Protected forest', 'bar')) + new_chart.post_processing_section_name = "Surface" instanciated_charts.append(new_chart) - # CO2 graph - - CO2_emissions_df = self.get_sosdisc_outputs( - GlossaryCore.CO2EmissionsDetailDfValue) + if "Emissions" in chart_list: + CO2_emissions_df = self.get_sosdisc_outputs(GlossaryCore.CO2EmissionsDetailDfValue) delta_reforestation = CO2_emissions_df['delta_CO2_reforestation'].values reforestation = CO2_emissions_df['CO2_reforestation'].values @@ -666,65 +466,43 @@ def get_post_processing_list(self, chart_filters=None): new_chart = TwoAxesInstanciatedChart(GlossaryCore.Years, 'CO2 emission & capture [GtCO2 / year]', chart_name='Yearly forest delta CO2 emissions', stacked_bar=True) - CO2_deforestation_series = InstanciatedSeries( - years, delta_deforestation.tolist(), 'Deforestation emissions', InstanciatedSeries.BAR_DISPLAY) - CO2_reforestation_series = InstanciatedSeries( - years, delta_reforestation.tolist(), 'Reforestation emissions', InstanciatedSeries.BAR_DISPLAY) - CO2_total_series = InstanciatedSeries( - years, delta_global.tolist(), 'Global CO2 balance', InstanciatedSeries.LINES_DISPLAY) - - new_chart.add_series(CO2_deforestation_series) - new_chart.add_series(CO2_total_series) - new_chart.add_series(CO2_reforestation_series) + new_chart.add_series(InstanciatedSeries(years, delta_deforestation.tolist(), 'Deforestation emissions', InstanciatedSeries.BAR_DISPLAY)) + new_chart.add_series(InstanciatedSeries(years, delta_reforestation.tolist(), 'Reforestation emissions', InstanciatedSeries.BAR_DISPLAY)) + new_chart.add_series(InstanciatedSeries(years, delta_global.tolist(), 'Global CO2 balance', InstanciatedSeries.LINES_DISPLAY)) + new_chart.post_processing_section_name = "Emissions" instanciated_charts.append(new_chart) # in Gt new_chart = TwoAxesInstanciatedChart(GlossaryCore.Years, 'CO2 emission & capture [GtCO2]', chart_name='Forest CO2 emissions', stacked_bar=True) - CO2_deforestation_series = InstanciatedSeries( - years, deforestation.tolist(), 'Deforestation emissions', InstanciatedSeries.BAR_DISPLAY) - CO2_reforestation_series = InstanciatedSeries( - years, reforestation.tolist(), 'Reforestation emissions', InstanciatedSeries.BAR_DISPLAY) - CO2_total_series = InstanciatedSeries( - years, global_surface.tolist(), 'Global CO2 balance', InstanciatedSeries.LINES_DISPLAY) - CO2_init_balance_serie = InstanciatedSeries( - years, init_balance.tolist(), 'initial forest emissions', InstanciatedSeries.BAR_DISPLAY) - - new_chart.add_series(CO2_deforestation_series) - new_chart.add_series(CO2_reforestation_series) - new_chart.add_series(CO2_init_balance_serie) - new_chart.add_series(CO2_total_series) + new_chart.add_series(InstanciatedSeries(years, deforestation.tolist(), 'Deforestation emissions', InstanciatedSeries.BAR_DISPLAY)) + new_chart.add_series(InstanciatedSeries(years, reforestation.tolist(), 'Reforestation emissions', InstanciatedSeries.BAR_DISPLAY)) + new_chart.add_series(InstanciatedSeries(years, global_surface.tolist(), 'Global CO2 balance', InstanciatedSeries.LINES_DISPLAY)) + new_chart.add_series(InstanciatedSeries(years, init_balance.tolist(), 'initial forest emissions', InstanciatedSeries.BAR_DISPLAY)) + new_chart.post_processing_section_name = "Emissions" instanciated_charts.append(new_chart) + if "Biomass and energy production" in chart_list: # biomass chart - biomass_dry_df = self.get_sosdisc_outputs( - 'biomass_dry_detail_df') + biomass_dry_df = self.get_sosdisc_outputs('biomass_dry_detail_df') # chart biomass dry for energy production new_chart = TwoAxesInstanciatedChart(GlossaryCore.Years, 'Biomass dry [Mt]', chart_name='Break down of biomass dry production for energy', stacked_bar=True) - mw_residues_energy = managed_wood_df[ - 'residues_production_for_energy (Mt)'] + mw_residues_energy = managed_wood_df['residues_production_for_energy (Mt)'] mw_wood_energy = managed_wood_df['wood_production_for_energy (Mt)'] biomass_dry_energy = biomass_dry_df['biomass_dry_for_energy (Mt)'] deforestation_energy = biomass_dry_df['deforestation_for_energy'] - mn_residues_series = InstanciatedSeries( - years, mw_residues_energy.tolist(), 'Residues from managed wood', InstanciatedSeries.BAR_DISPLAY) - mn_wood_series = InstanciatedSeries( - years, mw_wood_energy.tolist(), 'Wood from managed wood', InstanciatedSeries.BAR_DISPLAY) - deforestation_series = InstanciatedSeries( - years, deforestation_energy.tolist(), 'Biomass from deforestation', InstanciatedSeries.BAR_DISPLAY) - biomass_dry_energy_series = InstanciatedSeries( - years, biomass_dry_energy.tolist(), 'Total biomass dry produced', InstanciatedSeries.LINES_DISPLAY) - - new_chart.add_series(mn_residues_series) - new_chart.add_series(mn_wood_series) - new_chart.add_series(deforestation_series) - new_chart.add_series(biomass_dry_energy_series) + new_chart.add_series(InstanciatedSeries(years, mw_residues_energy.tolist(), 'Residues from managed wood', InstanciatedSeries.BAR_DISPLAY)) + new_chart.add_series(InstanciatedSeries(years, mw_wood_energy.tolist(), 'Wood from managed wood', InstanciatedSeries.BAR_DISPLAY)) + new_chart.add_series(InstanciatedSeries(years, deforestation_energy.tolist(), 'Biomass from deforestation', InstanciatedSeries.BAR_DISPLAY)) + new_chart.add_series(InstanciatedSeries(years, biomass_dry_energy.tolist(), 'Total biomass dry produced', InstanciatedSeries.LINES_DISPLAY)) + + new_chart.post_processing_section_name = "Biomass and energy production" instanciated_charts.append(new_chart) # chart biomass dry for energy production @@ -733,26 +511,16 @@ def get_post_processing_list(self, chart_filters=None): stacked_bar=True) mw_residues_energy_twh = managed_wood_df[ 'residues_production_for_energy (Mt)'] * ForestDiscipline.biomass_cal_val - mw_wood_energy_twh = managed_wood_df['wood_production_for_energy (Mt)'] * \ - ForestDiscipline.biomass_cal_val - biomass_dry_energy_twh = biomass_dry_df['biomass_dry_for_energy (Mt)'] * \ - ForestDiscipline.biomass_cal_val - deforestation_energy_twh = biomass_dry_df['deforestation_for_energy'] * \ - ForestDiscipline.biomass_cal_val - - mn_residues_series = InstanciatedSeries( - years, mw_residues_energy_twh.tolist(), 'Residues from managed wood', InstanciatedSeries.BAR_DISPLAY) - mn_wood_series = InstanciatedSeries( - years, mw_wood_energy_twh.tolist(), 'Wood from managed wood', InstanciatedSeries.BAR_DISPLAY) - deforestation_series = InstanciatedSeries( - years, deforestation_energy_twh.tolist(), 'Biomass from deforestation', InstanciatedSeries.BAR_DISPLAY) - biomass_dry_energy_series = InstanciatedSeries( - years, biomass_dry_energy_twh.tolist(), 'Total biomass dry produced', InstanciatedSeries.LINES_DISPLAY) - - new_chart.add_series(mn_residues_series) - new_chart.add_series(mn_wood_series) - new_chart.add_series(deforestation_series) - new_chart.add_series(biomass_dry_energy_series) + mw_wood_energy_twh = managed_wood_df['wood_production_for_energy (Mt)'] * ForestDiscipline.biomass_cal_val + biomass_dry_energy_twh = biomass_dry_df['biomass_dry_for_energy (Mt)'] * ForestDiscipline.biomass_cal_val + deforestation_energy_twh = biomass_dry_df['deforestation_for_energy'] * ForestDiscipline.biomass_cal_val + + new_chart.add_series(InstanciatedSeries(years, mw_residues_energy_twh.tolist(), 'Residues from managed wood', InstanciatedSeries.BAR_DISPLAY)) + new_chart.add_series(InstanciatedSeries(years, mw_wood_energy_twh.tolist(), 'Wood from managed wood', InstanciatedSeries.BAR_DISPLAY)) + new_chart.add_series(InstanciatedSeries(years, deforestation_energy_twh.tolist(), 'Biomass from deforestation', InstanciatedSeries.BAR_DISPLAY)) + new_chart.add_series(InstanciatedSeries(years, biomass_dry_energy_twh.tolist(), 'Total biomass dry produced', InstanciatedSeries.LINES_DISPLAY)) + + new_chart.post_processing_section_name = "Biomass and energy production" instanciated_charts.append(new_chart) # chart total biomass dry production @@ -767,33 +535,26 @@ def get_post_processing_list(self, chart_filters=None): wood_energy = mw_wood_energy biomass_energy = residues_energy + wood_energy + deforestation_energy - biomass_industry_series = InstanciatedSeries( - years, biomass_industry.tolist(), 'Biomass dedicated to industry', InstanciatedSeries.BAR_DISPLAY) - biomass_energy_series = InstanciatedSeries( - years, biomass_energy.tolist(), 'Biomass dedicated to energy', InstanciatedSeries.BAR_DISPLAY) - - new_chart.add_series(biomass_industry_series) - new_chart.add_series(biomass_energy_series) + new_chart.add_series(InstanciatedSeries(years, biomass_industry.tolist(), 'Biomass dedicated to industry', InstanciatedSeries.BAR_DISPLAY)) + new_chart.add_series(InstanciatedSeries(years, biomass_energy.tolist(), 'Biomass dedicated to energy', InstanciatedSeries.BAR_DISPLAY)) + new_chart.post_processing_section_name = "Biomass and energy production" instanciated_charts.append(new_chart) + if "Biomass price" in chart_list: # biomassdry price per kWh new_chart = TwoAxesInstanciatedChart(GlossaryCore.Years, 'Price [$/MWh]', chart_name='Biomass dry price evolution', stacked_bar=True) + biomass_dry_df = self.get_sosdisc_outputs('biomass_dry_detail_df') mw_price = biomass_dry_df['managed_wood_price_per_MWh'] deforestation_price = biomass_dry_df['deforestation_price_per_MWh'] average_price = biomass_dry_df['price_per_MWh'] - mw_price_series = InstanciatedSeries( - years, mw_price.tolist(), 'Managed wood', InstanciatedSeries.LINES_DISPLAY) - average_price_series = InstanciatedSeries( - years, average_price.tolist(), 'Biomass dry', InstanciatedSeries.LINES_DISPLAY) - deforestation_price_series = InstanciatedSeries( - years, deforestation_price.tolist(), 'Deforestation', InstanciatedSeries.LINES_DISPLAY) + new_chart.add_series(InstanciatedSeries(years, mw_price.tolist(), 'Managed wood', InstanciatedSeries.LINES_DISPLAY)) + new_chart.add_series(InstanciatedSeries(years, average_price.tolist(), 'Biomass dry', InstanciatedSeries.LINES_DISPLAY)) + new_chart.add_series(InstanciatedSeries(years, deforestation_price.tolist(), 'Deforestation', InstanciatedSeries.LINES_DISPLAY)) - new_chart.add_series(mw_price_series) - new_chart.add_series(deforestation_price_series) - new_chart.add_series(average_price_series) + new_chart.post_processing_section_name = "Biomass price" instanciated_charts.append(new_chart) # biomass dry price per ton @@ -803,39 +564,26 @@ def get_post_processing_list(self, chart_filters=None): deforestation_price = biomass_dry_df['deforestation_price_per_ton'] average_price = biomass_dry_df['price_per_ton'] - mw_price_series = InstanciatedSeries( - years, mw_price.tolist(), 'Managed wood', InstanciatedSeries.LINES_DISPLAY) - average_price_series = InstanciatedSeries( - years, average_price.tolist(), 'Biomass dry', InstanciatedSeries.LINES_DISPLAY) - deforestation_price_series = InstanciatedSeries( - years, deforestation_price.tolist(), 'Deforestation', InstanciatedSeries.LINES_DISPLAY) + new_chart.add_series(InstanciatedSeries(years, mw_price.tolist(), 'Managed wood', InstanciatedSeries.LINES_DISPLAY)) + new_chart.add_series(InstanciatedSeries(years, average_price.tolist(), 'Biomass dry', InstanciatedSeries.LINES_DISPLAY)) + new_chart.add_series(InstanciatedSeries(years, deforestation_price.tolist(), 'Deforestation', InstanciatedSeries.LINES_DISPLAY)) - new_chart.add_series(mw_price_series) - new_chart.add_series(deforestation_price_series) - new_chart.add_series(average_price_series) + new_chart.post_processing_section_name = "Biomass price" instanciated_charts.append(new_chart) + if "Capital" in chart_list: # lost capital graph lost_capital_df = self.get_sosdisc_outputs('forest_lost_capital') - new_chart = TwoAxesInstanciatedChart(GlossaryCore.Years, 'Capital [G$]', + new_chart = TwoAxesInstanciatedChart(GlossaryCore.Years, 'Lost capital [G$]', chart_name='Lost capital due to deforestation', stacked_bar=True) - lost_capital_reforestation = lost_capital_df['reforestation'] - lost_capital_managed_wood = lost_capital_df['managed_wood'] - lost_capital_deforestation = lost_capital_df['deforestation'] - - lost_capital_reforestation_series = InstanciatedSeries( - years, lost_capital_reforestation.tolist(), 'Reforestation lost capital', - InstanciatedSeries.BAR_DISPLAY) - lost_capital_managed_wood_series = InstanciatedSeries( - years, lost_capital_managed_wood.tolist(), 'Managed wood lost capital', InstanciatedSeries.BAR_DISPLAY) - lost_capital_deforestation = InstanciatedSeries( - years, lost_capital_deforestation.tolist(), 'Deforestation Lost Capital', 'bar') - - # new_chart.add_series(total_capital_series) - new_chart.add_series(lost_capital_reforestation_series) - new_chart.add_series(lost_capital_managed_wood_series) - new_chart.series.append(lost_capital_deforestation) + new_chart.add_series(InstanciatedSeries(years, lost_capital_df['deforestation'], 'Deforestation Lost Capital', 'bar')) + new_chart.post_processing_section_name = "Investments & Capital" instanciated_charts.append(new_chart) return instanciated_charts + +def pimp_string(val: str): + val = val.replace("_", ' ') + val = val.capitalize() + return val \ No newline at end of file diff --git a/climateeconomics/sos_wrapping/sos_wrapping_forest/README.md b/climateeconomics/sos_wrapping/sos_wrapping_forest/README.md deleted file mode 100644 index b9aba0edd..000000000 --- a/climateeconomics/sos_wrapping/sos_wrapping_forest/README.md +++ /dev/null @@ -1 +0,0 @@ -# Forest discipline wrappings diff --git a/climateeconomics/sos_wrapping/sos_wrapping_forest/__init__.py b/climateeconomics/sos_wrapping/sos_wrapping_forest/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/climateeconomics/sos_wrapping/sos_wrapping_forest/_sidebar.md b/climateeconomics/sos_wrapping/sos_wrapping_forest/_sidebar.md deleted file mode 100644 index d7e52bb04..000000000 --- a/climateeconomics/sos_wrapping/sos_wrapping_forest/_sidebar.md +++ /dev/null @@ -1,5 +0,0 @@ - -[Up](/climateeconomics/sos_wrapping/) | [Forest](/climateeconomics/sos_wrapping/sos_wrapping_forest/) - -* [Forest v1](forest_v1/documentation/forest_disc) - \ No newline at end of file diff --git a/climateeconomics/sos_wrapping/sos_wrapping_forest/forest_v1/__init__.py b/climateeconomics/sos_wrapping/sos_wrapping_forest/forest_v1/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/climateeconomics/sos_wrapping/sos_wrapping_forest/forest_v1/documentation/_sidebar.md b/climateeconomics/sos_wrapping/sos_wrapping_forest/forest_v1/documentation/_sidebar.md deleted file mode 100644 index c12c72c1d..000000000 --- a/climateeconomics/sos_wrapping/sos_wrapping_forest/forest_v1/documentation/_sidebar.md +++ /dev/null @@ -1,2 +0,0 @@ - -[Up](/climateeconomics/sos_wrapping/) | [Forest](/climateeconomics/sos_wrapping/sos_wrapping_forest/) diff --git a/climateeconomics/sos_wrapping/sos_wrapping_forest/forest_v1/documentation/forest_disc.markdown b/climateeconomics/sos_wrapping/sos_wrapping_forest/forest_v1/documentation/forest_disc.markdown deleted file mode 100644 index e073e22da..000000000 --- a/climateeconomics/sos_wrapping/sos_wrapping_forest/forest_v1/documentation/forest_disc.markdown +++ /dev/null @@ -1,44 +0,0 @@ -Forests are a natural consumer of CO2. As CO2 has a major impact on the temperature evolution, tracking the evolution of forests and their CO2 consumption is important in order to get precise results. -Many factors can lead to a reduction of the global forests surfaces, such as consequencies of temperature change and human activities. On the other side, environmental care and policies can lead to a rise of the global forest surface. - -## Model data - -The forest model takes the following data as inputs: - -- **year_start**, the first year of the study. Default is 2020. -- **year_end**, the final year of the study. Default is 2100. -- **time_step**, the number of year between two data computation. Default is 1. -- **limit_deforestation_surface**, the maximum surface in Mha which can be deforested during the study. -- **deforestation_surface**, forest surface removed by year in Mha. Default is set to 10Mha per year (2020 value). -- **CO2_per_ha**, the quantity of CO2 captured by 1 hectare of forest during one year. Unit is kgCO2/ha/year. Default value is 4000kgC02/ha/year [^1]. -As forest captures 16 Gt of CO2 per year, reducing forest by 1% results in a deficit of CO2 captured of 160 Mt. The value of 4000kgCO2/year/ha is coherent with these data. -- **Initial CO2 emissions**, CO2 emissions in GtCO2 due to deforestation at the first year of the study. Default value is 3.21 GtCO2 at 2020, which is the value found at [^2]. -- **reforestation_cost_per_ha**, which is the average price to plant 1ha of tree. Unit is $/ha. The default value is 3800 $/ha [^3]. -- **reforestation_investment**, the quantity of money dedicated to reforestation each year in billions of $. - -The outputs of the model are: - -- **forest_surface_df**, giving the evolution of forest surface year by year, and cumulative in Gha. -- **CO2_emitted_df**, gives evolution of CO2 captured by forest in GtCO2. - -## Evolution of forest surface - -Forest evolution is the sum of deforestation and reforestation contributions. -Deforestation is directly the **deforestation_surface** from the inputs. -Reforestation is calculated by -$$Reforestation\_surface = Reforestation\_investment / cost\_per\_ha$$ - -The cumulative value is the sum of all the forest surface evolution from the first year of the study to the given year of the data. - -## Evolution of CO2 captured -The evolution of CO2 captured by forest is directly linked to the surface of forest. This evolution of CO2 captured is given by: -$$CO2\_captured\_evolution = Forest\_surface\_evolution * CO2\_per\_ha$$ - -## Model limitations -In this model, the quantity of CO2 captured by ha of forest is assumed to be the same all over the world. However, the CO2 captured change with the climate condition. Forest in tropical regions are more effective than forest in cold regions. As a result, cutting trees of the Amazon forest does not have the same impact than cutting trees in cold region, in term of captured CO2. - -## References - -[^1]: World Resources Institute, Forests Absorb Twice As Much Carbon As They Emit Each Year, January 21, 2021 By Nancy Harris and David Gibbs, found online at https://www.wri.org/insights/forests-absorb-twice-much-carbon-they-emit-each-year -[^2]: Our World In Data, Global CO2 emissions from fossil fuels and land use change, found online at https://ourworldindata.org/co2-emissions -[^3]: Agriculture and Food Development Authority, Reforestation, found online at https://www.teagasc.ie/crops/forestry/advice/establishment/reforestation/ diff --git a/climateeconomics/sos_wrapping/sos_wrapping_forest/forest_v1/documentation/forest_disc.md b/climateeconomics/sos_wrapping/sos_wrapping_forest/forest_v1/documentation/forest_disc.md deleted file mode 100644 index 6d9a3ab4d..000000000 --- a/climateeconomics/sos_wrapping/sos_wrapping_forest/forest_v1/documentation/forest_disc.md +++ /dev/null @@ -1,43 +0,0 @@ -Forests are a natural consumer of CO2. As CO2 has a major impact on the temperature evolution, tracking the evolution of forests and their CO2 consumption is important in order to get precise results. -Many factors can lead to a reduction of the global forests surfaces, such as consequencies of temperature change and human activities. On the other side, environmental care and policies can lead to a rise of the global forest surface. - -## Model data - -The forest model takes the following data as inputs: - -- **year_start**, the first year of the study. Default is 2020. -- **year_end**, the final year of the study. Default is 2100. -- **limit_deforestation_surface**, the maximum surface in Mha which can be deforested during the study. -- **deforestation_surface**, forest surface removed by year in Mha. Default is set to 10Mha per year (2020 value). -- **CO2_per_ha**, the quantity of CO2 captured by 1 hectare of forest during one year. Unit is kgCO2/ha/year. Default value is 4000kgC02/ha/year [^1]. -As forest captures 16 Gt of CO2 per year, reducing forest by 1% results in a deficit of CO2 captured of 160 Mt. The value of 4000kgCO2/year/ha is coherent with these data. -- **Initial CO2 emissions**, CO2 emissions in GtCO2 due to deforestation at the first year of the study. Default value is 3.21 GtCO2 at 2020, which is the value found at [^2]. -- **reforestation_cost_per_ha**, which is the average price to plant 1ha of tree. Unit is $/ha. The default value is 3800 $/ha [^3]. -- **reforestation_investment**, the quantity of money dedicated to reforestation each year in billions of $. - -The outputs of the model are: - -- **forest_surface_df**, giving the evolution of forest surface year by year, and cumulative in Gha. -- **CO2_emitted_df**, gives evolution of CO2 captured by forest in GtCO2. - -## Evolution of forest surface - -Forest evolution is the sum of deforestation and reforestation contributions. -Deforestation is directly the **deforestation_surface** from the inputs. -Reforestation is calculated by -$$Reforestation\_surface = Reforestation\_investment / cost\_per\_ha$$ - -The cumulative value is the sum of all the forest surface evolution from the first year of the study to the given year of the data. - -## Evolution of CO2 captured -The evolution of CO2 captured by forest is directly linked to the surface of forest. This evolution of CO2 captured is given by: -$$CO2\_captured\_evolution = Forest\_surface\_evolution * CO2\_per\_ha$$ - -## Model limitations -In this model, the quantity of CO2 captured by ha of forest is assumed to be the same all over the world. However, the CO2 captured change with the climate condition. Forest in tropical regions are more effective than forest in cold regions. As a result, cutting trees of the Amazon forest does not have the same impact than cutting trees in cold region, in term of captured CO2. - -## References - -[^1]: World Resources Institute, Forests Absorb Twice As Much Carbon As They Emit Each Year, January 21, 2021 By Nancy Harris and David Gibbs, found online at https://www.wri.org/insights/forests-absorb-twice-much-carbon-they-emit-each-year -[^2]: Our World In Data, Global CO2 emissions from fossil fuels and land use change, found online at https://ourworldindata.org/co2-emissions -[^3]: Agriculture and Food Development Authority, Reforestation, found online at https://www.teagasc.ie/crops/forestry/advice/establishment/reforestation/ diff --git a/climateeconomics/sos_wrapping/sos_wrapping_forest/forest_v1/forest_disc.py b/climateeconomics/sos_wrapping/sos_wrapping_forest/forest_v1/forest_disc.py deleted file mode 100644 index 022af1b5b..000000000 --- a/climateeconomics/sos_wrapping/sos_wrapping_forest/forest_v1/forest_disc.py +++ /dev/null @@ -1,281 +0,0 @@ -''' -Copyright 2022 Airbus SAS -Modifications on 2023/09/06-2023/11/03 Copyright 2023 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. -''' -from sostrades_core.tools.post_processing.charts.chart_filter import ChartFilter -from sostrades_core.tools.post_processing.charts.two_axes_instanciated_chart import ( - InstanciatedSeries, - TwoAxesInstanciatedChart, -) - -from climateeconomics.core.core_forest.forest_v1 import Forest -from climateeconomics.core.core_witness.climateeco_discipline import ( - ClimateEcoDiscipline, -) -from climateeconomics.glossarycore import GlossaryCore - - -class ForestDiscipline(ClimateEcoDiscipline): - ''' Forest discipline - ''' - - # ontology information - _ontology_data = { - 'label': 'Forest', - 'type': '', - 'source': '', - 'validated': '', - 'validated_by': '', - 'last_modification_date': '', - 'category': '', - 'definition': '', - 'icon': 'fas fa-tree fa-fw', - 'version': 'Version 1', - } - - DESC_IN = {GlossaryCore.YearStart: ClimateEcoDiscipline.YEAR_START_DESC_IN, - GlossaryCore.YearEnd: GlossaryCore.YearEndVar, - Forest.DEFORESTATION_SURFACE: {'type': 'dataframe', 'unit': 'Mha', - 'dataframe_descriptor': {GlossaryCore.Years: ('float', None, False), - 'deforested_surface': ('float', [0, 1e9], True)}, 'dataframe_edition_locked': False, - 'visibility': ClimateEcoDiscipline.SHARED_VISIBILITY, 'namespace': GlossaryCore.NS_WITNESS}, - Forest.LIMIT_DEFORESTATION_SURFACE: {'type': 'float', 'unit': 'Mha', 'default': 1000, - 'namespace': 'ns_forest', }, - Forest.INITIAL_CO2_EMISSIONS: {'type': 'float', 'unit': 'GtCO2', 'default': 3.21, - 'namespace': 'ns_forest', }, - Forest.CO2_PER_HA: {'type': 'float', 'unit': 'kgCO2/ha/year', 'default': 4000, 'namespace': 'ns_forest'}, - Forest.REFORESTATION_COST_PER_HA: {'type': 'float', 'unit': '$/ha', 'default': 3800, 'namespace': 'ns_forest'}, - Forest.REFORESTATION_INVESTMENT: {'type': 'dataframe', 'unit': 'G$', - 'dataframe_descriptor': {GlossaryCore.Years: ('float', None, False), - 'forest_investment': ('float', [0, 1e9], True)}, 'dataframe_edition_locked': False, - 'visibility': ClimateEcoDiscipline.SHARED_VISIBILITY, 'namespace': 'ns_invest'}, - GlossaryCore.CheckRangeBeforeRunBoolName: GlossaryCore.CheckRangeBeforeRunBool, - } - - DESC_OUT = { - GlossaryCore.CO2EmissionsDetailDfValue: { - 'type': 'dataframe', 'unit': 'GtCO2', 'namespace': 'ns_forest'}, - Forest.FOREST_SURFACE_DF: { - 'type': 'dataframe', 'unit': 'Gha', 'visibility': ClimateEcoDiscipline.SHARED_VISIBILITY, 'namespace': GlossaryCore.NS_WITNESS}, - Forest.FOREST_DETAIL_SURFACE_DF: { - 'type': 'dataframe', 'unit': 'Gha'}, - Forest.CO2_EMITTED_FOREST_DF: { - 'type': 'dataframe', 'unit': 'GtCO2', 'visibility': ClimateEcoDiscipline.SHARED_VISIBILITY, 'namespace': GlossaryCore.NS_WITNESS}, - } - - FOREST_CHARTS = 'Forest chart' - - def init_execution(self): - inputs = list(self.DESC_IN.keys()) - param = self.get_sosdisc_inputs(inputs, in_dict=True) - - self.forest_model = Forest(param) - - def run(self): - - #-- get inputs - # inputs = list(self.DESC_IN.keys()) - # inp_dict = self.get_sosdisc_inputs(inputs, in_dict=True) - - #-- compute - in_dict = self.get_sosdisc_inputs() - - self.forest_model.compute(in_dict) - - outputs_dict = { - Forest.CO2_EMITTED_DETAIL_DF: self.forest_model.CO2_emitted_df, - Forest.FOREST_DETAIL_SURFACE_DF: self.forest_model.forest_surface_df, - Forest.FOREST_SURFACE_DF: self.forest_model.forest_surface_df[[GlossaryCore.Years, 'forest_surface_evol', 'global_forest_surface']], - Forest.CO2_EMITTED_FOREST_DF: self.forest_model.CO2_emitted_df[[GlossaryCore.Years, 'emitted_CO2_evol_cumulative']], - } - - #-- store outputs - self.store_sos_outputs_values(outputs_dict) - - def compute_sos_jacobian(self): - """ - Compute jacobian for each coupling variable - """ - in_dict = self.get_sosdisc_inputs() - self.forest_model.compute(in_dict) - - # gradient for deforestation rate - d_deforestation_surface_d_deforestation_surface = self.forest_model.d_deforestation_surface_d_deforestation_surface() - d_cum_deforestation_d_deforestation_surface = self.forest_model.d_cum( - d_deforestation_surface_d_deforestation_surface) - d_forest_surface_d_invest = self.forest_model.d_forestation_surface_d_invest() - d_cun_forest_surface_d_invest = self.forest_model.d_cum( - d_forest_surface_d_invest) - - # forest surface vs deforestation grad - self.set_partial_derivative_for_other_types( - (Forest.FOREST_SURFACE_DF, 'forest_surface_evol'), ( - Forest.DEFORESTATION_SURFACE, 'deforested_surface'), - d_deforestation_surface_d_deforestation_surface) -# self.set_partial_derivative_for_other_types( -# (Forest.FOREST_SURFACE_DF, -# 'forest_surface_evol_cumulative'), -# (Forest.DEFORESTATION_SURFACE, 'deforested_surface'), -# d_cum_deforestation_d_deforestation_surface) - - # forest surface vs forest invest - self.set_partial_derivative_for_other_types( - (Forest.FOREST_SURFACE_DF, 'forest_surface_evol'), ( - Forest.REFORESTATION_INVESTMENT, 'forest_investment'), - d_forest_surface_d_invest) -# self.set_partial_derivative_for_other_types( -# (Forest.FOREST_SURFACE_DF, -# 'forest_surface_evol_cumulative'), -# (Forest.REFORESTATION_INVESTMENT, 'forest_investment'), -# d_cun_forest_surface_d_invest) - - # d_CO2 d deforestation - d_CO2_emitted_d_deforestation_surface = self.forest_model.d_CO2_emitted( - d_deforestation_surface_d_deforestation_surface) - d_cum_CO2_emitted_d_deforestation_surface = self.forest_model.d_cum( - d_CO2_emitted_d_deforestation_surface) - - self.set_partial_derivative_for_other_types( - (Forest.CO2_EMITTED_FOREST_DF, 'emitted_CO2_evol_cumulative'), - (Forest.DEFORESTATION_SURFACE, 'deforested_surface'), - d_cum_CO2_emitted_d_deforestation_surface) - - # d_CO2 d invest - d_CO2_emitted_d_invest = self.forest_model.d_CO2_emitted( - d_forest_surface_d_invest) - d_cum_CO2_emitted_d_invest = self.forest_model.d_cum( - d_CO2_emitted_d_invest) - - self.set_partial_derivative_for_other_types( - (Forest.CO2_EMITTED_FOREST_DF, 'emitted_CO2_evol_cumulative'), - (Forest.REFORESTATION_INVESTMENT, 'forest_investment'), - d_cum_CO2_emitted_d_invest) - - def get_chart_filter_list(self): - - # For the outputs, making a graph for tco vs year for each range and for specific - # value of ToT with a shift of five year between then - - chart_filters = [] - - chart_list = [ForestDiscipline.FOREST_CHARTS] - - # First filter to deal with the view : program or actor - chart_filters.append(ChartFilter( - 'Charts filter', chart_list, chart_list, 'charts')) - - return chart_filters - - def get_post_processing_list(self, chart_filters=None): - ''' - For the outputs, making a graph for tco vs year for each range and for specific - value of ToT with a shift of five year between then - ''' - instanciated_charts = [] - chart_list = [] - - # Overload default value with chart filter - if chart_filters is not None: - for chart_filter in chart_filters: - if chart_filter.filter_key == 'charts': - chart_list = chart_filter.selected_values - - if ForestDiscipline.FOREST_CHARTS in chart_list: - - forest_surface_df = self.get_sosdisc_outputs( - Forest.FOREST_DETAIL_SURFACE_DF) - years = forest_surface_df[GlossaryCore.Years].values.tolist() - # values are *1000 to convert from Gha to Mha - surface_evol_by_year = forest_surface_df['forest_surface_evol'].values * 1000 - surface_evol_cum = forest_surface_df['forest_surface_evol_cumulative'].values * 1000 - deforested_surface_by_year = forest_surface_df['deforested_surface'].values * 1000 - deforested_surface_cum = forest_surface_df['deforested_surface_cumulative'].values * 1000 - forested_surface_by_year = forest_surface_df['forested_surface'].values * 1000 - forested_surface_cum = forest_surface_df['forested_surface_cumulative'].values * 1000 - - # forest evolution year by year chart - new_chart = TwoAxesInstanciatedChart(GlossaryCore.Years, 'Forest surface evolution [Mha / year]', - chart_name='Forest surface evolution', stacked_bar=True) - - deforested_series = InstanciatedSeries( - years, deforested_surface_by_year.tolist(), 'Deforestation', 'bar') - forested_series = InstanciatedSeries( - years, forested_surface_by_year.tolist(), 'Reforestation', 'bar') - total_series = InstanciatedSeries( - years, surface_evol_by_year.tolist(), 'Surface evolution', InstanciatedSeries.LINES_DISPLAY) - - new_chart.add_series(deforested_series) - new_chart.add_series(total_series) - new_chart.add_series(forested_series) - - instanciated_charts.append(new_chart) - - # forest cumulative evolution chart - new_chart = TwoAxesInstanciatedChart(GlossaryCore.Years, 'Cumulative forest surface evolution [Mha]', - chart_name='Cumulative forest surface evolution', stacked_bar=True) - - deforested_series = InstanciatedSeries( - years, deforested_surface_cum.tolist(), 'Deforested surface', 'bar') - forested_series = InstanciatedSeries( - years, forested_surface_cum.tolist(), 'Forested surface', 'bar') - total_series = InstanciatedSeries( - years, surface_evol_cum.tolist(), 'Surface evolution', InstanciatedSeries.LINES_DISPLAY) - new_chart.add_series(deforested_series) - new_chart.add_series(total_series) - new_chart.add_series(forested_series) - - instanciated_charts.append(new_chart) - - # CO2 graph - - CO2_emissions_df = self.get_sosdisc_outputs( - GlossaryCore.CO2EmissionsDetailDfValue) - CO2_emitted_year_by_year = CO2_emissions_df['emitted_CO2'] - CO2_captured_year_by_year = CO2_emissions_df['captured_CO2'] - CO2_total_year_by_year = CO2_emissions_df['emitted_CO2_evol'] - CO2_emitted_cum = CO2_emissions_df['emitted_CO2_cumulative'] - CO2_captured_cum = CO2_emissions_df['captured_CO2_cumulative'] - CO2_total_cum = CO2_emissions_df['emitted_CO2_evol_cumulative'] - - new_chart = TwoAxesInstanciatedChart(GlossaryCore.Years, 'CO2 emission & capture [GtCO2 / year]', - chart_name='Yearly forest delta CO2 emissions', stacked_bar=True) - - CO2_emitted_series = InstanciatedSeries( - years, CO2_emitted_year_by_year.tolist(), 'CO2 emissions', InstanciatedSeries.BAR_DISPLAY) - CO2_captured_series = InstanciatedSeries( - years, CO2_captured_year_by_year.tolist(), 'CO2 capture', InstanciatedSeries.BAR_DISPLAY) - CO2_total_series = InstanciatedSeries( - years, CO2_total_year_by_year.tolist(), 'CO2 evolution', InstanciatedSeries.LINES_DISPLAY) - - new_chart.add_series(CO2_emitted_series) - new_chart.add_series(CO2_total_series) - new_chart.add_series(CO2_captured_series) - instanciated_charts.append(new_chart) - - # in Gt - new_chart = TwoAxesInstanciatedChart(GlossaryCore.Years, 'CO2 emission & capture [GtCO2]', - chart_name='Forest CO2 emissions', stacked_bar=True) - CO2_emitted_series = InstanciatedSeries( - years, CO2_emitted_cum.tolist(), 'CO2 emissions', InstanciatedSeries.BAR_DISPLAY) - CO2_captured_series = InstanciatedSeries( - years, CO2_captured_cum.tolist(), 'CO2 capture', InstanciatedSeries.BAR_DISPLAY) - CO2_total_series = InstanciatedSeries( - years, CO2_total_cum.tolist(), 'CO2 evolution', InstanciatedSeries.LINES_DISPLAY, custom_data=['width']) - - new_chart.add_series(CO2_emitted_series) - new_chart.add_series(CO2_total_series) - new_chart.add_series(CO2_captured_series) - instanciated_charts.append(new_chart) - return instanciated_charts diff --git a/climateeconomics/sos_wrapping/sos_wrapping_sectors/agriculture/agriculture_economy_discipline.py b/climateeconomics/sos_wrapping/sos_wrapping_sectors/agriculture/agriculture_economy_discipline.py index 65a856c8d..00f21e38b 100644 --- a/climateeconomics/sos_wrapping/sos_wrapping_sectors/agriculture/agriculture_economy_discipline.py +++ b/climateeconomics/sos_wrapping/sos_wrapping_sectors/agriculture/agriculture_economy_discipline.py @@ -104,7 +104,7 @@ def setup_sos_disciplines(self): GlossaryCore.FoodTypeCapitalAmortizationCostName: GlossaryCore.FoodTypeCapitalAmortizationCostVar, GlossaryCore.FoodTypesPriceMarginShareName: GlossaryCore.FoodTypesPriceMarginShareVar, GlossaryCore.FoodTypeFeedingCostsName: GlossaryCore.FoodTypeFeedingCostsVar, - GlossaryCore.FoodTypeFertilizationAndPesticidesCostsName: GlossaryCore.FoodTypeFertilizationAndPesticidesCostsVar, + GlossaryCore.FoodTypeFertilizationAndPesticidesCostsName: GlossaryCore.FoodTypeFertilizationAndPesticidesCostsVar } dataframes_inputs = { # economic data @@ -215,7 +215,7 @@ def get_post_processing_list(self, filters=None): unit=GlossaryCore.ProductionDf['unit'], df_total=outputs[f"{GlossaryCore.SectorAgriculture}.{GlossaryCore.ProductionDfValue}"], column_total=GlossaryCore.OutputNetOfDamage, - post_proc_category="Output" + post_proc_category="Economical output" ) instanciated_charts.append(new_chart) @@ -402,7 +402,7 @@ def get_dict_bar_plot(self, post_proc_category: Union[None, str], note: Union[dict, None] = None): - new_chart = TwoAxesInstanciatedChart('', unit, stacked_bar=True, chart_name=charts_name) + new_chart = TwoAxesInstanciatedChart('', unit, stacked_bar=True, chart_name=charts_name, show_legend=False) for key, value in dict_values.items(): if key != GlossaryCore.Years: diff --git a/climateeconomics/sos_wrapping/sos_wrapping_sectors/objectives/objectives_discipline.py b/climateeconomics/sos_wrapping/sos_wrapping_sectors/objectives/objectives_discipline.py index 5c5f0ae67..9e5962587 100644 --- a/climateeconomics/sos_wrapping/sos_wrapping_sectors/objectives/objectives_discipline.py +++ b/climateeconomics/sos_wrapping/sos_wrapping_sectors/objectives/objectives_discipline.py @@ -147,9 +147,9 @@ def setup_sos_disciplines(self): dynamic_outputs[f'{sector}.gdp_error'] = { 'type': 'array', 'unit': '-', 'visibility': ClimateEcoDiscipline.SHARED_VISIBILITY, 'namespace': 'ns_obj'} - dynamic_outputs[f'{sector}.cap_error'] = { - 'type': 'array', 'unit': '-', 'visibility': ClimateEcoDiscipline.SHARED_VISIBILITY, - 'namespace': 'ns_obj'} + # dynamic_outputs[f'{sector}.cap_error'] = { + # 'type': 'array', 'unit': '-', 'visibility': ClimateEcoDiscipline.SHARED_VISIBILITY, + # 'namespace': 'ns_obj'} dynamic_outputs[f'{sector}.energy_eff_error'] = { 'type': 'array', 'unit': '-', 'visibility': ClimateEcoDiscipline.SHARED_VISIBILITY, 'namespace': 'ns_obj'} diff --git a/climateeconomics/sos_wrapping/sos_wrapping_sectors/sectors_redistribution_invests/sectors_redistribution_invest_discipline.py b/climateeconomics/sos_wrapping/sos_wrapping_sectors/sectors_redistribution_invests/sectors_redistribution_invest_discipline.py index 93a959749..d14504bf4 100644 --- a/climateeconomics/sos_wrapping/sos_wrapping_sectors/sectors_redistribution_invests/sectors_redistribution_invest_discipline.py +++ b/climateeconomics/sos_wrapping/sos_wrapping_sectors/sectors_redistribution_invests/sectors_redistribution_invest_discipline.py @@ -46,7 +46,7 @@ class SectorsRedistributionInvestsDiscipline(SoSWrapp): _maturity = 'Research' DESC_IN = { - "mdo_mode": {"visibility": "Shared", "namespace": GlossaryCore.NS_PUBLIC, "type": "bool", "default": False}, + "mdo_mode": {"visibility": "Shared", "namespace": GlossaryCore.NS_PUBLIC, "type": "bool", "default": False, 'structuring': True}, GlossaryCore.SectorListValue: GlossaryCore.SectorList, } diff --git a/climateeconomics/tests/_l1_test_dice_parallel.py b/climateeconomics/tests/_l1_test_dice_parallel.py index 30b23130f..f86f48bdf 100644 --- a/climateeconomics/tests/_l1_test_dice_parallel.py +++ b/climateeconomics/tests/_l1_test_dice_parallel.py @@ -23,11 +23,11 @@ from energy_models.core.stream_type.resources_models.resource_glossary import ( ResourceGlossary, ) -from gemseo.utils.compare_data_manager_tooling import ( +from sostrades_core.execution_engine.execution_engine import ExecutionEngine +from sostrades_core.tools.compare_data_manager_tooling import ( compare_dict, delete_keys_from_dict, ) -from sostrades_core.execution_engine.execution_engine import ExecutionEngine from climateeconomics.glossarycore import GlossaryCore from climateeconomics.sos_processes.iam.witness_wo_energy.datacase_witness_wo_energy import ( @@ -118,7 +118,7 @@ def test_01_exec_parallel(self): values_dict[f'{self.name}.{GlossaryCore.CO2EmissionsGtValue}'] = self.co2_emissions_gt values_dict[f'{self.name}.{GlossaryCore.EnergyPriceValue}'] = self.energy_mean_price values_dict[f'{self.name}.CCS_price'] = CCS_price - values_dict[f'{self.name}.sub_mda_class'] = "GSPureNewtonMDA" + values_dict[f'{self.name}.inner_mda_name'] = "MDAGSNewton" values_dict[f'{self.name}.n_processes'] = n_proc values_dict[f'{self.name}.co2_emissions_ccus_Gt'] = self.co2_emissions_ccus_Gt values_dict[f'{self.name}.CO2_emissions_by_use_sources'] = self.CO2_emissions_by_use_sources @@ -130,7 +130,7 @@ def test_01_exec_parallel(self): self.ee.configure() self.ee.execute() dm_dict_1 = deepcopy(self.ee.get_anonimated_data_dict()) - residual_history = self.ee.root_process.mdo_discipline_wrapp.mdo_discipline.sub_mda_list[0].residual_history + residual_history = self.ee.root_process.discipline_wrapp.discipline.sub_mda_list[0].residual_history """ 8 proc """ @@ -152,7 +152,7 @@ def test_01_exec_parallel(self): values_dict[f'{self.name}.{GlossaryCore.CO2EmissionsGtValue}'] = self.co2_emissions_gt values_dict[f'{self.name}.{GlossaryCore.EnergyPriceValue}'] = self.energy_mean_price values_dict[f'{self.name}.CCS_price'] = CCS_price - values_dict[f'{self.name}.sub_mda_class'] = "GSPureNewtonMDA" + values_dict[f'{self.name}.inner_mda_name'] = "MDAGSNewton" values_dict[f'{self.name}.n_processes'] = n_proc values_dict[f'{self.name}.co2_emissions_ccus_Gt'] = self.co2_emissions_ccus_Gt values_dict[f'{self.name}.CO2_emissions_by_use_sources'] = self.CO2_emissions_by_use_sources @@ -174,7 +174,7 @@ def test_01_exec_parallel(self): compare_dict(dm_dict_1, dm_dict_8, '', dict_error) - residual_history8 = self.ee8.root_process.mdo_discipline_wrapp.mdo_discipline.sub_mda_list[0].residual_history + residual_history8 = self.ee8.root_process.discipline_wrapp.discipline.sub_mda_list[0].residual_history self.assertListEqual(residual_history, residual_history8) # The only different value is n_processes diff --git a/climateeconomics/tests/_l1_test_witness_parallel.py b/climateeconomics/tests/_l1_test_witness_parallel.py index 014a32e81..306a618ad 100644 --- a/climateeconomics/tests/_l1_test_witness_parallel.py +++ b/climateeconomics/tests/_l1_test_witness_parallel.py @@ -19,11 +19,11 @@ from tempfile import gettempdir import numpy as np -from gemseo.utils.compare_data_manager_tooling import ( +from sostrades_core.execution_engine.execution_engine import ExecutionEngine +from sostrades_core.tools.compare_data_manager_tooling import ( compare_dict, delete_keys_from_dict, ) -from sostrades_core.execution_engine.execution_engine import ExecutionEngine from climateeconomics.sos_processes.iam.witness.witness_coarse.usecase_witness_coarse_new import ( Study, @@ -57,7 +57,7 @@ def test_01_exec_parallel(self): for dict_item in usecase.setup_usecase(): values_dict.update(dict_item) - values_dict[f'{self.name}.sub_mda_class'] = "GSPureNewtonMDA" + values_dict[f'{self.name}.inner_mda_name'] = "MDAGSNewton" values_dict[f'{self.name}.max_mda_iter'] = 50 values_dict[f'{self.name}.n_processes'] = n_proc @@ -84,7 +84,7 @@ def test_01_exec_parallel(self): for dict_item in usecase.setup_usecase(): values_dict.update(dict_item) - values_dict[f'{self.name}.sub_mda_class'] = "GSPureNewtonMDA" + values_dict[f'{self.name}.inner_mda_name'] = "MDAGSNewton" values_dict[f'{self.name}.max_mda_iter'] = 50 values_dict[f'{self.name}.n_processes'] = n_proc diff --git a/climateeconomics/tests/_l2_test_gradient_witness_full.py b/climateeconomics/tests/_l2_test_gradient_witness_full.py index b4f0526ce..3eb69bb8b 100644 --- a/climateeconomics/tests/_l2_test_gradient_witness_full.py +++ b/climateeconomics/tests/_l2_test_gradient_witness_full.py @@ -57,7 +57,7 @@ def all_usecase_disciplines_jacobian_test(self, usecase, directory=AbstractJacob full_values_dict[f'{self.name}.{usecase.coupling_name}.warm_start'] = False full_values_dict[f'{self.name}.{usecase.coupling_name}.tolerance'] = 1.0e-12 full_values_dict[f'{self.name}.{usecase.coupling_name}.chain_linearize'] = False - full_values_dict[f'{self.name}.{usecase.coupling_name}.sub_mda_class'] = 'MDAGaussSeidel' + full_values_dict[f'{self.name}.{usecase.coupling_name}.inner_mda_name'] = 'MDAGaussSeidel' full_values_dict[f'{self.name}.{usecase.coupling_name}.max_mda_iter'] = max_mda_iter self.ee.load_study_from_input_dict(full_values_dict) @@ -222,7 +222,7 @@ def test_02_gradient_all_disciplines_witness_coarse(self): # full_values_dict[f'{self.name}.{usecase.coupling_name}.warm_start'] = False # full_values_dict[f'{self.name}.{usecase.coupling_name}.tolerance'] = 1.0e-12 # full_values_dict[f'{self.name}.{usecase.coupling_name}.chain_linearize'] = False - # full_values_dict[f'{self.name}.{usecase.coupling_name}.sub_mda_class'] = 'MDAGaussSeidel' + # full_values_dict[f'{self.name}.{usecase.coupling_name}.inner_mda_name'] = 'MDAGaussSeidel' # full_values_dict[f'{self.name}.{usecase.coupling_name}.max_mda_iter'] = 2 # # self.ee.load_study_from_input_dict(full_values_dict) diff --git a/climateeconomics/tests/_l2_test_gradient_witnesscoarse_subprocess.py b/climateeconomics/tests/_l2_test_gradient_witnesscoarse_subprocess.py index a675c2a23..f2da8bb1c 100644 --- a/climateeconomics/tests/_l2_test_gradient_witnesscoarse_subprocess.py +++ b/climateeconomics/tests/_l2_test_gradient_witnesscoarse_subprocess.py @@ -133,7 +133,7 @@ def test_01_gradient_subprocess_objective_over_design_var(self): # Do not use a gradient method to validate gradient is better, Gauss Seidel works full_values_dict[f'{usecase.study_name}.{usecase.coupling_name}.tolerance'] = 1.0e-12 - full_values_dict[f'{usecase.study_name}.{usecase.coupling_name}.sub_mda_class'] = 'MDAGaussSeidel' + full_values_dict[f'{usecase.study_name}.{usecase.coupling_name}.inner_mda_name'] = 'MDAGaussSeidel' full_values_dict[f'{usecase.study_name}.{usecase.coupling_name}.max_mda_iter'] = 30 full_values_dict[f'{usecase.study_name}.{usecase.coupling_name}.warm_start'] = False full_values_dict[ @@ -181,9 +181,9 @@ def test_01_gradient_subprocess_objective_over_design_var(self): # self.override_dump_jacobian = True #TODO: correct if condition of 597 of gemseo/mda/mda.py self.check_jacobian(location=dirname(__file__), filename=pkl_name, - discipline=coupling_disc.mdo_discipline_wrapp.mdo_discipline, + discipline=coupling_disc.discipline_wrapp.discipline, step=1.0e-4, derr_approx='finite_differences', threshold=1e-15, - local_data=coupling_disc.mdo_discipline_wrapp.mdo_discipline.local_data, + local_data=coupling_disc.discipline_wrapp.discipline.local_data, inputs=inputs, outputs=outputs) @@ -225,7 +225,7 @@ def test_02_gradient_subprocess_objective_over_design_var_for_all_iterations(sel # Do not use a gradient method to validate gradient is better, Gauss Seidel works full_values_dict[f'{usecase.study_name}.{usecase.coupling_name}.tolerance'] = 1.0e-15 - full_values_dict[f'{usecase.study_name}.{usecase.coupling_name}.sub_mda_class'] = 'MDAGaussSeidel' + full_values_dict[f'{usecase.study_name}.{usecase.coupling_name}.inner_mda_name'] = 'MDAGaussSeidel' full_values_dict[f'{usecase.study_name}.{usecase.coupling_name}.max_mda_iter'] = 30 full_values_dict[f'{usecase.study_name}.{usecase.coupling_name}.warm_start'] = False # same hypothesis as uc1 @@ -305,9 +305,9 @@ def test_02_gradient_subprocess_objective_over_design_var_for_all_iterations(sel self.override_dump_jacobian = True # TODO: correct if condition of 597 of gemseo/mda/mda.py self.check_jacobian(location=dirname(__file__), filename=pkl_name, - discipline=coupling_disc.mdo_discipline_wrapp.mdo_discipline, + discipline=coupling_disc.discipline_wrapp.discipline, step=1.0e-18, derr_approx='complex_step', threshold=1e-16, - local_data=dict_values_cleaned,#coupling_disc.mdo_discipline_wrapp.mdo_discipline.local_data,#design_space_values_dict, + local_data=dict_values_cleaned,#coupling_disc.discipline_wrapp.discipline.local_data,#design_space_values_dict, inputs=inputs, outputs=outputs) test_results.append((iter, True)) @@ -366,7 +366,7 @@ def _test_03_func_manager_w_point(self): ee.dm.get_all_namespaces_from_var_name('land_demand_constraint')[0], ] outputs = [ee.dm.get_all_namespaces_from_var_name('objective_lagrangian')[0]] - disc = ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc = ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline disc.check_jacobian( input_data=disc.local_data, threshold=1e-15, inputs=inputs, step=1e-4, @@ -402,7 +402,7 @@ def test_04_gradient_subprocess_objective2_over_design_var(self): # Do not use a gradient method to validate gradient is better, Gauss Seidel works full_values_dict[f'{usecase.study_name}.{usecase.coupling_name}.tolerance'] = 1.0e-12 - full_values_dict[f'{usecase.study_name}.{usecase.coupling_name}.sub_mda_class'] = 'MDAGaussSeidel' + full_values_dict[f'{usecase.study_name}.{usecase.coupling_name}.inner_mda_name'] = 'MDAGaussSeidel' full_values_dict[f'{usecase.study_name}.{usecase.coupling_name}.max_mda_iter'] = 30 full_values_dict[f'{usecase.study_name}.{usecase.coupling_name}.warm_start'] = False full_values_dict[ @@ -493,9 +493,9 @@ def test_04_gradient_subprocess_objective2_over_design_var(self): try: # TODO: correct if condition of 597 of gemseo/mda/mda.py self.check_jacobian(location=dirname(__file__), filename=pkl_name, - discipline=coupling_disc.mdo_discipline_wrapp.mdo_discipline, + discipline=coupling_disc.discipline_wrapp.discipline, step=1.0e-4, derr_approx='finite_differences', threshold=1e-8, - local_data=coupling_disc.mdo_discipline_wrapp.mdo_discipline.local_data, + local_data=coupling_disc.discipline_wrapp.discipline.local_data, inputs=[input], outputs=[output]) dict_success[output].append(input) @@ -584,7 +584,7 @@ def test_05_jacobian_func_manager_disc(self): self.assertEqual(outputs[OBJECTIVE_LAGR][0], res) - disc_techno = ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline assert disc_techno.check_jacobian( input_data=disc_techno.local_data, @@ -635,7 +635,7 @@ def test_06_generate_ref_pkl(self): f.close() coupling_disc = self.ee.root_process.proxy_disciplines[0].proxy_disciplines[0] - discipline = coupling_disc.mdo_discipline_wrapp.mdo_discipline + discipline = coupling_disc.discipline_wrapp.discipline # export all the coupled variables list_coupled_var = discipline.all_couplings @@ -708,7 +708,7 @@ def test_06_gradient_process_objective2_over_design_var(self): import pickle pickle.dump(self.ee.dm.get_data_dict_values(), f) pass - discipline = coupling_disc.mdo_discipline_wrapp.mdo_discipline + discipline = coupling_disc.discipline_wrapp.discipline inputs = [ self.ee.dm.get_all_namespaces_from_var_name( @@ -816,7 +816,7 @@ def test_07_gradient_population(self): raise ValueError( f'outputs for discipline {self.model_name} are not varying in X+h. No gradient will be computed') - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline location = dirname(__file__) filename = f'jacobian_{self.name}_discipline_output.pkl' step = 1.e-4 @@ -939,7 +939,7 @@ def test_08_gradient_macroeconomics(self): raise ValueError( f'outputs for discipline {self.model_name} are not varying in X+h. No gradient will be computed') - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline location = dirname(__file__) filename = f'jacobian_{self.name}_discipline_output.pkl' step = 1.e-4 @@ -981,7 +981,7 @@ def test_09_gradient_energy_mix(self): self.ee.execute() disc_techno = self.ee.dm.get_disciplines_with_name( - f'{self.name}.WITNESS_MDO.WITNESS_Eval.WITNESS.EnergyMix')[0].mdo_discipline_wrapp.mdo_discipline + f'{self.name}.WITNESS_MDO.WITNESS_Eval.WITNESS.EnergyMix')[0].discipline_wrapp.discipline list_input_var_all = disc_techno.input_grammar.data_names list_output_var_all = disc_techno.output_grammar.data_names @@ -1192,7 +1192,7 @@ def test_111_gradient_process_objective2_over_design_var(self): import pickle pickle.dump(self.ee.dm.get_data_dict_values(), f) pass - discipline = coupling_disc.mdo_discipline_wrapp.mdo_discipline + discipline = coupling_disc.discipline_wrapp.discipline """ inputs = [ #self.ee.dm.get_all_namespaces_from_var_name('RenewableSimpleTechno.renewable_RenewableSimpleTechno_array_mix')[0], #OK lagr diff --git a/climateeconomics/tests/data/mda_coarse_data_energymix_input_dict.pkl b/climateeconomics/tests/data/mda_coarse_data_energymix_input_dict.pkl index f01b32acb..091b90427 100644 Binary files a/climateeconomics/tests/data/mda_coarse_data_energymix_input_dict.pkl and b/climateeconomics/tests/data/mda_coarse_data_energymix_input_dict.pkl differ diff --git a/climateeconomics/tests/data/mda_coarse_data_energymix_output_dict.pkl b/climateeconomics/tests/data/mda_coarse_data_energymix_output_dict.pkl index b1090a0bc..fc50e4cfc 100644 Binary files a/climateeconomics/tests/data/mda_coarse_data_energymix_output_dict.pkl and b/climateeconomics/tests/data/mda_coarse_data_energymix_output_dict.pkl differ diff --git a/climateeconomics/tests/data/mda_coarse_data_generator.py b/climateeconomics/tests/data/mda_coarse_data_generator.py index 7c3d75a71..d598e6917 100644 --- a/climateeconomics/tests/data/mda_coarse_data_generator.py +++ b/climateeconomics/tests/data/mda_coarse_data_generator.py @@ -47,7 +47,7 @@ def launch_data_pickle_generation(directory=''): full_values_dict[f'{name}.epsilon0'] = 1.0 full_values_dict[f'{name}.tolerance'] = 1.0e-8 - full_values_dict[f'{name}.sub_mda_class'] = 'MDAGaussSeidel' + full_values_dict[f'{name}.inner_mda_name'] = 'MDAGaussSeidel' full_values_dict[f'{name}.max_mda_iter'] = 200 ee.load_study_from_input_dict(full_values_dict) diff --git a/climateeconomics/tests/data/mda_coarse_data_streams_input_dict.pkl b/climateeconomics/tests/data/mda_coarse_data_streams_input_dict.pkl index c45b9e4ad..8fafdfebc 100644 Binary files a/climateeconomics/tests/data/mda_coarse_data_streams_input_dict.pkl and b/climateeconomics/tests/data/mda_coarse_data_streams_input_dict.pkl differ diff --git a/climateeconomics/tests/data/mda_coarse_data_streams_output_dict.pkl b/climateeconomics/tests/data/mda_coarse_data_streams_output_dict.pkl index 0954341e6..bd2af9883 100644 Binary files a/climateeconomics/tests/data/mda_coarse_data_streams_output_dict.pkl and b/climateeconomics/tests/data/mda_coarse_data_streams_output_dict.pkl differ diff --git a/climateeconomics/tests/data/mda_coarse_data_technologies_input_dict.pkl b/climateeconomics/tests/data/mda_coarse_data_technologies_input_dict.pkl index f6c286434..e355749c8 100644 Binary files a/climateeconomics/tests/data/mda_coarse_data_technologies_input_dict.pkl and b/climateeconomics/tests/data/mda_coarse_data_technologies_input_dict.pkl differ diff --git a/climateeconomics/tests/data/mda_coarse_data_technologies_output_dict.pkl b/climateeconomics/tests/data/mda_coarse_data_technologies_output_dict.pkl index f9553e1a1..8ef49f9a2 100644 Binary files a/climateeconomics/tests/data/mda_coarse_data_technologies_output_dict.pkl and b/climateeconomics/tests/data/mda_coarse_data_technologies_output_dict.pkl differ diff --git a/climateeconomics/tests/dump_database_configure.py b/climateeconomics/tests/dump_database_configure.py index b0845f455..ee5471249 100644 --- a/climateeconomics/tests/dump_database_configure.py +++ b/climateeconomics/tests/dump_database_configure.py @@ -26,7 +26,7 @@ from sostrades_core.sos_processes.processes_factory import SoSProcessFactory from sostrades_core.study_manager.base_study_manager import BaseStudyManager -NUMERICAL_KEYS = ['.sub_mda_class', '.max_mda_iter', '.n_processes', +NUMERICAL_KEYS = ['.inner_mda_name', '.max_mda_iter', '.n_processes', '.chain_linearize', '.tolerance', '.use_lu_fact', '.warm_start', '.acceleration', '.warm_start_threshold', '.n_subcouplings_parallel', '.tolerance_gs', diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_CarbonStorageTechno.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_CarbonStorageTechno.pkl index 8e2ea3014..1dd23fe30 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_CarbonStorageTechno.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_CarbonStorageTechno.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_CleanEnergySimpleTechno.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_CleanEnergySimpleTechno.pkl index 0dd9ed087..f25469305 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_CleanEnergySimpleTechno.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_CleanEnergySimpleTechno.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_FossilSimpleTechno.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_FossilSimpleTechno.pkl index 456d5c68e..bdb40ecfd 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_FossilSimpleTechno.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_FossilSimpleTechno.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_agriculture_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_agriculture_discipline.pkl index a18f197ad..3b005109d 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_agriculture_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_agriculture_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_agriculture_ghg_emission_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_agriculture_ghg_emission_discipline.pkl index cfefe4e94..a528b2064 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_agriculture_ghg_emission_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_agriculture_ghg_emission_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_agriculture_mix_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_agriculture_mix_discipline.pkl index a22f7ee96..edcb38c18 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_agriculture_mix_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_agriculture_mix_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_all_resource_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_all_resource_discipline.pkl index 94eaaafdc..a1e12856f 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_all_resource_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_all_resource_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_clean_energy.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_clean_energy.pkl index 9e4caa06b..60a8ae574 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_clean_energy.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_clean_energy.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_coal_demand_variable_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_coal_demand_variable_discipline.pkl index 4ef396954..4bef81d49 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_coal_demand_variable_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_coal_demand_variable_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_coal_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_coal_discipline.pkl index 95a83bc2e..fa2cd752a 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_coal_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_coal_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_consumption_discipline_last_utility.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_consumption_discipline_last_utility.pkl index 857ce5f7a..addf2fc91 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_consumption_discipline_last_utility.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_consumption_discipline_last_utility.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_consumption_discipline_welfare.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_consumption_discipline_welfare.pkl index 918b62751..8a4c0c6df 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_consumption_discipline_welfare.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_consumption_discipline_welfare.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_consumption_low_economy.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_consumption_low_economy.pkl index ad95caece..e53981fc7 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_consumption_low_economy.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_consumption_low_economy.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_copper_demand_variable_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_copper_demand_variable_discipline.pkl index 02b06a0d6..36bbee5b8 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_copper_demand_variable_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_copper_demand_variable_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_copper_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_copper_discipline.pkl index 91ca565ed..01fb5d2fb 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_copper_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_copper_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_crop_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_crop_discipline.pkl index 94afc3b5e..3d74712c7 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_crop_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_crop_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_crop_discipline_2.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_crop_discipline_2.pkl index dd72b9c7c..e31e9ca87 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_crop_discipline_2.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_crop_discipline_2.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_damage_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_damage_discipline.pkl index ca6bd2cbf..265860dd5 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_damage_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_damage_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_damage_discipline_dev_formula.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_damage_discipline_dev_formula.pkl index a67e6c3d6..d1a9c9e5a 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_damage_discipline_dev_formula.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_damage_discipline_dev_formula.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_damage_discipline_dice.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_damage_discipline_dice.pkl index 3e587edd7..6186e19ad 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_damage_discipline_dice.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_damage_discipline_dice.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_damage_discipline_wo_damage_on_climate.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_damage_discipline_wo_damage_on_climate.pkl index ca6bd2cbf..265860dd5 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_damage_discipline_wo_damage_on_climate.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_damage_discipline_wo_damage_on_climate.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_direct_air_capture.DirectAirCaptureTechno.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_direct_air_capture.DirectAirCaptureTechno.pkl index a662627a3..35a78185a 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_direct_air_capture.DirectAirCaptureTechno.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_direct_air_capture.DirectAirCaptureTechno.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_energymix_coarse.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_energymix_coarse.pkl index 3ee9db10e..8786be406 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_energymix_coarse.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_energymix_coarse.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_flue_gas_capture.FlueGasTechno.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_flue_gas_capture.FlueGasTechno.pkl index 737288a1e..24afeb22d 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_flue_gas_capture.FlueGasTechno.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_flue_gas_capture.FlueGasTechno.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_forest_autodiff.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_forest_autodiff.pkl new file mode 100644 index 000000000..cbf4f9e03 Binary files /dev/null and b/climateeconomics/tests/jacobian_pkls/jacobian_forest_autodiff.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_forest_v1_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_forest_v1_discipline.pkl index eadd309d1..3c3ffddeb 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_forest_v1_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_forest_v1_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_forest_v2_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_forest_v2_discipline.pkl index fd727284c..b50005e0b 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_forest_v2_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_forest_v2_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_forest_v2_discipline_2.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_forest_v2_discipline_2.pkl index b39c5971e..db6a89771 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_forest_v2_discipline_2.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_forest_v2_discipline_2.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_forest_v2_discipline_3.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_forest_v2_discipline_3.pkl index b39c5971e..6601c01c0 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_forest_v2_discipline_3.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_forest_v2_discipline_3.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_forest_v2_discipline_4.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_forest_v2_discipline_4.pkl index a0d963812..441f92700 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_forest_v2_discipline_4.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_forest_v2_discipline_4.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_fossil.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_fossil.pkl index fbb83e860..6364b2420 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_fossil.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_fossil.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_ghg_cycle_discipline1.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_ghg_cycle_discipline1.pkl index 2794d6855..b6a201b2a 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_ghg_cycle_discipline1.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_ghg_cycle_discipline1.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_ghg_emission_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_ghg_emission_discipline.pkl index 8bef39904..0a730e578 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_ghg_emission_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_ghg_emission_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_ghg_emission_discipline_affine_co2_objective.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_ghg_emission_discipline_affine_co2_objective.pkl index 8bef39904..0a730e578 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_ghg_emission_discipline_affine_co2_objective.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_ghg_emission_discipline_affine_co2_objective.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_labormarket_sectorization_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_labormarket_sectorization_discipline.pkl index eb43d3204..be7016c15 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_labormarket_sectorization_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_labormarket_sectorization_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_land_use_v1_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_land_use_v1_discipline.pkl index e8a80d784..1458b9d50 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_land_use_v1_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_land_use_v1_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_land_use_v2_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_land_use_v2_discipline.pkl index e92b08d0d..bd34699aa 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_land_use_v2_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_land_use_v2_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_macro_sectorization_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_macro_sectorization_discipline.pkl index 9a8f5d726..86912527d 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_macro_sectorization_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_macro_sectorization_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline.pkl index bde874f97..f9bc4b8cb 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_gigantic_energy_production_damage_productivity.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_gigantic_energy_production_damage_productivity.pkl index 3dab3e4c4..0fef967cd 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_gigantic_energy_production_damage_productivity.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_gigantic_energy_production_damage_productivity.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_gigantic_energy_production_no_damage_productivity.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_gigantic_energy_production_no_damage_productivity.pkl index 468926a83..50af8d7b0 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_gigantic_energy_production_no_damage_productivity.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_gigantic_energy_production_no_damage_productivity.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_gigantic_energy_production_wo_compute_gdp.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_gigantic_energy_production_wo_compute_gdp.pkl index 5188f0296..b8f29fc4c 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_gigantic_energy_production_wo_compute_gdp.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_gigantic_energy_production_wo_compute_gdp.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_grad_damageproductivity.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_grad_damageproductivity.pkl index b06f883ae..914a1cbc0 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_grad_damageproductivity.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_grad_damageproductivity.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_grad_gigantic_invest.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_grad_gigantic_invest.pkl index 59fac041e..3b4b02267 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_grad_gigantic_invest.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_grad_gigantic_invest.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_grad_max_damage.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_grad_max_damage.pkl index d38cd3abf..960e60f0e 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_grad_max_damage.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_grad_max_damage.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_negative_co2_tax.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_negative_co2_tax.pkl index bde874f97..f9bc4b8cb 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_negative_co2_tax.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_negative_co2_tax.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_negative_emissions.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_negative_emissions.pkl index bde874f97..f9bc4b8cb 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_negative_emissions.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_negative_emissions.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_very_high_emissions.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_very_high_emissions.pkl index bde874f97..f9bc4b8cb 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_very_high_emissions.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_very_high_emissions.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_without_compute_gdp.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_without_compute_gdp.pkl index 13f8e5dc2..154a83ce0 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_without_compute_gdp.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_macroeconomics_discipline_without_compute_gdp.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_natural_gas_demand_variable_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_natural_gas_demand_variable_discipline.pkl index 92a7f626a..53c9fe393 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_natural_gas_demand_variable_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_natural_gas_demand_variable_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_natural_gas_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_natural_gas_discipline.pkl index cbc5ffcb8..2df3c81be 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_natural_gas_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_natural_gas_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_non_use_capital_objective.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_non_use_capital_objective.pkl index fa781e48a..db1a0554c 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_non_use_capital_objective.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_non_use_capital_objective.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_oil_demand_variable_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_oil_demand_variable_discipline.pkl index 31f3a40a5..5cab3add5 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_oil_demand_variable_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_oil_demand_variable_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_oil_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_oil_discipline.pkl index e1f638ee2..6b3ed5de2 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_oil_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_oil_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_platinum_demand_variable_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_platinum_demand_variable_discipline.pkl index d51f29fe0..0977d359c 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_platinum_demand_variable_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_platinum_demand_variable_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_platinum_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_platinum_discipline.pkl index 3fec2aac4..e5feeed26 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_platinum_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_platinum_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_policy_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_policy_discipline.pkl index 91240e7da..95f169f99 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_policy_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_policy_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_policy_discipline2.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_policy_discipline2.pkl index 4689aaa19..7e1d37fcb 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_policy_discipline2.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_policy_discipline2.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_policy_discipline3.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_policy_discipline3.pkl index 92a293369..065b19674 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_policy_discipline3.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_policy_discipline3.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_population_discipline_output_wo_climate_effect.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_population_discipline_output_wo_climate_effect.pkl index a8c416532..b8803fb02 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_population_discipline_output_wo_climate_effect.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_population_discipline_output_wo_climate_effect.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_agriculture.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_agriculture.pkl index 686123122..d8b6def82 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_agriculture.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_agriculture.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_industry.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_industry.pkl index f2858538e..c55c81a7b 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_industry.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_industry.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_services.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_services.pkl index 9a7e8dc17..e5d5fa6e6 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_services.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_services.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_agriculture.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_agriculture.pkl index 1b417f491..9d2ca3b29 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_agriculture.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_agriculture.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_industry.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_industry.pkl index e1c15bb7b..6ca40afb1 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_industry.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_industry.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_on_gdp_agriculture.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_on_gdp_agriculture.pkl index 6b64b17ce..e63c8b60f 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_on_gdp_agriculture.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_on_gdp_agriculture.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_on_gdp_industry.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_on_gdp_industry.pkl index 2b8aef182..a5850ef46 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_on_gdp_industry.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_on_gdp_industry.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_on_gdp_services.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_on_gdp_services.pkl index 95bf88438..86a6d9203 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_on_gdp_services.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_on_gdp_services.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_services.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_services.pkl index cf599c7c8..08dbad5a3 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_services.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_sector_discipline_withoutdamage_services.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_sectorized_utility_discipline_sectors_obj.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_sectorized_utility_discipline_sectors_obj.pkl index 4650524cd..6590eb9f5 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_sectorized_utility_discipline_sectors_obj.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_sectorized_utility_discipline_sectors_obj.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_sectors_consumption_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_sectors_consumption_discipline.pkl index c40f6e028..d66ddc3d4 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_sectors_consumption_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_sectors_consumption_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_sectors_demand_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_sectors_demand_discipline.pkl index 83bc93cbf..f4f525963 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_sectors_demand_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_sectors_demand_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_sectors_redistribution_energy_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_sectors_redistribution_energy_discipline.pkl index a916fd07a..12eeca289 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_sectors_redistribution_energy_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_sectors_redistribution_energy_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_sectors_redistribution_invest_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_sectors_redistribution_invest_discipline.pkl index fdde3aa20..b8d44a1b9 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_sectors_redistribution_invest_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_sectors_redistribution_invest_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_sectors_redistribution_invest_discipline_2.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_sectors_redistribution_invest_discipline_2.pkl index f0d11fdcc..f1c156b83 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_sectors_redistribution_invest_discipline_2.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_sectors_redistribution_invest_discipline_2.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_temperature_discipline_Meinshausen.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_temperature_discipline_Meinshausen.pkl index 21a6255d7..7693ff1bc 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_temperature_discipline_Meinshausen.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_temperature_discipline_Meinshausen.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_temperature_discipline_Myhre.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_temperature_discipline_Myhre.pkl index 9c1f444ea..7d9b09dd1 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_temperature_discipline_Myhre.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_temperature_discipline_Myhre.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_temperature_discipline_integral.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_temperature_discipline_integral.pkl index b609bddc5..fff4ce1e4 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_temperature_discipline_integral.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_temperature_discipline_integral.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_temperature_discipline_last_temperature.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_temperature_discipline_last_temperature.pkl index 2c4c4555a..9a29a7143 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_temperature_discipline_last_temperature.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_temperature_discipline_last_temperature.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_temperature_v2_discipline_DICE.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_temperature_v2_discipline_DICE.pkl index 9dc8c6f00..e469e9989 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_temperature_v2_discipline_DICE.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_temperature_v2_discipline_DICE.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_temperature_v2_discipline_Meinshausen.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_temperature_v2_discipline_Meinshausen.pkl index 7cd3cb674..9dd0e8e5f 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_temperature_v2_discipline_Meinshausen.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_temperature_v2_discipline_Meinshausen.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_temperature_v2_discipline_Myhre.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_temperature_v2_discipline_Myhre.pkl index b145d16d3..03ca12793 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_temperature_v2_discipline_Myhre.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_temperature_v2_discipline_Myhre.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_uranium_demand_variable_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_uranium_demand_variable_discipline.pkl index cfe61471e..ca4809aed 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_uranium_demand_variable_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_uranium_demand_variable_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_uranium_discipline.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_uranium_discipline.pkl index de0024e17..0ed5f5582 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_uranium_discipline.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_uranium_discipline.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_utility_discipline_welfare.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_utility_discipline_welfare.pkl index a648b2ae4..c928f42df 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_utility_discipline_welfare.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_utility_discipline_welfare.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobian_utility_discipline_welfare2.pkl b/climateeconomics/tests/jacobian_pkls/jacobian_utility_discipline_welfare2.pkl index a648b2ae4..c928f42df 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobian_utility_discipline_welfare2.pkl and b/climateeconomics/tests/jacobian_pkls/jacobian_utility_discipline_welfare2.pkl differ diff --git a/climateeconomics/tests/jacobian_pkls/jacobianc_agriculture_economy_disc.pkl b/climateeconomics/tests/jacobian_pkls/jacobianc_agriculture_economy_disc.pkl index 26ed38909..e3825558b 100644 Binary files a/climateeconomics/tests/jacobian_pkls/jacobianc_agriculture_economy_disc.pkl and b/climateeconomics/tests/jacobian_pkls/jacobianc_agriculture_economy_disc.pkl differ diff --git a/climateeconomics/tests/l0_test_agriculture_mix_discipline.py b/climateeconomics/tests/l0_test_agriculture_mix_discipline.py index 6903e4bac..de292c9f8 100644 --- a/climateeconomics/tests/l0_test_agriculture_mix_discipline.py +++ b/climateeconomics/tests/l0_test_agriculture_mix_discipline.py @@ -98,9 +98,9 @@ def setUp(self): deforestation_surface = np.linspace(10, 5, year_range) self.deforestation_surface_df = pd.DataFrame( {GlossaryCore.Years: self.years, "deforested_surface": deforestation_surface}) - forest_invest = np.linspace(5, 8, year_range) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: self.years, "forest_investment": forest_invest}) + reforestation_invest = np.linspace(5.0, 8.0, len(self.years)) + self.reforestation_investment_df = pd.DataFrame( + {GlossaryCore.Years: self.years, "reforestation_investment": reforestation_invest}) mw_invest = np.linspace(1, 4, year_range) uw_invest = np.linspace(0, 1, year_range) self.mw_invest_df = pd.DataFrame( diff --git a/climateeconomics/tests/l0_test_differentiable_model.py b/climateeconomics/tests/l0_test_differentiable_model.py index 3f976a87d..ca3821c40 100644 --- a/climateeconomics/tests/l0_test_differentiable_model.py +++ b/climateeconomics/tests/l0_test_differentiable_model.py @@ -30,7 +30,7 @@ def compute(self): z = self.inputs["z"] matrix = self.inputs["matrix"] - self.outputs["output1"] = agnp.sin(x) * agnp.cos(y) + z**2 + self.outputs["output1"] = agnp.sin(x) * agnp.cos(y) + z ** 2 self.outputs["output2"] = agnp.dot(matrix, agnp.array([x, y, z])) self.outputs["output3"] = {"a": x * y, "b": y * z, "c": x * z} diff --git a/climateeconomics/tests/l0_test_forest_v1_discipline.py b/climateeconomics/tests/l0_test_forest_v1_discipline.py deleted file mode 100644 index f3c93245a..000000000 --- a/climateeconomics/tests/l0_test_forest_v1_discipline.py +++ /dev/null @@ -1,113 +0,0 @@ -''' -Copyright 2022 Airbus SAS -Modifications on 2023/09/06-2023/11/03 Copyright 2023 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 unittest - -import numpy as np -import pandas as pd -from sostrades_core.execution_engine.execution_engine import ExecutionEngine - -from climateeconomics.core.core_forest.forest_v1 import Forest -from climateeconomics.glossarycore import GlossaryCore - - -class ForestTestCase(unittest.TestCase): - - def setUp(self): - ''' - Initialize third data needed for testing - ''' - self.year_start = GlossaryCore.YearStartDefault - self.year_end = 2055 - years = np.arange(self.year_start, self.year_end + 1, 1) - year_range = self.year_end - self.year_start + 1 - deforestation_surface = np.array(np.linspace(10, 100, year_range)) - self.deforestation_surface_df = pd.DataFrame( - {GlossaryCore.Years: years, "deforested_surface": deforestation_surface}) - self.CO2_per_ha = 4000 - # Mha - self.limit_deforestation_surface = 1000 - # GtCO2 - self.initial_emissions = 3.21 - forest_invest = np.linspace(2, 10, year_range) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, "forest_investment": forest_invest}) - self.reforestation_cost_per_ha = 3800 - - self.param = {GlossaryCore.YearStart: self.year_start, - GlossaryCore.YearEnd: self.year_end, - Forest.DEFORESTATION_SURFACE: self.deforestation_surface_df, - Forest.LIMIT_DEFORESTATION_SURFACE: self.limit_deforestation_surface, - Forest.CO2_PER_HA: self.CO2_per_ha, - Forest.INITIAL_CO2_EMISSIONS: self.initial_emissions, - Forest.REFORESTATION_INVESTMENT: self.forest_invest_df, - Forest.REFORESTATION_COST_PER_HA: self.reforestation_cost_per_ha, - GlossaryCore.CheckRangeBeforeRunBoolName: False, - } - - def test_forest_model(self): - ''' - Basique test of forest pyworld3 - Mainly check the overal run without value checks (will be done in another test) - ''' - - forest = Forest(self.param) - - forest.compute(self.param) - - def test_forest_discipline(self): - ''' - Check discipline setup and run - ''' - - name = 'Test' - model_name = 'forest' - ee = ExecutionEngine(name) - ns_dict = {'ns_public': f'{name}', - GlossaryCore.NS_WITNESS: f'{name}.{model_name}', - GlossaryCore.NS_FUNCTIONS: f'{name}.{model_name}', - 'ns_forest': f'{name}.{model_name}', - 'ns_invest': f'{name}.{model_name}' } - ee.ns_manager.add_ns_def(ns_dict) - - mod_path = 'climateeconomics.sos_wrapping.sos_wrapping_forest.forest_v1.forest_disc.ForestDiscipline' - 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() - - inputs_dict = {f'{name}.{GlossaryCore.YearStart}': self.year_start, - f'{name}.{GlossaryCore.YearEnd}': self.year_end, - f'{name}.{model_name}.{Forest.LIMIT_DEFORESTATION_SURFACE}': self.limit_deforestation_surface, - f'{name}.{model_name}.{Forest.DEFORESTATION_SURFACE}': self.deforestation_surface_df, - f'{name}.{model_name}.{Forest.CO2_PER_HA}': self.CO2_per_ha, - f'{name}.{model_name}.{Forest.INITIAL_CO2_EMISSIONS}': self.initial_emissions, - f'{name}.{model_name}.{Forest.REFORESTATION_INVESTMENT}': self.forest_invest_df, - f'{name}.{model_name}.{Forest.REFORESTATION_COST_PER_HA}': self.reforestation_cost_per_ha, - } - - ee.load_study_from_input_dict(inputs_dict) - - ee.execute() - - disc = ee.dm.get_disciplines_with_name( - f'{name}.{model_name}')[0] - filter = disc.get_chart_filter_list() - graph_list = disc.get_post_processing_list(filter) -# for graph in graph_list: -# graph.to_plotly().show() diff --git a/climateeconomics/tests/l0_test_forest_v2_discipline.py b/climateeconomics/tests/l0_test_forest_v2_discipline.py deleted file mode 100644 index a444f2867..000000000 --- a/climateeconomics/tests/l0_test_forest_v2_discipline.py +++ /dev/null @@ -1,535 +0,0 @@ -''' -Copyright 2022 Airbus SAS -Modifications on 2023/09/06-2023/11/03 Copyright 2023 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 unittest - -import numpy as np -import pandas as pd -from sostrades_core.execution_engine.execution_engine import ExecutionEngine - -from climateeconomics.core.core_forest.forest_v2 import Forest -from climateeconomics.glossarycore import GlossaryCore - - -class ForestTestCase(unittest.TestCase): - - def setUp(self): - ''' - Initialize third data needed for testing - ''' - self.year_start = GlossaryCore.YearStartDefault - self.year_end = 2050 - years = np.arange(self.year_start, self.year_end + 1, 1) - year_range = self.year_end - self.year_start + 1 - self.CO2_per_ha = 4000 - # GtCO2 - self.initial_emissions = -7.6 - forest_invest = np.linspace(45, 50, year_range) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, "forest_investment": forest_invest}) - self.reforestation_cost_per_ha = 13800 - - construction_delay = 3 - - self.invest_before_year_start = pd.DataFrame( - {'past_years': np.arange(-construction_delay, 0), - GlossaryCore.InvestmentsValue: np.array([1.135081] * construction_delay)}) - - mw_invest = np.linspace(10, 10, year_range) - self.mw_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: mw_invest}) - transport = np.linspace(7.6, 7.6, year_range) - self.transport_df = pd.DataFrame( - {GlossaryCore.Years: years, "transport": transport}) - self.margin = pd.DataFrame( - {GlossaryCore.Years: years, 'margin': np.ones(len(years)) * 110.0}) - self.initial_protected_forest_surface = 4 * 0.21 - self.initial_unmanaged_forest_surface = 4 - \ - 1.25 - self.initial_protected_forest_surface - deforest_invest = np.linspace(10, 5, year_range) - # case over deforestation - # deforest_invest = np.linspace(1000, 5000, year_range) - self.deforest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: deforest_invest}) - - # def test_forest_discipline_IEA_NZE_calibration(self): - # ''' - # Calibrating forest invests so that the energy production matches the IEA-NZE production - # ''' - # - # name = 'Test' - # model_name = 'forest' - # ee = ExecutionEngine(name) - # ns_dict = {'ns_public': f'{name}', - # GlossaryCore.NS_WITNESS: f'{name}.{model_name}', - # GlossaryCore.NS_FUNCTIONS: f'{name}.{model_name}', - # 'ns_forest': f'{name}.{model_name}', - # 'ns_agriculture': f'{name}.{model_name}', - # 'ns_invest': f'{name}.{model_name}'} - # - # ee.ns_manager.add_ns_def(ns_dict) - # - # mod_path = 'climateeconomics.sos_wrapping.sos_wrapping_agriculture.forest.forest_disc.ForestDiscipline' - # 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() - # - # self.year_start = GlossaryCore.YearStartDefault - # self.year_end = 2100 - # years = np.arange(self.year_start, self.year_end + 1, 1) - # year_range = self.year_end - self.year_start + 1 - # - # ''' - # Invests - # ''' - # # 38.6 [G$] Investments in deforestation in 2021 [1] - # # 1.16 [Mha/year] of deforestation in 2050 => NZE would require reducing deforestation by two‐thirds by 2050 [wrt 2020?] [tab crop ref 1 p.92] - # # assume same trend until deforestation reaches 0 since eventually to preserve biodiversity and forest surface, forest will be either - # # protected or managed - # deforest_invest = np.concatenate((np.linspace(38.6, 12.83, 30), 12.83 * np.ones(year_range - 30)), axis=0) #np.concatenate((np.linspace(38.6, 12.83, 30), np.linspace(11.98, 0, 15), np.zeros((year_range - 30 - 15))), axis=0) - # - # # 250 [Mha] of new forest to be planted by 2050 [crop tab ref 1 p 92] - # mw_invest = 1/3. * 0.027 * np.concatenate((np.linspace(5555., 8888., 20), 0. * np.ones(year_range - 20)), axis=0) # 3 years construction delay - # forest_invest = 0. * np.linspace(2, 10, year_range) - # ''' - # End of invests - # ''' - # self.forest_invest_df = pd.DataFrame( - # {GlossaryCore.Years: years, "forest_investment": forest_invest}) - # - # - # self.mw_invest_df = pd.DataFrame( - # {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: mw_invest}) - # - # # case over deforestation - # # deforest_invest = np.linspace(1000, 5000, year_range) - # self.deforest_invest_df = pd.DataFrame( - # {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: deforest_invest}) - # inputs_dict = {f'{name}.{GlossaryCore.YearStart}': self.year_start, - # f'{name}.{GlossaryCore.YearEnd}': self.year_end, - # f'{name}.{model_name}.{Forest.DEFORESTATION_INVESTMENT}': self.deforest_invest_df, - # f'{name}.{model_name}.{Forest.DEFORESTATION_COST_PER_HA}': 8000, - # f'{name}.{model_name}.{Forest.CO2_PER_HA}': self.CO2_per_ha, - # f'{name}.{model_name}.{Forest.INITIAL_CO2_EMISSIONS}': self.initial_emissions, - # f'{name}.{model_name}.{Forest.REFORESTATION_INVESTMENT}': self.forest_invest_df, - # f'{name}.{model_name}.{Forest.REFORESTATION_COST_PER_HA}': self.reforestation_cost_per_ha, - # f'{name}.{model_name}.managed_wood_initial_surface': 1.25 * 0.92, - # f'{name}.{model_name}.managed_wood_invest_before_year_start': self.invest_before_year_start, - # f'{name}.{model_name}.managed_wood_investment': self.mw_invest_df, - # f'{name}.{model_name}.transport_cost': self.transport_df, - # f'{name}.{model_name}.margin': self.margin, - # f'{name}.{model_name}.initial_unmanaged_forest_surface': self.initial_unmanaged_forest_surface, - # f'{name}.{model_name}.protected_forest_surface': self.initial_protected_forest_surface, - # f'{name}.{model_name}.{GlossaryCore.CheckRangeBeforeRunBoolName}': False, - # } - # - # ee.load_study_from_input_dict(inputs_dict) - # - # ee.execute() - # - # disc = ee.dm.get_disciplines_with_name( - # f'{name}.{model_name}')[0] - # filter = disc.get_chart_filter_list() - # graph_list = disc.get_post_processing_list(filter) - # - # for graph in graph_list: - # graph.to_plotly().show() - - def test_forest_discipline_low_deforestation(self): - ''' - Check discipline setup and run - ''' - - name = 'Test' - model_name = 'forest' - ee = ExecutionEngine(name) - ns_dict = {'ns_public': f'{name}', - GlossaryCore.NS_WITNESS: f'{name}.{model_name}', - GlossaryCore.NS_FUNCTIONS: f'{name}.{model_name}', - 'ns_forest': f'{name}.{model_name}', - 'ns_agriculture': f'{name}.{model_name}', - 'ns_invest': f'{name}.{model_name}'} - - ee.ns_manager.add_ns_def(ns_dict) - - mod_path = 'climateeconomics.sos_wrapping.sos_wrapping_agriculture.forest.forest_disc.ForestDiscipline' - 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() - - self.year_start = GlossaryCore.YearStartDefault - self.year_end = 2050 - years = np.arange(self.year_start, self.year_end + 1, 1) - year_range = self.year_end - self.year_start + 1 - forest_invest = np.linspace(2, 10, year_range) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, "forest_investment": forest_invest}) - - mw_invest = np.linspace(10, 10, year_range) - self.mw_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: mw_invest}) - deforest_invest = np.linspace(10, 5, year_range) - # case over deforestation - # deforest_invest = np.linspace(1000, 5000, year_range) - self.deforest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: deforest_invest}) - inputs_dict = {f'{name}.{GlossaryCore.YearStart}': self.year_start, - f'{name}.{GlossaryCore.YearEnd}': self.year_end, - f'{name}.{model_name}.{Forest.DEFORESTATION_INVESTMENT}': self.deforest_invest_df, - f'{name}.{model_name}.{Forest.DEFORESTATION_COST_PER_HA}': 8000, - f'{name}.{model_name}.{Forest.CO2_PER_HA}': self.CO2_per_ha, - f'{name}.{model_name}.{Forest.INITIAL_CO2_EMISSIONS}': self.initial_emissions, - f'{name}.{model_name}.{Forest.REFORESTATION_INVESTMENT}': self.forest_invest_df, - f'{name}.{model_name}.{Forest.REFORESTATION_COST_PER_HA}': self.reforestation_cost_per_ha, - f'{name}.{model_name}.managed_wood_initial_surface': 1.25 * 0.92, - f'{name}.{model_name}.managed_wood_invest_before_year_start': self.invest_before_year_start, - f'{name}.{model_name}.managed_wood_investment': self.mw_invest_df, - f'{name}.{model_name}.transport_cost': self.transport_df, - f'{name}.{model_name}.margin': self.margin, - f'{name}.{model_name}.initial_unmanaged_forest_surface': self.initial_unmanaged_forest_surface, - f'{name}.{model_name}.protected_forest_surface': self.initial_protected_forest_surface, - } - - ee.load_study_from_input_dict(inputs_dict) - - ee.execute() - - disc = ee.dm.get_disciplines_with_name( - f'{name}.{model_name}')[0] - filter = disc.get_chart_filter_list() - graph_list = disc.get_post_processing_list(filter) - - # for graph in graph_list: - # graph.to_plotly().show() - - def test_forest_discipline_high_deforestation(self): - ''' - Check discipline setup and run - ''' - - name = 'Test' - model_name = 'forest' - ee = ExecutionEngine(name) - ns_dict = {'ns_public': f'{name}', - GlossaryCore.NS_WITNESS: f'{name}.{model_name}', - GlossaryCore.NS_FUNCTIONS: f'{name}.{model_name}', - 'ns_forest': f'{name}.{model_name}', - 'ns_agriculture': f'{name}.{model_name}', - 'ns_invest': f'{name}.{model_name}'} - - ee.ns_manager.add_ns_def(ns_dict) - - mod_path = 'climateeconomics.sos_wrapping.sos_wrapping_agriculture.forest.forest_disc.ForestDiscipline' - 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() - - self.year_start = GlossaryCore.YearStartDefault - self.year_end = 2050 - years = np.arange(self.year_start, self.year_end + 1, 1) - year_range = self.year_end - self.year_start + 1 - forest_invest = np.linspace(2, 10, year_range) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, "forest_investment": forest_invest}) - - mw_invest = np.linspace(300, 10, year_range) - self.mw_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: mw_invest}) - self.deforest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: np.linspace(2000., 1., len(years))}) - inputs_dict = {f'{name}.{GlossaryCore.YearStart}': self.year_start, - f'{name}.{GlossaryCore.YearEnd}': self.year_end, - f'{name}.{model_name}.{Forest.DEFORESTATION_INVESTMENT}': self.deforest_invest_df, - f'{name}.{model_name}.{Forest.DEFORESTATION_COST_PER_HA}': 8000, - f'{name}.{model_name}.{Forest.CO2_PER_HA}': self.CO2_per_ha, - f'{name}.{model_name}.{Forest.INITIAL_CO2_EMISSIONS}': self.initial_emissions, - f'{name}.{model_name}.{Forest.REFORESTATION_INVESTMENT}': self.forest_invest_df, - f'{name}.{model_name}.{Forest.REFORESTATION_COST_PER_HA}': self.reforestation_cost_per_ha, - f'{name}.{model_name}.managed_wood_initial_surface': 1.25 * 0.92, - f'{name}.{model_name}.managed_wood_invest_before_year_start': self.invest_before_year_start, - f'{name}.{model_name}.managed_wood_investment': self.mw_invest_df, - f'{name}.{model_name}.transport_cost': self.transport_df, - f'{name}.{model_name}.margin': self.margin, - f'{name}.{model_name}.initial_unmanaged_forest_surface': self.initial_unmanaged_forest_surface, - f'{name}.{model_name}.protected_forest_surface': self.initial_protected_forest_surface, - } - - ee.load_study_from_input_dict(inputs_dict) - - ee.execute() - - disc = ee.dm.get_disciplines_with_name( - f'{name}.{model_name}')[0] - filter = disc.get_chart_filter_list() - graph_list = disc.get_post_processing_list(filter) - - # for graph in graph_list: - # graph.to_plotly().show() - - def test_forest_discipline_high_lost_capital(self): - ''' - Check discipline setup and run - ''' - - name = 'Test' - model_name = 'forest' - ee = ExecutionEngine(name) - ns_dict = {'ns_public': f'{name}', - GlossaryCore.NS_WITNESS: f'{name}.{model_name}', - GlossaryCore.NS_FUNCTIONS: f'{name}.{model_name}', - 'ns_forest': f'{name}.{model_name}', - 'ns_agriculture': f'{name}.{model_name}', - 'ns_invest': f'{name}.{model_name}'} - - ee.ns_manager.add_ns_def(ns_dict) - - mod_path = 'climateeconomics.sos_wrapping.sos_wrapping_agriculture.forest.forest_disc.ForestDiscipline' - 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() - - self.year_start = GlossaryCore.YearStartDefault - self.year_end = 2080 - years = np.arange(self.year_start, self.year_end + 1, 1) - year_range = self.year_end - self.year_start + 1 - construction_delay = 3 - self.invest_before_year_start = pd.DataFrame( - {'past_years': np.arange(-construction_delay, 0), - GlossaryCore.InvestmentsValue: np.array([1.135081] * construction_delay)}) - self.mw_initial_production = 23000 # TWh - - mw_invest = np.linspace(10, 10, year_range) - self.mw_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: mw_invest}) - transport = np.linspace(7.6, 7.6, year_range) - self.transport_df = pd.DataFrame( - {GlossaryCore.Years: years, "transport": transport}) - self.margin = pd.DataFrame( - {GlossaryCore.Years: years, 'margin': np.ones(len(years)) * 110.0}) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, - 'forest_investment': np.linspace(1000., 1., len(years))}) - - mw_invest = np.linspace(300, 10, year_range) - self.mw_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: mw_invest}) - self.deforest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: 40.}) - inputs_dict = {f'{name}.{GlossaryCore.YearStart}': self.year_start, - f'{name}.{GlossaryCore.YearEnd}': self.year_end, - f'{name}.{model_name}.{Forest.DEFORESTATION_INVESTMENT}': self.deforest_invest_df, - f'{name}.{model_name}.{Forest.DEFORESTATION_COST_PER_HA}': 8000, - f'{name}.{model_name}.{Forest.CO2_PER_HA}': self.CO2_per_ha, - f'{name}.{model_name}.{Forest.INITIAL_CO2_EMISSIONS}': self.initial_emissions, - f'{name}.{model_name}.{Forest.REFORESTATION_INVESTMENT}': self.forest_invest_df, - f'{name}.{model_name}.{Forest.REFORESTATION_COST_PER_HA}': self.reforestation_cost_per_ha, - f'{name}.{model_name}.managed_wood_initial_surface': 1.25 * 0.92, - f'{name}.{model_name}.managed_wood_invest_before_year_start': self.invest_before_year_start, - f'{name}.{model_name}.managed_wood_investment': self.mw_invest_df, - f'{name}.{model_name}.transport_cost': self.transport_df, - f'{name}.{model_name}.margin': self.margin, - f'{name}.{model_name}.initial_unmanaged_forest_surface': self.initial_unmanaged_forest_surface, - f'{name}.{model_name}.protected_forest_surface': self.initial_protected_forest_surface, - } - - ee.load_study_from_input_dict(inputs_dict) - - ee.execute() - - disc = ee.dm.get_disciplines_with_name( - f'{name}.{model_name}')[0] - filter = disc.get_chart_filter_list() - graph_list = disc.get_post_processing_list(filter) - - # for graph in graph_list: - # graph.to_plotly().show() - - def test_forest_discipline_low_lost_capital(self): - ''' - Check discipline setup and run - ''' - - name = 'Test' - model_name = 'forest' - ee = ExecutionEngine(name) - ns_dict = {'ns_public': f'{name}', - GlossaryCore.NS_WITNESS: f'{name}.{model_name}', - GlossaryCore.NS_FUNCTIONS: f'{name}.{model_name}', - 'ns_forest': f'{name}.{model_name}', - 'ns_agriculture': f'{name}.{model_name}', - 'ns_invest': f'{name}.{model_name}'} - - ee.ns_manager.add_ns_def(ns_dict) - - mod_path = 'climateeconomics.sos_wrapping.sos_wrapping_agriculture.forest.forest_disc.ForestDiscipline' - 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() - - self.year_start = GlossaryCore.YearStartDefault - self.year_end = 2080 - years = np.arange(self.year_start, self.year_end + 1, 1) - year_range = self.year_end - self.year_start + 1 - construction_delay = 3 - self.invest_before_year_start = pd.DataFrame( - {'past_years': np.arange(-construction_delay, 0), - GlossaryCore.InvestmentsValue: np.array([1.135081] * construction_delay)}) - self.mw_initial_production = 23000 # TWh - - mw_invest = np.linspace(10, 10, year_range) - self.mw_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: mw_invest}) - transport = np.linspace(7.6, 7.6, year_range) - self.transport_df = pd.DataFrame( - {GlossaryCore.Years: years, "transport": transport}) - self.margin = pd.DataFrame( - {GlossaryCore.Years: years, 'margin': np.ones(len(years)) * 110.0}) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, - 'forest_investment': np.linspace(100., 1., len(years))}) - - mw_invest = np.linspace(300, 10, year_range) - self.mw_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: mw_invest}) - self.deforest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: 40.}) - inputs_dict = {f'{name}.{GlossaryCore.YearStart}': self.year_start, - f'{name}.{GlossaryCore.YearEnd}': self.year_end, - f'{name}.{model_name}.{Forest.DEFORESTATION_INVESTMENT}': self.deforest_invest_df, - f'{name}.{model_name}.{Forest.DEFORESTATION_COST_PER_HA}': 8000, - f'{name}.{model_name}.{Forest.CO2_PER_HA}': self.CO2_per_ha, - f'{name}.{model_name}.{Forest.INITIAL_CO2_EMISSIONS}': self.initial_emissions, - f'{name}.{model_name}.{Forest.REFORESTATION_INVESTMENT}': self.forest_invest_df, - f'{name}.{model_name}.{Forest.REFORESTATION_COST_PER_HA}': self.reforestation_cost_per_ha, - f'{name}.{model_name}.managed_wood_initial_surface': 1.25 * 0.92, - f'{name}.{model_name}.managed_wood_invest_before_year_start': self.invest_before_year_start, - f'{name}.{model_name}.managed_wood_investment': self.mw_invest_df, - f'{name}.{model_name}.transport_cost': self.transport_df, - f'{name}.{model_name}.margin': self.margin, - f'{name}.{model_name}.initial_unmanaged_forest_surface': self.initial_unmanaged_forest_surface, - f'{name}.{model_name}.protected_forest_surface': self.initial_protected_forest_surface, - } - - ee.load_study_from_input_dict(inputs_dict) - - ee.execute() - - disc = ee.dm.get_disciplines_with_name( - f'{name}.{model_name}')[0] - filter = disc.get_chart_filter_list() - graph_list = disc.get_post_processing_list(filter) - - # for graph in graph_list: - # graph.to_plotly().show() - - def test_forest_discipline_equal_invests(self): - ''' - Check discipline setup and run - ''' - - name = 'Test' - model_name = 'forest' - ee = ExecutionEngine(name) - ns_dict = {'ns_public': f'{name}', - GlossaryCore.NS_WITNESS: f'{name}.{model_name}', - GlossaryCore.NS_FUNCTIONS: f'{name}.{model_name}', - 'ns_forest': f'{name}.{model_name}', - 'ns_agriculture': f'{name}.{model_name}', - 'ns_invest': f'{name}.{model_name}'} - - ee.ns_manager.add_ns_def(ns_dict) - - mod_path = 'climateeconomics.sos_wrapping.sos_wrapping_agriculture.forest.forest_disc.ForestDiscipline' - 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() - - self.year_start =GlossaryCore.YearStartDefault - self.year_end = 2080 - years = np.arange(self.year_start, self.year_end + 1, 1) - year_range = self.year_end - self.year_start + 1 - construction_delay = 3 - self.invest_before_year_start = pd.DataFrame( - {'past_years': np.arange(-construction_delay, 0), - GlossaryCore.InvestmentsValue: np.array([1.135081] * construction_delay)}) - self.mw_initial_production = 23000 # TWh - transport = np.linspace(7.6, 7.6, year_range) - self.transport_df = pd.DataFrame( - {GlossaryCore.Years: years, "transport": transport}) - self.margin = pd.DataFrame( - {GlossaryCore.Years: years, 'margin': np.ones(len(years)) * 110.0}) - forest_invest = np.linspace(10, 50, year_range) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, "forest_investment": forest_invest}) - - mw_invest = np.linspace(10, 10, year_range) - self.mw_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: mw_invest}) - deforest_invest = np.linspace(5.7971, 28.98550, year_range) - # case over deforestation - # deforest_invest = np.linspace(1000, 5000, year_range) - self.deforest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: deforest_invest}) - - mw_invest = np.linspace(300, 10, year_range) - self.mw_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: mw_invest}) - - inputs_dict = {f'{name}.{GlossaryCore.YearStart}': self.year_start, - f'{name}.{GlossaryCore.YearEnd}': self.year_end, - f'{name}.{model_name}.{Forest.DEFORESTATION_INVESTMENT}': self.deforest_invest_df, - f'{name}.{model_name}.{Forest.DEFORESTATION_COST_PER_HA}': 8000, - f'{name}.{model_name}.{Forest.CO2_PER_HA}': self.CO2_per_ha, - f'{name}.{model_name}.{Forest.INITIAL_CO2_EMISSIONS}': self.initial_emissions, - f'{name}.{model_name}.{Forest.REFORESTATION_INVESTMENT}': self.forest_invest_df, - f'{name}.{model_name}.{Forest.REFORESTATION_COST_PER_HA}': self.reforestation_cost_per_ha, - f'{name}.{model_name}.managed_wood_initial_surface': 1.25 * 0.92, - f'{name}.{model_name}.managed_wood_invest_before_year_start': self.invest_before_year_start, - f'{name}.{model_name}.managed_wood_investment': self.mw_invest_df, - f'{name}.{model_name}.transport_cost': self.transport_df, - f'{name}.{model_name}.margin': self.margin, - f'{name}.{model_name}.initial_unmanaged_forest_surface': self.initial_unmanaged_forest_surface, - f'{name}.{model_name}.protected_forest_surface': self.initial_protected_forest_surface, - } - - ee.load_study_from_input_dict(inputs_dict) - - ee.execute() - - disc = ee.dm.get_disciplines_with_name( - f'{name}.{model_name}')[0] - filter = disc.get_chart_filter_list() - graph_list = disc.get_post_processing_list(filter) -# for graph in graph_list: -# graph.to_plotly().show() diff --git a/climateeconomics/tests/l0_test_iea_data_preparation_discipline.py b/climateeconomics/tests/l0_test_iea_data_preparation_discipline.py index 4d2197520..eeb9987b8 100644 --- a/climateeconomics/tests/l0_test_iea_data_preparation_discipline.py +++ b/climateeconomics/tests/l0_test_iea_data_preparation_discipline.py @@ -60,9 +60,6 @@ def test_execute(self): energy_production_df = pd.DataFrame({Glossary.Years: years, Glossary.TotalProductionValue: [40, 70, 80, 10]}) - energy_consumption_df = pd.DataFrame({Glossary.Years: years, - Glossary.FinalConsumptionValue: [40, 70, 80, 10]}) - population_df = pd.DataFrame({Glossary.Years: years, Glossary.PopulationValue: [8, 8.2, 8.3, 8]}) @@ -94,7 +91,6 @@ def test_execute(self): f'{self.name}.{self.model_name}.{Glossary.EconomicsDfValue}': GDP_df, f'{self.name}.{self.model_name}.{Glossary.CO2TaxesValue}': CO2_tax_df, f'{self.name}.{self.model_name}.{Glossary.EnergyProductionValue}': energy_production_df, - f'{self.name}.{self.model_name}.{Glossary.EnergyFinalConsumptionName}': energy_consumption_df, f'{self.name}.{self.model_name}.{Glossary.TemperatureDfValue}': temperature_df, f'{self.name}.{self.model_name}.{Glossary.PopulationDfValue}': population_df, f'{self.name}.{self.model_name}.{LandUseV2.LAND_SURFACE_DETAIL_DF}': land_use_df, @@ -125,11 +121,11 @@ def test_execute(self): assert gdp_interpolated.loc[ gdp_interpolated[Glossary.Years].isin(years), f"{Glossary.OutputNetOfDamage} [T$]"].tolist() == GDP_values # check that the value at 2035 is the expected : 2030 : 140, 2040: 145 => 2035 should be equal to (140+145)/2 - expected_value_2035 = (140 + 145) / 2 + expected_value_2035 = (140+145)/2 assert gdp_interpolated.loc[gdp_interpolated[Glossary.Years] == 2035, f"{Glossary.OutputNetOfDamage} [T$]"].values[0] == expected_value_2035 filter = disc.get_chart_filter_list() graph_list = disc.get_post_processing_list(filter) for graph in graph_list: - # graph.to_plotly().show() + #graph.to_plotly().show() pass diff --git a/climateeconomics/tests/l0_test_ipcc_ssp_comparison.py b/climateeconomics/tests/l0_test_ipcc_ssp_comparison.py index a7d6b9ad9..a3f03caf4 100644 --- a/climateeconomics/tests/l0_test_ipcc_ssp_comparison.py +++ b/climateeconomics/tests/l0_test_ipcc_ssp_comparison.py @@ -50,7 +50,7 @@ def setUp(self): for values_dict_i in values_dict: self.ee.load_study_from_input_dict(values_dict_i) - self.ee.load_study_from_input_dict({f'{self.study_name}.sub_mda_class': 'MDAGaussSeidel', + self.ee.load_study_from_input_dict({f'{self.study_name}.inner_mda_name': 'MDAGaussSeidel', f'{self.study_name}.max_mda_iter': 2}) def test_ssps_scenario_plots(self): diff --git a/climateeconomics/tests/l0_year_start.py b/climateeconomics/tests/l0_year_start.py index ae3b454c7..c4faa8d99 100644 --- a/climateeconomics/tests/l0_year_start.py +++ b/climateeconomics/tests/l0_year_start.py @@ -88,8 +88,8 @@ def _test_year_witness_core(self, year: int) -> list[str]: missing_variables.append(attribute_name) # forest invest before year start : - is_available = DatabaseWitnessCore.get_forest_invest_before_year_start(year_start=year, construction_delay=3, - is_available_at_year=True) + is_available = DatabaseWitnessCore.get_reforestation_invest_before_year_start(year_start=year, construction_delay=3, + is_available_at_year=True) if not is_available: missing_variables.append("Forest.managed_wood_invest_before_year_start") diff --git a/climateeconomics/tests/l1_test_agriculture_economy_discipline.py b/climateeconomics/tests/l1_test_agriculture_economy_discipline.py index dcfff8a70..327a4bbd7 100644 --- a/climateeconomics/tests/l1_test_agriculture_economy_discipline.py +++ b/climateeconomics/tests/l1_test_agriculture_economy_discipline.py @@ -130,6 +130,7 @@ def test_agriculture_economy_discipline(self): ''' Check discipline setup and run ''' + self.override_dump_jacobian = 1 self.ee.load_study_from_input_dict(self.inputs_dict) self.ee.execute() @@ -141,7 +142,7 @@ def test_agriculture_economy_discipline(self): for graph in graph_list: #graph.to_plotly().show() pass - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobianc_agriculture_economy_disc.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data=disc_techno.local_data, inputs=self.coupling_inputs, diff --git a/climateeconomics/tests/l1_test_crop_discipline_2.py b/climateeconomics/tests/l1_test_crop_discipline_2.py index 1da752e6b..769916e10 100644 --- a/climateeconomics/tests/l1_test_crop_discipline_2.py +++ b/climateeconomics/tests/l1_test_crop_discipline_2.py @@ -143,7 +143,9 @@ def test_crop_discipline_2(self): for graph in graph_list: #graph.to_plotly().show() pass - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline + #self.override_dump_jacobian = 1 self.check_jacobian(location=dirname(__file__), filename='jacobian_crop_discipline_2.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data=disc_techno.local_data, inputs=self.coupling_inputs, diff --git a/climateeconomics/tests/l1_test_dice_mda_prerun.py b/climateeconomics/tests/l1_test_dice_mda_prerun.py index 03a49741b..a7f1110f4 100644 --- a/climateeconomics/tests/l1_test_dice_mda_prerun.py +++ b/climateeconomics/tests/l1_test_dice_mda_prerun.py @@ -84,7 +84,8 @@ def test_execute(self): 'adj_backstop_cost': data, 'abatecost': data, 'marg_abatecost': data, - 'carbon_price': data,}, + 'carbon_price': data, + 'base_carbon_price': data,}, index=np.arange(usecase.year_start, usecase.year_end + 1)) dice_input[f"{usecase.study_name}.{GlossaryCore.DamageDfValue}"] = df diff --git a/climateeconomics/tests/l1_test_gradient_agriculture_discipline.py b/climateeconomics/tests/l1_test_gradient_agriculture_discipline.py index 43f0aab9f..d03c366bf 100644 --- a/climateeconomics/tests/l1_test_gradient_agriculture_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_agriculture_discipline.py @@ -136,7 +136,7 @@ def test_agriculture_discipline_analytic_grad(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_agriculture_discipline.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step',local_data=disc_techno.local_data, diff --git a/climateeconomics/tests/l1_test_gradient_agriculture_ghgemissions_discipline.py b/climateeconomics/tests/l1_test_gradient_agriculture_ghgemissions_discipline.py index 7be88fbcf..7009b32e9 100644 --- a/climateeconomics/tests/l1_test_gradient_agriculture_ghgemissions_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_agriculture_ghgemissions_discipline.py @@ -81,7 +81,7 @@ def test_carbon_emissions_analytic_grad(self): self.ee.load_study_from_input_dict(values_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_agriculture_ghg_emission_discipline.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data = disc_techno.local_data, diff --git a/climateeconomics/tests/l1_test_gradient_agriculture_mix_discipline.py b/climateeconomics/tests/l1_test_gradient_agriculture_mix_discipline.py index 7cd6de18d..c25c4675f 100644 --- a/climateeconomics/tests/l1_test_gradient_agriculture_mix_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_agriculture_mix_discipline.py @@ -124,9 +124,9 @@ def test_agriculture_discipline_mix_analytic_grad(self): deforestation_surface = np.linspace(10, 5, year_range) self.deforestation_surface_df = pd.DataFrame( {GlossaryCore.Years: self.years, "deforested_surface": deforestation_surface}) - forest_invest = np.linspace(5, 8, year_range) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: self.years, "forest_investment": forest_invest}) + reforestation_invest = np.linspace(5.0, 8.0, len(self.years)) + self.reforestation_investment_df = pd.DataFrame( + {GlossaryCore.Years: self.years, "reforestation_investment": reforestation_invest}) mw_invest = np.linspace(1, 4, year_range) uw_invest = np.linspace(0, 1, year_range) self.mw_invest_df = pd.DataFrame( @@ -221,7 +221,7 @@ def test_agriculture_discipline_mix_analytic_grad(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_agriculture_mix_discipline.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data = disc_techno.local_data, diff --git a/climateeconomics/tests/l1_test_gradient_coal_resource_discipline.py b/climateeconomics/tests/l1_test_gradient_coal_resource_discipline.py index 7b3a15330..66ff3043c 100644 --- a/climateeconomics/tests/l1_test_gradient_coal_resource_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_coal_resource_discipline.py @@ -91,7 +91,7 @@ def test_coal_resource_analytic_grad(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_coal_discipline.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step',local_data = disc_techno.local_data, @@ -134,7 +134,7 @@ def test_coal_resource_damand_variable_analytic_grad(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_coal_demand_variable_discipline.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step',local_data = disc_techno.local_data, diff --git a/climateeconomics/tests/l1_test_gradient_coarse.py b/climateeconomics/tests/l1_test_gradient_coarse.py index 5d3e9d232..c9a0afd0e 100644 --- a/climateeconomics/tests/l1_test_gradient_coarse.py +++ b/climateeconomics/tests/l1_test_gradient_coarse.py @@ -94,7 +94,7 @@ def test_01_coarse_clean_energy_simple_techno_discipline_jacobian(self): coupled_inputs = [] for key in mda_data_input_dict[self.techno_name].keys(): # Modify namespace of input 'key' if needed - if key in ['linearization_mode', 'cache_type', 'cache_file_path', 'sub_mda_class', + if key in ['linearization_mode', 'cache_type', 'cache_file_path', 'inner_mda_name', 'max_mda_iter', 'n_processes', 'chain_linearize', 'tolerance', 'use_lu_fact', 'warm_start', 'acceleration', 'warm_start_threshold', 'n_subcouplings_parallel', 'max_mda_iter_gs', 'relax_factor', 'epsilon0', @@ -136,7 +136,7 @@ def test_01_coarse_clean_energy_simple_techno_discipline_jacobian(self): self.ee.execute() disc = self.ee.dm.get_disciplines_with_name( - f'{self.name}.{self.techno_name}')[0].mdo_discipline_wrapp.mdo_discipline + f'{self.name}.{self.techno_name}')[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename=f'jacobian_{self.techno_name}.pkl', discipline=disc, step=1.0e-18, derr_approx='complex_step', threshold=1e-5, @@ -176,7 +176,7 @@ def test_02_coarse_fossil_techno_discipline_jacobian(self): coupled_inputs = [] for key in mda_data_input_dict[self.techno_name].keys(): # Modify namespace of input 'key' if needed - if key in ['linearization_mode', 'cache_type', 'cache_file_path', 'sub_mda_class', + if key in ['linearization_mode', 'cache_type', 'cache_file_path', 'inner_mda_name', 'max_mda_iter', 'n_processes', 'chain_linearize', 'tolerance', 'use_lu_fact', 'warm_start', 'acceleration', 'warm_start_threshold', 'n_subcouplings_parallel', 'max_mda_iter_gs', 'relax_factor', 'epsilon0', @@ -218,7 +218,7 @@ def test_02_coarse_fossil_techno_discipline_jacobian(self): self.ee.execute() disc = self.ee.dm.get_disciplines_with_name( - f'{self.name}.{self.techno_name}')[0].mdo_discipline_wrapp.mdo_discipline + f'{self.name}.{self.techno_name}')[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename=f'jacobian_{self.techno_name}.pkl', discipline=disc, step=1.0e-18, derr_approx='complex_step', threshold=1e-5, @@ -258,7 +258,7 @@ def test_03_coarse_dac_techno_discipline_jacobian(self): coupled_inputs = [] for key in mda_data_input_dict[self.techno_name].keys(): # Modify namespace of input 'key' if needed - if key in ['linearization_mode', 'cache_type', 'cache_file_path', 'sub_mda_class', + if key in ['linearization_mode', 'cache_type', 'cache_file_path', 'inner_mda_name', 'max_mda_iter', 'n_processes', 'chain_linearize', 'tolerance', 'use_lu_fact', 'warm_start', 'acceleration', 'warm_start_threshold', 'n_subcouplings_parallel', 'max_mda_iter_gs', 'relax_factor', 'epsilon0', @@ -300,7 +300,7 @@ def test_03_coarse_dac_techno_discipline_jacobian(self): self.ee.execute() disc = self.ee.dm.get_disciplines_with_name( - f'{self.name}.{self.techno_name}')[0].mdo_discipline_wrapp.mdo_discipline + f'{self.name}.{self.techno_name}')[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename=f'jacobian_{self.techno_name}.pkl', discipline=disc, step=1.0e-18, derr_approx='complex_step', threshold=1e-5, @@ -341,7 +341,7 @@ def test_04_coarse_flue_gas_capture_techno_discipline_jacobian(self): coupled_inputs = [] for key in mda_data_input_dict[self.techno_name].keys(): # Modify namespace of input 'key' if needed - if key in ['linearization_mode', 'cache_type', 'cache_file_path', 'sub_mda_class', + if key in ['linearization_mode', 'cache_type', 'cache_file_path', 'inner_mda_name', 'max_mda_iter', 'n_processes', 'chain_linearize', 'tolerance', 'use_lu_fact', 'warm_start', 'acceleration', 'warm_start_threshold', 'n_subcouplings_parallel', 'max_mda_iter_gs', 'relax_factor', 'epsilon0', @@ -384,7 +384,7 @@ def test_04_coarse_flue_gas_capture_techno_discipline_jacobian(self): self.ee.execute() disc = self.ee.dm.get_disciplines_with_name( - f'{self.name}.{self.techno_name}')[0].mdo_discipline_wrapp.mdo_discipline + f'{self.name}.{self.techno_name}')[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename=f'jacobian_{self.techno_name}.pkl', discipline=disc, step=1.0e-18, derr_approx='complex_step', threshold=1e-5, @@ -424,7 +424,7 @@ def test_05_coarse_carbon_storage_techno_discipline_jacobian(self): coupled_inputs = [] for key in mda_data_input_dict[self.techno_name].keys(): # Modify namespace of input 'key' if needed - if key in ['linearization_mode', 'cache_type', 'cache_file_path', 'sub_mda_class', + if key in ['linearization_mode', 'cache_type', 'cache_file_path', 'inner_mda_name', 'max_mda_iter', 'n_processes', 'chain_linearize', 'tolerance', 'use_lu_fact', 'warm_start', 'acceleration', 'warm_start_threshold', 'n_subcouplings_parallel', 'max_mda_iter_gs', 'relax_factor', 'epsilon0', @@ -466,7 +466,7 @@ def test_05_coarse_carbon_storage_techno_discipline_jacobian(self): self.ee.execute() disc = self.ee.dm.get_disciplines_with_name( - f'{self.name}.{self.techno_name}')[0].mdo_discipline_wrapp.mdo_discipline + f'{self.name}.{self.techno_name}')[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename=f'jacobian_{self.techno_name}.pkl', discipline=disc, step=1.0e-18, derr_approx='complex_step', threshold=1e-5, @@ -545,7 +545,7 @@ def test_06_coarse_clean_energy_stream_discipline_jacobian(self): self.ee.execute() disc = self.ee.dm.get_disciplines_with_name( - f'{self.name}.{self.energy_name}')[0].mdo_discipline_wrapp.mdo_discipline + f'{self.name}.{self.energy_name}')[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename=f'jacobian_{self.energy_name}.pkl', discipline=disc, step=1.0e-18, derr_approx='complex_step', threshold=1e-5, @@ -623,7 +623,7 @@ def test_07_coarse_fossil_stream_discipline_jacobian(self): self.ee.execute() disc = self.ee.dm.get_disciplines_with_name( - f'{self.name}.{self.energy_name}')[0].mdo_discipline_wrapp.mdo_discipline + f'{self.name}.{self.energy_name}')[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename=f'jacobian_{self.energy_name}.pkl', discipline=disc, step=1.0e-18, derr_approx='complex_step', threshold=1e-5, @@ -698,7 +698,7 @@ def test_08_coarse_energymix_discipline_jacobian(self): self.ee.execute() disc = self.ee.dm.get_disciplines_with_name( - f'{self.name}.{self.energy_name}')[0].mdo_discipline_wrapp.mdo_discipline + f'{self.name}.{self.energy_name}')[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename=f'jacobian_{self.energy_name}.pkl', discipline=disc, step=1.0e-15, derr_approx='complex_step', threshold=1e-5, diff --git a/climateeconomics/tests/l1_test_gradient_copper_resource_discipline.py b/climateeconomics/tests/l1_test_gradient_copper_resource_discipline.py index f1e1f0493..33910d5a4 100644 --- a/climateeconomics/tests/l1_test_gradient_copper_resource_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_copper_resource_discipline.py @@ -104,7 +104,7 @@ def test_copper_resource_analytic_grad(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_copper_discipline.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data = disc_techno.local_data, @@ -150,7 +150,7 @@ def test_copper_resource_damand_variable_analytic_grad(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_copper_demand_variable_discipline.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data = disc_techno.local_data, diff --git a/climateeconomics/tests/l1_test_gradient_crop_discipline.py b/climateeconomics/tests/l1_test_gradient_crop_discipline.py index da9457dad..4dc98d5e4 100644 --- a/climateeconomics/tests/l1_test_gradient_crop_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_crop_discipline.py @@ -184,7 +184,7 @@ def test_agriculture_discipline_analytic_grad(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_crop_discipline.pkl', discipline=disc_techno, local_data=disc_techno.local_data, step=1e-15, derr_approx='complex_step', diff --git a/climateeconomics/tests/l1_test_gradient_damage_discipline.py b/climateeconomics/tests/l1_test_gradient_damage_discipline.py index 4992114a7..ba6f9bcbc 100644 --- a/climateeconomics/tests/l1_test_gradient_damage_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_damage_discipline.py @@ -104,7 +104,7 @@ def test_damage_analytic_grad(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_damage_discipline.pkl', discipline=disc_techno, local_data=disc_techno.local_data, @@ -147,7 +147,7 @@ def test_damage_analytic_grad_wo_damage_on_climate(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_damage_discipline_wo_damage_on_climate.pkl', discipline=disc_techno, local_data=disc_techno.local_data, @@ -180,7 +180,7 @@ def test_damage_analytic_grad_dev_formula(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_damage_discipline_dev_formula.pkl', discipline=disc_techno, local_data=disc_techno.local_data, @@ -212,7 +212,7 @@ def test_damage_analytic_grad_dice(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_damage_discipline_dice.pkl', discipline=disc_techno, local_data=disc_techno.local_data, diff --git a/climateeconomics/tests/l1_test_gradient_forest_v1_discipline.py b/climateeconomics/tests/l1_test_gradient_forest_v1_discipline.py deleted file mode 100644 index 91fdbaf01..000000000 --- a/climateeconomics/tests/l1_test_gradient_forest_v1_discipline.py +++ /dev/null @@ -1,95 +0,0 @@ -''' -Copyright 2022 Airbus SAS -Modifications on 2023/09/06-2023/11/03 Copyright 2023 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. -''' -from os.path import dirname - -import numpy as np -import pandas as pd -from sostrades_core.execution_engine.execution_engine import ExecutionEngine -from sostrades_core.tests.core.abstract_jacobian_unit_test import ( - AbstractJacobianUnittest, -) - -from climateeconomics.core.core_forest.forest_v1 import Forest -from climateeconomics.glossarycore import GlossaryCore - - -class ForestJacobianDiscTest(AbstractJacobianUnittest): - - def setUp(self): - - self.name = 'Test' - self.ee = ExecutionEngine(self.name) - - def analytic_grad_entry(self): - return [ - self.test_forest_analytic_grad - ] - - def test_forest_analytic_grad(self): - - model_name = 'Test' - ns_dict = {GlossaryCore.NS_WITNESS: f'{self.name}', - 'ns_public': f'{self.name}', - 'ns_forest': f'{self.name}.{model_name}', - 'ns_invest': f'{self.name}'} - self.ee.ns_manager.add_ns_def(ns_dict) - - mod_path = 'climateeconomics.sos_wrapping.sos_wrapping_forest.forest_v1.forest_disc.ForestDiscipline' - builder = self.ee.factory.get_builder_from_module(self.name, mod_path) - - self.ee.factory.set_builders_to_coupling_builder(builder) - - self.ee.configure() - self.ee.display_treeview_nodes() - - self.year_start =GlossaryCore.YearStartDefault - self.year_end = 2050 - years = np.arange(self.year_start, self.year_end + 1, 1) - year_range = self.year_end - self.year_start + 1 - deforestation_surface = np.array(np.linspace(10, 100, year_range)) - self.deforestation_surface_df = pd.DataFrame( - {GlossaryCore.Years: years, "deforested_surface": deforestation_surface}) - self.CO2_per_ha = 4000 - self.limit_deforestation_surface = 1000 - # GtCO2 - self.initial_emissions = 3.21 - forest_invest = np.linspace(2, 4, year_range) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, "forest_investment": forest_invest}) - self.reforestation_cost_per_ha = 3800 - - inputs_dict = {f'{self.name}.{GlossaryCore.YearStart}': self.year_start, - f'{self.name}.{GlossaryCore.YearEnd}': self.year_end, - f'{self.name}.{model_name}.{Forest.LIMIT_DEFORESTATION_SURFACE}': self.limit_deforestation_surface, - f'{self.name}.{Forest.DEFORESTATION_SURFACE}': self.deforestation_surface_df, - f'{self.name}.{model_name}.{Forest.CO2_PER_HA}': self.CO2_per_ha, - f'{self.name}.{model_name}.{Forest.INITIAL_CO2_EMISSIONS}': self.initial_emissions, - f'{self.name}.{Forest.REFORESTATION_INVESTMENT}': self.forest_invest_df, - f'{self.name}.{model_name}.{Forest.REFORESTATION_COST_PER_HA}': self.reforestation_cost_per_ha, - } - self.ee.load_study_from_input_dict(inputs_dict) - - self.ee.execute() - - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline - - self.check_jacobian(location=dirname(__file__), filename='jacobian_forest_v1_discipline.pkl', - discipline=disc_techno, local_data= disc_techno.local_data, step=1e-15, derr_approx='complex_step', - inputs=[ - f'{self.name}.{Forest.DEFORESTATION_SURFACE}', f'{self.name}.{Forest.REFORESTATION_INVESTMENT}'], - outputs=[f'{self.name}.{Forest.FOREST_SURFACE_DF}', - f'{self.name}.{Forest.CO2_EMITTED_FOREST_DF}']) diff --git a/climateeconomics/tests/l1_test_gradient_forest_v2_discipline.py b/climateeconomics/tests/l1_test_gradient_forest_v2_discipline.py index b9f8c4d80..c7ef1de23 100644 --- a/climateeconomics/tests/l1_test_gradient_forest_v2_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_forest_v2_discipline.py @@ -14,414 +14,80 @@ See the License for the specific language governing permissions and limitations under the License. ''' +import unittest from os.path import dirname import numpy as np import pandas as pd -from sostrades_core.execution_engine.execution_engine import ExecutionEngine -from sostrades_core.tests.core.abstract_jacobian_unit_test import ( - AbstractJacobianUnittest, -) -from climateeconomics.core.core_forest.forest_v2 import Forest +from climateeconomics.core.tools.discipline_tester import discipline_test_function from climateeconomics.glossarycore import GlossaryCore +from climateeconomics.sos_wrapping.sos_wrapping_agriculture.forest.forest_disc import ( + ForestDiscipline, +) -class ForestJacobianDiscTest(AbstractJacobianUnittest): - # np.set_printoptions(threshold=np.inf) +class ForestJacobianDiscTest(unittest.TestCase): def setUp(self): self.name = 'Test' - self.ee = ExecutionEngine(self.name) - - def analytic_grad_entry(self): - return [ - self.test_forest_analytic_grad - ] - - def test_forest_analytic_grad(self): - # deforestation do not reach the limits - model_name = 'Forest' - ns_dict = {'ns_public': f'{self.name}', + self.model_name = 'Forest' + self.ns_dict = {'ns_public': f'{self.name}', GlossaryCore.NS_WITNESS: f'{self.name}', - GlossaryCore.NS_FUNCTIONS: f'{self.name}.{model_name}', - 'ns_forest': f'{self.name}.{model_name}', - 'ns_agriculture': f'{self.name}.{model_name}', - 'ns_invest': f'{self.name}.{model_name}'} - - self.ee.ns_manager.add_ns_def(ns_dict) - - mod_path = 'climateeconomics.sos_wrapping.sos_wrapping_agriculture.forest.forest_disc.ForestDiscipline' - builder = self.ee.factory.get_builder_from_module(self.name, mod_path) - - self.ee.factory.set_builders_to_coupling_builder(builder) - - self.ee.configure() - self.ee.display_treeview_nodes() - - self.year_start =GlossaryCore.YearStartDefault - self.year_end = 2035 - years = np.arange(self.year_start, self.year_end + 1, 1) - year_range = self.year_end - self.year_start + 1 - self.CO2_per_ha = 13000 - # GtCO2 - self.initial_emissions = 3.21 - forest_invest = np.linspace(2, 10, year_range) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, "forest_investment": forest_invest}) - deforest_invest = np.linspace(10, 1, year_range) - self.deforest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: deforest_invest}) - self.reforestation_cost_per_ha = 13800 - - construction_delay = 3 - - self.invest_before_year_start = pd.DataFrame( - {'past_years': np.arange(-construction_delay, 0), - GlossaryCore.InvestmentsValue: [1.135081, 1.135081, 1.135081]}) - - mw_invest = np.linspace(1, 10, year_range) - self.mw_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: mw_invest}) - transport = np.linspace(7.6, 7.6, year_range) - self.transport_df = pd.DataFrame( - {GlossaryCore.Years: years, "transport": transport}) - self.margin = pd.DataFrame( - {GlossaryCore.Years: years, 'margin': np.ones(len(years)) * 110.0}) - self.initial_protected_forest_surface = 4 * 0.21 - self.initial_unmanaged_forest_surface = 4 - \ - 1.25 - self.initial_protected_forest_surface - - inputs_dict = {f'{self.name}.{GlossaryCore.YearStart}': self.year_start, - f'{self.name}.{GlossaryCore.YearEnd}': self.year_end, - f'{self.name}.{model_name}.{Forest.DEFORESTATION_INVESTMENT}': self.deforest_invest_df, - f'{self.name}.{model_name}.{Forest.DEFORESTATION_COST_PER_HA}': 8000, - f'{self.name}.{model_name}.{Forest.CO2_PER_HA}': self.CO2_per_ha, - f'{self.name}.{model_name}.{Forest.INITIAL_CO2_EMISSIONS}': self.initial_emissions, - f'{self.name}.{model_name}.{Forest.REFORESTATION_INVESTMENT}': self.forest_invest_df, - f'{self.name}.{model_name}.{Forest.REFORESTATION_COST_PER_HA}': self.reforestation_cost_per_ha, - f'{self.name}.{model_name}.managed_wood_initial_surface': 1.25 * 0.92, - f'{self.name}.{model_name}.managed_wood_invest_before_year_start': self.invest_before_year_start, - f'{self.name}.{model_name}.managed_wood_investment': self.mw_invest_df, - f'{self.name}.transport_cost': self.transport_df, - f'{self.name}.margin': self.margin, - f'{self.name}.{model_name}.initial_unmanaged_forest_surface': self.initial_unmanaged_forest_surface, - f'{self.name}.{model_name}.protected_forest_surface': self.initial_protected_forest_surface, - } - - self.ee.load_study_from_input_dict(inputs_dict) - - self.ee.execute() - - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline - - self.check_jacobian(location=dirname(__file__), filename='jacobian_forest_v2_discipline.pkl', - local_data=disc_techno.local_data, - discipline=disc_techno, step=1e-15, derr_approx='complex_step', - inputs=[ - f'{self.name}.{model_name}.{Forest.DEFORESTATION_INVESTMENT}', - f'{self.name}.{model_name}.{Forest.REFORESTATION_INVESTMENT}', - f'{self.name}.{model_name}.managed_wood_investment', - ], - outputs=[ - f'{self.name}.{Forest.FOREST_SURFACE_DF}', - f'{self.name}.{model_name}.CO2_land_emission_df', - f'{self.name}.Forest.techno_production', - f'{self.name}.Forest.techno_prices', - f'{self.name}.Forest.techno_consumption', - f'{self.name}.Forest.{GlossaryCore.TechnoConsumptionWithoutRatioValue}', - f'{self.name}.Forest.land_use_required', - f'{self.name}.Forest.CO2_emissions', - f'{self.name}.Forest.forest_lost_capital', - ] - ) - - def test_forest_analytic_grad_unmanaged_limit(self): - # deforestation reaches the unmanaged limits - model_name = 'Forest' - ns_dict = {'ns_public': f'{self.name}', - GlossaryCore.NS_WITNESS: f'{self.name}', - GlossaryCore.NS_FUNCTIONS: f'{self.name}.{model_name}', - 'ns_forest': f'{self.name}.{model_name}', - 'ns_agriculture': f'{self.name}.{model_name}', - 'ns_invest': f'{self.name}.{model_name}'} - - self.ee.ns_manager.add_ns_def(ns_dict) - - mod_path = 'climateeconomics.sos_wrapping.sos_wrapping_agriculture.forest.forest_disc.ForestDiscipline' - builder = self.ee.factory.get_builder_from_module(self.name, mod_path) - - self.ee.factory.set_builders_to_coupling_builder(builder) - - self.ee.configure() - self.ee.display_treeview_nodes() - - self.year_start =GlossaryCore.YearStartDefault - self.year_end = 2030 - years = np.arange(self.year_start, self.year_end + 1, 1) - year_range = self.year_end - self.year_start + 1 - deforestation_surface = np.array(np.linspace(4, 4, year_range)) - self.deforestation_surface_df = pd.DataFrame( - {GlossaryCore.Years: years, "deforested_surface": deforestation_surface}) - self.CO2_per_ha = 4000 - self.limit_deforestation_surface = 1000 - # GtCO2 - self.initial_emissions = 3.21 - forest_invest = np.linspace(2, 10, year_range) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, "forest_investment": forest_invest}) - self.deforest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: np.linspace(5000., 1., len(years))}) - self.reforestation_cost_per_ha = 13800 - - construction_delay = 3 - - self.invest_before_year_start = pd.DataFrame( - {'past_years': np.arange(-construction_delay, 0), - GlossaryCore.InvestmentsValue: np.array([1.135081] * construction_delay)}) - - mw_invest = np.linspace(1, 10, year_range) - self.mw_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: mw_invest}) - transport = np.linspace(7.6, 7.6, year_range) - self.transport_df = pd.DataFrame( - {GlossaryCore.Years: years, "transport": transport}) - self.margin = pd.DataFrame( - {GlossaryCore.Years: years, 'margin': np.ones(len(years)) * 110.0}) - self.initial_protected_forest_surface = 4 * 0.21 - self.initial_unmanaged_forest_surface = 4 - \ - 1.25 - self.initial_protected_forest_surface - - inputs_dict = {f'{self.name}.{GlossaryCore.YearStart}': self.year_start, - f'{self.name}.{GlossaryCore.YearEnd}': self.year_end, - f'{self.name}.{model_name}.{Forest.DEFORESTATION_INVESTMENT}': self.deforest_invest_df, - f'{self.name}.{model_name}.{Forest.DEFORESTATION_COST_PER_HA}': 8000, - f'{self.name}.{model_name}.{Forest.CO2_PER_HA}': self.CO2_per_ha, - f'{self.name}.{model_name}.{Forest.INITIAL_CO2_EMISSIONS}': self.initial_emissions, - f'{self.name}.{model_name}.{Forest.REFORESTATION_INVESTMENT}': self.forest_invest_df, - f'{self.name}.{model_name}.{Forest.REFORESTATION_COST_PER_HA}': self.reforestation_cost_per_ha, - f'{self.name}.{model_name}.managed_wood_invest_before_year_start': self.invest_before_year_start, - f'{self.name}.{model_name}.managed_wood_investment': self.mw_invest_df, - f'{self.name}.transport_cost': self.transport_df, - f'{self.name}.margin': self.margin, - f'{self.name}.{model_name}.initial_unmanaged_forest_surface': self.initial_unmanaged_forest_surface, - f'{self.name}.{model_name}.protected_forest_surface': self.initial_protected_forest_surface, - } - - self.ee.load_study_from_input_dict(inputs_dict) - - self.ee.execute() - - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline - - self.check_jacobian(location=dirname(__file__), filename='jacobian_forest_v2_discipline_2.pkl', - discipline=disc_techno, step=1e-15, derr_approx='complex_step', - local_data=disc_techno.local_data, - inputs=[ - f'{self.name}.{model_name}.{Forest.DEFORESTATION_INVESTMENT}', - f'{self.name}.{model_name}.{Forest.REFORESTATION_INVESTMENT}', - f'{self.name}.{model_name}.managed_wood_investment', - ], - outputs=[f'{self.name}.{Forest.FOREST_SURFACE_DF}', - f'{self.name}.Forest.land_use_required', - f'{self.name}.{model_name}.CO2_land_emission_df', - f'{self.name}.Forest.CO2_emissions', - f'{self.name}.Forest.techno_production', - f'{self.name}.Forest.techno_consumption', - f'{self.name}.Forest.{GlossaryCore.TechnoConsumptionWithoutRatioValue}', - f'{self.name}.Forest.techno_prices', - f'{self.name}.Forest.forest_lost_capital', - ] - ) - - def test_forest_analytic_grad_managed_limit(self): - # deforestation reaches the unmanaged and managed limits - model_name = 'Forest' - ns_dict = {'ns_public': f'{self.name}', - GlossaryCore.NS_WITNESS: f'{self.name}', - GlossaryCore.NS_FUNCTIONS: f'{self.name}.{model_name}', - 'ns_forest': f'{self.name}.{model_name}', - 'ns_agriculture': f'{self.name}.{model_name}', - 'ns_invest': f'{self.name}.{model_name}'} - - self.ee.ns_manager.add_ns_def(ns_dict) - - mod_path = 'climateeconomics.sos_wrapping.sos_wrapping_agriculture.forest.forest_disc.ForestDiscipline' - builder = self.ee.factory.get_builder_from_module(self.name, mod_path) - - self.ee.factory.set_builders_to_coupling_builder(builder) - - self.ee.configure() - self.ee.display_treeview_nodes() - - self.year_start =GlossaryCore.YearStartDefault - self.year_end = 2030 - years = np.arange(self.year_start, self.year_end + 1, 1) - year_range = self.year_end - self.year_start + 1 - deforestation_surface = np.array(np.linspace(4, 4, year_range)) - self.deforestation_surface_df = pd.DataFrame( - {GlossaryCore.Years: years, "deforested_surface": deforestation_surface}) - self.CO2_per_ha = 4000 - self.limit_deforestation_surface = 1000 - # GtCO2 - self.initial_emissions = 3.21 - forest_invest = np.linspace(2, 10, year_range) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, "forest_investment": forest_invest}) - self.deforest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: np.linspace(5000., 1., len(years))}) - self.reforestation_cost_per_ha = 13800 - construction_delay = 3 - self.invest_before_year_start = pd.DataFrame( - {'past_years': np.arange(-construction_delay, 0), - GlossaryCore.InvestmentsValue: np.array([1.135081] * construction_delay)}) - - mw_invest = np.linspace(1, 10, year_range) - self.mw_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: mw_invest}) - transport = np.linspace(7.6, 7.6, year_range) - self.transport_df = pd.DataFrame( - {GlossaryCore.Years: years, "transport": transport}) - self.margin = pd.DataFrame( - {GlossaryCore.Years: years, 'margin': np.ones(len(years)) * 110.0}) - self.initial_protected_forest_surface = 4 * 0.21 - self.initial_unmanaged_forest_surface = 4 - \ - 1.25 - self.initial_protected_forest_surface - - inputs_dict = {f'{self.name}.{GlossaryCore.YearStart}': self.year_start, - f'{self.name}.{GlossaryCore.YearEnd}': self.year_end, - f'{self.name}.{model_name}.{Forest.DEFORESTATION_INVESTMENT}': self.deforest_invest_df, - f'{self.name}.{model_name}.{Forest.DEFORESTATION_COST_PER_HA}': 8000, - f'{self.name}.{model_name}.{Forest.CO2_PER_HA}': self.CO2_per_ha, - f'{self.name}.{model_name}.{Forest.INITIAL_CO2_EMISSIONS}': self.initial_emissions, - f'{self.name}.{model_name}.{Forest.REFORESTATION_INVESTMENT}': self.forest_invest_df, - f'{self.name}.{model_name}.{Forest.REFORESTATION_COST_PER_HA}': self.reforestation_cost_per_ha, - f'{self.name}.{model_name}.managed_wood_invest_before_year_start': self.invest_before_year_start, - f'{self.name}.{model_name}.managed_wood_investment': self.mw_invest_df, - f'{self.name}.transport_cost': self.transport_df, - f'{self.name}.margin': self.margin, - f'{self.name}.{model_name}.initial_unmanaged_forest_surface': self.initial_unmanaged_forest_surface, - f'{self.name}.{model_name}.protected_forest_surface': self.initial_protected_forest_surface, - } - - self.ee.load_study_from_input_dict(inputs_dict) - self.ee.execute() - - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline - - self.check_jacobian(location=dirname(__file__), filename='jacobian_forest_v2_discipline_3.pkl', - discipline=disc_techno, step=1e-15, derr_approx='complex_step', - local_data=disc_techno.local_data, - inputs=[ - f'{self.name}.{model_name}.{Forest.DEFORESTATION_INVESTMENT}', - f'{self.name}.{model_name}.{Forest.REFORESTATION_INVESTMENT}', - f'{self.name}.{model_name}.managed_wood_investment', - ], - outputs=[f'{self.name}.{Forest.FOREST_SURFACE_DF}', - f'{self.name}.Forest.land_use_required', - f'{self.name}.{model_name}.CO2_land_emission_df', - f'{self.name}.Forest.CO2_emissions', - f'{self.name}.Forest.techno_production', - f'{self.name}.Forest.techno_consumption', - f'{self.name}.Forest.{GlossaryCore.TechnoConsumptionWithoutRatioValue}', - f'{self.name}.Forest.techno_prices', - f'{self.name}.Forest.forest_lost_capital', - ] - ) - - def test_forest_analytic_grad_bigmanaged_limit(self): - # deforestation reaches the unmanaged and managed limits in one time - # (not progressive) - model_name = 'Forest' - ns_dict = {'ns_public': f'{self.name}', - GlossaryCore.NS_WITNESS: f'{self.name}', - GlossaryCore.NS_FUNCTIONS: f'{self.name}.{model_name}', - 'ns_forest': f'{self.name}.{model_name}', - 'ns_agriculture': f'{self.name}.{model_name}', - 'ns_invest': f'{self.name}.{model_name}'} - - self.ee.ns_manager.add_ns_def(ns_dict) - - mod_path = 'climateeconomics.sos_wrapping.sos_wrapping_agriculture.forest.forest_disc.ForestDiscipline' - builder = self.ee.factory.get_builder_from_module(self.name, mod_path) - - self.ee.factory.set_builders_to_coupling_builder(builder) - - self.ee.configure() - self.ee.display_treeview_nodes() - - self.year_start =GlossaryCore.YearStartDefault - self.year_end = 2030 - years = np.arange(self.year_start, self.year_end + 1, 1) - year_range = self.year_end - self.year_start + 1 - deforestation_surface = np.array(np.linspace(4, 4, year_range)) - self.deforestation_surface_df = pd.DataFrame( - {GlossaryCore.Years: years, "deforested_surface": deforestation_surface}) - self.CO2_per_ha = 4000 - self.limit_deforestation_surface = 1000 - # GtCO2 - self.initial_emissions = 3.21 - forest_invest = np.linspace(2, 10, year_range) - self.forest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, "forest_investment": forest_invest}) - self.deforest_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: np.linspace(1000., 1., len(years))}) - self.reforestation_cost_per_ha = 13800 - construction_delay = 3 - self.invest_before_year_start = pd.DataFrame( - {'past_years': np.arange(-construction_delay, 0), - GlossaryCore.InvestmentsValue: np.array([1.135081] * construction_delay)}) - - mw_invest = np.linspace(1, 10, year_range) - self.mw_invest_df = pd.DataFrame( - {GlossaryCore.Years: years, GlossaryCore.InvestmentsValue: mw_invest}) - transport = np.linspace(7.6, 7.6, year_range) - self.transport_df = pd.DataFrame( - {GlossaryCore.Years: years, "transport": transport}) - self.margin = pd.DataFrame( - {GlossaryCore.Years: years, 'margin': np.ones(len(years)) * 110.0}) - self.initial_protected_forest_surface = 4 * 0.21 - self.initial_unmanaged_forest_surface = 4 - \ - 1.25 - self.initial_protected_forest_surface - - inputs_dict = {f'{self.name}.{GlossaryCore.YearStart}': self.year_start, - f'{self.name}.{GlossaryCore.YearEnd}': self.year_end, - f'{self.name}.{model_name}.{Forest.DEFORESTATION_INVESTMENT}': self.deforest_invest_df, - f'{self.name}.{model_name}.{Forest.DEFORESTATION_COST_PER_HA}': 8000, - f'{self.name}.{model_name}.{Forest.CO2_PER_HA}': self.CO2_per_ha, - f'{self.name}.{model_name}.{Forest.INITIAL_CO2_EMISSIONS}': self.initial_emissions, - f'{self.name}.{model_name}.{Forest.REFORESTATION_INVESTMENT}': self.forest_invest_df, - f'{self.name}.{model_name}.{Forest.REFORESTATION_COST_PER_HA}': self.reforestation_cost_per_ha, - f'{self.name}.{model_name}.managed_wood_invest_before_year_start': self.invest_before_year_start, - f'{self.name}.{model_name}.managed_wood_investment': self.mw_invest_df, - f'{self.name}.transport_cost': self.transport_df, - f'{self.name}.margin': self.margin, - f'{self.name}.{model_name}.initial_unmanaged_forest_surface': self.initial_unmanaged_forest_surface, - f'{self.name}.{model_name}.protected_forest_surface': self.initial_protected_forest_surface, - } - - self.ee.load_study_from_input_dict(inputs_dict) - self.ee.execute() - - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline - - self.check_jacobian(location=dirname(__file__), filename='jacobian_forest_v2_discipline_4.pkl', - discipline=disc_techno, step=1e-15, derr_approx='complex_step', - local_data=disc_techno.local_data, - inputs=[ - f'{self.name}.{model_name}.{Forest.DEFORESTATION_INVESTMENT}', - f'{self.name}.{model_name}.{Forest.REFORESTATION_INVESTMENT}', - f'{self.name}.{model_name}.managed_wood_investment', - ], - outputs=[f'{self.name}.{Forest.FOREST_SURFACE_DF}', - f'{self.name}.Forest.land_use_required', - f'{self.name}.{model_name}.CO2_land_emission_df', - f'{self.name}.Forest.CO2_emissions', - f'{self.name}.Forest.techno_production', - f'{self.name}.Forest.techno_consumption', - f'{self.name}.Forest.{GlossaryCore.TechnoConsumptionWithoutRatioValue}', - f'{self.name}.Forest.techno_prices', - f'{self.name}.Forest.forest_lost_capital', - - ] - ) + GlossaryCore.NS_FUNCTIONS: f'{self.name}.{self.model_name}', + 'ns_forest': f'{self.name}.{self.model_name}', + 'ns_agriculture': f'{self.name}.{self.model_name}', + 'ns_invest': f'{self.name}.{self.model_name}'} + + year_start = GlossaryCore.YearStartDefault + year_end = GlossaryCore.YearEndDefaultTest + years = np.arange(year_start, year_end + 1, 1) + crop_reduction_productivity_df = pd.DataFrame({ + GlossaryCore.Years: years, + GlossaryCore.CropProductivityReductionName: np.linspace(0, 1.5, len(years)) + }) + + years = np.arange(year_start, year_end + 1, 1) + year_range = year_end - year_start + 1 + + reforestation_invest_df = pd.DataFrame({ + GlossaryCore.Years: years, + "reforestation_investment": np.linspace(2, 10, year_range) + }) + deforest_invest_df = pd.DataFrame({ + GlossaryCore.Years: years, + GlossaryCore.InvestmentsValue: np.linspace(10, 1, year_range) + }) + + mw_invest_df = pd.DataFrame({GlossaryCore.Years: years, + GlossaryCore.InvestmentsValue: np.linspace(1, 10, year_range)}) + transport_df = pd.DataFrame({GlossaryCore.Years: years, "transport": 7.6}) + margin = pd.DataFrame({GlossaryCore.Years: years, 'margin': 110.}) + + self.inputs_dict = { + f'{self.name}.{GlossaryCore.YearStart}': year_start, + f'{self.name}.{GlossaryCore.YearEnd}': year_end, + f'{self.name}.{self.model_name}.deforestation_investment': deforest_invest_df, + f'{self.name}.{self.model_name}.reforestation_investment': reforestation_invest_df, + f'{self.name}.{self.model_name}.managed_wood_investment': mw_invest_df, + f'{self.name}.transport_cost': transport_df, + f'{self.name}.margin': margin, + f'{self.name}.{GlossaryCore.CropProductivityReductionName}': crop_reduction_productivity_df, + } + + + def test_forest(self): + discipline_test_function( + module_path='climateeconomics.sos_wrapping.sos_wrapping_agriculture.forest.forest_disc.ForestDiscipline', + model_name=self.model_name, + name=self.name, + jacobian_test=True, + coupling_inputs=ForestDiscipline.coupling_inputs, + coupling_outputs=ForestDiscipline.coupling_outputs, + show_graphs=False, + inputs_dict=self.inputs_dict, + namespaces_dict=self.ns_dict, + pickle_directory=dirname(__file__), + pickle_name='jacobian_forest_autodiff.pkl', + override_dump_jacobian=False + ) \ No newline at end of file diff --git a/climateeconomics/tests/l1_test_gradient_ghgcycle_discipline.py b/climateeconomics/tests/l1_test_gradient_ghgcycle_discipline.py index bcce51b12..6d4a00994 100644 --- a/climateeconomics/tests/l1_test_gradient_ghgcycle_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_ghgcycle_discipline.py @@ -73,7 +73,7 @@ def test_execute(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_ghg_cycle_discipline1.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data = disc_techno.local_data, diff --git a/climateeconomics/tests/l1_test_gradient_ghgemissions_discipline.py b/climateeconomics/tests/l1_test_gradient_ghgemissions_discipline.py index 42060109a..8a78519fc 100644 --- a/climateeconomics/tests/l1_test_gradient_ghgemissions_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_ghgemissions_discipline.py @@ -149,7 +149,7 @@ def test_carbon_emissions_analytic_grad(self): self.ee.load_study_from_input_dict(values_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_ghg_emission_discipline.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data = disc_techno.local_data, @@ -195,7 +195,7 @@ def test_carbon_emissions_analytic_grad_affine_co2_objective(self): self.ee.load_study_from_input_dict(values_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_ghg_emission_discipline_affine_co2_objective.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data = disc_techno.local_data, diff --git a/climateeconomics/tests/l1_test_gradient_labor_market_sectorization_discipline.py b/climateeconomics/tests/l1_test_gradient_labor_market_sectorization_discipline.py index 828cdb871..a1e7adec2 100644 --- a/climateeconomics/tests/l1_test_gradient_labor_market_sectorization_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_labor_market_sectorization_discipline.py @@ -86,7 +86,7 @@ def test_labor_analytic_grad(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_labormarket_sectorization_discipline.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data = disc_techno.local_data, diff --git a/climateeconomics/tests/l1_test_gradient_land_use_v1_discipline.py b/climateeconomics/tests/l1_test_gradient_land_use_v1_discipline.py index b516e0b40..3d18e8abe 100644 --- a/climateeconomics/tests/l1_test_gradient_land_use_v1_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_land_use_v1_discipline.py @@ -100,7 +100,7 @@ def test_land_use_v1_discipline_analytic_grad(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_land_use_v1_discipline.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data = disc_techno.local_data, diff --git a/climateeconomics/tests/l1_test_gradient_land_use_v2_discipline.py b/climateeconomics/tests/l1_test_gradient_land_use_v2_discipline.py index 711015b54..0b26b518c 100644 --- a/climateeconomics/tests/l1_test_gradient_land_use_v2_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_land_use_v2_discipline.py @@ -103,7 +103,7 @@ def test_land_use_v2_discipline_analytic_grad(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline #np.set_printoptions(threshold=np.inf) self.check_jacobian(location=dirname(__file__), filename='jacobian_land_use_v2_discipline.pkl', diff --git a/climateeconomics/tests/l1_test_gradient_macroeconomics_discipline.py b/climateeconomics/tests/l1_test_gradient_macroeconomics_discipline.py index 7d88e5c6f..8321ab7bc 100644 --- a/climateeconomics/tests/l1_test_gradient_macroeconomics_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_macroeconomics_discipline.py @@ -41,7 +41,7 @@ def setUp(self): 'ns_energy_study': f'{self.name}', 'ns_public': f'{self.name}', GlossaryCore.NS_FUNCTIONS: f'{self.name}', - GlossaryCore.NS_GHGEMISSIONS: f'{self.name}',} + GlossaryCore.NS_GHGEMISSIONS: f'{self.name}', } self.ee.ns_manager.add_ns_def(ns_dict) @@ -211,7 +211,7 @@ def test_macro_economics_analytic_grad(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline disc = self.ee.dm.get_disciplines_with_name( f'{self.name}.{self.model_name}')[0] @@ -235,7 +235,7 @@ def test_macro_economics_analytic_grad_damageproductivity(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_macroeconomics_discipline_grad_damageproductivity.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', @@ -262,7 +262,7 @@ def test_macro_economics_analytic_grad_max_damage(self): #graph.to_plotly().show() pass - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_macroeconomics_discipline_grad_max_damage.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', @@ -287,7 +287,7 @@ def test_macro_economics_analytic_grad_gigantic_invest(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline disc = self.ee.dm.get_disciplines_with_name( f'{self.name}.{self.model_name}')[0] @@ -316,7 +316,7 @@ def test_macro_economics_very_high_emissions(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_macroeconomics_discipline_very_high_emissions.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', @@ -337,7 +337,7 @@ def test_macro_economics_negativeco2_emissions(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_macroeconomics_discipline_negative_emissions.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', @@ -355,7 +355,7 @@ def test_macro_economics_negativeco2_tax(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_macroeconomics_discipline_negative_co2_tax.pkl', @@ -384,7 +384,7 @@ def test_macro_economics_without_compute_gdp_analytic_grad(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_macroeconomics_discipline_without_compute_gdp.pkl', @@ -406,7 +406,7 @@ def test_gigantic_energy_production_no_damage_productivity(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline disc = self.ee.dm.get_disciplines_with_name( f'{self.name}.{self.model_name}')[0] @@ -436,7 +436,7 @@ def test_gigantic_energy_production_damage_productivity(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline disc = self.ee.dm.get_disciplines_with_name( f'{self.name}.{self.model_name}')[0] @@ -473,7 +473,7 @@ def test_gigantic_energy_production_wo_compute_gdp(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline disc = self.ee.dm.get_disciplines_with_name( f'{self.name}.{self.model_name}')[0] diff --git a/climateeconomics/tests/l1_test_gradient_macroeconomics_sectorization_discipline.py b/climateeconomics/tests/l1_test_gradient_macroeconomics_sectorization_discipline.py index 5094162f2..9a0bf84a2 100644 --- a/climateeconomics/tests/l1_test_gradient_macroeconomics_sectorization_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_macroeconomics_sectorization_discipline.py @@ -162,7 +162,7 @@ def test_macro_sectorized_analytic_grad(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_macro_sectorization_discipline.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data=disc_techno.local_data, diff --git a/climateeconomics/tests/l1_test_gradient_natural_gas_resource_discipline.py b/climateeconomics/tests/l1_test_gradient_natural_gas_resource_discipline.py index 501564ae7..9f9abce76 100644 --- a/climateeconomics/tests/l1_test_gradient_natural_gas_resource_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_natural_gas_resource_discipline.py @@ -89,7 +89,7 @@ def test_natural_gas_resource_analytic_grad(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_natural_gas_discipline.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step',local_data=disc_techno.local_data, inputs=[ @@ -130,7 +130,7 @@ def test_natural_gas_resource_demand_variable_analytic_grad(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_natural_gas_demand_variable_discipline.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data=disc_techno.local_data, inputs=[ diff --git a/climateeconomics/tests/l1_test_gradient_non_use_capital_obj_disc.py b/climateeconomics/tests/l1_test_gradient_non_use_capital_obj_disc.py index 2a67e6aae..319f401d7 100644 --- a/climateeconomics/tests/l1_test_gradient_non_use_capital_obj_disc.py +++ b/climateeconomics/tests/l1_test_gradient_non_use_capital_obj_disc.py @@ -122,7 +122,7 @@ def test_01_grad_non_use_capital_objective(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_non_use_capital_objective.pkl', discipline=disc_techno, step=1e-15, local_data=disc_techno.local_data, diff --git a/climateeconomics/tests/l1_test_gradient_oil_resource_discipline.py b/climateeconomics/tests/l1_test_gradient_oil_resource_discipline.py index 8a0b8cdb4..a4179957a 100644 --- a/climateeconomics/tests/l1_test_gradient_oil_resource_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_oil_resource_discipline.py @@ -89,7 +89,7 @@ def test_oil_resource_analytic_grad(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_oil_discipline.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data = disc_techno.local_data, inputs=[f'{self.name}.{self.model_name}.resources_demand'], @@ -128,7 +128,7 @@ def test_oil_resource_demand_variable_analytic_grad(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_oil_demand_variable_discipline.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data=disc_techno.local_data, inputs=[f'{self.name}.{self.model_name}.resources_demand'], diff --git a/climateeconomics/tests/l1_test_gradient_platinum_resource_discipline.py b/climateeconomics/tests/l1_test_gradient_platinum_resource_discipline.py index acf720e6c..f5b2d06e5 100644 --- a/climateeconomics/tests/l1_test_gradient_platinum_resource_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_platinum_resource_discipline.py @@ -103,7 +103,7 @@ def test_platinum_resource_analytic_grad(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_platinum_discipline.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data = disc_techno.local_data, inputs=[ @@ -148,7 +148,7 @@ def test_platinum_resource_damand_variable_analytic_grad(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_platinum_demand_variable_discipline.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data = disc_techno.local_data, inputs=[ diff --git a/climateeconomics/tests/l1_test_gradient_population_discipline.py b/climateeconomics/tests/l1_test_gradient_population_discipline.py index fba602b57..babc0c204 100644 --- a/climateeconomics/tests/l1_test_gradient_population_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_population_discipline.py @@ -121,7 +121,7 @@ def test_population_discipline_analytic_grad_output(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.override_dump_jacobian = True pickle_filename = 'jacobian_population_discipline_output.pkl' self.check_jacobian(location=dirname(__file__), filename='jacobian_population_discipline_output.pkl', @@ -145,7 +145,7 @@ def test_working_population_discipline_analytic_grad_output(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.override_dump_jacobian = True pickle_filename = 'jacobian_working_population_discipline_output.pkl' self.check_jacobian(location=dirname(__file__), filename='jacobian_working_population_discipline_output.pkl', @@ -168,7 +168,7 @@ def test_working_population_discipline_analytic_grad_temp(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.override_dump_jacobian = True pickle_filename = 'jacobian_working_population_discipline_temp.pkl' self.check_jacobian(location=dirname(__file__), filename='jacobian_working_population_discipline_temp.pkl', @@ -193,7 +193,7 @@ def test_population_discipline_analytic_grad_temperature(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.override_dump_jacobian = True pickle_filename = 'jacobian_population_discipline_temp.pkl' self.check_jacobian(location=dirname(__file__), filename='jacobian_population_discipline_temp.pkl', @@ -242,7 +242,7 @@ def test_population_discipline_analytic_grad_temp_negative(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.override_dump_jacobian = True pickle_filename = 'jacobian_population_discipline_temp_neg.pkl' self.check_jacobian(location=dirname(__file__), filename='jacobian_population_discipline_temp_neg.pkl', @@ -293,7 +293,7 @@ def test_population_discipline_analytic_grad_big_gdp(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.override_dump_jacobian = True pickle_filename = 'jacobian_population_discipline_big_gdp.pkl' self.check_jacobian(location=dirname(__file__), filename='jacobian_population_discipline_big_gdp.pkl', @@ -344,7 +344,7 @@ def test_population_discipline_analytic_grad_big_temp(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.override_dump_jacobian = True pickle_filename = 'jacobian_population_discipline_augmente_temp.pkl' self.check_jacobian(location=dirname(__file__), filename='jacobian_population_discipline_augmente_temp.pkl', @@ -399,7 +399,7 @@ def test_population_discipline_analytic_small_pop(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.override_dump_jacobian = True pickle_filename = 'jacobian_population_discipline_small_pop.pkl' self.check_jacobian(location=dirname(__file__), filename=pickle_filename, @@ -454,7 +454,7 @@ def test_population_discipline_analytic_big_pop(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.override_dump_jacobian = True pickle_filename = 'jacobian_population_discipline_big_pop.pkl' @@ -489,7 +489,7 @@ def test_population_discipline_analytic_3000_calories_pc(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.override_dump_jacobian = True pickle_filename = 'jacobian_population_discipline_3000_kcal.pkl' @@ -524,7 +524,7 @@ def test_population_discipline_deactivate_climate_effect(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_population_discipline_output_wo_climate_effect.pkl', discipline=disc_techno, local_data=disc_techno.local_data, inputs=[f'{self.name}.{GlossaryCore.EconomicsDfValue}', @@ -553,7 +553,7 @@ def test_population_discipline_activate_pandemic_effect(self): self.override_dump_jacobian = True pickle_filename = 'jacobian_population_discipline_output_w_pandemic_effect.pkl' - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename=pickle_filename, discipline=disc_techno, local_data=disc_techno.local_data, inputs=[f'{self.name}.{GlossaryCore.EconomicsDfValue}', @@ -563,3 +563,4 @@ def test_population_discipline_activate_pandemic_effect(self): ], step=1e-15, derr_approx='complex_step') os.remove(os.path.join(dirname(__file__), 'jacobian_pkls', pickle_filename)) + diff --git a/climateeconomics/tests/l1_test_gradient_resource_mix_discipline.py b/climateeconomics/tests/l1_test_gradient_resource_mix_discipline.py index b348b5346..366351b11 100644 --- a/climateeconomics/tests/l1_test_gradient_resource_mix_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_resource_mix_discipline.py @@ -19,6 +19,7 @@ from os.path import dirname, join import numpy as np +import pandas as pd from pandas import read_csv from sostrades_core.execution_engine.execution_engine import ExecutionEngine from sostrades_core.tests.core.abstract_jacobian_unit_test import ( @@ -108,7 +109,7 @@ def setUp(self): join(data_dir, 'all_demand_with_high_demand.csv')) self.year_start = 2020 - self.year_end = GlossaryCore.YearEndDefault + self.year_end = GlossaryCore.YearEndDefaultTest self.years = np.arange(self.year_start, self.year_end + 1, 1) self.year_range = self.year_end - self.year_start + 1 @@ -208,11 +209,14 @@ def test_All_resource_discipline_analytic_grad(self): f'{self.name}.{self.model_name}.platinum_resource.recycled_production': self.platinum_recycled_production_df, f'{self.name}.{self.model_name}.{ResourceMixModel.NON_MODELED_RESOURCE_PRICE}': self.non_modeled_resource_df } + for name, value in values_dict.items(): + if isinstance(value, pd.DataFrame) and GlossaryCore.Years in value.columns: + values_dict[name] = value.loc[value[GlossaryCore.Years] <= self.year_end] self.ee.load_study_from_input_dict(values_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline input_names = [] input_stock = [ f'{self.name}.{self.model_name}.{resource}.resource_stock' for resource in ResourceMixModel.RESOURCE_LIST] @@ -243,6 +247,7 @@ def test_All_resource_discipline_analytic_grad(self): f'{self.name}.{self.model_name}.{ResourceMixModel.ALL_RESOURCE_DEMAND}', ] + self.override_dump_jacobian = 1 self.check_jacobian(location=dirname(__file__), filename='jacobian_all_resource_discipline.pkl', discipline=disc_techno, local_data = disc_techno.local_data, inputs=input_names, outputs=resource_output, step=1e-15, diff --git a/climateeconomics/tests/l1_test_gradient_sector_discipline.py b/climateeconomics/tests/l1_test_gradient_sector_discipline.py index 982df2438..bb4f13251 100644 --- a/climateeconomics/tests/l1_test_gradient_sector_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_sector_discipline.py @@ -136,7 +136,7 @@ def test_analytic_grad_industry(self): #graph.to_plotly().show() pass - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=os.path.abspath(dirname(__file__)), filename='jacobian_sector_discipline_industry.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data=disc_techno.local_data, inputs=self.generate_checked_inputs(), @@ -180,7 +180,7 @@ def test_gradient_withotudamagetoproductivity_industry(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_sector_discipline_withoutdamage_industry.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data=disc_techno.local_data, inputs=self.generate_checked_inputs(), @@ -230,7 +230,7 @@ def test_gradient_without_climate_impact_on_gdp_industry(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_sector_discipline_withoutdamage_on_gdp_industry.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data=disc_techno.local_data, inputs=self.generate_checked_inputs(), @@ -286,7 +286,7 @@ def test_analytic_grad_services(self): #graph.to_plotly().show() pass - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=os.path.abspath(dirname(__file__)), filename='jacobian_sector_discipline_services.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data=disc_techno.local_data, inputs=self.generate_checked_inputs(), @@ -330,7 +330,7 @@ def test_gradient_withotudamagetoproductivity_services(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_sector_discipline_withoutdamage_services.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data=disc_techno.local_data, inputs=self.generate_checked_inputs(), @@ -380,7 +380,7 @@ def test_gradient_without_climate_impact_on_gdp_services(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_sector_discipline_withoutdamage_on_gdp_services.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data=disc_techno.local_data, inputs=self.generate_checked_inputs(), @@ -436,7 +436,7 @@ def test_analytic_grad_agriculture(self): #graph.to_plotly().show() pass - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=os.path.abspath(dirname(__file__)), filename='jacobian_sector_discipline_agriculture.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data=disc_techno.local_data, inputs=self.generate_checked_inputs(), @@ -480,7 +480,7 @@ def test_gradient_withotudamagetoproductivity_agriculture(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_sector_discipline_withoutdamage_agriculture.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data=disc_techno.local_data, inputs=self.generate_checked_inputs(), @@ -530,7 +530,7 @@ def test_gradient_without_climate_impact_on_gdp_agriculture(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_sector_discipline_withoutdamage_on_gdp_agriculture.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data=disc_techno.local_data, inputs=self.generate_checked_inputs(), diff --git a/climateeconomics/tests/l1_test_gradient_sectorized_utility_discipline.py b/climateeconomics/tests/l1_test_gradient_sectorized_utility_discipline.py index 08241b358..6da42e79d 100644 --- a/climateeconomics/tests/l1_test_gradient_sectorized_utility_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_sectorized_utility_discipline.py @@ -97,12 +97,17 @@ def test_01_sectorization_gradients(self): """ self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + # self.override_dump_jacobian = True + + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline + self.check_jacobian(location=dirname(__file__), filename='jacobian_sectorized_utility_discipline_sectors_obj.pkl', discipline=disc_techno, step=1e-15, local_data=disc_techno.local_data, inputs=[f'{self.name}.{GlossaryCore.EnergyMeanPriceValue}', f'{self.name}.{GlossaryCore.PopulationDfValue}', f'{self.name}.{GlossaryCore.EconomicsDfValue}', f'{self.name}.{GlossaryCore.SectorizedConsumptionDfValue}'], - outputs=[f'{self.name}.{sector}.{GlossaryCore.UtilityObjectiveName}' for sector in self.sector_list] + [f'{self.name}.{GlossaryCore.DecreasingGdpIncrementsObjectiveValue}',], + outputs=[f'{self.name}.{sector}.{GlossaryCore.UtilityObjectiveName}' for sector in + self.sector_list] + [ + f'{self.name}.{GlossaryCore.DecreasingGdpIncrementsObjectiveValue}', ], derr_approx='complex_step') diff --git a/climateeconomics/tests/l1_test_gradient_sectors_redistribution_energy_discipline.py b/climateeconomics/tests/l1_test_gradient_sectors_redistribution_energy_discipline.py index dce12632c..84b431f1a 100644 --- a/climateeconomics/tests/l1_test_gradient_sectors_redistribution_energy_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_sectors_redistribution_energy_discipline.py @@ -89,8 +89,8 @@ def test_analytic_grad(self): inputs_checked += [f'{name}.{sector}.{GlossaryCore.ShareSectorEnergyDfValue}' for sector in self.sector_list[:-1]] output_checked = [f'{name}.{sector}.{GlossaryCore.EnergyProductionValue}' for sector in self.sector_list] output_checked += [f'{name}.{GlossaryCore.ResidentialEnergyConsumptionDfValue}'] - - disc_techno = ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + + disc_techno = ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_sectors_redistribution_energy_discipline.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data=disc_techno.local_data, diff --git a/climateeconomics/tests/l1_test_gradient_sectors_redistribution_invests_discipline.py b/climateeconomics/tests/l1_test_gradient_sectors_redistribution_invests_discipline.py index 9eb0590b0..dfde7bc77 100644 --- a/climateeconomics/tests/l1_test_gradient_sectors_redistribution_invests_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_sectors_redistribution_invests_discipline.py @@ -106,8 +106,7 @@ def test_analytic_grad(self): inputs_checked += [f'{name}.{sector}.{GlossaryCore.ShareSectorInvestmentDfValue}' for sector in self.sector_list] output_checked = [f'{name}.{sector}.{GlossaryCore.InvestmentDfValue}' for sector in self.sector_list] - - disc_techno = ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_sectors_redistribution_invest_discipline.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data= disc_techno.local_data, inputs=inputs_checked, @@ -156,7 +155,7 @@ def test_analytic_grad_mdo_mode(self): self.sector_list] output_checked = [f'{name}.{sector}.{GlossaryCore.InvestmentDfValue}' for sector in self.sector_list] - disc_techno = ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_sectors_redistribution_invest_discipline_2.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', diff --git a/climateeconomics/tests/l1_test_gradient_temperature_discipline.py b/climateeconomics/tests/l1_test_gradient_temperature_discipline.py index b6e7190e5..b4083121a 100644 --- a/climateeconomics/tests/l1_test_gradient_temperature_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_temperature_discipline.py @@ -88,7 +88,7 @@ def __temperature_discipline_analytic_grad(self, temperature_obj_option): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename=f'jacobian_temperature_discipline_{temperature_obj_option}.pkl', discipline=disc_techno, step=1e-15, local_data= disc_techno.local_data, @@ -134,7 +134,7 @@ def test_03_temperature_discipline_analytic_grad_myhre(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_temperature_discipline_Myhre.pkl', local_data=disc_techno.local_data, discipline=disc_techno, step=1e-15, inputs=[f'{self.name}.{GlossaryCore.CarbonCycleDfValue}'], @@ -179,7 +179,7 @@ def _test_04_temperature_discipline_analytic_grad_etminan(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_temperature_discipline_etminan.pkl', local_data= disc_techno.local_data,discipline=disc_techno, step=1e-15, inputs=[f'{self.name}.{GlossaryCore.CarbonCycleDfValue}'], outputs=[f'{self.name}.{self.model_name}.forcing_detail_df', f'{self.name}.{GlossaryCore.TemperatureDfValue}', f'{self.name}.temperature_objective', f'{self.name}.temperature_constraint'], derr_approx='complex_step') @@ -223,7 +223,7 @@ def test_05_temperature_discipline_analytic_grad_meinshausen(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_temperature_discipline_Meinshausen.pkl',local_data= disc_techno.local_data, discipline=disc_techno, step=1e-15, inputs=[f'{self.name}.{GlossaryCore.CarbonCycleDfValue}'], outputs=[f'{self.name}.{GlossaryCore.TemperatureDfValue}', f'{self.name}.temperature_objective', f'{self.name}.temperature_constraint'], derr_approx='complex_step') @@ -268,7 +268,7 @@ def _test_06_temperature_discipline_analytic_grad_etminan_lower_atmo_conc(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_temperature_discipline_etminan_lower.pkl',local_data= disc_techno.local_data, discipline=disc_techno, step=1e-10, inputs=[f'{self.name}.{GlossaryCore.CarbonCycleDfValue}'], outputs=[f'{self.name}.{self.model_name}.forcing_detail_df', f'{self.name}.{GlossaryCore.TemperatureDfValue}', f'{self.name}.temperature_objective', f'{self.name}.temperature_constraint'], output_column='CO2 forcing', derr_approx='finite_differences') diff --git a/climateeconomics/tests/l1_test_gradient_temperature_v2_discipline.py b/climateeconomics/tests/l1_test_gradient_temperature_v2_discipline.py index 982f58621..32742c723 100644 --- a/climateeconomics/tests/l1_test_gradient_temperature_v2_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_temperature_v2_discipline.py @@ -86,7 +86,7 @@ def test_02_temperature_discipline_analytic_grad_DICE(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_temperature_v2_discipline_DICE.pkl', discipline=disc_techno,local_data = disc_techno.local_data, @@ -144,7 +144,7 @@ def test_03_temperature_discipline_analytic_grad_FUND_myhre(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_temperature_v2_discipline_Myhre.pkl', discipline=disc_techno,local_data = disc_techno.local_data, @@ -202,7 +202,7 @@ def test_03_1_temperature_discipline_analytic_grad_FUND_Meinshausen(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_temperature_v2_discipline_Meinshausen.pkl', discipline=disc_techno,local_data = disc_techno.local_data, @@ -260,7 +260,7 @@ def _test_04_temperature_discipline_analytic_grad_etminan(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_temperature_v2_discipline_etminan.pkl', discipline=disc_techno,local_data = disc_techno.local_data, @@ -316,7 +316,7 @@ def _test_05_temperature_discipline_analytic_grad_meinshausen(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_temperature_v2_discipline_Meinshausen.pkl', discipline=disc_techno,local_data = disc_techno.local_data, @@ -367,7 +367,7 @@ def _test_06_temperature_discipline_analytic_grad_etminan_lower_atmo_conc(self): self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_temperature_v2_discipline_etminan_lower.pkl', local_data = disc_techno.local_data, diff --git a/climateeconomics/tests/l1_test_gradient_uranium_resource_discipline.py b/climateeconomics/tests/l1_test_gradient_uranium_resource_discipline.py index 2649af22d..3afe6f472 100644 --- a/climateeconomics/tests/l1_test_gradient_uranium_resource_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_uranium_resource_discipline.py @@ -89,7 +89,7 @@ def test_uranium_resource_analytic_grad(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_uranium_discipline.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step', local_data = disc_techno.local_data, inputs=[f'{self.name}.{self.model_name}.resources_demand'], @@ -129,7 +129,7 @@ def test_uranium_variable_demand_resource_analytic_grad(self): self.ee.load_study_from_input_dict(inputs_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_uranium_demand_variable_discipline.pkl', discipline=disc_techno, step=1e-15, derr_approx='complex_step',local_data = disc_techno.local_data, inputs=[f'{self.name}.{self.model_name}.resources_demand'], diff --git a/climateeconomics/tests/l1_test_gradient_utility_discipline.py b/climateeconomics/tests/l1_test_gradient_utility_discipline.py index 3d7593a39..491efd060 100644 --- a/climateeconomics/tests/l1_test_gradient_utility_discipline.py +++ b/climateeconomics/tests/l1_test_gradient_utility_discipline.py @@ -93,7 +93,7 @@ def test_01_utility_analytic_grad_welfare(self): }) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_utility_discipline_welfare.pkl', discipline=disc_techno, step=1e-15,local_data = disc_techno.local_data, inputs=[f'{self.name}.{GlossaryCore.EconomicsDfValue}', f'{self.name}.{GlossaryCore.EnergyMeanPriceValue}', @@ -111,7 +111,7 @@ def test_02_utility_analytic_grad_welfare_no_population_multiplication_in_obj(se self.ee.load_study_from_input_dict(self.values_dict) self.ee.execute() - disc_techno = self.ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = self.ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_utility_discipline_welfare2.pkl', discipline=disc_techno, step=1e-15,local_data = disc_techno.local_data, inputs=[f'{self.name}.{GlossaryCore.EconomicsDfValue}', f'{self.name}.{GlossaryCore.EnergyMeanPriceValue}', diff --git a/climateeconomics/tests/l1_test_policy_model_gradient.py b/climateeconomics/tests/l1_test_policy_model_gradient.py index fcc19a370..761f0f3af 100644 --- a/climateeconomics/tests/l1_test_policy_model_gradient.py +++ b/climateeconomics/tests/l1_test_policy_model_gradient.py @@ -44,7 +44,7 @@ def test_policy_analytic_grad(self): self.model_name = 'policy' ns_dict = {GlossaryCore.NS_WITNESS: f'{self.name}', - 'ns_public': f'{self.name}',} + 'ns_public': f'{self.name}', } self.ee.ns_manager.add_ns_def(ns_dict) @@ -73,7 +73,7 @@ def test_policy_analytic_grad(self): self.ee.execute() disc = self.ee.dm.get_disciplines_with_name( - f'{self.name}.{self.model_name}')[0].mdo_discipline_wrapp.mdo_discipline + f'{self.name}.{self.model_name}')[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_policy_discipline.pkl', local_data = disc.local_data,discipline=disc, inputs=self.checked_inputs, outputs=[f'{self.name}.{GlossaryCore.CO2TaxesValue}'], step=1e-15, derr_approx='complex_step') @@ -82,7 +82,7 @@ def test_policy_analytic_grad_2(self): self.model_name = 'policy' ns_dict = {GlossaryCore.NS_WITNESS: f'{self.name}', - 'ns_public': f'{self.name}',} + 'ns_public': f'{self.name}', } self.ee.ns_manager.add_ns_def(ns_dict) @@ -110,7 +110,7 @@ def test_policy_analytic_grad_2(self): self.ee.load_study_from_input_dict(values_dict) self.ee.execute() disc = self.ee.dm.get_disciplines_with_name( - f'{self.name}.{self.model_name}')[0].mdo_discipline_wrapp.mdo_discipline + f'{self.name}.{self.model_name}')[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_policy_discipline2.pkl', discipline=disc, local_data = disc.local_data,inputs=self.checked_inputs, outputs=[f'{self.name}.{GlossaryCore.CO2TaxesValue}'], step=1e-15, derr_approx='complex_step') @@ -118,7 +118,7 @@ def test_policy_analytic_grad_3(self): self.model_name = 'policy' ns_dict = {GlossaryCore.NS_WITNESS: f'{self.name}', - 'ns_public': f'{self.name}',} + 'ns_public': f'{self.name}', } self.ee.ns_manager.add_ns_def(ns_dict) @@ -146,7 +146,6 @@ def test_policy_analytic_grad_3(self): self.ee.load_study_from_input_dict(values_dict) self.ee.execute() disc = self.ee.dm.get_disciplines_with_name( - f'{self.name}.{self.model_name}')[0].mdo_discipline_wrapp.mdo_discipline + f'{self.name}.{self.model_name}')[0].discipline_wrapp.discipline self.check_jacobian(location=dirname(__file__), filename='jacobian_policy_discipline3.pkl', discipline=disc, local_data = disc.local_data,inputs=self.checked_inputs, outputs=[f'{self.name}.{GlossaryCore.CO2TaxesValue}'], step=1e-15, derr_approx='complex_step') - diff --git a/climateeconomics/tests/l1_test_sectors_consumption_discipline.py b/climateeconomics/tests/l1_test_sectors_consumption_discipline.py index 26133d8e1..a148ff6fa 100644 --- a/climateeconomics/tests/l1_test_sectors_consumption_discipline.py +++ b/climateeconomics/tests/l1_test_sectors_consumption_discipline.py @@ -132,7 +132,7 @@ def test_analytic_grad(self): output_checked = [f"{name}.{GlossaryCore.SectorizedConsumptionDfValue}"] - disc_techno = ee.root_process.proxy_disciplines[0].mdo_discipline_wrapp.mdo_discipline + disc_techno = ee.root_process.proxy_disciplines[0].discipline_wrapp.discipline self.check_jacobian( location=dirname(__file__), diff --git a/climateeconomics/tests/l2_test_MDA_comparison_nr_gs_witness_full.py b/climateeconomics/tests/l2_test_MDA_comparison_nr_gs_witness_full.py index 64f648e0a..64d774496 100644 --- a/climateeconomics/tests/l2_test_MDA_comparison_nr_gs_witness_full.py +++ b/climateeconomics/tests/l2_test_MDA_comparison_nr_gs_witness_full.py @@ -67,7 +67,7 @@ def test_01_check_comparison_nr_gs(self): for dict_v in values_dict: full_values_dict.update(dict_v) - full_values_dict[f'{usecase.study_name}.sub_mda_class'] = 'GSNewtonMDA' + full_values_dict[f'{usecase.study_name}.inner_mda_name'] = 'MDAGSNewton' full_values_dict[f'{usecase.study_name}.linearization_mode'] = 'adjoint' full_values_dict[f'{usecase.study_name}.tolerance'] = 1e-7 full_values_dict[f'{usecase.study_name}.relax_factor'] = 0.99 @@ -92,7 +92,7 @@ def test_01_check_comparison_nr_gs(self): for dict_v in values_dict: full_values_dict.update(dict_v) - full_values_dict[f'{usecase.study_name}.sub_mda_class'] = 'MDAGaussSeidel' + full_values_dict[f'{usecase.study_name}.inner_mda_name'] = 'MDAGaussSeidel' full_values_dict[f'{usecase.study_name}.max_mda_iter'] = 200 full_values_dict[f'{usecase.study_name}.linearization_mode'] = 'adjoint' full_values_dict[f'{usecase.study_name}.tolerance'] = 1e-7 @@ -126,7 +126,7 @@ def test_01_check_comparison_nr_gs(self): for dict_v in values_dict: full_values_dict.update(dict_v) - full_values_dict[f'{usecase.study_name}.sub_mda_class'] = 'GSPureNewtonMDA' + full_values_dict[f'{usecase.study_name}.inner_mda_name'] = 'MDAGSNewton' full_values_dict[f'{usecase.study_name}.max_mda_iter'] = 200 full_values_dict[f'{usecase.study_name}.linearization_mode'] = 'adjoint' full_values_dict[f'{usecase.study_name}.tolerance'] = 1e-7 diff --git a/climateeconomics/tests/l2_test_witness_coarse_cache.py b/climateeconomics/tests/l2_test_witness_coarse_cache.py index f791514c4..3579a44bc 100644 --- a/climateeconomics/tests/l2_test_witness_coarse_cache.py +++ b/climateeconomics/tests/l2_test_witness_coarse_cache.py @@ -19,8 +19,8 @@ from energy_models.core.energy_process_builder import INVEST_DISCIPLINE_OPTIONS from energy_models.glossaryenergy import GlossaryEnergy -from gemseo.utils.compare_data_manager_tooling import compare_dict from sostrades_core.execution_engine.execution_engine import ExecutionEngine +from sostrades_core.tools.compare_data_manager_tooling import compare_dict from climateeconomics.sos_processes.iam.witness.witness_coarse_optim_process.usecase_witness_optim_invest_distrib import ( Study as witness_proc_usecase, @@ -58,7 +58,7 @@ def test_01_cache_on_witness_coarse_optim_with_unconverged_mda(self): full_values_dict['Test.WITNESS_MDO.algo_options'] = algo_options full_values_dict['Test.WITNESS_MDO.WITNESS_Eval.warm_start'] = False full_values_dict['Test.WITNESS_MDO.max_iter'] = 1 - full_values_dict['Test.WITNESS_MDO.WITNESS_Eval.sub_mda_class'] = 'MDAGaussSeidel' + full_values_dict['Test.WITNESS_MDO.WITNESS_Eval.inner_mda_name'] = 'MDAGaussSeidel' full_values_dict['Test.WITNESS_MDO.WITNESS_Eval.max_mda_iter'] = 1 # execute optim without cache and retrieve dm @@ -134,7 +134,7 @@ def test_02_cache_on_witness_coarse_optim(self): full_values_dict['Test.WITNESS_MDO.algo_options'] = algo_options full_values_dict['Test.WITNESS_MDO.WITNESS_Eval.warm_start'] = False full_values_dict['Test.WITNESS_MDO.max_iter'] = 2 - full_values_dict['Test.WITNESS_MDO.WITNESS_Eval.sub_mda_class'] = 'MDAGaussSeidel' + full_values_dict['Test.WITNESS_MDO.WITNESS_Eval.inner_mda_name'] = 'MDAGaussSeidel' full_values_dict['Test.WITNESS_MDO.WITNESS_Eval.max_mda_iter'] = 10 # execute optim without cache and retrieve dm diff --git a/climateeconomics/tests/l2_test_witness_coarse_df_desynchro.py b/climateeconomics/tests/l2_test_witness_coarse_df_desynchro.py index fdf324333..4226629bf 100644 --- a/climateeconomics/tests/l2_test_witness_coarse_df_desynchro.py +++ b/climateeconomics/tests/l2_test_witness_coarse_df_desynchro.py @@ -60,22 +60,22 @@ def test_01_desynchro_on_witness_coarse_optim_outputs(self): full_values_dict['Test.WITNESS_MDO.max_iter'] = 2 self.ee.load_study_from_input_dict(full_values_dict) - sub_mda_class_list = ['MDAJacobi', 'MDAGaussSeidel', 'MDANewtonRaphson', 'GSNewtonMDA', - 'GSPureNewtonMDA', 'GSorNewtonMDA'] + inner_mda_name_list = ['MDAJacobi', 'MDAGaussSeidel', 'MDANewtonRaphson', 'MDAGSNewton', + 'MDAGSNewton', 'GSorNewtonMDA'] - for sub_mda_class in sub_mda_class_list: + for inner_mda_name in inner_mda_name_list: dict_values = {} - dict_values['Test.WITNESS_MDO.WITNESS_Eval.sub_mda_class'] = sub_mda_class + dict_values['Test.WITNESS_MDO.WITNESS_Eval.inner_mda_name'] = inner_mda_name - if sub_mda_class == 'MDAJacobi': + if inner_mda_name == 'MDAJacobi': dict_values['Test.WITNESS_MDO.WITNESS_Eval.max_mda_iter'] = 20 else: dict_values['Test.WITNESS_MDO.WITNESS_Eval.max_mda_iter'] = 5 self.ee.load_study_from_input_dict(dict_values) - # execute process with sub_mda_class + # execute process with inner_mda_name self.ee.execute() df_coupled = self.ee.dm.get_value( diff --git a/climateeconomics/tests/utility_tests/_l0_test_witnessMDA_perfos.py b/climateeconomics/tests/utility_tests/_l0_test_witnessMDA_perfos.py index fe93addff..54c022f48 100644 --- a/climateeconomics/tests/utility_tests/_l0_test_witnessMDA_perfos.py +++ b/climateeconomics/tests/utility_tests/_l0_test_witnessMDA_perfos.py @@ -87,12 +87,12 @@ def test_01_witness_perfos_execute(self): input_dict_to_load[f'{self.name}.n_processes'] = 1 input_dict_to_load[f'{self.name}.max_mda_iter'] = 300 - input_dict_to_load[f'{self.name}.sub_mda_class'] = 'GSPureNewtonMDA' + input_dict_to_load[f'{self.name}.inner_mda_name'] = 'MDAGSNewton' self.ee.load_study_from_input_dict(input_dict_to_load) profil = cProfile.Profile() profil.enable() self.ee.execute() - mda_class = self.ee.dm.get_value(f'{self.name}.sub_mda_class') + mda_class = self.ee.dm.get_value(f'{self.name}.inner_mda_name') n_processes = self.ee.dm.get_value(f'{self.name}.n_processes') profil.disable() result = StringIO() @@ -203,12 +203,12 @@ def test_02_witness_perfos_execute_GSNR(self): input_dict_to_load[f'{self.name}.n_processes'] = 1 input_dict_to_load[f'{self.name}.max_mda_iter'] = 300 - input_dict_to_load[f'{self.name}.sub_mda_class'] = 'GSNewtonMDA' + input_dict_to_load[f'{self.name}.inner_mda_name'] = 'MDAGSNewton' self.ee.load_study_from_input_dict(input_dict_to_load) profil = cProfile.Profile() profil.enable() self.ee.execute() - mda_class = self.ee.dm.get_value(f'{self.name}.sub_mda_class') + mda_class = self.ee.dm.get_value(f'{self.name}.inner_mda_name') n_processes = self.ee.dm.get_value(f'{self.name}.n_processes') profil.disable() result = StringIO() @@ -313,12 +313,12 @@ def test_03_witness_perfos_execute_PureParallel(self): input_dict_to_load[f'{self.name}.n_processes'] = 1 input_dict_to_load[f'{self.name}.max_mda_iter'] = 300 - input_dict_to_load[f'{self.name}.sub_mda_class'] = 'GSPureNewtonMDA' + input_dict_to_load[f'{self.name}.inner_mda_name'] = 'MDAGSNewton' self.ee.load_study_from_input_dict(input_dict_to_load) profil = cProfile.Profile() profil.enable() self.ee.execute() - mda_class = self.ee.dm.get_value(f'{self.name}.sub_mda_class') + mda_class = self.ee.dm.get_value(f'{self.name}.inner_mda_name') n_processes = self.ee.dm.get_value(f'{self.name}.n_processes') profil.disable() result = StringIO() @@ -419,12 +419,12 @@ def test_04_witness_coarseperfos_multiproc(self): input_dict_to_load[f'{self.name}.n_processes'] = n_proc input_dict_to_load[f'{self.name}.max_mda_iter'] = 300 - input_dict_to_load[f'{self.name}.sub_mda_class'] = 'GSPureNewtonMDA' + input_dict_to_load[f'{self.name}.inner_mda_name'] = 'MDAGSNewton' self.ee.load_study_from_input_dict(input_dict_to_load) profil = cProfile.Profile() profil.enable() self.ee.execute() - mda_class = self.ee.dm.get_value(f'{self.name}.sub_mda_class') + mda_class = self.ee.dm.get_value(f'{self.name}.inner_mda_name') n_processes = self.ee.dm.get_value(f'{self.name}.n_processes') profil.disable() result = StringIO() @@ -527,12 +527,12 @@ def test_05_witness_perfos_multiproc(self): input_dict_to_load[f'{self.name}.n_processes'] = n_proc input_dict_to_load[f'{self.name}.max_mda_iter'] = 300 - input_dict_to_load[f'{self.name}.sub_mda_class'] = 'GSPureNewtonMDA' + input_dict_to_load[f'{self.name}.inner_mda_name'] = 'MDAGSNewton' self.ee.load_study_from_input_dict(input_dict_to_load) profil = cProfile.Profile() profil.enable() self.ee.execute() - mda_class = self.ee.dm.get_value(f'{self.name}.sub_mda_class') + mda_class = self.ee.dm.get_value(f'{self.name}.inner_mda_name') n_processes = self.ee.dm.get_value(f'{self.name}.n_processes') profil.disable() result = StringIO() @@ -646,7 +646,7 @@ def execute_full_usecase(n_processes): input_dict_to_load[f'{self.name}.n_processes'] = n_processes input_dict_to_load[f'{self.name}.max_mda_iter'] = 300 - input_dict_to_load[f'{self.name}.sub_mda_class'] = 'GSPureNewtonMDA' + input_dict_to_load[f'{self.name}.inner_mda_name'] = 'MDAGSNewton' self.ee.load_study_from_input_dict(input_dict_to_load) profil = cProfile.Profile() profil.enable() @@ -858,7 +858,7 @@ def test_07_witness_memory_perfos(self): # input_dict_to_load[f'{self.name}.WITNESS_MDO.WITNESS_Eval.max_mda_iter'] = 5 input_dict_to_load[f'{self.name}.max_mda_iter'] = 300 - input_dict_to_load[f'{self.name}.sub_mda_class'] = 'GSPureNewtonMDA' + input_dict_to_load[f'{self.name}.inner_mda_name'] = 'MDAGSNewton' self.ee.load_study_from_input_dict(input_dict_to_load) import tracemalloc @@ -974,7 +974,7 @@ def launch_execution(self, mda_type, n_proc=1): input_dict_to_load[f'{self.name}.n_processes'] = n_proc input_dict_to_load[f'{self.name}.max_mda_iter'] = 300 - input_dict_to_load[f'{self.name}.sub_mda_class'] = mda_type + input_dict_to_load[f'{self.name}.inner_mda_name'] = mda_type self.ee.load_study_from_input_dict(input_dict_to_load) profil = cProfile.Profile() @@ -1063,8 +1063,8 @@ def get_operation_bar_chart(labels, values, title='Fig Title', save=False, fig.savefig(join(dirname(__file__), f'perfo_dir/{filename}.jpg')) return fig - case_dict = {'GSNR-sequential': [1, 'GSNewtonMDA'], 'GSNR-10core': [10, 'GSNewtonMDA'], - 'GSNR-64core': [64, 'GSNewtonMDA']} + case_dict = {'GSNR-sequential': [1, 'MDAGSNewton'], 'GSNR-10core': [10, 'MDAGSNewton'], + 'GSNR-64core': [64, 'MDAGSNewton']} operations_dict = { 'Total': None, @@ -1148,7 +1148,7 @@ def launch_execution(self, mda_type, n_proc=1): input_dict_to_load[f'{self.name}.n_processes'] = n_proc input_dict_to_load[f'{self.name}.max_mda_iter'] = 300 - input_dict_to_load[f'{self.name}.sub_mda_class'] = mda_type + input_dict_to_load[f'{self.name}.inner_mda_name'] = mda_type self.ee.load_study_from_input_dict(input_dict_to_load) profil = cProfile.Profile() @@ -1238,8 +1238,8 @@ def get_operation_bar_chart(labels, values, title='Fig Title', save=False, return fig case_dict = { - 'GSNR-sequential': [1, 'GSNewtonMDA'], 'GSNR-10thread': [10, 'GSNewtonMDA'], - 'GSNR-64thread': [64, 'GSNewtonMDA'] + 'GSNR-sequential': [1, 'MDAGSNewton'], 'GSNR-10thread': [10, 'MDAGSNewton'], + 'GSNR-64thread': [64, 'MDAGSNewton'] } operations_dict = { 'Total': None, diff --git a/climateeconomics/tests/utility_tests/_l0_test_witness_woenergy_perfos.py b/climateeconomics/tests/utility_tests/_l0_test_witness_woenergy_perfos.py index 7304bebb3..688f5ae29 100644 --- a/climateeconomics/tests/utility_tests/_l0_test_witness_woenergy_perfos.py +++ b/climateeconomics/tests/utility_tests/_l0_test_witness_woenergy_perfos.py @@ -98,13 +98,13 @@ def test_01_witness_perfos_execute(self): input_dict_to_load[f'{self.name}.{GlossaryCore.CO2EmissionsGtValue}'] = self.co2_emissions_gt input_dict_to_load[f'{self.name}.{GlossaryCore.EnergyPriceValue}'] = energy_mean_price input_dict_to_load[f'{self.name}.CCS_price'] = CCS_price - input_dict_to_load[f'{self.name}.sub_mda_class'] = "MDANewtonRaphson" + input_dict_to_load[f'{self.name}.inner_mda_name'] = "MDANewtonRaphson" input_dict_to_load[f'{self.name}.n_processes'] = 1 self.ee.load_study_from_input_dict(input_dict_to_load) profil = cProfile.Profile() profil.enable() self.ee.execute() - mda_class = self.ee.dm.get_value(f'{self.name}.sub_mda_class') + mda_class = self.ee.dm.get_value(f'{self.name}.inner_mda_name') n_processes = self.ee.dm.get_value(f'{self.name}.n_processes') profil.disable() result = StringIO() diff --git a/climateeconomics/tests/utility_tests/_l1_test_gradient_infos_witness.py b/climateeconomics/tests/utility_tests/_l1_test_gradient_infos_witness.py index 82e01c90f..cd2ab95d7 100644 --- a/climateeconomics/tests/utility_tests/_l1_test_gradient_infos_witness.py +++ b/climateeconomics/tests/utility_tests/_l1_test_gradient_infos_witness.py @@ -68,7 +68,7 @@ def test_01_gradient_objective_wrt_design_var_on_witness_full(self): for dict_item in usecase.setup_usecase(): values_dict.update(dict_item) - values_dict['Test.WITNESS_Eval.sub_mda_class'] = 'MDAGaussSeidel' + values_dict['Test.WITNESS_Eval.inner_mda_name'] = 'MDAGaussSeidel' # values_dict['Test.WITNESS_Eval.max_mda_iter'] = 1 values_dict['Test.WITNESS_Eval.WITNESS.EnergyMix.methane.{GlossaryEnergy.FossilGas}.methane_FossilGas_array_mix'] = 81 * [30.] self.ee.load_study_from_input_dict(values_dict) diff --git a/climateeconomics/tests/utility_tests/_l1_test_gradient_witness_full.py b/climateeconomics/tests/utility_tests/_l1_test_gradient_witness_full.py index 0f3059b81..895970133 100644 --- a/climateeconomics/tests/utility_tests/_l1_test_gradient_witness_full.py +++ b/climateeconomics/tests/utility_tests/_l1_test_gradient_witness_full.py @@ -81,7 +81,7 @@ def test_01_gradient_objective_wrt_state_var_on_witness_full(self): values_dict['Test.tolerance_linear_solver_MDO'] = 1.0e-12 values_dict['Test.linearization_mode'] = 'adjoint' values_dict['Test.tolerance'] = 1.0e-10 - values_dict['Test.sub_mda_class'] = 'MDAGaussSeidel' + values_dict['Test.inner_mda_name'] = 'MDAGaussSeidel' self.ee.load_study_from_input_dict(values_dict) @@ -127,7 +127,7 @@ def test_02_gradient_residus_wrt_state_var_on_witness_full(self): values_dict['Test.tolerance_linear_solver_MDO'] = 1.0e-8 values_dict['Test.linearization_mode'] = 'adjoint' values_dict['Test.tolerance'] = 1.0e-10 - values_dict['Test.sub_mda_class'] = 'MDAGaussSeidel' + values_dict['Test.inner_mda_name'] = 'MDAGaussSeidel' self.ee.load_study_from_input_dict(values_dict) @@ -211,7 +211,7 @@ def test_03_gradient_residus_wrt_design_var_on_witness_full(self): full_values_dict[f'{usecase.study_name}.WITNESS_Eval.linearization_mode'] = 'adjoint' full_values_dict[f'{usecase.study_name}.WITNESS_Eval.tolerance'] = 1.0e-12 full_values_dict[f'{usecase.study_name}.WITNESS_Eval.max_mda_iter'] = 200 - full_values_dict[f'{usecase.study_name}.WITNESS_Eval.sub_mda_class'] = 'MDAGaussSeidel' + full_values_dict[f'{usecase.study_name}.WITNESS_Eval.inner_mda_name'] = 'MDAGaussSeidel' self.ee.load_study_from_input_dict(full_values_dict) @@ -268,7 +268,7 @@ def test_04_gradient_objective_wrt_design_var_on_witness_full(self): full_values_dict['Test.WITNESS_Eval.linearization_mode'] = 'adjoint' full_values_dict['Test.WITNESS_Eval.tolerance'] = 1.0e-10 full_values_dict['Test.WITNESS_Eval.warm_start'] = False - full_values_dict['Test.WITNESS_Eval.sub_mda_class'] = 'MDAGaussSeidel' + full_values_dict['Test.WITNESS_Eval.inner_mda_name'] = 'MDAGaussSeidel' self.ee.load_study_from_input_dict(full_values_dict) disc = self.ee.root_process.proxy_disciplines[0] @@ -355,7 +355,7 @@ def test_05_adjoint_with_bsplines(self): full_values_dict[f'{usecase.study_name}.WITNESS_Eval.linearization_mode'] = 'adjoint' full_values_dict[f'{usecase.study_name}.WITNESS_Eval.tolerance'] = 1.0e-12 full_values_dict[f'{usecase.study_name}.WITNESS_Eval.max_mda_iter'] = 200 - full_values_dict[f'{usecase.study_name}.WITNESS_Eval.sub_mda_class'] = 'MDAGaussSeidel' + full_values_dict[f'{usecase.study_name}.WITNESS_Eval.inner_mda_name'] = 'MDAGaussSeidel' input_full_names = ['Test.WITNESS_Eval.WITNESS.CO2_taxes_array'] nb_poles = 5 diff --git a/climateeconomics/tests/utility_tests/_l2_test_EnergyMix_co2emissions_nonreg.py b/climateeconomics/tests/utility_tests/_l2_test_EnergyMix_co2emissions_nonreg.py index 1c9e9fa1b..74529d4f2 100644 --- a/climateeconomics/tests/utility_tests/_l2_test_EnergyMix_co2emissions_nonreg.py +++ b/climateeconomics/tests/utility_tests/_l2_test_EnergyMix_co2emissions_nonreg.py @@ -24,7 +24,7 @@ from energy_models.sos_processes.energy.MDA.energy_process_v0.usecase import ( Study as EnergyMDA, ) -from gemseo.utils.compare_data_manager_tooling import compare_dict +from sostrades_core.tools.compare_data_manager_tooling import compare_dict from climateeconomics.glossarycore import GlossaryCore @@ -54,7 +54,7 @@ def run_non_reg(self, usecase, dm_ref, compare_list, usecase_name): # load and run usecase usecase.load_data() inputs_dict = {} - inputs_dict[usecase.ee.dm.get_all_namespaces_from_var_name('sub_mda_class')[ + inputs_dict[usecase.ee.dm.get_all_namespaces_from_var_name('inner_mda_name')[ 0]] = 'MDAGaussSeidel' usecase.ee.load_study_from_input_dict(inputs_dict) usecase.run(for_test=False) diff --git a/climateeconomics/tests/utility_tests/_l2_test_constraints_gradient_witness.py b/climateeconomics/tests/utility_tests/_l2_test_constraints_gradient_witness.py index 67e09057e..b5245f274 100644 --- a/climateeconomics/tests/utility_tests/_l2_test_constraints_gradient_witness.py +++ b/climateeconomics/tests/utility_tests/_l2_test_constraints_gradient_witness.py @@ -112,7 +112,7 @@ def test_01_constraints_wrt_design_var_bspline(self): full_values_dict[f'{usecase.study_name}.WITNESS_Eval.linearization_mode'] = 'adjoint' full_values_dict[f'{usecase.study_name}.WITNESS_Eval.tolerance'] = 1.0e-12 full_values_dict[f'{usecase.study_name}.WITNESS_Eval.max_mda_iter'] = 200 - full_values_dict[f'{usecase.study_name}.WITNESS_Eval.sub_mda_class'] = 'MDAGaussSeidel' + full_values_dict[f'{usecase.study_name}.WITNESS_Eval.inner_mda_name'] = 'MDAGaussSeidel' input_full_names = [] nb_poles = 8 diff --git a/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_coarse.py b/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_coarse.py index a4ea32a14..8c8882230 100644 --- a/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_coarse.py +++ b/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_coarse.py @@ -112,7 +112,7 @@ def test_02_gradient_objective_constraint_wrt_design_var_on_witness_coarse_subpr full_values_dict[f'{self.name}.{usecase.coupling_name}.warm_start'] = False full_values_dict[f'{self.name}.{usecase.coupling_name}.tolerance'] = 1.0e-12 full_values_dict[f'{self.name}.{usecase.coupling_name}.chain_linearize'] = False - full_values_dict[f'{self.name}.{usecase.coupling_name}.sub_mda_class'] = 'MDAGaussSeidel' + full_values_dict[f'{self.name}.{usecase.coupling_name}.inner_mda_name'] = 'MDAGaussSeidel' self.ee.load_study_from_input_dict(full_values_dict) disc = self.ee.root_process.proxy_disciplines[0] @@ -180,7 +180,7 @@ def test_03_gradient_lagrangian_objective_wrt_design_var_on_witness_coarse_subpr full_values_dict[f'{self.name}.{usecase.coupling_name}.warm_start'] = False full_values_dict[f'{self.name}.{usecase.coupling_name}.tolerance'] = 1.0e-12 full_values_dict[f'{self.name}.{usecase.coupling_name}.chain_linearize'] = False - full_values_dict[f'{self.name}.{usecase.coupling_name}.sub_mda_class'] = 'MDAGaussSeidel' + full_values_dict[f'{self.name}.{usecase.coupling_name}.inner_mda_name'] = 'MDAGaussSeidel' self.ee.load_study_from_input_dict(full_values_dict) disc = self.ee.root_process.proxy_disciplines[0] @@ -254,7 +254,7 @@ def test_05_gradient_witness_coarse_eachdiscipline(self): full_values_dict[f'{self.name}.warm_start'] = False full_values_dict[f'{self.name}.tolerance'] = 1.0e-10 full_values_dict[f'{self.name}.chain_linearize'] = False - full_values_dict[f'{self.name}.sub_mda_class'] = 'GSNewtonMDA' + full_values_dict[f'{self.name}.inner_mda_name'] = 'MDAGSNewton' full_values_dict[f'{self.name}.max_mda_iter'] = 1 self.ee.load_study_from_input_dict(full_values_dict) @@ -262,7 +262,7 @@ def test_05_gradient_witness_coarse_eachdiscipline(self): full_values_dict = {} full_values_dict[f'{self.name}.CCUS.ccs_percentage'] = pd.DataFrame( {GlossaryCore.Years: np.arange(GlossaryCore.YearStartDefault, GlossaryCore.YearEndDefault + 1), 'ccs_percentage': 25}) - full_values_dict[f'{self.name}.sub_mda_class'] = 'GSNewtonMDA' + full_values_dict[f'{self.name}.inner_mda_name'] = 'MDAGSNewton' full_values_dict[f'{self.name}.max_mda_iter'] = 1 self.ee.load_study_from_input_dict(full_values_dict) disc = self.ee.root_process.proxy_disciplines[0] @@ -382,7 +382,7 @@ def test_06_gradient_witness_coarse_subprocess_each_discipline(self): full_values_dict[f'{self.name}.warm_start'] = False full_values_dict[f'{self.name}.tolerance'] = 1.0e-10 full_values_dict[f'{self.name}.chain_linearize'] = False - full_values_dict[f'{self.name}.WITNESS_Eval.sub_mda_class'] = 'GSNewtonMDA' + full_values_dict[f'{self.name}.WITNESS_Eval.inner_mda_name'] = 'MDAGSNewton' full_values_dict[f'{self.name}.WITNESS_Eval.max_mda_iter'] = 2 self.ee.load_study_from_input_dict(full_values_dict) diff --git a/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_dev.py b/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_dev.py index 3598923d4..78b7330d3 100644 --- a/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_dev.py +++ b/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_dev.py @@ -74,7 +74,7 @@ def test_01_gradient_dev_specific_disciplines(self): full_values_dict[f'{self.name}.WITNESS_MDO.WITNESS_Eval.warm_start'] = False full_values_dict[f'{self.name}.WITNESS_MDO.WITNESS_Eval.tolerance'] = 1.0e-12 full_values_dict[f'{self.name}.WITNESS_MDO.WITNESS_Eval.chain_linearize'] = False - full_values_dict[f'{self.name}.WITNESS_MDO.WITNESS_Eval.sub_mda_class'] = 'MDANewtonRaphson' + full_values_dict[f'{self.name}.WITNESS_MDO.WITNESS_Eval.inner_mda_name'] = 'MDANewtonRaphson' full_values_dict[f'{self.name}.WITNESS_MDO.WITNESS_Eval.max_mda_iter'] = 1 self.ee.load_study_from_input_dict(full_values_dict) @@ -142,7 +142,7 @@ def test_02_gradient_dev_root_process(self): full_values_dict[f'{self.name}.warm_start'] = False full_values_dict[f'{self.name}.tolerance'] = 1.0e-12 full_values_dict[f'{self.name}.chain_linearize'] = False - full_values_dict[f'{self.name}.sub_mda_class'] = 'MDAGaussSeidel' + full_values_dict[f'{self.name}.inner_mda_name'] = 'MDAGaussSeidel' full_values_dict[f'{self.name}.max_mda_iter'] = 1 self.ee.load_study_from_input_dict(full_values_dict) self.ee.execute() @@ -204,7 +204,7 @@ def test_05_gradient_lagrangian_objective_wrt_csv_design_var_on_witness_full_sub full_values_dict[f'{self.name}.warm_start'] = False full_values_dict[f'{self.name}.tolerance'] = 1.0e-12 full_values_dict[f'{self.name}.chain_linearize'] = False - full_values_dict[f'{self.name}.sub_mda_class'] = 'MDAGaussSeidel' + full_values_dict[f'{self.name}.inner_mda_name'] = 'MDAGaussSeidel' full_values_dict[f'{self.name}.max_mda_iter'] = 1 self.ee.load_study_from_input_dict(full_values_dict) diff --git a/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_full.py b/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_full.py index 689e99bd7..8185cd3e9 100644 --- a/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_full.py +++ b/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_full.py @@ -96,7 +96,7 @@ def _test_01_gradient_objective_wrt_state_var_on_witness_full_mda(self): values_dict[f'{self.name}.tolerance_linear_solver_MDO'] = 1.0e-12 values_dict[f'{self.name}.linearization_mode'] = 'adjoint' values_dict[f'{self.name}.tolerance'] = 1.0e-10 - values_dict[f'{self.name}.sub_mda_class'] = 'MDAGaussSeidel' + values_dict[f'{self.name}.inner_mda_name'] = 'MDAGaussSeidel' self.ee.load_study_from_input_dict(values_dict) @@ -174,7 +174,7 @@ def test_02_gradient_objective_constraint_wrt_design_var_on_witness_full_subproc full_values_dict[f'{self.name}.{usecase.coupling_name}.warm_start'] = False full_values_dict[f'{self.name}.{usecase.coupling_name}.tolerance'] = 1.0e-12 full_values_dict[f'{self.name}.{usecase.coupling_name}.chain_linearize'] = False - full_values_dict[f'{self.name}.{usecase.coupling_name}.sub_mda_class'] = 'MDAGaussSeidel' + full_values_dict[f'{self.name}.{usecase.coupling_name}.inner_mda_name'] = 'MDAGaussSeidel' self.ee.load_study_from_input_dict(full_values_dict) disc = self.ee.root_process.proxy_disciplines[0] @@ -244,7 +244,7 @@ def test_03_gradient_lagrangian_objective_wrt_design_var_on_witness_full_subproc full_values_dict[f'{self.name}.{usecase.coupling_name}.warm_start'] = False full_values_dict[f'{self.name}.{usecase.coupling_name}.tolerance'] = 1.0e-12 full_values_dict[f'{self.name}.{usecase.coupling_name}.chain_linearize'] = False - full_values_dict[f'{self.name}.{usecase.coupling_name}.sub_mda_class'] = 'MDAGaussSeidel' + full_values_dict[f'{self.name}.{usecase.coupling_name}.inner_mda_name'] = 'MDAGaussSeidel' self.ee.load_study_from_input_dict(full_values_dict) disc = self.ee.root_process.proxy_disciplines[0] @@ -317,7 +317,7 @@ def test_04_gradient_lagrangian_objective_wrt_csv_design_var_on_witness_full_sub full_values_dict[f'{self.name}.{usecase.coupling_name}.warm_start'] = False full_values_dict[f'{self.name}.{usecase.coupling_name}.tolerance'] = 1.0e-12 full_values_dict[f'{self.name}.{usecase.coupling_name}.chain_linearize'] = False - full_values_dict[f'{self.name}.{usecase.coupling_name}.sub_mda_class'] = 'MDAGaussSeidel' + full_values_dict[f'{self.name}.{usecase.coupling_name}.inner_mda_name'] = 'MDAGaussSeidel' self.ee.load_study_from_input_dict(full_values_dict) @@ -406,7 +406,7 @@ def test_05_gradient_lagrangian_objective_wrt_csv_design_var_on_witness_full_sub full_values_dict[f'{self.name}.{usecase.coupling_name}.warm_start'] = False full_values_dict[f'{self.name}.{usecase.coupling_name}.tolerance'] = 1.0e-12 full_values_dict[f'{self.name}.{usecase.coupling_name}.chain_linearize'] = False - full_values_dict[f'{self.name}.{usecase.coupling_name}.sub_mda_class'] = 'GSNewtonMDA' + full_values_dict[f'{self.name}.{usecase.coupling_name}.inner_mda_name'] = 'MDAGSNewton' full_values_dict[f'{self.name}.{usecase.coupling_name}.max_mda_iter'] = 2 full_values_dict[f'{self.name}.{usecase.coupling_name}.WITNESS.CCUS.carbon_capture.flue_gas_effect'] = False @@ -456,7 +456,7 @@ def test_05_gradient_lagrangian_objective_wrt_csv_design_var_on_witness_full_sub # f'{self.name}.{usecase.coupling_name}.{usecase.extra_name}.CCUS.{energy}.{technology}.{energy_wo_dot}_{technology_wo_dot}_array_mix') # values_dict_step = {} - # values_dict_step[f'{self.name}.{usecase.coupling_name}.sub_mda_class'] = 'GSNewtonMDA' + # values_dict_step[f'{self.name}.{usecase.coupling_name}.inner_mda_name'] = 'MDAGSNewton' # values_dict_step[f'{self.name}.{usecase.coupling_name}.max_mda_iter_gs'] = 2 # values_dict_step[f'{self.name}.{usecase.coupling_name}.max_mda_iter'] = step + 2 # self.ee.load_study_from_input_dict(values_dict_step) @@ -621,7 +621,7 @@ def test_06_gradient_lagrangian_objective_wrt_csv_design_var_on_crashed_x(self): full_values_dict[f'{self.name}.{usecase.coupling_name}.warm_start'] = False full_values_dict[f'{self.name}.{usecase.coupling_name}.tolerance'] = 1.0e-12 full_values_dict[f'{self.name}.{usecase.coupling_name}.chain_linearize'] = False - full_values_dict[f'{self.name}.{usecase.coupling_name}.sub_mda_class'] = 'MDAGaussSeidel' + full_values_dict[f'{self.name}.{usecase.coupling_name}.inner_mda_name'] = 'MDAGaussSeidel' full_values_dict[f'{self.name}.{usecase.coupling_name}.max_mda_iter'] = 200 self.ee.load_study_from_input_dict(full_values_dict) # self.ee.execute() @@ -698,7 +698,7 @@ def test_07_gradient_all_disciplines_on_crashed_x(self): full_values_dict[f'{self.name}.{usecase.coupling_name}.warm_start'] = False full_values_dict[f'{self.name}.{usecase.coupling_name}.tolerance'] = 1.0e-12 full_values_dict[f'{self.name}.{usecase.coupling_name}.chain_linearize'] = False - full_values_dict[f'{self.name}.{usecase.coupling_name}.sub_mda_class'] = 'MDAGaussSeidel' + full_values_dict[f'{self.name}.{usecase.coupling_name}.inner_mda_name'] = 'MDAGaussSeidel' full_values_dict[f'{self.name}.{usecase.coupling_name}.max_mda_iter'] = 200 self.ee.load_study_from_input_dict(full_values_dict) # self.ee.execute() diff --git a/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_full_invest_distrib.py b/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_full_invest_distrib.py index 18ab00562..179354051 100644 --- a/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_full_invest_distrib.py +++ b/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_full_invest_distrib.py @@ -88,7 +88,7 @@ def test_05_gradient_lagrangian_objective_wrt_csv_design_var_on_witness_full_sub full_values_dict[f'{self.name}.{usecase.coupling_name}.warm_start'] = False full_values_dict[f'{self.name}.{usecase.coupling_name}.tolerance'] = 1.0e-12 full_values_dict[f'{self.name}.{usecase.coupling_name}.chain_linearize'] = False - full_values_dict[f'{self.name}.{usecase.coupling_name}.sub_mda_class'] = 'GSNewtonMDA' + full_values_dict[f'{self.name}.{usecase.coupling_name}.inner_mda_name'] = 'MDAGSNewton' full_values_dict[f'{self.name}.{usecase.coupling_name}.max_mda_iter'] = 1 self.ee.load_study_from_input_dict(full_values_dict) diff --git a/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_wo_energy.py b/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_wo_energy.py index 5c8975ac8..beda97e54 100644 --- a/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_wo_energy.py +++ b/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_wo_energy.py @@ -85,7 +85,7 @@ def setUp(self): values_dict[f'{self.name}.tolerance'] = 1.0e-12 values_dict[f'{self.name}.tolerance_linear_solver_MDO'] = 1.0e-14 - values_dict[f'{self.name}.sub_mda_class'] = 'MDAGaussSeidel' + values_dict[f'{self.name}.inner_mda_name'] = 'MDAGaussSeidel' values_dict[f'{self.name}.{GlossaryCore.CO2TaxesValue}'] = pd.DataFrame( {GlossaryCore.Years: np.arange(year_start, year_end + 1), GlossaryCore.CO2Tax: 50.0}) self.ee.load_study_from_input_dict(values_dict) diff --git a/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_wo_energy_after_MDA.py b/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_wo_energy_after_MDA.py index 1575578b4..4b2930f3e 100644 --- a/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_wo_energy_after_MDA.py +++ b/climateeconomics/tests/utility_tests/_l2_test_gradient_witness_wo_energy_after_MDA.py @@ -88,7 +88,7 @@ def setUp(self): values_dict[f'{self.name}.linearization_mode'] = 'adjoint' # dict_v[f'{self.name}.warm_start'] = True values_dict[f'{self.name}.tolerance'] = 1.0e-10 - values_dict[f'{self.name}.sub_mda_class'] = 'MDAGaussSeidel' + values_dict[f'{self.name}.inner_mda_name'] = 'MDAGaussSeidel' values_dict[f'{self.name}.{GlossaryCore.TemperatureDfValue}'] = temperature_df_y self.ee.load_study_from_input_dict(values_dict) self.ee.execute() diff --git a/climateeconomics/tests/witness_jacobian_disc_test.py b/climateeconomics/tests/witness_jacobian_disc_test.py index 61f4f84bc..4ca0b72dd 100644 --- a/climateeconomics/tests/witness_jacobian_disc_test.py +++ b/climateeconomics/tests/witness_jacobian_disc_test.py @@ -45,7 +45,7 @@ def all_usecase_disciplines_jacobian_test(self, usecase, directory=AbstractJacob full_values_dict[f'{self.name}.{usecase.coupling_name}.warm_start'] = False full_values_dict[f'{self.name}.{usecase.coupling_name}.tolerance'] = 1.0e-12 full_values_dict[f'{self.name}.{usecase.coupling_name}.chain_linearize'] = False - full_values_dict[f'{self.name}.{usecase.coupling_name}.sub_mda_class'] = 'MDAGaussSeidel' + full_values_dict[f'{self.name}.{usecase.coupling_name}.inner_mda_name'] = 'MDAGaussSeidel' full_values_dict[f'{self.name}.{usecase.coupling_name}.max_mda_iter'] = max_mda_iter self.ee.load_study_from_input_dict(full_values_dict) @@ -159,7 +159,7 @@ def all_usecase_disciplines_jacobian_test(self, usecase, directory=AbstractJacob # full_values_dict[f'{self.name}.{usecase.coupling_name}.warm_start'] = False # full_values_dict[f'{self.name}.{usecase.coupling_name}.tolerance'] = 1.0e-12 # full_values_dict[f'{self.name}.{usecase.coupling_name}.chain_linearize'] = False - # full_values_dict[f'{self.name}.{usecase.coupling_name}.sub_mda_class'] = 'MDAGaussSeidel' + # full_values_dict[f'{self.name}.{usecase.coupling_name}.inner_mda_name'] = 'MDAGaussSeidel' # full_values_dict[f'{self.name}.{usecase.coupling_name}.max_mda_iter'] = 2 # # self.ee.load_study_from_input_dict(full_values_dict) diff --git a/platform_version_required.txt b/platform_version_required.txt index 15a2b33b2..8eb389131 100644 --- a/platform_version_required.txt +++ b/platform_version_required.txt @@ -1 +1 @@ -v4.2.0 \ No newline at end of file +v5.0.0 \ No newline at end of file diff --git a/requirements.in b/requirements.in index baec42c44..a6a18cc3f 100644 --- a/requirements.in +++ b/requirements.in @@ -8,9 +8,10 @@ matplotlib>=3.4.3 numpy==1.24.4 pandas==2.2.2 plotly>=5.3.0 -scipy==1.10.1 +scipy==1.13.1 seaborn==0.9.0 tqdm>=4.61.0 +autograd==1.7.0 # Development requirements pytest>=7.4.3