ert 19.0.1__py3-none-any.whl → 20.0.0b1__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.
Files changed (87) hide show
  1. ert/__main__.py +94 -63
  2. ert/analysis/_es_update.py +11 -14
  3. ert/cli/main.py +1 -1
  4. ert/config/__init__.py +3 -2
  5. ert/config/_create_observation_dataframes.py +52 -375
  6. ert/config/_observations.py +527 -200
  7. ert/config/_read_summary.py +4 -5
  8. ert/config/ert_config.py +52 -117
  9. ert/config/everest_control.py +40 -39
  10. ert/config/everest_response.py +3 -15
  11. ert/config/field.py +4 -76
  12. ert/config/forward_model_step.py +17 -1
  13. ert/config/gen_data_config.py +14 -17
  14. ert/config/observation_config_migrations.py +821 -0
  15. ert/config/parameter_config.py +18 -28
  16. ert/config/parsing/__init__.py +0 -1
  17. ert/config/parsing/_parse_zonemap.py +45 -0
  18. ert/config/parsing/config_keywords.py +1 -0
  19. ert/config/parsing/config_schema.py +2 -0
  20. ert/config/parsing/observations_parser.py +2 -0
  21. ert/config/response_config.py +5 -23
  22. ert/config/rft_config.py +129 -31
  23. ert/config/summary_config.py +1 -13
  24. ert/config/surface_config.py +0 -57
  25. ert/dark_storage/compute/misfits.py +0 -42
  26. ert/dark_storage/endpoints/__init__.py +0 -2
  27. ert/dark_storage/endpoints/experiments.py +2 -5
  28. ert/dark_storage/json_schema/experiment.py +1 -2
  29. ert/field_utils/__init__.py +0 -2
  30. ert/field_utils/field_utils.py +1 -117
  31. ert/gui/ertwidgets/listeditbox.py +9 -1
  32. ert/gui/ertwidgets/models/ertsummary.py +20 -6
  33. ert/gui/ertwidgets/pathchooser.py +9 -1
  34. ert/gui/ertwidgets/stringbox.py +11 -3
  35. ert/gui/ertwidgets/textbox.py +10 -3
  36. ert/gui/ertwidgets/validationsupport.py +19 -1
  37. ert/gui/main_window.py +11 -6
  38. ert/gui/simulation/experiment_panel.py +1 -1
  39. ert/gui/simulation/run_dialog.py +11 -1
  40. ert/gui/tools/manage_experiments/export_dialog.py +4 -0
  41. ert/gui/tools/manage_experiments/manage_experiments_panel.py +1 -0
  42. ert/gui/tools/manage_experiments/storage_info_widget.py +1 -1
  43. ert/gui/tools/manage_experiments/storage_widget.py +21 -4
  44. ert/gui/tools/plot/data_type_proxy_model.py +1 -1
  45. ert/gui/tools/plot/plot_api.py +35 -27
  46. ert/gui/tools/plot/plot_widget.py +5 -0
  47. ert/gui/tools/plot/plot_window.py +4 -7
  48. ert/run_models/ensemble_experiment.py +2 -9
  49. ert/run_models/ensemble_smoother.py +1 -9
  50. ert/run_models/everest_run_model.py +31 -23
  51. ert/run_models/initial_ensemble_run_model.py +19 -22
  52. ert/run_models/manual_update.py +11 -5
  53. ert/run_models/model_factory.py +7 -7
  54. ert/run_models/multiple_data_assimilation.py +3 -16
  55. ert/sample_prior.py +12 -14
  56. ert/scheduler/job.py +24 -4
  57. ert/services/__init__.py +7 -3
  58. ert/services/_storage_main.py +59 -22
  59. ert/services/ert_server.py +186 -24
  60. ert/shared/version.py +3 -3
  61. ert/storage/local_ensemble.py +50 -116
  62. ert/storage/local_experiment.py +94 -109
  63. ert/storage/local_storage.py +10 -12
  64. ert/storage/migration/to24.py +26 -0
  65. ert/storage/migration/to25.py +91 -0
  66. ert/utils/__init__.py +20 -0
  67. {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/METADATA +4 -51
  68. {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/RECORD +80 -83
  69. everest/bin/everest_script.py +5 -5
  70. everest/bin/kill_script.py +2 -2
  71. everest/bin/monitor_script.py +2 -2
  72. everest/bin/utils.py +4 -4
  73. everest/detached/everserver.py +6 -6
  74. everest/gui/everest_client.py +0 -6
  75. everest/gui/main_window.py +2 -2
  76. everest/util/__init__.py +1 -19
  77. ert/dark_storage/compute/__init__.py +0 -0
  78. ert/dark_storage/endpoints/compute/__init__.py +0 -0
  79. ert/dark_storage/endpoints/compute/misfits.py +0 -95
  80. ert/services/_base_service.py +0 -387
  81. ert/services/webviz_ert_service.py +0 -20
  82. ert/shared/storage/command.py +0 -38
  83. ert/shared/storage/extraction.py +0 -42
  84. {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/WHEEL +0 -0
  85. {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/entry_points.txt +0 -0
  86. {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/licenses/COPYING +0 -0
  87. {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/top_level.txt +0 -0
@@ -22,8 +22,7 @@ class ExperimentIn(_Experiment):
22
22
  class ExperimentOut(_Experiment):
23
23
  id: UUID
24
24
  ensemble_ids: list[UUID]
25
- priors: Mapping[str, dict[str, Any]]
26
25
  userdata: Mapping[str, Any]
27
26
  parameters: Mapping[str, dict[str, Any]]
28
- responses: Mapping[str, list[dict[str, Any]]]
27
+ responses: Mapping[str, dict[str, Any]]
29
28
  observations: Mapping[str, dict[str, list[str]]]
@@ -4,7 +4,6 @@ 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,
8
7
  calculate_ertbox_parameters,
9
8
  get_shape,
10
9
  localization_scaling_function,
@@ -19,7 +18,6 @@ __all__ = [
19
18
  "ErtboxParameters",
20
19
  "FieldFileFormat",
21
20
  "Shape",
22
- "calc_rho_for_2d_grid_layer",
23
21
  "calculate_ertbox_parameters",
24
22
  "get_shape",
25
23
  "localization_scaling_function",
@@ -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
- color = ValidationSupport.ERROR_COLOR if not valid else self._valid_color
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
- counts: list[ObservationCount] = []
42
- for df in self.ert_config.observations.values():
43
- counts.extend(df.group_by("observation_key").len(name="count").to_dicts()) # type: ignore
44
-
45
- return sorted(counts, key=lambda k: k["observation_key"].lower())
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
- color = ValidationSupport.ERROR_COLOR if not valid else self.valid_color
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)
@@ -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
- palette.setColor(
69
- self.backgroundRole(), ValidationSupport.ERROR_COLOR
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
@@ -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
- palette.setColor(
55
- self.backgroundRole(), ValidationSupport.ERROR_COLOR
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
- self._error_widget.setText(ErrorPopup.error_template % html.escape(error))
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
- date_time: str = datetime.datetime.now(datetime.UTC).isoformat(
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
@@ -178,7 +178,7 @@ class ExperimentPanel(QWidget):
178
178
 
179
179
  experiment_type_valid = any(
180
180
  p.update for p in config.ensemble_config.parameter_configs.values()
181
- ) and bool(config.observations)
181
+ ) and bool(config.observation_declarations)
182
182
 
183
183
  self.addExperimentConfigPanel(
184
184
  MultipleDataAssimilationPanel(
@@ -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
@@ -255,8 +255,8 @@ class _EnsembleWidget(QWidget):
255
255
  df[x_axis_col]
256
256
  .map_elements(
257
257
  lambda x: response_config.display_column(x, x_axis_col),
258
- return_dtype=pl.String,
259
258
  )
259
+ .cast(pl.String)
260
260
  .alias("temp")
261
261
  ).filter(pl.col("temp").eq(observation_label))[
262
262
  [x for x in df.columns if x != "temp"]
@@ -169,12 +169,29 @@ class StorageWidget(QWidget):
169
169
  if create_experiment_dialog.exec():
170
170
  try:
171
171
  with self._notifier.write_storage() as storage:
172
+ parameter_configuration = (
173
+ self._ert_config.parameter_configurations_with_design_matrix
174
+ )
175
+ response_configuration = (
176
+ self._ert_config.ensemble_config.response_configuration
177
+ )
172
178
  ensemble = storage.create_experiment(
173
- parameters=self._ert_config.parameter_configurations_with_design_matrix,
174
- responses=self._ert_config.ensemble_config.response_configuration,
175
- observations=self._ert_config.observations,
179
+ experiment_config={
180
+ "parameter_configuration": [
181
+ c.model_dump(mode="json")
182
+ for c in parameter_configuration
183
+ ],
184
+ "response_configuration": [
185
+ c.model_dump(mode="json")
186
+ for c in response_configuration
187
+ ],
188
+ "observations": [
189
+ d.model_dump(mode="json")
190
+ for d in self._ert_config.observation_declarations
191
+ ],
192
+ "ert_templates": self._ert_config.ert_templates,
193
+ },
176
194
  name=create_experiment_dialog.experiment_name,
177
- templates=self._ert_config.ert_templates,
178
195
  ).create_ensemble(
179
196
  name=create_experiment_dialog.ensemble_name,
180
197
  ensemble_size=self._ensemble_size,
@@ -42,7 +42,7 @@ class DataTypeProxyModel(QSortFilterProxyModel):
42
42
  show = False
43
43
 
44
44
  # Filter out non-finalized responses
45
- if key.response_metadata and not key.response_metadata.finalized:
45
+ if key.response and not key.response.has_finalized_keys:
46
46
  show = False
47
47
 
48
48
  return show