ert 17.1.9__py3-none-any.whl → 18.0.1__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 (164) hide show
  1. _ert/events.py +19 -2
  2. ert/__main__.py +8 -7
  3. ert/analysis/_update_commons.py +12 -3
  4. ert/cli/main.py +6 -3
  5. ert/cli/monitor.py +7 -0
  6. ert/config/__init__.py +13 -3
  7. ert/config/_create_observation_dataframes.py +60 -12
  8. ert/config/_observations.py +14 -1
  9. ert/config/_read_summary.py +8 -6
  10. ert/config/ensemble_config.py +6 -14
  11. ert/config/ert_config.py +19 -13
  12. ert/config/{everest_objective_config.py → everest_response.py} +23 -12
  13. ert/config/ext_param_config.py +133 -1
  14. ert/config/field.py +12 -8
  15. ert/config/forward_model_step.py +108 -6
  16. ert/config/gen_data_config.py +2 -6
  17. ert/config/gen_kw_config.py +0 -9
  18. ert/config/known_response_types.py +14 -0
  19. ert/config/parameter_config.py +0 -17
  20. ert/config/parsing/config_keywords.py +1 -0
  21. ert/config/parsing/config_schema.py +12 -0
  22. ert/config/parsing/config_schema_deprecations.py +11 -0
  23. ert/config/parsing/config_schema_item.py +1 -1
  24. ert/config/queue_config.py +4 -4
  25. ert/config/response_config.py +0 -7
  26. ert/config/rft_config.py +230 -0
  27. ert/config/summary_config.py +2 -6
  28. ert/config/violations.py +0 -0
  29. ert/config/workflow_fixtures.py +2 -1
  30. ert/dark_storage/client/__init__.py +2 -2
  31. ert/dark_storage/client/_session.py +4 -4
  32. ert/dark_storage/client/client.py +2 -2
  33. ert/dark_storage/compute/misfits.py +7 -6
  34. ert/dark_storage/endpoints/compute/misfits.py +2 -2
  35. ert/dark_storage/endpoints/observations.py +4 -4
  36. ert/dark_storage/endpoints/responses.py +15 -1
  37. ert/ensemble_evaluator/__init__.py +8 -1
  38. ert/ensemble_evaluator/evaluator.py +81 -29
  39. ert/ensemble_evaluator/event.py +6 -0
  40. ert/ensemble_evaluator/snapshot.py +3 -1
  41. ert/ensemble_evaluator/state.py +1 -0
  42. ert/field_utils/__init__.py +8 -0
  43. ert/field_utils/field_utils.py +211 -1
  44. ert/gui/ertwidgets/__init__.py +23 -16
  45. ert/gui/ertwidgets/analysismoduleedit.py +2 -2
  46. ert/gui/ertwidgets/checklist.py +1 -1
  47. ert/gui/ertwidgets/create_experiment_dialog.py +3 -1
  48. ert/gui/ertwidgets/ensembleselector.py +2 -2
  49. ert/gui/ertwidgets/models/__init__.py +2 -0
  50. ert/gui/ertwidgets/models/activerealizationsmodel.py +2 -1
  51. ert/gui/ertwidgets/models/path_model.py +1 -1
  52. ert/gui/ertwidgets/models/targetensemblemodel.py +2 -1
  53. ert/gui/ertwidgets/models/text_model.py +1 -1
  54. ert/gui/ertwidgets/searchbox.py +13 -4
  55. ert/gui/{suggestor → ertwidgets/suggestor}/_suggestor_message.py +13 -4
  56. ert/gui/main.py +14 -7
  57. ert/gui/main_window.py +1 -2
  58. ert/gui/simulation/ensemble_experiment_panel.py +1 -1
  59. ert/gui/simulation/ensemble_information_filter_panel.py +1 -1
  60. ert/gui/simulation/ensemble_smoother_panel.py +1 -1
  61. ert/gui/simulation/evaluate_ensemble_panel.py +1 -1
  62. ert/gui/simulation/experiment_panel.py +1 -1
  63. ert/gui/simulation/manual_update_panel.py +31 -8
  64. ert/gui/simulation/multiple_data_assimilation_panel.py +12 -8
  65. ert/gui/simulation/run_dialog.py +25 -4
  66. ert/gui/simulation/single_test_run_panel.py +2 -2
  67. ert/gui/summarypanel.py +1 -1
  68. ert/gui/tools/load_results/load_results_panel.py +1 -1
  69. ert/gui/tools/manage_experiments/storage_info_widget.py +7 -7
  70. ert/gui/tools/manage_experiments/storage_widget.py +1 -2
  71. ert/gui/tools/plot/plot_api.py +13 -10
  72. ert/gui/tools/plot/plot_window.py +12 -0
  73. ert/gui/tools/plot/plottery/plot_config.py +2 -0
  74. ert/gui/tools/plot/plottery/plot_context.py +14 -0
  75. ert/gui/tools/plot/plottery/plots/ensemble.py +9 -2
  76. ert/gui/tools/plot/plottery/plots/statistics.py +59 -19
  77. ert/mode_definitions.py +2 -0
  78. ert/plugins/__init__.py +0 -1
  79. ert/plugins/hook_implementations/workflows/gen_data_rft_export.py +10 -2
  80. ert/plugins/hook_specifications/__init__.py +0 -2
  81. ert/plugins/hook_specifications/jobs.py +0 -9
  82. ert/plugins/plugin_manager.py +2 -33
  83. ert/resources/shell_scripts/delete_directory.py +2 -2
  84. ert/run_models/__init__.py +18 -5
  85. ert/run_models/_create_run_path.py +33 -21
  86. ert/run_models/ensemble_experiment.py +10 -4
  87. ert/run_models/ensemble_information_filter.py +8 -1
  88. ert/run_models/ensemble_smoother.py +9 -3
  89. ert/run_models/evaluate_ensemble.py +8 -6
  90. ert/run_models/event.py +7 -3
  91. ert/run_models/everest_run_model.py +155 -44
  92. ert/run_models/initial_ensemble_run_model.py +23 -22
  93. ert/run_models/manual_update.py +4 -2
  94. ert/run_models/manual_update_enif.py +37 -0
  95. ert/run_models/model_factory.py +81 -22
  96. ert/run_models/multiple_data_assimilation.py +21 -10
  97. ert/run_models/run_model.py +54 -34
  98. ert/run_models/single_test_run.py +7 -4
  99. ert/run_models/update_run_model.py +4 -2
  100. ert/runpaths.py +5 -6
  101. ert/sample_prior.py +9 -4
  102. ert/scheduler/driver.py +37 -0
  103. ert/scheduler/event.py +3 -1
  104. ert/scheduler/job.py +23 -13
  105. ert/scheduler/lsf_driver.py +6 -2
  106. ert/scheduler/openpbs_driver.py +7 -1
  107. ert/scheduler/scheduler.py +5 -0
  108. ert/scheduler/slurm_driver.py +6 -2
  109. ert/services/__init__.py +2 -2
  110. ert/services/_base_service.py +31 -15
  111. ert/services/ert_server.py +317 -0
  112. ert/shared/_doc_utils/ert_jobs.py +1 -4
  113. ert/shared/storage/connection.py +3 -3
  114. ert/shared/version.py +3 -3
  115. ert/storage/local_ensemble.py +25 -5
  116. ert/storage/local_experiment.py +6 -14
  117. ert/storage/local_storage.py +36 -30
  118. ert/storage/migration/to18.py +12 -0
  119. ert/storage/migration/to8.py +4 -4
  120. ert/substitutions.py +12 -28
  121. ert/validation/active_range.py +7 -7
  122. ert/validation/rangestring.py +16 -16
  123. {ert-17.1.9.dist-info → ert-18.0.1.dist-info}/METADATA +8 -7
  124. {ert-17.1.9.dist-info → ert-18.0.1.dist-info}/RECORD +159 -158
  125. everest/bin/config_branch_script.py +3 -6
  126. everest/bin/everconfigdump_script.py +1 -9
  127. everest/bin/everest_script.py +21 -11
  128. everest/bin/kill_script.py +2 -2
  129. everest/bin/monitor_script.py +2 -2
  130. everest/bin/utils.py +6 -3
  131. everest/config/__init__.py +4 -1
  132. everest/config/control_config.py +61 -2
  133. everest/config/control_variable_config.py +2 -1
  134. everest/config/everest_config.py +38 -16
  135. everest/config/forward_model_config.py +5 -3
  136. everest/config/install_data_config.py +7 -5
  137. everest/config/install_job_config.py +7 -3
  138. everest/config/install_template_config.py +3 -3
  139. everest/config/optimization_config.py +19 -6
  140. everest/config/output_constraint_config.py +8 -2
  141. everest/config/server_config.py +6 -49
  142. everest/config/utils.py +25 -105
  143. everest/config/validation_utils.py +10 -10
  144. everest/config_file_loader.py +13 -2
  145. everest/detached/everserver.py +7 -8
  146. everest/everest_storage.py +6 -10
  147. everest/gui/everest_client.py +0 -1
  148. everest/gui/main_window.py +2 -2
  149. everest/optimizer/everest2ropt.py +59 -32
  150. everest/optimizer/opt_model_transforms.py +12 -13
  151. everest/optimizer/utils.py +0 -29
  152. everest/strings.py +0 -5
  153. ert/config/everest_constraints_config.py +0 -95
  154. ert/services/storage_service.py +0 -127
  155. everest/config/sampler_config.py +0 -103
  156. everest/simulator/__init__.py +0 -88
  157. everest/simulator/everest_to_ert.py +0 -51
  158. /ert/gui/{suggestor → ertwidgets/suggestor}/__init__.py +0 -0
  159. /ert/gui/{suggestor → ertwidgets/suggestor}/_colors.py +0 -0
  160. /ert/gui/{suggestor → ertwidgets/suggestor}/suggestor.py +0 -0
  161. {ert-17.1.9.dist-info → ert-18.0.1.dist-info}/WHEEL +0 -0
  162. {ert-17.1.9.dist-info → ert-18.0.1.dist-info}/entry_points.txt +0 -0
  163. {ert-17.1.9.dist-info → ert-18.0.1.dist-info}/licenses/COPYING +0 -0
  164. {ert-17.1.9.dist-info → ert-18.0.1.dist-info}/top_level.txt +0 -0
