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
|
@@ -6,6 +6,8 @@ import threading
|
|
|
6
6
|
import traceback
|
|
7
7
|
from collections import defaultdict
|
|
8
8
|
from collections.abc import Awaitable, Callable, Iterable, Sequence
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from math import ceil
|
|
9
11
|
from typing import Any, cast, get_args
|
|
10
12
|
|
|
11
13
|
import zmq.asyncio
|
|
@@ -15,6 +17,7 @@ from _ert.events import (
|
|
|
15
17
|
EESnapshot,
|
|
16
18
|
EESnapshotUpdate,
|
|
17
19
|
EnsembleCancelled,
|
|
20
|
+
EnsembleEvaluationWarning,
|
|
18
21
|
EnsembleFailed,
|
|
19
22
|
EnsembleStarted,
|
|
20
23
|
EnsembleSucceeded,
|
|
@@ -49,6 +52,13 @@ from .state import (
|
|
|
49
52
|
ENSEMBLE_STATE_STOPPED,
|
|
50
53
|
)
|
|
51
54
|
|
|
55
|
+
|
|
56
|
+
@dataclass(order=True)
|
|
57
|
+
class ParallelismViolation:
|
|
58
|
+
amount: float = 0
|
|
59
|
+
message: str = ""
|
|
60
|
+
|
|
61
|
+
|
|
52
62
|
logger = logging.getLogger(__name__)
|
|
53
63
|
|
|
54
64
|
EVENT_HANDLER = Callable[[list[SnapshotInputEvent]], Awaitable[None]]
|
|
@@ -68,6 +78,13 @@ class EventSentinel:
|
|
|
68
78
|
|
|
69
79
|
class EnsembleEvaluator:
|
|
70
80
|
BATCHING_INTERVAL = 0.5
|
|
81
|
+
DEFAULT_SLEEP_PERIOD = 0.1
|
|
82
|
+
|
|
83
|
+
# These properties help us determine whether the user
|
|
84
|
+
# has misconfigured NUM_CPU in their config.
|
|
85
|
+
ALLOWED_CPU_OVERSPENDING = 1.05
|
|
86
|
+
MINIMUM_WALLTIME_SECONDS = 30 # Information is only polled every 5 sec
|
|
87
|
+
CPU_OVERSPENDING_WARNING_THRESHOLD = 1.50
|
|
71
88
|
|
|
72
89
|
def __init__(
|
|
73
90
|
self,
|
|
@@ -123,6 +140,7 @@ class EnsembleEvaluator:
|
|
|
123
140
|
submit_sleep=self.ensemble._queue_config.submit_sleep,
|
|
124
141
|
ens_id=self.ensemble.id_,
|
|
125
142
|
)
|
|
143
|
+
self.max_parallelism_violation = ParallelismViolation()
|
|
126
144
|
|
|
127
145
|
async def _publisher(self) -> None:
|
|
128
146
|
heartbeat_interval = 0.1
|
|
@@ -145,6 +163,11 @@ class EnsembleEvaluator:
|
|
|
145
163
|
self._evaluation_result.set_result(True)
|
|
146
164
|
return
|
|
147
165
|
|
|
166
|
+
elif isinstance(event, EnsembleEvaluationWarning):
|
|
167
|
+
if self._event_handler:
|
|
168
|
+
self._event_handler(event)
|
|
169
|
+
self._events_to_send.task_done()
|
|
170
|
+
|
|
148
171
|
elif type(event) in {
|
|
149
172
|
EESnapshot,
|
|
150
173
|
EESnapshotUpdate,
|
|
@@ -191,7 +214,7 @@ class EnsembleEvaluator:
|
|
|
191
214
|
await self._signal_cancel()
|
|
192
215
|
logger.debug("Run model cancelled - during evaluation - cancel sent")
|
|
193
216
|
self._end_event.clear()
|
|
194
|
-
await asyncio.sleep(
|
|
217
|
+
await asyncio.sleep(self.DEFAULT_SLEEP_PERIOD)
|
|
195
218
|
|
|
196
219
|
async def _send_terminate_message_to_dispatchers(self) -> None:
|
|
197
220
|
event = TERMINATE_MSG
|
|
@@ -244,6 +267,7 @@ class EnsembleEvaluator:
|
|
|
244
267
|
event_handler[event_type] = func
|
|
245
268
|
|
|
246
269
|
set_event_handler(set(get_args(FMEvent | RealizationEvent)), self._fm_handler)
|
|
270
|
+
set_event_handler({EnsembleEvaluationWarning}, self._warning_event_handler)
|
|
247
271
|
set_event_handler({EnsembleStarted}, self._started_handler)
|
|
248
272
|
set_event_handler({EnsembleSucceeded}, self._stopped_handler)
|
|
249
273
|
set_event_handler({EnsembleCancelled}, self._cancelled_handler)
|
|
@@ -264,7 +288,7 @@ class EnsembleEvaluator:
|
|
|
264
288
|
batch.append((function, event))
|
|
265
289
|
self._events.task_done()
|
|
266
290
|
except asyncio.QueueEmpty:
|
|
267
|
-
await asyncio.sleep(
|
|
291
|
+
await asyncio.sleep(self.DEFAULT_SLEEP_PERIOD)
|
|
268
292
|
continue
|
|
269
293
|
self._complete_batch.set()
|
|
270
294
|
await self._batch_processing_queue.put(batch)
|
|
@@ -274,6 +298,12 @@ class EnsembleEvaluator:
|
|
|
274
298
|
async def _fm_handler(self, events: Sequence[FMEvent | RealizationEvent]) -> None:
|
|
275
299
|
await self._append_message(self.ensemble.update_snapshot(events))
|
|
276
300
|
|
|
301
|
+
async def _warning_event_handler(
|
|
302
|
+
self, events: Sequence[EnsembleEvaluationWarning]
|
|
303
|
+
) -> None:
|
|
304
|
+
for event in events:
|
|
305
|
+
await self._events_to_send.put(event)
|
|
306
|
+
|
|
277
307
|
async def _started_handler(self, events: Sequence[EnsembleStarted]) -> None:
|
|
278
308
|
if self.ensemble.status != ENSEMBLE_STATE_FAILED:
|
|
279
309
|
await self._append_message(self.ensemble.update_snapshot(events))
|
|
@@ -288,11 +318,9 @@ class EnsembleEvaluator:
|
|
|
288
318
|
memory_usage = fm_step.get(ids.MAX_MEMORY_USAGE) or "-1"
|
|
289
319
|
max_memory_usage = max(int(memory_usage), max_memory_usage)
|
|
290
320
|
|
|
291
|
-
|
|
321
|
+
self.detect_overspent_cpu(
|
|
292
322
|
self.ensemble.reals[int(real_id)].num_cpu, real_id, fm_step
|
|
293
323
|
)
|
|
294
|
-
if self.ensemble.queue_system != QueueSystem.LOCAL and cpu_message:
|
|
295
|
-
logger.warning(cpu_message)
|
|
296
324
|
|
|
297
325
|
logger.info(
|
|
298
326
|
"Ensemble ran with maximum memory usage for a "
|
|
@@ -368,6 +396,7 @@ class EnsembleEvaluator:
|
|
|
368
396
|
logger.warning(
|
|
369
397
|
"Evaluator receiver closed, no new messages are received"
|
|
370
398
|
)
|
|
399
|
+
return # The socket is closed, and we won't re-establish it.
|
|
371
400
|
else:
|
|
372
401
|
logger.error(f"Unexpected error when listening to messages: {e}")
|
|
373
402
|
except asyncio.CancelledError:
|
|
@@ -416,7 +445,7 @@ class EnsembleEvaluator:
|
|
|
416
445
|
while True:
|
|
417
446
|
if self._evaluation_result.done():
|
|
418
447
|
break
|
|
419
|
-
await asyncio.sleep(
|
|
448
|
+
await asyncio.sleep(self.DEFAULT_SLEEP_PERIOD)
|
|
420
449
|
logger.debug("Async server exiting.")
|
|
421
450
|
finally:
|
|
422
451
|
try:
|
|
@@ -647,27 +676,50 @@ class EnsembleEvaluator:
|
|
|
647
676
|
else:
|
|
648
677
|
await self._events.put(EnsembleCancelled(ensemble=self.ensemble.id_))
|
|
649
678
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
end_time = fm_step.get(ids.END_TIME)
|
|
659
|
-
if start_time is None or end_time is None:
|
|
660
|
-
return ""
|
|
661
|
-
duration = (end_time - start_time).total_seconds()
|
|
662
|
-
if duration <= minimum_wallclock_time_seconds:
|
|
663
|
-
return ""
|
|
664
|
-
cpu_seconds = fm_step.get(ids.CPU_SECONDS) or 0.0
|
|
665
|
-
parallelization_obtained = cpu_seconds / duration
|
|
666
|
-
if parallelization_obtained > num_cpu * allowed_overspending:
|
|
667
|
-
return (
|
|
668
|
-
f"Misconfigured NUM_CPU, forward model step '{fm_step.get(ids.NAME)}' for "
|
|
669
|
-
f"realization {real_id} spent {cpu_seconds} cpu seconds "
|
|
670
|
-
f"with wall clock duration {duration:.1f} seconds, "
|
|
671
|
-
f"a factor of {parallelization_obtained:.2f}, while NUM_CPU was {num_cpu}."
|
|
679
|
+
def detect_overspent_cpu(
|
|
680
|
+
self, num_cpu: int, real_id: str, fm_step: FMStepSnapshot
|
|
681
|
+
) -> None:
|
|
682
|
+
"""Produces a message warning about misconfiguration of NUM_CPU if
|
|
683
|
+
so is detected. Returns an empty string if everything is ok."""
|
|
684
|
+
allowed_overspending = self.ALLOWED_CPU_OVERSPENDING * num_cpu
|
|
685
|
+
overspending_warning_threshold = (
|
|
686
|
+
self.CPU_OVERSPENDING_WARNING_THRESHOLD * num_cpu
|
|
672
687
|
)
|
|
673
|
-
|
|
688
|
+
|
|
689
|
+
start_time = fm_step.get(ids.START_TIME)
|
|
690
|
+
|
|
691
|
+
end_time = fm_step.get(ids.END_TIME)
|
|
692
|
+
if start_time is None or end_time is None:
|
|
693
|
+
return
|
|
694
|
+
|
|
695
|
+
duration = (end_time - start_time).total_seconds()
|
|
696
|
+
if duration <= self.MINIMUM_WALLTIME_SECONDS:
|
|
697
|
+
return
|
|
698
|
+
|
|
699
|
+
cpu_seconds = fm_step.get(ids.CPU_SECONDS) or 0.0
|
|
700
|
+
parallelization_obtained = cpu_seconds / duration
|
|
701
|
+
if (
|
|
702
|
+
parallelization_obtained > allowed_overspending
|
|
703
|
+
and self.ensemble.queue_system != QueueSystem.LOCAL
|
|
704
|
+
):
|
|
705
|
+
logger.warning(
|
|
706
|
+
f"Misconfigured NUM_CPU, forward model step '{fm_step.get(ids.NAME)}' "
|
|
707
|
+
f"for realization {real_id} spent {cpu_seconds} cpu seconds "
|
|
708
|
+
f"with wall clock duration {duration:.1f} seconds, a factor of "
|
|
709
|
+
f"{parallelization_obtained:.2f}, while NUM_CPU was {num_cpu}."
|
|
710
|
+
)
|
|
711
|
+
if parallelization_obtained > overspending_warning_threshold:
|
|
712
|
+
warning_msg = (
|
|
713
|
+
"Overusage of CPUs detected!\n"
|
|
714
|
+
f"Your experiment has used up to {ceil(parallelization_obtained)} "
|
|
715
|
+
f"CPUs in step '{fm_step.get(ids.NAME)}', "
|
|
716
|
+
f"while the Ert config has only requested {num_cpu}.\n"
|
|
717
|
+
f"This means your experiment is consuming more CPU-resources than "
|
|
718
|
+
f"requested and will slow down other users experiments.\n"
|
|
719
|
+
f"We kindly ask you to set "
|
|
720
|
+
f"NUM_CPU={ceil(parallelization_obtained)} in your Ert config."
|
|
721
|
+
)
|
|
722
|
+
self.max_parallelism_violation = max(
|
|
723
|
+
self.max_parallelism_violation,
|
|
724
|
+
ParallelismViolation(parallelization_obtained, warning_msg),
|
|
725
|
+
)
|
ert/ensemble_evaluator/event.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from collections.abc import Mapping
|
|
2
|
+
from datetime import datetime
|
|
2
3
|
from typing import Any, Literal
|
|
3
4
|
|
|
4
5
|
from pydantic import BaseModel, ConfigDict, field_serializer, field_validator
|
|
@@ -42,6 +43,11 @@ class SnapshotUpdateEvent(_UpdateEvent):
|
|
|
42
43
|
event_type: Literal["SnapshotUpdateEvent"] = "SnapshotUpdateEvent"
|
|
43
44
|
|
|
44
45
|
|
|
46
|
+
class StartEvent(BaseModel):
|
|
47
|
+
event_type: Literal["StartEvent"] = "StartEvent"
|
|
48
|
+
timestamp: datetime
|
|
49
|
+
|
|
50
|
+
|
|
45
51
|
class EndEvent(BaseModel):
|
|
46
52
|
model_config = ConfigDict(arbitrary_types_allowed=True, extra="forbid")
|
|
47
53
|
event_type: Literal["EndEvent"] = "EndEvent"
|
|
@@ -12,6 +12,7 @@ from _ert.events import (
|
|
|
12
12
|
EESnapshot,
|
|
13
13
|
EESnapshotUpdate,
|
|
14
14
|
EnsembleCancelled,
|
|
15
|
+
EnsembleEvaluationWarning,
|
|
15
16
|
EnsembleEvent,
|
|
16
17
|
EnsembleFailed,
|
|
17
18
|
EnsembleStarted,
|
|
@@ -424,7 +425,8 @@ class EnsembleSnapshot:
|
|
|
424
425
|
|
|
425
426
|
elif e_type in get_args(EnsembleEvent):
|
|
426
427
|
event = cast(EnsembleEvent, event)
|
|
427
|
-
|
|
428
|
+
if not isinstance(event, EnsembleEvaluationWarning):
|
|
429
|
+
self._ensemble_state = _ENSEMBLE_TYPE_EVENT_TO_STATUS[type(event)]
|
|
428
430
|
elif type(event) is EESnapshotUpdate:
|
|
429
431
|
self.merge_snapshot(EnsembleSnapshot.from_nested_dict(event.snapshot))
|
|
430
432
|
elif type(event) is EESnapshot:
|
ert/ensemble_evaluator/state.py
CHANGED
|
@@ -7,6 +7,7 @@ COLOR_RUNNING: Final = (255, 255, 153)
|
|
|
7
7
|
COLOR_UNKNOWN: Final = (128, 128, 128)
|
|
8
8
|
COLOR_WAITING: Final = (164, 200, 255)
|
|
9
9
|
COLOR_CANCELLED: Final = (235, 242, 246)
|
|
10
|
+
COLOR_WARNING: Final = (255, 103, 0)
|
|
10
11
|
|
|
11
12
|
ENSEMBLE_STATE_CANCELLED: Final = "Cancelled"
|
|
12
13
|
ENSEMBLE_STATE_FAILED: Final = "Failed"
|
ert/field_utils/__init__.py
CHANGED
|
@@ -4,20 +4,28 @@ from .field_file_format import FieldFileFormat
|
|
|
4
4
|
from .field_utils import (
|
|
5
5
|
ErtboxParameters,
|
|
6
6
|
Shape,
|
|
7
|
+
calc_rho_for_2d_grid_layer,
|
|
7
8
|
calculate_ertbox_parameters,
|
|
8
9
|
get_shape,
|
|
10
|
+
localization_scaling_function,
|
|
9
11
|
read_field,
|
|
10
12
|
read_mask,
|
|
11
13
|
save_field,
|
|
14
|
+
transform_local_ellipse_angle_to_local_coords,
|
|
15
|
+
transform_positions_to_local_field_coordinates,
|
|
12
16
|
)
|
|
13
17
|
|
|
14
18
|
__all__ = [
|
|
15
19
|
"ErtboxParameters",
|
|
16
20
|
"FieldFileFormat",
|
|
17
21
|
"Shape",
|
|
22
|
+
"calc_rho_for_2d_grid_layer",
|
|
18
23
|
"calculate_ertbox_parameters",
|
|
19
24
|
"get_shape",
|
|
25
|
+
"localization_scaling_function",
|
|
20
26
|
"read_field",
|
|
21
27
|
"read_mask",
|
|
22
28
|
"save_field",
|
|
29
|
+
"transform_local_ellipse_angle_to_local_coords",
|
|
30
|
+
"transform_positions_to_local_field_coordinates",
|
|
23
31
|
]
|
ert/field_utils/field_utils.py
CHANGED
|
@@ -15,7 +15,7 @@ from .roff_io import export_roff, import_roff
|
|
|
15
15
|
|
|
16
16
|
if TYPE_CHECKING:
|
|
17
17
|
import numpy.typing as npt
|
|
18
|
-
import xtgeo
|
|
18
|
+
import xtgeo
|
|
19
19
|
|
|
20
20
|
_PathLike: TypeAlias = str | os.PathLike[str]
|
|
21
21
|
|
|
@@ -216,7 +216,6 @@ def calculate_ertbox_parameters(
|
|
|
216
216
|
def read_field(
|
|
217
217
|
field_path: _PathLike,
|
|
218
218
|
field_name: str,
|
|
219
|
-
mask: npt.NDArray[np.bool_],
|
|
220
219
|
shape: Shape,
|
|
221
220
|
) -> np.ma.MaskedArray[Any, np.dtype[np.float32]]:
|
|
222
221
|
path = Path(field_path)
|
|
@@ -239,7 +238,7 @@ def read_field(
|
|
|
239
238
|
ext = path.suffix
|
|
240
239
|
raise ValueError(f'Could not read {field_path}. Unrecognized suffix "{ext}"')
|
|
241
240
|
|
|
242
|
-
return np.ma.MaskedArray(data=values,
|
|
241
|
+
return np.ma.MaskedArray(data=values, fill_value=np.nan)
|
|
243
242
|
|
|
244
243
|
|
|
245
244
|
def save_field(
|
|
@@ -263,3 +262,213 @@ def save_field(
|
|
|
263
262
|
export_grdecl(field, output_path, field_name, binary=True)
|
|
264
263
|
else:
|
|
265
264
|
raise ValueError(f"Cannot export, invalid file format: {file_format}")
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def transform_positions_to_local_field_coordinates(
|
|
268
|
+
coordsys_origin: tuple[float, float],
|
|
269
|
+
coordsys_rotation_angle: float,
|
|
270
|
+
utmx: npt.NDArray[np.float64],
|
|
271
|
+
utmy: npt.NDArray[np.float64],
|
|
272
|
+
) -> tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]:
|
|
273
|
+
"""Calculates coordinate transformation from global to local coordinates.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
coordys_origin: (x,y) coordinate of local coordinate
|
|
277
|
+
origin in global coordinates.
|
|
278
|
+
coordsys_rotation_angle: Angle for how much the local x-axis is rotated
|
|
279
|
+
anti-clockwise relative to the global x-axis in degrees.
|
|
280
|
+
utmx: vector of x-coordinates in global coordinates.
|
|
281
|
+
utmy: vector of y-coordinates in global coordinates.
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
First vector is local x-coordinates and second vector is local y-coordinates.
|
|
285
|
+
"""
|
|
286
|
+
# Translate
|
|
287
|
+
x1 = utmx - coordsys_origin[0]
|
|
288
|
+
y1 = utmy - coordsys_origin[1]
|
|
289
|
+
# Rotate
|
|
290
|
+
# Input angle is the local coordinate systems rotation
|
|
291
|
+
# anticlockwise relative to global x-axis in degrees
|
|
292
|
+
rotation_of_ertbox = coordsys_rotation_angle
|
|
293
|
+
rotation_angle = np.deg2rad(rotation_of_ertbox)
|
|
294
|
+
cos_theta = np.cos(rotation_angle)
|
|
295
|
+
sin_theta = np.sin(rotation_angle)
|
|
296
|
+
x2 = x1 * cos_theta + y1 * sin_theta
|
|
297
|
+
y2 = -x1 * sin_theta + y1 * cos_theta
|
|
298
|
+
return x2, y2
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def transform_local_ellipse_angle_to_local_coords(
|
|
302
|
+
coordsys_rotation_angle: float,
|
|
303
|
+
ellipse_anisotropy_angle: npt.NDArray[np.float64],
|
|
304
|
+
) -> npt.NDArray[np.float64]:
|
|
305
|
+
"""Calculate angles relative to local coordinate system.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
coordsys_rotation_angle: Local coordinate systems rotation angle
|
|
309
|
+
relative to the global coordinate system.
|
|
310
|
+
ellipse_anisotropy_angle: Vector of input angles in global coordinates.
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
Vector of output angles relative to the local coordinate system.
|
|
314
|
+
"""
|
|
315
|
+
# Both angles measured anti-clock from global coordinate systems x-axis in degrees
|
|
316
|
+
return ellipse_anisotropy_angle - coordsys_rotation_angle
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def localization_scaling_function(
|
|
320
|
+
distances: npt.NDArray[np.float64],
|
|
321
|
+
) -> npt.NDArray[np.float64]:
|
|
322
|
+
"""Calculate scaling factor to be used as values in
|
|
323
|
+
RHO matrix in distance-based localization.
|
|
324
|
+
The scaling function implements the commonly
|
|
325
|
+
used function published by Gaspari and Cohn.
|
|
326
|
+
For input normalized distance >= 2, the value will be 0.
|
|
327
|
+
|
|
328
|
+
Args:
|
|
329
|
+
distances: Vector of values for normalized distances.
|
|
330
|
+
|
|
331
|
+
Returns:
|
|
332
|
+
Values of scaling factors for each value of input distance.
|
|
333
|
+
"""
|
|
334
|
+
# "gaspari-cohn"
|
|
335
|
+
# Commonly used in distance-based localization
|
|
336
|
+
# Is exact 0 for normalized distance > 2.
|
|
337
|
+
scaling_factor = distances
|
|
338
|
+
d2 = distances**2
|
|
339
|
+
d3 = d2 * distances
|
|
340
|
+
d4 = d3 * distances
|
|
341
|
+
d5 = d4 * distances
|
|
342
|
+
s = -1 / 4 * d5 + 1 / 2 * d4 + 5 / 8 * d3 - 5 / 3 * d2 + 1
|
|
343
|
+
scaling_factor[distances <= 1] = s[distances <= 1]
|
|
344
|
+
s = (
|
|
345
|
+
1 / 12 * d5
|
|
346
|
+
- 1 / 2 * d4
|
|
347
|
+
+ 5 / 8 * d3
|
|
348
|
+
+ 5 / 3 * d2
|
|
349
|
+
- 5 * distances
|
|
350
|
+
+ 4
|
|
351
|
+
- 2 / 3 * 1 / distances
|
|
352
|
+
)
|
|
353
|
+
scaling_factor[(distances > 1) & (distances <= 2)] = s[
|
|
354
|
+
(distances > 1) & (distances <= 2)
|
|
355
|
+
]
|
|
356
|
+
scaling_factor[distances > 2] = 0.0
|
|
357
|
+
|
|
358
|
+
return scaling_factor
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def calc_rho_for_2d_grid_layer(
|
|
362
|
+
nx: int,
|
|
363
|
+
ny: int,
|
|
364
|
+
xinc: float,
|
|
365
|
+
yinc: float,
|
|
366
|
+
obs_xpos: npt.NDArray[np.float64],
|
|
367
|
+
obs_ypos: npt.NDArray[np.float64],
|
|
368
|
+
obs_main_range: npt.NDArray[np.float64],
|
|
369
|
+
obs_perp_range: npt.NDArray[np.float64],
|
|
370
|
+
obs_anisotropy_angle: npt.NDArray[np.float64],
|
|
371
|
+
right_handed_grid_indexing: bool = True,
|
|
372
|
+
) -> npt.NDArray[np.float64]:
|
|
373
|
+
"""Calculate scaling values (RHO matrix elements) for a set of observations
|
|
374
|
+
with associated localization ellipse. The method will first
|
|
375
|
+
calculate the distances from each observation position to each grid cell
|
|
376
|
+
center point of all grid cells for a 2D grid.
|
|
377
|
+
The localization method will only consider lateral distances, and it is
|
|
378
|
+
therefore sufficient to calculate the distances in 2D.
|
|
379
|
+
All input observation positions are in the local grid coordinate system
|
|
380
|
+
to simplify the calculation of the distances.
|
|
381
|
+
|
|
382
|
+
The position: xpos[n], ypos[n] and
|
|
383
|
+
localization ellipse defined by obs_main_range[n],obs_perp_range[n],
|
|
384
|
+
obs_anisotropy_angle[n]) refers to observation[n].
|
|
385
|
+
|
|
386
|
+
The distance between an observation with index n and a grid cell (i,j) is
|
|
387
|
+
d[m,n] = dist((xpos_obs[n],ypos_obs[n]),(xpos_field[i,j],ypos_field[i,j]))
|
|
388
|
+
|
|
389
|
+
RHO[[m,n] = scaling(d)
|
|
390
|
+
where m = j + i * ny for left-handed grid index origo and
|
|
391
|
+
m = (ny - j - 1) + i * ny for right-handed grid index origo
|
|
392
|
+
Note that since d[m,n] does only depend on observation index n and
|
|
393
|
+
grid cell index (i,j). The values for RHO is
|
|
394
|
+
calculated for the combination ((i,j), n) and this covers
|
|
395
|
+
one grid layer in ertbox grid or a 2D surface grid.
|
|
396
|
+
|
|
397
|
+
Args:
|
|
398
|
+
nx: Number of grid cells in x-direction of local coordinate system.
|
|
399
|
+
ny: Number of grid cells in y-direction of local coordinate system.
|
|
400
|
+
xinc: Grid cell size in x-direction.
|
|
401
|
+
yinc: Grid cell size in y-direction.
|
|
402
|
+
obs_xpos: Observations x coordinates in local coordinates
|
|
403
|
+
obs_ypos: Observatiopns y coordinates in local coordinates
|
|
404
|
+
obs_main_range: Localization ellipse first range
|
|
405
|
+
obs_perp_range: Localization ellipse second range
|
|
406
|
+
obs_anisotropy_angle: Localization ellipse orientation relative
|
|
407
|
+
to local coordinate system in degrees
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
Rho matrix values for one layer of the 3D ertbox grid or for a 2D surface grid.
|
|
411
|
+
"""
|
|
412
|
+
# Center points of each grid cell in field parameter grid
|
|
413
|
+
x_local = (np.arange(nx, dtype=np.float64) + 0.5) * xinc
|
|
414
|
+
if right_handed_grid_indexing:
|
|
415
|
+
# y coordinate descreases from max to min
|
|
416
|
+
y_local = (np.arange(ny - 1, -1, -1, dtype=np.float64) + 0.5) * yinc
|
|
417
|
+
else:
|
|
418
|
+
# y coordinate increases from min to max
|
|
419
|
+
y_local = (np.arange(ny, dtype=np.float64) + 0.5) * yinc
|
|
420
|
+
mesh_x_coord, mesh_y_coord = np.meshgrid(x_local, y_local, indexing="ij")
|
|
421
|
+
|
|
422
|
+
# Number of observations
|
|
423
|
+
nobs = len(obs_xpos)
|
|
424
|
+
assert nobs == len(obs_ypos), (
|
|
425
|
+
"Number of coordinates must match number of observations"
|
|
426
|
+
)
|
|
427
|
+
assert nobs == len(obs_anisotropy_angle), (
|
|
428
|
+
"Number of ellipse orientation angles must match number of observations"
|
|
429
|
+
)
|
|
430
|
+
assert nobs == len(obs_main_range), (
|
|
431
|
+
"Number of ellipse main range values must match number of observations"
|
|
432
|
+
)
|
|
433
|
+
assert nobs == len(obs_perp_range), (
|
|
434
|
+
"Number of ellipse second range values must match number of observations"
|
|
435
|
+
)
|
|
436
|
+
assert np.all(obs_main_range > 0.0), (
|
|
437
|
+
"All range values for all observations must be positive"
|
|
438
|
+
)
|
|
439
|
+
assert np.all(obs_perp_range > 0.0), (
|
|
440
|
+
"All range values for all observations must be positive"
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
# Expand grid coordinates to match observations
|
|
444
|
+
mesh_x_coord_flat = mesh_x_coord.flatten()[:, np.newaxis] # (nx * ny, 1)
|
|
445
|
+
mesh_y_coord_flat = mesh_y_coord.flatten()[:, np.newaxis] # (nx * ny, 1)
|
|
446
|
+
|
|
447
|
+
# Observation coordinates and parameters
|
|
448
|
+
obs_xpos = obs_xpos[np.newaxis, :] # (1, nobs)
|
|
449
|
+
obs_ypos = obs_ypos[np.newaxis, :] # (1, nobs)
|
|
450
|
+
obs_main_range = obs_main_range[np.newaxis, :] # (1, nobs)
|
|
451
|
+
obs_perp_range = obs_perp_range[np.newaxis, :] # (1, nobs)
|
|
452
|
+
obs_anisotropy_angle = obs_anisotropy_angle[np.newaxis, :] # (1, nobs)
|
|
453
|
+
|
|
454
|
+
# Compute displacement between grid points and observations
|
|
455
|
+
dX = mesh_x_coord_flat - obs_xpos # (nx * ny, nobs)
|
|
456
|
+
dY = mesh_y_coord_flat - obs_ypos # (nx * ny, nobs)
|
|
457
|
+
|
|
458
|
+
# Compute rotation parameters
|
|
459
|
+
rotation = np.deg2rad(obs_anisotropy_angle)
|
|
460
|
+
cos_angle = np.cos(rotation) # (1, nobs)
|
|
461
|
+
sin_angle = np.sin(rotation) # (1, nobs)
|
|
462
|
+
|
|
463
|
+
# Rotate and scale displacements to local coordinate system defined
|
|
464
|
+
# by the two half axes of the influence ellipse. First coordinate (local x) is in
|
|
465
|
+
# direction defined by anisotropy angle and local y is perpendicular to that.
|
|
466
|
+
# Scale the distance by the ranges to get a normalized distance
|
|
467
|
+
# (with value 1 at the edge of the ellipse)
|
|
468
|
+
dX_ellipse = (dX * cos_angle + dY * sin_angle) / obs_main_range # (nx * ny, nobs)
|
|
469
|
+
dY_ellipse = (-dX * sin_angle + dY * cos_angle) / obs_perp_range # (nx * ny, nobs)
|
|
470
|
+
|
|
471
|
+
# Compute distances in the elliptical coordinate system
|
|
472
|
+
distances = np.hypot(dX_ellipse, dY_ellipse) # (nx * ny, nobs)
|
|
473
|
+
# Apply the scaling function
|
|
474
|
+
return localization_scaling_function(distances).reshape((nx, ny, nobs))
|
ert/field_utils/roff_io.py
CHANGED
|
@@ -23,7 +23,7 @@ def export_roff(
|
|
|
23
23
|
binary: bool,
|
|
24
24
|
) -> None:
|
|
25
25
|
dimensions = data.shape
|
|
26
|
-
data = np.flip(data, -1).ravel()
|
|
26
|
+
data = np.flip(data, -1).ravel()
|
|
27
27
|
data = data.astype(np.float32).filled(RMS_UNDEFINED_FLOAT) # type: ignore
|
|
28
28
|
if not np.isfinite(data).all():
|
|
29
29
|
raise ValueError(
|
ert/gui/__init__.py
CHANGED
|
@@ -20,12 +20,15 @@ else:
|
|
|
20
20
|
|
|
21
21
|
def is_high_contrast_mode() -> bool:
|
|
22
22
|
app = cast(QWidget, QApplication.instance())
|
|
23
|
-
return
|
|
23
|
+
return (
|
|
24
|
+
app is not None
|
|
25
|
+
and app.palette().color(QPalette.ColorRole.Window).lightness() > 245
|
|
26
|
+
)
|
|
24
27
|
|
|
25
28
|
|
|
26
29
|
def is_dark_mode() -> bool:
|
|
27
30
|
app = cast(QWidget, QApplication.instance())
|
|
28
|
-
return app.palette().base().color().value() < 70
|
|
31
|
+
return app is not None and app.palette().base().color().value() < 70
|
|
29
32
|
|
|
30
33
|
|
|
31
34
|
__version__ = ert.shared.__version__
|
ert/gui/ertnotifier.py
CHANGED
ert/gui/ertwidgets/__init__.py
CHANGED
|
@@ -1,31 +1,34 @@
|
|
|
1
|
-
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
2
4
|
from PyQt6.QtCore import Qt
|
|
3
5
|
from PyQt6.QtGui import QCursor
|
|
4
6
|
from PyQt6.QtWidgets import QApplication
|
|
5
|
-
from typing import Any
|
|
6
|
-
from collections.abc import Callable
|
|
7
|
-
|
|
8
7
|
|
|
9
|
-
from .closabledialog import ClosableDialog
|
|
10
8
|
from .analysismoduleedit import AnalysisModuleEdit
|
|
11
|
-
from .searchbox import SearchBox
|
|
12
|
-
from .ensembleselector import EnsembleSelector
|
|
13
9
|
from .checklist import CheckList
|
|
14
|
-
from .
|
|
15
|
-
from .
|
|
16
|
-
from .
|
|
10
|
+
from .closabledialog import ClosableDialog
|
|
11
|
+
from .copy_button import CopyButton
|
|
12
|
+
from .copyablelabel import CopyableLabel
|
|
13
|
+
from .create_experiment_dialog import CreateExperimentDialog
|
|
17
14
|
from .customdialog import CustomDialog
|
|
18
|
-
from .
|
|
15
|
+
from .ensembleselector import EnsembleSelector
|
|
16
|
+
from .listeditbox import ListEditBox
|
|
19
17
|
from .models import (
|
|
20
|
-
TextModel,
|
|
21
18
|
ActiveRealizationsModel,
|
|
19
|
+
ErtSummary,
|
|
20
|
+
PathModel,
|
|
21
|
+
SelectableListModel,
|
|
22
22
|
TargetEnsembleModel,
|
|
23
|
+
TextModel,
|
|
23
24
|
ValueModel,
|
|
24
|
-
SelectableListModel,
|
|
25
|
-
PathModel,
|
|
26
25
|
)
|
|
27
|
-
from .
|
|
28
|
-
from .
|
|
26
|
+
from .parameterviewer import get_parameters_button
|
|
27
|
+
from .pathchooser import PathChooser
|
|
28
|
+
from .searchbox import SearchBox
|
|
29
|
+
from .stringbox import StringBox
|
|
30
|
+
from .suggestor import Suggestor
|
|
31
|
+
from .textbox import TextBox
|
|
29
32
|
|
|
30
33
|
|
|
31
34
|
def showWaitCursorWhileWaiting(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
@@ -49,17 +52,21 @@ __all__ = [
|
|
|
49
52
|
"ClosableDialog",
|
|
50
53
|
"CopyButton",
|
|
51
54
|
"CopyableLabel",
|
|
55
|
+
"CreateExperimentDialog",
|
|
52
56
|
"CustomDialog",
|
|
53
57
|
"EnsembleSelector",
|
|
58
|
+
"ErtSummary",
|
|
54
59
|
"ListEditBox",
|
|
55
60
|
"PathChooser",
|
|
56
61
|
"PathModel",
|
|
57
62
|
"SearchBox",
|
|
58
63
|
"SelectableListModel",
|
|
59
64
|
"StringBox",
|
|
65
|
+
"Suggestor",
|
|
60
66
|
"TargetEnsembleModel",
|
|
61
67
|
"TextBox",
|
|
62
68
|
"TextModel",
|
|
63
69
|
"ValueModel",
|
|
70
|
+
"get_parameters_button",
|
|
64
71
|
"showWaitCursorWhileWaiting",
|
|
65
72
|
]
|
|
@@ -6,8 +6,8 @@ from PyQt6.QtCore import QMargins, Qt
|
|
|
6
6
|
from PyQt6.QtGui import QIcon
|
|
7
7
|
from PyQt6.QtWidgets import QHBoxLayout, QToolButton, QWidget
|
|
8
8
|
|
|
9
|
-
from
|
|
10
|
-
from
|
|
9
|
+
from .analysismodulevariablespanel import AnalysisModuleVariablesPanel
|
|
10
|
+
from .closabledialog import ClosableDialog
|
|
11
11
|
|
|
12
12
|
if TYPE_CHECKING:
|
|
13
13
|
from ert.config import AnalysisModule
|
ert/gui/ertwidgets/checklist.py
CHANGED
|
@@ -9,9 +9,11 @@ from PyQt6.QtWidgets import (
|
|
|
9
9
|
)
|
|
10
10
|
|
|
11
11
|
from ert.gui.ertnotifier import ErtNotifier
|
|
12
|
-
from ert.gui.ertwidgets import StringBox, TextModel, ValueModel
|
|
13
12
|
from ert.validation import ExperimentValidation, IntegerArgument, ProperNameArgument
|
|
14
13
|
|
|
14
|
+
from .models import TextModel, ValueModel
|
|
15
|
+
from .stringbox import StringBox
|
|
16
|
+
|
|
15
17
|
|
|
16
18
|
class CreateExperimentDialog(QDialog):
|
|
17
19
|
onDone = Signal(str, str, int)
|
|
@@ -9,11 +9,11 @@ from PyQt6.QtCore import Qt
|
|
|
9
9
|
from PyQt6.QtCore import pyqtSignal as Signal
|
|
10
10
|
from PyQt6.QtWidgets import QComboBox
|
|
11
11
|
|
|
12
|
+
from ert.config import ErrorInfo
|
|
12
13
|
from ert.gui.ertnotifier import ErtNotifier
|
|
13
14
|
from ert.storage import RealizationStorageState
|
|
14
15
|
|
|
15
|
-
from
|
|
16
|
-
from ..suggestor import Suggestor
|
|
16
|
+
from .suggestor import Suggestor
|
|
17
17
|
|
|
18
18
|
if TYPE_CHECKING:
|
|
19
19
|
from ert.storage import Ensemble
|