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
@@ -5,25 +5,21 @@ import logging
5
5
  import warnings
6
6
  from argparse import ArgumentParser
7
7
  from collections.abc import Callable, Mapping, Sequence
8
- from pathlib import Path
9
- from types import TracebackType
10
8
  from typing import TYPE_CHECKING, Any, Literal, TypeVar, overload
11
9
 
12
10
  import pluggy
13
11
  from pydantic import BaseModel, Field
14
12
  from typing_extensions import TypedDict
15
13
 
16
- from ert.base_model_context import init_context_var
17
14
  from ert.config import (
18
- ForwardModelStep,
19
15
  ForwardModelStepDocumentation,
20
16
  ForwardModelStepPlugin,
21
17
  KnownQueueOptions,
22
18
  LegacyWorkflowConfigs,
23
19
  LocalQueueOptions,
20
+ SiteInstalledForwardModelStep,
24
21
  WorkflowConfigs,
25
22
  WorkflowJob,
26
- forward_model_step_from_config_contents,
27
23
  workflow_job_from_file,
28
24
  )
29
25
  from ert.trace import add_span_processor
@@ -129,21 +125,6 @@ class ErtPluginManager(pluggy.PluginManager):
129
125
 
130
126
  return response.data
131
127
 
132
- def get_ecl100_config_path(self) -> str | None:
133
- return ErtPluginManager._evaluate_config_hook(
134
- hook=self.hook.ecl100_config_path, config_name="ecl100"
135
- )
136
-
137
- def get_ecl300_config_path(self) -> str | None:
138
- return ErtPluginManager._evaluate_config_hook(
139
- hook=self.hook.ecl300_config_path, config_name="ecl300"
140
- )
141
-
142
- def get_flow_config_path(self) -> str | None:
143
- return ErtPluginManager._evaluate_config_hook(
144
- hook=self.hook.flow_config_path, config_name="flow"
145
- )
146
-
147
128
  def get_forward_model_configuration(self) -> dict[str, dict[str, Any]]:
148
129
  response: list[PluginResponse[dict[str, str]]] = (
149
130
  self.hook.forward_model_configuration()
@@ -268,32 +249,9 @@ class ErtPluginManager(pluggy.PluginManager):
268
249
  return merged_dict
269
250
  return {k: v[0] for k, v in merged_dict.items()}
270
251
 
271
- def get_installable_jobs(self) -> Mapping[str, str]:
272
- return ErtPluginManager._merge_dicts(self.hook.installable_jobs())
273
-
274
252
  def _get_config_workflow_jobs(self) -> dict[str, str]:
275
253
  return ErtPluginManager._merge_dicts(self.hook.installable_workflow_jobs())
276
254
 
277
- def get_documentation_for_jobs(self) -> dict[str, Any]:
278
- job_docs = {
279
- k: {
280
- "config_file": v[0],
281
- "source_package": v[1].plugin_name,
282
- "source_function_name": v[1].function_name,
283
- }
284
- for k, v in ErtPluginManager._merge_dicts(
285
- self.hook.installable_jobs(), include_plugin_data=True
286
- ).items()
287
- }
288
- for key, value in job_docs.items():
289
- value.update(
290
- ErtPluginManager._evaluate_job_doc_hook(
291
- self.hook.job_documentation,
292
- key,
293
- )
294
- )
295
- return job_docs
296
-
297
255
  def get_documentation_for_forward_model_steps(
298
256
  self,
299
257
  ) -> dict[str, ForwardModelStepDocumentation]:
@@ -370,7 +328,7 @@ class ErtPluginManager(pluggy.PluginManager):
370
328
 
371
329
 
372
330
  class ErtRuntimePlugins(BaseModel):
373
- installed_forward_model_steps: Mapping[str, ForwardModelStep] = Field(
331
+ installed_forward_model_steps: Mapping[str, SiteInstalledForwardModelStep] = Field(
374
332
  default_factory=dict
375
333
  )
376
334
  installed_workflow_jobs: Mapping[str, WorkflowJob] = Field(default_factory=dict)
@@ -380,95 +338,66 @@ class ErtRuntimePlugins(BaseModel):
380
338
  environment_variables: Mapping[str, str] = Field(default_factory=dict)
381
339
  env_pr_fm_step: Mapping[str, Mapping[str, Any]] = Field(default_factory=dict)
382
340
  help_links: dict[str, str] = Field(default_factory=dict)
341
+ prioritize_private_ip_address: bool = False
383
342
 
384
343
 
385
- class ErtPluginContext:
386
- def __init__(
387
- self,
388
- plugins: list[object] | None = None,
389
- logger: logging.Logger | None = None,
390
- ) -> None:
391
- self.plugin_manager = ErtPluginManager(plugins=plugins)
392
- self._logger = logger
393
-
394
- def __enter__(self) -> ErtRuntimePlugins:
395
- if self._logger is not None:
396
- self.plugin_manager.add_logging_handle_to_root(logger=self._logger)
397
- self.plugin_manager.add_span_processor_to_trace_provider()
398
- logger.debug(str(self.plugin_manager))
399
-
400
- site_configurations = self.plugin_manager.get_site_configurations()
401
-
402
- ecl100_config_path = self.plugin_manager.get_ecl100_config_path()
403
- ecl300_config_path = self.plugin_manager.get_ecl300_config_path()
404
- flow_config_path = self.plugin_manager.get_flow_config_path()
405
-
406
- config_env_vars = {}
407
- if ecl100_config_path is not None:
408
- config_env_vars["ECL100_SITE_CONFIG"] = ecl100_config_path
344
+ def get_site_plugins(
345
+ plugin_manager: ErtPluginManager | None = None,
346
+ ) -> ErtRuntimePlugins:
347
+ if plugin_manager is None:
348
+ plugin_manager = ErtPluginManager()
409
349
 
410
- if ecl300_config_path is not None:
411
- config_env_vars["ECL300_SITE_CONFIG"] = ecl300_config_path
350
+ site_configurations = plugin_manager.get_site_configurations()
351
+ installable_workflow_jobs = plugin_manager.get_installable_workflow_jobs()
412
352
 
413
- if flow_config_path is not None:
414
- config_env_vars["FLOW_SITE_CONFIG"] = flow_config_path
353
+ all_forward_model_steps = (
354
+ dict(site_configurations.installed_forward_model_steps)
355
+ if site_configurations
356
+ else {}
357
+ )
415
358
 
416
- installable_workflow_jobs = self.plugin_manager.get_installable_workflow_jobs()
359
+ all_workflow_jobs: dict[str, WorkflowJob] = dict[str, WorkflowJob](
360
+ plugin_manager.get_ertscript_workflows().get_workflows()
361
+ ) | dict[str, WorkflowJob](
362
+ plugin_manager.get_legacy_ertscript_workflows().get_workflows()
363
+ )
417
364
 
418
- all_forward_model_steps = (
419
- dict(site_configurations.installed_forward_model_steps)
365
+ for _, job_path in installable_workflow_jobs.items():
366
+ wf_job = workflow_job_from_file(job_path, origin="site")
367
+ all_workflow_jobs[wf_job.name] = wf_job
368
+
369
+ for fm_step_subclass in plugin_manager.forward_model_steps:
370
+ # we call without required arguments to
371
+ # ForwardModelStepPlugin.__init__ as
372
+ # we expect the subclass to override __init__
373
+ # and provide those arguments
374
+ fm_step = fm_step_subclass() # type: ignore
375
+ all_forward_model_steps[fm_step.name] = fm_step
376
+
377
+ runtime_plugins = ErtRuntimePlugins(
378
+ installed_forward_model_steps=all_forward_model_steps,
379
+ installed_workflow_jobs=all_workflow_jobs,
380
+ queue_options=site_configurations.queue_options
381
+ if site_configurations
382
+ else None,
383
+ environment_variables=(
384
+ dict(site_configurations.environment_variables)
420
385
  if site_configurations
421
386
  else {}
422
- )
387
+ ),
388
+ env_pr_fm_step=plugin_manager.get_forward_model_configuration(),
389
+ help_links=plugin_manager.get_help_links(),
390
+ prioritize_private_ip_address=site_configurations.prioritize_private_ip_address
391
+ if site_configurations
392
+ else False,
393
+ )
423
394
 
424
- for job_name, job_path in self.plugin_manager.get_installable_jobs().items():
425
- fm_step = forward_model_step_from_config_contents(
426
- Path(job_path).read_text(encoding="utf-8"), job_path, job_name
427
- )
428
- all_forward_model_steps[job_name] = fm_step
395
+ return runtime_plugins
429
396
 
430
- all_workflow_jobs: dict[str, WorkflowJob] = dict[str, WorkflowJob](
431
- self.plugin_manager.get_ertscript_workflows().get_workflows()
432
- ) | dict[str, WorkflowJob](
433
- self.plugin_manager.get_legacy_ertscript_workflows().get_workflows()
434
- )
435
397
 
436
- for _, job_path in installable_workflow_jobs.items():
437
- wf_job = workflow_job_from_file(job_path)
438
- all_workflow_jobs[wf_job.name] = wf_job
439
-
440
- for fm_step_subclass in self.plugin_manager.forward_model_steps:
441
- # we call without required arguments to
442
- # ForwardModelStepPlugin.__init__ as
443
- # we expect the subclass to override __init__
444
- # and provide those arguments
445
- fm_step = fm_step_subclass() # type: ignore
446
- all_forward_model_steps[fm_step.name] = fm_step
447
-
448
- runtime_plugins = ErtRuntimePlugins(
449
- installed_forward_model_steps=all_forward_model_steps,
450
- installed_workflow_jobs=all_workflow_jobs,
451
- queue_options=site_configurations.queue_options
452
- if site_configurations
453
- else None,
454
- environment_variables=config_env_vars
455
- | (
456
- dict(site_configurations.environment_variables)
457
- if site_configurations
458
- else {}
459
- ),
460
- env_pr_fm_step=self.plugin_manager.get_forward_model_configuration(),
461
- help_links=self.plugin_manager.get_help_links(),
462
- )
463
-
464
- self._context_token = init_context_var.set(runtime_plugins) # type: ignore
465
- return runtime_plugins
398
+ def setup_site_logging(root_logger: logging.Logger | None = None) -> None:
399
+ pm = ErtPluginManager()
400
+ if root_logger is not None:
401
+ pm.add_logging_handle_to_root(logger=root_logger)
466
402
 
467
- def __exit__(
468
- self,
469
- exception: BaseException,
470
- exception_type: type[BaseException],
471
- traceback: TracebackType,
472
- ) -> None:
473
- logger.debug("Exiting plugin context")
474
- init_context_var.reset(self._context_token)
403
+ pm.add_span_processor_to_trace_provider()
@@ -2,17 +2,15 @@
2
2
  import datetime
3
3
  import glob
4
4
  import os
5
- import os.path
6
5
  import re
7
6
  import shutil
8
7
  import subprocess
9
8
  import sys
10
9
  import time
11
10
  from argparse import ArgumentParser
12
- from collections import namedtuple
13
11
  from pathlib import Path
14
12
  from random import random
15
- from typing import Literal, get_args
13
+ from typing import Literal, NamedTuple, get_args
16
14
 
17
15
  import resfo
18
16
 
@@ -44,7 +42,13 @@ class EclError(RuntimeError):
44
42
 
45
43
 
46
44
  Simulators = Literal["flow", "eclipse", "e300"]
47
- EclipseResult = namedtuple("EclipseResult", "errors bugs")
45
+
46
+
47
+ class EclipseResult(NamedTuple):
48
+ errors: int
49
+ bugs: int
50
+
51
+
48
52
  body_sub_pattern = r"(\s^\s@.+$)*"
49
53
  date_sub_pattern = r"\s+AT TIME\s+(?P<Days>\d+\.\d+)\s+DAYS\s+\((?P<Date>(.+)):\s*$"
50
54
  error_pattern_e100 = (
@@ -19,16 +19,16 @@ def load_data(filename: str) -> dict[str, Any]:
19
19
  """
20
20
  yaml_err = ""
21
21
  json_err = ""
22
- with open(filename, encoding="utf-8") as fin:
23
- try:
24
- return yaml.safe_load(fin)
25
- except yaml.YAMLError as err:
26
- yaml_err = str(err)
27
-
28
- try:
29
- return json.load(fin)
30
- except yaml.YAMLError as err:
31
- json_err = str(err)
22
+ raw_data = Path(filename).read_text(encoding="utf-8")
23
+ try:
24
+ return yaml.safe_load(raw_data)
25
+ except yaml.YAMLError as err:
26
+ yaml_err = str(err)
27
+
28
+ try:
29
+ return json.loads(raw_data)
30
+ except yaml.YAMLError as err:
31
+ json_err = str(err)
32
32
 
33
33
  raise OSError(
34
34
  f"{filename} is neither yaml (err_msg={yaml_err}) nor json (err_msg={json_err})"
@@ -48,8 +48,8 @@ def delete_directory(path: str) -> None:
48
48
  for file in files:
49
49
  delete_file(os.path.join(root, file))
50
50
 
51
- for _dir in dirs:
52
- delete_empty_directory(os.path.join(root, _dir))
51
+ for dir_ in dirs:
52
+ delete_empty_directory(os.path.join(root, dir_))
53
53
 
54
54
  else:
55
55
  raise OSError(f"Entry:'{path}' is not a directory")
@@ -1,6 +1,10 @@
1
- from .ensemble_experiment import EnsembleExperiment
2
- from .ensemble_information_filter import EnsembleInformationFilter
3
- from .ensemble_smoother import EnsembleSmoother
1
+ from .ensemble_experiment import EnsembleExperiment, EnsembleExperimentConfig
2
+ from .ensemble_information_filter import (
3
+ EnsembleInformationFilter,
4
+ EnsembleInformationFilterConfig,
5
+ )
6
+ from .ensemble_smoother import EnsembleSmoother, EnsembleSmootherConfig
7
+ from .evaluate_ensemble import EvaluateEnsembleConfig
4
8
  from .event import (
5
9
  RunModelEvent,
6
10
  RunModelStatusEvent,
@@ -9,16 +13,29 @@ from .event import (
9
13
  RunModelUpdateEndEvent,
10
14
  )
11
15
  from .model_factory import create_model
12
- from .multiple_data_assimilation import MultipleDataAssimilation
13
- from .run_model import ErtRunError, RunModel, RunModelAPI, StatusEvents
14
- from .single_test_run import SingleTestRun
16
+ from .multiple_data_assimilation import (
17
+ MultipleDataAssimilation,
18
+ MultipleDataAssimilationConfig,
19
+ )
20
+ from .run_model import (
21
+ ErtRunError,
22
+ RunModel,
23
+ RunModelAPI,
24
+ StatusEvents,
25
+ )
26
+ from .single_test_run import SingleTestRun, SingleTestRunConfig
15
27
 
16
28
  __all__ = [
17
29
  "EnsembleExperiment",
30
+ "EnsembleExperimentConfig",
18
31
  "EnsembleInformationFilter",
32
+ "EnsembleInformationFilterConfig",
19
33
  "EnsembleSmoother",
34
+ "EnsembleSmootherConfig",
20
35
  "ErtRunError",
36
+ "EvaluateEnsembleConfig",
21
37
  "MultipleDataAssimilation",
38
+ "MultipleDataAssimilationConfig",
22
39
  "RunModel",
23
40
  "RunModelAPI",
24
41
  "RunModelEvent",
@@ -27,6 +44,7 @@ __all__ = [
27
44
  "RunModelUpdateBeginEvent",
28
45
  "RunModelUpdateEndEvent",
29
46
  "SingleTestRun",
47
+ "SingleTestRunConfig",
30
48
  "StatusEvents",
31
49
  "create_model",
32
50
  ]
@@ -2,22 +2,30 @@ from __future__ import annotations
2
2
 
3
3
  import json
4
4
  import logging
5
+ import math
5
6
  import os
7
+ import time
8
+ from collections import defaultdict
6
9
  from collections.abc import Iterable, Mapping
10
+ from copy import deepcopy
7
11
  from datetime import UTC, datetime
8
12
  from pathlib import Path
9
13
  from typing import TYPE_CHECKING, Any
10
14
 
11
15
  import orjson
12
16
 
17
+ from _ert.utils import file_safe_timestamp
13
18
  from ert.config import (
14
- ExtParamConfig,
19
+ EverestControl,
15
20
  Field,
16
21
  ForwardModelStep,
17
22
  GenKwConfig,
23
+ ParameterCardinality,
18
24
  ParameterConfig,
19
25
  SurfaceConfig,
20
26
  )
27
+ from ert.config.design_matrix import DESIGN_MATRIX_GROUP
28
+ from ert.config.distribution import LogNormalSettings, LogUnifSettings
21
29
  from ert.config.ert_config import create_forward_model_json
22
30
  from ert.substitutions import Substitutions, substitute_runpath_name
23
31
  from ert.utils import log_duration
@@ -33,7 +41,7 @@ logger = logging.getLogger(__name__)
33
41
  def _backup_if_existing(path: Path) -> None:
34
42
  if not path.exists():
35
43
  return
36
- timestamp = datetime.now(UTC).isoformat(timespec="seconds")
44
+ timestamp = file_safe_timestamp(datetime.now(UTC).isoformat(timespec="seconds"))
37
45
  new_path = path.parent / f"{path.name}_backup_{timestamp}"
38
46
  path.rename(new_path)
39
47
 
@@ -52,10 +60,17 @@ def _value_export_txt(
52
60
  with path.open("w") as f:
53
61
  for key, param_map in values.items():
54
62
  for param, value in param_map.items():
55
- if isinstance(value, (int | float)):
56
- print(f"{key}:{param} {value:g}", file=f)
63
+ if isinstance(value, int):
64
+ value_str = str(value)
65
+ elif isinstance(value, float):
66
+ value_str = f"{value:g}"
57
67
  else:
58
- print(f"{key}:{param} {value}", file=f)
68
+ value_str = str(value)
69
+
70
+ if key == DESIGN_MATRIX_GROUP:
71
+ print(f"{param} {value_str}", file=f)
72
+ else:
73
+ print(f"{key}:{param} {value_str}", file=f)
59
74
 
60
75
 
61
76
  def _value_export_json(
@@ -69,18 +84,18 @@ def _value_export_json(
69
84
  if len(values) == 0:
70
85
  return
71
86
 
72
- # Hierarchical
73
- json_out: dict[str, float | dict[str, float | str]] = {
74
- key: dict(param_map.items()) for key, param_map in values.items()
75
- }
87
+ # parameter file is {param: {"value": value}}
88
+ json_out: dict[str, dict[str, float | str]] = {}
89
+ for param_map in values.values():
90
+ for param, value in param_map.items():
91
+ json_out[param] = {"value": value}
76
92
 
77
93
  # Disallow NaN from being written: ERT produces the parameters and the only
78
94
  # way for the output to be NaN is if the input is invalid or if the sampling
79
95
  # function is buggy. Either way, that would be a bug and we can report it by
80
96
  # having json throw an error.
81
- json.dump(
82
- json_out, path.open("w"), allow_nan=False, indent=0, separators=(", ", " : ")
83
- )
97
+ with path.open("w") as f:
98
+ json.dump(json_out, f, allow_nan=False, indent=0, separators=(", ", " : "))
84
99
 
85
100
 
86
101
  def _generate_parameter_files(
@@ -90,7 +105,7 @@ def _generate_parameter_files(
90
105
  iens: int,
91
106
  fs: Ensemble,
92
107
  iteration: int,
93
- ) -> Mapping[str, Mapping[str, float | str]]:
108
+ ) -> tuple[Mapping[str, Mapping[str, float | str]], Mapping[str, float]]:
94
109
  """
95
110
  Generate parameter files that are placed in each runtime directory for
96
111
  forward-model jobs to consume.
@@ -105,10 +120,25 @@ def _generate_parameter_files(
105
120
  fs: Ensemble from which to load parameter data
106
121
 
107
122
  Returns:
108
- Returns the union of parameters returned by write_to_runpath for each
109
- parameter_config.
123
+ Returns a tuple containing: the union of parameters returned by
124
+ write_to_runpath for each parameter_config, and a dict with
125
+ timings/durations for each parameter type.
110
126
  """
127
+ # preload scalar parameters for this realization
128
+ keys = [
129
+ p.name
130
+ for p in parameter_configs
131
+ if p.cardinality == ParameterCardinality.multiple_configs_per_ensemble_dataset
132
+ ]
133
+ export_timings: defaultdict[str, float] = defaultdict(float)
134
+ scalar_data: dict[str, float | str] = {}
135
+ if keys:
136
+ start_time = time.perf_counter()
137
+ df = fs.load_scalar_keys(keys=keys, realizations=iens, transformed=True)
138
+ scalar_data = df.to_dicts()[0]
139
+ export_timings["load_scalar_keys"] = time.perf_counter() - start_time
111
140
  exports: dict[str, dict[str, float | str]] = {}
141
+ log_exports: dict[str, dict[str, float | str]] = {}
112
142
 
113
143
  for param in parameter_configs:
114
144
  # For the first iteration we do not write the parameter
@@ -116,15 +146,42 @@ def _generate_parameter_files(
116
146
  # model has completed.
117
147
  if param.forward_init and iteration == 0:
118
148
  continue
119
- export_values = param.write_to_runpath(Path(run_path), iens, fs)
149
+ start_time = time.perf_counter()
150
+ export_values: dict[str, dict[str, float | str]] | None = None
151
+ log_export_values: dict[str, dict[str, float | str]] | None = {}
152
+ if param.name in scalar_data:
153
+ scalar_value = scalar_data[param.name]
154
+ export_values = {param.group_name: {param.name: scalar_value}}
155
+ if isinstance(param, GenKwConfig) and isinstance(
156
+ param.distribution, (LogNormalSettings, LogUnifSettings)
157
+ ):
158
+ if isinstance(scalar_value, float) and scalar_value > 0:
159
+ log_value = math.log10(scalar_value)
160
+ log_export_values = {
161
+ f"LOG10_{param.group_name}": {param.name: log_value}
162
+ }
163
+ else:
164
+ logger.warning(
165
+ "Could not export the log10 value of "
166
+ f"{scalar_value} as it is invalid"
167
+ )
168
+ else:
169
+ export_values = param.write_to_runpath(Path(run_path), iens, fs)
120
170
  if export_values:
121
171
  for group, vals in export_values.items():
122
172
  exports.setdefault(group, {}).update(vals)
173
+ if log_export_values:
174
+ for group, vals in log_export_values.items():
175
+ log_exports.setdefault(group, {}).update(vals)
176
+ export_timings[param.type] += time.perf_counter() - start_time
123
177
  continue
124
-
125
- _value_export_txt(run_path, export_base_name, exports)
178
+ start_time = time.perf_counter()
179
+ _value_export_txt(run_path, export_base_name, exports | log_exports)
180
+ export_timings["value_export_txt"] = time.perf_counter() - start_time
181
+ start_time = time.perf_counter()
126
182
  _value_export_json(run_path, export_base_name, exports)
127
- return exports
183
+ export_timings["value_export_json"] = time.perf_counter() - start_time
184
+ return (exports, dict(export_timings))
128
185
 
129
186
 
130
187
  def _manifest_to_json(ensemble: Ensemble, iens: int, iter_: int) -> dict[str, Any]:
@@ -133,7 +190,7 @@ def _manifest_to_json(ensemble: Ensemble, iens: int, iter_: int) -> dict[str, An
133
190
  for param_config in ensemble.experiment.parameter_configuration.values():
134
191
  assert isinstance(
135
192
  param_config,
136
- ExtParamConfig | GenKwConfig | Field | SurfaceConfig,
193
+ EverestControl | GenKwConfig | Field | SurfaceConfig,
137
194
  )
138
195
  if param_config.forward_init and ensemble.iteration == 0:
139
196
  assert not isinstance(param_config, GenKwConfig)
@@ -145,13 +202,32 @@ def _manifest_to_json(ensemble: Ensemble, iens: int, iter_: int) -> dict[str, An
145
202
  # Add expected response files to manifest
146
203
  for response_config in ensemble.experiment.response_configuration.values():
147
204
  for input_file in response_config.expected_input_files:
148
- manifest[f"{response_config.response_type}_{input_file}"] = (
149
- substitute_runpath_name(input_file, iens, iter_)
205
+ manifest[f"{response_config.type}_{input_file}"] = substitute_runpath_name(
206
+ input_file, iens, iter_
150
207
  )
151
208
 
152
209
  return manifest
153
210
 
154
211
 
212
+ def _make_param_substituter(
213
+ substituter: Substitutions,
214
+ param_data: Mapping[str, Mapping[str, str | float]],
215
+ ) -> Substitutions:
216
+ param_substituter = deepcopy(substituter)
217
+ for values in param_data.values():
218
+ for param_name, value in values.items():
219
+ if isinstance(value, int):
220
+ formatted_value = str(value)
221
+ elif isinstance(value, float):
222
+ formatted_value = f"{value:g}"
223
+ else:
224
+ formatted_value = str(value)
225
+
226
+ param_substituter[f"<{param_name}>"] = formatted_value
227
+
228
+ return param_substituter
229
+
230
+
155
231
  @log_duration(logger, logging.INFO)
156
232
  def create_run_path(
157
233
  run_args: list[RunArg],
@@ -168,13 +244,19 @@ def create_run_path(
168
244
  if context_env is None:
169
245
  context_env = {}
170
246
  runpaths.set_ert_ensemble(ensemble.name)
171
-
247
+ timings = {
248
+ "generate_parameter_files": 0.0,
249
+ "substitute_parameters": 0.0,
250
+ "substitute_real_iter": 0.0,
251
+ "result_file_to_target": 0.0,
252
+ }
172
253
  substituter = Substitutions(substitutions)
173
254
  for run_arg in run_args:
174
255
  run_path = Path(run_arg.runpath)
175
256
  if run_arg.active:
176
257
  run_path.mkdir(parents=True, exist_ok=True)
177
- param_data = _generate_parameter_files(
258
+ start_time = time.perf_counter()
259
+ (param_data, detailed_parameter_timings) = _generate_parameter_files(
178
260
  ensemble.experiment.parameter_configuration.values(),
179
261
  parameters_file,
180
262
  run_path,
@@ -182,22 +264,29 @@ def create_run_path(
182
264
  ensemble,
183
265
  ensemble.iteration,
184
266
  )
267
+ for parameter_type, duration in detailed_parameter_timings.items():
268
+ if parameter_type not in timings:
269
+ timings[parameter_type] = 0.0
270
+ timings[parameter_type] += duration
271
+
272
+ timings["generate_parameter_files"] += time.perf_counter() - start_time
273
+ real_iter_substituter = substituter.real_iter_substituter(
274
+ run_arg.iens, ensemble.iteration
275
+ )
276
+ param_substituter = _make_param_substituter(
277
+ real_iter_substituter, param_data
278
+ )
185
279
  for (
186
280
  source_file_content,
187
281
  target_file,
188
282
  ) in ensemble.experiment.templates_configuration:
189
- target_file = substituter.substitute_real_iter(
190
- target_file, run_arg.iens, ensemble.iteration
191
- )
192
- result = substituter.substitute_real_iter(
193
- source_file_content,
194
- run_arg.iens,
195
- ensemble.iteration,
196
- )
197
- result = substituter.substitute_parameters(
198
- result,
199
- param_data,
200
- )
283
+ start_time = time.perf_counter()
284
+ target_file = real_iter_substituter.substitute(target_file)
285
+ timings["substitute_real_iter"] += time.perf_counter() - start_time
286
+ start_time = time.perf_counter()
287
+ result = param_substituter.substitute(source_file_content)
288
+ timings["substitute_parameters"] += time.perf_counter() - start_time
289
+ start_time = time.perf_counter()
201
290
  target = run_path / target_file
202
291
  if not target.parent.exists():
203
292
  os.makedirs(
@@ -205,10 +294,13 @@ def create_run_path(
205
294
  exist_ok=True,
206
295
  )
207
296
  target.write_text(result)
297
+ timings["result_file_to_target"] += time.perf_counter() - start_time
208
298
 
209
299
  path = run_path / "jobs.json"
300
+ start_time = time.perf_counter()
210
301
  _backup_if_existing(path)
211
-
302
+ timings["backup_if_existing"] = time.perf_counter() - start_time
303
+ start_time = time.perf_counter()
212
304
  forward_model_output = create_forward_model_json(
213
305
  context=substitutions,
214
306
  forward_model_steps=forward_model_steps,
@@ -225,12 +317,15 @@ def create_run_path(
225
317
  option=orjson.OPT_NON_STR_KEYS | orjson.OPT_INDENT_2,
226
318
  )
227
319
  )
320
+ timings["jobs_to_json"] = time.perf_counter() - start_time
228
321
  # Write MANIFEST file to runpath use to avoid NFS sync issues
322
+ start_time = time.perf_counter()
229
323
  data = _manifest_to_json(ensemble, run_arg.iens, run_arg.itr)
230
324
  Path(run_path / "manifest.json").write_bytes(
231
325
  orjson.dumps(data, option=orjson.OPT_NON_STR_KEYS | orjson.OPT_INDENT_2)
232
326
  )
233
-
327
+ timings["manifest_to_json"] = time.perf_counter() - start_time
328
+ logger.info(f"_create_run_path durations: {timings}")
234
329
  runpaths.write_runpath_list(
235
330
  [ensemble.iteration], [real.iens for real in run_args if real.active]
236
331
  )