Skip to content

Commit

Permalink
refactor: add inlet/outlet stream also for compressor train results (#…
Browse files Browse the repository at this point in the history
…590)

chore: update failing tests

chore: update snapshots

ECALC-1583
  • Loading branch information
olelod committed Sep 15, 2024
1 parent e24df7e commit c27e5bd
Show file tree
Hide file tree
Showing 26 changed files with 18,281 additions and 1,245 deletions.
9 changes: 9 additions & 0 deletions docs/docs/changelog/next.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ sidebar_position: -1002

## New Features

- In the results, the fluid streams entering and leaving a compressor train are now defined separately from the fluid
streams entering and leaving the individual compressor stages (pressure, temperature, composition, density, etc.).
This used to be covered only by reporting the inlet/outlet pressures before choking.
If there is any upstream choking, this will happen between the inlet of the compressor train, and the inlet of the
first compressor stage. This means that in a situation with upstream choking, the fluid stream entering the compressor
train will have higher e.g. pressure and density than the fluid stream entering the first compressor stage. If there
is any downstream choking, this will happen between the outlet of the last compressor stage and the outlet of the
compressor train. This means that in a situation with downstream choking, the fluid stream leaving the compressor
train will have lower e.g. pressure and density than the fluid stream leaving the last compressor stage.

## Fixes

Expand Down
2 changes: 2 additions & 0 deletions src/libecalc/core/consumers/compressor/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ def evaluate(
rate_sm3_day=model_result.rate_sm3_day,
stage_results=model_result.stage_results,
failure_status=model_result.failure_status,
inlet_stream_condition=model_result.inlet_stream_condition,
outlet_stream_condition=model_result.outlet_stream_condition,
)
],
)
2 changes: 2 additions & 0 deletions src/libecalc/core/consumers/legacy_consumer/result_mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ def map_energy_function_results(
failure_status=result.failure_status,
turbine_result=result.turbine_result,
max_standard_rate=result.max_standard_rate,
inlet_stream_condition=result.inlet_stream_condition,
outlet_stream_condition=result.outlet_stream_condition,
)
)
elif isinstance(result, PumpModelResult):
Expand Down
197 changes: 136 additions & 61 deletions src/libecalc/core/models/compressor/results.py

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ def evaluate_rate_ps_pd(
# Note that actual rates are not available since it is not possible to convert from standard rates to
# actual rates when information about fluid composition (density in particular) is not available
result = CompressorTrainResult(
inlet_stream_condition=inlet_stream_condition,
outlet_stream_condition=outlet_stream_condition,
energy_usage=energy_usage,
energy_usage_unit=Unit.MEGA_WATT if self.function_values_are_power else Unit.STANDARD_CUBIC_METER_PER_DAY,
power=array_to_list(interpolated_consumer_values) if self.function_values_are_power else turbine_power,
Expand Down
8 changes: 7 additions & 1 deletion src/libecalc/core/models/compressor/train/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,18 @@ def evaluate_rate_ps_pd(
)
max_standard_rate[valid_indices] = max_standard_rate_for_valid_indices

stage_results = CompressorTrainResultSingleTimeStep.from_result_list_to_dto(
(
inlet_stream_condition,
outlet_stream_condition,
stage_results,
) = CompressorTrainResultSingleTimeStep.from_result_list_to_dto(
result_list=train_results,
compressor_charts=[stage.compressor_chart.data_transfer_object for stage in self.stages],
)

return CompressorTrainResult(
inlet_stream_condition=inlet_stream_condition,
outlet_stream_condition=outlet_stream_condition,
energy_usage=list(power_mw_adjusted),
energy_usage_unit=Unit.MEGA_WATT,
power=list(power_mw_adjusted),
Expand Down
2 changes: 2 additions & 0 deletions src/libecalc/core/models/compressor/train/simplified_train.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ def _evaluate_rate_ps_pd(
time_step
].discharge_pressure,
),
inlet_stream=compressor_stages_result_per_time_step[0][time_step].inlet_stream,
outlet_stream=compressor_stages_result_per_time_step[-1][time_step].outlet_stream,
)
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from copy import deepcopy
from typing import List, Optional

import numpy as np
Expand All @@ -21,6 +20,7 @@
find_root,
maximize_x_given_boolean_condition_function,
)
from libecalc.core.models.results.compressor import TargetPressureStatus
from libecalc.dto.types import ChartAreaFlag, FixedSpeedPressureControl

EPSILON = 1e-5
Expand Down Expand Up @@ -197,29 +197,27 @@ def _evaluate_train_result_downstream_choking(

if self.maximum_discharge_pressure is not None:
if train_result.discharge_pressure * (1 + PRESSURE_CALCULATION_TOLERANCE) > self.maximum_discharge_pressure:
train_result = self._evaluate_rate_pd(
new_train_result = self._evaluate_rate_pd(
mass_rate_kg_per_hour=mass_rate_kg_per_hour,
outlet_pressure_train_bara=self.maximum_discharge_pressure,
)
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
train_result.stage_results[0] = choked_stage_results
train_result.stage_results = new_train_result.stage_results
train_result.outlet_stream = new_train_result.outlet_stream
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,
calculated_suction_pressure=train_result.inlet_stream.pressure_bara,
calculated_discharge_pressure=train_result.outlet_stream.pressure_bara,
)

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
train_result.stage_results[-1] = choked_stage_results
if train_result.target_pressure_status == TargetPressureStatus.ABOVE_TARGET_DISCHARGE_PRESSURE:
new_outlet_stream = FluidStream(
fluid_model=train_result.outlet_stream,
pressure_bara=discharge_pressure,
temperature_kelvin=train_result.outlet_stream.temperature_kelvin,
)
train_result.outlet_stream = dto.FluidStream.from_fluid_domain_object(fluid_stream=new_outlet_stream)
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,
calculated_suction_pressure=train_result.inlet_stream.pressure_bara,
calculated_discharge_pressure=train_result.outlet_stream.pressure_bara,
)

return train_result
Expand All @@ -245,14 +243,16 @@ def _evaluate_train_results_upstream_choking(
outlet_pressure_train_bara=discharge_pressure,
)

if train_result.suction_pressure < suction_pressure * (1 - PRESSURE_CALCULATION_TOLERANCE):
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
train_result.stage_results[0] = choked_stage_results
if train_result.target_pressure_status == TargetPressureStatus.BELOW_TARGET_SUCTION_PRESSURE:
new_inlet_stream = FluidStream(
fluid_model=train_result.inlet_stream,
pressure_bara=suction_pressure,
temperature_kelvin=train_result.inlet_stream.temperature_kelvin,
)
train_result.inlet_stream = dto.FluidStream.from_fluid_domain_object(fluid_stream=new_inlet_stream)
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,
calculated_suction_pressure=train_result.inlet_stream.pressure_bara,
calculated_discharge_pressure=train_result.outlet_stream.pressure_bara,
)

