rock-physics-open 0.2.3__py3-none-any.whl → 0.3.1__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/equinor_utilities/gen_utilities/dict_to_float.py +6 -1
- rock_physics_open/equinor_utilities/gen_utilities/dim_check_vector.py +35 -5
- rock_physics_open/equinor_utilities/gen_utilities/filter_input.py +11 -6
- rock_physics_open/equinor_utilities/gen_utilities/filter_output.py +29 -19
- rock_physics_open/equinor_utilities/machine_learning_utilities/__init__.py +6 -5
- rock_physics_open/equinor_utilities/machine_learning_utilities/base_pressure_model.py +172 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/exponential_model.py +100 -86
- rock_physics_open/equinor_utilities/machine_learning_utilities/import_ml_models.py +7 -4
- rock_physics_open/equinor_utilities/machine_learning_utilities/polynomial_model.py +128 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/run_regression.py +6 -4
- rock_physics_open/equinor_utilities/machine_learning_utilities/sigmoidal_model.py +204 -155
- rock_physics_open/equinor_utilities/optimisation_utilities/__init__.py +19 -0
- rock_physics_open/equinor_utilities/snapshot_test_utilities/compare_snapshots.py +1 -2
- rock_physics_open/equinor_utilities/std_functions/backus_ave.py +16 -1
- rock_physics_open/equinor_utilities/std_functions/dvorkin_nur.py +10 -2
- rock_physics_open/equinor_utilities/std_functions/gassmann.py +32 -7
- rock_physics_open/equinor_utilities/std_functions/hashin_shtrikman.py +36 -7
- rock_physics_open/equinor_utilities/std_functions/hertz_mindlin.py +9 -1
- rock_physics_open/equinor_utilities/std_functions/moduli_velocity.py +22 -6
- rock_physics_open/equinor_utilities/std_functions/reflection_eq.py +28 -6
- rock_physics_open/equinor_utilities/std_functions/rho.py +12 -2
- rock_physics_open/equinor_utilities/std_functions/voigt_reuss_hill.py +25 -4
- rock_physics_open/equinor_utilities/std_functions/walton.py +8 -1
- rock_physics_open/equinor_utilities/std_functions/wood_brie.py +20 -3
- rock_physics_open/equinor_utilities/various_utilities/display_result_statistics.py +16 -9
- rock_physics_open/equinor_utilities/various_utilities/gassmann_dry_mod.py +21 -2
- rock_physics_open/equinor_utilities/various_utilities/gassmann_mod.py +21 -2
- rock_physics_open/equinor_utilities/various_utilities/gassmann_sub_mod.py +23 -12
- rock_physics_open/equinor_utilities/various_utilities/hs_average.py +20 -1
- rock_physics_open/equinor_utilities/various_utilities/pressure.py +9 -1
- rock_physics_open/equinor_utilities/various_utilities/reflectivity.py +26 -10
- rock_physics_open/equinor_utilities/various_utilities/timeshift.py +15 -2
- rock_physics_open/equinor_utilities/various_utilities/vp_vs_rho_set_statistics.py +40 -24
- rock_physics_open/equinor_utilities/various_utilities/vrh_3_min.py +24 -2
- rock_physics_open/fluid_models/brine_model/brine_properties.py +70 -35
- rock_physics_open/fluid_models/gas_model/gas_properties.py +79 -37
- rock_physics_open/fluid_models/oil_model/dead_oil_density.py +21 -16
- rock_physics_open/fluid_models/oil_model/dead_oil_velocity.py +9 -7
- rock_physics_open/fluid_models/oil_model/live_oil_density.py +16 -13
- rock_physics_open/fluid_models/oil_model/live_oil_velocity.py +3 -3
- rock_physics_open/fluid_models/oil_model/oil_properties.py +59 -29
- rock_physics_open/sandstone_models/__init__.py +2 -0
- rock_physics_open/sandstone_models/constant_cement_optimisation.py +4 -1
- rock_physics_open/sandstone_models/friable_models.py +6 -7
- rock_physics_open/sandstone_models/friable_optimisation.py +4 -1
- rock_physics_open/sandstone_models/patchy_cement_model.py +103 -5
- rock_physics_open/sandstone_models/patchy_cement_optimisation.py +4 -1
- rock_physics_open/t_matrix_models/__init__.py +0 -10
- rock_physics_open/t_matrix_models/carbonate_pressure_substitution.py +1 -1
- rock_physics_open/t_matrix_models/curvefit_t_matrix_exp.py +1 -2
- rock_physics_open/t_matrix_models/t_matrix_opt_fluid_sub_exp.py +3 -3
- rock_physics_open/t_matrix_models/t_matrix_opt_fluid_sub_petec.py +5 -1
- rock_physics_open/t_matrix_models/t_matrix_opt_forward_model_exp.py +5 -1
- rock_physics_open/t_matrix_models/t_matrix_opt_forward_model_min.py +4 -1
- rock_physics_open/t_matrix_models/t_matrix_parameter_optimisation_exp.py +5 -1
- rock_physics_open/t_matrix_models/t_matrix_parameter_optimisation_min.py +4 -1
- rock_physics_open/ternary_plots/ternary_plot_utilities.py +3 -3
- rock_physics_open/version.py +2 -2
- {rock_physics_open-0.2.3.dist-info → rock_physics_open-0.3.1.dist-info}/METADATA +4 -8
- {rock_physics_open-0.2.3.dist-info → rock_physics_open-0.3.1.dist-info}/RECORD +64 -61
- /rock_physics_open/{t_matrix_models → equinor_utilities/optimisation_utilities}/opt_subst_utilities.py +0 -0
- {rock_physics_open-0.2.3.dist-info → rock_physics_open-0.3.1.dist-info}/WHEEL +0 -0
- {rock_physics_open-0.2.3.dist-info → rock_physics_open-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {rock_physics_open-0.2.3.dist-info → rock_physics_open-0.3.1.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import numpy as np
|
|
2
|
+
import numpy.typing as npt
|
|
2
3
|
|
|
3
4
|
|
|
4
|
-
def pressure(
|
|
5
|
+
def pressure(
|
|
6
|
+
rho: npt.NDArray[np.float64],
|
|
7
|
+
tvd_msl: npt.NDArray[np.float64],
|
|
8
|
+
water_depth: float,
|
|
9
|
+
p_form: float,
|
|
10
|
+
tvd_p_form: float,
|
|
11
|
+
n: float,
|
|
12
|
+
) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]:
|
|
5
13
|
"""
|
|
6
14
|
Function to estimate overburden pressure and vertical effective stress (lithostatic pressure)
|
|
7
15
|
based on density.
|
|
@@ -1,9 +1,19 @@
|
|
|
1
|
+
from typing import Literal, cast
|
|
2
|
+
|
|
1
3
|
import numpy as np
|
|
4
|
+
import numpy.typing as npt
|
|
2
5
|
|
|
3
6
|
from rock_physics_open.equinor_utilities import gen_utilities, std_functions
|
|
4
7
|
|
|
5
8
|
|
|
6
|
-
def reflectivity(
|
|
9
|
+
def reflectivity(
|
|
10
|
+
vp_inp: npt.NDArray[np.float64],
|
|
11
|
+
vs_inp: npt.NDArray[np.float64],
|
|
12
|
+
rho_inp: npt.NDArray[np.float64],
|
|
13
|
+
theta: float = 0.0,
|
|
14
|
+
k: float = 2.0,
|
|
15
|
+
model: Literal["AkiRichards", "SmithGidlow"] = "AkiRichards",
|
|
16
|
+
) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.bool_]]:
|
|
7
17
|
"""
|
|
8
18
|
Reflectivity model according to Aki and Richards or Smith and Gidlow for weak contrasts
|
|
9
19
|
and angles less than critical angle.
|
|
@@ -35,12 +45,17 @@ def reflectivity(vp_inp, vs_inp, rho_inp, theta=0.0, k=2.0, model="AkiRichards")
|
|
|
35
45
|
idx_inp: index to accepted part of the input arrays [bool].
|
|
36
46
|
"""
|
|
37
47
|
|
|
38
|
-
vp, vs, rho,
|
|
39
|
-
|
|
48
|
+
vp, vs, rho, theta_, k_ = cast(
|
|
49
|
+
list[npt.NDArray[np.float64]],
|
|
50
|
+
gen_utilities.dim_check_vector((vp_inp, vs_inp, rho_inp, theta, k)),
|
|
40
51
|
)
|
|
41
52
|
|
|
42
|
-
idx_inp, (vp, vs, rho,
|
|
43
|
-
[
|
|
53
|
+
idx_inp, (vp, vs, rho, theta_, k_) = cast(
|
|
54
|
+
tuple[npt.NDArray[np.bool_], list[npt.NDArray[np.float64]]],
|
|
55
|
+
gen_utilities.filter_input_log(
|
|
56
|
+
[vp, vs, rho, theta_, k_],
|
|
57
|
+
positive=True,
|
|
58
|
+
),
|
|
44
59
|
)
|
|
45
60
|
|
|
46
61
|
if np.any(~idx_inp):
|
|
@@ -69,16 +84,17 @@ def reflectivity(vp_inp, vs_inp, rho_inp, theta=0.0, k=2.0, model="AkiRichards")
|
|
|
69
84
|
> 1
|
|
70
85
|
) * "s"
|
|
71
86
|
raise ValueError(
|
|
72
|
-
"{0:} reflectivity: Missing or illegal values in input log{1:}: {2:}interpolation of input log{1:} "
|
|
73
|
-
|
|
87
|
+
"{0:} reflectivity: Missing or illegal values in input log{1:}: {2:}interpolation of input log{1:} is needed\n".format(
|
|
88
|
+
model, log_str, pl_str
|
|
89
|
+
)
|
|
74
90
|
)
|
|
75
91
|
|
|
76
92
|
if model == "AkiRichards":
|
|
77
|
-
refl_coef = std_functions.aki_richards(vp, vs, rho,
|
|
93
|
+
refl_coef = std_functions.aki_richards(vp, vs, rho, theta_, k_)
|
|
78
94
|
elif model == "SmithGidlow":
|
|
79
|
-
refl_coef = std_functions.smith_gidlow(vp, vs, rho,
|
|
95
|
+
refl_coef = std_functions.smith_gidlow(vp, vs, rho, theta_, k_)
|
|
80
96
|
else:
|
|
81
|
-
raise ValueError(
|
|
97
|
+
raise ValueError( # pyright: ignore[reportUnreachable] | Kept for backward compatibility
|
|
82
98
|
f'{__file__}: unknown model: {model}, should be one of "AkiRichards", "SmithGidlow"'
|
|
83
99
|
)
|
|
84
100
|
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import numpy as np
|
|
2
|
+
import numpy.typing as npt
|
|
2
3
|
|
|
3
4
|
|
|
4
|
-
def time_shift_pp(
|
|
5
|
+
def time_shift_pp(
|
|
6
|
+
tvd: npt.NDArray[np.float64],
|
|
7
|
+
vp_base: npt.NDArray[np.float64],
|
|
8
|
+
vp_mon: npt.NDArray[np.float64],
|
|
9
|
+
multiplier: int,
|
|
10
|
+
) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]:
|
|
5
11
|
"""
|
|
6
12
|
Cumulative time shift calculation for 4D case. According to Equinor standard
|
|
7
13
|
the time shift is negative for an increase in velocity from base to monitor
|
|
@@ -43,7 +49,14 @@ def time_shift_pp(tvd, vp_base, vp_mon, multiplier):
|
|
|
43
49
|
return owt_pp_shift, twt_pp_shift
|
|
44
50
|
|
|
45
51
|
|
|
46
|
-
def time_shift_ps(
|
|
52
|
+
def time_shift_ps(
|
|
53
|
+
tvd: npt.NDArray[np.float64],
|
|
54
|
+
vp_base: npt.NDArray[np.float64],
|
|
55
|
+
vp_mon: npt.NDArray[np.float64],
|
|
56
|
+
vs_base: npt.NDArray[np.float64],
|
|
57
|
+
vs_mon: npt.NDArray[np.float64],
|
|
58
|
+
multiplier: int,
|
|
59
|
+
) -> npt.NDArray[np.float64]:
|
|
47
60
|
"""
|
|
48
61
|
Cumulative time shift calculation for 4D case. According to Equinor standard
|
|
49
62
|
the time shift is negative for an increase in velocity from base to monitor
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from typing import Literal, cast
|
|
2
3
|
|
|
3
4
|
import numpy as np
|
|
5
|
+
import numpy.typing as npt
|
|
4
6
|
import pandas as pd
|
|
5
7
|
from sklearn.metrics import r2_score
|
|
6
8
|
|
|
@@ -8,18 +10,18 @@ from .display_result_statistics import disp_result_stats
|
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
def vp_vs_rho_stats(
|
|
11
|
-
vp_observed,
|
|
12
|
-
vs_observed,
|
|
13
|
-
rho_observed,
|
|
14
|
-
vp_estimated,
|
|
15
|
-
vs_estimated,
|
|
16
|
-
rho_estimated,
|
|
17
|
-
fname,
|
|
18
|
-
estimated_set_names,
|
|
19
|
-
well_names,
|
|
20
|
-
file_mode="a",
|
|
21
|
-
disp_results=True,
|
|
22
|
-
):
|
|
13
|
+
vp_observed: npt.NDArray[np.float64] | list[npt.NDArray[np.float64]],
|
|
14
|
+
vs_observed: npt.NDArray[np.float64] | list[npt.NDArray[np.float64]],
|
|
15
|
+
rho_observed: npt.NDArray[np.float64] | list[npt.NDArray[np.float64]],
|
|
16
|
+
vp_estimated: npt.NDArray[np.float64] | list[npt.NDArray[np.float64]],
|
|
17
|
+
vs_estimated: npt.NDArray[np.float64] | list[npt.NDArray[np.float64]],
|
|
18
|
+
rho_estimated: npt.NDArray[np.float64] | list[npt.NDArray[np.float64]],
|
|
19
|
+
fname: str,
|
|
20
|
+
estimated_set_names: str | list[str],
|
|
21
|
+
well_names: str | list[str],
|
|
22
|
+
file_mode: Literal["a", "w"] = "a",
|
|
23
|
+
disp_results: bool = True,
|
|
24
|
+
) -> None:
|
|
23
25
|
"""
|
|
24
26
|
Utility to estimate statistics between vp-vs-rho sets - observed and estimated values. The results are displayed
|
|
25
27
|
on screen (optional) and saved to a .csv file. If the file exists, the results will be appended.
|
|
@@ -83,33 +85,42 @@ def vp_vs_rho_stats(
|
|
|
83
85
|
]
|
|
84
86
|
est_frame = pd.DataFrame(columns=est_frame_columns, index=estimated_set_names)
|
|
85
87
|
est_frame.index.name = "Estimated set name"
|
|
86
|
-
est_frame.iloc[:, 0] = well_names
|
|
88
|
+
est_frame.iloc[:, 0] = well_names # pyright: ignore[reportArgumentType]
|
|
87
89
|
|
|
88
90
|
# If inputs are found to satisfy expectations in _verify, and they are numpy arrays, cast to lists, and run through
|
|
89
91
|
if isinstance(vp_observed, np.ndarray):
|
|
90
92
|
vp_observed = [vp_observed]
|
|
93
|
+
if isinstance(vs_observed, np.ndarray):
|
|
91
94
|
vs_observed = [vs_observed]
|
|
95
|
+
if isinstance(rho_observed, np.ndarray):
|
|
92
96
|
rho_observed = [rho_observed]
|
|
97
|
+
if isinstance(vp_estimated, np.ndarray):
|
|
93
98
|
vp_estimated = [vp_estimated]
|
|
99
|
+
if isinstance(vs_estimated, np.ndarray):
|
|
94
100
|
vs_estimated = [vs_estimated]
|
|
101
|
+
if isinstance(rho_estimated, np.ndarray):
|
|
95
102
|
rho_estimated = [rho_estimated]
|
|
96
103
|
|
|
97
104
|
for i in range(len(vp_observed)):
|
|
98
|
-
res = []
|
|
105
|
+
res: list[float] = []
|
|
99
106
|
for obs, est in zip(
|
|
100
107
|
[vp_observed[i], vs_observed[i], rho_observed[i]],
|
|
101
108
|
[vp_estimated[i], vs_estimated[i], rho_estimated[i]],
|
|
102
109
|
):
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
)
|
|
110
|
+
rmae = float(
|
|
111
|
+
np.mean(np.abs((est.flatten() - obs.flatten()) / obs.flatten()))
|
|
112
|
+
)
|
|
113
|
+
rrmse: float = np.sqrt(
|
|
114
|
+
np.mean(np.square((est.flatten() - obs.flatten()) / obs.flatten()))
|
|
108
115
|
)
|
|
109
|
-
|
|
116
|
+
r2 = cast(float, r2_score(obs.flatten(), est.flatten()))
|
|
117
|
+
|
|
118
|
+
res.append(rmae)
|
|
119
|
+
res.append(rrmse)
|
|
120
|
+
res.append(r2)
|
|
110
121
|
|
|
111
122
|
res_dict = dict(zip(est_frame_columns[1:], res))
|
|
112
|
-
est_frame.iloc[i, 1:] = res_dict
|
|
123
|
+
est_frame.iloc[i, 1:] = res_dict # pyright: ignore[reportArgumentType]
|
|
113
124
|
if disp_results:
|
|
114
125
|
disp_result_stats(
|
|
115
126
|
estimated_set_names[i], res, est_frame_columns[1:], values_only=True
|
|
@@ -122,7 +133,12 @@ def vp_vs_rho_stats(
|
|
|
122
133
|
est_frame.to_csv(fname, mode=file_mode)
|
|
123
134
|
|
|
124
135
|
|
|
125
|
-
def _verify(
|
|
136
|
+
def _verify(
|
|
137
|
+
*args: npt.NDArray[np.float64] | list[npt.NDArray[np.float64]],
|
|
138
|
+
set_names: list[str],
|
|
139
|
+
well_names: list[str],
|
|
140
|
+
file_mode: Literal["a", "w"],
|
|
141
|
+
):
|
|
126
142
|
"""Verify that arguments are either numpy arrays or lists of numpy arrays.
|
|
127
143
|
Raises
|
|
128
144
|
------
|
|
@@ -142,7 +158,7 @@ def _verify(*args, set_names=None, well_names=None, file_mode=None):
|
|
|
142
158
|
if isinstance(arg, np.ndarray):
|
|
143
159
|
arg = [arg]
|
|
144
160
|
for this_arg in arg:
|
|
145
|
-
if not isinstance(this_arg, np.ndarray):
|
|
161
|
+
if not isinstance(this_arg, np.ndarray): # pyright: ignore[reportUnnecessaryIsInstance] | For backward compatibility
|
|
146
162
|
raise ValueError(f"{__file__}: input not numpy array: {type(arg)}")
|
|
147
163
|
if np.any(np.isnan(this_arg)):
|
|
148
164
|
raise ValueError(f"{__file__}: input contains NaNs")
|
|
@@ -151,4 +167,4 @@ def _verify(*args, set_names=None, well_names=None, file_mode=None):
|
|
|
151
167
|
if not len(arg) == len(set_names) == len(well_names):
|
|
152
168
|
raise ValueError(f"{__file__}: mismatch in argument lengths")
|
|
153
169
|
if not (file_mode == "a" or file_mode == "w"):
|
|
154
|
-
raise ValueError(f'{__file__}: file_mode must be one of ["a", "w"]')
|
|
170
|
+
raise ValueError(f'{__file__}: file_mode must be one of ["a", "w"]') # pyright: ignore[reportUnreachable] | For backward compatibility
|
|
@@ -1,9 +1,31 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import numpy.typing as npt
|
|
3
|
+
|
|
1
4
|
from rock_physics_open.equinor_utilities import std_functions
|
|
2
5
|
|
|
3
6
|
|
|
4
7
|
def min_3_voigt_reuss_hill(
|
|
5
|
-
vp1
|
|
6
|
-
|
|
8
|
+
vp1: npt.NDArray[np.float64],
|
|
9
|
+
vs1: npt.NDArray[np.float64],
|
|
10
|
+
rhob1: npt.NDArray[np.float64],
|
|
11
|
+
f1: npt.NDArray[np.float64],
|
|
12
|
+
vp2: npt.NDArray[np.float64],
|
|
13
|
+
vs2: npt.NDArray[np.float64],
|
|
14
|
+
rhob2: npt.NDArray[np.float64],
|
|
15
|
+
f2: npt.NDArray[np.float64],
|
|
16
|
+
vp3: npt.NDArray[np.float64],
|
|
17
|
+
vs3: npt.NDArray[np.float64],
|
|
18
|
+
rhob3: npt.NDArray[np.float64],
|
|
19
|
+
f3: npt.NDArray[np.float64],
|
|
20
|
+
) -> tuple[
|
|
21
|
+
npt.NDArray[np.float64],
|
|
22
|
+
npt.NDArray[np.float64],
|
|
23
|
+
npt.NDArray[np.float64],
|
|
24
|
+
npt.NDArray[np.float64],
|
|
25
|
+
npt.NDArray[np.float64],
|
|
26
|
+
npt.NDArray[np.float64],
|
|
27
|
+
npt.NDArray[np.float64],
|
|
28
|
+
]:
|
|
7
29
|
"""
|
|
8
30
|
Mix of three phases by Voigt-Reuss-Hill model. The fractions should add up to 1 with input of vp, vs and rho.
|
|
9
31
|
|
|
@@ -14,16 +14,16 @@ def brine_properties(
|
|
|
14
14
|
p_cacl: np.ndarray | float | None = None,
|
|
15
15
|
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
16
16
|
"""
|
|
17
|
-
:param salinity: Salinity of solution as ppm of NaCl.
|
|
18
|
-
:param pressure: Pressure
|
|
19
|
-
:param temperature: Temperature
|
|
17
|
+
:param salinity: Salinity of solution as [ppm] of NaCl.
|
|
18
|
+
:param pressure: Pressure [Pa]
|
|
19
|
+
:param temperature: Temperature [°C]
|
|
20
20
|
:param p_nacl: NaCl percentage, for future use
|
|
21
21
|
:param p_kcl: KCl percentage, for future use
|
|
22
22
|
:param p_cacl: CaCl percentage, for future use
|
|
23
|
-
:return: vel_b [m/s], den_b [kg/m^3], k_b [Pa]
|
|
23
|
+
:return: Brine velocity vel_b [m/s], brine density den_b [kg/m^3], brine bulk modulus k_b [Pa]
|
|
24
24
|
"""
|
|
25
|
-
vel_b = brine_primary_velocity(temperature, pressure
|
|
26
|
-
den_b = brine_density(temperature, pressure
|
|
25
|
+
vel_b = brine_primary_velocity(temperature, pressure, salinity)
|
|
26
|
+
den_b = brine_density(temperature, pressure, salinity)
|
|
27
27
|
k_b = vel_b**2 * den_b
|
|
28
28
|
return vel_b, den_b, k_b
|
|
29
29
|
|
|
@@ -35,19 +35,27 @@ def brine_density(
|
|
|
35
35
|
) -> np.ndarray | float:
|
|
36
36
|
"""
|
|
37
37
|
density of sodium chloride solutions, equation 27 in Batzle & Wang [1].
|
|
38
|
-
:param salinity: Salinity of solution
|
|
39
|
-
|
|
40
|
-
:param
|
|
41
|
-
:
|
|
42
|
-
:return: density of solution in g/cc.
|
|
38
|
+
:param salinity: Salinity of solution in ppm
|
|
39
|
+
:param pressure: Pressure [Pa]
|
|
40
|
+
:param temperature: Temperature [°C]
|
|
41
|
+
:return: density of solution in [kg/m^3].
|
|
43
42
|
"""
|
|
43
|
+
# Change unit of pressure to MPa
|
|
44
|
+
pressure_mpa = pressure / 1.0e6
|
|
45
|
+
# Change unit of salinity to fraction
|
|
46
|
+
salinity_frac = salinity / 1.0e6
|
|
47
|
+
|
|
44
48
|
coefficients = [
|
|
45
49
|
[[0.668, 3e-4], [8e-5, -13e-6], [3e-6, 0.0]],
|
|
46
50
|
[[0.44, -24e-4], [-33e-4, 47e-6], [0.0, 0.0]],
|
|
47
51
|
]
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
water_den = water_density(temperature, pressure)
|
|
53
|
+
brine_correction = (
|
|
54
|
+
salinity_frac
|
|
55
|
+
* polyval3d(salinity_frac, temperature, pressure_mpa, coefficients)
|
|
56
|
+
* 1000.0
|
|
50
57
|
)
|
|
58
|
+
return water_den + brine_correction
|
|
51
59
|
|
|
52
60
|
|
|
53
61
|
def brine_primary_velocity(
|
|
@@ -58,12 +66,16 @@ def brine_primary_velocity(
|
|
|
58
66
|
"""
|
|
59
67
|
Primary wave velocity of sodium chloride solutions, equation 29 in Batzle & Wang [1]
|
|
60
68
|
|
|
61
|
-
:param salinity: Salinity of solution as
|
|
62
|
-
|
|
63
|
-
:param
|
|
64
|
-
:param temperature: Temperature (Celsius) of oil.
|
|
69
|
+
:param salinity: Salinity of solution as [ppm] of sodium chloride
|
|
70
|
+
:param pressure: Pressure [Pa]
|
|
71
|
+
:param temperature: Temperature [°C]
|
|
65
72
|
:return: velocity of solution in m/s.
|
|
66
73
|
"""
|
|
74
|
+
# Change unit for salinity from ppm to fraction
|
|
75
|
+
salinity_frac = salinity / 1.0e6
|
|
76
|
+
# Change the unit for pressure from Pa to MPa
|
|
77
|
+
pressure_mpa = pressure / 1.0e6
|
|
78
|
+
|
|
67
79
|
coefficients = np.zeros((3, 4, 3))
|
|
68
80
|
coefficients[0, 0, 0] = 1170
|
|
69
81
|
coefficients[0, 1, 0] = -9.6
|
|
@@ -77,8 +89,8 @@ def brine_primary_velocity(
|
|
|
77
89
|
coefficients[1, 0, 2] = 0.16
|
|
78
90
|
coefficients[2, 0, 0] = -820
|
|
79
91
|
|
|
80
|
-
return water_primary_velocity(temperature, pressure) +
|
|
81
|
-
sqrt(
|
|
92
|
+
return water_primary_velocity(temperature, pressure) + salinity_frac * polyval3d(
|
|
93
|
+
sqrt(salinity_frac), temperature, pressure_mpa, coefficients
|
|
82
94
|
)
|
|
83
95
|
|
|
84
96
|
|
|
@@ -88,17 +100,20 @@ def water_density(
|
|
|
88
100
|
) -> np.ndarray | float:
|
|
89
101
|
"""
|
|
90
102
|
Density of water,, equation 27a in Batzle & Wang [1].
|
|
91
|
-
:param pressure: Pressure
|
|
92
|
-
:param temperature: Temperature
|
|
93
|
-
:return: Density of water in
|
|
103
|
+
:param pressure: Pressure [Pa]
|
|
104
|
+
:param temperature: Temperature [°C]
|
|
105
|
+
:return: Density of water in [kg/m^3].
|
|
94
106
|
"""
|
|
107
|
+
# Change unit of pressure from Pa to MPa
|
|
108
|
+
pressure_mpa = pressure / 1.0e6
|
|
109
|
+
|
|
95
110
|
coefficients = [
|
|
96
111
|
[1.0, 489e-6, -333e-9],
|
|
97
112
|
[-8e-5, -2e-6, -2e-09],
|
|
98
113
|
[-33e-7, 16e-9, 0.0],
|
|
99
114
|
[1.75e-9, -13e-12, 0.0],
|
|
100
115
|
]
|
|
101
|
-
return polyval2d(temperature,
|
|
116
|
+
return polyval2d(temperature, pressure_mpa, coefficients) * 1000.0
|
|
102
117
|
|
|
103
118
|
|
|
104
119
|
def water_primary_velocity(
|
|
@@ -107,11 +122,14 @@ def water_primary_velocity(
|
|
|
107
122
|
) -> np.ndarray | float:
|
|
108
123
|
"""
|
|
109
124
|
Primary wave velocity of water, table 1 and equation 28 in Batzle & Wang [1].
|
|
110
|
-
:param pressure: Pressure
|
|
111
|
-
:param temperature: Temperature
|
|
125
|
+
:param pressure: Pressure [Pa]
|
|
126
|
+
:param temperature: Temperature [°C]
|
|
112
127
|
:return: primary wave velocity of water in m/s.
|
|
113
128
|
"""
|
|
114
|
-
|
|
129
|
+
# Change unit of pressure from Pa to MPa
|
|
130
|
+
pressure_mpa = pressure / 1.0e6
|
|
131
|
+
|
|
132
|
+
if np.any(pressure_mpa > 100):
|
|
115
133
|
warnings.warn(
|
|
116
134
|
"Calculations for water velocity is not precise for\n"
|
|
117
135
|
+ "pressure outside [0,100]MPa"
|
|
@@ -125,19 +143,36 @@ def water_primary_velocity(
|
|
|
125
143
|
[1.487e-4, -6.503e-7, -1.455e-8, 1.327e-10],
|
|
126
144
|
[-2.197e-7, 7.987e-10, 5.23e-11, -4.614e-13],
|
|
127
145
|
]
|
|
128
|
-
return polyval2d(temperature,
|
|
146
|
+
return polyval2d(temperature, pressure_mpa, coefficients)
|
|
129
147
|
|
|
130
148
|
|
|
131
149
|
def water(
|
|
132
150
|
temperature: np.ndarray | float, pressure: np.ndarray | float
|
|
133
151
|
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
134
152
|
"""
|
|
135
|
-
:param pressure: Pressure
|
|
136
|
-
:param temperature: Temperature
|
|
137
|
-
:return:
|
|
153
|
+
:param pressure: Pressure [Pa]
|
|
154
|
+
:param temperature: Temperature [°C]
|
|
155
|
+
:return: water_velocity [m/s], water_density [kg/m^3], water_bulk_modulus [Pa]
|
|
138
156
|
"""
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
157
|
+
water_den = water_density(temperature, pressure)
|
|
158
|
+
water_vel = water_primary_velocity(temperature, pressure)
|
|
159
|
+
water_k = water_vel**2 * water_den
|
|
160
|
+
return water_vel, water_den, water_k
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def brine_viscosity(
|
|
164
|
+
temperature: np.ndarray | float,
|
|
165
|
+
salinity: np.ndarray | float,
|
|
166
|
+
) -> np.ndarray | float:
|
|
167
|
+
"""
|
|
168
|
+
Brine viscosity according to Batzle & Wang [1].
|
|
169
|
+
|
|
170
|
+
Based on equation 32.
|
|
171
|
+
"""
|
|
172
|
+
salinity_frac = salinity / 1.0e6
|
|
173
|
+
return (
|
|
174
|
+
0.1
|
|
175
|
+
+ 0.333 * salinity_frac
|
|
176
|
+
+ (1.65 + 91.9 * salinity_frac**3)
|
|
177
|
+
* np.exp(-(0.42 * (salinity_frac**0.8 - 0.17) ** 2 + 0.045) * temperature**0.8)
|
|
178
|
+
)
|
|
@@ -4,7 +4,7 @@ from scipy.constants import gas_constant
|
|
|
4
4
|
|
|
5
5
|
from rock_physics_open.equinor_utilities.conversions import celsius_to_kelvin
|
|
6
6
|
|
|
7
|
-
AIR_WEIGHT = 28.8 #
|
|
7
|
+
AIR_WEIGHT = 28.8 * 1.0e-3 # kg/mol
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def gas_properties(
|
|
@@ -20,10 +20,11 @@ def gas_properties(
|
|
|
20
20
|
:param model: for future use
|
|
21
21
|
:return: vel_gas [m/s], den_gas [kg/m^3], k_gas [Pa], eta_gas [cP]
|
|
22
22
|
"""
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
)
|
|
23
|
+
|
|
24
|
+
den_gas = gas_density(celsius_to_kelvin(temperature), pressure, gas_gravity)
|
|
25
|
+
|
|
26
|
+
k_gas = gas_bulk_modulus(celsius_to_kelvin(temperature), pressure, gas_gravity)
|
|
27
|
+
|
|
27
28
|
vel_gas = (k_gas / den_gas) ** 0.5
|
|
28
29
|
|
|
29
30
|
eta_gas = lee_gas_viscosity(celsius_to_kelvin(temperature), pressure, gas_gravity)
|
|
@@ -33,9 +34,9 @@ def gas_properties(
|
|
|
33
34
|
|
|
34
35
|
def molecular_weight(gas_gravity: np.ndarray | float) -> np.ndarray | float:
|
|
35
36
|
"""
|
|
36
|
-
calculates
|
|
37
|
+
calculates molecular weight of a gas from gas gravity.
|
|
37
38
|
:param gas_gravity: molar mass of gas relative to air molar mas.
|
|
38
|
-
:return: The volume of the gas in
|
|
39
|
+
:return: The volume of the gas in kg/mol.
|
|
39
40
|
"""
|
|
40
41
|
return gas_gravity * AIR_WEIGHT
|
|
41
42
|
|
|
@@ -47,9 +48,10 @@ def molar_volume(
|
|
|
47
48
|
"""
|
|
48
49
|
calculates molar volume using the ideal gas law.
|
|
49
50
|
:param absolute_temperature: The absolute temperature of the gas in kelvin.
|
|
50
|
-
:param pressure: Confining pressure in
|
|
51
|
-
:return: The volume of the gas in
|
|
51
|
+
:param pressure: Confining pressure in Pa.
|
|
52
|
+
:return: The volume of the gas in m^3/mol.
|
|
52
53
|
"""
|
|
54
|
+
|
|
53
55
|
return gas_constant * absolute_temperature / pressure
|
|
54
56
|
|
|
55
57
|
|
|
@@ -62,8 +64,8 @@ def ideal_gas_density(
|
|
|
62
64
|
calculates molar volume using the ideal gas law.
|
|
63
65
|
:param gas_gravity: molar mass of gas relative to air molar mas.
|
|
64
66
|
:param absolute_temperature: The absolute temperature of the gas in kelvin.
|
|
65
|
-
:param pressure: Confining pressure in
|
|
66
|
-
:return: The density of the gas in
|
|
67
|
+
:param pressure: Confining pressure in Pa.
|
|
68
|
+
:return: The density of the gas in kg/m^3
|
|
67
69
|
"""
|
|
68
70
|
return molecular_weight(gas_gravity) / molar_volume(absolute_temperature, pressure)
|
|
69
71
|
|
|
@@ -89,13 +91,11 @@ def ideal_gas(
|
|
|
89
91
|
:param gas_gravity: molar mass of gas relative to air molar mas.
|
|
90
92
|
:param absolute_temperature: The absolute temperature of the gas in kelvin.
|
|
91
93
|
:param pressure: Confining pressure in Pa.
|
|
92
|
-
:return: ideal_gas_density,
|
|
94
|
+
:return: ideal_gas_velocity [m/s], ideal_gas_density [kg/m^3],
|
|
93
95
|
"""
|
|
94
|
-
ideal_gas_den =
|
|
95
|
-
absolute_temperature, pressure * 1e6, gas_gravity
|
|
96
|
-
)
|
|
96
|
+
ideal_gas_den = ideal_gas_density(absolute_temperature, pressure, gas_gravity)
|
|
97
97
|
ideal_gas_vel = ideal_gas_primary_velocity(absolute_temperature, gas_gravity)
|
|
98
|
-
return
|
|
98
|
+
return ideal_gas_vel, ideal_gas_den
|
|
99
99
|
|
|
100
100
|
|
|
101
101
|
def pseudoreduced_temperature(
|
|
@@ -132,13 +132,13 @@ def pseudoreduced_pressure(
|
|
|
132
132
|
Tech., 22, 889-892.
|
|
133
133
|
|
|
134
134
|
:param gas_gravity: molar mass of gas relative to air molar mas.
|
|
135
|
-
:param pressure: Confining pressure in
|
|
136
|
-
:return: Pseudoreduced pressure in
|
|
135
|
+
:param pressure: Confining pressure in Pa.
|
|
136
|
+
:return: Pseudoreduced pressure in Pa.
|
|
137
137
|
"""
|
|
138
138
|
return pressure / (4.892 - 0.4048 * gas_gravity)
|
|
139
139
|
|
|
140
140
|
|
|
141
|
-
def
|
|
141
|
+
def compressibility_factor(
|
|
142
142
|
absolute_temperature: np.ndarray | float,
|
|
143
143
|
pressure: np.ndarray | float,
|
|
144
144
|
gas_gravity: np.ndarray | float,
|
|
@@ -149,11 +149,13 @@ def compressability_factor(
|
|
|
149
149
|
|
|
150
150
|
:param gas_gravity: molar mass of gas relative to air molar mas.
|
|
151
151
|
:param absolute_temperature: The absolute temperature of the gas in kelvin.
|
|
152
|
-
:param pressure: Confining pressure in
|
|
153
|
-
:return:
|
|
152
|
+
:param pressure: Confining pressure in Pa.
|
|
153
|
+
:return: Gas compressibility - unitless
|
|
154
154
|
"""
|
|
155
155
|
tpr = pseudoreduced_temperature(absolute_temperature, gas_gravity)
|
|
156
|
-
|
|
156
|
+
|
|
157
|
+
# Pseudoreduced pressure has unit MPa in equation
|
|
158
|
+
ppr = pseudoreduced_pressure(pressure, gas_gravity) * 1.0e-6
|
|
157
159
|
|
|
158
160
|
return (
|
|
159
161
|
(0.03 + 0.00527 * (3.5 - tpr) ** 3) * ppr
|
|
@@ -162,7 +164,7 @@ def compressability_factor(
|
|
|
162
164
|
- 0.52
|
|
163
165
|
+ 0.109
|
|
164
166
|
* (3.85 - tpr) ** 2
|
|
165
|
-
/ exp((0.45 + 8 * (0.56 - 1 / tpr) ** 2) * ppr**1.2 / tpr)
|
|
167
|
+
/ exp((0.45 + 8.0 * (0.56 - 1 / tpr) ** 2) * ppr**1.2 / tpr)
|
|
166
168
|
)
|
|
167
169
|
|
|
168
170
|
|
|
@@ -176,18 +178,17 @@ def gas_density(
|
|
|
176
178
|
|
|
177
179
|
:param gas_gravity: molar mass of gas relative to air molar mas.
|
|
178
180
|
:param absolute_temperature: The absolute temperature of the gas in kelvin.
|
|
179
|
-
:param pressure: Confining pressure in
|
|
180
|
-
:return: The density of the gas in
|
|
181
|
+
:param pressure: Confining pressure in Pa.
|
|
182
|
+
:return: The density of the gas in kg/m^3
|
|
181
183
|
"""
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
return ideal_gas_den / compressability_factor(
|
|
184
|
+
|
|
185
|
+
_, ideal_gas_den = ideal_gas(absolute_temperature, pressure, gas_gravity)
|
|
186
|
+
return ideal_gas_den / compressibility_factor(
|
|
186
187
|
absolute_temperature, pressure, gas_gravity
|
|
187
188
|
)
|
|
188
189
|
|
|
189
190
|
|
|
190
|
-
def
|
|
191
|
+
def compressibility_rate_per_pseudoreduced_pressure(
|
|
191
192
|
absolute_temperature: np.ndarray | float,
|
|
192
193
|
pressure: np.ndarray | float,
|
|
193
194
|
gas_gravity: np.ndarray | float,
|
|
@@ -198,10 +199,12 @@ def compressability_rate_per_pseudoreduced_pressure(
|
|
|
198
199
|
:param gas_gravity: molar mass of gas relative to air molar mas.
|
|
199
200
|
:param absolute_temperature: The absolute temperature of the gas in kelvin.
|
|
200
201
|
:param pressure: Confining pressure in MPa.
|
|
201
|
-
:return:
|
|
202
|
+
:return: Derivative of the compressibility factor (unitless) with respect to pseudoreduced pressure
|
|
202
203
|
"""
|
|
203
204
|
tpr = pseudoreduced_temperature(absolute_temperature, gas_gravity)
|
|
204
|
-
|
|
205
|
+
|
|
206
|
+
# Pseudoreduced pressure is expected to be in MPa in the expression
|
|
207
|
+
ppr = pseudoreduced_pressure(pressure, gas_gravity) * 1.0e-6
|
|
205
208
|
|
|
206
209
|
return (
|
|
207
210
|
0.03
|
|
@@ -226,15 +229,16 @@ def gas_bulk_modulus(
|
|
|
226
229
|
|
|
227
230
|
:param gas_gravity: molar mass of gas relative to air molar mas.
|
|
228
231
|
:param absolute_temperature: The absolute temperature of the gas in kelvin.
|
|
229
|
-
:param pressure: Confining pressure in
|
|
230
|
-
:return: The bulk modulus of the gas in
|
|
232
|
+
:param pressure: Confining pressure in Pa.
|
|
233
|
+
:return: The bulk modulus of the gas in Pa.
|
|
231
234
|
"""
|
|
232
|
-
z =
|
|
233
|
-
dz_dppr =
|
|
235
|
+
z = compressibility_factor(absolute_temperature, pressure, gas_gravity)
|
|
236
|
+
dz_dppr = compressibility_rate_per_pseudoreduced_pressure(
|
|
234
237
|
absolute_temperature, pressure, gas_gravity
|
|
235
238
|
)
|
|
236
239
|
|
|
237
|
-
ppr
|
|
240
|
+
# Set ppr in unit MPa in order to use it in calculation of gamma_0
|
|
241
|
+
ppr = pseudoreduced_pressure(pressure, gas_gravity) * 1.0e-6
|
|
238
242
|
|
|
239
243
|
# Equation 11b
|
|
240
244
|
gamma_0 = (
|
|
@@ -247,6 +251,44 @@ def gas_bulk_modulus(
|
|
|
247
251
|
return gamma_0 * pressure / (1 - dz_dppr * ppr / z)
|
|
248
252
|
|
|
249
253
|
|
|
254
|
+
def gas_viscosity(
|
|
255
|
+
absolute_temperature: np.ndarray | float,
|
|
256
|
+
pressure: np.ndarray | float,
|
|
257
|
+
gas_gravity: np.ndarray | float,
|
|
258
|
+
) -> np.ndarray | float:
|
|
259
|
+
"""
|
|
260
|
+
The gas viscosity of hydrocarbon gas, using equations 12 and 13 of Batzle & Wang [1].
|
|
261
|
+
|
|
262
|
+
:param absolute_temperature: The absolute temperature of the gas in kelvin.
|
|
263
|
+
:param pressure: Confining pressure in Pa.
|
|
264
|
+
:param gas_gravity: molar mass of gas relative to air mas.
|
|
265
|
+
:return: The gas viscosity of the gas in cP.
|
|
266
|
+
"""
|
|
267
|
+
temp_pr = pseudoreduced_temperature(absolute_temperature, gas_gravity)
|
|
268
|
+
|
|
269
|
+
# Pseudoreduced pressure should be in unit MPa
|
|
270
|
+
pres_pr = pseudoreduced_pressure(pressure, gas_gravity) * 1.0e-6
|
|
271
|
+
|
|
272
|
+
eta_1 = 0.0001 * (
|
|
273
|
+
temp_pr * (28.0 + 48.0 * gas_gravity - 5.0 * gas_gravity**2)
|
|
274
|
+
- 6.47 * gas_gravity**-2
|
|
275
|
+
+ 35.0 * gas_gravity**-1
|
|
276
|
+
+ 1.14 * gas_gravity
|
|
277
|
+
- 15.55
|
|
278
|
+
)
|
|
279
|
+
return eta_1 * (
|
|
280
|
+
0.001
|
|
281
|
+
* pres_pr
|
|
282
|
+
* (
|
|
283
|
+
(1057.0 - 8.08 * temp_pr) / pres_pr
|
|
284
|
+
+ (796.0 * pres_pr**0.5 - 704.0)
|
|
285
|
+
/ (((temp_pr - 1.0) ** 0.7) * (pres_pr + 1.0))
|
|
286
|
+
- 3.24 * temp_pr
|
|
287
|
+
- 38.0
|
|
288
|
+
)
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
|
|
250
292
|
def lee_gas_viscosity(
|
|
251
293
|
absolute_temperature: np.ndarray | float,
|
|
252
294
|
pressure: np.ndarray | float,
|