ert 19.0.1__py3-none-any.whl → 20.0.0b1__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 (87) hide show
  1. ert/__main__.py +94 -63
  2. ert/analysis/_es_update.py +11 -14
  3. ert/cli/main.py +1 -1
  4. ert/config/__init__.py +3 -2
  5. ert/config/_create_observation_dataframes.py +52 -375
  6. ert/config/_observations.py +527 -200
  7. ert/config/_read_summary.py +4 -5
  8. ert/config/ert_config.py +52 -117
  9. ert/config/everest_control.py +40 -39
  10. ert/config/everest_response.py +3 -15
  11. ert/config/field.py +4 -76
  12. ert/config/forward_model_step.py +17 -1
  13. ert/config/gen_data_config.py +14 -17
  14. ert/config/observation_config_migrations.py +821 -0
  15. ert/config/parameter_config.py +18 -28
  16. ert/config/parsing/__init__.py +0 -1
  17. ert/config/parsing/_parse_zonemap.py +45 -0
  18. ert/config/parsing/config_keywords.py +1 -0
  19. ert/config/parsing/config_schema.py +2 -0
  20. ert/config/parsing/observations_parser.py +2 -0
  21. ert/config/response_config.py +5 -23
  22. ert/config/rft_config.py +129 -31
  23. ert/config/summary_config.py +1 -13
  24. ert/config/surface_config.py +0 -57
  25. ert/dark_storage/compute/misfits.py +0 -42
  26. ert/dark_storage/endpoints/__init__.py +0 -2
  27. ert/dark_storage/endpoints/experiments.py +2 -5
  28. ert/dark_storage/json_schema/experiment.py +1 -2
  29. ert/field_utils/__init__.py +0 -2
  30. ert/field_utils/field_utils.py +1 -117
  31. ert/gui/ertwidgets/listeditbox.py +9 -1
  32. ert/gui/ertwidgets/models/ertsummary.py +20 -6
  33. ert/gui/ertwidgets/pathchooser.py +9 -1
  34. ert/gui/ertwidgets/stringbox.py +11 -3
  35. ert/gui/ertwidgets/textbox.py +10 -3
  36. ert/gui/ertwidgets/validationsupport.py +19 -1
  37. ert/gui/main_window.py +11 -6
  38. ert/gui/simulation/experiment_panel.py +1 -1
  39. ert/gui/simulation/run_dialog.py +11 -1
  40. ert/gui/tools/manage_experiments/export_dialog.py +4 -0
  41. ert/gui/tools/manage_experiments/manage_experiments_panel.py +1 -0
  42. ert/gui/tools/manage_experiments/storage_info_widget.py +1 -1
  43. ert/gui/tools/manage_experiments/storage_widget.py +21 -4
  44. ert/gui/tools/plot/data_type_proxy_model.py +1 -1
  45. ert/gui/tools/plot/plot_api.py +35 -27
  46. ert/gui/tools/plot/plot_widget.py +5 -0
  47. ert/gui/tools/plot/plot_window.py +4 -7
  48. ert/run_models/ensemble_experiment.py +2 -9
  49. ert/run_models/ensemble_smoother.py +1 -9
  50. ert/run_models/everest_run_model.py +31 -23
  51. ert/run_models/initial_ensemble_run_model.py +19 -22
  52. ert/run_models/manual_update.py +11 -5
  53. ert/run_models/model_factory.py +7 -7
  54. ert/run_models/multiple_data_assimilation.py +3 -16
  55. ert/sample_prior.py +12 -14
  56. ert/scheduler/job.py +24 -4
  57. ert/services/__init__.py +7 -3
  58. ert/services/_storage_main.py +59 -22
  59. ert/services/ert_server.py +186 -24
  60. ert/shared/version.py +3 -3
  61. ert/storage/local_ensemble.py +50 -116
  62. ert/storage/local_experiment.py +94 -109
  63. ert/storage/local_storage.py +10 -12
  64. ert/storage/migration/to24.py +26 -0
  65. ert/storage/migration/to25.py +91 -0
  66. ert/utils/__init__.py +20 -0
  67. {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/METADATA +4 -51
  68. {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/RECORD +80 -83
  69. everest/bin/everest_script.py +5 -5
  70. everest/bin/kill_script.py +2 -2
  71. everest/bin/monitor_script.py +2 -2
  72. everest/bin/utils.py +4 -4
  73. everest/detached/everserver.py +6 -6
  74. everest/gui/everest_client.py +0 -6
  75. everest/gui/main_window.py +2 -2
  76. everest/util/__init__.py +1 -19
  77. ert/dark_storage/compute/__init__.py +0 -0
  78. ert/dark_storage/endpoints/compute/__init__.py +0 -0
  79. ert/dark_storage/endpoints/compute/misfits.py +0 -95
  80. ert/services/_base_service.py +0 -387
  81. ert/services/webviz_ert_service.py +0 -20
  82. ert/shared/storage/command.py +0 -38
  83. ert/shared/storage/extraction.py +0 -42
  84. {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/WHEEL +0 -0
  85. {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/entry_points.txt +0 -0
  86. {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/licenses/COPYING +0 -0
  87. {ert-19.0.1.dist-info → ert-20.0.0b1.dist-info}/top_level.txt +0 -0
@@ -126,35 +126,28 @@ class ParameterConfig(BaseModel):
126
126
  def transform_data(self) -> Callable[[float], float]:
127
127
  return lambda x: x
128
128
 
129
- def sample_value(
130
- self,
131
- global_seed: str,
132
- realization: int,
129
+ def sample_values(
130
+ self, global_seed: str, active_realizations: list[int], num_realizations: int
133
131
  ) -> npt.NDArray[np.double]:
134
132
  """
135
- Generate a sample value for each key in a parameter group.
133
+ Generate reproducible standard-normal samples for active realizations.
136
134
 
137
- The sampling is reproducible and dependent on a global seed combined
138
- with the parameter group name and individual key names. The 'realization'
139
- parameter determines the specific sample point from the distribution for each
140
- parameter.
135
+ For this parameter (identified by self.group_name and self.name), a random
136
+ sampling of size `num_realizations` is constructed using an RNG
137
+ seeded from `global_seed` and the parameter name. The entries at the
138
+ indices specified by `active_realizations` are then returned.
141
139
 
142
140
  Parameters:
143
- - global_seed (str): A global seed string used for RNG seed generation to ensure
144
- reproducibility across runs.
145
- - realization (int): An integer used to advance the RNG to a specific point in
146
- its sequence, effectively selecting the 'realization'-th sample from the
147
- distribution.
141
+ - global_seed (str): A global seed string used for RNG seed generation to
142
+ ensure reproducibility across runs.
143
+ - active_realizations (list[int]): indices of the realizations
144
+ to select from the sampling vector; each must satisfy 0 <= i < num_realizations.
145
+ - num_realizations (int): Total number of realizations. Assures stable sampling
146
+ for a given global_seed regardless of currently active realizations.
148
147
 
149
148
  Returns:
150
- - npt.NDArray[np.double]: An array of sample values, one for each key in the
151
- provided list.
152
-
153
- Note:
154
- The method uses SHA-256 for hash generation and numpy's default random number
155
- generator for sampling. The RNG state is advanced to the 'realization' point
156
- before generating a single sample, enhancing efficiency by avoiding the
157
- generation of large, unused sample sets.
149
+ - npt.NDArray[np.double]: Array of shape (len(active_realizations),
150
+ containing sample values, one for each `active_realization`.
158
151
  """
159
152
  key_hash = sha256(
160
153
  global_seed.encode("utf-8") + f"{self.group_name}:{self.name}".encode()
@@ -162,9 +155,6 @@ class ParameterConfig(BaseModel):
162
155
  seed = np.frombuffer(key_hash.digest(), dtype="uint32")
163
156
  rng = np.random.default_rng(seed)
164
157
 
165
- # Advance the RNG state to the realization point
166
- rng.standard_normal(realization)
167
-
168
- # Generate a single sample
169
- value = rng.standard_normal(1)
170
- return np.array([value[0]])
158
+ # Generate samples for all active realizations
159
+ all_values = rng.standard_normal(num_realizations)
160
+ return all_values[active_realizations]
@@ -36,7 +36,6 @@ __all__ = [
36
36
  "ObservationConfigError",
37
37
  "ObservationDict",
38
38
  "ObservationType",
39
- "ObservationType",
40
39
  "QueueSystem",
41
40
  "SchemaItemType",
42
41
  "WarningInfo",
@@ -0,0 +1,45 @@
1
+ from .config_errors import ConfigValidationError
2
+
3
+
4
+ def parse_zonemap(filename: str, content: str) -> dict[int, list[str]]:
5
+ """A zonemap is a map from a simulation grids layers to a list of zone names
6
+ that the k-layers belongs to.
7
+
8
+ Note that the map uses 1-indexing of layers.
9
+ """
10
+ zones_at_k_value: dict[int, list[str]] = {}
11
+
12
+ base_err_msg = "On Line {line_number} in the ZONEMAP file: "
13
+ for idx, line in enumerate(content.splitlines()):
14
+ line_number = idx + 1
15
+ zonemap_line = _strip_comments(line).split()
16
+
17
+ if not zonemap_line:
18
+ continue
19
+
20
+ if len(zonemap_line) < 2:
21
+ raise ConfigValidationError.with_context(
22
+ "Number of zonenames must be 1 or more.",
23
+ filename,
24
+ )
25
+ try:
26
+ k = int(zonemap_line[0])
27
+ except ValueError as err:
28
+ raise ConfigValidationError.with_context(
29
+ base_err_msg.format(line_number=line_number)
30
+ + f"k must be an integer, was {zonemap_line[0]}.",
31
+ filename,
32
+ ) from err
33
+ if k <= 0:
34
+ raise ConfigValidationError.with_context(
35
+ base_err_msg.format(line_number=line_number)
36
+ + "k must be at least 1. Layers are 1-indexed.",
37
+ filename,
38
+ )
39
+ zones_at_k_value[k] = [zone.strip() for zone in zonemap_line[1:]]
40
+
41
+ return zones_at_k_value
42
+
43
+
44
+ def _strip_comments(line: str) -> str:
45
+ return line.partition("--")[0].rstrip()
@@ -31,6 +31,7 @@ class ConfigKeys(StrEnum):
31
31
  NUM_REALIZATIONS = "NUM_REALIZATIONS"
32
32
  MIN_REALIZATIONS = "MIN_REALIZATIONS"
33
33
  OBS_CONFIG = "OBS_CONFIG"
34
+ ZONEMAP = "ZONEMAP"
34
35
  QUEUE_SYSTEM = "QUEUE_SYSTEM"
35
36
  QUEUE_OPTION = "QUEUE_OPTION"
36
37
  HOOK_WORKFLOW = "HOOK_WORKFLOW"
@@ -1,3 +1,4 @@
1
+ from ._parse_zonemap import parse_zonemap
1
2
  from .config_dict import ConfigDict
2
3
  from .config_keywords import ConfigKeys
3
4
  from .config_schema_deprecations import deprecated_keywords_list
@@ -333,6 +334,7 @@ def init_user_config_schema() -> ConfigSchemaDict:
333
334
  existing_path_inline_keyword(
334
335
  ConfigKeys.OBS_CONFIG, parser=lambda f, c: parse_observations(c, f)
335
336
  ),
337
+ existing_path_inline_keyword(ConfigKeys.ZONEMAP, parser=parse_zonemap),
336
338
  existing_path_inline_keyword(ConfigKeys.TIME_MAP),
337
339
  single_arg_keyword(ConfigKeys.GEN_KW_EXPORT_NAME),
338
340
  history_source_keyword(),
@@ -19,6 +19,7 @@ class ObservationType(StrEnum):
19
19
  SUMMARY = "SUMMARY_OBSERVATION"
20
20
  GENERAL = "GENERAL_OBSERVATION"
21
21
  RFT = "RFT_OBSERVATION"
22
+ BREAKTHROUGH = "BREAKTHROUGH_OBSERVATION"
22
23
 
23
24
 
24
25
  ObservationDict = dict[str, Any]
@@ -127,6 +128,7 @@ observations_parser = Lark(
127
128
  | "SUMMARY_OBSERVATION"
128
129
  | "GENERAL_OBSERVATION"
129
130
  | "RFT_OBSERVATION"
131
+ | "BREAKTHROUGH_OBSERVATION"
130
132
  type: TYPE
131
133
  ?value: object
132
134
  | STRING
@@ -14,35 +14,12 @@ class InvalidResponseFile(Exception):
14
14
  """
15
15
 
16
16
 
17
- class ResponseMetadata(BaseModel):
18
- response_type: str
19
- response_key: str
20
- finalized: bool
21
- filter_on: dict[str, list[Any]] | None = Field(
22
- default=None,
23
- description="""
24
- Holds information about which columns the response can be filtered on.
25
- For example, for gen data, { "report_step": [0, 199, 299] } indicates
26
- that we can filter the response by report step with the potential values
27
- [0, 199, 299].
28
- """,
29
- )
30
-
31
-
32
17
  class ResponseConfig(BaseModel):
33
18
  type: str
34
19
  input_files: list[str] = Field(default_factory=list)
35
20
  keys: list[str] = Field(default_factory=list)
36
21
  has_finalized_keys: bool = False
37
22
 
38
- @property
39
- @abstractmethod
40
- def metadata(self) -> list[ResponseMetadata]:
41
- """
42
- Returns metadata describing this response
43
-
44
- """
45
-
46
23
  @abstractmethod
47
24
  def read_from_file(self, run_path: str, iens: int, iter_: int) -> pl.DataFrame:
48
25
  """Reads the data for the response from run_path.
@@ -76,3 +53,8 @@ class ResponseConfig(BaseModel):
76
53
  def display_column(cls, value: Any, column_name: str) -> str:
77
54
  """Formats a value to a user-friendly displayable format."""
78
55
  return str(value)
56
+
57
+ @property
58
+ def filter_on(self) -> dict[str, dict[str, list[int]]] | None:
59
+ """Optional filters for this response."""
60
+ return None
ert/config/rft_config.py CHANGED
@@ -5,8 +5,10 @@ import fnmatch
5
5
  import logging
6
6
  import os
7
7
  import re
8
+ import warnings
8
9
  from collections import defaultdict
9
- from typing import IO, Any, Literal
10
+ from dataclasses import dataclass
11
+ from typing import IO, Any, Literal, TypeAlias, cast
10
12
 
11
13
  import numpy as np
12
14
  import numpy.typing as npt
@@ -15,31 +17,67 @@ from pydantic import Field
15
17
  from resfo_utilities import CornerpointGrid, InvalidRFTError, RFTReader
16
18
 
17
19
  from ert.substitutions import substitute_runpath_name
20
+ from ert.warnings import PostSimulationWarning
18
21
 
19
22
  from .parsing import ConfigDict, ConfigKeys, ConfigValidationError, ConfigWarning
20
- from .response_config import InvalidResponseFile, ResponseConfig, ResponseMetadata
23
+ from .response_config import InvalidResponseFile, ResponseConfig
21
24
  from .responses_index import responses_index
22
25
 
23
26
  logger = logging.getLogger(__name__)
24
27
 
25
28
 
29
+ # A Point in UTM/TVD coordinates
30
+ Point: TypeAlias = tuple[float, float, float]
31
+ # Index to a cell in a grid
32
+ GridIndex: TypeAlias = tuple[int, int, int]
33
+ ZoneName: TypeAlias = str
34
+ WellName: TypeAlias = str
35
+ DateString: TypeAlias = str
36
+ RFTProperty: TypeAlias = str
37
+
38
+
39
+ @dataclass(frozen=True)
40
+ class _ZonedPoint:
41
+ """A point optionally constrained to be in a given zone."""
42
+
43
+ point: tuple[float | None, float | None, float | None] = (None, None, None)
44
+ zone_name: ZoneName | None = None
45
+
46
+ def has_zone(self) -> bool:
47
+ return self.zone_name is not None
48
+
49
+
26
50
  class RFTConfig(ResponseConfig):
51
+ """:term:`RFT` response from a :term:`reservoir simulator`.
52
+
53
+ RFTConfig is the configuration of responses in the <RUNPATH>/<ECLBASE>.RFT
54
+ file which may be generated from a reservoir simulator forward model step.
55
+
56
+ The file contains values for grid cells along a wellpath (see RFTReader for
57
+ details). RFTConfig will match the values against the given :term:`UTM`/:term:`TVD`
58
+ locations.
59
+
60
+ Parameters:
61
+ data_to_read: dictionary of the values that should be read from the rft file.
62
+ loations: list of optionally zone constrained points that the rft values should
63
+ be labeled with.
64
+ zonemap: The mapping from grid layer index to zone name.
65
+ """
66
+
27
67
  type: Literal["rft"] = "rft"
28
68
  name: str = "rft"
29
69
  has_finalized_keys: bool = False
30
- data_to_read: dict[str, dict[str, list[str]]] = Field(default_factory=dict)
31
- locations: list[tuple[float, float, float]] = Field(default_factory=list)
70
+ data_to_read: dict[WellName, dict[DateString, list[RFTProperty]]] = Field(
71
+ default_factory=dict
72
+ )
73
+ locations: list[Point | tuple[Point, ZoneName]] = Field(default_factory=list)
74
+ zonemap: dict[int, list[ZoneName]] = Field(default_factory=dict)
32
75
 
33
76
  @property
34
- def metadata(self) -> list[ResponseMetadata]:
77
+ def _zoned_locations(self) -> list[_ZonedPoint]:
35
78
  return [
36
- ResponseMetadata(
37
- response_type=self.name,
38
- response_key=response_key,
39
- filter_on=None,
40
- finalized=self.has_finalized_keys,
41
- )
42
- for response_key in self.keys
79
+ _ZonedPoint(*p) if isinstance(p[1], ZoneName) else _ZonedPoint(p)
80
+ for p in self.locations
43
81
  ]
44
82
 
45
83
  @property
@@ -55,19 +93,56 @@ class RFTConfig(ResponseConfig):
55
93
 
56
94
  def _find_indices(
57
95
  self, egrid_file: str | os.PathLike[str] | IO[Any]
58
- ) -> dict[tuple[int, int, int] | None, set[tuple[float, float, float]]]:
96
+ ) -> dict[GridIndex | None, set[_ZonedPoint]]:
59
97
  indices = defaultdict(set)
60
98
  for a, b in zip(
61
99
  CornerpointGrid.read_egrid(egrid_file).find_cell_containing_point(
62
- self.locations
100
+ [cast(Point, loc.point) for loc in self._zoned_locations]
63
101
  ),
64
- self.locations,
102
+ self._zoned_locations,
65
103
  strict=True,
66
104
  ):
67
105
  indices[a].add(b)
68
106
  return indices
69
107
 
108
+ def _filter_zones(
109
+ self,
110
+ indices: dict[GridIndex | None, set[_ZonedPoint]],
111
+ iens: int,
112
+ iter_: int,
113
+ ) -> dict[GridIndex | None, set[_ZonedPoint]]:
114
+ for idx, locs in indices.items():
115
+ if idx is not None:
116
+ for loc in list(locs):
117
+ if loc.has_zone():
118
+ zone = cast(ZoneName, loc.zone_name)
119
+ # zonemap is 1-indexed so +1
120
+ if zone not in self.zonemap.get(idx[-1] + 1, []):
121
+ warnings.warn(
122
+ PostSimulationWarning(
123
+ f"An RFT observation with location {loc.point}, "
124
+ f"in iteration {iter_}, realization {iens} did "
125
+ f"not match expected zone {zone}. The observation "
126
+ "was deactivated",
127
+ ),
128
+ stacklevel=2,
129
+ )
130
+ locs.remove(loc)
131
+ return indices
132
+
70
133
  def read_from_file(self, run_path: str, iens: int, iter_: int) -> pl.DataFrame:
134
+ """Reads the RFT values from <RUNPATH>/<ECLBASE>.RFT
135
+
136
+ Also labels those values by which optionally zone constrained point
137
+ it belongs to.
138
+
139
+ The columns east, north, tvd is none when the value does not belong to
140
+ any point, otherwise it is the x,y,z values of that point. If the point
141
+ is constrained to be in a certain zone then the zone column is also populated.
142
+
143
+ Points which were constrained to be in a given zone, but were not contained
144
+ in that zone, is not labeled, and instead a warning is emitted.
145
+ """
71
146
  filename = substitute_runpath_name(self.input_files[0], iens, iter_)
72
147
  if filename.upper().endswith(".DATA"):
73
148
  # For backwards compatibility, it is
@@ -78,12 +153,12 @@ class RFTConfig(ResponseConfig):
78
153
  if grid_filename.upper().endswith(".RFT"):
79
154
  grid_filename = grid_filename[:-4]
80
155
  grid_filename += ".EGRID"
81
- fetched: dict[tuple[str, datetime.date], dict[str, npt.NDArray[np.float32]]] = (
82
- defaultdict(dict)
83
- )
156
+ fetched: dict[
157
+ tuple[WellName, datetime.date], dict[RFTProperty, npt.NDArray[np.float32]]
158
+ ] = defaultdict(dict)
84
159
  indices = {}
85
160
  if self.locations:
86
- indices = self._find_indices(grid_filename)
161
+ indices = self._filter_zones(self._find_indices(grid_filename), iens, iter_)
87
162
  if None in indices:
88
163
  raise InvalidResponseFile(
89
164
  f"Did not find grid coordinate for location(s) {indices[None]}"
@@ -99,7 +174,10 @@ class RFTConfig(ResponseConfig):
99
174
  "time": [],
100
175
  "depth": [],
101
176
  "values": [],
102
- "location": [],
177
+ "east": [],
178
+ "north": [],
179
+ "tvd": [],
180
+ "zone": [],
103
181
  }
104
182
  )
105
183
 
@@ -146,7 +224,7 @@ class RFTConfig(ResponseConfig):
146
224
  list(
147
225
  indices.get(
148
226
  (c[0] - 1, c[1] - 1, c[2] - 1),
149
- [(None, None, None)],
227
+ [_ZonedPoint()],
150
228
  )
151
229
  )
152
230
  for c in entry.connections
@@ -165,7 +243,10 @@ class RFTConfig(ResponseConfig):
165
243
  "time": [],
166
244
  "depth": [],
167
245
  "values": [],
168
- "location": [],
246
+ "east": [],
247
+ "north": [],
248
+ "tvd": [],
249
+ "zone": [],
169
250
  }
170
251
  )
171
252
 
@@ -180,21 +261,37 @@ class RFTConfig(ResponseConfig):
180
261
  "values": [vals],
181
262
  "location": pl.Series(
182
263
  [
183
- locations.get(
184
- (well, time), [(None, None, None)] * len(vals)
185
- )
264
+ [
265
+ [loc.point for loc in locs]
266
+ for locs in locations.get(
267
+ (well, time),
268
+ [[_ZonedPoint()]] * len(vals),
269
+ )
270
+ ]
186
271
  ],
187
272
  dtype=pl.Array(
188
273
  pl.List(pl.Array(pl.Float32, 3)), len(vals)
189
274
  ),
190
275
  ),
276
+ "zone": pl.Series(
277
+ [
278
+ [
279
+ [loc.zone_name for loc in locs]
280
+ for locs in locations.get(
281
+ (well, time),
282
+ [[_ZonedPoint()]] * len(vals),
283
+ )
284
+ ]
285
+ ],
286
+ dtype=pl.Array(pl.List(pl.String), len(vals)),
287
+ ),
191
288
  }
192
289
  )
193
- .explode("depth", "values", "location")
194
- .explode("location")
290
+ .explode("depth", "values", "location", "zone")
291
+ .explode("location", "zone")
195
292
  for (well, time), inner_dict in fetched.items()
196
293
  for prop, vals in inner_dict.items()
197
- if prop != "DEPTH"
294
+ if prop != "DEPTH" and len(vals) > 0
198
295
  ]
199
296
  )
200
297
  except KeyError as err:
@@ -214,7 +311,7 @@ class RFTConfig(ResponseConfig):
214
311
 
215
312
  @property
216
313
  def primary_key(self) -> list[str]:
217
- return ["east", "north", "tvd"]
314
+ return ["east", "north", "tvd", "zone"]
218
315
 
219
316
  @classmethod
220
317
  def from_config_dict(cls, config_dict: ConfigDict) -> RFTConfig | None:
@@ -236,8 +333,8 @@ class RFTConfig(ResponseConfig):
236
333
  "step known to generate rft files"
237
334
  )
