Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: what fluid to recirculate when there is no rate entering a compressor stage in a multiple streams compressor train at a given time step #164

Merged
merged 1 commit into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
TeeeJay marked this conversation as resolved.
Show resolved Hide resolved
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
TeeeJay marked this conversation as resolved.
Show resolved Hide resolved
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