@@ -1,14 +1,19 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import importlib
3
4
  import json
5
+ import logging
4
6
  from collections.abc import Iterator, Mapping, MutableMapping
5
7
  from dataclasses import field
6
8
  from pathlib import Path
7
- from typing import TYPE_CHECKING, Literal
9
+ from textwrap import dedent
10
+ from typing import TYPE_CHECKING, Any, Literal, Self
8
11
 
9
12
  import networkx as nx
10
13
  import numpy as np
11
14
  import xarray as xr
15
+ from pydantic import BaseModel, ConfigDict, Field, model_validator
16
+ from ropt.workflow import find_sampler_plugin
12
17
 
13
18
  from ert.substitutions import substitute_runpath_name
14
19
 
@@ -24,6 +29,119 @@ if TYPE_CHECKING:
24
29
  MutableDataType = MutableMapping[str, Number | MutableMapping[str, Number]]
25
30
 
26
31
 
32
+ class SamplerConfig(BaseModel):
33
+ backend: str | None = Field(
34
+ default=None,
35
+ description=dedent(
36
+ """
37
+ [Deprecated]
38
+
39
+ The correct backend will be inferred by the method. If several backends
40
+ have a method named `A`, pick a specific backend `B` by putting `B/A` in
41
+ the `method` field.
42
+ """
43
+ ),
44
+ )
45
+ method: str = Field(
46
+ default="norm",
47
+ description=dedent(
48
+ """
49
+ The sampling method or distribution used by the sampler backend.
50
+
51
+ The set of available methods depends on the sampler backend used. By
52
+ default a plugin based on `scipy.stats` is used, implementing the
53
+ following methods:
54
+
55
+ - From Probability Distributions:
56
+ - `norm`: Samples from a standard normal distribution (mean 0,
57
+ standard deviation 1).
58
+ - `truncnorm`: Samples from a truncated normal distribution
59
+ (mean 0, std. dev. 1), truncated to the range `[-1, 1]`.
60
+ - `uniform`: Samples from a uniform distribution in the range
61
+ `[-1, 1]`.
62
+
63
+ - From Quasi-Monte Carlo Sequences:
64
+ - `sobol`: Uses Sobol' sequences.
65
+ - `halton`: Uses Halton sequences.
66
+ - `lhs`: Uses Latin Hypercube Sampling.
67
+
68
+ Note: QMC samples are generated in the unit hypercube `[0, 1]^d`
69
+ and then scaled to the hypercube `[-1, 1]^d`.
70
+ """
71
+ ),
72
+ )
73
+ options: dict[str, Any] | None = Field(
74
+ default=None,
75
+ description=dedent(
76
+ """
77
+ Specifies a dict of optional parameters for the sampler backend.
78
+
79
+ This dict of values is passed unchanged to the selected method in
80
+ the backend.
81
+ """
82
+ ),
83
+ )
84
+ shared: bool | None = Field(
85
+ default=None,
86
+ description=dedent(
87
+ """
88
+ Whether to share perturbations between realizations.
89
+ """
90
+ ),
91
+ )
92
+ model_config = ConfigDict(extra="forbid")
93
+
94
+ @model_validator(mode="after")
95
+ def validate_backend_and_method(self) -> Self:
96
+ if self.backend is not None:
97
+ message = (
98
+ "sampler.backend is deprecated. "
99
+ "The correct backend will be inferred by the method. "
100
+ "If several backends have a method named A, you need to pick "
101
+ "a specific backend B by putting B/A in sampler.method."
102
+ )
103
+ print(message)
104
+ # Note: Importing EVEREST.everest
105
+ # leads to circular import, but we still wish to log
106
+ # from "everest" here as per old behavior.
107
+ # Can consider logging this as if from ERT,
108
+ # which is valid if we store SamplerConfig as part of
109
+ # ExtParam configs.
110
+ logging.getLogger("everest").warning(message)
111
+
112
+ # Update the default for backends that are not scipy:
113
+ if (
114
+ self.backend not in {None, "scipy"}
115
+ and "method" not in self.model_fields_set
116
+ ):
117
+ self.method = "default"
118
+
119
+ if self.backend is not None:
120
+ self.method = f"{self.backend}/{self.method}"
121
+
122
+ try:
123
+ plugin = find_sampler_plugin(f"{self.method}")
124
+ except ValueError:
125
+ raise
126
+ except Exception as exc:
127
+ ert_version = importlib.metadata.version("ert")
128
+ ropt_version = importlib.metadata.version("ropt")
129
+ msg = (
130
+ f"Error while initializing ropt:\n\n{exc}.\n\n"
131
+ "There may a be version mismatch between "
132
+ f"ERT ({ert_version}) and ropt ({ropt_version})\n"
133
+ "If the installation is correct, please report this as a bug."
134
+ )
135
+ raise RuntimeError(msg) from exc
136
+
137
+ if plugin is None:
138
+ raise ValueError(f"Sampler method '{self.method}' not found")
139
+
140
+ self.backend = None
141
+
142
+ return self
143
+
144
+
27
145
  class ExtParamConfig(ParameterConfig):