return train_result
Expand Down Expand Up @@ -304,7 +304,7 @@ def _evaluate_train_result_asv_pressure(
suction_pressure=suction_pressure,
discharge_pressure=discharge_pressure,
)
inlet_stream_stage = inlet_stream_train
inlet_stream_stage = outlet_stream_stage = inlet_stream_train
stage_results = []
for stage in self.stages:
outlet_pressure_for_stage = inlet_stream_stage.pressure_bara * pressure_ratio_per_stage
Expand All @@ -327,6 +327,8 @@ def _evaluate_train_result_asv_pressure(
calculated_discharge_pressure=stage_results[-1].outlet_stream.pressure_bara,
)
return CompressorTrainResultSingleTimeStep(
inlet_stream=dto.FluidStream.from_fluid_domain_object(fluid_stream=inlet_stream_train),
outlet_stream=dto.FluidStream.from_fluid_domain_object(fluid_stream=outlet_stream_stage),
speed=np.nan,
stage_results=stage_results,
target_pressure_status=target_pressure_status,
Expand Down Expand Up @@ -707,6 +709,8 @@ def calculate_single_speed_train(
)

return CompressorTrainResultSingleTimeStep(
inlet_stream=dto.FluidStream.from_fluid_domain_object(fluid_stream=train_inlet_stream),
outlet_stream=dto.FluidStream.from_fluid_domain_object(fluid_stream=outlet_stream),
speed=np.nan,
stage_results=stage_results,
above_maximum_power=sum([stage_result.power_megawatt for stage_result in stage_results])
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from copy import deepcopy
from functools import partial
from typing import List, Optional

Expand Down Expand Up @@ -31,6 +30,7 @@
from libecalc.core.models.compressor.train.utils.variable_speed_compressor_train_common_shaft import (
get_single_speed_equivalent,
)
from libecalc.core.models.results.compressor import TargetPressureStatus
from libecalc.dto.types import FixedSpeedPressureControl

EPSILON = 1e-5
Expand Down Expand Up @@ -234,6 +234,8 @@ def calculate_compressor_train_given_rate_ps_speed(
)

return CompressorTrainResultSingleTimeStep(
inlet_stream=dto.FluidStream.from_fluid_domain_object(fluid_stream=train_inlet_stream),
outlet_stream=dto.FluidStream.from_fluid_domain_object(fluid_stream=outlet_stream),
stage_results=stage_results,
speed=speed,
above_maximum_power=sum([stage_result.power_megawatt for stage_result in stage_results])
Expand Down Expand Up @@ -631,36 +633,49 @@ def _calculate_train_result_given_rate_ps_speed_asv_rate_fraction(
):
# Checking for upstream choke also, to find if we are in a situation where upstream choking is feasible
# (can inlet_pressure and speed give at least the required outlet_pressure)
train_results = self.calculate_compressor_train_given_rate_ps_speed(
train_result = self.calculate_compressor_train_given_rate_ps_speed(
mass_rate_kg_per_hour=mass_rate_kg_per_hour,
inlet_pressure_bara=inlet_pressure,
speed=speed,
)
if train_results.discharge_pressure * (1 + PRESSURE_CALCULATION_TOLERANCE) < outlet_pressure:
if train_result.discharge_pressure * (1 + PRESSURE_CALCULATION_TOLERANCE) < outlet_pressure:
pass
elif self.pressure_control == FixedSpeedPressureControl.UPSTREAM_CHOKE:
train_results = self.calculate_compressor_train_given_rate_pd_speed(
train_result = self.calculate_compressor_train_given_rate_pd_speed(
mass_rate_kg_per_hour=mass_rate_kg_per_hour,
outlet_pressure=outlet_pressure,
speed=speed,
upper_bound_for_inlet_pressure=inlet_pressure,
)
# Set pressure before upstream choking to the given inlet pressure
train_results.stage_results[0].inlet_pressure_before_choking = inlet_pressure
train_results.target_pressure_status = self.check_target_pressures(
calculated_suction_pressure=train_results.suction_pressure_before_choking,
calculated_discharge_pressure=train_results.discharge_pressure,
)
if train_result.target_pressure_status == TargetPressureStatus.BELOW_TARGET_SUCTION_PRESSURE:
# Here the train result will have lower inlet pressure than the requested pressure
# Set train inlet stream to have the requested inlet pressure and check target pressure status again
new_inlet_stream = FluidStream(
fluid_model=train_result.inlet_stream,
pressure_bara=inlet_pressure,
temperature_kelvin=train_result.inlet_stream.temperature_kelvin,
)
train_result.inlet_stream = dto.FluidStream.from_fluid_domain_object(fluid_stream=new_inlet_stream)
train_result.target_pressure_status = self.check_target_pressures(
calculated_suction_pressure=train_result.inlet_stream.pressure_bara,
calculated_discharge_pressure=train_result.outlet_stream.pressure_bara,
)
elif self.pressure_control == FixedSpeedPressureControl.DOWNSTREAM_CHOKE:
choked_stage_results = deepcopy(train_results.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 = outlet_pressure
train_results.stage_results[-1] = choked_stage_results
train_results.target_pressure_status = self.check_target_pressures(
calculated_suction_pressure=train_results.suction_pressure,
calculated_discharge_pressure=train_results.discharge_pressure,
)
if train_result.target_pressure_status == TargetPressureStatus.ABOVE_TARGET_DISCHARGE_PRESSURE:
# Here the train result will have higher outlet pressure than the requested pressure
# Set train outlet stream to have the requested outlet pressure and check target pressure status again
new_outlet_stream = FluidStream(
fluid_model=train_result.outlet_stream,
pressure_bara=outlet_pressure,
temperature_kelvin=train_result.outlet_stream.temperature_kelvin,
)
train_result.outlet_stream = dto.FluidStream.from_fluid_domain_object(
fluid_stream=new_outlet_stream
)
train_result.target_pressure_status = self.check_target_pressures(
calculated_suction_pressure=train_result.inlet_stream.pressure_bara,
calculated_discharge_pressure=train_result.outlet_stream.pressure_bara,
)

elif self.pressure_control == FixedSpeedPressureControl.INDIVIDUAL_ASV_RATE:
# first check if there is room for recirculation
Expand Down Expand Up @@ -696,7 +711,7 @@ def _calculate_train_result_given_rate_ps_speed_asv_rate_fraction(
).discharge_pressure
- outlet_pressure,
)
train_results = self.calculate_compressor_train_given_rate_ps_speed(
train_result = self.calculate_compressor_train_given_rate_ps_speed(
mass_rate_kg_per_hour=mass_rate_kg_per_hour,
inlet_pressure_bara=inlet_pressure,
speed=speed,
Expand All @@ -720,14 +735,14 @@ def _calculate_train_result_given_rate_ps_speed_asv_rate_fraction(
for train_result in single_speed_train_results:
train_result.speed = speed
# return CompressorTrainResultSingleTimeStep for first time step (should be only one here, really)
train_results = single_speed_train_results[0]
train_result = single_speed_train_results[0]
else:
raise IllegalStateException(
f"Pressure control {self.pressure_control} not supported, should be one of"
f"{list(FixedSpeedPressureControl)}. Should not end up here, please contact support."
)

return train_results
return train_result


def get_single_speed_equivalent_train(
Expand Down
Loading

0 comments on commit c27e5bd

Please sign in to comment.