ert 19.0.0rc1__py3-none-any.whl → 19.0.0rc3__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 (48) hide show
  1. ert/__main__.py +63 -94
  2. ert/analysis/_es_update.py +14 -11
  3. ert/config/_create_observation_dataframes.py +262 -23
  4. ert/config/_observations.py +153 -181
  5. ert/config/_read_summary.py +5 -4
  6. ert/config/ert_config.py +56 -1
  7. ert/config/parsing/observations_parser.py +0 -6
  8. ert/config/rft_config.py +1 -1
  9. ert/dark_storage/compute/__init__.py +0 -0
  10. ert/dark_storage/compute/misfits.py +42 -0
  11. ert/dark_storage/endpoints/__init__.py +2 -0
  12. ert/dark_storage/endpoints/compute/__init__.py +0 -0
  13. ert/dark_storage/endpoints/compute/misfits.py +95 -0
  14. ert/dark_storage/endpoints/experiments.py +3 -0
  15. ert/dark_storage/json_schema/experiment.py +1 -0
  16. ert/gui/main_window.py +0 -2
  17. ert/gui/tools/manage_experiments/export_dialog.py +0 -4
  18. ert/gui/tools/manage_experiments/storage_info_widget.py +5 -1
  19. ert/gui/tools/plot/plot_api.py +10 -10
  20. ert/gui/tools/plot/plot_widget.py +0 -5
  21. ert/gui/tools/plot/plot_window.py +1 -1
  22. ert/services/__init__.py +3 -7
  23. ert/services/_base_service.py +387 -0
  24. ert/services/_storage_main.py +22 -59
  25. ert/services/ert_server.py +24 -186
  26. ert/services/webviz_ert_service.py +20 -0
  27. ert/shared/storage/command.py +38 -0
  28. ert/shared/storage/extraction.py +42 -0
  29. ert/shared/version.py +3 -3
  30. ert/storage/local_ensemble.py +95 -2
  31. ert/storage/local_experiment.py +16 -0
  32. ert/storage/local_storage.py +1 -3
  33. ert/utils/__init__.py +0 -20
  34. {ert-19.0.0rc1.dist-info → ert-19.0.0rc3.dist-info}/METADATA +2 -2
  35. {ert-19.0.0rc1.dist-info → ert-19.0.0rc3.dist-info}/RECORD +46 -41
  36. {ert-19.0.0rc1.dist-info → ert-19.0.0rc3.dist-info}/WHEEL +1 -1
  37. everest/bin/everest_script.py +5 -5
  38. everest/bin/kill_script.py +2 -2
  39. everest/bin/monitor_script.py +2 -2
  40. everest/bin/utils.py +4 -4
  41. everest/detached/everserver.py +6 -6
  42. everest/gui/main_window.py +2 -2
  43. everest/util/__init__.py +19 -1
  44. ert/config/observation_config_migrations.py +0 -793
  45. ert/storage/migration/to22.py +0 -18
  46. {ert-19.0.0rc1.dist-info → ert-19.0.0rc3.dist-info}/entry_points.txt +0 -0
  47. {ert-19.0.0rc1.dist-info → ert-19.0.0rc3.dist-info}/licenses/COPYING +0 -0
  48. {ert-19.0.0rc1.dist-info → ert-19.0.0rc3.dist-info}/top_level.txt +0 -0
@@ -1,14 +1,10 @@
1
- from __future__ import annotations
2
-
3
- import logging
4
1
  import os
5
2
  from collections.abc import Sequence
6
3
  from dataclasses import dataclass
7
4
  from enum import StrEnum
5
+ from itertools import starmap
8
6
  from typing import Any, Self
9
7
 
