ert 20.0.0b0__py3-none-any.whl → 20.0.0b1__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.
- ert/cli/main.py +1 -2
- ert/config/_create_observation_dataframes.py +1 -0
- ert/config/_observations.py +44 -0
- ert/config/ert_config.py +5 -62
- ert/config/everest_response.py +2 -2
- ert/config/field.py +4 -4
- ert/config/rft_config.py +101 -28
- ert/dark_storage/endpoints/experiment_server.py +3 -5
- ert/ensemble_evaluator/config.py +1 -2
- ert/gui/simulation/experiment_panel.py +2 -9
- ert/gui/tools/manage_experiments/storage_widget.py +20 -18
- ert/plugins/plugin_manager.py +0 -4
- ert/run_models/ensemble_experiment.py +2 -7
- ert/run_models/ensemble_smoother.py +1 -7
- ert/run_models/everest_run_model.py +19 -10
- ert/run_models/manual_update.py +11 -5
- ert/run_models/multiple_data_assimilation.py +3 -14
- ert/scheduler/job.py +24 -4
- ert/shared/net_utils.py +18 -43
- ert/shared/version.py +3 -3
- ert/storage/local_ensemble.py +4 -1
- ert/storage/local_experiment.py +96 -95
- ert/storage/local_storage.py +10 -12
- ert/storage/migration/to24.py +26 -0
- ert/storage/migration/to25.py +91 -0
- {ert-20.0.0b0.dist-info → ert-20.0.0b1.dist-info}/METADATA +1 -1
- {ert-20.0.0b0.dist-info → ert-20.0.0b1.dist-info}/RECORD +31 -29
- {ert-20.0.0b0.dist-info → ert-20.0.0b1.dist-info}/WHEEL +0 -0
- {ert-20.0.0b0.dist-info → ert-20.0.0b1.dist-info}/entry_points.txt +0 -0
- {ert-20.0.0b0.dist-info → ert-20.0.0b1.dist-info}/licenses/COPYING +0 -0
- {ert-20.0.0b0.dist-info → ert-20.0.0b1.dist-info}/top_level.txt +0 -0
ert/cli/main.py
CHANGED
|
@@ -59,7 +59,7 @@ def run_cli(args: Namespace, runtime_plugins: ErtRuntimePlugins | None = None) -
|
|
|
59
59
|
f"Config contains forward model step {fm_step_name} {count} time(s)",
|
|
60
60
|
)
|
|
61
61
|
|
|
62
|
-
if not ert_config.
|
|
62
|
+
if not ert_config.observation_declarations and args.mode not in {
|
|
63
63
|
ENSEMBLE_EXPERIMENT_MODE,
|
|
64
64
|
TEST_RUN_MODE,
|
|
65
65
|
WORKFLOW_MODE,
|
|
@@ -133,7 +133,6 @@ def run_cli(args: Namespace, runtime_plugins: ErtRuntimePlugins | None = None) -
|
|
|
133
133
|
if args.port_range is None
|
|
134
134
|
else (min(args.port_range), max(args.port_range) + 1),
|
|
135
135
|
use_ipc_protocol=using_local_queuesystem,
|
|
136
|
-
prioritize_private_ip_address=ert_config.prioritize_private_ip_address,
|
|
137
136
|
)
|
|
138
137
|
|
|
139
138
|
if model.check_if_runpath_exists():
|
|
@@ -188,6 +188,7 @@ def _handle_rft_observation(
|
|
|
188
188
|
"east": pl.Series([location[0]], dtype=pl.Float32),
|
|
189
189
|
"north": pl.Series([location[1]], dtype=pl.Float32),
|
|
190
190
|
"tvd": pl.Series([location[2]], dtype=pl.Float32),
|
|
191
|
+
"zone": pl.Series([rft_observation.zone], dtype=pl.String),
|
|
191
192
|
"observations": pl.Series([rft_observation.value], dtype=pl.Float32),
|
|
192
193
|
"std": pl.Series([rft_observation.error], dtype=pl.Float32),
|
|
193
194
|
"radius": pl.Series([None], dtype=pl.Float32),
|
ert/config/_observations.py
CHANGED
|
@@ -329,6 +329,12 @@ class GeneralObservation(_GeneralObservation):
|
|
|
329
329
|
|
|
330
330
|
|
|
331
331
|
class RFTObservation(BaseModel):
|
|
332
|
+
"""Represents an RFT (Repeat Formation Tester) observation.
|
|
333
|
+
|
|
334
|
+
RFT observations are used to condition on pressure, saturation, or other
|
|
335
|
+
properties measured at specific well locations and times.
|
|
336
|
+
"""
|
|
337
|
+
|
|
332
338
|
type: Literal["rft_observation"] = "rft_observation"
|
|
333
339
|
name: str
|
|
334
340
|
well: str
|
|
@@ -349,6 +355,26 @@ class RFTObservation(BaseModel):
|
|
|
349
355
|
filename: str,
|
|
350
356
|
observed_property: str = "PRESSURE",
|
|
351
357
|
) -> list[Self]:
|
|
358
|
+
"""Create RFT observations from a CSV file.
|
|
359
|
+
|
|
360
|
+
The CSV file must contain the following columns: WELL_NAME, DATE,
|
|
361
|
+
ERROR, NORTH, EAST, TVD, and a column for the observed property
|
|
362
|
+
(e.g., PRESSURE, SWAT). An optional ZONE column may also be present.
|
|
363
|
+
|
|
364
|
+
Args:
|
|
365
|
+
directory: Base directory for resolving relative file paths.
|
|
366
|
+
observation_dict: Dictionary containing the observation configuration.
|
|
367
|
+
filename: Path to the CSV file containing RFT observations.
|
|
368
|
+
observed_property: Property to observe (default: PRESSURE).
|
|
369
|
+
|
|
370
|
+
Returns:
|
|
371
|
+
List of RFTObservation instances created from the CSV file.
|
|
372
|
+
|
|
373
|
+
Raises:
|
|
374
|
+
ObservationConfigError: If the file is missing, inaccessible,
|
|
375
|
+
lacks required columns, or contains invalid observation values
|
|
376
|
+
(value=-1 and error=0).
|
|
377
|
+
"""
|
|
352
378
|
if not os.path.isabs(filename):
|
|
353
379
|
filename = os.path.join(directory, filename)
|
|
354
380
|
if not os.path.exists(filename):
|
|
@@ -425,6 +451,24 @@ class RFTObservation(BaseModel):
|
|
|
425
451
|
def from_obs_dict(
|
|
426
452
|
cls, directory: str, observation_dict: ObservationDict
|
|
427
453
|
) -> list[Self]:
|
|
454
|
+
"""Create RFT observations from an observation dictionary.
|
|
455
|
+
|
|
456
|
+
Supports two modes:
|
|
457
|
+
1. CSV mode: Load observations from a CSV file specified by the CSV key.
|
|
458
|
+
2. Direct mode: Create a single observation from individual keys
|
|
459
|
+
(WELL, PROPERTY, VALUE, ERROR, DATE, NORTH, EAST, TVD, ZONE).
|
|
460
|
+
|
|
461
|
+
Args:
|
|
462
|
+
directory: Base directory for resolving relative file paths.
|
|
463
|
+
observation_dict: Dictionary containing the observation configuration.
|
|
464
|
+
|
|
465
|
+
Returns:
|
|
466
|
+
List of RFTObservation instances. Returns multiple observations when
|
|
467
|
+
loading from CSV, or a single observation when using direct mode.
|
|
468
|
+
|
|
469
|
+
Raises:
|
|
470
|
+
ObservationConfigError: If required keys are missing or invalid.
|
|
471
|
+
"""
|
|
428
472
|
csv_filename = None
|
|
429
473
|
well = None
|
|
430
474
|
observed_property = None
|
ert/config/ert_config.py
CHANGED
|
@@ -12,14 +12,13 @@ from os import path
|
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
from typing import TYPE_CHECKING, Any, ClassVar, Self, cast, overload
|
|
14
14
|
|
|
15
|
-
import polars as pl
|
|
16
15
|
from numpy.random import SeedSequence
|
|
17
|
-
from pydantic import BaseModel, Field,
|
|
16
|
+
from pydantic import BaseModel, Field, model_validator
|
|
18
17
|
from pydantic import ValidationError as PydanticValidationError
|
|
19
18
|
|
|
19
|
+
from ert.config._create_observation_dataframes import create_observation_dataframes
|
|
20
20
|
from ert.substitutions import Substitutions
|
|
21
21
|
|
|
22
|
-
from ._create_observation_dataframes import create_observation_dataframes
|
|
23
22
|
from ._design_matrix_validator import DesignMatrixValidator
|
|
24
23
|
from ._observations import (
|
|
25
24
|
GeneralObservation,
|
|
@@ -689,7 +688,6 @@ class ErtConfig(BaseModel):
|
|
|
689
688
|
QUEUE_OPTIONS: ClassVar[KnownQueueOptions | None] = None
|
|
690
689
|
RESERVED_KEYWORDS: ClassVar[list[str]] = RESERVED_KEYWORDS
|
|
691
690
|
ENV_VARS: ClassVar[dict[str, str]] = {}
|
|
692
|
-
PRIORITIZE_PRIVATE_IP_ADDRESS: ClassVar[bool] = False
|
|
693
691
|
|
|
694
692
|
substitutions: dict[str, str] = Field(default_factory=dict)
|
|
695
693
|
ensemble_config: EnsembleConfig = Field(default_factory=EnsembleConfig)
|
|
@@ -704,7 +702,6 @@ class ErtConfig(BaseModel):
|
|
|
704
702
|
default_factory=lambda: defaultdict(lambda: cast(list[Workflow], []))
|
|
705
703
|
)
|
|
706
704
|
runpath_file: Path = Path(DEFAULT_RUNPATH_FILE)
|
|
707
|
-
prioritize_private_ip_address: bool = False
|
|
708
705
|
|
|
709
706
|
ert_templates: list[tuple[str, str]] = Field(default_factory=list)
|
|
710
707
|
|
|
@@ -714,32 +711,6 @@ class ErtConfig(BaseModel):
|
|
|
714
711
|
config_path: str = Field(init=False, default="")
|
|
715
712
|
observation_declarations: list[Observation] = Field(default_factory=list)
|
|
716
713
|
zonemap: dict[int, list[str]] = Field(default_factory=dict)
|
|
717
|
-
_observations: dict[str, pl.DataFrame] | None = PrivateAttr(None)
|
|
718
|
-
|
|
719
|
-
@property
|
|
720
|
-
def observations(self) -> dict[str, pl.DataFrame]:
|
|
721
|
-
if self._observations is None:
|
|
722
|
-
has_rft_observations = any(
|
|
723
|
-
isinstance(o, RFTObservation) for o in self.observation_declarations
|
|
724
|
-
)
|
|
725
|
-
if (
|
|
726
|
-
has_rft_observations
|
|
727
|
-
and "rft" not in self.ensemble_config.response_configs
|
|
728
|
-
):
|
|
729
|
-
self.ensemble_config.response_configs["rft"] = RFTConfig(
|
|
730
|
-
input_files=[self.runpath_config.eclbase_format_string],
|
|
731
|
-
data_to_read={},
|
|
732
|
-
locations=[],
|
|
733
|
-
zonemap=self.zonemap,
|
|
734
|
-
)
|
|
735
|
-
self._observations = create_observation_dataframes(
|
|
736
|
-
self.observation_declarations,
|
|
737
|
-
cast(
|
|
738
|
-
RFTConfig | None,
|
|
739
|
-
self.ensemble_config.response_configs.get("rft", None),
|
|
740
|
-
),
|
|
741
|
-
)
|
|
742
|
-
return self._observations
|
|
743
714
|
|
|
744
715
|
@model_validator(mode="after")
|
|
745
716
|
def set_fields(self) -> Self:
|
|
@@ -834,28 +805,6 @@ class ErtConfig(BaseModel):
|
|
|
834
805
|
|
|
835
806
|
return self
|
|
836
807
|
|
|
837
|
-
def __eq__(self, other: object) -> bool:
|
|
838
|
-
if not isinstance(other, ErtConfig):
|
|
839
|
-
return False
|
|
840
|
-
|
|
841
|
-
for attr in vars(self):
|
|
842
|
-
if attr == "observations":
|
|
843
|
-
if self.observations.keys() != other.observations.keys():
|
|
844
|
-
return False
|
|
845
|
-
|
|
846
|
-
if not all(
|
|
847
|
-
self.observations[k].equals(other.observations[k])
|
|
848
|
-
for k in self.observations
|
|
849
|
-
):
|
|
850
|
-
return False
|
|
851
|
-
|
|
852
|
-
continue
|
|
853
|
-
|
|
854
|
-
if getattr(self, attr) != getattr(other, attr):
|
|
855
|
-
return False
|
|
856
|
-
|
|
857
|
-
return True
|
|
858
|
-
|
|
859
808
|
@staticmethod
|
|
860
809
|
def with_plugins(runtime_plugins: ErtRuntimePlugins) -> type[ErtConfig]:
|
|
861
810
|
class ErtConfigWithPlugins(ErtConfig):
|
|
@@ -870,9 +819,6 @@ class ErtConfig(BaseModel):
|
|
|
870
819
|
)
|
|
871
820
|
ENV_VARS = dict(runtime_plugins.environment_variables)
|
|
872
821
|
QUEUE_OPTIONS = runtime_plugins.queue_options
|
|
873
|
-
PRIORITIZE_PRIVATE_IP_ADDRESS = (
|
|
874
|
-
runtime_plugins.prioritize_private_ip_address
|
|
875
|
-
)
|
|
876
822
|
|
|
877
823
|
ErtConfigWithPlugins.model_rebuild()
|
|
878
824
|
assert issubclass(ErtConfigWithPlugins, ErtConfig)
|
|
@@ -1137,7 +1083,6 @@ class ErtConfig(BaseModel):
|
|
|
1137
1083
|
runpath_config=model_config,
|
|
1138
1084
|
user_config_file=config_file_path,
|
|
1139
1085
|
observation_declarations=list(obs_configs),
|
|
1140
|
-
prioritize_private_ip_address=cls.PRIORITIZE_PRIVATE_IP_ADDRESS,
|
|
1141
1086
|
zonemap=config_dict.get(ConfigKeys.ZONEMAP, ("", {}))[1],
|
|
1142
1087
|
)
|
|
1143
1088
|
|
|
@@ -1157,12 +1102,10 @@ class ErtConfig(BaseModel):
|
|
|
1157
1102
|
|
|
1158
1103
|
# PS:
|
|
1159
1104
|
# This mutates the rft config and is necessary for the moment
|
|
1160
|
-
|
|
1105
|
+
# Consider changing this pattern
|
|
1106
|
+
_ = create_observation_dataframes(
|
|
1161
1107
|
obs_configs,
|
|
1162
|
-
cast(
|
|
1163
|
-
RFTConfig | None,
|
|
1164
|
-
ensemble_config.response_configs.get("rft", None),
|
|
1165
|
-
),
|
|
1108
|
+
cast(RFTConfig | None, ensemble_config.response_configs.get("rft")),
|
|
1166
1109
|
)
|
|
1167
1110
|
except PydanticValidationError as err:
|
|
1168
1111
|
raise ConfigValidationError.from_pydantic(err) from err
|
ert/config/everest_response.py
CHANGED
|
@@ -79,8 +79,8 @@ class EverestResponse(ResponseConfig):
|
|
|
79
79
|
class EverestConstraintsConfig(EverestResponse):
|
|
80
80
|
type: Literal["everest_constraints"] = "everest_constraints"
|
|
81
81
|
targets: list[float | None]
|
|
82
|
-
upper_bounds: list[float]
|
|
83
|
-
lower_bounds: list[float]
|
|
82
|
+
upper_bounds: list[float | None]
|
|
83
|
+
lower_bounds: list[float | None]
|
|
84
84
|
|
|
85
85
|
|
|
86
86
|
responses_index.add_response_type(EverestConstraintsConfig)
|
ert/config/field.py
CHANGED
|
@@ -70,10 +70,10 @@ class Field(ParameterConfig):
|
|
|
70
70
|
dimensionality: Literal[3] = 3
|
|
71
71
|
ertbox_params: ErtboxParameters
|
|
72
72
|
file_format: FieldFileFormat
|
|
73
|
-
output_transformation: str | None
|
|
74
|
-
input_transformation: str | None
|
|
75
|
-
truncation_min: float | None
|
|
76
|
-
truncation_max: float | None
|
|
73
|
+
output_transformation: str | None = None
|
|
74
|
+
input_transformation: str | None = None
|
|
75
|
+
truncation_min: float | None = None
|
|
76
|
+
truncation_max: float | None = None
|
|
77
77
|
forward_init_file: str
|
|
78
78
|
output_file: Path
|
|
79
79
|
grid_file: str
|
ert/config/rft_config.py
CHANGED
|
@@ -7,7 +7,8 @@ import os
|
|
|
7
7
|
import re
|
|
8
8
|
import warnings
|
|
9
9
|
from collections import defaultdict
|
|
10
|
-
from
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from typing import IO, Any, Literal, TypeAlias, cast
|
|
11
12
|
|
|
12
13
|
import numpy as np
|
|
13
14
|
import numpy.typing as npt
|
|
@@ -25,19 +26,60 @@ from .responses_index import responses_index
|
|
|
25
26
|
logger = logging.getLogger(__name__)
|
|
26
27
|
|
|
27
28
|
|
|
29
|
+
# A Point in UTM/TVD coordinates
|
|
28
30
|
Point: TypeAlias = tuple[float, float, float]
|
|
31
|
+
# Index to a cell in a grid
|
|
29
32
|
GridIndex: TypeAlias = tuple[int, int, int]
|
|
30
33
|
ZoneName: TypeAlias = str
|
|
34
|
+
WellName: TypeAlias = str
|
|
35
|
+
DateString: TypeAlias = str
|
|
36
|
+
RFTProperty: TypeAlias = str
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass(frozen=True)
|
|
40
|
+
class _ZonedPoint:
|
|
41
|
+
"""A point optionally constrained to be in a given zone."""
|
|
42
|
+
|
|
43
|
+
point: tuple[float | None, float | None, float | None] = (None, None, None)
|
|
44
|
+
zone_name: ZoneName | None = None
|
|
45
|
+
|
|
46
|
+
def has_zone(self) -> bool:
|
|
47
|
+
return self.zone_name is not None
|
|
31
48
|
|
|
32
49
|
|
|
33
50
|
class RFTConfig(ResponseConfig):
|
|
51
|
+
""":term:`RFT` response from a :term:`reservoir simulator`.
|
|
52
|
+
|
|
53
|
+
RFTConfig is the configuration of responses in the <RUNPATH>/<ECLBASE>.RFT
|
|
54
|
+
file which may be generated from a reservoir simulator forward model step.
|
|
55
|
+
|
|
56
|
+
The file contains values for grid cells along a wellpath (see RFTReader for
|
|
57
|
+
details). RFTConfig will match the values against the given :term:`UTM`/:term:`TVD`
|
|
58
|
+
locations.
|
|
59
|
+
|
|
60
|
+
Parameters:
|
|
61
|
+
data_to_read: dictionary of the values that should be read from the rft file.
|
|
62
|
+
loations: list of optionally zone constrained points that the rft values should
|
|
63
|
+
be labeled with.
|
|
64
|
+
zonemap: The mapping from grid layer index to zone name.
|
|
65
|
+
"""
|
|
66
|
+
|
|
34
67
|
type: Literal["rft"] = "rft"
|
|
35
68
|
name: str = "rft"
|
|
36
69
|
has_finalized_keys: bool = False
|
|
37
|
-
data_to_read: dict[
|
|
70
|
+
data_to_read: dict[WellName, dict[DateString, list[RFTProperty]]] = Field(
|
|
71
|
+
default_factory=dict
|
|
72
|
+
)
|
|
38
73
|
locations: list[Point | tuple[Point, ZoneName]] = Field(default_factory=list)
|
|
39
74
|
zonemap: dict[int, list[ZoneName]] = Field(default_factory=dict)
|
|
40
75
|
|
|
76
|
+
@property
|
|
77
|
+
def _zoned_locations(self) -> list[_ZonedPoint]:
|
|
78
|
+
return [
|
|
79
|
+
_ZonedPoint(*p) if isinstance(p[1], ZoneName) else _ZonedPoint(p)
|
|
80
|
+
for p in self.locations
|
|
81
|
+
]
|
|
82
|
+
|
|
41
83
|
@property
|
|
42
84
|
def expected_input_files(self) -> list[str]:
|
|
43
85
|
base = self.input_files[0]
|
|
@@ -51,13 +93,13 @@ class RFTConfig(ResponseConfig):
|
|
|
51
93
|
|
|
52
94
|
def _find_indices(
|
|
53
95
|
self, egrid_file: str | os.PathLike[str] | IO[Any]
|
|
54
|
-
) -> dict[GridIndex | None, set[
|
|
96
|
+
) -> dict[GridIndex | None, set[_ZonedPoint]]:
|
|
55
97
|
indices = defaultdict(set)
|
|
56
98
|
for a, b in zip(
|
|
57
99
|
CornerpointGrid.read_egrid(egrid_file).find_cell_containing_point(
|
|
58
|
-
[
|
|
100
|
+
[cast(Point, loc.point) for loc in self._zoned_locations]
|
|
59
101
|
),
|
|
60
|
-
self.
|
|
102
|
+
self._zoned_locations,
|
|
61
103
|
strict=True,
|
|
62
104
|
):
|
|
63
105
|
indices[a].add(b)
|
|
@@ -65,20 +107,20 @@ class RFTConfig(ResponseConfig):
|
|
|
65
107
|
|
|
66
108
|
def _filter_zones(
|
|
67
109
|
self,
|
|
68
|
-
indices: dict[GridIndex | None, set[
|
|
110
|
+
indices: dict[GridIndex | None, set[_ZonedPoint]],
|
|
69
111
|
iens: int,
|
|
70
112
|
iter_: int,
|
|
71
|
-
) -> dict[GridIndex | None, set[
|
|
113
|
+
) -> dict[GridIndex | None, set[_ZonedPoint]]:
|
|
72
114
|
for idx, locs in indices.items():
|
|
73
115
|
if idx is not None:
|
|
74
116
|
for loc in list(locs):
|
|
75
|
-
if
|
|
76
|
-
zone = loc
|
|
117
|
+
if loc.has_zone():
|
|
118
|
+
zone = cast(ZoneName, loc.zone_name)
|
|
77
119
|
# zonemap is 1-indexed so +1
|
|
78
120
|
if zone not in self.zonemap.get(idx[-1] + 1, []):
|
|
79
121
|
warnings.warn(
|
|
80
122
|
PostSimulationWarning(
|
|
81
|
-
f"An RFT observation with location {loc
|
|
123
|
+
f"An RFT observation with location {loc.point}, "
|
|
82
124
|
f"in iteration {iter_}, realization {iens} did "
|
|
83
125
|
f"not match expected zone {zone}. The observation "
|
|
84
126
|
"was deactivated",
|
|
@@ -86,12 +128,21 @@ class RFTConfig(ResponseConfig):
|
|
|
86
128
|
stacklevel=2,
|
|
87
129
|
)
|
|
88
130
|
locs.remove(loc)
|
|
89
|
-
return
|
|
90
|
-
k: {v[0] if isinstance(v[1], str) else v for v in vs}
|
|
91
|
-
for k, vs in indices.items()
|
|
92
|
-
}
|
|
131
|
+
return indices
|
|
93
132
|
|
|
94
133
|
def read_from_file(self, run_path: str, iens: int, iter_: int) -> pl.DataFrame:
|
|
134
|
+
"""Reads the RFT values from <RUNPATH>/<ECLBASE>.RFT
|
|
135
|
+
|
|
136
|
+
Also labels those values by which optionally zone constrained point
|
|
137
|
+
it belongs to.
|
|
138
|
+
|
|
139
|
+
The columns east, north, tvd is none when the value does not belong to
|
|
140
|
+
any point, otherwise it is the x,y,z values of that point. If the point
|
|
141
|
+
is constrained to be in a certain zone then the zone column is also populated.
|
|
142
|
+
|
|
143
|
+
Points which were constrained to be in a given zone, but were not contained
|
|
144
|
+
in that zone, is not labeled, and instead a warning is emitted.
|
|
145
|
+
"""
|
|
95
146
|
filename = substitute_runpath_name(self.input_files[0], iens, iter_)
|
|
96
147
|
if filename.upper().endswith(".DATA"):
|
|
97
148
|
# For backwards compatibility, it is
|
|
@@ -102,9 +153,9 @@ class RFTConfig(ResponseConfig):
|
|
|
102
153
|
if grid_filename.upper().endswith(".RFT"):
|
|
103
154
|
grid_filename = grid_filename[:-4]
|
|
104
155
|
grid_filename += ".EGRID"
|
|
105
|
-
fetched: dict[
|
|
106
|
-
|
|
107
|
-
)
|
|
156
|
+
fetched: dict[
|
|
157
|
+
tuple[WellName, datetime.date], dict[RFTProperty, npt.NDArray[np.float32]]
|
|
158
|
+
] = defaultdict(dict)
|
|
108
159
|
indices = {}
|
|
109
160
|
if self.locations:
|
|
110
161
|
indices = self._filter_zones(self._find_indices(grid_filename), iens, iter_)
|
|
@@ -123,7 +174,10 @@ class RFTConfig(ResponseConfig):
|
|
|
123
174
|
"time": [],
|
|
124
175
|
"depth": [],
|
|
125
176
|
"values": [],
|
|
126
|
-
"
|
|
177
|
+
"east": [],
|
|
178
|
+
"north": [],
|
|
179
|
+
"tvd": [],
|
|
180
|
+
"zone": [],
|
|
127
181
|
}
|
|
128
182
|
)
|
|
129
183
|
|
|
@@ -170,7 +224,7 @@ class RFTConfig(ResponseConfig):
|
|
|
170
224
|
list(
|
|
171
225
|
indices.get(
|
|
172
226
|
(c[0] - 1, c[1] - 1, c[2] - 1),
|
|
173
|
-
[(
|
|
227
|
+
[_ZonedPoint()],
|
|
174
228
|
)
|
|
175
229
|
)
|
|
176
230
|
for c in entry.connections
|
|
@@ -189,7 +243,10 @@ class RFTConfig(ResponseConfig):
|
|
|
189
243
|
"time": [],
|
|
190
244
|
"depth": [],
|
|
191
245
|
"values": [],
|
|
192
|
-
"
|
|
246
|
+
"east": [],
|
|
247
|
+
"north": [],
|
|
248
|
+
"tvd": [],
|
|
249
|
+
"zone": [],
|
|
193
250
|
}
|
|
194
251
|
)
|
|
195
252
|
|
|
@@ -204,18 +261,34 @@ class RFTConfig(ResponseConfig):
|
|
|
204
261
|
"values": [vals],
|
|
205
262
|
"location": pl.Series(
|
|
206
263
|
[
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
264
|
+
[
|
|
265
|
+
[loc.point for loc in locs]
|
|
266
|
+
for locs in locations.get(
|
|
267
|
+
(well, time),
|
|
268
|
+
[[_ZonedPoint()]] * len(vals),
|
|
269
|
+
)
|
|
270
|
+
]
|
|
210
271
|
],
|
|
211
272
|
dtype=pl.Array(
|
|
212
273
|
pl.List(pl.Array(pl.Float32, 3)), len(vals)
|
|
213
274
|
),
|
|
214
275
|
),
|
|
276
|
+
"zone": pl.Series(
|
|
277
|
+
[
|
|
278
|
+
[
|
|
279
|
+
[loc.zone_name for loc in locs]
|
|
280
|
+
for locs in locations.get(
|
|
281
|
+
(well, time),
|
|
282
|
+
[[_ZonedPoint()]] * len(vals),
|
|
283
|
+
)
|
|
284
|
+
]
|
|
285
|
+
],
|
|
286
|
+
dtype=pl.Array(pl.List(pl.String), len(vals)),
|
|
287
|
+
),
|
|
215
288
|
}
|
|
216
289
|
)
|
|
217
|
-
.explode("depth", "values", "location")
|
|
218
|
-
.explode("location")
|
|
290
|
+
.explode("depth", "values", "location", "zone")
|
|
291
|
+
.explode("location", "zone")
|
|
219
292
|
for (well, time), inner_dict in fetched.items()
|
|
220
293
|
for prop, vals in inner_dict.items()
|
|
221
294
|
if prop != "DEPTH" and len(vals) > 0
|
|
@@ -238,7 +311,7 @@ class RFTConfig(ResponseConfig):
|
|
|
238
311
|
|
|
239
312
|
@property
|
|
240
313
|
def primary_key(self) -> list[str]:
|
|
241
|
-
return ["east", "north", "tvd"]
|
|
314
|
+
return ["east", "north", "tvd", "zone"]
|
|
242
315
|
|
|
243
316
|
@classmethod
|
|
244
317
|
def from_config_dict(cls, config_dict: ConfigDict) -> RFTConfig | None:
|
|
@@ -260,8 +333,8 @@ class RFTConfig(ResponseConfig):
|
|
|
260
333
|
"step known to generate rft files"
|
|
261
334
|
)
|
|
262
335
|
|
|
263
|
-
declared_data: dict[
|
|
264
|
-
lambda: defaultdict(list)
|
|
336
|
+
declared_data: dict[WellName, dict[datetime.date, list[RFTProperty]]] = (
|
|
337
|
+
defaultdict(lambda: defaultdict(list))
|
|
265
338
|
)
|
|
266
339
|
for rft in rfts:
|
|
267
340
|
for expected in ["WELL", "DATE", "PROPERTIES"]:
|
|
@@ -317,11 +317,9 @@ class ExperimentRunner:
|
|
|
317
317
|
simulation_future = loop.run_in_executor(
|
|
318
318
|
None,
|
|
319
319
|
lambda: run_model.start_simulations_thread(
|
|
320
|
-
EvaluatorServerConfig(
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
prioritize_private_ip_address=site_plugins.prioritize_private_ip_address,
|
|
324
|
-
)
|
|
320
|
+
EvaluatorServerConfig()
|
|
321
|
+
if run_model.queue_config.queue_system == QueueSystem.LOCAL
|
|
322
|
+
else EvaluatorServerConfig(use_ipc_protocol=False)
|
|
325
323
|
),
|
|
326
324
|
)
|
|
327
325
|
while True:
|
ert/ensemble_evaluator/config.py
CHANGED
|
@@ -27,7 +27,6 @@ class EvaluatorServerConfig:
|
|
|
27
27
|
use_token: bool = True,
|
|
28
28
|
host: str | None = None,
|
|
29
29
|
use_ipc_protocol: bool = True,
|
|
30
|
-
prioritize_private_ip_address: bool = False,
|
|
31
30
|
) -> None:
|
|
32
31
|
self.host: str | None = host
|
|
33
32
|
self.router_port: int | None = None
|
|
@@ -51,7 +50,7 @@ class EvaluatorServerConfig:
|
|
|
51
50
|
if use_ipc_protocol:
|
|
52
51
|
self.uri = f"ipc:///tmp/socket-{uuid.uuid4().hex[:8]}"
|
|
53
52
|
elif self.host is None:
|
|
54
|
-
self.host = get_ip_address(
|
|
53
|
+
self.host = get_ip_address()
|
|
55
54
|
|
|
56
55
|
if use_token:
|
|
57
56
|
self.server_public_key, self.server_secret_key = zmq.curve_keypair()
|
|
@@ -55,15 +55,9 @@ def create_md_table(kv: dict[str, str], output: str) -> str:
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
def get_simulation_thread(
|
|
58
|
-
model: Any,
|
|
59
|
-
rerun_failed_realizations: bool = False,
|
|
60
|
-
use_ipc_protocol: bool = False,
|
|
61
|
-
prioritize_private_ip_address: bool = False,
|
|
58
|
+
model: Any, rerun_failed_realizations: bool = False, use_ipc_protocol: bool = False
|
|
62
59
|
) -> ErtThread:
|
|
63
|
-
evaluator_server_config = EvaluatorServerConfig(
|
|
64
|
-
use_ipc_protocol=use_ipc_protocol,
|
|
65
|
-
prioritize_private_ip_address=prioritize_private_ip_address,
|
|
66
|
-
)
|
|
60
|
+
evaluator_server_config = EvaluatorServerConfig(use_ipc_protocol=use_ipc_protocol)
|
|
67
61
|
|
|
68
62
|
def run() -> None:
|
|
69
63
|
model.api.start_simulations_thread(
|
|
@@ -387,7 +381,6 @@ class ExperimentPanel(QWidget):
|
|
|
387
381
|
rerun_failed_realizations,
|
|
388
382
|
use_ipc_protocol=self.config.queue_config.queue_system
|
|
389
383
|
== QueueSystem.LOCAL,
|
|
390
|
-
prioritize_private_ip_address=self.config.prioritize_private_ip_address,
|
|
391
384
|
)
|
|
392
385
|
self._dialog.setup_event_monitoring(rerun_failed_realizations)
|
|
393
386
|
simulation_thread.start()
|
|
@@ -22,8 +22,7 @@ from PyQt6.QtWidgets import (
|
|
|
22
22
|
QWidget,
|
|
23
23
|
)
|
|
24
24
|
|
|
25
|
-
from ert.config import ErrorInfo, ErtConfig
|
|
26
|
-
from ert.config._create_observation_dataframes import create_observation_dataframes
|
|
25
|
+
from ert.config import ErrorInfo, ErtConfig
|
|
27
26
|
from ert.gui.ertnotifier import ErtNotifier
|
|
28
27
|
from ert.gui.ertwidgets import CreateExperimentDialog, Suggestor
|
|
29
28
|
from ert.storage import Ensemble, Experiment
|
|
@@ -170,26 +169,29 @@ class StorageWidget(QWidget):
|
|
|
170
169
|
if create_experiment_dialog.exec():
|
|
171
170
|
try:
|
|
172
171
|
with self._notifier.write_storage() as storage:
|
|
173
|
-
|
|
174
|
-
self._ert_config.
|
|
172
|
+
parameter_configuration = (
|
|
173
|
+
self._ert_config.parameter_configurations_with_design_matrix
|
|
175
174
|
)
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
(r for r in response_configs if r.type == "rft"),
|
|
179
|
-
None,
|
|
175
|
+
response_configuration = (
|
|
176
|
+
self._ert_config.ensemble_config.response_configuration
|
|
180
177
|
)
|
|
181
|
-
if rft_config is not None:
|
|
182
|
-
rft_config = cast(RFTConfig, rft_config)
|
|
183
|
-
|
|
184
178
|
ensemble = storage.create_experiment(
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
179
|
+
experiment_config={
|
|
180
|
+
"parameter_configuration": [
|
|
181
|
+
c.model_dump(mode="json")
|
|
182
|
+
for c in parameter_configuration
|
|
183
|
+
],
|
|
184
|
+
"response_configuration": [
|
|
185
|
+
c.model_dump(mode="json")
|
|
186
|
+
for c in response_configuration
|
|
187
|
+
],
|
|
188
|
+
"observations": [
|
|
189
|
+
d.model_dump(mode="json")
|
|
190
|
+
for d in self._ert_config.observation_declarations
|
|
191
|
+
],
|
|
192
|
+
"ert_templates": self._ert_config.ert_templates,
|
|
193
|
+
},
|
|
191
194
|
name=create_experiment_dialog.experiment_name,
|
|
192
|
-
templates=self._ert_config.ert_templates,
|
|
193
195
|
).create_ensemble(
|
|
194
196
|
name=create_experiment_dialog.ensemble_name,
|
|
195
197
|
ensemble_size=self._ensemble_size,
|
ert/plugins/plugin_manager.py
CHANGED
|
@@ -338,7 +338,6 @@ class ErtRuntimePlugins(BaseModel):
|
|
|
338
338
|
environment_variables: Mapping[str, str] = Field(default_factory=dict)
|
|
339
339
|
env_pr_fm_step: Mapping[str, Mapping[str, Any]] = Field(default_factory=dict)
|
|
340
340
|
help_links: dict[str, str] = Field(default_factory=dict)
|
|
341
|
-
prioritize_private_ip_address: bool = False
|
|
342
341
|
|
|
343
342
|
|
|
344
343
|
def get_site_plugins(
|
|
@@ -387,9 +386,6 @@ def get_site_plugins(
|
|
|
387
386
|
),
|
|
388
387
|
env_pr_fm_step=plugin_manager.get_forward_model_configuration(),
|
|
389
388
|
help_links=plugin_manager.get_help_links(),
|
|
390
|
-
prioritize_private_ip_address=site_configurations.prioritize_private_ip_address
|
|
391
|
-
if site_configurations
|
|
392
|
-
else False,
|
|
393
389
|
)
|
|
394
390
|
|
|
395
391
|
return runtime_plugins
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from typing import ClassVar
|
|
4
|
+
from typing import ClassVar
|
|
5
5
|
from uuid import UUID
|
|
6
6
|
|
|
7
7
|
from pydantic import PrivateAttr
|
|
8
8
|
|
|
9
9
|
from ert.config import (
|
|
10
|
-
ParameterConfig,
|
|
11
10
|
PostExperimentFixtures,
|
|
12
11
|
PreExperimentFixtures,
|
|
13
|
-
ResponseConfig,
|
|
14
12
|
)
|
|
15
13
|
from ert.ensemble_evaluator import EvaluatorServerConfig
|
|
16
14
|
from ert.run_models.initial_ensemble_run_model import (
|
|
@@ -54,11 +52,8 @@ class EnsembleExperiment(InitialEnsembleRunModel, EnsembleExperimentConfig):
|
|
|
54
52
|
self.run_workflows(fixtures=PreExperimentFixtures(random_seed=self.random_seed))
|
|
55
53
|
|
|
56
54
|
experiment_storage = self._storage.create_experiment(
|
|
57
|
-
|
|
58
|
-
observations=self.observation_dataframes(),
|
|
59
|
-
responses=cast(list[ResponseConfig], self.response_configuration),
|
|
55
|
+
experiment_config=self.model_dump(mode="json"),
|
|
60
56
|
name=self.experiment_name,
|
|
61
|
-
templates=self.ert_templates,
|
|
62
57
|
)
|
|
63
58
|
|
|
64
59
|
ensemble_storage = self._storage.create_ensemble(
|