Skip to content

Commit b3e6662

Browse files
authored
refactor: merge single and variable speed charts into one single chart (#1134)
refactor: merge single and variable speed pumps into one pump model fix: remove old charts when validating json results
1 parent 3dbd515 commit b3e6662

43 files changed

Lines changed: 877 additions & 1123 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/libecalc/common/serializable_chart.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
from typing import Annotated, Literal, Self
1+
from typing import Annotated, Self
22

33
import numpy as np
44
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
55

6-
from libecalc.common.chart_type import ChartType
76
from libecalc.common.logger import logger
87
from libecalc.common.string.string_utils import to_camel_case
98

@@ -75,12 +74,7 @@ def speed(self) -> float:
7574
return self.speed_rpm
7675

7776

78-
class SingleSpeedChartDTO(ChartCurveDTO):
79-
typ: Literal[ChartType.SINGLE_SPEED] = ChartType.SINGLE_SPEED
80-
81-
82-
class VariableSpeedChartDTO(EcalcBaseModel):
83-
typ: Literal[ChartType.VARIABLE_SPEED] = ChartType.VARIABLE_SPEED
77+
class ChartDTO(EcalcBaseModel):
8478
curves: list[ChartCurveDTO]
8579
control_margin: float | None = None # Todo: Raise warning if this is used in an un-supported model.
8680
design_rate: float | None = Field(None, ge=0)

src/libecalc/core/result/base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from typing import Self
55

66
from libecalc.common.logger import logger
7-
from libecalc.common.serializable_chart import SingleSpeedChartDTO, VariableSpeedChartDTO
7+
from libecalc.common.serializable_chart import ChartDTO
88
from libecalc.common.time_utils import Periods
99
from libecalc.common.utils.rates import TimeSeries
1010

@@ -26,7 +26,7 @@ def extend(self, other: Self) -> Self:
2626
logger.warning(
2727
f"Concatenating two temporal compressor results where result attribute '{attribute}' is undefined."
2828
)
29-
elif isinstance(values, Enum | str | dict | SingleSpeedChartDTO | VariableSpeedChartDTO):
29+
elif isinstance(values, Enum | str | dict | ChartDTO):
3030
if values != other_values:
3131
logger.warning(
3232
f"Concatenating two temporal compressor model results where attribute {attribute} changes"

src/libecalc/domain/process/compressor/core/results.py

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

33
import numpy as np
44

5-
from libecalc.common.serializable_chart import SingleSpeedChartDTO, VariableSpeedChartDTO
5+
from libecalc.common.serializable_chart import ChartDTO
66
from libecalc.common.units import Unit
77
from libecalc.domain.process.core.results.compressor import (
88
CompressorStageResult,
@@ -203,7 +203,7 @@ def __init__(
203203
@staticmethod
204204
def from_result_list_to_dto(
205205
result_list: list[CompressorTrainResultSingleTimeStep],
206-
compressor_charts: list[SingleSpeedChartDTO | VariableSpeedChartDTO] | None,
206+
compressor_charts: list[ChartDTO] | None,
207207
) -> tuple[CompressorStreamCondition, CompressorStreamCondition, list[CompressorStageResult]]:
208208
number_of_stages = max([len(t.stage_results) for t in result_list])
209209

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ def evaluate(
192192
stage_results,
193193
) = CompressorTrainResultSingleTimeStep.from_result_list_to_dto(
194194
result_list=train_results,
195-
compressor_charts=[stage.compressor_chart.data_transfer_object for stage in self.stages], # type: ignore[misc]
195+
compressor_charts=[stage.compressor_chart.data_transfer_object for stage in self.stages],
196196
)
197197

198198
return CompressorTrainResult(

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
calculate_polytropic_head_campbell,
2222
)
2323
from libecalc.domain.process.value_objects.chart.chart_area_flag import ChartAreaFlag
24-
from libecalc.domain.process.value_objects.chart.compressor import VariableSpeedCompressorChart
24+
from libecalc.domain.process.value_objects.chart.compressor import CompressorChart
2525
from libecalc.domain.process.value_objects.chart.compressor.chart_creator import CompressorChartCreator
2626
from libecalc.domain.process.value_objects.fluid_stream import FluidStream, ProcessConditions
2727
from libecalc.domain.process.value_objects.fluid_stream.fluid_factory import FluidFactoryInterface
@@ -398,7 +398,7 @@ def _get_max_std_rate_single_timestep(
398398
temperature_kelvin=inlet_temperatures_kelvin_to_use[stage_number],
399399
mass_rate_kg_per_h=1,
400400
),
401-
compressor_chart=chart, # type: ignore[arg-type]
401+
compressor_chart=chart,
402402
pressure_ratio=pressure_ratios_per_stage, # type: ignore[arg-type]
403403
)
404404
for stage_number, chart in enumerate(compressor_charts)
@@ -427,7 +427,7 @@ def _calculate_inlet_pressure_stages(
427427
def calculate_maximum_rate_for_stage(
428428
pressure_ratio: float,
429429
inlet_stream: FluidStream,
430-
compressor_chart: VariableSpeedCompressorChart,
430+
compressor_chart: CompressorChart,
431431
) -> float:
432432
"""Calculate the maximum standard rate for a single set of inputs."""
433433
outlet_pressure = inlet_stream.pressure_bara * pressure_ratio

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
find_root,
1616
maximize_x_given_boolean_condition_function,
1717
)
18-
from libecalc.domain.process.value_objects.chart.compressor import SingleSpeedCompressorChart
1918
from libecalc.domain.process.value_objects.fluid_stream.fluid_factory import FluidFactoryInterface
2019

2120

@@ -158,11 +157,13 @@ def calculate_compressor_train(
158157
outlet_stream = train_inlet_stream
159158

160159
for stage in self.stages:
160+
speed = stage.compressor_chart.curves[0].speed
161161
inlet_stream = outlet_stream
162162
stage_result = stage.evaluate(
163163
inlet_stream_stage=inlet_stream,
164164
asv_rate_fraction=asv_rate_fraction,
165165
asv_additional_mass_rate=asv_additional_mass_rate,
166+
speed=speed,
166167
)
167168
stage_results.append(stage_result)
168169

@@ -423,8 +424,14 @@ def _validate_maximum_discharge_pressure(self):
423424

424425
def _validate_stages(self, stages):
425426
for stage in stages:
426-
if not isinstance(stage.compressor_chart, SingleSpeedCompressorChart):
427-
msg = "Single Speed Compressor train only accepts Single Speed Compressor Charts."
428-
f" Given type was {type(stage.compressor_chart)}"
427+
if len(stage.compressor_chart.curves) != 1:
428+
msg = "Single Speed Compressor train only accepts one speed curve for each compressor chart."
429+
f" The number of curves given is {len(stage.compressor_chart.curves)}."
429430

430431
raise ProcessChartTypeValidationException(message=str(msg))
432+
if not all(
433+
stage.compressor_chart.curves[0].speed == stages[0].compressor_chart.curves[0].speed for stage in stages
434+
):
435+
msg = "All compressor charts in a Single Speed Compressor Train must have the same speed."
436+
437+
raise ProcessChartTypeValidationException(message=str(msg))

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

Lines changed: 24 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@
1414
from libecalc.domain.process.compressor.core.train.utils.numeric_methods import find_root
1515
from libecalc.domain.process.compressor.dto import InterstagePressureControl
1616
from libecalc.domain.process.value_objects.chart.compressor import (
17-
SingleSpeedCompressorChart,
18-
VariableSpeedCompressorChart,
17+
CompressorChart,
1918
)
2019
from libecalc.domain.process.value_objects.fluid_stream import FluidStream, ProcessConditions
2120

@@ -28,7 +27,7 @@ class CompressorTrainStage:
2827

2928
def __init__(
3029
self,
31-
compressor_chart: SingleSpeedCompressorChart | VariableSpeedCompressorChart,
30+
compressor_chart: CompressorChart,
3231
inlet_temperature_kelvin: float,
3332
remove_liquid_after_cooling: bool,
3433
pressure_drop_ahead_of_stage: float | None = None,
@@ -47,7 +46,7 @@ def has_control_pressure(self):
4746
def evaluate(
4847
self,
4948
inlet_stream_stage: FluidStream,
50-
speed: float | None = None,
49+
speed: float,
5150
asv_rate_fraction: float | None = 0.0,
5251
asv_additional_mass_rate: float | None = 0.0,
5352
increase_rate_left_of_minimum_flow_assuming_asv: bool | None = True,
@@ -67,22 +66,13 @@ def evaluate(
6766
6867
Returns: Results of the evaluation
6968
"""
70-
if isinstance(self.compressor_chart, VariableSpeedCompressorChart):
71-
if speed is None:
72-
msg = (
73-
f"Speed value ({speed}) is not allowed for a variable speed compressor chart."
74-
f"You should not end up here, please contact support."
75-
)
76-
logger.exception(msg)
77-
raise IllegalStateException(msg)
78-
79-
if speed < self.compressor_chart.minimum_speed or speed > self.compressor_chart.maximum_speed:
80-
msg = (
81-
f"Speed value ({speed}) outside allowed range ({self.compressor_chart.minimum_speed} -"
82-
f" {self.compressor_chart.maximum_speed}). You should not end up here, please contact support."
83-
)
84-
logger.exception(msg)
85-
raise IllegalStateException(msg)
69+
if speed < self.compressor_chart.minimum_speed or speed > self.compressor_chart.maximum_speed:
70+
msg = (
71+
f"Speed value ({speed}) outside allowed range ({self.compressor_chart.minimum_speed} -"
72+
f" {self.compressor_chart.maximum_speed}). You should not end up here, please contact support."
73+
)
74+
logger.exception(msg)
75+
raise IllegalStateException(msg)
8676

8777
if asv_rate_fraction is not None and asv_additional_mass_rate is not None:
8878
if asv_rate_fraction > 0 and asv_additional_mass_rate > 0:
@@ -107,11 +97,7 @@ def evaluate(
10797
)
10898

10999
actual_rate_m3_per_hour_to_use = actual_rate_m3_per_hour = inlet_stream_compressor.volumetric_rate
110-
compressor_maximum_actual_rate_m3_per_hour = float(
111-
self.compressor_chart.maximum_rate_as_function_of_speed(speed) # type: ignore[arg-type]
112-
if isinstance(self.compressor_chart, VariableSpeedCompressorChart)
113-
else self.compressor_chart.maximum_rate
114-
)
100+
compressor_maximum_actual_rate_m3_per_hour = self.compressor_chart.maximum_rate_as_function_of_speed(speed)
115101
available_capacity_for_actual_rate_m3_per_hour = max(
116102
0, compressor_maximum_actual_rate_m3_per_hour - actual_rate_m3_per_hour
117103
) # if the actual_rate_m3_per_hour is above capacity, the available capacity should be zero, not negative
@@ -124,24 +110,15 @@ def evaluate(
124110
if asv_additional_mass_rate:
125111
additional_rate_m3_per_hour = asv_additional_mass_rate / inlet_stream_compressor.density
126112

127-
if isinstance(self.compressor_chart, VariableSpeedCompressorChart):
128-
compressor_chart_head_and_efficiency_result = (
129-
self.compressor_chart.calculate_polytropic_head_and_efficiency_single_point(
130-
speed=speed, # type: ignore[arg-type]
131-
actual_rate_m3_per_hour=actual_rate_m3_per_hour,
132-
recirculated_rate_m3_per_hour=additional_rate_m3_per_hour,
133-
increase_rate_left_of_minimum_flow_assuming_asv=increase_rate_left_of_minimum_flow_assuming_asv, # type: ignore[arg-type]
134-
increase_speed_below_assuming_choke=increase_speed_below_assuming_choke, # type: ignore[arg-type]
135-
)
136-
)
137-
else:
138-
compressor_chart_head_and_efficiency_result = (
139-
self.compressor_chart.calculate_polytropic_head_and_efficiency_single_point(
140-
actual_rate_m3_per_hour=actual_rate_m3_per_hour,
141-
recirculated_rate_m3_per_hour=additional_rate_m3_per_hour,
142-
increase_rate_left_of_minimum_flow_assuming_asv=increase_rate_left_of_minimum_flow_assuming_asv, # type: ignore[arg-type]
143-
)
113+
compressor_chart_head_and_efficiency_result = (
114+
self.compressor_chart.calculate_polytropic_head_and_efficiency_single_point(
115+
speed=speed,
116+
actual_rate_m3_per_hour=actual_rate_m3_per_hour,
117+
recirculated_rate_m3_per_hour=additional_rate_m3_per_hour,
118+
increase_rate_left_of_minimum_flow_assuming_asv=increase_rate_left_of_minimum_flow_assuming_asv, # type: ignore[arg-type]
119+
increase_speed_below_assuming_choke=increase_speed_below_assuming_choke, # type: ignore[arg-type]
144120
)
121+
)
145122

146123
actual_rate_m3_per_hour_to_use += additional_rate_m3_per_hour
147124

@@ -160,9 +137,7 @@ def evaluate(
160137
_,
161138
mass_rate_asv_corrected_kg_per_hour,
162139
) = calculate_asv_corrected_rate(
163-
minimum_actual_rate_m3_per_hour=float(self.compressor_chart.minimum_rate_as_function_of_speed(speed)) # type: ignore[arg-type]
164-
if isinstance(self.compressor_chart, VariableSpeedCompressorChart)
165-
else float(self.compressor_chart.minimum_rate),
140+
minimum_actual_rate_m3_per_hour=float(self.compressor_chart.minimum_rate_as_function_of_speed(speed)),
166141
actual_rate_m3_per_hour=actual_rate_m3_per_hour_to_use,
167142
density_kg_per_m3=inlet_stream_compressor.density,
168143
)
@@ -230,8 +205,8 @@ def evaluate_given_speed_and_target_discharge_pressure(
230205
CompressorTrainStageResultSingleTimeStep: The result of the evaluation for the compressor stage,
231206
including the outlet stream and operational details.
232207
"""
233-
# If no speed is defined for VariableSpeedCompressorChart, use the minimum speed
234-
if isinstance(self.compressor_chart, VariableSpeedCompressorChart) and speed is None:
208+
# If no speed is defined for CompressorChart, use the minimum speed
209+
if isinstance(self.compressor_chart, CompressorChart) and speed is None:
235210
speed = self.compressor_chart.minimum_speed
236211

237212
result_no_recirculation = self.evaluate(
@@ -242,11 +217,7 @@ def evaluate_given_speed_and_target_discharge_pressure(
242217

243218
# result_no_recirculation.inlet_stream.density_kg_per_m3 will have correct pressure and temperature
244219
# to find max mass rate, inlet_stream_stage will not
245-
maximum_rate = (
246-
self.compressor_chart.maximum_rate
247-
if isinstance(self.compressor_chart, SingleSpeedCompressorChart)
248-
else self.compressor_chart.maximum_rate_as_function_of_speed(speed) # type: ignore[arg-type]
249-
)
220+
maximum_rate = self.compressor_chart.maximum_rate_as_function_of_speed(speed)
250221

251222
max_recirculation = max(
252223
maximum_rate * float(result_no_recirculation.inlet_stream.density)
@@ -292,7 +263,7 @@ class UndefinedCompressorStage(CompressorTrainStage):
292263
def __init__(
293264
self,
294265
polytropic_efficiency: float,
295-
compressor_chart: VariableSpeedCompressorChart = None, # Not in use. Not relevant when undefined.
266+
compressor_chart: CompressorChart = None, # Not in use. Not relevant when undefined.
296267
inlet_temperature_kelvin: float = 0.0,
297268
remove_liquid_after_cooling: bool = False,
298269
pressure_drop_ahead_of_stage: float | None = None,

src/libecalc/domain/process/compressor/core/train/utils/variable_speed_compressor_train_common_shaft.py

Lines changed: 0 additions & 44 deletions
This file was deleted.

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
maximize_x_given_boolean_condition_function,
2323
)
2424
from libecalc.domain.process.core.results.compressor import TargetPressureStatus
25-
from libecalc.domain.process.value_objects.chart.compressor import VariableSpeedCompressorChart
25+
from libecalc.domain.process.value_objects.chart.compressor import CompressorChart
2626
from libecalc.domain.process.value_objects.fluid_stream.fluid_factory import FluidFactoryInterface
2727

2828

@@ -176,7 +176,7 @@ def _validate_stages(self, stages):
176176
min_speed_per_stage = []
177177
max_speed_per_stage = []
178178
for stage in stages:
179-
if not isinstance(stage.compressor_chart, VariableSpeedCompressorChart):
179+
if not isinstance(stage.compressor_chart, CompressorChart):
180180
msg = "Variable Speed Compressor train only accepts Variable Speed Compressor Charts."
181181
f" Given type was {type(stage.compressor_chart)}"
182182

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from libecalc.common.errors.exceptions import IllegalStateException
99
from libecalc.common.fixed_speed_pressure_control import FixedSpeedPressureControl
1010
from libecalc.common.logger import logger
11-
from libecalc.common.serializable_chart import VariableSpeedChartDTO
11+
from libecalc.common.serializable_chart import ChartDTO
1212
from libecalc.domain.component_validation_error import ProcessChartTypeValidationException
1313
from libecalc.domain.process.compressor.core.results import CompressorTrainResultSingleTimeStep
1414
from libecalc.domain.process.compressor.core.train.base import CompressorTrainModel
@@ -20,7 +20,7 @@
2020
maximize_x_given_boolean_condition_function,
2121
)
2222
from libecalc.domain.process.core.results.compressor import TargetPressureStatus
23-
from libecalc.domain.process.value_objects.chart.compressor import VariableSpeedCompressorChart
23+
from libecalc.domain.process.value_objects.chart.compressor import CompressorChart
2424
from libecalc.domain.process.value_objects.fluid_stream import ProcessConditions, SimplifiedStreamMixing
2525
from libecalc.domain.process.value_objects.fluid_stream.fluid_factory import FluidFactoryInterface
2626
from libecalc.domain.process.value_objects.fluid_stream.fluid_model import FluidModel
@@ -175,12 +175,12 @@ def _validate_stages(self, stages):
175175
min_speed_per_stage = []
176176
max_speed_per_stage = []
177177
for stage in stages:
178-
if not isinstance(stage.compressor_chart, VariableSpeedCompressorChart | VariableSpeedChartDTO):
178+
if not isinstance(stage.compressor_chart, CompressorChart | ChartDTO):
179179
msg = "Variable Speed Compressor train only accepts Variable Speed Compressor Charts."
180180
f" Given type was {type(stage.compressor_chart)}"
181181

182182
raise ProcessChartTypeValidationException(message=str(msg))
183-
if isinstance(stage.compressor_chart, VariableSpeedCompressorChart):
183+
if isinstance(stage.compressor_chart, CompressorChart):
184184
max_speed_per_stage.append(stage.compressor_chart.maximum_speed)
185185
min_speed_per_stage.append(stage.compressor_chart.minimum_speed)
186186
else:
@@ -472,6 +472,7 @@ def calculate_compressor_train(
472472
# This multiple streams train also requires stream_rates to be set
473473
assert constraints.stream_rates is not None
474474
assert constraints.suction_pressure is not None
475+
assert constraints.speed is not None
475476
mixing_strategy = SimplifiedStreamMixing()
476477
stage_results = []
477478
# Make list of fluid streams for the ingoing streams

0 commit comments

Comments
 (0)