-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathfluid_service.py
More file actions
220 lines (178 loc) · 7.13 KB
/
Copy pathfluid_service.py
File metadata and controls
220 lines (178 loc) · 7.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
"""Stateless service interface for fluid thermodynamic operations.
This module defines the FluidService Protocol which provides
a clean abstraction layer between domain models and infrastructure.
All methods take fluid_model as parameter - the service has no bound state.
Designed to work as a singleton for global caching.
"""
from __future__ import annotations
import abc
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from libecalc.process.fluid_stream.fluid import Fluid
from libecalc.process.fluid_stream.fluid_model import FluidModel
from libecalc.process.fluid_stream.fluid_properties import FluidProperties
from libecalc.process.fluid_stream.fluid_stream import FluidStream
class FluidService(abc.ABC):
"""Stateless service interface for fluid thermodynamic operations.
All methods take fluid_model as parameter - service has no bound state.
Designed to work as a singleton for global caching.
"""
# === Flash Operations ===
@abc.abstractmethod
def flash_pt(
self,
fluid_model: FluidModel,
pressure_bara: float,
temperature_kelvin: float,
) -> FluidProperties:
"""TP flash returning fluid properties at specified conditions.
Args:
fluid_model: The fluid model (composition + EoS)
pressure_bara: Target pressure in bara
temperature_kelvin: Target temperature in Kelvin
Returns:
FluidProperties at the specified conditions.
"""
...
@abc.abstractmethod
def flash_ph(
self,
fluid_model: FluidModel,
pressure_bara: float,
target_enthalpy_joule_per_kg: float,
temperature_guess_kelvin: float | None = None,
) -> FluidProperties:
"""PH flash to target pressure and enthalpy.
Note: The target_enthalpy_joule_per_kg is reference-state dependent. Thermodynamic packages
may use arbitrary enthalpy reference states, so the caller must ensure that
target_enthalpy_joule_per_kg was computed from enthalpy values obtained from the same
EoS/fluid model session. Mixing enthalpy values from different thermodynamic
packages or sessions may produce incorrect results.
Args:
fluid_model: The fluid model (composition + EoS)
pressure_bara: Target pressure in bara
target_enthalpy_joule_per_kg: Target specific enthalpy in J/kg (must be from same EoS session)
temperature_guess_kelvin: Optional initial temperature for the PH flash. Implementations may use this
as a convergence aid by preconditioning the underlying thermodynamic system at
(pressure_bara, temperature_guess_kelvin) before solving the same PH target. Use it when the caller
has a physically relevant temperature estimate closer to the expected final state than the reference
fluid temperature. It should not change a correctly converged PH result, but may improve robustness
for difficult states by avoiding intermediate low-temperature/high-pressure guesses.
Returns:
FluidProperties at the specified conditions.
"""
...
@abc.abstractmethod
def remove_liquid(
self,
fluid: Fluid,
) -> Fluid:
"""Remove liquid phase from fluid, returning gas-phase only.
Performs a TP flash at the fluid's current conditions and extracts only
the gas phase. The returned Fluid will have updated composition reflecting
the gas-phase composition.
Args:
fluid: The fluid to remove liquid from
Returns:
New Fluid with liquid removed (gas-phase only). Composition will be
updated to reflect the gas-phase composition if liquid was present.
"""
...
# === Convenience Methods ===
@abc.abstractmethod
def create_fluid(
self,
fluid_model: FluidModel,
pressure_bara: float,
temperature_kelvin: float,
) -> Fluid:
"""Create a Fluid at specified conditions via TP flash.
Args:
fluid_model: The fluid model (composition + EoS)
pressure_bara: Target pressure in bara
temperature_kelvin: Target temperature in Kelvin
Returns:
New Fluid instance at the specified conditions.
"""
...
@abc.abstractmethod
def create_stream_from_standard_rate(
self,
fluid_model: FluidModel,
pressure_bara: float,
temperature_kelvin: float,
standard_rate_m3_per_day: float,
) -> FluidStream:
"""Create a fluid stream from standard volumetric rate.
Args:
fluid_model: The fluid model (composition + EoS)
pressure_bara: Target pressure in bara
temperature_kelvin: Target temperature in Kelvin
standard_rate_m3_per_day: Volumetric flow rate at standard conditions [Sm3/day]
Returns:
A FluidStream instance
"""
...
@abc.abstractmethod
def create_stream_from_mass_rate(
self,
fluid_model: FluidModel,
pressure_bara: float,
temperature_kelvin: float,
mass_rate_kg_per_h: float,
) -> FluidStream:
"""Create a fluid stream from mass rate.
Args:
fluid_model: The fluid model (composition + EoS)
pressure_bara: Target pressure in bara
temperature_kelvin: Target temperature in Kelvin
mass_rate_kg_per_h: Mass flow rate [kg/h]
Returns:
A FluidStream instance
"""
...
# === Rate Conversions ===
@abc.abstractmethod
def standard_rate_to_mass_rate(
self,
fluid_model: FluidModel,
standard_rate_m3_per_day: float,
) -> float:
"""Convert standard volumetric rate to mass rate.
Args:
fluid_model: The fluid model (composition + EoS)
standard_rate_m3_per_day: Volumetric flow rate at standard conditions [Sm3/day]
Returns:
Mass flow rate [kg/h]
"""
...
@abc.abstractmethod
def mass_rate_to_standard_rate(
self,
fluid_model: FluidModel,
mass_rate_kg_per_h: float,
) -> float:
"""Convert mass rate to standard volumetric rate.
Args:
fluid_model: The fluid model (composition + EoS)
mass_rate_kg_per_h: Mass flow rate [kg/h]
Returns:
Volumetric flow rate at standard conditions [Sm3/day]
"""
...
# === Critical Point ===
@abc.abstractmethod
def get_critical_point(
self,
fluid_model: FluidModel,
) -> tuple[float, float]:
"""Get the critical temperature and pressure for a fluid composition.
Uses the equation of state to compute the true mixture critical point.
Results should be cached by composition + EoS since the critical point
is independent of the stream's actual T and P.
Args:
fluid_model: The fluid model (composition + EoS)
Returns:
Tuple of (critical_temperature_kelvin, critical_pressure_bara)
"""
...