28
146
  """Create an ExtParamConfig for @key with the given @input_keys
29
147
 
@@ -46,6 +164,20 @@ class ExtParamConfig(ParameterConfig):
46
164
  output_file: str = ""
47
165
  forward_init_file: str = ""
48
166
  update: bool = False
167
+ types: list[Literal["well_control", "generic_control"]]
168
+ initial_guesses: list[float]
169
+ control_types: list[Literal["real", "integer"]]
170
+ enabled: list[bool]
171
+ min: list[float]
172
+ max: list[float]
173
+ perturbation_types: list[Literal["absolute", "relative"]]
174
+ perturbation_magnitudes: list[float]
175
+ scaled_ranges: list[tuple[float, float]]
176
+ samplers: list[SamplerConfig | None]
177
+
178
+ # Set up for deprecation, but has to live here until support for the
179
+ # "dotdash" notation is removed for everest controls via everest config.
180
+ input_keys_dotdash: list[str] = field(default_factory=list)
49
181
 
50
182
  def read_from_runpath(
51
183
  self, run_path: Path, real_nr: int, iteration: int
ert/config/field.py CHANGED
@@ -3,14 +3,14 @@ from __future__ import annotations
3
3
  import itertools
4
4
  import logging
5
5
  import os
6
- from collections.abc import Iterator
6
+ from collections.abc import Callable, Iterator
7
7
  from pathlib import Path
8
- from typing import TYPE_CHECKING, Any, Literal, Self, cast, overload
8
+ from typing import TYPE_CHECKING, Any, Final, Literal, Self, cast, overload
9
9
 
10
10
  import networkx as nx
11
11
  import numpy as np
12
12
  import xarray as xr
13
- import xtgeo # type: ignore
13
+ import xtgeo
14
14
  from pydantic import field_serializer
15
15
 
16
16
  from ert.field_utils import (
@@ -258,10 +258,14 @@ class Field(ParameterConfig):
258
258
  from_data: npt.NDArray[np.float64],
259
259
  iens_active_index: npt.NDArray[np.int_],
260
260
  ) -> Iterator[tuple[int, xr.Dataset]]:
261
- nx, ny, nz = self.ertbox_params.nx, self.ertbox_params.ny, self.ertbox_params.nz
261
+ dim_nx, dim_ny, dim_nz = (
262
+ self.ertbox_params.nx,
263
+ self.ertbox_params.ny,
264
+ self.ertbox_params.nz,
265
+ )
262
266
 
263
267
  for i, realization in enumerate(iens_active_index):
264
- values = from_data[:, i].reshape((nx, ny, nz))
268
+ values = from_data[:, i].reshape((dim_nx, dim_ny, dim_nz))
265
269
  ds = xr.Dataset({"values": (["x", "y", "z"], values)})
266
270
  yield int(realization), ds
267
271
 
@@ -299,7 +303,7 @@ class Field(ParameterConfig):
299
303
  fill_value=np.nan,
300
304
  )
301
305
 
302
- def load_parameter_graph(self) -> nx.Graph: # type: ignore
306
+ def load_parameter_graph(self) -> nx.Graph[int]:
303
307
  parameter_graph = create_flattened_cube_graph(
304
308
  px=self.ertbox_params.nx, py=self.ertbox_params.ny, pz=self.ertbox_params.nz
305
309
  )
@@ -322,7 +326,7 @@ class Field(ParameterConfig):
322
326
  return self.ertbox_params.nz
323
327
 
324
328
 
325
- TRANSFORM_FUNCTIONS = {
329
+ TRANSFORM_FUNCTIONS: Final[dict[str, Callable[[Any], Any]]] = {
326
330
  "LN": np.log,
327
331
  "LOG": np.log,
328
332
  "LN0": lambda v: np.log(v + 0.000001),
@@ -353,7 +357,7 @@ def field_transform(
353
357
  ) -> npt.NDArray[np.float32] | xr.DataArray:
354
358
  if transform_name is None:
355
359
  return data
356
- return TRANSFORM_FUNCTIONS[transform_name](data) # type: ignore
360
+ return TRANSFORM_FUNCTIONS[transform_name](data)
357
361
 
358
362
 
359
363
  def _field_truncate(data: npt.ArrayLike, min_: float | None, max_: float | None) -> Any:
@@ -1,13 +1,33 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
- from typing import ClassVar, Literal, NotRequired, Self
5
-
6
- from pydantic import BaseModel, Field, field_validator, model_validator
4
+ from typing import (
5
+ TYPE_CHECKING,
6
+ Annotated,
7
+ Any,
8
+ ClassVar,
9
+ Literal,
10
+ NotRequired,
11
+ Self,
12
+ cast,
13
+ )
14
+
15
+ from pydantic import (
16
+ BaseModel,
17
+ Field,
18
+ field_validator,
19
+ model_serializer,
20
+ model_validator,
21
+ )
22
+ from pydantic_core.core_schema import ValidationInfo
7
23
  from typing_extensions import TypedDict, Unpack
8
24
 
25
+ from ..base_model_context import BaseModelWithContextSupport
9
26
  from .parsing import ConfigValidationError, ConfigWarning, SchemaItemType
10
27
 
28
+ if TYPE_CHECKING:
29
+ from ert.plugins import ErtRuntimePlugins
30
+
11
31
  logger = logging.getLogger(__name__)
12
32
 
13
33
 
@@ -72,6 +92,7 @@ class ForwardModelStepOptions(TypedDict, total=False):
72
92
  max_running_minutes: NotRequired[int]
73
93
  environment: NotRequired[dict[str, str]]
74
94
  default_mapping: NotRequired[dict[str, str]]
95
+ required_keywords: NotRequired[list[str]]
75
96
 
76
97
 
77
98
  class ForwardModelStepDocumentation(BaseModel):
@@ -91,7 +112,7 @@ class ForwardModelStepDocumentation(BaseModel):
91
112
  ) = Field(default="Uncategorized")
92
113
 
93
114
 
94
- class ForwardModelStep(BaseModel):
115
+ class ForwardModelStep(BaseModelWithContextSupport):
95
116
  """
