ert 19.0.0rc4__py3-none-any.whl → 20.0.0b0__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/__main__.py +94 -63
- ert/analysis/_es_update.py +11 -14
- ert/config/__init__.py +3 -2
- ert/config/_create_observation_dataframes.py +51 -375
- ert/config/_observations.py +483 -200
- ert/config/_read_summary.py +4 -5
- ert/config/ert_config.py +53 -80
- ert/config/everest_control.py +40 -39
- ert/config/everest_response.py +1 -13
- ert/config/field.py +0 -72
- ert/config/forward_model_step.py +17 -1
- ert/config/gen_data_config.py +14 -17
- ert/config/observation_config_migrations.py +821 -0
- ert/config/parameter_config.py +18 -28
- ert/config/parsing/__init__.py +0 -1
- ert/config/parsing/_parse_zonemap.py +45 -0
- ert/config/parsing/config_keywords.py +1 -1
- ert/config/parsing/config_schema.py +2 -8
- ert/config/parsing/observations_parser.py +2 -0
- ert/config/response_config.py +5 -23
- ert/config/rft_config.py +44 -19
- ert/config/summary_config.py +1 -13
- ert/config/surface_config.py +0 -57
- ert/dark_storage/compute/misfits.py +0 -42
- ert/dark_storage/endpoints/__init__.py +0 -2
- ert/dark_storage/endpoints/experiments.py +2 -5
- ert/dark_storage/json_schema/experiment.py +1 -2
- ert/field_utils/__init__.py +0 -2
- ert/field_utils/field_utils.py +1 -117
- ert/gui/ertwidgets/listeditbox.py +9 -1
- ert/gui/ertwidgets/models/ertsummary.py +20 -6
- ert/gui/ertwidgets/pathchooser.py +9 -1
- ert/gui/ertwidgets/stringbox.py +11 -3
- ert/gui/ertwidgets/textbox.py +10 -3
- ert/gui/ertwidgets/validationsupport.py +19 -1
- ert/gui/main_window.py +11 -6
- ert/gui/simulation/experiment_panel.py +1 -1
- ert/gui/simulation/run_dialog.py +11 -1
- ert/gui/tools/manage_experiments/export_dialog.py +4 -0
- ert/gui/tools/manage_experiments/manage_experiments_panel.py +1 -0
- ert/gui/tools/manage_experiments/storage_info_widget.py +5 -2
- ert/gui/tools/manage_experiments/storage_widget.py +18 -3
- ert/gui/tools/plot/data_type_proxy_model.py +1 -1
- ert/gui/tools/plot/plot_api.py +35 -27
- ert/gui/tools/plot/plot_widget.py +5 -0
- ert/gui/tools/plot/plot_window.py +4 -7
- ert/run_models/ensemble_experiment.py +1 -3
- ert/run_models/ensemble_smoother.py +1 -3
- ert/run_models/everest_run_model.py +12 -13
- ert/run_models/initial_ensemble_run_model.py +19 -22
- ert/run_models/model_factory.py +7 -7
- ert/run_models/multiple_data_assimilation.py +1 -3
- ert/sample_prior.py +12 -14
- ert/services/__init__.py +7 -3
- ert/services/_storage_main.py +59 -22
- ert/services/ert_server.py +186 -24
- ert/shared/version.py +3 -3
- ert/storage/local_ensemble.py +46 -115
- ert/storage/local_experiment.py +0 -16
- ert/utils/__init__.py +20 -0
- ert/warnings/specific_warning_handler.py +3 -2
- {ert-19.0.0rc4.dist-info → ert-20.0.0b0.dist-info}/METADATA +4 -51
- {ert-19.0.0rc4.dist-info → ert-20.0.0b0.dist-info}/RECORD +75 -80
- everest/bin/everest_script.py +5 -5
- everest/bin/kill_script.py +2 -2
- everest/bin/monitor_script.py +2 -2
- everest/bin/utils.py +4 -4
- everest/detached/everserver.py +6 -6
- everest/gui/everest_client.py +0 -6
- everest/gui/main_window.py +2 -2
- everest/util/__init__.py +1 -19
- ert/dark_storage/compute/__init__.py +0 -0
- ert/dark_storage/endpoints/compute/__init__.py +0 -0
- ert/dark_storage/endpoints/compute/misfits.py +0 -95
- ert/services/_base_service.py +0 -387
- ert/services/webviz_ert_service.py +0 -20
- ert/shared/storage/command.py +0 -38
- ert/shared/storage/extraction.py +0 -42
- {ert-19.0.0rc4.dist-info → ert-20.0.0b0.dist-info}/WHEEL +0 -0
- {ert-19.0.0rc4.dist-info → ert-20.0.0b0.dist-info}/entry_points.txt +0 -0
- {ert-19.0.0rc4.dist-info → ert-20.0.0b0.dist-info}/licenses/COPYING +0 -0
- {ert-19.0.0rc4.dist-info → ert-20.0.0b0.dist-info}/top_level.txt +0 -0
ert/field_utils/field_utils.py
CHANGED
|
@@ -2,12 +2,12 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import math
|
|
4
4
|
import os
|
|
5
|
+
from dataclasses import dataclass
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from typing import TYPE_CHECKING, Any, NamedTuple, TypeAlias
|
|
7
8
|
|
|
8
9
|
import numpy as np
|
|
9
10
|
import resfo
|
|
10
|
-
from pydantic.dataclasses import dataclass
|
|
11
11
|
|
|
12
12
|
from .field_file_format import ROFF_FORMATS, FieldFileFormat
|
|
13
13
|
from .grdecl_io import export_grdecl, import_bgrdecl, import_grdecl
|
|
@@ -356,119 +356,3 @@ def localization_scaling_function(
|
|
|
356
356
|
scaling_factor[distances > 2] = 0.0
|
|
357
357
|
|
|
358
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))
|
|
@@ -14,6 +14,7 @@ from PyQt6.QtWidgets import (
|
|
|
14
14
|
)
|
|
15
15
|
from typing_extensions import override
|
|
16
16
|
|
|
17
|
+
from .. import is_dark_mode
|
|
17
18
|
from .validationsupport import ValidationSupport
|
|
18
19
|
|
|
19
20
|
|
|
@@ -164,7 +165,14 @@ class ListEditBox(QWidget):
|
|
|
164
165
|
break
|
|
165
166
|
|
|
166
167
|
validity_type = ValidationSupport.WARNING
|
|
167
|
-
|
|
168
|
+
if is_dark_mode():
|
|
169
|
+
color = (
|
|
170
|
+
ValidationSupport.DARKMODE_ERROR_COLOR
|
|
171
|
+
if not valid
|
|
172
|
+
else self._valid_color
|
|
173
|
+
)
|
|
174
|
+
else:
|
|
175
|
+
color = ValidationSupport.ERROR_COLOR if not valid else self._valid_color
|
|
168
176
|
self._validation_support.setValidationMessage(message, validity_type)
|
|
169
177
|
self._list_edit_line.setToolTip(message)
|
|
170
178
|
palette.setColor(self._list_edit_line.backgroundRole(), color)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from collections import defaultdict
|
|
1
|
+
from collections import Counter, defaultdict
|
|
2
2
|
|
|
3
3
|
from typing_extensions import TypedDict
|
|
4
4
|
|
|
@@ -38,8 +38,22 @@ class ErtSummary:
|
|
|
38
38
|
return sorted(parameters, key=lambda k: k.lower()), count
|
|
39
39
|
|
|
40
40
|
def getObservations(self) -> list[ObservationCount]:
|
|
41
|
-
|
|
42
|
-
for
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
name_to_types = defaultdict(set)
|
|
42
|
+
for obs in self.ert_config.observation_declarations:
|
|
43
|
+
name_to_types[obs.name].add(obs.type)
|
|
44
|
+
|
|
45
|
+
multi_type_names = {
|
|
46
|
+
name for name, types in name_to_types.items() if len(types) > 1
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
keys = (
|
|
50
|
+
f"{obs.name}[{obs.type}]" if obs.name in multi_type_names else obs.name
|
|
51
|
+
for obs in self.ert_config.observation_declarations
|
|
52
|
+
)
|
|
53
|
+
counts = Counter(keys)
|
|
54
|
+
|
|
55
|
+
counts_list = [
|
|
56
|
+
ObservationCount({"observation_key": key, "count": count})
|
|
57
|
+
for key, count in counts.items()
|
|
58
|
+
]
|
|
59
|
+
return sorted(counts_list, key=lambda k: k["observation_key"].lower())
|
|
@@ -8,6 +8,7 @@ from PyQt6.QtCore import QSize
|
|
|
8
8
|
from PyQt6.QtGui import QIcon
|
|
9
9
|
from PyQt6.QtWidgets import QFileDialog, QHBoxLayout, QLineEdit, QToolButton, QWidget
|
|
10
10
|
|
|
11
|
+
from .. import is_dark_mode
|
|
11
12
|
from .validationsupport import ValidationSupport
|
|
12
13
|
|
|
13
14
|
if TYPE_CHECKING:
|
|
@@ -107,7 +108,14 @@ class PathChooser(QWidget):
|
|
|
107
108
|
|
|
108
109
|
validity_type = ValidationSupport.WARNING
|
|
109
110
|
|
|
110
|
-
|
|
111
|
+
if is_dark_mode():
|
|
112
|
+
color = (
|
|
113
|
+
ValidationSupport.DARKMODE_ERROR_COLOR
|
|
114
|
+
if not valid
|
|
115
|
+
else self.valid_color
|
|
116
|
+
)
|
|
117
|
+
else:
|
|
118
|
+
color = ValidationSupport.ERROR_COLOR if not valid else self.valid_color
|
|
111
119
|
|
|
112
120
|
self._validation_support.setValidationMessage(message, validity_type)
|
|
113
121
|
self._path_line.setToolTip(message)
|
ert/gui/ertwidgets/stringbox.py
CHANGED
|
@@ -6,6 +6,7 @@ from PyQt6.QtGui import QPalette
|
|
|
6
6
|
from PyQt6.QtWidgets import QLineEdit
|
|
7
7
|
from typing_extensions import override
|
|
8
8
|
|
|
9
|
+
from .. import is_dark_mode
|
|
9
10
|
from .validationsupport import ValidationSupport
|
|
10
11
|
|
|
11
12
|
if TYPE_CHECKING:
|
|
@@ -65,9 +66,16 @@ class StringBox(QLineEdit):
|
|
|
65
66
|
|
|
66
67
|
palette = QPalette()
|
|
67
68
|
if not status:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
if is_dark_mode():
|
|
70
|
+
palette.setColor(
|
|
71
|
+
self.backgroundRole(),
|
|
72
|
+
ValidationSupport.DARKMODE_ERROR_COLOR,
|
|
73
|
+
)
|
|
74
|
+
else:
|
|
75
|
+
palette.setColor(
|
|
76
|
+
self.backgroundRole(),
|
|
77
|
+
ValidationSupport.ERROR_COLOR,
|
|
78
|
+
)
|
|
71
79
|
self.setPalette(palette)
|
|
72
80
|
self._validation.setValidationMessage(
|
|
73
81
|
str(status), ValidationSupport.EXCLAMATION
|
ert/gui/ertwidgets/textbox.py
CHANGED
|
@@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, Any
|
|
|
5
5
|
from PyQt6.QtGui import QPalette
|
|
6
6
|
from PyQt6.QtWidgets import QTextEdit
|
|
7
7
|
|
|
8
|
+
from .. import is_dark_mode
|
|
8
9
|
from .validationsupport import ValidationSupport
|
|
9
10
|
|
|
10
11
|
if TYPE_CHECKING:
|
|
@@ -51,9 +52,15 @@ class TextBox(QTextEdit):
|
|
|
51
52
|
|
|
52
53
|
palette = QPalette()
|
|
53
54
|
if not status:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
if is_dark_mode():
|
|
56
|
+
palette.setColor(
|
|
57
|
+
self.backgroundRole(),
|
|
58
|
+
ValidationSupport.DARKMODE_ERROR_COLOR,
|
|
59
|
+
)
|
|
60
|
+
else:
|
|
61
|
+
palette.setColor(
|
|
62
|
+
self.backgroundRole(), ValidationSupport.ERROR_COLOR
|
|
63
|
+
)
|
|
57
64
|
self.setPalette(palette)
|
|
58
65
|
self._validation.setValidationMessage(
|
|
59
66
|
str(status), ValidationSupport.EXCLAMATION
|
|
@@ -8,6 +8,8 @@ from PyQt6.QtCore import pyqtSignal as Signal
|
|
|
8
8
|
from PyQt6.QtGui import QColor, QEnterEvent
|
|
9
9
|
from PyQt6.QtWidgets import QFrame, QLabel, QSizePolicy, QVBoxLayout, QWidget
|
|
10
10
|
|
|
11
|
+
from ert.gui import is_dark_mode
|
|
12
|
+
|
|
11
13
|
if TYPE_CHECKING:
|
|
12
14
|
from PyQt6.QtCore import QEvent
|
|
13
15
|
from PyQt6.QtGui import QHideEvent
|
|
@@ -23,6 +25,15 @@ class ErrorPopup(QWidget):
|
|
|
23
25
|
"</html>"
|
|
24
26
|
)
|
|
25
27
|
|
|
28
|
+
darkmode_error_template = (
|
|
29
|
+
"<html>"
|
|
30
|
+
"<table style='background-color: #b03941;'width='100%%'>"
|
|
31
|
+
"<tr><td style='font-weight: bold; padding-left: 0px;'>Warning:</td></tr>"
|
|
32
|
+
"%s"
|
|
33
|
+
"</table>"
|
|
34
|
+
"</html>"
|
|
35
|
+
)
|
|
36
|
+
|
|
26
37
|
def __init__(self) -> None:
|
|
27
38
|
QWidget.__init__(self, None, Qt.WindowType.ToolTip)
|
|
28
39
|
self.resize(300, 50)
|
|
@@ -46,7 +57,13 @@ class ErrorPopup(QWidget):
|
|
|
46
57
|
def presentError(self, widget: QWidget, error: str) -> None:
|
|
47
58
|
assert isinstance(widget, QWidget)
|
|
48
59
|
|
|
49
|
-
|
|
60
|
+
if is_dark_mode():
|
|
61
|
+
self._error_widget.setText(
|
|
62
|
+
self.darkmode_error_template % html.escape(error)
|
|
63
|
+
)
|
|
64
|
+
else:
|
|
65
|
+
self._error_widget.setText(self.error_template % html.escape(error))
|
|
66
|
+
|
|
50
67
|
self.show()
|
|
51
68
|
|
|
52
69
|
size_hint = self.sizeHint()
|
|
@@ -63,6 +80,7 @@ class ErrorPopup(QWidget):
|
|
|
63
80
|
class ValidationSupport(QObject):
|
|
64
81
|
STRONG_ERROR_COLOR = QColor(255, 215, 215)
|
|
65
82
|
ERROR_COLOR = QColor(255, 235, 235)
|
|
83
|
+
DARKMODE_ERROR_COLOR = QColor(176, 57, 64)
|
|
66
84
|
INVALID_COLOR = QColor(235, 235, 255)
|
|
67
85
|
|
|
68
86
|
WARNING = "warning"
|
ert/gui/main_window.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import datetime
|
|
4
3
|
import functools
|
|
5
4
|
import logging
|
|
6
5
|
import webbrowser
|
|
@@ -116,8 +115,10 @@ class ErtMainWindow(QMainWindow):
|
|
|
116
115
|
|
|
117
116
|
if is_dark_mode():
|
|
118
117
|
self.side_frame.setStyleSheet("background-color: rgb(64, 64, 64);")
|
|
118
|
+
logger.info("Running Ert with dark mode")
|
|
119
119
|
else:
|
|
120
120
|
self.side_frame.setStyleSheet("background-color: lightgray;")
|
|
121
|
+
logger.info("Running Ert with light mode")
|
|
121
122
|
|
|
122
123
|
if is_high_contrast_mode():
|
|
123
124
|
msg_box = QMessageBox()
|
|
@@ -233,15 +234,17 @@ class ErtMainWindow(QMainWindow):
|
|
|
233
234
|
widget.setVisible(False)
|
|
234
235
|
|
|
235
236
|
run_dialog.setParent(self)
|
|
236
|
-
|
|
237
|
-
timespec="seconds"
|
|
238
|
-
)
|
|
239
|
-
experiment_type = run_dialog._run_model_api.experiment_name
|
|
240
|
-
simulation_id = experiment_type + " : " + date_time
|
|
237
|
+
simulation_id = run_dialog.property("experiment_name")
|
|
241
238
|
self.central_panels_map[simulation_id] = run_dialog
|
|
242
239
|
self.run_dialog_counter += 1
|
|
243
240
|
self.central_layout.addWidget(run_dialog)
|
|
244
241
|
|
|
242
|
+
def mark_action_bold(menu: QMenu, action_to_mark: QAction) -> None:
|
|
243
|
+
for action in menu.actions():
|
|
244
|
+
font = action.font()
|
|
245
|
+
font.setBold(action is action_to_mark)
|
|
246
|
+
action.setFont(font)
|
|
247
|
+
|
|
245
248
|
def add_sim_run_option(simulation_id: str) -> None:
|
|
246
249
|
menu = self.results_button.menu()
|
|
247
250
|
if menu:
|
|
@@ -249,11 +252,13 @@ class ErtMainWindow(QMainWindow):
|
|
|
249
252
|
act = QAction(text=simulation_id, parent=menu)
|
|
250
253
|
act.setProperty("index", simulation_id)
|
|
251
254
|
act.triggered.connect(self.select_central_widget)
|
|
255
|
+
act.triggered.connect(lambda _: mark_action_bold(menu, act))
|
|
252
256
|
|
|
253
257
|
if action_list:
|
|
254
258
|
menu.insertAction(action_list[0], act)
|
|
255
259
|
else:
|
|
256
260
|
menu.addAction(act)
|
|
261
|
+
mark_action_bold(menu, menu.actions()[0])
|
|
257
262
|
|
|
258
263
|
if self.run_dialog_counter == 2:
|
|
259
264
|
# swap from button to menu selection
|
|
@@ -184,7 +184,7 @@ class ExperimentPanel(QWidget):
|
|
|
184
184
|
|
|
185
185
|
experiment_type_valid = any(
|
|
186
186
|
p.update for p in config.ensemble_config.parameter_configs.values()
|
|
187
|
-
) and bool(config.
|
|
187
|
+
) and bool(config.observation_declarations)
|
|
188
188
|
|
|
189
189
|
self.addExperimentConfigPanel(
|
|
190
190
|
MultipleDataAssimilationPanel(
|
ert/gui/simulation/run_dialog.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from datetime import datetime
|
|
4
|
+
from datetime import UTC, datetime
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from queue import SimpleQueue
|
|
7
7
|
from typing import cast
|
|
@@ -244,6 +244,15 @@ class RunDialog(QFrame):
|
|
|
244
244
|
self,
|
|
245
245
|
)
|
|
246
246
|
|
|
247
|
+
date_time: str = datetime.now(UTC).isoformat(timespec="seconds")
|
|
248
|
+
experiment_type = self._run_model_api.experiment_name
|
|
249
|
+
simulation_id = experiment_type + " : " + date_time
|
|
250
|
+
|
|
251
|
+
self._experiment_name_label = QLabel(self)
|
|
252
|
+
self._experiment_name_label.setText(simulation_id)
|
|
253
|
+
self._experiment_name_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
254
|
+
self.setProperty("experiment_name", simulation_id)
|
|
255
|
+
|
|
247
256
|
self._total_progress_bar = QProgressBar(self)
|
|
248
257
|
self._total_progress_bar.setRange(0, 100)
|
|
249
258
|
self._total_progress_bar.setTextVisible(False)
|
|
@@ -325,6 +334,7 @@ class RunDialog(QFrame):
|
|
|
325
334
|
footer_widget_container.setLayout(footer_layout)
|
|
326
335
|
|
|
327
336
|
layout = QVBoxLayout()
|
|
337
|
+
layout.addWidget(self._experiment_name_label)
|
|
328
338
|
layout.addWidget(self._total_progress_label)
|
|
329
339
|
layout.addWidget(self._total_progress_bar)
|
|
330
340
|
layout.addWidget(self._iteration_progress_label)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import contextlib
|
|
2
|
+
import logging
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
from typing import cast
|
|
4
5
|
|
|
@@ -22,6 +23,8 @@ from PyQt6.QtWidgets import (
|
|
|
22
23
|
QWidget,
|
|
23
24
|
)
|
|
24
25
|
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
25
28
|
|
|
26
29
|
class ExportDialog(QDialog):
|
|
27
30
|
"""Base dialog for exporting ensemble-related data to files."""
|
|
@@ -85,6 +88,7 @@ class ExportDialog(QDialog):
|
|
|
85
88
|
f"<span style='color: red;'>Could not export data: {e!s}</span><br>"
|
|
86
89
|
)
|
|
87
90
|
finally:
|
|
91
|
+
logger.info(f"Export dialog used: '{self.windowTitle()}'")
|
|
88
92
|
self._export_button.setEnabled(True)
|
|
89
93
|
|
|
90
94
|
@Slot()
|
|
@@ -123,6 +123,7 @@ class ManageExperimentsPanel(QTabWidget):
|
|
|
123
123
|
active_realizations=active_realizations,
|
|
124
124
|
parameters=parameters,
|
|
125
125
|
random_seed=self.ert_config.random_seed,
|
|
126
|
+
num_realizations=self.ert_config.runpath_config.num_realizations,
|
|
126
127
|
design_matrix_df=(
|
|
127
128
|
self.ert_config.analysis_config.design_matrix.design_matrix_df
|
|
128
129
|
if self.ert_config.analysis_config.design_matrix
|
|
@@ -7,6 +7,7 @@ import seaborn as sns
|
|
|
7
7
|
import yaml
|
|
8
8
|
from matplotlib.backends.backend_qt5agg import FigureCanvas # type: ignore
|
|
9
9
|
from matplotlib.figure import Figure
|
|
10
|
+
from pandas.errors import PerformanceWarning
|
|
10
11
|
from polars import DataFrame
|
|
11
12
|
from PyQt6.QtCore import Qt
|
|
12
13
|
from PyQt6.QtCore import pyqtSlot as Slot
|
|
@@ -29,6 +30,7 @@ from PyQt6.QtWidgets import (
|
|
|
29
30
|
|
|
30
31
|
from ert import LibresFacade
|
|
31
32
|
from ert.storage import Ensemble, Experiment, RealizationStorageState
|
|
33
|
+
from ert.warnings import capture_specific_warning
|
|
32
34
|
|
|
33
35
|
from .export_dialog import ExportDialog
|
|
34
36
|
|
|
@@ -253,8 +255,8 @@ class _EnsembleWidget(QWidget):
|
|
|
253
255
|
df[x_axis_col]
|
|
254
256
|
.map_elements(
|
|
255
257
|
lambda x: response_config.display_column(x, x_axis_col),
|
|
256
|
-
return_dtype=pl.String,
|
|
257
258
|
)
|
|
259
|
+
.cast(pl.String)
|
|
258
260
|
.alias("temp")
|
|
259
261
|
).filter(pl.col("temp").eq(observation_label))[
|
|
260
262
|
[x for x in df.columns if x != "temp"]
|
|
@@ -446,7 +448,8 @@ class _EnsembleWidget(QWidget):
|
|
|
446
448
|
|
|
447
449
|
def get_misfit_df(self) -> DataFrame:
|
|
448
450
|
assert self._ensemble is not None
|
|
449
|
-
|
|
451
|
+
with capture_specific_warning(PerformanceWarning):
|
|
452
|
+
df = LibresFacade.load_all_misfit_data(self._ensemble)
|
|
450
453
|
realization_column = pl.Series(df.index)
|
|
451
454
|
df = pl.from_pandas(df)
|
|
452
455
|
df.insert_column(0, realization_column)
|
|
@@ -22,7 +22,8 @@ from PyQt6.QtWidgets import (
|
|
|
22
22
|
QWidget,
|
|
23
23
|
)
|
|
24
24
|
|
|
25
|
-
from ert.config import ErrorInfo, ErtConfig
|
|
25
|
+
from ert.config import ErrorInfo, ErtConfig, RFTConfig
|
|
26
|
+
from ert.config._create_observation_dataframes import create_observation_dataframes
|
|
26
27
|
from ert.gui.ertnotifier import ErtNotifier
|
|
27
28
|
from ert.gui.ertwidgets import CreateExperimentDialog, Suggestor
|
|
28
29
|
from ert.storage import Ensemble, Experiment
|
|
@@ -169,10 +170,24 @@ class StorageWidget(QWidget):
|
|
|
169
170
|
if create_experiment_dialog.exec():
|
|
170
171
|
try:
|
|
171
172
|
with self._notifier.write_storage() as storage:
|
|
173
|
+
response_configs = (
|
|
174
|
+
self._ert_config.ensemble_config.response_configuration
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
rft_config = next(
|
|
178
|
+
(r for r in response_configs if r.type == "rft"),
|
|
179
|
+
None,
|
|
180
|
+
)
|
|
181
|
+
if rft_config is not None:
|
|
182
|
+
rft_config = cast(RFTConfig, rft_config)
|
|
183
|
+
|
|
172
184
|
ensemble = storage.create_experiment(
|
|
173
185
|
parameters=self._ert_config.parameter_configurations_with_design_matrix,
|
|
174
|
-
responses=
|
|
175
|
-
observations=
|
|
186
|
+
responses=response_configs,
|
|
187
|
+
observations=create_observation_dataframes(
|
|
188
|
+
observations=self._ert_config.observation_declarations,
|
|
189
|
+
rft_config=rft_config,
|
|
190
|
+
),
|
|
176
191
|
name=create_experiment_dialog.experiment_name,
|
|
177
192
|
templates=self._ert_config.ert_templates,
|
|
178
193
|
).create_ensemble(
|
|
@@ -42,7 +42,7 @@ class DataTypeProxyModel(QSortFilterProxyModel):
|
|
|
42
42
|
show = False
|
|
43
43
|
|
|
44
44
|
# Filter out non-finalized responses
|
|
45
|
-
if key.
|
|
45
|
+
if key.response and not key.response.has_finalized_keys:
|
|
46
46
|
show = False
|
|
47
47
|
|
|
48
48
|
return show
|