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
@@ -5,14 +5,14 @@ import logging
5
5
  import os
6
6
  import re
7
7
  import shutil
8
+ import types
8
9
  from collections.abc import Generator, MutableSequence
9
10
  from datetime import datetime
10
11
  from functools import cached_property
11
12
  from pathlib import Path
12
13
  from tempfile import NamedTemporaryFile
13
14
  from textwrap import dedent
14
- from types import TracebackType
15
- from typing import Any
15
+ from typing import Any, Self
16
16
  from uuid import UUID, uuid4
17
17
 
18
18
  import polars as pl
@@ -20,6 +20,7 @@ import xarray as xr
20
20
  from filelock import FileLock, Timeout
21
21
  from pydantic import BaseModel, Field
22
22
 
23
+ import ert.storage
23
24
  from ert.config import ErtConfig, ParameterConfig, ResponseConfig
24
25
  from ert.shared import __version__
25
26
 
@@ -30,7 +31,7 @@ from .realization_storage_state import RealizationStorageState
30
31
 
31
32
  logger = logging.getLogger(__name__)
32
33
 
33
- _LOCAL_STORAGE_VERSION = 15
34
+ _LOCAL_STORAGE_VERSION = 21
34
35
 
35
36
 
36
37
  class _Migrations(BaseModel):
@@ -64,8 +65,7 @@ class LocalStorage(BaseMode):
64
65
  self,
65
66
  path: str | os.PathLike[str],
66
67
  mode: Mode,
67
- *,
68
- ignore_migration_check: bool = False,
68
+ stage_for_migration: bool = False,
69
69
  ) -> None:
70
70
  """
71
71
  Initializes the LocalStorage instance.
@@ -76,19 +76,22 @@ class LocalStorage(BaseMode):
76
76
  The file system path to the storage.
77
77
  mode : Mode
78
78
  The access mode for the storage (read/write).
79
- ignore_migration_check : bool
80
- If True, skips migration checks during initialization.
79
+ stage_for_migration : bool
80
+ Whether to avoid reloading storage to allow migration
81
81
  """
82
82
 
83
- super().__init__(mode)
84
83
  self.path = Path(path).absolute()
84
+ super().__init__(mode)
85
+
86
+ if mode.can_write:
87
+ self._acquire_lock()
85
88
 
86
- self._experiments: dict[UUID, LocalExperiment]
87
- self._ensembles: dict[UUID, LocalEnsemble]
88
- self._index: _Index
89
+ self._experiments: dict[UUID, LocalExperiment] = {}
90
+ self._ensembles: dict[UUID, LocalEnsemble] = {}
91
+ self._index: _Index = _Index()
89
92
 
90
93
  try:
91
- version = _storage_version(self.path)
94
+ self.version = _storage_version(self.path)
92
95
  except FileNotFoundError as err:
93
96
  # No index json, will have a problem if other components of storage exists
94
97
  errors = []
@@ -100,28 +103,45 @@ class LocalStorage(BaseMode):
100
103
  errors.append(f"ensemble path: {self.path / self.ENSEMBLES_PATH}")
101
104
  if errors:
102
105
  raise ValueError(f"No index.json, but found: {errors}") from err
103
- version = _LOCAL_STORAGE_VERSION
106
+ self.version = _LOCAL_STORAGE_VERSION
104
107
 
