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,188 @@
|
|
|
1
|
+
import pickle
|
|
2
|
+
from io import BufferedIOBase
|
|
3
|
+
from typing import Union
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Sigmoid:
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
amplitude: float = None,
|
|
12
|
+
median_point: float = None,
|
|
13
|
+
x_scaling: float = None,
|
|
14
|
+
bias: float = None,
|
|
15
|
+
description="",
|
|
16
|
+
):
|
|
17
|
+
self._amplitude = amplitude
|
|
18
|
+
self._median_point = median_point
|
|
19
|
+
self._x_scaling = x_scaling
|
|
20
|
+
self._bias = bias
|
|
21
|
+
self._description = description
|
|
22
|
+
|
|
23
|
+
def todict(self):
|
|
24
|
+
return {
|
|
25
|
+
"amplitude": self._amplitude,
|
|
26
|
+
"median_point": self._median_point,
|
|
27
|
+
"x_scaling": self._x_scaling,
|
|
28
|
+
"bias": self._bias,
|
|
29
|
+
"description": self._description,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def amplitude(self):
|
|
34
|
+
return self._amplitude
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def median_point(self):
|
|
38
|
+
return self._median_point
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def x_scaling(self):
|
|
42
|
+
return self._x_scaling
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def bias(self):
|
|
46
|
+
return self._bias
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def description(self):
|
|
50
|
+
return self._description
|
|
51
|
+
|
|
52
|
+
def predict(self, x):
|
|
53
|
+
if isinstance(x, list):
|
|
54
|
+
x = np.array(x)
|
|
55
|
+
if not self._valid():
|
|
56
|
+
return None
|
|
57
|
+
return (
|
|
58
|
+
self._amplitude / (1 + np.exp(-self._x_scaling * (x - self._median_point)))
|
|
59
|
+
+ self._bias
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def predict_amp(self, x, amp):
|
|
63
|
+
if isinstance(x, list):
|
|
64
|
+
x = np.array(x)
|
|
65
|
+
if isinstance(amp, list):
|
|
66
|
+
amp = np.array(amp)
|
|
67
|
+
if not self._valid(variant="amp"):
|
|
68
|
+
return None
|
|
69
|
+
return (
|
|
70
|
+
amp / (1 + np.exp(-self._x_scaling * (x - self._median_point))) + self._bias
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
@classmethod
|
|
74
|
+
def save(cls, file):
|
|
75
|
+
with open(file, "wb") as f_out:
|
|
76
|
+
pickle.dump(cls, f_out)
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def load(cls, file):
|
|
80
|
+
with open(file, "rb") as f_in:
|
|
81
|
+
return pickle.load(f_in)
|
|
82
|
+
|
|
83
|
+
def _valid(self, variant=None):
|
|
84
|
+
if variant is None and self.amplitude is None:
|
|
85
|
+
raise ValueError('object field "variant" is not set')
|
|
86
|
+
if self.median_point is None:
|
|
87
|
+
raise ValueError('object field "median_point" is not set')
|
|
88
|
+
if self.x_scaling is None:
|
|
89
|
+
raise ValueError('object field "x_scaling" is not set')
|
|
90
|
+
if self.bias is None:
|
|
91
|
+
raise ValueError('object field "bias" is not set')
|
|
92
|
+
return True
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class CarbonateSigmoidalPressure:
|
|
96
|
+
def __init__(
|
|
97
|
+
self,
|
|
98
|
+
phi_model: Union[dict, Sigmoid] = None,
|
|
99
|
+
p_eff_model: Union[dict, Sigmoid] = None,
|
|
100
|
+
):
|
|
101
|
+
if isinstance(phi_model, Sigmoid):
|
|
102
|
+
self._phi_model = phi_model
|
|
103
|
+
elif isinstance(phi_model, dict):
|
|
104
|
+
self._phi_model = Sigmoid(**phi_model)
|
|
105
|
+
else:
|
|
106
|
+
self._phi_model = None
|
|
107
|
+
if isinstance(p_eff_model, Sigmoid):
|
|
108
|
+
self._p_eff_model = p_eff_model
|
|
109
|
+
elif isinstance(p_eff_model, dict):
|
|
110
|
+
self._p_eff_model = Sigmoid(**p_eff_model)
|
|
111
|
+
else:
|
|
112
|
+
self._p_eff_model = None
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def phi_model(self):
|
|
116
|
+
return self._phi_model
|
|
117
|
+
|
|
118
|
+
@phi_model.setter
|
|
119
|
+
def phi_model(self, phi_mod):
|
|
120
|
+
if isinstance(phi_mod, Sigmoid):
|
|
121
|
+
self._phi_model = phi_mod
|
|
122
|
+
else:
|
|
123
|
+
raise ValueError(
|
|
124
|
+
f"{type(self)}: expected input Sigmoid object, received {type(phi_mod)}"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def p_eff_model(self):
|
|
129
|
+
return self._p_eff_model
|
|
130
|
+
|
|
131
|
+
@p_eff_model.setter
|
|
132
|
+
def p_eff_model(self, p_eff_mod):
|
|
133
|
+
if isinstance(p_eff_mod, Sigmoid):
|
|
134
|
+
self._p_eff_model = p_eff_mod
|
|
135
|
+
else:
|
|
136
|
+
raise ValueError(
|
|
137
|
+
f"{type(self)}: expected input Sigmoid object, received {type(p_eff_mod)}"
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
def predict(self, inp_arr: np.ndarray) -> np.ndarray:
|
|
141
|
+
# Don't save any of the intermediate calculations, only return the difference between the effective pressure
|
|
142
|
+
# cases. The method name is set to be the same as for other machine learning models
|
|
143
|
+
self._validate_input(inp_arr)
|
|
144
|
+
velocity_amp = self._phi_model.predict(inp_arr[:, 0].flatten())
|
|
145
|
+
velocity_init = self._p_eff_model.predict_amp(
|
|
146
|
+
inp_arr[:, 1].flatten(), velocity_amp
|
|
147
|
+
)
|
|
148
|
+
velocity_depl = self._p_eff_model.predict_amp(
|
|
149
|
+
inp_arr[:, 2].flatten(), velocity_amp
|
|
150
|
+
)
|
|
151
|
+
return velocity_depl - velocity_init
|
|
152
|
+
|
|
153
|
+
def predict_abs(self, inp_arr: np.ndarray, case: str = "in_situ") -> np.ndarray:
|
|
154
|
+
# Method for access to absolute results, not just the difference
|
|
155
|
+
self._validate_input(inp_arr)
|
|
156
|
+
velocity_amp = self._phi_model.predict(inp_arr[:, 0].flatten())
|
|
157
|
+
if case == "in_situ":
|
|
158
|
+
return self._p_eff_model.predict_amp(inp_arr[:, 1].flatten(), velocity_amp)
|
|
159
|
+
return self._p_eff_model.predict_amp(inp_arr[:, 2].flatten(), velocity_amp)
|
|
160
|
+
|
|
161
|
+
def _validate_input(self, input_array):
|
|
162
|
+
if not isinstance(input_array, np.ndarray):
|
|
163
|
+
raise ValueError(
|
|
164
|
+
f"{type(input_array)}: should be numpy array with shape n x 3"
|
|
165
|
+
)
|
|
166
|
+
if not ((input_array.ndim == 2) and (input_array.shape[1] == 3)):
|
|
167
|
+
raise ValueError(
|
|
168
|
+
f'{type(self)}: Input array should be of shape n x 3, with columns in sequence "PHIT", '
|
|
169
|
+
f'"P_EFF_in_situ" and "P_EFF_depleted"'
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
def todict(self):
|
|
173
|
+
return {
|
|
174
|
+
"phi_model": self._phi_model.todict(),
|
|
175
|
+
"p_eff_model": self._p_eff_model.todict(),
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
def save(self, file: Union[str, BufferedIOBase]):
|
|
179
|
+
with open(file, "wb") as f_out:
|
|
180
|
+
pickle.dump(self.todict(), f_out)
|
|
181
|
+
|
|
182
|
+
@classmethod
|
|
183
|
+
def load(cls, file: Union[str, BufferedIOBase]):
|
|
184
|
+
with open(file, "rb") as f_in:
|
|
185
|
+
load_dict = pickle.load(f_in)
|
|
186
|
+
return cls(
|
|
187
|
+
phi_model=load_dict["phi_model"], p_eff_model=load_dict["p_eff_model"]
|
|
188
|
+
)
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import math
|
|
3
|
+
import re
|
|
4
|
+
from typing import Union
|
|
5
|
+
from warnings import warn
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
from pandas import DataFrame
|
|
9
|
+
|
|
10
|
+
from rock_physics_open.equinor_utilities.various_utilities import disp_result_stats
|
|
11
|
+
|
|
12
|
+
from .snapshots import get_snapshot_name
|
|
13
|
+
|
|
14
|
+
DISPLAY_RESULTS = False
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def compare_snapshots(
|
|
18
|
+
test_results: Union[np.ndarray, tuple, DataFrame],
|
|
19
|
+
saved_results: tuple,
|
|
20
|
+
) -> bool:
|
|
21
|
+
test_results = _validate_input(test_results, saved_results)
|
|
22
|
+
|
|
23
|
+
if DISPLAY_RESULTS:
|
|
24
|
+
title = str(inspect.stack()[1].function)
|
|
25
|
+
name_arr = [f"arr_{i}" for i in range(len(test_results))]
|
|
26
|
+
disp_result_stats(title, test_results, name_arr)
|
|
27
|
+
|
|
28
|
+
r_tol = 0.01
|
|
29
|
+
equal_nan = True
|
|
30
|
+
no_difference_found = True
|
|
31
|
+
|
|
32
|
+
for i, (test_item, saved_item) in enumerate(zip(test_results, saved_results)):
|
|
33
|
+
try:
|
|
34
|
+
np.testing.assert_allclose(
|
|
35
|
+
test_item,
|
|
36
|
+
saved_item,
|
|
37
|
+
rtol=r_tol,
|
|
38
|
+
equal_nan=equal_nan,
|
|
39
|
+
err_msg=f"saved and generated result for variable {i} differ",
|
|
40
|
+
)
|
|
41
|
+
except AssertionError as error:
|
|
42
|
+
open_mode = "w" if no_difference_found else "a"
|
|
43
|
+
no_difference_found = False
|
|
44
|
+
warn(f"comparison test failed for item {i}: {error}")
|
|
45
|
+
log_file = re.sub("npz", "log", get_snapshot_name(step=2))
|
|
46
|
+
|
|
47
|
+
with open(log_file, open_mode) as file:
|
|
48
|
+
file.write(f"Test filepath: {str(inspect.stack()[1].filename)} \n")
|
|
49
|
+
file.write(f"Test function: {str(inspect.stack()[1].function)} \n")
|
|
50
|
+
file.write(f"Test variable number: {i} \n")
|
|
51
|
+
|
|
52
|
+
for line in str(error).splitlines():
|
|
53
|
+
mismatched_elements_index = (
|
|
54
|
+
line.replace(" ", "").lower().find("mismatchedelements")
|
|
55
|
+
)
|
|
56
|
+
if mismatched_elements_index != -1:
|
|
57
|
+
file.write(line + "\n")
|
|
58
|
+
continue
|
|
59
|
+
|
|
60
|
+
max_abs_diff_index = (
|
|
61
|
+
line.replace(" ", "").lower().find("maxabsolutedifference")
|
|
62
|
+
)
|
|
63
|
+
if max_abs_diff_index != -1:
|
|
64
|
+
file.write(line + "\n")
|
|
65
|
+
continue
|
|
66
|
+
|
|
67
|
+
max_rel_diff_index = (
|
|
68
|
+
line.replace(" ", "").lower().find("maxrelativedifference")
|
|
69
|
+
)
|
|
70
|
+
if max_rel_diff_index != -1:
|
|
71
|
+
file.write(line + "\n")
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
reg_index = re.search(r"\d+ differ", line)
|
|
75
|
+
|
|
76
|
+
if reg_index:
|
|
77
|
+
if isinstance(test_item, np.ndarray):
|
|
78
|
+
differences, num_nans = _compare_ndarray(
|
|
79
|
+
saved_item, test_item, equal_nan, r_tol
|
|
80
|
+
)
|
|
81
|
+
elif isinstance(test_results, DataFrame):
|
|
82
|
+
differences, num_nans = _compare_df(
|
|
83
|
+
saved_item, test_item, equal_nan, r_tol
|
|
84
|
+
)
|
|
85
|
+
file.write("Number of NaN elements: " + str(num_nans) + "\n")
|
|
86
|
+
file.write("Index:\t\tSaved:\t\tGenerated:\n")
|
|
87
|
+
|
|
88
|
+
# Write test results and saved results differences to file
|
|
89
|
+
if len(differences) > 0:
|
|
90
|
+
tab = "\t"
|
|
91
|
+
for difference in differences:
|
|
92
|
+
file.write(
|
|
93
|
+
f"{tab}[{difference[0]:4}]=> {difference[1]:.4g} != {difference[2]:.4g}\n"
|
|
94
|
+
)
|
|
95
|
+
file.write(f"{'_' * 40}\n")
|
|
96
|
+
return no_difference_found
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _compare_ndarray(
|
|
100
|
+
saved_array: np.ndarray, test_array: np.ndarray, eq_nan: bool, rel_tol: float
|
|
101
|
+
) -> (list, int):
|
|
102
|
+
differ_indexes = np.where(saved_array != test_array)[0]
|
|
103
|
+
differences = []
|
|
104
|
+
num_nans = 0
|
|
105
|
+
|
|
106
|
+
for index in differ_indexes:
|
|
107
|
+
if eq_nan and (
|
|
108
|
+
np.isnan(test_array[int(index)]) and np.isnan(saved_array[int(index)])
|
|
109
|
+
):
|
|
110
|
+
num_nans += 1
|
|
111
|
+
elif math.isclose(
|
|
112
|
+
saved_array[int(index)], test_array[int(index)], rel_tol=rel_tol
|
|
113
|
+
):
|
|
114
|
+
pass
|
|
115
|
+
else:
|
|
116
|
+
differences.append([index, saved_array[index], test_array[index]])
|
|
117
|
+
return differences, num_nans
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _compare_df(saved_results, test_results, equal_nan, r_tol):
|
|
121
|
+
return _compare_ndarray(
|
|
122
|
+
saved_results.to_numpy().flatten(),
|
|
123
|
+
test_results.to_numpy().flatten(),
|
|
124
|
+
equal_nan,
|
|
125
|
+
r_tol,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _validate_input(test_obj, saved_obj: tuple) -> tuple:
|
|
130
|
+
# Check for compatibility of test results and stored data
|
|
131
|
+
if isinstance(test_obj, (np.ndarray, DataFrame)):
|
|
132
|
+
return_test_obj = (test_obj,)
|
|
133
|
+
else:
|
|
134
|
+
return_test_obj = test_obj
|
|
135
|
+
if isinstance(return_test_obj, (tuple, list)):
|
|
136
|
+
if len(saved_obj) != len(return_test_obj):
|
|
137
|
+
raise ValueError(
|
|
138
|
+
f"unable to compare snapshots, different number of saved: ({len(saved_obj)})"
|
|
139
|
+
f" and generated results ({len(test_obj)})"
|
|
140
|
+
)
|
|
141
|
+
else:
|
|
142
|
+
raise ValueError(
|
|
143
|
+
f"test_obj should be one of list, tuple, numpy array or pandas DataFrame, is {type(test_obj)}"
|
|
144
|
+
)
|
|
145
|
+
return return_test_obj
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import os
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
INITIATE = False
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_snapshot_name(step: int = 1, include_snapshot_dir=True) -> str:
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
Parameters
|
|
14
|
+
----------
|
|
15
|
+
step: number of steps in the trace to collect information from
|
|
16
|
+
include_snapshot_dir: absolute directory name included in snapshot name
|
|
17
|
+
|
|
18
|
+
Returns
|
|
19
|
+
-------
|
|
20
|
+
name of snapshot file
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
# Get filename and function name of calling function
|
|
24
|
+
trace = inspect.stack()
|
|
25
|
+
dir_name = Path(trace[step].filename).parent.joinpath("snapshots")
|
|
26
|
+
file_name = "_".join(
|
|
27
|
+
(Path(trace[step].filename).stem, trace[step].function + ".npz")
|
|
28
|
+
)
|
|
29
|
+
return os.path.join(dir_name, file_name) if include_snapshot_dir else file_name
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def store_snapshot(snapshot_name: str, *args: np.ndarray) -> bool:
|
|
33
|
+
"""
|
|
34
|
+
Examples
|
|
35
|
+
--------
|
|
36
|
+
In case there are multiple arrays to store:
|
|
37
|
+
store_snapshot(snapshot_name='snap_to_store.npz', *args)
|
|
38
|
+
|
|
39
|
+
Important: If there is only one array to store:
|
|
40
|
+
store_snapshot(snapshot_name='snap_to_store.npz', args)
|
|
41
|
+
"""
|
|
42
|
+
try:
|
|
43
|
+
np.savez(snapshot_name, *args)
|
|
44
|
+
except IOError as e:
|
|
45
|
+
raise IOError(f"Could not store snapshot {snapshot_name}: {e}")
|
|
46
|
+
return True
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def read_snapshot(snapshot_name: str) -> tuple:
|
|
50
|
+
try:
|
|
51
|
+
with np.load(snapshot_name) as stored_npz:
|
|
52
|
+
return tuple(stored_npz[arr_name] for arr_name in stored_npz.files)
|
|
53
|
+
except IOError as e:
|
|
54
|
+
raise ValueError(f"unable to load snapshot {snapshot_name}: {e}")
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from .backus_ave import backus_average
|
|
2
|
+
from .dvorkin_nur import dvorkin_contact_cement
|
|
3
|
+
from .gassmann import gassmann, gassmann2, gassmann_dry
|
|
4
|
+
from .hashin_shtrikman import (
|
|
5
|
+
hashin_shtrikman,
|
|
6
|
+
hashin_shtrikman_average,
|
|
7
|
+
hashin_shtrikman_walpole,
|
|
8
|
+
multi_hashin_shtrikman,
|
|
9
|
+
)
|
|
10
|
+
from .hertz_mindlin import hertz_mindlin
|
|
11
|
+
from .moduli_velocity import moduli, velocity
|
|
12
|
+
from .reflection_eq import aki_richards, smith_gidlow
|
|
13
|
+
from .rho import rho_b, rho_m
|
|
14
|
+
from .voigt_reuss_hill import multi_voigt_reuss_hill, reuss, voigt, voigt_reuss_hill
|
|
15
|
+
from .walton import walton_smooth
|
|
16
|
+
from .wood_brie import brie, multi_wood, wood
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"backus_average",
|
|
20
|
+
"dvorkin_contact_cement",
|
|
21
|
+
"gassmann",
|
|
22
|
+
"gassmann2",
|
|
23
|
+
"gassmann_dry",
|
|
24
|
+
"hashin_shtrikman",
|
|
25
|
+
"hashin_shtrikman_average",
|
|
26
|
+
"hashin_shtrikman_walpole",
|
|
27
|
+
"multi_hashin_shtrikman",
|
|
28
|
+
"hertz_mindlin",
|
|
29
|
+
"moduli",
|
|
30
|
+
"velocity",
|
|
31
|
+
"aki_richards",
|
|
32
|
+
"smith_gidlow",
|
|
33
|
+
"rho_b",
|
|
34
|
+
"rho_m",
|
|
35
|
+
"multi_voigt_reuss_hill",
|
|
36
|
+
"reuss",
|
|
37
|
+
"voigt",
|
|
38
|
+
"voigt_reuss_hill",
|
|
39
|
+
"walton_smooth",
|
|
40
|
+
"brie",
|
|
41
|
+
"multi_wood",
|
|
42
|
+
"wood",
|
|
43
|
+
]
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def backus_average(vp1, vs1, rho1, vp2, vs2, rho2, f1):
|
|
5
|
+
"""
|
|
6
|
+
Backus average for a combination of two phases. The individual phases are isotropic
|
|
7
|
+
but the resulting effective medium is not.
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
vp1 : np.ndarray
|
|
12
|
+
Pressure wave velocity for phase 1 [m/s].
|
|
13
|
+
vs1 : np.ndarray
|
|
14
|
+
Shear wave velocity for phase 1 [m/s].
|
|
15
|
+
rho1 : np.ndarray
|
|
16
|
+
Density for phase 1 [kg/m^3].
|
|
17
|
+
vp2 : np.ndarray
|
|
18
|
+
Pressure wave velocity for phase 2 [m/s].
|
|
19
|
+
vs2 : np.ndarray
|
|
20
|
+
Shear wave velocity for phase 2 [m/s].
|
|
21
|
+
rho2 : np.ndarray
|
|
22
|
+
Density for phase 2 [kg/m^3].
|
|
23
|
+
f1 : np.ndarray
|
|
24
|
+
Fraction of phase 1.
|
|
25
|
+
|
|
26
|
+
Returns
|
|
27
|
+
-------
|
|
28
|
+
tuple
|
|
29
|
+
vpv, vsv, vph, vsh, rho : np.ndarray
|
|
30
|
+
vpv: vertical pressure velocity, vsv: vertical shear velocity, vph: horizontal pressure velocity,
|
|
31
|
+
vsh: horizontal shear velocity, rho: density.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
a = (
|
|
35
|
+
4 * f1 * rho1 * vs1**2 * (1 - vs1**2 / vp1**2)
|
|
36
|
+
+ 4 * (1 - f1) * rho2 * vs2**2 * (1 - (vs2 / vp2) ** 2)
|
|
37
|
+
+ (f1 * (1 - 2 * (vs1 / vp1) ** 2) + (1 - f1) * (1 - 2 * (vs2 / vp2) ** 2)) ** 2
|
|
38
|
+
* (1 / (f1 / (rho1 * vp1**2) + (1 - f1) / (rho2 * vp2**2)))
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
c = 1 / (f1 / (rho1 * vp1**2) + (1 - f1) / (rho2 * vp2**2))
|
|
42
|
+
d = 1 / (f1 / (rho1 * vs1**2) + (1 - f1) / (rho2 * vs2**2))
|
|
43
|
+
|
|
44
|
+
m = f1 * rho1 * vs1**2 + (1 - f1) * rho2 * vs2**2
|
|
45
|
+
|
|
46
|
+
rho = f1 * rho1 + (1 - f1) * rho2
|
|
47
|
+
|
|
48
|
+
vpv = np.sqrt(c / rho)
|
|
49
|
+
vsv = np.sqrt(d / rho)
|
|
50
|
+
vph = np.sqrt(a / rho)
|
|
51
|
+
vsh = np.sqrt(m / rho)
|
|
52
|
+
|
|
53
|
+
return vpv, vsv, vph, vsh, rho
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def dvorkin_contact_cement(
|
|
5
|
+
frac_cem, por0_sst, mu0_sst, k0_sst, mu0_cem, k0_cem, vs_red, c
|
|
6
|
+
):
|
|
7
|
+
"""
|
|
8
|
+
Dvorkin-Nur contact cement model for estimation of elastic moduli.
|
|
9
|
+
|
|
10
|
+
Parameters
|
|
11
|
+
----------
|
|
12
|
+
frac_cem : numpy.ndarray
|
|
13
|
+
Cement fraction of volume [ratio].
|
|
14
|
+
por0_sst : numpy.ndarray
|
|
15
|
+
Critical porosity of sand [ratio].
|
|
16
|
+
mu0_sst : numpy.ndarray
|
|
17
|
+
Mineral shear modulus of sand [Pa].
|
|
18
|
+
k0_sst : numpy.ndarray
|
|
19
|
+
Mineral bulk modulus of sand [Pa].
|
|
20
|
+
mu0_cem : numpy.ndarray
|
|
21
|
+
Mineral shear modulus of cement [Pa].
|
|
22
|
+
k0_cem : numpy.ndarray
|
|
23
|
+
Mineral bulk modulus of cement [Pa].
|
|
24
|
+
vs_red : numpy.ndarray
|
|
25
|
+
Shear modulus reduction factor [ratio].
|
|
26
|
+
c : float
|
|
27
|
+
Coordination number (grain contacts per grain) [unitless].
|
|
28
|
+
|
|
29
|
+
Returns
|
|
30
|
+
-------
|
|
31
|
+
tuple
|
|
32
|
+
k_cc, mu_cc : numpy.ndarray.
|
|
33
|
+
k_cc: bulk modulus [Pa], mu_cc: shear modulus [Pa].
|
|
34
|
+
"""
|
|
35
|
+
alpha = (2 * frac_cem / (3 * (1 - por0_sst))) ** 0.5
|
|
36
|
+
poiss = (3 * k0_sst - 2 * mu0_sst) / (2 * (3 * k0_sst + mu0_sst))
|
|
37
|
+
poiss_c = (3 * k0_cem - 2 * mu0_cem) / (2 * (3 * k0_cem + mu0_cem))
|
|
38
|
+
a_an = (2 * mu0_cem / (np.pi * mu0_sst)) * (
|
|
39
|
+
(1 - poiss) * (1 - poiss_c) / (1 - 2 * poiss_c)
|
|
40
|
+
)
|
|
41
|
+
a_at = mu0_cem / (np.pi * mu0_sst)
|
|
42
|
+
|
|
43
|
+
a_t = (
|
|
44
|
+
-1e-2
|
|
45
|
+
* (2.26 * poiss**2 + 2.07 * poiss + 2.3)
|
|
46
|
+
* a_at ** (0.079 * poiss**2 + 0.1754 * poiss - 1.342)
|
|
47
|
+
)
|
|
48
|
+
b_t = (0.0573 * poiss**2 + 0.0937 * poiss + 0.202) * a_at ** (
|
|
49
|
+
0.0274 * poiss**2 + 0.0529 * poiss - 0.8765
|
|
50
|
+
)
|
|
51
|
+
c_t = (
|
|
52
|
+
1e-4
|
|
53
|
+
* (9.654 * poiss**2 + 4.945 * poiss + 3.1)
|
|
54
|
+
* a_at ** (0.01867 * poiss**2 + 0.4011 * poiss - 1.8186)
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
s_t = a_t * alpha**2 + b_t * alpha + c_t
|
|
58
|
+
|
|
59
|
+
c_n = 0.00024649 * a_an ** (-1.9864)
|
|
60
|
+
b_n = 0.20405 * a_an ** (-0.89008)
|
|
61
|
+
a_n = -0.024153 * a_an ** (-1.3646)
|
|
62
|
+
|
|
63
|
+
s_n = a_n * alpha**2 + b_n * alpha + c_n
|
|
64
|
+
|
|
65
|
+
m0_cem = k0_cem + 4 / 3 * mu0_cem
|
|
66
|
+
k_cc = (1 / 6) * c * (1 - por0_sst) * m0_cem * s_n
|
|
67
|
+
mu_cc = (3 / 5) * k_cc + vs_red * (3 / 20) * c * (1 - por0_sst) * mu0_cem * s_t
|
|
68
|
+
|
|
69
|
+
return k_cc, mu_cc
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from rock_physics_open.equinor_utilities.gen_utilities import dim_check_vector
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def gassmann(k_dry, por, k_fl, k_min):
|
|
9
|
+
"""
|
|
10
|
+
Fluid substitution according to the Gassmann equation.
|
|
11
|
+
|
|
12
|
+
Parameters
|
|
13
|
+
----------
|
|
14
|
+
k_dry : np.ndarray
|
|
15
|
+
Dry rock bulk modulus [Pa].
|
|
16
|
+
por : np.ndarray
|
|
17
|
+
Porosity [fraction].
|
|
18
|
+
k_fl : np.ndarray
|
|
19
|
+
Fluid bulk modulus.
|
|
20
|
+
k_min : np.ndarray
|
|
21
|
+
Mineral bulk modulus [Pa].
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
np.ndarray
|
|
26
|
+
k_sat: bulk modulus for saturated rock [Pa]
|
|
27
|
+
"""
|
|
28
|
+
k_dry, por, k_fl, k_min = dim_check_vector((k_dry, por, k_fl, k_min))
|
|
29
|
+
|
|
30
|
+
idx = np.logical_or(k_dry == k_min, por == 0)
|
|
31
|
+
k_sat = np.ones(k_dry.shape) * np.nan
|
|
32
|
+
b = k_dry[~idx] / (k_min[~idx] - k_dry[~idx]) + k_fl[~idx] / (
|
|
33
|
+
(k_min[~idx] - k_fl[~idx]) * por[~idx]
|
|
34
|
+
)
|
|
35
|
+
idx1 = b < 0
|
|
36
|
+
if any(idx1):
|
|
37
|
+
b[idx1] = np.nan
|
|
38
|
+
|
|
39
|
+
k_sat[~idx] = b / (1 + b) * k_min[~idx]
|
|
40
|
+
k_sat[idx] = k_min[idx]
|
|
41
|
+
|
|
42
|
+
return k_sat
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def gassmann2(k_sat_1, k_fl_1, k_fl_2, por, k_min):
|
|
46
|
+
"""
|
|
47
|
+
Fluid substitution by Gassmann method with substitution of one fluid to another
|
|
48
|
+
|
|
49
|
+
Parameters
|
|
50
|
+
----------
|
|
51
|
+
k_sat_1 : np.ndarray
|
|
52
|
+
bulk modulus for saturated rock with original fluid [Pa]
|
|
53
|
+
k_fl_1 : np.ndarray
|
|
54
|
+
bulk modulus for original fluid [Pa]
|
|
55
|
+
k_fl_2 : np.ndarray
|
|
56
|
+
bulk modulus for replaced fluid [Pa]
|
|
57
|
+
por : np.ndarray
|
|
58
|
+
porosity of rock [fraction]
|
|
59
|
+
k_min : np.ndarray
|
|
60
|
+
mineral bulk modulus of rock [Pa]
|
|
61
|
+
|
|
62
|
+
Returns
|
|
63
|
+
-------
|
|
64
|
+
np.ndarray
|
|
65
|
+
k_sat_2: bulk modulus of rock saturated with replaced fluid [Pa]
|
|
66
|
+
"""
|
|
67
|
+
k_sat_1, k_fl_1, k_fl_2, por, k_min = dim_check_vector(
|
|
68
|
+
(k_sat_1, k_fl_1, k_fl_2, por, k_min)
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
idx = np.any(
|
|
72
|
+
np.array([k_sat_1 == k_min, por == 0, k_fl_1 == k_min, k_fl_2 == k_min]), axis=0
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
k_sat_2 = np.ones(k_sat_1.shape) * np.nan
|
|
76
|
+
|
|
77
|
+
b = (
|
|
78
|
+
k_fl_2[~idx] / (por[~idx] * (k_min[~idx] - k_fl_2[~idx]))
|
|
79
|
+
- k_fl_1[~idx] / (por[~idx] * (k_min[~idx] - k_fl_1[~idx]))
|
|
80
|
+
+ k_sat_1[~idx] / (k_min[~idx] - k_sat_1[~idx])
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
idx1 = b < 0
|
|
84
|
+
if any(idx1):
|
|
85
|
+
warn_str = (
|
|
86
|
+
"{0:d} unstable solution(s) to Gassmann equation, changed to NaN".format(
|
|
87
|
+
np.sum(idx1)
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
warnings.warn(warn_str, UserWarning)
|
|
91
|
+
b[idx1] = np.nan
|
|
92
|
+
|
|
93
|
+
k_sat_2[~idx] = b / (1 + b) * k_min[~idx]
|
|
94
|
+
k_sat_2[idx] = k_sat_1[idx]
|
|
95
|
+
|
|
96
|
+
return k_sat_2
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def gassmann_dry(k_sat, por, k_fl, k_min):
|
|
100
|
+
"""
|
|
101
|
+
Dry rock properties of saturated rock by Gassmann equation
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
k_sat : np.ndarray
|
|
106
|
+
saturated rock bulk modulus [Pa]
|
|
107
|
+
por : np.ndarray
|
|
108
|
+
porosity of rock [fraction]
|
|
109
|
+
k_fl : np.ndarray
|
|
110
|
+
bulk modulus of fluid [Pa]
|
|
111
|
+
k_min : np.ndarray
|
|
112
|
+
bulk modulus of mineral [Pa]
|
|
113
|
+
|
|
114
|
+
Returns
|
|
115
|
+
-------
|
|
116
|
+
np.ndarray
|
|
117
|
+
k_dry: dry rock bulk modulus [Pa]
|
|
118
|
+
"""
|
|
119
|
+
k_sat, por, k_fl, k_min = dim_check_vector((k_sat, por, k_fl, k_min))
|
|
120
|
+
|
|
121
|
+
idx = np.any(np.array([k_sat == k_min, por == 0, k_fl == k_min]), axis=0)
|
|
122
|
+
k_dry = np.ones(k_sat.shape)
|
|
123
|
+
b = k_sat[~idx] / (k_min[~idx] - k_sat[~idx]) - k_fl[~idx] / (
|
|
124
|
+
(k_min[~idx] - k_fl[~idx]) * por[~idx]
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
idx1 = b < 0
|
|
128
|
+
if any(idx1):
|
|
129
|
+
warn_str = (
|
|
130
|
+
"{0:d} unstable solution(s) to Gassmann equation, changed to NaN".format(
|
|
131
|
+
np.sum(idx1)
|
|
132
|
+
)
|
|
133
|
+
)
|
|
134
|
+
warnings.warn(warn_str, UserWarning)
|
|
135
|
+
b[idx1] = np.nan
|
|
136
|
+
|
|
137
|
+
k_dry[~idx] = b / (1 + b) * k_min[~idx]
|
|
138
|
+
k_dry[idx] = k_min[idx]
|
|
139
|
+
|
|
140
|
+
return k_dry
|