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
510from libecalc .domain .process .stream_distribution .common_stream_distribution import Overflow
611from libecalc .domain .process .value_objects .fluid_stream .time_series_stream import TimeSeriesStream
712from 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
4043class 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
0 commit comments