Skip to content

Commit

Permalink
merge post_integration to validation
Browse files Browse the repository at this point in the history
  • Loading branch information
b4pm-devops committed Jun 24, 2024
2 parents 61b0e69 + 7d2a217 commit f7bf13a
Show file tree
Hide file tree
Showing 14 changed files with 286 additions and 106 deletions.
2 changes: 1 addition & 1 deletion energy_models/core/investments/convex_combination_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def compute_convex_combination(self):

self.convex_combination_df = pd.DataFrame({
'years': self.dataframes[0]['years'],
**dict(zip(self.dataframes[0].columns, convex_combination.T)) # demander à Antoine
**dict(zip(self.dataframes[0].columns, convex_combination.T))
})

def compute(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,10 @@ The Investments Profile Builder uses the following formulas :
$$

These calculations ensure that the investments are proportionally distributed based on the given coefficients.
The output investment profile can be exported either as a dataframe 'invest_mix' where the values of the variables
are provided for each year or as a 1D array per variable (named 'variable_array_mix') where the values are provided
only for a selected number of years referred to as the poles.
Therefore, the number of poles have to be provided by the user in the second case. To activate the second case,
the user must set to True the input variable 'export_invest_profiles_at_poles'


Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@
InstanciatedSeries,
TwoAxesInstanciatedChart,
)

from energy_models.core.investments.convex_combination_model import (
ConvexCombinationModel,
)
from energy_models.glossaryenergy import GlossaryEnergy
import numpy as np

class InvestmentsProfileBuilderDisc(SoSWrapp):
# ontology information
Expand All @@ -41,10 +45,18 @@ class InvestmentsProfileBuilderDisc(SoSWrapp):
'icon': 'fas fa-coins fa-fw',
'version': '',
}
'''
Discipline that generates an output invest profile based on generic input invest profiles and input weights for
each of those profiles.
Based on the input boolean EXPORT_PROFILES_AT_POLES, it can either export the output profile at the poles or for all years
then, the output variable is not named the same, as in the first case it becomes an input of the design_var discipline and
in the second case it is an input of the investment distribution
'''

DESC_IN = {
'n_profiles': {'type': 'int', 'unit': '-', 'user_level': 3},
'column_names': {'type': 'list', 'subtype_descriptor': {'list': 'string'}}
'column_names': {'type': 'list', 'subtype_descriptor': {'list': 'string'}},
GlossaryEnergy.EXPORT_PROFILES_AT_POLES: {'type': 'bool', 'editable': True, 'structuring': True, 'user_level': 3, 'namespace': 'ns_invest'},
}

DESC_OUT = {}
Expand All @@ -56,64 +68,124 @@ def setup_sos_disciplines(self):
dynamic_inputs = {}
dynamic_outputs = {}
df_descriptor = None
column_names = None
n_profiles = None
export_profiles_at_poles = None

if 'n_profiles' in self.get_data_in():
n_profiles = self.get_sosdisc_inputs('n_profiles')
if n_profiles is not None:
for i in range(n_profiles):
dynamic_inputs[f'coeff_{i}'] = {'type': 'float', 'unit': '-'}


if 'column_names' in self.get_data_in():
column_names = self.get_sosdisc_inputs('column_names')
if column_names is not None and n_profiles is not None:
df_descriptor = {GlossaryEnergy.Years: ("int", [1900, GlossaryCore.YearEndDefault],False)}
df_descriptor = {GlossaryEnergy.Years: ("int", [1900, GlossaryCore.YearEndDefault], False)}
df_descriptor.update({col: ("float", [0.0, 1e30], False) for col in column_names})
for i in range(n_profiles):
dynamic_inputs[f'df_{i}'] = {
"type": "dataframe", "unit": "G$",
"dataframe_descriptor": df_descriptor,
}

if df_descriptor is not None:
dynamic_outputs[GlossaryEnergy.invest_mix] = {
"type": "dataframe", "unit": "G$",
"dataframe_descriptor": df_descriptor,
"namespace": "ns_invest", #same namespace as for invest_mix in discipline InvestmentDistribution
"visibility": "Shared",
}
if GlossaryEnergy.EXPORT_PROFILES_AT_POLES in self.get_data_in():
export_profiles_at_poles = self.get_sosdisc_inputs(GlossaryEnergy.EXPORT_PROFILES_AT_POLES)
if column_names is not None:
if export_profiles_at_poles is not None and export_profiles_at_poles:
dynamic_inputs['nb_poles'] = {'type': 'int', 'unit': '-', 'user_level': 3}


