fmu-pem 0.0.1__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.
- fmu/__init__.py +2 -0
- fmu/pem/__init__.py +19 -0
- fmu/pem/__main__.py +53 -0
- fmu/pem/forward_models/__init__.py +7 -0
- fmu/pem/forward_models/pem_model.py +72 -0
- fmu/pem/hook_implementations/__init__.py +0 -0
- fmu/pem/hook_implementations/jobs.py +19 -0
- fmu/pem/pem_functions/__init__.py +17 -0
- fmu/pem/pem_functions/density.py +55 -0
- fmu/pem/pem_functions/effective_pressure.py +168 -0
- fmu/pem/pem_functions/estimate_saturated_rock.py +90 -0
- fmu/pem/pem_functions/fluid_properties.py +281 -0
- fmu/pem/pem_functions/mineral_properties.py +230 -0
- fmu/pem/pem_functions/regression_models.py +261 -0
- fmu/pem/pem_functions/run_friable_model.py +119 -0
- fmu/pem/pem_functions/run_patchy_cement_model.py +120 -0
- fmu/pem/pem_functions/run_t_matrix_and_pressure.py +186 -0
- fmu/pem/pem_utilities/__init__.py +66 -0
- fmu/pem/pem_utilities/cumsum_properties.py +104 -0
- fmu/pem/pem_utilities/delta_cumsum_time.py +104 -0
- fmu/pem/pem_utilities/enum_defs.py +54 -0
- fmu/pem/pem_utilities/export_routines.py +272 -0
- fmu/pem/pem_utilities/import_config.py +93 -0
- fmu/pem/pem_utilities/import_routines.py +161 -0
- fmu/pem/pem_utilities/pem_class_definitions.py +113 -0
- fmu/pem/pem_utilities/pem_config_validation.py +505 -0
- fmu/pem/pem_utilities/rpm_models.py +177 -0
- fmu/pem/pem_utilities/update_grid.py +54 -0
- fmu/pem/pem_utilities/utils.py +262 -0
- fmu/pem/run_pem.py +98 -0
- fmu/pem/version.py +21 -0
- fmu_pem-0.0.1.dist-info/METADATA +768 -0
- fmu_pem-0.0.1.dist-info/RECORD +37 -0
- fmu_pem-0.0.1.dist-info/WHEEL +5 -0
- fmu_pem-0.0.1.dist-info/entry_points.txt +5 -0
- fmu_pem-0.0.1.dist-info/licenses/LICENSE +674 -0
- fmu_pem-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from .cumsum_properties import calculate_diff_properties
|
|
2
|
+
from .delta_cumsum_time import (
|
|
3
|
+
estimate_delta_time,
|
|
4
|
+
estimate_sum_delta_time,
|
|
5
|
+
)
|
|
6
|
+
from .export_routines import save_results
|
|
7
|
+
from .import_config import get_global_params_and_dates, read_pem_config
|
|
8
|
+
from .import_routines import (
|
|
9
|
+
import_fractions,
|
|
10
|
+
read_geogrid,
|
|
11
|
+
read_ntg_grid,
|
|
12
|
+
read_sim_grid_props,
|
|
13
|
+
)
|
|
14
|
+
from .pem_class_definitions import (
|
|
15
|
+
DryRockProperties,
|
|
16
|
+
EffectiveFluidProperties,
|
|
17
|
+
MatrixProperties,
|
|
18
|
+
PressureProperties,
|
|
19
|
+
SaturatedRockProperties,
|
|
20
|
+
SimInitProperties,
|
|
21
|
+
SimRstProperties,
|
|
22
|
+
)
|
|
23
|
+
from .pem_config_validation import Fluids, PemConfig, possible_date_string
|
|
24
|
+
from .update_grid import update_inactive_grid_cells
|
|
25
|
+
from .utils import (
|
|
26
|
+
estimate_cement,
|
|
27
|
+
filter_and_one_dim,
|
|
28
|
+
get_shale_fraction,
|
|
29
|
+
ntg_to_shale_fraction,
|
|
30
|
+
restore_dir,
|
|
31
|
+
reverse_filter_and_restore,
|
|
32
|
+
to_masked_array,
|
|
33
|
+
update_dict_list,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
"PemConfig",
|
|
38
|
+
"Fluids",
|
|
39
|
+
"calculate_diff_properties",
|
|
40
|
+
"estimate_cement",
|
|
41
|
+
"estimate_sum_delta_time",
|
|
42
|
+
"estimate_delta_time",
|
|
43
|
+
"filter_and_one_dim",
|
|
44
|
+
"get_global_params_and_dates",
|
|
45
|
+
"get_shale_fraction",
|
|
46
|
+
"import_fractions",
|
|
47
|
+
"ntg_to_shale_fraction",
|
|
48
|
+
"possible_date_string",
|
|
49
|
+
"read_geogrid",
|
|
50
|
+
"read_ntg_grid",
|
|
51
|
+
"read_pem_config",
|
|
52
|
+
"read_sim_grid_props",
|
|
53
|
+
"restore_dir",
|
|
54
|
+
"reverse_filter_and_restore",
|
|
55
|
+
"save_results",
|
|
56
|
+
"to_masked_array",
|
|
57
|
+
"update_dict_list",
|
|
58
|
+
"update_inactive_grid_cells",
|
|
59
|
+
"DryRockProperties",
|
|
60
|
+
"EffectiveFluidProperties",
|
|
61
|
+
"MatrixProperties",
|
|
62
|
+
"PressureProperties",
|
|
63
|
+
"SaturatedRockProperties",
|
|
64
|
+
"SimInitProperties",
|
|
65
|
+
"SimRstProperties",
|
|
66
|
+
]
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
from dataclasses import asdict
|
|
2
|
+
from typing import Tuple
|
|
3
|
+
|
|
4
|
+
from .pem_config_validation import PemConfig
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def calculate_diff_properties(props: list, conf_params: PemConfig) -> Tuple[list, list]:
|
|
8
|
+
"""
|
|
9
|
+
Function to calculate difference attributes between grid properties
|
|
10
|
+
|
|
11
|
+
:param props: grid properties
|
|
12
|
+
:param conf_params: configuration parameters
|
|
13
|
+
:return: difference of time steps according to config file
|
|
14
|
+
"""
|
|
15
|
+
_verify_diff_inputs(props, conf_params)
|
|
16
|
+
props = _filter_diff_inputs(props, conf_params)
|
|
17
|
+
|
|
18
|
+
def diff(x, y):
|
|
19
|
+
return x - y
|
|
20
|
+
|
|
21
|
+
def diffpercent(x, y):
|
|
22
|
+
return 100.0 * (x - y) / y
|
|
23
|
+
|
|
24
|
+
def ratio(x, y):
|
|
25
|
+
return x / y
|
|
26
|
+
|
|
27
|
+
lookup = dict(
|
|
28
|
+
zip(
|
|
29
|
+
conf_params.global_params.seis_dates, # type: ignore
|
|
30
|
+
range(len(conf_params.global_params.seis_dates)), # type: ignore
|
|
31
|
+
)
|
|
32
|
+
)
|
|
33
|
+
date_str = []
|
|
34
|
+
diff_prop = []
|
|
35
|
+
# Need to iterate over the lists in props, which contain the properties
|
|
36
|
+
# for each date
|
|
37
|
+
for monitor, base in conf_params.global_params.diff_dates: # type: ignore
|
|
38
|
+
tmp_dict = {}
|
|
39
|
+
for k, v_base in props[lookup[base]].items():
|
|
40
|
+
if k.upper() in conf_params.diff_calculation:
|
|
41
|
+
operations = conf_params.diff_calculation[k.upper()]
|
|
42
|
+
v_monitor = props[lookup[monitor]][k]
|
|
43
|
+
for op in operations:
|
|
44
|
+
if op in locals():
|
|
45
|
+
tmp_dict[k + op] = locals()[op](v_monitor, v_base)
|
|
46
|
+
else:
|
|
47
|
+
raise ValueError(
|
|
48
|
+
f"{__file__}: unknown difference operation: {op}, should "
|
|
49
|
+
f'be one of "diff","diff_percent" or "ratio"'
|
|
50
|
+
)
|
|
51
|
+
if tmp_dict:
|
|
52
|
+
diff_prop.append(tmp_dict)
|
|
53
|
+
date_str.append(monitor + "_" + base)
|
|
54
|
+
return diff_prop, date_str
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _verify_diff_inputs(prop_set, conf):
|
|
58
|
+
if not isinstance(prop_set, list):
|
|
59
|
+
raise ValueError(
|
|
60
|
+
f"{__file__}: input grid properties must be contained in a list "
|
|
61
|
+
f"of lists, with one set of properties for each simulator model "
|
|
62
|
+
f"date in the inner lists"
|
|
63
|
+
)
|
|
64
|
+
for prop_list in prop_set:
|
|
65
|
+
if len(prop_list) != len(conf.global_params.seis_dates):
|
|
66
|
+
raise ValueError(
|
|
67
|
+
f"{__file__}: mismatch between property sets and "
|
|
68
|
+
f"simulation model dates: "
|
|
69
|
+
f"{len(prop_list)} vs. {len(conf.global_params.seis_dates)}"
|
|
70
|
+
)
|
|
71
|
+
if not {a for ll in conf.global_params.diff_dates for a in ll}.issubset(
|
|
72
|
+
set(conf.global_params.seis_dates)
|
|
73
|
+
):
|
|
74
|
+
raise ValueError(
|
|
75
|
+
f"{__file__}: trying to take difference between dates not saved from "
|
|
76
|
+
f"simulation model"
|
|
77
|
+
)
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _filter_diff_inputs(prop_list_list, conf):
|
|
82
|
+
# Filter out the properties that are not in the diff_calculation list.
|
|
83
|
+
# Keep the time-step order in the list
|
|
84
|
+
return_list = [{} for _ in range(len(prop_list_list[0]))]
|
|
85
|
+
for prop_list in prop_list_list:
|
|
86
|
+
for i, prop_set in enumerate(prop_list):
|
|
87
|
+
tmp_dict = {
|
|
88
|
+
k: v
|
|
89
|
+
for k, v in asdict(prop_set).items()
|
|
90
|
+
if k.upper() in conf.diff_calculation
|
|
91
|
+
}
|
|
92
|
+
if tmp_dict:
|
|
93
|
+
return_list[i].update(tmp_dict)
|
|
94
|
+
if conf.global_params is None:
|
|
95
|
+
raise ValueError(
|
|
96
|
+
f"{__file__}: unable to calculate cumsum values, global "
|
|
97
|
+
f"settings are missing"
|
|
98
|
+
)
|
|
99
|
+
if conf.global_params.seis_dates is None:
|
|
100
|
+
raise ValueError(
|
|
101
|
+
f"{__file__}: unable to calculate cumsum values, global "
|
|
102
|
+
f"settings seismic dates are missing"
|
|
103
|
+
)
|
|
104
|
+
return return_list
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
from typing import Dict, List, Union
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from .pem_class_definitions import (
|
|
6
|
+
SaturatedRockProperties,
|
|
7
|
+
SimInitProperties,
|
|
8
|
+
TwoWayTime,
|
|
9
|
+
)
|
|
10
|
+
from .pem_config_validation import PemConfig
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def estimate_delta_time(
|
|
14
|
+
delta_z: np.ma.MaskedArray, vp: np.ma.MaskedArray, vs: np.ma.MaskedArray
|
|
15
|
+
) -> Dict[str, np.ma.MaskedArray]:
|
|
16
|
+
"""Estimate seismic TWT parameters - PP, PS, SS modes
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
delta_z: delta depth cube [m]
|
|
20
|
+
vp: compressional velocity [m/s]
|
|
21
|
+
vs: shear velocity [m/s]
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
delta PP time, delta SS time, delta PS time, all in [ms]
|
|
25
|
+
"""
|
|
26
|
+
_verify_delta_t([delta_z, vp, vs])
|
|
27
|
+
dt_pp = 2000.0 * delta_z / vp
|
|
28
|
+
dt_ss = 2000.0 * delta_z / vs
|
|
29
|
+
return {"twtpp": dt_pp, "twtss": dt_ss, "twtps": (dt_pp + dt_ss) / 2.0}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _verify_delta_t(arrays: List[np.ma.MaskedArray]) -> None:
|
|
33
|
+
for arr in arrays:
|
|
34
|
+
if not isinstance(arr, np.ma.MaskedArray):
|
|
35
|
+
raise TypeError(
|
|
36
|
+
f"inputs to estimate_delta_time must be a numpy masked "
|
|
37
|
+
f"arrays, is {type(arr)}."
|
|
38
|
+
)
|
|
39
|
+
dim_arr = np.array([arr.shape for arr in arrays])
|
|
40
|
+
if not (dim_arr == dim_arr[0]).all():
|
|
41
|
+
raise ValueError(
|
|
42
|
+
f"{__file__}: the shape of the arrays to estimate_delta_t do not match"
|
|
43
|
+
)
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def calculate_time_cumsum(
|
|
48
|
+
props: Union[list, dict], conf_params: PemConfig
|
|
49
|
+
) -> list[Dict[str, np.ma.MaskedArray]]:
|
|
50
|
+
"""
|
|
51
|
+
Function to calculate cumulative sum of time difference properties
|
|
52
|
+
|
|
53
|
+
:param props: grid properties
|
|
54
|
+
:param conf_params: configuration parameters
|
|
55
|
+
:return: cumulative sum of properties along the z-axis
|
|
56
|
+
"""
|
|
57
|
+
props = _verify_cumsum_inputs(props)
|
|
58
|
+
sum_prop = []
|
|
59
|
+
for prop_set in props:
|
|
60
|
+
prop_set_dict = {}
|
|
61
|
+
for k, v in prop_set.items():
|
|
62
|
+
prop_set_dict[k] = np.cumsum(v, axis=2)
|
|
63
|
+
sum_prop.append(prop_set_dict)
|
|
64
|
+
|
|
65
|
+
return sum_prop
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _verify_cumsum_inputs(input_set):
|
|
69
|
+
if not isinstance(input_set, list):
|
|
70
|
+
input_set = [
|
|
71
|
+
input_set,
|
|
72
|
+
]
|
|
73
|
+
for this_input in input_set:
|
|
74
|
+
if not isinstance(this_input, dict):
|
|
75
|
+
raise ValueError(
|
|
76
|
+
f"{__file__}: unexpected input type, should be dict, is "
|
|
77
|
+
f"{type(this_input)}"
|
|
78
|
+
)
|
|
79
|
+
return input_set
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def estimate_sum_delta_time(
|
|
83
|
+
constant_props: SimInitProperties,
|
|
84
|
+
sat_rock_props: List[SaturatedRockProperties],
|
|
85
|
+
config: PemConfig,
|
|
86
|
+
) -> List[TwoWayTime]:
|
|
87
|
+
"""Calculate TWT (two-way-time) for seismic signal for each restart date
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
constant_props: constant properties, here the delta Z property of the eclipse
|
|
91
|
+
grid is used sat_rock_props: effective properties for the saturated rock per
|
|
92
|
+
restart date
|
|
93
|
+
sat_rock_props: effective properties for the saturated rock per restart date
|
|
94
|
+
config: configuration parameters
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
list of delta time and cumulative time per restart date
|
|
98
|
+
"""
|
|
99
|
+
delta_time = [
|
|
100
|
+
estimate_delta_time(constant_props.delta_z, sat_rock.vp, sat_rock.vs)
|
|
101
|
+
for sat_rock in sat_rock_props
|
|
102
|
+
]
|
|
103
|
+
cum_time_list = calculate_time_cumsum(delta_time, config)
|
|
104
|
+
return [TwoWayTime(**time_set) for time_set in cum_time_list]
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Define enumerated strings
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from enum import Enum
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class OverburdenPressure(str, Enum):
|
|
9
|
+
CONSTANT = "constant"
|
|
10
|
+
TREND = "trend"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Lithology(str, Enum):
|
|
14
|
+
SILICICLASTICS = "siliciclastics"
|
|
15
|
+
CARBONATE = "carbonate"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MineralMixModel(str, Enum):
|
|
19
|
+
VOIGT_REUSS_HILL = "voigt-reuss-hill"
|
|
20
|
+
HASHIN_SHTRIKMAN = "hashin-shtrikman-average"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class FluidMixModel(str, Enum):
|
|
24
|
+
WOOD = "wood"
|
|
25
|
+
BRIE = "brie"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class SaveTypes(str, Enum):
|
|
29
|
+
SAVE_TO_RMS = "save_results_to_rms"
|
|
30
|
+
SAVE_TO_DISK = "save_results_to_disk"
|
|
31
|
+
SAVE_INTERMEDIATE_RESULTS = "save_intermediate_results"
|
|
32
|
+
SAVE_RESULTS_TO_CSV = "save_results_to_csv"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class CO2Models(str, Enum):
|
|
36
|
+
FLAG = "flag"
|
|
37
|
+
SPAN_WAGNER = "span_wagner"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class RegressionModelLithologies(str, Enum):
|
|
41
|
+
SANDSTONE = "sandstone"
|
|
42
|
+
SHALE = "shale"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class RPMType(str, Enum):
|
|
46
|
+
PATCHY_CEMENT = "patchy_cement"
|
|
47
|
+
FRIABLE = "friable"
|
|
48
|
+
T_MATRIX = "t_matrix"
|
|
49
|
+
REGRESSION = "regression"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class VolumeFractions(str, Enum):
|
|
53
|
+
NTG_SIM = "ntg_sim"
|
|
54
|
+
VOL_FRAC = "fraction_files"
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
from dataclasses import asdict
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import List, Union
|
|
5
|
+
|
|
6
|
+
import xtgeo
|
|
7
|
+
|
|
8
|
+
from .pem_class_definitions import (
|
|
9
|
+
EffectiveFluidProperties,
|
|
10
|
+
MatrixProperties,
|
|
11
|
+
PressureProperties,
|
|
12
|
+
SaturatedRockProperties,
|
|
13
|
+
)
|
|
14
|
+
from .pem_config_validation import FromGlobal, PemConfig
|
|
15
|
+
from .utils import _verify_export_inputs, restore_dir
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def save_results(
|
|
19
|
+
start_dir: Path,
|
|
20
|
+
run_from_rms_flag: bool,
|
|
21
|
+
config_settings: PemConfig,
|
|
22
|
+
rms_project: object,
|
|
23
|
+
sim_grid: xtgeo.grid3d.Grid,
|
|
24
|
+
eff_pres_props: list[PressureProperties],
|
|
25
|
+
sat_rock_props: list[SaturatedRockProperties],
|
|
26
|
+
difference_props: List[dict],
|
|
27
|
+
difference_date_strs: List[str],
|
|
28
|
+
matrix_props: MatrixProperties,
|
|
29
|
+
fluid_props: list[EffectiveFluidProperties],
|
|
30
|
+
) -> None:
|
|
31
|
+
"""Saves all intermediate and final results according to the settings in the PEM
|
|
32
|
+
and global config files
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
start_dir: initial directory setting
|
|
36
|
+
run_from_rms_flag: call to PEM from RMS
|
|
37
|
+
config_settings: PEM and global settings
|
|
38
|
+
rms_project: RMS project
|
|
39
|
+
sim_grid: grid definition
|
|
40
|
+
eff_pres_props: effective, overburden and formation pressure per time step
|
|
41
|
+
sat_rock_props: elastic properties of saturated rock
|
|
42
|
+
difference_props: differences in elastic properties between selected restart
|
|
43
|
+
dates
|
|
44
|
+
difference_date_strs: dates for difference calculation
|
|
45
|
+
matrix_props: intermediate results - mineral properties
|
|
46
|
+
fluid_props: intermediate results - fluid properties per time step
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
None, warning or KeyError
|
|
50
|
+
"""
|
|
51
|
+
# Saving results:
|
|
52
|
+
# 1. Mandatory part: Save Vp, Vs, Density to disk for seismic forward modelling.
|
|
53
|
+
# Use FMU standard term "DENS" for density
|
|
54
|
+
|
|
55
|
+
# mypy needs an assert in the same function as the usage - it did not pick up a
|
|
56
|
+
# verification in a separate function without it, mypy reports errors, assuming
|
|
57
|
+
# global_params is None
|
|
58
|
+
|
|
59
|
+
assert isinstance(config_settings.global_params, FromGlobal)
|
|
60
|
+
|
|
61
|
+
pem_output_path = start_dir.joinpath(
|
|
62
|
+
config_settings.paths.rel_path_mandatory_output
|
|
63
|
+
)
|
|
64
|
+
output_path = start_dir.joinpath(config_settings.paths.rel_path_output)
|
|
65
|
+
output_set = [
|
|
66
|
+
{
|
|
67
|
+
k: v
|
|
68
|
+
for (k, v) in asdict(sat_prop).items() # type: ignore
|
|
69
|
+
if k.upper() in ["VP", "VS", "DENS"]
|
|
70
|
+
}
|
|
71
|
+
for sat_prop in sat_rock_props
|
|
72
|
+
]
|
|
73
|
+
export_results_disk(
|
|
74
|
+
output_set,
|
|
75
|
+
sim_grid,
|
|
76
|
+
config_settings.global_params.grid_model,
|
|
77
|
+
pem_output_path,
|
|
78
|
+
time_steps=config_settings.global_params.seis_dates,
|
|
79
|
+
export_format="grdecl",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# 2. Save results to rms and/or disk according to config file
|
|
83
|
+
|
|
84
|
+
# create list of dict from list of pressure and saturated rock objects
|
|
85
|
+
eff_pres_dict_list = [asdict(obj) for obj in eff_pres_props] # type: ignore # NB: this is a pycharm bug to be removed
|
|
86
|
+
sat_prop_dict_list = [asdict(obj) for obj in sat_rock_props] # type: ignore
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
if config_settings.results.save_results_to_rms and run_from_rms_flag:
|
|
90
|
+
grid_model = config_settings.global_params.grid_model
|
|
91
|
+
# Time dependent absolute properties
|
|
92
|
+
for props in [eff_pres_dict_list, sat_prop_dict_list]:
|
|
93
|
+
prop_dict = list(props)
|
|
94
|
+
export_results_roxar(
|
|
95
|
+
rms_project,
|
|
96
|
+
prop_dict,
|
|
97
|
+
sim_grid,
|
|
98
|
+
grid_model,
|
|
99
|
+
time_steps=config_settings.global_params.seis_dates,
|
|
100
|
+
)
|
|
101
|
+
# Difference properties
|
|
102
|
+
export_results_roxar(
|
|
103
|
+
rms_project,
|
|
104
|
+
difference_props,
|
|
105
|
+
sim_grid,
|
|
106
|
+
grid_model,
|
|
107
|
+
time_steps=difference_date_strs,
|
|
108
|
+
)
|
|
109
|
+
except KeyError: # warn user that results are not saved
|
|
110
|
+
warnings.warn(
|
|
111
|
+
f"{__file__}: no parameter for saving results to rms is found in the "
|
|
112
|
+
f"config file"
|
|
113
|
+
)
|
|
114
|
+
try:
|
|
115
|
+
if config_settings.results.save_results_to_disk:
|
|
116
|
+
for props in [eff_pres_dict_list, sat_prop_dict_list]:
|
|
117
|
+
prop_dict = list(props)
|
|
118
|
+
export_results_disk(
|
|
119
|
+
prop_dict,
|
|
120
|
+
sim_grid,
|
|
121
|
+
sim_grid.name,
|
|
122
|
+
output_path,
|
|
123
|
+
time_steps=config_settings.global_params.seis_dates,
|
|
124
|
+
)
|
|
125
|
+
export_results_disk(
|
|
126
|
+
difference_props,
|
|
127
|
+
sim_grid,
|
|
128
|
+
config_settings.global_params.grid_model,
|
|
129
|
+
output_path,
|
|
130
|
+
time_steps=difference_date_strs,
|
|
131
|
+
)
|
|
132
|
+
except KeyError: # warn user that results are not saved
|
|
133
|
+
warnings.warn(
|
|
134
|
+
f"{__file__}: no parameter for saving results to disk is found in the "
|
|
135
|
+
f"config file"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# 3. Save intermediate results only if specified in the config file
|
|
139
|
+
try:
|
|
140
|
+
if config_settings.results.save_intermediate_results:
|
|
141
|
+
export_results_disk(
|
|
142
|
+
[asdict(fl_props) for fl_props in fluid_props], # type: ignore
|
|
143
|
+
sim_grid,
|
|
144
|
+
config_settings.global_params.grid_model,
|
|
145
|
+
output_path,
|
|
146
|
+
time_steps=config_settings.global_params.seis_dates,
|
|
147
|
+
name_suffix="_FLUID",
|
|
148
|
+
)
|
|
149
|
+
export_results_disk(
|
|
150
|
+
asdict(matrix_props), # type: ignore
|
|
151
|
+
sim_grid,
|
|
152
|
+
config_settings.global_params.grid_model,
|
|
153
|
+
output_path,
|
|
154
|
+
name_suffix="_MINERAL",
|
|
155
|
+
)
|
|
156
|
+
except KeyError:
|
|
157
|
+
# just skip silently if save_intermediate_results is not present in the
|
|
158
|
+
# pem_config
|
|
159
|
+
pass
|
|
160
|
+
return
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def export_results_roxar(
|
|
164
|
+
prj: object,
|
|
165
|
+
result_props: Union[List[dict], dict],
|
|
166
|
+
grid: xtgeo.grid3d.Grid,
|
|
167
|
+
rms_grid_name: str,
|
|
168
|
+
time_steps: Union[List[str], None] = None,
|
|
169
|
+
name_suffix: str = "",
|
|
170
|
+
force_write_grid: bool = False,
|
|
171
|
+
) -> None:
|
|
172
|
+
"""Export results directly to RMS. Properties to be exported can be with time-steps
|
|
173
|
+
or single
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
prj: rms project
|
|
177
|
+
result_props: properties, list of or single dict, with numpy masked array
|
|
178
|
+
values
|
|
179
|
+
grid: 3D grid definition
|
|
180
|
+
rms_grid_name: name of grid within rms project
|
|
181
|
+
time_steps: list of simulation model dates, None if properties is not linked to
|
|
182
|
+
a simulation model date
|
|
183
|
+
name_suffix: extra suffix for variable names
|
|
184
|
+
force_write_grid: lag to overwrite grid model in RMS
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
None
|
|
188
|
+
"""
|
|
189
|
+
result_props, time_steps = _verify_export_inputs(result_props, grid, time_steps)
|
|
190
|
+
if force_write_grid:
|
|
191
|
+
grid.to_roxar(prj, rms_grid_name) # type: ignore
|
|
192
|
+
else:
|
|
193
|
+
_verify_gridmodel(prj, rms_grid_name, grid)
|
|
194
|
+
for props, step in zip(result_props, time_steps): # type: ignore
|
|
195
|
+
if step != "":
|
|
196
|
+
step = "_" + step
|
|
197
|
+
for key, value in props.items():
|
|
198
|
+
grid_property_name = key.upper() + name_suffix + step
|
|
199
|
+
grid_prop = xtgeo.grid3d.GridProperty(
|
|
200
|
+
grid, values=value, name=grid_property_name, date=step
|
|
201
|
+
)
|
|
202
|
+
grid_prop.to_roxar(prj, rms_grid_name, grid_property_name) # type: ignore
|
|
203
|
+
return
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _verify_gridmodel(prj: object, rms_grid_model_name: str, grid: xtgeo.grid3d.Grid):
|
|
207
|
+
if hasattr(prj, "grid_models"):
|
|
208
|
+
for model in prj.grid_models:
|
|
209
|
+
if model.name == rms_grid_model_name:
|
|
210
|
+
return
|
|
211
|
+
grid.to_roxar(prj, rms_grid_model_name) # type: ignore
|
|
212
|
+
else:
|
|
213
|
+
raise AttributeError(
|
|
214
|
+
f'{__file__}: RMS project object does not have "grid_model" attribute '
|
|
215
|
+
f"({print(prj)})s"
|
|
216
|
+
)
|
|
217
|
+
return
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def export_results_disk(
|
|
221
|
+
result_props: Union[List[dict], dict],
|
|
222
|
+
grid: xtgeo.grid3d.Grid,
|
|
223
|
+
grid_name: str,
|
|
224
|
+
results_dir: Path,
|
|
225
|
+
time_steps: Union[List[str], None] = None,
|
|
226
|
+
name_suffix: str = "",
|
|
227
|
+
export_format: str = "roff",
|
|
228
|
+
) -> None:
|
|
229
|
+
"""Disk export of PEM results, all file names in lower case
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
result_props: list of dicts with properties to export
|
|
233
|
+
grid: grid definition
|
|
234
|
+
grid_name: name of grid
|
|
235
|
+
results_dir: output directory
|
|
236
|
+
time_steps: dates for simulation run
|
|
237
|
+
name_suffix: extra string suffix for export names
|
|
238
|
+
export_format: one of "roff" or "grdecl"
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
None
|
|
242
|
+
"""
|
|
243
|
+
result_props, time_steps = _verify_export_inputs(
|
|
244
|
+
result_props, grid, time_steps, export_format
|
|
245
|
+
)
|
|
246
|
+
with restore_dir(results_dir):
|
|
247
|
+
# First write the grid itself to disk
|
|
248
|
+
if export_format == "grdecl":
|
|
249
|
+
grid.to_file(grid_name.lower() + ".grdecl", fformat="grdecl")
|
|
250
|
+
else:
|
|
251
|
+
grid.to_file(grid_name.lower() + ".roff")
|
|
252
|
+
out_file = ""
|
|
253
|
+
for props, step in zip(result_props, time_steps): # type: ignore
|
|
254
|
+
if step != "":
|
|
255
|
+
step = "--" + step
|
|
256
|
+
if export_format == "grdecl":
|
|
257
|
+
out_file = "pem" + step + ".grdecl"
|
|
258
|
+
grid.units = None
|
|
259
|
+
grid.to_file(out_file, fformat="grdecl")
|
|
260
|
+
for key, value in props.items():
|
|
261
|
+
attr_name = (
|
|
262
|
+
grid_name.lower() + "--" + key.lower() + name_suffix.lower() + step
|
|
263
|
+
)
|
|
264
|
+
grid_prop = xtgeo.grid3d.GridProperty(
|
|
265
|
+
grid, values=value, name=attr_name, date=step
|
|
266
|
+
)
|
|
267
|
+
if export_format == "grdecl":
|
|
268
|
+
grid_prop.to_file(
|
|
269
|
+
out_file, fformat="grdecl", append=True, name=key.upper()
|
|
270
|
+
)
|
|
271
|
+
else:
|
|
272
|
+
grid_prop.to_file(attr_name + ".roff") # "roff" is default format
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import yaml
|
|
5
|
+
|
|
6
|
+
from fmu.config.utilities import yaml_load
|
|
7
|
+
|
|
8
|
+
from .pem_config_validation import PemConfig
|
|
9
|
+
from .utils import restore_dir
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_global_params_and_dates(root_dir: Path, conf_path: Path) -> dict:
|
|
13
|
+
"""Read global configuration parameters, simulation model dates and seismic dates
|
|
14
|
+
for difference calculation
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
root_dir: start dir for PEM script run
|
|
18
|
+
conf_path: path to global variables configuration file
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
global parameter configuration dict, list of strings for simulation dates,
|
|
22
|
+
list of tuples with
|
|
23
|
+
strings of dates to calculate difference properties
|
|
24
|
+
"""
|
|
25
|
+
# prediction_mode is set to empty string if HIST else to PRED. Normally set in
|
|
26
|
+
# env variable
|
|
27
|
+
env_flowsim = os.getenv("FLOWSIM_IS_PREDICTION", default=False)
|
|
28
|
+
if env_flowsim:
|
|
29
|
+
conf_file = conf_path.joinpath("global_variables_pred.yml")
|
|
30
|
+
date_str = "SEISMIC_PRED_DATES"
|
|
31
|
+
diff_str = "SEISMIC_PRED_DIFFDATES"
|
|
32
|
+
else:
|
|
33
|
+
conf_file = conf_path.joinpath("global_variables.yml")
|
|
34
|
+
date_str = "SEISMIC_HIST_DATES"
|
|
35
|
+
diff_str = "SEISMIC_HIST_DIFFDATES"
|
|
36
|
+
with restore_dir(root_dir):
|
|
37
|
+
global_config_par = yaml_load(str(conf_file))
|
|
38
|
+
seismic_dates = [
|
|
39
|
+
str(sdate).replace("-", "")
|
|
40
|
+
for sdate in global_config_par["global"]["dates"][date_str]
|
|
41
|
+
]
|
|
42
|
+
diff_dates = [
|
|
43
|
+
[str(sdate).replace("-", "") for sdate in datepairs]
|
|
44
|
+
for datepairs in global_config_par["global"]["dates"][diff_str]
|
|
45
|
+
]
|
|
46
|
+
# Grid model name can be under different top dicts - search for it. If more
|
|
47
|
+
# than one is found, and they are not equal - raise an error
|
|
48
|
+
found_grid_name = False
|
|
49
|
+
for key in global_config_par:
|
|
50
|
+
try:
|
|
51
|
+
if not found_grid_name:
|
|
52
|
+
grid_model_name = global_config_par[key]["ECLGRIDNAME_PEM"]
|
|
53
|
+
found_grid_name = True
|
|
54
|
+
else:
|
|
55
|
+
if not grid_model_name == global_config_par[key]["ECLGRIDNAME_PEM"]:
|
|
56
|
+
raise ValueError(
|
|
57
|
+
f"{__file__}: inconsistent names for "
|
|
58
|
+
f"ECLGRIDNAME_PEM in global config file"
|
|
59
|
+
)
|
|
60
|
+
except KeyError:
|
|
61
|
+
pass
|
|
62
|
+
if not found_grid_name:
|
|
63
|
+
raise ValueError(
|
|
64
|
+
f"{__file__}: no value for ECLGRIDNAME_PEM in global config file"
|
|
65
|
+
)
|
|
66
|
+
return {
|
|
67
|
+
"grid_model": grid_model_name,
|
|
68
|
+
"seis_dates": seismic_dates,
|
|
69
|
+
"diff_dates": diff_dates,
|
|
70
|
+
"global_config": global_config_par,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def read_pem_config(yaml_file: Path) -> PemConfig:
|
|
75
|
+
"""Read PEM specific parameters
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
yaml_file: file name for PEM parameters
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
PemConfig object with PEM parameters
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
def join(loader, node):
|
|
85
|
+
seq = loader.construct_sequence(node)
|
|
86
|
+
return "".join([str(i) for i in seq])
|
|
87
|
+
|
|
88
|
+
# register the tag handler
|
|
89
|
+
yaml.add_constructor("!join", join)
|
|
90
|
+
|
|
91
|
+
with yaml_file.open() as f:
|
|
92
|
+
data = yaml.load(f, Loader=yaml.Loader)
|
|
93
|
+
return PemConfig(**data)
|