ert 17.0.0__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 (218) hide show
  1. _ert/events.py +19 -2
  2. _ert/forward_model_runner/client.py +6 -2
  3. ert/__main__.py +28 -13
  4. ert/analysis/_enif_update.py +8 -4
  5. ert/analysis/_es_update.py +19 -6
  6. ert/analysis/_update_commons.py +16 -6
  7. ert/cli/main.py +13 -6
  8. ert/cli/monitor.py +7 -0
  9. ert/config/__init__.py +15 -6
  10. ert/config/_create_observation_dataframes.py +117 -20
  11. ert/config/_get_num_cpu.py +1 -1
  12. ert/config/_observations.py +91 -2
  13. ert/config/_read_summary.py +8 -6
  14. ert/config/design_matrix.py +51 -24
  15. ert/config/distribution.py +1 -1
  16. ert/config/ensemble_config.py +9 -17
  17. ert/config/ert_config.py +103 -19
  18. ert/config/everest_control.py +234 -0
  19. ert/config/{everest_objective_config.py → everest_response.py} +24 -15
  20. ert/config/field.py +96 -84
  21. ert/config/forward_model_step.py +122 -17
  22. ert/config/gen_data_config.py +5 -10
  23. ert/config/gen_kw_config.py +5 -35
  24. ert/config/known_response_types.py +14 -0
  25. ert/config/parameter_config.py +1 -33
  26. ert/config/parsing/_option_dict.py +10 -2
  27. ert/config/parsing/config_keywords.py +2 -0
  28. ert/config/parsing/config_schema.py +23 -3
  29. ert/config/parsing/config_schema_deprecations.py +3 -14
  30. ert/config/parsing/config_schema_item.py +26 -11
  31. ert/config/parsing/context_values.py +3 -3
  32. ert/config/parsing/file_context_token.py +1 -1
  33. ert/config/parsing/observations_parser.py +6 -2
  34. ert/config/parsing/queue_system.py +9 -0
  35. ert/config/parsing/schema_item_type.py +1 -0
  36. ert/config/queue_config.py +4 -5
  37. ert/config/response_config.py +0 -8
  38. ert/config/rft_config.py +275 -0
  39. ert/config/summary_config.py +3 -8
  40. ert/config/surface_config.py +59 -16
  41. ert/config/workflow_fixtures.py +2 -1
  42. ert/dark_storage/client/__init__.py +2 -2
  43. ert/dark_storage/client/_session.py +4 -4
  44. ert/dark_storage/client/client.py +2 -2
  45. ert/dark_storage/common.py +1 -1
  46. ert/dark_storage/compute/misfits.py +11 -7
  47. ert/dark_storage/endpoints/compute/misfits.py +6 -4
  48. ert/dark_storage/endpoints/experiment_server.py +12 -9
  49. ert/dark_storage/endpoints/experiments.py +2 -2
  50. ert/dark_storage/endpoints/observations.py +8 -6
  51. ert/dark_storage/endpoints/parameters.py +2 -18
  52. ert/dark_storage/endpoints/responses.py +24 -5
  53. ert/dark_storage/json_schema/experiment.py +1 -1
  54. ert/data/_measured_data.py +6 -5
  55. ert/ensemble_evaluator/__init__.py +8 -1
  56. ert/ensemble_evaluator/config.py +2 -1
  57. ert/ensemble_evaluator/evaluator.py +81 -29
  58. ert/ensemble_evaluator/event.py +6 -0
  59. ert/ensemble_evaluator/snapshot.py +3 -1
  60. ert/ensemble_evaluator/state.py +1 -0
  61. ert/field_utils/__init__.py +8 -0
  62. ert/field_utils/field_utils.py +212 -3
  63. ert/field_utils/roff_io.py +1 -1
  64. ert/gui/__init__.py +5 -2
  65. ert/gui/ertnotifier.py +1 -1
  66. ert/gui/ertwidgets/__init__.py +23 -16
  67. ert/gui/ertwidgets/analysismoduleedit.py +2 -2
  68. ert/gui/ertwidgets/checklist.py +1 -1
  69. ert/gui/ertwidgets/create_experiment_dialog.py +3 -1
  70. ert/gui/ertwidgets/ensembleselector.py +2 -2
  71. ert/gui/ertwidgets/models/__init__.py +2 -0
  72. ert/gui/ertwidgets/models/activerealizationsmodel.py +2 -1
  73. ert/gui/ertwidgets/models/path_model.py +1 -1
  74. ert/gui/ertwidgets/models/targetensemblemodel.py +2 -1
  75. ert/gui/ertwidgets/models/text_model.py +1 -1
  76. ert/gui/ertwidgets/pathchooser.py +0 -3
  77. ert/gui/ertwidgets/searchbox.py +13 -4
  78. ert/gui/{suggestor → ertwidgets/suggestor}/_suggestor_message.py +13 -4
  79. ert/gui/{suggestor → ertwidgets/suggestor}/suggestor.py +63 -30
  80. ert/gui/main.py +37 -8
  81. ert/gui/main_window.py +1 -7
  82. ert/gui/simulation/ensemble_experiment_panel.py +1 -1
  83. ert/gui/simulation/ensemble_information_filter_panel.py +1 -1
  84. ert/gui/simulation/ensemble_smoother_panel.py +1 -1
  85. ert/gui/simulation/evaluate_ensemble_panel.py +1 -1
  86. ert/gui/simulation/experiment_panel.py +16 -3
  87. ert/gui/simulation/manual_update_panel.py +31 -8
  88. ert/gui/simulation/multiple_data_assimilation_panel.py +12 -8
  89. ert/gui/simulation/run_dialog.py +27 -20
  90. ert/gui/simulation/single_test_run_panel.py +2 -2
  91. ert/gui/summarypanel.py +20 -1
  92. ert/gui/tools/load_results/load_results_panel.py +1 -1
  93. ert/gui/tools/manage_experiments/export_dialog.py +136 -0
  94. ert/gui/tools/manage_experiments/storage_info_widget.py +121 -16
  95. ert/gui/tools/manage_experiments/storage_widget.py +1 -2
  96. ert/gui/tools/plot/plot_api.py +37 -25
  97. ert/gui/tools/plot/plot_widget.py +10 -2
  98. ert/gui/tools/plot/plot_window.py +38 -18
  99. ert/gui/tools/plot/plottery/plot_config.py +2 -0
  100. ert/gui/tools/plot/plottery/plot_context.py +14 -0
  101. ert/gui/tools/plot/plottery/plots/__init__.py +2 -0
  102. ert/gui/tools/plot/plottery/plots/cesp.py +3 -1
  103. ert/gui/tools/plot/plottery/plots/distribution.py +6 -1
  104. ert/gui/tools/plot/plottery/plots/ensemble.py +12 -3
  105. ert/gui/tools/plot/plottery/plots/gaussian_kde.py +12 -2
  106. ert/gui/tools/plot/plottery/plots/histogram.py +3 -1
  107. ert/gui/tools/plot/plottery/plots/misfits.py +436 -0
  108. ert/gui/tools/plot/plottery/plots/observations.py +18 -4
  109. ert/gui/tools/plot/plottery/plots/statistics.py +62 -20
  110. ert/gui/tools/plot/plottery/plots/std_dev.py +3 -1
  111. ert/mode_definitions.py +2 -0
  112. ert/plugins/__init__.py +0 -1
  113. ert/plugins/hook_implementations/workflows/csv_export.py +2 -3
  114. ert/plugins/hook_implementations/workflows/gen_data_rft_export.py +10 -2
  115. ert/plugins/hook_specifications/__init__.py +0 -2
  116. ert/plugins/hook_specifications/jobs.py +0 -9
  117. ert/plugins/plugin_manager.py +6 -33
  118. ert/resources/forward_models/run_reservoirsimulator.py +8 -3
  119. ert/resources/shell_scripts/delete_directory.py +2 -2
  120. ert/run_models/__init__.py +18 -5
  121. ert/run_models/_create_run_path.py +131 -37
  122. ert/run_models/ensemble_experiment.py +10 -4
  123. ert/run_models/ensemble_information_filter.py +8 -1
  124. ert/run_models/ensemble_smoother.py +9 -3
  125. ert/run_models/evaluate_ensemble.py +8 -6
  126. ert/run_models/event.py +7 -3
  127. ert/run_models/everest_run_model.py +159 -46
  128. ert/run_models/initial_ensemble_run_model.py +25 -24
  129. ert/run_models/manual_update.py +6 -3
  130. ert/run_models/manual_update_enif.py +37 -0
  131. ert/run_models/model_factory.py +81 -21
  132. ert/run_models/multiple_data_assimilation.py +22 -11
  133. ert/run_models/run_model.py +64 -55
  134. ert/run_models/single_test_run.py +7 -4
  135. ert/run_models/update_run_model.py +4 -2
  136. ert/runpaths.py +5 -6
  137. ert/sample_prior.py +9 -4
  138. ert/scheduler/driver.py +37 -0
  139. ert/scheduler/event.py +3 -1
  140. ert/scheduler/job.py +23 -13
  141. ert/scheduler/lsf_driver.py +6 -2
  142. ert/scheduler/openpbs_driver.py +7 -1
  143. ert/scheduler/scheduler.py +5 -0
  144. ert/scheduler/slurm_driver.py +6 -2
  145. ert/services/__init__.py +2 -2
  146. ert/services/_base_service.py +37 -20
  147. ert/services/ert_server.py +317 -0
  148. ert/shared/_doc_utils/__init__.py +4 -2
  149. ert/shared/_doc_utils/ert_jobs.py +1 -4
  150. ert/shared/net_utils.py +43 -18
  151. ert/shared/storage/connection.py +3 -3
  152. ert/shared/version.py +3 -3
  153. ert/storage/__init__.py +2 -0
  154. ert/storage/local_ensemble.py +38 -12
  155. ert/storage/local_experiment.py +8 -16
  156. ert/storage/local_storage.py +68 -42
  157. ert/storage/migration/to11.py +1 -1
  158. ert/storage/migration/to16.py +38 -0
  159. ert/storage/migration/to17.py +42 -0
  160. ert/storage/migration/to18.py +11 -0
  161. ert/storage/migration/to19.py +34 -0
  162. ert/storage/migration/to20.py +23 -0
  163. ert/storage/migration/to21.py +25 -0
  164. ert/storage/migration/to8.py +4 -4
  165. ert/substitutions.py +12 -28
  166. ert/validation/active_range.py +7 -7
  167. ert/validation/rangestring.py +16 -16
  168. ert/workflow_runner.py +2 -1
  169. {ert-17.0.0.dist-info → ert-19.0.0rc2.dist-info}/METADATA +9 -8
  170. {ert-17.0.0.dist-info → ert-19.0.0rc2.dist-info}/RECORD +208 -205
  171. {ert-17.0.0.dist-info → ert-19.0.0rc2.dist-info}/WHEEL +1 -1
  172. everest/api/everest_data_api.py +14 -1
  173. everest/bin/config_branch_script.py +3 -6
  174. everest/bin/everconfigdump_script.py +1 -9
  175. everest/bin/everest_script.py +21 -11
  176. everest/bin/everlint_script.py +0 -2
  177. everest/bin/kill_script.py +2 -2
  178. everest/bin/monitor_script.py +2 -2
  179. everest/bin/utils.py +8 -4
  180. everest/bin/visualization_script.py +6 -14
  181. everest/config/__init__.py +4 -1
  182. everest/config/control_config.py +81 -6
  183. everest/config/control_variable_config.py +4 -3
  184. everest/config/everest_config.py +75 -42
  185. everest/config/forward_model_config.py +5 -3
  186. everest/config/install_data_config.py +7 -5
  187. everest/config/install_job_config.py +7 -3
  188. everest/config/install_template_config.py +3 -3
  189. everest/config/optimization_config.py +19 -6
  190. everest/config/output_constraint_config.py +8 -2
  191. everest/config/server_config.py +6 -49
  192. everest/config/utils.py +25 -105
  193. everest/config/validation_utils.py +17 -11
  194. everest/config_file_loader.py +13 -4
  195. everest/detached/client.py +3 -3
  196. everest/detached/everserver.py +7 -8
  197. everest/everest_storage.py +6 -12
  198. everest/gui/everest_client.py +2 -3
  199. everest/gui/main_window.py +2 -2
  200. everest/optimizer/everest2ropt.py +59 -32
  201. everest/optimizer/opt_model_transforms.py +12 -13
  202. everest/optimizer/utils.py +0 -29
  203. everest/strings.py +0 -5
  204. ert/config/everest_constraints_config.py +0 -95
  205. ert/config/ext_param_config.py +0 -106
  206. ert/gui/tools/export/__init__.py +0 -3
  207. ert/gui/tools/export/export_panel.py +0 -83
  208. ert/gui/tools/export/export_tool.py +0 -69
  209. ert/gui/tools/export/exporter.py +0 -36
  210. ert/services/storage_service.py +0 -127
  211. everest/config/sampler_config.py +0 -103
  212. everest/simulator/__init__.py +0 -88
  213. everest/simulator/everest_to_ert.py +0 -51
  214. /ert/gui/{suggestor → ertwidgets/suggestor}/__init__.py +0 -0
  215. /ert/gui/{suggestor → ertwidgets/suggestor}/_colors.py +0 -0
  216. {ert-17.0.0.dist-info → ert-19.0.0rc2.dist-info}/entry_points.txt +0 -0
  217. {ert-17.0.0.dist-info → ert-19.0.0rc2.dist-info}/licenses/COPYING +0 -0
  218. {ert-17.0.0.dist-info → ert-19.0.0rc2.dist-info}/top_level.txt +0 -0
