Skip to content

Commit a938235

Browse files
committed
chore: expose configuration handlers
1 parent f5639d7 commit a938235

7 files changed

Lines changed: 120 additions & 20 deletions

File tree

src/libecalc/ecalc_model/process_simulation.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,50 @@
1-
from dataclasses import dataclass
1+
from collections.abc import Sequence
22
from typing import Final, Literal, NewType, Self, assert_never
33
from uuid import UUID
44

5+
from libecalc.common.ddd import value_object
56
from libecalc.common.ddd.entity import Entity
67
from libecalc.common.utils.ecalc_uuid import ecalc_id_generator
8+
from libecalc.ecalc_model.time_series_configuration import (
9+
TimeSeriesChokeConfiguration,
10+
TimeSeriesTemperatureSetterConfiguration,
11+
)
712
from libecalc.ecalc_model.time_series_stream import TimeSeriesStream
813
from libecalc.presentation.yaml.domain.time_series_expression import TimeSeriesExpression
914
from libecalc.process.process_pipeline.process_pipeline import ProcessPipelineId
15+
from libecalc.process.process_solver.configuration import ConfigurationHandlerId
16+
from libecalc.process.process_solver.configuration_handler import ConfigurationHandler
1017
from libecalc.process.stream_distribution.common_stream_distribution import Overflow
1118

1219

13-
@dataclass
20+
@value_object
1421
class CommonStreamSettings:
1522
rate_fractions: list[TimeSeriesExpression]
1623
overflow: list[Overflow]
1724

1825

19-
@dataclass
26+
@value_object
2027
class CommonStreamDistributionConfig: # TODO: Rather a strategy that splits a stream in a method according to a policy?
2128
inlet_stream: TimeSeriesStream
2229
settings: list[CommonStreamSettings]
2330

2431

25-
@dataclass
32+
@value_object
2633
class IndividualStreamDistributionConfig:
2734
inlet_streams: list[TimeSeriesStream]
2835

2936

30-
@dataclass
37+
@value_object
3138
class Constraint: # should this instead be more flexible wrt. matching one or more stream conditions?
3239
outlet_pressure: TimeSeriesExpression
3340

3441

35-
@dataclass
42+
@value_object
3643
class PressureControlConfig: # Spec
3744
type: Literal["UPSTREAM_CHOKE", "DOWNSTREAM_CHOKE", "COMMON_ASV", "INDIVIDUAL_ASV_RATE", "INDIVIDUAL_ASV_PRESSURE"]
3845

3946

40-
@dataclass
47+
@value_object
4148
class AntiSurgeConfig:
4249
type: Literal["INDIVIDUAL_ASV", "COMMON_ASV"]
4350

@@ -55,13 +62,20 @@ def __init__(
5562
pressure_control_strategy: PressureControlConfig,
5663
anti_surge_strategy: AntiSurgeConfig,
5764
constraint: Constraint,
65+
configuration_handlers: Sequence[ConfigurationHandler],
5866
process_pipeline_id: ProcessPipelineId,
67+
configurations: dict[
68+
ConfigurationHandlerId, TimeSeriesChokeConfiguration | TimeSeriesTemperatureSetterConfiguration
69+
]
70+
| None = None,
5971
process_problem_id: ProcessProblemId | None = None,
6072
):
6173
self.pressure_control_strategy = pressure_control_strategy
6274
self.anti_surge_strategy = anti_surge_strategy
6375
self.constraint = constraint
6476
self.process_pipeline_id = process_pipeline_id
77+
self.configuration_handlers = configuration_handlers
78+
self.configurations = configurations or []
6579
self._id: Final[ProcessProblemId] = process_problem_id or ProcessProblem._create_id()
6680

6781
def get_id(self) -> ProcessProblemId:
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from libecalc.common.ddd import value_object
2+
from libecalc.presentation.yaml.domain.time_series_expression import TimeSeriesExpression
3+
4+
5+
@value_object
6+
class TimeSeriesChokeConfiguration:
7+
delta_pressure: TimeSeriesExpression
8+
9+
10+
@value_object
11+
class TimeSeriesTemperatureSetterConfiguration:
12+
temperature: TimeSeriesExpression

src/libecalc/ecalc_model/time_series_stream.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
from dataclasses import dataclass
2-
1+
from libecalc.common.ddd import value_object
32
from libecalc.presentation.yaml.domain.expression_time_series_flow_rate import ExpressionTimeSeriesFlowRate
43
from libecalc.presentation.yaml.domain.time_series_expression import TimeSeriesExpression
54
from libecalc.process.fluid_stream.fluid_model import FluidModel
65

76