105
- if version > _LOCAL_STORAGE_VERSION:
106
- raise RuntimeError(
107
- f"Cannot open storage '{self.path}': Storage version {version} "
108
- f"is newer than the current version {_LOCAL_STORAGE_VERSION}, "
109
- "upgrade ert to continue, or run with a different ENSPATH"
110
- )
111
- if self.can_write:
112
- self._acquire_lock()
113
- if version < _LOCAL_STORAGE_VERSION and not ignore_migration_check:
114
- self._migrate(version)
115
- self._index = self._load_index()
116
- self._save_index()
117
- elif version < _LOCAL_STORAGE_VERSION:
108
+ if self.check_migration_needed(Path(self.path)) and not self.can_write:
118
109
  raise RuntimeError(
119
110
  f"Cannot open storage '{self.path}' in read-only mode: "
120
- f"Storage version {version} is too old. Run ert to initiate migration."
111
+ f"Storage version {self.version} is too old. "
112
+ f"Run ert to initiate migration."
113
+ )
114
+
115
+ if not stage_for_migration:
116
+ self.reload()
117
+
118
+ if mode.can_write:
119
+ self._save_index()
120
+
121
+ @staticmethod
122
+ def check_migration_needed(storage_dir: Path) -> bool:
123
+ try:
124
+ version = _storage_version(storage_dir)
125
+ except FileNotFoundError:
126
+ version = _LOCAL_STORAGE_VERSION
127
+
128
+ if version > _LOCAL_STORAGE_VERSION:
129
+ raise ert.storage.ErtStorageException(
130
+ f"Cannot open storage '{storage_dir.absolute()}': Storage version "
131
+ f"{version} is newer than the current version {_LOCAL_STORAGE_VERSION}"
132
+ f", upgrade ert to continue, or run with a different ENSPATH"
121
133
  )
122
- self.refresh()
123
134
 
124
- def refresh(self) -> None:
135
+ return version < _LOCAL_STORAGE_VERSION
136
+
137
+ @staticmethod
138
+ def perform_migration(path: Path) -> None:
139
+ if LocalStorage.check_migration_needed(path):
140
+ with LocalStorage(path, Mode("w"), True) as storage:
141
+ storage._migrate(storage.version)
142
+ storage.reload()
143
+
144
+ def reload(self) -> None:
125
145
  """
126
146
  Reloads the index, experiments, and ensembles from the storage.
127
147
 
@@ -257,14 +277,14 @@ class LocalStorage(BaseMode):
257
277
  def _swap_path(self) -> Path:
258
278
  return self.path / self.SWAP_PATH
259
279
 
260
- def __enter__(self) -> LocalStorage:
280
+ def __enter__(self) -> Self:
261
281
  return self
262
282
 
263
283
  def __exit__(
264
284
  self,
265
- exception: Exception,
266
- exception_type: type[Exception],
267
- traceback: TracebackType,
285
+ exception: type[BaseException] | None,
286
+ exception_type: BaseException | None,
287
+ traceback: types.TracebackType | None,
268
288
  ) -> None:
269
289
  self.close()
270
290
 
@@ -283,22 +303,16 @@ class LocalStorage(BaseMode):
283
303
  def close(self) -> None:
284
304
  """
285
305
  Closes the storage, releasing any acquired locks and saving the index.
286
-
287
306
  This method should be called to cleanly close the storage, especially
288
307
  when it was opened in write mode. Failing to call this method may leave
289
308
  a lock file behind, which would interfere with subsequent access to
290
309
  the storage.
291
310
  """
292
-
293
311
  self._ensembles.clear()
294
312
  self._experiments.clear()
295
313
 
296
- if not self.can_write:
297
- return
298
-
299
- self._save_index()
300
-
301
314
  if self.can_write:
315
+ self._save_index()
302
316
  self._release_lock()
303
317
 
304
318
  def _release_lock(self) -> None:
@@ -497,6 +511,12 @@ class LocalStorage(BaseMode):
497
511
  to13,
498
512
  to14,
499
513
  to15,
514
+ to16,
515
+ to17,
516
+ to18,
517
+ to19,
518
+ to20,
519
+ to21,
500
520
  )
501
521
 
502
522
  try:
@@ -526,7 +546,7 @@ class LocalStorage(BaseMode):
526
546
 
527
547
  self._index = self._load_index()
528
548
 
529
- logger.info("storage backed up for version less than 6")
549
+ logger.info("Storage backed up for version less than 5")
530
550
  print(self._legacy_storage_migration_message(bkup_path, "14.6.*"))
531
551
  return None
532
552
  elif version < _LOCAL_STORAGE_VERSION:
@@ -541,6 +561,12 @@ class LocalStorage(BaseMode):
541
561
  12: to13,
542
562
  13: to14,
543
563
  14: to15,
564
+ 15: to16,
565
+ 16: to17,
566
+ 17: to18,
567
+ 18: to19,
568
+ 19: to20,
569
+ 20: to21,
544
570
  }
545
571
  for from_version in range(version, _LOCAL_STORAGE_VERSION):
546
572
  migrations[from_version].migrate(self.path)
@@ -558,7 +584,7 @@ class LocalStorage(BaseMode):
558
584
  Get a unique experiment name
559
585
 
560
586
  If an experiment with the given name exists an _0 is appended
561
- or _n+1 where n is the the largest postfix found for the given experiment name
587
+ or _n+1 where n is the largest postfix found for the given experiment name
562
588
  """
