Skip to content

Commit 0e4083c

Browse files
authored
chore: implement asv solver (#1394)
This includes all three asv solvers, individual_asv_rate, individual_asv_pressure and common_asv Tests comparing new and legacy code has also been added Refs. equinor/ecalc-internal#1538
1 parent 6566921 commit 0e4083c

7 files changed

Lines changed: 735 additions & 150 deletions

File tree

src/libecalc/domain/process/process_solver/asv_solvers.py

Lines changed: 411 additions & 0 deletions
Large diffs are not rendered by default.

src/libecalc/domain/process/process_solver/common_asv_solver.py

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

src/libecalc/domain/process/process_system/process_system.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ def __init__(
1313
):
1414
self._process_units = process_units
1515

16+
def get_process_units(self):
17+
return self._process_units
18+
1619
def propagate_stream(self, inlet_stream: FluidStream) -> FluidStream:
1720
current_inlet = inlet_stream
1821
for process_unit in self._process_units:

tests/libecalc/domain/process/conftest.py

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

33
from libecalc.domain.process.compressor.core.train.stage import CompressorTrainStage
44
from libecalc.domain.process.entities.shaft import Shaft
5+
from libecalc.domain.process.process_solver.asv_solvers import CompressorStageProcessUnit
56
from libecalc.domain.process.process_solver.boundary import Boundary
6-
from libecalc.domain.process.process_solver.common_asv_solver import CompressorStageProcessUnit
77
from libecalc.domain.process.process_solver.search_strategies import (
88
CONVERGENCE_TOLERANCE,
99
BinarySearchStrategy,
@@ -194,12 +194,25 @@ def get_speed_boundary(self) -> Boundary:
194194
def get_maximum_standard_rate(self, inlet_stream: FluidStream) -> float:
195195
compressor_inlet_stream = self._compressor_stage.get_compressor_inlet_stream(inlet_stream_stage=inlet_stream)
196196
density = compressor_inlet_stream.density
197-
max_actual_rate = self._compressor_stage.compressor.compressor_chart.maximum_rate
197+
max_actual_rate = self._compressor_stage.compressor.compressor_chart.maximum_rate_as_function_of_speed(
198+
self._compressor_stage.compressor.speed
199+
)
198200
max_mass_rate = max_actual_rate * density
199201
return self._compressor_stage.fluid_service.mass_rate_to_standard_rate(
200202
fluid_model=compressor_inlet_stream.fluid_model, mass_rate_kg_per_h=max_mass_rate
201203
)
202204

205+
def get_minimum_standard_rate(self, inlet_stream: FluidStream) -> float:
206+
compressor_inlet_stream = self._compressor_stage.get_compressor_inlet_stream(inlet_stream_stage=inlet_stream)
207+
density = compressor_inlet_stream.density
208+
min_actual_rate = self._compressor_stage.compressor.compressor_chart.minimum_rate_as_function_of_speed(
209+
self._compressor_stage.compressor.speed
210+
)
211+
min_mass_rate = min_actual_rate * density
212+
return self._compressor_stage.fluid_service.mass_rate_to_standard_rate(
213+
fluid_model=compressor_inlet_stream.fluid_model, mass_rate_kg_per_h=min_mass_rate
214+
)
215+
203216
def propagate_stream(self, inlet_stream: FluidStream) -> FluidStream:
204217
result = self._compressor_stage.evaluate(inlet_stream_stage=inlet_stream)
205218
if result.chart_area_flag == ChartAreaFlag.ABOVE_MAXIMUM_FLOW_RATE:

tests/libecalc/domain/process/process_solver/test_common_asv_solver.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from inline_snapshot import snapshot
33

44
from libecalc.domain.process.entities.shaft import VariableSpeedShaft
5-
from libecalc.domain.process.process_solver.common_asv_solver import CommonASVSolver
5+
from libecalc.domain.process.process_solver.asv_solvers import ASVSolver
66
from libecalc.domain.process.process_solver.float_constraint import FloatConstraint
77

88

@@ -28,7 +28,7 @@ def test_common_asv_solver(
2828
stage1_chart_data = chart_data_factory.from_design_point(rate=1200, head=70000, efficiency=0.75)
2929
stage2_chart_data = chart_data_factory.from_design_point(rate=900, head=50000, efficiency=0.72)
3030

31-
common_asv_solver = CommonASVSolver(
31+
common_asv_solver = ASVSolver(
3232
shaft=shaft,
3333
compressors=[
3434
compressor_train_stage_process_unit_factory(
@@ -39,14 +39,15 @@ def test_common_asv_solver(
3939
compressor_train_stage_process_unit_factory(chart_data=stage2_chart_data, shaft=shaft),
4040
],
4141
fluid_service=fluid_service,
42+
individual_asv_control=False,
4243
)
4344

4445
inlet_stream = stream_factory(
4546
standard_rate_m3_per_day=500_000.0, pressure_bara=30.0, temperature_kelvin=temperature
4647
)
4748
assert inlet_stream.volumetric_rate_m3_per_hour == snapshot(681.2529349883239)
4849

49-
speed_solution, recirculation_solution = common_asv_solver.find_common_asv_solution(
50+
speed_solution, recirculation_solution = common_asv_solver.find_asv_solution(
5051
pressure_constraint=target_pressure,
5152
inlet_stream=inlet_stream,
5253
)
@@ -59,15 +60,15 @@ def test_common_asv_solver(
5960
).solve(common_asv_solver.get_recirculation_func(inlet_stream=inlet_stream))
6061

6162
assert speed_solution.success
62-
assert speed_solution.configuration.speed == snapshot(94.38349228734866)
63-
assert recirculation_solution.success
63+
assert speed_solution.configuration.speed == snapshot(94.4000351009834)
64+
assert recirculation_solution[0].success
6465

6566
recirculation_rate_at_capacity = recirculation_at_capacity_solution.configuration.recirculation_rate
66-
recirculation_rate_after_pressure_control = recirculation_solution.configuration.recirculation_rate
67+
recirculation_rate_after_pressure_control = recirculation_solution[0].configuration.recirculation_rate
6768

68-
assert recirculation_rate_at_capacity == snapshot(335543.7583238268)
69+
assert recirculation_rate_at_capacity == snapshot(336261.09561854857)
6970
assert recirculation_rate_after_pressure_control >= recirculation_rate_at_capacity
7071

71-
recirculation_loop.set_recirculation_rate(recirculation_solution.configuration.recirculation_rate)
72+
recirculation_loop.set_recirculation_rate(recirculation_solution[0].configuration.recirculation_rate)
7273
outlet_stream = recirculation_loop.propagate_stream(inlet_stream=inlet_stream)
7374
assert outlet_stream.pressure_bara == target_pressure

tests/libecalc/domain/process/process_solver/test_common_asv_solver_vs_legacy_train.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from libecalc.domain.process.compressor.core.results import CompressorTrainResultSingleTimeStep
66
from libecalc.domain.process.compressor.core.train.train_evaluation_input import CompressorTrainEvaluationInput
77
from libecalc.domain.process.entities.shaft import VariableSpeedShaft
8-
from libecalc.domain.process.process_solver.common_asv_solver import CommonASVSolver
8+
from libecalc.domain.process.process_solver.asv_solvers import ASVSolver
99
from libecalc.domain.process.process_solver.float_constraint import FloatConstraint
1010
from libecalc.domain.process.value_objects.chart import ChartCurve
1111
from libecalc.domain.process.value_objects.fluid_stream import FluidModel, FluidService
@@ -132,18 +132,19 @@ def test_common_asv_solver_vs_legacy_train(
132132
)
133133
stages_new = [stage1_new, stage2_new]
134134

135-
train_solver = CommonASVSolver(
135+
train_solver = ASVSolver(
136136
compressors=stages_new,
137137
fluid_service=fluid_service,
138138
shaft=shaft_new,
139+
individual_asv_control=False,
139140
)
140-
speed_solution, recirculation_solution = train_solver.find_common_asv_solution(
141+
speed_solution, recirculation_solution = train_solver.find_asv_solution(
141142
pressure_constraint=FloatConstraint(target_pressure),
142143
inlet_stream=inlet_stream,
143144
)
144145
recirculation_loop = train_solver.get_recirculation_loop()
145146
shaft_new.set_speed(speed_solution.configuration.speed)
146-
recirculation_loop.set_recirculation_rate(recirculation_solution.configuration.recirculation_rate)
147+
recirculation_loop.set_recirculation_rate(recirculation_solution[0].configuration.recirculation_rate)
147148
new_outlet_stream = recirculation_loop.propagate_stream(inlet_stream=inlet_stream)
148149

149150
assert new_outlet_stream.volumetric_rate_m3_per_hour == pytest.approx(
@@ -153,14 +154,20 @@ def test_common_asv_solver_vs_legacy_train(
153154
old_outlet_stream.pressure_bara, rel=0.00000001
154155
) # 0.000001 %
155156
assert new_outlet_stream.density == pytest.approx(old_outlet_stream.density, rel=0.001) # 0.1 %
156-
assert shaft_new.get_speed() == pytest.approx(shaft_old.get_speed(), rel=0.021) # 2.1 %
157+
assert shaft_new.get_speed() > shaft_old.get_speed() # only recirc in one compressor in old result
157158

158159
# For now new and old recirculation rate is not expected to match - as the old train recirculates individual stages and finds a solution
159160
# without common asv
160-
new_recirculation_rate = recirculation_solution.configuration.recirculation_rate
161-
old_recirculation_rate = _calc_recirculation_rate_from_loss_mw(
162-
fluid_service=fluid_service, result=old_result, fluid_model=old_outlet_stream.fluid_model
161+
new_recirculation_rate = recirculation_solution[0].configuration.recirculation_rate
162+
old_recirculation_rate_1 = (
163+
old_result.stage_results[0].standard_rate_asv_corrected_sm3_per_day
164+
- old_result.stage_results[0].standard_rate_sm3_per_day
165+
)
166+
old_recirculation_rate_2 = (
167+
old_result.stage_results[1].standard_rate_asv_corrected_sm3_per_day
168+
- old_result.stage_results[1].standard_rate_sm3_per_day
163169
)
164170

165-
assert new_recirculation_rate == snapshot(250211.12092008846)
166-
assert old_recirculation_rate == snapshot(141127.6427142186)
171+
assert new_recirculation_rate == snapshot(250007.5)
172+
assert old_recirculation_rate_1 == snapshot(249999.99999999988)
173+
assert old_recirculation_rate_2 == 0.0

0 commit comments

Comments
 (0)