Skip to content

Commit 2b429a2

Browse files
authored
refactor: merge multiple streams and pressures train into common shaft train (#1295)
1 parent a7aeabd commit 2b429a2

11 files changed

Lines changed: 551 additions & 964 deletions

File tree

src/libecalc/domain/process/compressor/core/train/base.py

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
)
1717
from libecalc.domain.process.compressor.core.train.stage import CompressorTrainStage
1818
from libecalc.domain.process.compressor.core.train.train_evaluation_input import CompressorTrainEvaluationInput
19+
from libecalc.domain.process.compressor.core.train.types import StreamPort
1920
from libecalc.domain.process.compressor.core.train.utils.common import EPSILON, PRESSURE_CALCULATION_TOLERANCE
2021
from libecalc.domain.process.core.results import CompressorTrainResult
2122
from libecalc.domain.process.core.results.compressor import TargetPressureStatus
@@ -57,6 +58,14 @@ def __init__(
5758
self.calculate_max_rate = calculate_max_rate
5859
self.stage_number_interstage_pressure = stage_number_interstage_pressure
5960

61+
self.ports: list[StreamPort] = [
62+
StreamPort(is_inlet_port=True, connected_to_stage_no=0)
63+
] # inlet port for first stage
64+
self.inlet_port_connected_to_stage: dict[int, list[int]] = {key: [] for key in range(len(self.stages))}
65+
self.outlet_port_connected_to_stage: dict[int, list[int]] = {key: [] for key in range(len(self.stages))}
66+
67+
self._make_ports_from_splitters_and_mixers()
68+
6069
@property
6170
def number_of_compressor_stages(self) -> int:
6271
return len(self.stages)
@@ -101,6 +110,24 @@ def get_consumption_type(self) -> ConsumptionType:
101110
# that consumes FUEL, the turbine is modeled separately.
102111
return ConsumptionType.ELECTRICITY
103112

113+
def _make_ports_from_splitters_and_mixers(self):
114+
"""Create stream ports based on the splitters and mixers defined in each stage."""
115+
for i, stage in enumerate(self.stages):
116+
# Outlet ports from splitters
117+
if stage.splitter:
118+
for _ in range(stage.splitter.number_of_outputs - 1):
119+
self.ports.append(StreamPort(is_inlet_port=False, connected_to_stage_no=i))
120+
# Inlet ports from mixers
121+
if stage.mixer:
122+
for _ in range(stage.mixer.number_of_inputs - 1):
123+
self.ports.append(StreamPort(is_inlet_port=True, connected_to_stage_no=i))
124+
125+
for i, port in enumerate(self.ports):
126+
if port.is_inlet_port:
127+
self.inlet_port_connected_to_stage[port.connected_to_stage_no].append(i)
128+
else:
129+
self.outlet_port_connected_to_stage[port.connected_to_stage_no].append(i)
130+
104131
def set_evaluation_input(
105132
self,
106133
rate: NDArray[np.float64],
@@ -113,12 +140,16 @@ def set_evaluation_input(
113140
raise DomainValidationException("Suction pressure is required for model")
114141
if discharge_pressure is None:
115142
raise DomainValidationException("Discharge pressure is required for model")
116-
143+
has_interstage_pressure = any(stage.interstage_pressure_control is not None for stage in self.stages)
144+
if has_interstage_pressure and intermediate_pressure is None:
145+
raise DomainValidationException("Interstage control pressure is required for model")
146+
if not has_interstage_pressure and intermediate_pressure is not None:
147+
raise DomainValidationException("Model is not set up to receive interstage pressure")
117148
self._rate = rate
118149
self._suction_pressure = suction_pressure
119150
self._discharge_pressure = discharge_pressure
120151
self._intermediate_pressure = intermediate_pressure
121-
self._fluid_model = fluid_model
152+
self._fluid_model = fluid_model if isinstance(fluid_model, list) else [fluid_model]
122153

123154
def reset_rate_modifiers(self):
124155
for stage in self.stages:
@@ -168,17 +199,14 @@ def evaluate(
168199
self.reset_rate_modifiers()
169200
if isinstance(rate_value, np.ndarray):
170201
rate_value = list(rate_value)
171-
constraints_rate = rate_value[0]
172-
constraints_stream_rates = rate_value
202+
constraints_rates = rate_value
173203
else:
174-
constraints_rate = rate_value
175-
constraints_stream_rates = None
204+
constraints_rates = [rate_value]
176205
evaluation_constraints = CompressorTrainEvaluationInput(
177-
rate=constraints_rate,
178206
suction_pressure=suction_pressure_value,
179207
discharge_pressure=discharge_pressure_value,
180208
interstage_pressure=intermediate_pressure_value,
181-
stream_rates=constraints_stream_rates,
209+
rates=constraints_rates,
182210
)
183211
train_results.append(self.evaluate_given_constraints(constraints=evaluation_constraints))
184212

@@ -250,8 +278,9 @@ def train_inlet_stream(
250278
"""Find inlet stream given constraints.
251279
252280
Args:
253-
constraints (CompressorTrainEvaluationInput): The constraints for the evaluation.
254-
281+
pressure: Inlet pressure [bara].
282+
temperature: Inlet temperature [K].
283+
rate: Standard volume rate [Sm3/day].
255284
Returns:
256285
FluidStream: Inlet fluid stream at the compressor train inlet.
257286
"""
@@ -296,7 +325,7 @@ def check_target_pressures(
296325
train_suction_pressure = results.suction_pressure
297326
calculated_discharge_pressure = results.discharge_pressure
298327
stage_suction_pressure = results.stage_results[0].inlet_stream.pressure_bara
299-
if constraints.stream_rates is not None:
328+
if constraints.rates is not None:
300329
calculated_intermediate_pressure = (
301330
results.stage_results[self.stage_number_interstage_pressure - 1].discharge_pressure
302331
if self.stage_number_interstage_pressure is not None
@@ -347,7 +376,13 @@ def get_max_standard_rate(
347376
If the maximum rate cannot be determined, it returns INVALID_MAX_RATE for that time step.
348377
"""
349378
if fluid_model is not None:
350-
self._fluid_model = fluid_model
379+
self._fluid_model = [fluid_model]
380+
381+
# Check for multiple ports
382+
if len(self.ports) > 1:
383+
raise DomainValidationException(
384+
"Calculation of max standard rate is not well defined for compressor trains with more than one port."
385+
)
351386

352387
max_standard_rate = np.full_like(suction_pressures, fill_value=INVALID_MAX_RATE, dtype=float)
353388
for i, (suction_pressure_value, discharge_pressure_value) in enumerate(
@@ -359,7 +394,7 @@ def get_max_standard_rate(
359394
constraints = CompressorTrainEvaluationInput(
360395
suction_pressure=suction_pressure_value,
361396
discharge_pressure=discharge_pressure_value,
362-
rate=EPSILON,
397+
rates=[EPSILON],
363398
)
364399
try:
365400
max_standard_rate[i] = self._get_max_std_rate_single_timestep(constraints=constraints)

0 commit comments

Comments
 (0)