rock-physics-open 0.0__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.
Potentially problematic release.
This version of rock-physics-open might be problematic. Click here for more details.
- rock_physics_open/__init__.py +0 -0
- rock_physics_open/equinor_utilities/__init__.py +0 -0
- rock_physics_open/equinor_utilities/anisotropy.py +162 -0
- rock_physics_open/equinor_utilities/classification_functions/__init__.py +17 -0
- rock_physics_open/equinor_utilities/classification_functions/class_stats.py +58 -0
- rock_physics_open/equinor_utilities/classification_functions/lin_class.py +47 -0
- rock_physics_open/equinor_utilities/classification_functions/mahal_class.py +56 -0
- rock_physics_open/equinor_utilities/classification_functions/norm_class.py +65 -0
- rock_physics_open/equinor_utilities/classification_functions/poly_class.py +40 -0
- rock_physics_open/equinor_utilities/classification_functions/post_prob.py +26 -0
- rock_physics_open/equinor_utilities/classification_functions/two_step_classification.py +46 -0
- rock_physics_open/equinor_utilities/conversions.py +10 -0
- rock_physics_open/equinor_utilities/gen_utilities/__init__.py +11 -0
- rock_physics_open/equinor_utilities/gen_utilities/dict_to_float.py +33 -0
- rock_physics_open/equinor_utilities/gen_utilities/dim_check_vector.py +83 -0
- rock_physics_open/equinor_utilities/gen_utilities/filter_input.py +126 -0
- rock_physics_open/equinor_utilities/gen_utilities/filter_output.py +78 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/__init__.py +14 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/dummy_vars.py +42 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/exponential_model.py +119 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/import_ml_models.py +61 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/run_regression.py +151 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/sigmoidal_model.py +188 -0
- rock_physics_open/equinor_utilities/snapshot_test_utilities/__init__.py +10 -0
- rock_physics_open/equinor_utilities/snapshot_test_utilities/compare_snapshots.py +145 -0
- rock_physics_open/equinor_utilities/snapshot_test_utilities/snapshots.py +54 -0
- rock_physics_open/equinor_utilities/std_functions/__init__.py +43 -0
- rock_physics_open/equinor_utilities/std_functions/backus_ave.py +53 -0
- rock_physics_open/equinor_utilities/std_functions/dvorkin_nur.py +69 -0
- rock_physics_open/equinor_utilities/std_functions/gassmann.py +140 -0
- rock_physics_open/equinor_utilities/std_functions/hashin_shtrikman.py +195 -0
- rock_physics_open/equinor_utilities/std_functions/hertz_mindlin.py +43 -0
- rock_physics_open/equinor_utilities/std_functions/moduli_velocity.py +51 -0
- rock_physics_open/equinor_utilities/std_functions/reflection_eq.py +98 -0
- rock_physics_open/equinor_utilities/std_functions/rho.py +59 -0
- rock_physics_open/equinor_utilities/std_functions/voigt_reuss_hill.py +128 -0
- rock_physics_open/equinor_utilities/std_functions/walton.py +38 -0
- rock_physics_open/equinor_utilities/std_functions/wood_brie.py +77 -0
- rock_physics_open/equinor_utilities/various_utilities/Equinor_logo.gif +0 -0
- rock_physics_open/equinor_utilities/various_utilities/Equinor_logo.ico +0 -0
- rock_physics_open/equinor_utilities/various_utilities/__init__.py +24 -0
- rock_physics_open/equinor_utilities/various_utilities/display_result_statistics.py +83 -0
- rock_physics_open/equinor_utilities/various_utilities/gassmann_dry_mod.py +37 -0
- rock_physics_open/equinor_utilities/various_utilities/gassmann_mod.py +37 -0
- rock_physics_open/equinor_utilities/various_utilities/gassmann_sub_mod.py +53 -0
- rock_physics_open/equinor_utilities/various_utilities/hs_average.py +40 -0
- rock_physics_open/equinor_utilities/various_utilities/pressure.py +88 -0
- rock_physics_open/equinor_utilities/various_utilities/reflectivity.py +85 -0
- rock_physics_open/equinor_utilities/various_utilities/timeshift.py +91 -0
- rock_physics_open/equinor_utilities/various_utilities/vp_vs_rho_set_statistics.py +154 -0
- rock_physics_open/equinor_utilities/various_utilities/vrh_3_min.py +61 -0
- rock_physics_open/fluid_models/__init__.py +9 -0
- rock_physics_open/fluid_models/brine_model/__init__.py +5 -0
- rock_physics_open/fluid_models/brine_model/brine_properties.py +143 -0
- rock_physics_open/fluid_models/gas_model/__init__.py +5 -0
- rock_physics_open/fluid_models/gas_model/gas_properties.py +277 -0
- rock_physics_open/fluid_models/oil_model/__init__.py +5 -0
- rock_physics_open/fluid_models/oil_model/dead_oil_density.py +60 -0
- rock_physics_open/fluid_models/oil_model/dead_oil_velocity.py +28 -0
- rock_physics_open/fluid_models/oil_model/live_oil_density.py +79 -0
- rock_physics_open/fluid_models/oil_model/live_oil_velocity.py +24 -0
- rock_physics_open/fluid_models/oil_model/oil_bubble_point.py +69 -0
- rock_physics_open/fluid_models/oil_model/oil_properties.py +114 -0
- rock_physics_open/sandstone_models/__init__.py +57 -0
- rock_physics_open/sandstone_models/cemented_shalysand_sandyshale_models.py +304 -0
- rock_physics_open/sandstone_models/constant_cement_models.py +204 -0
- rock_physics_open/sandstone_models/constant_cement_optimisation.py +122 -0
- rock_physics_open/sandstone_models/contact_cement_model.py +138 -0
- rock_physics_open/sandstone_models/curvefit_sandstone_models.py +143 -0
- rock_physics_open/sandstone_models/friable_models.py +178 -0
- rock_physics_open/sandstone_models/friable_optimisation.py +112 -0
- rock_physics_open/sandstone_models/friable_shalysand_sandyshale_models.py +235 -0
- rock_physics_open/sandstone_models/patchy_cement_fluid_substitution_model.py +477 -0
- rock_physics_open/sandstone_models/patchy_cement_model.py +286 -0
- rock_physics_open/sandstone_models/patchy_cement_optimisation.py +251 -0
- rock_physics_open/sandstone_models/unresolved_cemented_sandshale_models.py +134 -0
- rock_physics_open/sandstone_models/unresolved_friable_sandshale_models.py +126 -0
- rock_physics_open/shale_models/__init__.py +19 -0
- rock_physics_open/shale_models/dem.py +174 -0
- rock_physics_open/shale_models/dem_dual_por.py +61 -0
- rock_physics_open/shale_models/kus_tok.py +59 -0
- rock_physics_open/shale_models/multi_sca.py +133 -0
- rock_physics_open/shale_models/pq.py +102 -0
- rock_physics_open/shale_models/sca.py +90 -0
- rock_physics_open/shale_models/shale4_mineral.py +147 -0
- rock_physics_open/shale_models/shale4_mineral_dem_overlay.py +92 -0
- rock_physics_open/span_wagner/__init__.py +5 -0
- rock_physics_open/span_wagner/co2_properties.py +438 -0
- rock_physics_open/span_wagner/coefficients.py +165 -0
- rock_physics_open/span_wagner/equations.py +104 -0
- rock_physics_open/span_wagner/tables/__init__.py +0 -0
- rock_physics_open/span_wagner/tables/carbon_dioxide_density.npz +0 -0
- rock_physics_open/span_wagner/tables/lookup_table.py +33 -0
- rock_physics_open/t_matrix_models/Equinor_logo.ico +0 -0
- rock_physics_open/t_matrix_models/__init__.py +45 -0
- rock_physics_open/t_matrix_models/carbonate_pressure_substitution.py +124 -0
- rock_physics_open/t_matrix_models/curvefit_t_matrix_exp.py +124 -0
- rock_physics_open/t_matrix_models/curvefit_t_matrix_min.py +86 -0
- rock_physics_open/t_matrix_models/opt_subst_utilities.py +415 -0
- rock_physics_open/t_matrix_models/parse_t_matrix_inputs.py +297 -0
- rock_physics_open/t_matrix_models/run_t_matrix.py +243 -0
- rock_physics_open/t_matrix_models/t_matrix_C.py +210 -0
- rock_physics_open/t_matrix_models/t_matrix_opt_fluid_sub_exp.py +137 -0
- rock_physics_open/t_matrix_models/t_matrix_opt_fluid_sub_petec.py +163 -0
- rock_physics_open/t_matrix_models/t_matrix_opt_forward_model_exp.py +72 -0
- rock_physics_open/t_matrix_models/t_matrix_opt_forward_model_min.py +86 -0
- rock_physics_open/t_matrix_models/t_matrix_parameter_optimisation_exp.py +172 -0
- rock_physics_open/t_matrix_models/t_matrix_parameter_optimisation_min.py +159 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/__init__.py +12 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/array_functions.py +75 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_c_eff.py +163 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_isolated.py +95 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_kd.py +40 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_kd_eff.py +116 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_kd_uuv.py +18 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_pressure.py +140 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_t.py +71 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_td.py +42 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_theta.py +43 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_x.py +33 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_z.py +50 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/check_and_tile.py +43 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/g_tensor.py +140 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/iso_av.py +60 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/iso_ave_all.py +55 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/pressure_input.py +44 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/t_matrix_vec.py +278 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/velocity_vti_angles.py +81 -0
- rock_physics_open/t_matrix_models/tmatrix_python.dll +0 -0
- rock_physics_open/t_matrix_models/tmatrix_python.so +0 -0
- rock_physics_open/ternary_plots/__init__.py +3 -0
- rock_physics_open/ternary_plots/gen_ternary_plot.py +73 -0
- rock_physics_open/ternary_plots/shale_prop_ternary.py +337 -0
- rock_physics_open/ternary_plots/ternary_patches.py +277 -0
- rock_physics_open/ternary_plots/ternary_plot_utilities.py +197 -0
- rock_physics_open/ternary_plots/unconventionals_ternary.py +75 -0
- rock_physics_open/version.py +21 -0
- rock_physics_open-0.0.dist-info/METADATA +92 -0
- rock_physics_open-0.0.dist-info/RECORD +142 -0
- rock_physics_open-0.0.dist-info/WHEEL +5 -0
- rock_physics_open-0.0.dist-info/licenses/LICENSE +165 -0
- rock_physics_open-0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pkg_resources
|
|
3
|
+
import scipy.optimize
|
|
4
|
+
from scipy.interpolate import RegularGridInterpolator
|
|
5
|
+
|
|
6
|
+
from rock_physics_open.equinor_utilities.conversions import celsius_to_kelvin
|
|
7
|
+
from rock_physics_open.span_wagner import equations
|
|
8
|
+
from rock_physics_open.span_wagner.coefficients import (
|
|
9
|
+
a0,
|
|
10
|
+
theta0,
|
|
11
|
+
)
|
|
12
|
+
from rock_physics_open.span_wagner.tables.lookup_table import (
|
|
13
|
+
load_lookup_table_interpolator,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
# Constants
|
|
17
|
+
CO2_GAS_CONSTANT = 0.1889241 * 1e3 # J / kg K
|
|
18
|
+
CO2_CRITICAL_TEMPERATURE = 304.1282 # K
|
|
19
|
+
CO2_CRITICAL_DENSITY = 467.6 # kg / m^3
|
|
20
|
+
CO2_CRITICAL_PRESSURE = 7.3773 # MPa
|
|
21
|
+
CO2_TRIPLE_TEMPERATURE = 216.592 # K
|
|
22
|
+
CO2_TRIPLE_PRESSURE = 0.51795 # MPa
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def co2_properties(temp, pres):
|
|
26
|
+
"""
|
|
27
|
+
CO2 properties are estimated according to Span & Wagner's equation of state model.
|
|
28
|
+
References
|
|
29
|
+
----------
|
|
30
|
+
R. Span and W. Wagner: A new equation of state for carbon dioxide covering the
|
|
31
|
+
fluid region from the triple point temperature to 1100K at pressures up to
|
|
32
|
+
800 MPa.
|
|
33
|
+
J. Phys. Chem. Ref. Data, Vol. 25, No. 6, 1996, pp 1509 - 1596
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
temp: np.ndarray
|
|
38
|
+
Temperature [°C]
|
|
39
|
+
pres: np.ndarray
|
|
40
|
+
Pressure [Pa]
|
|
41
|
+
|
|
42
|
+
Returns
|
|
43
|
+
-------
|
|
44
|
+
tuple
|
|
45
|
+
vel_co2, den_co2, k_co2 : (np.ndarray, np.ndarray, np.ndarray).
|
|
46
|
+
vel_co2: velocity [m/s], den_co2: density [kg/m^3], k_co2: bulk modulus [Pa].
|
|
47
|
+
"""
|
|
48
|
+
abs_temp = celsius_to_kelvin(temp)
|
|
49
|
+
pres_mpa = pres * 1.0e-6
|
|
50
|
+
den_co2 = carbon_dioxide_density(abs_temp, pres_mpa)
|
|
51
|
+
k_co2 = carbon_dioxide_bulk_modulus(abs_temp, den_co2)
|
|
52
|
+
vel_co2 = (k_co2 / den_co2) ** 0.5
|
|
53
|
+
|
|
54
|
+
return vel_co2, den_co2, k_co2
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def co2_helmholtz_energy(delta, tau, dd, dt):
|
|
58
|
+
"""
|
|
59
|
+
Helmholtz energy as defined by equation 6.1 in Span & Wagner [2]
|
|
60
|
+
|
|
61
|
+
:param delta: Reduced density, unit-less. That is, density / CO2_CRITICAL_DENSITY.
|
|
62
|
+
:param tau: Inverse reduced temperature, unit-less. That is, CO2_CRITICAL_TEMPERATURE / (absolute) temperature
|
|
63
|
+
:param dd: Degree of derivation wrt. delta. Integer between 0 and 2.
|
|
64
|
+
:param dt: Degree of derivation wrt. tau. Integer between 0 and 2, as long as (dt + dd < 3)
|
|
65
|
+
"""
|
|
66
|
+
return ideal_gas_helmholtz_energy(
|
|
67
|
+
delta, tau, dd, dt
|
|
68
|
+
) + co2_residual_helmholtz_energy(delta, tau, dd, dt)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def ideal_gas_helmholtz_energy(delta, tau, dd, dt):
|
|
72
|
+
"""
|
|
73
|
+
Helmholtz energy from ideal gas behavior as defined by equation 2.3 in Span & Wagner [2]. See function
|
|
74
|
+
co2_helmholtz_energy for argument documentation.
|
|
75
|
+
"""
|
|
76
|
+
# Adjust array shapes
|
|
77
|
+
tau = np.asarray(tau)
|
|
78
|
+
delta = np.asarray(delta)
|
|
79
|
+
return_scalar = tau.ndim == 0
|
|
80
|
+
tau2 = tau.reshape(-1, 1) # Needed for array-based sums
|
|
81
|
+
|
|
82
|
+
if dt == dd == 0:
|
|
83
|
+
_sum = np.sum(a0[0, 3:] * np.log(1 - np.exp(-theta0 * tau2)), axis=-1)
|
|
84
|
+
result = (
|
|
85
|
+
np.log(delta) + a0[0, 0] + a0[0, 1] * tau + a0[0, 2] * np.log(tau) + _sum
|
|
86
|
+
)
|
|
87
|
+
elif dt == 1 and dd == 0:
|
|
88
|
+
_sum = np.sum(
|
|
89
|
+
a0[0, 3:] * theta0 * ((1 - np.exp(-theta0 * tau2)) ** -1 - 1), axis=-1
|
|
90
|
+
)
|
|
91
|
+
result = a0[0, 1] + a0[0, 2] / tau + _sum
|
|
92
|
+
elif dt == 0 and dd == 1:
|
|
93
|
+
return 1 / delta
|
|
94
|
+
elif dt == 2 and dd == 0:
|
|
95
|
+
_sum = np.sum(
|
|
96
|
+
a0[0, 3:]
|
|
97
|
+
* theta0**2
|
|
98
|
+
* np.exp(-theta0 * tau2)
|
|
99
|
+
* (1 - np.exp(-theta0 * tau2)) ** -2,
|
|
100
|
+
axis=-1,
|
|
101
|
+
)
|
|
102
|
+
result = -a0[0, 2] / tau**2 - _sum
|
|
103
|
+
elif dt == 0 and dd == 2:
|
|
104
|
+
return -1 / delta**2
|
|
105
|
+
elif dt == 1 and dd == 1:
|
|
106
|
+
return 0
|
|
107
|
+
else:
|
|
108
|
+
raise ValueError
|
|
109
|
+
if return_scalar:
|
|
110
|
+
return result[0]
|
|
111
|
+
return result
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def co2_residual_helmholtz_energy(delta, tau, dd, dt):
|
|
115
|
+
"""
|
|
116
|
+
Residual part of Helmholtz energy as defined by the equation in Table 32 of Span & Wagner [2]. See
|
|
117
|
+
co2_helmholtz_energy for argument documentation.
|
|
118
|
+
"""
|
|
119
|
+
tau = np.asarray(tau)
|
|
120
|
+
delta = np.asarray(delta)
|
|
121
|
+
return_scalar = (tau.ndim == 0) & (delta.ndim == 0)
|
|
122
|
+
tau = tau.reshape(-1, 1)
|
|
123
|
+
delta = delta.reshape(-1, 1)
|
|
124
|
+
# tau == 1.0 or delta == 1.0 leads to numerically invalid results. The values are nudged to avoid nan output.
|
|
125
|
+
tau[tau == 1.0] -= 1e-15
|
|
126
|
+
delta[delta == 1.0] -= 1e-15
|
|
127
|
+
|
|
128
|
+
res = equations.residual_helmholtz_energy(delta, tau, dd, dt)
|
|
129
|
+
|
|
130
|
+
if return_scalar:
|
|
131
|
+
return res[0]
|
|
132
|
+
return res
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def carbon_dioxide_pressure(
|
|
136
|
+
absolute_temperature, density, d_density=0, d_temperature=0, isentropic=False
|
|
137
|
+
):
|
|
138
|
+
"""
|
|
139
|
+
CO2 pressure (MPa) as given by Table 3 of Span & Wagner [2]
|
|
140
|
+
|
|
141
|
+
:param absolute_temperature: Temperature in K.
|
|
142
|
+
:param density: CO2 density (kg / m^3).
|
|
143
|
+
:param d_density: Degree of derivation wrt. density.
|
|
144
|
+
:param d_temperature: Degree of derivation wrt. temperature.
|
|
145
|
+
:param isentropic: Correction for isentropic conditions. Relevant primarily for isentropic bulk modulus
|
|
146
|
+
"""
|
|
147
|
+
tau = CO2_CRITICAL_TEMPERATURE / absolute_temperature
|
|
148
|
+
delta = density / CO2_CRITICAL_DENSITY
|
|
149
|
+
if d_temperature != 0:
|
|
150
|
+
raise ValueError(f"d_temperature must be 0, but was {d_temperature}")
|
|
151
|
+
if d_density == 0:
|
|
152
|
+
return (
|
|
153
|
+
density
|
|
154
|
+
* CO2_GAS_CONSTANT
|
|
155
|
+
* absolute_temperature
|
|
156
|
+
* (1 + delta * co2_residual_helmholtz_energy(delta, tau, 1, 0))
|
|
157
|
+
/ 1e6
|
|
158
|
+
)
|
|
159
|
+
if d_density == 1:
|
|
160
|
+
first = 2 * delta * co2_residual_helmholtz_energy(delta, tau, 1, 0)
|
|
161
|
+
second = delta**2 * co2_residual_helmholtz_energy(delta, tau, 2, 0)
|
|
162
|
+
if isentropic is False:
|
|
163
|
+
third = 0
|
|
164
|
+
else:
|
|
165
|
+
# See Table 3 of Span & Wagner (speed of sound)
|
|
166
|
+
nom = (
|
|
167
|
+
1
|
|
168
|
+
+ delta * co2_residual_helmholtz_energy(delta, tau, 1, 0)
|
|
169
|
+
- delta * tau * co2_residual_helmholtz_energy(delta, tau, 1, 1)
|
|
170
|
+
) ** 2
|
|
171
|
+
den = tau**2 * (co2_helmholtz_energy(delta, tau, 0, 2))
|
|
172
|
+
third = -nom / den
|
|
173
|
+
return (
|
|
174
|
+
absolute_temperature * CO2_GAS_CONSTANT * (1 + first + second + third) / 1e6
|
|
175
|
+
)
|
|
176
|
+
return None
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def saturated_liquid_density(absolute_temperature):
|
|
180
|
+
"""
|
|
181
|
+
Saturated liquid density as defined by equation 3.14 of Span & Wagner [2]
|
|
182
|
+
|
|
183
|
+
:param absolute_temperature: Absolute temperature in K. Should satisfy:
|
|
184
|
+
CO2_TRIPLE_TEMPERATURE < absolute_temperature < CO2_CRITICAL_TEMPERATURE
|
|
185
|
+
"""
|
|
186
|
+
_a1 = 1.9245108
|
|
187
|
+
_a2 = -0.62385555
|
|
188
|
+
_a3 = -0.32731127
|
|
189
|
+
_a4 = 0.39245142
|
|
190
|
+
_t = 1 - absolute_temperature / CO2_CRITICAL_TEMPERATURE
|
|
191
|
+
inner = _a1 * _t**0.34 + _a2 * _t**0.5 + _a3 * _t ** (10 / 6) + _a4 * _t ** (11 / 6)
|
|
192
|
+
return CO2_CRITICAL_DENSITY * np.exp(inner)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def saturated_vapor_density(absolute_temperature):
|
|
196
|
+
"""
|
|
197
|
+
Saturated vapor density as defined by equation 3.15 of Span & Wagner
|
|
198
|
+
|
|
199
|
+
:param absolute_temperature: Absolute temperature in K. Should satisfy:
|
|
200
|
+
CO2_TRIPLE_TEMPERATURE < absolute_temperature < CO2_CRITICAL_TEMPERATURE
|
|
201
|
+
"""
|
|
202
|
+
# Assert temp < critical
|
|
203
|
+
_a1 = -1.7074879
|
|
204
|
+
_a2 = -0.82274670
|
|
205
|
+
_a3 = -4.6008549
|
|
206
|
+
_a4 = -10.111178
|
|
207
|
+
_a5 = -29.742252
|
|
208
|
+
_t = 1 - absolute_temperature / CO2_CRITICAL_TEMPERATURE
|
|
209
|
+
inner = (
|
|
210
|
+
_a1 * _t**0.34
|
|
211
|
+
+ _a2 * _t**0.5
|
|
212
|
+
+ _a3 * _t
|
|
213
|
+
+ _a4 * _t ** (7 / 3)
|
|
214
|
+
+ _a5 * _t ** (14 / 3)
|
|
215
|
+
)
|
|
216
|
+
return CO2_CRITICAL_DENSITY * np.exp(inner)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def sublimation_pressure(absolute_temperature):
|
|
220
|
+
"""
|
|
221
|
+
Sublimation pressure as defined by equation 3.12 of Span & Wagner [2]
|
|
222
|
+
|
|
223
|
+
:param absolute_temperature: Absolute temperature in K. Should satisfy absolute_temperature < CO2_TRIPLE_TEMPERATURE
|
|
224
|
+
"""
|
|
225
|
+
_a1 = -14.740846
|
|
226
|
+
_a2 = 2.4327015
|
|
227
|
+
_a3 = -5.3061778
|
|
228
|
+
_t = 1 - absolute_temperature / CO2_TRIPLE_TEMPERATURE
|
|
229
|
+
inner = _a1 * _t + _a2 * _t**1.9 + _a3 * _t**2.9
|
|
230
|
+
return CO2_TRIPLE_PRESSURE * np.exp(inner / (1 - _t))
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def vapor_pressure(absolute_temperature):
|
|
234
|
+
"""
|
|
235
|
+
Vapor pressure as defined by equation 3.13 of Span & Wagner [2]
|
|
236
|
+
|
|
237
|
+
:param absolute_temperature: Absolute temperature in K. Should satisfy:
|
|
238
|
+
CO2_TRIPLE_TEMPERATURE < absolute_temperature < CO2_CRITICAL_TEMPERATURE
|
|
239
|
+
"""
|
|
240
|
+
_a1 = -7.0602087
|
|
241
|
+
_a2 = 1.9391218
|
|
242
|
+
_a3 = -1.6463597
|
|
243
|
+
_a4 = -3.2995634
|
|
244
|
+
_t = 1 - absolute_temperature / CO2_CRITICAL_TEMPERATURE
|
|
245
|
+
inner = _a1 * _t**1.0 + _a2 * _t**1.5 + _a3 * _t**2.0 + _a4 * _t**4.0
|
|
246
|
+
return CO2_CRITICAL_PRESSURE * np.exp(inner / (1 - _t))
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def melting_pressure(absolute_temperature):
|
|
250
|
+
"""
|
|
251
|
+
Melting pressure as defined by equation 3.10 of Span & Wagner [2]
|
|
252
|
+
|
|
253
|
+
:param absolute_temperature: Absolute temperature in K. Should satisfy CO2_TRIPLE_TEMPERATURE < absolute_temperature
|
|
254
|
+
"""
|
|
255
|
+
_a1 = 1955.5390
|
|
256
|
+
_a2 = 2055.4593
|
|
257
|
+
_t = absolute_temperature / CO2_TRIPLE_TEMPERATURE - 1
|
|
258
|
+
return CO2_TRIPLE_PRESSURE * (1 + _a1 * _t + _a2 * _t**2)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def _determine_density_bounds(absolute_temperature, pressure, force_vapor):
|
|
262
|
+
"""
|
|
263
|
+
Calculate the upper and lower bound on density
|
|
264
|
+
"""
|
|
265
|
+
bounds = np.zeros((absolute_temperature.size, 2))
|
|
266
|
+
bounds[:, 0] = 0.1
|
|
267
|
+
bounds[:, 1] = 1500.0
|
|
268
|
+
|
|
269
|
+
below_triple = absolute_temperature < CO2_TRIPLE_TEMPERATURE
|
|
270
|
+
below_critical = ~below_triple & (absolute_temperature < CO2_CRITICAL_TEMPERATURE)
|
|
271
|
+
|
|
272
|
+
bounds[below_triple, 1] = saturated_vapor_density(
|
|
273
|
+
absolute_temperature[below_triple]
|
|
274
|
+
)
|
|
275
|
+
if force_vapor is True:
|
|
276
|
+
bounds[below_critical, 1] = saturated_vapor_density(
|
|
277
|
+
absolute_temperature[below_critical]
|
|
278
|
+
)
|
|
279
|
+
elif force_vapor is False:
|
|
280
|
+
bounds[below_critical, 0] = saturated_liquid_density(
|
|
281
|
+
absolute_temperature[below_critical]
|
|
282
|
+
)
|
|
283
|
+
else: # force_vapor == 'auto'
|
|
284
|
+
below_vapor_pressure = pressure < vapor_pressure(absolute_temperature)
|
|
285
|
+
is_vapor = below_critical & below_vapor_pressure
|
|
286
|
+
bounds[is_vapor, 1] = saturated_vapor_density(absolute_temperature[is_vapor])
|
|
287
|
+
is_liquid = below_critical & ~below_vapor_pressure
|
|
288
|
+
bounds[is_liquid, 0] = saturated_liquid_density(absolute_temperature[is_liquid])
|
|
289
|
+
return bounds
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def _find_initial_density_values(bounds, absolute_temperature, pressure):
|
|
293
|
+
"""
|
|
294
|
+
Finds approximate density values for the provided temperature(s) and pressure(s). The result is only intended to be
|
|
295
|
+
used by array_carbon_dioxide_density.
|
|
296
|
+
"""
|
|
297
|
+
|
|
298
|
+
temps = np.geomspace(
|
|
299
|
+
np.min(absolute_temperature) * 0.99, np.max(absolute_temperature) * 1.01, 41
|
|
300
|
+
)
|
|
301
|
+
press = np.geomspace(
|
|
302
|
+
np.min(pressure) * 0.99, np.max(absolute_temperature) * 1.01, 41
|
|
303
|
+
)
|
|
304
|
+
tt, pp = np.meshgrid(temps, press, indexing="ij")
|
|
305
|
+
densi = carbon_dioxide_density(
|
|
306
|
+
tt.flatten(), pp.flatten(), force_vapor="auto", raise_error=False
|
|
307
|
+
).reshape(temps.size, press.size)
|
|
308
|
+
rgi = RegularGridInterpolator(np.array((temps, press)), densi, method="linear")
|
|
309
|
+
iv = rgi(np.array((absolute_temperature, pressure)).T)
|
|
310
|
+
oob = (iv < bounds[:, 0]) | (iv > bounds[:, 1]) | np.isnan(iv)
|
|
311
|
+
iv[oob] = np.mean(bounds[oob], axis=1)
|
|
312
|
+
return iv
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def array_carbon_dioxide_density(absolute_temperature, pressure, force_vapor):
|
|
316
|
+
"""
|
|
317
|
+
Alternative implementation of a vectorized carbon dioxide density function. Implemented primarily for demonstration
|
|
318
|
+
purposes. For large arrays, a look-up-table approach should be preferred.
|
|
319
|
+
|
|
320
|
+
Utilizes scipy.optimize.newton, which is the only root-finding method of scipy that supports a vectorized functions.
|
|
321
|
+
|
|
322
|
+
For argument documentation, see carbon_dioxide_density.
|
|
323
|
+
"""
|
|
324
|
+
absolute_temperature = np.asarray(absolute_temperature)
|
|
325
|
+
pressure = np.asarray(pressure)
|
|
326
|
+
bounds = _determine_density_bounds(absolute_temperature, pressure, force_vapor)
|
|
327
|
+
iv = _find_initial_density_values(bounds, absolute_temperature, pressure)
|
|
328
|
+
opt = scipy.optimize.newton(
|
|
329
|
+
lambda x: carbon_dioxide_pressure(absolute_temperature, x) - pressure,
|
|
330
|
+
x0=iv,
|
|
331
|
+
maxiter=10,
|
|
332
|
+
full_output=True,
|
|
333
|
+
)
|
|
334
|
+
# scipy.optimize.newton may not always converge. We need to determine which of the elements of the solution are
|
|
335
|
+
# invalid. The opt.converged variable does not seem to suffice, so we perform separate checks. First, check that
|
|
336
|
+
# the solution is a valid root
|
|
337
|
+
invalid = ~np.isclose(
|
|
338
|
+
carbon_dioxide_pressure(absolute_temperature, opt.root),
|
|
339
|
+
pressure,
|
|
340
|
+
atol=1e-5,
|
|
341
|
+
rtol=0.0,
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
# Next, check if the solution is anywhere out of bounds (since newton does not support brackets), and check for nan
|
|
345
|
+
# values.
|
|
346
|
+
invalid |= (
|
|
347
|
+
(opt.root < bounds[:, 0]) | (opt.root > bounds[:, 1]) | np.isnan(opt.root)
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
# Finally, use the robust density method to determine the invalid results
|
|
351
|
+
sol = opt.root
|
|
352
|
+
sol[invalid] = carbon_dioxide_density(
|
|
353
|
+
absolute_temperature[invalid], pressure[invalid], force_vapor=force_vapor
|
|
354
|
+
)
|
|
355
|
+
return sol
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def float_vectorize(f):
|
|
359
|
+
# TODO:
|
|
360
|
+
# Should test if execution time is longer when using the float_vectorize instead of using numpy arrays directly.
|
|
361
|
+
# It may not be possible to use numpy arrays directly in span-wagner, but that should be tested
|
|
362
|
+
return np.vectorize(f, otypes=[float])
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
@float_vectorize
|
|
366
|
+
def _calculate_carbon_dioxide_density(
|
|
367
|
+
absolute_temperature, pressure, force_vapor="auto", raise_error=True
|
|
368
|
+
):
|
|
369
|
+
"""
|
|
370
|
+
Density of carbon dioxide. Found solving the Pressure equation of Table 3 in Span & Wagner [2] numerically for
|
|
371
|
+
density. To ensure a single solution, the phase of the liquid must first be determined.
|
|
372
|
+
|
|
373
|
+
:param absolute_temperature: Absolute temperature (K).
|
|
374
|
+
:param pressure: Pressure (MPa).
|
|
375
|
+
:param force_vapor: Boolean or 'auto'. If 'auto', the phase of the fluid is automatically determined. However, along
|
|
376
|
+
the vaporization line (assuming T_triple < absolute_temperature < T_critical), the fluid is in two-phase
|
|
377
|
+
equilibrium and the phase cannot be uniquely determined. If force_vapor is set to True, vapor phase is always
|
|
378
|
+
selected, if False, liquid phase is selected. Outide the temperature bounds, this argument has no effect. This
|
|
379
|
+
argument should only be used close to the vaporization boundary, otherwise the behavior might not be as
|
|
380
|
+
expected.
|
|
381
|
+
:param raise_error: Boolean. If True, raises an error if density cannot be determined. Otherwise, returns np.nan.
|
|
382
|
+
|
|
383
|
+
:return: Density (kg / m^3)
|
|
384
|
+
"""
|
|
385
|
+
bounds = _determine_density_bounds(
|
|
386
|
+
np.array([absolute_temperature]), np.array([pressure]), force_vapor
|
|
387
|
+
)[0, :]
|
|
388
|
+
|
|
389
|
+
# Extend bounds slightly to ensure toms748 can converge
|
|
390
|
+
bounds = [bounds[0] * 0.95, bounds[1] * 1.05]
|
|
391
|
+
|
|
392
|
+
try:
|
|
393
|
+
opt = scipy.optimize.root_scalar(
|
|
394
|
+
lambda x: carbon_dioxide_pressure(absolute_temperature, x) - pressure,
|
|
395
|
+
method="toms748",
|
|
396
|
+
bracket=bounds,
|
|
397
|
+
x0=np.sum(bounds) / 2,
|
|
398
|
+
)
|
|
399
|
+
except ValueError:
|
|
400
|
+
if raise_error:
|
|
401
|
+
raise
|
|
402
|
+
return np.nan
|
|
403
|
+
return opt.root
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def carbon_dioxide_density(absolute_temperature, pressure, interpolate=False, **kwargs):
|
|
407
|
+
"""
|
|
408
|
+
Density of carbon dioxide. Found either by direct calculation or interpolation. Any additional arguments are passed
|
|
409
|
+
to _calculate_carbon_dioxide_density.
|
|
410
|
+
|
|
411
|
+
:param absolute_temperature: Absolute temperature (K).
|
|
412
|
+
:param pressure: Pressure (MPa).
|
|
413
|
+
:param interpolate: Flag whether to interpolate data or not. If not, data is calculated directly. This is more
|
|
414
|
+
accurate, but also more time-consuming. Data outside the bounds of the interpolator will be set to np.nan.
|
|
415
|
+
|
|
416
|
+
:return: Density (kg / m^3)
|
|
417
|
+
"""
|
|
418
|
+
if interpolate is False:
|
|
419
|
+
return _calculate_carbon_dioxide_density(
|
|
420
|
+
absolute_temperature, pressure, **kwargs
|
|
421
|
+
)
|
|
422
|
+
assert interpolate is True
|
|
423
|
+
fp = pkg_resources.resource_filename(
|
|
424
|
+
"rock_physics.fluid_models.gas_model.span_wagner.tables",
|
|
425
|
+
"carbon_dioxide_density.npz",
|
|
426
|
+
)
|
|
427
|
+
interpolator = load_lookup_table_interpolator(fp)
|
|
428
|
+
return interpolator(absolute_temperature, pressure)
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def carbon_dioxide_bulk_modulus(absolute_temperature, density):
|
|
432
|
+
"""
|
|
433
|
+
Isentropic bulk modulus, derived from the expression for speed of sound in Table 3 of Span & Wagner
|
|
434
|
+
"""
|
|
435
|
+
d_pressure = carbon_dioxide_pressure(
|
|
436
|
+
absolute_temperature, density, d_density=1, isentropic=True
|
|
437
|
+
)
|
|
438
|
+
return density * d_pressure * 1e6
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Span-Wagner Coefficients
|
|
3
|
+
|
|
4
|
+
Coefficients defined in tables of Span & Wagner. Coefficients are divided into groups according to horizontal lines in
|
|
5
|
+
the table. This is also convenient when evaluating the Helmholtz energy expressions (phi functions).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from numpy import array
|
|
9
|
+
|
|
10
|
+
""" Ideal gas part (table 27) """
|
|
11
|
+
a0 = array(
|
|
12
|
+
[
|
|
13
|
+
[
|
|
14
|
+
8.37304456,
|
|
15
|
+
-3.70454304,
|
|
16
|
+
2.50000000,
|
|
17
|
+
1.99427042,
|
|
18
|
+
0.62105248,
|
|
19
|
+
0.41195293,
|
|
20
|
+
1.04028922,
|
|
21
|
+
0.08327678,
|
|
22
|
+
]
|
|
23
|
+
]
|
|
24
|
+
)
|
|
25
|
+
theta0 = array(
|
|
26
|
+
[
|
|
27
|
+
[
|
|
28
|
+
3.15163,
|
|
29
|
+
6.11190,
|
|
30
|
+
6.77708,
|
|
31
|
+
11.32384,
|
|
32
|
+
27.08792,
|
|
33
|
+
]
|
|
34
|
+
]
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
""" Residual part (table 31) """
|
|
38
|
+
|
|
39
|
+
# First group
|
|
40
|
+
n1 = array(
|
|
41
|
+
[
|
|
42
|
+
[
|
|
43
|
+
0.38856823203161 * 1e0,
|
|
44
|
+
0.29385475942740 * 1e1,
|
|
45
|
+
-0.55867188534934 * 1e1,
|
|
46
|
+
-0.76753199592477 * 1e0,
|
|
47
|
+
0.31729005580416 * 1e0,
|
|
48
|
+
0.54803315897767 * 1e0,
|
|
49
|
+
0.12279411220335 * 1e0,
|
|
50
|
+
]
|
|
51
|
+
]
|
|
52
|
+
)
|
|
53
|
+
d1 = array([[1, 1, 1, 1, 2, 2, 3]])
|
|
54
|
+
t1 = array([[0.00, 0.75, 1.00, 2.00, 0.75, 2.00, 0.75]])
|
|
55
|
+
|
|
56
|
+
# Second group
|
|
57
|
+
n2 = array(
|
|
58
|
+
[
|
|
59
|
+
[
|
|
60
|
+
0.21658961543220 * 1e1,
|
|
61
|
+
0.15841735109724 * 1e1,
|
|
62
|
+
-0.23132705405503 * 1e0,
|
|
63
|
+
0.58116916431436 * 1e-1,
|
|
64
|
+
-0.55369137205382 * 1e0,
|
|
65
|
+
0.48946615909422 * 1e0,
|
|
66
|
+
-0.24275739843501 * 1e-1,
|
|
67
|
+
0.62494790501678 * 1e-1,
|
|
68
|
+
-0.12175860225246 * 1e0,
|
|
69
|
+
-0.37055685270086 * 1e0,
|
|
70
|
+
-0.16775879700426 * 1e-1,
|
|
71
|
+
-0.11960736637987 * 1e0,
|
|
72
|
+
-0.45619362508778 * 1e-1,
|
|
73
|
+
0.35612789270346 * 1e-1,
|
|
74
|
+
-0.74427727132052 * 1e-2,
|
|
75
|
+
-0.17395704902432 * 1e-2,
|
|
76
|
+
-0.21810121289527 * 1e-1,
|
|
77
|
+
0.24332166559236 * 1e-1,
|
|
78
|
+
-0.37440133423463 * 1e-1,
|
|
79
|
+
0.143387157568781 * 1e0,
|
|
80
|
+
-0.13491969083286 * 1e0,
|
|
81
|
+
-0.23151225053480 * 1e-1,
|
|
82
|
+
0.12363125492901 * 1e-1,
|
|
83
|
+
0.21058321972940 * 1e-2,
|
|
84
|
+
-0.33958519026368 * 1e-3,
|
|
85
|
+
0.55993651771592 * 1e-2,
|
|
86
|
+
-0.30335118055646 * 1e-3,
|
|
87
|
+
]
|
|
88
|
+
]
|
|
89
|
+
)
|
|
90
|
+
d2 = array(
|
|
91
|
+
[[1, 2, 4, 5, 5, 5, 6, 6, 6, 1, 1, 4, 4, 4, 7, 8, 2, 3, 3, 5, 5, 6, 7, 8, 10, 4, 8]]
|
|
92
|
+
)
|
|
93
|
+
t2 = array(
|
|
94
|
+
[
|
|
95
|
+
[
|
|
96
|
+
1.50,
|
|
97
|
+
1.50,
|
|
98
|
+
2.50,
|
|
99
|
+
0.00,
|
|
100
|
+
1.50,
|
|
101
|
+
2.00,
|
|
102
|
+
0.00,
|
|
103
|
+
1.00,
|
|
104
|
+
2.00,
|
|
105
|
+
3.00,
|
|
106
|
+
6.00,
|
|
107
|
+
3.00,
|
|
108
|
+
6.00,
|
|
109
|
+
8.00,
|
|
110
|
+
6.00,
|
|
111
|
+
0.00,
|
|
112
|
+
7.00,
|
|
113
|
+
12.00,
|
|
114
|
+
16.00,
|
|
115
|
+
22.00,
|
|
116
|
+
24.00,
|
|
117
|
+
16.00,
|
|
118
|
+
24.00,
|
|
119
|
+
8.00,
|
|
120
|
+
2.00,
|
|
121
|
+
28.00,
|
|
122
|
+
14.00,
|
|
123
|
+
]
|
|
124
|
+
]
|
|
125
|
+
)
|
|
126
|
+
c2 = array(
|
|
127
|
+
[[1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 6]]
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Third group
|
|
131
|
+
n3 = array(
|
|
132
|
+
[
|
|
133
|
+
[
|
|
134
|
+
-0.21365488688320 * 1e3,
|
|
135
|
+
0.26641569149272 * 1e5,
|
|
136
|
+
-0.24027212204557 * 1e5,
|
|
137
|
+
-0.28341603423999 * 1e3,
|
|
138
|
+
0.21247284400179 * 1e3,
|
|
139
|
+
]
|
|
140
|
+
]
|
|
141
|
+
)
|
|
142
|
+
d3 = array([[2, 2, 2, 3, 3]])
|
|
143
|
+
t3 = array([[1.00, 0.00, 1.00, 3.00, 3.00]])
|
|
144
|
+
alpha3 = array([[25, 25, 25, 15, 20]])
|
|
145
|
+
beta3 = array([[325, 300, 300, 275, 275]])
|
|
146
|
+
gamma3 = array([[1.16, 1.19, 1.19, 1.25, 1.22]])
|
|
147
|
+
epsilon3 = array([[1.00, 1.00, 1.00, 1.00, 1.00]])
|
|
148
|
+
|
|
149
|
+
# Fourth group
|
|
150
|
+
n4 = array(
|
|
151
|
+
[
|
|
152
|
+
[
|
|
153
|
+
-0.66642276540751 * 1e0,
|
|
154
|
+
0.72608632349897 * 1e0,
|
|
155
|
+
0.55068668612842 * 1e-1,
|
|
156
|
+
]
|
|
157
|
+
]
|
|
158
|
+
)
|
|
159
|
+
a4 = array([[3.500, 3.500, 3.000]])
|
|
160
|
+
b4 = array([[0.875, 0.925, 0.875]])
|
|
161
|
+
beta4 = array([[0.300, 0.300, 0.300]])
|
|
162
|
+
A4 = array([[0.700, 0.700, 0.700]])
|
|
163
|
+
B4 = array([[0.3, 0.3, 1.0]])
|
|
164
|
+
C4 = array([[10.0, 10.0, 12.5]])
|
|
165
|
+
D4 = array([[275, 275, 275]])
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Package for calculating the residual helmholtz energy for CO2. The module uses sympy to evaluate the functions defined
|
|
3
|
+
in Span & Wagner [2], as well as its derivatives.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import sympy as sp
|
|
8
|
+
|
|
9
|
+
from . import coefficients
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def residual_helmholtz_energy(delta_, tau_, diff_delta, diff_tau):
|
|
13
|
+
"""
|
|
14
|
+
Equation 6.1 from Span & Wagner [2]. Calculates the residual helmholtz energy of co2 or its derivatives. tau_ and
|
|
15
|
+
delta_ must have be numpy arrays of shape (N, 1). This allows for vectorization.
|
|
16
|
+
|
|
17
|
+
:param delta_: Reduced density. Unit-less. numpy.ndarray with shape (N, 1)
|
|
18
|
+
:param tau_: Inverse reduced temperature. Unit-less. numpy.ndarray with shape (N, 1)
|
|
19
|
+
:param diff_delta: Degree of derivation wrt. delta. Integer.
|
|
20
|
+
:param diff_tau: Degree of derivation wrt. tau. Integer.
|
|
21
|
+
|
|
22
|
+
:return: Helmholtz free energy. Unit-less. numpy.ndarray with shape (N,)
|
|
23
|
+
"""
|
|
24
|
+
_s1 = np.sum(
|
|
25
|
+
_LAMBDIFIED_EXPRESSIONS[(s1, diff_delta, diff_tau)](tau_, delta_), axis=-1
|
|
26
|
+
)
|
|
27
|
+
_s2 = np.sum(
|
|
28
|
+
_LAMBDIFIED_EXPRESSIONS[(s2, diff_delta, diff_tau)](tau_, delta_), axis=-1
|
|
29
|
+
)
|
|
30
|
+
_s3 = np.sum(
|
|
31
|
+
_LAMBDIFIED_EXPRESSIONS[(s3, diff_delta, diff_tau)](tau_, delta_), axis=-1
|
|
32
|
+
)
|
|
33
|
+
_s4 = np.sum(
|
|
34
|
+
_LAMBDIFIED_EXPRESSIONS[(s4, diff_delta, diff_tau)](tau_, delta_), axis=-1
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
return _s1 + _s2 + _s3 + _s4
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# Define coefficients symbols. These should correspond one-to-one with variable names in the coefficient module
|
|
41
|
+
coeff_symbols = (
|
|
42
|
+
n1,
|
|
43
|
+
t1,
|
|
44
|
+
d1,
|
|
45
|
+
n2,
|
|
46
|
+
d2,
|
|
47
|
+
t2,
|
|
48
|
+
c2,
|
|
49
|
+
n3,
|
|
50
|
+
d3,
|
|
51
|
+
t3,
|
|
52
|
+
alpha3,
|
|
53
|
+
epsilon3,
|
|
54
|
+
beta3,
|
|
55
|
+
gamma3,
|
|
56
|
+
n4,
|
|
57
|
+
b4,
|
|
58
|
+
a4,
|
|
59
|
+
beta4,
|
|
60
|
+
A4,
|
|
61
|
+
B4,
|
|
62
|
+
C4,
|
|
63
|
+
D4,
|
|
64
|
+
) = sp.symbols(
|
|
65
|
+
"n1 t1 d1 n2 d2 t2 c2 n3 d3 t3 alpha3 epsilon3 beta3 gamma3 n4 b4 a4 beta4 A4 B4 C4 D4"
|
|
66
|
+
)
|
|
67
|
+
tau, delta = sp.symbols("tau delta", real=True)
|
|
68
|
+
i = sp.symbols("i", integer=True)
|
|
69
|
+
s1 = n1 * delta**d1 * tau**t1
|
|
70
|
+
s2 = n2 * delta**d2 * tau**t2 * sp.exp(-(delta**c2))
|
|
71
|
+
s3 = (
|
|
72
|
+
n3
|
|
73
|
+
* delta**d3
|
|
74
|
+
* tau**t3
|
|
75
|
+
* sp.exp(-alpha3 * (delta - epsilon3) ** 2 - beta3 * (tau - gamma3) ** 2)
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
theta_expr = (1 - tau) + A4 * ((delta - 1) ** 2) ** (1 / (2 * beta4))
|
|
79
|
+
bigdelta_expr = theta_expr**2 + B4 * ((delta - 1) ** 2) ** a4
|
|
80
|
+
bigphi_expr = sp.exp(-C4 * (delta - 1) ** 2 - D4 * (tau - 1) ** 2)
|
|
81
|
+
s4 = n4 * bigdelta_expr**b4 * delta * bigphi_expr
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _lambdify(expr, diff_delta, diff_tau):
|
|
85
|
+
diff = [delta] * diff_delta + [tau] * diff_tau
|
|
86
|
+
if len(diff) > 0:
|
|
87
|
+
expr = expr.diff(*diff)
|
|
88
|
+
expr = expr.powsimp()
|
|
89
|
+
coeff_vars = [getattr(coefficients, s.name) for s in coeff_symbols]
|
|
90
|
+
_sympy_lambda = sp.utilities.lambdify(
|
|
91
|
+
list(coeff_symbols) + [tau, delta],
|
|
92
|
+
expr,
|
|
93
|
+
modules=["numpy", {"DiracDelta": lambda x: x == 0}],
|
|
94
|
+
)
|
|
95
|
+
return lambda _tau, _delta: _sympy_lambda(*coeff_vars, _tau, _delta)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
_LAMBDIFIED_EXPRESSIONS = {
|
|
99
|
+
(e, dd, dt): _lambdify(e, dd, dt)
|
|
100
|
+
for e in (s1, s2, s3, s4)
|
|
101
|
+
for dd in (0, 1, 2)
|
|
102
|
+
for dt in (0, 1, 2)
|
|
103
|
+
if dd + dt <= 2
|
|
104
|
+
}
|
|
File without changes
|
|
Binary file
|