Skip to content

Commit

Permalink
frodehk/v8.4 changelog (#231)
Browse files Browse the repository at this point in the history
* chore: handle requested pressures for compressor systems (#215)

(cherry picked from commit 6b05439)

* chore: move feature experimental to main method for requested pressures (#230)

(cherry picked from commit 00ad854)
  • Loading branch information
frodehk authored Oct 4, 2023
1 parent a3f163d commit 0b16685
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 23 deletions.
6 changes: 3 additions & 3 deletions src/libecalc/common/feature_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ def experimental(feature_description: str) -> Optional[Any]:

def decorate(experimental_feature: Callable):
@wraps(experimental_feature)
def with_experimental(self, *args, **kwargs):
def with_experimental(*args, **kwargs):
logger.warning(
f"!EXPERIMENTAL! {feature_description}."
f" It has not thoroughly tested and Quality Assured yet. Use at own risk. !EXPERIMENTAL!"
f" It has not been thoroughly tested and Quality Assured yet. Use at own risk. !EXPERIMENTAL!"
)
try:
return experimental_feature(self, *args, **kwargs)
return experimental_feature(*args, **kwargs)
except Exception as e:
logger.error(
f"Error in {feature_description}: {e}."
Expand Down
93 changes: 73 additions & 20 deletions src/libecalc/core/graph_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
import operator
from datetime import datetime
from functools import reduce
from typing import Any, Dict, List
from typing import Any, Dict, List, Optional

import libecalc
from libecalc import dto
from libecalc.common.component_info.component_level import ComponentLevel
from libecalc.common.component_info.compressor import CompressorPressureType
from libecalc.common.exceptions import ProgrammingError
from libecalc.common.feature_flags import Feature
from libecalc.common.temporal_model import TemporalExpression, TemporalModel
from libecalc.common.time_utils import Period
from libecalc.common.units import Unit
Expand All @@ -18,6 +19,7 @@
from libecalc.common.utils.rates import (
TimeSeriesBoolean,
TimeSeriesFloat,
TimeSeriesInt,
TimeSeriesRate,
)
from libecalc.core.result import ComponentResult, EcalcModelResult
Expand Down Expand Up @@ -215,49 +217,90 @@ def _parse_emissions(emissions: Dict[str, EmissionResult]) -> Dict[str, libecalc
}

@staticmethod
def operational_setting_used_id(timestep: datetime, operational_settings_used: TimeSeriesInt) -> int:
timestep_index = operational_settings_used.timesteps.index(timestep)

operational_setting_id = operational_settings_used.values[timestep_index]

return operational_setting_id - 1

@staticmethod
@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 = {}
for period, model in TemporalModel(energy_usage_model).items():
if isinstance(model, CompressorSystemConsumerFunction):
for compressor, operational_setting in zip(model.compressors, model.operational_settings):
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:
pressures = operational_setting.suction_pressure
operational_setting_used_id = GraphResult.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.OUTLET_PRESSURE:
pressures = operational_setting.discharge_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:
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_subset.suction_pressure

evaluated_temporal_energy_usage_models[period.start] = pressures
else:
pressures = model.suction_pressure

if pressure_type.value == CompressorPressureType.OUTLET_PRESSURE:
pressures = model.discharge_pressure
if pressure_type.value == CompressorPressureType.OUTLET_PRESSURE:
pressures = model_subset.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)
if not isinstance(pressures, Expression):
pressures = Expression.setup_from_expression(value=pressures)

evaluated_temporal_energy_usage_models[period.start] = pressures
evaluated_temporal_energy_usage_models[model_period.start] = pressures

return TemporalModel(evaluated_temporal_energy_usage_models)

Expand All @@ -280,19 +323,29 @@ def get_asset_result(self) -> libecalc.dto.result.EcalcModelResult:
component = self.graph.get_component(consumer_id)

for model in consumer_result.models:
period = Period(model.timesteps[0], model.timesteps[-1])

inlet_pressure_eval = GraphResult.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 = GraphResult.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,
)

period = Period(model.timesteps[0], model.timesteps[-1])

requested_inlet_pressure = TimeSeriesFloat(
timesteps=self.timesteps,
values=TemporalExpression.evaluate(inlet_pressure_eval, self.variables_map),
Expand Down

0 comments on commit 0b16685

Please sign in to comment.