From c6170b39d07ebd9b796b7a1067f46715cf802ad3 Mon Sep 17 00:00:00 2001 From: Frode Helgetun Krogh <70878501+frodehk@users.noreply.github.com> Date: Wed, 31 Jul 2024 10:59:55 +0200 Subject: [PATCH 01/12] fix: read requested pressures in compressor system ECALC-1230 --- .../presentation/json_result/mapper.py | 84 ++++++++++++++++++- .../test_requested_pressures_compressors.py | 30 ++++++- 2 files changed, 109 insertions(+), 5 deletions(-) diff --git a/src/libecalc/presentation/json_result/mapper.py b/src/libecalc/presentation/json_result/mapper.py index 14692f7562..938487d3a2 100644 --- a/src/libecalc/presentation/json_result/mapper.py +++ b/src/libecalc/presentation/json_result/mapper.py @@ -138,6 +138,82 @@ def get_requested_compressor_pressures( return TemporalModel(evaluated_temporal_energy_usage_models) +@Feature.experimental(feature_description="Reporting requested pressures is an experimental feature.") +def get_requested_compressor_pressures_test( + energy_usage_model: Dict[datetime, Any], + pressure_type: CompressorPressureType, + name: str, + model_timesteps: List[datetime], + operational_settings_used: Optional[TimeSeriesInt] = None, +) -> TemporalModel[Expression]: + """Get temporal model for compressor inlet- and outlet pressures. + The pressures are the actual pressures defined by user in input. + + :param energy_usage_model: Temporal energy model + :param pressure_type: Compressor pressure type, inlet- or outlet + :param name: name of compressor + :param model_period: start- and stop time for model + :param model_timesteps: actual timesteps in model + :param operational_settings_used: time series indicating which priority is active + :return: Temporal model with pressures as expressions + """ + + evaluated_temporal_energy_usage_models = {} + + for period, model in TemporalModel(energy_usage_model).items(): + if isinstance(model, CompressorSystemConsumerFunction): + # if period.start in model_timesteps: + # Loop timesteps in temporal model, to find correct operational settings used: + # start, end = period.get_timestep_indices(model_timesteps) + for timestep in model_timesteps: + if timestep in operational_settings_used.timesteps: + for compressor in model.compressors: + if compressor.name == name: + operational_setting_used_id = get_operational_setting_used_id( + timestep=timestep, operational_settings_used=operational_settings_used + ) + + operational_setting = model.operational_settings[operational_setting_used_id] + + # Find correct compressor in case of different pressures for different components in system: + compressor_nr = int( + [i for i, compressor in enumerate(model.compressors) if compressor.name == name][0] + ) + + if pressure_type.value == CompressorPressureType.INLET_PRESSURE: + if operational_setting.suction_pressures is not None: + pressures = operational_setting.suction_pressures[compressor_nr] + else: + pressures = operational_setting.suction_pressure + else: + if operational_setting.discharge_pressures is not None: + pressures = operational_setting.discharge_pressures[compressor_nr] + else: + pressures = operational_setting.discharge_pressure + + if pressures is None: + pressures = math.nan + + if not isinstance(pressures, Expression): + pressures = Expression.setup_from_expression(value=pressures) + evaluated_temporal_energy_usage_models[timestep] = pressures + else: + pressures = model.suction_pressure + + if pressure_type.value == CompressorPressureType.OUTLET_PRESSURE: + pressures = model.discharge_pressure + + if pressures is None: + pressures = math.nan + + if not isinstance(pressures, Expression): + pressures = Expression.setup_from_expression(value=pressures) + + evaluated_temporal_energy_usage_models[period.start] = pressures + + return TemporalModel(evaluated_temporal_energy_usage_models) + + def _compute_intensity( hydrocarbon_export_rate: TimeSeriesRate, emissions: Dict[str, PartialEmissionResult], @@ -439,24 +515,24 @@ def get_asset_result(graph_result: GraphResult) -> libecalc.presentation.json_re for model in consumer_result.models: period = Period(model.timesteps[0], model.timesteps[-1]) - inlet_pressure_eval = get_requested_compressor_pressures( + inlet_pressure_eval = get_requested_compressor_pressures_test( energy_usage_model=component.energy_usage_model, pressure_type=CompressorPressureType.INLET_PRESSURE, name=model.name, operational_settings_used=consumer_result.component_result.operational_settings_used if consumer_node_info.component_type == ComponentType.COMPRESSOR_SYSTEM else None, - model_period=period, + # model_period=period, model_timesteps=model.timesteps, ) - outlet_pressure_eval = get_requested_compressor_pressures( + outlet_pressure_eval = get_requested_compressor_pressures_test( energy_usage_model=component.energy_usage_model, pressure_type=CompressorPressureType.OUTLET_PRESSURE, name=model.name, operational_settings_used=consumer_result.component_result.operational_settings_used if consumer_node_info.component_type == ComponentType.COMPRESSOR_SYSTEM else None, - model_period=period, + # model_period=period, model_timesteps=model.timesteps, ) diff --git a/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py b/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py index 1c0a7b244e..1f470ad353 100644 --- a/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py +++ b/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py @@ -4,9 +4,13 @@ import pytest from libecalc.application.energy_calculator import EnergyCalculator from libecalc.application.graph_result import EcalcModelResult, GraphResult +from libecalc.common.component_info.compressor import CompressorPressureType from libecalc.common.units import Unit from libecalc.common.utils.rates import TimeSeriesFloat -from libecalc.presentation.json_result.mapper import get_asset_result +from libecalc.presentation.json_result.mapper import ( + get_asset_result, + get_requested_compressor_pressures_test, +) from libecalc.presentation.json_result.result.results import CompressorModelResult @@ -22,6 +26,7 @@ def result(compressor_systems_and_compressor_train_temporal_dto) -> EcalcModelRe variables_map=variables, consumer_results=consumer_results, ) + result = get_asset_result( GraphResult( graph=graph, @@ -116,3 +121,26 @@ def test_requested_pressures_compressor_system_temporal_model(result: EcalcModel assert requested_inlet_pressure_train2_upgr == 45 assert requested_outlet_pressure_train1_upgr == 240 assert requested_outlet_pressure_train2_upgr == 245 + + +def test_for_bug(compressor_systems_and_compressor_train_temporal_dto): + graph = compressor_systems_and_compressor_train_temporal_dto.ecalc_model.get_graph() + compressor_system = graph.get_node("compressor_system_variable_speed_compressor_trains_multiple_pressures") + + energy_usage_model = compressor_system.energy_usage_model + + variables = compressor_systems_and_compressor_train_temporal_dto.variables + energy_calculator = EnergyCalculator(graph=graph) + consumer_results = energy_calculator.evaluate_energy_usage(variables) + operational_settings_used = consumer_results[ + "compressor_system_variable_speed_compressor_trains_multiple_pressures" + ].component_result.operational_settings_used + + model_timesteps = [datetime(2018, 1, 1), datetime(2021, 1, 1), datetime(2022, 1, 1)] + get_requested_compressor_pressures_test( + energy_usage_model=energy_usage_model, + pressure_type=CompressorPressureType.INLET_PRESSURE, + model_timesteps=model_timesteps, + name="train1", + operational_settings_used=operational_settings_used, + ) From 42c3ab42d3a89747d856c060d3dac4b8053ae7a0 Mon Sep 17 00:00:00 2001 From: Frode Helgetun Krogh <70878501+frodehk@users.noreply.github.com> Date: Fri, 2 Aug 2024 13:31:59 +0200 Subject: [PATCH 02/12] chore: update pressure function ECALC-1230 --- .../presentation/json_result/mapper.py | 91 +------------------ 1 file changed, 3 insertions(+), 88 deletions(-) diff --git a/src/libecalc/presentation/json_result/mapper.py b/src/libecalc/presentation/json_result/mapper.py index 938487d3a2..298388ffe9 100644 --- a/src/libecalc/presentation/json_result/mapper.py +++ b/src/libecalc/presentation/json_result/mapper.py @@ -59,87 +59,6 @@ def get_operational_setting_used_id(timestep: datetime, operational_settings_use @Feature.experimental(feature_description="Reporting requested pressures is an experimental feature.") def get_requested_compressor_pressures( - energy_usage_model: Dict[datetime, Any], - pressure_type: CompressorPressureType, - name: str, - model_period: Period, - model_timesteps: List[datetime], - operational_settings_used: Optional[TimeSeriesInt] = None, -) -> TemporalModel[Expression]: - """Get temporal model for compressor inlet- and outlet pressures. - The pressures are the actual pressures defined by user in input. - - :param energy_usage_model: Temporal energy model - :param pressure_type: Compressor pressure type, inlet- or outlet - :param name: name of compressor - :param model_period: start- and stop time for model - :param model_timesteps: actual timesteps in model - :param operational_settings_used: time series indicating which priority is active - :return: Temporal model with pressures as expressions - """ - - evaluated_temporal_energy_usage_models = {} - default_date = datetime(1900, 1, 1, 0, 0) - - # Extract relevant temporal model: - if len(energy_usage_model.items()) == 1 and default_date in energy_usage_model: - model_subset = TemporalModel(energy_usage_model).models[0].model - else: - model_subset = TemporalModel({model_period.start: energy_usage_model[model_period.start]}).models[0].model - - if isinstance(model_subset, CompressorSystemConsumerFunction): - # Loop timesteps in temporal model, to find correct operational settings used: - for timestep in model_timesteps: - for compressor in model_subset.compressors: - if compressor.name == name: - operational_setting_used_id = get_operational_setting_used_id( - timestep=timestep, operational_settings_used=operational_settings_used - ) - - operational_setting = model_subset.operational_settings[operational_setting_used_id] - - # Find correct compressor in case of different pressures for different components in system: - compressor_nr = int( - [i for i, compressor in enumerate(model_subset.compressors) if compressor.name == name][0] - ) - - if pressure_type.value == CompressorPressureType.INLET_PRESSURE: - if operational_setting.suction_pressures is not None: - pressures = operational_setting.suction_pressures[compressor_nr] - else: - pressures = operational_setting.suction_pressure - else: - if operational_setting.discharge_pressures is not None: - pressures = operational_setting.discharge_pressures[compressor_nr] - else: - pressures = operational_setting.discharge_pressure - - if pressures is None: - pressures = math.nan - - if not isinstance(pressures, Expression): - pressures = Expression.setup_from_expression(value=pressures) - evaluated_temporal_energy_usage_models[timestep] = pressures - else: - for period, model in TemporalModel(energy_usage_model).items(): - pressures = model.suction_pressure - - if pressure_type.value == CompressorPressureType.OUTLET_PRESSURE: - pressures = model.discharge_pressure - - if pressures is None: - pressures = math.nan - - if not isinstance(pressures, Expression): - pressures = Expression.setup_from_expression(value=pressures) - - evaluated_temporal_energy_usage_models[period.start] = pressures - - return TemporalModel(evaluated_temporal_energy_usage_models) - - -@Feature.experimental(feature_description="Reporting requested pressures is an experimental feature.") -def get_requested_compressor_pressures_test( energy_usage_model: Dict[datetime, Any], pressure_type: CompressorPressureType, name: str, @@ -162,11 +81,9 @@ def get_requested_compressor_pressures_test( for period, model in TemporalModel(energy_usage_model).items(): if isinstance(model, CompressorSystemConsumerFunction): - # if period.start in model_timesteps: # Loop timesteps in temporal model, to find correct operational settings used: - # start, end = period.get_timestep_indices(model_timesteps) for timestep in model_timesteps: - if timestep in operational_settings_used.timesteps: + if timestep >= period.start and timestep <= period.end: for compressor in model.compressors: if compressor.name == name: operational_setting_used_id = get_operational_setting_used_id( @@ -515,24 +432,22 @@ def get_asset_result(graph_result: GraphResult) -> libecalc.presentation.json_re for model in consumer_result.models: period = Period(model.timesteps[0], model.timesteps[-1]) - inlet_pressure_eval = get_requested_compressor_pressures_test( + inlet_pressure_eval = get_requested_compressor_pressures( energy_usage_model=component.energy_usage_model, pressure_type=CompressorPressureType.INLET_PRESSURE, name=model.name, operational_settings_used=consumer_result.component_result.operational_settings_used if consumer_node_info.component_type == ComponentType.COMPRESSOR_SYSTEM else None, - # model_period=period, model_timesteps=model.timesteps, ) - outlet_pressure_eval = get_requested_compressor_pressures_test( + outlet_pressure_eval = get_requested_compressor_pressures( energy_usage_model=component.energy_usage_model, pressure_type=CompressorPressureType.OUTLET_PRESSURE, name=model.name, operational_settings_used=consumer_result.component_result.operational_settings_used if consumer_node_info.component_type == ComponentType.COMPRESSOR_SYSTEM else None, - # model_period=period, model_timesteps=model.timesteps, ) From ed5fa78a34fb14109077f0950596d1f00d759a2f Mon Sep 17 00:00:00 2001 From: Frode Helgetun Krogh <70878501+frodehk@users.noreply.github.com> Date: Fri, 2 Aug 2024 13:38:48 +0200 Subject: [PATCH 03/12] chore: update method name ECALC-1230 --- .../dto/results/model/test_requested_pressures_compressors.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py b/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py index 1f470ad353..e87a03116b 100644 --- a/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py +++ b/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py @@ -9,7 +9,7 @@ from libecalc.common.utils.rates import TimeSeriesFloat from libecalc.presentation.json_result.mapper import ( get_asset_result, - get_requested_compressor_pressures_test, + get_requested_compressor_pressures, ) from libecalc.presentation.json_result.result.results import CompressorModelResult @@ -137,7 +137,7 @@ def test_for_bug(compressor_systems_and_compressor_train_temporal_dto): ].component_result.operational_settings_used model_timesteps = [datetime(2018, 1, 1), datetime(2021, 1, 1), datetime(2022, 1, 1)] - get_requested_compressor_pressures_test( + get_requested_compressor_pressures( energy_usage_model=energy_usage_model, pressure_type=CompressorPressureType.INLET_PRESSURE, model_timesteps=model_timesteps, From 245e4132e080a95ec58b35b316ae4a9490767d25 Mon Sep 17 00:00:00 2001 From: Frode Helgetun Krogh <70878501+frodehk@users.noreply.github.com> Date: Fri, 2 Aug 2024 14:46:26 +0200 Subject: [PATCH 04/12] test: update test ECALC-1230 --- .../results/model/test_requested_pressures_compressors.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py b/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py index e87a03116b..4c8de7ff94 100644 --- a/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py +++ b/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py @@ -123,7 +123,7 @@ def test_requested_pressures_compressor_system_temporal_model(result: EcalcModel assert requested_outlet_pressure_train2_upgr == 245 -def test_for_bug(compressor_systems_and_compressor_train_temporal_dto): +def test_model_timestep_not_in_energy_usage_model(compressor_systems_and_compressor_train_temporal_dto): graph = compressor_systems_and_compressor_train_temporal_dto.ecalc_model.get_graph() compressor_system = graph.get_node("compressor_system_variable_speed_compressor_trains_multiple_pressures") @@ -136,7 +136,9 @@ def test_for_bug(compressor_systems_and_compressor_train_temporal_dto): "compressor_system_variable_speed_compressor_trains_multiple_pressures" ].component_result.operational_settings_used - model_timesteps = [datetime(2018, 1, 1), datetime(2021, 1, 1), datetime(2022, 1, 1)] + model_timesteps = [datetime(2018, 1, 1), datetime(2021, 7, 1), datetime(2022, 1, 1)] + + # Ensure that method do not fail if model timesteps do not correspond exactly to dates in temporal model get_requested_compressor_pressures( energy_usage_model=energy_usage_model, pressure_type=CompressorPressureType.INLET_PRESSURE, From 480bcb83241c7cd0fbcd33559bba419a1f5c57c3 Mon Sep 17 00:00:00 2001 From: Frode Helgetun Krogh <70878501+frodehk@users.noreply.github.com> Date: Fri, 2 Aug 2024 16:10:36 +0200 Subject: [PATCH 05/12] test: update test ECALC-1230 --- .../model/test_requested_pressures_compressors.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py b/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py index 4c8de7ff94..fe528e7eec 100644 --- a/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py +++ b/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py @@ -136,7 +136,15 @@ def test_model_timestep_not_in_energy_usage_model(compressor_systems_and_compres "compressor_system_variable_speed_compressor_trains_multiple_pressures" ].component_result.operational_settings_used - model_timesteps = [datetime(2018, 1, 1), datetime(2021, 7, 1), datetime(2022, 1, 1)] + # Set some model time steps different from dates in temporal model + model_timesteps = [ + datetime(2018, 1, 1), + datetime(2018, 6, 23, 17, 20, 50), + datetime(2018, 7, 5, 12, 20, 50), + datetime(2022, 1, 1), + ] + + operational_settings_used.timesteps = model_timesteps # Ensure that method do not fail if model timesteps do not correspond exactly to dates in temporal model get_requested_compressor_pressures( From 514dab5e5168a5505ddefef0cd38f3f5c2d25ee4 Mon Sep 17 00:00:00 2001 From: Frode Helgetun Krogh <70878501+frodehk@users.noreply.github.com> Date: Tue, 6 Aug 2024 11:27:23 +0200 Subject: [PATCH 06/12] chore: add method to period to avoid interations over all timesteps ECALC-1230 --- src/libecalc/common/time_utils.py | 12 +++++ .../presentation/json_result/mapper.py | 52 +++++++++---------- .../test_requested_pressures_compressors.py | 11 ++-- 3 files changed, 46 insertions(+), 29 deletions(-) diff --git a/src/libecalc/common/time_utils.py b/src/libecalc/common/time_utils.py index 15738e347f..85456fef50 100644 --- a/src/libecalc/common/time_utils.py +++ b/src/libecalc/common/time_utils.py @@ -68,6 +68,18 @@ def get_timestep_indices(self, timesteps: List[datetime]) -> Tuple[int, int]: f"Period: {self.start}:{self.end} - timesteps: {timesteps}" ) from e + def get_timesteps(self, timesteps: List[datetime]) -> List[datetime]: + """ + Finds all timesteps from global time that are within the period. + Returns empty list if no timesteps are outside period. + """ + timesteps_included_in_period = [self.__contains__(timestep) for timestep in timesteps] + timesteps_in_period = [ + timestep for timestep, included in zip(timesteps, timesteps_included_in_period) if included + ] + + return timesteps_in_period + @dataclass class Periods: diff --git a/src/libecalc/presentation/json_result/mapper.py b/src/libecalc/presentation/json_result/mapper.py index 298388ffe9..fa7c4a8e09 100644 --- a/src/libecalc/presentation/json_result/mapper.py +++ b/src/libecalc/presentation/json_result/mapper.py @@ -82,38 +82,38 @@ def get_requested_compressor_pressures( for period, model in TemporalModel(energy_usage_model).items(): if isinstance(model, CompressorSystemConsumerFunction): # Loop timesteps in temporal model, to find correct operational settings used: - for timestep in model_timesteps: - if timestep >= period.start and timestep <= period.end: - for compressor in model.compressors: - if compressor.name == name: - operational_setting_used_id = get_operational_setting_used_id( - timestep=timestep, operational_settings_used=operational_settings_used - ) + timesteps_in_period = period.get_timesteps(model_timesteps) + for timestep in timesteps_in_period: + for compressor in model.compressors: + if compressor.name == name: + operational_setting_used_id = get_operational_setting_used_id( + timestep=timestep, operational_settings_used=operational_settings_used + ) - operational_setting = model.operational_settings[operational_setting_used_id] + operational_setting = model.operational_settings[operational_setting_used_id] - # Find correct compressor in case of different pressures for different components in system: - compressor_nr = int( - [i for i, compressor in enumerate(model.compressors) if compressor.name == name][0] - ) + # Find correct compressor in case of different pressures for different components in system: + compressor_nr = int( + [i for i, compressor in enumerate(model.compressors) if compressor.name == name][0] + ) - if pressure_type.value == CompressorPressureType.INLET_PRESSURE: - if operational_setting.suction_pressures is not None: - pressures = operational_setting.suction_pressures[compressor_nr] - else: - pressures = operational_setting.suction_pressure + if pressure_type.value == CompressorPressureType.INLET_PRESSURE: + if operational_setting.suction_pressures is not None: + pressures = operational_setting.suction_pressures[compressor_nr] + else: + pressures = operational_setting.suction_pressure + else: + if operational_setting.discharge_pressures is not None: + pressures = operational_setting.discharge_pressures[compressor_nr] else: - if operational_setting.discharge_pressures is not None: - pressures = operational_setting.discharge_pressures[compressor_nr] - else: - pressures = operational_setting.discharge_pressure + pressures = operational_setting.discharge_pressure - if pressures is None: - pressures = math.nan + if pressures is None: + pressures = math.nan - if not isinstance(pressures, Expression): - pressures = Expression.setup_from_expression(value=pressures) - evaluated_temporal_energy_usage_models[timestep] = pressures + if not isinstance(pressures, Expression): + pressures = Expression.setup_from_expression(value=pressures) + evaluated_temporal_energy_usage_models[timestep] = pressures else: pressures = model.suction_pressure diff --git a/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py b/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py index fe528e7eec..b98242ee4d 100644 --- a/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py +++ b/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py @@ -138,19 +138,24 @@ def test_model_timestep_not_in_energy_usage_model(compressor_systems_and_compres # Set some model time steps different from dates in temporal model model_timesteps = [ - datetime(2018, 1, 1), + datetime(2017, 1, 1), + datetime(2018, 1, 5), datetime(2018, 6, 23, 17, 20, 50), - datetime(2018, 7, 5, 12, 20, 50), datetime(2022, 1, 1), ] operational_settings_used.timesteps = model_timesteps # Ensure that method do not fail if model timesteps do not correspond exactly to dates in temporal model - get_requested_compressor_pressures( + requested_inlet_pressures = get_requested_compressor_pressures( energy_usage_model=energy_usage_model, pressure_type=CompressorPressureType.INLET_PRESSURE, model_timesteps=model_timesteps, name="train1", operational_settings_used=operational_settings_used, ) + + # train1 is only active in first period defined in energy usage model, 1.1.2018 - 1.1.2019, + # hence only timesteps within this interval should be included: + assert requested_inlet_pressures.models[0].period.start == datetime(2018, 1, 5) + assert requested_inlet_pressures.models[0].period.end == datetime(2018, 6, 23, 17, 20, 50) From 7233762985f7eefbb501c702f97da15e08f9ce6f Mon Sep 17 00:00:00 2001 From: Frode Helgetun Krogh <70878501+frodehk@users.noreply.github.com> Date: Tue, 6 Aug 2024 12:33:41 +0200 Subject: [PATCH 07/12] test: update test ECALC-1230 --- .../test_requested_pressures_compressors.py | 51 ++++++++++++++++--- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py b/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py index b98242ee4d..9a3ccda0b2 100644 --- a/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py +++ b/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py @@ -137,13 +137,19 @@ def test_model_timestep_not_in_energy_usage_model(compressor_systems_and_compres ].component_result.operational_settings_used # Set some model time steps different from dates in temporal model + global_time_1 = datetime(2017, 1, 1) + global_time_2 = datetime(2018, 1, 5) + global_time_3 = datetime(2018, 6, 23, 17, 20, 50) + global_time_4 = datetime(2022, 1, 1) + model_timesteps = [ - datetime(2017, 1, 1), - datetime(2018, 1, 5), - datetime(2018, 6, 23, 17, 20, 50), - datetime(2022, 1, 1), + global_time_1, + global_time_2, + global_time_3, + global_time_4, ] + compressor_nr = 0 operational_settings_used.timesteps = model_timesteps # Ensure that method do not fail if model timesteps do not correspond exactly to dates in temporal model @@ -157,5 +163,38 @@ def test_model_timestep_not_in_energy_usage_model(compressor_systems_and_compres # train1 is only active in first period defined in energy usage model, 1.1.2018 - 1.1.2019, # hence only timesteps within this interval should be included: - assert requested_inlet_pressures.models[0].period.start == datetime(2018, 1, 5) - assert requested_inlet_pressures.models[0].period.end == datetime(2018, 6, 23, 17, 20, 50) + assert requested_inlet_pressures.models[0].period.start == global_time_2 + assert requested_inlet_pressures.models[1].period.start == global_time_3 + + # Get requested pressures for the two relevant timesteps + requested_inlet_pressure_2018_1_5 = requested_inlet_pressures.models[0].model.evaluate( + variables=variables.variables, fill_length=len(model_timesteps) + ) + requested_inlet_pressure_2018_6_23 = requested_inlet_pressures.models[1].model.evaluate( + variables=variables.variables, fill_length=len(model_timesteps) + ) + + # Extract the correct operational setting (index) used for the given timesteps. + operational_setting_2018_1_5 = operational_settings_used.for_timestep(global_time_2).values[0] - 1 + operational_setting_2018_6_23 = operational_settings_used.for_timestep(global_time_3).values[0] - 1 + + # Get input requested pressures from the relevant compressor and operational settings: + train1_inlet_pressures_operational_settings_2 = ( + compressor_system.energy_usage_model[datetime(2018, 1, 1)] + .operational_settings[operational_setting_2018_1_5] + .suction_pressures[compressor_nr] + .evaluate(variables=variables.variables, fill_length=len(model_timesteps)) + ) + + train1_inlet_pressures_operational_settings_1 = ( + compressor_system.energy_usage_model[datetime(2018, 1, 1)] + .operational_settings[operational_setting_2018_6_23] + .suction_pressures[compressor_nr] + .evaluate(variables=variables.variables, fill_length=len(model_timesteps)) + ) + + # Verify that extracted pressures for first timestep is correct, using the second operational setting + assert list(requested_inlet_pressure_2018_1_5) == list(train1_inlet_pressures_operational_settings_2) + + # Verify that extracted pressures for second timestep is correct, using the first operational setting + assert list(requested_inlet_pressure_2018_6_23) == list(train1_inlet_pressures_operational_settings_1) From ff8fa40d75d495a1a21c6c84a714707ddb8c9682 Mon Sep 17 00:00:00 2001 From: Frode Helgetun Krogh <70878501+frodehk@users.noreply.github.com> Date: Tue, 6 Aug 2024 12:36:24 +0200 Subject: [PATCH 08/12] test: update test ECALC-1230 --- .../dto/results/model/test_requested_pressures_compressors.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py b/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py index 9a3ccda0b2..5779d60d5f 100644 --- a/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py +++ b/src/tests/libecalc/dto/results/model/test_requested_pressures_compressors.py @@ -150,6 +150,10 @@ def test_model_timestep_not_in_energy_usage_model(compressor_systems_and_compres ] compressor_nr = 0 + + # Timesteps in operational settings must be equal to model timesteps. + # Basically replacing global timesteps, to have them different from + # the timesteps in the temporal model. operational_settings_used.timesteps = model_timesteps # Ensure that method do not fail if model timesteps do not correspond exactly to dates in temporal model From 9e737c8a14dccb5c861561d9eb5589ebc524a22c Mon Sep 17 00:00:00 2001 From: Frode Helgetun Krogh <70878501+frodehk@users.noreply.github.com> Date: Tue, 6 Aug 2024 15:05:47 +0200 Subject: [PATCH 09/12] Update src/libecalc/common/time_utils.py Co-authored-by: Thomas Falch Johansen --- src/libecalc/common/time_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libecalc/common/time_utils.py b/src/libecalc/common/time_utils.py index 85456fef50..2bee8dd399 100644 --- a/src/libecalc/common/time_utils.py +++ b/src/libecalc/common/time_utils.py @@ -73,7 +73,7 @@ def get_timesteps(self, timesteps: List[datetime]) -> List[datetime]: Finds all timesteps from global time that are within the period. Returns empty list if no timesteps are outside period. """ - timesteps_included_in_period = [self.__contains__(timestep) for timestep in timesteps] + timesteps = [timestep for timestep in timesteps if self.__contains__(timestep)] timesteps_in_period = [ timestep for timestep, included in zip(timesteps, timesteps_included_in_period) if included ] From 8495a04f3edf856cd31d346895ef32de6176347c Mon Sep 17 00:00:00 2001 From: Frode Helgetun Krogh <70878501+frodehk@users.noreply.github.com> Date: Tue, 6 Aug 2024 15:07:30 +0200 Subject: [PATCH 10/12] chore: update get timesteps ECALC-1230 --- src/libecalc/common/time_utils.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/libecalc/common/time_utils.py b/src/libecalc/common/time_utils.py index 2bee8dd399..dfade7ead6 100644 --- a/src/libecalc/common/time_utils.py +++ b/src/libecalc/common/time_utils.py @@ -74,11 +74,8 @@ def get_timesteps(self, timesteps: List[datetime]) -> List[datetime]: Returns empty list if no timesteps are outside period. """ timesteps = [timestep for timestep in timesteps if self.__contains__(timestep)] - timesteps_in_period = [ - timestep for timestep, included in zip(timesteps, timesteps_included_in_period) if included - ] - return timesteps_in_period + return timesteps @dataclass From 74f5669aa7b824413fede22f363c26b4f94a3529 Mon Sep 17 00:00:00 2001 From: Frode Helgetun Krogh <70878501+frodehk@users.noreply.github.com> Date: Tue, 6 Aug 2024 15:23:57 +0200 Subject: [PATCH 11/12] test: add unit tests ECALC-1230 --- src/tests/libecalc/common/test_time_utils.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/tests/libecalc/common/test_time_utils.py b/src/tests/libecalc/common/test_time_utils.py index 3cf3d0e6b1..293f9652a7 100644 --- a/src/tests/libecalc/common/test_time_utils.py +++ b/src/tests/libecalc/common/test_time_utils.py @@ -53,6 +53,21 @@ def test_end_not_defined(self): assert datetime(2050, 1, 1) in period assert datetime(4000, 1, 1) in period + def test_all_timesteps_in_period(self): + period = Period(start=datetime(2022, 1, 1), end=datetime(2030, 1, 1)) + timesteps = [datetime(2022, 1, 1), datetime(2023, 1, 1)] + assert period.get_timesteps(timesteps) == timesteps + + def test_some_timesteps_in_period(self): + period = Period(start=datetime(2022, 1, 1), end=datetime(2030, 1, 1)) + timesteps = [datetime(2018, 1, 1), datetime(2022, 1, 1), datetime(2023, 1, 1)] + assert period.get_timesteps(timesteps) == [timesteps[1], timesteps[2]] + + def test_no_timesteps_in_period(self): + period = Period(start=datetime(2022, 1, 1), end=datetime(2030, 1, 1)) + timesteps = [datetime(2018, 1, 1), datetime(2019, 1, 1)] + assert period.get_timesteps(timesteps) == [] + class TestCreatePeriods: def test_single_date(self): From ae0c2a9499b2a32030dd1eca000b5d811c4215b1 Mon Sep 17 00:00:00 2001 From: Frode Helgetun Krogh <70878501+frodehk@users.noreply.github.com> Date: Tue, 6 Aug 2024 15:26:22 +0200 Subject: [PATCH 12/12] test: update description ECALC-1230 --- src/libecalc/common/time_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libecalc/common/time_utils.py b/src/libecalc/common/time_utils.py index dfade7ead6..82e9daa6ee 100644 --- a/src/libecalc/common/time_utils.py +++ b/src/libecalc/common/time_utils.py @@ -70,7 +70,7 @@ def get_timestep_indices(self, timesteps: List[datetime]) -> Tuple[int, int]: def get_timesteps(self, timesteps: List[datetime]) -> List[datetime]: """ - Finds all timesteps from global time that are within the period. + Get all given timesteps that are within this period. Returns empty list if no timesteps are outside period. """ timesteps = [timestep for timestep in timesteps if self.__contains__(timestep)]