diff --git a/flixopt/components.py b/flixopt/components.py index d483ee28c..933ef1791 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -223,29 +223,29 @@ def transform_data(self, flow_system: 'FlowSystem') -> None: ) if not isinstance(self.initial_charge_state, str): self.initial_charge_state = flow_system.fit_to_model_coords( - f'{self.label_full}|initial_charge_state', self.initial_charge_state, has_time_dim=False + f'{self.label_full}|initial_charge_state', self.initial_charge_state, dims=['year', 'scenario'] ) self.minimal_final_charge_state = flow_system.fit_to_model_coords( - f'{self.label_full}|minimal_final_charge_state', self.minimal_final_charge_state, has_time_dim=False + f'{self.label_full}|minimal_final_charge_state', self.minimal_final_charge_state, dims=['year', 'scenario'] ) self.maximal_final_charge_state = flow_system.fit_to_model_coords( - f'{self.label_full}|maximal_final_charge_state', self.maximal_final_charge_state, has_time_dim=False + f'{self.label_full}|maximal_final_charge_state', self.maximal_final_charge_state, dims=['year', 'scenario'] ) self.relative_minimum_final_charge_state = flow_system.fit_to_model_coords( f'{self.label_full}|relative_minimum_final_charge_state', self.relative_minimum_final_charge_state, - has_time_dim=False, + dims=['year', 'scenario'], ) self.relative_maximum_final_charge_state = flow_system.fit_to_model_coords( f'{self.label_full}|relative_maximum_final_charge_state', self.relative_maximum_final_charge_state, - has_time_dim=False, + dims=['year', 'scenario'], ) if isinstance(self.capacity_in_flow_hours, InvestParameters): self.capacity_in_flow_hours.transform_data(flow_system, f'{self.label_full}|InvestParameters') else: self.capacity_in_flow_hours = flow_system.fit_to_model_coords( - f'{self.label_full}|capacity_in_flow_hours', self.capacity_in_flow_hours, has_time_dim=False + f'{self.label_full}|capacity_in_flow_hours', self.capacity_in_flow_hours, dims=['year', 'scenario'] ) def _plausibility_checks(self) -> None: diff --git a/flixopt/effects.py b/flixopt/effects.py index 79a44e67a..d0b552bf8 100644 --- a/flixopt/effects.py +++ b/flixopt/effects.py @@ -105,27 +105,30 @@ def transform_data(self, flow_system: 'FlowSystem'): ) self.minimum_operation = flow_system.fit_to_model_coords( - f'{self.label_full}|minimum_operation', self.minimum_operation, has_time_dim=False + f'{self.label_full}|minimum_operation', self.minimum_operation, dims=['year', 'scenario'] ) self.maximum_operation = flow_system.fit_to_model_coords( - f'{self.label_full}|maximum_operation', self.maximum_operation, has_time_dim=False + f'{self.label_full}|maximum_operation', self.maximum_operation, dims=['year', 'scenario'] ) self.minimum_invest = flow_system.fit_to_model_coords( - f'{self.label_full}|minimum_invest', self.minimum_invest, has_time_dim=False + f'{self.label_full}|minimum_invest', self.minimum_invest, dims=['year', 'scenario'] ) self.maximum_invest = flow_system.fit_to_model_coords( - f'{self.label_full}|maximum_invest', self.maximum_invest, has_time_dim=False + f'{self.label_full}|maximum_invest', self.maximum_invest, dims=['year', 'scenario'] ) self.minimum_total = flow_system.fit_to_model_coords( f'{self.label_full}|minimum_total', self.minimum_total, - has_time_dim=False, + dims=['year', 'scenario'], ) self.maximum_total = flow_system.fit_to_model_coords( - f'{self.label_full}|maximum_total', self.maximum_total, has_time_dim=False + f'{self.label_full}|maximum_total', self.maximum_total, dims=['year', 'scenario'] ) self.specific_share_to_other_effects_invest = flow_system.fit_effects_to_model_coords( - f'{self.label_full}|invest->', self.specific_share_to_other_effects_invest, 'invest', has_time_dim=False + f'{self.label_full}|invest->', + self.specific_share_to_other_effects_invest, + 'invest', + dims=['year', 'scenario'], ) def create_model(self, model: FlowSystemModel) -> 'EffectModel': diff --git a/flixopt/elements.py b/flixopt/elements.py index e1c0fcbc3..160dac660 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -252,16 +252,16 @@ def transform_data(self, flow_system: 'FlowSystem'): self.label_full, self.effects_per_flow_hour, 'per_flow_hour' ) self.flow_hours_total_max = flow_system.fit_to_model_coords( - f'{self.label_full}|flow_hours_total_max', self.flow_hours_total_max, has_time_dim=False + f'{self.label_full}|flow_hours_total_max', self.flow_hours_total_max, dims=['year', 'scenario'] ) self.flow_hours_total_min = flow_system.fit_to_model_coords( - f'{self.label_full}|flow_hours_total_min', self.flow_hours_total_min, has_time_dim=False + f'{self.label_full}|flow_hours_total_min', self.flow_hours_total_min, dims=['year', 'scenario'] ) self.load_factor_max = flow_system.fit_to_model_coords( - f'{self.label_full}|load_factor_max', self.load_factor_max, has_time_dim=False + f'{self.label_full}|load_factor_max', self.load_factor_max, dims=['year', 'scenario'] ) self.load_factor_min = flow_system.fit_to_model_coords( - f'{self.label_full}|load_factor_min', self.load_factor_min, has_time_dim=False + f'{self.label_full}|load_factor_min', self.load_factor_min, dims=['year', 'scenario'] ) if self.on_off_parameters is not None: @@ -269,7 +269,7 @@ def transform_data(self, flow_system: 'FlowSystem'): if isinstance(self.size, InvestParameters): self.size.transform_data(flow_system, self.label_full) else: - self.size = flow_system.fit_to_model_coords(f'{self.label_full}|size', self.size, has_time_dim=False) + self.size = flow_system.fit_to_model_coords(f'{self.label_full}|size', self.size, dims=['year', 'scenario']) def _plausibility_checks(self) -> None: # TODO: Incorporate into Variable? (Lower_bound can not be greater than upper bound diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index dd202114e..e8ddc22b9 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -7,7 +7,7 @@ import pathlib import warnings from io import StringIO -from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Tuple, Union +from typing import TYPE_CHECKING, Any, Collection, Dict, List, Literal, Optional, Tuple, Union import numpy as np import pandas as pd @@ -349,6 +349,7 @@ def fit_to_model_coords( name: str, data: Optional[Union[TemporalDataUser, NonTemporalDataUser]], has_time_dim: bool = True, + dims: Optional[Collection[FlowSystemDimensions]] = None, ) -> Optional[Union[TemporalData, NonTemporalData]]: """ Fit data to model coordinate system (currently time, but extensible). @@ -357,6 +358,7 @@ def fit_to_model_coords( name: Name of the data data: Data to fit to model coordinates has_time_dim: Wether to use the time dimension or not + dims: Collection of dimension names to use for fitting. If None, all dimensions are used. Returns: xr.DataArray aligned to model coordinate system. If data is None, returns None. @@ -364,10 +366,19 @@ def fit_to_model_coords( if data is None: return None - coords = self.coords + if dims is None: + coords = self.coords - if not has_time_dim: - coords.pop('time') + if not has_time_dim: + warnings.warn( + 'has_time_dim is deprecated. Please pass dims to fit_to_model_coords instead.', + DeprecationWarning, + stacklevel=2, + ) + coords.pop('time') + else: + coords = self.coords + coords = {k: coords[k] for k in dims if k in coords} # Rest of your method stays the same, just pass coords if isinstance(data, TimeSeriesData): @@ -390,6 +401,7 @@ def fit_effects_to_model_coords( effect_values: Optional[Union[TemporalEffectsUser, NonTemporalEffectsUser]], label_suffix: Optional[str] = None, has_time_dim: bool = True, + dims: Optional[Collection[FlowSystemDimensions]] = None, ) -> Optional[Union[TemporalEffects, NonTemporalEffects]]: """ Transform EffectValues from the user to Internal Datatypes aligned with model coordinates. @@ -401,7 +413,10 @@ def fit_effects_to_model_coords( return { effect: self.fit_to_model_coords( - '|'.join(filter(None, [label_prefix, effect, label_suffix])), value, has_time_dim=has_time_dim + '|'.join(filter(None, [label_prefix, effect, label_suffix])), + value, + has_time_dim=has_time_dim, + dims=dims, ) for effect, value in effect_values_dict.items() } @@ -412,7 +427,7 @@ def connect_and_transform(self): logger.debug('FlowSystem already connected and transformed') return - self.weights = self.fit_to_model_coords('weights', self.weights, has_time_dim=False) + self.weights = self.fit_to_model_coords('weights', self.weights, dims=['year', 'scenario']) if self.weights is not None and self.weights.sum() != 1: logger.warning( f'Scenario weights are not normalized to 1. This is recomended for a better scaled model. ' diff --git a/flixopt/interface.py b/flixopt/interface.py index cae1757c7..7cb9604ac 100644 --- a/flixopt/interface.py +++ b/flixopt/interface.py @@ -33,8 +33,9 @@ def __init__(self, start: TemporalDataUser, end: TemporalDataUser): self.has_time_dim = False def transform_data(self, flow_system: 'FlowSystem', name_prefix: str): - self.start = flow_system.fit_to_model_coords(f'{name_prefix}|start', self.start, has_time_dim=self.has_time_dim) - self.end = flow_system.fit_to_model_coords(f'{name_prefix}|end', self.end, has_time_dim=self.has_time_dim) + dims = None if self.has_time_dim else ['year', 'scenario'] + self.start = flow_system.fit_to_model_coords(f'{name_prefix}|start', self.start, dims=dims) + self.end = flow_system.fit_to_model_coords(f'{name_prefix}|end', self.end, dims=dims) @register_class_for_io @@ -189,33 +190,33 @@ def transform_data(self, flow_system: 'FlowSystem', name_prefix: str): label_prefix=name_prefix, effect_values=self.fix_effects, label_suffix='fix_effects', - has_time_dim=False, + dims=['year', 'scenario'], ) self.divest_effects = flow_system.fit_effects_to_model_coords( label_prefix=name_prefix, effect_values=self.divest_effects, label_suffix='divest_effects', - has_time_dim=False, + dims=['year', 'scenario'], ) self.specific_effects = flow_system.fit_effects_to_model_coords( label_prefix=name_prefix, effect_values=self.specific_effects, label_suffix='specific_effects', - has_time_dim=False, + dims=['year', 'scenario'], ) if self.piecewise_effects is not None: self.piecewise_effects.has_time_dim = False self.piecewise_effects.transform_data(flow_system, f'{name_prefix}|PiecewiseEffects') self.minimum_size = flow_system.fit_to_model_coords( - f'{name_prefix}|minimum_size', self.minimum_size, has_time_dim=False + f'{name_prefix}|minimum_size', self.minimum_size, dims=['year', 'scenario'] ) self.maximum_size = flow_system.fit_to_model_coords( - f'{name_prefix}|maximum_size', self.maximum_size, has_time_dim=False + f'{name_prefix}|maximum_size', self.maximum_size, dims=['year', 'scenario'] ) if self.fixed_size is not None: self.fixed_size = flow_system.fit_to_model_coords( - f'{name_prefix}|fixed_size', self.fixed_size, has_time_dim=False + f'{name_prefix}|fixed_size', self.fixed_size, dims=['year', 'scenario'] ) def _plausibility_checks(self, flow_system): @@ -312,13 +313,13 @@ def transform_data(self, flow_system: 'FlowSystem', name_prefix: str): f'{name_prefix}|consecutive_off_hours_max', self.consecutive_off_hours_max ) self.on_hours_total_max = flow_system.fit_to_model_coords( - f'{name_prefix}|on_hours_total_max', self.on_hours_total_max, has_time_dim=False + f'{name_prefix}|on_hours_total_max', self.on_hours_total_max, dims=['year', 'scenario'] ) self.on_hours_total_min = flow_system.fit_to_model_coords( - f'{name_prefix}|on_hours_total_min', self.on_hours_total_min, has_time_dim=False + f'{name_prefix}|on_hours_total_min', self.on_hours_total_min, dims=['year', 'scenario'] ) self.switch_on_total_max = flow_system.fit_to_model_coords( - f'{name_prefix}|switch_on_total_max', self.switch_on_total_max, has_time_dim=False + f'{name_prefix}|switch_on_total_max', self.switch_on_total_max, dims=['year', 'scenario'] ) @property diff --git a/flixopt/structure.py b/flixopt/structure.py index da67f9620..24934547b 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -178,7 +178,7 @@ def get_coords( def weights(self) -> Union[int, xr.DataArray]: """Returns the scenario weights of the FlowSystem. If None, return weights that are normalized to 1 (one)""" if self.flow_system.weights is None: - weights = self.flow_system.fit_to_model_coords('weights', 1, has_time_dim=False) + weights = self.flow_system.fit_to_model_coords('weights', 1, dims=['year', 'scenario']) return weights / weights.sum()