ert 18.0.9__py3-none-any.whl → 19.0.0__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/cli/main.py +7 -3
- ert/config/__init__.py +3 -4
- ert/config/_create_observation_dataframes.py +85 -59
- ert/config/_get_num_cpu.py +1 -1
- ert/config/_observations.py +106 -31
- ert/config/distribution.py +1 -1
- ert/config/ensemble_config.py +3 -3
- ert/config/ert_config.py +50 -0
- 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 +3 -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 +12 -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 +14 -4
- 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/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 -3
- ert/gui/simulation/run_dialog.py +2 -16
- ert/gui/tools/manage_experiments/export_dialog.py +136 -0
- ert/gui/tools/manage_experiments/storage_info_widget.py +133 -28
- ert/gui/tools/plot/plot_api.py +24 -15
- ert/gui/tools/plot/plot_widget.py +19 -4
- ert/gui/tools/plot/plot_window.py +35 -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 +9 -0
- 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 +25 -8
- ert/storage/local_experiment.py +2 -2
- ert/storage/local_storage.py +45 -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/storage/migration/to22.py +18 -0
- ert/storage/migration/to23.py +49 -0
- ert/workflow_runner.py +2 -1
- {ert-18.0.9.dist-info → ert-19.0.0.dist-info}/METADATA +1 -1
- {ert-18.0.9.dist-info → ert-19.0.0.dist-info}/RECORD +111 -109
- {ert-18.0.9.dist-info → ert-19.0.0.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.0.dist-info}/entry_points.txt +0 -0
- {ert-18.0.9.dist-info → ert-19.0.0.dist-info}/licenses/COPYING +0 -0
- {ert-18.0.9.dist-info → ert-19.0.0.dist-info}/top_level.txt +0 -0
|
@@ -2,8 +2,9 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import logging
|
|
5
|
+
import types
|
|
5
6
|
import uuid
|
|
6
|
-
from typing import
|
|
7
|
+
from typing import Self
|
|
7
8
|
|
|
8
9
|
import zmq
|
|
9
10
|
import zmq.asyncio
|
|
@@ -67,7 +68,10 @@ class Client:
|
|
|
67
68
|
return self
|
|
68
69
|
|
|
69
70
|
async def __aexit__(
|
|
70
|
-
self,
|
|
71
|
+
self,
|
|
72
|
+
exc_type: type[BaseException] | None,
|
|
73
|
+
exc_value: BaseException | None,
|
|
74
|
+
exc_traceback: types.TracebackType | None,
|
|
71
75
|
) -> None:
|
|
72
76
|
try:
|
|
73
77
|
await self.send(DISCONNECT_MSG)
|
ert/__main__.py
CHANGED
|
@@ -80,18 +80,32 @@ def run_webviz_ert(args: Namespace, _: ErtRuntimePlugins | None = None) -> None:
|
|
|
80
80
|
# only use the base name of the config file path
|
|
81
81
|
kwargs["ert_config"] = os.path.basename(args.config)
|
|
82
82
|
kwargs["project"] = os.path.abspath(ens_path)
|
|
83
|
+
|
|
84
|
+
yellow = "\x1b[33m"
|
|
85
|
+
green = "\x1b[32m"
|
|
86
|
+
bold = "\x1b[1m"
|
|
87
|
+
reset = "\x1b[0m"
|
|
88
|
+
|
|
83
89
|
try:
|
|
84
90
|
with ErtServer.init_service(project=Path(ens_path).absolute()) as storage:
|
|
85
91
|
storage.wait_until_ready()
|
|
86
92
|
print(
|
|
87
|
-
"""
|
|
88
|
-
|
|
93
|
+
f"""
|
|
94
|
+
---------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
{yellow}{bold}Webviz-ERT is deprecated and will be removed in the near future{reset}
|
|
97
|
+
|
|
98
|
+
{green}{bold}Plotting capabilities provided by Webviz-ERT are now available
|
|
99
|
+
using the ERT plotter{reset}
|
|
100
|
+
|
|
101
|
+
---------------------------------------------------------------
|
|
89
102
|
|
|
90
103
|
Starting up Webviz-ERT. This might take more than a minute.
|
|
91
104
|
|
|
92
|
-
|
|
105
|
+
---------------------------------------------------------------
|
|
93
106
|
"""
|
|
94
107
|
)
|
|
108
|
+
logger.info("Show Webviz-ert deprecation warning")
|
|
95
109
|
webviz_kwargs = {
|
|
96
110
|
"experimental_mode": args.experimental_mode,
|
|
97
111
|
"verbose": args.verbose,
|
|
@@ -567,15 +581,15 @@ def get_ert_parser(parser: ArgumentParser | None = None) -> ArgumentParser:
|
|
|
567
581
|
"--color-always",
|
|
568
582
|
action="store_true",
|
|
569
583
|
help="Force coloring of monitor output, which is automatically"
|
|
570
|
-
|
|
584
|
+
" disabled if the output stream is not a terminal.",
|
|
571
585
|
default=False,
|
|
572
586
|
)
|
|
573
587
|
cli_parser.add_argument(
|
|
574
588
|
"--disable-monitoring",
|
|
575
589
|
action="store_true",
|
|
576
590
|
help="Monitoring will continuously print the status of the realisations"
|
|
577
|
-
|
|
578
|
-
|
|
591
|
+
" classified into Waiting, Pending, Running, Failed, Finished"
|
|
592
|
+
" and Unknown.",
|
|
579
593
|
default=False,
|
|
580
594
|
)
|
|
581
595
|
cli_parser.add_argument(
|
ert/cli/main.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
1
|
from __future__ import annotations
|
|
3
2
|
|
|
4
3
|
import contextlib
|
|
@@ -7,6 +6,7 @@ import os
|
|
|
7
6
|
import queue
|
|
8
7
|
import sys
|
|
9
8
|
from collections import Counter
|
|
9
|
+
from pathlib import Path
|
|
10
10
|
from typing import TextIO
|
|
11
11
|
|
|
12
12
|
from _ert.threading import ErtThread
|
|
@@ -27,7 +27,7 @@ from ert.namespace import Namespace
|
|
|
27
27
|
from ert.plugins import ErtRuntimePlugins, get_site_plugins
|
|
28
28
|
from ert.run_models.event import StatusEvents
|
|
29
29
|
from ert.run_models.model_factory import create_model
|
|
30
|
-
from ert.storage import open_storage
|
|
30
|
+
from ert.storage import LocalStorage, open_storage
|
|
31
31
|
from ert.storage.local_storage import local_storage_set_ert_config
|
|
32
32
|
|
|
33
33
|
|
|
@@ -88,7 +88,10 @@ def run_cli(args: Namespace, runtime_plugins: ErtRuntimePlugins | None = None) -
|
|
|
88
88
|
)
|
|
89
89
|
|
|
90
90
|
if args.mode == WORKFLOW_MODE:
|
|
91
|
-
|
|
91
|
+
path = Path(ert_config.ens_path)
|
|
92
|
+
if LocalStorage.check_migration_needed(path):
|
|
93
|
+
LocalStorage.perform_migration(path)
|
|
94
|
+
with open_storage(path, "w") as storage:
|
|
92
95
|
execute_workflow(ert_config, storage, args.name)
|
|
93
96
|
return
|
|
94
97
|
|
|
@@ -130,6 +133,7 @@ def run_cli(args: Namespace, runtime_plugins: ErtRuntimePlugins | None = None) -
|
|
|
130
133
|
if args.port_range is None
|
|
131
134
|
else (min(args.port_range), max(args.port_range) + 1),
|
|
132
135
|
use_ipc_protocol=using_local_queuesystem,
|
|
136
|
+
prioritize_private_ip_address=ert_config.prioritize_private_ip_address,
|
|
133
137
|
)
|
|
134
138
|
|
|
135
139
|
if model.check_if_runpath_exists():
|
ert/config/__init__.py
CHANGED
|
@@ -11,8 +11,8 @@ from .ensemble_config import EnsembleConfig
|
|
|
11
11
|
from .ert_config import ErtConfig, forward_model_step_from_config_contents
|
|
12
12
|
from .ert_plugin import ErtPlugin
|
|
13
13
|
from .ert_script import ErtScript
|
|
14
|
+
from .everest_control import EverestControl, SamplerConfig
|
|
14
15
|
from .everest_response import EverestConstraintsConfig, EverestObjectivesConfig
|
|
15
|
-
from .ext_param_config import ExtParamConfig, SamplerConfig
|
|
16
16
|
from .external_ert_script import ExternalErtScript
|
|
17
17
|
from .field import Field, field_transform
|
|
18
18
|
from .forward_model_step import (
|
|
@@ -31,7 +31,7 @@ from .gen_kw_config import DataSource, GenKwConfig, PriorDict
|
|
|
31
31
|
from .known_response_types import KnownResponseTypes
|
|
32
32
|
from .lint_file import lint_file
|
|
33
33
|
from .model_config import ModelConfig
|
|
34
|
-
from .parameter_config import ParameterCardinality, ParameterConfig
|
|
34
|
+
from .parameter_config import ParameterCardinality, ParameterConfig
|
|
35
35
|
from .parsing import (
|
|
36
36
|
ConfigValidationError,
|
|
37
37
|
ConfigWarning,
|
|
@@ -90,9 +90,9 @@ __all__ = [
|
|
|
90
90
|
"ErtScript",
|
|
91
91
|
"ErtScriptWorkflow",
|
|
92
92
|
"EverestConstraintsConfig",
|
|
93
|
+
"EverestControl",
|
|
93
94
|
"EverestObjectivesConfig",
|
|
94
95
|
"ExecutableWorkflow",
|
|
95
|
-
"ExtParamConfig",
|
|
96
96
|
"ExternalErtScript",
|
|
97
97
|
"Field",
|
|
98
98
|
"ForwardModelStep",
|
|
@@ -119,7 +119,6 @@ __all__ = [
|
|
|
119
119
|
"OutlierSettings",
|
|
120
120
|
"ParameterCardinality",
|
|
121
121
|
"ParameterConfig",
|
|
122
|
-
"ParameterMetadata",
|
|
123
122
|
"PostExperimentFixtures",
|
|
124
123
|
"PostSimulationFixtures",
|
|
125
124
|
"PostUpdateFixtures",
|
|
@@ -18,6 +18,7 @@ from ._observations import (
|
|
|
18
18
|
Observation,
|
|
19
19
|
ObservationDate,
|
|
20
20
|
ObservationError,
|
|
21
|
+
RFTObservation,
|
|
21
22
|
SummaryObservation,
|
|
22
23
|
)
|
|
23
24
|
from .gen_data_config import GenDataConfig
|
|
@@ -28,19 +29,21 @@ from .parsing import (
|
|
|
28
29
|
ObservationConfigError,
|
|
29
30
|
)
|
|
30
31
|
from .refcase import Refcase
|
|
32
|
+
from .rft_config import RFTConfig
|
|
31
33
|
|
|
32
34
|
if TYPE_CHECKING:
|
|
33
35
|
import numpy.typing as npt
|
|
34
36
|
|
|
35
37
|
|
|
36
38
|
DEFAULT_TIME_DELTA = timedelta(seconds=30)
|
|
37
|
-
|
|
39
|
+
DEFAULT_LOCALIZATION_RADIUS = 3000
|
|
38
40
|
|
|
39
41
|
|
|
40
42
|
def create_observation_dataframes(
|
|
41
43
|
observations: Sequence[Observation],
|
|
42
44
|
refcase: Refcase | None,
|
|
43
45
|
gen_data_config: GenDataConfig | None,
|
|
46
|
+
rft_config: RFTConfig | None,
|
|
44
47
|
time_map: list[datetime] | None,
|
|
45
48
|
history: HistorySource,
|
|
46
49
|
) -> dict[str, pl.DataFrame]:
|
|
@@ -56,7 +59,6 @@ def create_observation_dataframes(
|
|
|
56
59
|
config_errors: list[ErrorInfo] = []
|
|
57
60
|
grouped: dict[str, list[pl.DataFrame]] = defaultdict(list)
|
|
58
61
|
for obs in observations:
|
|
59
|
-
obs_name = obs.name
|
|
60
62
|
try:
|
|
61
63
|
match obs:
|
|
62
64
|
case HistoryObservation():
|
|
@@ -64,7 +66,7 @@ def create_observation_dataframes(
|
|
|
64
66
|
_handle_history_observation(
|
|
65
67
|
refcase,
|
|
66
68
|
obs,
|
|
67
|
-
|
|
69
|
+
obs.name,
|
|
68
70
|
history,
|
|
69
71
|
time_len,
|
|
70
72
|
)
|
|
@@ -73,7 +75,7 @@ def create_observation_dataframes(
|
|
|
73
75
|
grouped["summary"].append(
|
|
74
76
|
_handle_summary_observation(
|
|
75
77
|
obs,
|
|
76
|
-
|
|
78
|
+
obs.name,
|
|
77
79
|
obs_time_list,
|
|
78
80
|
bool(refcase),
|
|
79
81
|
)
|
|
@@ -83,11 +85,18 @@ def create_observation_dataframes(
|
|
|
83
85
|
_handle_general_observation(
|
|
84
86
|
gen_data_config,
|
|
85
87
|
obs,
|
|
86
|
-
|
|
88
|
+
obs.name,
|
|
87
89
|
obs_time_list,
|
|
88
90
|
bool(refcase),
|
|
89
91
|
)
|
|
90
92
|
)
|
|
93
|
+
case RFTObservation():
|
|
94
|
+
if rft_config is None:
|
|
95
|
+
raise TypeError(
|
|
96
|
+
"create_observation_dataframes requires "
|
|
97
|
+
"rft_config is not None when using RFTObservation"
|
|
98
|
+
)
|
|
99
|
+
grouped["rft"].append(_handle_rft_observation(rft_config, obs))
|
|
91
100
|
case default:
|
|
92
101
|
assert_never(default)
|
|
93
102
|
except ObservationConfigError as err:
|
|
@@ -196,6 +205,9 @@ def _handle_history_observation(
|
|
|
196
205
|
"time": dates_series,
|
|
197
206
|
"observations": pl.Series(values, dtype=pl.Float32),
|
|
198
207
|
"std": pl.Series(std_dev, dtype=pl.Float32),
|
|
208
|
+
"east": pl.Series([None] * len(values), dtype=pl.Float32),
|
|
209
|
+
"north": pl.Series([None] * len(values), dtype=pl.Float32),
|
|
210
|
+
"radius": pl.Series([None] * len(values), dtype=pl.Float32),
|
|
199
211
|
}
|
|
200
212
|
)
|
|
201
213
|
|
|
@@ -288,40 +300,7 @@ def _get_restart(
|
|
|
288
300
|
|
|
289
301
|
|
|
290
302
|
def _has_localization(summary_dict: SummaryObservation) -> bool:
|
|
291
|
-
return
|
|
292
|
-
[
|
|
293
|
-
summary_dict.location_x is not None,
|
|
294
|
-
summary_dict.location_y is not None,
|
|
295
|
-
summary_dict.location_range is not None,
|
|
296
|
-
]
|
|
297
|
-
)
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
def _validate_localization_values(summary_dict: SummaryObservation) -> None:
|
|
301
|
-
"""The user must provide LOCATION_X and LOCATION_Y to use localization, while
|
|
302
|
-
unprovided LOCATION_RANGE should default to some value.
|
|
303
|
-
|
|
304
|
-
This method assumes the summary dict contains at least one LOCATION key.
|
|
305
|
-
"""
|
|
306
|
-
if summary_dict.location_x is None or summary_dict.location_y is None:
|
|
307
|
-
loc_values = {
|
|
308
|
-
"LOCATION_X": summary_dict.location_x,
|
|
309
|
-
"LOCATION_Y": summary_dict.location_y,
|
|
310
|
-
"LOCATION_RANGE": summary_dict.location_range,
|
|
311
|
-
}
|
|
312
|
-
provided_loc_values = {k: v for k, v in loc_values.items() if v is not None}
|
|
313
|
-
|
|
314
|
-
provided_loc_values_string = ", ".join(
|
|
315
|
-
key.upper() for key in provided_loc_values
|
|
316
|
-
)
|
|
317
|
-
raise ObservationConfigError.with_context(
|
|
318
|
-
f"Localization for observation {summary_dict.name} is misconfigured.\n"
|
|
319
|
-
f"Only {provided_loc_values_string} were provided. To enable "
|
|
320
|
-
f"localization for an observation, ensure that both LOCATION_X and "
|
|
321
|
-
f"LOCATION_Y are defined - or remove LOCATION keywords to disable "
|
|
322
|
-
f"localization.",
|
|
323
|
-
summary_dict,
|
|
324
|
-
)
|
|
303
|
+
return summary_dict.east is not None and summary_dict.north is not None
|
|
325
304
|
|
|
326
305
|
|
|
327
306
|
def _handle_summary_observation(
|
|
@@ -361,23 +340,24 @@ def _handle_summary_observation(
|
|
|
361
340
|
"Observation uncertainty must be strictly > 0", summary_key
|
|
362
341
|
) from None
|
|
363
342
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
"std": pl.Series([std_dev], dtype=pl.Float32),
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
if _has_localization(summary_dict):
|
|
373
|
-
_validate_localization_values(summary_dict)
|
|
374
|
-
data_dict["location_x"] = summary_dict.location_x
|
|
375
|
-
data_dict["location_y"] = summary_dict.location_y
|
|
376
|
-
data_dict["location_range"] = (
|
|
377
|
-
summary_dict.location_range or DEFAULT_LOCATION_RANGE_M
|
|
378
|
-
)
|
|
343
|
+
localization_radius = (
|
|
344
|
+
summary_dict.radius or DEFAULT_LOCALIZATION_RADIUS
|
|
345
|
+
if _has_localization(summary_dict)
|
|
346
|
+
else None
|
|
347
|
+
)
|
|
379
348
|
|
|
380
|
-
return pl.DataFrame(
|
|
349
|
+
return pl.DataFrame(
|
|
350
|
+
{
|
|
351
|
+
"response_key": [summary_key],
|
|
352
|
+
"observation_key": [obs_key],
|
|
353
|
+
"time": pl.Series([date]).dt.cast_time_unit("ms"),
|
|
354
|
+
"observations": pl.Series([value], dtype=pl.Float32),
|
|
355
|
+
"std": pl.Series([std_dev], dtype=pl.Float32),
|
|
356
|
+
"east": pl.Series([summary_dict.east], dtype=pl.Float32),
|
|
357
|
+
"north": pl.Series([summary_dict.north], dtype=pl.Float32),
|
|
358
|
+
"radius": pl.Series([localization_radius], dtype=pl.Float32),
|
|
359
|
+
}
|
|
360
|
+
)
|
|
381
361
|
|
|
382
362
|
|
|
383
363
|
def _handle_general_observation(
|
|
@@ -460,10 +440,8 @@ def _handle_general_observation(
|
|
|
460
440
|
stds = file_values[1::2]
|
|
461
441
|
|
|
462
442
|
else:
|
|
463
|
-
assert
|
|
464
|
-
|
|
465
|
-
and general_observation.error is not None
|
|
466
|
-
)
|
|
443
|
+
assert general_observation.value is not None
|
|
444
|
+
assert general_observation.error is not None
|
|
467
445
|
values = np.array([general_observation.value])
|
|
468
446
|
stds = np.array([general_observation.error])
|
|
469
447
|
|
|
@@ -507,5 +485,53 @@ def _handle_general_observation(
|
|
|
507
485
|
"index": pl.Series(indices, dtype=pl.UInt16),
|
|
508
486
|
"observations": pl.Series(values, dtype=pl.Float32),
|
|
509
487
|
"std": pl.Series(stds, dtype=pl.Float32),
|
|
488
|
+
# Location attributes will always be None for general observations, but are
|
|
489
|
+
# necessary to concatenate with other observation dataframes.
|
|
490
|
+
"east": pl.Series([None] * len(values), dtype=pl.Float32),
|
|
491
|
+
"north": pl.Series([None] * len(values), dtype=pl.Float32),
|
|
492
|
+
"radius": pl.Series([None] * len(values), dtype=pl.Float32),
|
|
493
|
+
}
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
def _handle_rft_observation(
|
|
498
|
+
rft_config: RFTConfig,
|
|
499
|
+
rft_observation: RFTObservation,
|
|
500
|
+
) -> pl.DataFrame:
|
|
501
|
+
location = (rft_observation.east, rft_observation.north, rft_observation.tvd)
|
|
502
|
+
if location not in rft_config.locations:
|
|
503
|
+
rft_config.locations.append(location)
|
|
504
|
+
|
|
505
|
+
data_to_read = rft_config.data_to_read
|
|
506
|
+
if rft_observation.well not in data_to_read:
|
|
507
|
+
rft_config.data_to_read[rft_observation.well] = {}
|
|
508
|
+
|
|
509
|
+
well_dict = data_to_read[rft_observation.well]
|
|
510
|
+
if rft_observation.date not in well_dict:
|
|
511
|
+
well_dict[rft_observation.date] = []
|
|
512
|
+
|
|
513
|
+
property_list = well_dict[rft_observation.date]
|
|
514
|
+
if rft_observation.property not in property_list:
|
|
515
|
+
property_list.append(rft_observation.property)
|
|
516
|
+
|
|
517
|
+
if rft_observation.error <= 0.0:
|
|
518
|
+
raise ObservationConfigError.with_context(
|
|
519
|
+
"Observation uncertainty must be strictly > 0", rft_observation.well
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
return pl.DataFrame(
|
|
523
|
+
{
|
|
524
|
+
"response_key": (
|
|
525
|
+
f"{rft_observation.well}:"
|
|
526
|
+
f"{rft_observation.date}:"
|
|
527
|
+
f"{rft_observation.property}"
|
|
528
|
+
),
|
|
529
|
+
"observation_key": rft_observation.name,
|
|
530
|
+
"east": pl.Series([location[0]], dtype=pl.Float32),
|
|
531
|
+
"north": pl.Series([location[1]], dtype=pl.Float32),
|
|
532
|
+
"tvd": pl.Series([location[2]], dtype=pl.Float32),
|
|
533
|
+
"observations": pl.Series([rft_observation.value], dtype=pl.Float32),
|
|
534
|
+
"std": pl.Series([rft_observation.error], dtype=pl.Float32),
|
|
535
|
+
"radius": pl.Series([None], dtype=pl.Float32),
|
|
510
536
|
}
|
|
511
537
|
)
|
ert/config/_get_num_cpu.py
CHANGED
|
@@ -156,7 +156,7 @@ class _Parser:
|
|
|
156
156
|
def next_line(self) -> Iterator[str]: ...
|
|
157
157
|
|
|
158
158
|
@overload
|
|
159
|
-
def next_line(self, __default: T) -> Iterator[str] | T: ...
|
|
159
|
+
def next_line(self, __default: T, /) -> Iterator[str] | T: ...
|
|
160
160
|
|
|
161
161
|
def next_line(self, *args: T) -> Iterator[str] | T:
|
|
162
162
|
self.line_number += 1
|
ert/config/_observations.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import os
|
|
2
|
-
from collections import Counter
|
|
3
2
|
from collections.abc import Sequence
|
|
4
3
|
from dataclasses import dataclass
|
|
5
4
|
from enum import StrEnum
|
|
@@ -90,9 +89,9 @@ class _SummaryValues:
|
|
|
90
89
|
name: str
|
|
91
90
|
value: float
|
|
92
91
|
key: str #: The :term:`summary key` in the summary response
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
92
|
+
east: float | None = None
|
|
93
|
+
north: float | None = None
|
|
94
|
+
radius: float | None = None
|
|
96
95
|
|
|
97
96
|
|
|
98
97
|
@dataclass
|
|
@@ -104,7 +103,7 @@ class SummaryObservation(ObservationDate, _SummaryValues, ObservationError):
|
|
|
104
103
|
|
|
105
104
|
date_dict: ObservationDate = ObservationDate()
|
|
106
105
|
float_values: dict[str, float] = {"ERROR_MIN": 0.1}
|
|
107
|
-
localization_values: dict[str, float] = {}
|
|
106
|
+
localization_values: dict[str, float | None] = {}
|
|
108
107
|
for key, value in observation_dict.items():
|
|
109
108
|
match key:
|
|
110
109
|
case "type" | "name":
|
|
@@ -125,12 +124,15 @@ class SummaryObservation(ObservationDate, _SummaryValues, ObservationError):
|
|
|
125
124
|
summary_key = value
|
|
126
125
|
case "DATE":
|
|
127
126
|
date_dict.date = value
|
|
128
|
-
case "
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
localization_values["
|
|
132
|
-
|
|
133
|
-
|
|
127
|
+
case "LOCALIZATION":
|
|
128
|
+
validate_localization(value, observation_dict["name"])
|
|
129
|
+
localization_values["east"] = validate_float(value["EAST"], key)
|
|
130
|
+
localization_values["north"] = validate_float(value["NORTH"], key)
|
|
131
|
+
localization_values["radius"] = (
|
|
132
|
+
validate_float(value["RADIUS"], key)
|
|
133
|
+
if "RADIUS" in value
|
|
134
|
+
else None
|
|
135
|
+
)
|
|
134
136
|
case _:
|
|
135
137
|
raise _unknown_key_error(str(key), observation_dict["name"])
|
|
136
138
|
if "VALUE" not in float_values:
|
|
@@ -147,9 +149,9 @@ class SummaryObservation(ObservationDate, _SummaryValues, ObservationError):
|
|
|
147
149
|
error_min=float_values["ERROR_MIN"],
|
|
148
150
|
key=summary_key,
|
|
149
151
|
value=float_values["VALUE"],
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
152
|
+
east=localization_values.get("east"),
|
|
153
|
+
north=localization_values.get("north"),
|
|
154
|
+
radius=localization_values.get("radius"),
|
|
153
155
|
**date_dict.__dict__,
|
|
154
156
|
)
|
|
155
157
|
|
|
@@ -214,12 +216,88 @@ class GeneralObservation(ObservationDate, _GeneralObservation):
|
|
|
214
216
|
return output
|
|
215
217
|
|
|
216
218
|
|
|
217
|
-
|
|
219
|
+
@dataclass
|
|
220
|
+
class RFTObservation:
|
|
221
|
+
name: str
|
|
222
|
+
well: str
|
|
223
|
+
date: str
|
|
224
|
+
property: str
|
|
225
|
+
value: float
|
|
226
|
+
error: float
|
|
227
|
+
north: float
|
|
228
|
+
east: float
|
|
229
|
+
tvd: float
|
|
230
|
+
|
|
231
|
+
@classmethod
|
|
232
|
+
def from_obs_dict(cls, directory: str, observation_dict: ObservationDict) -> Self:
|
|
233
|
+
well = None
|
|
234
|
+
observed_property = None
|
|
235
|
+
observed_value = None
|
|
236
|
+
error = None
|
|
237
|
+
date = None
|
|
238
|
+
north = None
|
|
239
|
+
east = None
|
|
240
|
+
tvd = None
|
|
241
|
+
for key, value in observation_dict.items():
|
|
242
|
+
match key:
|
|
243
|
+
case "type" | "name":
|
|
244
|
+
pass
|
|
245
|
+
case "WELL":
|
|
246
|
+
well = value
|
|
247
|
+
case "PROPERTY":
|
|
248
|
+
observed_property = value
|
|
249
|
+
case "VALUE":
|
|
250
|
+
observed_value = validate_float(value, key)
|
|
251
|
+
case "ERROR":
|
|
252
|
+
error = validate_float(value, key)
|
|
253
|
+
case "DATE":
|
|
254
|
+
date = value
|
|
255
|
+
case "NORTH":
|
|
256
|
+
north = validate_float(value, key)
|
|
257
|
+
case "EAST":
|
|
258
|
+
east = validate_float(value, key)
|
|
259
|
+
case "TVD":
|
|
260
|
+
tvd = validate_float(value, key)
|
|
261
|
+
case _:
|
|
262
|
+
raise _unknown_key_error(str(key), observation_dict["name"])
|
|
263
|
+
if well is None:
|
|
264
|
+
raise _missing_value_error(observation_dict["name"], "WELL")
|
|
265
|
+
if observed_value is None:
|
|
266
|
+
raise _missing_value_error(observation_dict["name"], "VALUE")
|
|
267
|
+
if observed_property is None:
|
|
268
|
+
raise _missing_value_error(observation_dict["name"], "PROPERTY")
|
|
269
|
+
if error is None:
|
|
270
|
+
raise _missing_value_error(observation_dict["name"], "ERROR")
|
|
271
|
+
if date is None:
|
|
272
|
+
raise _missing_value_error(observation_dict["name"], "DATE")
|
|
273
|
+
if north is None:
|
|
274
|
+
raise _missing_value_error(observation_dict["name"], "NORTH")
|
|
275
|
+
if east is None:
|
|
276
|
+
raise _missing_value_error(observation_dict["name"], "EAST")
|
|
277
|
+
if tvd is None:
|
|
278
|
+
raise _missing_value_error(observation_dict["name"], "TVD")
|
|
279
|
+
return cls(
|
|
280
|
+
observation_dict["name"],
|
|
281
|
+
well,
|
|
282
|
+
date,
|
|
283
|
+
observed_property,
|
|
284
|
+
observed_value,
|
|
285
|
+
error,
|
|
286
|
+
north,
|
|
287
|
+
east,
|
|
288
|
+
tvd,
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
Observation = (
|
|
293
|
+
HistoryObservation | SummaryObservation | GeneralObservation | RFTObservation
|
|
294
|
+
)
|
|
218
295
|
|
|
219
296
|
_TYPE_TO_CLASS: dict[ObservationType, type[Observation]] = {
|
|
220
297
|
ObservationType.HISTORY: HistoryObservation,
|
|
221
298
|
ObservationType.SUMMARY: SummaryObservation,
|
|
222
299
|
ObservationType.GENERAL: GeneralObservation,
|
|
300
|
+
ObservationType.RFT: RFTObservation,
|
|
223
301
|
}
|
|
224
302
|
|
|
225
303
|
|
|
@@ -248,25 +326,9 @@ def make_observations(
|
|
|
248
326
|
if error_list:
|
|
249
327
|
raise ObservationConfigError.from_collected(error_list)
|
|
250
328
|
|
|
251
|
-
_validate_unique_names(result)
|
|
252
329
|
return result
|
|
253
330
|
|
|
254
331
|
|
|
255
|
-
def _validate_unique_names(
|
|
256
|
-
observations: Sequence[Observation],
|
|
257
|
-
) -> None:
|
|
258
|
-
names_counter = Counter(d.name for d in observations)
|
|
259
|
-
duplicate_names = [n for n, c in names_counter.items() if c > 1]
|
|
260
|
-
errors = [
|
|
261
|
-
ErrorInfo(
|
|
262
|
-
f"Duplicate observation name {n}",
|
|
263
|
-
).set_context(n)
|
|
264
|
-
for n in duplicate_names
|
|
265
|
-
]
|
|
266
|
-
if errors:
|
|
267
|
-
raise ObservationConfigError.from_collected(errors)
|
|
268
|
-
|
|
269
|
-
|
|
270
332
|
def _validate_segment_dict(name_token: str, inp: dict[str, Any]) -> Segment:
|
|
271
333
|
start = None
|
|
272
334
|
stop = None
|
|
@@ -339,6 +401,19 @@ def validate_positive_float(val: str, key: str) -> float:
|
|
|
339
401
|
return v
|
|
340
402
|
|
|
341
403
|
|
|
404
|
+
def validate_localization(val: dict[str, Any], obs_name: str) -> None:
|
|
405
|
+
errors = []
|
|
406
|
+
if "EAST" not in val:
|
|
407
|
+
errors.append(_missing_value_error(f"LOCALIZATION for {obs_name}", "EAST"))
|
|
408
|
+
if "NORTH" not in val:
|
|
409
|
+
errors.append(_missing_value_error(f"LOCALIZATION for {obs_name}", "NORTH"))
|
|
410
|
+
for key in val:
|
|
411
|
+
if key not in {"EAST", "NORTH", "RADIUS"}:
|
|
412
|
+
errors.append(_unknown_key_error(key, f"LOCALIZATION for {obs_name}"))
|
|
413
|
+
if errors:
|
|
414
|
+
raise ObservationConfigError.from_collected(errors)
|
|
415
|
+
|
|
416
|
+
|
|
342
417
|
def validate_positive_int(val: str, key: str) -> int:
|
|
343
418
|
try:
|
|
344
419
|
v = int(val)
|
ert/config/distribution.py
CHANGED
|
@@ -17,7 +17,7 @@ class TransSettingsValidation(BaseModel):
|
|
|
17
17
|
model_config = {"extra": "forbid"}
|
|
18
18
|
|
|
19
19
|
@classmethod
|
|
20
|
-
def create(cls
|
|
20
|
+
def create(cls, *args: Any, **kwargs: Any) -> Self:
|
|
21
21
|
return cls(*args, **kwargs)
|
|
22
22
|
|
|
23
23
|
@classmethod
|
ert/config/ensemble_config.py
CHANGED
|
@@ -7,7 +7,7 @@ from typing import Self
|
|
|
7
7
|
|
|
8
8
|
from pydantic import BaseModel, Field, model_validator
|
|
9
9
|
|
|
10
|
-
from .
|
|
10
|
+
from .everest_control import EverestControl
|
|
11
11
|
from .field import Field as FieldConfig
|
|
12
12
|
from .gen_kw_config import GenKwConfig
|
|
13
13
|
from .known_response_types import KNOWN_ERT_RESPONSE_TYPES, KnownErtResponseTypes
|
|
@@ -22,7 +22,7 @@ logger = logging.getLogger(__name__)
|
|
|
22
22
|
class EnsembleConfig(BaseModel):
|
|
23
23
|
response_configs: dict[str, KnownErtResponseTypes] = Field(default_factory=dict)
|
|
24
24
|
parameter_configs: dict[
|
|
25
|
-
str, GenKwConfig | FieldConfig | SurfaceConfig |
|
|
25
|
+
str, GenKwConfig | FieldConfig | SurfaceConfig | EverestControl
|
|
26
26
|
] = Field(default_factory=dict)
|
|
27
27
|
|
|
28
28
|
@model_validator(mode="after")
|
|
@@ -132,7 +132,7 @@ class EnsembleConfig(BaseModel):
|
|
|
132
132
|
response_configs.append(instance)
|
|
133
133
|
|
|
134
134
|
return cls(
|
|
135
|
-
response_configs={response.
|
|
135
|
+
response_configs={response.type: response for response in response_configs},
|
|
136
136
|
parameter_configs={
|
|
137
137
|
parameter.name: parameter for parameter in parameter_configs
|
|
138
138
|
},
|