ert 16.0.9__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 (286) hide show
  1. _ert/events.py +19 -2
  2. _ert/forward_model_runner/client.py +6 -2
  3. _ert/forward_model_runner/fm_dispatch.py +9 -6
  4. _ert/forward_model_runner/reporting/event.py +1 -0
  5. _ert/forward_model_runner/runner.py +1 -2
  6. _ert/utils.py +12 -0
  7. ert/__main__.py +58 -38
  8. ert/analysis/_enif_update.py +8 -4
  9. ert/analysis/_es_update.py +19 -6
  10. ert/analysis/_update_commons.py +16 -6
  11. ert/base_model_context.py +1 -1
  12. ert/cli/main.py +17 -12
  13. ert/cli/monitor.py +7 -0
  14. ert/config/__init__.py +17 -6
  15. ert/config/_create_observation_dataframes.py +118 -21
  16. ert/config/_get_num_cpu.py +1 -1
  17. ert/config/_observations.py +91 -2
  18. ert/config/_read_summary.py +74 -328
  19. ert/config/design_matrix.py +62 -23
  20. ert/config/distribution.py +1 -1
  21. ert/config/ensemble_config.py +9 -17
  22. ert/config/ert_config.py +155 -58
  23. ert/config/everest_control.py +234 -0
  24. ert/config/{everest_constraints_config.py → everest_response.py} +27 -15
  25. ert/config/field.py +99 -90
  26. ert/config/forward_model_step.py +122 -17
  27. ert/config/gen_data_config.py +5 -10
  28. ert/config/gen_kw_config.py +11 -41
  29. ert/config/known_response_types.py +14 -0
  30. ert/config/parameter_config.py +1 -33
  31. ert/config/parsing/_option_dict.py +10 -2
  32. ert/config/parsing/config_errors.py +1 -1
  33. ert/config/parsing/config_keywords.py +2 -1
  34. ert/config/parsing/config_schema.py +23 -11
  35. ert/config/parsing/config_schema_deprecations.py +3 -3
  36. ert/config/parsing/config_schema_item.py +26 -11
  37. ert/config/parsing/context_values.py +3 -3
  38. ert/config/parsing/file_context_token.py +1 -1
  39. ert/config/parsing/observations_parser.py +6 -2
  40. ert/config/parsing/queue_system.py +9 -0
  41. ert/config/parsing/schema_item_type.py +1 -0
  42. ert/config/queue_config.py +42 -50
  43. ert/config/response_config.py +0 -8
  44. ert/config/rft_config.py +275 -0
  45. ert/config/summary_config.py +3 -8
  46. ert/config/surface_config.py +73 -26
  47. ert/config/workflow_fixtures.py +2 -1
  48. ert/config/workflow_job.py +135 -54
  49. ert/dark_storage/client/__init__.py +2 -2
  50. ert/dark_storage/client/_session.py +4 -4
  51. ert/dark_storage/client/client.py +2 -2
  52. ert/dark_storage/common.py +12 -3
  53. ert/dark_storage/compute/misfits.py +11 -7
  54. ert/dark_storage/endpoints/compute/misfits.py +6 -4
  55. ert/dark_storage/endpoints/ensembles.py +4 -0
  56. ert/dark_storage/endpoints/experiment_server.py +30 -24
  57. ert/dark_storage/endpoints/experiments.py +2 -2
  58. ert/dark_storage/endpoints/observations.py +8 -6
  59. ert/dark_storage/endpoints/parameters.py +4 -12
  60. ert/dark_storage/endpoints/responses.py +24 -5
  61. ert/dark_storage/json_schema/ensemble.py +3 -0
  62. ert/dark_storage/json_schema/experiment.py +1 -1
  63. ert/data/_measured_data.py +6 -5
  64. ert/ensemble_evaluator/__init__.py +8 -1
  65. ert/ensemble_evaluator/config.py +2 -1
  66. ert/ensemble_evaluator/evaluator.py +81 -29
  67. ert/ensemble_evaluator/event.py +6 -0
  68. ert/ensemble_evaluator/snapshot.py +3 -1
  69. ert/ensemble_evaluator/state.py +1 -0
  70. ert/field_utils/__init__.py +8 -0
  71. ert/field_utils/field_utils.py +228 -15
  72. ert/field_utils/grdecl_io.py +1 -1
  73. ert/field_utils/roff_io.py +1 -1
  74. ert/gui/__init__.py +5 -2
  75. ert/gui/ertnotifier.py +1 -1
  76. ert/gui/ertwidgets/__init__.py +23 -16
  77. ert/gui/ertwidgets/analysismoduleedit.py +2 -2
  78. ert/gui/ertwidgets/checklist.py +1 -1
  79. ert/gui/ertwidgets/closabledialog.py +2 -0
  80. ert/gui/ertwidgets/copyablelabel.py +2 -0
  81. ert/gui/ertwidgets/create_experiment_dialog.py +3 -1
  82. ert/gui/ertwidgets/ensembleselector.py +2 -2
  83. ert/gui/ertwidgets/listeditbox.py +2 -0
  84. ert/gui/ertwidgets/models/__init__.py +2 -0
  85. ert/gui/ertwidgets/models/activerealizationsmodel.py +5 -1
  86. ert/gui/ertwidgets/models/path_model.py +1 -1
  87. ert/gui/ertwidgets/models/targetensemblemodel.py +5 -1
  88. ert/gui/ertwidgets/models/text_model.py +4 -1
  89. ert/gui/ertwidgets/pathchooser.py +0 -3
  90. ert/gui/ertwidgets/searchbox.py +17 -4
  91. ert/gui/ertwidgets/stringbox.py +2 -0
  92. ert/gui/{suggestor → ertwidgets/suggestor}/_suggestor_message.py +13 -4
  93. ert/gui/{suggestor → ertwidgets/suggestor}/suggestor.py +63 -30
  94. ert/gui/main.py +41 -13
  95. ert/gui/main_window.py +3 -7
  96. ert/gui/model/fm_step_list.py +3 -0
  97. ert/gui/model/real_list.py +1 -0
  98. ert/gui/model/snapshot.py +1 -0
  99. ert/gui/simulation/combobox_with_description.py +3 -0
  100. ert/gui/simulation/ensemble_experiment_panel.py +8 -2
  101. ert/gui/simulation/ensemble_information_filter_panel.py +7 -2
  102. ert/gui/simulation/ensemble_smoother_panel.py +8 -2
  103. ert/gui/simulation/evaluate_ensemble_panel.py +17 -7
  104. ert/gui/simulation/experiment_panel.py +18 -6
  105. ert/gui/simulation/manual_update_panel.py +35 -10
  106. ert/gui/simulation/multiple_data_assimilation_panel.py +13 -9
  107. ert/gui/simulation/run_dialog.py +47 -20
  108. ert/gui/simulation/single_test_run_panel.py +6 -3
  109. ert/gui/simulation/view/progress_widget.py +2 -0
  110. ert/gui/simulation/view/realization.py +5 -1
  111. ert/gui/simulation/view/update.py +2 -0
  112. ert/gui/summarypanel.py +20 -1
  113. ert/gui/tools/event_viewer/panel.py +3 -4
  114. ert/gui/tools/event_viewer/tool.py +2 -0
  115. ert/gui/tools/load_results/load_results_panel.py +1 -1
  116. ert/gui/tools/load_results/load_results_tool.py +2 -0
  117. ert/gui/tools/manage_experiments/export_dialog.py +136 -0
  118. ert/gui/tools/manage_experiments/manage_experiments_panel.py +2 -0
  119. ert/gui/tools/manage_experiments/storage_info_widget.py +121 -16
  120. ert/gui/tools/manage_experiments/storage_widget.py +4 -3
  121. ert/gui/tools/plot/customize/color_chooser.py +5 -2
  122. ert/gui/tools/plot/customize/customize_plot_dialog.py +2 -0
  123. ert/gui/tools/plot/customize/default_customization_view.py +4 -0
  124. ert/gui/tools/plot/customize/limits_customization_view.py +3 -0
  125. ert/gui/tools/plot/customize/statistics_customization_view.py +3 -0
  126. ert/gui/tools/plot/customize/style_chooser.py +2 -0
  127. ert/gui/tools/plot/customize/style_customization_view.py +3 -0
  128. ert/gui/tools/plot/data_type_keys_widget.py +2 -0
  129. ert/gui/tools/plot/data_type_proxy_model.py +3 -0
  130. ert/gui/tools/plot/plot_api.py +50 -28
  131. ert/gui/tools/plot/plot_ensemble_selection_widget.py +17 -10
  132. ert/gui/tools/plot/plot_widget.py +15 -2
  133. ert/gui/tools/plot/plot_window.py +41 -19
  134. ert/gui/tools/plot/plottery/plot_config.py +2 -0
  135. ert/gui/tools/plot/plottery/plot_context.py +14 -0
  136. ert/gui/tools/plot/plottery/plots/__init__.py +2 -0
  137. ert/gui/tools/plot/plottery/plots/cesp.py +3 -1
  138. ert/gui/tools/plot/plottery/plots/distribution.py +6 -1
  139. ert/gui/tools/plot/plottery/plots/ensemble.py +13 -5
  140. ert/gui/tools/plot/plottery/plots/gaussian_kde.py +12 -2
  141. ert/gui/tools/plot/plottery/plots/histogram.py +3 -1
  142. ert/gui/tools/plot/plottery/plots/misfits.py +436 -0
  143. ert/gui/tools/plot/plottery/plots/observations.py +18 -4
  144. ert/gui/tools/plot/plottery/plots/statistics.py +62 -20
  145. ert/gui/tools/plot/plottery/plots/std_dev.py +3 -1
  146. ert/gui/tools/plot/widgets/clearable_line_edit.py +9 -0
  147. ert/gui/tools/plot/widgets/filter_popup.py +2 -0
  148. ert/gui/tools/plot/widgets/filterable_kw_list_model.py +3 -0
  149. ert/gui/tools/plugins/plugin.py +1 -1
  150. ert/gui/tools/plugins/plugins_tool.py +2 -0
  151. ert/gui/tools/plugins/process_job_dialog.py +3 -0
  152. ert/gui/tools/workflows/workflow_dialog.py +2 -0
  153. ert/gui/tools/workflows/workflows_tool.py +2 -0
  154. ert/libres_facade.py +5 -7
  155. ert/logging/__init__.py +4 -1
  156. ert/mode_definitions.py +2 -0
  157. ert/plugins/__init__.py +4 -6
  158. ert/plugins/hook_implementations/workflows/csv_export.py +2 -3
  159. ert/plugins/hook_implementations/workflows/gen_data_rft_export.py +10 -2
  160. ert/plugins/hook_specifications/__init__.py +0 -10
  161. ert/plugins/hook_specifications/jobs.py +0 -9
  162. ert/plugins/plugin_manager.py +53 -124
  163. ert/resources/forward_models/run_reservoirsimulator.py +8 -4
  164. ert/resources/forward_models/template_render.py +10 -10
  165. ert/resources/shell_scripts/delete_directory.py +2 -2
  166. ert/run_models/__init__.py +24 -6
  167. ert/run_models/_create_run_path.py +133 -38
  168. ert/run_models/ensemble_experiment.py +10 -4
  169. ert/run_models/ensemble_information_filter.py +8 -1
  170. ert/run_models/ensemble_smoother.py +9 -3
  171. ert/run_models/evaluate_ensemble.py +8 -6
  172. ert/run_models/event.py +7 -3
  173. ert/run_models/everest_run_model.py +337 -113
  174. ert/run_models/initial_ensemble_run_model.py +25 -24
  175. ert/run_models/manual_update.py +6 -3
  176. ert/run_models/manual_update_enif.py +37 -0
  177. ert/run_models/model_factory.py +78 -18
  178. ert/run_models/multiple_data_assimilation.py +22 -11
  179. ert/run_models/run_model.py +72 -73
  180. ert/run_models/single_test_run.py +7 -4
  181. ert/run_models/update_run_model.py +4 -2
  182. ert/runpaths.py +5 -6
  183. ert/sample_prior.py +9 -4
  184. ert/scheduler/__init__.py +10 -5
  185. ert/scheduler/driver.py +40 -0
  186. ert/scheduler/event.py +3 -1
  187. ert/scheduler/job.py +23 -13
  188. ert/scheduler/lsf_driver.py +15 -5
  189. ert/scheduler/openpbs_driver.py +10 -4
  190. ert/scheduler/scheduler.py +5 -0
  191. ert/scheduler/slurm_driver.py +20 -5
  192. ert/services/__init__.py +2 -2
  193. ert/services/_base_service.py +37 -20
  194. ert/services/_storage_main.py +20 -18
  195. ert/services/ert_server.py +317 -0
  196. ert/shared/_doc_utils/__init__.py +4 -2
  197. ert/shared/_doc_utils/ert_jobs.py +1 -4
  198. ert/shared/net_utils.py +43 -18
  199. ert/shared/storage/connection.py +3 -3
  200. ert/shared/version.py +3 -3
  201. ert/storage/__init__.py +14 -1
  202. ert/storage/local_ensemble.py +44 -13
  203. ert/storage/local_experiment.py +54 -34
  204. ert/storage/local_storage.py +90 -58
  205. ert/storage/migration/to10.py +3 -2
  206. ert/storage/migration/to11.py +9 -10
  207. ert/storage/migration/to12.py +19 -20
  208. ert/storage/migration/to13.py +28 -27
  209. ert/storage/migration/to14.py +3 -3
  210. ert/storage/migration/to15.py +25 -0
  211. ert/storage/migration/to16.py +38 -0
  212. ert/storage/migration/to17.py +42 -0
  213. ert/storage/migration/to18.py +11 -0
  214. ert/storage/migration/to19.py +34 -0
  215. ert/storage/migration/to20.py +23 -0
  216. ert/storage/migration/to21.py +25 -0
  217. ert/storage/migration/to6.py +3 -2
  218. ert/storage/migration/to7.py +12 -13
  219. ert/storage/migration/to8.py +9 -11
  220. ert/storage/migration/to9.py +5 -4
  221. ert/storage/realization_storage_state.py +7 -7
  222. ert/substitutions.py +12 -28
  223. ert/validation/active_range.py +7 -7
  224. ert/validation/ensemble_realizations_argument.py +4 -2
  225. ert/validation/rangestring.py +16 -16
  226. ert/workflow_runner.py +6 -3
  227. {ert-16.0.9.dist-info → ert-19.0.0rc2.dist-info}/METADATA +21 -15
  228. ert-19.0.0rc2.dist-info/RECORD +524 -0
  229. {ert-16.0.9.dist-info → ert-19.0.0rc2.dist-info}/WHEEL +1 -1
  230. everest/api/everest_data_api.py +14 -1
  231. everest/assets/everest_logo.svg +406 -0
  232. everest/bin/config_branch_script.py +30 -14
  233. everest/bin/everconfigdump_script.py +2 -10
  234. everest/bin/everest_script.py +53 -33
  235. everest/bin/everlint_script.py +3 -5
  236. everest/bin/kill_script.py +7 -5
  237. everest/bin/main.py +11 -24
  238. everest/bin/monitor_script.py +64 -35
  239. everest/bin/utils.py +58 -43
  240. everest/bin/visualization_script.py +23 -13
  241. everest/config/__init__.py +4 -1
  242. everest/config/control_config.py +81 -6
  243. everest/config/control_variable_config.py +4 -3
  244. everest/config/everest_config.py +102 -79
  245. everest/config/forward_model_config.py +5 -3
  246. everest/config/install_data_config.py +7 -5
  247. everest/config/install_job_config.py +45 -3
  248. everest/config/install_template_config.py +3 -3
  249. everest/config/optimization_config.py +19 -6
  250. everest/config/output_constraint_config.py +8 -2
  251. everest/config/server_config.py +6 -55
  252. everest/config/simulator_config.py +62 -17
  253. everest/config/utils.py +25 -105
  254. everest/config/validation_utils.py +34 -15
  255. everest/config_file_loader.py +30 -21
  256. everest/detached/__init__.py +0 -6
  257. everest/detached/client.py +7 -52
  258. everest/detached/everserver.py +19 -45
  259. everest/everest_storage.py +24 -40
  260. everest/gui/everest_client.py +2 -3
  261. everest/gui/main_window.py +2 -2
  262. everest/optimizer/everest2ropt.py +68 -42
  263. everest/optimizer/opt_model_transforms.py +15 -20
  264. everest/optimizer/utils.py +0 -29
  265. everest/plugins/hook_specs.py +0 -24
  266. everest/strings.py +1 -6
  267. everest/util/__init__.py +3 -1
  268. ert/config/everest_objective_config.py +0 -95
  269. ert/config/ext_param_config.py +0 -107
  270. ert/gui/tools/export/__init__.py +0 -3
  271. ert/gui/tools/export/export_panel.py +0 -83
  272. ert/gui/tools/export/export_tool.py +0 -67
  273. ert/gui/tools/export/exporter.py +0 -36
  274. ert/plugins/hook_specifications/ecl_config.py +0 -29
  275. ert/services/storage_service.py +0 -127
  276. ert/summary_key_type.py +0 -234
  277. ert-16.0.9.dist-info/RECORD +0 -521
  278. everest/bin/everexport_script.py +0 -53
  279. everest/config/sampler_config.py +0 -103
  280. everest/simulator/__init__.py +0 -88
  281. everest/simulator/everest_to_ert.py +0 -252
  282. /ert/gui/{suggestor → ertwidgets/suggestor}/__init__.py +0 -0
  283. /ert/gui/{suggestor → ertwidgets/suggestor}/_colors.py +0 -0
  284. {ert-16.0.9.dist-info → ert-19.0.0rc2.dist-info}/entry_points.txt +0 -0
  285. {ert-16.0.9.dist-info → ert-19.0.0rc2.dist-info}/licenses/COPYING +0 -0
  286. {ert-16.0.9.dist-info → ert-19.0.0rc2.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,4 @@
1
- import json
2
1
  import os
3
- from pathlib import Path
4
2
  from textwrap import dedent
5
3
  from typing import Any
6
4
 
@@ -12,15 +10,9 @@ from ert.config.queue_config import (
12
10
  SlurmQueueOptions,
13
11
  TorqueQueueOptions,
14
12
  )
15
- from ert.dark_storage.client import ConnInfo
13
+ from ert.dark_storage.client import ErtClientConnectionInfo
16
14
 
17
- from ..strings import (
18
- CERTIFICATE_DIR,
19
- DETACHED_NODE_DIR,
20
- HOSTFILE_NAME,
21
- SERVER_STATUS,
22
- SESSION_DIR,
23
- )
15
+ from ..strings import SESSION_DIR
24
16
  from .simulator_config import check_removed_config
25
17
 
26
18
 
@@ -60,21 +52,11 @@ class ServerConfig(BaseModel):
60
52
  check_removed_config(data.get("queue_system"))
61
53
  return data
62
54
 
63
- @staticmethod
64
- def get_server_url(output_dir: str) -> str:
65
- """Return the url of the server.
66
-
67
- If server_info are given, the url is generated using that info. Otherwise
68
- server information are retrieved from the hostfile
69
- """
70
- server_info = ServerConfig.get_server_info(output_dir)
71
- return f"https://{server_info['host']}:{server_info['port']}/experiment_server"
72
-
73
55
  @staticmethod
74
56
  def get_server_context_from_conn_info(
75
- conn_info: ConnInfo,
57
+ conn_info: ErtClientConnectionInfo,
76
58
  ) -> tuple[str, str, tuple[str, str]]:
77
- """Get server connection context information from a storage_service ConnInfo.
59
+ """Get server connection context information from a ErtClientConnectionInfo.
78
60
 
79
61
  Returns a tuple containing the server URL, certificate file path,
80
62
  and authentication credentials.
@@ -84,7 +66,7 @@ class ServerConfig(BaseModel):
84
66
  This should be refactored to use the ERT Storage Client class directly instead.
85
67
 
86
68
  Args:
87
- conn_info: An instance of the storage_service client ConnInfo
69
+ conn_info: An instance of the ErtClientConnectionInfo
88
70
 
89
71
  Returns:
90
72
  tuple: A tuple containing:
@@ -103,39 +85,8 @@ class ServerConfig(BaseModel):
103
85
 
104
86
  return url, cert_file, auth
105
87
 
106
- @staticmethod
107
- def get_server_info(output_dir: str) -> dict[str, Any]:
108
- """Load server information from the hostfile"""
109
- host_file_path = Path(ServerConfig.get_hostfile_path(output_dir))
110
- if not host_file_path.exists():
111
- return {"host": None, "port": None, "cert": None, "auth": None}
112
-
113
- data = json.loads(host_file_path.read_text(encoding="utf-8"))
114
-
115
- if not all(k in data for k in ("host", "port", "cert", "auth")):
116
- raise RuntimeError("Malformed hostfile")
117
- return data
118
-
119
- @staticmethod
120
- def get_detached_node_dir(output_dir: str) -> str:
121
- return os.path.join(os.path.abspath(output_dir), DETACHED_NODE_DIR)
122
-
123
- @staticmethod
124
- def get_hostfile_path(output_dir: str) -> str:
125
- return os.path.join(ServerConfig.get_session_dir(output_dir), HOSTFILE_NAME)
126
-
127
88
  @staticmethod
128
89
  def get_session_dir(output_dir: str) -> str:
129
90
  """Return path to the session directory containing information about the
130
91
  certificates and host information"""
131
- return os.path.join(ServerConfig.get_detached_node_dir(output_dir), SESSION_DIR)
132
-
133
- @staticmethod
134
- def get_everserver_status_path(output_dir: str) -> str:
135
- """Returns path to the everest server status file"""
136
- return os.path.join(ServerConfig.get_session_dir(output_dir), SERVER_STATUS)
137
-
138
- @staticmethod
139
- def get_certificate_dir(output_dir: str) -> str:
140
- """Return the path to certificate folder"""
141
- return os.path.join(ServerConfig.get_session_dir(output_dir), CERTIFICATE_DIR)
92
+ return os.path.join(os.path.abspath(output_dir), SESSION_DIR)
@@ -75,9 +75,22 @@ class SimulatorConfig(BaseModelWithContextSupport, extra="forbid"):
75
75
  default=None,
76
76
  description=dedent(
77
77
  """
78
- Maximum allowed memory usage of a forward model.
78
+ Amount of memory to set aside for a forward model.
79
79
 
80
- When set, a job is only allowed to use `max_memory` of memory.
80
+ This information is propagated to the queue system as the amount of memory
81
+ to reserve/book for a realization to complete. It is up to the configuration
82
+ of the queuing system how to treat this information, but usually it will
83
+ stop more realizations being assigned to a compute node if the compute nodes
84
+ memory is already fully booked.
85
+
86
+ Setting this number lower than the peak memory consumption of each
87
+ realization puts the realization at risk of being killed in an out-of-memory
88
+ situation. Setting this number higher than needed will give longer wait
89
+ times in the queue.
90
+
91
+ For the local queue system, this keyword has no effect. In that scenario,
92
+ you can use `max_running` to choke the memory consumption.
93
+ scheduling of compute jobs.
81
94
 
82
95
  `max_memory` may be an integer value, indicating the number of
83
96
  bytes, or a string consisting of a number followed by a unit. The
@@ -94,9 +107,6 @@ class SimulatorConfig(BaseModelWithContextSupport, extra="forbid"):
94
107
  Spaces between the number and the unit are ignored, and so are any
95
108
  characters after the first. For example: 2g, 2G, and 2 GB all
96
109
  resolve to the same value: 2 gigabytes, equaling 2 * 1024**3 bytes.
97
-
98
- If not set, or a set to zero, the allowed amount of memory is
99
- unlimited.
100
110
  """
101
111
  ),
102
112
  )