238
335
 
239
- declared_data: dict[str, dict[datetime.date, list[str]]] = defaultdict(
240
- lambda: defaultdict(list)
336
+ declared_data: dict[WellName, dict[datetime.date, list[RFTProperty]]] = (
337
+ defaultdict(lambda: defaultdict(list))
241
338
  )
242
339
  for rft in rfts:
243
340
  for expected in ["WELL", "DATE", "PROPERTIES"]:
@@ -267,6 +364,7 @@ class RFTConfig(ResponseConfig):
267
364
  input_files=[eclbase.replace("%d", "<IENS>")],
268
365
  keys=keys,
269
366
  data_to_read=data_to_read,
367
+ zonemap=config_dict.get(ConfigKeys.ZONEMAP, ("", {}))[1],
270
368
  )
271
369
 
272
370
  return None
@@ -11,7 +11,7 @@ from ert.substitutions import substitute_runpath_name
11
11
  from ._read_summary import read_summary
12
12
  from .parsing import ConfigDict, ConfigKeys
13
13
  from .parsing.config_errors import ConfigValidationError, ConfigWarning
14
- from .response_config import InvalidResponseFile, ResponseConfig, ResponseMetadata
14
+ from .response_config import InvalidResponseFile, ResponseConfig
15
15
  from .responses_index import responses_index