@@ -10,11 +10,16 @@ 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
+ )
13
18
  from ert.substitutions import substitute_runpath_name
14
19
 
15
20
  from ._str_to_bool import str_to_bool
16
21
  from .field import create_flattened_cube_graph
17
- from .parameter_config import InvalidParameterFile, ParameterConfig, ParameterMetadata
22
+ from .parameter_config import InvalidParameterFile, ParameterConfig
18
23
  from .parsing import ConfigValidationError, ErrorInfo
19
24
 
20
25
  if TYPE_CHECKING:
@@ -46,6 +51,7 @@ class SurfaceMismatchError(InvalidParameterFile):
46
51
 
47
52
  class SurfaceConfig(ParameterConfig):
48
53
  type: Literal["surface"] = "surface"
54
+ dimensionality: Literal[2] = 2
49
55
  ncol: int
50
56
  nrow: int
51
57
  xori: float
@@ -70,21 +76,6 @@ class SurfaceConfig(ParameterConfig):
70
76
  def parameter_keys(self) -> list[str]:
71
77
  return []
72
78
 
73
- @property
74
- def metadata(self) -> list[ParameterMetadata]:
75
- return [
76
- ParameterMetadata(
77
- key=self.name,
78
- dimensionality=2,
79
- transformation=None,
80
- userdata={
81
- "data_origin": "SURFACE",
82
- "nx": self.ncol,
83
- "ny": self.nrow,
84
- },
85
- )
86
- ]
87
-
88
79
  @classmethod
