ert 18.0.9__py3-none-any.whl → 19.0.0rc0__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/forward_model_runner/client.py +6 -2
- ert/__main__.py +20 -6
- ert/analysis/_es_update.py +6 -19
- ert/cli/main.py +7 -3
- ert/config/__init__.py +3 -4
- ert/config/_create_observation_dataframes.py +57 -8
- ert/config/_get_num_cpu.py +1 -1
- ert/config/_observations.py +77 -1
- ert/config/distribution.py +1 -1
- ert/config/ensemble_config.py +3 -3
- ert/config/ert_config.py +50 -8
- ert/config/{ext_param_config.py → everest_control.py} +8 -12
- ert/config/everest_response.py +3 -5
- ert/config/field.py +76 -14
- ert/config/forward_model_step.py +12 -9
- ert/config/gen_data_config.py +3 -4
- ert/config/gen_kw_config.py +2 -12
- ert/config/parameter_config.py +1 -16
- ert/config/parsing/_option_dict.py +10 -2
- ert/config/parsing/config_keywords.py +1 -0
- ert/config/parsing/config_schema.py +8 -0
- ert/config/parsing/config_schema_deprecations.py +14 -3
- ert/config/parsing/config_schema_item.py +12 -3
- ert/config/parsing/context_values.py +3 -3
- ert/config/parsing/file_context_token.py +1 -1
- ert/config/parsing/observations_parser.py +6 -2
- ert/config/parsing/queue_system.py +9 -0
- ert/config/queue_config.py +0 -1
- ert/config/response_config.py +0 -1
- ert/config/rft_config.py +78 -33
- ert/config/summary_config.py +1 -2
- ert/config/surface_config.py +59 -16
- ert/dark_storage/common.py +1 -1
- ert/dark_storage/compute/misfits.py +4 -1
- ert/dark_storage/endpoints/compute/misfits.py +4 -2
- ert/dark_storage/endpoints/experiment_server.py +12 -9
- ert/dark_storage/endpoints/experiments.py +2 -2
- ert/dark_storage/endpoints/observations.py +4 -2
- ert/dark_storage/endpoints/parameters.py +2 -18
- ert/dark_storage/endpoints/responses.py +10 -5
- ert/dark_storage/json_schema/experiment.py +1 -1
- ert/data/_measured_data.py +6 -5
- ert/ensemble_evaluator/config.py +2 -1
- ert/field_utils/field_utils.py +1 -1
- ert/field_utils/grdecl_io.py +9 -26
- ert/field_utils/roff_io.py +1 -1
- ert/gui/__init__.py +5 -2
- ert/gui/ertnotifier.py +1 -1
- ert/gui/ertwidgets/pathchooser.py +0 -3
- ert/gui/ertwidgets/suggestor/suggestor.py +63 -30
- ert/gui/main.py +27 -5
- ert/gui/main_window.py +0 -5
- ert/gui/simulation/experiment_panel.py +12 -7
- ert/gui/simulation/run_dialog.py +2 -16
- ert/gui/summarypanel.py +0 -19
- ert/gui/tools/manage_experiments/export_dialog.py +136 -0
- ert/gui/tools/manage_experiments/storage_info_widget.py +110 -9
- ert/gui/tools/plot/plot_api.py +24 -15
- ert/gui/tools/plot/plot_widget.py +10 -2
- ert/gui/tools/plot/plot_window.py +26 -18
- ert/gui/tools/plot/plottery/plots/__init__.py +2 -0
- ert/gui/tools/plot/plottery/plots/cesp.py +3 -1
- ert/gui/tools/plot/plottery/plots/distribution.py +6 -1
- ert/gui/tools/plot/plottery/plots/ensemble.py +3 -1
- ert/gui/tools/plot/plottery/plots/gaussian_kde.py +12 -2
- ert/gui/tools/plot/plottery/plots/histogram.py +3 -1
- ert/gui/tools/plot/plottery/plots/misfits.py +436 -0
- ert/gui/tools/plot/plottery/plots/observations.py +18 -4
- ert/gui/tools/plot/plottery/plots/statistics.py +3 -1
- ert/gui/tools/plot/plottery/plots/std_dev.py +3 -1
- ert/plugins/hook_implementations/workflows/csv_export.py +2 -3
- ert/plugins/plugin_manager.py +4 -0
- ert/resources/forward_models/run_reservoirsimulator.py +8 -3
- ert/run_models/_create_run_path.py +3 -3
- ert/run_models/everest_run_model.py +13 -11
- ert/run_models/initial_ensemble_run_model.py +2 -2
- ert/run_models/run_model.py +30 -1
- ert/services/_base_service.py +6 -5
- ert/services/ert_server.py +4 -4
- ert/shared/_doc_utils/__init__.py +4 -2
- ert/shared/net_utils.py +43 -18
- ert/shared/version.py +3 -3
- ert/storage/__init__.py +2 -0
- ert/storage/local_ensemble.py +13 -7
- ert/storage/local_experiment.py +2 -2
- ert/storage/local_storage.py +41 -25
- ert/storage/migration/to11.py +1 -1
- ert/storage/migration/to18.py +0 -1
- ert/storage/migration/to19.py +34 -0
- ert/storage/migration/to20.py +23 -0
- ert/storage/migration/to21.py +25 -0
- ert/workflow_runner.py +2 -1
- {ert-18.0.9.dist-info → ert-19.0.0rc0.dist-info}/METADATA +1 -1
- {ert-18.0.9.dist-info → ert-19.0.0rc0.dist-info}/RECORD +112 -112
- {ert-18.0.9.dist-info → ert-19.0.0rc0.dist-info}/WHEEL +1 -1
- everest/bin/everlint_script.py +0 -2
- everest/bin/utils.py +2 -1
- everest/bin/visualization_script.py +4 -11
- everest/config/control_config.py +4 -4
- everest/config/control_variable_config.py +2 -2
- everest/config/everest_config.py +9 -0
- everest/config/utils.py +2 -2
- everest/config/validation_utils.py +7 -1
- everest/config_file_loader.py +0 -2
- everest/detached/client.py +3 -3
- everest/everest_storage.py +0 -2
- everest/gui/everest_client.py +2 -2
- everest/optimizer/everest2ropt.py +4 -4
- everest/optimizer/opt_model_transforms.py +2 -2
- ert/config/violations.py +0 -0
- ert/gui/tools/export/__init__.py +0 -3
- ert/gui/tools/export/export_panel.py +0 -83
- ert/gui/tools/export/export_tool.py +0 -69
- ert/gui/tools/export/exporter.py +0 -36
- {ert-18.0.9.dist-info → ert-19.0.0rc0.dist-info}/entry_points.txt +0 -0
- {ert-18.0.9.dist-info → ert-19.0.0rc0.dist-info}/licenses/COPYING +0 -0
- {ert-18.0.9.dist-info → ert-19.0.0rc0.dist-info}/top_level.txt +0 -0
ert/config/rft_config.py
CHANGED
|
@@ -3,15 +3,16 @@ from __future__ import annotations
|
|
|
3
3
|
import datetime
|
|
4
4
|
import fnmatch
|
|
5
5
|
import logging
|
|
6
|
+
import os
|
|
6
7
|
import re
|
|
7
8
|
from collections import defaultdict
|
|
8
|
-
from typing import Literal
|
|
9
|
+
from typing import IO, Any, Literal
|
|
9
10
|
|
|
10
11
|
import numpy as np
|
|
11
12
|
import numpy.typing as npt
|
|
12
13
|
import polars as pl
|
|
13
14
|
from pydantic import Field
|
|
14
|
-
from resfo_utilities import InvalidRFTError, RFTReader
|
|
15
|
+
from resfo_utilities import CornerpointGrid, InvalidRFTError, RFTReader
|
|
15
16
|
|
|
16
17
|
from ert.substitutions import substitute_runpath_name
|
|
17
18
|
|
|
@@ -27,6 +28,7 @@ class RFTConfig(ResponseConfig):
|
|
|
27
28
|
name: str = "rft"
|
|
28
29
|
has_finalized_keys: bool = False
|
|
29
30
|
data_to_read: dict[str, dict[str, list[str]]] = Field(default_factory=dict)
|
|
31
|
+
locations: list[tuple[float, float, float]] = Field(default_factory=list)
|
|
30
32
|
|
|
31
33
|
@property
|
|
32
34
|
def metadata(self) -> list[ResponseMetadata]:
|
|
@@ -51,6 +53,20 @@ class RFTConfig(ResponseConfig):
|
|
|
51
53
|
|
|
52
54
|
return [f"{base}.RFT"]
|
|
53
55
|
|
|
56
|
+
def _find_indices(
|
|
57
|
+
self, egrid_file: str | os.PathLike[str] | IO[Any]
|
|
58
|
+
) -> dict[tuple[int, int, int] | None, set[tuple[float, float, float]]]:
|
|
59
|
+
indices = defaultdict(set)
|
|
60
|
+
for a, b in zip(
|
|
61
|
+
CornerpointGrid.read_egrid(egrid_file).find_cell_containing_point(
|
|
62
|
+
self.locations
|
|
63
|
+
),
|
|
64
|
+
self.locations,
|
|
65
|
+
strict=True,
|
|
66
|
+
):
|
|
67
|
+
indices[a].add(b)
|
|
68
|
+
return indices
|
|
69
|
+
|
|
54
70
|
def read_from_file(self, run_path: str, iens: int, iter_: int) -> pl.DataFrame:
|
|
55
71
|
filename = substitute_runpath_name(self.input_files[0], iens, iter_)
|
|
56
72
|
if filename.upper().endswith(".DATA"):
|
|
@@ -58,9 +74,20 @@ class RFTConfig(ResponseConfig):
|
|
|
58
74
|
# allowed to give REFCASE and ECLBASE both
|
|
59
75
|
# with and without .DATA extensions
|
|
60
76
|
filename = filename[:-5]
|
|
77
|
+
grid_filename = f"{run_path}/{filename}"
|
|
78
|
+
if grid_filename.upper().endswith(".RFT"):
|
|
79
|
+
grid_filename = grid_filename[:-4]
|
|
80
|
+
grid_filename += ".EGRID"
|
|
61
81
|
fetched: dict[tuple[str, datetime.date], dict[str, npt.NDArray[np.float32]]] = (
|
|
62
82
|
defaultdict(dict)
|
|
63
83
|
)
|
|
84
|
+
indices = {}
|
|
85
|
+
if self.locations:
|
|
86
|
+
indices = self._find_indices(grid_filename)
|
|
87
|
+
if None in indices:
|
|
88
|
+
raise InvalidResponseFile(
|
|
89
|
+
f"Did not find grid coordinate for location(s) {indices[None]}"
|
|
90
|
+
)
|
|
64
91
|
# This is a somewhat complicated optimization in order to
|
|
65
92
|
# support wildcards in well names, dates and properties
|
|
66
93
|
# A python for loop is too slow so we use a compiled regex
|
|
@@ -72,6 +99,7 @@ class RFTConfig(ResponseConfig):
|
|
|
72
99
|
"time": [],
|
|
73
100
|
"depth": [],
|
|
74
101
|
"values": [],
|
|
102
|
+
"location": [],
|
|
75
103
|
}
|
|
76
104
|
)
|
|
77
105
|
|
|
@@ -104,6 +132,7 @@ class RFTConfig(ResponseConfig):
|
|
|
104
132
|
for time, props in inner_dict.items()
|
|
105
133
|
)
|
|
106
134
|
)
|
|
135
|
+
locations = {}
|
|
107
136
|
try:
|
|
108
137
|
with RFTReader.open(f"{run_path}/{filename}") as rft:
|
|
109
138
|
for entry in rft:
|
|
@@ -113,6 +142,15 @@ class RFTConfig(ResponseConfig):
|
|
|
113
142
|
key = f"{well}{sep}{date}{sep}{rft_property}"
|
|
114
143
|
if matcher.fullmatch(key) is not None:
|
|
115
144
|
values = entry[rft_property]
|
|
145
|
+
locations[well, date] = [
|
|
146
|
+
list(
|
|
147
|
+
indices.get(
|
|
148
|
+
(c[0] - 1, c[1] - 1, c[2] - 1),
|
|
149
|
+
[(None, None, None)],
|
|
150
|
+
)
|
|
151
|
+
)
|
|
152
|
+
for c in entry.connections
|
|
153
|
+
]
|
|
116
154
|
if np.isdtype(values.dtype, np.float32):
|
|
117
155
|
fetched[well, date][rft_property] = values
|
|
118
156
|
except (FileNotFoundError, InvalidRFTError) as err:
|
|
@@ -127,41 +165,48 @@ class RFTConfig(ResponseConfig):
|
|
|
127
165
|
"time": [],
|
|
128
166
|
"depth": [],
|
|
129
167
|
"values": [],
|
|
168
|
+
"location": [],
|
|
130
169
|
}
|
|
131
170
|
)
|
|
132
171
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
172
|
+
try:
|
|
173
|
+
df = pl.concat(
|
|
174
|
+
[
|
|
175
|
+
pl.DataFrame(
|
|
176
|
+
{
|
|
177
|
+
"response_key": [f"{well}:{time.isoformat()}:{prop}"],
|
|
178
|
+
"time": [time],
|
|
179
|
+
"depth": [fetched[well, time]["DEPTH"]],
|
|
180
|
+
"values": [vals],
|
|
181
|
+
"location": pl.Series(
|
|
182
|
+
[
|
|
183
|
+
locations.get(
|
|
184
|
+
(well, time), [(None, None, None)] * len(vals)
|
|
185
|
+
)
|
|
186
|
+
],
|
|
187
|
+
dtype=pl.Array(
|
|
188
|
+
pl.List(pl.Array(pl.Float32, 3)), len(vals)
|
|
189
|
+
),
|
|
190
|
+
),
|
|
191
|
+
}
|
|
192
|
+
)
|
|
193
|
+
.explode("depth", "values", "location")
|
|
194
|
+
.explode("location")
|
|
195
|
+
for (well, time), inner_dict in fetched.items()
|
|
196
|
+
for prop, vals in inner_dict.items()
|
|
197
|
+
if prop != "DEPTH"
|
|
198
|
+
]
|
|
160
199
|
)
|
|
200
|
+
except KeyError as err:
|
|
201
|
+
raise InvalidResponseFile(
|
|
202
|
+
f"Could not find {err.args[0]} in RFTFile {filename}"
|
|
203
|
+
) from err
|
|
161
204
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
205
|
+
return df.with_columns(
|
|
206
|
+
east=pl.col("location").arr.get(0),
|
|
207
|
+
north=pl.col("location").arr.get(1),
|
|
208
|
+
tvd=pl.col("location").arr.get(2),
|
|
209
|
+
).drop("location")
|
|
165
210
|
|
|
166
211
|
@property
|
|
167
212
|
def response_type(self) -> str:
|
|
@@ -169,7 +214,7 @@ class RFTConfig(ResponseConfig):
|
|
|
169
214
|
|
|
170
215
|
@property
|
|
171
216
|
def primary_key(self) -> list[str]:
|
|
172
|
-
return []
|
|
217
|
+
return ["east", "north", "tvd"]
|
|
173
218
|
|
|
174
219
|
@classmethod
|
|
175
220
|
def from_config_dict(cls, config_dict: ConfigDict) -> RFTConfig | None:
|
ert/config/summary_config.py
CHANGED
|
@@ -19,14 +19,13 @@ logger = logging.getLogger(__name__)
|
|
|
19
19
|
|
|
20
20
|
class SummaryConfig(ResponseConfig):
|
|
21
21
|
type: Literal["summary"] = "summary"
|
|
22
|
-
name: str = "summary"
|
|
23
22
|
has_finalized_keys: bool = False
|
|
24
23
|
|
|
25
24
|
@property
|
|
26
25
|
def metadata(self) -> list[ResponseMetadata]:
|
|
27
26
|
return [
|
|
28
27
|
ResponseMetadata(
|
|
29
|
-
response_type=self.
|
|
28
|
+
response_type=self.type,
|
|
30
29
|
response_key=response_key,
|
|
31
30
|
filter_on=None,
|
|
32
31
|
finalized=self.has_finalized_keys,
|
ert/config/surface_config.py
CHANGED
|
@@ -10,11 +10,16 @@ import xarray as xr
|
|
|
10
10
|
from pydantic import field_serializer
|
|
11
11
|
from surfio import IrapHeader, IrapSurface
|
|
12
12
|
|
|
13
|
+
from ert.field_utils import (
|
|
14
|
+
calc_rho_for_2d_grid_layer,
|
|
15
|
+
transform_local_ellipse_angle_to_local_coords,
|
|
16
|
+
transform_positions_to_local_field_coordinates,
|
|
17
|
+
)
|
|
13
18
|
from ert.substitutions import substitute_runpath_name
|
|
14
19
|
|
|
15
20
|
from ._str_to_bool import str_to_bool
|
|
16
21
|
from .field import create_flattened_cube_graph
|
|
17
|
-
from .parameter_config import InvalidParameterFile, ParameterConfig
|
|
22
|
+
from .parameter_config import InvalidParameterFile, ParameterConfig
|
|
18
23
|
from .parsing import ConfigValidationError, ErrorInfo
|
|
19
24
|
|
|
20
25
|
if TYPE_CHECKING:
|
|
@@ -46,6 +51,7 @@ class SurfaceMismatchError(InvalidParameterFile):
|
|
|
46
51
|
|
|
47
52
|
class SurfaceConfig(ParameterConfig):
|
|
48
53
|
type: Literal["surface"] = "surface"
|
|
54
|
+
dimensionality: Literal[2] = 2
|
|
49
55
|
ncol: int
|
|
50
56
|
nrow: int
|
|
51
57
|
xori: float
|
|
@@ -70,21 +76,6 @@ class SurfaceConfig(ParameterConfig):
|
|
|
70
76
|
def parameter_keys(self) -> list[str]:
|
|
71
77
|
return []
|
|
72
78
|
|
|
73
|
-
@property
|
|
74
|
-
def metadata(self) -> list[ParameterMetadata]:
|
|
75
|
-
return [
|
|
76
|
-
ParameterMetadata(
|
|
77
|
-
key=self.name,
|
|
78
|
-
dimensionality=2,
|
|
79
|
-
transformation=None,
|
|
80
|
-
userdata={
|
|
81
|
-
"data_origin": "SURFACE",
|
|
82
|
-
"nx": self.ncol,
|
|
83
|
-
"ny": self.nrow,
|
|
84
|
-
},
|
|
85
|
-
)
|
|
86
|
-
]
|
|
87
|
-
|
|
88
79
|
@classmethod
|
|
89
80
|
def from_config_list(cls, config_list: list[str | dict[str, str]]) -> Self:
|
|
90
81
|
name = cast(str, config_list[0])
|
|
@@ -248,3 +239,55 @@ class SurfaceConfig(ParameterConfig):
|
|
|
248
239
|
this flattening process"""
|
|
249
240
|
|
|
250
241
|
return create_flattened_cube_graph(px=self.ncol, py=self.nrow, pz=1)
|
|
242
|
+
|
|
243
|
+
def calc_rho_for_2d_grid_layer(
|
|
244
|
+
self,
|
|
245
|
+
obs_xpos: npt.NDArray[np.float64],
|
|
246
|
+
obs_ypos: npt.NDArray[np.float64],
|
|
247
|
+
obs_main_range: npt.NDArray[np.float64],
|
|
248
|
+
obs_perp_range: npt.NDArray[np.float64],
|
|
249
|
+
obs_anisotropy_angle: npt.NDArray[np.float64],
|
|
250
|
+
) -> npt.NDArray[np.float64]:
|
|
251
|
+
"""Function to calculate scaling values to be used in the RHO matrix
|
|
252
|
+
for distance-based localization.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
obs_xpos: x-coordinates in global coordinates of observations
|
|
256
|
+
obs_ypos: y-coordinates in global coordinates of observations
|
|
257
|
+
obs_main_range: Size of influence ellipse main principal direction.
|
|
258
|
+
obs_perp_range: Size of influence ellipse second principal direction.
|
|
259
|
+
obs_anisotropy_angle: Rotation angle anticlock wise of main principal
|
|
260
|
+
direction of influence ellipse relative to global coordinate
|
|
261
|
+
system's x-axis.
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Scaling values (elements of the RHO matrix) as a numpy array
|
|
265
|
+
of shape=(nx,ny,nobservations)
|
|
266
|
+
|
|
267
|
+
"""
|
|
268
|
+
# Transform observation positions to local surface coordinates
|
|
269
|
+
xpos, ypos = transform_positions_to_local_field_coordinates(
|
|
270
|
+
(self.xori, self.yori), self.rotation, obs_xpos, obs_ypos
|
|
271
|
+
)
|
|
272
|
+
# Transform ellipse orientation to local surface coordinates
|
|
273
|
+
rotation_angle_of_localization_ellipse = (
|
|
274
|
+
transform_local_ellipse_angle_to_local_coords(
|
|
275
|
+
self.rotation, obs_anisotropy_angle
|
|
276
|
+
)
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
# Assume the coordinate system is not flipped.
|
|
280
|
+
# This means the right_handed_grid_indexing is False
|
|
281
|
+
assert self.yflip == 1
|
|
282
|
+
return calc_rho_for_2d_grid_layer(
|
|
283
|
+
self.ncol,
|
|
284
|
+
self.nrow,
|
|
285
|
+
self.xinc,
|
|
286
|
+
self.yinc,
|
|
287
|
+
xpos,
|
|
288
|
+
ypos,
|
|
289
|
+
obs_main_range,
|
|
290
|
+
obs_perp_range,
|
|
291
|
+
rotation_angle_of_localization_ellipse,
|
|
292
|
+
right_handed_grid_indexing=False,
|
|
293
|
+
)
|
ert/dark_storage/common.py
CHANGED
|
@@ -29,7 +29,10 @@ def calculate_signed_chi_squared_misfits(
|
|
|
29
29
|
for realization_index in reponses_dict:
|
|
30
30
|
misfits_dict[realization_index] = _calculate_signed_chi_squared_misfit(
|
|
31
31
|
observation["values"],
|
|
32
|
-
reponses_dict[realization_index]
|
|
32
|
+
reponses_dict[realization_index]
|
|
33
|
+
.loc[:, observation.index]
|
|
34
|
+
.to_numpy()
|
|
35
|
+
.flatten(),
|
|
33
36
|
observation["errors"],
|
|
34
37
|
)
|
|
35
38
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from datetime import datetime
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Annotated, Any
|
|
4
4
|
from uuid import UUID
|
|
5
5
|
|
|
6
6
|
import pandas as pd
|
|
@@ -36,7 +36,9 @@ async def get_response_misfits(
|
|
|
36
36
|
response_name: str,
|
|
37
37
|
realization_index: int | None = None,
|
|
38
38
|
summary_misfits: bool = False,
|
|
39
|
-
filter_on:
|
|
39
|
+
filter_on: Annotated[
|
|
40
|
+
str | None, Query(description="JSON string with filters")
|
|
41
|
+
] = None,
|
|
40
42
|
) -> Response:
|
|
41
43
|
ensemble = storage.get_ensemble(ensemble_id)
|
|
42
44
|
dataframe = data_for_response(
|
|
@@ -9,6 +9,7 @@ import traceback
|
|
|
9
9
|
import uuid
|
|
10
10
|
from base64 import b64decode
|
|
11
11
|
from queue import SimpleQueue
|
|
12
|
+
from typing import Annotated
|
|
12
13
|
|
|
13
14
|
from fastapi import (
|
|
14
15
|
APIRouter,
|
|
@@ -153,7 +154,7 @@ def _log(request: Request) -> None:
|
|
|
153
154
|
|
|
154
155
|
@router.get("/")
|
|
155
156
|
def get_status(
|
|
156
|
-
request: Request, credentials: HTTPBasicCredentials
|
|
157
|
+
request: Request, credentials: Annotated[HTTPBasicCredentials, Depends(security)]
|
|
157
158
|
) -> PlainTextResponse:
|
|
158
159
|
_log(request)
|
|
159
160
|
_check_user(credentials)
|
|
@@ -162,7 +163,7 @@ def get_status(
|
|
|
162
163
|
|
|
163
164
|
@router.get("/status")
|
|
164
165
|
def experiment_status(
|
|
165
|
-
request: Request, credentials: HTTPBasicCredentials
|
|
166
|
+
request: Request, credentials: Annotated[HTTPBasicCredentials, Depends(security)]
|
|
166
167
|
) -> ExperimentStatus:
|
|
167
168
|
_log(request)
|
|
168
169
|
_check_user(credentials)
|
|
@@ -171,7 +172,7 @@ def experiment_status(
|
|
|
171
172
|
|
|
172
173
|
@router.post("/" + EverEndpoints.stop)
|
|
173
174
|
def stop(
|
|
174
|
-
request: Request, credentials: HTTPBasicCredentials
|
|
175
|
+
request: Request, credentials: Annotated[HTTPBasicCredentials, Depends(security)]
|
|
175
176
|
) -> Response:
|
|
176
177
|
_log(request)
|
|
177
178
|
_check_user(credentials)
|
|
@@ -185,7 +186,7 @@ def stop(
|
|
|
185
186
|
async def start_experiment(
|
|
186
187
|
request: Request,
|
|
187
188
|
background_tasks: BackgroundTasks,
|
|
188
|
-
credentials: HTTPBasicCredentials
|
|
189
|
+
credentials: Annotated[HTTPBasicCredentials, Depends(security)],
|
|
189
190
|
) -> Response:
|
|
190
191
|
_log(request)
|
|
191
192
|
_check_user(credentials)
|
|
@@ -218,7 +219,7 @@ async def start_experiment(
|
|
|
218
219
|
|
|
219
220
|
@router.get("/" + EverEndpoints.config_path)
|
|
220
221
|
async def config_path(
|
|
221
|
-
request: Request, credentials: HTTPBasicCredentials
|
|
222
|
+
request: Request, credentials: Annotated[HTTPBasicCredentials, Depends(security)]
|
|
222
223
|
) -> JSONResponse:
|
|
223
224
|
_log(request)
|
|
224
225
|
_check_user(credentials)
|
|
@@ -237,7 +238,7 @@ async def config_path(
|
|
|
237
238
|
|
|
238
239
|
@router.get("/" + EverEndpoints.start_time)
|
|
239
240
|
async def start_time(
|
|
240
|
-
request: Request, credentials: HTTPBasicCredentials
|
|
241
|
+
request: Request, credentials: Annotated[HTTPBasicCredentials, Depends(security)]
|
|
241
242
|
) -> Response:
|
|
242
243
|
_log(request)
|
|
243
244
|
_check_user(credentials)
|
|
@@ -316,9 +317,11 @@ class ExperimentRunner:
|
|
|
316
317
|
simulation_future = loop.run_in_executor(
|
|
317
318
|
None,
|
|
318
319
|
lambda: run_model.start_simulations_thread(
|
|
319
|
-
EvaluatorServerConfig(
|
|
320
|
-
|
|
321
|
-
|
|
320
|
+
EvaluatorServerConfig(
|
|
321
|
+
use_ipc_protocol=run_model.queue_config.queue_system
|
|
322
|
+
== QueueSystem.LOCAL,
|
|
323
|
+
prioritize_private_ip_address=site_plugins.prioritize_private_ip_address,
|
|
324
|
+
)
|
|
322
325
|
),
|
|
323
326
|
)
|
|
324
327
|
while True:
|
|
@@ -29,7 +29,7 @@ def get_experiments(
|
|
|
29
29
|
priors=create_priors(experiment),
|
|
30
30
|
userdata={},
|
|
31
31
|
parameters={
|
|
32
|
-
group:
|
|
32
|
+
group: config.model_dump()
|
|
33
33
|
for group, config in experiment.parameter_configuration.items()
|
|
34
34
|
if not isinstance(config, SurfaceConfig)
|
|
35
35
|
},
|
|
@@ -65,7 +65,7 @@ def get_experiment_by_id(
|
|
|
65
65
|
priors=create_priors(experiment),
|
|
66
66
|
userdata={},
|
|
67
67
|
parameters={
|
|
68
|
-
group:
|
|
68
|
+
group: config.model_dump()
|
|
69
69
|
for group, config in experiment.parameter_configuration.items()
|
|
70
70
|
},
|
|
71
71
|
responses={
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
3
|
import operator
|
|
4
|
-
from typing import Any
|
|
4
|
+
from typing import Annotated, Any
|
|
5
5
|
from urllib.parse import unquote
|
|
6
6
|
from uuid import UUID, uuid4
|
|
7
7
|
|
|
@@ -57,7 +57,9 @@ async def get_observations_for_response(
|
|
|
57
57
|
storage: Storage = DEFAULT_STORAGE,
|
|
58
58
|
ensemble_id: UUID,
|
|
59
59
|
response_key: str,
|
|
60
|
-
filter_on:
|
|
60
|
+
filter_on: Annotated[
|
|
61
|
+
str | None, Query(description="JSON string with filters")
|
|
62
|
+
] = None,
|
|
61
63
|
) -> list[js.ObservationOut]:
|
|
62
64
|
response_key = unquote(response_key)
|
|
63
65
|
try:
|
|
@@ -113,27 +113,11 @@ def get_parameter_std_dev(
|
|
|
113
113
|
return Response(content=buffer.getvalue(), media_type="application/octet-stream")
|
|
114
114
|
|
|
115
115
|
|
|
116
|
-
def _extract_parameter_group_and_key(key: str) -> tuple[str, str] | tuple[None, None]:
|
|
117
|
-
key = key.removeprefix("LOG10_")
|
|
118
|
-
if ":" not in key:
|
|
119
|
-
# Assume all incoming keys are in format group:key for now
|
|
120
|
-
return None, None
|
|
121
|
-
|
|
122
|
-
param_group, param_key = key.split(":", maxsplit=1)
|
|
123
|
-
return param_group, param_key
|
|
124
|
-
|
|
125
|
-
|
|
126
116
|
def data_for_parameter(ensemble: Ensemble, key: str) -> pd.DataFrame:
|
|
127
|
-
group, _ = _extract_parameter_group_and_key(key)
|
|
128
|
-
if group is None:
|
|
129
|
-
logger.warning(
|
|
130
|
-
f"Parameter with key '{key}' does not have a group, "
|
|
131
|
-
"fetching data for all parameters"
|
|
132
|
-
)
|
|
133
117
|
try:
|
|
134
|
-
df = ensemble.
|
|
118
|
+
df = ensemble.load_scalar_keys([key], transformed=True)
|
|
135
119
|
if df.is_empty():
|
|
136
|
-
logger.warning(f"No data found for parameter '{
|
|
120
|
+
logger.warning(f"No data found for parameter '{key}'")
|
|
137
121
|
return pd.DataFrame()
|
|
138
122
|
except KeyError as e:
|
|
139
123
|
logger.error(e)
|
|
@@ -45,7 +45,9 @@ async def get_response(
|
|
|
45
45
|
storage: Storage = DEFAULT_STORAGE,
|
|
46
46
|
ensemble_id: UUID,
|
|
47
47
|
response_key: str,
|
|
48
|
-
filter_on:
|
|
48
|
+
filter_on: Annotated[
|
|
49
|
+
str | None, Query(description="JSON string with filters")
|
|
50
|
+
] = None,
|
|
49
51
|
accept: Annotated[str | None, Header()] = None,
|
|
50
52
|
) -> Response:
|
|
51
53
|
try:
|
|
@@ -96,6 +98,7 @@ async def get_response(
|
|
|
96
98
|
response_to_pandas_x_axis_fns: dict[str, Callable[[tuple[Any, ...]], Any]] = {
|
|
97
99
|
"summary": lambda t: pd.Timestamp(t[2]).isoformat(),
|
|
98
100
|
"gen_data": lambda t: str(t[3]),
|
|
101
|
+
"rft": lambda t: str(t[4]),
|
|
99
102
|
}
|
|
100
103
|
|
|
101
104
|
|
|
@@ -147,8 +150,9 @@ def data_for_response(
|
|
|
147
150
|
# This performs the same aggragation by mean of duplicate values
|
|
148
151
|
# as in ert/analysis/_es_update.py
|
|
149
152
|
df = df.groupby(["Date", "Realization"]).mean()
|
|
150
|
-
data = df.
|
|
151
|
-
|
|
153
|
+
data = df.reset_index().pivot_table(
|
|
154
|
+
index="Realization", columns="Date", values=df.columns[0]
|
|
155
|
+
)
|
|
152
156
|
return data.astype(float)
|
|
153
157
|
|
|
154
158
|
if response_type == "rft":
|
|
@@ -159,8 +163,9 @@ def data_for_response(
|
|
|
159
163
|
)
|
|
160
164
|
.rename({"realization": "Realization"})
|
|
161
165
|
.select(["Realization", "depth", "values"])
|
|
166
|
+
.unique()
|
|
162
167
|
.to_pandas()
|
|
163
|
-
.
|
|
168
|
+
.pivot_table(index="Realization", columns="depth", values="values")
|
|
164
169
|
.reset_index(drop=True)
|
|
165
170
|
)
|
|
166
171
|
|
|
@@ -172,7 +177,7 @@ def data_for_response(
|
|
|
172
177
|
assert "report_step" in filter_on
|
|
173
178
|
report_step = int(filter_on["report_step"])
|
|
174
179
|
vals = data.filter(pl.col("report_step").eq(report_step))
|
|
175
|
-
pivoted = vals.drop("response_key", "report_step").pivot(
|
|
180
|
+
pivoted = vals.drop("response_key", "report_step").pivot( # noqa: PD010
|
|
176
181
|
on="index", values="values"
|
|
177
182
|
)
|
|
178
183
|
data = pivoted.to_pandas().set_index("realization")
|
|
@@ -24,6 +24,6 @@ class ExperimentOut(_Experiment):
|
|
|
24
24
|
ensemble_ids: list[UUID]
|
|
25
25
|
priors: Mapping[str, dict[str, Any]]
|
|
26
26
|
userdata: Mapping[str, Any]
|
|
27
|
-
parameters: Mapping[str,
|
|
27
|
+
parameters: Mapping[str, dict[str, Any]]
|
|
28
28
|
responses: Mapping[str, list[dict[str, Any]]]
|
|
29
29
|
observations: Mapping[str, dict[str, list[str]]]
|
ert/data/_measured_data.py
CHANGED
|
@@ -141,13 +141,14 @@ class MeasuredData:
|
|
|
141
141
|
|
|
142
142
|
# Pandas differentiates vs int and str keys.
|
|
143
143
|
# Legacy-wise we use int keys for realizations
|
|
144
|
-
pddf
|
|
145
|
-
|
|
146
|
-
|
|
144
|
+
pddf = (
|
|
145
|
+
pddf.rename(
|
|
146
|
+
columns={str(k): int(k) for k in active_realizations},
|
|
147
|
+
)
|
|
148
|
+
.set_index(["observation_key", "key_index"])
|
|
149
|
+
.transpose()
|
|
147
150
|
)
|
|
148
151
|
|
|
149
|
-
pddf = pddf.set_index(["observation_key", "key_index"]).transpose()
|
|
150
|
-
|
|
151
152
|
return pddf
|
|
152
153
|
|
|
153
154
|
|
ert/ensemble_evaluator/config.py
CHANGED
|
@@ -27,6 +27,7 @@ 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,
|
|
30
31
|
) -> None:
|
|
31
32
|
self.host: str | None = host
|
|
32
33
|
self.router_port: int | None = None
|
|
@@ -50,7 +51,7 @@ class EvaluatorServerConfig:
|
|
|
50
51
|
if use_ipc_protocol:
|
|
51
52
|
self.uri = f"ipc:///tmp/socket-{uuid.uuid4().hex[:8]}"
|
|
52
53
|
elif self.host is None:
|
|
53
|
-
self.host = get_ip_address()
|
|
54
|
+
self.host = get_ip_address(prioritize_private_ip_address)
|
|
54
55
|
|
|
55
56
|
if use_token:
|
|
56
57
|
self.server_public_key, self.server_secret_key = zmq.curve_keypair()
|
ert/field_utils/field_utils.py
CHANGED
|
@@ -238,7 +238,7 @@ def read_field(
|
|
|
238
238
|
ext = path.suffix
|
|
239
239
|
raise ValueError(f'Could not read {field_path}. Unrecognized suffix "{ext}"')
|
|
240
240
|
|
|
241
|
-
return np.ma.MaskedArray(data=values, fill_value=np.nan)
|
|
241
|
+
return np.ma.MaskedArray(data=values, fill_value=np.nan)
|
|
242
242
|
|
|
243
243
|
|
|
244
244
|
def save_field(
|
ert/field_utils/grdecl_io.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import io
|
|
4
3
|
import operator
|
|
5
4
|
import os
|
|
6
5
|
from collections.abc import Iterator
|
|
@@ -258,9 +257,6 @@ def import_bgrdecl(
|
|
|
258
257
|
raise ValueError(f"Did not find field parameter {field_name} in {file_path}")
|
|
259
258
|
|
|
260
259
|
|
|
261
|
-
_BUFFER_SIZE = 2**20 # 1.04 megabytes
|
|
262
|
-
|
|
263
|
-
|
|
264
260
|
def export_grdecl(
|
|
265
261
|
values: np.ma.MaskedArray[Any, np.dtype[np.float32]] | npt.NDArray[np.float32],
|
|
266
262
|
file_path: str | os.PathLike[str],
|
|
@@ -275,25 +271,12 @@ def export_grdecl(
|
|
|
275
271
|
if binary:
|
|
276
272
|
resfo.write(file_path, [(param_name.ljust(8), values.astype(np.float32))])
|
|
277
273
|
else:
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
io.TextIOWrapper(bw, write_through=True, encoding="utf-8") as tw,
|
|
288
|
-
):
|
|
289
|
-
tw.write(param_name + "\n")
|
|
290
|
-
i = 0
|
|
291
|
-
while i + per_iter <= length:
|
|
292
|
-
tw.write(fmt % tuple(values[i : i + per_iter]))
|
|
293
|
-
i += per_iter
|
|
294
|
-
|
|
295
|
-
for j, v in enumerate(values[length - (length % per_iter) :]):
|
|
296
|
-
tw.write(f" {v:3e}")
|
|
297
|
-
if j % 6 == 5:
|
|
298
|
-
tw.write("\n")
|
|
299
|
-
tw.write(" /\n")
|
|
274
|
+
with open(file_path, "w", encoding="utf-8") as fh:
|
|
275
|
+
fh.write(param_name + "\n")
|
|
276
|
+
for i, v in enumerate(values):
|
|
277
|
+
fh.write(" ")
|
|
278
|
+
fh.write(f"{v:3e}")
|
|
279
|
+
if i % 6 == 5:
|
|
280
|
+
fh.write("\n")
|
|
281
|
+
|
|
282
|
+
fh.write(" /\n")
|
ert/field_utils/roff_io.py
CHANGED
|
@@ -23,7 +23,7 @@ def export_roff(
|
|
|
23
23
|
binary: bool,
|
|
24
24
|
) -> None:
|
|
25
25
|
dimensions = data.shape
|
|
26
|
-
data = np.flip(data, -1).ravel()
|
|
26
|
+
data = np.flip(data, -1).ravel()
|
|
27
27
|
data = data.astype(np.float32).filled(RMS_UNDEFINED_FLOAT) # type: ignore
|
|
28
28
|
if not np.isfinite(data).all():
|
|
29
29
|
raise ValueError(
|