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.
Files changed (37) hide show
  1. fmu/__init__.py +2 -0
  2. fmu/pem/__init__.py +19 -0
  3. fmu/pem/__main__.py +53 -0
  4. fmu/pem/forward_models/__init__.py +7 -0
  5. fmu/pem/forward_models/pem_model.py +72 -0
  6. fmu/pem/hook_implementations/__init__.py +0 -0
  7. fmu/pem/hook_implementations/jobs.py +19 -0
  8. fmu/pem/pem_functions/__init__.py +17 -0
  9. fmu/pem/pem_functions/density.py +55 -0
  10. fmu/pem/pem_functions/effective_pressure.py +168 -0
  11. fmu/pem/pem_functions/estimate_saturated_rock.py +90 -0
  12. fmu/pem/pem_functions/fluid_properties.py +281 -0
  13. fmu/pem/pem_functions/mineral_properties.py +230 -0
  14. fmu/pem/pem_functions/regression_models.py +261 -0
  15. fmu/pem/pem_functions/run_friable_model.py +119 -0
  16. fmu/pem/pem_functions/run_patchy_cement_model.py +120 -0
  17. fmu/pem/pem_functions/run_t_matrix_and_pressure.py +186 -0
  18. fmu/pem/pem_utilities/__init__.py +66 -0
  19. fmu/pem/pem_utilities/cumsum_properties.py +104 -0
  20. fmu/pem/pem_utilities/delta_cumsum_time.py +104 -0
  21. fmu/pem/pem_utilities/enum_defs.py +54 -0
  22. fmu/pem/pem_utilities/export_routines.py +272 -0
  23. fmu/pem/pem_utilities/import_config.py +93 -0
  24. fmu/pem/pem_utilities/import_routines.py +161 -0
  25. fmu/pem/pem_utilities/pem_class_definitions.py +113 -0
  26. fmu/pem/pem_utilities/pem_config_validation.py +505 -0
  27. fmu/pem/pem_utilities/rpm_models.py +177 -0
  28. fmu/pem/pem_utilities/update_grid.py +54 -0
  29. fmu/pem/pem_utilities/utils.py +262 -0
  30. fmu/pem/run_pem.py +98 -0
  31. fmu/pem/version.py +21 -0
  32. fmu_pem-0.0.1.dist-info/METADATA +768 -0
  33. fmu_pem-0.0.1.dist-info/RECORD +37 -0
  34. fmu_pem-0.0.1.dist-info/WHEEL +5 -0
  35. fmu_pem-0.0.1.dist-info/entry_points.txt +5 -0
  36. fmu_pem-0.0.1.dist-info/licenses/LICENSE +674 -0
  37. 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)