10
- import pandas as pd
11
-
12
8
  from .parsing import (
13
9
  ErrorInfo,
14
10
  ObservationConfigError,
@@ -16,8 +12,6 @@ from .parsing import (
16
12
  ObservationType,
17
13
  )
18
14
 
19
- logger = logging.getLogger(__name__)
20
-
21
15
 
22
16
  class ErrorModes(StrEnum):
23
17
  REL = "REL"
@@ -25,52 +19,102 @@ class ErrorModes(StrEnum):
25
19
  RELMIN = "RELMIN"
26
20
 
27
21
 
22
+ @dataclass
23
+ class ObservationError:
24
+ error_mode: ErrorModes
25
+ error: float
26
+ error_min: float
27
+
28
+
29
+ @dataclass
30
+ class Segment(ObservationError):
31
+ name: str
32
+ start: int
33
+ stop: int
34
+
35
+
36
+ @dataclass
37
+ class HistoryObservation(ObservationError):
38
+ name: str
39
+ segments: list[Segment]
40
+
41
+ @property
42
+ def key(self) -> str:
43
+ """The :term:`summary key` to be fetched from :ref:`refcase`."""
44
+ # For history observations the key is also the name, ie.
45
+ # "HISTORY_OBSERVATION FOPR" means to add the values from
46
+ # the summary vector FOPRH in refcase as observations.
47
+ return self.name
48
+
49
+ @classmethod
50
+ def from_obs_dict(cls, directory: str, observation_dict: ObservationDict) -> Self:
51
+ error_mode = ErrorModes.RELMIN
52
+ error = 0.1
53
+ error_min = 0.1
54
+ segments = []
55
+ for key, value in observation_dict.items():
56
+ match key:
57
+ case "type" | "name":
58
+ pass
59
+ case "ERROR":
60
+ error = validate_positive_float(value, key)
61
+ case "ERROR_MIN":
62
+ error_min = validate_positive_float(value, key)
63
+ case "ERROR_MODE":
64
+ error_mode = validate_error_mode(value)
65
+ case "segments":
66
+ segments = list(starmap(_validate_segment_dict, value))
67
+ case _:
68
+ raise _unknown_key_error(str(key), observation_dict["name"])
69
+
70
+ return cls(
71
+ name=observation_dict["name"],
72
+ error_mode=error_mode,
73
+ error=error,
74
+ error_min=error_min,
75
+ segments=segments,
76
+ )
77
+
78
+
79
+ @dataclass
80
+ class ObservationDate:
81
+ days: float | None = None
82
+ hours: float | None = None
83
+ date: str | None = None
84
+ restart: int | None = None
85
+
86
+
28
87
  @dataclass
29
88
  class _SummaryValues:
30
89
  name: str
31
90
  value: float
32
91
  key: str #: The :term:`summary key` in the summary response
33
- date: str
34
92
  location_x: float | None = None
35
93
  location_y: float | None = None
36
94
  location_range: float | None = None
37
95
 
38
96
 
39
97
  @dataclass
40
- class ObservationError:
41
- error_mode: ErrorModes
42
- error: float
43
- error_min: float
44
-
45
-
46
- @dataclass
47
- class SummaryObservation(_SummaryValues, ObservationError):
98
+ class SummaryObservation(ObservationDate, _SummaryValues, ObservationError):
48
99
  @classmethod
49
- def from_obs_dict(
50
- cls, directory: str, observation_dict: ObservationDict
51
- ) -> list[Self]:
100
+ def from_obs_dict(cls, directory: str, observation_dict: ObservationDict) -> Self:
52
101
  error_mode = ErrorModes.ABS
53
102
  summary_key = None
54
103
 
55
- date: str | None = None
104
+ date_dict: ObservationDate = ObservationDate()
56
105
  float_values: dict[str, float] = {"ERROR_MIN": 0.1}
57
- localization_values: dict[str, float | None] = {}
106
+ localization_values: dict[str, float] = {}
58
107
  for key, value in observation_dict.items():
59
108
  match key:
60
109
  case "type" | "name":
61
110
  pass
111
+ case "RESTART":
112
+ date_dict.restart = validate_positive_int(value, key)
62
113
  case "ERROR" | "ERROR_MIN":
63
114
  float_values[str(key)] = validate_positive_float(value, key)
64
- case "DAYS" | "HOURS" | "RESTART":
65
- raise ObservationConfigError.with_context(
66
- (
67
- "SUMMARY_OBSERVATION must use DATE to specify "
68
- "date, DAYS | HOURS is no longer allowed. "
69
- "Please run:\n ert convert_observations "
70
- "<your_ert_config.ert>\nto migrate the observation config "
71
- "to use the correct format."
72
- ),
73
- key,
115
+ case "DAYS" | "HOURS":
116
+ setattr(
117
+ date_dict, str(key).lower(), validate_positive_float(value, key)
74
118
  )
75
119
  case "VALUE":
76
120
  float_values[str(key)] = validate_float(value, key)
@@ -79,16 +123,13 @@ class SummaryObservation(_SummaryValues, ObservationError):
79
123
  case "KEY":
80
124
  summary_key = value
81
125
  case "DATE":
82
- date = value
83
- case "LOCALIZATION":
84
- validate_localization(value, observation_dict["name"])
85
- localization_values["x"] = validate_float(value["EAST"], key)
86
- localization_values["y"] = validate_float(value["NORTH"], key)
87
- localization_values["range"] = (
88
- validate_float(value["RADIUS"], key)
89
- if "RADIUS" in value
90
- else None
91
- )
126
+ date_dict.date = value
127
+ case "LOCATION_X":
128
+ localization_values["x"] = validate_float(value, key)
129
+ case "LOCATION_Y":
130
+ localization_values["y"] = validate_float(value, key)
131
+ case "LOCATION_RANGE":
132
+ localization_values["range"] = validate_float(value, key)
92
133
  case _:
93
134
  raise _unknown_key_error(str(key), observation_dict["name"])
94
135
  if "VALUE" not in float_values:
@@ -98,21 +139,18 @@ class SummaryObservation(_SummaryValues, ObservationError):
98
139
  if "ERROR" not in float_values:
99
140
  raise _missing_value_error(observation_dict["name"], "ERROR")
100
141
 
101
- assert date is not None
102
- return [
103
- cls(
104
- name=observation_dict["name"],
105
- error_mode=error_mode,
106
- error=float_values["ERROR"],
107
- error_min=float_values["ERROR_MIN"],
108
- key=summary_key,
109
- value=float_values["VALUE"],
110
- location_x=localization_values.get("x"),
111
- location_y=localization_values.get("y"),
112
- location_range=localization_values.get("range"),
113
- date=date,
114
- )
115
- ]
142
+ return cls(
143
+ name=observation_dict["name"],
144
+ error_mode=error_mode,
145
+ error=float_values["ERROR"],
146
+ error_min=float_values["ERROR_MIN"],
147
+ key=summary_key,
148
+ value=float_values["VALUE"],
149
+ location_x=localization_values.get("x"),
150
+ location_y=localization_values.get("y"),
151
+ location_range=localization_values.get("range"),
152
+ **date_dict.__dict__,
153
+ )
116
154
 
117
155
 
118
156
  @dataclass
@@ -124,15 +162,12 @@ class _GeneralObservation:
124
162
  index_list: str | None = None
125
163
  index_file: str | None = None
126
164
  obs_file: str | None = None
127
- restart: int | None = None
128
165
 
129
166
 
130
167
  @dataclass
131
- class GeneralObservation(_GeneralObservation):
168
+ class GeneralObservation(ObservationDate, _GeneralObservation):
132
169
  @classmethod
133
- def from_obs_dict(
134
- cls, directory: str, observation_dict: ObservationDict
135
- ) -> list[Self]:
170
+ def from_obs_dict(cls, directory: str, observation_dict: ObservationDict) -> Self:
136
171
  try:
137
172
  data = observation_dict["DATA"]
138
173
  except KeyError as err:
@@ -147,22 +182,12 @@ class GeneralObservation(_GeneralObservation):
147
182
  output.restart = validate_positive_int(value, key)
148
183
  case "VALUE":
149
184
  output.value = validate_float(value, key)
150
- case "ERROR":
185
+ case "ERROR" | "DAYS" | "HOURS":
151
186
  setattr(
152
187
  output, str(key).lower(), validate_positive_float(value, key)
153
188
  )
154
- case "DATE" | "DAYS" | "HOURS":
155
- raise ObservationConfigError.with_context(
156
- (
157
- "GENERAL_OBSERVATION must use RESTART to specify "
158
- "report step. Please run:\n ert convert_observations "
159
- "<your_ert_config.ert>\nto migrate the observation config "
160
- "to use the correct format."
161
- ),
162
- key,
163
- )
164
- case "INDEX_LIST":
165
- output.index_list = value
189
+ case "DATE" | "INDEX_LIST":
190
+ setattr(output, str(key).lower(), value)
166
191
  case "OBS_FILE" | "INDEX_FILE":
167
192
  assert not isinstance(key, tuple)
168
193
  filename = value
@@ -185,8 +210,7 @@ class GeneralObservation(_GeneralObservation):
185
210
  f" VALUE = {output.value}, ERROR must also be given.",
186
211
  observation_dict["name"],
187
212
  )
188
-
189
- return [output]
213
+ return output
190
214
 
191
215
 
192
216
  @dataclass
@@ -202,63 +226,7 @@ class RFTObservation:
202
226
  tvd: float
203
227
 
204
228
  @classmethod
205
- def from_csv(
206
- cls,
207
- directory: str,
208
- observation_dict: ObservationDict,
209
- filename: str,
210
- observed_property: str = "PRESSURE",
211
- ) -> list[Self]:
212
- if not os.path.isabs(filename):
213
- filename = os.path.join(directory, filename)
214
- if not os.path.exists(filename):
215
- raise ObservationConfigError.with_context(
216
- f"The CSV file ({filename}) does not exist or is not accessible.",
217
- filename,
218
- )
219
- csv_file = pd.read_csv(
220
- filename,
221
- encoding="utf-8",
222
- on_bad_lines="error",
223
- )
224
-
225
- required_columns = {
226
- "WELL_NAME",
227
- "DATE",
228
- observed_property,
229
- "ERROR",
230
- "NORTH",
231
- "EAST",
232
- "TVD",
233
- }
234
- missing_required_columns = required_columns - set(csv_file.keys())
235
- if missing_required_columns:
236
- raise ObservationConfigError.with_context(
237
- f"The rft observations file {filename} is missing required column(s) "
238
- f"{', '.join(sorted(missing_required_columns))}.",
239
- filename,
240
- )
241
-
242
- return [
243
- cls(
244
- f"{observation_dict['name']}[{row.Index}]",
245
- str(row.WELL_NAME),
246
- str(row.DATE),
247
- observed_property,
248
- validate_float(str(getattr(row, observed_property)), observed_property),
249
- validate_float(str(row.ERROR), "ERROR"),
250
- validate_float(str(row.NORTH), "NORTH"),
251
- validate_float(str(row.EAST), "EAST"),
252
- validate_float(str(row.TVD), "TVD"),
253
- )
254
- for row in csv_file.itertuples(index=True)
255
- ]
256
-
257
- @classmethod
258
- def from_obs_dict(
259
- cls, directory: str, observation_dict: ObservationDict
260
- ) -> list[Self]:
261
- csv_filename = None
229
+ def from_obs_dict(cls, directory: str, observation_dict: ObservationDict) -> Self:
262
230
  well = None
263
231
  observed_property = None
264
232
  observed_value = None
@@ -287,17 +255,8 @@ class RFTObservation:
287
255
  east = validate_float(value, key)
288
256
  case "TVD":
289
257
  tvd = validate_float(value, key)
290
- case "CSV":
291
- csv_filename = value
292
258
  case _:
293
259
  raise _unknown_key_error(str(key), observation_dict["name"])
294
- if csv_filename is not None:
295
- return cls.from_csv(
296
- directory,
297
- observation_dict,
298
- csv_filename,
299
- observed_property or "PRESSURE",
300
- )
301
260
  if well is None:
302
261
  raise _missing_value_error(observation_dict["name"], "WELL")
303
262
  if observed_value is None:
@@ -314,24 +273,25 @@ class RFTObservation:
314
273
  raise _missing_value_error(observation_dict["name"], "EAST")
315
274
  if tvd is None:
316
275
  raise _missing_value_error(observation_dict["name"], "TVD")
317
- return [
318
- cls(
319
- observation_dict["name"],
320
- well,
321
- date,
322
- observed_property,
323
- observed_value,
324
- error,
325
- north,
326
- east,
327
- tvd,
328
- )
329
- ]
276
+ return cls(
277
+ observation_dict["name"],
278
+ well,
279
+ date,
280
+ observed_property,
281
+ observed_value,
282
+ error,
283
+ north,
284
+ east,
285
+ tvd,
286
+ )
330
287
 
331
288
 
332
- Observation = SummaryObservation | GeneralObservation | RFTObservation
289
+ Observation = (
290
+ HistoryObservation | SummaryObservation | GeneralObservation | RFTObservation
291
+ )
333
292
 
334
293
  _TYPE_TO_CLASS: dict[ObservationType, type[Observation]] = {
294
+ ObservationType.HISTORY: HistoryObservation,
335
295
  ObservationType.SUMMARY: SummaryObservation,
336
296
  ObservationType.GENERAL: GeneralObservation,
337
297
  ObservationType.RFT: RFTObservation,
@@ -349,20 +309,10 @@ def make_observations(
349
309
  inp: The collection of statements to validate.
350
310
  """
351
311
  result: list[Observation] = []
352
- error_list: list[ErrorInfo | ObservationConfigError] = []
312
+ error_list: list[ErrorInfo] = []
353
313
  for obs_dict in observation_dicts:
354
- if obs_dict["type"] == ObservationType.HISTORY:
355
- msg = (
356
- "HISTORY_OBSERVATION is deprecated, and must be specified "
357
- "as SUMMARY_OBSERVATION. Run"
358
- " ert convert_observations <ert_config.ert> to convert your "
359
- "observations automatically"
360
- )
361
- logger.error(msg)
362
- error_list.append(ObservationConfigError(msg))
363
- continue
364
314
  try:
365
- result.extend(
315
+ result.append(
366
316
  _TYPE_TO_CLASS[obs_dict["type"]].from_obs_dict(directory, obs_dict)
367
317
  )
368
318
  except KeyError as err:
@@ -376,6 +326,41 @@ def make_observations(
376
326
  return result
377
327
 
378
328
 
329
+ def _validate_segment_dict(name_token: str, inp: dict[str, Any]) -> Segment:
330
+ start = None
331
+ stop = None
332
+ error_mode = ErrorModes.RELMIN
333
+ error = 0.1
334
+ error_min = 0.1
335
+ for key, value in inp.items():
336
+ match key:
337
+ case "START":
338
+ start = validate_int(value, key)
339
+ case "STOP":
340
+ stop = validate_int(value, key)
341
+ case "ERROR":
342
+ error = validate_positive_float(value, key)
343
+ case "ERROR_MIN":
344
+ error_min = validate_positive_float(value, key)
345
+ case "ERROR_MODE":
346
+ error_mode = validate_error_mode(value)
347
+ case _:
348
+ raise _unknown_key_error(key, name_token)
349
+
350
+ if start is None:
351
+ raise _missing_value_error(name_token, "START")
352
+ if stop is None:
353
+ raise _missing_value_error(name_token, "STOP")
354
+ return Segment(
355
+ name=name_token,
356
+ start=start,
357
+ stop=stop,
358
+ error_mode=error_mode,
359
+ error=error,
360
+ error_min=error_min,
361
+ )
362
+
363
+
379
364
  def validate_error_mode(inp: str) -> ErrorModes:
380
365
  if inp == "REL":
381
366
  return ErrorModes.REL
@@ -413,19 +398,6 @@ def validate_positive_float(val: str, key: str) -> float:
413
398
  return v
414
399
 
415
400
 
416
- def validate_localization(val: dict[str, Any], obs_name: str) -> None:
417
- errors = []
418
- if "EAST" not in val:
419
- errors.append(_missing_value_error(f"LOCALIZATION for {obs_name}", "EAST"))
420
- if "NORTH" not in val:
421
- errors.append(_missing_value_error(f"LOCALIZATION for {obs_name}", "NORTH"))
422
- for key in val:
423
- if key not in {"EAST", "NORTH", "RADIUS"}:
424
- errors.append(_unknown_key_error(key, f"LOCALIZATION for {obs_name}"))
425
- if errors:
426
- raise ObservationConfigError.from_collected(errors)
427
-
428
-
429
401
  def validate_positive_int(val: str, key: str) -> int:
430
402
  try:
431
403
  v = int(val)
@@ -8,6 +8,7 @@ from __future__ import annotations
8
8
 
9
9
  import fnmatch
10
10
  import re
11
+ import warnings
11
12
  from collections.abc import Callable, Sequence
12
13
  from datetime import datetime, timedelta
13
14
  from enum import Enum, auto
@@ -157,10 +158,10 @@ def _read_spec(
157
158
  if kw.summary_variable == "TIME":
158
159
  date_index = i
159
160
  date_unit_str = kw.unit
160
- except InvalidSummaryKeyError:
161
- # InvalidSummaryKeyError will happen under normal conditions when
162
- # the the number of wells set for WELLDIMS in the .DATA file is
163
- # larger than the number of declared wells/groups/etc. These are skipped.
161
+ except InvalidSummaryKeyError as err:
162
+ warnings.warn(
163
+ f"Found {err} in summary specification, key not loaded", stacklevel=2
164
+ )
164
165
  continue
165
166
 
166
167
  if should_load_key(key):
ert/config/ert_config.py CHANGED
@@ -7,6 +7,7 @@ import pprint
7
7
  import re
8
8
  from collections import Counter, defaultdict
9
9
  from collections.abc import Mapping
10
+ from datetime import datetime
10
11
  from functools import cached_property
11
12
  from os import path
12
13
  from pathlib import Path
@@ -22,6 +23,7 @@ from ert.substitutions import Substitutions
22
23
  from ._create_observation_dataframes import create_observation_dataframes
23
24
  from ._design_matrix_validator import DesignMatrixValidator
24
25
  from ._observations import (
26
+ HistoryObservation,
25
27
  Observation,
26
28
  RFTObservation,
27
29
  SummaryObservation,
@@ -50,6 +52,7 @@ from .parsing import (
50
52
  ConfigWarning,
51
53
  ErrorInfo,
52
54
  ForwardModelStepKeys,
55
+ HistorySource,
53
56
  HookRuntime,
54
57
  ObservationConfigError,
55
58
  init_forward_model_schema,
@@ -59,6 +62,7 @@ from .parsing import (
59
62
  )
60
63
  from .parsing.observations_parser import ObservationDict
61
64
  from .queue_config import KnownQueueOptions, QueueConfig
65
+ from .refcase import Refcase
62
66
  from .rft_config import RFTConfig
63
67
  from .workflow import Workflow
64
68
  from .workflow_fixtures import fixtures_per_hook
@@ -100,6 +104,23 @@ def _seed_sequence(seed: int | None) -> int:
100
104
  return int_seed
101
105
 
102
106
 
107
+ def _read_time_map(file_contents: str) -> list[datetime]:
108
+ def str_to_datetime(date_str: str) -> datetime:
109
+ try:
110
+ return datetime.fromisoformat(date_str)
111
+ except ValueError:
112
+ logger.warning(
113
+ "DD/MM/YYYY date format is deprecated"
114
+ ", please use ISO date format YYYY-MM-DD."
115
+ )
116
+ return datetime.strptime(date_str, "%d/%m/%Y")
117
+
118
+ dates = []
119
+ for line in file_contents.splitlines():
120
+ dates.append(str_to_datetime(line.strip()))
121
+ return dates
122
+
123
+
103
124
  def create_forward_model_json(
104
125
  context: dict[str, str],
105
126
  forward_model_steps: list[ForwardModelStep],
@@ -667,6 +688,14 @@ def log_observation_keys(
667
688
  if key not in {"name", "type"}
668
689
  )
669
690
 
691
+ if "HISTORY_OBSERVATION" in observation_type_counts:
692
+ msg = (
693
+ "HISTORY_OBSERVATION is deprecated and will be removed. "
694
+ "Please use SUMMARY_OBSERVATION instead."
695
+ )
696
+ ConfigWarning.warn(msg)
697
+ logger.warning(msg)
698
+
670
699
  logger.info(
671
700
  f"Count of observation types:\n\t{dict(observation_type_counts)}\n"
672
701
  f"Count of observation keywords:\n\t{dict(observation_keyword_counts)}"
@@ -712,6 +741,9 @@ class ErtConfig(BaseModel):
712
741
  user_config_file: str = "no_config"
713
742
  config_path: str = Field(init=False, default="")
714
743
  observation_declarations: list[Observation] = Field(default_factory=list)
744
+ time_map: list[datetime] | None = None
745
+ history_source: HistorySource = HistorySource.REFCASE_HISTORY
746
+ refcase: Refcase | None = None
715
747
  _observations: dict[str, pl.DataFrame] | None = PrivateAttr(None)
716
748
 
717
749
  @property
@@ -731,6 +763,7 @@ class ErtConfig(BaseModel):
731
763
  )
732
764
  computed = create_observation_dataframes(
733
765
  self.observation_declarations,
766
+ self.refcase,
734
767
  cast(
735
768
  GenDataConfig | None,
736
769
  self.ensemble_config.response_configs.get("gen_data", None),
@@ -739,6 +772,8 @@ class ErtConfig(BaseModel):
739
772
  RFTConfig | None,
740
773
  self.ensemble_config.response_configs.get("rft", None),
741
774
  ),
775
+ self.time_map,
776
+ self.history_source,
742
777
  )
743
778
  self._observations = computed
744
779
  return computed
@@ -994,7 +1029,7 @@ class ErtConfig(BaseModel):
994
1029
  summary_obs = {
995
1030
  obs.key
996
1031
  for obs in obs_configs
997
- if isinstance(obs, SummaryObservation)
1032
+ if isinstance(obs, HistoryObservation | SummaryObservation)
998
1033
  }
999
1034
  if summary_obs:
1000
1035
  summary_keys = ErtConfig._read_summary_keys(config_dict)
@@ -1002,6 +1037,16 @@ class ErtConfig(BaseModel):
1002
1037
  [key] for key in summary_obs if key not in summary_keys
1003
1038
  ]
1004
1039
  ensemble_config = EnsembleConfig.from_dict(config_dict=config_dict)
1040
+ time_map = None
1041
+ if time_map_args := config_dict.get(ConfigKeys.TIME_MAP):
1042
+ time_map_file, time_map_contents = time_map_args
1043
+ try:
1044
+ time_map = _read_time_map(time_map_contents)
1045
+ except ValueError as err:
1046
+ raise ConfigValidationError.with_context(
1047
+ f"Could not read timemap file {time_map_file}: {err}",
1048
+ time_map_file,
1049
+ ) from err
1005
1050
  except ConfigValidationError as err:
1006
1051
  errors.append(err)
1007
1052
  except PydanticValidationError as err:
@@ -1054,6 +1099,9 @@ class ErtConfig(BaseModel):
1054
1099
 
1055
1100
  env_vars = {}
1056
1101
  substituter = Substitutions(substitutions)
1102
+ history_source = config_dict.get(
1103
+ ConfigKeys.HISTORY_SOURCE, HistorySource.REFCASE_HISTORY
1104
+ )
1057
1105
 
1058
1106
  # Insert env vars from plugins/site config
1059
1107
  for key, val in cls.ENV_VARS.items():
@@ -1089,6 +1137,7 @@ class ErtConfig(BaseModel):
1089
1137
  prioritize_private_ip_address = user_prioritize_private_ip_address
1090
1138
 
1091
1139
  try:
1140
+ refcase = Refcase.from_config_dict(config_dict)
1092
1141
  cls_config = cls(
1093
1142
  substitutions=substitutions,
1094
1143
  ensemble_config=ensemble_config,
@@ -1110,6 +1159,9 @@ class ErtConfig(BaseModel):
1110
1159
  runpath_config=model_config,
1111
1160
  user_config_file=config_file_path,
1112
1161
  observation_declarations=list(obs_configs),
1162
+ time_map=time_map,
1163
+ history_source=history_source,
1164
+ refcase=refcase,
1113
1165
  prioritize_private_ip_address=prioritize_private_ip_address,
1114
1166
  )
1115
1167
 
@@ -1127,6 +1179,7 @@ class ErtConfig(BaseModel):
1127
1179
  )
1128
1180
  cls_config._observations = create_observation_dataframes(
1129
1181
  obs_configs,
1182
+ refcase,
1130
1183
  cast(
1131
1184
  GenDataConfig | None,
1132
1185
  ensemble_config.response_configs.get("gen_data", None),
@@ -1135,6 +1188,8 @@ class ErtConfig(BaseModel):
1135
1188
  RFTConfig | None,
1136
1189
  ensemble_config.response_configs.get("rft", None),
1137
1190
  ),
1191
+ time_map,
1192
+ history_source,
1138
1193
  )
1139
1194
  except PydanticValidationError as err:
1140
1195
  raise ConfigValidationError.from_pydantic(err) from err
@@ -138,7 +138,6 @@ observations_parser = Lark(
138
138
  PARAMETER_NAME : CHAR+
139
139
  object : "{" [(declaration";")*] "}"
140
140
  ?declaration: "SEGMENT" STRING object -> segment
141
- | "LOCALIZATION" object -> localization
142
141
  | pair
143
142
  pair : PARAMETER_NAME "=" value
144
143
 
@@ -194,11 +193,6 @@ class TreeToObservations(Transformer[FileContextToken, list[ObservationDict]]):
194
193
  def segment(tree):
195
194
  return (("SEGMENT", tree[0]), tree[1])
196
195
 
197
- @staticmethod
198
- @no_type_check
199
- def localization(tree):
200
- return ("LOCALIZATION", tree[0])
201
-
202
196
  @staticmethod
203
197
  @no_type_check
204
198
  def object(tree):
ert/config/rft_config.py CHANGED
@@ -194,7 +194,7 @@ class RFTConfig(ResponseConfig):
194
194
  .explode("location")
195
195
  for (well, time), inner_dict in fetched.items()
196
196
  for prop, vals in inner_dict.items()
197
- if prop != "DEPTH" and len(vals) > 0
197
+ if prop != "DEPTH"
198
198
  ]
199
199
  )
200
200
  except KeyError as err:
File without changes