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.
Files changed (218) hide show
  1. _ert/events.py +19 -2
  2. _ert/forward_model_runner/client.py +6 -2
  3. ert/__main__.py +28 -13
  4. ert/analysis/_enif_update.py +8 -4
  5. ert/analysis/_es_update.py +19 -6
  6. ert/analysis/_update_commons.py +16 -6
  7. ert/cli/main.py +13 -6
  8. ert/cli/monitor.py +7 -0
  9. ert/config/__init__.py +15 -6
  10. ert/config/_create_observation_dataframes.py +117 -20
  11. ert/config/_get_num_cpu.py +1 -1
  12. ert/config/_observations.py +91 -2
  13. ert/config/_read_summary.py +8 -6
  14. ert/config/design_matrix.py +51 -24
  15. ert/config/distribution.py +1 -1
  16. ert/config/ensemble_config.py +9 -17
  17. ert/config/ert_config.py +103 -19
  18. ert/config/everest_control.py +234 -0
  19. ert/config/{everest_objective_config.py → everest_response.py} +24 -15
  20. ert/config/field.py +96 -84
  21. ert/config/forward_model_step.py +122 -17
  22. ert/config/gen_data_config.py +5 -10
  23. ert/config/gen_kw_config.py +5 -35
  24. ert/config/known_response_types.py +14 -0
  25. ert/config/parameter_config.py +1 -33
  26. ert/config/parsing/_option_dict.py +10 -2
  27. ert/config/parsing/config_keywords.py +2 -0
  28. ert/config/parsing/config_schema.py +23 -3
  29. ert/config/parsing/config_schema_deprecations.py +3 -14
  30. ert/config/parsing/config_schema_item.py +26 -11
  31. ert/config/parsing/context_values.py +3 -3
  32. ert/config/parsing/file_context_token.py +1 -1
  33. ert/config/parsing/observations_parser.py +6 -2
  34. ert/config/parsing/queue_system.py +9 -0
  35. ert/config/parsing/schema_item_type.py +1 -0
  36. ert/config/queue_config.py +4 -5
  37. ert/config/response_config.py +0 -8
  38. ert/config/rft_config.py +275 -0
  39. ert/config/summary_config.py +3 -8
  40. ert/config/surface_config.py +59 -16
  41. ert/config/workflow_fixtures.py +2 -1
  42. ert/dark_storage/client/__init__.py +2 -2
  43. ert/dark_storage/client/_session.py +4 -4
  44. ert/dark_storage/client/client.py +2 -2
  45. ert/dark_storage/common.py +1 -1
  46. ert/dark_storage/compute/misfits.py +11 -7
  47. ert/dark_storage/endpoints/compute/misfits.py +6 -4
  48. ert/dark_storage/endpoints/experiment_server.py +12 -9
  49. ert/dark_storage/endpoints/experiments.py +2 -2
  50. ert/dark_storage/endpoints/observations.py +8 -6
  51. ert/dark_storage/endpoints/parameters.py +2 -18
  52. ert/dark_storage/endpoints/responses.py +24 -5
  53. ert/dark_storage/json_schema/experiment.py +1 -1
  54. ert/data/_measured_data.py +6 -5
  55. ert/ensemble_evaluator/__init__.py +8 -1
  56. ert/ensemble_evaluator/config.py +2 -1
  57. ert/ensemble_evaluator/evaluator.py +81 -29
  58. ert/ensemble_evaluator/event.py +6 -0
  59. ert/ensemble_evaluator/snapshot.py +3 -1
  60. ert/ensemble_evaluator/state.py +1 -0
  61. ert/field_utils/__init__.py +8 -0
  62. ert/field_utils/field_utils.py +212 -3
  63. ert/field_utils/roff_io.py +1 -1
  64. ert/gui/__init__.py +5 -2
  65. ert/gui/ertnotifier.py +1 -1
  66. ert/gui/ertwidgets/__init__.py +23 -16
  67. ert/gui/ertwidgets/analysismoduleedit.py +2 -2
  68. ert/gui/ertwidgets/checklist.py +1 -1
  69. ert/gui/ertwidgets/create_experiment_dialog.py +3 -1
  70. ert/gui/ertwidgets/ensembleselector.py +2 -2
  71. ert/gui/ertwidgets/models/__init__.py +2 -0
  72. ert/gui/ertwidgets/models/activerealizationsmodel.py +2 -1
  73. ert/gui/ertwidgets/models/path_model.py +1 -1
  74. ert/gui/ertwidgets/models/targetensemblemodel.py +2 -1
  75. ert/gui/ertwidgets/models/text_model.py +1 -1
  76. ert/gui/ertwidgets/pathchooser.py +0 -3
  77. ert/gui/ertwidgets/searchbox.py +13 -4
  78. ert/gui/{suggestor → ertwidgets/suggestor}/_suggestor_message.py +13 -4
  79. ert/gui/{suggestor → ertwidgets/suggestor}/suggestor.py +63 -30
  80. ert/gui/main.py +37 -8
  81. ert/gui/main_window.py +1 -7
  82. ert/gui/simulation/ensemble_experiment_panel.py +1 -1
  83. ert/gui/simulation/ensemble_information_filter_panel.py +1 -1
  84. ert/gui/simulation/ensemble_smoother_panel.py +1 -1
  85. ert/gui/simulation/evaluate_ensemble_panel.py +1 -1
  86. ert/gui/simulation/experiment_panel.py +16 -3
  87. ert/gui/simulation/manual_update_panel.py +31 -8
  88. ert/gui/simulation/multiple_data_assimilation_panel.py +12 -8
  89. ert/gui/simulation/run_dialog.py +27 -20
  90. ert/gui/simulation/single_test_run_panel.py +2 -2
  91. ert/gui/summarypanel.py +20 -1
  92. ert/gui/tools/load_results/load_results_panel.py +1 -1
  93. ert/gui/tools/manage_experiments/export_dialog.py +136 -0
  94. ert/gui/tools/manage_experiments/storage_info_widget.py +121 -16
  95. ert/gui/tools/manage_experiments/storage_widget.py +1 -2
  96. ert/gui/tools/plot/plot_api.py +37 -25
  97. ert/gui/tools/plot/plot_widget.py +10 -2
  98. ert/gui/tools/plot/plot_window.py +38 -18
  99. ert/gui/tools/plot/plottery/plot_config.py +2 -0
  100. ert/gui/tools/plot/plottery/plot_context.py +14 -0
  101. ert/gui/tools/plot/plottery/plots/__init__.py +2 -0
  102. ert/gui/tools/plot/plottery/plots/cesp.py +3 -1
  103. ert/gui/tools/plot/plottery/plots/distribution.py +6 -1
  104. ert/gui/tools/plot/plottery/plots/ensemble.py +12 -3
  105. ert/gui/tools/plot/plottery/plots/gaussian_kde.py +12 -2
  106. ert/gui/tools/plot/plottery/plots/histogram.py +3 -1
  107. ert/gui/tools/plot/plottery/plots/misfits.py +436 -0
  108. ert/gui/tools/plot/plottery/plots/observations.py +18 -4
  109. ert/gui/tools/plot/plottery/plots/statistics.py +62 -20
  110. ert/gui/tools/plot/plottery/plots/std_dev.py +3 -1
  111. ert/mode_definitions.py +2 -0
  112. ert/plugins/__init__.py +0 -1
  113. ert/plugins/hook_implementations/workflows/csv_export.py +2 -3
  114. ert/plugins/hook_implementations/workflows/gen_data_rft_export.py +10 -2
  115. ert/plugins/hook_specifications/__init__.py +0 -2
  116. ert/plugins/hook_specifications/jobs.py +0 -9
  117. ert/plugins/plugin_manager.py +6 -33
  118. ert/resources/forward_models/run_reservoirsimulator.py +8 -3
  119. ert/resources/shell_scripts/delete_directory.py +2 -2
  120. ert/run_models/__init__.py +18 -5
  121. ert/run_models/_create_run_path.py +131 -37
  122. ert/run_models/ensemble_experiment.py +10 -4
  123. ert/run_models/ensemble_information_filter.py +8 -1
  124. ert/run_models/ensemble_smoother.py +9 -3
  125. ert/run_models/evaluate_ensemble.py +8 -6
  126. ert/run_models/event.py +7 -3
  127. ert/run_models/everest_run_model.py +159 -46
  128. ert/run_models/initial_ensemble_run_model.py +25 -24
  129. ert/run_models/manual_update.py +6 -3
  130. ert/run_models/manual_update_enif.py +37 -0
  131. ert/run_models/model_factory.py +81 -21
  132. ert/run_models/multiple_data_assimilation.py +22 -11
  133. ert/run_models/run_model.py +64 -55
  134. ert/run_models/single_test_run.py +7 -4
  135. ert/run_models/update_run_model.py +4 -2
  136. ert/runpaths.py +5 -6
  137. ert/sample_prior.py +9 -4
  138. ert/scheduler/driver.py +37 -0
  139. ert/scheduler/event.py +3 -1
  140. ert/scheduler/job.py +23 -13
  141. ert/scheduler/lsf_driver.py +6 -2
  142. ert/scheduler/openpbs_driver.py +7 -1
  143. ert/scheduler/scheduler.py +5 -0
  144. ert/scheduler/slurm_driver.py +6 -2
  145. ert/services/__init__.py +2 -2
  146. ert/services/_base_service.py +37 -20
  147. ert/services/ert_server.py +317 -0
  148. ert/shared/_doc_utils/__init__.py +4 -2
  149. ert/shared/_doc_utils/ert_jobs.py +1 -4
  150. ert/shared/net_utils.py +43 -18
  151. ert/shared/storage/connection.py +3 -3
  152. ert/shared/version.py +3 -3
  153. ert/storage/__init__.py +2 -0
  154. ert/storage/local_ensemble.py +38 -12
  155. ert/storage/local_experiment.py +8 -16
  156. ert/storage/local_storage.py +68 -42
  157. ert/storage/migration/to11.py +1 -1
  158. ert/storage/migration/to16.py +38 -0
  159. ert/storage/migration/to17.py +42 -0
  160. ert/storage/migration/to18.py +11 -0
  161. ert/storage/migration/to19.py +34 -0
  162. ert/storage/migration/to20.py +23 -0
  163. ert/storage/migration/to21.py +25 -0
  164. ert/storage/migration/to8.py +4 -4
  165. ert/substitutions.py +12 -28
  166. ert/validation/active_range.py +7 -7
  167. ert/validation/rangestring.py +16 -16
  168. ert/workflow_runner.py +2 -1
  169. {ert-17.0.0.dist-info → ert-19.0.0rc2.dist-info}/METADATA +9 -8
  170. {ert-17.0.0.dist-info → ert-19.0.0rc2.dist-info}/RECORD +208 -205
  171. {ert-17.0.0.dist-info → ert-19.0.0rc2.dist-info}/WHEEL +1 -1
  172. everest/api/everest_data_api.py +14 -1
  173. everest/bin/config_branch_script.py +3 -6
  174. everest/bin/everconfigdump_script.py +1 -9
  175. everest/bin/everest_script.py +21 -11
  176. everest/bin/everlint_script.py +0 -2
  177. everest/bin/kill_script.py +2 -2
  178. everest/bin/monitor_script.py +2 -2
  179. everest/bin/utils.py +8 -4
  180. everest/bin/visualization_script.py +6 -14
  181. everest/config/__init__.py +4 -1
  182. everest/config/control_config.py +81 -6
  183. everest/config/control_variable_config.py +4 -3
  184. everest/config/everest_config.py +75 -42
  185. everest/config/forward_model_config.py +5 -3
  186. everest/config/install_data_config.py +7 -5
  187. everest/config/install_job_config.py +7 -3
  188. everest/config/install_template_config.py +3 -3
  189. everest/config/optimization_config.py +19 -6
  190. everest/config/output_constraint_config.py +8 -2
  191. everest/config/server_config.py +6 -49
  192. everest/config/utils.py +25 -105
  193. everest/config/validation_utils.py +17 -11
  194. everest/config_file_loader.py +13 -4
  195. everest/detached/client.py +3 -3
  196. everest/detached/everserver.py +7 -8
  197. everest/everest_storage.py +6 -12
  198. everest/gui/everest_client.py +2 -3
  199. everest/gui/main_window.py +2 -2
  200. everest/optimizer/everest2ropt.py +59 -32
  201. everest/optimizer/opt_model_transforms.py +12 -13
  202. everest/optimizer/utils.py +0 -29
  203. everest/strings.py +0 -5
  204. ert/config/everest_constraints_config.py +0 -95
  205. ert/config/ext_param_config.py +0 -106
  206. ert/gui/tools/export/__init__.py +0 -3
  207. ert/gui/tools/export/export_panel.py +0 -83
  208. ert/gui/tools/export/export_tool.py +0 -69
  209. ert/gui/tools/export/exporter.py +0 -36
  210. ert/services/storage_service.py +0 -127
  211. everest/config/sampler_config.py +0 -103
  212. everest/simulator/__init__.py +0 -88
  213. everest/simulator/everest_to_ert.py +0 -51
  214. /ert/gui/{suggestor → ertwidgets/suggestor}/__init__.py +0 -0
  215. /ert/gui/{suggestor → ertwidgets/suggestor}/_colors.py +0 -0
  216. {ert-17.0.0.dist-info → ert-19.0.0rc2.dist-info}/entry_points.txt +0 -0
  217. {ert-17.0.0.dist-info → ert-19.0.0rc2.dist-info}/licenses/COPYING +0 -0
  218. {ert-17.0.0.dist-info → ert-19.0.0rc2.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,5 @@
1
1
  from .activerealizationsmodel import ActiveRealizationsModel
2
+ from .ertsummary import ErtSummary
2
3
  from .path_model import PathModel
3
4
  from .selectable_list_model import SelectableListModel
4
5
  from .targetensemblemodel import TargetEnsembleModel
@@ -7,6 +8,7 @@ from .valuemodel import ValueModel
7
8
 
8
9
  __all__ = [
9
10
  "ActiveRealizationsModel",
11
+ "ErtSummary",
10
12
  "PathModel",
11
13
  "SelectableListModel",
12
14
  "TargetEnsembleModel",
@@ -2,9 +2,10 @@ from collections.abc import Collection
2
2
 
3
3
  from typing_extensions import override
4
4
 
5
- from ert.gui.ertwidgets.models.valuemodel import ValueModel
6
5
  from ert.validation import ActiveRange, mask_to_rangestring
7
6
 
7
+ from .valuemodel import ValueModel
8
+
8
9
 
9
10
  class ActiveRealizationsModel(ValueModel):
10
11
  def __init__(self, ensemble_size: int, show_default: bool = True) -> None:
@@ -1,4 +1,4 @@
1
- from ert.gui.ertwidgets.models.valuemodel import ValueModel
1
+ from .valuemodel import ValueModel
2
2
 
3
3
 
4
4
  class PathModel(ValueModel):
@@ -4,7 +4,8 @@ from typing_extensions import override
4
4
 
5
5
  from ert.config import AnalysisConfig
6
6
  from ert.gui.ertnotifier import ErtNotifier
7
- from ert.gui.ertwidgets.models.valuemodel import ValueModel
7
+
8
+ from .valuemodel import ValueModel
8
9
 
9
10
 
10
11
  class TargetEnsembleModel(ValueModel):
@@ -1,6 +1,6 @@
1
1
  from typing_extensions import override
2
2
 
3
- from ert.gui.ertwidgets.models.valuemodel import ValueModel
3
+ from .valuemodel import ValueModel
4
4
 
5
5
 
6
6
  class TextModel(ValueModel):
@@ -83,7 +83,6 @@ class PathChooser(QWidget):
83
83
  if self._model.pathMustExist():
84
84
  valid = False
85
85
  message = PathChooser.PATH_DOES_NOT_EXIST_MSG
86
- # todo: check if new (non-existing) file has directory or file format?
87
86
  elif path_exists:
88
87
  if self._model.pathMustBeExecutable() and is_file and not is_executable:
89
88
  valid = False
@@ -122,8 +121,6 @@ class PathChooser(QWidget):
122
121
 
123
122
  def selectPath(self) -> None:
124
123
  """Pops up the 'select a file/directory' dialog"""
125
- # todo: This probably needs some reworking to work properly with
126
- # different scenarios... (file + dir)
127
124
  self._editing = True
128
125
  current_directory = self.getPath()
129
126
 
@@ -1,6 +1,6 @@
1
1
  from typing import Any
2
2
 
3
- from PyQt6.QtCore import Qt
3
+ from PyQt6.QtCore import Qt, QTimer
4
4
  from PyQt6.QtCore import pyqtSignal as Signal
5
5
  from PyQt6.QtGui import QColor, QFocusEvent, QKeyEvent
6
6
  from PyQt6.QtWidgets import QLineEdit
@@ -12,16 +12,25 @@ class SearchBox(QLineEdit):
12
12
 
13
13
  filterChanged = Signal(object)
14
14
 
15
- def __init__(self) -> None:
15
+ def __init__(self, debounce_timeout: int = 1000) -> None:
16
16
  QLineEdit.__init__(self)
17
17
 
18
18
  self.setToolTip("Type to search!")
19
19
  self.active_color = self.palette().color(self.foregroundRole())
20
20
  self.disable_search = True
21
21
  self.presentSearch()
22
- self.textChanged.connect(self.__emitFilterChanged)
22
+ self.textChanged.connect(self._start_debounce_timer)
23
+ self._debounce_timout = debounce_timeout
24
+ self._debounce_timer = QTimer(self)
25
+ self._debounce_timer.setSingleShot(True)
26
+ self._debounce_timer.timeout.connect(self._emit_filter_changed)
23
27
 
24
- def __emitFilterChanged(self, _filter: Any) -> None:
28
+ def _start_debounce_timer(self, _filter: Any) -> None:
29
+ if not self._debounce_timer.isActive():
30
+ self._debounce_timer.start(self._debounce_timout)
31
+ self._debounce_timer.setInterval(self._debounce_timout)
32
+
33
+ def _emit_filter_changed(self) -> None:
25
34
  self.filterChanged.emit(self.filter())
26
35
 
27
36
  def filter(self) -> str:
@@ -13,10 +13,11 @@ from PyQt6.QtWidgets import (
13
13
  QVBoxLayout,
14
14
  QWidget,
15
15
  )
16
+ from typing_extensions import override
16
17
 
17
18
  from ert.gui import is_dark_mode
18
19
 
19
- from ..ertwidgets.copyablelabel import _CopyButton
20
+ from ..copy_button import CopyButton
20
21
  from ._colors import (
21
22
  BLUE_BACKGROUND,
22
23
  BLUE_TEXT,
@@ -33,6 +34,16 @@ def _svg_icon(image_name: str) -> QSvgWidget:
33
34
  return widget
34
35
 
35
36
 
37
+ class _CopyButton(CopyButton):
38
+ def __init__(self, message: str) -> None:
39
+ super().__init__()
40
+ self.message = message
41
+
42
+ @override
43
+ def copy(self) -> None:
44
+ self.copy_text(self.message)
45
+
46
+
36
47
  class SuggestorMessage(QWidget):
37
48
  def __init__(
38
49
  self,
@@ -100,9 +111,7 @@ class SuggestorMessage(QWidget):
100
111
  else:
101
112
  self._expand_collapse_label = QLabel()
102
113
  self._hbox.addWidget(self.lbl, alignment=Qt.AlignmentFlag.AlignTop)
103
- self._hbox.addWidget(
104
- _CopyButton(QLabel(message)), alignment=Qt.AlignmentFlag.AlignTop
105
- )
114
+ self._hbox.addWidget(_CopyButton(message), alignment=Qt.AlignmentFlag.AlignTop)
106
115
 
107
116
  def _toggle_expand(self, _link: Any) -> None:
108
117
  if self._expanded:
@@ -19,9 +19,11 @@ from PyQt6.QtWidgets import (
19
19
  QVBoxLayout,
20
20
  QWidget,
21
21
  )
22
+ from typing_extensions import override
22
23
 
23
24
  from ert.gui import is_dark_mode
24
25
 
26
+ from .. import CopyButton
25
27
  from ._colors import BLUE_TEXT
26
28
  from ._suggestor_message import SuggestorMessage
27
29
 
@@ -105,6 +107,32 @@ QPushButton:hover {{
105
107
  """
106
108
 
107
109
 
110
+ class _CopyAllButton(CopyButton):
111
+ def __init__(
112
+ self,
113
+ errors: list[ErrorInfo],
114
+ warnings: list[WarningInfo],
115
+ deprecations: list[WarningInfo],
116
+ ) -> None:
117
+ super().__init__()
118
+ self.setText(" Copy all messages")
119
+ self.all_messages = "\n\n".join(
120
+ [
121
+ f"{info.message}" + (f"\n{info.location()}" if info.location() else "")
122
+ for info in (errors + warnings + deprecations)
123
+ ]
124
+ )
125
+ self.setStyleSheet(SECONDARY_BUTTON_STYLE)
126
+
127
+ @override
128
+ def copy(self) -> None:
129
+ logger.info(
130
+ "Copy all button in Suggestor used. "
131
+ f"Copied {len(self.all_messages)} characters"
132
+ )
133
+ self.copy_text(self.all_messages)
134
+
135
+
108
136
  class Suggestor(QWidget):
109
137
  def __init__(
110
138
  self,
@@ -145,7 +173,24 @@ class Suggestor(QWidget):
145
173
  data_layout.addWidget(self._help_panel(help_links))
146
174
  self.__layout.addWidget(data_widget)
147
175
 
148
- self.__layout.addWidget(self._action_buttons())
176
+ action_buttons = QWidget(parent=self)
177
+ action_buttons_layout = QHBoxLayout()
178
+ action_buttons_layout.setContentsMargins(0, 24, 0, 0)
179
+
180
+ if any([errors, warnings, deprecations]):
181
+ action_buttons_layout.addWidget(
182
+ _CopyAllButton(errors, warnings, deprecations)
183
+ )
184
+
185
+ action_buttons_layout.addStretch()
186
+
187
+ if continue_action:
188
+ action_buttons_layout.addWidget(self._continue_button())
189
+
190
+ action_buttons_layout.addWidget(self._close_button())
191
+
192
+ action_buttons.setLayout(action_buttons_layout)
193
+ self.__layout.addWidget(action_buttons)
149
194
 
150
195
  def _help_panel(self, help_links: dict[str, str]) -> QFrame:
151
196
  help_button_frame = QFrame(parent=self)
@@ -204,35 +249,23 @@ class Suggestor(QWidget):
204
249
  area_layout.addWidget(self._messages(errors, warnings, deprecations))
205
250
  return problem_area
206
251
 
207
- def _action_buttons(self) -> QWidget:
208
- def run_pressed() -> None:
209
- assert self._continue_action
210
- self._continue_action()
211
- self.close()
212
-
213
- buttons = QWidget(parent=self)
214
- buttons_layout = QHBoxLayout()
215
- buttons_layout.insertStretch(-1, -1)
216
- buttons_layout.setContentsMargins(0, 24, 0, 0)
217
-
218
- give_up = QPushButton("Close")
219
- give_up.setObjectName("close_button")
220
- give_up.setStyleSheet(BUTTON_STYLE)
221
- give_up.pressed.connect(self.close)
222
-
223
- if self._continue_action:
224
- run = QPushButton("Open ERT")
225
- run.setStyleSheet(BUTTON_STYLE)
226
- run.setObjectName("run_ert_button")
227
- run.pressed.connect(run_pressed)
228
- buttons_layout.addWidget(run)
229
-
230
- give_up.setStyleSheet(SECONDARY_BUTTON_STYLE)
231
-
232
- buttons_layout.addWidget(give_up)
233
- buttons.setLayout(buttons_layout)
234
-
235
- return buttons
252
+ def _continue_button(self) -> QWidget:
253
+ assert self._continue_action
254
+ continue_button = QPushButton("Open ERT")
255
+ continue_button.setStyleSheet(BUTTON_STYLE)
256
+ continue_button.setObjectName("run_ert_button")
257
+ continue_button.pressed.connect(self._continue_action)
258
+ continue_button.pressed.connect(self.close)
259
+ return continue_button
260
+
261
+ def _close_button(self) -> QPushButton:
262
+ close_button = QPushButton("Close")
263
+ close_button.setObjectName("close_button")
264
+ close_button.pressed.connect(self.close)
265
+ close_button.setStyleSheet(
266
+ SECONDARY_BUTTON_STYLE if self._continue_action else BUTTON_STYLE
267
+ )
268
+ return close_button
236
269
 
237
270
  def _messages(
238
271
  self,
ert/gui/main.py CHANGED
@@ -8,12 +8,13 @@ import traceback
8
8
  import types
9
9
  from collections import Counter
10
10
  from importlib.resources import files
11
+ from pathlib import Path
11
12
  from signal import SIG_DFL, SIGINT, signal
12
13
 
13
14
  from opentelemetry.trace import Status, StatusCode
14
15
  from PyQt6.QtCore import QDir
15
16
  from PyQt6.QtGui import QIcon
16
- from PyQt6.QtWidgets import QApplication, QWidget
17
+ from PyQt6.QtWidgets import QApplication, QMessageBox, QWidget
17
18
 
18
19
  from ert.config import (
19
20
  ErrorInfo,
@@ -27,11 +28,16 @@ from ert.gui.tools.event_viewer import (
27
28
  )
28
29
  from ert.namespace import Namespace
29
30
  from ert.plugins import ErtRuntimePlugins, get_site_plugins
30
- from ert.services import StorageService
31
- from ert.storage import ErtStorageException, local_storage_set_ert_config, open_storage
31
+ from ert.services import ErtServer
32
+ from ert.storage import (
33
+ ErtStorageException,
34
+ LocalStorage,
35
+ local_storage_set_ert_config,
36
+ )
37
+ from ert.storage.local_storage import _LOCAL_STORAGE_VERSION, _storage_version
32
38
  from ert.trace import trace, tracer
33
39
 
34
- from .suggestor import Suggestor
40
+ from .ertwidgets import Suggestor
35
41
 
36
42
  logger = logging.getLogger(__name__)
37
43
 
@@ -113,7 +119,7 @@ def run_gui(args: Namespace, plugins: ErtRuntimePlugins | None = None) -> int:
113
119
  return show_window()
114
120
 
115
121
  try:
116
- with StorageService.init_service(project=os.path.abspath(ens_path)):
122
+ with ErtServer.init_service(project=Path(ens_path).absolute()):
117
123
  return show_window()
118
124
  except PermissionError as pe:
119
125
  print(f"Error: {pe}", file=sys.stderr)
@@ -157,9 +163,32 @@ def _start_initial_gui_window(
157
163
  storage_path = None
158
164
  if ert_config is not None:
159
165
  try:
160
- # Open write to initialize the storage,so that
161
- # dark storage can be mounted onto it
162
- open_storage(ert_config.ens_path, mode="w").close()
166
+ if LocalStorage.check_migration_needed(Path(ert_config.ens_path)):
167
+ storage_version = _storage_version(Path(ert_config.ens_path))
168
+ current_version = _LOCAL_STORAGE_VERSION
169
+
170
+ migrate_dialog = QMessageBox.warning(
171
+ None,
172
+ f"Migrate storage to version {current_version}?",
173
+ f"Ert storage is version {storage_version} and needs to be migrated"
174
+ f" to version {current_version} to be compatible with the current"
175
+ f" version of Ert\n\n"
176
+ f"After migration, this storage can only be opened with current or"
177
+ f" later versions of Ert\n\n"
178
+ "Do you wish to continue migrating storage?\n",
179
+ QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
180
+ )
181
+
182
+ if migrate_dialog == QMessageBox.StandardButton.Yes:
183
+ logger.info(
184
+ f"Migrating from version {storage_version} to"
185
+ f" version {current_version}"
186
+ )
187
+ else:
188
+ logger.info("Storage migration cancelled by user")
189
+ os._exit(0)
190
+
191
+ LocalStorage.perform_migration(Path(ert_config.ens_path))
163
192
  storage_path = ert_config.ens_path
164
193
  except ErtStorageException as err:
165
194
  validation_messages.errors.append(
ert/gui/main_window.py CHANGED
@@ -9,7 +9,7 @@ from pathlib import Path
9
9
  from PyQt6.QtCore import QCoreApplication, QEvent, QSize, Qt
10
10
  from PyQt6.QtCore import pyqtSignal as Signal
11
11
  from PyQt6.QtCore import pyqtSlot as Slot
12
- from PyQt6.QtGui import QAction, QCloseEvent, QCursor, QFontDatabase, QIcon, QMouseEvent
12
+ from PyQt6.QtGui import QAction, QCloseEvent, QCursor, QIcon, QMouseEvent
13
13
  from PyQt6.QtWidgets import (
14
14
  QButtonGroup,
15
15
  QFrame,
@@ -31,7 +31,6 @@ from ert.gui.find_ert_info import find_ert_info
31
31
  from ert.gui.simulation import ExperimentPanel
32
32
  from ert.gui.simulation.run_dialog import RunDialog
33
33
  from ert.gui.tools.event_viewer import EventViewerTool, GUILogHandler
34
- from ert.gui.tools.export import ExportTool
35
34
  from ert.gui.tools.load_results import LoadResultsTool
36
35
  from ert.gui.tools.manage_experiments import ManageExperimentsPanel
37
36
  from ert.gui.tools.plot.plot_window import PlotWindow
@@ -96,7 +95,6 @@ class ErtMainWindow(QMainWindow):
96
95
  log_handler: GUILogHandler | None = None,
97
96
  ) -> None:
98
97
  QMainWindow.__init__(self)
99
- self.available_fonts = QFontDatabase.families()
100
98
  self.notifier = ErtNotifier()
101
99
  self.plugins_tool: PluginsTool | None = None
102
100
  self.ert_config = ert_config
@@ -374,10 +372,6 @@ class ErtMainWindow(QMainWindow):
374
372
  tools_menu.addAction(self._event_viewer_tool.getAction())
375
373
  self.close_signal.connect(self._event_viewer_tool.close_wnd)
376
374
 
377
- self.export_tool = ExportTool(self.ert_config, self.notifier)
378
- self.export_tool.setParent(self)
379
- tools_menu.addAction(self.export_tool.getAction())
380
-
381
375
  self.workflows_tool = WorkflowsTool(self.ert_config, self.notifier)
382
376
  self.workflows_tool.setParent(self)
383
377
  tools_menu.addAction(self.workflows_tool.getAction())
@@ -17,6 +17,7 @@ from ert.gui.ertwidgets import (
17
17
  CopyableLabel,
18
18
  StringBox,
19
19
  TextModel,
20
+ get_parameters_button,
20
21
  )
21
22
  from ert.mode_definitions import ENSEMBLE_EXPERIMENT_MODE
22
23
  from ert.run_models import EnsembleExperiment
@@ -24,7 +25,6 @@ from ert.validation import ExperimentValidation, ProperNameArgument
24
25
  from ert.validation.active_range import ActiveRange
25
26
  from ert.validation.range_string_argument import RangeSubsetStringArgument
26
27
 
27
- from ..ertwidgets.parameterviewer import get_parameters_button
28
28
  from ._design_matrix_panel import DesignMatrixPanel
29
29
  from .experiment_config_panel import ExperimentConfigPanel
30
30
 
@@ -14,6 +14,7 @@ from ert.gui.ertwidgets import (
14
14
  StringBox,
15
15
  TargetEnsembleModel,
16
16
  TextModel,
17
+ get_parameters_button,
17
18
  )
18
19
  from ert.mode_definitions import ENIF_MODE
19
20
  from ert.run_models import EnsembleInformationFilter
@@ -24,7 +25,6 @@ from ert.validation import (
24
25
  from ert.validation.active_range import ActiveRange
25
26
  from ert.validation.range_string_argument import RangeSubsetStringArgument
26
27
 
27
- from ..ertwidgets.parameterviewer import get_parameters_button
28
28
  from ._design_matrix_panel import DesignMatrixPanel
29
29
  from .experiment_config_panel import ExperimentConfigPanel
30
30
 
@@ -16,6 +16,7 @@ from ert.gui.ertwidgets import (
16
16
  StringBox,
17
17
  TargetEnsembleModel,
18
18
  TextModel,
19
+ get_parameters_button,
19
20
  )
20
21
  from ert.mode_definitions import ENSEMBLE_SMOOTHER_MODE
21
22
  from ert.run_models import EnsembleSmoother
@@ -26,7 +27,6 @@ from ert.validation import (
26
27
  from ert.validation.active_range import ActiveRange
27
28
  from ert.validation.range_string_argument import RangeSubsetStringArgument
28
29
 
29
- from ..ertwidgets.parameterviewer import get_parameters_button
30
30
  from ._design_matrix_panel import DesignMatrixPanel
31
31
  from .experiment_config_panel import ExperimentConfigPanel
32
32
 
@@ -14,9 +14,9 @@ from ert.gui.ertwidgets import (
14
14
  CopyableLabel,
15
15
  EnsembleSelector,
16
16
  StringBox,
17
+ Suggestor,
17
18
  )
18
19
  from ert.gui.simulation.experiment_config_panel import ExperimentConfigPanel
19
- from ert.gui.suggestor import Suggestor
20
20
  from ert.mode_definitions import EVALUATE_ENSEMBLE_MODE
21
21
  from ert.run_models.evaluate_ensemble import EvaluateEnsemble
22
22
  from ert.storage import RealizationStorageState
@@ -55,9 +55,15 @@ def create_md_table(kv: dict[str, str], output: str) -> str:
55
55
 
56
56
 
57
57
  def get_simulation_thread(
58
- model: Any, rerun_failed_realizations: bool = False, use_ipc_protocol: bool = False
58
+ model: Any,
59
+ rerun_failed_realizations: bool = False,
60
+ use_ipc_protocol: bool = False,
61
+ prioritize_private_ip_address: bool = False,
59
62
  ) -> ErtThread:
60
- evaluator_server_config = EvaluatorServerConfig(use_ipc_protocol=use_ipc_protocol)
63
+ evaluator_server_config = EvaluatorServerConfig(
64
+ use_ipc_protocol=use_ipc_protocol,
65
+ prioritize_private_ip_address=prioritize_private_ip_address,
66
+ )
61
67
 
62
68
  def run() -> None:
63
69
  model.api.start_simulations_thread(
@@ -354,6 +360,10 @@ class ExperimentPanel(QWidget):
354
360
  return
355
361
  QApplication.restoreOverrideCursor()
356
362
 
363
+ self.configuration_summary.log_summary(
364
+ args.mode, model.get_number_of_active_realizations()
365
+ )
366
+
357
367
  self._dialog = RunDialog(
358
368
  f"Experiment - {self._config_file} {find_ert_info()}",
359
369
  model.api,
@@ -364,7 +374,9 @@ class ExperimentPanel(QWidget):
364
374
  run_path=Path(self.config.runpath_config.runpath_format_string),
365
375
  storage_path=self._notifier.storage.path,
366
376
  )
367
- self._dialog.set_queue_system_name(model.queue_system)
377
+ self._dialog.queue_system.setText(
378
+ f"Queue system:\n{model.queue_config.queue_system.formatted_name}"
379
+ )
368
380
  self.experiment_started.emit(self._dialog)
369
381
  self._simulation_done = False
370
382
  self.run_button.setEnabled(self._simulation_done)
@@ -375,6 +387,7 @@ class ExperimentPanel(QWidget):
375
387
  rerun_failed_realizations,
376
388
  use_ipc_protocol=self.config.queue_config.queue_system
377
389
  == QueueSystem.LOCAL,
390
+ prioritize_private_ip_address=self.config.prioritize_private_ip_address,
378
391
  )
379
392
  self._dialog.setup_event_monitoring(rerun_failed_realizations)
380
393
  simulation_thread.start()
@@ -3,9 +3,9 @@ from dataclasses import dataclass
3
3
  from typing import cast
4
4
 
5
5
  import numpy as np
6
- from PyQt6.QtCore import Qt
6
+ from PyQt6.QtCore import pyqtSignal
7
7
  from PyQt6.QtCore import pyqtSlot as Slot
8
- from PyQt6.QtWidgets import QFormLayout, QLabel, QWidget
8
+ from PyQt6.QtWidgets import QComboBox, QFormLayout, QLabel, QWidget
9
9
  from typing_extensions import override
10
10
 
11
11
  from ert.config import AnalysisConfig, ErrorInfo
@@ -16,11 +16,11 @@ from ert.gui.ertwidgets import (
16
16
  CopyableLabel,
17
17
  EnsembleSelector,
18
18
  StringBox,
19
+ Suggestor,
19
20
  TargetEnsembleModel,
20
21
  )
21
22
  from ert.gui.simulation.experiment_config_panel import ExperimentConfigPanel
22
- from ert.gui.suggestor import Suggestor
23
- from ert.mode_definitions import MANUAL_UPDATE_MODE
23
+ from ert.mode_definitions import MANUAL_ENIF_UPDATE_MODE, MANUAL_UPDATE_MODE
24
24
  from ert.run_models.manual_update import ManualUpdate
25
25
  from ert.storage import Ensemble, RealizationStorageState
26
26
  from ert.validation import EnsembleRealizationsArgument, ProperNameFormatArgument
@@ -38,6 +38,8 @@ class Arguments:
38
38
 
39
39
 
40
40
  class ManualUpdatePanel(ExperimentConfigPanel):
41
+ updateMethodChanged = pyqtSignal(str)
42
+
41
43
  def __init__(
42
44
  self,
43
45
  run_path: str,
@@ -49,9 +51,17 @@ class ManualUpdatePanel(ExperimentConfigPanel):
49
51
  self.setObjectName("Manual_update_panel")
50
52
 
51
53
  layout = QFormLayout()
52
- lab = QLabel(ManualUpdate.name())
53
- lab.setAlignment(Qt.AlignmentFlag.AlignLeft)
54
- layout.addRow(lab)
54
+ self._update_method_dropdown = QComboBox()
55
+ self._update_method_dropdown.addItems(
56
+ ["ES Update", "EnIF Update (Experimental)"]
57
+ )
58
+ self._update_method_dropdown.currentTextChanged.connect(
59
+ self._on_update_method_changed
60
+ )
61
+ self._update_method_dropdown.setObjectName("manual_update_method_dropdown")
62
+
63
+ layout.addRow("Update method:", self._update_method_dropdown)
64
+
55
65
  self._ensemble_selector = EnsembleSelector(
56
66
  notifier, show_only_with_response_data=True
57
67
  )
@@ -97,6 +107,17 @@ class ManualUpdatePanel(ExperimentConfigPanel):
97
107
  self._ensemble_selector.currentIndexChanged.connect(self._realizations_from_fs)
98
108
  self.setLayout(layout)
99
109
 
110
+ @property
111
+ def selected_update_method(self) -> str:
112
+ return self._update_method_dropdown.currentText()
113
+
114
+ @Slot(str)
115
+ def _on_update_method_changed(self, new_method: str) -> None:
116
+ if new_method == "ES Update":
117
+ self._analysis_module_edit.show()
118
+ else:
119
+ self._analysis_module_edit.hide()
120
+
100
121
  @override
101
122
  def isConfigurationValid(self) -> bool:
102
123
  return (
@@ -107,7 +128,9 @@ class ManualUpdatePanel(ExperimentConfigPanel):
107
128
  @override
108
129
  def get_experiment_arguments(self) -> Arguments:
109
130
  return Arguments(
110
- mode=MANUAL_UPDATE_MODE,
131
+ mode=MANUAL_UPDATE_MODE
132
+ if self.selected_update_method == "ES Update"
133
+ else MANUAL_ENIF_UPDATE_MODE,
111
134
  ensemble_id=str(
112
135
  cast(Ensemble, self._ensemble_selector.selected_ensemble).id
113
136
  ),
@@ -18,14 +18,14 @@ from ert.gui.ertwidgets import (
18
18
  CopyableLabel,
19
19
  EnsembleSelector,
20
20
  StringBox,
21
+ Suggestor,
21
22
  TargetEnsembleModel,
22
23
  TextModel,
23
24
  ValueModel,
25
+ get_parameters_button,
24
26
  )
25
- from ert.gui.ertwidgets.parameterviewer import get_parameters_button
26
- from ert.gui.suggestor import Suggestor
27
27
  from ert.mode_definitions import ES_MDA_MODE
28
- from ert.run_models import MultipleDataAssimilation
28
+ from ert.run_models import MultipleDataAssimilation, MultipleDataAssimilationConfig
29
29
  from ert.storage.realization_storage_state import RealizationStorageState
30
30
  from ert.validation import (
31
31
  ExperimentValidation,
@@ -114,7 +114,7 @@ class MultipleDataAssimilationPanel(ExperimentConfigPanel):
114
114
  self._target_ensemble_format_field.setValidator(ProperNameFormatArgument())
115
115
  layout.addRow("Target ensemble format:", self._target_ensemble_format_field)
116
116
 
117
- self.weights = MultipleDataAssimilation.default_weights
117
+ self.weights = MultipleDataAssimilationConfig.default_weights
118
118
  self.weights_valid = True
119
119
  self._createInputForWeights(layout)
120
120
 
@@ -135,6 +135,8 @@ class MultipleDataAssimilationPanel(ExperimentConfigPanel):
135
135
  self._active_realizations_field.model.setValueFromMask( # type: ignore
136
136
  active_realizations
137
137
  )
138
+ self._initial_active_realizations = active_realizations
139
+
138
140
  self._ensemble_selector = EnsembleSelector(notifier)
139
141
  self._previous_ensemble_realizations_validator = EnsembleRealizationsArgument(
140
142
  lambda: self._ensemble_selector.selected_ensemble,
@@ -215,7 +217,7 @@ class MultipleDataAssimilationPanel(ExperimentConfigPanel):
215
217
 
216
218
  self._relative_iteration_weights_box.setText(
217
219
  self._ensemble_selector.selected_ensemble.relative_weights
218
- or MultipleDataAssimilation.default_weights
220
+ or MultipleDataAssimilationConfig.default_weights
219
221
  )
220
222
  self._evaluate_weights_box_enabled()
221
223
 
@@ -250,9 +252,9 @@ class MultipleDataAssimilationPanel(ExperimentConfigPanel):
250
252
  self._ensemble_selector.selected_ensemble is not None
251
253
  and self._ensemble_selector.selected_ensemble.relative_weights
252
254
  )
253
- or MultipleDataAssimilation.default_weights
255
+ or MultipleDataAssimilationConfig.default_weights
254
256
  if self._restart_box.isChecked()
255
- else MultipleDataAssimilation.default_weights
257
+ else MultipleDataAssimilationConfig.default_weights
256
258
  )
257
259
  if self._restart_box.isChecked():
258
260
  self._active_realizations_field.setValidator(
@@ -264,7 +266,9 @@ class MultipleDataAssimilationPanel(ExperimentConfigPanel):
264
266
  self._active_realizations_field.setValidator(
265
267
  self._new_ensemble_realizations_validator
266
268
  )
267
- self._active_realizations_field.model.setValue(value="")
269
+ self._active_realizations_field.model.setValueFromMask( # type: ignore
270
+ self._initial_active_realizations
271
+ )
268
272
 
269
273
  def _createInputForWeights(self, layout: QFormLayout) -> None:
270
274
  relative_iteration_weights_model = ValueModel(self.weights)