Skip to content

Commit 742c773

Browse files
authored
chore: mapping from yaml to temporary domain/DTO representation (#1490)
Mainly parsing and mapping from YAML to a semi-domain representation for further processing.
1 parent c7922e5 commit 742c773

File tree

10 files changed

+209
-80
lines changed

10 files changed

+209
-80
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,5 @@ docs/.docusaurus/
148148
src/sandbox/
149149

150150
.ruff_cache
151+
152+
*.iml

libecalc.iml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<module type="PYTHON_MODULE" version="4">
3+
<component name="NewModuleRootManager" inherit-compiler-output="true">
4+
<exclude-output />
5+
<content url="file://$MODULE_DIR$">
6+
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
7+
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
8+
<excludeFolder url="file://$MODULE_DIR$/.venv" />
9+
<excludeFolder url="file://$MODULE_DIR$/docs" />
10+
</content>
11+
<orderEntry type="jdk" jdkName="Python 3.12 (libecalc)" jdkType="Python SDK" />
12+
<orderEntry type="sourceFolder" forTests="false" />
13+
</component>
14+
<component name="PyDocumentationSettings">
15+
<option name="format" value="GOOGLE" />
16+
<option name="myDocStringFormat" value="Google" />
17+
</component>
18+
<component name="TestRunnerService">
19+
<option name="PROJECT_TEST_RUNNER" value="py.test" />
20+
</component>
21+
</module>
Lines changed: 91 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
from dataclasses import dataclass
2-
from typing import Literal
1+
import uuid
2+
from collections.abc import Sequence
3+
from dataclasses import dataclass, field
4+
from typing import Literal, NewType
5+
from uuid import UUID
36

4-
from libecalc.domain.process.process_system.process_system import ProcessSystem, ProcessSystemId
7+
from libecalc.domain.process.process_system.process_system import ProcessSystem
8+
from libecalc.domain.process.process_system.process_unit import ProcessUnit
9+
from libecalc.domain.process.process_system.stream_propagator import StreamPropagator
510
from libecalc.domain.process.stream_distribution.common_stream_distribution import Overflow
611
from libecalc.domain.process.value_objects.fluid_stream.time_series_stream import TimeSeriesStream
712
from libecalc.presentation.yaml.domain.time_series_expression import TimeSeriesExpression
@@ -14,7 +19,7 @@ class CommonStreamSettings:
1419

1520

1621
@dataclass
17-
class CommonStreamDistributionConfig:
22+
class CommonStreamDistributionConfig: # TODO: Rather a strategy that splits a stream in a method according to a policy?
1823
inlet_stream: TimeSeriesStream
1924
settings: list[CommonStreamSettings]
2025

@@ -25,27 +30,98 @@ class IndividualStreamDistributionConfig:
2530

2631

2732
@dataclass
28-
class Constraint:
29-
process_system_id: ProcessSystemId
33+
class Constraint: # should this instead be more flexible wrt. matching one or more stream conditions?
3034
outlet_pressure: TimeSeriesExpression
3135

3236

3337
@dataclass
34-
class PressureControlConfig:
35-
process_system_id: ProcessSystemId
38+
class PressureControlConfig: # Spec
3639
type: Literal["UPSTREAM_CHOKE", "DOWNSTREAM_CHOKE", "COMMON_ASV", "INDIVIDUAL_ASV_RATE", "INDIVIDUAL_ASV_PRESSURE"]
3740

3841

3942
@dataclass
4043
class AntiSurgeConfig:
41-
process_system_id: ProcessSystemId
4244
type: Literal["INDIVIDUAL_ASV", "COMMON_ASV"]
4345

4446

47+
ProcessProblemId = NewType("ProcessProblemId", UUID)
48+
49+
50+
def create_process_problem_id() -> ProcessProblemId:
51+
return ProcessProblemId(uuid.uuid4())
52+
53+
54+
ProcessPipelineId = NewType("ProcessPipelineId", UUID)
55+
56+
57+
def create_process_pipeline_id() -> ProcessPipelineId:
58+
return ProcessPipelineId(uuid.uuid4())
59+
60+
4561
@dataclass
46-
class ProcessSimulation:
47-
process_systems: list[ProcessSystem]
48-
pressure_control_strategies: list[PressureControlConfig]
49-
anti_surge_strategies: list[AntiSurgeConfig]
50-
constraints: list[Constraint]
51-
stream_distribution: IndividualStreamDistributionConfig | CommonStreamDistributionConfig
62+
class ProcessPipeline: # or simulator?
63+
"""
64+
A part of a process topology that is calculated independently
65+
container propagators - ie systems or units ...
66+
67+
the static physical stuff that we know a priori
68+
TODO: subpipelines?
69+
"""
70+
71+
id: ProcessPipelineId
72+
stream_propagators: Sequence[StreamPropagator]
73+
74+
def get_process_units(self) -> list[ProcessUnit]:
75+
process_units = []
76+
for stream_propagator in self.stream_propagators:
77+
match stream_propagator:
78+
case ProcessSystem():
79+
process_units.extend(stream_propagator.get_process_units())
80+
case ProcessUnit():
81+
process_units.append(stream_propagator)
82+
83+
return process_units
84+
85+
86+
@dataclass
87+
class ProcessProblem: # TODO: Rename to subproblem?
88+
# can a problem exist wo. a simulation? yes, e.g. get max rate ...
89+
# given a physical pipeline (a contained problem, such as a compressor train), the user needs to define strategies to find a solution for the sub problem
90+
# TODO: might have subproblems, or dependencies, but we may want to add those as problems that depend on each other and needs to be evaluated in a given order
91+
pressure_control_strategy: PressureControlConfig
92+
anti_surge_strategy: AntiSurgeConfig
93+
constraint: Constraint # ie target pressure - and intermediate pressures ...
94+
process_pipeline_id: UUID # embedded ref here now for convenience, but not a part of this aggr, so FK/ID later
95+
id: ProcessProblemId = field(default_factory=create_process_problem_id)
96+
97+
def get_id(self) -> ProcessProblemId:
98+
return self.id
99+
100+
def get_constraint(self) -> Constraint:
101+
return self.constraint
102+
103+
def get_pressure_control_strategy(self) -> PressureControlConfig:
104+
return self.pressure_control_strategy
105+
106+
def get_anti_surge_strategy(self) -> AntiSurgeConfig:
107+
return self.anti_surge_strategy
108+
109+
110+
@dataclass
111+
class ProcessSimulation: # process_model?
112+
"""
113+
TODO: one or more subproblems, where we first need to find the stream distribution before looking at each subproblem separately
114+
quit and notify as soon as we notice we are not able to find a solution, or always finish?
115+
"""
116+
117+
id: UUID
118+
# we wait with stream distr ...
119+
# might be input param instead ...
120+
# stream_distribution: (
121+
# IndividualStreamDistributionConfig | CommonStreamDistributionConfig
122+
# ) # the inlet stream is only indirectly a part of sim, through the strategy. It could be separate, where the strategy is just a policy how to distr it
123+
inlet_streams: list[TimeSeriesStream]
124+
process_problems: list[ProcessProblem] # todo: subproblem? TODO: a part of aggr or not?
125+
126+
# def get_stream_distribution_config(self) -> IndividualStreamDistributionConfig | CommonStreamDistributionConfig:
127+
# return self.stream_distribution

src/libecalc/domain/process/process_system/serial_process_system.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from libecalc.domain.process.process_system.process_system import ProcessSystem, ProcessSystemId
44
from libecalc.domain.process.process_system.process_unit import ProcessUnit
5+
from libecalc.domain.process.process_system.stream_propagator import StreamPropagator
56
from libecalc.domain.process.value_objects.fluid_stream import FluidStream
67

78

@@ -26,6 +27,9 @@ def get_process_units(self) -> Sequence[ProcessUnit | ProcessSystem]:
2627
process_units.append(propagator)
2728
return process_units
2829

30+
def get_propagators(self) -> Sequence[StreamPropagator]:
31+
return self._propagators
32+
2933
def propagate_stream(self, inlet_stream: FluidStream) -> FluidStream:
3034
current_inlet = inlet_stream
3135
for process_unit in self._propagators:
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from dataclasses import dataclass
22

33
from libecalc.domain.process.value_objects.fluid_stream import FluidModel
4-
from libecalc.domain.time_series_flow_rate import TimeSeriesFlowRate
4+
from libecalc.presentation.yaml.domain.expression_time_series_flow_rate import ExpressionTimeSeriesFlowRate
55
from libecalc.presentation.yaml.domain.time_series_expression import TimeSeriesExpression
66

77

@@ -10,4 +10,4 @@ class TimeSeriesStream:
1010
fluid_model: FluidModel
1111
pressure_bara: TimeSeriesExpression
1212
temperature_kelvin: TimeSeriesExpression
13-
standard_rate_m3_per_day: TimeSeriesFlowRate
13+
standard_rate_m3_per_day: ExpressionTimeSeriesFlowRate

src/libecalc/domain/shared/__init__.py

Whitespace-only changes.

src/libecalc/presentation/yaml/domain/expression_time_series_flow_rate.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ def _validate(self):
5050
if rate < 0:
5151
raise InvalidFlowRateException(rate, str(self._time_series_expression.get_expression()))
5252

53+
def get_original_expression(self) -> TimeSeriesExpression:
54+
return self._time_series_expression
55+
5356
def _get_stream_day_values(self) -> list[float]:
5457
"""
5558
Returns the stream day flow rate values.

src/libecalc/presentation/yaml/domain/time_series_expression.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ def __init__(
2626
2727
"""
2828

29+
self._original_expression = expression
2930
self._expression = convert_expression(expression)
3031
self.expression_evaluator = expression_evaluator
3132
self._condition = convert_expression(condition) if condition is not None else None
@@ -36,6 +37,9 @@ def get_expression(self) -> Expression:
3637
"""
3738
return self._expression
3839

40+
def get_original_expression(self) -> ExpressionType:
41+
return self._original_expression
42+
3943
def get_evaluated_expression(self) -> list[float]:
4044
"""
4145
Evaluates expression and returns the result as a NumPy array.

0 commit comments

Comments
 (0)