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.

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