Skip to content

Commit

Permalink
return raw MF results
Browse files Browse the repository at this point in the history
  • Loading branch information
jacob-mannhardt committed Feb 14, 2024
1 parent 7012ee1 commit 2dff83a
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 25 deletions.
39 changes: 30 additions & 9 deletions zen_garden/postprocess/results/multi_hdf_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ def _combine_dataseries(
self,
component: AbstractComponent,
scenario: AbstractScenario,
pd_dict: dict[str, "pd.Series[Any]"],
pd_dict: dict[int, "pd.Series[Any]"],
) -> "pd.DataFrame | pd.Series[Any]":
"""
Method that combines the values when a solution is created without perfect
Expand All @@ -214,7 +214,7 @@ def _combine_dataseries(
n_years = len(pd_dict)

for year in range(n_years):
current_mf = pd_dict[f"MF_{year}"]
current_mf = pd_dict[year]
if component.timestep_type is TimestepType.yearly:
year_series = current_mf[
current_mf.index.get_level_values("year") == year
Expand All @@ -241,13 +241,29 @@ def _combine_dataseries(

return pd.concat(series_to_concat)

def _concatenate_raw_dataseries(
self,
pd_dict: dict[int, "pd.Series[Any]"],
) -> "pd.DataFrame | pd.Series[Any]":
"""
Method that concatenates the raw values when a solution is created without perfect
foresight given a component, a scenario and a dictionary containing the name of
the MF-data (Format: "MF_{year}"). The raw values are not combined, i.e.,
the data is kept for all the foresight steps.
"""
series = pd.concat(pd_dict, keys=pd_dict.keys())
index_names = pd_dict[list(pd_dict.keys())[0]].index.names
new_index_names = ["mf"] + index_names
series.index.names = new_index_names
return series

def get_component_data(
self, scenario: AbstractScenario, component: AbstractComponent
self, scenario: AbstractScenario, component: AbstractComponent, keep_raw: bool = False
) -> "pd.DataFrame | pd.Series[Any]":
"""
Implementation of the abstract method. Returns the actual component values given
a component and a scenario. Already combines the yearly data if the solution does
not use perfect foresight.
not use perfect foresight, unless explicitly desired otherwise (keep_raw = True).
"""
if self.has_rh:
# If solution has rolling horizon, load the values for all the foresight
Expand All @@ -256,16 +272,21 @@ def get_component_data(
pd_series_dict = {}

for subfolder_name in subfolder_names:
mf_idx = int(subfolder_name.replace("MF_", ""))
file_path = os.path.join(
scenario.path, subfolder_name, component.file_name
)
pd_series_dict[subfolder_name] = get_df_form_path(
pd_series_dict[mf_idx] = get_df_form_path(
file_path, component.name
)
combined_dataseries = self._combine_dataseries(
component, scenario, pd_series_dict
)

if not keep_raw:
combined_dataseries = self._combine_dataseries(
component, scenario, pd_series_dict
)
else:
combined_dataseries = self._concatenate_raw_dataseries(
pd_series_dict
)
return combined_dataseries
else:
# If solution does not use rolling horizon, simply load the HDF file.
Expand Down
34 changes: 20 additions & 14 deletions zen_garden/postprocess/results/results.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
)
from zen_garden.postprocess.results.multi_hdf_loader import MultiHdfLoader
from functools import cache
from zen_garden.model.default_config import Config
from zen_garden.model.default_config import Config,Analysis,Solver,System
import importlib
import os
import logging
Expand Down Expand Up @@ -185,6 +185,7 @@ def get_total_per_scenario(
component: Component,
element_name: Optional[str] = None,
year: Optional[int] = None,
keep_raw: Optional[bool] = False,
) -> "pd.DataFrame | pd.Series[Any]":
"""
Calculates the total values of a component for a specific scenario.
Expand All @@ -193,8 +194,9 @@ def get_total_per_scenario(
:param component: Component
:param element_name: Filter results by a given element name
:param year: Filter the results by a given year
:param keep_raw: Keep the raw values of the rolling horizon optimization
"""
series = self.solution_loader.get_component_data(scenario, component)
series = self.solution_loader.get_component_data(scenario, component, keep_raw)

if year is None:
years = [i for i in range(0, scenario.system.optimized_years)]
Expand Down Expand Up @@ -223,10 +225,13 @@ def get_total_per_scenario(
for y in years:
timesteps = self.solution_loader.get_timesteps(scenario, component, int(y))
try:
ans.insert(int(y), y, total_value[timesteps].sum(axis=1)) # type: ignore
ans.insert(int(y), y, total_value[timesteps].sum(axis=1,skipna=False)) # type: ignore
except KeyError:
timestep_list = [i for i in timesteps if i in total_value]
ans.insert(year, year, total_value[timestep_list].sum(axis=1)) # type: ignore # noqa
ans.insert(year, year, total_value[timestep_list].sum(axis=1,skipna=False)) # type: ignore # noqa

if "mf" in ans.index.names:
ans = ans.reorder_levels([i for i in ans.index.names if i != "mf"] + ["mf"]).sort_index(axis=0)

return ans

Expand All @@ -237,6 +242,7 @@ def get_total(
element_name: Optional[str] = None,
year: Optional[int] = None,
scenario_name: Optional[str] = None,
keep_raw: Optional[bool] = False,
) -> "pd.DataFrame | pd.Series[Any]":
"""
Calculates the total values of a component for a all scenarios.
Expand All @@ -258,7 +264,7 @@ def get_total(
for scenario_name in scenario_names:
scenario = self.solution_loader.scenarios[scenario_name]
current_total = self.get_total_per_scenario(
scenario, component, element_name, year
scenario, component, element_name, year, keep_raw
)

if type(current_total) is pd.Series:
Expand Down Expand Up @@ -354,7 +360,7 @@ def _get_annuity(self, scenario: Scenario, discount_to_first_step: bool = True):
def get_dual(
self,
constraint: str,
scenario_name: str,
scenario_name: Optional[str] = None,
element_name: Optional[str] = None,
year: Optional[int] = None,
discount_to_first_step=True,
Expand All @@ -367,7 +373,7 @@ def get_dual(
:param year: Year
:param discount_to_first_step: apply annuity to first year of interval or entire interval
"""
if not self.solution_loader.scenarios[scenario_name].solver.add_duals:
if not r.get_solver(scenario_name=scenario_name).add_duals:
logging.warning("Duals are not calculated. Skip.")
return None

Expand All @@ -385,37 +391,37 @@ def get_dual(
)
return _duals

def get_system(self, scenario_name: Optional[str] = None) -> dict[Any, Any]:
def get_system(self, scenario_name: Optional[str] = None) -> System:
"""
Extracts the System config of a given Scenario. If no scenario is given, a random one is taken.
:param scenario_name: Name of the scenario
"""
if scenario_name is None:
scenario_name = next(iter(self.solution_loader.scenarios.keys()))
return self.solution_loader.scenarios[scenario_name].system.model_dump()
return self.solution_loader.scenarios[scenario_name].system

def get_analysis(self, scenario_name: Optional[str] = None) -> dict[Any, Any]:
def get_analysis(self, scenario_name: Optional[str] = None) -> Analysis:
"""
Extracts the Analysis config of a given Scenario. If no scenario is given, a random one is taken.
:param scenario_name: Name of the scenario
"""
if scenario_name is None:
scenario_name = next(iter(self.solution_loader.scenarios.keys()))
return self.solution_loader.scenarios[scenario_name].analysis.model_dump()
return self.solution_loader.scenarios[scenario_name].analysis

def get_solver(self, scenario_name: Optional[str] = None) -> dict[Any, Any]:
def get_solver(self, scenario_name: Optional[str] = None) -> Solver:
"""
Extracts the Solver config of a given Scenario. If no scenario is given, a random one is taken.
:param scenario_name: Name of the scenario
"""
if scenario_name is None:
scenario_name = next(iter(self.solution_loader.scenarios.keys()))
return self.solution_loader.scenarios[scenario_name].solver.model_dump()
return self.solution_loader.scenarios[scenario_name].solver

def get_years(self, scenario_name: Optional[str] = None) -> dict[Any, Any]:
def get_years(self, scenario_name: Optional[str] = None) -> list[int]:
"""
Extracts the years of a given Scenario. If no scenario is given, a random one is taken.
Expand Down
4 changes: 2 additions & 2 deletions zen_garden/postprocess/results/solution_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,15 +154,15 @@ def has_rh(self) -> bool:

@abstractmethod
def get_component_data(
self, scenario: Scenario, component: Component
self, scenario: Scenario, component: Component, keep_raw: bool = False
) -> "pd.DataFrame | pd.Series[Any]":
"""
Abstract method that should return the component values of a given scenario and a
given component.
If the solution uses rolling horizon, the returned component values should already
take into account the limited foresight and therefore should not include all the
data of the different foresight years but combine them to one series.
data of the different foresight years but combine them to one series, unless explicitly desired (keep_raw = True).
"""
pass

Expand Down

0 comments on commit 2dff83a

Please sign in to comment.