Skip to content

Commit 2cd45a3

Browse files
committed
chore: init process simulation
1 parent 7b2db7a commit 2cd45a3

15 files changed

Lines changed: 357 additions & 15 deletions

File tree

src/libecalc/domain/process/compressor/core/train/compressor_train_common_shaft.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ def _validate_nonnegative_stage_rates(self, constraints: CompressorTrainEvaluati
141141
def evaluate_given_constraints(
142142
self,
143143
constraints: CompressorTrainEvaluationInput,
144-
fixed_speed: float | None = None,
144+
fixed_speed: float | None = None, # TODO: not used?
145145
) -> CompressorTrainResultSingleTimeStep:
146146
self.reset_rate_modifiers()
147147
self._validate_nonnegative_stage_rates(constraints)
@@ -1111,13 +1111,14 @@ def _calculate_compressor_train(_speed: float) -> CompressorTrainResultSingleTim
11111111
constraints=constraints,
11121112
)
11131113

1114-
train_result_for_minimum_speed = _calculate_compressor_train(_speed=minimum_speed)
11151114
train_result_for_maximum_speed = _calculate_compressor_train(_speed=maximum_speed)
11161115

11171116
if not train_result_for_maximum_speed.within_capacity:
11181117
# will not find valid result - the rate is above maximum rate, return invalid results at maximum speed
11191118
self.shaft.set_speed(maximum_speed)
11201119
return maximum_speed
1120+
1121+
train_result_for_minimum_speed = _calculate_compressor_train(_speed=minimum_speed)
11211122
if not train_result_for_minimum_speed.within_capacity:
11221123
# rate is above maximum rate for minimum speed. Find the lowest minimum speed which gives a valid result
11231124
minimum_speed = -maximize_x_given_boolean_condition_function(
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from libecalc.domain.process.process_system.process_unit import ProcessUnit
2+
from libecalc.domain.process.value_objects.fluid_stream import FluidService, FluidStream
3+
4+
5+
class Choke(ProcessUnit):
6+
def __init__(
7+
self,
8+
fluid_service: FluidService,
9+
delta_pressure: float = 0,
10+
):
11+
self._delta_pressure = delta_pressure
12+
self._fluid_service = fluid_service
13+
14+
def get_delta_pressure(self) -> float:
15+
return self._delta_pressure
16+
17+
def set_delta_pressure(self, delta_pressure: float) -> None:
18+
self._delta_pressure = delta_pressure
19+
20+
def propagate_stream(self, inlet_stream: FluidStream) -> FluidStream | None:
21+
choked_inlet_pressure = inlet_stream.pressure_bara - self._delta_pressure
22+
return self._fluid_service.create_stream_from_standard_rate(
23+
fluid_model=inlet_stream.fluid_model,
24+
pressure_bara=choked_inlet_pressure,
25+
temperature_kelvin=inlet_stream.pressure_bara,
26+
standard_rate_m3_per_day=inlet_stream.standard_rate_sm3_per_day,
27+
)

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

Whitespace-only changes.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from dataclasses import dataclass
2+
3+
4+
@dataclass
5+
class Boundary:
6+
min: float
7+
max: float
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from libecalc.domain.process.process_solver.solver import Solver
2+
from libecalc.domain.process.process_solver.stream_constraint import StreamConstraint
3+
from libecalc.domain.process.process_system.process_system import ProcessSystem
4+
from libecalc.domain.process.value_objects.fluid_stream import FluidStream
5+
6+
7+
class ProcessSolver:
8+
def __init__(
9+
self,
10+
inlet_stream: FluidStream,
11+
process_system: ProcessSystem,
12+
solvers: list[Solver],
13+
stream_constraint: StreamConstraint,
14+
):
15+
self._process_system = process_system
16+
self._inlet_stream = inlet_stream
17+
self._solvers = solvers
18+
self._stream_constraint = stream_constraint
19+
20+
def find_solution(self) -> bool:
21+
for solver in self._solvers:
22+
outlet_stream = solver.solve(process_system=self._process_system, inlet_stream=self._inlet_stream)
23+
if self._stream_constraint.check(outlet_stream):
24+
return True
25+
26+
return False
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import abc
2+
3+
from libecalc.domain.process.process_system.process_system import ProcessSystem
4+
from libecalc.domain.process.value_objects.fluid_stream import FluidStream
5+
6+
7+
class Solver(abc.ABC):
8+
@abc.abstractmethod
9+
def solve(self, process_system: ProcessSystem, inlet_stream: FluidStream) -> FluidStream | None: ...

src/libecalc/domain/process/process_solver/solvers/__init__.py

Whitespace-only changes.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from libecalc.domain.process.process_solver.solver import Solver
2+
from libecalc.domain.process.process_system.process_system import ProcessSystem
3+
from libecalc.domain.process.value_objects.fluid_stream import FluidStream
4+
5+
6+
class DownstreamChokeSolver(Solver):
7+
def __init__(self, outlet_pressure: float):
8+
self._outlet_pressure = outlet_pressure
9+
10+
def solve(self, process_system: ProcessSystem, inlet_stream: FluidStream) -> FluidStream | None:
11+
outlet_stream = process_system.propagate_stream(inlet_stream=inlet_stream)
12+
downstream_choke = process_system.get_downstream_choke()
13+
assert downstream_choke is not None, "DownstreamChoke solver needs a downstream choke"
14+
if outlet_stream is not None and outlet_stream.pressure_bara >= self._outlet_pressure:
15+
delta_pressure = self._outlet_pressure - outlet_stream.pressure_bara
16+
downstream_choke.set_delta_pressure(delta_pressure)
17+
# Adjust pressure
18+
return process_system.propagate_stream(inlet_stream=inlet_stream)
19+
return outlet_stream
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
from libecalc.domain.process.compressor.core.train.utils.numeric_methods import (
2+
find_root,
3+
maximize_x_given_boolean_condition_function,
4+
)
5+
from libecalc.domain.process.process_solver.boundary import Boundary
6+
from libecalc.domain.process.process_solver.solver import Solver
7+
from libecalc.domain.process.process_system.process_system import ProcessSystem
8+
from libecalc.domain.process.value_objects.fluid_stream import FluidStream
9+
10+
11+
class SpeedSolver(Solver):
12+
def __init__(self, boundary: Boundary, target_pressure: float):
13+
self._boundary = boundary
14+
self._target_pressure = target_pressure
15+
16+
def solve(
17+
self,
18+
process_system: ProcessSystem,
19+
inlet_stream: FluidStream,
20+
) -> FluidStream | None:
21+
shaft = process_system.get_shaft()
22+
23+
def get_outlet_stream(speed: float) -> FluidStream | None:
24+
shaft.set_speed(speed)
25+
return process_system.propagate_stream(inlet_stream)
26+
27+
maximum_speed = self._boundary.max
28+
maximum_speed_outlet_stream = get_outlet_stream(speed=maximum_speed)
29+
if maximum_speed_outlet_stream is None:
30+
# Outside capacity
31+
return maximum_speed_outlet_stream
32+
33+
minimum_speed = self._boundary.min
34+
minimum_speed_outlet_stream = get_outlet_stream(speed=minimum_speed)
35+
if minimum_speed_outlet_stream is None:
36+
# rate is above maximum rate for minimum speed. Find the lowest minimum speed which gives a valid result
37+
minimum_speed = -maximize_x_given_boolean_condition_function(
38+
x_min=-maximum_speed,
39+
x_max=-minimum_speed,
40+
bool_func=lambda x: get_outlet_stream(speed=x) is not None,
41+
)
42+
minimum_speed_outlet_stream = get_outlet_stream(speed=minimum_speed)
43+
44+
if (
45+
minimum_speed_outlet_stream.pressure_bara
46+
<= self._target_pressure
47+
<= maximum_speed_outlet_stream.pressure_bara
48+
):
49+
# Solution 1, iterate on speed until target discharge pressure is found
50+
def f(speed: float) -> float:
51+
out = get_outlet_stream(speed=speed)
52+
assert out is not None, "What to do"
53+
return out.pressure_bara - self._target_pressure
54+
55+
speed = find_root(
56+
lower_bound=minimum_speed,
57+
upper_bound=maximum_speed,
58+
func=f,
59+
)
60+
return get_outlet_stream(speed=speed)
61+
elif self._target_pressure < minimum_speed_outlet_stream.pressure_bara:
62+
# Solution 2, target pressure is too low
63+
shaft.set_speed(minimum_speed)
64+
return minimum_speed_outlet_stream
65+
66+
# Solution 3, target discharge pressure is too high
67+
shaft.set_speed(maximum_speed)
68+
return maximum_speed_outlet_stream
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from libecalc.domain.process.compressor.core.train.utils.numeric_methods import find_root
2+
from libecalc.domain.process.process_solver.boundary import Boundary
3+
from libecalc.domain.process.process_solver.solver import Solver
4+
from libecalc.domain.process.process_system.process_system import ProcessSystem
5+
from libecalc.domain.process.value_objects.fluid_stream import FluidService, FluidStream
6+
7+
8+
class UpstreamChokeSolver(Solver):
9+
def __init__(self, outlet_pressure: float, fluid_service: FluidService, inlet_pressure_boundary: Boundary):
10+
self._outlet_pressure = outlet_pressure
11+
self._fluid_service = fluid_service
12+
self._inlet_pressure_boundary = inlet_pressure_boundary
13+
14+
def solve(self, process_system: ProcessSystem, inlet_stream: FluidStream) -> FluidStream | None:
15+
def get_outlet_pressure(inlet_pressure: float) -> float:
16+
choked_inlet_stream = self._fluid_service.create_stream_from_standard_rate(
17+
fluid_model=inlet_stream.fluid_model,
18+
pressure_bara=inlet_pressure,
19+
temperature_kelvin=inlet_stream.pressure_bara,
20+
standard_rate_m3_per_day=inlet_stream.standard_rate_sm3_per_day,
21+
)
22+
outlet_stream = process_system.propagate_stream(inlet_stream=choked_inlet_stream)
23+
assert outlet_stream is not None, "What to do"
24+
return outlet_stream.pressure_bara
25+
26+
choked_inlet_pressure = find_root(
27+
lower_bound=self._inlet_pressure_boundary.min,
28+
upper_bound=self._inlet_pressure_boundary.max,
29+
func=lambda x: get_outlet_pressure(inlet_pressure=x) - self._outlet_pressure,
30+
)
31+
32+
return self._fluid_service.create_stream_from_standard_rate(
33+
fluid_model=inlet_stream.fluid_model,
34+
pressure_bara=choked_inlet_pressure,
35+
temperature_kelvin=inlet_stream.pressure_bara,
36+
standard_rate_m3_per_day=inlet_stream.standard_rate_sm3_per_day,
37+
)

0 commit comments

Comments
 (0)