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,235 @@
1
+ import numpy as np
2
+
3
+ from rock_physics_open import sandstone_models
4
+ from rock_physics_open.equinor_utilities import gen_utilities, std_functions
5
+
6
+
7
+ def friable_shaly_sand_sandy_shale_model(
8
+ k_sst,
9
+ mu_sst,
10
+ rho_sst,
11
+ k_mud,
12
+ mu_mud,
13
+ rho_mud,
14
+ k_fl_sst,
15
+ rho_fl_sst,
16
+ k_fl_mud,
17
+ rho_fl_mud,
18
+ phi,
19
+ p_eff_sst,
20
+ p_eff_mud,
21
+ shale_frac,
22
+ phi_c_sst,
23
+ phi_c_mud,
24
+ phi_intr_mud,
25
+ coord_num_func_sst,
26
+ n_sst,
27
+ coord_num_func_mud,
28
+ n_mud,
29
+ shear_red_sst,
30
+ shear_red_mud,
31
+ ):
32
+ """
33
+ Model for mixing of friable sand and friable shale.
34
+
35
+ It is no point to use this model to calculate the shale response only,
36
+ in that case Friable Model with shale parameters does the job.
37
+
38
+ The shale fluid should be brine.
39
+
40
+ Shale fraction shaleFrac is in the range 0 to 1. For shaleFrac = 0 we
41
+ have a pure sand end member with phi = phiC for sand. For shaleFrac = 1
42
+ we have pure shale with phi = intrinsic porosity. For shaleFrac < phiC
43
+ the model is on the shaly sand trend, for shaleFrac > phiC it is on the
44
+ sandy shale trend.
45
+
46
+ Parameters
47
+ ----------
48
+ k_sst : np.ndarray
49
+ Sandstone bulk modulus [Pa].
50
+ mu_sst : np.ndarray
51
+ Sandstone shear modulus [Pa].
52
+ rho_sst : np.ndarray
53
+ Sandstone bulk density [kg/m^3].
54
+ k_mud : np.ndarray
55
+ Shale bulk modulus [Pa].
56
+ mu_mud : np.ndarray
57
+ Shale shear modulus [Pa].
58
+ rho_mud : np.ndarray
59
+ Shale bulk density [kg/m^3].
60
+ k_fl_sst : np.ndarray
61
+ Fluid bulk modulus for sandstone fluid [Pa].
62
+ rho_fl_sst : np.ndarray
63
+ Fluid bulk density for sandstone fluid [kg/m^3].
64
+ k_fl_mud : np.ndarray
65
+ Fluid bulk modulus for shale fluid [Pa].
66
+ rho_fl_mud : np.ndarray
67
+ Fluid bulk density for shale fluid[kg/m^3].
68
+ phi : np.ndarray
69
+ Total porosity [fraction].
70
+ p_eff_sst : np.ndarray
71
+ Effective pressure in sandstone [Pa].
72
+ p_eff_mud : np.ndarray
73
+ Effective pressure in mud [Pa].
74
+ shale_frac : np.ndarray
75
+ Shale fraction [fraction].
76
+ phi_c_sst : float
77
+ Critical porosity for sandstone [fraction].
78
+ phi_c_mud : float
79
+ Critical porosity for mud [fraction].
80
+ phi_intr_mud : float
81
+ Intrinsic shale porosity [fraction].
82
+ coord_num_func_sst : str
83
+ Indication if coordination number should be calculated from porosity or kept constant for sandstone.
84
+ coord_num_func_mud : str
85
+ Indication if coordination number should be calculated from porosity or kept constant for shale.
86
+ n_sst : float
87
+ Coordination number for sandstone [unitless].
88
+ n_mud : float
89
+ Coordination number for shale [unitless].
90
+ shear_red_sst : float
91
+ Shear reduction factor for sandstone [fraction].
92
+ shear_red_mud : float
93
+ Shear reduction factor for mud [fraction].
94
+
95
+ Returns
96
+ -------
97
+ tuple
98
+ vp, vs, rho, ai, vpvs : (np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray)
99
+ vp [m/s] and vs [m/s], bulk density [kg/m^3], ai [m/s x kg/m^3], vpvs [ratio] of saturated rock.
100
+ """
101
+
102
+ # Filter out values of phi that are above phi_c, assumed only to apply for the sandstone
103
+ (
104
+ idx_phi,
105
+ (
106
+ k_sst,
107
+ mu_sst,
108
+ rho_sst,
109
+ k_mud,
110
+ mu_mud,
111
+ rho_mud,
112
+ k_fl_sst,
113
+ rho_fl_sst,
114
+ k_fl_mud,
115
+ rho_fl_mud,
116
+ phi,
117
+ p_eff_sst,
118
+ p_eff_mud,
119
+ shale_frac,
120
+ _,
121
+ _,
122
+ ),
123
+ ) = gen_utilities.filter_input_log(
124
+ (
125
+ k_sst,
126
+ mu_sst,
127
+ rho_sst,
128
+ k_mud,
129
+ mu_mud,
130
+ rho_mud,
131
+ k_fl_sst,
132
+ rho_fl_sst,
133
+ k_fl_mud,
134
+ rho_fl_mud,
135
+ phi,
136
+ p_eff_sst,
137
+ p_eff_mud,
138
+ shale_frac,
139
+ phi_c_sst - phi,
140
+ phi - phi_intr_mud,
141
+ ),
142
+ no_zero=False,
143
+ )
144
+
145
+ # Expand the needed variables from float to numpy array
146
+ phi, phi_intr_mud = gen_utilities.dim_check_vector((phi, phi_intr_mud))
147
+
148
+ sandy_shale_idx = shale_frac > phi
149
+ shaly_sand_idx = ~sandy_shale_idx
150
+
151
+ # Fraction of silt in silt - shale trend
152
+ frac_silt = (1 - shale_frac) / (1 - phi)
153
+ # Fraction of sand in sand - silt trend
154
+ frac_sand = 1 - shale_frac / phi
155
+
156
+ # Shale properties for intrinsic porosity point
157
+ vp_sat_mud, vs_sat_mud, rho_b_mud = sandstone_models.friable_model(
158
+ k_mud,
159
+ mu_mud,
160
+ rho_mud,
161
+ k_fl_mud,
162
+ rho_fl_mud,
163
+ phi_intr_mud,
164
+ p_eff_mud,
165
+ phi_c_mud,
166
+ coord_num_func_mud,
167
+ n_mud,
168
+ shear_red_mud,
169
+ )[0:3]
170
+ k_sat_mud, mu_sat_mud = std_functions.moduli(vp_sat_mud, vs_sat_mud, rho_b_mud)
171
+
172
+ # Silt end member
173
+ k_silt, mu_silt = std_functions.hashin_shtrikman_walpole(
174
+ k_sat_mud, mu_sat_mud, k_sst, mu_sst, phi_c_sst
175
+ )
176
+ rho_silt = rho_b_mud * phi + rho_sst * (1 - phi)
177
+
178
+ # Estimate the sand end member through the friable model with phi = phiC
179
+ vp_sat_sst, vs_sat_sst, rho_sat_sst = sandstone_models.friable_model(
180
+ k_sst,
181
+ mu_sst,
182
+ rho_sst,
183
+ k_fl_sst,
184
+ rho_fl_sst,
185
+ phi,
186
+ p_eff_sst,
187
+ phi_c_sst,
188
+ coord_num_func_sst,
189
+ n_sst,
190
+ shear_red_sst,
191
+ )[0:3]
192
+ k_sat_sst, mu_sat_sst = std_functions.moduli(vp_sat_sst, vs_sat_sst, rho_sat_sst)
193
+
194
+ k = np.ones(shale_frac.shape) * np.nan
195
+ mu = np.ones(shale_frac.shape) * np.nan
196
+ rho = np.ones(shale_frac.shape) * np.nan
197
+
198
+ # Points on sandy shale trend
199
+ k[sandy_shale_idx], mu[sandy_shale_idx] = std_functions.hashin_shtrikman_walpole(
200
+ k_silt[sandy_shale_idx],
201
+ mu_silt[sandy_shale_idx],
202
+ k_sat_mud[sandy_shale_idx],
203
+ mu_sat_mud[sandy_shale_idx],
204
+ frac_silt[sandy_shale_idx],
205
+ )
206
+
207
+ rho[sandy_shale_idx] = (
208
+ rho_b_mud[sandy_shale_idx] * (1 - frac_silt[sandy_shale_idx])
209
+ + rho_silt[sandy_shale_idx] * frac_silt[sandy_shale_idx]
210
+ )
211
+
212
+ # Points on shaly sand trend
213
+ k[shaly_sand_idx], mu[shaly_sand_idx] = std_functions.hashin_shtrikman_walpole(
214
+ k_sat_sst[shaly_sand_idx],
215
+ mu_sat_sst[shaly_sand_idx],
216
+ k_silt[shaly_sand_idx],
217
+ mu_silt[shaly_sand_idx],
218
+ frac_sand[shaly_sand_idx],
219
+ )
220
+
221
+ rho[shaly_sand_idx] = (
222
+ (1 - phi[shaly_sand_idx]) * rho_sst[shaly_sand_idx]
223
+ + phi[shaly_sand_idx] * rho_fl_sst[shaly_sand_idx]
224
+ ) * frac_sand[shaly_sand_idx] + (1 - frac_sand[shaly_sand_idx]) * rho_silt[
225
+ shaly_sand_idx
226
+ ]
227
+
228
+ vp, vs, ai, vpvs = std_functions.velocity(k, mu, rho)
229
+
230
+ # Restore original array length
231
+ vp, vs, rho, ai, vpvs = gen_utilities.filter_output(
232
+ idx_phi, (vp, vs, rho, ai, vpvs)
233
+ )
234
+
235
+ return vp, vs, rho, ai, vpvs
@@ -0,0 +1,477 @@
1
+ from warnings import warn
2
+
3
+ import numpy as np
4
+
5
+ from rock_physics_open.equinor_utilities import std_functions
6
+
7
+ from .constant_cement_models import constant_cement_model_dry
8
+ from .friable_models import friable_model_dry
9
+ from .patchy_cement_model import constant_cement_model_pcm
10
+
11
+
12
+ def patchy_cement_pressure_fluid_substitution(
13
+ k_min,
14
+ mu_min,
15
+ rho_min,
16
+ k_cem,
17
+ mu_cem,
18
+ rho_cem,
19
+ k_fl_old,
20
+ rho_fl_old,
21
+ k_fl_new,
22
+ rho_fl_new,
23
+ phi,
24
+ p_eff_old,
25
+ p_eff_new,
26
+ vp_old,
27
+ vs_old,
28
+ rho_b_old,
29
+ p_eff_low,
30
+ frac_cem_up,
31
+ frac_cem,
32
+ shear_red,
33
+ phi_c,
34
+ coord_num_func,
35
+ n,
36
+ model_type="weight",
37
+ phi_below_zero="disregard",
38
+ phi_above_phi_c="snap",
39
+ k_sat_above_k_min="disregard",
40
+ above_upper_bound="snap",
41
+ below_lower_bound="disregard",
42
+ ):
43
+ """
44
+ Patchy cement model, developed by Per Avseth and Norunn Skjei. A sandstone model in which part of the
45
+ matrix is pressure sensitive, and part is cemented.
46
+
47
+ Control parameters with default value:
48
+ model_type = 'weight': A sample by sample weight between cemented (upper) and friable (lower) bound are calculated.
49
+ The alternative is 'cement_fraction', in which the weights are calculated from one constant cement model
50
+
51
+ phi_below_zero='disregard': Values with negative porosity will either have their value snapped to zero ('snap')
52
+ or the input value will be returned ('disregard'). These option should give the same
53
+ result.
54
+
55
+ phi_above_phi_c = 'disregard': Values with porosity above critical porosity will either have their values snapped
56
+ to critical porosity ('sbap') or the input value will be returned ('disregard').
57
+
58
+ k_sat_above_k_min='disregard': Values with saturated bulk modulus above the mineral bulk modulus will have their
59
+ value snapped to the mineral modulus ('snap') or the input value will be returned ('disregard').
60
+
61
+ above_upper_bound='snap': Values with moduli above upper bound will either be snapped to upper bound ('snap')
62
+ or the input value will be returned ('disregard').
63
+
64
+ below_lower_bound='disregard': Values with moduli below lower bound will either be snapped to lower bound ('snap')
65
+ or the input value returned ('disregard').
66
+
67
+ Comment
68
+ -------
69
+ Based on program by Norunn Skjei.
70
+ Translated to Python by Harald Flesche.
71
+
72
+ Parameters
73
+ ----------
74
+ k_min : np.ndarray
75
+ Mineral bulk modulus [Pa].
76
+ mu_min : np.ndarray
77
+ Mineral shear modulus [Pa].
78
+ rho_min : np.ndarray
79
+ Mineral density [kg/m^3]
80
+ k_cem : np.ndarray
81
+ Sandstone cement bulk modulus [Pa].
82
+ mu_cem : np.ndarray
83
+ Sandstone cement shear modulus [Pa].
84
+ rho_cem : np.ndarray
85
+ Cement density [kg/m^3]
86
+ k_fl_old : np.ndarray
87
+ Initial fluid bulk modulus for sandstone fluid [Pa].
88
+ rho_fl_old : np.ndarray
89
+ Initial fluid bulk density for sandstone fluid [kg/m^3].
90
+ k_fl_new : np.ndarray
91
+ Substituted fluid bulk modulus for sandstone fluid [Pa].
92
+ rho_fl_new : np.ndarray
93
+ Substituted fluid bulk density for sandstone fluid [kg/m^3].
94
+ phi : np.ndarray
95
+ Total porosity [fraction].
96
+ p_eff_old : np.ndarray
97
+ Initial effective pressure [Pa].
98
+ p_eff_new : np.ndarray
99
+ Substituted effective pressure [Pa].
100
+ p_eff_low : float
101
+ Lower bound effective pressure [Pa].
102
+ frac_cem_up : float
103
+ Upper bound cement volume fraction [fraction].
104
+ frac_cem : cement fraction of constant cement model - representative for observed data. Must be lower than
105
+ frac_cem_up
106
+ shear_red : float
107
+ Shear reduction factor for sandstone [fraction].
108
+ phi_c : float
109
+ Critical porosity [fraction].
110
+ n : float
111
+ Coordination number [unitless].
112
+ coord_num_func : str
113
+ Indication if coordination number should be calculated from porosity or kept constant.
114
+ vp_old : np.ndarray
115
+ Initial p-wave velocity [m/s].
116
+ vs_old : np.ndarray
117
+ Initial s-wave velocity [m/s].
118
+ rho_b_old : np.ndarray
119
+ Initial bulk density [kg/m^3].
120
+ model_type : str
121
+ Version of model, either 'weight' or 'cement_fraction'
122
+ phi_below_zero : str
123
+ Control for handling of negative porosity samples.
124
+ phi_above_phi_c : str
125
+ Control for handling of porosity samples above critical porosity.
126
+ k_sat_above_k_min : str
127
+ Control for handling of bulk modulus samples above mineral bulk modulus.
128
+ above_upper_bound : str
129
+ Control for handling of samples above the upper bound.
130
+ below_lower_bound : str
131
+ Control for handling of samples below the lower bound.
132
+
133
+ Returns
134
+ -------
135
+ tuple
136
+ vp_new, vs_new, rho_b_new, ai_new, vpvs_new, k_sat_new, mu_new, wk, wmu, idx_valid :
137
+ (np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray).
138
+ vp_new :Saturated P-velocity [m/s] after fluid and pressure substitution,
139
+ vs_new : Saturated S-velocity [m/s] after fluid and pressure substitution,
140
+ rho_b_new : Saturated density [kg/m3] after fluid and pressure substitution,
141
+ ai_new : Saturated acoustic impedance [kg/m3 x m/s] after fluid and pressure substitution,
142
+ vpvs_new : Saturated Vp/Vs ratio [unitless] after fluid and pressure substitution,
143
+ k_sat_new : New saturated rock bulk modulus [Pa],
144
+ mu_new : New shear modulus [Pa],
145
+ wk, wmu : weights for k and mu between upper and lower bound,
146
+ idx_valid : samples fluid substitution is performed
147
+ """
148
+
149
+ # Original saturated bulk and shear modulus
150
+ k_sat_old, mu_old = std_functions.moduli(vp_old, vs_old, rho_b_old)
151
+
152
+ # Handling of samples that violate the assumptions of the Patchy Cement
153
+ # substitution:
154
+ # 1: phi <= 0
155
+ # 2: phi > phi_c
156
+ # 3: k_sat > k_min
157
+ idx1 = _handle_exceptions_part_1(
158
+ phi,
159
+ phi_c,
160
+ k_sat_old,
161
+ mu_old,
162
+ k_min,
163
+ mu_min,
164
+ phi_below_zero=phi_below_zero,
165
+ phi_above_phi_c=phi_above_phi_c,
166
+ k_sat_above_k_min=k_sat_above_k_min,
167
+ )
168
+
169
+ # k_dry for original pressure from Gassmann for valid samples
170
+ k_dry_old = np.ones_like(phi) * np.nan
171
+ k_dry_old[~idx1] = std_functions.gassmann_dry(
172
+ k_sat_old[~idx1], phi[~idx1], k_fl_old[~idx1], k_min[~idx1]
173
+ )
174
+
175
+ # For common handling of zero-porosity point: calculate effective
176
+ # properties using Hashin-Shtrikman
177
+ k_zero, mu_zero = std_functions.hashin_shtrikman_walpole(
178
+ k_cem, mu_cem, k_min, mu_min, frac_cem_up, bound="lower"
179
+ )
180
+
181
+ # Lower bound for estimation of weight W (input moduli in Pa, pressure in Pa)
182
+ k_low, mu_low = friable_model_dry(
183
+ k_zero,
184
+ mu_zero,
185
+ phi,
186
+ p_eff_low * np.ones_like(phi),
187
+ phi_c,
188
+ coord_num_func,
189
+ n,
190
+ shear_red,
191
+ )
192
+
193
+ # Upper bounds for estimation of weight W
194
+ k_up, mu_up = constant_cement_model_dry(
195
+ k_min, mu_min, k_cem, mu_cem, phi, frac_cem_up, phi_c, n, shear_red
196
+ )
197
+
198
+ # Handling of samples that violate the assumptions of the Patchy Cement
199
+ # substitution:
200
+ # 4: Model violation in Gassmann (k_dry < 0.0), friable model or constant cement
201
+ # model
202
+ # 5: k_dry, mu below lower bound
203
+ # 6: k_dry, mu above upper bound
204
+
205
+ idx2 = _handle_exceptions_part_2(
206
+ k_dry_old,
207
+ mu_old,
208
+ k_up,
209
+ mu_up,
210
+ k_low,
211
+ mu_low,
212
+ idx1,
213
+ above_upper_bound=above_upper_bound,
214
+ below_lower_bound=below_lower_bound,
215
+ )
216
+ idx_valid = np.logical_not(idx2)
217
+
218
+ # Pressure sensitive model for initial pressure
219
+ k_dry_p_init, mu_p_init = friable_model_dry(
220
+ k_zero, mu_zero, phi, p_eff_old, phi_c, coord_num_func, n, shear_red
221
+ )
222
+
223
+ # Pressure sensitive model for final pressure
224
+ k_dry_p_final, mu_p_final = friable_model_dry(
225
+ k_zero, mu_zero, phi, p_eff_new, phi_c, coord_num_func, n, shear_red
226
+ )
227
+
228
+ # Weights - either on a sample basis or by a representative constant cement model
229
+ if model_type == "weight":
230
+ w_k = _calculate_weight(k_dry_old, k_low, k_up)
231
+ w_mu = _calculate_weight(mu_old, mu_low, mu_up)
232
+ else:
233
+ k_cc, mu_cc = constant_cement_model_pcm(
234
+ k_min,
235
+ mu_min,
236
+ k_cem,
237
+ mu_cem,
238
+ k_zero,
239
+ mu_zero,
240
+ phi,
241
+ frac_cem,
242
+ phi_c,
243
+ n,
244
+ shear_red,
245
+ )[0:2]
246
+ w_k = _calculate_weight(k_cc, k_low, k_up)
247
+ w_mu = _calculate_weight(mu_cc, mu_low, mu_up)
248
+
249
+ w_cem = 0.5 * (w_mu + w_k)
250
+ k_mod_dry = np.nan * np.ones_like(phi)
251
+ k_mod = np.nan * np.ones_like(phi)
252
+ mu_mod = np.nan * np.ones_like(phi)
253
+ rho_mod = np.nan * np.ones_like(phi)
254
+
255
+ # Calculate modelled values - first for the valid samples
256
+ k_mod_dry[idx_valid] = k_dry_p_init[idx_valid] + w_k[idx_valid] * (
257
+ k_up[idx_valid] - k_low[idx_valid]
258
+ )
259
+ k_mod[idx_valid] = std_functions.gassmann(
260
+ k_mod_dry[idx_valid], phi[idx_valid], k_fl_old[idx_valid], k_min[idx_valid]
261
+ )
262
+ mu_mod[idx_valid] = mu_p_init[idx_valid] + w_mu[idx_valid] * (
263
+ mu_up[idx_valid] - mu_low[idx_valid]
264
+ )
265
+ rho_mod[idx_valid] = (
266
+ phi[idx_valid] * rho_fl_old[idx_valid]
267
+ + (1.0 - phi[idx_valid] - w_cem[idx_valid] * frac_cem_up) * rho_min[idx_valid]
268
+ + w_cem[idx_valid] * frac_cem_up * rho_cem[idx_valid]
269
+ )
270
+
271
+ # Fill in input values for invalid samples
272
+ k_mod[idx2] = k_sat_old[idx2]
273
+ mu_mod[idx2] = mu_old[idx2]
274
+ rho_mod[idx2] = rho_b_old[idx2]
275
+ vp_mod, vs_mod = std_functions.velocity(k_mod, mu_mod, rho_mod)[0:2]
276
+
277
+ # Calculate residuals. For invalid samples, the residuals will be set to zero
278
+ vp_res = vp_old - vp_mod
279
+ vs_res = vs_old - vs_mod
280
+ rho_res = rho_b_old - rho_mod
281
+
282
+ # Estimate dry moduli values according to initial dry values, changed
283
+ # pressure and estimated pressure sensitivity
284
+ k_dry_new = (
285
+ k_dry_old
286
+ * (k_dry_p_final + w_k * (k_up - k_dry_p_final))
287
+ / (k_dry_p_init + w_k * (k_up - k_dry_p_init))
288
+ )
289
+ mu_new = (
290
+ mu_old
291
+ * (mu_p_final + w_mu * (mu_up - mu_p_final))
292
+ / (mu_p_init + w_mu * (mu_up - mu_p_init))
293
+ )
294
+
295
+ # New k_sat for new pressure from Gassmann
296
+ k_sat_new = np.ones_like(phi) * np.nan
297
+ if np.any(idx_valid):
298
+ k_sat_new[idx_valid] = std_functions.gassmann(
299
+ k_dry_new[idx_valid], phi[idx_valid], k_fl_new[idx_valid], k_min[idx_valid]
300
+ )
301
+ k_sat_new[idx2] = k_sat_old[idx2]
302
+ mu_new[idx2] = mu_old[idx2]
303
+
304
+ # New saturated density
305
+ rho_b_new = np.ones_like(phi) * np.nan
306
+ rho_b_new[idx_valid] = rho_b_old[idx_valid] + phi[idx_valid] * (
307
+ rho_fl_new[idx_valid] - rho_fl_old[idx_valid]
308
+ )
309
+ rho_b_new[idx2] = rho_b_old[idx2]
310
+
311
+ # New saturated velocities and derived values
312
+ vp_new, vs_new = std_functions.velocity(k_sat_new, mu_new, rho_b_new)[0:2]
313
+ ai_new = vp_new * rho_b_new
314
+ vpvs_new = vp_new / vs_new
315
+
316
+ return (
317
+ vp_new,
318
+ vs_new,
319
+ rho_b_new,
320
+ ai_new,
321
+ vpvs_new,
322
+ w_k,
323
+ w_mu,
324
+ idx_valid,
325
+ vp_res,
326
+ vs_res,
327
+ rho_res,
328
+ )
329
+
330
+
331
+ def _handle_exceptions_part_1(
332
+ phi_vec: np.ndarray,
333
+ phi_c_const: float,
334
+ k_sat: np.ndarray,
335
+ mu: np.ndarray,
336
+ k_min: np.ndarray,
337
+ mu_min: np.ndarray,
338
+ phi_below_zero: str = "disregard",
339
+ phi_above_phi_c: str = "snap",
340
+ k_sat_above_k_min: str = "disregard",
341
+ ) -> np.ndarray:
342
+ # Handling of samples that violate assumptions of the Patchy Cement substitution:
343
+ # 1: phi < 0
344
+ # 2: phi > phi_c
345
+ # 3: k_sat > k_min, mu > mu_min
346
+
347
+ # Handling of case 1:
348
+ idx1 = phi_vec < 0.0
349
+ if phi_below_zero == "snap":
350
+ phi_vec[idx1] = 0.0
351
+ idx1 = np.zeros(phi_vec.shape).astype(bool)
352
+ elif phi_below_zero == "disregard":
353
+ pass
354
+ else:
355
+ raise ValueError('unknown argument for parameter "phi_below_zero"')
356
+
357
+ # Handling of case 2:
358
+ idx2 = phi_vec > phi_c_const
359
+ if phi_above_phi_c == "snap":
360
+ phi_vec[idx2] = phi_c_const
361
+ idx2 = np.zeros(phi_vec.shape).astype(bool)
362
+ elif phi_above_phi_c == "disregard":
363
+ pass
364
+ else:
365
+ raise ValueError('unknown argument for parameter "phi_above_phi_c"')
366
+
367
+ # Handling of case 3:
368
+ idx3 = np.logical_or(k_sat > k_min, mu > mu_min)
369
+ if k_sat_above_k_min == "snap":
370
+ k_min[idx3] = k_sat[idx3]
371
+ mu_min[idx3] = mu[idx3]
372
+ idx3 = np.zeros(k_sat.shape).astype(bool)
373
+ elif k_sat_above_k_min == "disregard":
374
+ pass
375
+ else:
376
+ raise ValueError('unknown argument for parameter "k_sat_above_k_min"')
377
+ return np.any(np.vstack((idx1, idx2, idx3)), axis=0)
378
+
379
+
380
+ def _handle_exceptions_part_2(
381
+ k_dry: np.ndarray,
382
+ mu: np.ndarray,
383
+ k_up: np.ndarray,
384
+ mu_up: np.ndarray,
385
+ k_low: np.ndarray,
386
+ mu_low: np.ndarray,
387
+ idx: np.ndarray,
388
+ above_upper_bound: str = "snap",
389
+ below_lower_bound: str = "disregard",
390
+ ) -> np.ndarray:
391
+ # Handling of samples that violate the assumptions of the Patchy Cement
392
+ # substitution:
393
+ # 4: Model violation in Gassmann (k_dry < 0.0), friable model or constant cement
394
+ # model
395
+ # 5: k_dry, mu below lower bound
396
+ # 6: k_dry, mu above upper bound
397
+
398
+ # Identification of case 4 and calculation of dry rock properties:
399
+ # gassmann_dry returns dry property < 0 as NaN
400
+ idx_nan = np.isnan(k_dry)
401
+
402
+ # Can be non-physical cases in friable model
403
+ idx_nan = np.logical_or(idx_nan, np.isnan(k_low))
404
+
405
+ # Can be non-physical cases in constant cement model
406
+ idx_nan = np.logical_or(idx_nan, np.isnan(k_up))
407
+
408
+ # Handling of case 5:
409
+ idx5k = np.zeros_like(k_dry).astype(bool)
410
+ idx5mu = np.zeros_like(k_dry).astype(bool)
411
+ np.less(k_dry, k_low, out=idx5k, where=~idx_nan)
412
+ np.less(mu, mu_low, out=idx5mu, where=~idx_nan)
413
+ if below_lower_bound == "snap":
414
+ k_dry[idx5k] = k_low[idx5k]
415
+ mu[idx5mu] = mu_low[idx5mu]
416
+ idx5 = np.zeros(k_dry.shape).astype(bool)
417
+ elif below_lower_bound == "disregard":
418
+ idx5 = np.logical_or(idx5k, idx5mu)
419
+ else:
420
+ raise ValueError('unknown argument for parameter "below_lower_bound"')
421
+
422
+ # Handling of case 6:
423
+ if above_upper_bound == "snap":
424
+ idx6k = np.zeros_like(k_dry).astype(bool)
425
+ idx6mu = np.zeros_like(k_dry).astype(bool)
426
+ np.greater(k_dry, k_up, out=idx6k, where=~idx_nan)
427
+ np.greater(mu, mu_up, out=idx6mu, where=~idx_nan)
428
+ k_dry[idx6k] = k_up[idx6k]
429
+ mu[idx6mu] = mu_up[idx6mu]
430
+ idx6 = np.zeros(k_dry.shape).astype(bool)
431
+ elif above_upper_bound == "disregard":
432
+ idx6 = np.logical_or(
433
+ np.greater(k_dry, k_up, where=~idx_nan),
434
+ np.greater(mu, mu_up, where=~idx_nan),
435
+ )
436
+ else:
437
+ raise ValueError('unknown argument for parameter "above_upper_bound"')
438
+
439
+ # Exception for all cases 1-6:
440
+ return np.any(np.vstack((idx, idx5, idx6, idx_nan)), axis=0)
441
+
442
+
443
+ def _calculate_weight(
444
+ dry_modulus: np.ndarray,
445
+ low_modulus: np.ndarray,
446
+ high_modulus: np.ndarray,
447
+ force_full_range: str = False,
448
+ ) -> np.ndarray:
449
+ """
450
+ Calculates a weight for a value between a lower and upper bound. Used for moduli
451
+
452
+
453
+ Parameters
454
+ ----------
455
+ dry_modulus: value to be evaluated
456
+ low_modulus: lower bound
457
+ high_modulus: upper bound
458
+ force_full_range: do not clip weight to a range of [0.0, 1.0]
459
+
460
+ Returns
461
+ -------
462
+ weight: value between [0.0, 1.0]
463
+ """
464
+ idx = np.abs(high_modulus - low_modulus) < 2.0 * np.finfo(float).eps
465
+ if np.any(idx):
466
+ warn(
467
+ f"weight estimation: high and low bound is identical for "
468
+ f"{np.sum(high_modulus == low_modulus)} samples"
469
+ )
470
+ weight = np.ones_like(dry_modulus)
471
+ weight[~idx] = (dry_modulus[~idx] - low_modulus[~idx]) / (
472
+ high_modulus[~idx] - low_modulus[~idx]
473
+ )
474
+ # Catch cases outside the allowed range
475
+ if not force_full_range:
476
+ weight = np.clip(weight, 0.0, 1.0)
477
+ return weight