563
589
  if not experiment_name:
564
590
  return self.get_unique_experiment_name("default")
@@ -44,7 +44,7 @@ def migrate(path: Path) -> None:
44
44
  array = ds.isel(realizations=0, drop=True)["values"]
45
45
  realization = int(real_dir.name.split("-")[1])
46
46
 
47
- def parse_value(value: float | int | str) -> float | int | str:
47
+ def parse_value(value: float | str) -> float | int | str:
48
48
  if isinstance(value, float | int):
49
49
  return value
50
50
  try:
@@ -0,0 +1,38 @@
1
+ import json
2
+ from pathlib import Path
3
+ from typing import Any
4
+
5
+ info = "Remove mask file from field config"
6
+
7
+
8
+ def migrate_field_param(parameters_json: dict[str, Any]) -> dict[str, Any]:
9
+ new_configs = {}
10
+ for param_config in parameters_json.values():
11
+ if param_config["type"] == "field":
12
+ del param_config["mask_file"]
13
+
14
+ new_configs[param_config["name"]] = param_config
15
+ return new_configs
16
+
17
+
18
+ def delete_mask_file(experiment: Path) -> None:
19
+ # delete grid mask file if it exists
20
+ grid_mask_file = experiment / "grid_mask.npy"
21
+ if grid_mask_file.exists():
22
+ grid_mask_file.unlink()
23
+
24
+
25
+ def migrate_fields(experiment: Path) -> None:
26
+ with open(experiment / "parameter.json", encoding="utf-8") as fin:
27
+ parameters_json = json.load(fin)
28
+
29
+ new_parameter_configs = migrate_field_param(parameters_json)
30
+ Path(experiment / "parameter.json").write_text(
31
+ json.dumps(new_parameter_configs, indent=2), encoding="utf-8"
32
+ )
33
+
34
+
35
+ def migrate(path: Path) -> None:
36
+ for experiment in path.glob("experiments/*"):
37
+ migrate_fields(experiment)
38
+ delete_mask_file(experiment)
@@ -0,0 +1,42 @@
1
+ import json
2
+ from pathlib import Path
3
+ from typing import Any
4
+
5
+ info = "Migrate realization error type from IntEnum to StrEnum"
6
+
7
+
8
+ def migrate_realization_errors_json_content(
9
+ error_json: dict[str, Any],
10
+ ) -> dict[str, Any]:
11
+ int_to_str = {
12
+ 1: "undefined",
13
+ 2: "parameters_loaded",
14
+ 4: "responses_loaded",
15
+ 8: "failure_in_current",
16
+ 16: "failure_in_parent",
17
+ # To cover cases who ran with storage version 16
18
+ # and StrEnum _RealizationStorageState
19
+ "undefined": "undefined",
20
+ "parameters_loaded": "parameters_loaded",
21
+ "responses_loaded": "responses_loaded",
22
+ "failure_in_current": "failure_in_current",
23
+ "failure_in_parent": "failure_in_parent",
24
+ }
25
+ return error_json | {"type": int_to_str[error_json["type"]]}
26
+
27
+
28
+ def migrate_realization_errors(path: Path) -> None:
29
+ for realization_error in path.glob("ensembles/*/realization-*/error.json"):
30
+ old_error_json_content = json.loads(
31
+ realization_error.read_text(encoding="utf-8")
32
+ )
33
+ realization_error.write_text(
34
+ json.dumps(
35
+ migrate_realization_errors_json_content(old_error_json_content),
36
+ indent=2,
37
+ )
38
+ )
39
+
40
+
41
+ def migrate(path: Path) -> None:
42
+ migrate_realization_errors(path)
@@ -0,0 +1,11 @@
1
+ from pathlib import Path
2
+
3
+ info = (
4
+ "Added localization attributes to summary observations."
5
+ "Added RFT observations."
6
+ "No change to current storage, only additions. "
7
+ )
8
+
9
+
10
+ def migrate(path: Path) -> None:
11
+ pass
@@ -0,0 +1,34 @@
1
+ import json
2
+ from pathlib import Path
3
+ from typing import Any
4
+
5
+ info = "Add dimensionality attribute to parameters"
6
+
7
+
8
+ def migrate_param(parameters_json: dict[str, Any]) -> dict[str, Any]:
9
+ new_configs = {}
10
+ for param_config in parameters_json.values():
11
+ if param_config["type"] == "surface":
12
+ param_config["dimensionality"] = 2
13
+ elif param_config["type"] == "field":
14
+ param_config["dimensionality"] = 3
15
+ else:
16
+ param_config["dimensionality"] = 1
17
+
18
+ new_configs[param_config["name"]] = param_config
19
+ return new_configs
20
+
21
+
22
+ def migrate_parameters_for_experiment(experiment: Path) -> None:
23
+ with open(experiment / "parameter.json", encoding="utf-8") as fin:
24
+ parameters_json = json.load(fin)
25
+
26
+ new_parameter_configs = migrate_param(parameters_json)
27
+ Path(experiment / "parameter.json").write_text(
28
+ json.dumps(new_parameter_configs, indent=2), encoding="utf-8"
29
+ )
30
+
31
+
32
+ def migrate(path: Path) -> None:
33
+ for experiment in path.glob("experiments/*"):
34
+ migrate_parameters_for_experiment(experiment)
@@ -0,0 +1,23 @@
1
+ import json
2
+ from pathlib import Path
3
+ from typing import Any
4
+
5
+ info = "Remove redundant .name attribute from responses."
6
+
7
+
8
+ def config_without_name_attr(config: dict[str, Any]) -> dict[str, Any]:
9
+ new_json = {**config}
10
+ new_json.pop("name", None)
11
+
12
+ return new_json
13
+
14
+
15
+ def migrate(path: Path) -> None:
16
+ for response_json_path in path.glob("experiments/*/responses.json"):
17
+ old_json = json.loads((response_json_path).read_text(encoding="utf-8"))
18
+ new_json = {
19
+ response_type: config_without_name_attr(config)
20
+ for response_type, config in old_json.items()
21
+ }
22
+
23
+ response_json_path.write_text(json.dumps(new_json, indent=2), encoding="utf-8")
@@ -0,0 +1,25 @@
1
+ import json
2
+ from pathlib import Path
3
+ from typing import Any
4
+
5
+ info = "Remove refcase from summary response configs"
6
+
7
+
8
+ def config_without_refcase(summary_config: dict[str, Any]) -> dict[str, Any]:
9
+ new_json = {**summary_config}
10
+ new_json.pop("refcase", None)
11
+
12
+ return new_json
13
+
14
+
15
+ def migrate(path: Path) -> None:
16
+ for response_json_path in path.glob("experiments/*/responses.json"):
17
+ old_json = json.loads((response_json_path).read_text(encoding="utf-8"))
18
+ new_json = {
19
+ response_type: config_without_refcase(response_config)
20
+ if response_config["type"] == "summary"
21
+ else response_config
22
+ for response_type, response_config in old_json.items()
23
+ }
24
+
25
+ response_json_path.write_text(json.dumps(new_json, indent=2), encoding="utf-8")
@@ -138,14 +138,14 @@ def _migrate_observations_to_grouped_parquet(path: Path) -> None:
138
138
 
