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
@@ -7,30 +7,22 @@ from typing import Self
7
7
 
8
8
  from pydantic import BaseModel, Field, model_validator
9
9
 
10
- from .ext_param_config import ExtParamConfig
10
+ from .everest_control import EverestControl
11
11
  from .field import Field as FieldConfig
12
- from .gen_data_config import GenDataConfig
13
12
  from .gen_kw_config import GenKwConfig
13
+ from .known_response_types import KNOWN_ERT_RESPONSE_TYPES, KnownErtResponseTypes
14
14
  from .parameter_config import ParameterConfig
15
15
  from .parsing import ConfigDict, ConfigKeys, ConfigValidationError
16
16
  from .response_config import ResponseConfig
17
- from .summary_config import SummaryConfig
18
17
  from .surface_config import SurfaceConfig
19
18
 
20
- KnownResponseTypes = SummaryConfig | GenDataConfig
21
-
22
- _KNOWN_RESPONSE_TYPES = (
23
- SummaryConfig,
24
- GenDataConfig,
25
- )
26
-
27
19
  logger = logging.getLogger(__name__)
28
20
 
29
21
 
30
22
  class EnsembleConfig(BaseModel):
31
- response_configs: dict[str, KnownResponseTypes] = Field(default_factory=dict)
23
+ response_configs: dict[str, KnownErtResponseTypes] = Field(default_factory=dict)
32
24
  parameter_configs: dict[
33
- str, GenKwConfig | FieldConfig | SurfaceConfig | ExtParamConfig
25
+ str, GenKwConfig | FieldConfig | SurfaceConfig | EverestControl
34
26
  ] = Field(default_factory=dict)
35
27
 
36
28
  @model_validator(mode="after")
@@ -131,16 +123,16 @@ class EnsembleConfig(BaseModel):
131
123
  + [make_field(f) for f in field_list]
132
124
  )
133
125
  EnsembleConfig._check_for_duplicate_gen_kw_param_names(gen_kw_cfgs)
134
- response_configs: list[KnownResponseTypes] = []
126
+ response_configs: list[KnownErtResponseTypes] = []
135
127
 
136
- for config_cls in _KNOWN_RESPONSE_TYPES:
128
+ for config_cls in KNOWN_ERT_RESPONSE_TYPES:
137
129
  instance = config_cls.from_config_dict(config_dict)
138
130
 
139
131
  if instance is not None and instance.keys:
140
132
  response_configs.append(instance)
141
133
 