96
117
  Holds information to execute one step of a forward model
97
118
 
@@ -202,7 +223,87 @@ class ForwardModelStep(BaseModel):
202
223
  return None if v == "null" else v
203
224
 
204
225
 
205
- class ForwardModelStepPlugin(ForwardModelStep):
226
+ class UserInstalledForwardModelStep(ForwardModelStep):
227
+ """
228
+ Represents a forward model step installed by a user via the ERT Config
229
+ forward model step format provided via the INSTALL_JOB keyword.
230
+ User-installed forward model steps serialize with their full configuration,
231
+ unlike site-installed steps which only serialize as references.
232
+ """
233
+
234
+ type: Literal["user_installed"] = "user_installed"
235
+
236
+
237
+ class _SerializedSiteInstalledForwardModelStep(TypedDict):
238
+ type: Literal["site_installed"]
239
+ name: str
240
+ private_args: dict[str, str]
241
+
242
+
243
+ class SiteInstalledForwardModelStep(ForwardModelStep):
244
+ """
245
+ Represents a forward model step installed via external plugins.
246
+ Instances of this class serialize only as references to the plugin by name, and
247
+ the user-provided private_args, allowing them to dynamically update when plugins
248
+ change, rather than being locked to a specific executable at serialization time.
249
+ """
250
+
251
+ type: Literal["site_installed"] = "site_installed"
252
+
253
+ @model_serializer(mode="plain")
254
+ def serialize_model(self) -> _SerializedSiteInstalledForwardModelStep:
255
+ return {
256
+ "type": "site_installed",
257
+ "name": self.name,
258
+ "private_args": self.private_args,
259
+ }
260
+
261
+ @model_validator(mode="before")
262
+ @classmethod
263
+ def deserialize_model(
264
+ cls, values: dict[str, Any], info: ValidationInfo
265
+ ) -> dict[str, Any]:
266
+ runtime_plugins = cast("ErtRuntimePlugins", info.context)
267
+ name = values["name"]
268
+
269
+ if runtime_plugins is None:
270
+ if values.get("type") == "site_installed":
271
+ msg = (
272
+ f"Trying to find site-installed forward model step {name} "
273
+ f"without site plugins. This forward model must be loaded "
274
+ f"with ERT site plugins available."
275
+ )
276
+ raise KeyError(msg)
277
+ return values
278
+
279
+ if name not in runtime_plugins.installed_forward_model_steps:
280
+ msg = (
281
+ f"Expected forward model step {name} to be installed "
282
+ f"via plugins, but it was not found. Please check that "
283
+ f"your python environment has it installed."
284
+ )
285
+ raise KeyError(msg)
286
+ site_installed_fm = runtime_plugins.installed_forward_model_steps[name]
287
+
288
+ # Intent: copy the site installed forward model to this instance.
289
+ # bypassing the model_serializer
290
+ site_fm_instance = {
291
+ k: getattr(site_installed_fm, k)
292
+ for k in SiteInstalledForwardModelStep.model_fields
293
+ }
294
+
295
+ return site_fm_instance | (
296
+ {"private_args": values["private_args"]} if "private_args" in values else {}
297
+ )
298
+
299
+
300
+ SiteOrUserForwardModelStep = Annotated[
301
+ (UserInstalledForwardModelStep | SiteInstalledForwardModelStep),
302
+ Field(discriminator="type"),
303
+ ]
304
+
305
+
306
+ class ForwardModelStepPlugin(SiteInstalledForwardModelStep):
206
307
  def __init__(
207
308
  self, name: str, command: list[str], **kwargs: Unpack[ForwardModelStepOptions]
208
309
  ) -> None:
