Skip to content

Commit 5075e8c

Browse files
committed
refactor: implement condition for direct consumer
1 parent 0808ddf commit 5075e8c

11 files changed

Lines changed: 149 additions & 92 deletions

File tree

src/libecalc/domain/condition.py

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,42 +18,37 @@ class Condition:
1818

1919
def __init__(
2020
self,
21+
expression_evaluator: ExpressionEvaluator,
2122
expression_input: ExpressionType | None,
2223
):
2324
"""
2425
Initialize a Condition instance.
2526
2627
Args:
2728
expression_input (ExpressionType | None): The raw input for the condition expression.
29+
expression_evaluator (ExpressionEvaluator): Evaluator for variable and condition expressions.
2830
"""
31+
self.expression_evaluator = expression_evaluator
2932
self._expression_input = expression_input
3033
self.expression = convert_expression(self._expression_input)
3134

32-
def as_vector(
33-
self,
34-
expression_evaluator: ExpressionEvaluator,
35-
) -> NDArray[np.int_] | None:
35+
def as_vector(self) -> NDArray[np.int_] | None:
3636
"""
3737
Evaluate the condition expression and return a vector indicating where the condition is met.
3838
39-
Args:
40-
expression_evaluator (ExpressionEvaluator): Evaluator for variable and condition expressions.
41-
4239
Returns:
4340
NDArray[np.int_] | None: An integer array (1 where condition is true, 0 otherwise),
4441
or None if no condition is set.
4542
"""
4643
if self.expression is None:
4744
return None
4845

49-
condition = expression_evaluator.evaluate(expression=self.expression)
46+
condition = self.expression_evaluator.evaluate(expression=self.expression)
5047
condition = (condition != 0).astype(int)
5148

5249
return np.array(condition)
5350

54-
def _apply(
55-
self, input_array: NDArray[np.float64], expression_evaluator: ExpressionEvaluator
56-
) -> NDArray[np.float64]:
51+
def _apply(self, input_array: NDArray[np.float64]) -> NDArray[np.float64]:
5752
"""
5853
Internal method containing the core logic for applying the condition to an array.
5954
This method performs the actual masking/zeroing operation based on the condition:
@@ -63,32 +58,31 @@ def _apply(
6358
6459
Args:
6560
input_array (NDArray[np.float64]): The array to apply the condition to.
66-
expression_evaluator (ExpressionEvaluator): Evaluator for the condition expression.
6761
6862
Returns:
6963
NDArray[np.float64]: The resulting array after applying the condition.
7064
7165
"""
72-
condition_vector = self.as_vector(expression_evaluator)
66+
condition_vector = self.as_vector()
7367
if condition_vector is None:
7468
return input_array.copy()
7569
return np.where(condition_vector, input_array, 0)
7670

77-
def apply_to_array(self, input_array: np.ndarray, expression_evaluator: ExpressionEvaluator) -> np.ndarray:
71+
def apply_to_array(self, input_array: np.ndarray) -> np.ndarray:
7872
"""
7973
Convenience method for applying the condition and returning the result as a Python list.
8074
8175
This is useful when a standard Python list is required . Internally, it calls `_apply`
8276
and converts the result to a list.
8377
"""
84-
return self._apply(input_array, expression_evaluator)
78+
return self._apply(input_array)
8579

86-
def apply_to_array_as_list(self, input_array: np.ndarray, expression_evaluator: ExpressionEvaluator) -> list:
80+
def apply_to_array_as_list(self, input_array: np.ndarray) -> list:
8781
"""
8882
Apply the condition to a numpy array and return the result as a Python list.
8983
9084
This method is a convenience wrapper around `apply_to_array` for cases where a standard
9185
Python list is needed instead of a numpy array, for example when serializing results
9286
or interfacing with code that does not use numpy.
9387
"""
94-
return self._apply(input_array, expression_evaluator).tolist()
88+
return self._apply(input_array).tolist()

src/libecalc/domain/infrastructure/energy_components/legacy_consumer/consumer_function/direct_expression_consumer_function.py

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
import numpy as np
22

33
from libecalc.common.energy_usage_type import EnergyUsageType
4-
from libecalc.common.list.list_utils import array_to_list
54
from libecalc.common.units import Unit
65
from libecalc.common.utils.rates import Rates, RateType
76
from libecalc.common.variables import ExpressionEvaluator
7+
from libecalc.domain.condition import Condition
88
from libecalc.domain.infrastructure.energy_components.legacy_consumer.consumer_function import (
99
ConsumerFunction,
1010
ConsumerFunctionResult,
1111
)
1212
from libecalc.domain.infrastructure.energy_components.legacy_consumer.consumer_function.utils import (
13-
apply_condition,
1413
apply_power_loss_factor,
15-
get_condition_from_expression,
1614
get_power_loss_factor_from_expression,
1715
)
1816
from libecalc.domain.process.core.results import EnergyFunctionGenericResult
@@ -23,19 +21,20 @@ class DirectExpressionConsumerFunction(ConsumerFunction):
2321
def __init__(
2422
self,
2523
energy_usage_type: EnergyUsageType,
26-
condition: Expression | None = None,
24+
condition: Condition,
2725
fuel_rate: Expression | None = None,
2826
load: Expression | None = None,
2927
power_loss_factor: Expression | None = None,
3028
consumption_rate_type: RateType = RateType.STREAM_DAY,
3129
):
30+
self.condition = condition
3231
expression = fuel_rate if energy_usage_type == EnergyUsageType.FUEL.value else load
3332
power_loss_factor_expression = power_loss_factor
3433
assert isinstance(consumption_rate_type, RateType)
3534
self._energy_usage_type = energy_usage_type
3635
self._expression = expression
3736
self._convert_to_stream_day = consumption_rate_type == RateType.CALENDAR_DAY
38-
self._condition_expression = condition
37+
3938
self._power_loss_factor_expression = power_loss_factor_expression
4039

4140
@property
@@ -68,26 +67,21 @@ def evaluate(
6867
) -> ConsumerFunctionResult:
6968
energy_usage_expression_evaluated = expression_evaluator.evaluate(expression=self._expression) # type: ignore[arg-type]
7069