8-
@dataclass(frozen=True)
7+
@value_object
98
class TimeSeriesStream:
109
fluid_model: FluidModel
1110
pressure_bara: TimeSeriesExpression

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

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
ProcessProblem,
2020
ProcessSimulation,
2121
)
22+
from libecalc.ecalc_model.time_series_configuration import (
23+
TimeSeriesChokeConfiguration,
24+
TimeSeriesTemperatureSetterConfiguration,
25+
)
2226
from libecalc.ecalc_model.time_series_stream import TimeSeriesStream
2327
from libecalc.expression.expression import ExpressionType
2428
from libecalc.presentation.yaml.domain.expression_time_series_flow_rate import ExpressionTimeSeriesFlowRate
@@ -63,6 +67,9 @@
6367
from libecalc.process.process_solver.process_runner import ProcessRunner
6468
from libecalc.process.process_solver.recirculation_loop import RecirculationLoop
6569
from libecalc.process.process_solver.search_strategies import ScipyRootFindingStrategy
70+
from libecalc.process.process_solver.temperature_setter_configuration_handler import (
71+
TemperatureSetterConfigurationHandler,
72+
)
6673
from libecalc.process.process_units.choke import Choke
6774
from libecalc.process.process_units.compressor import Compressor
6875
from libecalc.process.process_units.direct_mixer import DirectMixer
@@ -286,6 +293,11 @@ def map_process_simulation(
286293
constraints: dict[ProcessPipelineId, Constraint] = {}
287294
pressure_control_configs: dict[ProcessPipelineId, PressureControlConfig] = {}
288295
anti_surge_configs: dict[ProcessPipelineId, AntiSurgeConfig] = {}
296+
configuration_handlers: dict[ProcessPipelineId, Sequence[ConfigurationHandler]] = {}
297+
configurations: dict[
298+
ProcessPipelineId,
299+
dict[ConfigurationHandlerId, TimeSeriesChokeConfiguration | TimeSeriesTemperatureSetterConfiguration],
300+
] = {}
289301

290302
process_pipeline_reference_to_id_map: dict[str, ProcessPipelineId] = {}
291303
for yaml_compressor_train_item in yaml_process_simulation.targets:
@@ -294,15 +306,34 @@ def map_process_simulation(
294306
process_unit_map: dict[ProcessUnitId, ProcessUnit] = {}
295307
compressor_stages: list[list[ProcessUnitId]] = []
296308
compressor_ids: list[ProcessUnitId] = []
309+
problem_configuration_handlers = []
310+
problem_time_series_configurations: dict[
311+
ConfigurationHandlerId, TimeSeriesChokeConfiguration | TimeSeriesTemperatureSetterConfiguration
312+
] = {}
297313
for yaml_serial_item in item.items:
298314
yaml_compressor_stage = self._resolve_compressor_stage_reference(yaml_serial_item.target)
299315
compressor = self._get_compressor(yaml_compressor_stage=yaml_compressor_stage)
300316
compressor_ids.append(compressor.get_id())
317+
301318
temperature_setter = TemperatureSetter(
302-
required_temperature_kelvin=0,
303319
fluid_service=self._fluid_service,
304320
)
305-
choke = Choke(fluid_service=self._fluid_service, pressure_change=0)
321+
temperature_setter_configuration_handler = TemperatureSetterConfigurationHandler(
322+
temperature_setter=temperature_setter
323+
)
324+
problem_time_series_configurations[temperature_setter_configuration_handler.get_id()] = (
325+
TimeSeriesTemperatureSetterConfiguration(
326+
temperature=self._map_temperature(yaml_compressor_stage.inlet_temperature)
327+
)
328+
)
329+
problem_configuration_handlers.append(temperature_setter_configuration_handler)
330+
331+
choke, choke_configuration_handler = choke_factory(fluid_service=self._fluid_service)
332+
problem_time_series_configurations[choke_configuration_handler.get_id()] = TimeSeriesChokeConfiguration(
333+
delta_pressure=self._map_pressure(yaml_compressor_stage.pressure_drop_ahead_of_stage)
334+
)
335+
problem_configuration_handlers.append(choke_configuration_handler)
336+
306337
liquid_remover = LiquidRemover(fluid_service=self._fluid_service)
307338
process_unit_map[temperature_setter.get_id()] = temperature_setter
308339
process_unit_map[choke.get_id()] = choke
@@ -323,32 +354,34 @@ def map_process_simulation(
323354
except KeyError as e:
324355
raise EcalcValidationException(f"Missing pressure control for process system '{item.name}'") from e
325356

326-
configuration_handlers = []
327-
328357
if pressure_control == "COMMON_ASV":
329358
recirculation_loop, process_units = with_asv(units=list(process_unit_map.values()))
330-
configuration_handlers.append(recirculation_loop)
359+
problem_configuration_handlers.append(recirculation_loop)
331360
else:
332361
process_units = []
333-
configuration_handlers = []
362+
problem_configuration_handlers = []
334363
for compressor_stage_ids in compressor_stages:
335364
stage_units = [process_unit_map[stage_unit_id] for stage_unit_id in compressor_stage_ids]
336365
recirculation_loop, stage_process_units = with_asv(units=stage_units)
337-
configuration_handlers.append(recirculation_loop)
366+
problem_configuration_handlers.append(recirculation_loop)
338367
process_units.extend(stage_process_units)
339368

340369
if pressure_control == "DOWNSTREAM_CHOKE":
341370
choke, choke_configuration_handler = choke_factory(fluid_service=self._fluid_service)
342-
configuration_handlers.append(choke_configuration_handler)
371+
problem_configuration_handlers.append(choke_configuration_handler)
343372
process_units.append(choke)
344373
elif pressure_control == "UPSTREAM_CHOKE":
345374
choke, choke_configuration_handler = choke_factory(fluid_service=self._fluid_service)
346-
configuration_handlers.append(choke_configuration_handler)
375+
problem_configuration_handlers.append(choke_configuration_handler)
347376
process_units = [choke, *process_units]
377+
348378
process_pipeline = ProcessPipeline(
349379
stream_propagators=process_units,
350380
)
351381

382+
configuration_handlers[process_pipeline.get_id()] = problem_configuration_handlers
383+
configurations[process_pipeline.get_id()] = problem_time_series_configurations
384+
352385
pressure_control_configs[process_pipeline.get_id()] = PressureControlConfig(
353386
type=pressure_control,
354387
)
@@ -452,6 +485,8 @@ def map_process_simulation(
452485
constraint=constraints[process_pipeline.get_id()],
453486
anti_surge_strategy=anti_surge_configs[process_pipeline.get_id()],
454487
pressure_control_strategy=pressure_control_configs[process_pipeline.get_id()],
488+
configuration_handlers=configuration_handlers[process_pipeline.get_id()],
489+
configurations=configurations[process_pipeline.get_id()],
455490
)
456491
for process_pipeline in process_pipelines
457492
]

src/libecalc/process/process_solver/configuration.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ class ChokeConfiguration:
3939
delta_pressure: float
4040

4141

42+
@dataclass
43+
class TemperatureSetterConfiguration:
44+
temperature: float
45+
46+
4247
@dataclass
4348
class RecirculationConfiguration:
4449
recirculation_rate: float
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from typing import Final
2+
3+
from libecalc.process.process_solver.configuration import (
4+
Configuration,
5+
ConfigurationHandlerId,
6+
TemperatureSetterConfiguration,
7+
)
8+
from libecalc.process.process_solver.configuration_handler import ConfigurationHandler
9+
from libecalc.process.process_units.temperature_setter import TemperatureSetter
10+
11+
12+
class TemperatureSetterConfigurationHandler(ConfigurationHandler):
13+
def __init__(
14+
self, temperature_setter: TemperatureSetter, configuration_handler_id: ConfigurationHandlerId | None = None
15+
):
16+
self._temperature_setter = temperature_setter
17+
self._id: Final[ConfigurationHandlerId] = configuration_handler_id or ConfigurationHandler._create_id()
18+
19+
def get_id(self) -> ConfigurationHandlerId:
20+
return self._id
21+
22+
def handle_configuration(self, configuration: Configuration) -> None:
23+
assert configuration.configuration_handler_id == self._id, (
24+
f"Configuration id '{configuration.configuration_handler_id}' does not match configuration handler id '{self._id}'"
25+
)
26+
assert isinstance(configuration.value, TemperatureSetterConfiguration), (
27+
f"Expected configuration value to be of type '{TemperatureSetterConfiguration.__name__}', got '{type(configuration.value)}'"
28+
)
29+
self._temperature_setter.set_temperature(configuration.value.temperature)
30+
31+
def reset(self) -> None:
32+
raise NotImplementedError("Calling reset on this should not happen, as it's shouldn't be manipulated by solver")

src/libecalc/process/process_units/temperature_setter.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
class TemperatureSetter(ProcessUnit):
99
def __init__(
1010
self,
11-
required_temperature_kelvin: float,
1211
fluid_service: FluidService,
12+
required_temperature_kelvin: float = 0,
1313
process_unit_id: ProcessUnitId | None = None,
1414
):
1515
self._id: Final[ProcessUnitId] = process_unit_id or ProcessUnit._create_id()
@@ -19,6 +19,9 @@ def __init__(
1919
def get_id(self) -> ProcessUnitId:
2020
return self._id
2121

22+
def set_temperature(self, temperature_kelvin: float) -> None:
23+
self._required_temperature_kelvin = temperature_kelvin
24+
2225
@property
2326
def required_temperature_kelvin(self) -> float:
2427
return self._required_temperature_kelvin

0 commit comments

Comments
 (0)