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,161 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import List, Tuple
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import xtgeo
|
|
6
|
+
|
|
7
|
+
from .pem_class_definitions import SimInitProperties, SimRstProperties
|
|
8
|
+
from .pem_config_validation import PemConfig
|
|
9
|
+
from .utils import restore_dir
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def read_geogrid(root_dir: Path, config: PemConfig) -> dict:
|
|
13
|
+
"""Not in use? Read porosity from geo-grid
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
root_dir: start dir for PEM script run
|
|
17
|
+
config: PEM specific parameters
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Dict object with porosity
|
|
21
|
+
"""
|
|
22
|
+
with restore_dir(root_dir.joinpath(config.paths.rel_path_geogrid)):
|
|
23
|
+
return {"poro": xtgeo.gridproperty_from_file("geogrid--phit.roff").values}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def read_init_properties(
|
|
27
|
+
property_file: Path, sim_grid: xtgeo.Grid
|
|
28
|
+
) -> SimInitProperties:
|
|
29
|
+
"""Read initial properties from INIT file
|
|
30
|
+
Args:
|
|
31
|
+
property_file: Full path to the .INIT file
|
|
32
|
+
sim_grid: The simulation grid to use for reading properties
|
|
33
|
+
Returns:
|
|
34
|
+
SimInitProperties: The loaded initial grid properties
|
|
35
|
+
"""
|
|
36
|
+
INIT_PROPS = ["PORO", "DEPTH", "NTG"]
|
|
37
|
+
sim_init_props = xtgeo.gridproperties_from_file(
|
|
38
|
+
property_file, fformat="init", names=INIT_PROPS, grid=sim_grid
|
|
39
|
+
)
|
|
40
|
+
props_dict = {
|
|
41
|
+
sim_init_props[name].name.lower(): sim_init_props[name].values
|
|
42
|
+
for name in INIT_PROPS
|
|
43
|
+
}
|
|
44
|
+
return SimInitProperties(**props_dict)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def create_rst_list(
|
|
48
|
+
rst_props: xtgeo.GridProperties,
|
|
49
|
+
seis_dates: List[str],
|
|
50
|
+
rst_prop_names: List[str],
|
|
51
|
+
) -> List[SimRstProperties]:
|
|
52
|
+
"""Create list of SimRstProperties from raw restart properties
|
|
53
|
+
Args:
|
|
54
|
+
rst_props: Raw restart properties
|
|
55
|
+
seis_dates: List of dates to process
|
|
56
|
+
rst_prop_names: List of property names to include
|
|
57
|
+
Returns:
|
|
58
|
+
List[SimRstProperties]: List of processed restart properties by date
|
|
59
|
+
"""
|
|
60
|
+
return [
|
|
61
|
+
SimRstProperties(
|
|
62
|
+
**{
|
|
63
|
+
name.lower(): rst_props[name + "_" + date].values
|
|
64
|
+
for name in rst_prop_names
|
|
65
|
+
if name + "_" + date in rst_props.names
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
for date in seis_dates
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def read_sim_grid_props(
|
|
73
|
+
egrid_file: Path,
|
|
74
|
+
init_property_file: Path,
|
|
75
|
+
restart_property_file: Path,
|
|
76
|
+
seis_dates: List[str],
|
|
77
|
+
) -> Tuple[xtgeo.Grid, SimInitProperties, List[SimRstProperties]]:
|
|
78
|
+
"""Read grid and properties from simulation run, both initial and restart properties
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
egrid_file: Path to the EGRID file
|
|
82
|
+
init_property_file: Path to the INIT file
|
|
83
|
+
restart_property_file: Path to the UNRST file
|
|
84
|
+
seis_dates: List of dates for which to read restart properties
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
sim_grid: grid definition for eclipse input
|
|
88
|
+
init_props: object with initial properties of simulation grid
|
|
89
|
+
rst_list: list with time-dependent simulation properties
|
|
90
|
+
"""
|
|
91
|
+
sim_grid = xtgeo.grid_from_file(egrid_file)
|
|
92
|
+
|
|
93
|
+
init_props = read_init_properties(init_property_file, sim_grid)
|
|
94
|
+
|
|
95
|
+
RST_PROPS = ["SWAT", "SGAS", "SOIL", "RS", "RV", "PRESSURE", "SALT"]
|
|
96
|
+
|
|
97
|
+
# Restart properties - set strict to False, False in case RV is not included in
|
|
98
|
+
# the UNRST file
|
|
99
|
+
rst_props = xtgeo.gridproperties_from_file(
|
|
100
|
+
restart_property_file,
|
|
101
|
+
fformat="unrst",
|
|
102
|
+
names=RST_PROPS,
|
|
103
|
+
dates=seis_dates,
|
|
104
|
+
grid=sim_grid,
|
|
105
|
+
strict=(False, False),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
rst_list = create_rst_list(rst_props, seis_dates, RST_PROPS)
|
|
109
|
+
|
|
110
|
+
return sim_grid, init_props, rst_list
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def read_ntg_grid(ntg_grid_file: Path) -> np.ma.MaskedArray:
|
|
114
|
+
"""Read PEM specific NTG property
|
|
115
|
+
Args:
|
|
116
|
+
ntg_grid_file: path to the NTG grid file
|
|
117
|
+
Returns:
|
|
118
|
+
net to gross property from simgrid adapted to PEM definition
|
|
119
|
+
"""
|
|
120
|
+
return xtgeo.gridproperty_from_file(ntg_grid_file).values
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def import_fractions(root_dir: Path, config: PemConfig) -> list:
|
|
124
|
+
"""Import volume fractions
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
root_dir (str): model directory, relative paths refer to it
|
|
128
|
+
config (PemConfig): configuration file with PEM parameters
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
list: fraction properties
|
|
132
|
+
"""
|
|
133
|
+
with restore_dir(
|
|
134
|
+
root_dir.joinpath(config.rock_matrix.volume_fractions.rel_path_fractions)
|
|
135
|
+
):
|
|
136
|
+
try:
|
|
137
|
+
grd = xtgeo.grid_from_file(
|
|
138
|
+
config.rock_matrix.volume_fractions.fractions_grid_file_name,
|
|
139
|
+
)
|
|
140
|
+
except ValueError as exc:
|
|
141
|
+
raise ImportError(
|
|
142
|
+
f"{__file__}: failed to import volume fractions file "
|
|
143
|
+
f"{config.rock_matrix.volume_fractions.fractions_grid_file_name}"
|
|
144
|
+
) from exc
|
|
145
|
+
try:
|
|
146
|
+
fracs = config.rock_matrix.fraction_names
|
|
147
|
+
grid_props = [
|
|
148
|
+
xtgeo.gridproperty_from_file(
|
|
149
|
+
file,
|
|
150
|
+
name=name,
|
|
151
|
+
grid=grd,
|
|
152
|
+
)
|
|
153
|
+
for name in fracs
|
|
154
|
+
for file in config.rock_matrix.volume_fractions.fractions_prop_file_names # noqa: E501
|
|
155
|
+
]
|
|
156
|
+
except ValueError as exc:
|
|
157
|
+
raise ImportError(
|
|
158
|
+
f"{__file__}: failed to import volume fractions files "
|
|
159
|
+
f"{config.rock_matrix.volume_fractions.fractions_prop_file_names}"
|
|
160
|
+
) from exc
|
|
161
|
+
return [grid_prop.values for grid_prop in grid_props]
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
from numpy.ma import MaskedArray
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Eclipse simulator file classes - SimInitProperties and time step SimRstProperties
|
|
9
|
+
@dataclass
|
|
10
|
+
class SimInitProperties:
|
|
11
|
+
poro: MaskedArray
|
|
12
|
+
depth: MaskedArray
|
|
13
|
+
ntg: MaskedArray
|
|
14
|
+
ntg_pem: Optional[MaskedArray] = None
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def delta_z(self) -> MaskedArray:
|
|
18
|
+
"""Estimate delta depth in the vertical direction"""
|
|
19
|
+
|
|
20
|
+
def _verify_delta_z(inp_arr: MaskedArray) -> None:
|
|
21
|
+
if not isinstance(inp_arr, MaskedArray) or inp_arr.dtype.kind != "f":
|
|
22
|
+
raise TypeError(
|
|
23
|
+
f"input to estimate_delta_z must be a 3D numpy masked "
|
|
24
|
+
f"array with float data, is {type(inp_arr)}."
|
|
25
|
+
)
|
|
26
|
+
if np.ndim(inp_arr) != 3:
|
|
27
|
+
raise ValueError(
|
|
28
|
+
f"{__file__}: 3-dimensional array must be input to "
|
|
29
|
+
f"estimate_delta_z. Depth difference is calculated along the "
|
|
30
|
+
f"third axis"
|
|
31
|
+
)
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
_verify_delta_z(self.depth)
|
|
35
|
+
d_z = np.zeros_like(self.depth)
|
|
36
|
+
d_z[:, :, 1:] = self.depth.data[:, :, 1:] - self.depth.data[:, :, 0:-1]
|
|
37
|
+
d_z[...] = np.clip(d_z, 0.0, a_max=None)
|
|
38
|
+
delta_z: MaskedArray = MaskedArray(d_z, mask=self.depth.mask)
|
|
39
|
+
return delta_z
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class SimRstProperties:
|
|
44
|
+
swat: MaskedArray
|
|
45
|
+
sgas: MaskedArray
|
|
46
|
+
soil: MaskedArray
|
|
47
|
+
rs: MaskedArray
|
|
48
|
+
pressure: MaskedArray
|
|
49
|
+
temp: MaskedArray | None = None
|
|
50
|
+
rv: MaskedArray | None = None
|
|
51
|
+
salt: MaskedArray | None = None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# Elastic properties for matrix, i.e. mixed minerals and volume fractions
|
|
55
|
+
@dataclass
|
|
56
|
+
class MatrixProperties:
|
|
57
|
+
bulk_modulus: MaskedArray
|
|
58
|
+
shear_modulus: MaskedArray
|
|
59
|
+
dens: MaskedArray
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# Separate class for dry rock, i.e. with porosity, can use MatrixProperties as base
|
|
63
|
+
# class
|
|
64
|
+
@dataclass
|
|
65
|
+
class DryRockProperties(MatrixProperties):
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# Acoustic properties for mixed fluids. If non-Newtonian fluids are to be considered,
|
|
70
|
+
# shear modulus and vs must be added
|
|
71
|
+
@dataclass
|
|
72
|
+
class EffectiveFluidProperties:
|
|
73
|
+
bulk_modulus: MaskedArray
|
|
74
|
+
dens: MaskedArray
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def vp(self):
|
|
78
|
+
return np.sqrt(self.bulk_modulus / self.dens)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# Pressure properties - overburden, formation and effective (strictly speaking
|
|
82
|
+
# differential) pressure
|
|
83
|
+
@dataclass
|
|
84
|
+
class PressureProperties:
|
|
85
|
+
formation_pressure: MaskedArray
|
|
86
|
+
effective_pressure: MaskedArray
|
|
87
|
+
overburden_pressure: MaskedArray
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# Seismic two-way time
|
|
91
|
+
@dataclass
|
|
92
|
+
class TwoWayTime:
|
|
93
|
+
twtpp: MaskedArray
|
|
94
|
+
twtss: MaskedArray
|
|
95
|
+
twtps: MaskedArray
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
# For isotropic elastic properties, only three independent components are needed
|
|
99
|
+
# to be defined, others can be derived from them, but this construction is needed
|
|
100
|
+
# to have all properties recognised by dataclasses.asdict()
|
|
101
|
+
@dataclass
|
|
102
|
+
class SaturatedRockProperties:
|
|
103
|
+
vp: MaskedArray
|
|
104
|
+
vs: MaskedArray
|
|
105
|
+
dens: MaskedArray
|
|
106
|
+
ai: MaskedArray = field(init=False)
|
|
107
|
+
si: MaskedArray = field(init=False)
|
|
108
|
+
vpvs: MaskedArray = field(init=False)
|
|
109
|
+
|
|
110
|
+
def __post_init__(self):
|
|
111
|
+
self.ai = self.vp * self.dens
|
|
112
|
+
self.si = self.vs * self.dens
|
|
113
|
+
self.vpvs = self.vp / self.vs
|