@@ -221,6 +322,7 @@ class ForwardModelStepPlugin(ForwardModelStep):
221
322
  max_running_minutes = kwargs.get("max_running_minutes")
222
323
  environment = kwargs.get("environment", {}) or {}
223
324
  default_mapping = kwargs.get("default_mapping", {}) or {}
325
+ required_keywords = kwargs.get("required_keywords", []) or []
224
326
 
225
327
  super().__init__(
226
328
  name=name,
@@ -235,7 +337,7 @@ class ForwardModelStepPlugin(ForwardModelStep):
235
337
  max_running_minutes=max_running_minutes,
236
338
  min_arg=0,
237
339
  max_arg=0,
238
- required_keywords=[],
340
+ required_keywords=required_keywords,
239
341
  arg_types=[],
240
342
  environment=environment,
241
343
  default_mapping=default_mapping,
@@ -210,16 +210,12 @@ class GenDataConfig(ResponseConfig):
210
210
  return combined
211
211
 
212
212
  def get_args_for_key(self, key: str) -> tuple[str | None, list[int] | None]:
213
- for i, _key in enumerate(self.keys):
214
- if key == _key:
213
+ for i, key_ in enumerate(self.keys):
214
+ if key == key_:
215
215
  return self.input_files[i], self.report_steps_list[i]
216
216
 
217
217
  return None, None
218
218
 
219
- @property
220
- def response_type(self) -> str:
221
- return "gen_data"
222
-
223
219
  @property
224
220
  def primary_key(self) -> list[str]:
225
221
  return ["report_step", "index"]
@@ -248,15 +248,6 @@ class GenKwConfig(ParameterConfig):
248
248
  def group_name(self) -> str:
249
249
  return self.group
250
250
 
251
- def copy_parameters(
252
- self,
253
- source_ensemble: Ensemble,
254
- target_ensemble: Ensemble,
255
- realizations: npt.NDArray[np.int_],
256
- ) -> None:
257
- df = source_ensemble.load_parameters(self.name, realizations)
258
- target_ensemble.save_parameters(dataset=df)
259
-
260
251
  def get_priors(self) -> list[PriorDict]:
261
252
  dist_json = self.distribution.model_dump(exclude={"name"})
262
253
  return [
@@ -0,0 +1,14 @@
1
+ from .everest_response import EverestConstraintsConfig, EverestObjectivesConfig
2
+ from .gen_data_config import GenDataConfig
3
+ from .rft_config import RFTConfig
4
+ from .summary_config import SummaryConfig
5
+
6
+ KnownErtResponseTypes = SummaryConfig | GenDataConfig | RFTConfig
7
+ KNOWN_ERT_RESPONSE_TYPES = (
8
+ SummaryConfig,
9
+ GenDataConfig,
10
+ RFTConfig,
11
+ )
12
+ KnownResponseTypes = (
13
+ KnownErtResponseTypes | EverestConstraintsConfig | EverestObjectivesConfig
14
+ )
@@ -108,23 +108,6 @@ class ParameterConfig(BaseModel):
108
108
  or polars DataFrame from the numpy data
109
109
  """
110
110
 
111
- def copy_parameters(
112
- self,
113
- source_ensemble: Ensemble,
114
- target_ensemble: Ensemble,
115
- realizations: npt.NDArray[np.int_],
116
- ) -> None:
117
- """
118
- Copy parameters from one ensemble to another.
119
- If realizations is None, copy all realizations.
120
- If realizations is given, copy only those realizations.
121
- """
122
- for realization in realizations:
123
- # Converts to standard python scalar due to mypy
124
- realization_int = int(realization)
125
- ds = source_ensemble.load_parameters(self.name, realization_int)
126
- target_ensemble.save_parameters(ds, self.name, realization_int)
127
-
128
111
  @abstractmethod
129
112
  def load_parameters(
130
113
  self, ensemble: Ensemble, realizations: npt.NDArray[np.int_]
@@ -42,6 +42,7 @@ class ConfigKeys(StrEnum):
42
42
  SETENV = "SETENV"
43
43
  STD_CUTOFF = "STD_CUTOFF"
44
44
  SUMMARY = "SUMMARY"
45
+ RFT = "RFT"
45
46
  SURFACE = "SURFACE"
46
47
  UPDATE_LOG_PATH = "UPDATE_LOG_PATH"
47
48
  RANDOM_SEED = "RANDOM_SEED"
@@ -63,6 +63,17 @@ def data_kw_keyword() -> SchemaItem:
63
63
  )
64
64
 
65
65
 
66
+ def rft_keyword() -> SchemaItem:
67
+ return SchemaItem(
68
+ kw=ConfigKeys.RFT,
69
+ required_set=False,
70
+ multi_occurrence=True,
71
+ options_after=0,
72
+ argc_min=1,
73
+ argc_max=1,
74
+ )
75
+
76
+
66
77
  def define_keyword() -> SchemaItem:
67
78
  return SchemaItem(
68
79
  kw=ConfigKeys.DEFINE,
@@ -303,6 +314,7 @@ def init_user_config_schema() -> ConfigSchemaDict:
303
314
  gen_kw_keyword(),
304
315
  gen_data_keyword(),
305
316
  summary_keyword(),
317
+ rft_keyword(),
306
318
  surface_keyword(),
307
319
  field_keyword(),
308
320
  single_arg_keyword(ConfigKeys.ECLBASE),
@@ -217,4 +217,15 @@ deprecated_keywords_list = [
217
217
  ),
218
218
  check=lambda line: line[0] == "DESIGN2PARAMS",
219
219
  ),
220
+ DeprecationInfo(
221
+ keyword="FORWARD_MODEL",
222
+ message=(
223
+ "FORWARD_MODEL DESIGN_KW will be replaced with RUN_TEMPLATE. "
224
+ "DESIGN2PARAMS has been replaced by DESIGN_MATRIX, so the "
225
+ "parameters are already available for magic string replacement "
226
+ "with the RUN_TEMPLATE keyword. Please use this format: "
227
+ "'RUN_TEMPLATE my_text_file_template.txt my_text_output_file.txt'"
228
+ ),
229
+ check=lambda line: line[0] == "DESIGN_KW",
230
+ ),
220
231
  ]
@@ -48,7 +48,7 @@ class SchemaItem:
48
48
  # if positive, arguments after this count will be concatenated with a " " between
49
49
  join_after: PositiveInt | None = None
50
50
  # if positive, arguments after this count will be interpreted as options
51
- options_after: PositiveInt | Varies | None = None
51
+ options_after: NonNegativeInt | Varies | None = None
52
52
  # if true, will accumulate many values set for key, otherwise each entry will
53
53
  # overwrite any previous value set
54
54
  multi_occurrence: bool = False
@@ -478,11 +478,11 @@ class QueueConfig(BaseModelWithContextSupport):
478
478
 
479
479
  # validate all queue options for the unselected queues
480
480
  # and show a warning
481
- for _queue_system in QueueSystem:
482
- if _queue_system != selected_queue_system:
481
+ for queue_system in QueueSystem:
482
+ if queue_system != selected_queue_system:
483
483
  _ = QueueOptions.create_queue_options(
484
- _queue_system,
485
- grouped_queue_options[_queue_system],
484
+ queue_system,
485
+ grouped_queue_options[queue_system],
486
486
  False,
487
487
  )
488
488
 
@@ -60,13 +60,6 @@ class ResponseConfig(BaseModel):
60
60
  def expected_input_files(self) -> list[str]:
61
61
  """Returns a list of filenames expected to be produced by the forward model"""
62
62
 
63
- @property
64
- @abstractmethod
65
- def response_type(self) -> str:
66
- """Label to identify what kind of response it is.
67
- Must not overlap with that of other response configs."""
68
- ...
69
-
70
63
  @property
71
64
  @abstractmethod
72
65
  def primary_key(self) -> list[str]: