barotropy 0.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- barotropy/__init__.py +69 -0
- barotropy/barotropic_model.py +322 -0
- barotropy/fluent_automation/__init__.py +5 -0
- barotropy/fluent_automation/fluent_automation.py +970 -0
- barotropy/fluent_automation/fluent_plot_nozzle_data.py +183 -0
- barotropy/fluent_automation/stream_residuals.py +93 -0
- barotropy/fluent_automation/stream_transcript.py +24 -0
- barotropy/fluid_properties/__init__.py +5 -0
- barotropy/fluid_properties/core.py +1470 -0
- barotropy/fluid_properties/low_level_interface.py +918 -0
- barotropy/isentropic_nozzle.py +102 -0
- barotropy/math.py +446 -0
- barotropy/matlab/FluidCoolProp_2Phase.m +365 -0
- barotropy/matlab/compute_properties_isentrope.m +36 -0
- barotropy/matlab/compute_properties_metastable_Td.m +53 -0
- barotropy/matlab/compute_properties_metastable_ps.m +64 -0
- barotropy/matlab/compute_pseudocritical_line.m +27 -0
- barotropy/matlab/compute_pseudocritical_point.m +36 -0
- barotropy/matlab/compute_saturation_line.m +49 -0
- barotropy/matlab/compute_saturation_point_entropy.m +48 -0
- barotropy/matlab/compute_spinodal_line.m +65 -0
- barotropy/matlab/compute_spinodal_point.m +125 -0
- barotropy/matlab/compute_spinodal_point_entropy.m +192 -0
- barotropy/matlab/create_barotropic_model.m +281 -0
- barotropy/matlab/create_barotropic_model_segment.m +48 -0
- barotropy/matlab/evaluate_isentrope_polynomials.m +58 -0
- barotropy/matlab/export_fluent_expressions.m +122 -0
- barotropy/matlab/plot_barotropic_model.m +106 -0
- barotropy/matlab/plot_barotropic_polynomials.m +86 -0
- barotropy/matlab/plot_barotropic_process_ps.m +90 -0
- barotropy/matlab/plot_phase_diagram.m +83 -0
- barotropy/matlab/set_plot_options.m +70 -0
- barotropy/plot_options.py +260 -0
- barotropy/pysolver_view/__init__.py +25 -0
- barotropy/pysolver_view/nonlinear_system.py +614 -0
- barotropy/pysolver_view/nonlinear_system_problems.py +59 -0
- barotropy/pysolver_view/numerical_differentiation.py +236 -0
- barotropy/pysolver_view/optimization.py +988 -0
- barotropy/pysolver_view/optimization_problems.py +273 -0
- barotropy/pysolver_view/optimization_wrappers.py +509 -0
- barotropy/pysolver_view/pysolver_utilities.py +288 -0
- barotropy/sCO2_utilities.py +259 -0
- barotropy/utilities.py +780 -0
- barotropy-0.0.2.dist-info/LICENSE.md +9 -0
- barotropy-0.0.2.dist-info/METADATA +218 -0
- barotropy-0.0.2.dist-info/RECORD +47 -0
- barotropy-0.0.2.dist-info/WHEEL +4 -0
barotropy/__init__.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Highlight exception messages
|
|
2
|
+
# https://stackoverflow.com/questions/25109105/how-to-colorize-the-output-of-python-errors-in-the-gnome-terminal/52797444#52797444
|
|
3
|
+
try:
|
|
4
|
+
import IPython.core.ultratb
|
|
5
|
+
except ImportError:
|
|
6
|
+
# No IPython. Use default exception printing.
|
|
7
|
+
pass
|
|
8
|
+
else:
|
|
9
|
+
import sys
|
|
10
|
+
sys.excepthook = IPython.core.ultratb.ColorTB()
|
|
11
|
+
|
|
12
|
+
# Import subpackages
|
|
13
|
+
from .fluent_automation import *
|
|
14
|
+
from .fluid_properties import *
|
|
15
|
+
from .barotropic_model import *
|
|
16
|
+
|
|
17
|
+
# Import submodules
|
|
18
|
+
from .math import *
|
|
19
|
+
from .plot_options import *
|
|
20
|
+
from .sCO2_utilities import *
|
|
21
|
+
from .utilities import *
|
|
22
|
+
|
|
23
|
+
# Package info
|
|
24
|
+
__version__ = "0.0.2"
|
|
25
|
+
PACKAGE_NAME = "barotropy"
|
|
26
|
+
URL_GITHUB = "https://github.com/turbo-sim/barotropy"
|
|
27
|
+
URL_DOCS = "https://turbo-sim.github.io/barotropy/"
|
|
28
|
+
URL_DTU = "https://thermalpower.dtu.dk/"
|
|
29
|
+
BREAKLINE = 80 * "-"
|
|
30
|
+
|
|
31
|
+
def print_banner():
|
|
32
|
+
"""Prints a banner."""
|
|
33
|
+
banner = """
|
|
34
|
+
____ __
|
|
35
|
+
/ __ )____ __________ / /__________ ____ __ __
|
|
36
|
+
/ __ / __ `/ ___/ __ \/ __/ ___/ __ \/ __ \/ / / /
|
|
37
|
+
/ /_/ / /_/ / / / /_/ / /_/ / / /_/ / /_/ / /_/ /
|
|
38
|
+
/_____/\__,_/_/ \____/\__/_/ \____/ .___/\__, /
|
|
39
|
+
/_/ /____/
|
|
40
|
+
"""
|
|
41
|
+
# banner = """
|
|
42
|
+
# ____ ___ ____ ____ __________ ____ ______ __
|
|
43
|
+
# / __ )/ | / __ \/ __ \/_ __/ __ \/ __ \/ __ \ \/ /
|
|
44
|
+
# / __ / /| | / /_/ / / / / / / / /_/ / / / / /_/ /\ /
|
|
45
|
+
# / /_/ / ___ |/ _, _/ /_/ / / / / _, _/ /_/ / ____/ / /
|
|
46
|
+
# /_____/_/ |_/_/ |_|\____/ /_/ /_/ |_|\____/_/ /_/
|
|
47
|
+
|
|
48
|
+
# """
|
|
49
|
+
print(BREAKLINE)
|
|
50
|
+
print(banner)
|
|
51
|
+
print(BREAKLINE)
|
|
52
|
+
# https://manytools.org/hacker-tools/ascii-banner/
|
|
53
|
+
# Style: Sland
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def print_package_info():
|
|
57
|
+
"""Prints package information with predefined values."""
|
|
58
|
+
|
|
59
|
+
info = f""" Version: {__version__}
|
|
60
|
+
Repository: {URL_GITHUB}
|
|
61
|
+
Documentation: {URL_DOCS}"""
|
|
62
|
+
print_banner()
|
|
63
|
+
print(info)
|
|
64
|
+
print(BREAKLINE)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from scipy.integrate import solve_ivp
|
|
4
|
+
|
|
5
|
+
from . import fluid_properties as props
|
|
6
|
+
|
|
7
|
+
PROCESS_TYPES = ["polytropic", "adiabatic"]
|
|
8
|
+
MULTIPHASE_MODELS = ["blended", "blending", "equilibrium", "metastable"]
|
|
9
|
+
|
|
10
|
+
def barotropic_model_one_fluid(
|
|
11
|
+
fluid_name=None,
|
|
12
|
+
T_in=None,
|
|
13
|
+
p_in=None,
|
|
14
|
+
p_out=None,
|
|
15
|
+
efficiency=None,
|
|
16
|
+
number_of_points=None,
|
|
17
|
+
multiphase_model="blended",
|
|
18
|
+
calculation_type="polytropic",
|
|
19
|
+
tolerance=1e-6,
|
|
20
|
+
):
|
|
21
|
+
|
|
22
|
+
# Compute process as an adiabatic or polytropic expansion
|
|
23
|
+
if calculation_type == "adiabatic":
|
|
24
|
+
states = calculate_adiabatic_process(
|
|
25
|
+
fluid_name=fluid_name,
|
|
26
|
+
T_in=T_in,
|
|
27
|
+
p_in=p_in,
|
|
28
|
+
p_out=p_out,
|
|
29
|
+
efficiency=efficiency,
|
|
30
|
+
# multiphase_model=multiphase_model,
|
|
31
|
+
number_of_points=number_of_points,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
elif calculation_type == "polytropic":
|
|
35
|
+
states, _ = polytropic_process_one_fluid(
|
|
36
|
+
fluid_name=fluid_name,
|
|
37
|
+
T_in=T_in,
|
|
38
|
+
p_in=p_in,
|
|
39
|
+
p_out=p_out,
|
|
40
|
+
efficiency=efficiency,
|
|
41
|
+
multiphase_model=multiphase_model,
|
|
42
|
+
number_of_points=number_of_points,
|
|
43
|
+
tolerance=tolerance,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
else:
|
|
47
|
+
options = ", ".join(f"'{k}'" for k in PROCESS_TYPES)
|
|
48
|
+
raise ValueError(
|
|
49
|
+
f"Invalid process type '{calculation_type}'. Available options: {options}"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
return states
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def polytropic_process_one_fluid(
|
|
56
|
+
fluid_name,
|
|
57
|
+
T_in,
|
|
58
|
+
p_in,
|
|
59
|
+
p_out,
|
|
60
|
+
efficiency,
|
|
61
|
+
multiphase_model="blended",
|
|
62
|
+
q_onset=0.00,
|
|
63
|
+
q_transition=0.01,
|
|
64
|
+
tolerance=1e-6,
|
|
65
|
+
number_of_points=None,
|
|
66
|
+
):
|
|
67
|
+
""" """
|
|
68
|
+
|
|
69
|
+
# Differential equation defining the polytropic process
|
|
70
|
+
def odefun(t, y):
|
|
71
|
+
# Keep variables in memory to use previous state as initial guess
|
|
72
|
+
nonlocal rho_guess, T_guess
|
|
73
|
+
|
|
74
|
+
# Rename input variables
|
|
75
|
+
p = t
|
|
76
|
+
(h,) = y
|
|
77
|
+
|
|
78
|
+
# Compute equilibrium state
|
|
79
|
+
state_eq = fluid.set_state(props.HmassP_INPUTS, h, p, generalize_quality=True)
|
|
80
|
+
|
|
81
|
+
# Compute metastable state
|
|
82
|
+
state_meta = fluid.set_state_metastable("p", p, "hmass", h, rho_guess, T_guess)
|
|
83
|
+
rho_guess = state_meta.rho # Update guess for the next iteration
|
|
84
|
+
T_guess = state_meta.T # Update guess for the next iteration
|
|
85
|
+
|
|
86
|
+
# Determine the actual thermodynamic state
|
|
87
|
+
if multiphase_model == "equilibrium":
|
|
88
|
+
state = state_eq
|
|
89
|
+
elif multiphase_model == "metastable":
|
|
90
|
+
state = state_meta
|
|
91
|
+
elif multiphase_model == "blended" or multiphase_model == "blending":
|
|
92
|
+
x = 1 - (state_eq["Q"] - q_onset) / q_transition
|
|
93
|
+
# sigma = sigmoid_smoothstep(x)
|
|
94
|
+
sigma = sigmoid_smootherstep(x)
|
|
95
|
+
state = {}
|
|
96
|
+
for key in state_eq.keys():
|
|
97
|
+
state[key] = state_eq[key]*(1-sigma) + state_meta[key]*sigma
|
|
98
|
+
else:
|
|
99
|
+
options = ", ".join(f"'{k}'" for k in MULTIPHASE_MODELS)
|
|
100
|
+
raise ValueError(
|
|
101
|
+
f"Invalid multiphase model {multiphase_model}'. Available options: {options}"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Compute right-hand-side of the ODE
|
|
105
|
+
dhdp = efficiency / state["rho"]
|
|
106
|
+
|
|
107
|
+
return dhdp, state
|
|
108
|
+
|
|
109
|
+
# Compute inlet state
|
|
110
|
+
fluid = props.Fluid(name=fluid_name, backend="HEOS", exceptions=True)
|
|
111
|
+
state_in = fluid.set_state(props.PT_INPUTS, p_in, T_in)
|
|
112
|
+
rho_guess = state_in.rho
|
|
113
|
+
T_guess = state_in.T
|
|
114
|
+
|
|
115
|
+
# Solve polytropic expansion differential equation
|
|
116
|
+
solution = solve_ivp(
|
|
117
|
+
lambda p, h: odefun(p, h)[0],
|
|
118
|
+
[state_in.p, p_out],
|
|
119
|
+
[state_in.h],
|
|
120
|
+
t_eval=np.linspace(state_in.p, p_out, number_of_points)
|
|
121
|
+
if number_of_points
|
|
122
|
+
else None,
|
|
123
|
+
method="LSODA", # "RK45", "Radau", "LSODA"
|
|
124
|
+
rtol=tolerance,
|
|
125
|
+
atol=tolerance,
|
|
126
|
+
)
|
|
127
|
+
if not solution.success:
|
|
128
|
+
raise Exception(solution.message)
|
|
129
|
+
|
|
130
|
+
# Postprocess solution
|
|
131
|
+
states = postprocess_ode(solution.t, solution.y, odefun)
|
|
132
|
+
|
|
133
|
+
return states, solution
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def calculate_adiabatic_process(
|
|
137
|
+
fluid_name,
|
|
138
|
+
T_in,
|
|
139
|
+
p_in,
|
|
140
|
+
p_out,
|
|
141
|
+
efficiency,
|
|
142
|
+
number_of_points=None,
|
|
143
|
+
):
|
|
144
|
+
# Compute inlet state
|
|
145
|
+
fluid = props.Fluid(name=fluid_name, backend="HEOS", exceptions=True)
|
|
146
|
+
state_in = fluid.set_state(props.PT_INPUTS, p_in, T_in)
|
|
147
|
+
|
|
148
|
+
# Compute outlet state
|
|
149
|
+
state_out_s = fluid.set_state(props.PSmass_INPUTS, p_out, state_in.s)
|
|
150
|
+
h_out = state_in.h - efficiency * (state_in.h - state_out_s.h)
|
|
151
|
+
state_out = fluid.set_state(props.HmassP_INPUTS, h_out, p_out)
|
|
152
|
+
|
|
153
|
+
# Prepare list of states
|
|
154
|
+
if number_of_points is not None:
|
|
155
|
+
p_array = np.logspace(np.log10(state_in.p), np.log10(state_out.p), number_of_points)
|
|
156
|
+
s_array = np.linspace(state_in.s, state_out.s, number_of_points)
|
|
157
|
+
states = []
|
|
158
|
+
for p, s in zip(p_array, s_array):
|
|
159
|
+
states.append(fluid.set_state(props.PSmass_INPUTS, p, s))
|
|
160
|
+
else:
|
|
161
|
+
states = [state_in, state_out]
|
|
162
|
+
|
|
163
|
+
return props.states_to_dict(states)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def postprocess_ode(t, y, ode_handle):
|
|
167
|
+
"""
|
|
168
|
+
Post-processes the output of an ordinary differential equation (ODE) solver.
|
|
169
|
+
|
|
170
|
+
This function takes the time points and corresponding ODE solution matrix,
|
|
171
|
+
and for each time point, it calls a user-defined ODE handling function to
|
|
172
|
+
process the state of the ODE system. It collects the results into a
|
|
173
|
+
dictionary where each key corresponds to a state variable and the values
|
|
174
|
+
are numpy arrays of that state variable at each integration step
|
|
175
|
+
|
|
176
|
+
Parameters
|
|
177
|
+
----------
|
|
178
|
+
t : array_like
|
|
179
|
+
Integration points at which the ODE was solved, as a 1D numpy array.
|
|
180
|
+
y : array_like
|
|
181
|
+
The solution of the ODE system, as a 2D numpy array with shape (n,m) where
|
|
182
|
+
n is the number of points and m is the number of state variables.
|
|
183
|
+
ode_handle : callable
|
|
184
|
+
A function that takes in a integration point and state vector and returns a tuple,
|
|
185
|
+
where the first element is ignored (can be None) and the second element
|
|
186
|
+
is a dictionary representing the processed state of the system.
|
|
187
|
+
|
|
188
|
+
Returns
|
|
189
|
+
-------
|
|
190
|
+
ode_out : dict
|
|
191
|
+
A dictionary where each key corresponds to a state variable and each value
|
|
192
|
+
is a numpy array containing the values of that state variable at each integration step.
|
|
193
|
+
"""
|
|
194
|
+
# Initialize ode_out as a dictionary
|
|
195
|
+
ode_out = {}
|
|
196
|
+
for t_i, y_i in zip(t, y.T):
|
|
197
|
+
_, out = ode_handle(t_i, y_i)
|
|
198
|
+
|
|
199
|
+
for key, value in out.items():
|
|
200
|
+
# Initialize with an empty list
|
|
201
|
+
if key not in ode_out:
|
|
202
|
+
ode_out[key] = []
|
|
203
|
+
# Append the value to list of current key
|
|
204
|
+
ode_out[key].append(value)
|
|
205
|
+
|
|
206
|
+
# Convert lists to numpy arrays
|
|
207
|
+
for key in ode_out:
|
|
208
|
+
ode_out[key] = np.array(ode_out[key])
|
|
209
|
+
|
|
210
|
+
return ode_out
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
# def sigmoid_hyperbolic(x, x0=0.5, alpha=1):
|
|
214
|
+
# r"""
|
|
215
|
+
# Compute the sigmoid hyperbolic function.
|
|
216
|
+
|
|
217
|
+
# This function calculates a sigmoid function based on the hyperbolic tangent.
|
|
218
|
+
# The formula is given by:
|
|
219
|
+
|
|
220
|
+
# .. math::
|
|
221
|
+
# \sigma(x) = \frac{1 + \tanh\left(\frac{x - x_0}{\alpha}\right)}{2}
|
|
222
|
+
|
|
223
|
+
# Parameters
|
|
224
|
+
# ----------
|
|
225
|
+
# x : array_like
|
|
226
|
+
# Input data.
|
|
227
|
+
# x0 : float, optional
|
|
228
|
+
# Center of the sigmoid function. Default is 0.5.
|
|
229
|
+
# alpha : float, optional
|
|
230
|
+
# Scale parameter. Default is 0.1.
|
|
231
|
+
|
|
232
|
+
# Returns
|
|
233
|
+
# -------
|
|
234
|
+
# array_like
|
|
235
|
+
# Output of the sigmoid hyperbolic function.
|
|
236
|
+
|
|
237
|
+
# """
|
|
238
|
+
# x = np.array(x) # Ensure x is a NumPy array for vectorized operations
|
|
239
|
+
# sigma = (1 + np.tanh((x - x0) / alpha)) / 2
|
|
240
|
+
# return sigma
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
# def sigmoid_rational(x, n, m):
|
|
244
|
+
# r"""
|
|
245
|
+
# Compute the sigmoid rational function.
|
|
246
|
+
|
|
247
|
+
# This function calculates a sigmoid function using an algebraic approach based on a rational function.
|
|
248
|
+
# The formula is given by:
|
|
249
|
+
|
|
250
|
+
# .. math::
|
|
251
|
+
# \sigma(x) = \frac{x^n}{x^n + (1-x)^m}
|
|
252
|
+
|
|
253
|
+
# Parameters
|
|
254
|
+
# ----------
|
|
255
|
+
# x : array_like
|
|
256
|
+
# Input data.
|
|
257
|
+
# n : int
|
|
258
|
+
# Power of the numerator.
|
|
259
|
+
# m : int
|
|
260
|
+
# Power of the denominator.
|
|
261
|
+
|
|
262
|
+
# Returns
|
|
263
|
+
# -------
|
|
264
|
+
# array_like
|
|
265
|
+
# Output of the sigmoid algebraic function.
|
|
266
|
+
|
|
267
|
+
# """
|
|
268
|
+
# x = np.array(x) # Ensure x is a NumPy array for vectorized operations
|
|
269
|
+
# x = np.where(x < 0, 0, x) # Set x to 0 where x < 0
|
|
270
|
+
# x = np.where(x > 1, 1, x) # Set x to 1 where x > 1
|
|
271
|
+
# sigma = x**n / (x**n + (1 - x) ** m)
|
|
272
|
+
# return sigma
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
# def sigmoid_smoothstep(x):
|
|
276
|
+
# """
|
|
277
|
+
# Compute the smooth step function.
|
|
278
|
+
|
|
279
|
+
# This function calculates a smooth step function with zero first-order
|
|
280
|
+
# derivatives at the endpoints. More information available at:
|
|
281
|
+
# https://resources.wolframcloud.com/FunctionRepository/resources/SmoothStep/
|
|
282
|
+
|
|
283
|
+
# Parameters
|
|
284
|
+
# ----------
|
|
285
|
+
# x : array_like
|
|
286
|
+
# Input data.
|
|
287
|
+
|
|
288
|
+
# Returns
|
|
289
|
+
# -------
|
|
290
|
+
# array_like
|
|
291
|
+
# Output of the sigmoid smoothstep function.
|
|
292
|
+
|
|
293
|
+
# """
|
|
294
|
+
# x = np.array(x) # Ensure x is a NumPy array for vectorized operations
|
|
295
|
+
# x = np.where(x < 0, 0, x) # Set x to 0 where x < 0
|
|
296
|
+
# x = np.where(x > 1, 1, x) # Set x to 1 where x > 1
|
|
297
|
+
# return x**2 * (3 - 2 * x)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
# def sigmoid_smootherstep(x):
|
|
301
|
+
# """
|
|
302
|
+
# Compute the smoother step function.
|
|
303
|
+
|
|
304
|
+
# This function calculates a smoother step function with zero second-order
|
|
305
|
+
# derivatives at the endpoints. It is a modification of the smoothstep
|
|
306
|
+
# function to provide smoother transitions.
|
|
307
|
+
|
|
308
|
+
# Parameters
|
|
309
|
+
# ----------
|
|
310
|
+
# x : array_like
|
|
311
|
+
# Input data.
|
|
312
|
+
|
|
313
|
+
# Returns
|
|
314
|
+
# -------
|
|
315
|
+
# array_like
|
|
316
|
+
# Output of the sigmoid smootherstep function.
|
|
317
|
+
|
|
318
|
+
# """
|
|
319
|
+
# x = np.array(x) # Ensure x is a NumPy array for vectorized operations
|
|
320
|
+
# x = np.where(x < 0, 0, x) # Set x to 0 where x < 0
|
|
321
|
+
# x = np.where(x > 1, 1, x) # Set x to 1 where x > 1
|
|
322
|
+
# return 6 * x**5 - 15 * x**4 + 10 * x**3
|