Skip to content

Commit

Permalink
refactor: move target pressure status from stages to compressor train (
Browse files Browse the repository at this point in the history
…#589)

chore: updated snapshots to reflect changes made

ECALC-1582
  • Loading branch information
olelod authored Sep 4, 2024
1 parent d0548e7 commit 098cfa9
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 200 deletions.
68 changes: 28 additions & 40 deletions src/libecalc/core/models/compressor/results.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
CompressorStageResult,
CompressorStreamCondition,
CompressorTrainCommonShaftFailureStatus,
StageTargetPressureStatus,
TargetPressureStatus,
)
from libecalc.dto.types import ChartAreaFlag

Expand Down Expand Up @@ -51,7 +51,6 @@ class CompressorTrainStageResultSingleTimeStep(BaseModel):
power_megawatt: float

chart_area_flag: ChartAreaFlag
target_pressure_status: StageTargetPressureStatus

rate_has_recirculation: Optional[bool] = None
rate_exceeds_maximum: Optional[bool] = None
Expand Down Expand Up @@ -92,15 +91,11 @@ def create_empty(cls) -> CompressorTrainStageResultSingleTimeStep:
inlet_pressure_before_choking=np.nan,
outlet_pressure_before_choking=np.nan,
point_is_valid=True,
target_pressure_status=StageTargetPressureStatus.NOT_CALCULATED,
)

@property
def is_valid(self) -> bool:
return self.within_capacity and self.target_pressure_status in (
StageTargetPressureStatus.TARGET_PRESSURES_MET,
StageTargetPressureStatus.NOT_CALCULATED,
)
return self.within_capacity

@property
def within_capacity(self) -> bool:
Expand Down Expand Up @@ -141,6 +136,7 @@ class CompressorTrainResultSingleTimeStep(BaseModel):
speed: float
stage_results: List[CompressorTrainStageResultSingleTimeStep]
above_maximum_power: bool = False
target_pressure_status: TargetPressureStatus

@staticmethod
def from_result_list_to_dto(
Expand Down Expand Up @@ -380,39 +376,30 @@ def from_result_list_to_dto(
@property
def failure_status(self):
if not all(r.is_valid for r in self.stage_results):
for i, stage in enumerate(self.stage_results):
if not stage.is_valid:
if not stage.within_capacity:
if stage.chart_area_flag in (
ChartAreaFlag.ABOVE_MAXIMUM_FLOW_RATE,
ChartAreaFlag.BELOW_MINIMUM_SPEED_AND_ABOVE_MAXIMUM_FLOW_RATE,
):
return CompressorTrainCommonShaftFailureStatus.ABOVE_MAXIMUM_FLOW_RATE
elif stage.chart_area_flag in (
ChartAreaFlag.BELOW_MINIMUM_FLOW_RATE,
ChartAreaFlag.BELOW_MINIMUM_SPEED_AND_BELOW_MINIMUM_FLOW_RATE,
):
return CompressorTrainCommonShaftFailureStatus.BELOW_MINIMUM_FLOW_RATE
elif stage.target_pressure_status == StageTargetPressureStatus.ABOVE_TARGET_SUCTION_PRESSURE:
if i == 0: # first stage
return CompressorTrainCommonShaftFailureStatus.TARGET_DISCHARGE_PRESSURE_TOO_HIGH
else:
return CompressorTrainCommonShaftFailureStatus.TARGET_INTERMEDIATE_PRESSURE_TOO_LOW
elif stage.target_pressure_status == StageTargetPressureStatus.BELOW_TARGET_SUCTION_PRESSURE:
if i == 0: # first stage
return CompressorTrainCommonShaftFailureStatus.TARGET_SUCTION_PRESSURE_TOO_HIGH
else:
return CompressorTrainCommonShaftFailureStatus.TARGET_INTERMEDIATE_PRESSURE_TOO_HIGH
elif stage.target_pressure_status == StageTargetPressureStatus.ABOVE_TARGET_DISCHARGE_PRESSURE:
if i == len(self.stage_results) - 1: # last stage
return CompressorTrainCommonShaftFailureStatus.TARGET_DISCHARGE_PRESSURE_TOO_LOW
else:
return CompressorTrainCommonShaftFailureStatus.TARGET_INTERMEDIATE_PRESSURE_TOO_LOW
elif stage.target_pressure_status == StageTargetPressureStatus.BELOW_TARGET_DISCHARGE_PRESSURE:
if i == len(self.stage_results) - 1: # last stage
return CompressorTrainCommonShaftFailureStatus.TARGET_DISCHARGE_PRESSURE_TOO_HIGH
else:
return CompressorTrainCommonShaftFailureStatus.TARGET_INTERMEDIATE_PRESSURE_TOO_HIGH
for stage in self.stage_results:
if not stage.within_capacity:
if stage.chart_area_flag in (
ChartAreaFlag.ABOVE_MAXIMUM_FLOW_RATE,
ChartAreaFlag.BELOW_MINIMUM_SPEED_AND_ABOVE_MAXIMUM_FLOW_RATE,
):
return CompressorTrainCommonShaftFailureStatus.ABOVE_MAXIMUM_FLOW_RATE
elif stage.chart_area_flag in (
ChartAreaFlag.BELOW_MINIMUM_FLOW_RATE,
ChartAreaFlag.BELOW_MINIMUM_SPEED_AND_BELOW_MINIMUM_FLOW_RATE,
):
return CompressorTrainCommonShaftFailureStatus.BELOW_MINIMUM_FLOW_RATE
if self.target_pressure_status == TargetPressureStatus.ABOVE_TARGET_SUCTION_PRESSURE:
return CompressorTrainCommonShaftFailureStatus.TARGET_DISCHARGE_PRESSURE_TOO_HIGH
elif self.target_pressure_status == TargetPressureStatus.BELOW_TARGET_SUCTION_PRESSURE:
return CompressorTrainCommonShaftFailureStatus.TARGET_SUCTION_PRESSURE_TOO_HIGH
elif self.target_pressure_status == TargetPressureStatus.ABOVE_TARGET_DISCHARGE_PRESSURE:
return CompressorTrainCommonShaftFailureStatus.TARGET_DISCHARGE_PRESSURE_TOO_LOW
elif self.target_pressure_status == TargetPressureStatus.BELOW_TARGET_DISCHARGE_PRESSURE:
return CompressorTrainCommonShaftFailureStatus.TARGET_DISCHARGE_PRESSURE_TOO_HIGH
elif self.target_pressure_status == TargetPressureStatus.ABOVE_TARGET_INTERMEDIATE_PRESSURE:
return CompressorTrainCommonShaftFailureStatus.TARGET_INTERMEDIATE_PRESSURE_TOO_LOW
elif self.target_pressure_status == TargetPressureStatus.BELOW_TARGET_INTERMEDIATE_PRESSURE:
return CompressorTrainCommonShaftFailureStatus.TARGET_INTERMEDIATE_PRESSURE_TOO_HIGH
elif self.above_maximum_power:
return CompressorTrainCommonShaftFailureStatus.ABOVE_MAXIMUM_POWER

Expand Down Expand Up @@ -624,4 +611,5 @@ def create_empty(cls, number_of_stages: int) -> CompressorTrainResultSingleTimeS
return cls(
speed=np.nan,
stage_results=[CompressorTrainStageResultSingleTimeStep.create_empty()] * number_of_stages,
target_pressure_status=TargetPressureStatus.NOT_CALCULATED,
)
38 changes: 38 additions & 0 deletions src/libecalc/core/models/compressor/train/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
from libecalc.core.models.compressor.base import CompressorModel
from libecalc.core.models.compressor.results import CompressorTrainResultSingleTimeStep
from libecalc.core.models.compressor.train.fluid import FluidStream
from libecalc.core.models.compressor.train.utils.common import PRESSURE_CALCULATION_TOLERANCE
from libecalc.core.models.compressor.utils import map_compressor_train_stage_to_domain
from libecalc.core.models.results import CompressorTrainResult
from libecalc.core.models.results.compressor import TargetPressureStatus
from libecalc.domain.stream_conditions import StreamConditions
from libecalc.dto.models.compressor.train import CompressorTrain as CompressorTrainDTO
from libecalc.dto.models.compressor.train import (
Expand Down Expand Up @@ -228,3 +230,39 @@ def calculate_pressure_ratios_per_stage(
discharge_pressure, suction_pressure, out=np.ones_like(suction_pressure), where=suction_pressure != 0
)
return pressure_ratios ** (1.0 / len(self.stages))

def check_target_pressures(
self,
calculated_suction_pressure: float,
calculated_discharge_pressure: float,
calculated_intermediate_pressure: Optional[float] = None,
) -> TargetPressureStatus:
"""Check to see how the calculated pressures compare to the required pressures
Args:
calculated_suction_pressure: The calculated suction pressure
calculated_discharge_pressure: The calculated discharge pressure
calculated_intermediate_pressure: The calculated intermediate pressure
Returns:
TargetPressureStatus
"""
if self.target_suction_pressure:
if (calculated_suction_pressure / self.target_suction_pressure) - 1 > PRESSURE_CALCULATION_TOLERANCE:
return TargetPressureStatus.ABOVE_TARGET_SUCTION_PRESSURE
if (self.target_suction_pressure / calculated_suction_pressure) - 1 > PRESSURE_CALCULATION_TOLERANCE:
return TargetPressureStatus.BELOW_TARGET_SUCTION_PRESSURE
if self.target_discharge_pressure:
if (calculated_discharge_pressure / self.target_discharge_pressure) - 1 > PRESSURE_CALCULATION_TOLERANCE:
return TargetPressureStatus.ABOVE_TARGET_DISCHARGE_PRESSURE
if (self.target_discharge_pressure / calculated_discharge_pressure) - 1 > PRESSURE_CALCULATION_TOLERANCE:
return TargetPressureStatus.BELOW_TARGET_DISCHARGE_PRESSURE
if self.target_intermediate_pressure:
if (
calculated_intermediate_pressure / self.target_intermediate_pressure
) - 1 > PRESSURE_CALCULATION_TOLERANCE:
return TargetPressureStatus.ABOVE_TARGET_INTERMEDIATE_PRESSURE
if (
self.target_intermediate_pressure / calculated_intermediate_pressure
) - 1 > PRESSURE_CALCULATION_TOLERANCE:
return TargetPressureStatus.BELOW_TARGET_INTERMEDIATE_PRESSURE

return TargetPressureStatus.TARGET_PRESSURES_MET
31 changes: 23 additions & 8 deletions src/libecalc/core/models/compressor/train/simplified_train.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
)
from libecalc.core.models.compressor.utils import map_compressor_train_stage_to_domain
from libecalc.core.models.results.compressor import (
StageTargetPressureStatus,
TargetPressureStatus,
)
from libecalc.dto.types import ChartAreaFlag

Expand Down Expand Up @@ -127,6 +127,7 @@ def _evaluate_rate_ps_pd(

mass_rate_kg_per_hour = self.fluid.standard_rate_to_mass_rate(standard_rates=rate)
compressor_stages_result_per_time_step = []
compressor_result_per_time_step = []
inlet_pressure = suction_pressure.copy()
for stage in self.stages:
inlet_temperatures_kelvin = np.full_like(rate, fill_value=stage.inlet_temperature_kelvin, dtype=float)
Expand All @@ -142,13 +143,27 @@ def _evaluate_rate_ps_pd(
inlet_pressure = inlet_pressure * pressure_ratios_per_stage

# Converting from individual stage results to a train results and adding max rate per time step.
return [
CompressorTrainResultSingleTimeStep(
speed=np.nan,
stage_results=[result[time_step].stage_results[0] for result in compressor_stages_result_per_time_step],
for time_step in range(len(compressor_stages_result_per_time_step[0])):
self.target_suction_pressure = suction_pressure[time_step]
self.target_discharge_pressure = discharge_pressure[time_step]
compressor_result_per_time_step.append(
CompressorTrainResultSingleTimeStep(
speed=np.nan,
stage_results=[
result[time_step].stage_results[0] for result in compressor_stages_result_per_time_step
],
target_pressure_status=self.check_target_pressures(
calculated_suction_pressure=compressor_stages_result_per_time_step[0][
time_step
].suction_pressure,
calculated_discharge_pressure=compressor_stages_result_per_time_step[-1][
time_step
].discharge_pressure,
),
)
)
for time_step in range(len(compressor_stages_result_per_time_step[0]))
]

return compressor_result_per_time_step

def calculate_compressor_stage_work_given_outlet_pressure(
self,
Expand Down Expand Up @@ -332,9 +347,9 @@ def efficiency_as_function_of_rate_and_head(rates, heads):
outlet_pressure_before_choking=np.nan, # We do not have this value here
# Assuming choking and ASV. Valid points are to the left and below the compressor chart.
point_is_valid=~np.isnan(power_mw[i]), # power_mw is set to np.NaN if invalid step.
target_pressure_status=StageTargetPressureStatus.TARGET_PRESSURES_MET,
)
],
target_pressure_status=TargetPressureStatus.TARGET_PRESSURES_MET,
)
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@
find_root,
maximize_x_given_boolean_condition_function,
)
from libecalc.core.models.results.compressor import (
StageTargetPressureStatus,
)
from libecalc.dto.types import ChartAreaFlag, FixedSpeedPressureControl

EPSILON = 1e-5
Expand Down Expand Up @@ -207,21 +204,23 @@ def _evaluate_train_result_downstream_choking(
choked_stage_results = deepcopy(train_result.stage_results[0])
choked_stage_results.pressure_is_choked = True
choked_stage_results.inlet_pressure_before_choking = suction_pressure
self.target_suction_pressure = train_result.suction_pressure
self.stages[0].target_suction_pressure = train_result.suction_pressure
choked_stage_results.target_pressure_status = self.stages[0].check_target_pressures(
inlet_stream=choked_stage_results.inlet_stream, outlet_stream=choked_stage_results.outlet_stream
)
train_result.stage_results[0] = choked_stage_results
train_result.target_pressure_status = self.check_target_pressures(
calculated_suction_pressure=train_result.suction_pressure_before_choking,
calculated_discharge_pressure=train_result.discharge_pressure,
)

if train_result.discharge_pressure * (1 + PRESSURE_CALCULATION_TOLERANCE) > discharge_pressure:
# Fixme: Set new outlet pressure (or should we really make new stream - tp_flash?)
choked_stage_results = deepcopy(train_result.stage_results[-1])
choked_stage_results.pressure_is_choked = True
choked_stage_results.outlet_pressure_before_choking = float(choked_stage_results.discharge_pressure)
choked_stage_results.outlet_stream.pressure_bara = discharge_pressure
choked_stage_results.target_pressure_status = StageTargetPressureStatus.TARGET_PRESSURES_MET
train_result.stage_results[-1] = choked_stage_results
train_result.target_pressure_status = self.check_target_pressures(
calculated_suction_pressure=train_result.suction_pressure_before_choking, # because of the potential maximum discharge pressure
calculated_discharge_pressure=train_result.discharge_pressure,
)

return train_result

Expand Down Expand Up @@ -250,12 +249,11 @@ def _evaluate_train_results_upstream_choking(
choked_stage_results = deepcopy(train_result.stage_results[0])
choked_stage_results.pressure_is_choked = True
choked_stage_results.inlet_pressure_before_choking = suction_pressure
self.target_suction_pressure = train_result.suction_pressure
self.stages[0].target_suction_pressure = train_result.suction_pressure
choked_stage_results.target_pressure_status = self.stages[0].check_target_pressures(
inlet_stream=choked_stage_results.inlet_stream, outlet_stream=choked_stage_results.outlet_stream
)
train_result.stage_results[0] = choked_stage_results
train_result.target_pressure_status = self.check_target_pressures(
calculated_suction_pressure=train_result.suction_pressure_before_choking,
calculated_discharge_pressure=train_result.discharge_pressure,
)

return train_result

Expand Down Expand Up @@ -308,17 +306,8 @@ def _evaluate_train_result_asv_pressure(
)
inlet_stream_stage = inlet_stream_train
stage_results = []
for i, stage in enumerate(self.stages):
for stage in self.stages:
outlet_pressure_for_stage = inlet_stream_stage.pressure_bara * pressure_ratio_per_stage
# set stage target pressures for first and last stage
if i == 0:
stage.target_suction_pressure = self.target_suction_pressure
else:
stage.target_suction_pressure = None
if i == self.number_of_compressor_stages - 1:
stage.target_discharge_pressure = self.target_discharge_pressure
else:
stage.target_discharge_pressure = None
stage_result = calculate_single_speed_compressor_stage_given_target_discharge_pressure(
outlet_pressure_stage_bara=outlet_pressure_for_stage,
mass_rate_kg_per_hour=mass_rate_kg_per_hour,
Expand All @@ -332,9 +321,15 @@ def _evaluate_train_result_asv_pressure(
inlet_stream_stage = outlet_stream_stage
stage_results.append(stage_result)

# check if target pressures are met
target_pressure_status = self.check_target_pressures(
calculated_suction_pressure=inlet_stream_train.pressure_bara,
calculated_discharge_pressure=stage_results[-1].outlet_stream.pressure_bara,
)
return CompressorTrainResultSingleTimeStep(
speed=np.nan,
stage_results=stage_results,
target_pressure_status=target_pressure_status,
)
else:
return CompressorTrainResultSingleTimeStep.create_empty(number_of_stages=len(self.stages))
Expand Down Expand Up @@ -689,17 +684,8 @@ def calculate_single_speed_train(
stage_results = []
outlet_stream = train_inlet_stream

for i, (stage, mass_rate_kg_per_hour) in enumerate(zip(self.stages, mass_rate_kg_per_hour_per_stage)):
for stage, mass_rate_kg_per_hour in zip(self.stages, mass_rate_kg_per_hour_per_stage):
inlet_stream = outlet_stream
# set stage target pressures for first and last stage
if i == 0:
stage.target_suction_pressure = self.target_suction_pressure
else:
stage.target_suction_pressure = None
if i == self.number_of_compressor_stages - 1:
stage.target_discharge_pressure = self.target_discharge_pressure
else:
stage.target_discharge_pressure = None
stage_result = stage.evaluate(
inlet_stream_stage=inlet_stream,
mass_rate_kg_per_hour=mass_rate_kg_per_hour,
Expand All @@ -714,12 +700,11 @@ def calculate_single_speed_train(
new_temperature_kelvin=stage_result.outlet_stream.temperature_kelvin,
)

if (
target_discharge_pressure
and not np.isclose(stage_results[-1].discharge_pressure, target_discharge_pressure)
and stage_results[-1].discharge_pressure < target_discharge_pressure
):
stage_results[-1].chart_area_flag = ChartAreaFlag.ABOVE_MAXIMUM_HEAD
# check if target pressures are met
target_pressure_status = self.check_target_pressures(
calculated_suction_pressure=train_inlet_stream.pressure_bara,
calculated_discharge_pressure=outlet_stream.pressure_bara,
)

return CompressorTrainResultSingleTimeStep(
speed=np.nan,
Expand All @@ -728,6 +713,7 @@ def calculate_single_speed_train(
> self.maximum_power
if self.maximum_power
else False,
target_pressure_status=target_pressure_status,
)

def get_max_standard_rate(
Expand Down
Loading

0 comments on commit 098cfa9

Please sign in to comment.