89
80
  def from_config_list(cls, config_list: list[str | dict[str, str]]) -> Self:
90
81
  name = cast(str, config_list[0])
@@ -248,3 +239,55 @@ class SurfaceConfig(ParameterConfig):
248
239
  this flattening process"""
249
240
 
250
241
  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
+ )
@@ -6,12 +6,13 @@ import typing
6
6
  from dataclasses import dataclass, fields
7
7
  from typing import TYPE_CHECKING, Literal
8
8
 
9
- from PyQt6.QtWidgets import QWidget
10
9
  from typing_extensions import TypedDict
11
10
 
12
11
  from ert.config.parsing.hook_runtime import HookRuntime
13
12
 
14
13
  if TYPE_CHECKING:
14
+ from PyQt6.QtWidgets import QWidget
15
+
15
16
  from ert.config import ESSettings, ObservationSettings
16
17
  from ert.runpaths import Runpaths
17
18
  from ert.storage import Ensemble, Storage
@@ -1,4 +1,4 @@
1
- from ._session import ConnInfo
1
+ from ._session import ErtClientConnectionInfo
2
2
  from .client import Client
3
3
 
4
- __all__ = ["Client", "ConnInfo"]
4
+ __all__ = ["Client", "ErtClientConnectionInfo"]
@@ -5,7 +5,7 @@ from pathlib import Path
5
5
  from pydantic import BaseModel, ValidationError
6
6
 
7
7
 
8
- class ConnInfo(BaseModel):
8
+ class ErtClientConnectionInfo(BaseModel):
9
9
  base_url: str
10
10
  auth_token: str | None = None
11
11
  cert: str | bool = False
@@ -17,10 +17,10 @@ ENV_VAR = "ERT_STORAGE_CONNECTION_STRING"
17
17
  # that a single client process will only ever want to connect to a single ERT
18
18
  # Storage server during its lifetime, so we don't provide an API for managing
19
19
  # this cache.
20
- _CACHED_CONN_INFO: ConnInfo | None = None
20
+ _CACHED_CONN_INFO: ErtClientConnectionInfo | None = None
21
21
 
22
22
 
23
- def find_conn_info() -> ConnInfo:
23
+ def find_conn_info() -> ErtClientConnectionInfo:
24
24
  """