16
16
 
17
17
  logger = logging.getLogger(__name__)
@@ -21,18 +21,6 @@ class SummaryConfig(ResponseConfig):
21
21
  type: Literal["summary"] = "summary"
22
22
  has_finalized_keys: bool = False
23
23
 
24
- @property
25
- def metadata(self) -> list[ResponseMetadata]:
26
- return [
27
- ResponseMetadata(
28
- response_type=self.type,
29
- response_key=response_key,
30
- filter_on=None,
31
- finalized=self.has_finalized_keys,
32
- )
33
- for response_key in self.keys
34
- ]
35
-
36
24
  @property
37
25
  def expected_input_files(self) -> list[str]:
38
26
  base = self.input_files[0]
@@ -10,11 +10,6 @@ import xarray as xr
10
10
  from pydantic import field_serializer
11
11
  from surfio import IrapHeader, IrapSurface
12
12
 
13
- from ert.field_utils import (
14
- calc_rho_for_2d_grid_layer,
15
- transform_local_ellipse_angle_to_local_coords,
16
- transform_positions_to_local_field_coordinates,
17
- )
18
13
  from ert.substitutions import substitute_runpath_name
19
14
 
20
15
  from ._str_to_bool import str_to_bool
@@ -239,55 +234,3 @@ class SurfaceConfig(ParameterConfig):
239
234
  this flattening process"""
240
235
 
241
236
  return create_flattened_cube_graph(px=self.ncol, py=self.nrow, pz=1)
242
-
243
- def calc_rho_for_2d_grid_layer(
244
- self,
245
- obs_xpos: npt.NDArray[np.float64],
246
- obs_ypos: npt.NDArray[np.float64],
247
- obs_main_range: npt.NDArray[np.float64],
248
- obs_perp_range: npt.NDArray[np.float64],
249
- obs_anisotropy_angle: npt.NDArray[np.float64],
250
- ) -> npt.NDArray[np.float64]:
251
- """Function to calculate scaling values to be used in the RHO matrix
252
- for distance-based localization.
253
-
254
- Args:
255
- obs_xpos: x-coordinates in global coordinates of observations
256
- obs_ypos: y-coordinates in global coordinates of observations
257
- obs_main_range: Size of influence ellipse main principal direction.
258
- obs_perp_range: Size of influence ellipse second principal direction.
259
- obs_anisotropy_angle: Rotation angle anticlock wise of main principal
260
- direction of influence ellipse relative to global coordinate
261
- system's x-axis.
262
-
263
- Returns:
264
- Scaling values (elements of the RHO matrix) as a numpy array
265
- of shape=(nx,ny,nobservations)
266
-
267
- """
268
- # Transform observation positions to local surface coordinates
269
- xpos, ypos = transform_positions_to_local_field_coordinates(
270
- (self.xori, self.yori), self.rotation, obs_xpos, obs_ypos
271
- )
272
- # Transform ellipse orientation to local surface coordinates
273
- rotation_angle_of_localization_ellipse = (
274
- transform_local_ellipse_angle_to_local_coords(
275
- self.rotation, obs_anisotropy_angle
276
- )
277
- )
278
-
279
- # Assume the coordinate system is not flipped.
280
- # This means the right_handed_grid_indexing is False
281
- assert self.yflip == 1
282
- return calc_rho_for_2d_grid_layer(
283
- self.ncol,
284
- self.nrow,
285
- self.xinc,
286
- self.yinc,
287
- xpos,
288
- ypos,
289
- obs_main_range,
290
- obs_perp_range,
291
- rotation_angle_of_localization_ellipse,
292
- right_handed_grid_indexing=False,
293
- )
@@ -1,42 +0,0 @@
1
- from collections.abc import Mapping
2
-
3
- import numpy as np
4
- import numpy.typing as npt
5
- import pandas as pd
6
-
7
-
8
- def _calculate_signed_chi_squared_misfit(
9
- obs_value: npt.NDArray[np.float64],
10
- response_value: npt.NDArray[np.float64],
11
- obs_std: npt.NDArray[np.float64],
12
- ) -> list[float]:
13
- """The signed version is intended for visualization. For data assimiliation one
14
- would normally use the normal chi-square"""
15
- residual = response_value - obs_value
16
- return (np.sign(residual) * residual * residual / (obs_std * obs_std)).tolist()
17
-
18
-
19
- def calculate_signed_chi_squared_misfits(
20
- reponses_dict: Mapping[int, pd.DataFrame],
21
- observation: pd.DataFrame,
22
- summary_misfits: bool = False,
23
- ) -> pd.DataFrame:
24
- """
25
- Compute misfits from reponses_dict (real_id, values in dataframe)
26
- and observation
27
- """
28
- misfits_dict = {}
29
- for realization_index in reponses_dict:
30
- misfits_dict[realization_index] = _calculate_signed_chi_squared_misfit(
31
- observation["values"],
32
- reponses_dict[realization_index]
33
- .loc[:, observation.index]
34
- .to_numpy()
35
- .flatten(),
36
- observation["errors"],
37
- )
38
-
39
- df = pd.DataFrame(data=misfits_dict, index=observation.index)
40
- if summary_misfits:
41
- df = pd.DataFrame([df.abs().sum(axis=0)], columns=df.columns, index=[0])
42
- return df.T
@@ -1,6 +1,5 @@
1
1
  from fastapi import APIRouter
2
2
 
3
- from .compute.misfits import router as misfits_router
4
3
  from .ensembles import router as ensembles_router
5
4
  from .experiment_server import router as experiment_server_router
6
5
  from .experiments import router as experiments_router
@@ -15,7 +14,6 @@ router.include_router(experiments_router)
15
14
  router.include_router(ensembles_router)
16
15
  router.include_router(observations_router)
17
16
  router.include_router(updates_router)
18
- router.include_router(misfits_router)
19
17
  router.include_router(parameters_router)
20
18
  router.include_router(responses_router)
21
19
  router.include_router(experiment_server_router)
@@ -6,7 +6,6 @@ from fastapi import APIRouter, Body, Depends, HTTPException
6
6
  from ert.config import SurfaceConfig
7
7
  from ert.dark_storage import json_schema as js
8
8
  from ert.dark_storage.common import get_storage
9
- from ert.shared.storage.extraction import create_priors
10
9
  from ert.storage import Storage
11
10
 
12
11
  router = APIRouter(tags=["experiment"])
@@ -26,7 +25,6 @@ def get_experiments(
26
25
  id=experiment.id,
27
26
  name=experiment.name,
28
27
  ensemble_ids=[ens.id for ens in experiment.ensembles],
29
- priors=create_priors(experiment),
30
28
  userdata={},
31
29
  parameters={
32
30
  group: config.model_dump()
@@ -34,7 +32,7 @@ def get_experiments(
34
32
  if not isinstance(config, SurfaceConfig)
35
33
  },
36
34
  responses={
37
- response_type: [m.model_dump() for m in config.metadata]
35
+ response_type: config.model_dump()
38
36
  for response_type, config in experiment.response_configuration.items()
39
37
  },
40
38
  observations=experiment.response_key_to_observation_key,
@@ -62,14 +60,13 @@ def get_experiment_by_id(
62
60
  name=experiment.name,
63
61
  id=experiment.id,
64
62
  ensemble_ids=[ens.id for ens in experiment.ensembles],
65
- priors=create_priors(experiment),
66
63
  userdata={},
67
64
  parameters={
68
65
  group: config.model_dump()
69
66
  for group, config in experiment.parameter_configuration.items()
70
67
  },
71
68
  responses={
72
- response_type: [m.model_dump() for m in config.metadata]
69
+ response_type: config.model_dump()
73
70
  for response_type, config in experiment.response_configuration.items()
74
71
  },
75
72
  observations=experiment.response_key_to_observation_key,