rock-physics-open 0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rock-physics-open might be problematic. Click here for more details.
- rock_physics_open/__init__.py +0 -0
- rock_physics_open/equinor_utilities/__init__.py +0 -0
- rock_physics_open/equinor_utilities/anisotropy.py +162 -0
- rock_physics_open/equinor_utilities/classification_functions/__init__.py +17 -0
- rock_physics_open/equinor_utilities/classification_functions/class_stats.py +58 -0
- rock_physics_open/equinor_utilities/classification_functions/lin_class.py +47 -0
- rock_physics_open/equinor_utilities/classification_functions/mahal_class.py +56 -0
- rock_physics_open/equinor_utilities/classification_functions/norm_class.py +65 -0
- rock_physics_open/equinor_utilities/classification_functions/poly_class.py +40 -0
- rock_physics_open/equinor_utilities/classification_functions/post_prob.py +26 -0
- rock_physics_open/equinor_utilities/classification_functions/two_step_classification.py +46 -0
- rock_physics_open/equinor_utilities/conversions.py +10 -0
- rock_physics_open/equinor_utilities/gen_utilities/__init__.py +11 -0
- rock_physics_open/equinor_utilities/gen_utilities/dict_to_float.py +33 -0
- rock_physics_open/equinor_utilities/gen_utilities/dim_check_vector.py +83 -0
- rock_physics_open/equinor_utilities/gen_utilities/filter_input.py +126 -0
- rock_physics_open/equinor_utilities/gen_utilities/filter_output.py +78 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/__init__.py +14 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/dummy_vars.py +42 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/exponential_model.py +119 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/import_ml_models.py +61 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/run_regression.py +151 -0
- rock_physics_open/equinor_utilities/machine_learning_utilities/sigmoidal_model.py +188 -0
- rock_physics_open/equinor_utilities/snapshot_test_utilities/__init__.py +10 -0
- rock_physics_open/equinor_utilities/snapshot_test_utilities/compare_snapshots.py +145 -0
- rock_physics_open/equinor_utilities/snapshot_test_utilities/snapshots.py +54 -0
- rock_physics_open/equinor_utilities/std_functions/__init__.py +43 -0
- rock_physics_open/equinor_utilities/std_functions/backus_ave.py +53 -0
- rock_physics_open/equinor_utilities/std_functions/dvorkin_nur.py +69 -0
- rock_physics_open/equinor_utilities/std_functions/gassmann.py +140 -0
- rock_physics_open/equinor_utilities/std_functions/hashin_shtrikman.py +195 -0
- rock_physics_open/equinor_utilities/std_functions/hertz_mindlin.py +43 -0
- rock_physics_open/equinor_utilities/std_functions/moduli_velocity.py +51 -0
- rock_physics_open/equinor_utilities/std_functions/reflection_eq.py +98 -0
- rock_physics_open/equinor_utilities/std_functions/rho.py +59 -0
- rock_physics_open/equinor_utilities/std_functions/voigt_reuss_hill.py +128 -0
- rock_physics_open/equinor_utilities/std_functions/walton.py +38 -0
- rock_physics_open/equinor_utilities/std_functions/wood_brie.py +77 -0
- rock_physics_open/equinor_utilities/various_utilities/Equinor_logo.gif +0 -0
- rock_physics_open/equinor_utilities/various_utilities/Equinor_logo.ico +0 -0
- rock_physics_open/equinor_utilities/various_utilities/__init__.py +24 -0
- rock_physics_open/equinor_utilities/various_utilities/display_result_statistics.py +83 -0
- rock_physics_open/equinor_utilities/various_utilities/gassmann_dry_mod.py +37 -0
- rock_physics_open/equinor_utilities/various_utilities/gassmann_mod.py +37 -0
- rock_physics_open/equinor_utilities/various_utilities/gassmann_sub_mod.py +53 -0
- rock_physics_open/equinor_utilities/various_utilities/hs_average.py +40 -0
- rock_physics_open/equinor_utilities/various_utilities/pressure.py +88 -0
- rock_physics_open/equinor_utilities/various_utilities/reflectivity.py +85 -0
- rock_physics_open/equinor_utilities/various_utilities/timeshift.py +91 -0
- rock_physics_open/equinor_utilities/various_utilities/vp_vs_rho_set_statistics.py +154 -0
- rock_physics_open/equinor_utilities/various_utilities/vrh_3_min.py +61 -0
- rock_physics_open/fluid_models/__init__.py +9 -0
- rock_physics_open/fluid_models/brine_model/__init__.py +5 -0
- rock_physics_open/fluid_models/brine_model/brine_properties.py +143 -0
- rock_physics_open/fluid_models/gas_model/__init__.py +5 -0
- rock_physics_open/fluid_models/gas_model/gas_properties.py +277 -0
- rock_physics_open/fluid_models/oil_model/__init__.py +5 -0
- rock_physics_open/fluid_models/oil_model/dead_oil_density.py +60 -0
- rock_physics_open/fluid_models/oil_model/dead_oil_velocity.py +28 -0
- rock_physics_open/fluid_models/oil_model/live_oil_density.py +79 -0
- rock_physics_open/fluid_models/oil_model/live_oil_velocity.py +24 -0
- rock_physics_open/fluid_models/oil_model/oil_bubble_point.py +69 -0
- rock_physics_open/fluid_models/oil_model/oil_properties.py +114 -0
- rock_physics_open/sandstone_models/__init__.py +57 -0
- rock_physics_open/sandstone_models/cemented_shalysand_sandyshale_models.py +304 -0
- rock_physics_open/sandstone_models/constant_cement_models.py +204 -0
- rock_physics_open/sandstone_models/constant_cement_optimisation.py +122 -0
- rock_physics_open/sandstone_models/contact_cement_model.py +138 -0
- rock_physics_open/sandstone_models/curvefit_sandstone_models.py +143 -0
- rock_physics_open/sandstone_models/friable_models.py +178 -0
- rock_physics_open/sandstone_models/friable_optimisation.py +112 -0
- rock_physics_open/sandstone_models/friable_shalysand_sandyshale_models.py +235 -0
- rock_physics_open/sandstone_models/patchy_cement_fluid_substitution_model.py +477 -0
- rock_physics_open/sandstone_models/patchy_cement_model.py +286 -0
- rock_physics_open/sandstone_models/patchy_cement_optimisation.py +251 -0
- rock_physics_open/sandstone_models/unresolved_cemented_sandshale_models.py +134 -0
- rock_physics_open/sandstone_models/unresolved_friable_sandshale_models.py +126 -0
- rock_physics_open/shale_models/__init__.py +19 -0
- rock_physics_open/shale_models/dem.py +174 -0
- rock_physics_open/shale_models/dem_dual_por.py +61 -0
- rock_physics_open/shale_models/kus_tok.py +59 -0
- rock_physics_open/shale_models/multi_sca.py +133 -0
- rock_physics_open/shale_models/pq.py +102 -0
- rock_physics_open/shale_models/sca.py +90 -0
- rock_physics_open/shale_models/shale4_mineral.py +147 -0
- rock_physics_open/shale_models/shale4_mineral_dem_overlay.py +92 -0
- rock_physics_open/span_wagner/__init__.py +5 -0
- rock_physics_open/span_wagner/co2_properties.py +438 -0
- rock_physics_open/span_wagner/coefficients.py +165 -0
- rock_physics_open/span_wagner/equations.py +104 -0
- rock_physics_open/span_wagner/tables/__init__.py +0 -0
- rock_physics_open/span_wagner/tables/carbon_dioxide_density.npz +0 -0
- rock_physics_open/span_wagner/tables/lookup_table.py +33 -0
- rock_physics_open/t_matrix_models/Equinor_logo.ico +0 -0
- rock_physics_open/t_matrix_models/__init__.py +45 -0
- rock_physics_open/t_matrix_models/carbonate_pressure_substitution.py +124 -0
- rock_physics_open/t_matrix_models/curvefit_t_matrix_exp.py +124 -0
- rock_physics_open/t_matrix_models/curvefit_t_matrix_min.py +86 -0
- rock_physics_open/t_matrix_models/opt_subst_utilities.py +415 -0
- rock_physics_open/t_matrix_models/parse_t_matrix_inputs.py +297 -0
- rock_physics_open/t_matrix_models/run_t_matrix.py +243 -0
- rock_physics_open/t_matrix_models/t_matrix_C.py +210 -0
- rock_physics_open/t_matrix_models/t_matrix_opt_fluid_sub_exp.py +137 -0
- rock_physics_open/t_matrix_models/t_matrix_opt_fluid_sub_petec.py +163 -0
- rock_physics_open/t_matrix_models/t_matrix_opt_forward_model_exp.py +72 -0
- rock_physics_open/t_matrix_models/t_matrix_opt_forward_model_min.py +86 -0
- rock_physics_open/t_matrix_models/t_matrix_parameter_optimisation_exp.py +172 -0
- rock_physics_open/t_matrix_models/t_matrix_parameter_optimisation_min.py +159 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/__init__.py +12 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/array_functions.py +75 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_c_eff.py +163 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_isolated.py +95 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_kd.py +40 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_kd_eff.py +116 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_kd_uuv.py +18 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_pressure.py +140 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_t.py +71 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_td.py +42 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_theta.py +43 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_x.py +33 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/calc_z.py +50 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/check_and_tile.py +43 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/g_tensor.py +140 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/iso_av.py +60 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/iso_ave_all.py +55 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/pressure_input.py +44 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/t_matrix_vec.py +278 -0
- rock_physics_open/t_matrix_models/t_matrix_vector/velocity_vti_angles.py +81 -0
- rock_physics_open/t_matrix_models/tmatrix_python.dll +0 -0
- rock_physics_open/t_matrix_models/tmatrix_python.so +0 -0
- rock_physics_open/ternary_plots/__init__.py +3 -0
- rock_physics_open/ternary_plots/gen_ternary_plot.py +73 -0
- rock_physics_open/ternary_plots/shale_prop_ternary.py +337 -0
- rock_physics_open/ternary_plots/ternary_patches.py +277 -0
- rock_physics_open/ternary_plots/ternary_plot_utilities.py +197 -0
- rock_physics_open/ternary_plots/unconventionals_ternary.py +75 -0
- rock_physics_open/version.py +21 -0
- rock_physics_open-0.0.dist-info/METADATA +92 -0
- rock_physics_open-0.0.dist-info/RECORD +142 -0
- rock_physics_open-0.0.dist-info/WHEEL +5 -0
- rock_physics_open-0.0.dist-info/licenses/LICENSE +165 -0
- rock_physics_open-0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
from sys import byteorder
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
WRONG_BYTEORDER = ">" if byteorder == "little" else "<"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def filter_input_log(
|
|
10
|
+
args, working_int=None, negative=False, no_zero=False, positive=True
|
|
11
|
+
):
|
|
12
|
+
"""
|
|
13
|
+
Check for valid input values in numpy arrays or pandas data frames. Default behaviour is to
|
|
14
|
+
identify missing values - assumed to be NaN and Inf. Other conditions
|
|
15
|
+
can be stated in the key word arguments. Unknown conditions are ignored and a warning
|
|
16
|
+
is issued. Run dim_check_vector to make sure that all inputs have the same length.
|
|
17
|
+
Erroneous values in a sample in one log will remove the sample from all the logs.
|
|
18
|
+
All inputs must have the same array length (data frames the same number of indices).
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
args : list or tuple or np.ndarray or pd.DataFrame
|
|
23
|
+
Inputs to be filtered, single array or dataframe or lists of arrays or data frames.
|
|
24
|
+
working_int : np.ndarray
|
|
25
|
+
Valid positions are shown as values > 0.
|
|
26
|
+
negative : bool
|
|
27
|
+
Positive values are excluded (zero values are retained).
|
|
28
|
+
no_zero : bool
|
|
29
|
+
Zero values are excluded.
|
|
30
|
+
positive : bool
|
|
31
|
+
Negative values are excluded.
|
|
32
|
+
|
|
33
|
+
Returns
|
|
34
|
+
-------
|
|
35
|
+
tuple
|
|
36
|
+
idx, output_args : (np.ndarray, list)
|
|
37
|
+
indices of valid values [bool],
|
|
38
|
+
list of input arrays at valid indices.
|
|
39
|
+
"""
|
|
40
|
+
type_error = "filter_input_log: unknown input data type: {}".format(type(args))
|
|
41
|
+
size_error = "filter_input_log: inputs of different length"
|
|
42
|
+
|
|
43
|
+
if not isinstance(args, (list, tuple, np.ndarray, pd.DataFrame)):
|
|
44
|
+
raise ValueError(type_error)
|
|
45
|
+
|
|
46
|
+
# Make sure that 'args' is iterable
|
|
47
|
+
if isinstance(args, (np.ndarray, pd.DataFrame)):
|
|
48
|
+
args = [args]
|
|
49
|
+
|
|
50
|
+
# Input tuple
|
|
51
|
+
if isinstance(args, tuple):
|
|
52
|
+
args = list(args)
|
|
53
|
+
|
|
54
|
+
# Need to preserve original inputs
|
|
55
|
+
input_args = args.copy()
|
|
56
|
+
|
|
57
|
+
# Test that inputs are of the right types and the same length
|
|
58
|
+
if not np.all([isinstance(log, (np.ndarray, pd.DataFrame)) for log in args]):
|
|
59
|
+
raise ValueError(type_error)
|
|
60
|
+
if not np.all([log.shape[0] == args[0].shape[0] for log in args]):
|
|
61
|
+
raise ValueError(size_error)
|
|
62
|
+
|
|
63
|
+
# Generate pandas series from numpy arrays
|
|
64
|
+
args = [pd.Series(log) if isinstance(log, np.ndarray) else log for log in args]
|
|
65
|
+
# Merge into a data frame
|
|
66
|
+
logs = pd.concat(args, axis=1)
|
|
67
|
+
|
|
68
|
+
# If any of the input logs are of type boolean, False means that they should not be included,
|
|
69
|
+
# regardless of filter flags
|
|
70
|
+
# https://github.com/pandas-dev/pandas/issues/32432
|
|
71
|
+
# idx = ~logs.any(bool_only=True, axis=1)
|
|
72
|
+
# Need to do it the cumbersome way for the time being
|
|
73
|
+
bool_col = logs.dtypes.apply(lambda dtype: dtype == "bool")
|
|
74
|
+
if any(bool_col):
|
|
75
|
+
idx = ~logs.loc[:, logs.columns[bool_col]].any(axis=1)
|
|
76
|
+
logs.drop(columns=logs.columns[bool_col], inplace=True)
|
|
77
|
+
else:
|
|
78
|
+
idx = pd.Series(index=logs.index, data=np.zeros_like(logs.index).astype(bool))
|
|
79
|
+
|
|
80
|
+
# Standard checks: NaN and Inf
|
|
81
|
+
idx = np.logical_or(idx, logs.isna().any(axis=1))
|
|
82
|
+
idx = np.logical_or(idx, logs.isin([np.inf, -np.inf]).any(axis=1))
|
|
83
|
+
|
|
84
|
+
# Remove columns with dtype that is not numeric
|
|
85
|
+
obj_col = [dd.kind not in ["i", "u", "f", "c"] for dd in logs.dtypes]
|
|
86
|
+
logs.drop(columns=logs.columns[obj_col], inplace=True)
|
|
87
|
+
|
|
88
|
+
# Checks according to the input options input_dict
|
|
89
|
+
# Only consider working interval if it is included or set to some value
|
|
90
|
+
if working_int is not None and not np.all(working_int == 0):
|
|
91
|
+
idx = np.logical_or(idx, working_int == 0)
|
|
92
|
+
if negative:
|
|
93
|
+
# noinspection PyTypeChecker
|
|
94
|
+
idx = np.logical_or(idx, (logs >= 0.0).all(axis=1))
|
|
95
|
+
# idx = np.logical_or(idx, logs.loc[logs > 0.0]).any(axis=1)
|
|
96
|
+
if no_zero:
|
|
97
|
+
idx = np.logical_or(idx, (logs == 0.0).any(axis=1))
|
|
98
|
+
if positive:
|
|
99
|
+
# noinspection PyTypeChecker
|
|
100
|
+
idx = np.logical_or(idx, (logs < 0.0).any(axis=1))
|
|
101
|
+
|
|
102
|
+
# Negate idx to identify samples to retain
|
|
103
|
+
idx = np.logical_not(idx)
|
|
104
|
+
num_valid_samples = idx.sum()
|
|
105
|
+
if num_valid_samples == 0:
|
|
106
|
+
raise ValueError("No acceptable input values")
|
|
107
|
+
for i in range(len(input_args)):
|
|
108
|
+
if isinstance(input_args[i], np.ndarray):
|
|
109
|
+
input_args[i] = input_args[i][idx]
|
|
110
|
+
else: # data frame
|
|
111
|
+
# https://pandas.pydata.org/pandas-docs/stable/user_guide/gotchas.html#byte-ordering-issues
|
|
112
|
+
|
|
113
|
+
check_type = (
|
|
114
|
+
np.array([col_type.byteorder for col_type in input_args[i].dtypes])
|
|
115
|
+
== WRONG_BYTEORDER
|
|
116
|
+
)
|
|
117
|
+
if np.any(check_type):
|
|
118
|
+
tmp_array = (
|
|
119
|
+
input_args[i].to_numpy().byteswap().newbyteorder().astype(float)
|
|
120
|
+
)
|
|
121
|
+
cols = input_args[i].columns
|
|
122
|
+
for j in range(check_type.shape[0]):
|
|
123
|
+
if check_type[j]:
|
|
124
|
+
input_args[i][cols[j]] = tmp_array[:, j]
|
|
125
|
+
input_args[i] = input_args[i].loc[idx]
|
|
126
|
+
return np.array(idx), input_args
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from sys import byteorder
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
WRONG_BYTEORDER = ">" if byteorder == "little" else "<"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def filter_output(idx_inp, inp_log):
|
|
10
|
+
"""
|
|
11
|
+
Function to restore outputs from a plugin to original length and
|
|
12
|
+
with values at correct positions. The logs are assumed to go through
|
|
13
|
+
matching input filtering done by gen_utilities.filter_input_log earlier.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
idx_inp: np.ndarray
|
|
18
|
+
boolean array which is True at locations to be filled, length idx_inp is returned length of
|
|
19
|
+
arrays or data frames.
|
|
20
|
+
inp_log: tuple or list or np.ndarray or pd.DataFrame
|
|
21
|
+
input numpy array(s) or pandas data frame(s), in list or tuple that are to be expanded to original
|
|
22
|
+
length.
|
|
23
|
+
|
|
24
|
+
Returns
|
|
25
|
+
-------
|
|
26
|
+
return_logs : list
|
|
27
|
+
Expanded inputs.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def _expand_array(idx, inp_single_log):
|
|
31
|
+
logs = np.ones(idx.shape, dtype=float) * np.nan
|
|
32
|
+
try:
|
|
33
|
+
logs[idx] = inp_single_log.flatten()
|
|
34
|
+
except ValueError:
|
|
35
|
+
# Assume that the dtype of the input log is not fit for casting to float, set to object and retry
|
|
36
|
+
logs = logs.astype(object)
|
|
37
|
+
logs[idx] = inp_single_log
|
|
38
|
+
return logs.reshape(idx.shape)
|
|
39
|
+
|
|
40
|
+
def _expand_df(idx, inp_df):
|
|
41
|
+
logs = pd.DataFrame(columns=inp_df.columns, index=np.arange(idx.shape[0]))
|
|
42
|
+
logs.loc[idx] = inp_df
|
|
43
|
+
return logs
|
|
44
|
+
|
|
45
|
+
if not isinstance(inp_log, (list, tuple, np.ndarray, pd.DataFrame)):
|
|
46
|
+
raise ValueError(
|
|
47
|
+
"filter_output: unknown input data type: {}".format(type(inp_log))
|
|
48
|
+
)
|
|
49
|
+
if not isinstance(idx_inp, (list, np.ndarray)):
|
|
50
|
+
raise ValueError(
|
|
51
|
+
"filter_output: unknown filter array data type: {}".format(type(idx_inp))
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Make iterable in case of single input
|
|
55
|
+
if isinstance(inp_log, (np.ndarray, pd.DataFrame)):
|
|
56
|
+
inp_log = [inp_log]
|
|
57
|
+
if isinstance(idx_inp, np.ndarray):
|
|
58
|
+
idx_inp = [idx_inp]
|
|
59
|
+
|
|
60
|
+
# Possible to simplify?
|
|
61
|
+
if len(idx_inp) != len(inp_log):
|
|
62
|
+
if len(idx_inp) == 1:
|
|
63
|
+
idx_inp = idx_inp * len(inp_log)
|
|
64
|
+
else:
|
|
65
|
+
raise ValueError(
|
|
66
|
+
"filter_output: mismatch between length of filter arrays and inputs: {} and {}".format(
|
|
67
|
+
len(idx_inp), len(inp_log)
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
return_logs = []
|
|
72
|
+
for this_idx, this_log in zip(idx_inp, inp_log):
|
|
73
|
+
if isinstance(this_log, np.ndarray):
|
|
74
|
+
return_logs.append(_expand_array(this_idx, this_log))
|
|
75
|
+
elif isinstance(this_log, pd.DataFrame):
|
|
76
|
+
return_logs.append(_expand_df(this_idx, this_log))
|
|
77
|
+
|
|
78
|
+
return return_logs
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from .dummy_vars import generate_dummy_vars
|
|
2
|
+
from .exponential_model import CarbonateExponentialPressure
|
|
3
|
+
from .import_ml_models import import_model
|
|
4
|
+
from .run_regression import run_regression
|
|
5
|
+
from .sigmoidal_model import CarbonateSigmoidalPressure, Sigmoid
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"generate_dummy_vars",
|
|
9
|
+
"CarbonateExponentialPressure",
|
|
10
|
+
"import_model",
|
|
11
|
+
"run_regression",
|
|
12
|
+
"CarbonateSigmoidalPressure",
|
|
13
|
+
"Sigmoid",
|
|
14
|
+
]
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from sklearn.preprocessing import OneHotEncoder
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def generate_dummy_vars(inp_frame, class_var, ohe=None):
|
|
6
|
+
"""
|
|
7
|
+
From categorical variables generate a one-hot-encoder, i.e. each value in the categorical variable becomes a binary
|
|
8
|
+
variable. See sklearn.preprocessing.OneHotEncoder.
|
|
9
|
+
|
|
10
|
+
Parameters
|
|
11
|
+
----------
|
|
12
|
+
inp_frame : pd.DataFrame
|
|
13
|
+
Input data containing categorical variables.
|
|
14
|
+
class_var : str
|
|
15
|
+
Name of categorical variable.
|
|
16
|
+
ohe : preprocessing.OneHotEncoder
|
|
17
|
+
One-hot-encoder object.
|
|
18
|
+
|
|
19
|
+
Returns
|
|
20
|
+
-------
|
|
21
|
+
dum_features, no_dummy_cols, dum_var_names : (np.ndarray, int, np.ndarray)
|
|
22
|
+
dum_features: 2D array with transformed dummy variables, no_dummy_cols: number of columns in returned array,
|
|
23
|
+
dum_var_names: automatically generated feature names.
|
|
24
|
+
"""
|
|
25
|
+
from pandas.api.types import is_numeric_dtype
|
|
26
|
+
|
|
27
|
+
if is_numeric_dtype(inp_frame[class_var]):
|
|
28
|
+
# Make sure that the chosen indicator variable contains discrete values
|
|
29
|
+
inp_frame = inp_frame.astype({class_var: "int32"})
|
|
30
|
+
|
|
31
|
+
features_in = np.array(inp_frame[class_var]).reshape(-1, 1)
|
|
32
|
+
|
|
33
|
+
if ohe is None:
|
|
34
|
+
classes = features_in
|
|
35
|
+
ohe = OneHotEncoder(categories="auto", sparse_output=False)
|
|
36
|
+
ohe.fit(classes)
|
|
37
|
+
|
|
38
|
+
dum_features = ohe.transform(features_in)
|
|
39
|
+
no_dummy_cols = dum_features.shape[1]
|
|
40
|
+
dum_var_names = ohe.get_feature_names_out()
|
|
41
|
+
|
|
42
|
+
return dum_features, no_dummy_cols, dum_var_names
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import pickle
|
|
2
|
+
from typing import Union
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _verify_input(inp_arr):
|
|
8
|
+
if isinstance(inp_arr, np.ndarray) and not (
|
|
9
|
+
inp_arr.ndim == 2 and inp_arr.shape[1] == 3
|
|
10
|
+
):
|
|
11
|
+
raise ValueError(
|
|
12
|
+
"Input to predict method should be an nx3 numpy array with columns velocity, in situ "
|
|
13
|
+
"pressure and depleted pressure"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CarbonateExponentialPressure:
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
a_factor: float = None,
|
|
21
|
+
b_factor: float = None,
|
|
22
|
+
model_max_pressure: float = None,
|
|
23
|
+
description: str = "",
|
|
24
|
+
):
|
|
25
|
+
self._a_factor = a_factor
|
|
26
|
+
self._b_factor = b_factor
|
|
27
|
+
self._model_max_pressure = model_max_pressure
|
|
28
|
+
self._description = description
|
|
29
|
+
|
|
30
|
+
def todict(self):
|
|
31
|
+
return {
|
|
32
|
+
"a_factor": self._a_factor,
|
|
33
|
+
"b_factor": self._b_factor,
|
|
34
|
+
"model_max_pressure": self._model_max_pressure,
|
|
35
|
+
"description": self._description,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def a_factor(self) -> float:
|
|
40
|
+
return self._a_factor
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def b_factor(self) -> float:
|
|
44
|
+
return self._b_factor
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def max_pressure(self) -> float:
|
|
48
|
+
return self._model_max_pressure
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def description(self) -> str:
|
|
52
|
+
return self._description
|
|
53
|
+
|
|
54
|
+
def predict(self, inp_arr: np.ndarray) -> Union[np.ndarray, None]:
|
|
55
|
+
_verify_input(inp_arr)
|
|
56
|
+
if not self._valid():
|
|
57
|
+
return None
|
|
58
|
+
vel = inp_arr[:, 0]
|
|
59
|
+
eff_pres_in_situ = inp_arr[:, 1]
|
|
60
|
+
eff_pres_depl = inp_arr[:, 2]
|
|
61
|
+
# Return differential velocity to match alternative models
|
|
62
|
+
return (
|
|
63
|
+
vel
|
|
64
|
+
* (1.0 - self._a_factor * np.exp(-eff_pres_depl / self._b_factor))
|
|
65
|
+
/ (1.0 - self._a_factor * np.exp(-eff_pres_in_situ / self._b_factor))
|
|
66
|
+
- vel
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def predict_max(self, inp_arr: np.ndarray) -> Union[np.ndarray, None]:
|
|
70
|
+
_verify_input(inp_arr)
|
|
71
|
+
if not self._valid():
|
|
72
|
+
return None
|
|
73
|
+
vel = inp_arr[:, 0]
|
|
74
|
+
eff_pres_in_situ = inp_arr[:, 1]
|
|
75
|
+
return (
|
|
76
|
+
vel
|
|
77
|
+
* (
|
|
78
|
+
1.0
|
|
79
|
+
- self._a_factor * np.exp(-self._model_max_pressure / self._b_factor)
|
|
80
|
+
)
|
|
81
|
+
/ (1.0 - self._a_factor * np.exp(-eff_pres_in_situ / self.b_factor))
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def predict_abs(self, inp_arr: np.ndarray) -> Union[np.ndarray, None]:
|
|
85
|
+
_verify_input(inp_arr)
|
|
86
|
+
if not self._valid():
|
|
87
|
+
return None
|
|
88
|
+
vel = inp_arr[:, 0]
|
|
89
|
+
eff_pres_in_situ = inp_arr[:, 1]
|
|
90
|
+
eff_pres_depl = inp_arr[:, 2]
|
|
91
|
+
return (
|
|
92
|
+
vel
|
|
93
|
+
* (1.0 - self._a_factor * np.exp(-eff_pres_depl / self._b_factor))
|
|
94
|
+
/ (1.0 - self._a_factor * np.exp(-eff_pres_in_situ / self._b_factor))
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def save(self, file):
|
|
98
|
+
with open(file, "wb") as f_out:
|
|
99
|
+
pickle.dump(self.todict(), f_out)
|
|
100
|
+
|
|
101
|
+
@classmethod
|
|
102
|
+
def load(cls, file):
|
|
103
|
+
with open(file, "rb") as f_in:
|
|
104
|
+
inp_pcl = pickle.load(f_in)
|
|
105
|
+
return cls(
|
|
106
|
+
a_factor=inp_pcl["a_factor"],
|
|
107
|
+
b_factor=inp_pcl["b_factor"],
|
|
108
|
+
model_max_pressure=inp_pcl["model_max_pressure"],
|
|
109
|
+
description=inp_pcl["description"],
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
def _valid(self):
|
|
113
|
+
if self.a_factor is None:
|
|
114
|
+
raise ValueError('object field "a_factor" is not set')
|
|
115
|
+
if self.b_factor is None:
|
|
116
|
+
raise ValueError('object field "b_factor" is not set')
|
|
117
|
+
if self.max_pressure is None:
|
|
118
|
+
raise ValueError('object field "max_pressure" is not set')
|
|
119
|
+
return True
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from .exponential_model import CarbonateExponentialPressure
|
|
2
|
+
from .sigmoidal_model import CarbonateSigmoidalPressure
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def import_model(model_file_name):
|
|
6
|
+
"""
|
|
7
|
+
Utility to import a pickled dict containing information needed to run a classification or regression based on
|
|
8
|
+
a calibrated model.
|
|
9
|
+
|
|
10
|
+
Parameters
|
|
11
|
+
----------
|
|
12
|
+
model_file_name : str
|
|
13
|
+
Full name including path for model file.
|
|
14
|
+
|
|
15
|
+
Returns
|
|
16
|
+
-------
|
|
17
|
+
models, scaler, ohe, label_var, label_units, feature_var, cat_var : Any
|
|
18
|
+
models: various regression or classification models from e.g. sklearn or tensorflow keras, scaler: preprocessing
|
|
19
|
+
Robust Scaler, label_var: name(s) of label variable(s), label_unit: unit(s) of label variable(s), cat_var:
|
|
20
|
+
categorical variables that should be encoded with one-hot-encoder.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from pickle import load
|
|
24
|
+
|
|
25
|
+
with open(model_file_name, "rb") as fin:
|
|
26
|
+
# 11.04.2021 HFLE: There is an issue that is not connected to the local function, in that a warning is issued
|
|
27
|
+
# when the model is loaded, claiming that it is of an older version. This is debugged in detail, and the model
|
|
28
|
+
# IS of the correct version, so the error arise elsewhere. To avoid confusion, the warning is suppressed here
|
|
29
|
+
import warnings
|
|
30
|
+
|
|
31
|
+
with warnings.catch_warnings():
|
|
32
|
+
warnings.simplefilter("ignore", category=UserWarning)
|
|
33
|
+
mod_dict = load(fin)
|
|
34
|
+
|
|
35
|
+
if mod_dict["model_type"] == "Sigmoid":
|
|
36
|
+
models = CarbonateSigmoidalPressure.load(mod_dict["nn_mod"])
|
|
37
|
+
elif mod_dict["model_type"] == "Exponential":
|
|
38
|
+
models = CarbonateExponentialPressure.load(mod_dict["nn_mod"])
|
|
39
|
+
else:
|
|
40
|
+
raise ValueError("unknown model type {}".format(mod_dict["model_type"]))
|
|
41
|
+
|
|
42
|
+
ohe = None
|
|
43
|
+
cat_var = []
|
|
44
|
+
try:
|
|
45
|
+
if mod_dict["ohe"]:
|
|
46
|
+
with open(mod_dict["ohe"], "rb") as f:
|
|
47
|
+
ohe_dict = load(f)
|
|
48
|
+
ohe = ohe_dict["ohe"]
|
|
49
|
+
cat_var = ohe_dict["cat_var"]
|
|
50
|
+
except (FileExistsError, FileNotFoundError):
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
models,
|
|
55
|
+
mod_dict["scaler"],
|
|
56
|
+
ohe,
|
|
57
|
+
mod_dict["label_var"],
|
|
58
|
+
mod_dict["label_units"],
|
|
59
|
+
mod_dict["feature_var"],
|
|
60
|
+
cat_var,
|
|
61
|
+
)
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from re import match
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from .dummy_vars import generate_dummy_vars
|
|
8
|
+
from .import_ml_models import import_model
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _read_models(*model_files, model_dir=None):
|
|
12
|
+
# Find the directory of the model files, change working directory, return to original directory at end of function
|
|
13
|
+
orig_dir = os.getcwd()
|
|
14
|
+
if model_dir is None:
|
|
15
|
+
model_dir, _ = os.path.split(model_files[0])
|
|
16
|
+
os.chdir(model_dir)
|
|
17
|
+
# Allocate lists and read model
|
|
18
|
+
reg_model, scaler, ohe, label_var, label_unit, feat_var, cat_var = (
|
|
19
|
+
[] for _ in range(7)
|
|
20
|
+
)
|
|
21
|
+
answer_lists = [reg_model, scaler, ohe, label_var, label_unit, feat_var, cat_var]
|
|
22
|
+
for mod_name in model_files:
|
|
23
|
+
answer = import_model(mod_name)
|
|
24
|
+
for ans, lst in zip(answer, answer_lists):
|
|
25
|
+
lst.append(ans)
|
|
26
|
+
|
|
27
|
+
# Need to modify names
|
|
28
|
+
col_names, col_units = ([] for _ in range(2))
|
|
29
|
+
for i in range(len(label_var)):
|
|
30
|
+
col_names.append(label_var[i] + "_" + model_files[i].replace(label_var[i], ""))
|
|
31
|
+
col_units.append(label_unit[i])
|
|
32
|
+
|
|
33
|
+
os.chdir(orig_dir)
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
reg_model,
|
|
37
|
+
scaler,
|
|
38
|
+
ohe,
|
|
39
|
+
label_var,
|
|
40
|
+
label_unit,
|
|
41
|
+
feat_var,
|
|
42
|
+
cat_var,
|
|
43
|
+
col_names,
|
|
44
|
+
col_units,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _perform_regression(
|
|
49
|
+
inp_frame, col_names, feat_var, cat_var, ohe, scaler, reg_model
|
|
50
|
+
):
|
|
51
|
+
depth = inp_frame.index.to_numpy()
|
|
52
|
+
|
|
53
|
+
res_frame = pd.DataFrame(index=depth, columns=col_names)
|
|
54
|
+
|
|
55
|
+
for j, model_name in enumerate(col_names):
|
|
56
|
+
tmp_frame = inp_frame.copy()
|
|
57
|
+
|
|
58
|
+
# Limit to columns used in estimation before dropping NaNs
|
|
59
|
+
num_var = [i for i in feat_var[j] if not bool(match(r"x\d", i))]
|
|
60
|
+
no_num_var = len(num_var)
|
|
61
|
+
if cat_var[j]:
|
|
62
|
+
num_var.append(cat_var[j])
|
|
63
|
+
tmp_frame = tmp_frame[num_var]
|
|
64
|
+
idx_na_n = tmp_frame.isna().any(axis=1)
|
|
65
|
+
|
|
66
|
+
if cat_var[j]:
|
|
67
|
+
dum_features, _, dum_var_names = generate_dummy_vars(
|
|
68
|
+
tmp_frame.loc[~idx_na_n], cat_var[j], ohe=ohe[j]
|
|
69
|
+
)
|
|
70
|
+
# Add dummy features to data frame
|
|
71
|
+
kept_dum_var = []
|
|
72
|
+
for i, name in enumerate(dum_var_names):
|
|
73
|
+
if name in feat_var[j]:
|
|
74
|
+
tmp_frame.loc[~idx_na_n, name] = dum_features[:, i]
|
|
75
|
+
kept_dum_var.append(name)
|
|
76
|
+
tmp_frame.drop(columns=[cat_var[j]], inplace=True)
|
|
77
|
+
|
|
78
|
+
# Need to assure that we have the correct sequence of features
|
|
79
|
+
tmp_frame = tmp_frame.reindex(columns=feat_var[j])
|
|
80
|
+
|
|
81
|
+
new_features = np.zeros((np.sum(~idx_na_n), tmp_frame.shape[1]))
|
|
82
|
+
# Make scaling optional
|
|
83
|
+
if scaler[j] is not None:
|
|
84
|
+
new_features[:, :no_num_var] = scaler[j].transform(
|
|
85
|
+
tmp_frame.to_numpy()[~idx_na_n, :no_num_var]
|
|
86
|
+
)
|
|
87
|
+
else:
|
|
88
|
+
new_features[:, :no_num_var] = tmp_frame.to_numpy()[
|
|
89
|
+
~idx_na_n, :no_num_var
|
|
90
|
+
]
|
|
91
|
+
new_features[:, no_num_var:] = tmp_frame.loc[
|
|
92
|
+
~idx_na_n, kept_dum_var
|
|
93
|
+
].to_numpy()
|
|
94
|
+
else:
|
|
95
|
+
# Much simpler if there are no dummy variables
|
|
96
|
+
# Need to assure that we have the correct sequence of features
|
|
97
|
+
tmp_frame = tmp_frame.reindex(columns=feat_var[j])
|
|
98
|
+
# Make scaling optional
|
|
99
|
+
if scaler[j] is not None:
|
|
100
|
+
new_features = scaler[j].transform(tmp_frame.to_numpy()[~idx_na_n, :])
|
|
101
|
+
else:
|
|
102
|
+
new_features = tmp_frame.to_numpy()[~idx_na_n, :]
|
|
103
|
+
|
|
104
|
+
new_var = np.ones(depth.shape[0]) * np.nan
|
|
105
|
+
new_var[~idx_na_n] = reg_model[j].predict(new_features).flatten()
|
|
106
|
+
res_frame[col_names[j]] = new_var
|
|
107
|
+
|
|
108
|
+
return res_frame
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def run_regression(inp_df, vp_model_file_name, vs_model_file_name, model_dir=None):
|
|
112
|
+
"""
|
|
113
|
+
Estimate Vp and Vs by neural network regression with multiple inputs.
|
|
114
|
+
|
|
115
|
+
Parameters
|
|
116
|
+
----------
|
|
117
|
+
inp_df : pd.DataFrame
|
|
118
|
+
Input logs required for the regression.
|
|
119
|
+
vp_model_file_name : str
|
|
120
|
+
Full file name for vp model.
|
|
121
|
+
vs_model_file_name : str
|
|
122
|
+
Full file name for vs model.
|
|
123
|
+
model_dir : str
|
|
124
|
+
Directory.
|
|
125
|
+
|
|
126
|
+
Returns
|
|
127
|
+
-------
|
|
128
|
+
vp, vs : pd.DataFrame
|
|
129
|
+
Estimated vp and vs as series in Pandas DataFrame.
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
(
|
|
133
|
+
regression_model,
|
|
134
|
+
scaler_obj,
|
|
135
|
+
ohe_obj,
|
|
136
|
+
label_var,
|
|
137
|
+
label_var_unit,
|
|
138
|
+
feature_var,
|
|
139
|
+
category_var,
|
|
140
|
+
column_names,
|
|
141
|
+
column_units,
|
|
142
|
+
) = _read_models(vp_model_file_name, vs_model_file_name, model_dir=model_dir)
|
|
143
|
+
return _perform_regression(
|
|
144
|
+
inp_df,
|
|
145
|
+
column_names,
|
|
146
|
+
feature_var,
|
|
147
|
+
category_var,
|
|
148
|
+
ohe_obj,
|
|
149
|
+
scaler_obj,
|
|
150
|
+
regression_model,
|
|
151
|
+
)
|