25
25
  The base url and auth token are read from either:
26
26
  The file `storage_server.json`, starting from the current working directory
@@ -54,7 +54,7 @@ def find_conn_info() -> ConnInfo:
54
54
  raise RuntimeError("No Storage connection configuration found")
55
55
 
56
56
  try:
57
- conn_info = ConnInfo.model_validate_json(conn_str)
57
+ conn_info = ErtClientConnectionInfo.model_validate_json(conn_str)
58
58
  except (json.JSONDecodeError, ValidationError) as e:
59
59
  raise RuntimeError("Invalid storage connection configuration") from e
60
60
  else:
@@ -3,7 +3,7 @@ import ssl
3
3
  import httpx
4
4
  from httpx_retries import Retry, RetryTransport
5
5
 
6
- from ._session import ConnInfo, find_conn_info
6
+ from ._session import ErtClientConnectionInfo, find_conn_info
7
7
 
8
8
 
9
9
  class Client(httpx.Client):
@@ -14,7 +14,7 @@ class Client(httpx.Client):
14
14
  Stores 'conn_info' to bridge the gap to the Everest client setup
15
15
  """
16
16
 
17
- def __init__(self, conn_info: ConnInfo | None = None) -> None:
17
+ def __init__(self, conn_info: ErtClientConnectionInfo | None = None) -> None:
18
18
  if conn_info is None:
19
19
  conn_info = find_conn_info()
20
20
 
@@ -28,7 +28,7 @@ def get_storage() -> Storage:
28
28
  except ErtStorageException as err:
29
29
  logger.exception(f"Error accessing storage: {err!s}")
30
30
  raise InternalServerError("Error accessing storage") from None
31
- _storage.refresh()
31
+ _storage.reload()
32
32
  return _storage
33
33
 
34
34
 
@@ -5,17 +5,18 @@ import numpy.typing as npt
5
5
  import pandas as pd
6
6
 
7
7
 
8
- def _calculate_misfit(
8
+ def _calculate_signed_chi_squared_misfit(
9
9
  obs_value: npt.NDArray[np.float64],
10
10
  response_value: npt.NDArray[np.float64],
11
11
  obs_std: npt.NDArray[np.float64],
12
12
  ) -> list[float]:
13
- difference = response_value - obs_value
14
- misfit = (difference / obs_std) ** 2
15
- return (misfit * np.sign(difference)).tolist()
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()
16
17
 
17
18
 
18
- def calculate_misfits_from_pandas(
19
+ def calculate_signed_chi_squared_misfits(
19
20
  reponses_dict: Mapping[int, pd.DataFrame],
20
21
  observation: pd.DataFrame,
21
22
  summary_misfits: bool = False,
@@ -26,9 +27,12 @@ def calculate_misfits_from_pandas(
26
27
  """