142
134
  return cls(
143
- response_configs={response.name: response for response in response_configs},
135
+ response_configs={response.type: response for response in response_configs},
144
136
  parameter_configs={
145
137
  parameter.name: parameter for parameter in parameter_configs
146
138
  },
@@ -151,13 +143,13 @@ class EnsembleConfig(BaseModel):
151
143
  return self.parameter_configs[key]
152
144
  elif key in self.response_configs:
153
145
  return self.response_configs[key]
154
- elif _config := next(
146
+ elif config := next(
155
147
  (c for c in self.response_configs.values() if key in c.keys), None
156
148
  ):
157
149
  # Only hit by blockfs migration
158
150
  # returns the same config for one call per
159
151
  # response type. Is later deduped before saving to json
160
- return _config
152
+ return config
161
153
  else:
162
154
  raise KeyError(f"The key:{key} is not in the ensemble configuration")
163
155
 
ert/config/ert_config.py CHANGED
@@ -25,6 +25,7 @@ from ._design_matrix_validator import DesignMatrixValidator
25
25
  from ._observations import (
26
26
  HistoryObservation,
27
27
  Observation,
28
+ RFTObservation,
28
29
  SummaryObservation,
29
30
  make_observations,
30
31
  )
@@ -36,9 +37,12 @@ from .forward_model_step import (
36
37
  ForwardModelStepJSON,
37
38
  ForwardModelStepValidationError,
38
39
  ForwardModelStepWarning,
40
+ SiteInstalledForwardModelStep,
41
+ SiteOrUserForwardModelStep,
42
+ UserInstalledForwardModelStep,
39
43
  )
40
44
  from .gen_data_config import GenDataConfig
41
- from .gen_kw_config import GenKwConfig
45
+ from .gen_kw_config import DataSource, GenKwConfig
42
46
  from .model_config import ModelConfig
43
47
  from .parse_arg_types_list import parse_arg_types_list
44
48
  from .parsing import (
@@ -59,9 +63,11 @@ from .parsing import (
59
63
  from .parsing.observations_parser import ObservationDict
60
64
  from .queue_config import KnownQueueOptions, QueueConfig
61
65
  from .refcase import Refcase
66
+ from .rft_config import RFTConfig
62
67
  from .workflow import Workflow
63
68
  from .workflow_fixtures import fixtures_per_hook
64
69
  from .workflow_job import (
70
+ BaseErtScriptWorkflow,
65
71
  ErtScriptLoadFailure,
66
72
  ErtScriptWorkflow,
67
73
  WorkflowJob,
@@ -132,6 +138,7 @@ def create_forward_model_json(
132
138
  env_pr_fm_step = {}
133
139
 
134
140
  context_substitutions = Substitutions(context)
141
+ real_iter_substituter = context_substitutions.real_iter_substituter(iens, itr)
135
142
 
136
143
  class Substituter:
137
144
  def __init__(self, fm_step: ForwardModelStep) -> None:
@@ -145,7 +152,7 @@ def create_forward_model_json(
145
152
  )
146
153
  self.copy_private_args = Substitutions(
147
154
  {
148
- key: context_substitutions.substitute_real_iter(val, iens, itr)
155
+ key: real_iter_substituter.substitute(val)
149
156
  for key, val in fm_step.private_args.items()
150
157
  }
151
158
  )
@@ -162,7 +169,7 @@ def create_forward_model_json(
162
169
  string = self.copy_private_args.substitute(
163
170
  string, self.substitution_context_hint, 1, warn_max_iter=False
164
171
  )
165
- return context_substitutions.substitute_real_iter(string, iens, itr)
172
+ return real_iter_substituter.substitute(string)
166
173
 
167
174
  def filter_env_dict(self, env_dict: dict[str, str]) -> dict[str, str] | None:
168
175
  substituted_dict = {}
@@ -323,63 +330,74 @@ def read_templates(config_dict: ConfigDict) -> list[tuple[str, str]]:
323
330
 
324
331
  def workflow_jobs_from_dict(
325
332
  content_dict: ConfigDict,
326
- installed_workflows: dict[str, WorkflowJob] | None = None,
333
+ site_installed_workflows_jobs: dict[str, WorkflowJob] | None = None,
327
334
  ) -> dict[str, WorkflowJob]:
328
- workflow_job_info = content_dict.get(ConfigKeys.LOAD_WORKFLOW_JOB, [])
329
- workflow_job_dir_info = content_dict.get(ConfigKeys.WORKFLOW_JOB_DIRECTORY, [])
335
+ user_installed_workflow_job_info = content_dict.get(
336
+ ConfigKeys.LOAD_WORKFLOW_JOB, []
337
+ )
338
+ user_installed_workflow_job_dir_info = content_dict.get(
339
+ ConfigKeys.WORKFLOW_JOB_DIRECTORY, []
340
+ )
330
341
 
331
- workflow_jobs = copy.copy(installed_workflows) if installed_workflows else {}
342
+ workflow_jobs = (
343
+ copy.copy(site_installed_workflows_jobs)
344
+ if site_installed_workflows_jobs
345
+ else {}
346
+ )
332
347
 
333
348
  errors: list[ErrorInfo | ConfigValidationError] = []
334
349
 
335
- for workflow_job in workflow_job_info:
350
+ for user_workflow_job in user_installed_workflow_job_info:
336
351
  try:
337
352
  # workflow_job_from_file only throws error if a
338
353
  # non-readable file is provided.
339
354
  # Non-existing files are caught by the new parser
340
- new_job = workflow_job_from_file(
341
- config_file=workflow_job[0],
342
- name=None if len(workflow_job) == 1 else workflow_job[1],
355
+ user_job = workflow_job_from_file(
356
+ config_file=user_workflow_job[0],
357
+ name=None if len(user_workflow_job) == 1 else user_workflow_job[1],
358
+ origin="user",
343
359
  )
344
- name = new_job.name
360
+ name = user_job.name
345
361
  if name in workflow_jobs:
346
362
  ConfigWarning.warn(
347
363
  f"Duplicate workflow jobs with name {name!r}, choosing "
348
- f"{new_job.location()!r} over "
364
+ f"{user_job.location()!r} over "
349
365
  f"{workflow_jobs[name].location()!r}",
350
366
  name,
351
367
  )
352
- workflow_jobs[name] = new_job
368
+ workflow_jobs[name] = user_job
353
369
  except ErtScriptLoadFailure as err:
354
370
  ConfigWarning.warn(
355
- f"Loading workflow job {workflow_job[0]!r}"
371
+ f"Loading workflow job {user_workflow_job[0]!r}"
356
372
  f" failed with '{err}'. It will not be loaded.",
357
- workflow_job[0],
373
+ user_workflow_job[0],
358
374
  )
359
375
  except ConfigValidationError as err:
360
- errors.append(ErrorInfo(message=str(err)).set_context(workflow_job[0]))
376
+ errors.append(ErrorInfo(message=str(err)).set_context(user_workflow_job[0]))
361
377
 
362
- for job_path in workflow_job_dir_info:
363
- for file_name in _get_files_in_directory(job_path, errors):
378
+ for user_job_path in user_installed_workflow_job_dir_info:
379
+ for user_job_file in _get_files_in_directory(user_job_path, errors):
364
380
  try:
365
- new_job = workflow_job_from_file(config_file=file_name)
366
- name = new_job.name
381
+ user_job = workflow_job_from_file(
382
+ config_file=user_job_file, origin="user"
383
+ )
384
+ name = user_job.name
367
385
  if name in workflow_jobs:
368
386
  ConfigWarning.warn(
369
387
  f"Duplicate workflow jobs with name {name!r}, choosing "
370
- f"{new_job.location()!r} over "
388
+ f"{user_job.location()!r} over "
371
389
  f"{workflow_jobs[name].location()!r}",
372
390
  name,
373
391
  )
374
- workflow_jobs[name] = new_job
392
+ workflow_jobs[name] = user_job
375
393
  except ErtScriptLoadFailure as err:
376
394
  ConfigWarning.warn(
377
- f"Loading workflow job {file_name!r}"
395
+ f"Loading workflow job {user_job_file!r}"
378
396
  f" failed with '{err}'. It will not be loaded.",
379
- file_name,
397
+ user_job_file,
380
398
  )
381
399
  except ConfigValidationError as err:
382
- errors.append(ErrorInfo(message=str(err)).set_context(job_path))
400
+ errors.append(ErrorInfo(message=str(err)).set_context(user_job_path))
383
401
  if errors:
384
402
  raise ConfigValidationError.from_collected(errors)
385
403
 
@@ -387,13 +405,11 @@ def workflow_jobs_from_dict(
387
405
 
388
406
 
389
407
  def create_and_hook_workflows(
390
- content_dict: ConfigDict,
408
+ hook_workflow_info: list[tuple[str, HookRuntime]],
409
+ workflow_info: list[tuple[str, str]],
391
410
  workflow_jobs: dict[str, WorkflowJob],
392
411
  substitutions: dict[str, str],
393
412
  ) -> tuple[dict[str, Workflow], defaultdict[HookRuntime, list[Workflow]]]:
394
- hook_workflow_info = content_dict.get(ConfigKeys.HOOK_WORKFLOW, [])
395
- workflow_info = content_dict.get(ConfigKeys.LOAD_WORKFLOW, [])
396
-
397
413
  workflows = {}
398
414
  hooked_workflows = defaultdict(list)
399
415
 
@@ -411,7 +427,7 @@ def create_and_hook_workflows(
411
427
  for job, args in workflow:
412
428
  if isinstance(job, ErtScriptWorkflow):
413
429
  try:
414
- job.ert_script.validate(args)
430
+ job.load_ert_script_class().validate(args)
415
431
  except ConfigValidationError as err:
416
432
  errors.append(ErrorInfo(message=str(err)).set_context(work[0]))
417
433
  continue
@@ -439,11 +455,11 @@ def create_and_hook_workflows(
439
455
  wf = workflows[hook_name]
440
456
  available_fixtures = fixtures_per_hook[mode]
441
457
  for job, _ in wf.cmd_list:
442
- if not hasattr(job, "ert_script") or job.ert_script is None:
458
+ if not isinstance(job, BaseErtScriptWorkflow):
443
459
  continue
444
460
 
445
- assert isinstance(job, ErtScriptWorkflow)
446
- ert_script_instance = job.ert_script()
461
+ ert_script_class = job.load_ert_script_class()
462
+ ert_script_instance = ert_script_class()
447
463
  requested_fixtures = ert_script_instance.requested_fixtures
448
464
 
449
465
  # Look for requested fixtures that are not available for the given
@@ -485,7 +501,7 @@ def create_and_hook_workflows(
485
501
  def workflows_from_dict(
486
502
  content_dict: ConfigDict,
487
503
  substitutions: dict[str, str],
488
- installed_workflows: Mapping[str, WorkflowJob] | None = None,
504
+ site_installed_workflows_jobs: Mapping[str, WorkflowJob] | None = None,
489
505
  ) -> tuple[
490
506
  dict[str, WorkflowJob],
491
507
  dict[str, Workflow],
@@ -493,28 +509,31 @@ def workflows_from_dict(
493
509
  ]:
494
510
  workflow_jobs = workflow_jobs_from_dict(
495
511
  content_dict,
496
- dict(copy.copy(installed_workflows)) if installed_workflows else {},
512
+ dict(copy.copy(site_installed_workflows_jobs))
513
+ if site_installed_workflows_jobs
514
+ else {},
497
515
  )
498
516
  workflows, hooked_workflows = create_and_hook_workflows(
499
- content_dict, workflow_jobs, substitutions
517
+ content_dict.get(ConfigKeys.HOOK_WORKFLOW, []),
518
+ content_dict.get(ConfigKeys.LOAD_WORKFLOW, []),
519
+ workflow_jobs,
520
+ substitutions,
500
521
  )
501
522
  return workflow_jobs, workflows, hooked_workflows
502
523
 
503
524
 
504
525
  def installed_forward_model_steps_from_dict(
505
526
  config_dict: ConfigDict,
506
- ) -> dict[str, ForwardModelStep]:
527
+ ) -> dict[str, UserInstalledForwardModelStep]:
507
528
  errors: list[ErrorInfo | ConfigValidationError] = []
508
- fm_steps: dict[str, ForwardModelStep] = {}
529
+ fm_steps: dict[str, UserInstalledForwardModelStep] = {}
509
530
  for name, (fm_step_config_file, config_contents) in config_dict.get(
510
531
  ConfigKeys.INSTALL_JOB, []
511
532
  ):
512
533
  fm_step_config_file = path.abspath(fm_step_config_file)
513
534
  try:
514
535
  new_fm_step = forward_model_step_from_config_contents(
515
- config_contents,
516
- name=name,
517
- config_file=fm_step_config_file,
536
+ config_contents, name=name, config_file=fm_step_config_file
518
537
  )
519
538
  except ConfigValidationError as e:
520
539
  errors.append(e)
@@ -677,6 +696,8 @@ def log_observation_keys(
677
696
 
678
697
  RESERVED_KEYWORDS = ["realization", "IENS", "ITER"]
679
698
 
699
+ USER_CONFIG_SCHEMA = init_user_config_schema()
700
+
680
701
 
681
702
  class ErtConfig(BaseModel):
682
703
  DEFAULT_ENSPATH: ClassVar[str] = "storage"
@@ -688,6 +709,7 @@ class ErtConfig(BaseModel):
688
709
  QUEUE_OPTIONS: ClassVar[KnownQueueOptions | None] = None
689
710
  RESERVED_KEYWORDS: ClassVar[list[str]] = RESERVED_KEYWORDS
690
711
  ENV_VARS: ClassVar[dict[str, str]] = {}
712
+ PRIORITIZE_PRIVATE_IP_ADDRESS: ClassVar[bool] = False
691
713
 
692
714
  substitutions: dict[str, str] = Field(default_factory=dict)
693
715
  ensemble_config: EnsembleConfig = Field(default_factory=EnsembleConfig)
@@ -702,13 +724,11 @@ class ErtConfig(BaseModel):
702
724
  default_factory=lambda: defaultdict(lambda: cast(list[Workflow], []))
703
725
  )
704
726
  runpath_file: Path = Path(DEFAULT_RUNPATH_FILE)
727
+ prioritize_private_ip_address: bool = False
705
728
 
706
729
  ert_templates: list[tuple[str, str]] = Field(default_factory=list)
707
- installed_forward_model_steps: dict[str, ForwardModelStep] = Field(
708
- default_factory=dict
709
- )
710
730
 
711
- forward_model_steps: list[ForwardModelStep] = Field(default_factory=list)
731
+ forward_model_steps: list[SiteOrUserForwardModelStep] = Field(default_factory=list)
712
732
  runpath_config: ModelConfig = Field(default_factory=ModelConfig)
713
733
  user_config_file: str = "no_config"
714
734
  config_path: str = Field(init=False, default="")
@@ -721,6 +741,18 @@ class ErtConfig(BaseModel):
721
741
  @property
722
742
  def observations(self) -> dict[str, pl.DataFrame]:
723
743
  if self._observations is None:
744
+ has_rft_observations = any(
745
+ isinstance(o, RFTObservation) for o in self.observation_declarations
746
+ )
747
+ if (
748
+ has_rft_observations
749
+ and "rft" not in self.ensemble_config.response_configs
750
+ ):
751
+ self.ensemble_config.response_configs["rft"] = RFTConfig(
752
+ input_files=[self.runpath_config.eclbase_format_string],
753
+ data_to_read={},
754
+ locations=[],
755
+ )
724
756
  computed = create_observation_dataframes(
725
757
  self.observation_declarations,
726
758
  self.refcase,
@@ -728,6 +760,10 @@ class ErtConfig(BaseModel):
728
760
  GenDataConfig | None,
729
761
  self.ensemble_config.response_configs.get("gen_data", None),
730
762
  ),
763
+ cast(
764
+ RFTConfig | None,
765
+ self.ensemble_config.response_configs.get("rft", None),
766
+ ),
731
767
  self.time_map,
732
768
  self.history_source,
733
769
  )
@@ -779,6 +815,15 @@ class ErtConfig(BaseModel):
779
815
  )
780
816
  return self
781
817
 
818
+ @model_validator(mode="after")
819
+ def log_ensemble_config_contents(self) -> Self:
820
+ all_parameters = self.parameter_configurations_with_design_matrix
821
+ parameter_type_count = Counter(parameter.type for parameter in all_parameters)
822
+ logger.info(
823
+ f"EnsembleConfig contains parameters of type {dict(parameter_type_count)}"
824
+ )
825
+ return self
826
+
782
827
  def __eq__(self, other: object) -> bool:
783
828
  if not isinstance(other, ErtConfig):
784
829
  return False
@@ -805,7 +850,7 @@ class ErtConfig(BaseModel):
805
850
  def with_plugins(runtime_plugins: ErtRuntimePlugins) -> type[ErtConfig]:
806
851
  class ErtConfigWithPlugins(ErtConfig):
807
852
  PREINSTALLED_FORWARD_MODEL_STEPS: ClassVar[
808
- Mapping[str, ForwardModelStep]
853
+ Mapping[str, SiteInstalledForwardModelStep]
809
854
  ] = runtime_plugins.installed_forward_model_steps
810
855
  PREINSTALLED_WORKFLOWS = dict(runtime_plugins.installed_workflow_jobs)
811
856
  ENV_PR_FM_STEP: ClassVar[dict[str, dict[str, Any]]] = (
@@ -815,6 +860,9 @@ class ErtConfig(BaseModel):
815
860
  )
816
861
  ENV_VARS = dict(runtime_plugins.environment_variables)
817
862
  QUEUE_OPTIONS = runtime_plugins.queue_options
863
+ PRIORITIZE_PRIVATE_IP_ADDRESS = (
864
+ runtime_plugins.prioritize_private_ip_address
865
+ )
818
866
 
819
867
  ErtConfigWithPlugins.model_rebuild()
820
868
  assert issubclass(ErtConfigWithPlugins, ErtConfig)
@@ -941,7 +989,9 @@ class ErtConfig(BaseModel):
941
989
  errors.append(e)
942
990
 
943
991
  try:
944
- queue_config = QueueConfig.from_dict(config_dict, cls.QUEUE_OPTIONS)
992
+ queue_config = QueueConfig.from_dict(
993
+ config_dict, site_queue_options=cls.QUEUE_OPTIONS
994
+ )
945
995
 
946
996
  substitutions["<NUM_CPU>"] = str(queue_config.queue_options.num_cpu)
947
997
 
@@ -1011,11 +1061,30 @@ class ErtConfig(BaseModel):
1011
1061
  if isinstance(cfg, GenKwConfig) and cfg.name in dm_params
1012
1062
  ]
1013
1063
  if overwrite_params:
1014
- ConfigWarning.warn(
1015
- f"Parameters {overwrite_params} "
1016
- "will be overridden by design matrix. This will cause "
1017
- "updates to be turned off for these parameters."
1018
- )
1064
+ param_sampled = [
1065
+ k
1066
+ for k in overwrite_params
1067
+ if analysis_config.design_matrix.parameter_priority[k]
1068
+ == DataSource.SAMPLED
1069
+ ]
1070
+ param_design = [
1071
+ k
1072
+ for k in overwrite_params
1073
+ if analysis_config.design_matrix.parameter_priority[k]
1074
+ == DataSource.DESIGN_MATRIX
1075
+ ]
1076
+ if param_sampled:
1077
+ ConfigWarning.warn(
1078
+ f"Parameters {param_sampled} "
1079
+ "are also defined in design matrix, but due to the sampled"
1080
+ " priority they will remain as such."
1081
+ )
1082
+ if param_design:
1083
+ ConfigWarning.warn(
1084
+ f"Parameters {param_design} "
1085
+ "will be overridden by design matrix. This will cause "
1086
+ "updates to be turned off for these parameters."
1087
+ )
1019
1088
 
1020
1089
  if dm_errors:
1021
1090
  raise ConfigValidationError.from_collected(dm_errors)
@@ -1046,6 +1115,19 @@ class ErtConfig(BaseModel):
1046
1115
  user_configured_.add(key)
1047
1116
  env_vars[key] = substituter.substitute(val)
1048
1117
 
1118
+ prioritize_private_ip_address: bool = cls.PRIORITIZE_PRIVATE_IP_ADDRESS
1119
+ if ConfigKeys.PRIORITIZE_PRIVATE_IP_ADDRESS in config_dict:
1120
+ user_prioritize_private_ip_address = bool(
1121
+ config_dict[ConfigKeys.PRIORITIZE_PRIVATE_IP_ADDRESS]
1122
+ )
1123
+ if prioritize_private_ip_address != user_prioritize_private_ip_address:
1124
+ logger.warning(
1125
+ "PRIORITIZE_PRIVATE_IP_ADDRESS was overwritten by user: "
1126
+ f"{prioritize_private_ip_address} -> "
1127
+ f"{user_prioritize_private_ip_address}"
1128
+ )
1129
+ prioritize_private_ip_address = user_prioritize_private_ip_address
1130
+
1049
1131
  try:
1050
1132
  refcase = Refcase.from_config_dict(config_dict)
1051
1133
  cls_config = cls(
@@ -1061,7 +1143,6 @@ class ErtConfig(BaseModel):
1061
1143
  hooked_workflows=hooked_workflows,
1062
1144
  runpath_file=Path(runpath_file),
1063
1145
  ert_templates=read_templates(config_dict),
1064
- installed_forward_model_steps=installed_forward_model_steps,
1065
1146
  forward_model_steps=cls._create_list_of_forward_model_steps_to_run(
1066
1147
  installed_forward_model_steps,
1067
1148
  substitutions,
@@ -1073,11 +1154,21 @@ class ErtConfig(BaseModel):
1073
1154
  time_map=time_map,
1074
1155
  history_source=history_source,
1075
1156
  refcase=refcase,
1157
+ prioritize_private_ip_address=prioritize_private_ip_address,
1076
1158
  )
1077
1159
 
1078
1160
  # The observations are created here because create_observation_dataframes
1079
1161
  # will perform additonal validation which needs the context in
1080
1162
  # obs_configs which is stripped by pydantic
1163
+ has_rft_observations = any(
1164
+ isinstance(o, RFTObservation) for o in obs_configs
1165
+ )
1166
+ if has_rft_observations and "rft" not in ensemble_config.response_configs:
1167
+ ensemble_config.response_configs["rft"] = RFTConfig(
1168
+ input_files=[eclbase],
1169
+ data_to_read={},
1170
+ locations=[],
1171
+ )
1081
1172
  cls_config._observations = create_observation_dataframes(
1082
1173
  obs_configs,
1083
1174
  refcase,
@@ -1085,6 +1176,10 @@ class ErtConfig(BaseModel):
1085
1176
  GenDataConfig | None,
1086
1177
  ensemble_config.response_configs.get("gen_data", None),
1087
1178
  ),
1179
+ cast(
1180
+ RFTConfig | None,
1181
+ ensemble_config.response_configs.get("rft", None),
1182
+ ),
1088
1183
  time_map,
1089
1184
  history_source,
1090
1185
  )
@@ -1262,7 +1357,7 @@ class ErtConfig(BaseModel):
1262
1357
  @classmethod
1263
1358
  def _read_user_config_contents(cls, user_config: str, file_name: str) -> ConfigDict:
1264
1359
  return parse_contents(
1265
- user_config, file_name=file_name, schema=init_user_config_schema()
1360
+ user_config, file_name=file_name, schema=USER_CONFIG_SCHEMA
1266
1361
  )
1267
1362
 
1268
1363
  @classmethod
@@ -1352,8 +1447,10 @@ def uppercase_subkeys_and_stringify_subvalues(
1352
1447
 
1353
1448
 
1354
1449
  def forward_model_step_from_config_contents(
1355
- config_contents: str, config_file: str, name: str | None = None
1356
- ) -> ForwardModelStep:
1450
+ config_contents: str,
1451
+ config_file: str,
1452
+ name: str | None = None,
1453
+ ) -> UserInstalledForwardModelStep:
1357
1454
  if name is None:
1358
1455
  name = os.path.basename(config_file)
1359
1456
 
@@ -1377,7 +1474,7 @@ def forward_model_step_from_config_contents(
1377
1474
  environment = {k: v for [k, v] in content_dict.get("ENV", [])}
1378
1475
  default_mapping = {k: v for [k, v] in content_dict.get("DEFAULT", [])}
1379
1476
 
1380
- return ForwardModelStep(
1477
+ return UserInstalledForwardModelStep(
1381
1478
  name=name,
1382
1479
  executable=content_dict["EXECUTABLE"],
1383
1480
  stdin_file=content_dict.get("STDIN"),