ert 17.0.0__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/__main__.py +28 -13
- ert/analysis/_enif_update.py +8 -4
- ert/analysis/_es_update.py +19 -6
- ert/analysis/_update_commons.py +16 -6
- ert/cli/main.py +13 -6
- ert/cli/monitor.py +7 -0
- ert/config/__init__.py +15 -6
- ert/config/_create_observation_dataframes.py +117 -20
- ert/config/_get_num_cpu.py +1 -1
- ert/config/_observations.py +91 -2
- ert/config/_read_summary.py +8 -6
- ert/config/design_matrix.py +51 -24
- ert/config/distribution.py +1 -1
- ert/config/ensemble_config.py +9 -17
- ert/config/ert_config.py +103 -19
- ert/config/everest_control.py +234 -0
- ert/config/{everest_objective_config.py → everest_response.py} +24 -15
- ert/config/field.py +96 -84
- ert/config/forward_model_step.py +122 -17
- ert/config/gen_data_config.py +5 -10
- ert/config/gen_kw_config.py +5 -35
- 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_keywords.py +2 -0
- ert/config/parsing/config_schema.py +23 -3
- ert/config/parsing/config_schema_deprecations.py +3 -14
- 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 +4 -5
- 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 +59 -16
- ert/config/workflow_fixtures.py +2 -1
- 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 +1 -1
- ert/dark_storage/compute/misfits.py +11 -7
- ert/dark_storage/endpoints/compute/misfits.py +6 -4
- ert/dark_storage/endpoints/experiment_server.py +12 -9
- ert/dark_storage/endpoints/experiments.py +2 -2
- ert/dark_storage/endpoints/observations.py +8 -6
- ert/dark_storage/endpoints/parameters.py +2 -18
- ert/dark_storage/endpoints/responses.py +24 -5
- 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 +212 -3
- 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/create_experiment_dialog.py +3 -1
- ert/gui/ertwidgets/ensembleselector.py +2 -2
- ert/gui/ertwidgets/models/__init__.py +2 -0
- ert/gui/ertwidgets/models/activerealizationsmodel.py +2 -1
- ert/gui/ertwidgets/models/path_model.py +1 -1
- ert/gui/ertwidgets/models/targetensemblemodel.py +2 -1
- ert/gui/ertwidgets/models/text_model.py +1 -1
- ert/gui/ertwidgets/pathchooser.py +0 -3
- ert/gui/ertwidgets/searchbox.py +13 -4
- ert/gui/{suggestor → ertwidgets/suggestor}/_suggestor_message.py +13 -4
- ert/gui/{suggestor → ertwidgets/suggestor}/suggestor.py +63 -30
- ert/gui/main.py +37 -8
- ert/gui/main_window.py +1 -7
- ert/gui/simulation/ensemble_experiment_panel.py +1 -1
- ert/gui/simulation/ensemble_information_filter_panel.py +1 -1
- ert/gui/simulation/ensemble_smoother_panel.py +1 -1
- ert/gui/simulation/evaluate_ensemble_panel.py +1 -1
- ert/gui/simulation/experiment_panel.py +16 -3
- ert/gui/simulation/manual_update_panel.py +31 -8
- ert/gui/simulation/multiple_data_assimilation_panel.py +12 -8
- ert/gui/simulation/run_dialog.py +27 -20
- ert/gui/simulation/single_test_run_panel.py +2 -2
- ert/gui/summarypanel.py +20 -1
- ert/gui/tools/load_results/load_results_panel.py +1 -1
- ert/gui/tools/manage_experiments/export_dialog.py +136 -0
- ert/gui/tools/manage_experiments/storage_info_widget.py +121 -16
- ert/gui/tools/manage_experiments/storage_widget.py +1 -2
- ert/gui/tools/plot/plot_api.py +37 -25
- ert/gui/tools/plot/plot_widget.py +10 -2
- ert/gui/tools/plot/plot_window.py +38 -18
- 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 +12 -3
- 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/mode_definitions.py +2 -0
- ert/plugins/__init__.py +0 -1
- 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 -2
- ert/plugins/hook_specifications/jobs.py +0 -9
- ert/plugins/plugin_manager.py +6 -33
- ert/resources/forward_models/run_reservoirsimulator.py +8 -3
- ert/resources/shell_scripts/delete_directory.py +2 -2
- ert/run_models/__init__.py +18 -5
- ert/run_models/_create_run_path.py +131 -37
- 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 +159 -46
- 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 +81 -21
- ert/run_models/multiple_data_assimilation.py +22 -11
- ert/run_models/run_model.py +64 -55
- 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/driver.py +37 -0
- ert/scheduler/event.py +3 -1
- ert/scheduler/job.py +23 -13
- ert/scheduler/lsf_driver.py +6 -2
- ert/scheduler/openpbs_driver.py +7 -1
- ert/scheduler/scheduler.py +5 -0
- ert/scheduler/slurm_driver.py +6 -2
- ert/services/__init__.py +2 -2
- ert/services/_base_service.py +37 -20
- 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 +2 -0
- ert/storage/local_ensemble.py +38 -12
- ert/storage/local_experiment.py +8 -16
- ert/storage/local_storage.py +68 -42
- ert/storage/migration/to11.py +1 -1
- 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/to8.py +4 -4
- ert/substitutions.py +12 -28
- ert/validation/active_range.py +7 -7
- ert/validation/rangestring.py +16 -16
- ert/workflow_runner.py +2 -1
- {ert-17.0.0.dist-info → ert-19.0.0rc2.dist-info}/METADATA +9 -8
- {ert-17.0.0.dist-info → ert-19.0.0rc2.dist-info}/RECORD +208 -205
- {ert-17.0.0.dist-info → ert-19.0.0rc2.dist-info}/WHEEL +1 -1
- everest/api/everest_data_api.py +14 -1
- everest/bin/config_branch_script.py +3 -6
- everest/bin/everconfigdump_script.py +1 -9
- everest/bin/everest_script.py +21 -11
- everest/bin/everlint_script.py +0 -2
- everest/bin/kill_script.py +2 -2
- everest/bin/monitor_script.py +2 -2
- everest/bin/utils.py +8 -4
- everest/bin/visualization_script.py +6 -14
- 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 +75 -42
- everest/config/forward_model_config.py +5 -3
- everest/config/install_data_config.py +7 -5
- everest/config/install_job_config.py +7 -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 -49
- everest/config/utils.py +25 -105
- everest/config/validation_utils.py +17 -11
- everest/config_file_loader.py +13 -4
- everest/detached/client.py +3 -3
- everest/detached/everserver.py +7 -8
- everest/everest_storage.py +6 -12
- everest/gui/everest_client.py +2 -3
- everest/gui/main_window.py +2 -2
- everest/optimizer/everest2ropt.py +59 -32
- everest/optimizer/opt_model_transforms.py +12 -13
- everest/optimizer/utils.py +0 -29
- everest/strings.py +0 -5
- ert/config/everest_constraints_config.py +0 -95
- ert/config/ext_param_config.py +0 -106
- 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/services/storage_service.py +0 -127
- everest/config/sampler_config.py +0 -103
- everest/simulator/__init__.py +0 -88
- everest/simulator/everest_to_ert.py +0 -51
- /ert/gui/{suggestor → ertwidgets/suggestor}/__init__.py +0 -0
- /ert/gui/{suggestor → ertwidgets/suggestor}/_colors.py +0 -0
- {ert-17.0.0.dist-info → ert-19.0.0rc2.dist-info}/entry_points.txt +0 -0
- {ert-17.0.0.dist-info → ert-19.0.0rc2.dist-info}/licenses/COPYING +0 -0
- {ert-17.0.0.dist-info → ert-19.0.0rc2.dist-info}/top_level.txt +0 -0
ert/config/ert_config.py
CHANGED
|
@@ -25,6 +25,7 @@ from ._design_matrix_validator import DesignMatrixValidator
|
|
|
25
25
|
from ._observations import (
|
|
26
26
|
HistoryObservation,
|
|
27
27
|
Observation,
|
|
28
|
+
RFTObservation,
|
|
28
29
|
SummaryObservation,
|
|
29
30
|
make_observations,
|
|
30
31
|
)
|
|
@@ -36,9 +37,12 @@ from .forward_model_step import (
|
|
|
36
37
|
ForwardModelStepJSON,
|
|
37
38
|
ForwardModelStepValidationError,
|
|
38
39
|
ForwardModelStepWarning,
|
|
40
|
+
SiteInstalledForwardModelStep,
|
|
41
|
+
SiteOrUserForwardModelStep,
|
|
42
|
+
UserInstalledForwardModelStep,
|
|
39
43
|
)
|
|
40
44
|
from .gen_data_config import GenDataConfig
|
|
41
|
-
from .gen_kw_config import GenKwConfig
|
|
45
|
+
from .gen_kw_config import DataSource, GenKwConfig
|
|
42
46
|
from .model_config import ModelConfig
|
|
43
47
|
from .parse_arg_types_list import parse_arg_types_list
|
|
44
48
|
from .parsing import (
|
|
@@ -59,6 +63,7 @@ from .parsing import (
|
|
|
59
63
|
from .parsing.observations_parser import ObservationDict
|
|
60
64
|
from .queue_config import KnownQueueOptions, QueueConfig
|
|
61
65
|
from .refcase import Refcase
|
|
66
|
+
from .rft_config import RFTConfig
|
|
62
67
|
from .workflow import Workflow
|
|
63
68
|
from .workflow_fixtures import fixtures_per_hook
|
|
64
69
|
from .workflow_job import (
|
|
@@ -133,6 +138,7 @@ def create_forward_model_json(
|
|
|
133
138
|
env_pr_fm_step = {}
|
|
134
139
|
|
|
135
140
|
context_substitutions = Substitutions(context)
|
|
141
|
+
real_iter_substituter = context_substitutions.real_iter_substituter(iens, itr)
|
|
136
142
|
|
|
137
143
|
class Substituter:
|
|
138
144
|
def __init__(self, fm_step: ForwardModelStep) -> None:
|
|
@@ -146,7 +152,7 @@ def create_forward_model_json(
|
|
|
146
152
|
)
|
|
147
153
|
self.copy_private_args = Substitutions(
|
|
148
154
|
{
|
|
149
|
-
key:
|
|
155
|
+
key: real_iter_substituter.substitute(val)
|
|
150
156
|
for key, val in fm_step.private_args.items()
|
|
151
157
|
}
|
|
152
158
|
)
|
|
@@ -163,7 +169,7 @@ def create_forward_model_json(
|
|
|
163
169
|
string = self.copy_private_args.substitute(
|
|
164
170
|
string, self.substitution_context_hint, 1, warn_max_iter=False
|
|
165
171
|
)
|
|
166
|
-
return
|
|
172
|
+
return real_iter_substituter.substitute(string)
|
|
167
173
|
|
|
168
174
|
def filter_env_dict(self, env_dict: dict[str, str]) -> dict[str, str] | None:
|
|
169
175
|
substituted_dict = {}
|
|
@@ -518,18 +524,16 @@ def workflows_from_dict(
|
|
|
518
524
|
|
|
519
525
|
def installed_forward_model_steps_from_dict(
|
|
520
526
|
config_dict: ConfigDict,
|
|
521
|
-
) -> dict[str,
|
|
527
|
+
) -> dict[str, UserInstalledForwardModelStep]:
|
|
522
528
|
errors: list[ErrorInfo | ConfigValidationError] = []
|
|
523
|
-
fm_steps: dict[str,
|
|
529
|
+
fm_steps: dict[str, UserInstalledForwardModelStep] = {}
|
|
524
530
|
for name, (fm_step_config_file, config_contents) in config_dict.get(
|
|
525
531
|
ConfigKeys.INSTALL_JOB, []
|
|
526
532
|
):
|
|
527
533
|
fm_step_config_file = path.abspath(fm_step_config_file)
|
|
528
534
|
try:
|
|
529
535
|
new_fm_step = forward_model_step_from_config_contents(
|
|
530
|
-
config_contents,
|
|
531
|
-
name=name,
|
|
532
|
-
config_file=fm_step_config_file,
|
|
536
|
+
config_contents, name=name, config_file=fm_step_config_file
|
|
533
537
|
)
|
|
534
538
|
except ConfigValidationError as e:
|
|
535
539
|
errors.append(e)
|
|
@@ -692,6 +696,8 @@ def log_observation_keys(
|
|
|
692
696
|
|
|
693
697
|
RESERVED_KEYWORDS = ["realization", "IENS", "ITER"]
|
|
694
698
|
|
|
699
|
+
USER_CONFIG_SCHEMA = init_user_config_schema()
|
|
700
|
+
|
|
695
701
|
|
|
696
702
|
class ErtConfig(BaseModel):
|
|
697
703
|
DEFAULT_ENSPATH: ClassVar[str] = "storage"
|
|
@@ -703,6 +709,7 @@ class ErtConfig(BaseModel):
|
|
|
703
709
|
QUEUE_OPTIONS: ClassVar[KnownQueueOptions | None] = None
|
|
704
710
|
RESERVED_KEYWORDS: ClassVar[list[str]] = RESERVED_KEYWORDS
|
|
705
711
|
ENV_VARS: ClassVar[dict[str, str]] = {}
|
|
712
|
+
PRIORITIZE_PRIVATE_IP_ADDRESS: ClassVar[bool] = False
|
|
706
713
|
|
|
707
714
|
substitutions: dict[str, str] = Field(default_factory=dict)
|
|
708
715
|
ensemble_config: EnsembleConfig = Field(default_factory=EnsembleConfig)
|
|
@@ -717,10 +724,11 @@ class ErtConfig(BaseModel):
|
|
|
717
724
|
default_factory=lambda: defaultdict(lambda: cast(list[Workflow], []))
|
|
718
725
|
)
|
|
719
726
|
runpath_file: Path = Path(DEFAULT_RUNPATH_FILE)
|
|
727
|
+
prioritize_private_ip_address: bool = False
|
|
720
728
|
|
|
721
729
|
ert_templates: list[tuple[str, str]] = Field(default_factory=list)
|
|
722
730
|
|
|
723
|
-
forward_model_steps: list[
|
|
731
|
+
forward_model_steps: list[SiteOrUserForwardModelStep] = Field(default_factory=list)
|
|
724
732
|
runpath_config: ModelConfig = Field(default_factory=ModelConfig)
|
|
725
733
|
user_config_file: str = "no_config"
|
|
726
734
|
config_path: str = Field(init=False, default="")
|
|
@@ -733,6 +741,18 @@ class ErtConfig(BaseModel):
|
|
|
733
741
|
@property
|
|
734
742
|
def observations(self) -> dict[str, pl.DataFrame]:
|
|
735
743
|
if self._observations is None:
|
|
744
|
+
has_rft_observations = any(
|
|
745
|
+
isinstance(o, RFTObservation) for o in self.observation_declarations
|
|
746
|
+
)
|
|
747
|
+
if (
|
|
748
|
+
has_rft_observations
|
|
749
|
+
and "rft" not in self.ensemble_config.response_configs
|
|
750
|
+
):
|
|
751
|
+
self.ensemble_config.response_configs["rft"] = RFTConfig(
|
|
752
|
+
input_files=[self.runpath_config.eclbase_format_string],
|
|
753
|
+
data_to_read={},
|
|
754
|
+
locations=[],
|
|
755
|
+
)
|
|
736
756
|
computed = create_observation_dataframes(
|
|
737
757
|
self.observation_declarations,
|
|
738
758
|
self.refcase,
|
|
@@ -740,6 +760,10 @@ class ErtConfig(BaseModel):
|
|
|
740
760
|
GenDataConfig | None,
|
|
741
761
|
self.ensemble_config.response_configs.get("gen_data", None),
|
|
742
762
|
),
|
|
763
|
+
cast(
|
|
764
|
+
RFTConfig | None,
|
|
765
|
+
self.ensemble_config.response_configs.get("rft", None),
|
|
766
|
+
),
|
|
743
767
|
self.time_map,
|
|
744
768
|
self.history_source,
|
|
745
769
|
)
|
|
@@ -791,6 +815,15 @@ class ErtConfig(BaseModel):
|
|
|
791
815
|
)
|
|
792
816
|
return self
|
|
793
817
|
|
|
818
|
+
@model_validator(mode="after")
|
|
819
|
+
def log_ensemble_config_contents(self) -> Self:
|
|
820
|
+
all_parameters = self.parameter_configurations_with_design_matrix
|
|
821
|
+
parameter_type_count = Counter(parameter.type for parameter in all_parameters)
|
|
822
|
+
logger.info(
|
|
823
|
+
f"EnsembleConfig contains parameters of type {dict(parameter_type_count)}"
|
|
824
|
+
)
|
|
825
|
+
return self
|
|
826
|
+
|
|
794
827
|
def __eq__(self, other: object) -> bool:
|
|
795
828
|
if not isinstance(other, ErtConfig):
|
|
796
829
|
return False
|
|
@@ -817,7 +850,7 @@ class ErtConfig(BaseModel):
|
|
|
817
850
|
def with_plugins(runtime_plugins: ErtRuntimePlugins) -> type[ErtConfig]:
|
|
818
851
|
class ErtConfigWithPlugins(ErtConfig):
|
|
819
852
|
PREINSTALLED_FORWARD_MODEL_STEPS: ClassVar[
|
|
820
|
-
Mapping[str,
|
|
853
|
+
Mapping[str, SiteInstalledForwardModelStep]
|
|
821
854
|
] = runtime_plugins.installed_forward_model_steps
|
|
822
855
|
PREINSTALLED_WORKFLOWS = dict(runtime_plugins.installed_workflow_jobs)
|
|
823
856
|
ENV_PR_FM_STEP: ClassVar[dict[str, dict[str, Any]]] = (
|
|
@@ -827,6 +860,9 @@ class ErtConfig(BaseModel):
|
|
|
827
860
|
)
|
|
828
861
|
ENV_VARS = dict(runtime_plugins.environment_variables)
|
|
829
862
|
QUEUE_OPTIONS = runtime_plugins.queue_options
|
|
863
|
+
PRIORITIZE_PRIVATE_IP_ADDRESS = (
|
|
864
|
+
runtime_plugins.prioritize_private_ip_address
|
|
865
|
+
)
|
|
830
866
|
|
|
831
867
|
ErtConfigWithPlugins.model_rebuild()
|
|
832
868
|
assert issubclass(ErtConfigWithPlugins, ErtConfig)
|
|
@@ -1025,11 +1061,30 @@ class ErtConfig(BaseModel):
|
|
|
1025
1061
|
if isinstance(cfg, GenKwConfig) and cfg.name in dm_params
|
|
1026
1062
|
]
|
|
1027
1063
|
if overwrite_params:
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1064
|
+
param_sampled = [
|
|
1065
|
+
k
|
|
1066
|
+
for k in overwrite_params
|
|
1067
|
+
if analysis_config.design_matrix.parameter_priority[k]
|
|
1068
|
+
== DataSource.SAMPLED
|
|
1069
|
+
]
|
|
1070
|
+
param_design = [
|
|
1071
|
+
k
|
|
1072
|
+
for k in overwrite_params
|
|
1073
|
+
if analysis_config.design_matrix.parameter_priority[k]
|
|
1074
|
+
== DataSource.DESIGN_MATRIX
|
|
1075
|
+
]
|
|
1076
|
+
if param_sampled:
|
|
1077
|
+
ConfigWarning.warn(
|
|
1078
|
+
f"Parameters {param_sampled} "
|
|
1079
|
+
"are also defined in design matrix, but due to the sampled"
|
|
1080
|
+
" priority they will remain as such."
|
|
1081
|
+
)
|
|
1082
|
+
if param_design:
|
|
1083
|
+
ConfigWarning.warn(
|
|
1084
|
+
f"Parameters {param_design} "
|
|
1085
|
+
"will be overridden by design matrix. This will cause "
|
|
1086
|
+
"updates to be turned off for these parameters."
|
|
1087
|
+
)
|
|
1033
1088
|
|
|
1034
1089
|
if dm_errors:
|
|
1035
1090
|
raise ConfigValidationError.from_collected(dm_errors)
|
|
@@ -1060,6 +1115,19 @@ class ErtConfig(BaseModel):
|
|
|
1060
1115
|
user_configured_.add(key)
|
|
1061
1116
|
env_vars[key] = substituter.substitute(val)
|
|
1062
1117
|
|
|
1118
|
+
prioritize_private_ip_address: bool = cls.PRIORITIZE_PRIVATE_IP_ADDRESS
|
|
1119
|
+
if ConfigKeys.PRIORITIZE_PRIVATE_IP_ADDRESS in config_dict:
|
|
1120
|
+
user_prioritize_private_ip_address = bool(
|
|
1121
|
+
config_dict[ConfigKeys.PRIORITIZE_PRIVATE_IP_ADDRESS]
|
|
1122
|
+
)
|
|
1123
|
+
if prioritize_private_ip_address != user_prioritize_private_ip_address:
|
|
1124
|
+
logger.warning(
|
|
1125
|
+
"PRIORITIZE_PRIVATE_IP_ADDRESS was overwritten by user: "
|
|
1126
|
+
f"{prioritize_private_ip_address} -> "
|
|
1127
|
+
f"{user_prioritize_private_ip_address}"
|
|
1128
|
+
)
|
|
1129
|
+
prioritize_private_ip_address = user_prioritize_private_ip_address
|
|
1130
|
+
|
|
1063
1131
|
try:
|
|
1064
1132
|
refcase = Refcase.from_config_dict(config_dict)
|
|
1065
1133
|
cls_config = cls(
|
|
@@ -1086,11 +1154,21 @@ class ErtConfig(BaseModel):
|
|
|
1086
1154
|
time_map=time_map,
|
|
1087
1155
|
history_source=history_source,
|
|
1088
1156
|
refcase=refcase,
|
|
1157
|
+
prioritize_private_ip_address=prioritize_private_ip_address,
|
|
1089
1158
|
)
|
|
1090
1159
|
|
|
1091
1160
|
# The observations are created here because create_observation_dataframes
|
|
1092
1161
|
# will perform additonal validation which needs the context in
|
|
1093
1162
|
# obs_configs which is stripped by pydantic
|
|
1163
|
+
has_rft_observations = any(
|
|
1164
|
+
isinstance(o, RFTObservation) for o in obs_configs
|
|
1165
|
+
)
|
|
1166
|
+
if has_rft_observations and "rft" not in ensemble_config.response_configs:
|
|
1167
|
+
ensemble_config.response_configs["rft"] = RFTConfig(
|
|
1168
|
+
input_files=[eclbase],
|
|
1169
|
+
data_to_read={},
|
|
1170
|
+
locations=[],
|
|
1171
|
+
)
|
|
1094
1172
|
cls_config._observations = create_observation_dataframes(
|
|
1095
1173
|
obs_configs,
|
|
1096
1174
|
refcase,
|
|
@@ -1098,6 +1176,10 @@ class ErtConfig(BaseModel):
|
|
|
1098
1176
|
GenDataConfig | None,
|
|
1099
1177
|
ensemble_config.response_configs.get("gen_data", None),
|
|
1100
1178
|
),
|
|
1179
|
+
cast(
|
|
1180
|
+
RFTConfig | None,
|
|
1181
|
+
ensemble_config.response_configs.get("rft", None),
|
|
1182
|
+
),
|
|
1101
1183
|
time_map,
|
|
1102
1184
|
history_source,
|
|
1103
1185
|
)
|
|
@@ -1275,7 +1357,7 @@ class ErtConfig(BaseModel):
|
|
|
1275
1357
|
@classmethod
|
|
1276
1358
|
def _read_user_config_contents(cls, user_config: str, file_name: str) -> ConfigDict:
|
|
1277
1359
|
return parse_contents(
|
|
1278
|
-
user_config, file_name=file_name, schema=
|
|
1360
|
+
user_config, file_name=file_name, schema=USER_CONFIG_SCHEMA
|
|
1279
1361
|
)
|
|
1280
1362
|
|
|
1281
1363
|
@classmethod
|
|
@@ -1365,8 +1447,10 @@ def uppercase_subkeys_and_stringify_subvalues(
|
|
|
1365
1447
|
|
|
1366
1448
|
|
|
1367
1449
|
def forward_model_step_from_config_contents(
|
|
1368
|
-
config_contents: str,
|
|
1369
|
-
|
|
1450
|
+
config_contents: str,
|
|
1451
|
+
config_file: str,
|
|
1452
|
+
name: str | None = None,
|
|
1453
|
+
) -> UserInstalledForwardModelStep:
|
|
1370
1454
|
if name is None:
|
|
1371
1455
|
name = os.path.basename(config_file)
|
|
1372
1456
|
|
|
@@ -1390,7 +1474,7 @@ def forward_model_step_from_config_contents(
|
|
|
1390
1474
|
environment = {k: v for [k, v] in content_dict.get("ENV", [])}
|
|
1391
1475
|
default_mapping = {k: v for [k, v] in content_dict.get("DEFAULT", [])}
|
|
1392
1476
|
|
|
1393
|
-
return
|
|
1477
|
+
return UserInstalledForwardModelStep(
|
|
1394
1478
|
name=name,
|
|
1395
1479
|
executable=content_dict["EXECUTABLE"],
|
|
1396
1480
|
stdin_file=content_dict.get("STDIN"),
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import importlib
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
from collections.abc import Iterator, Mapping, MutableMapping
|
|
7
|
+
from dataclasses import field
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from textwrap import dedent
|
|
10
|
+
from typing import TYPE_CHECKING, Any, Literal, Self
|
|
11
|
+
|
|
12
|
+
import networkx as nx
|
|
13
|
+
import numpy as np
|
|
14
|
+
import xarray as xr
|
|
15
|
+
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
16
|
+
from ropt.workflow import find_sampler_plugin
|
|
17
|
+
|
|
18
|
+
from ert.substitutions import substitute_runpath_name
|
|
19
|
+
|
|
20
|
+
from .parameter_config import ParameterConfig
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
import numpy.typing as npt
|
|
24
|
+
|
|
25
|
+
from ert.storage import Ensemble
|
|
26
|
+
|
|
27
|
+
Number = int | float
|
|
28
|
+
DataType = Mapping[str, Number | Mapping[str, Number]]
|
|
29
|
+
MutableDataType = MutableMapping[str, Number | MutableMapping[str, Number]]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class SamplerConfig(BaseModel):
|
|
33
|
+
backend: str | None = Field(
|
|
34
|
+
default=None,
|
|
35
|
+
description=dedent(
|
|
36
|
+
"""
|
|
37
|
+
[Deprecated]
|
|
38
|
+
|
|
39
|
+
The correct backend will be inferred by the method. If several backends
|
|
40
|
+
have a method named `A`, pick a specific backend `B` by putting `B/A` in
|
|
41
|
+
the `method` field.
|
|
42
|
+
"""
|
|
43
|
+
),
|
|
44
|
+
)
|
|
45
|
+
method: str = Field(
|
|
46
|
+
default="norm",
|
|
47
|
+
description=dedent(
|
|
48
|
+
"""
|
|
49
|
+
The sampling method or distribution used by the sampler backend.
|
|
50
|
+
|
|
51
|
+
The set of available methods depends on the sampler backend used. By
|
|
52
|
+
default a plugin based on `scipy.stats` is used, implementing the
|
|
53
|
+
following methods:
|
|
54
|
+
|
|
55
|
+
- From Probability Distributions:
|
|
56
|
+
- `norm`: Samples from a standard normal distribution (mean 0,
|
|
57
|
+
standard deviation 1).
|
|
58
|
+
- `truncnorm`: Samples from a truncated normal distribution
|
|
59
|
+
(mean 0, std. dev. 1), truncated to the range `[-1, 1]`.
|
|
60
|
+
- `uniform`: Samples from a uniform distribution in the range
|
|
61
|
+
`[-1, 1]`.
|
|
62
|
+
|
|
63
|
+
- From Quasi-Monte Carlo Sequences:
|
|
64
|
+
- `sobol`: Uses Sobol' sequences.
|
|
65
|
+
- `halton`: Uses Halton sequences.
|
|
66
|
+
- `lhs`: Uses Latin Hypercube Sampling.
|
|
67
|
+
|
|
68
|
+
Note: QMC samples are generated in the unit hypercube `[0, 1]^d`
|
|
69
|
+
and then scaled to the hypercube `[-1, 1]^d`.
|
|
70
|
+
"""
|
|
71
|
+
),
|
|
72
|
+
)
|
|
73
|
+
options: dict[str, Any] | None = Field(
|
|
74
|
+
default=None,
|
|
75
|
+
description=dedent(
|
|
76
|
+
"""
|
|
77
|
+
Specifies a dict of optional parameters for the sampler backend.
|
|
78
|
+
|
|
79
|
+
This dict of values is passed unchanged to the selected method in
|
|
80
|
+
the backend.
|
|
81
|
+
"""
|
|
82
|
+
),
|
|
83
|
+
)
|
|
84
|
+
shared: bool | None = Field(
|
|
85
|
+
default=None,
|
|
86
|
+
description=dedent(
|
|
87
|
+
"""
|
|
88
|
+
Whether to share perturbations between realizations.
|
|
89
|
+
"""
|
|
90
|
+
),
|
|
91
|
+
)
|
|
92
|
+
model_config = ConfigDict(extra="forbid")
|
|
93
|
+
|
|
94
|
+
@model_validator(mode="after")
|
|
95
|
+
def validate_backend_and_method(self) -> Self:
|
|
96
|
+
if self.backend is not None:
|
|
97
|
+
message = (
|
|
98
|
+
"sampler.backend is deprecated. "
|
|
99
|
+
"The correct backend will be inferred by the method. "
|
|
100
|
+
"If several backends have a method named A, you need to pick "
|
|
101
|
+
"a specific backend B by putting B/A in sampler.method."
|
|
102
|
+
)
|
|
103
|
+
print(message)
|
|
104
|
+
# Note: Importing EVEREST.everest
|
|
105
|
+
# leads to circular import, but we still wish to log
|
|
106
|
+
# from "everest" here as per old behavior.
|
|
107
|
+
# Can consider logging this as if from ERT,
|
|
108
|
+
# which is valid if we store SamplerConfig as part of
|
|
109
|
+
# EverestControl configs.
|
|
110
|
+
logging.getLogger("everest").warning(message)
|
|
111
|
+
|
|
112
|
+
# Update the default for backends that are not scipy:
|
|
113
|
+
if (
|
|
114
|
+
self.backend not in {None, "scipy"}
|
|
115
|
+
and "method" not in self.model_fields_set
|
|
116
|
+
):
|
|
117
|
+
self.method = "default"
|
|
118
|
+
|
|
119
|
+
if self.backend is not None:
|
|
120
|
+
self.method = f"{self.backend}/{self.method}"
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
plugin = find_sampler_plugin(f"{self.method}")
|
|
124
|
+
except ValueError:
|
|
125
|
+
raise
|
|
126
|
+
except Exception as exc:
|
|
127
|
+
ert_version = importlib.metadata.version("ert")
|
|
128
|
+
ropt_version = importlib.metadata.version("ropt")
|
|
129
|
+
msg = (
|
|
130
|
+
f"Error while initializing ropt:\n\n{exc}.\n\n"
|
|
131
|
+
"There may a be version mismatch between "
|
|
132
|
+
f"ERT ({ert_version}) and ropt ({ropt_version})\n"
|
|
133
|
+
"If the installation is correct, please report this as a bug."
|
|
134
|
+
)
|
|
135
|
+
raise RuntimeError(msg) from exc
|
|
136
|
+
|
|
137
|
+
if plugin is None:
|
|
138
|
+
raise ValueError(f"Sampler method '{self.method}' not found")
|
|
139
|
+
|
|
140
|
+
self.backend = None
|
|
141
|
+
|
|
142
|
+
return self
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class EverestControl(ParameterConfig):
|
|
146
|
+
"""Create an EverestControl for @key with the given @input_keys
|
|
147
|
+
|
|
148
|
+
@input_keys can be either a list of keys as strings or a dict with
|
|
149
|
+
keys as strings and a list of suffixes for each key.
|
|
150
|
+
If a list of strings is given, the order is preserved.
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
type: Literal["everest_parameters"] = "everest_parameters"
|
|
154
|
+
input_keys: list[str] = field(default_factory=list)
|
|
155
|
+
forward_init: bool = False
|
|
156
|
+
output_file: str = ""
|
|
157
|
+
forward_init_file: str = ""
|
|
158
|
+
update: bool = False
|
|
159
|
+
types: list[Literal["well_control", "generic_control"]]
|
|
160
|
+
initial_guesses: list[float]
|
|
161
|
+
control_types: list[Literal["real", "integer"]]
|
|
162
|
+
enabled: list[bool]
|
|
163
|
+
min: list[float]
|
|
164
|
+
max: list[float]
|
|
165
|
+
perturbation_types: list[Literal["absolute", "relative"]]
|
|
166
|
+
perturbation_magnitudes: list[float]
|
|
167
|
+
scaled_ranges: list[tuple[float, float]]
|
|
168
|
+
samplers: list[SamplerConfig | None]
|
|
169
|
+
|
|
170
|
+
# Set up for deprecation, but has to live here until support for the
|
|
171
|
+
# "dotdash" notation is removed for everest controls via everest config.
|
|
172
|
+
input_keys_dotdash: list[str] = field(default_factory=list)
|
|
173
|
+
|
|
174
|
+
@property
|
|
175
|
+
def parameter_keys(self) -> list[str]:
|
|
176
|
+
return self.input_keys
|
|
177
|
+
|
|
178
|
+
def read_from_runpath(
|
|
179
|
+
self, run_path: Path, real_nr: int, iteration: int
|
|
180
|
+
) -> xr.Dataset:
|
|
181
|
+
raise NotImplementedError
|
|
182
|
+
|
|
183
|
+
def write_to_runpath(
|
|
184
|
+
self, run_path: Path, real_nr: int, ensemble: Ensemble
|
|
185
|
+
) -> None:
|
|
186
|
+
file_path = run_path / substitute_runpath_name(
|
|
187
|
+
self.output_file, real_nr, ensemble.iteration
|
|
188
|
+
)
|
|
189
|
+
Path.mkdir(file_path.parent, exist_ok=True, parents=True)
|
|
190
|
+
|
|
191
|
+
data: MutableDataType = {}
|
|
192
|
+
for da in ensemble.load_parameters(self.name, real_nr)["values"]:
|
|
193
|
+
assert isinstance(da, xr.DataArray)
|
|
194
|
+
name = str(da.names.values)
|
|
195
|
+
try:
|
|
196
|
+
outer, inner = name.split("\0")
|
|
197
|
+
|
|
198
|
+
if outer not in data:
|
|
199
|
+
data[outer] = {}
|
|
200
|
+
data[outer][inner] = float(da) # type: ignore
|
|
201
|
+
except ValueError:
|
|
202
|
+
data[name] = float(da)
|
|
203
|
+
|
|
204
|
+
file_path.write_text(json.dumps(data), encoding="utf-8")
|
|
205
|
+
|
|
206
|
+
def create_storage_datasets(
|
|
207
|
+
self,
|
|
208
|
+
from_data: npt.NDArray[np.float64],
|
|
209
|
+
iens_active_index: npt.NDArray[np.int_],
|
|
210
|
+
) -> Iterator[tuple[int, xr.Dataset]]:
|
|
211
|
+
for i, realization in enumerate(iens_active_index):
|
|
212
|
+
yield (
|
|
213
|
+
int(realization),
|
|
214
|
+
xr.Dataset(
|
|
215
|
+
{
|
|
216
|
+
"values": ("names", from_data[:, i]),
|
|
217
|
+
"names": [
|
|
218
|
+
x.split(f"{self.name}.")[1].replace(".", "\0")
|
|
219
|
+
for x in self.parameter_keys
|
|
220
|
+
],
|
|
221
|
+
}
|
|
222
|
+
),
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
def load_parameters(
|
|
226
|
+
self, ensemble: Ensemble, realizations: npt.NDArray[np.int_]
|
|
227
|
+
) -> npt.NDArray[np.float64]:
|
|
228
|
+
raise NotImplementedError
|
|
229
|
+
|
|
230
|
+
def load_parameter_graph(self) -> nx.Graph[int]:
|
|
231
|
+
raise NotImplementedError
|
|
232
|
+
|
|
233
|
+
def __len__(self) -> int:
|
|
234
|
+
return len(self.input_keys)
|
|
@@ -11,19 +11,21 @@ from .response_config import InvalidResponseFile, ResponseConfig, ResponseMetada
|
|
|
11
11
|
from .responses_index import responses_index
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
class
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
class EverestResponse(ResponseConfig):
|
|
15
|
+
"""Base class for Everest response configurations."""
|
|
16
|
+
|
|
17
17
|
has_finalized_keys: bool = True
|
|
18
|
-
weights: list[float | None]
|
|
19
18
|
scales: list[float | None]
|
|
20
|
-
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def primary_key(self) -> list[str]:
|
|
22
|
+
return []
|
|
21
23
|
|
|
22
24
|
@property
|
|
23
25
|
def metadata(self) -> list[ResponseMetadata]:
|
|
24
26
|
return [
|
|
25
27
|
ResponseMetadata(
|
|
26
|
-
response_type=self.
|
|
28
|
+
response_type=self.type,
|
|
27
29
|
response_key=response_key,
|
|
28
30
|
finalized=self.has_finalized_keys,
|
|
29
31
|
filter_on=None,
|
|
@@ -52,7 +54,6 @@ class EverestObjectivesConfig(ResponseConfig):
|
|
|
52
54
|
)
|
|
53
55
|
|
|
54
56
|
errors = []
|
|
55
|
-
|
|
56
57
|
run_path_ = Path(run_path)
|
|
57
58
|
datasets_per_name = []
|
|
58
59
|
|
|
@@ -75,24 +76,32 @@ class EverestObjectivesConfig(ResponseConfig):
|
|
|
75
76
|
if all(isinstance(err, FileNotFoundError) for err in errors):
|
|
76
77
|
raise FileNotFoundError(
|
|
77
78
|
"Could not find one or more files/directories while reading "
|
|
78
|
-
f"{self.
|
|
79
|
+
f"{self.type}: {','.join([str(err) for err in errors])}"
|
|
79
80
|
)
|
|
80
81
|
else:
|
|
81
82
|
raise InvalidResponseFile(
|
|
82
83
|
"Error reading "
|
|
83
|
-
f"{self.
|
|
84
|
+
f"{self.type}, errors: {','.join([str(err) for err in errors])}"
|
|
84
85
|
)
|
|
85
86
|
|
|
86
87
|
combined = pl.concat(datasets_per_name)
|
|
87
88
|
return combined
|
|
88
89
|
|
|
89
|
-
@property
|
|
90
|
-
def response_type(self) -> str:
|
|
91
|
-
return "everest_objectives"
|
|
92
90
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
class EverestConstraintsConfig(EverestResponse):
|
|
92
|
+
type: Literal["everest_constraints"] = "everest_constraints"
|
|
93
|
+
targets: list[float | None]
|
|
94
|
+
upper_bounds: list[float]
|
|
95
|
+
lower_bounds: list[float]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
responses_index.add_response_type(EverestConstraintsConfig)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class EverestObjectivesConfig(EverestResponse):
|
|
102
|
+
type: Literal["everest_objectives"] = "everest_objectives"
|
|
103
|
+
weights: list[float | None]
|
|
104
|
+
objective_types: list[Literal["mean", "stddev"]]
|
|
96
105
|
|
|
97
106
|
|
|
98
107
|
responses_index.add_response_type(EverestObjectivesConfig)
|