27
28
  misfits_dict = {}
28
29
  for realization_index in reponses_dict:
29
- misfits_dict[realization_index] = _calculate_misfit(
30
+ misfits_dict[realization_index] = _calculate_signed_chi_squared_misfit(
30
31
  observation["values"],
31
- reponses_dict[realization_index].loc[:, observation.index].values.flatten(),
32
+ reponses_dict[realization_index]
33
+ .loc[:, observation.index]
34
+ .to_numpy()
35
+ .flatten(),
32
36
  observation["errors"],
33
37
  )
34
38
 
@@ -1,6 +1,6 @@
1
1
  import json
2
2
  from datetime import datetime
3
- from typing import Any
3
+ from typing import Annotated, Any
4
4
  from uuid import UUID
5
5
 
6
6
  import pandas as pd
@@ -10,7 +10,7 @@ from fastapi.responses import Response
10
10
 
11
11
  from ert.dark_storage import exceptions as exc
12
12
  from ert.dark_storage.common import get_storage
13
- from ert.dark_storage.compute.misfits import calculate_misfits_from_pandas
13
+ from ert.dark_storage.compute.misfits import calculate_signed_chi_squared_misfits
14
14
  from ert.dark_storage.endpoints.observations import (
15
15
  _get_observations,
16
16
  )
@@ -36,7 +36,9 @@ async def get_response_misfits(
36
36
  response_name: str,
37
37
  realization_index: int | None = None,
38
38
  summary_misfits: bool = False,
39
- filter_on: str | None = Query(None, description="JSON string with filters"),
39
+ filter_on: Annotated[
40
+ str | None, Query(description="JSON string with filters")
41
+ ] = None,
40
42
  ) -> Response:
41
43
  ensemble = storage.get_ensemble(ensemble_id)
42
44
  dataframe = data_for_response(
@@ -80,7 +82,7 @@ async def get_response_misfits(
80
82
  index=[parse_index(x) for x in o["x_axis"]],
81
83
  )
82
84
  try:
83
- result_df = calculate_misfits_from_pandas(
85
+ result_df = calculate_signed_chi_squared_misfits(
84
86
  response_dict, observation_df, summary_misfits
85
87
  )
86
88
  except Exception as misfits_exc:
@@ -9,6 +9,7 @@ import traceback
9
9
  import uuid
10
10
  from base64 import b64decode
11
11
  from queue import SimpleQueue
12
+ from typing import Annotated
12
13
 
13
14
  from fastapi import (
14
15
  APIRouter,
@@ -153,7 +154,7 @@ def _log(request: Request) -> None:
153
154
 
154
155
  @router.get("/")
155
156
  def get_status(
156
- request: Request, credentials: HTTPBasicCredentials = Depends(security)
157
+ request: Request, credentials: Annotated[HTTPBasicCredentials, Depends(security)]
157
158
  ) -> PlainTextResponse:
158
159
  _log(request)
159
160
  _check_user(credentials)
@@ -162,7 +163,7 @@ def get_status(
162
163
 
163
164
  @router.get("/status")
164
165
  def experiment_status(
165
- request: Request, credentials: HTTPBasicCredentials = Depends(security)
166
+ request: Request, credentials: Annotated[HTTPBasicCredentials, Depends(security)]
166
167
  ) -> ExperimentStatus:
167
168
  _log(request)
168
169
  _check_user(credentials)
@@ -171,7 +172,7 @@ def experiment_status(
171
172
 
172
173
  @router.post("/" + EverEndpoints.stop)
173
174
  def stop(
174
- request: Request, credentials: HTTPBasicCredentials = Depends(security)
175
+ request: Request, credentials: Annotated[HTTPBasicCredentials, Depends(security)]
175
176
  ) -> Response:
176
177
  _log(request)
177
178
  _check_user(credentials)
@@ -185,7 +186,7 @@ def stop(
185
186
  async def start_experiment(
186
187
  request: Request,
187
188
  background_tasks: BackgroundTasks,
188
- credentials: HTTPBasicCredentials = Depends(security),
189
+ credentials: Annotated[HTTPBasicCredentials, Depends(security)],
189
190
  ) -> Response:
190
191
  _log(request)
191
192
  _check_user(credentials)
@@ -218,7 +219,7 @@ async def start_experiment(
218
219
 
219
220
  @router.get("/" + EverEndpoints.config_path)
220
221
  async def config_path(
221
- request: Request, credentials: HTTPBasicCredentials = Depends(security)
222
+ request: Request, credentials: Annotated[HTTPBasicCredentials, Depends(security)]
222
223
  ) -> JSONResponse:
223
224
  _log(request)
224
225
  _check_user(credentials)
@@ -237,7 +238,7 @@ async def config_path(
237
238
 
238
239
  @router.get("/" + EverEndpoints.start_time)
239
240
  async def start_time(
240
- request: Request, credentials: HTTPBasicCredentials = Depends(security)
241
+ request: Request, credentials: Annotated[HTTPBasicCredentials, Depends(security)]
241
242
  ) -> Response:
242
243
  _log(request)
243
244
  _check_user(credentials)
@@ -316,9 +317,11 @@ class ExperimentRunner:
316
317
  simulation_future = loop.run_in_executor(
317
318
  None,
318
319
  lambda: run_model.start_simulations_thread(
319
- EvaluatorServerConfig()
320
- if run_model.queue_config.queue_system == QueueSystem.LOCAL
321
- else EvaluatorServerConfig(use_ipc_protocol=False)
320
+ EvaluatorServerConfig(
321
+ use_ipc_protocol=run_model.queue_config.queue_system
322
+ == QueueSystem.LOCAL,
323
+ prioritize_private_ip_address=site_plugins.prioritize_private_ip_address,
324
+ )
322
325
  ),
323
326
  )
324
327
  while True:
@@ -29,7 +29,7 @@ def get_experiments(
29
29
  priors=create_priors(experiment),
30
30
  userdata={},
31
31
  parameters={
32
- group: [m.model_dump() for m in config.metadata]
32
+ group: config.model_dump()
33
33
  for group, config in experiment.parameter_configuration.items()
34
34
  if not isinstance(config, SurfaceConfig)
35
35
  },
@@ -65,7 +65,7 @@ def get_experiment_by_id(
65
65
  priors=create_priors(experiment),
66
66
  userdata={},
67
67
  parameters={
68
- group: [m.model_dump() for m in config.metadata]
68
+ group: config.model_dump()
69
69
  for group, config in experiment.parameter_configuration.items()
70
70
  },
71
71
  responses={
@@ -1,7 +1,7 @@
1
1
  import json
2
2
  import logging
3
3
  import operator
4
- from typing import Any
4
+ from typing import Annotated, Any
5
5
  from urllib.parse import unquote
6
6
  from uuid import UUID, uuid4
7
7
 
@@ -57,7 +57,9 @@ async def get_observations_for_response(
57
57
  storage: Storage = DEFAULT_STORAGE,
58
58
  ensemble_id: UUID,
59
59
  response_key: str,
60
- filter_on: str | None = Query(None, description="JSON string with filters"),
60
+ filter_on: Annotated[
61
+ str | None, Query(description="JSON string with filters")
62
+ ] = None,
61
63
  ) -> list[js.ObservationOut]:
62
64
  response_key = unquote(response_key)
63
65
  try:
@@ -136,13 +138,13 @@ def _get_observations(
136
138
  df = df.with_columns(pl.Series(name="x_axis", values=df.map_rows(x_axis_fn)))
137
139
  df = df.sort("x_axis")
138
140
 
139
- for obs_key, _obs_df in df.group_by("name"):
141
+ for obs_key, obs_df in df.group_by("name"):
140
142
  observations.append(
141
143
  {
142
144
  "name": obs_key[0],
143
- "values": _obs_df["values"].to_list(),
144
- "errors": _obs_df["errors"].to_list(),
145
- "x_axis": _obs_df["x_axis"].to_list(),
145
+ "values": obs_df["values"].to_list(),
146
+ "errors": obs_df["errors"].to_list(),
147
+ "x_axis": obs_df["x_axis"].to_list(),
146
148
  }
147
149
  )
148
150
 
@@ -113,27 +113,11 @@ def get_parameter_std_dev(
113
113
  return Response(content=buffer.getvalue(), media_type="application/octet-stream")
114
114
 
115
115
 
116
- def _extract_parameter_group_and_key(key: str) -> tuple[str, str] | tuple[None, None]:
117
- key = key.removeprefix("LOG10_")
118
- if ":" not in key:
119
- # Assume all incoming keys are in format group:key for now
120
- return None, None
121
-
122
- param_group, param_key = key.split(":", maxsplit=1)
123
- return param_group, param_key
124
-
125
-
126
116
  def data_for_parameter(ensemble: Ensemble, key: str) -> pd.DataFrame:
127
- group, _ = _extract_parameter_group_and_key(key)
128
- if group is None:
129
- logger.warning(
130
- f"Parameter with key '{key}' does not have a group, "
131
- "fetching data for all parameters"
132
- )
133
117
  try:
134
- df = ensemble.load_scalars(group)
118
+ df = ensemble.load_scalar_keys([key], transformed=True)
135
119
  if df.is_empty():
136
- logger.warning(f"No data found for parameter '{group}:{key}'")
120
+ logger.warning(f"No data found for parameter '{key}'")
137
121
  return pd.DataFrame()
138
122
  except KeyError as e:
139
123
  logger.error(e)
@@ -45,7 +45,9 @@ async def get_response(
45
45
  storage: Storage = DEFAULT_STORAGE,
46
46
  ensemble_id: UUID,
47
47
  response_key: str,
48
- filter_on: str | None = Query(None, description="JSON string with filters"),
48
+ filter_on: Annotated[
49
+ str | None, Query(description="JSON string with filters")
50
+ ] = None,
49
51
  accept: Annotated[str | None, Header()] = None,
50
52
  ) -> Response:
51
53
  try:
@@ -96,6 +98,7 @@ async def get_response(
96
98
  response_to_pandas_x_axis_fns: dict[str, Callable[[tuple[Any, ...]], Any]] = {
97
99
  "summary": lambda t: pd.Timestamp(t[2]).isoformat(),
98
100
  "gen_data": lambda t: str(t[3]),
101
+ "rft": lambda t: str(t[4]),
99
102
  }
100
103
 
101
104
 
@@ -116,7 +119,7 @@ def _extract_response_type_and_key(
116
119
 
117
120
  def data_for_response(
118
121
  ensemble: Ensemble, key: str, filter_on: dict[str, Any] | None = None
119
- ) -> pd.DataFrame:
122
+ ) -> pd.DataFrame | pd.Series:
120
123
  response_key, response_type = _extract_response_type_and_key(
121
124
  key, ensemble.experiment.response_key_to_response_type
122
125
  )
@@ -147,10 +150,25 @@ def data_for_response(
147
150
  # This performs the same aggragation by mean of duplicate values
148
151
  # as in ert/analysis/_es_update.py
149
152
  df = df.groupby(["Date", "Realization"]).mean()
150
- data = df.unstack(level="Date")
151
- data.columns = data.columns.droplevel(0)
153
+ data = df.reset_index().pivot_table(
154
+ index="Realization", columns="Date", values=df.columns[0]
155
+ )
152
156
  return data.astype(float)
153
157
 
158
+ if response_type == "rft":
159
+ return (
160
+ ensemble.load_responses(
161
+ response_key,
162
+ tuple(realizations_with_responses),
163
+ )
164
+ .rename({"realization": "Realization"})
165
+ .select(["Realization", "depth", "values"])
166
+ .unique()
167
+ .to_pandas()
168
+ .pivot_table(index="Realization", columns="depth", values="values")
169
+ .reset_index(drop=True)
170
+ )
171
+
154
172
  if response_type == "gen_data":
155
173
  data = ensemble.load_responses(response_key, tuple(realizations_with_responses))
156
174
 
@@ -159,7 +177,7 @@ def data_for_response(
159
177
  assert "report_step" in filter_on
160
178
  report_step = int(filter_on["report_step"])
161
179
  vals = data.filter(pl.col("report_step").eq(report_step))
162
- pivoted = vals.drop("response_key", "report_step").pivot(
180
+ pivoted = vals.drop("response_key", "report_step").pivot( # noqa: PD010
163
181
  on="index", values="values"
164
182
  )
165
183
  data = pivoted.to_pandas().set_index("realization")
@@ -169,3 +187,4 @@ def data_for_response(
169
187
 
170
188
  except (ValueError, KeyError, ColumnNotFoundError):
171
189
  return pd.DataFrame()
190
+ return pd.DataFrame()
@@ -24,6 +24,6 @@ class ExperimentOut(_Experiment):
24
24
  ensemble_ids: list[UUID]
25
25
  priors: Mapping[str, dict[str, Any]]
26
26
  userdata: Mapping[str, Any]
27
- parameters: Mapping[str, list[dict[str, Any]]]
27
+ parameters: Mapping[str, dict[str, Any]]
28
28
  responses: Mapping[str, list[dict[str, Any]]]
29
29
  observations: Mapping[str, dict[str, list[str]]]
@@ -141,13 +141,14 @@ class MeasuredData:
141
141
 
142
142
  # Pandas differentiates vs int and str keys.
143
143
  # Legacy-wise we use int keys for realizations
144
- pddf.rename(
145
- columns={str(k): int(k) for k in active_realizations},
146
- inplace=True,
144
+ pddf = (
145
+ pddf.rename(
146
+ columns={str(k): int(k) for k in active_realizations},
147
+ )
148
+ .set_index(["observation_key", "key_index"])
149
+ .transpose()
147
150
  )
148
151
 
149
- pddf = pddf.set_index(["observation_key", "key_index"]).transpose()
150
-
151
152
  return pddf
152
153
 
153
154
 
@@ -2,7 +2,13 @@ from ._ensemble import LegacyEnsemble as Ensemble
2
2
  from ._ensemble import Realization
3
3
  from .config import EvaluatorServerConfig
4
4
  from .evaluator import EnsembleEvaluator
5
- from .event import EndEvent, FullSnapshotEvent, SnapshotUpdateEvent, WarningEvent
5
+ from .event import (
6
+ EndEvent,
7
+ FullSnapshotEvent,
8
+ SnapshotUpdateEvent,
9
+ StartEvent,
10
+ WarningEvent,
11
+ )
6
12
  from .snapshot import EnsembleSnapshot, FMStepSnapshot, RealizationSnapshot
7
13
 
8
14
  __all__ = [
@@ -16,5 +22,6 @@ __all__ = [
16
22
  "Realization",
17
23
  "RealizationSnapshot",
18
24
  "SnapshotUpdateEvent",
25
+ "StartEvent",
19
26
  "WarningEvent",
20
27
  ]
@@ -27,6 +27,7 @@ class EvaluatorServerConfig:
27
27
  use_token: bool = True,
28
28
  host: str | None = None,
29
29
  use_ipc_protocol: bool = True,
30
+ prioritize_private_ip_address: bool = False,
30
31
  ) -> None:
31
32
  self.host: str | None = host
32
33
  self.router_port: int | None = None
@@ -50,7 +51,7 @@ class EvaluatorServerConfig:
50
51
  if use_ipc_protocol:
51
52
  self.uri = f"ipc:///tmp/socket-{uuid.uuid4().hex[:8]}"
52
53
  elif self.host is None:
53
- self.host = get_ip_address()
54
+ self.host = get_ip_address(prioritize_private_ip_address)
54
55
 
55
56
  if use_token:
56
57
  self.server_public_key, self.server_secret_key = zmq.curve_keypair()