rock-physics-open 0.3.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rock_physics_open/__init__.py +0 -0
- rock_physics_open/equinor_utilities/__init__.py +0 -0
- rock_physics_open/equinor_utilities/anisotropy.py +211 -0
- rock_physics_open/equinor_utilities/classification_functions/__init__.py +17 -0
- rock_physics_open/equinor_utilities/classification_functions/class_stats.py +68 -0
- rock_physics_open/equinor_utilities/classification_functions/lin_class.py +53 -0
- rock_physics_open/equinor_utilities/classification_functions/mahal_class.py +63 -0
- rock_physics_open/equinor_utilities/classification_functions/norm_class.py +73 -0
- rock_physics_open/equinor_utilities/classification_functions/poly_class.py +45 -0
- rock_physics_open/equinor_utilities/classification_functions/post_prob.py +27 -0
- rock_physics_open/equinor_utilities/classification_functions/two_step_classification.py +60 -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 +38 -0
- rock_physics_open/equinor_utilities/gen_utilities/dim_check_vector.py +113 -0
- rock_physics_open/equinor_utilities/gen_utilities/filter_input.py +131 -0
- rock_physics_open/equinor_utilities/gen_utilities/filter_output.py +88 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/__init__.py +15 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/base_pressure_model.py +170 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/dummy_vars.py +53 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/exponential_model.py +137 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/import_ml_models.py +77 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/polynomial_model.py +132 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/run_regression.py +209 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/sigmoidal_model.py +241 -0
- rock_physics_open/equinor_utilities/optimisation_utilities/__init__.py +19 -0
- rock_physics_open/equinor_utilities/optimisation_utilities/opt_subst_utilities.py +455 -0
- rock_physics_open/equinor_utilities/snapshot_test_utilities/__init__.py +10 -0
- rock_physics_open/equinor_utilities/snapshot_test_utilities/compare_snapshots.py +184 -0
- rock_physics_open/equinor_utilities/snapshot_test_utilities/snapshots.py +97 -0
- rock_physics_open/equinor_utilities/std_functions/__init__.py +43 -0
- rock_physics_open/equinor_utilities/std_functions/backus_ave.py +68 -0
- rock_physics_open/equinor_utilities/std_functions/dvorkin_nur.py +77 -0
- rock_physics_open/equinor_utilities/std_functions/gassmann.py +165 -0
- rock_physics_open/equinor_utilities/std_functions/hashin_shtrikman.py +224 -0
- rock_physics_open/equinor_utilities/std_functions/hertz_mindlin.py +51 -0
- rock_physics_open/equinor_utilities/std_functions/moduli_velocity.py +67 -0
- rock_physics_open/equinor_utilities/std_functions/reflection_eq.py +120 -0
- rock_physics_open/equinor_utilities/std_functions/rho.py +69 -0
- rock_physics_open/equinor_utilities/std_functions/voigt_reuss_hill.py +149 -0
- rock_physics_open/equinor_utilities/std_functions/walton.py +45 -0
- rock_physics_open/equinor_utilities/std_functions/wood_brie.py +94 -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 +90 -0
- rock_physics_open/equinor_utilities/various_utilities/gassmann_dry_mod.py +56 -0
- rock_physics_open/equinor_utilities/various_utilities/gassmann_mod.py +56 -0
- rock_physics_open/equinor_utilities/various_utilities/gassmann_sub_mod.py +64 -0
- rock_physics_open/equinor_utilities/various_utilities/hs_average.py +59 -0
- rock_physics_open/equinor_utilities/various_utilities/pressure.py +96 -0
- rock_physics_open/equinor_utilities/various_utilities/reflectivity.py +101 -0
- rock_physics_open/equinor_utilities/various_utilities/timeshift.py +104 -0
- rock_physics_open/equinor_utilities/various_utilities/vp_vs_rho_set_statistics.py +170 -0
- rock_physics_open/equinor_utilities/various_utilities/vrh_3_min.py +83 -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 +178 -0
- rock_physics_open/fluid_models/gas_model/__init__.py +5 -0
- rock_physics_open/fluid_models/gas_model/gas_properties.py +319 -0
- rock_physics_open/fluid_models/oil_model/__init__.py +5 -0
- rock_physics_open/fluid_models/oil_model/dead_oil_density.py +65 -0
- rock_physics_open/fluid_models/oil_model/dead_oil_velocity.py +30 -0
- rock_physics_open/fluid_models/oil_model/live_oil_density.py +82 -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 +146 -0
- rock_physics_open/sandstone_models/__init__.py +59 -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 +125 -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 +177 -0
- rock_physics_open/sandstone_models/friable_optimisation.py +115 -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 +384 -0
- rock_physics_open/sandstone_models/patchy_cement_optimisation.py +254 -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 +444 -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 +35 -0
- rock_physics_open/t_matrix_models/carbonate_pressure_substitution.py +124 -0
- rock_physics_open/t_matrix_models/curvefit_t_matrix_exp.py +123 -0
- rock_physics_open/t_matrix_models/curvefit_t_matrix_min.py +86 -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 +167 -0
- rock_physics_open/t_matrix_models/t_matrix_opt_forward_model_exp.py +76 -0
- rock_physics_open/t_matrix_models/t_matrix_opt_forward_model_min.py +89 -0
- rock_physics_open/t_matrix_models/t_matrix_parameter_optimisation_exp.py +176 -0
- rock_physics_open/t_matrix_models/t_matrix_parameter_optimisation_min.py +162 -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 +34 -0
- rock_physics_open-0.3.2.dist-info/METADATA +90 -0
- rock_physics_open-0.3.2.dist-info/RECORD +145 -0
- rock_physics_open-0.3.2.dist-info/WHEEL +5 -0
- rock_physics_open-0.3.2.dist-info/licenses/LICENSE +165 -0
- rock_physics_open-0.3.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import pickle
|
|
2
|
+
from typing import Any, Self, final
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
from typing_extensions import override
|
|
6
|
+
|
|
7
|
+
from .base_pressure_model import BasePressureModel
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@final
|
|
11
|
+
class ExponentialPressureModel(BasePressureModel):
|
|
12
|
+
"""
|
|
13
|
+
Exponential pressure sensitivity model for velocity prediction.
|
|
14
|
+
|
|
15
|
+
Uses exponential decay function: v = v0 * (1 - a*exp(-p/b)) / (1 - a*exp(-p0/b))
|
|
16
|
+
where v0 is reference velocity, p is pressure, a and b are model parameters.
|
|
17
|
+
|
|
18
|
+
Input format (n,3): [velocity, p_eff_in_situ, p_eff_depleted]
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
a_factor: float,
|
|
24
|
+
b_factor: float,
|
|
25
|
+
model_max_pressure: float | None = None,
|
|
26
|
+
description: str = "",
|
|
27
|
+
):
|
|
28
|
+
"""
|
|
29
|
+
Initialize exponential pressure model.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
----------
|
|
33
|
+
a_factor : float
|
|
34
|
+
Exponential amplitude parameter [unitless].
|
|
35
|
+
b_factor : float
|
|
36
|
+
Exponential decay parameter [Pa].
|
|
37
|
+
model_max_pressure : float | None
|
|
38
|
+
Maximum pressure for predict_max method [Pa].
|
|
39
|
+
description : str
|
|
40
|
+
Model description.
|
|
41
|
+
"""
|
|
42
|
+
super().__init__(model_max_pressure, description)
|
|
43
|
+
self._a_factor = a_factor
|
|
44
|
+
self._b_factor = b_factor
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def a_factor(self) -> float:
|
|
48
|
+
"""Exponential amplitude factor."""
|
|
49
|
+
return self._a_factor
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def b_factor(self) -> float:
|
|
53
|
+
"""Exponential decay factor."""
|
|
54
|
+
return self._b_factor
|
|
55
|
+
|
|
56
|
+
@override
|
|
57
|
+
def validate_input(self, inp_arr: np.ndarray) -> np.ndarray:
|
|
58
|
+
"""
|
|
59
|
+
Validate input for exponential model.
|
|
60
|
+
|
|
61
|
+
Parameters
|
|
62
|
+
----------
|
|
63
|
+
inp_arr : np.ndarray
|
|
64
|
+
Input array to validate.
|
|
65
|
+
|
|
66
|
+
Returns
|
|
67
|
+
-------
|
|
68
|
+
np.ndarray
|
|
69
|
+
Validated input array.
|
|
70
|
+
|
|
71
|
+
Raises
|
|
72
|
+
------
|
|
73
|
+
ValueError
|
|
74
|
+
If input format is invalid.
|
|
75
|
+
"""
|
|
76
|
+
if not isinstance(inp_arr, np.ndarray): # pyright: ignore[reportUnnecessaryIsInstance] | Kept for backward compatibility
|
|
77
|
+
raise ValueError("Input must be numpy ndarray.") # pyright: ignore[reportUnreachable] | Kept for backward compatibility
|
|
78
|
+
if inp_arr.ndim != 2 or inp_arr.shape[1] != 3:
|
|
79
|
+
raise ValueError(
|
|
80
|
+
"Input must be (n,3): [velocity, p_eff_in_situ, p_eff_depleted]"
|
|
81
|
+
)
|
|
82
|
+
return inp_arr
|
|
83
|
+
|
|
84
|
+
@override
|
|
85
|
+
def predict_abs(self, inp_arr: np.ndarray, case: str = "in_situ") -> np.ndarray:
|
|
86
|
+
"""
|
|
87
|
+
Calculate absolute velocity for specified pressure case.
|
|
88
|
+
|
|
89
|
+
Parameters
|
|
90
|
+
----------
|
|
91
|
+
inp_arr : np.ndarray
|
|
92
|
+
Validated input array (n,3).
|
|
93
|
+
case : str
|
|
94
|
+
Pressure case: "in_situ" or "depleted".
|
|
95
|
+
|
|
96
|
+
Returns
|
|
97
|
+
-------
|
|
98
|
+
np.ndarray
|
|
99
|
+
Velocity values [m/s].
|
|
100
|
+
"""
|
|
101
|
+
arr = self.validate_input(inp_arr)
|
|
102
|
+
|
|
103
|
+
vel = arr[:, 0]
|
|
104
|
+
p_in_situ = arr[:, 1]
|
|
105
|
+
p_depleted = arr[:, 2]
|
|
106
|
+
|
|
107
|
+
p_eff = p_in_situ if case == "in_situ" else p_depleted
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
vel
|
|
111
|
+
* (1.0 - self._a_factor * np.exp(-p_eff / self._b_factor))
|
|
112
|
+
/ (1.0 - self._a_factor * np.exp(-p_in_situ / self._b_factor))
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
@override
|
|
116
|
+
def todict(self) -> dict[str, Any]:
|
|
117
|
+
"""Convert model to dictionary."""
|
|
118
|
+
return {
|
|
119
|
+
"a_factor": self._a_factor,
|
|
120
|
+
"b_factor": self._b_factor,
|
|
121
|
+
"model_max_pressure": self._model_max_pressure,
|
|
122
|
+
"description": self._description,
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@override
|
|
126
|
+
@classmethod
|
|
127
|
+
def load(cls, file: str | bytes) -> Self:
|
|
128
|
+
"""Load exponential model from pickle file."""
|
|
129
|
+
with open(file, "rb") as f_in:
|
|
130
|
+
d = pickle.load(f_in)
|
|
131
|
+
|
|
132
|
+
return cls(
|
|
133
|
+
a_factor=d["a_factor"],
|
|
134
|
+
b_factor=d["b_factor"],
|
|
135
|
+
model_max_pressure=d["model_max_pressure"],
|
|
136
|
+
description=d["description"],
|
|
137
|
+
)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
from pickle import load
|
|
3
|
+
|
|
4
|
+
from sklearn.preprocessing import OneHotEncoder, RobustScaler
|
|
5
|
+
|
|
6
|
+
from .exponential_model import ExponentialPressureModel
|
|
7
|
+
from .polynomial_model import PolynomialPressureModel
|
|
8
|
+
from .sigmoidal_model import SigmoidalPressureModel
|
|
9
|
+
|
|
10
|
+
AnyModel = ExponentialPressureModel | PolynomialPressureModel | SigmoidalPressureModel
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def import_model(
|
|
14
|
+
model_file_name: str,
|
|
15
|
+
) -> tuple[
|
|
16
|
+
AnyModel,
|
|
17
|
+
RobustScaler,
|
|
18
|
+
OneHotEncoder | None,
|
|
19
|
+
str,
|
|
20
|
+
str,
|
|
21
|
+
list[str],
|
|
22
|
+
str | list[str],
|
|
23
|
+
]:
|
|
24
|
+
"""
|
|
25
|
+
Utility to import a pickled dict containing information needed to run a classification or regression based on
|
|
26
|
+
a calibrated model.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
model_file_name : str
|
|
31
|
+
Full name including path for model file.
|
|
32
|
+
|
|
33
|
+
Returns
|
|
34
|
+
-------
|
|
35
|
+
models, scaler, ohe, label_var, label_units, feature_var, cat_var : Any
|
|
36
|
+
models: various regression or classification models from e.g. sklearn or tensorflow keras, scaler: preprocessing
|
|
37
|
+
Robust Scaler, label_var: name(s) of label variable(s), label_unit: unit(s) of label variable(s), cat_var:
|
|
38
|
+
categorical variables that should be encoded with one-hot-encoder.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
with open(model_file_name, "rb") as fin, warnings.catch_warnings():
|
|
42
|
+
# 11.04.2021 HFLE: There is an issue that is not connected to the local function, in that a warning is issued
|
|
43
|
+
# when the model is loaded, claiming that it is of an older version. This is debugged in detail, and the model
|
|
44
|
+
# IS of the correct version, so the error arise elsewhere. To avoid confusion, the warning is suppressed here
|
|
45
|
+
|
|
46
|
+
warnings.simplefilter("ignore", category=UserWarning)
|
|
47
|
+
mod_dict = load(fin)
|
|
48
|
+
|
|
49
|
+
if mod_dict["model_type"] == "Sigmoid":
|
|
50
|
+
models = SigmoidalPressureModel.load(mod_dict["nn_mod"])
|
|
51
|
+
elif mod_dict["model_type"] == "Exponential":
|
|
52
|
+
models = ExponentialPressureModel.load(mod_dict["nn_mod"])
|
|
53
|
+
elif mod_dict["model_type"] == "Polynomial":
|
|
54
|
+
models = PolynomialPressureModel.load(mod_dict["nn_mod"])
|
|
55
|
+
else:
|
|
56
|
+
raise ValueError("unknown model type {}".format(mod_dict["model_type"]))
|
|
57
|
+
|
|
58
|
+
ohe: OneHotEncoder | None = None
|
|
59
|
+
cat_var: str | list[str] = []
|
|
60
|
+
try:
|
|
61
|
+
if mod_dict["ohe"]:
|
|
62
|
+
with open(mod_dict["ohe"], "rb") as f:
|
|
63
|
+
ohe_dict = load(f)
|
|
64
|
+
ohe = ohe_dict["ohe"]
|
|
65
|
+
cat_var = ohe_dict["cat_var"]
|
|
66
|
+
except (FileExistsError, FileNotFoundError):
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
models,
|
|
71
|
+
mod_dict["scaler"],
|
|
72
|
+
ohe,
|
|
73
|
+
mod_dict["label_var"],
|
|
74
|
+
mod_dict["label_units"],
|
|
75
|
+
mod_dict["feature_var"],
|
|
76
|
+
cat_var,
|
|
77
|
+
)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import pickle
|
|
2
|
+
from typing import Any, Self, final
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
from typing_extensions import override
|
|
6
|
+
|
|
7
|
+
from .base_pressure_model import BasePressureModel
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@final
|
|
11
|
+
class PolynomialPressureModel(BasePressureModel):
|
|
12
|
+
"""
|
|
13
|
+
Polynomial pressure sensitivity model for velocity prediction.
|
|
14
|
+
|
|
15
|
+
Uses polynomial function: v = v0 * P(p_depl) / P(p_in_situ)
|
|
16
|
+
where P(p) is a polynomial function with specified coefficients.
|
|
17
|
+
|
|
18
|
+
Input format (n,3): [velocity, p_eff_in_situ, p_eff_depleted]
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
weights: list[float],
|
|
24
|
+
model_max_pressure: float | None = None,
|
|
25
|
+
description: str = "",
|
|
26
|
+
):
|
|
27
|
+
"""
|
|
28
|
+
Initialize polynomial pressure model.
|
|
29
|
+
|
|
30
|
+
Parameters
|
|
31
|
+
----------
|
|
32
|
+
weights : list[float]
|
|
33
|
+
Polynomial coefficients [unitless]. First element is constant term,
|
|
34
|
+
second is linear coefficient, etc.
|
|
35
|
+
model_max_pressure : float | None
|
|
36
|
+
Maximum pressure for predict_max method [Pa].
|
|
37
|
+
description : str
|
|
38
|
+
Model description.
|
|
39
|
+
"""
|
|
40
|
+
super().__init__(model_max_pressure, description)
|
|
41
|
+
self._weights = weights
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def weights(self) -> list[float]:
|
|
45
|
+
"""Polynomial coefficients."""
|
|
46
|
+
return self._weights
|
|
47
|
+
|
|
48
|
+
@override
|
|
49
|
+
def validate_input(self, inp_arr: np.ndarray) -> np.ndarray:
|
|
50
|
+
"""
|
|
51
|
+
Validate input for polynomial model.
|
|
52
|
+
|
|
53
|
+
Parameters
|
|
54
|
+
----------
|
|
55
|
+
inp_arr : np.ndarray
|
|
56
|
+
Input array to validate.
|
|
57
|
+
|
|
58
|
+
Returns
|
|
59
|
+
-------
|
|
60
|
+
np.ndarray
|
|
61
|
+
Validated input array.
|
|
62
|
+
|
|
63
|
+
Raises
|
|
64
|
+
------
|
|
65
|
+
ValueError
|
|
66
|
+
If input format is invalid.
|
|
67
|
+
"""
|
|
68
|
+
if not isinstance(inp_arr, np.ndarray): # pyright: ignore[reportUnnecessaryIsInstance] | Kept for backward compatibility
|
|
69
|
+
raise ValueError("Input must be numpy ndarray.") # pyright: ignore[reportUnreachable] | Kept for backward compatibility
|
|
70
|
+
if inp_arr.ndim != 2 or inp_arr.shape[1] != 3:
|
|
71
|
+
raise ValueError(
|
|
72
|
+
"Input must be (n,3): [velocity, p_eff_in_situ, p_eff_depleted]"
|
|
73
|
+
)
|
|
74
|
+
return inp_arr
|
|
75
|
+
|
|
76
|
+
@override
|
|
77
|
+
def predict_abs(self, inp_arr: np.ndarray, case: str = "in_situ") -> np.ndarray:
|
|
78
|
+
"""
|
|
79
|
+
Calculate absolute velocity for specified pressure case.
|
|
80
|
+
|
|
81
|
+
Parameters
|
|
82
|
+
----------
|
|
83
|
+
inp_arr : np.ndarray
|
|
84
|
+
Validated input array (n,3).
|
|
85
|
+
case : str
|
|
86
|
+
Pressure case: "in_situ" or "depleted".
|
|
87
|
+
|
|
88
|
+
Returns
|
|
89
|
+
-------
|
|
90
|
+
np.ndarray
|
|
91
|
+
Velocity values [m/s].
|
|
92
|
+
"""
|
|
93
|
+
arr = self.validate_input(inp_arr)
|
|
94
|
+
|
|
95
|
+
# Validate weights are set
|
|
96
|
+
if not self._weights:
|
|
97
|
+
raise ValueError('Field "weights" is not set.')
|
|
98
|
+
|
|
99
|
+
vel = arr[:, 0]
|
|
100
|
+
p_in_situ = arr[:, 1]
|
|
101
|
+
p_depleted = arr[:, 2]
|
|
102
|
+
|
|
103
|
+
# Create polynomial from weights
|
|
104
|
+
polynomial_expr = np.polynomial.Polynomial(self._weights)
|
|
105
|
+
|
|
106
|
+
# Select pressure based on case
|
|
107
|
+
p_eff = p_in_situ if case == "in_situ" else p_depleted
|
|
108
|
+
|
|
109
|
+
# Calculate velocity using polynomial pressure correction
|
|
110
|
+
return vel * polynomial_expr(p_eff) / polynomial_expr(p_in_situ)
|
|
111
|
+
|
|
112
|
+
@override
|
|
113
|
+
def todict(self) -> dict[str, Any]:
|
|
114
|
+
"""Convert model to dictionary."""
|
|
115
|
+
return {
|
|
116
|
+
"weights": self._weights,
|
|
117
|
+
"model_max_pressure": self._model_max_pressure,
|
|
118
|
+
"description": self._description,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@override
|
|
122
|
+
@classmethod
|
|
123
|
+
def load(cls, file: str | bytes) -> Self:
|
|
124
|
+
"""Load polynomial model from pickle file."""
|
|
125
|
+
with open(file, "rb") as f_in:
|
|
126
|
+
d = pickle.load(f_in)
|
|
127
|
+
|
|
128
|
+
return cls(
|
|
129
|
+
weights=d["weights"],
|
|
130
|
+
model_max_pressure=d["model_max_pressure"],
|
|
131
|
+
description=d["description"],
|
|
132
|
+
)
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from collections.abc import Sequence
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from re import match
|
|
5
|
+
from typing import Any, cast
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
import numpy.typing as npt
|
|
9
|
+
import pandas as pd
|
|
10
|
+
from sklearn.preprocessing import OneHotEncoder, RobustScaler
|
|
11
|
+
|
|
12
|
+
from .dummy_vars import generate_dummy_vars
|
|
13
|
+
from .import_ml_models import AnyModel, import_model
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _read_models(
|
|
17
|
+
*model_files: str, model_dir: str | Path | None = None
|
|
18
|
+
) -> tuple[
|
|
19
|
+
list[AnyModel],
|
|
20
|
+
list[RobustScaler],
|
|
21
|
+
list[OneHotEncoder | None],
|
|
22
|
+
list[str],
|
|
23
|
+
list[str],
|
|
24
|
+
list[list[str]],
|
|
25
|
+
list[Any],
|
|
26
|
+
list[str],
|
|
27
|
+
list[str],
|
|
28
|
+
]:
|
|
29
|
+
# Find the directory of the model files, change working directory, return to original directory at end of function
|
|
30
|
+
orig_dir = os.getcwd()
|
|
31
|
+
if model_dir is None:
|
|
32
|
+
model_dir, _ = os.path.split(model_files[0])
|
|
33
|
+
os.chdir(model_dir)
|
|
34
|
+
# Allocate lists and read model
|
|
35
|
+
reg_models: list[AnyModel] = []
|
|
36
|
+
scalers: list[RobustScaler] = []
|
|
37
|
+
ohes: list[OneHotEncoder | None] = []
|
|
38
|
+
label_vars: list[str] = []
|
|
39
|
+
label_units: list[str] = []
|
|
40
|
+
feat_vars: list[list[str]] = []
|
|
41
|
+
cat_vars: list[Any] = []
|
|
42
|
+
col_names: list[str] = []
|
|
43
|
+
col_units: list[str] = []
|
|
44
|
+
|
|
45
|
+
for mod_name in model_files:
|
|
46
|
+
models, scaler, ohe, label_var, label_unit, feat_var, cat_var = import_model(
|
|
47
|
+
model_file_name=mod_name
|
|
48
|
+
)
|
|
49
|
+
reg_models.append(models)
|
|
50
|
+
scalers.append(scaler)
|
|
51
|
+
ohes.append(ohe)
|
|
52
|
+
label_vars.append(label_var)
|
|
53
|
+
# Need to modify names
|
|
54
|
+
col_names.append(label_var + "_" + mod_name.replace(label_var, ""))
|
|
55
|
+
col_units.append(label_unit)
|
|
56
|
+
label_units.append(label_unit)
|
|
57
|
+
feat_vars.append(feat_var)
|
|
58
|
+
cat_vars.append(cat_var)
|
|
59
|
+
|
|
60
|
+
# Need to modify names
|
|
61
|
+
col_names, col_units = ([] for _ in range(2))
|
|
62
|
+
for i in range(len(label_var)):
|
|
63
|
+
col_names.append(
|
|
64
|
+
label_var[i] + "_" + model_files[i].replace(label_var[i], "")
|
|
65
|
+
)
|
|
66
|
+
col_units.append(label_unit[i])
|
|
67
|
+
|
|
68
|
+
os.chdir(orig_dir)
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
reg_models,
|
|
72
|
+
scalers,
|
|
73
|
+
ohes,
|
|
74
|
+
label_vars,
|
|
75
|
+
label_units,
|
|
76
|
+
feat_vars,
|
|
77
|
+
cat_vars,
|
|
78
|
+
col_names,
|
|
79
|
+
col_units,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _perform_regression(
|
|
84
|
+
inp_frame: pd.DataFrame,
|
|
85
|
+
col_names: list[str],
|
|
86
|
+
feat_var: list[list[str]],
|
|
87
|
+
cat_var: str | list[str],
|
|
88
|
+
ohe: Sequence[OneHotEncoder | None],
|
|
89
|
+
scaler: Sequence[RobustScaler | None],
|
|
90
|
+
reg_model: Sequence[AnyModel],
|
|
91
|
+
) -> pd.DataFrame:
|
|
92
|
+
depth = inp_frame.index.to_numpy()
|
|
93
|
+
|
|
94
|
+
res_frame = pd.DataFrame(index=depth, columns=col_names)
|
|
95
|
+
|
|
96
|
+
for j, _ in enumerate(col_names):
|
|
97
|
+
tmp_frame = inp_frame.copy()
|
|
98
|
+
|
|
99
|
+
# Limit to columns used in estimation before dropping NaNs
|
|
100
|
+
num_var = [i for i in feat_var[j] if not bool(match(r"x\d", i))]
|
|
101
|
+
no_num_var = len(num_var)
|
|
102
|
+
if cat_var[j]:
|
|
103
|
+
num_var.append(cat_var[j])
|
|
104
|
+
tmp_frame = tmp_frame[num_var]
|
|
105
|
+
idx_na_n = tmp_frame.isna().any(axis=1)
|
|
106
|
+
|
|
107
|
+
if cat_var[j]:
|
|
108
|
+
dum_features, _, dum_var_names = generate_dummy_vars(
|
|
109
|
+
inp_frame=tmp_frame.loc[~idx_na_n],
|
|
110
|
+
class_var=cat_var[j],
|
|
111
|
+
ohe=ohe[j],
|
|
112
|
+
)
|
|
113
|
+
# Add dummy features to data frame
|
|
114
|
+
kept_dum_var: list[str] = []
|
|
115
|
+
for i, name in enumerate(dum_var_names):
|
|
116
|
+
if name in feat_var[j]:
|
|
117
|
+
tmp_frame.loc[~idx_na_n, name] = dum_features[:, i]
|
|
118
|
+
kept_dum_var.append(name)
|
|
119
|
+
tmp_frame.drop(columns=[cat_var[j]], inplace=True)
|
|
120
|
+
|
|
121
|
+
# Need to assure that we have the correct sequence of features
|
|
122
|
+
tmp_frame = tmp_frame.reindex(columns=feat_var[j])
|
|
123
|
+
|
|
124
|
+
new_features = np.zeros((np.sum(~idx_na_n), tmp_frame.shape[1]))
|
|
125
|
+
# Make scaling optional
|
|
126
|
+
scaler_ = scaler[j]
|
|
127
|
+
if scaler_ is not None:
|
|
128
|
+
new_features = (
|
|
129
|
+
cast( # Casting since scikit-learn is not yet fully typed.
|
|
130
|
+
npt.NDArray[np.float64],
|
|
131
|
+
scaler_.transform(tmp_frame.to_numpy()[~idx_na_n, :]),
|
|
132
|
+
)
|
|
133
|
+
)
|
|
134
|
+
else:
|
|
135
|
+
new_features[:, :no_num_var] = tmp_frame.to_numpy()[
|
|
136
|
+
~idx_na_n, :no_num_var
|
|
137
|
+
]
|
|
138
|
+
new_features[:, no_num_var:] = tmp_frame.loc[
|
|
139
|
+
~idx_na_n, kept_dum_var
|
|
140
|
+
].to_numpy()
|
|
141
|
+
else:
|
|
142
|
+
# Much simpler if there are no dummy variables
|
|
143
|
+
# Need to assure that we have the correct sequence of features
|
|
144
|
+
tmp_frame = tmp_frame.reindex(columns=feat_var[j])
|
|
145
|
+
# Make scaling optional
|
|
146
|
+
scaler_ = scaler[j]
|
|
147
|
+
if scaler_ is not None:
|
|
148
|
+
new_features = (
|
|
149
|
+
cast( # Casting since scikit-learn is not yet fully typed.
|
|
150
|
+
npt.NDArray[np.float64],
|
|
151
|
+
scaler_.transform(tmp_frame.to_numpy()[~idx_na_n, :]),
|
|
152
|
+
)
|
|
153
|
+
)
|
|
154
|
+
else:
|
|
155
|
+
new_features = tmp_frame.to_numpy()[~idx_na_n, :]
|
|
156
|
+
|
|
157
|
+
new_var = np.ones(depth.shape[0]) * np.nan
|
|
158
|
+
new_var[~idx_na_n] = reg_model[j].predict(new_features).flatten()
|
|
159
|
+
res_frame[col_names[j]] = new_var
|
|
160
|
+
|
|
161
|
+
return res_frame
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def run_regression(
|
|
165
|
+
inp_df: pd.DataFrame,
|
|
166
|
+
first_model_file_name: str,
|
|
167
|
+
second_model_file_name: str,
|
|
168
|
+
model_dir: str | None = None,
|
|
169
|
+
) -> pd.DataFrame:
|
|
170
|
+
"""
|
|
171
|
+
Estimate Vp and Vs by neural network regression with multiple inputs.
|
|
172
|
+
|
|
173
|
+
Parameters
|
|
174
|
+
----------
|
|
175
|
+
inp_df : pd.DataFrame
|
|
176
|
+
Input logs required for the regression.
|
|
177
|
+
first_model_file_name : str
|
|
178
|
+
Full file name for vp model.
|
|
179
|
+
second_model_file_name : str
|
|
180
|
+
Full file name for vs model.
|
|
181
|
+
model_dir : str | None
|
|
182
|
+
Directory.
|
|
183
|
+
|
|
184
|
+
Returns
|
|
185
|
+
-------
|
|
186
|
+
vp, vs : pd.DataFrame
|
|
187
|
+
Estimated vp and vs as series in Pandas DataFrame.
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
(
|
|
191
|
+
regression_model,
|
|
192
|
+
scaler_obj,
|
|
193
|
+
ohe_obj,
|
|
194
|
+
_,
|
|
195
|
+
_,
|
|
196
|
+
feature_var,
|
|
197
|
+
category_var,
|
|
198
|
+
column_names,
|
|
199
|
+
_,
|
|
200
|
+
) = _read_models(first_model_file_name, second_model_file_name, model_dir=model_dir)
|
|
201
|
+
return _perform_regression(
|
|
202
|
+
inp_frame=inp_df,
|
|
203
|
+
col_names=column_names,
|
|
204
|
+
feat_var=feature_var,
|
|
205
|
+
cat_var=category_var,
|
|
206
|
+
ohe=ohe_obj,
|
|
207
|
+
scaler=scaler_obj,
|
|
208
|
+
reg_model=regression_model,
|
|
209
|
+
)
|