ert 16.0.9__py3-none-any.whl → 19.0.0rc2__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/events.py +19 -2
- _ert/forward_model_runner/client.py +6 -2
- _ert/forward_model_runner/fm_dispatch.py +9 -6
- _ert/forward_model_runner/reporting/event.py +1 -0
- _ert/forward_model_runner/runner.py +1 -2
- _ert/utils.py +12 -0
- ert/__main__.py +58 -38
- ert/analysis/_enif_update.py +8 -4
- ert/analysis/_es_update.py +19 -6
- ert/analysis/_update_commons.py +16 -6
- ert/base_model_context.py +1 -1
- ert/cli/main.py +17 -12
- ert/cli/monitor.py +7 -0
- ert/config/__init__.py +17 -6
- ert/config/_create_observation_dataframes.py +118 -21
- ert/config/_get_num_cpu.py +1 -1
- ert/config/_observations.py +91 -2
- ert/config/_read_summary.py +74 -328
- ert/config/design_matrix.py +62 -23
- ert/config/distribution.py +1 -1
- ert/config/ensemble_config.py +9 -17
- ert/config/ert_config.py +155 -58
- ert/config/everest_control.py +234 -0
- ert/config/{everest_constraints_config.py → everest_response.py} +27 -15
- ert/config/field.py +99 -90
- ert/config/forward_model_step.py +122 -17
- ert/config/gen_data_config.py +5 -10
- ert/config/gen_kw_config.py +11 -41
- ert/config/known_response_types.py +14 -0
- ert/config/parameter_config.py +1 -33
- ert/config/parsing/_option_dict.py +10 -2
- ert/config/parsing/config_errors.py +1 -1
- ert/config/parsing/config_keywords.py +2 -1
- ert/config/parsing/config_schema.py +23 -11
- ert/config/parsing/config_schema_deprecations.py +3 -3
- ert/config/parsing/config_schema_item.py +26 -11
- 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/parsing/schema_item_type.py +1 -0
- ert/config/queue_config.py +42 -50
- ert/config/response_config.py +0 -8
- ert/config/rft_config.py +275 -0
- ert/config/summary_config.py +3 -8
- ert/config/surface_config.py +73 -26
- ert/config/workflow_fixtures.py +2 -1
- ert/config/workflow_job.py +135 -54
- ert/dark_storage/client/__init__.py +2 -2
- ert/dark_storage/client/_session.py +4 -4
- ert/dark_storage/client/client.py +2 -2
- ert/dark_storage/common.py +12 -3
- ert/dark_storage/compute/misfits.py +11 -7
- ert/dark_storage/endpoints/compute/misfits.py +6 -4
- ert/dark_storage/endpoints/ensembles.py +4 -0
- ert/dark_storage/endpoints/experiment_server.py +30 -24
- ert/dark_storage/endpoints/experiments.py +2 -2
- ert/dark_storage/endpoints/observations.py +8 -6
- ert/dark_storage/endpoints/parameters.py +4 -12
- ert/dark_storage/endpoints/responses.py +24 -5
- ert/dark_storage/json_schema/ensemble.py +3 -0
- ert/dark_storage/json_schema/experiment.py +1 -1
- ert/data/_measured_data.py +6 -5
- ert/ensemble_evaluator/__init__.py +8 -1
- ert/ensemble_evaluator/config.py +2 -1
- ert/ensemble_evaluator/evaluator.py +81 -29
- ert/ensemble_evaluator/event.py +6 -0
- ert/ensemble_evaluator/snapshot.py +3 -1
- ert/ensemble_evaluator/state.py +1 -0
- ert/field_utils/__init__.py +8 -0
- ert/field_utils/field_utils.py +228 -15
- ert/field_utils/grdecl_io.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/__init__.py +23 -16
- ert/gui/ertwidgets/analysismoduleedit.py +2 -2
- ert/gui/ertwidgets/checklist.py +1 -1
- ert/gui/ertwidgets/closabledialog.py +2 -0
- ert/gui/ertwidgets/copyablelabel.py +2 -0
- ert/gui/ertwidgets/create_experiment_dialog.py +3 -1
- ert/gui/ertwidgets/ensembleselector.py +2 -2
- ert/gui/ertwidgets/listeditbox.py +2 -0
- ert/gui/ertwidgets/models/__init__.py +2 -0
- ert/gui/ertwidgets/models/activerealizationsmodel.py +5 -1
- ert/gui/ertwidgets/models/path_model.py +1 -1
- ert/gui/ertwidgets/models/targetensemblemodel.py +5 -1
- ert/gui/ertwidgets/models/text_model.py +4 -1
- ert/gui/ertwidgets/pathchooser.py +0 -3
- ert/gui/ertwidgets/searchbox.py +17 -4
- ert/gui/ertwidgets/stringbox.py +2 -0
- ert/gui/{suggestor → ertwidgets/suggestor}/_suggestor_message.py +13 -4
- ert/gui/{suggestor → ertwidgets/suggestor}/suggestor.py +63 -30
- ert/gui/main.py +41 -13
- ert/gui/main_window.py +3 -7
- ert/gui/model/fm_step_list.py +3 -0
- ert/gui/model/real_list.py +1 -0
- ert/gui/model/snapshot.py +1 -0
- ert/gui/simulation/combobox_with_description.py +3 -0
- ert/gui/simulation/ensemble_experiment_panel.py +8 -2
- ert/gui/simulation/ensemble_information_filter_panel.py +7 -2
- ert/gui/simulation/ensemble_smoother_panel.py +8 -2
- ert/gui/simulation/evaluate_ensemble_panel.py +17 -7
- ert/gui/simulation/experiment_panel.py +18 -6
- ert/gui/simulation/manual_update_panel.py +35 -10
- ert/gui/simulation/multiple_data_assimilation_panel.py +13 -9
- ert/gui/simulation/run_dialog.py +47 -20
- ert/gui/simulation/single_test_run_panel.py +6 -3
- ert/gui/simulation/view/progress_widget.py +2 -0
- ert/gui/simulation/view/realization.py +5 -1
- ert/gui/simulation/view/update.py +2 -0
- ert/gui/summarypanel.py +20 -1
- ert/gui/tools/event_viewer/panel.py +3 -4
- ert/gui/tools/event_viewer/tool.py +2 -0
- ert/gui/tools/load_results/load_results_panel.py +1 -1
- ert/gui/tools/load_results/load_results_tool.py +2 -0
- ert/gui/tools/manage_experiments/export_dialog.py +136 -0
- ert/gui/tools/manage_experiments/manage_experiments_panel.py +2 -0
- ert/gui/tools/manage_experiments/storage_info_widget.py +121 -16
- ert/gui/tools/manage_experiments/storage_widget.py +4 -3
- ert/gui/tools/plot/customize/color_chooser.py +5 -2
- ert/gui/tools/plot/customize/customize_plot_dialog.py +2 -0
- ert/gui/tools/plot/customize/default_customization_view.py +4 -0
- ert/gui/tools/plot/customize/limits_customization_view.py +3 -0
- ert/gui/tools/plot/customize/statistics_customization_view.py +3 -0
- ert/gui/tools/plot/customize/style_chooser.py +2 -0
- ert/gui/tools/plot/customize/style_customization_view.py +3 -0
- ert/gui/tools/plot/data_type_keys_widget.py +2 -0
- ert/gui/tools/plot/data_type_proxy_model.py +3 -0
- ert/gui/tools/plot/plot_api.py +50 -28
- ert/gui/tools/plot/plot_ensemble_selection_widget.py +17 -10
- ert/gui/tools/plot/plot_widget.py +15 -2
- ert/gui/tools/plot/plot_window.py +41 -19
- ert/gui/tools/plot/plottery/plot_config.py +2 -0
- ert/gui/tools/plot/plottery/plot_context.py +14 -0
- 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 +13 -5
- 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 +62 -20
- ert/gui/tools/plot/plottery/plots/std_dev.py +3 -1
- ert/gui/tools/plot/widgets/clearable_line_edit.py +9 -0
- ert/gui/tools/plot/widgets/filter_popup.py +2 -0
- ert/gui/tools/plot/widgets/filterable_kw_list_model.py +3 -0
- ert/gui/tools/plugins/plugin.py +1 -1
- ert/gui/tools/plugins/plugins_tool.py +2 -0
- ert/gui/tools/plugins/process_job_dialog.py +3 -0
- ert/gui/tools/workflows/workflow_dialog.py +2 -0
- ert/gui/tools/workflows/workflows_tool.py +2 -0
- ert/libres_facade.py +5 -7
- ert/logging/__init__.py +4 -1
- ert/mode_definitions.py +2 -0
- ert/plugins/__init__.py +4 -6
- ert/plugins/hook_implementations/workflows/csv_export.py +2 -3
- ert/plugins/hook_implementations/workflows/gen_data_rft_export.py +10 -2
- ert/plugins/hook_specifications/__init__.py +0 -10
- ert/plugins/hook_specifications/jobs.py +0 -9
- ert/plugins/plugin_manager.py +53 -124
- ert/resources/forward_models/run_reservoirsimulator.py +8 -4
- ert/resources/forward_models/template_render.py +10 -10
- ert/resources/shell_scripts/delete_directory.py +2 -2
- ert/run_models/__init__.py +24 -6
- ert/run_models/_create_run_path.py +133 -38
- ert/run_models/ensemble_experiment.py +10 -4
- ert/run_models/ensemble_information_filter.py +8 -1
- ert/run_models/ensemble_smoother.py +9 -3
- ert/run_models/evaluate_ensemble.py +8 -6
- ert/run_models/event.py +7 -3
- ert/run_models/everest_run_model.py +337 -113
- ert/run_models/initial_ensemble_run_model.py +25 -24
- ert/run_models/manual_update.py +6 -3
- ert/run_models/manual_update_enif.py +37 -0
- ert/run_models/model_factory.py +78 -18
- ert/run_models/multiple_data_assimilation.py +22 -11
- ert/run_models/run_model.py +72 -73
- ert/run_models/single_test_run.py +7 -4
- ert/run_models/update_run_model.py +4 -2
- ert/runpaths.py +5 -6
- ert/sample_prior.py +9 -4
- ert/scheduler/__init__.py +10 -5
- ert/scheduler/driver.py +40 -0
- ert/scheduler/event.py +3 -1
- ert/scheduler/job.py +23 -13
- ert/scheduler/lsf_driver.py +15 -5
- ert/scheduler/openpbs_driver.py +10 -4
- ert/scheduler/scheduler.py +5 -0
- ert/scheduler/slurm_driver.py +20 -5
- ert/services/__init__.py +2 -2
- ert/services/_base_service.py +37 -20
- ert/services/_storage_main.py +20 -18
- ert/services/ert_server.py +317 -0
- ert/shared/_doc_utils/__init__.py +4 -2
- ert/shared/_doc_utils/ert_jobs.py +1 -4
- ert/shared/net_utils.py +43 -18
- ert/shared/storage/connection.py +3 -3
- ert/shared/version.py +3 -3
- ert/storage/__init__.py +14 -1
- ert/storage/local_ensemble.py +44 -13
- ert/storage/local_experiment.py +54 -34
- ert/storage/local_storage.py +90 -58
- ert/storage/migration/to10.py +3 -2
- ert/storage/migration/to11.py +9 -10
- ert/storage/migration/to12.py +19 -20
- ert/storage/migration/to13.py +28 -27
- ert/storage/migration/to14.py +3 -3
- ert/storage/migration/to15.py +25 -0
- ert/storage/migration/to16.py +38 -0
- ert/storage/migration/to17.py +42 -0
- ert/storage/migration/to18.py +11 -0
- ert/storage/migration/to19.py +34 -0
- ert/storage/migration/to20.py +23 -0
- ert/storage/migration/to21.py +25 -0
- ert/storage/migration/to6.py +3 -2
- ert/storage/migration/to7.py +12 -13
- ert/storage/migration/to8.py +9 -11
- ert/storage/migration/to9.py +5 -4
- ert/storage/realization_storage_state.py +7 -7
- ert/substitutions.py +12 -28
- ert/validation/active_range.py +7 -7
- ert/validation/ensemble_realizations_argument.py +4 -2
- ert/validation/rangestring.py +16 -16
- ert/workflow_runner.py +6 -3
- {ert-16.0.9.dist-info → ert-19.0.0rc2.dist-info}/METADATA +21 -15
- ert-19.0.0rc2.dist-info/RECORD +524 -0
- {ert-16.0.9.dist-info → ert-19.0.0rc2.dist-info}/WHEEL +1 -1
- everest/api/everest_data_api.py +14 -1
- everest/assets/everest_logo.svg +406 -0
- everest/bin/config_branch_script.py +30 -14
- everest/bin/everconfigdump_script.py +2 -10
- everest/bin/everest_script.py +53 -33
- everest/bin/everlint_script.py +3 -5
- everest/bin/kill_script.py +7 -5
- everest/bin/main.py +11 -24
- everest/bin/monitor_script.py +64 -35
- everest/bin/utils.py +58 -43
- everest/bin/visualization_script.py +23 -13
- everest/config/__init__.py +4 -1
- everest/config/control_config.py +81 -6
- everest/config/control_variable_config.py +4 -3
- everest/config/everest_config.py +102 -79
- everest/config/forward_model_config.py +5 -3
- everest/config/install_data_config.py +7 -5
- everest/config/install_job_config.py +45 -3
- everest/config/install_template_config.py +3 -3
- everest/config/optimization_config.py +19 -6
- everest/config/output_constraint_config.py +8 -2
- everest/config/server_config.py +6 -55
- everest/config/simulator_config.py +62 -17
- everest/config/utils.py +25 -105
- everest/config/validation_utils.py +34 -15
- everest/config_file_loader.py +30 -21
- everest/detached/__init__.py +0 -6
- everest/detached/client.py +7 -52
- everest/detached/everserver.py +19 -45
- everest/everest_storage.py +24 -40
- everest/gui/everest_client.py +2 -3
- everest/gui/main_window.py +2 -2
- everest/optimizer/everest2ropt.py +68 -42
- everest/optimizer/opt_model_transforms.py +15 -20
- everest/optimizer/utils.py +0 -29
- everest/plugins/hook_specs.py +0 -24
- everest/strings.py +1 -6
- everest/util/__init__.py +3 -1
- ert/config/everest_objective_config.py +0 -95
- ert/config/ext_param_config.py +0 -107
- 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 -67
- ert/gui/tools/export/exporter.py +0 -36
- ert/plugins/hook_specifications/ecl_config.py +0 -29
- ert/services/storage_service.py +0 -127
- ert/summary_key_type.py +0 -234
- ert-16.0.9.dist-info/RECORD +0 -521
- everest/bin/everexport_script.py +0 -53
- everest/config/sampler_config.py +0 -103
- everest/simulator/__init__.py +0 -88
- everest/simulator/everest_to_ert.py +0 -252
- /ert/gui/{suggestor → ertwidgets/suggestor}/__init__.py +0 -0
- /ert/gui/{suggestor → ertwidgets/suggestor}/_colors.py +0 -0
- {ert-16.0.9.dist-info → ert-19.0.0rc2.dist-info}/entry_points.txt +0 -0
- {ert-16.0.9.dist-info → ert-19.0.0rc2.dist-info}/licenses/COPYING +0 -0
- {ert-16.0.9.dist-info → ert-19.0.0rc2.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from json import JSONEncoder
|
|
4
|
-
from typing import Any, TypeVar, no_type_check
|
|
4
|
+
from typing import Any, Self, TypeVar, no_type_check
|
|
5
5
|
|
|
6
6
|
from .file_context_token import FileContextToken
|
|
7
7
|
|
|
@@ -37,7 +37,7 @@ class ContextBool:
|
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
class ContextInt(int):
|
|
40
|
-
def __new__(cls, val: int, token: FileContextToken) ->
|
|
40
|
+
def __new__(cls, val: int, token: FileContextToken) -> Self:
|
|
41
41
|
obj = super().__new__(cls, val)
|
|
42
42
|
obj.token = token
|
|
43
43
|
return obj
|
|
@@ -50,7 +50,7 @@ class ContextInt(int):
|
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
class ContextFloat(float):
|
|
53
|
-
def __new__(cls, val: float, token: FileContextToken) ->
|
|
53
|
+
def __new__(cls, val: float, token: FileContextToken) -> Self:
|
|
54
54
|
obj = super().__new__(cls, val)
|
|
55
55
|
obj.token = token
|
|
56
56
|
return obj
|
|
@@ -18,6 +18,7 @@ class ObservationType(StrEnum):
|
|
|
18
18
|
HISTORY = "HISTORY_OBSERVATION"
|
|
19
19
|
SUMMARY = "SUMMARY_OBSERVATION"
|
|
20
20
|
GENERAL = "GENERAL_OBSERVATION"
|
|
21
|
+
RFT = "RFT_OBSERVATION"
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
ObservationDict = dict[str, Any]
|
|
@@ -92,7 +93,7 @@ def parse_observations(content: str, filename: str) -> list[ObservationDict]:
|
|
|
92
93
|
), ["TYPE"]:
|
|
93
94
|
message = (
|
|
94
95
|
f"Unknown observation type '{unexpected_token}', "
|
|
95
|
-
f"expected either 'GENERAL_OBSERVATION', "
|
|
96
|
+
f"expected either 'RFT_OBSERVATION', 'GENERAL_OBSERVATION', "
|
|
96
97
|
f"'SUMMARY_OBSERVATION' or 'HISTORY_OBSERVATION'."
|
|
97
98
|
)
|
|
98
99
|
case UnexpectedToken(token=unexpected_char, expected=allowed_chars), _:
|
|
@@ -122,7 +123,10 @@ observations_parser = Lark(
|
|
|
122
123
|
r"""
|
|
123
124
|
start: observation*
|
|
124
125
|
?observation: type OBSERVATION_NAME object? ";"
|
|
125
|
-
TYPE: "HISTORY_OBSERVATION"
|
|
126
|
+
TYPE: "HISTORY_OBSERVATION"
|
|
127
|
+
| "SUMMARY_OBSERVATION"
|
|
128
|
+
| "GENERAL_OBSERVATION"
|
|
129
|
+
| "RFT_OBSERVATION"
|
|
126
130
|
type: TYPE
|
|
127
131
|
?value: object
|
|
128
132
|
| STRING
|
|
@@ -23,3 +23,12 @@ class QueueSystem(StrEnum):
|
|
|
23
23
|
@staticmethod
|
|
24
24
|
def ert_config_case() -> str:
|
|
25
25
|
return "upper"
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def formatted_name(self) -> str:
|
|
29
|
+
return {
|
|
30
|
+
self.LSF: "LSF",
|
|
31
|
+
self.LOCAL: "Local",
|
|
32
|
+
self.TORQUE: "Torque/OpenPBS",
|
|
33
|
+
self.SLURM: "Slurm",
|
|
34
|
+
}[self]
|
|
@@ -9,6 +9,7 @@ class SchemaItemType(StrEnum):
|
|
|
9
9
|
POSITIVE_FLOAT = "POSITIVE_FLOAT"
|
|
10
10
|
PATH = "PATH"
|
|
11
11
|
EXISTING_PATH = "EXISTING_PATH"
|
|
12
|
+
EXISTING_FILE = "EXISTING_FILE"
|
|
12
13
|
# EXISTING_PATH_INLINE is a directive to the
|
|
13
14
|
# schema validation to inline the contents of
|
|
14
15
|
# the file.
|
ert/config/queue_config.py
CHANGED
|
@@ -3,8 +3,8 @@ from __future__ import annotations
|
|
|
3
3
|
import logging
|
|
4
4
|
import os
|
|
5
5
|
import re
|
|
6
|
-
import shutil
|
|
7
6
|
from abc import abstractmethod
|
|
7
|
+
from textwrap import dedent
|
|
8
8
|
from typing import (
|
|
9
9
|
TYPE_CHECKING,
|
|
10
10
|
Annotated,
|
|
@@ -51,7 +51,6 @@ class QueueOptions(
|
|
|
51
51
|
BaseModelWithContextSupport,
|
|
52
52
|
validate_assignment=True,
|
|
53
53
|
extra="forbid",
|
|
54
|
-
use_enum_values=True,
|
|
55
54
|
validate_default=True,
|
|
56
55
|
):
|
|
57
56
|
name: QueueSystem
|
|
@@ -60,11 +59,26 @@ class QueueOptions(
|
|
|
60
59
|
num_cpu: pydantic.NonNegativeInt = 1
|
|
61
60
|
realization_memory: pydantic.NonNegativeInt = Field(
|
|
62
61
|
default=0,
|
|
63
|
-
description="""
|
|
64
|
-
of memory.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
description=dedent("""
|
|
63
|
+
Amount of memory to set aside for a forward model.
|
|
64
|
+
|
|
65
|
+
This information is propagated to the queue system as the amount of memory
|
|
66
|
+
to reserve/book for a realization to complete. It is up to the configuration
|
|
67
|
+
of the queuing system how to treat this information, but usually it will
|
|
68
|
+
stop more realizations being assigned to a compute node if the compute nodes
|
|
69
|
+
memory is already fully booked.
|
|
70
|
+
|
|
71
|
+
Setting this number lower than the peak memory consumption of each
|
|
72
|
+
realization puts the realization at risk of being killed in an out-of-memory
|
|
73
|
+
situation. Setting this number higher than needed will give longer wait
|
|
74
|
+
times in the queue.
|
|
75
|
+
|
|
76
|
+
For the local queue system, this keyword has no effect. In that scenario,
|
|
77
|
+
you can use `max_running` to choke the memory consumption.
|
|
78
|
+
scheduling of compute jobs.
|
|
79
|
+
|
|
80
|
+
`realization_memory` may be an integer value, indicating the number of bytes, or
|
|
81
|
+
a string consisting of a number followed by a unit. The unit indicates the
|
|
68
82
|
multiplier that is applied, and must start with one of these characters:
|
|
69
83
|
|
|
70
84
|
* b, B: bytes
|
|
@@ -77,11 +91,8 @@ class QueueOptions(
|
|
|
77
91
|
Spaces between the number and the unit are ignored, and so are any
|
|
78
92
|
characters after the first. For example: 2g, 2G, and 2 GB all resolve
|
|
79
93
|
to the same value: 2 gigabytes, equaling 2 * 1024**3 bytes.
|
|
80
|
-
|
|
81
|
-
If not set, or set to zero, the allowed amount of memory is unlimited.
|
|
82
|
-
""",
|
|
94
|
+
"""),
|
|
83
95
|
)
|
|
84
|
-
job_script: str = shutil.which("fm_dispatch.py") or "fm_dispatch.py"
|
|
85
96
|
project_code: str | None = None
|
|
86
97
|
activate_script: str | None = Field(default=None, validate_default=True)
|
|
87
98
|
|
|
@@ -184,7 +195,6 @@ class LsfQueueOptions(QueueOptions):
|
|
|
184
195
|
"max_running",
|
|
185
196
|
"num_cpu",
|
|
186
197
|
"realization_memory",
|
|
187
|
-
"job_script",
|
|
188
198
|
}
|
|
189
199
|
)
|
|
190
200
|
driver_dict["exclude_hosts"] = driver_dict.pop("exclude_host")
|
|
@@ -212,7 +222,6 @@ class TorqueQueueOptions(QueueOptions):
|
|
|
212
222
|
"submit_sleep",
|
|
213
223
|
"num_cpu",
|
|
214
224
|
"realization_memory",
|
|
215
|
-
"job_script",
|
|
216
225
|
}
|
|
217
226
|
)
|
|
218
227
|
driver_dict["queue_name"] = driver_dict.pop("queue")
|
|
@@ -241,7 +250,6 @@ class SlurmQueueOptions(QueueOptions):
|
|
|
241
250
|
"submit_sleep",
|
|
242
251
|
"num_cpu",
|
|
243
252
|
"realization_memory",
|
|
244
|
-
"job_script",
|
|
245
253
|
}
|
|
246
254
|
)
|
|
247
255
|
driver_dict["sbatch_cmd"] = driver_dict.pop("sbatch")
|
|
@@ -390,6 +398,11 @@ class QueueConfig(BaseModelWithContextSupport):
|
|
|
390
398
|
config_dict: ConfigDict,
|
|
391
399
|
site_queue_options: QueueOptions | None = None,
|
|
392
400
|
) -> QueueConfig:
|
|
401
|
+
"""Merge ConfigDict with QueueOptions.
|
|
402
|
+
|
|
403
|
+
A ConfigDict is parsed from an Ert configuration.
|
|
404
|
+
|
|
405
|
+
site_queue_options is overridden by config_dict"""
|
|
393
406
|
site_queue_options_dict = (
|
|
394
407
|
site_queue_options.model_dump(exclude_unset=True)
|
|
395
408
|
if site_queue_options
|
|
@@ -406,48 +419,27 @@ class QueueConfig(BaseModelWithContextSupport):
|
|
|
406
419
|
usr_queue_options_dict = cls._user_queue_options_from_dict(
|
|
407
420
|
selected_queue_system, config_dict
|
|
408
421
|
)
|
|
409
|
-
default_queue_options_dict = {
|
|
410
|
-
name: generic_option.default
|
|
411
|
-
for name, generic_option in QueueOptions.model_fields.items()
|
|
412
|
-
}
|
|
413
422
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
or site_job_script
|
|
420
|
-
or shutil.which("fm_dispatch.py")
|
|
421
|
-
or "fm_dispatch.py"
|
|
422
|
-
)
|
|
423
|
+
merged_queue_options_dict = (
|
|
424
|
+
site_queue_options_dict
|
|
425
|
+
if str(selected_queue_system) == site_queue_system
|
|
426
|
+
else {}
|
|
427
|
+
) | usr_queue_options_dict
|
|
423
428
|
|
|
424
429
|
max_submit: int = config_dict.get(ConfigKeys.MAX_SUBMIT, 1)
|
|
425
430
|
stop_long_running = config_dict.get(ConfigKeys.STOP_LONG_RUNNING, False)
|
|
426
431
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
config_dict["NUM_CPU"] = usr_num_cpu
|
|
432
|
-
elif (
|
|
433
|
-
site_num_cpu is None
|
|
434
|
-
and (data_file := config_dict.get(ConfigKeys.DATA_FILE))
|
|
435
|
-
and (num_cpu := get_num_cpu_from_data_file(data_file))
|
|
432
|
+
if "num_cpu" in merged_queue_options_dict:
|
|
433
|
+
config_dict["NUM_CPU"] = merged_queue_options_dict.get("num_cpu")
|
|
434
|
+
elif (data_file := config_dict.get(ConfigKeys.DATA_FILE)) and (
|
|
435
|
+
num_cpu := get_num_cpu_from_data_file(data_file)
|
|
436
436
|
):
|
|
437
437
|
logger.info(f"Parsed NUM_CPU={num_cpu} from {data_file}")
|
|
438
|
-
|
|
438
|
+
merged_queue_options_dict[ConfigKeys.NUM_CPU] = num_cpu
|
|
439
439
|
|
|
440
440
|
selected_queue_options = QueueOptions.create_queue_options(
|
|
441
441
|
selected_queue_system,
|
|
442
|
-
|
|
443
|
-
default_queue_options_dict
|
|
444
|
-
| (
|
|
445
|
-
site_queue_options_dict
|
|
446
|
-
if str(selected_queue_system) == site_queue_system
|
|
447
|
-
else {}
|
|
448
|
-
)
|
|
449
|
-
| usr_queue_options_dict
|
|
450
|
-
),
|
|
442
|
+
merged_queue_options_dict,
|
|
451
443
|
True,
|
|
452
444
|
)
|
|
453
445
|
|
|
@@ -485,11 +477,11 @@ class QueueConfig(BaseModelWithContextSupport):
|
|
|
485
477
|
|
|
486
478
|
# validate all queue options for the unselected queues
|
|
487
479
|
# and show a warning
|
|
488
|
-
for
|
|
489
|
-
if
|
|
480
|
+
for queue_system in QueueSystem:
|
|
481
|
+
if queue_system != selected_queue_system:
|
|
490
482
|
_ = QueueOptions.create_queue_options(
|
|
491
|
-
|
|
492
|
-
grouped_queue_options[
|
|
483
|
+
queue_system,
|
|
484
|
+
grouped_queue_options[queue_system],
|
|
493
485
|
False,
|
|
494
486
|
)
|
|
495
487
|
|
ert/config/response_config.py
CHANGED
|
@@ -31,7 +31,6 @@ class ResponseMetadata(BaseModel):
|
|
|
31
31
|
|
|
32
32
|
class ResponseConfig(BaseModel):
|
|
33
33
|
type: str
|
|
34
|
-
name: str
|
|
35
34
|
input_files: list[str] = Field(default_factory=list)
|
|
36
35
|
keys: list[str] = Field(default_factory=list)
|
|
37
36
|
has_finalized_keys: bool = False
|
|
@@ -60,13 +59,6 @@ class ResponseConfig(BaseModel):
|
|
|
60
59
|
def expected_input_files(self) -> list[str]:
|
|
61
60
|
"""Returns a list of filenames expected to be produced by the forward model"""
|
|
62
61
|
|
|
63
|
-
@property
|
|
64
|
-
@abstractmethod
|
|
65
|
-
def response_type(self) -> str:
|
|
66
|
-
"""Label to identify what kind of response it is.
|
|
67
|
-
Must not overlap with that of other response configs."""
|
|
68
|
-
...
|
|
69
|
-
|
|
70
62
|
@property
|
|
71
63
|
@abstractmethod
|
|
72
64
|
def primary_key(self) -> list[str]:
|
ert/config/rft_config.py
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import datetime
|
|
4
|
+
import fnmatch
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
import re
|
|
8
|
+
from collections import defaultdict
|
|
9
|
+
from typing import IO, Any, Literal
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
import numpy.typing as npt
|
|
13
|
+
import polars as pl
|
|
14
|
+
from pydantic import Field
|
|
15
|
+
from resfo_utilities import CornerpointGrid, InvalidRFTError, RFTReader
|
|
16
|
+
|
|
17
|
+
from ert.substitutions import substitute_runpath_name
|
|
18
|
+
|
|
19
|
+
from .parsing import ConfigDict, ConfigKeys, ConfigValidationError, ConfigWarning
|
|
20
|
+
from .response_config import InvalidResponseFile, ResponseConfig, ResponseMetadata
|
|
21
|
+
from .responses_index import responses_index
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class RFTConfig(ResponseConfig):
|
|
27
|
+
type: Literal["rft"] = "rft"
|
|
28
|
+
name: str = "rft"
|
|
29
|
+
has_finalized_keys: bool = False
|
|
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)
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def metadata(self) -> list[ResponseMetadata]:
|
|
35
|
+
return [
|
|
36
|
+
ResponseMetadata(
|
|
37
|
+
response_type=self.name,
|
|
38
|
+
response_key=response_key,
|
|
39
|
+
filter_on=None,
|
|
40
|
+
finalized=self.has_finalized_keys,
|
|
41
|
+
)
|
|
42
|
+
for response_key in self.keys
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def expected_input_files(self) -> list[str]:
|
|
47
|
+
base = self.input_files[0]
|
|
48
|
+
if base.upper().endswith(".DATA"):
|
|
49
|
+
# For backwards compatibility, it is
|
|
50
|
+
# allowed to give REFCASE and ECLBASE both
|
|
51
|
+
# with and without .DATA extensions
|
|
52
|
+
base = base[:-5]
|
|
53
|
+
|
|
54
|
+
return [f"{base}.RFT"]
|
|
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
|
+
|
|
70
|
+
def read_from_file(self, run_path: str, iens: int, iter_: int) -> pl.DataFrame:
|
|
71
|
+
filename = substitute_runpath_name(self.input_files[0], iens, iter_)
|
|
72
|
+
if filename.upper().endswith(".DATA"):
|
|
73
|
+
# For backwards compatibility, it is
|
|
74
|
+
# allowed to give REFCASE and ECLBASE both
|
|
75
|
+
# with and without .DATA extensions
|
|
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"
|
|
81
|
+
fetched: dict[tuple[str, datetime.date], dict[str, npt.NDArray[np.float32]]] = (
|
|
82
|
+
defaultdict(dict)
|
|
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
|
+
)
|
|
91
|
+
# This is a somewhat complicated optimization in order to
|
|
92
|
+
# support wildcards in well names, dates and properties
|
|
93
|
+
# A python for loop is too slow so we use a compiled regex
|
|
94
|
+
# instead
|
|
95
|
+
if not self.data_to_read:
|
|
96
|
+
return pl.DataFrame(
|
|
97
|
+
{
|
|
98
|
+
"response_key": [],
|
|
99
|
+
"time": [],
|
|
100
|
+
"depth": [],
|
|
101
|
+
"values": [],
|
|
102
|
+
"location": [],
|
|
103
|
+
}
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
sep = "\x31"
|
|
107
|
+
|
|
108
|
+
def _translate(pat: str) -> str:
|
|
109
|
+
"""Translates fnmatch pattern to match anywhere"""
|
|
110
|
+
return fnmatch.translate(pat).replace("\\z", "").replace("\\Z", "")
|
|
111
|
+
|
|
112
|
+
def _props_matcher(props: list[str]) -> str:
|
|
113
|
+
"""Regex for matching given props _and_ DEPTH"""
|
|
114
|
+
pattern = f"({'|'.join(_translate(p) for p in props)})"
|
|
115
|
+
if re.fullmatch(pattern, "DEPTH") is None:
|
|
116
|
+
return f"({'|'.join(_translate(p) for p in [*props, 'DEPTH'])})"
|
|
117
|
+
else:
|
|
118
|
+
return pattern
|
|
119
|
+
|
|
120
|
+
matcher = re.compile(
|
|
121
|
+
"|".join(
|
|
122
|
+
"("
|
|
123
|
+
+ re.escape(sep).join(
|
|
124
|
+
(
|
|
125
|
+
_translate(well),
|
|
126
|
+
_translate(time),
|
|
127
|
+
_props_matcher(props),
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
+ ")"
|
|
131
|
+
for well, inner_dict in self.data_to_read.items()
|
|
132
|
+
for time, props in inner_dict.items()
|
|
133
|
+
)
|
|
134
|
+
)
|
|
135
|
+
locations = {}
|
|
136
|
+
try:
|
|
137
|
+
with RFTReader.open(f"{run_path}/{filename}") as rft:
|
|
138
|
+
for entry in rft:
|
|
139
|
+
date = entry.date
|
|
140
|
+
well = entry.well
|
|
141
|
+
for rft_property in entry:
|
|
142
|
+
key = f"{well}{sep}{date}{sep}{rft_property}"
|
|
143
|
+
if matcher.fullmatch(key) is not None:
|
|
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
|
+
]
|
|
154
|
+
if np.isdtype(values.dtype, np.float32):
|
|
155
|
+
fetched[well, date][rft_property] = values
|
|
156
|
+
except (FileNotFoundError, InvalidRFTError) as err:
|
|
157
|
+
raise InvalidResponseFile(
|
|
158
|
+
f"Could not read RFT from {run_path}/{filename}: {err}"
|
|
159
|
+
) from err
|
|
160
|
+
|
|
161
|
+
if not fetched:
|
|
162
|
+
return pl.DataFrame(
|
|
163
|
+
{
|
|
164
|
+
"response_key": [],
|
|
165
|
+
"time": [],
|
|
166
|
+
"depth": [],
|
|
167
|
+
"values": [],
|
|
168
|
+
"location": [],
|
|
169
|
+
}
|
|
170
|
+
)
|
|
171
|
+
|
|
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
|
+
]
|
|
199
|
+
)
|
|
200
|
+
except KeyError as err:
|
|
201
|
+
raise InvalidResponseFile(
|
|
202
|
+
f"Could not find {err.args[0]} in RFTFile {filename}"
|
|
203
|
+
) from err
|
|
204
|
+
|
|
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")
|
|
210
|
+
|
|
211
|
+
@property
|
|
212
|
+
def response_type(self) -> str:
|
|
213
|
+
return "rft"
|
|
214
|
+
|
|
215
|
+
@property
|
|
216
|
+
def primary_key(self) -> list[str]:
|
|
217
|
+
return ["east", "north", "tvd"]
|
|
218
|
+
|
|
219
|
+
@classmethod
|
|
220
|
+
def from_config_dict(cls, config_dict: ConfigDict) -> RFTConfig | None:
|
|
221
|
+
if rfts := config_dict.get(ConfigKeys.RFT, []):
|
|
222
|
+
eclbase: str | None = config_dict.get("ECLBASE")
|
|
223
|
+
if eclbase is None:
|
|
224
|
+
raise ConfigValidationError(
|
|
225
|
+
"In order to use rft responses, ECLBASE has to be set."
|
|
226
|
+
)
|
|
227
|
+
fm_steps = config_dict.get(ConfigKeys.FORWARD_MODEL, [])
|
|
228
|
+
names = [fm_step[0] for fm_step in fm_steps]
|
|
229
|
+
simulation_step_exists = any(
|
|
230
|
+
any(sim in name.lower() for sim in ["eclipse", "flow"])
|
|
231
|
+
for name in names
|
|
232
|
+
)
|
|
233
|
+
if not simulation_step_exists:
|
|
234
|
+
ConfigWarning.warn(
|
|
235
|
+
"Config contains a RFT key but no forward model "
|
|
236
|
+
"step known to generate rft files"
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
declared_data: dict[str, dict[datetime.date, list[str]]] = defaultdict(
|
|
240
|
+
lambda: defaultdict(list)
|
|
241
|
+
)
|
|
242
|
+
for rft in rfts:
|
|
243
|
+
for expected in ["WELL", "DATE", "PROPERTIES"]:
|
|
244
|
+
if expected not in rft:
|
|
245
|
+
raise ConfigValidationError.with_context(
|
|
246
|
+
f"For RFT keyword {expected} must be specified.", rft
|
|
247
|
+
)
|
|
248
|
+
well = rft["WELL"]
|
|
249
|
+
props = [p.strip() for p in rft["PROPERTIES"].split(",")]
|
|
250
|
+
time = rft["DATE"]
|
|
251
|
+
declared_data[well][time] += props
|
|
252
|
+
data_to_read = {
|
|
253
|
+
well: {time: sorted(set(p)) for time, p in inner_dict.items()}
|
|
254
|
+
for well, inner_dict in declared_data.items()
|
|
255
|
+
}
|
|
256
|
+
keys = sorted(
|
|
257
|
+
{
|
|
258
|
+
f"{well}:{time}:{p}"
|
|
259
|
+
for well, inner_dict in declared_data.items()
|
|
260
|
+
for time, props in inner_dict.items()
|
|
261
|
+
for p in props
|
|
262
|
+
}
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
return cls(
|
|
266
|
+
name="rft",
|
|
267
|
+
input_files=[eclbase.replace("%d", "<IENS>")],
|
|
268
|
+
keys=keys,
|
|
269
|
+
data_to_read=data_to_read,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
return None
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
responses_index.add_response_type(RFTConfig)
|
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,
|
|
@@ -72,10 +71,6 @@ class SummaryConfig(ResponseConfig):
|
|
|
72
71
|
df = df.sort(by=["time"])
|
|
73
72
|
return df
|
|
74
73
|
|
|
75
|
-
@property
|
|
76
|
-
def response_type(self) -> str:
|
|
77
|
-
return "summary"
|
|
78
|
-
|
|
79
74
|
@property
|
|
80
75
|
def primary_key(self) -> list[str]:
|
|
81
76
|
return ["time"]
|
|
@@ -91,8 +86,8 @@ class SummaryConfig(ResponseConfig):
|
|
|
91
86
|
fm_steps = config_dict.get(ConfigKeys.FORWARD_MODEL, [])
|
|
92
87
|
names = [fm_step[0] for fm_step in fm_steps]
|
|
93
88
|
simulation_step_exists = any(
|
|
94
|
-
any(sim in
|
|
95
|
-
for
|
|
89
|
+
any(sim in name.lower() for sim in ["eclipse", "flow"])
|
|
90
|
+
for name in names
|
|
96
91
|
)
|
|
97
92
|
if not simulation_step_exists:
|
|
98
93
|
ConfigWarning.warn(
|