Skip to content

Commit

Permalink
fix: handle all situations where zero mass rate is entering a compres…
Browse files Browse the repository at this point in the history
…sor stage in a multiple streams compressor train

chore: calculate correct standard condition density when mixing two streams

chore: adding test of full recirculation in multiple streams compressor trains
  • Loading branch information
olelod committed Sep 13, 2023
1 parent e6d1f93 commit 4ea8210
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ def __init__(
existing_fluid: Initialize FluidStream from an existing (NeqSim) fluid. Warning: Be mindful of keeping fluid_model and existing fluid consistent. If not the fluid properties may be incorrect.
"""
self.fluid_model = fluid_model
self.initial_temperature_kelvin = temperature_kelvin
self.initial_pressure_bare = pressure_bara

if not temperature_kelvin > 0:
raise ValueError("FluidStream temperature needs to be above 0.")
Expand All @@ -60,8 +58,8 @@ def __init__(
self._enthalpy_joule_per_kg = _neqsim_fluid_stream.enthalpy_joule_per_kg

if (
pressure_bara == UnitConstants.STANDARD_PRESSURE_BARA
and temperature_kelvin == UnitConstants.STANDARD_TEMPERATURE_KELVIN
self._pressure_bara == UnitConstants.STANDARD_PRESSURE_BARA
and self._temperature_kelvin == UnitConstants.STANDARD_TEMPERATURE_KELVIN
):
self.standard_conditions_density = _neqsim_fluid_stream.density
self.molar_mass_kg_per_mol = _neqsim_fluid_stream.molar_mass
Expand All @@ -72,8 +70,8 @@ def __init__(
pressure_bara=UnitConstants.STANDARD_PRESSURE_BARA,
eos_model=self.fluid_model.eos_model,
)

self.standard_conditions_density = _neqsim_fluid_at_standard_conditions.density
self.molar_mass_kg_per_mol = _neqsim_fluid_stream.molar_mass

@property
def pressure_bara(self) -> float:
Expand Down Expand Up @@ -201,7 +199,10 @@ def set_new_pressure_and_temperature(
new_temperature_kelvin=new_temperature_kelvin,
remove_liquid=remove_liquid,
)
return FluidStream(existing_fluid=fluid_stream, fluid_model=self.fluid_model)
return FluidStream(
existing_fluid=fluid_stream,
fluid_model=self.fluid_model,
)

def set_new_pressure_and_enthalpy_change(
self, new_pressure: float, enthalpy_change_joule_per_kg: float, remove_liquid: bool = True
Expand Down Expand Up @@ -277,7 +278,5 @@ def mix_in_stream(

return FluidStream(
existing_fluid=mixed_neqsim_fluid_stream,
temperature_kelvin=temperature_kelvin,
pressure_bara=pressure_bara,
fluid_model=dto.FluidModel(composition=mixed_fluid_composition, eos_model=self.fluid_model.eos_model),
)
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ def __init__(
else:
self.outlet_stream_connected_to_stage[stream.connected_to_stage_no].append(i)

# in rare cases we can end up with trying to mix two streams with zero mass rate, and need the fluid from the
# previous time step to recirculate. This will take care of that.
self.fluid_to_recirculate_in_stage_when_inlet_rate_is_zero = [None] * len(self.stages)

@staticmethod
def _check_intermediate_pressure_stage_number_is_valid(
_stage_number_intermediate_pressure: int,
Expand Down Expand Up @@ -837,7 +841,7 @@ def calculate_compressor_train_given_rate_ps_speed(
mass_rate_this_stage_kg_per_hour = mass_rate_previous_stage_kg_per_hour
# take mass out before potential mixing with additional ingoing stream
# assume that volume/mass is always taken out before additional volume/mass is potentially added, no matter
# what order the streams are defined in in the yaml file
# what order the streams are defined in the yaml file
for stream_number in self.outlet_stream_connected_to_stage.get(stage_number):
mass_rate_this_stage_kg_per_hour -= inlet_stream.standard_rate_to_mass_rate(
std_rates_std_m3_per_day_per_stream[stream_number]
Expand All @@ -853,15 +857,27 @@ def calculate_compressor_train_given_rate_ps_speed(
mass_rate_additional_inlet_stream_kg_per_hour = additional_inlet_stream.standard_rate_to_mass_rate(
float(std_rates_std_m3_per_day_per_stream[stream_number])
)
if (mass_rate_this_stage_kg_per_hour > 0) or (mass_rate_additional_inlet_stream_kg_per_hour > 0):
inlet_stream = additional_inlet_stream.mix_in_stream(
other_fluid_stream=inlet_stream,
self_mass_rate=mass_rate_additional_inlet_stream_kg_per_hour,
other_mass_rate=mass_rate_this_stage_kg_per_hour,
temperature_kelvin=additional_inlet_stream.temperature_kelvin,
pressure_bara=additional_inlet_stream.pressure_bara,
)
mass_rate_this_stage_kg_per_hour += mass_rate_additional_inlet_stream_kg_per_hour

inlet_stream = additional_inlet_stream.mix_in_stream(
other_fluid_stream=inlet_stream,
self_mass_rate=mass_rate_additional_inlet_stream_kg_per_hour,
other_mass_rate=mass_rate_this_stage_kg_per_hour,
temperature_kelvin=additional_inlet_stream.temperature_kelvin,
pressure_bara=additional_inlet_stream.pressure_bara,
if mass_rate_this_stage_kg_per_hour == 0:
fluid_to_recirculate = self.get_fluid_to_recirculate_in_stage_when_inlet_rate_is_zero(
stage_number=stage_number
)
if fluid_to_recirculate:
inlet_stream = fluid_to_recirculate
else:
raise ValueError(
f"Trying to recirculate fluid in stage {stage_number} without defining which "
f"composition the fluid should have."
)
mass_rate_this_stage_kg_per_hour += mass_rate_additional_inlet_stream_kg_per_hour

stage_result = stage.evaluate(
inlet_stream_stage=inlet_stream,
Expand All @@ -879,7 +895,9 @@ def calculate_compressor_train_given_rate_ps_speed(
)

mass_rate_previous_stage_kg_per_hour = mass_rate_this_stage_kg_per_hour

self.set_fluid_to_recirculate_in_stage_when_inlet_rate_is_zero(
stage_number=stage_number, fluid_stream=inlet_stream
)
return CompressorTrainResultSingleTimeStep(
stage_results=stage_results,
speed=speed,
Expand Down Expand Up @@ -1122,19 +1140,27 @@ def _update_inlet_fluid_and_std_rates_for_last_subtrain(
float(std_rates_std_m3_per_day_per_stream[stream_number])
)
# mix streams to get inlet stream for first compressor stage
inlet_stream = additional_inlet_stream.mix_in_stream(
other_fluid_stream=inlet_stream,
self_mass_rate=mass_rate_additional_inlet_stream,
other_mass_rate=inlet_mass_rate,
temperature_kelvin=self.stages[0].inlet_temperature_kelvin,
pressure_bara=additional_inlet_stream.pressure_bara,
)
# if rate is 0 don't try to mix,
if (inlet_mass_rate > 0) or (mass_rate_additional_inlet_stream > 0):
inlet_stream = additional_inlet_stream.mix_in_stream(
other_fluid_stream=inlet_stream,
self_mass_rate=mass_rate_additional_inlet_stream,
other_mass_rate=inlet_mass_rate,
temperature_kelvin=additional_inlet_stream.temperature_kelvin,
pressure_bara=additional_inlet_stream.pressure_bara,
)
inlet_std_rate += std_rates_std_m3_per_day_per_stream[stream_number]
inlet_mass_rate = inlet_stream.standard_rate_to_mass_rate(inlet_std_rate)

# update inlet fluid for the subtrain with new composition
updated_inlet_fluid = FluidStream(fluid_model=inlet_stream.fluid_model)

if inlet_mass_rate == 0:
fluid_to_recirculate = self.get_fluid_to_recirculate_in_stage_when_inlet_rate_is_zero(stage_number=0)
if fluid_to_recirculate:
updated_inlet_fluid = fluid_to_recirculate
else:
raise ValueError("Trying to recirculate unknown fluid in compressor stage")
else:
updated_inlet_fluid = FluidStream(fluid_model=inlet_stream.fluid_model)
updated_std_rates_std_m3_per_day_per_stream = [inlet_std_rate]
updated_std_rates_std_m3_per_day_per_stream.extend(
[
Expand Down Expand Up @@ -1284,13 +1310,48 @@ def find_and_calculate_for_compressor_train_with_two_pressure_requirements(
+ compressor_train_results_to_return_last_part.stage_results
)

for stage_number in range(len(self.stages)):
self.set_fluid_to_recirculate_in_stage_when_inlet_rate_is_zero(
stage_number=stage_number,
fluid_stream=compressor_train_first_part.get_fluid_to_recirculate_in_stage_when_inlet_rate_is_zero(
stage_number=stage_number
)
if stage_number < stage_number_for_intermediate_pressure_target
else compressor_train_last_part.get_fluid_to_recirculate_in_stage_when_inlet_rate_is_zero(
stage_number=stage_number - stage_number_for_intermediate_pressure_target
),
)

return CompressorTrainResultSingleTimeStep(
speed=speed,
stage_results=compressor_train_results_to_return_stage_results,
failure_status=compressor_train_results_to_return_first_part.failure_status
or compressor_train_results_to_return_last_part.failure_status,
)

def set_fluid_to_recirculate_in_stage_when_inlet_rate_is_zero(
self, stage_number: int, fluid_stream: FluidStream
) -> None:
"""Keep track of what fluid passes through each compressor stage in the compressor train at a given calculation
step. This is done in case of the possibility of having a zero inlet rate at the next calculation step when
adding/subtracting ingoing/outgoing streams.
Args:
stage_number: The stage number (zero index)
fluid_stream: The fluid stream passing through compressor stage number <stage_number>
"""
self.fluid_to_recirculate_in_stage_when_inlet_rate_is_zero[stage_number] = fluid_stream

def get_fluid_to_recirculate_in_stage_when_inlet_rate_is_zero(self, stage_number: int) -> FluidStream:
"""Retrieve the fluid that passed through a given compressor stage in the compressor train at the previous
calculation step.
Arge:
stage_number: The stage number (zero index)
"""
return self.fluid_to_recirculate_in_stage_when_inlet_rate_is_zero[stage_number]


def split_rates_on_stage_number(
compressor_train: VariableSpeedCompressorTrainCommonShaftMultipleStreamsAndPressures,
Expand Down Expand Up @@ -1366,4 +1427,16 @@ def split_train_on_stage_number(
),
)

for stage_no in range(len(compressor_train.stages)):
if stage_no < stage_number:
compressor_train_first_part.set_fluid_to_recirculate_in_stage_when_inlet_rate_is_zero(
stage_number=stage_no,
fluid_stream=compressor_train.get_fluid_to_recirculate_in_stage_when_inlet_rate_is_zero(stage_no),
)
else:
compressor_train_last_part.set_fluid_to_recirculate_in_stage_when_inlet_rate_is_zero(
stage_number=stage_no - stage_number,
fluid_stream=compressor_train.get_fluid_to_recirculate_in_stage_when_inlet_rate_is_zero(stage_no),
)

return compressor_train_first_part, compressor_train_last_part
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,41 @@ def variable_speed_compressor_train_two_compressors_two_streams(
)


@pytest.fixture
def variable_speed_compressor_train_two_compressors_ingoning_and_outgoing_streams_between_compressors(
variable_speed_compressor_train_stage_dto,
dry_fluid,
rich_fluid,
mock_variable_speed_compressor_train_multiple_streams_and_pressures,
) -> VariableSpeedCompressorTrainCommonShaftMultipleStreamsAndPressures:
"""Train with only two compressors, and standard medium fluid, on stream in per stage, no liquid off take."""
fluid_streams = [
FluidStreamObjectForMultipleStreams(
fluid=FluidStream(rich_fluid),
is_inlet_stream=True,
connected_to_stage_no=0,
),
FluidStreamObjectForMultipleStreams(
is_inlet_stream=False,
connected_to_stage_no=1,
),
FluidStreamObjectForMultipleStreams(
fluid=FluidStream(dry_fluid),
is_inlet_stream=True,
connected_to_stage_no=1,
),
]
return VariableSpeedCompressorTrainCommonShaftMultipleStreamsAndPressures(
streams=fluid_streams,
data_transfer_object=mock_variable_speed_compressor_train_multiple_streams_and_pressures.copy(
update={
"stages": [variable_speed_compressor_train_stage_dto] * 2,
"pressure_control": dto.types.FixedSpeedPressureControl.DOWNSTREAM_CHOKE,
}
),
)


@pytest.fixture
def variable_speed_compressor_train_two_compressors_one_ingoing_and_one_outgoing_stream(
variable_speed_compressor_train_stage_dto,
Expand Down Expand Up @@ -649,3 +684,26 @@ def test_adjust_energy_usage(
np.asarray(result_comparison_intermediate.energy_usage) + energy_usage_adjustment_constant,
result_intermediate.energy_usage,
)


def test_recirculate_mixing_streams_with_zero_mass_rate(
variable_speed_compressor_train_two_compressors_ingoning_and_outgoing_streams_between_compressors,
):
result = variable_speed_compressor_train_two_compressors_ingoning_and_outgoing_streams_between_compressors.evaluate_rate_ps_pd(
rate=np.asarray(
[
[3000000, 3000000, 3000000, 3000000, 3000000, 3000000],
[0, 3000000, 2500000, 3000000, 3000000, 3000000],
[0, 0, 500000, 0, 1000000, 0],
]
),
suction_pressure=np.asarray([30, 30, 30, 30, 30, 30]),
discharge_pressure=np.asarray([150, 150, 150, 150, 150, 150]),
)
np.testing.assert_almost_equal(result.power[0], result.power[1], decimal=4)
np.testing.assert_almost_equal(result.power[2], result.power[3], decimal=4)
np.testing.assert_almost_equal(result.power[4], result.power[5], decimal=4)
assert result.recirculation_loss[0] < result.recirculation_loss[1]
assert result.recirculation_loss[2] < result.recirculation_loss[3]
assert result.recirculation_loss[4] < result.recirculation_loss[5]
assert result.power[0] < result.power[2] < result.power[4] # more and more of the heavy fluid

0 comments on commit 4ea8210

Please sign in to comment.