@@ -132,17 +142,40 @@ class SimulatorConfig(BaseModelWithContextSupport, extra="forbid"):
132
142
  ),
133
143
  )
134
144
 
135
- @field_validator("queue_system", mode="before")
145
+ @model_validator(mode="before")
136
146
  @classmethod
137
- def default_local_queue(cls, v: Any, info: ValidationInfo) -> Any:
138
- if v is None:
147
+ def apply_site_or_default_queue_if_no_user_queue(
148
+ cls, data: dict[str, Any], info: ValidationInfo
149
+ ) -> Any:
150
+ queue_system = data.get("queue_system")
151
+ if queue_system is None:
139
152
  options = None
140
153
  if info.context:
141
154
  options = info.context.queue_options
142
- return options or LocalQueueOptions(max_running=8)
143
- return v
144
155
 
145
- @field_validator("max_memory")
156
+ defaulted_queue_options = (
157
+ options.model_dump()
158
+ if options is not None
159
+ else LocalQueueOptions(max_running=8).model_dump()
160
+ )
161
+
162
+ user_configured_max_memory = data.get("max_memory")
163
+ if user_configured_max_memory is not None:
164
+ cls.validate_max_memory(max_memory=user_configured_max_memory)
165
+ defaulted_queue_options["realization_memory"] = (
166
+ user_configured_max_memory
167
+ )
168
+
169
+ user_configured_cores_per_node = data.get("cores_per_node")
170
+ if user_configured_cores_per_node is not None:
171
+ defaulted_queue_options["num_cpu"] = user_configured_cores_per_node
172
+
173
+ data["queue_system"] = defaulted_queue_options
174
+ data["max_memory"] = None
175
+
176
+ return data
177
+
178
+ @field_validator("max_memory", mode="before")
146
179
  @classmethod
