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
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,25 +76,10 @@ 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
|
-
def from_config_list(cls,
|
|
90
|
-
name = cast(str,
|
|
91
|
-
options = cast(dict[str, str],
|
|
80
|
+
def from_config_list(cls, config_list: list[str | dict[str, str]]) -> Self:
|
|
81
|
+
name = cast(str, config_list[0])
|
|
82
|
+
options = cast(dict[str, str], config_list[1])
|
|
92
83
|
init_file = options.get("INIT_FILES")
|
|
93
84
|
out_file = options.get("OUTPUT_FILE")
|
|
94
85
|
base_surface = options.get("BASE_SURFACE")
|
|
@@ -97,23 +88,27 @@ class SurfaceConfig(ParameterConfig):
|
|
|
97
88
|
errors = []
|
|
98
89
|
if not out_file:
|
|
99
90
|
errors.append(
|
|
100
|
-
ErrorInfo("Missing required OUTPUT_FILE").set_context(
|
|
91
|
+
ErrorInfo("Missing required OUTPUT_FILE").set_context(config_list)
|
|
101
92
|
)
|
|
102
93
|
if not init_file:
|
|
103
|
-
errors.append(
|
|
94
|
+
errors.append(
|
|
95
|
+
ErrorInfo("Missing required INIT_FILES").set_context(config_list)
|
|
96
|
+
)
|
|
104
97
|
elif not forward_init and not ("%d" in init_file or "<IENS>" in init_file):
|
|
105
98
|
errors.append(
|
|
106
99
|
ErrorInfo(
|
|
107
100
|
"INIT_FILES must contain %d or <IENS> when FORWARD_INIT:FALSE"
|
|
108
|
-
).set_context(
|
|
101
|
+
).set_context(config_list)
|
|
109
102
|
)
|
|
110
103
|
if not base_surface:
|
|
111
104
|
errors.append(
|
|
112
|
-
ErrorInfo("Missing required BASE_SURFACE").set_context(
|
|
105
|
+
ErrorInfo("Missing required BASE_SURFACE").set_context(config_list)
|
|
113
106
|
)
|
|
114
107
|
elif not Path(base_surface).exists():
|
|
115
108
|
errors.append(
|
|
116
|
-
ErrorInfo(f"BASE_SURFACE:{base_surface} not found").set_context(
|
|
109
|
+
ErrorInfo(f"BASE_SURFACE:{base_surface} not found").set_context(
|
|
110
|
+
config_list
|
|
111
|
+
)
|
|
117
112
|
)
|
|
118
113
|
if errors:
|
|
119
114
|
raise ConfigValidationError.from_collected(errors)
|
|
@@ -121,11 +116,11 @@ class SurfaceConfig(ParameterConfig):
|
|
|
121
116
|
assert out_file is not None
|
|
122
117
|
assert base_surface is not None
|
|
123
118
|
try:
|
|
124
|
-
surf = IrapSurface.from_ascii_file(base_surface)
|
|
119
|
+
surf = IrapSurface.from_ascii_file(Path(base_surface))
|
|
125
120
|
yflip = -1 if surf.header.yinc < 0 else 1
|
|
126
121
|
except Exception as err:
|
|
127
122
|
raise ConfigValidationError.with_context(
|
|
128
|
-
f"Could not load surface {base_surface!r}",
|
|
123
|
+
f"Could not load surface {base_surface!r}", config_list
|
|
129
124
|
) from err
|
|
130
125
|
return cls(
|
|
131
126
|
ncol=surf.header.ncol,
|
|
@@ -244,3 +239,55 @@ class SurfaceConfig(ParameterConfig):
|
|
|
244
239
|
this flattening process"""
|
|
245
240
|
|
|
246
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/config/workflow_fixtures.py
CHANGED
|
@@ -6,12 +6,13 @@ import typing
|
|
|
6
6
|
from dataclasses import dataclass, fields
|
|
7
7
|
from typing import TYPE_CHECKING, Literal
|
|
8
8
|
|
|
9
|
-
from PyQt6.QtWidgets import QWidget
|
|
10
9
|
from typing_extensions import TypedDict
|
|
11
10
|
|
|
12
11
|
from ert.config.parsing.hook_runtime import HookRuntime
|
|
13
12
|
|
|
14
13
|
if TYPE_CHECKING:
|
|
14
|
+
from PyQt6.QtWidgets import QWidget
|
|
15
|
+
|
|
15
16
|
from ert.config import ESSettings, ObservationSettings
|
|
16
17
|
from ert.runpaths import Runpaths
|
|
17
18
|
from ert.storage import Ensemble, Storage
|
ert/config/workflow_job.py
CHANGED
|
@@ -6,10 +6,23 @@ import os
|
|
|
6
6
|
import textwrap
|
|
7
7
|
from abc import ABC, abstractmethod
|
|
8
8
|
from dataclasses import field
|
|
9
|
-
from typing import
|
|
9
|
+
from typing import (
|
|
10
|
+
TYPE_CHECKING,
|
|
11
|
+
Annotated,
|
|
12
|
+
Any,
|
|
13
|
+
Literal,
|
|
14
|
+
Self,
|
|
15
|
+
TypeAlias,
|
|
16
|
+
cast,
|
|
17
|
+
)
|
|
10
18
|
|
|
11
|
-
from pydantic import
|
|
19
|
+
from pydantic import (
|
|
20
|
+
Field,
|
|
21
|
+
model_serializer,
|
|
22
|
+
model_validator,
|
|
23
|
+
)
|
|
12
24
|
from pydantic_core.core_schema import ValidationInfo
|
|
25
|
+
from typing_extensions import TypedDict
|
|
13
26
|
|
|
14
27
|
from ert.base_model_context import BaseModelWithContextSupport
|
|
15
28
|
|
|
@@ -42,7 +55,11 @@ def workflow_job_parser(file: str) -> ConfigDict:
|
|
|
42
55
|
return parse(file, schema=schema)
|
|
43
56
|
|
|
44
57
|
|
|
45
|
-
def workflow_job_from_file(
|
|
58
|
+
def workflow_job_from_file(
|
|
59
|
+
config_file: str,
|
|
60
|
+
origin: Literal["user", "site"] = "site",
|
|
61
|
+
name: str | None = None,
|
|
62
|
+
) -> WorkflowJob:
|
|
46
63
|
if not name:
|
|
47
64
|
name = os.path.basename(config_file)
|
|
48
65
|
|
|
@@ -71,20 +88,32 @@ def workflow_job_from_file(config_file: str, name: str | None = None) -> Workflo
|
|
|
71
88
|
)
|
|
72
89
|
logger.warning(msg)
|
|
73
90
|
ConfigWarning.deprecation_warn(msg, content_dict["SCRIPT"])
|
|
91
|
+
|
|
74
92
|
try:
|
|
75
93
|
ert_script = ErtScript.loadScriptFromFile(script)
|
|
76
94
|
# Bare Exception here as we have no control
|
|
77
95
|
# of exceptions in the loaded ErtScript
|
|
78
96
|
except Exception as err:
|
|
79
97
|
raise ErtScriptLoadFailure(f"Failed to load {name}: {err}") from err
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
98
|
+
|
|
99
|
+
if origin == "user":
|
|
100
|
+
return UserInstalledErtScriptWorkflow(
|
|
101
|
+
name=name,
|
|
102
|
+
min_args=min_args,
|
|
103
|
+
max_args=max_args,
|
|
104
|
+
arg_types=arg_types_list,
|
|
105
|
+
source=script,
|
|
106
|
+
stop_on_fail=bool(content_dict.get("STOP_ON_FAIL")),
|
|
107
|
+
)
|
|
108
|
+
else:
|
|
109
|
+
return ErtScriptWorkflow(
|
|
110
|
+
ert_script=ert_script,
|
|
111
|
+
name=name,
|
|
112
|
+
min_args=min_args,
|
|
113
|
+
max_args=max_args,
|
|
114
|
+
arg_types=arg_types_list,
|
|
115
|
+
stop_on_fail=bool(content_dict.get("STOP_ON_FAIL")),
|
|
116
|
+
)
|
|
88
117
|
else:
|
|
89
118
|
return ExecutableWorkflow(
|
|
90
119
|
name=name,
|
|
@@ -139,72 +168,124 @@ class _WorkflowJob(BaseModelWithContextSupport, ABC):
|
|
|
139
168
|
|
|
140
169
|
|
|
141
170
|
class ExecutableWorkflow(_WorkflowJob):
|
|
171
|
+
type: Literal["user_installed_executable"] = "user_installed_executable"
|
|
142
172
|
executable: str | None = None
|
|
143
173
|
|
|
144
174
|
def location(self) -> str | None:
|
|
145
175
|
return self.executable
|
|
146
176
|
|
|
147
177
|
|
|
148
|
-
class
|
|
178
|
+
class BaseErtScriptWorkflow(_WorkflowJob, ABC):
|
|
179
|
+
@abstractmethod
|
|
180
|
+
def load_ert_script_class(self) -> builtins.type[ErtScript]: ...
|
|
181
|
+
|
|
182
|
+
def location(self) -> str | None:
|
|
183
|
+
return (
|
|
184
|
+
str(self.load_ert_script_class()) if self.load_ert_script_class() else None
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
@model_validator(mode="after")
|
|
188
|
+
def validate_types(self) -> Self:
|
|
189
|
+
ertscript_class = self.load_ert_script_class()
|
|
190
|
+
if not isinstance(ertscript_class, type):
|
|
191
|
+
raise ErtScriptLoadFailure(
|
|
192
|
+
f"Failed to load {self.name}, ert_script is instance, expected "
|
|
193
|
+
f"type, got {ertscript_class}"
|
|
194
|
+
)
|
|
195
|
+
elif not issubclass(ertscript_class, ErtScript):
|
|
196
|
+
raise ErtScriptLoadFailure(
|
|
197
|
+
f"Failed to load {self.name}, script had wrong "
|
|
198
|
+
f"type, expected ErtScript, got {ertscript_class}"
|
|
199
|
+
)
|
|
200
|
+
if ertscript_class.__doc__:
|
|
201
|
+
self.description = textwrap.dedent(ertscript_class.__doc__.strip())
|
|
202
|
+
return self
|
|
203
|
+
|
|
204
|
+
@property
|
|
205
|
+
def source_package(self) -> str:
|
|
206
|
+
return self.load_ert_script_class().__module__.partition(".")[2]
|
|
207
|
+
|
|
208
|
+
def is_plugin(self) -> bool:
|
|
209
|
+
return issubclass(self.load_ert_script_class(), ErtPlugin)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class _SerializedSiteInstalledErtScriptWorkflow(TypedDict):
|
|
213
|
+
type: Literal["site_installed"]
|
|
214
|
+
name: str
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class SiteInstalledErtScriptWorkflow(BaseErtScriptWorkflow):
|
|
149
218
|
"""
|
|
150
|
-
Single workflow configuration object
|
|
219
|
+
Single workflow configuration object installed from site plugins
|
|
151
220
|
"""
|
|
152
221
|
|
|
222
|
+
type: Literal["site_installed"] = "site_installed"
|
|
153
223
|
ert_script: builtins.type[ErtScript] = None # type: ignore
|
|
154
224
|
description: str = ""
|
|
155
225
|
examples: str | None = None
|
|
156
226
|
category: str = "other"
|
|
157
227
|
|
|
158
|
-
@
|
|
159
|
-
def
|
|
160
|
-
return self.name
|
|
228
|
+
@model_serializer(mode="plain")
|
|
229
|
+
def serialize_model(self) -> _SerializedSiteInstalledErtScriptWorkflow:
|
|
230
|
+
return {"type": "site_installed", "name": self.name}
|
|
161
231
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
def deserialize_ert_script(
|
|
165
|
-
cls, ert_script: str | builtins.type[ErtScript], info: ValidationInfo
|
|
166
|
-
) -> builtins.type[ErtScript]:
|
|
167
|
-
if isinstance(ert_script, type) and issubclass(ert_script, ErtScript):
|
|
168
|
-
return ert_script
|
|
232
|
+
def load_ert_script_class(self) -> builtins.type[ErtScript]:
|
|
233
|
+
return self.ert_script
|
|
169
234
|
|
|
235
|
+
@model_validator(mode="before")
|
|
236
|
+
@classmethod
|
|
237
|
+
def deserialize_model(
|
|
238
|
+
cls, values: dict[str, Any], info: ValidationInfo
|
|
239
|
+
) -> dict[str, Any]:
|
|
170
240
|
runtime_plugins = cast("ErtRuntimePlugins", info.context)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if
|
|
241
|
+
name = values["name"]
|
|
242
|
+
|
|
243
|
+
if runtime_plugins is None:
|
|
244
|
+
if set(values.keys()) == {"name", "type"}:
|
|
245
|
+
raise ValueError(
|
|
246
|
+
f"Cannot resolve workflow job {values},"
|
|
247
|
+
f"as it expects a the workflow job {name}"
|
|
248
|
+
f"to be installed."
|
|
249
|
+
)
|
|
250
|
+
return values
|
|
251
|
+
|
|
252
|
+
if name not in runtime_plugins.installed_workflow_jobs:
|
|
174
253
|
raise KeyError(
|
|
175
|
-
f"
|
|
176
|
-
f"
|
|
177
|
-
f"
|
|
254
|
+
f"Expected workflow job {name} to be installed "
|
|
255
|
+
f"via plugins, but it was not found. Please check that "
|
|
256
|
+
f"your python environment has it installed."
|
|
178
257
|
)
|
|
179
|
-
|
|
258
|
+
site_installed_wfjob = runtime_plugins.installed_workflow_jobs[name]
|
|
180
259
|
|
|
181
|
-
|
|
260
|
+
# Intent: copy the site installed workflow to this instance.
|
|
261
|
+
# bypassing the model_serializer
|
|
262
|
+
return {
|
|
263
|
+
k: getattr(site_installed_wfjob, k)
|
|
264
|
+
for k in SiteInstalledErtScriptWorkflow.model_fields
|
|
265
|
+
}
|
|
182
266
|
|
|
183
|
-
def location(self) -> str | None:
|
|
184
|
-
return str(self.ert_script) if self.ert_script else None
|
|
185
267
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
raise ErtScriptLoadFailure(
|
|
190
|
-
f"Failed to load {self.name}, ert_script is instance, expected "
|
|
191
|
-
f"type, got {self.ert_script}"
|
|
192
|
-
)
|
|
193
|
-
elif not issubclass(self.ert_script, ErtScript):
|
|
194
|
-
raise ErtScriptLoadFailure(
|
|
195
|
-
f"Failed to load {self.name}, script had wrong "
|
|
196
|
-
f"type, expected ErtScript, got {self.ert_script}"
|
|
197
|
-
)
|
|
198
|
-
if self.ert_script.__doc__ is not None:
|
|
199
|
-
self.description = textwrap.dedent(self.ert_script.__doc__.strip())
|
|
200
|
-
return self
|
|
268
|
+
# We keep the old name for compatability with .legacy_ertscript_workflow
|
|
269
|
+
# all of which add ErtScriptWorkflow (always through plugins, i.e., site-installed)
|
|
270
|
+
ErtScriptWorkflow = SiteInstalledErtScriptWorkflow
|
|
201
271
|
|
|
202
|
-
@property
|
|
203
|
-
def source_package(self) -> str:
|
|
204
|
-
return self.ert_script.__module__.partition(".")[2]
|
|
205
272
|
|
|
206
|
-
|
|
207
|
-
|
|
273
|
+
class UserInstalledErtScriptWorkflow(BaseErtScriptWorkflow):
|
|
274
|
+
type: Literal["user_installed_ertscript"] = "user_installed_ertscript"
|
|
275
|
+
source: str
|
|
276
|
+
|
|
277
|
+
def load_ert_script_class(self) -> builtins.type[ErtScript]:
|
|
278
|
+
try:
|
|
279
|
+
return ErtScript.loadScriptFromFile(self.source)
|
|
280
|
+
except Exception as err:
|
|
281
|
+
raise ErtScriptLoadFailure(f"Failed to load {self.name}: {err}") from err
|
|
208
282
|
|
|
209
283
|
|
|
210
|
-
WorkflowJob: TypeAlias =
|
|
284
|
+
WorkflowJob: TypeAlias = Annotated[
|
|
285
|
+
(
|
|
286
|
+
SiteInstalledErtScriptWorkflow
|
|
287
|
+
| UserInstalledErtScriptWorkflow
|
|
288
|
+
| ExecutableWorkflow
|
|
289
|
+
),
|
|
290
|
+
Field(discriminator="type"),
|
|
291
|
+
]
|
|
@@ -5,7 +5,7 @@ from pathlib import Path
|
|
|
5
5
|
from pydantic import BaseModel, ValidationError
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
class
|
|
8
|
+
class ErtClientConnectionInfo(BaseModel):
|
|
9
9
|
base_url: str
|
|
10
10
|
auth_token: str | None = None
|
|
11
11
|
cert: str | bool = False
|
|
@@ -17,10 +17,10 @@ ENV_VAR = "ERT_STORAGE_CONNECTION_STRING"
|
|
|
17
17
|
# that a single client process will only ever want to connect to a single ERT
|
|
18
18
|
# Storage server during its lifetime, so we don't provide an API for managing
|
|
19
19
|
# this cache.
|
|
20
|
-
_CACHED_CONN_INFO:
|
|
20
|
+
_CACHED_CONN_INFO: ErtClientConnectionInfo | None = None
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
def find_conn_info() ->
|
|
23
|
+
def find_conn_info() -> ErtClientConnectionInfo:
|
|
24
24
|
"""
|
|
25
25
|
The base url and auth token are read from either:
|
|
26
26
|
The file `storage_server.json`, starting from the current working directory
|
|
@@ -54,7 +54,7 @@ def find_conn_info() -> ConnInfo:
|
|
|
54
54
|
raise RuntimeError("No Storage connection configuration found")
|
|
55
55
|
|
|
56
56
|
try:
|
|
57
|
-
conn_info =
|
|
57
|
+
conn_info = ErtClientConnectionInfo.model_validate_json(conn_str)
|
|
58
58
|
except (json.JSONDecodeError, ValidationError) as e:
|
|
59
59
|
raise RuntimeError("Invalid storage connection configuration") from e
|
|
60
60
|
else:
|
|
@@ -3,7 +3,7 @@ import ssl
|
|
|
3
3
|
import httpx
|
|
4
4
|
from httpx_retries import Retry, RetryTransport
|
|
5
5
|
|
|
6
|
-
from ._session import
|
|
6
|
+
from ._session import ErtClientConnectionInfo, find_conn_info
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class Client(httpx.Client):
|
|
@@ -14,7 +14,7 @@ class Client(httpx.Client):
|
|
|
14
14
|
Stores 'conn_info' to bridge the gap to the Everest client setup
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
-
def __init__(self, conn_info:
|
|
17
|
+
def __init__(self, conn_info: ErtClientConnectionInfo | None = None) -> None:
|
|
18
18
|
if conn_info is None:
|
|
19
19
|
conn_info = find_conn_info()
|
|
20
20
|
|
ert/dark_storage/common.py
CHANGED
|
@@ -4,7 +4,12 @@ import re
|
|
|
4
4
|
from importlib import metadata
|
|
5
5
|
|
|
6
6
|
from ert.dark_storage.exceptions import InternalServerError
|
|
7
|
-
from ert.storage import
|
|
7
|
+
from ert.storage import (
|
|
8
|
+
ErtStorageException,
|
|
9
|
+
ErtStoragePermissionError,
|
|
10
|
+
Storage,
|
|
11
|
+
open_storage,
|
|
12
|
+
)
|
|
8
13
|
|
|
9
14
|
logger = logging.getLogger(__name__)
|
|
10
15
|
|
|
@@ -17,9 +22,13 @@ def get_storage() -> Storage:
|
|
|
17
22
|
if _storage is None:
|
|
18
23
|
try:
|
|
19
24
|
return (_storage := open_storage(os.environ["ERT_STORAGE_ENS_PATH"]))
|
|
25
|
+
except ErtStoragePermissionError as err:
|
|
26
|
+
logger.error(f"Permission error accessing storage: {err!s}")
|
|
27
|
+
raise InternalServerError("Permission error accessing storage") from None
|
|
20
28
|
except ErtStorageException as err:
|
|
21
|
-
|
|
22
|
-
|
|
29
|
+
logger.exception(f"Error accessing storage: {err!s}")
|
|
30
|
+
raise InternalServerError("Error accessing storage") from None
|
|
31
|
+
_storage.reload()
|
|
23
32
|
return _storage
|
|
24
33
|
|
|
25
34
|
|
|
@@ -5,17 +5,18 @@ import numpy.typing as npt
|
|
|
5
5
|
import pandas as pd
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
def
|
|
8
|
+
def _calculate_signed_chi_squared_misfit(
|
|
9
9
|
obs_value: npt.NDArray[np.float64],
|
|
10
10
|
response_value: npt.NDArray[np.float64],
|
|
11
11
|
obs_std: npt.NDArray[np.float64],
|
|
12
12
|
) -> list[float]:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
"""The signed version is intended for visualization. For data assimiliation one
|
|
14
|
+
would normally use the normal chi-square"""
|
|
15
|
+
residual = response_value - obs_value
|
|
16
|
+
return (np.sign(residual) * residual * residual / (obs_std * obs_std)).tolist()
|
|
16
17
|
|
|
17
18
|
|
|
18
|
-
def
|
|
19
|
+
def calculate_signed_chi_squared_misfits(
|
|
19
20
|
reponses_dict: Mapping[int, pd.DataFrame],
|
|
20
21
|
observation: pd.DataFrame,
|
|
21
22
|
summary_misfits: bool = False,
|
|
@@ -26,9 +27,12 @@ def calculate_misfits_from_pandas(
|
|
|
26
27
|
"""
|
|
27
28
|
misfits_dict = {}
|
|
28
29
|
for realization_index in reponses_dict:
|
|
29
|
-
misfits_dict[realization_index] =
|
|
30
|
+
misfits_dict[realization_index] = _calculate_signed_chi_squared_misfit(
|
|
30
31
|
observation["values"],
|
|
31
|
-
reponses_dict[realization_index]
|
|
32
|
+
reponses_dict[realization_index]
|
|
33
|
+
.loc[:, observation.index]
|
|
34
|
+
.to_numpy()
|
|
35
|
+
.flatten(),
|
|
32
36
|
observation["errors"],
|
|
33
37
|
)
|
|
34
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
|
|
@@ -10,7 +10,7 @@ from fastapi.responses import Response
|
|
|
10
10
|
|
|
11
11
|
from ert.dark_storage import exceptions as exc
|
|
12
12
|
from ert.dark_storage.common import get_storage
|
|
13
|
-
from ert.dark_storage.compute.misfits import
|
|
13
|
+
from ert.dark_storage.compute.misfits import calculate_signed_chi_squared_misfits
|
|
14
14
|
from ert.dark_storage.endpoints.observations import (
|
|
15
15
|
_get_observations,
|
|
16
16
|
)
|
|
@@ -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(
|
|
@@ -80,7 +82,7 @@ async def get_response_misfits(
|
|
|
80
82
|
index=[parse_index(x) for x in o["x_axis"]],
|
|
81
83
|
)
|
|
82
84
|
try:
|
|
83
|
-
result_df =
|
|
85
|
+
result_df = calculate_signed_chi_squared_misfits(
|
|
84
86
|
response_dict, observation_df, summary_misfits
|
|
85
87
|
)
|
|
86
88
|
except Exception as misfits_exc:
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
from collections import Counter
|
|
2
3
|
from uuid import UUID
|
|
3
4
|
|
|
4
5
|
from fastapi import APIRouter, Body, Depends, HTTPException
|
|
@@ -38,4 +39,7 @@ def get_ensemble(
|
|
|
38
39
|
"started_at": ensemble.started_at,
|
|
39
40
|
},
|
|
40
41
|
size=ensemble.ensemble_size,
|
|
42
|
+
realization_storage_states=Counter(
|
|
43
|
+
state for states in ensemble.get_ensemble_state() for state in states
|
|
44
|
+
),
|
|
41
45
|
)
|