if df_descriptor is not None and export_profiles_at_poles is not None:
# the output invest profile can be provided either for all the years or for some limited number of poles.
if not export_profiles_at_poles:
dynamic_outputs[GlossaryEnergy.invest_mix] = {
"type": "dataframe", "unit": "G$",
"dataframe_descriptor": df_descriptor,
"namespace": "ns_invest", # same namespace as for invest_mix in discipline InvestmentDistribution
"visibility": "Shared",
}
else:
for var in column_names:
dynamic_outputs[f'{var}_array_mix'] = {
"type": "array",
"unit": "G$",
"namespace": "ns_invest", # same namespace as for design_var discipline inputs as described in design_var_descriptor
"visibility": "Shared",
}

self.add_inputs(dynamic_inputs)
self.add_outputs(dynamic_outputs)

def compute_poles(self, df, nb_poles):
'''
extract the years of the nb_poles and their corresponding index in a given dataframe
to avoid interpolation, accept non-even step size between the poles
args
df [dataframe]: dataframe that contains a column 'Years' (typically, an invest profile)
nb_poles [int]: number of poles to extract among the list of years
'''
years = df[GlossaryEnergy.Years]
years_poles = np.linspace(years.values.min(), years.values.max(), nb_poles,
dtype='int') # to avoid interpolation, accept non-even step size between the poles
poles_index = list(df[df[GlossaryEnergy.Years].isin(years_poles)].index)

return years_poles, poles_index

def run(self): # type: (...) -> None
self.model = ConvexCombinationModel()

inputs = self.get_sosdisc_inputs()
n_profiles = inputs['n_profiles']
column_names = inputs['column_names']
export_profiles_at_poles = inputs[GlossaryEnergy.EXPORT_PROFILES_AT_POLES]

self.model.store_inputs(
positive_coefficients={f'coeff_{i}': inputs[f'coeff_{i}'] for i in range(n_profiles)},
dataframes=[inputs[f'df_{i}'] for i in range(n_profiles)]
dataframes=[inputs[f'df_{i}'] for i in range(n_profiles)],
)

self.model.compute()
convex_combination_df = self.model.convex_combination_df
# some data are stored in invest_mix, others in _array_mix
if not export_profiles_at_poles:
outputs = {
GlossaryEnergy.invest_mix: self.model.convex_combination_df[[GlossaryEnergy.Years] + column_names]
}
self.store_sos_outputs_values(outputs)

outputs = {
GlossaryEnergy.invest_mix: self.model.convex_combination_df
}

self.store_sos_outputs_values(outputs)
else:
df = inputs['df_0']
nb_poles = inputs['nb_poles']
years_poles, poles_index = self.compute_poles(df, nb_poles)
for col in column_names: # extract data at the poles
df = self.model.convex_combination_df[[GlossaryEnergy.Years] + [col]]
outputs = {col + '_array_mix': df[df.index.isin(poles_index)][col].values}
self.store_sos_outputs_values(outputs)

def compute_sos_jacobian(self):
dict_in = self.get_sosdisc_inputs()
column_names_list = dict_in['column_names']
n_profiles = dict_in['n_profiles']
for col_name in column_names_list:
for i in range(n_profiles):
df = dict_in['df_0']
export_profiles_at_poles = dict_in[GlossaryEnergy.EXPORT_PROFILES_AT_POLES]
poles_index = None # initialize to avoid pylint error
if export_profiles_at_poles:
nb_poles = dict_in['nb_poles']
years_poles, poles_index = self.compute_poles(df, nb_poles)
for i in range(n_profiles):
for col_name in column_names_list:
derivative = self.model.d_convex_combination_d_coeff_in(col_name, f'coeff_{i}')
self.set_partial_derivative_for_other_types(
(GlossaryEnergy.invest_mix, col_name),
(f'coeff_{i}',), derivative.reshape((len(derivative), 1))
)
# data can be either stored in invest_mix or in _array_mix
if not export_profiles_at_poles:
self.set_partial_derivative_for_other_types(
(GlossaryEnergy.invest_mix, col_name),
(f'coeff_{i}',), derivative.reshape((len(derivative), 1))
)
else:
derivative_at_poles = derivative[poles_index].reshape((len(poles_index), 1)) #extract gradient at the poles only
self.set_partial_derivative(col_name + '_array_mix', f'coeff_{i}', derivative_at_poles)

def get_chart_filter_list(self):
chart_filters = []
Expand All @@ -132,37 +204,53 @@ def get_post_processing_list(self, filters=None):
if chart_filter.filter_key == 'charts_invest':
charts = chart_filter.selected_values

invest_profile = self.get_sosdisc_outputs(GlossaryEnergy.invest_mix)
years = list(invest_profile['years'].values)
n_profiles = self.get_sosdisc_inputs('n_profiles')
column_names = self.get_sosdisc_inputs('column_names')
graph_name = "Output profile invest"
df = self.get_sosdisc_inputs('df_0')
years = list(df[GlossaryEnergy.Years].values) # all profiles should have the same years
export_profiles_at_poles = self.get_sosdisc_inputs(GlossaryEnergy.EXPORT_PROFILES_AT_POLES)
years_poles = None # initialize to avoid pylint error
if export_profiles_at_poles:
nb_poles = self.get_sosdisc_inputs('nb_poles')
years_poles, poles_index = self.compute_poles(df, nb_poles)

