fmu-pem 0.0.1__py3-none-any.whl → 0.0.2__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/pem/pem_functions/density.py +2 -2
- fmu/pem/pem_functions/effective_pressure.py +7 -6
- fmu/pem/pem_functions/estimate_saturated_rock.py +3 -3
- fmu/pem/pem_functions/fluid_properties.py +13 -8
- fmu/pem/pem_functions/regression_models.py +3 -3
- fmu/pem/pem_functions/run_friable_model.py +2 -2
- fmu/pem/pem_functions/run_patchy_cement_model.py +2 -2
- fmu/pem/pem_functions/run_t_matrix_and_pressure.py +1 -1
- fmu/pem/pem_utilities/enum_defs.py +17 -1
- fmu/pem/pem_utilities/import_routines.py +2 -1
- fmu/pem/pem_utilities/pem_config_validation.py +65 -32
- fmu/pem/pem_utilities/rpm_models.py +47 -22
- fmu/pem/version.py +2 -2
- {fmu_pem-0.0.1.dist-info → fmu_pem-0.0.2.dist-info}/METADATA +17 -19
- {fmu_pem-0.0.1.dist-info → fmu_pem-0.0.2.dist-info}/RECORD +19 -19
- {fmu_pem-0.0.1.dist-info → fmu_pem-0.0.2.dist-info}/WHEEL +0 -0
- {fmu_pem-0.0.1.dist-info → fmu_pem-0.0.2.dist-info}/entry_points.txt +0 -0
- {fmu_pem-0.0.1.dist-info → fmu_pem-0.0.2.dist-info}/licenses/LICENSE +0 -0
- {fmu_pem-0.0.1.dist-info → fmu_pem-0.0.2.dist-info}/top_level.txt +0 -0
fmu/pem/pem_functions/density.py
CHANGED
|
@@ -35,7 +35,7 @@ def estimate_bulk_density(
|
|
|
35
35
|
Raises:
|
|
36
36
|
ValueError: If fluid_props is an empty list.
|
|
37
37
|
"""
|
|
38
|
-
if config.rock_matrix.
|
|
38
|
+
if isinstance(config.rock_matrix.model, PatchyCementRPM):
|
|
39
39
|
# Get cement mineral properties
|
|
40
40
|
cement_mineral = config.rock_matrix.cement
|
|
41
41
|
mineral = config.rock_matrix.minerals[cement_mineral]
|
|
@@ -44,7 +44,7 @@ def estimate_bulk_density(
|
|
|
44
44
|
mineral.bulk_modulus, mineral.shear_modulus, mineral.density, init_prop.poro
|
|
45
45
|
)
|
|
46
46
|
rel_frac_cem = (
|
|
47
|
-
config.rock_matrix.
|
|
47
|
+
config.rock_matrix.model.parameters.cement_fraction / init_prop.poro
|
|
48
48
|
)
|
|
49
49
|
rho_m = (
|
|
50
50
|
rel_frac_cem * cement_properties.dens
|
|
@@ -11,7 +11,7 @@ from fmu.pem.pem_utilities import (
|
|
|
11
11
|
SimRstProperties,
|
|
12
12
|
to_masked_array,
|
|
13
13
|
)
|
|
14
|
-
from fmu.pem.pem_utilities.enum_defs import
|
|
14
|
+
from fmu.pem.pem_utilities.enum_defs import OverburdenPressureTypes
|
|
15
15
|
|
|
16
16
|
from .density import estimate_bulk_density
|
|
17
17
|
|
|
@@ -44,13 +44,14 @@ def estimate_pressure(
|
|
|
44
44
|
# Saturated rock bulk density bulk
|
|
45
45
|
bulk_density = estimate_bulk_density(config, sim_init, fluid_props, matrix_props)
|
|
46
46
|
|
|
47
|
-
ovb = config.pressure.overburden
|
|
48
|
-
|
|
47
|
+
# ovb = config.pressure.overburden
|
|
48
|
+
ovb = config.pressure
|
|
49
|
+
if ovb.type == OverburdenPressureTypes.CONSTANT:
|
|
49
50
|
eff_pres = [
|
|
50
51
|
estimate_effective_pressure(
|
|
51
52
|
formation_pressure=sim_date.pressure * 1.0e5,
|
|
52
53
|
bulk_density=dens,
|
|
53
|
-
reference_overburden_pressure=ovb.
|
|
54
|
+
reference_overburden_pressure=ovb.value,
|
|
54
55
|
)
|
|
55
56
|
for (sim_date, dens) in zip(sim_rst, bulk_density)
|
|
56
57
|
]
|
|
@@ -60,8 +61,8 @@ def estimate_pressure(
|
|
|
60
61
|
formation_pressure=sim_date.pressure * 1.0e5,
|
|
61
62
|
bulk_density=dens,
|
|
62
63
|
reference_overburden_pressure=overburden_pressure_from_trend(
|
|
63
|
-
inter=ovb.
|
|
64
|
-
grad=ovb.
|
|
64
|
+
inter=ovb.intercept,
|
|
65
|
+
grad=ovb.gradient,
|
|
65
66
|
depth=sim_init.depth,
|
|
66
67
|
),
|
|
67
68
|
)
|
|
@@ -40,7 +40,7 @@ def estimate_saturated_rock(
|
|
|
40
40
|
Returns:
|
|
41
41
|
saturated rock properties per restart date
|
|
42
42
|
"""
|
|
43
|
-
if isinstance(config.rock_matrix.
|
|
43
|
+
if isinstance(config.rock_matrix.model, PatchyCementRPM):
|
|
44
44
|
# Patchy cement model
|
|
45
45
|
cement = config.rock_matrix.minerals[config.rock_matrix.cement]
|
|
46
46
|
cement_properties = estimate_cement(
|
|
@@ -57,7 +57,7 @@ def estimate_saturated_rock(
|
|
|
57
57
|
eff_pres,
|
|
58
58
|
config,
|
|
59
59
|
)
|
|
60
|
-
elif isinstance(config.rock_matrix.
|
|
60
|
+
elif isinstance(config.rock_matrix.model, FriableRPM):
|
|
61
61
|
# Friable sandstone model
|
|
62
62
|
sat_rock_props = run_friable(
|
|
63
63
|
matrix_props,
|
|
@@ -66,7 +66,7 @@ def estimate_saturated_rock(
|
|
|
66
66
|
eff_pres,
|
|
67
67
|
config,
|
|
68
68
|
)
|
|
69
|
-
elif isinstance(config.rock_matrix.
|
|
69
|
+
elif isinstance(config.rock_matrix.model, RegressionModels):
|
|
70
70
|
# Regression models for dry rock properties, saturation by Gassmann
|
|
71
71
|
sat_rock_props = run_regression_models(
|
|
72
72
|
matrix_props,
|
|
@@ -22,7 +22,7 @@ from fmu.pem.pem_utilities import (
|
|
|
22
22
|
reverse_filter_and_restore,
|
|
23
23
|
to_masked_array,
|
|
24
24
|
)
|
|
25
|
-
from fmu.pem.pem_utilities.enum_defs import CO2Models, FluidMixModel
|
|
25
|
+
from fmu.pem.pem_utilities.enum_defs import CO2Models, FluidMixModel, TemperatureMethod
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
def effective_fluid_properties(
|
|
@@ -61,8 +61,6 @@ def effective_fluid_properties(
|
|
|
61
61
|
|
|
62
62
|
# Convert pressure from bar to Pa
|
|
63
63
|
pres = 1.0e5 * prop.pressure
|
|
64
|
-
mix_model = fluid_params.mix_method
|
|
65
|
-
brie_exp = fluid_params.brie_exponent
|
|
66
64
|
|
|
67
65
|
# Salinity and temperature are either taken as constants from config file or
|
|
68
66
|
# from eclipse simulator model
|
|
@@ -73,10 +71,17 @@ def effective_fluid_properties(
|
|
|
73
71
|
salinity = to_masked_array(fluid_params.brine.salinity, sat_wat)
|
|
74
72
|
# Temperature will normally be set as a constant. It can come from eclipse in
|
|
75
73
|
# the case a compositional fluid model is run.
|
|
76
|
-
if fluid_params.
|
|
77
|
-
temp
|
|
74
|
+
if fluid_params.temperature.type == TemperatureMethod.FROMSIM:
|
|
75
|
+
if hasattr(prop, "temp") and prop.temp is not None:
|
|
76
|
+
temp = prop.temp
|
|
77
|
+
else:
|
|
78
|
+
raise ValueError(
|
|
79
|
+
"eclipse simulation restart file does not have "
|
|
80
|
+
"temperature attribute. Constant temperature must "
|
|
81
|
+
"be set in parameter file"
|
|
82
|
+
)
|
|
78
83
|
else:
|
|
79
|
-
temp = to_masked_array(fluid_params.temperature, sat_wat)
|
|
84
|
+
temp = to_masked_array(fluid_params.temperature.temperature_value, sat_wat)
|
|
80
85
|
|
|
81
86
|
# Gas gravity has to be expanded to a masked array if it comes as a float
|
|
82
87
|
if isinstance(fluid_params.gas.gas_gravity, float):
|
|
@@ -217,7 +222,7 @@ def effective_fluid_properties(
|
|
|
217
222
|
|
|
218
223
|
gas = dict(zip(fluid_keys, gas_props))
|
|
219
224
|
|
|
220
|
-
if
|
|
225
|
+
if fluid_params.fluid_mix_method == FluidMixModel.WOOD:
|
|
221
226
|
mixed_fluid_bulk_modulus = multi_wood(
|
|
222
227
|
[sat_wat, sat_gas, sat_oil],
|
|
223
228
|
[brine["bulk_modulus"], gas["bulk_modulus"], oil["bulk_modulus"]],
|
|
@@ -230,7 +235,7 @@ def effective_fluid_properties(
|
|
|
230
235
|
brine["bulk_modulus"],
|
|
231
236
|
sat_oil,
|
|
232
237
|
oil["bulk_modulus"],
|
|
233
|
-
|
|
238
|
+
fluid_params.fluid_mix_method.brie_exponent,
|
|
234
239
|
)
|
|
235
240
|
mixed_fluid_density = (
|
|
236
241
|
sat_wat * brine["density"]
|
|
@@ -200,14 +200,14 @@ def run_regression_models(
|
|
|
200
200
|
)
|
|
201
201
|
if not multiple_lithologies:
|
|
202
202
|
dry_props = dry_rock_regression(
|
|
203
|
-
tmp_por, tmp_min_rho, config.rock_matrix.
|
|
203
|
+
tmp_por, tmp_min_rho, config.rock_matrix.model.parameters.sandstone
|
|
204
204
|
)
|
|
205
205
|
else:
|
|
206
206
|
dry_props_sand = dry_rock_regression(
|
|
207
|
-
tmp_por, tmp_min_rho, config.rock_matrix.
|
|
207
|
+
tmp_por, tmp_min_rho, config.rock_matrix.model.parameters.sandstone
|
|
208
208
|
)
|
|
209
209
|
dry_props_shale = dry_rock_regression(
|
|
210
|
-
tmp_por, tmp_min_rho, config.rock_matrix.
|
|
210
|
+
tmp_por, tmp_min_rho, config.rock_matrix.model.parameters.shale
|
|
211
211
|
)
|
|
212
212
|
dry_rho = (
|
|
213
213
|
dry_props_sand.dens * (1.0 - tmp_vsh) + dry_props_shale.dens * tmp_vsh
|
|
@@ -41,7 +41,7 @@ def run_friable(
|
|
|
41
41
|
# effective_pressure can be lists
|
|
42
42
|
fluid, pressure = _verify_inputs(fluid, pressure)
|
|
43
43
|
saturated_props = []
|
|
44
|
-
friable_params = config.rock_matrix.
|
|
44
|
+
friable_params = config.rock_matrix.model.parameters
|
|
45
45
|
for fl_prop, pres in zip(fluid, pressure):
|
|
46
46
|
(
|
|
47
47
|
mask,
|
|
@@ -75,7 +75,7 @@ def run_friable(
|
|
|
75
75
|
tmp_por,
|
|
76
76
|
tmp_pres,
|
|
77
77
|
friable_params.critical_porosity,
|
|
78
|
-
friable_params.
|
|
78
|
+
friable_params.coordination_number_function.fcn,
|
|
79
79
|
friable_params.coordination_number,
|
|
80
80
|
friable_params.shear_reduction,
|
|
81
81
|
)
|
|
@@ -43,7 +43,7 @@ def run_patchy_cement(
|
|
|
43
43
|
# effective_pressure can be lists
|
|
44
44
|
fluid, pressure = _verify_inputs(fluid, pressure)
|
|
45
45
|
saturated_props = []
|
|
46
|
-
pat_cem_params = config.rock_matrix.
|
|
46
|
+
pat_cem_params = config.rock_matrix.model.parameters
|
|
47
47
|
for fl_prop, pres in zip(fluid, pressure):
|
|
48
48
|
(
|
|
49
49
|
mask,
|
|
@@ -88,7 +88,7 @@ def run_patchy_cement(
|
|
|
88
88
|
tmp_pres,
|
|
89
89
|
pat_cem_params.cement_fraction,
|
|
90
90
|
pat_cem_params.critical_porosity,
|
|
91
|
-
pat_cem_params.
|
|
91
|
+
pat_cem_params.coordination_number_function.fcn,
|
|
92
92
|
pat_cem_params.coordination_number,
|
|
93
93
|
pat_cem_params.shear_reduction,
|
|
94
94
|
)
|
|
@@ -73,7 +73,7 @@ def run_t_matrix_and_pressure_models(
|
|
|
73
73
|
exp_parameter_file = config.paths.rel_path_pem / exp_parameter_file
|
|
74
74
|
|
|
75
75
|
saturated_props = []
|
|
76
|
-
t_mat_params = config.rock_matrix.
|
|
76
|
+
t_mat_params = config.rock_matrix.model.parameters
|
|
77
77
|
|
|
78
78
|
if vsh is None and t_mat_params.t_mat_model_version == "EXP":
|
|
79
79
|
raise ValueError("Shale volume must be provided for EXP model")
|
|
@@ -5,7 +5,7 @@ Define enumerated strings
|
|
|
5
5
|
from enum import Enum
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
class
|
|
8
|
+
class OverburdenPressureTypes(str, Enum):
|
|
9
9
|
CONSTANT = "constant"
|
|
10
10
|
TREND = "trend"
|
|
11
11
|
|
|
@@ -52,3 +52,19 @@ class RPMType(str, Enum):
|
|
|
52
52
|
class VolumeFractions(str, Enum):
|
|
53
53
|
NTG_SIM = "ntg_sim"
|
|
54
54
|
VOL_FRAC = "fraction_files"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class GasModels(str, Enum):
|
|
58
|
+
GLOBAL = "Global"
|
|
59
|
+
LIGHT = "Light"
|
|
60
|
+
HC2016 = "HC2016"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class CoordinationNumberFunction(str, Enum):
|
|
64
|
+
PORBASED = "PorBased"
|
|
65
|
+
CONSTANT = "ConstVal"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class TemperatureMethod(str, Enum):
|
|
69
|
+
CONSTANT = "constant"
|
|
70
|
+
FROMSIM = "from_sim"
|
|
@@ -92,7 +92,8 @@ def read_sim_grid_props(
|
|
|
92
92
|
|
|
93
93
|
init_props = read_init_properties(init_property_file, sim_grid)
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
# TEMP will only be available for eclipse-300
|
|
96
|
+
RST_PROPS = ["SWAT", "SGAS", "SOIL", "RS", "RV", "PRESSURE", "SALT", "TEMP"]
|
|
96
97
|
|
|
97
98
|
# Restart properties - set strict to False, False in case RV is not included in
|
|
98
99
|
# the UNRST file
|
|
@@ -6,9 +6,9 @@ from typing import Any, Dict, List, Literal, Optional, Self, Union
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
from pydantic import (
|
|
8
8
|
BaseModel,
|
|
9
|
+
ConfigDict,
|
|
9
10
|
DirectoryPath,
|
|
10
11
|
Field,
|
|
11
|
-
FilePath,
|
|
12
12
|
field_validator,
|
|
13
13
|
model_validator,
|
|
14
14
|
)
|
|
@@ -20,8 +20,10 @@ from fmu.pem import INTERNAL_EQUINOR
|
|
|
20
20
|
from .enum_defs import (
|
|
21
21
|
CO2Models,
|
|
22
22
|
FluidMixModel,
|
|
23
|
+
GasModels,
|
|
23
24
|
MineralMixModel,
|
|
24
|
-
|
|
25
|
+
OverburdenPressureTypes,
|
|
26
|
+
TemperatureMethod,
|
|
25
27
|
VolumeFractions,
|
|
26
28
|
)
|
|
27
29
|
from .rpm_models import MineralProperties, PatchyCementRPM, RegressionRPM, TMatrixRPM
|
|
@@ -43,7 +45,7 @@ class FractionFiles(BaseModel):
|
|
|
43
45
|
default=Path("../../sim2seis/input/pem"),
|
|
44
46
|
description="Directory for volume fractions",
|
|
45
47
|
)
|
|
46
|
-
fractions_grid_file_name:
|
|
48
|
+
fractions_grid_file_name: Path = Field(
|
|
47
49
|
description="Grid definition of the volume fractions"
|
|
48
50
|
)
|
|
49
51
|
fractions_prop_file_names: list[Path] = Field(description="Volume fractions")
|
|
@@ -54,6 +56,21 @@ class FractionFiles(BaseModel):
|
|
|
54
56
|
"they have to represent real volume fractions, and this will be ignored",
|
|
55
57
|
)
|
|
56
58
|
|
|
59
|
+
@model_validator(mode="after")
|
|
60
|
+
def check_fractions(self) -> Self:
|
|
61
|
+
full_fraction_grid = self.rel_path_fractions / self.fractions_grid_file_name
|
|
62
|
+
if not full_fraction_grid.exists():
|
|
63
|
+
raise FileNotFoundError(
|
|
64
|
+
f"fraction grid file is missing: {full_fraction_grid}"
|
|
65
|
+
)
|
|
66
|
+
for frac_prop in self.fractions_prop_file_names:
|
|
67
|
+
full_fraction_prop = self.rel_path_fractions / frac_prop
|
|
68
|
+
if not full_fraction_prop.exists():
|
|
69
|
+
raise FileNotFoundError(
|
|
70
|
+
f"fraction prop file is missing: {full_fraction_prop}"
|
|
71
|
+
)
|
|
72
|
+
return self
|
|
73
|
+
|
|
57
74
|
|
|
58
75
|
class RockMatrixProperties(BaseModel):
|
|
59
76
|
"""Configuration for rock matrix properties.
|
|
@@ -63,7 +80,9 @@ class RockMatrixProperties(BaseModel):
|
|
|
63
80
|
and other lithologies.
|
|
64
81
|
"""
|
|
65
82
|
|
|
66
|
-
|
|
83
|
+
model_config = ConfigDict(title="Rock matrix properties:")
|
|
84
|
+
|
|
85
|
+
model: Union[PatchyCementRPM, TMatrixRPM, RegressionRPM] = Field(
|
|
67
86
|
description="Selection of parameter set for rock physics model"
|
|
68
87
|
)
|
|
69
88
|
minerals: Dict[str, MineralProperties] = Field(
|
|
@@ -174,23 +193,15 @@ class RockMatrixProperties(BaseModel):
|
|
|
174
193
|
|
|
175
194
|
|
|
176
195
|
# Pressure
|
|
177
|
-
class
|
|
196
|
+
class OverburdenPressureTrend(BaseModel):
|
|
197
|
+
type: SkipJsonSchema[OverburdenPressureTypes] = "trend"
|
|
178
198
|
intercept: float = Field(description="Intercept in pressure depth trend")
|
|
179
199
|
gradient: float = Field(description="Gradient in pressure depth trend")
|
|
180
200
|
|
|
181
201
|
|
|
182
|
-
class
|
|
183
|
-
type:
|
|
184
|
-
|
|
185
|
-
)
|
|
186
|
-
trend: Trend = Field(
|
|
187
|
-
description="Setting of intercept and gradient for pressure trend vs. depth"
|
|
188
|
-
)
|
|
189
|
-
constant: float = Field(description="Constant overburden pressure setting")
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
class Pressure(BaseModel):
|
|
193
|
-
overburden: Overburden
|
|
202
|
+
class OverburdenPressureConstant(BaseModel):
|
|
203
|
+
type: SkipJsonSchema[OverburdenPressureTypes] = "constant"
|
|
204
|
+
value: float = Field(description="Constant pressure")
|
|
194
205
|
|
|
195
206
|
|
|
196
207
|
# Fluids
|
|
@@ -269,12 +280,34 @@ class Gas(BaseModel):
|
|
|
269
280
|
le=0.87,
|
|
270
281
|
description="Gas gravity is a ratio of gas molecular weight to that air",
|
|
271
282
|
)
|
|
272
|
-
model:
|
|
283
|
+
model: GasModels = Field(
|
|
273
284
|
default="HC2016",
|
|
274
285
|
description="Gas model is one of 'Global', 'Light', or 'HC2016' (default)",
|
|
275
286
|
)
|
|
276
287
|
|
|
277
288
|
|
|
289
|
+
class MixModelWood(BaseModel):
|
|
290
|
+
method: SkipJsonSchema[FluidMixModel] = "wood"
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
class MixModelBrie(BaseModel):
|
|
294
|
+
method: SkipJsonSchema[FluidMixModel] = "brie"
|
|
295
|
+
brie_exponent: float = Field(
|
|
296
|
+
default=3.0,
|
|
297
|
+
description="Brie exponent selects the mixing curve shape, from linear mix to "
|
|
298
|
+
"harmonic mean",
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
class ConstantTemperature(BaseModel):
|
|
303
|
+
type: SkipJsonSchema[TemperatureMethod] = "constant"
|
|
304
|
+
temperature_value: float
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
class TemperatureFromSim(BaseModel):
|
|
308
|
+
type: SkipJsonSchema[TemperatureMethod] = "from_sim"
|
|
309
|
+
|
|
310
|
+
|
|
278
311
|
# Note that CO2 does not require a separate definition here, as it's properties only
|
|
279
312
|
# depend on temperature and pressure
|
|
280
313
|
class Fluids(BaseModel):
|
|
@@ -285,18 +318,16 @@ class Fluids(BaseModel):
|
|
|
285
318
|
gas: Gas = Field(description="Gas model parameters")
|
|
286
319
|
condensate: Oil | None = Field(
|
|
287
320
|
default=None,
|
|
321
|
+
title="Condensate properties",
|
|
288
322
|
description="Condensate is defined by the same set of parameters as oil, "
|
|
289
323
|
"optional setting for condensate cases",
|
|
290
324
|
)
|
|
291
|
-
|
|
325
|
+
fluid_mix_method: MixModelBrie | MixModelWood = Field(
|
|
326
|
+
default=MixModelBrie,
|
|
292
327
|
description="Selection between Wood's or Brie model. Wood's model gives more "
|
|
293
|
-
"radical response to adding small amounts of gas in brine or oil"
|
|
328
|
+
"radical response to adding small amounts of gas in brine or oil",
|
|
294
329
|
)
|
|
295
|
-
|
|
296
|
-
description="Brie exponent selects the mixing curve shape, from linear mix to "
|
|
297
|
-
"harmonic mean"
|
|
298
|
-
)
|
|
299
|
-
temperature: float = Field(
|
|
330
|
+
temperature: ConstantTemperature | TemperatureFromSim = Field(
|
|
300
331
|
description="In most cases it is sufficient with a constant temperature "
|
|
301
332
|
"setting for the reservoir. If temperature is modelled in the "
|
|
302
333
|
"simulation model, it is preferred to use that"
|
|
@@ -308,10 +339,6 @@ class Fluids(BaseModel):
|
|
|
308
339
|
"between formation water and injected water. If salinity is "
|
|
309
340
|
"modelled in the simulation model, it is preferred to use that",
|
|
310
341
|
)
|
|
311
|
-
temperature_from_sim: bool = Field(
|
|
312
|
-
default=False,
|
|
313
|
-
description="Flag to use temperature estimate from simulation model",
|
|
314
|
-
)
|
|
315
342
|
gas_saturation_is_co2: bool = Field(
|
|
316
343
|
default=False,
|
|
317
344
|
description="Eclipse model only provides one parameter for gas saturation, "
|
|
@@ -453,6 +480,7 @@ class Results(BaseModel):
|
|
|
453
480
|
|
|
454
481
|
class PemConfig(BaseModel):
|
|
455
482
|
paths: SkipJsonSchema[PemPaths] = Field(
|
|
483
|
+
default_factory=PemPaths,
|
|
456
484
|
description="Default path settings exist, it is possible to override them, "
|
|
457
485
|
"mostly relevant for input paths",
|
|
458
486
|
)
|
|
@@ -463,7 +491,8 @@ class PemConfig(BaseModel):
|
|
|
463
491
|
fluids: Fluids = Field(
|
|
464
492
|
description="Settings related to fluid composition",
|
|
465
493
|
)
|
|
466
|
-
pressure:
|
|
494
|
+
pressure: OverburdenPressureTrend | OverburdenPressureConstant = Field(
|
|
495
|
+
default=OverburdenPressureTrend,
|
|
467
496
|
description="Definition of overburden pressure model - constant or trend",
|
|
468
497
|
)
|
|
469
498
|
results: Results = Field(
|
|
@@ -476,10 +505,14 @@ class PemConfig(BaseModel):
|
|
|
476
505
|
"difference calculation is run - normal difference, percent "
|
|
477
506
|
"difference or ratio"
|
|
478
507
|
)
|
|
479
|
-
global_params:
|
|
508
|
+
global_params: SkipJsonSchema[FromGlobal | None] = Field(
|
|
509
|
+
default=None,
|
|
510
|
+
)
|
|
480
511
|
|
|
481
512
|
@field_validator("paths", mode="before")
|
|
482
|
-
def check_and_create_directories(cls, v):
|
|
513
|
+
def check_and_create_directories(cls, v: Dict, info: ValidationInfo):
|
|
514
|
+
if v is None:
|
|
515
|
+
return PemPaths()
|
|
483
516
|
for key, path in v.items():
|
|
484
517
|
if key == "rel_path_intermed_output" or key == "rel_path_output":
|
|
485
518
|
os.makedirs(path, exist_ok=True)
|
|
@@ -2,19 +2,40 @@
|
|
|
2
2
|
Define RPM model types and their parameters
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from enum import Enum
|
|
6
5
|
from typing import List, Literal
|
|
7
6
|
|
|
8
|
-
from pydantic import BaseModel, Field
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
8
|
+
from pydantic.json_schema import SkipJsonSchema
|
|
9
9
|
from typing_extensions import Annotated
|
|
10
10
|
|
|
11
|
-
from fmu.pem.pem_utilities.enum_defs import RPMType
|
|
11
|
+
from fmu.pem.pem_utilities.enum_defs import CoordinationNumberFunction, RPMType
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class MineralProperties(BaseModel):
|
|
15
|
-
bulk_modulus:
|
|
16
|
-
shear_modulus:
|
|
17
|
-
density:
|
|
15
|
+
bulk_modulus: float = Field(gt=1.0e9, lt=5.0e11, description="Units: Pa")
|
|
16
|
+
shear_modulus: float = Field(gt=1.0e9, lt=5.0e11, description="Units: Pa")
|
|
17
|
+
density: float = Field(gt=1.0e3, lt=1.0e4, description="Units: kg/m^3")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CoordinationNumberPorBased(BaseModel):
|
|
21
|
+
fcn: SkipJsonSchema[CoordinationNumberFunction] = Field(
|
|
22
|
+
default="PorBased",
|
|
23
|
+
description="Coordinate number is the number of grain-grain contacts. It is "
|
|
24
|
+
"normally assumed to be a function of porosity for friable sand",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class CoordinationNumberConstVal(BaseModel):
|
|
29
|
+
fcn: SkipJsonSchema[CoordinationNumberFunction] = Field(
|
|
30
|
+
default="ConstVal",
|
|
31
|
+
)
|
|
32
|
+
coordination_number: float = Field(
|
|
33
|
+
default=9.0,
|
|
34
|
+
gt=2.0,
|
|
35
|
+
lt=16.0,
|
|
36
|
+
description="In case of a constant value for the number of grain contacts, "
|
|
37
|
+
"a value of 8-9 is common",
|
|
38
|
+
)
|
|
18
39
|
|
|
19
40
|
|
|
20
41
|
class PatchyCementParams(BaseModel):
|
|
@@ -47,16 +68,13 @@ class PatchyCementParams(BaseModel):
|
|
|
47
68
|
"between grains. Shear reduction of 1 means frictionless contact, "
|
|
48
69
|
"and 0 means full friction",
|
|
49
70
|
)
|
|
50
|
-
|
|
51
|
-
|
|
71
|
+
coordination_number_function: (
|
|
72
|
+
CoordinationNumberPorBased | CoordinationNumberConstVal
|
|
73
|
+
) = Field(
|
|
74
|
+
default_factory=CoordinationNumberPorBased,
|
|
52
75
|
description="Coordinate number is the number of grain-grain contacts. It is "
|
|
53
76
|
"normally assumed to be a function of porosity for friable sand",
|
|
54
77
|
)
|
|
55
|
-
coordination_number: float = Field(
|
|
56
|
-
default=9.0,
|
|
57
|
-
description="In case of a constant value for the number of grain contacts, "
|
|
58
|
-
"a value of 8-9 is common",
|
|
59
|
-
)
|
|
60
78
|
|
|
61
79
|
|
|
62
80
|
class TMatrixParams(BaseModel):
|
|
@@ -98,8 +116,10 @@ class TMatrixParams(BaseModel):
|
|
|
98
116
|
|
|
99
117
|
|
|
100
118
|
class RhoRegressionMixin(BaseModel):
|
|
101
|
-
rho_weights: List[float]
|
|
102
|
-
default=
|
|
119
|
+
rho_weights: List[float] = Field(
|
|
120
|
+
default=[
|
|
121
|
+
1.0,
|
|
122
|
+
],
|
|
103
123
|
description="List of float values for polynomial regression for density "
|
|
104
124
|
"based on porosity",
|
|
105
125
|
)
|
|
@@ -108,7 +128,8 @@ class RhoRegressionMixin(BaseModel):
|
|
|
108
128
|
description="Matrix density is normally estimated from "
|
|
109
129
|
"mineral composition and the density of each mineral. "
|
|
110
130
|
"Setting this to True will estimate matrix "
|
|
111
|
-
"density based on porosity alone"
|
|
131
|
+
"density based on porosity alone. In that case, check the "
|
|
132
|
+
"rho regression parameters",
|
|
112
133
|
)
|
|
113
134
|
|
|
114
135
|
|
|
@@ -123,7 +144,7 @@ class VpVsRegressionParams(RhoRegressionMixin):
|
|
|
123
144
|
description="List of float values for polynomial regression for Vs "
|
|
124
145
|
"based on porosity",
|
|
125
146
|
)
|
|
126
|
-
mode: Literal["vp_vs"] = Field(
|
|
147
|
+
mode: SkipJsonSchema[Literal["vp_vs"]] = Field(
|
|
127
148
|
default="vp_vs",
|
|
128
149
|
description="Regression mode mode must be set to 'vp_vs' for "
|
|
129
150
|
"estimation of Vp and Vs based on porosity",
|
|
@@ -141,7 +162,7 @@ class KMuRegressionParams(RhoRegressionMixin):
|
|
|
141
162
|
description="List of float values for polynomial regression for shear modulus "
|
|
142
163
|
"based on porosity",
|
|
143
164
|
)
|
|
144
|
-
mode: Literal["k_mu"] = Field(
|
|
165
|
+
mode: SkipJsonSchema[Literal["k_mu"]] = Field(
|
|
145
166
|
default="k_mu",
|
|
146
167
|
description="Regression mode mode must be set to 'k_mu' for "
|
|
147
168
|
"estimation of bulk and shear modulus based on porosity",
|
|
@@ -158,20 +179,24 @@ class RegressionModels(BaseModel):
|
|
|
158
179
|
|
|
159
180
|
|
|
160
181
|
class PatchyCementRPM(BaseModel):
|
|
161
|
-
|
|
182
|
+
model_config = ConfigDict(title="Patchy Cement Model")
|
|
183
|
+
model: SkipJsonSchema[Literal[RPMType.PATCHY_CEMENT]]
|
|
162
184
|
parameters: PatchyCementParams
|
|
163
185
|
|
|
164
186
|
|
|
165
187
|
class FriableRPM(BaseModel):
|
|
166
|
-
|
|
188
|
+
model_config = ConfigDict(title="Friable Sand Model")
|
|
189
|
+
model: SkipJsonSchema[Literal[RPMType.FRIABLE]]
|
|
167
190
|
parameters: PatchyCementParams
|
|
168
191
|
|
|
169
192
|
|
|
170
193
|
class TMatrixRPM(BaseModel):
|
|
171
|
-
|
|
194
|
+
model_config = ConfigDict(title="T-Matrix Inclusion Model")
|
|
195
|
+
model: SkipJsonSchema[Literal[RPMType.T_MATRIX]]
|
|
172
196
|
parameters: TMatrixParams
|
|
173
197
|
|
|
174
198
|
|
|
175
199
|
class RegressionRPM(BaseModel):
|
|
176
|
-
|
|
200
|
+
model_config = ConfigDict(title="Regression Model")
|
|
201
|
+
model: SkipJsonSchema[Literal[RPMType.REGRESSION]]
|
|
177
202
|
parameters: RegressionModels
|
fmu/pem/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fmu-pem
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.2
|
|
4
4
|
Summary: pem
|
|
5
5
|
License: GNU GENERAL PUBLIC LICENSE
|
|
6
6
|
Version 3, 29 June 2007
|
|
@@ -686,12 +686,8 @@ Classifier: Topic :: Scientific/Engineering :: Physics
|
|
|
686
686
|
Classifier: Topic :: Software Development :: Libraries
|
|
687
687
|
Classifier: Topic :: Utilities
|
|
688
688
|
Classifier: Operating System :: POSIX :: Linux
|
|
689
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
690
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
691
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
692
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
693
689
|
Classifier: Natural Language :: English
|
|
694
|
-
Requires-Python: >=3.
|
|
690
|
+
Requires-Python: >=3.11
|
|
695
691
|
Description-Content-Type: text/markdown
|
|
696
692
|
License-File: LICENSE
|
|
697
693
|
Requires-Dist: numpy>=1.24.3
|
|
@@ -719,13 +715,19 @@ Requires-Dist: sphinx-togglebutton; extra == "docs"
|
|
|
719
715
|
Requires-Dist: sphinx_rtd_theme; extra == "docs"
|
|
720
716
|
Dynamic: license-file
|
|
721
717
|
|
|
722
|
-
|
|
718
|
+
> [!WARNING]
|
|
719
|
+
> `fmu-pem` is not yet qualified technology, and as of today only applicable for selected pilot test fields.
|
|
723
720
|
|
|
724
|
-
|
|
725
|
-
based on the [rock-physics-open](https://github.com/equinor/rock-physics-open)
|
|
726
|
-
library.
|
|
721
|
+
**[📚 User documentation](https://equinor.github.io/fmu-pem/)**
|
|
727
722
|
|
|
728
|
-
##
|
|
723
|
+
## What is fmu-pem?
|
|
724
|
+
|
|
725
|
+
Petro-elastic model (PEM) for use in e.g. [fmu-sim2seis](https://github.com/equinor/fmu-sim2seis)
|
|
726
|
+
based on the [rock-physics-open](https://github.com/equinor/rock-physics-open) library.
|
|
727
|
+
|
|
728
|
+
## How to use fmu-pem?
|
|
729
|
+
|
|
730
|
+
### Installation
|
|
729
731
|
|
|
730
732
|
To install `fmu-pem`, first activate a virtual environment, then type:
|
|
731
733
|
|
|
@@ -736,7 +738,7 @@ pip install fmu-pem
|
|
|
736
738
|
The PEM is controlled by parameter settings in a *yaml-file*, given as part of the
|
|
737
739
|
command line arguments, or by the workflow parameter if it is run as an ERT forward model.
|
|
738
740
|
|
|
739
|
-
|
|
741
|
+
### Calibration of rock physics models
|
|
740
742
|
|
|
741
743
|
Calibration of the rock physics models is normally carried out in
|
|
742
744
|
[RokDoc](https://www.ikonscience.com/rokdoc-geoprediction-software-platform/)
|
|
@@ -752,17 +754,13 @@ from LFP logs, if they are available.
|
|
|
752
754
|
> pip install "git+ssh://git@github.com/equinor/rock-physics"`
|
|
753
755
|
> ```
|
|
754
756
|
|
|
755
|
-
##
|
|
756
|
-
|
|
757
|
-
Users can visit https://equinor.github.io/fmu-pem/ in order to get help configuring the `fmu-pem` input data.
|
|
758
|
-
|
|
759
|
-
# How to develop fmu-pem
|
|
757
|
+
## How to develop fmu-pem?
|
|
760
758
|
|
|
761
759
|
Developing the user interface can be done by:
|
|
762
760
|
```bash
|
|
763
|
-
cd ./
|
|
761
|
+
cd ./documentation
|
|
764
762
|
npm ci # Install dependencies
|
|
765
763
|
npm run create-json-schema # Extract JSON schema from Python code
|
|
766
|
-
npm run dev # Start local development server
|
|
764
|
+
npm run docs:dev # Start local development server
|
|
767
765
|
```
|
|
768
766
|
The JSON schema itself (type, title, description etc.) comes from the corresponding Pydantic models in the Python code.
|
|
@@ -2,36 +2,36 @@ fmu/__init__.py,sha256=YWSE22UTDSocfqQHxEeXrRkdlA63t_aayQVdJkycwYs,83
|
|
|
2
2
|
fmu/pem/__init__.py,sha256=FhNaFibcFZEK1Rz7IBRXBgxMHGNlcgrHaD4FtU4pDjs,421
|
|
3
3
|
fmu/pem/__main__.py,sha256=L8HW2P6_AQX70gcgmMdbW0BouCQdI0OVab-U4QkcPOk,1361
|
|
4
4
|
fmu/pem/run_pem.py,sha256=z6UAuEAJlSVcbTeC2q5q8_ToKe0HIgQPHZfjRWSg6PI,3218
|
|
5
|
-
fmu/pem/version.py,sha256=
|
|
5
|
+
fmu/pem/version.py,sha256=wO7XWlZte1hxA4mMvRc6zhNdGm74Nhhn2bfWRAxaKbI,511
|
|
6
6
|
fmu/pem/forward_models/__init__.py,sha256=WuN66rmgfqrekCy3Ss9NHqsy-lZ4WGY-4VZa3jYDV2s,117
|
|
7
7
|
fmu/pem/forward_models/pem_model.py,sha256=fKDD1-euwLBMr6rAjDYG0o-k7uKYRxG6De6bywF3AXI,2203
|
|
8
8
|
fmu/pem/hook_implementations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
fmu/pem/hook_implementations/jobs.py,sha256=IQW5m7oeZ_MiemxXv-UGLCPeo4h3QT9oELPlWAzsN60,388
|
|
10
10
|
fmu/pem/pem_functions/__init__.py,sha256=G2tu5E1PFvlw36fLdmr2A4Xr9ZqbKMf104OU5ydh_3A,539
|
|
11
|
-
fmu/pem/pem_functions/density.py,sha256=
|
|
12
|
-
fmu/pem/pem_functions/effective_pressure.py,sha256=
|
|
13
|
-
fmu/pem/pem_functions/estimate_saturated_rock.py,sha256=
|
|
14
|
-
fmu/pem/pem_functions/fluid_properties.py,sha256=
|
|
11
|
+
fmu/pem/pem_functions/density.py,sha256=eiAvZC7L3LvD0VuJYKSKY_M1WpqnxUS935-kKsT8NyA,1791
|
|
12
|
+
fmu/pem/pem_functions/effective_pressure.py,sha256=AoiMlBkCtERIZ3R0Nct07J7Rj5FaOEeEtS4u738KU-0,6141
|
|
13
|
+
fmu/pem/pem_functions/estimate_saturated_rock.py,sha256=upWTEZ_FCi45FoG4aW5vRdX58VoEDRFlo1ZTyokQpw0,2762
|
|
14
|
+
fmu/pem/pem_functions/fluid_properties.py,sha256=fRS922lwJxca4m12V9XOV5bSa2KaQzt6OQ2WLg0o9EE,10811
|
|
15
15
|
fmu/pem/pem_functions/mineral_properties.py,sha256=ilf1TwE4DDev65TdHEuudxgLj41IRBWgSjXghNrCPak,8394
|
|
16
|
-
fmu/pem/pem_functions/regression_models.py,sha256=
|
|
17
|
-
fmu/pem/pem_functions/run_friable_model.py,sha256=
|
|
18
|
-
fmu/pem/pem_functions/run_patchy_cement_model.py,sha256=
|
|
19
|
-
fmu/pem/pem_functions/run_t_matrix_and_pressure.py,sha256=
|
|
16
|
+
fmu/pem/pem_functions/regression_models.py,sha256=fNpeKzVDhFniMTlWlypfcPsEx-ztfueR1Cuo7EspJhk,9091
|
|
17
|
+
fmu/pem/pem_functions/run_friable_model.py,sha256=PzDZjJ3KDGUfwN0nht90lk8yM0RMXS1ZzwEztjdULzk,4274
|
|
18
|
+
fmu/pem/pem_functions/run_patchy_cement_model.py,sha256=A8pIu3fHujoc5U382WK2R_q4GcG8D1F3wHKBRsV7k8Y,4217
|
|
19
|
+
fmu/pem/pem_functions/run_t_matrix_and_pressure.py,sha256=GmGcEq38Oii2mI5tHBaJ7LutE67wKx6MyrtCMpdGWVk,6823
|
|
20
20
|
fmu/pem/pem_utilities/__init__.py,sha256=r9vSDgrY4B2NwBK5PL-4GMD5o1aT2IpR1vaeOCgJ-tI,1687
|
|
21
21
|
fmu/pem/pem_utilities/cumsum_properties.py,sha256=W2FtawYPZzdydGsVoOYCWIgOItNGKCRzhHzI5sUw05M,3769
|
|
22
22
|
fmu/pem/pem_utilities/delta_cumsum_time.py,sha256=J83PSpO2xjxHP5tpDpleVxynt0eytqHLgbGzj79wTQI,3267
|
|
23
|
-
fmu/pem/pem_utilities/enum_defs.py,sha256=
|
|
23
|
+
fmu/pem/pem_utilities/enum_defs.py,sha256=SFs_5lru3Br9xHqnUrWJyeJzLvE6JAAsI_MMaA6tjlU,1354
|
|
24
24
|
fmu/pem/pem_utilities/export_routines.py,sha256=gZa3AscefV9h55JQXL7juQg5SpMvhnN5nbBHFU2VtCI,9879
|
|
25
25
|
fmu/pem/pem_utilities/import_config.py,sha256=H6nH_MVnVhuS4q-4KWhoOvQbGzV5Jnw1-KlD1BRccmY,3232
|
|
26
|
-
fmu/pem/pem_utilities/import_routines.py,sha256=
|
|
26
|
+
fmu/pem/pem_utilities/import_routines.py,sha256=nj-yJYFlGno7p0k67SdjjQzUUbA92Bgfv5g7q9Nx8pA,5321
|
|
27
27
|
fmu/pem/pem_utilities/pem_class_definitions.py,sha256=IOwzHfMsoPoGWIs1-I-kI2wVbhgR5TjZHygXOMsECMk,3282
|
|
28
|
-
fmu/pem/pem_utilities/pem_config_validation.py,sha256=
|
|
29
|
-
fmu/pem/pem_utilities/rpm_models.py,sha256=
|
|
28
|
+
fmu/pem/pem_utilities/pem_config_validation.py,sha256=8pUDTx0aGGpqBuTJth0adDTvbrODIvKifXqWy0J_WRU,19111
|
|
29
|
+
fmu/pem/pem_utilities/rpm_models.py,sha256=UjqCGy-nbo2uPp-KlRdUmFRG9up2XVTlI8DplDTjIdU,6836
|
|
30
30
|
fmu/pem/pem_utilities/update_grid.py,sha256=InGaWrgIH5G9xyGqazAxQmVHL0nve4B9dAttisBsIpU,1775
|
|
31
31
|
fmu/pem/pem_utilities/utils.py,sha256=Gm_onSB7KuHZ0iKzx5TjHCOBYkatewSyEXgC9J-UA70,8871
|
|
32
|
-
fmu_pem-0.0.
|
|
33
|
-
fmu_pem-0.0.
|
|
34
|
-
fmu_pem-0.0.
|
|
35
|
-
fmu_pem-0.0.
|
|
36
|
-
fmu_pem-0.0.
|
|
37
|
-
fmu_pem-0.0.
|
|
32
|
+
fmu_pem-0.0.2.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
33
|
+
fmu_pem-0.0.2.dist-info/METADATA,sha256=bJh3W1AKoDPrC4-CPet8xAoVpOKRRVkebtW6Rw0wpqM,43839
|
|
34
|
+
fmu_pem-0.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
35
|
+
fmu_pem-0.0.2.dist-info/entry_points.txt,sha256=NSoFnob1PAE6FzRyNJZn5idocnCB1ptNg6R8zYf7Gnc,98
|
|
36
|
+
fmu_pem-0.0.2.dist-info/top_level.txt,sha256=Z-FIY3pxn0UK2Wxi9IJ7fKoLSraaxuNGi1eokiE0ShM,4
|
|
37
|
+
fmu_pem-0.0.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|