71-
# Do conditioning first - set rates to zero if conditions are not met
72-
condition = get_condition_from_expression(
73-
expression_evaluator=expression_evaluator,
74-
condition_expression=self._condition_expression,
75-
)
76-
77-
energy_usage = apply_condition(
78-
input_array=Rates.to_stream_day(
79-
calendar_day_rates=energy_usage_expression_evaluated,
80-
regularity=regularity,
70+
energy_usage = self.condition.apply_to_array_as_list(
71+
input_array=(
72+
Rates.to_stream_day(
73+
calendar_day_rates=energy_usage_expression_evaluated,
74+
regularity=regularity,
75+
)
76+
if self._convert_to_stream_day
77+
else energy_usage_expression_evaluated
8178
)
82-
if self._convert_to_stream_day
83-
else energy_usage_expression_evaluated,
84-
condition=condition, # type: ignore[arg-type]
8579
)
8680

8781
energy_function_result = EnergyFunctionGenericResult(
88-
energy_usage=array_to_list(energy_usage), # type: ignore[arg-type]
82+
energy_usage=energy_usage, # type: ignore[arg-type]
8983
energy_usage_unit=self.energy_usage_unit,
90-
power=array_to_list(energy_usage) if self.is_electrical_consumer else None,
84+
power=energy_usage if self.is_electrical_consumer else None,
9185
power_unit=self.power_unit if self.is_electrical_consumer else None,
9286
)
9387

@@ -104,13 +98,13 @@ def evaluate(
10498
# but negative fuel rates are always invalid.
10599

106100
if self.is_fuel_consumer:
107-
is_valid[energy_usage < 0] = False
101+
is_valid[np.asarray(energy_usage) < 0] = False
108102

109103
consumer_function_result = ConsumerFunctionResult(
110104
periods=expression_evaluator.get_periods(),
111105
is_valid=is_valid,
112106
energy_function_result=energy_function_result,
113-
condition=condition,
107+
condition=self.condition.as_vector(),
114108
energy_usage_before_power_loss_factor=np.asarray(energy_function_result.energy_usage),
115109
power_loss_factor=power_loss_factor,
116110
energy_usage=apply_power_loss_factor(

src/libecalc/domain/infrastructure/energy_components/legacy_consumer/tabulated/tabular_consumer_function.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def __init__(
5656
energy_usage_adjustment_constant: float,
5757
energy_usage_adjustment_factor: float,
5858
variables_expressions: list[VariableExpression],
59-
condition: Condition | None = None,
59+
condition: Condition,
6060
power_loss_factor_expression: Expression | None = None,
6161
):
6262
"""Tabulated consumer function [MW] (energy) or [Sm3/day] (fuel)."""
@@ -70,7 +70,7 @@ def __init__(
7070
)
7171
self._variables_expressions = variables_expressions
7272

73-
self.condition = condition if condition is not None else Condition(None)
73+
self.condition = condition
7474
# Typically used for power line loss subsea et.c.
7575
self._power_loss_factor_expression = power_loss_factor_expression
7676

@@ -111,11 +111,11 @@ def evaluate(
111111
# for tabular, is_valid is based on energy_usage being NaN. This will also (correctly) change potential
112112
# invalid points to valid where the condition sets energy_usage to zero
113113
energy_function_result.energy_usage = self.condition.apply_to_array_as_list(
114-
np.asarray(energy_function_result.energy_usage), expression_evaluator
114+
np.asarray(energy_function_result.energy_usage)
115115
)
116116

117117
energy_function_result.power = (
118-
self.condition.apply_to_array_as_list(np.asarray(energy_function_result.power), expression_evaluator)
118+
self.condition.apply_to_array_as_list(np.asarray(energy_function_result.power))
119119
if energy_function_result.power is not None
120120
else None
121121
)
@@ -129,7 +129,7 @@ def evaluate(
129129
periods=expression_evaluator.get_periods(),
130130
is_valid=np.asarray(energy_function_result.is_valid),
131131
energy_function_result=energy_function_result,
132-
condition=self.condition.as_vector(expression_evaluator),
132+
condition=self.condition.as_vector(),
133133
energy_usage_before_power_loss_factor=np.asarray(energy_function_result.energy_usage),
134134
power_loss_factor=power_loss_factor,
135135
energy_usage=apply_power_loss_factor(

src/libecalc/presentation/yaml/mappers/component_mapper.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,10 @@ class ConsumerMapper:
152152
def __init__(self, references: ReferenceService, target_period: Period):
153153
self.__references = references
154154
self._target_period = target_period
155-
self.__energy_usage_model_mapper = ConsumerFunctionMapper(references=references, target_period=target_period)
155+
self.__energy_usage_model_mapper = ConsumerFunctionMapper(
156+
references=references,
157+
target_period=target_period,
158+
)
156159

157160
def from_yaml_to_domain(
158161
self,
@@ -198,6 +201,7 @@ def create_error_from_yaml_path(
198201
energy_usage_model = self.__energy_usage_model_mapper.from_yaml_to_dto(
199202
data.energy_usage_model,
200203
consumes=consumes,
204+
expression_evaluator=expression_evaluator,
201205
)
202206
except InvalidEnergyUsageModelException as e:
203207
energy_usage_model_yaml_path = yaml_path.append("ENERGY_USAGE_MODEL")

src/libecalc/presentation/yaml/mappers/consumer_function_mapper.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from libecalc.common.temporal_model import TemporalModel
99
from libecalc.common.time_utils import Period, define_time_model_for_period
1010
from libecalc.common.utils.rates import RateType
11+
from libecalc.common.variables import ExpressionEvaluator
1112
from libecalc.domain.condition import Condition
1213
from libecalc.domain.infrastructure.energy_components.legacy_consumer.consumer_function import ConsumerFunction
1314
from libecalc.domain.infrastructure.energy_components.legacy_consumer.consumer_function.compressor_consumer_function import (
@@ -182,9 +183,14 @@ def __init__(
182183
self._target_period = target_period
183184

184185
def _map_direct(
185-
self, model: YamlEnergyUsageModelDirectFuel | YamlEnergyUsageModelDirectElectricity, consumes: ConsumptionType
186+
self,
187+
model: YamlEnergyUsageModelDirectFuel | YamlEnergyUsageModelDirectElectricity,
188+
consumes: ConsumptionType,
189+
expression_evaluator: ExpressionEvaluator,
186190
) -> DirectExpressionConsumerFunction:
187-
condition = convert_expression(_map_condition(model))
191+
condition_input = _map_condition(model)
192+
condition = Condition(expression_input=condition_input, expression_evaluator=expression_evaluator)
193+
188194
consumption_rate_type = RateType((model.consumption_rate_type or ConsumptionRateType.STREAM_DAY).value)
189195
power_loss_factor = convert_expression(model.power_loss_factor)
190196
if isinstance(model, YamlEnergyUsageModelDirectFuel):
@@ -193,7 +199,7 @@ def _map_direct(
193199
return DirectExpressionConsumerFunction(
194200
energy_usage_type=EnergyUsageType.FUEL,
195201
fuel_rate=convert_expression(model.fuel_rate), # type: ignore[arg-type]
196-
condition=condition, # type: ignore[arg-type]
202+
condition=condition,
197203
power_loss_factor=power_loss_factor, # type: ignore[arg-type]
198204
consumption_rate_type=consumption_rate_type,
199205
)
@@ -206,12 +212,17 @@ def _map_direct(
206212
return DirectExpressionConsumerFunction(
207213
energy_usage_type=EnergyUsageType.POWER,
208214
load=convert_expression(model.load), # type: ignore[arg-type]
209-
condition=condition, # type: ignore[arg-type]
215+
condition=condition,
210216
power_loss_factor=power_loss_factor, # type: ignore[arg-type]
211217
consumption_rate_type=consumption_rate_type,
212218
)
213219

214-
def _map_tabular(self, model: YamlEnergyUsageModelTabulated, consumes: ConsumptionType) -> TabularConsumerFunction:
220+
def _map_tabular(
221+
self,
222+
model: YamlEnergyUsageModelTabulated,
223+
consumes: ConsumptionType,
224+
expression_evaluator: ExpressionEvaluator,
225+
) -> TabularConsumerFunction:
215226
energy_model = self.__references.get_tabulated_model(model.energy_function)
216227
energy_usage_type = energy_model.get_energy_usage_type()
217228
energy_usage_type_as_consumption_type = (
@@ -222,7 +233,7 @@ def _map_tabular(self, model: YamlEnergyUsageModelTabulated, consumes: Consumpti
222233
raise InvalidConsumptionType(actual=energy_usage_type_as_consumption_type, expected=consumes)
223234

224235
condition_input = _map_condition(model)
225-
condition = Condition(expression_input=condition_input)
236+
condition = Condition(expression_input=condition_input, expression_evaluator=expression_evaluator)
226237

227238
power_loss_factor = convert_expression(model.power_loss_factor)
228239

@@ -503,14 +514,17 @@ def from_yaml_to_dto(
503514
self,
504515
data: YamlTemporalModel[YamlFuelEnergyUsageModel] | YamlTemporalModel[YamlElectricityEnergyUsageModel],
505516
consumes: ConsumptionType,
517+
expression_evaluator: ExpressionEvaluator,
506518
) -> TemporalModel[ConsumerFunction]:
507519
time_adjusted_model = define_time_model_for_period(data, target_period=self._target_period)
508520

509521
temporal_dict: dict[Period, ConsumerFunction] = {}
510522
for period, model in time_adjusted_model.items():
511523
try:
512524
if isinstance(model, YamlEnergyUsageModelDirectElectricity | YamlEnergyUsageModelDirectFuel):
513-
mapped_model = self._map_direct(model=model, consumes=consumes)
525+
mapped_model = self._map_direct(
526+
model=model, consumes=consumes, expression_evaluator=expression_evaluator
527+
)
514528
elif isinstance(model, YamlEnergyUsageModelCompressor):
515529
mapped_model = self._map_compressor(model, consumes=consumes)
516530
elif isinstance(model, YamlEnergyUsageModelPump):
@@ -520,7 +534,9 @@ def from_yaml_to_dto(
520534
elif isinstance(model, YamlEnergyUsageModelPumpSystem):
521535
mapped_model = self._map_pump_system(model, consumes=consumes)
522536
elif isinstance(model, YamlEnergyUsageModelTabulated):
523-
mapped_model = self._map_tabular(model=model, consumes=consumes)
537+
mapped_model = self._map_tabular(
538+
model=model, consumes=consumes, expression_evaluator=expression_evaluator
539+
)
524540
elif isinstance(model, YamlEnergyUsageModelCompressorTrainMultipleStreams):
525541
mapped_model = self._map_multiple_streams_compressor(model, consumes=consumes)
526542
else:

0 commit comments

Comments
 (0)