graph = TwoAxesInstanciatedChart(GlossaryEnergy.Years, 'Invest [G$]',
chart_name=graph_name)
graph_years = TwoAxesInstanciatedChart(GlossaryEnergy.Years, f'Invest {GlossaryEnergy.invest_mix} [G$]',
chart_name="Output profile invest")
graph_poles = TwoAxesInstanciatedChart(GlossaryEnergy.Years, f'Invest array_mix [G$]',
chart_name="Output profile invest at the poles")

columns_to_include_in_output_graph = [column for column in column_names if column != GlossaryEnergy.Years]

for idx, column in enumerate(column_names):
chart_name = f"Investments in {column}"

# we want to plot the generic invest profiles of each column on 1 graph, not 1 graph for each of the n_profile
if idx < len(columns_to_include_in_output_graph):
if idx < len(column_names):
chart = TwoAxesInstanciatedChart(GlossaryEnergy.Years, 'Invest [G$]',
chart_name=chart_name)

n_profiles = self.get_sosdisc_inputs('n_profiles')
for i in range(n_profiles):
input_series_values = list(self.get_sosdisc_inputs(f'df_{i}')[column].values)
input_serie_obj = InstanciatedSeries(years, input_series_values, f'df_{i}', "lines")
chart.add_series(input_serie_obj)

instanciated_charts.append(chart)

if column in columns_to_include_in_output_graph:
series_values = list(invest_profile[column].values)
if not export_profiles_at_poles:
invest_profile_years = self.get_sosdisc_outputs(GlossaryEnergy.invest_mix)
series_values = list(invest_profile_years[column].values)
serie_obj = InstanciatedSeries(years, series_values, column, "lines")
graph.add_series(serie_obj)

instanciated_charts.append(graph)
graph_years.add_series(serie_obj)
else:
invest_profile_poles = self.get_sosdisc_outputs(column + '_array_mix')
series_values = list(invest_profile_poles)
serie_obj = InstanciatedSeries(list(years_poles), series_values, column + '_array_mix', display_type="scatter",
marker_symbol='circle',
#marker=dict(color='LightSkyBlue', size=20, line=dict(color='MediumPurple', width=2))
)
graph_poles.add_series(serie_obj)

if not export_profiles_at_poles:
instanciated_charts.append(graph_years)
else:
instanciated_charts.append(graph_poles)

return instanciated_charts
15 changes: 15 additions & 0 deletions energy_models/glossaryenergy.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class GlossaryEnergy(GlossaryWitnessCore):

NB_POLES_FULL: int = 8 # number of poles in witness full
NB_POLE_ENERGY_MIX_PROCESS = 12
EXPORT_PROFILES_AT_POLES = 'export_invest_profiles_at_poles'
YearEndDefaultValueGradientTest = 2030
LifetimeDefaultValueGradientTest = 7
YearEndDefault = 2050
Expand Down Expand Up @@ -451,6 +452,17 @@ class GlossaryEnergy(GlossaryWitnessCore):
"dataframe_edition_locked": False,
}

TechnoProductionDf = {
"var_name": TechnoProductionValue,
"type": "dataframe", "unit": "TWh or Mt",
"dynamic_dataframe_columns": True
}

EnergyPricesDf = {
"var_name": GlossaryWitnessCore.EnergyPricesValue,
"type": "dataframe", "unit": "$/MWh",
"dynamic_dataframe_columns": True}

# techno names
CropEnergy = "CropEnergy"
ManagedWood = "ManagedWood"
Expand All @@ -477,8 +489,10 @@ class GlossaryEnergy(GlossaryWitnessCore):
RenewableElectricitySimpleTechno = "RenewableElectricitySimpleTechno"
SolarPv = "SolarPv"
SolarThermal = "SolarThermal"
Solar = "Solar"
WindOffshore = "WindOffshore"
WindOnshore = "WindOnshore"
WindOnshoreAndOffshore = 'WindOnShoreandOffShore'
BiomassFermentation = "BiomassFermentation"
FossilSimpleTechno = "FossilSimpleTechno"
ElectrolysisAWE = "Electrolysis.AWE"
Expand Down Expand Up @@ -522,6 +536,7 @@ class GlossaryEnergy(GlossaryWitnessCore):
SMR = "SMR"
AnimalManure = "AnimalManure"
WetCropResidues = "WetCropResidues"
ForestProduction = "ForestProduction"

AmineScrubbing = "AmineScrubbing"
CalciumPotassiumScrubbing = "CalciumPotassiumScrubbing"
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading

0 comments on commit f7bf13a

Please sign in to comment.