139
139
  for response_type in ["gen_data", "summary"]:
140
140
  infos = [
141
- _info for _info in obs_ds_infos if _info.response_type == response_type
141
+ info_ for info_ in obs_ds_infos if info_.response_type == response_type
142
142
  ]
143
143
  if len(infos) > 0:
144
- concatd_df = pl.concat([_info.polars_df for _info in infos])
144
+ concatd_df = pl.concat([info_.polars_df for info_ in infos])
145
145
  concatd_df.write_parquet(experiment / "observations" / response_type)
146
146
 
147
- for _info in infos:
148
- os.remove(_info.original_ds_path)
147
+ for info_ in infos:
148
+ os.remove(info_.original_ds_path)
149
149
 
150
150
 
151
151
  def migrate(path: Path) -> None:
ert/substitutions.py CHANGED
@@ -4,6 +4,7 @@ import logging
4
4
  import re
5
5
  from collections import UserDict
6
6
  from collections.abc import Mapping
7
+ from typing import Self
7
8
 
8
9
  logger = logging.getLogger(__name__)
9
10
  _PATTERN = re.compile(r"<[^<>]+>")
@@ -25,44 +26,27 @@ class Substitutions(UserDict[str, str]):
25
26
  """
26
27
  return _substitute(self, to_substitute, context, max_iterations, warn_max_iter)
27
28
 
28
- @staticmethod
29
- def substitute_parameters(
30
- to_substitute: str, parameter_values: Mapping[str, Mapping[str, str | float]]
31
- ) -> str:
32
- """Applies the substitution '<param_name>' to parameter value
33
- Args:
34
- parameter_values: Mapping from parameter name to parameter value
35
- to_substitute: string to substitute magic strings in
36
- Returns:
37
- substituted string
38
- """
39
- for values in parameter_values.values():
40
- for param_name, value in values.items():
41
- if isinstance(value, (int, float)):
42
- formatted_value = f"{value:.6g}"
43
- else:
44
- formatted_value = str(value)
45
- to_substitute = to_substitute.replace(
46
- f"<{param_name}>", formatted_value
47
- )
48
- return to_substitute
49
-
50
- def substitute_real_iter(
51
- self, to_substitute: str, realization: int, iteration: int
52
- ) -> str:
29
+ def real_iter_substituter(self, realization: int, iteration: int) -> Self:
53
30
  extra_data = {
54
31
  "<IENS>": str(realization),
55
32
  "<ITER>": str(iteration),
56
33
  }
57
34
 
58
- model_id_key = f"<GEO_ID_{realization}_{iteration}>"
35
+ model_id_key = f"<REALIZATION_ID_{realization}_{iteration}>"
59
36
  if model_id_key in self:
60
- extra_data["<GEO_ID>"] = self[model_id_key]
37
+ extra_data["<REALIZATION_ID>"] = self[model_id_key]
61
38
  sim_id_key = f"<SIM_DIR_{realization}_{iteration}>"
62
39
  if sim_id_key in self:
63
40
  extra_data["<SIM_DIR>"] = self[sim_id_key]
64
41
 
65
- return Substitutions({**self, **extra_data}).substitute(to_substitute)
42
+ return type(self)({**self, **extra_data})
43
+
44
+ def substitute_real_iter(
45
+ self, to_substitute: str, realization: int, iteration: int
46
+ ) -> str:
47
+ return self.real_iter_substituter(realization, iteration).substitute(
48
+ to_substitute
49
+ )
66
50
 
67
51
  def _concise_representation(self) -> str:
68
52
  return (
@@ -47,19 +47,19 @@ class ActiveRange:
47
47
  raise ValueError(
48
48
  f"Only digits, commas, dashes and spaces are allowed, got {rangestring}"
49
49
  )
50
- for _range in rangestring.split(","):
51
- if "-" in _range:
52
- if len(_range.split("-")) != 2:
53
- raise ValueError(f"Invalid range specified, got {_range}")
54
- realization_bounds = _range.split("-")
50
+ for range_ in rangestring.split(","):
51
+ if "-" in range_:
52
+ if len(range_.split("-")) != 2:
53
+ raise ValueError(f"Invalid range specified, got {range_}")
54
+ realization_bounds = range_.split("-")
55
55
  start = int(realization_bounds[0])
56
56
  end = int(realization_bounds[1])
57
57
  if end < start:
58
58
  raise ValueError(
59
- f"Invalid direction in range specified, got {_range}"
59
+ f"Invalid direction in range specified, got {range_}"
60
60
  )
61
61
  else:
62
- int(_range)
62
+ int(range_)
63
63
  return rangestring
64
64
 
65
65
  @classmethod
@@ -66,11 +66,11 @@ def rangestring_to_mask(rangestring: str, length: int) -> list[bool]:
66
66
  # An empty string means no active indecies. Note that an
67
67
  # IndexRange-typed instance being None means the opposite
68
68
  return mask
69
- for _range in rangestring.split(","):
70
- if "-" in _range:
71
- if len(_range.strip().split("-")) != 2:
72
- raise ValueError(f"Wrong range syntax {_range}")
73
- start, end = map(int, _range.strip().split("-"))
69
+ for range_ in rangestring.split(","):
70
+ if "-" in range_:
71
+ if len(range_.strip().split("-")) != 2:
72
+ raise ValueError(f"Wrong range syntax {range_}")
73
+ start, end = map(int, range_.strip().split("-"))
74
74
  if end < start:
75
75
  raise ValueError(f"Range {start}-{end} has invalid direction")
76
76
  if end + 1 > length:
@@ -78,12 +78,12 @@ def rangestring_to_mask(rangestring: str, length: int) -> list[bool]:
78
78
  f"Range endpoint {end} is beyond the mask length {length}"
79
79
  )
80
80
  mask[start : end + 1] = [True] * (end + 1 - start)
81
- elif _range:
82
- if int(_range) + 1 > length:
81
+ elif range_:
82
+ if int(range_) + 1 > length:
83
83
  raise ValueError(
84
- f"Realization index {_range} is beyond the mask length {length}"
84
+ f"Realization index {range_} is beyond the mask length {length}"
85
85
  )
86
- mask[int(_range)] = True
86
+ mask[int(range_)] = True
87
87
  return mask
88
88
 
89
89
 
@@ -102,14 +102,14 @@ def rangestring_to_list(rangestring: str) -> list[int]:
102
102
  result: set[int] = set()
103
103
  if not rangestring:
104
104
  return []
105
- for _range in rangestring.split(","):
106
- if "-" in _range:
107
- if len(_range.strip().split("-")) != 2:
108
- raise ValueError(f"Wrong range syntax {_range}")
109
- start, end = map(int, _range.strip().split("-"))
105
+ for range_ in rangestring.split(","):
106
+ if "-" in range_:
107
+ if len(range_.strip().split("-")) != 2:
108
+ raise ValueError(f"Wrong range syntax {range_}")
109
+ start, end = map(int, range_.strip().split("-"))
110
110
  if end < start:
111
111
  raise ValueError(f"Range {start}-{end} has invalid direction")
112
112
  result.update(range(start, end + 1))
113
- elif _range:
114
- result.add(int(_range))
113
+ elif range_:
114
+ result.add(int(range_))
115
115
  return list(result)
ert/workflow_runner.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
+ import types
4
5
  from concurrent import futures
5
6
  from concurrent.futures import Future
6
7
  from typing import Any, Self
@@ -127,7 +128,7 @@ class WorkflowRunner:
127
128
  self,
128
129
  exc_type: type[BaseException] | None,
129
130
  exc_value: BaseException | None,
130
- traceback: Any,
131
+ traceback: types.TracebackType | None,
131
132
  ) -> None:
132
133
  self.wait()
133
134
 
@@ -1,15 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ert
3
- Version: 17.0.0
3
+ Version: 19.0.0rc2
4
4
  Summary: Ensemble based Reservoir Tool (ERT)
5
5
  Author-email: Equinor ASA <fg_sib-scout@equinor.com>
6
- License: GPL-3.0
6
+ License-Expression: GPL-3.0-only
7
7
  Project-URL: Repository, https://github.com/equinor/ert
8
8
  Platform: all
9
9
  Classifier: Development Status :: 5 - Production/Stable
10
10
  Classifier: Environment :: Other Environment
11
11
  Classifier: Intended Audience :: Science/Research
12
- Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
13
12
  Classifier: Natural Language :: English
14
13
  Classifier: Programming Language :: Python
15
14
  Classifier: Programming Language :: Python :: 3.11
@@ -21,6 +20,7 @@ Requires-Python: <3.14,>=3.11
21
20
  Description-Content-Type: text/markdown
22
21
  License-File: COPYING
23
22
  Requires-Dist: aiohttp
23
+ Requires-Dist: anyio
24
24
  Requires-Dist: colorama
25
25
  Requires-Dist: cryptography
26
26
  Requires-Dist: decorator
@@ -52,15 +52,15 @@ Requires-Dist: progressbar2
52
52
  Requires-Dist: psutil
53
53
  Requires-Dist: pyarrow
54
54
  Requires-Dist: pydantic>2
55
- Requires-Dist: pyqt6!=6.10.0
55
+ Requires-Dist: pyqt6<6.10.0
56
56
  Requires-Dist: python-dateutil
57
57
  Requires-Dist: python-multipart
58
58
  Requires-Dist: pyyaml
59
59
  Requires-Dist: pyzmq
60
60
  Requires-Dist: requests
61
61
  Requires-Dist: resfo>=5.0.0
62
- Requires-Dist: ropt-dakota<0.25,>=0.24
63
- Requires-Dist: ropt[pandas]<0.25,>=0.24
62
+ Requires-Dist: ropt-dakota<0.26,>=0.25
63
+ Requires-Dist: ropt[pandas]<0.26,>=0.25
64
64
  Requires-Dist: ruamel.yaml
65
65
  Requires-Dist: scipy>=1.10.1
66
66
  Requires-Dist: seaborn
@@ -73,7 +73,7 @@ Requires-Dist: uvicorn>=0.17.0
73
73
  Requires-Dist: websockets
74
74
  Requires-Dist: xarray
75
75
  Requires-Dist: xtgeo>=3.3.0
76
- Requires-Dist: resfo-utilities>=0.3.0
76
+ Requires-Dist: resfo-utilities>=0.5.0
77
77
  Provides-Extra: dev
78
78
  Requires-Dist: furo; extra == "dev"
79
79
  Requires-Dist: hypothesis!=6.102.0,!=6.112.3,>=6.85; extra == "dev"
@@ -94,6 +94,7 @@ Requires-Dist: pytest-rerunfailures!=16.0; extra == "dev"
94
94
  Requires-Dist: pytest-snapshot; extra == "dev"
95
95
  Requires-Dist: pytest-timeout; extra == "dev"
96
96
  Requires-Dist: pytest-xdist; extra == "dev"
97
+ Requires-Dist: pytest-durations; extra == "dev"
97
98
  Requires-Dist: pytest>6; extra == "dev"
98
99
  Requires-Dist: resdata; extra == "dev"
99
100
  Requires-Dist: rust-just; extra == "dev"
@@ -105,7 +106,7 @@ Requires-Dist: sphinx-copybutton; extra == "dev"
105
106
  Requires-Dist: sphinxcontrib.datatemplates; extra == "dev"
106
107
  Requires-Dist: json-schema-for-humans; extra == "dev"
107
108
  Requires-Dist: xlsxwriter>=3.2.3; extra == "dev"
108
- Requires-Dist: resfo-utilities[testing]>=0.3.0; extra == "dev"
109
+ Requires-Dist: resfo-utilities[testing]>=0.5.0; extra == "dev"
109
110
  Provides-Extra: style
110
111
  Requires-Dist: pre-commit; extra == "style"
111
112
  Provides-Extra: types