147
180
  def validate_max_memory(cls, max_memory: int | str | None) -> str | None:
148
181
  if max_memory is None:
@@ -163,14 +196,26 @@ class SimulatorConfig(BaseModelWithContextSupport, extra="forbid"):
163
196
 
164
197
  @model_validator(mode="after")
165
198
  def update_max_memory(config: "SimulatorConfig") -> "SimulatorConfig":
199
+ if config.max_memory is None:
200
+ return config
201
+ parsed_max_memory = (
202
+ parse_string_to_bytes(config.max_memory)
203
+ if type(config.max_memory) is str
204
+ else int(config.max_memory)
205
+ )
166
206
  if (
167
- config.max_memory is not None
168
- and config.queue_system is not None
207
+ config.queue_system is not None
169
208
  and config.queue_system.realization_memory == 0
170
209
  ):
171
- config.queue_system.realization_memory = (
172
- parse_string_to_bytes(config.max_memory)
173
- if type(config.max_memory) is str
174
- else int(config.max_memory)
210
+ config.queue_system.realization_memory = parsed_max_memory
211
+ elif (
212
+ config.queue_system is not None
213
+ and config.queue_system.realization_memory > 0
214
+ and config.queue_system.realization_memory != parsed_max_memory
215
+ ):
216
+ raise ConfigValidationError(
217
+ "Ambiguous configuration of realization_memory. "
218
+ "Specify either max_memory or realization_memory, not both"
175
219
  )
220
+
176
221
  return config
everest/config/utils.py CHANGED
@@ -1,108 +1,28 @@
1
- from copy import deepcopy
2
- from typing import Any, Literal
1
+ from ert.config import EverestControl, SamplerConfig
3
2
 
4
- from .control_config import ControlConfig
5
- from .control_variable_config import ControlVariableGuessListConfig
6
- from .sampler_config import SamplerConfig
7
3
 
8
-
9
- class FlattenedControls:
10
- def __init__(self, controls: list[ControlConfig]) -> None:
11
- control_dicts = _get_control_dicts(controls)
12
- self.names = [control["name"] for control in control_dicts]
13
- self.types: list[Literal["real", "integer"]] = [
14
- control["control_type"] for control in control_dicts
15
- ]
16
- self.initial_guesses = [control["initial_guess"] for control in control_dicts]
17
- self.lower_bounds = [control["min"] for control in control_dicts]
18
- self.upper_bounds = [control["max"] for control in control_dicts]
19
- self.scaled_ranges = [control["scaled_range"] for control in control_dicts]
20
- self.enabled = [control["enabled"] for control in control_dicts]
21
- self.perturbation_magnitudes = [
22
- control["perturbation_magnitude"] for control in control_dicts
23
- ]
24
- self.perturbation_types = [
25
- control["perturbation_type"] for control in control_dicts
26
- ]
27
- self.samplers, self.sampler_indices = _get_samplers(controls)
28
-
29
-
30
- def _get_control_dicts(controls: list[ControlConfig]) -> list[dict[str, Any]]:
31
- def _inject_defaults(control: ControlConfig, var_dict: dict[str, Any]) -> None:
32
- for key in [
33
- "type",
34
- "initial_guess",
35
- "control_type",
36
- "enabled",
37
- "min",
38
- "max",
39
- "perturbation_type",
40
- "perturbation_magnitude",
41
- "scaled_range",
42
- ]:
43
- if var_dict.get(key) is None:
44
- var_dict[key] = getattr(control, key)
45
-
46
- control_dicts: list[dict[str, Any]] = []
47
- for control in controls:
48
- for variable in control.variables:
49
- if isinstance(variable, ControlVariableGuessListConfig):
50
- for index, guess in enumerate(variable.initial_guess, start=1):
51
- var_dict = deepcopy(variable.model_dump())
52
- var_dict["name"] = (control.name, variable.name, index)
53
- var_dict["initial_guess"] = guess
54
- _inject_defaults(control, var_dict)
55
- control_dicts.append(var_dict)
56
- else:
57
- var_dict = deepcopy(variable.model_dump())
58
- var_dict["name"] = (
59
- (control.name, variable.name)
60
- if variable.index is None
61
- else (control.name, variable.name, variable.index)
62
- )
63
- _inject_defaults(control, var_dict)
64
- control_dicts.append(var_dict)
65
- return control_dicts
66
-
67
-
68
- def _get_samplers(
69
- controls: list[ControlConfig],
4
+ def get_samplers(
5
+ controls: list[EverestControl],
70
6
  ) -> tuple[list[SamplerConfig | None], list[int]]:
71
- samplers: list[SamplerConfig | None] = []
72
- sampler_indices: list[int] = []
73
-
74
- default_sampler_index: int | None = None
75
-
76
- for control in controls:
77
- control_sampler_index: int | None = None
78
-
79
- for variable in control.variables:
80
- if variable.sampler is not None:
81
- # Use the sampler of the variable:
82
- samplers.append(variable.sampler)
83
- variable_sampler_index = len(samplers) - 1
84
- elif control.sampler is not None:
85
- # Use the sampler of the control:
86
- if control_sampler_index is None:
87
- samplers.append(control.sampler)
88
- control_sampler_index = len(samplers) - 1
89
- variable_sampler_index = control_sampler_index
90
- else:
91
- # Use the default sampler:
92
- if default_sampler_index is None:
93
- samplers.append(None)
94
- default_sampler_index = len(samplers) - 1
95
- variable_sampler_index = default_sampler_index
96
-
97
- if isinstance(variable, ControlVariableGuessListConfig):
98
- sampler_indices.extend(
99
- [variable_sampler_index] * len(variable.initial_guess)
100
- )
101
- else:
102
- sampler_indices.append(variable_sampler_index)
103
-
104
- return samplers, sampler_indices
105
-
106
-
107
- def flatten_controls(controls: list[ControlConfig]) -> FlattenedControls:
108
- return FlattenedControls(controls)
7
+ """
8
+ Create a list of unique samplers, and a list mapping variable index
9
+ to sampler index. I.e., this points each control variable to
10
+ a sampler by index.
11
+ """
12
+ flattened_samplers = [
13
+ sampler for control in controls for sampler in control.samplers
14
+ ]
15
+ unique_samplers: list[SamplerConfig | None] = []
16
+ variable_to_unique_sampler_index: list[int] = []
17
+ for sampler in flattened_samplers:
18
+ try:
19
+ unique_sampler_index = next(
20
+ i for i, s in enumerate(unique_samplers) if s == sampler
21
+ )
22
+ except StopIteration:
23
+ unique_sampler_index = len(unique_samplers)
24
+ unique_samplers.append(sampler)
25
+
26
+ variable_to_unique_sampler_index.append(unique_sampler_index)
27
+
28
+ return unique_samplers, variable_to_unique_sampler_index
@@ -1,6 +1,7 @@
1
1
  import errno
2
2
  import os
3
3
  import tempfile
4
+ import types
4
5
  from collections import Counter
5
6
  from collections.abc import Sequence
6
7
  from pathlib import Path
@@ -15,7 +16,7 @@ from everest.util.forward_models import (
15
16
  parse_forward_model_file,
16
17
  )
17
18
 
18
- from .install_job_config import InstallJobConfig
19
+ from .install_job_config import InstallForwardModelStepConfig
19
20
 
20
21
  _VARIABLE_ERROR_MESSAGE = (
21
22
  "Variable {name} must define {variable_type} value either"
@@ -39,6 +40,8 @@ _RESERVED_WORDS = [
39
40
  "total_objective_value",
40
41
  ]
41
42
 
43
+ _OVERWRITE_MESSAGE = "Are you overwriting other parts of install_data?"
44
+
42
45
 
43
46
  class InstallDataContext:
44
47
  def __init__(
@@ -70,28 +73,44 @@ class InstallDataContext:
70
73
  source = item.source
71
74
  target = item.target
72
75
 
73
- if "<GEO_ID>" not in source:
76
+ if "<REALIZATION_ID>" not in source:
74
77
  self._set_symlink(source, target, None)
75
78
 
76
79
  return self
77
80
 
78
81
  def _set_symlink(self, source: str, target: str, realization: int | None) -> None:
79
82
  if realization is not None:
80
- source = source.replace("<GEO_ID>", str(realization))
81
- target = target.replace("<GEO_ID>", str(realization))
83
+ source = source.replace("<REALIZATION_ID>", str(realization))
84
+ target = target.replace("<REALIZATION_ID>", str(realization))
82
85
 
83
86
  tmp_target = Path(self._temp_dir.name) / Path(target)
84
87
  if tmp_target.exists():
88
+ if tmp_target.is_dir() and not tmp_target.is_symlink():
89
+ raise ValueError(
90
+ "Cannot make symlink due to existing directory at target location"
91
+ f" {tmp_target}. " + _OVERWRITE_MESSAGE
92
+ )
85
93
  tmp_target.unlink()
86
- tmp_target.parent.mkdir(parents=True, exist_ok=True)
87
- tmp_target.symlink_to(as_abs_path(source, self._config_dir))
94
+ try:
95
+ tmp_target.parent.mkdir(parents=True, exist_ok=True)
96
+ tmp_target.symlink_to(as_abs_path(source, self._config_dir))
97
+ except FileExistsError as err:
98
+ raise ValueError(
99
+ f"Cannot install data {source} into {target} due to existing "
100
+ "file or directory. " + _OVERWRITE_MESSAGE
101
+ ) from err
88
102
 
89
103
  def add_links_for_realization(self, realization: int) -> None:
90
104
  for data in self._install_data:
91
- if data.source is not None and "<GEO_ID>" in data.source:
105
+ if data.source is not None and "<REALIZATION_ID>" in data.source:
92
106
  self._set_symlink(data.source, data.target, realization)
93
107
 
94
- def __exit__(self, exc_type: Any, exc_value: Any, exc_tb: Any) -> None:
108
+ def __exit__(
109
+ self,
110
+ exc_type: type[BaseException] | None,
111
+ exc_value: BaseException | None,
112
+ exc_tb: types.TracebackType | None,
113
+ ) -> None:
95
114
  if self._temp_dir:
96
115
  self._temp_dir.cleanup()
97
116
  os.chdir(self._cwd)
@@ -177,7 +196,7 @@ def unique_items(items: Sequence[T]) -> Sequence[T]:
177
196
 
178
197
 
179
198
  def valid_range(range_value: tuple[float, float]) -> tuple[float, float]:
180
- if range_value[0] >= range_value[1]:
199
+ if range_value is not None and range_value[0] >= range_value[1]:
181
200
  raise ValueError("scaled_range must be a valid range [a, b], where a < b.")
182
201
  return range_value
183
202
 
@@ -229,8 +248,8 @@ def as_abs_path(path: str, config_dir: str) -> str:
229
248
 
230
249
 
231
250
  def expand_model_id_paths(path_source: str, realizations: list[int]) -> list[str]:
232
- if "<GEO_ID>" in path_source:
233
- return [path_source.replace("<GEO_ID>", str(r)) for r in realizations]
251
+ if "<REALIZATION_ID>" in path_source:
252
+ return [path_source.replace("<REALIZATION_ID>", str(r)) for r in realizations]
234
253
  return [path_source]
235
254
 
236
255
 
@@ -238,7 +257,8 @@ def check_path_exists(
238
257
  path_source: str, config_path: Path | None, realizations: list[int]
239
258
  ) -> None:
240
259
  """Check if the given path exists. If the given path contains <CONFIG_PATH>
241
- or GEO_ID they will be expanded and all instances of expanded paths need to exist.
260
+ or REALIZATION_ID they will be expanded and all instances of expanded paths
261
+ need to exist.
242
262
  """
243
263
  if not isinstance(path_source, str):
244
264
  raise ValueError(
@@ -272,8 +292,7 @@ def check_writeable_path(path_source: str, config_path: Path) -> None:
272
292
  if os.access(path, os.W_OK | os.X_OK):
273
293
  break
274
294
  elif os.path.isfile(path):
275
- # path is a file, cannot create folder
276
- raise ValueError(f"File {path} exists, cannot create path {path_source}")
295
+ raise ValueError(f"{path} is a file, cannot create folders inside it")
277
296
  parent = os.path.dirname(path)
278
297
  if parent == path: # ie, if path is root
279
298
  break
@@ -284,7 +303,7 @@ def check_writeable_path(path_source: str, config_path: Path) -> None:
284
303
 
285
304
 
286
305
  def validate_forward_model_configs(
287
- forward_model: list[str], install_jobs: list[InstallJobConfig]
306
+ forward_model: list[str], install_jobs: list[InstallForwardModelStepConfig]
288
307
  ) -> None:
289
308
  if not forward_model:
290
309
  return
@@ -1,9 +1,8 @@
1
- #!/usr/bin/env python
2
-
3
1
  import logging
4
2
  import os
5
3
  import re
6
4
  from io import StringIO
5
+ from pathlib import Path
7
6
  from typing import Any
8
7
 
9
8
  import jinja2
@@ -25,29 +24,28 @@ SUBSTITUTION_PATTERN = r"(r\{\{.*?\}\})"
25
24
 
26
25
  # Jinja vars which should NOT be included in definitions portion of config.
27
26
  ERT_CONFIG_TEMPLATES = {
28
- "realization": "GEO_ID",
27
+ "realization": "REALIZATION_ID",
29
28
  "runpath_file": "RUNPATH_FILE",
30
29
  }
31
30
 
32
31
 
33
32
  def load_yaml(file_name: str, safe: bool = False) -> dict[str, Any]:
34
- with open(file_name, encoding="utf-8") as input_file:
35
- input_data: list[str] = input_file.readlines()
36
- try:
37
- yaml = YAML(typ="safe", pure=True) if safe else YAML()
38
- yaml.preserve_quotes = True
39
- return yaml.load("".join(input_data))
40
- except YAMLError as exc:
41
- if hasattr(exc, "problem_mark"):
42
- mark = exc.problem_mark
43
- raise YAMLError(
44
- str(exc)
45
- + "\nError in line: {}\n {}^)".format(
46
- input_data[mark.line], " " * mark.column
47
- )
48
- ) from exc
49
- else:
50
- raise YAMLError(str(exc)) from exc
33
+ input_data = Path(file_name).read_text(encoding="utf-8").splitlines()
34
+ try:
35
+ yaml = YAML(typ="safe", pure=True) if safe else YAML()
36
+ yaml.preserve_quotes = True
37
+ return yaml.load("\n".join(input_data))
38
+ except YAMLError as exc:
39
+ if hasattr(exc, "problem_mark"):
40
+ mark = exc.problem_mark
41
+ raise YAMLError(
42
+ str(exc)
43
+ + "\nError in line: {}\n {}^)".format(
44
+ input_data[mark.line], " " * mark.column
45
+ )
46
+ ) from exc
47
+ else:
48
+ raise YAMLError(str(exc)) from exc
51
49
 
52
50
 
53
51
  def _get_definitions(
@@ -67,7 +65,7 @@ def _get_definitions(
67
65
  f"Internal key {key} specified by user as {defs[key]}. "
68
66
  f"Overriding as {val}"
69
67
  )
70
- defs[key] = f"<{val}>" # ert uses <GEO_ID> as format
68
+ defs[key] = f"<{val}>" # ert uses <REALIZATION_ID> as format
71
69
  else:
72
70
  logging.getLogger(EVEREST).warning("Empty configuration file provided!")
73
71
 
@@ -160,6 +158,17 @@ def yaml_file_to_substituted_config_dict(config_path: str) -> dict[str, Any]:
160
158
  # Replace in definitions
161
159
  config = jenv.from_string(txt).render(**definitions)
162
160
 
161
+ # Handle deprecated use of <GEO_ID>:
162
+ config, count = re.subn(
163
+ rf"<\s*{re.escape('GEO_ID')}\s*>", "<REALIZATION_ID>", config
164
+ )
165
+ if count > 0:
166
+ message = r"<GEO_ID> is deprecated, please replace with 'r{{realization}}'."
167
+ print(message)
168
+ logging.getLogger(EVEREST).warning(
169
+ "Deprecated key <GEO_ID> replaced by <REALIZATION_ID>."
170
+ )
171
+
163
172
  # Load the config with definitions again as yaml
164
173
  yaml = YAML(typ="safe", pure=True).load(config)
165
174
 
@@ -4,28 +4,22 @@ Client methods for interacting with everserver
4
4
 
5
5
  from .client import (
6
6
  PROXY,
7
- ExperimentState,
8
- everserver_status,
9
7
  server_is_running,
10
8
  start_experiment,
11
9
  start_monitor,
12
10
  start_server,
13
11
  stop_server,
14
- update_everserver_status,
15
12
  wait_for_server,
16
13
  wait_for_server_to_stop,
17
14
  )
18
15
 
19
16
  __all__ = [
20
17
  "PROXY",
21
- "ExperimentState",
22
- "everserver_status",
23
18
  "server_is_running",
24
19
  "start_experiment",
25
20
  "start_monitor",
26
21
  "start_server",
27
22
  "stop_server",
28
- "update_everserver_status",
29
23
  "wait_for_server",
30
24
  "wait_for_server_to_stop",
31
25
  ]