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.
Files changed (145) hide show
  1. rock_physics_open/__init__.py +0 -0
  2. rock_physics_open/equinor_utilities/__init__.py +0 -0
  3. rock_physics_open/equinor_utilities/anisotropy.py +211 -0
  4. rock_physics_open/equinor_utilities/classification_functions/__init__.py +17 -0
  5. rock_physics_open/equinor_utilities/classification_functions/class_stats.py +68 -0
  6. rock_physics_open/equinor_utilities/classification_functions/lin_class.py +53 -0
  7. rock_physics_open/equinor_utilities/classification_functions/mahal_class.py +63 -0
  8. rock_physics_open/equinor_utilities/classification_functions/norm_class.py +73 -0
  9. rock_physics_open/equinor_utilities/classification_functions/poly_class.py +45 -0
  10. rock_physics_open/equinor_utilities/classification_functions/post_prob.py +27 -0
  11. rock_physics_open/equinor_utilities/classification_functions/two_step_classification.py +60 -0
  12. rock_physics_open/equinor_utilities/conversions.py +10 -0
  13. rock_physics_open/equinor_utilities/gen_utilities/__init__.py +11 -0
  14. rock_physics_open/equinor_utilities/gen_utilities/dict_to_float.py +38 -0
  15. rock_physics_open/equinor_utilities/gen_utilities/dim_check_vector.py +113 -0
  16. rock_physics_open/equinor_utilities/gen_utilities/filter_input.py +131 -0
  17. rock_physics_open/equinor_utilities/gen_utilities/filter_output.py +88 -0
  18. rock_physics_open/equinor_utilities/machine_learning_utilities/__init__.py +15 -0
  19. rock_physics_open/equinor_utilities/machine_learning_utilities/base_pressure_model.py +170 -0
  20. rock_physics_open/equinor_utilities/machine_learning_utilities/dummy_vars.py +53 -0
  21. rock_physics_open/equinor_utilities/machine_learning_utilities/exponential_model.py +137 -0
  22. rock_physics_open/equinor_utilities/machine_learning_utilities/import_ml_models.py +77 -0
  23. rock_physics_open/equinor_utilities/machine_learning_utilities/polynomial_model.py +132 -0
  24. rock_physics_open/equinor_utilities/machine_learning_utilities/run_regression.py +209 -0
  25. rock_physics_open/equinor_utilities/machine_learning_utilities/sigmoidal_model.py +241 -0
  26. rock_physics_open/equinor_utilities/optimisation_utilities/__init__.py +19 -0
  27. rock_physics_open/equinor_utilities/optimisation_utilities/opt_subst_utilities.py +455 -0
  28. rock_physics_open/equinor_utilities/snapshot_test_utilities/__init__.py +10 -0
  29. rock_physics_open/equinor_utilities/snapshot_test_utilities/compare_snapshots.py +184 -0
  30. rock_physics_open/equinor_utilities/snapshot_test_utilities/snapshots.py +97 -0
  31. rock_physics_open/equinor_utilities/std_functions/__init__.py +43 -0
  32. rock_physics_open/equinor_utilities/std_functions/backus_ave.py +68 -0
  33. rock_physics_open/equinor_utilities/std_functions/dvorkin_nur.py +77 -0
  34. rock_physics_open/equinor_utilities/std_functions/gassmann.py +165 -0
  35. rock_physics_open/equinor_utilities/std_functions/hashin_shtrikman.py +224 -0
  36. rock_physics_open/equinor_utilities/std_functions/hertz_mindlin.py +51 -0
  37. rock_physics_open/equinor_utilities/std_functions/moduli_velocity.py +67 -0
  38. rock_physics_open/equinor_utilities/std_functions/reflection_eq.py +120 -0
  39. rock_physics_open/equinor_utilities/std_functions/rho.py +69 -0
  40. rock_physics_open/equinor_utilities/std_functions/voigt_reuss_hill.py +149 -0
  41. rock_physics_open/equinor_utilities/std_functions/walton.py +45 -0
  42. rock_physics_open/equinor_utilities/std_functions/wood_brie.py +94 -0
  43. rock_physics_open/equinor_utilities/various_utilities/Equinor_logo.gif +0 -0
  44. rock_physics_open/equinor_utilities/various_utilities/Equinor_logo.ico +0 -0
  45. rock_physics_open/equinor_utilities/various_utilities/__init__.py +24 -0
  46. rock_physics_open/equinor_utilities/various_utilities/display_result_statistics.py +90 -0
  47. rock_physics_open/equinor_utilities/various_utilities/gassmann_dry_mod.py +56 -0
  48. rock_physics_open/equinor_utilities/various_utilities/gassmann_mod.py +56 -0
  49. rock_physics_open/equinor_utilities/various_utilities/gassmann_sub_mod.py +64 -0
  50. rock_physics_open/equinor_utilities/various_utilities/hs_average.py +59 -0
  51. rock_physics_open/equinor_utilities/various_utilities/pressure.py +96 -0
  52. rock_physics_open/equinor_utilities/various_utilities/reflectivity.py +101 -0
  53. rock_physics_open/equinor_utilities/various_utilities/timeshift.py +104 -0
  54. rock_physics_open/equinor_utilities/various_utilities/vp_vs_rho_set_statistics.py +170 -0
  55. rock_physics_open/equinor_utilities/various_utilities/vrh_3_min.py +83 -0
  56. rock_physics_open/fluid_models/__init__.py +9 -0
  57. rock_physics_open/fluid_models/brine_model/__init__.py +5 -0
  58. rock_physics_open/fluid_models/brine_model/brine_properties.py +178 -0
  59. rock_physics_open/fluid_models/gas_model/__init__.py +5 -0
  60. rock_physics_open/fluid_models/gas_model/gas_properties.py +319 -0
  61. rock_physics_open/fluid_models/oil_model/__init__.py +5 -0
  62. rock_physics_open/fluid_models/oil_model/dead_oil_density.py +65 -0
  63. rock_physics_open/fluid_models/oil_model/dead_oil_velocity.py +30 -0
  64. rock_physics_open/fluid_models/oil_model/live_oil_density.py +82 -0
  65. rock_physics_open/fluid_models/oil_model/live_oil_velocity.py +24 -0
  66. rock_physics_open/fluid_models/oil_model/oil_bubble_point.py +69 -0
  67. rock_physics_open/fluid_models/oil_model/oil_properties.py +146 -0
  68. rock_physics_open/sandstone_models/__init__.py +59 -0
  69. rock_physics_open/sandstone_models/cemented_shalysand_sandyshale_models.py +304 -0
  70. rock_physics_open/sandstone_models/constant_cement_models.py +204 -0
  71. rock_physics_open/sandstone_models/constant_cement_optimisation.py +125 -0
  72. rock_physics_open/sandstone_models/contact_cement_model.py +138 -0
  73. rock_physics_open/sandstone_models/curvefit_sandstone_models.py +143 -0
  74. rock_physics_open/sandstone_models/friable_models.py +177 -0
  75. rock_physics_open/sandstone_models/friable_optimisation.py +115 -0
  76. rock_physics_open/sandstone_models/friable_shalysand_sandyshale_models.py +235 -0
  77. rock_physics_open/sandstone_models/patchy_cement_fluid_substitution_model.py +477 -0
  78. rock_physics_open/sandstone_models/patchy_cement_model.py +384 -0
  79. rock_physics_open/sandstone_models/patchy_cement_optimisation.py +254 -0
  80. rock_physics_open/sandstone_models/unresolved_cemented_sandshale_models.py +134 -0
  81. rock_physics_open/sandstone_models/unresolved_friable_sandshale_models.py +126 -0
  82. rock_physics_open/shale_models/__init__.py +19 -0
  83. rock_physics_open/shale_models/dem.py +174 -0
  84. rock_physics_open/shale_models/dem_dual_por.py +61 -0
  85. rock_physics_open/shale_models/kus_tok.py +59 -0
  86. rock_physics_open/shale_models/multi_sca.py +133 -0
  87. rock_physics_open/shale_models/pq.py +102 -0
  88. rock_physics_open/shale_models/sca.py +90 -0
  89. rock_physics_open/shale_models/shale4_mineral.py +147 -0
  90. rock_physics_open/shale_models/shale4_mineral_dem_overlay.py +92 -0
  91. rock_physics_open/span_wagner/__init__.py +5 -0
  92. rock_physics_open/span_wagner/co2_properties.py +444 -0
  93. rock_physics_open/span_wagner/coefficients.py +165 -0
  94. rock_physics_open/span_wagner/equations.py +104 -0
  95. rock_physics_open/span_wagner/tables/__init__.py +0 -0
  96. rock_physics_open/span_wagner/tables/carbon_dioxide_density.npz +0 -0
  97. rock_physics_open/span_wagner/tables/lookup_table.py +33 -0
  98. rock_physics_open/t_matrix_models/Equinor_logo.ico +0 -0
  99. rock_physics_open/t_matrix_models/__init__.py +35 -0
  100. rock_physics_open/t_matrix_models/carbonate_pressure_substitution.py +124 -0
  101. rock_physics_open/t_matrix_models/curvefit_t_matrix_exp.py +123 -0
  102. rock_physics_open/t_matrix_models/curvefit_t_matrix_min.py +86 -0
  103. rock_physics_open/t_matrix_models/parse_t_matrix_inputs.py +297 -0
  104. rock_physics_open/t_matrix_models/run_t_matrix.py +243 -0
  105. rock_physics_open/t_matrix_models/t_matrix_C.py +210 -0
  106. rock_physics_open/t_matrix_models/t_matrix_opt_fluid_sub_exp.py +137 -0
  107. rock_physics_open/t_matrix_models/t_matrix_opt_fluid_sub_petec.py +167 -0
  108. rock_physics_open/t_matrix_models/t_matrix_opt_forward_model_exp.py +76 -0
  109. rock_physics_open/t_matrix_models/t_matrix_opt_forward_model_min.py +89 -0
  110. rock_physics_open/t_matrix_models/t_matrix_parameter_optimisation_exp.py +176 -0
  111. rock_physics_open/t_matrix_models/t_matrix_parameter_optimisation_min.py +162 -0
  112. rock_physics_open/t_matrix_models/t_matrix_vector/__init__.py +12 -0
  113. rock_physics_open/t_matrix_models/t_matrix_vector/array_functions.py +75 -0
  114. rock_physics_open/t_matrix_models/t_matrix_vector/calc_c_eff.py +163 -0
  115. rock_physics_open/t_matrix_models/t_matrix_vector/calc_isolated.py +95 -0
  116. rock_physics_open/t_matrix_models/t_matrix_vector/calc_kd.py +40 -0
  117. rock_physics_open/t_matrix_models/t_matrix_vector/calc_kd_eff.py +116 -0
  118. rock_physics_open/t_matrix_models/t_matrix_vector/calc_kd_uuv.py +18 -0
  119. rock_physics_open/t_matrix_models/t_matrix_vector/calc_pressure.py +140 -0
  120. rock_physics_open/t_matrix_models/t_matrix_vector/calc_t.py +71 -0
  121. rock_physics_open/t_matrix_models/t_matrix_vector/calc_td.py +42 -0
  122. rock_physics_open/t_matrix_models/t_matrix_vector/calc_theta.py +43 -0
  123. rock_physics_open/t_matrix_models/t_matrix_vector/calc_x.py +33 -0
  124. rock_physics_open/t_matrix_models/t_matrix_vector/calc_z.py +50 -0
  125. rock_physics_open/t_matrix_models/t_matrix_vector/check_and_tile.py +43 -0
  126. rock_physics_open/t_matrix_models/t_matrix_vector/g_tensor.py +140 -0
  127. rock_physics_open/t_matrix_models/t_matrix_vector/iso_av.py +60 -0
  128. rock_physics_open/t_matrix_models/t_matrix_vector/iso_ave_all.py +55 -0
  129. rock_physics_open/t_matrix_models/t_matrix_vector/pressure_input.py +44 -0
  130. rock_physics_open/t_matrix_models/t_matrix_vector/t_matrix_vec.py +278 -0
  131. rock_physics_open/t_matrix_models/t_matrix_vector/velocity_vti_angles.py +81 -0
  132. rock_physics_open/t_matrix_models/tmatrix_python.dll +0 -0
  133. rock_physics_open/t_matrix_models/tmatrix_python.so +0 -0
  134. rock_physics_open/ternary_plots/__init__.py +3 -0
  135. rock_physics_open/ternary_plots/gen_ternary_plot.py +73 -0
  136. rock_physics_open/ternary_plots/shale_prop_ternary.py +337 -0
  137. rock_physics_open/ternary_plots/ternary_patches.py +277 -0
  138. rock_physics_open/ternary_plots/ternary_plot_utilities.py +197 -0
  139. rock_physics_open/ternary_plots/unconventionals_ternary.py +75 -0
  140. rock_physics_open/version.py +34 -0
  141. rock_physics_open-0.3.2.dist-info/METADATA +90 -0
  142. rock_physics_open-0.3.2.dist-info/RECORD +145 -0
  143. rock_physics_open-0.3.2.dist-info/WHEEL +5 -0
  144. rock_physics_open-0.3.2.dist-info/licenses/LICENSE +165 -0
  145. 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
+ )