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
@@ -0,0 +1,42 @@
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,5 +1,6 @@
1
1
  from fastapi import APIRouter
2
2
 
3
+ from .compute.misfits import router as misfits_router
3
4
  from .ensembles import router as ensembles_router
4
5
  from .experiment_server import router as experiment_server_router
5
6
  from .experiments import router as experiments_router
@@ -14,6 +15,7 @@ router.include_router(experiments_router)
14
15
  router.include_router(ensembles_router)
15
16
  router.include_router(observations_router)
16
17
  router.include_router(updates_router)
18
+ router.include_router(misfits_router)
17
19
  router.include_router(parameters_router)
18
20
  router.include_router(responses_router)
19
21
  router.include_router(experiment_server_router)
File without changes
@@ -0,0 +1,95 @@
1
+ import json
2
+ from datetime import datetime
3
+ from typing import Annotated, Any
4
+ from uuid import UUID
5
+
6
+ import pandas as pd
7
+ from dateutil.parser import parse
8
+ from fastapi import APIRouter, Depends, Query, status
9
+ from fastapi.responses import Response
10
+
11
+ from ert.dark_storage import exceptions as exc
12
+ from ert.dark_storage.common import get_storage
13
+ from ert.dark_storage.compute.misfits import calculate_signed_chi_squared_misfits
14
+ from ert.dark_storage.endpoints.observations import (
15
+ _get_observations,
16
+ )
17
+ from ert.dark_storage.endpoints.responses import data_for_response
18
+ from ert.storage import Storage
19
+
20
+ router = APIRouter(tags=["misfits"])
21
+ DEFAULT_STORAGEREADER = Depends(get_storage)
22
+
23
+
24
+ @router.get(
25
+ "/compute/misfits",
26
+ responses={
27
+ status.HTTP_200_OK: {
28
+ "content": {"text/csv": {}},
29
+ }
30
+ },
31
+ )
32
+ async def get_response_misfits(
33
+ *,
34
+ storage: Storage = DEFAULT_STORAGEREADER,
35
+ ensemble_id: UUID,
36
+ response_name: str,
37
+ realization_index: int | None = None,
38
+ summary_misfits: bool = False,
39
+ filter_on: Annotated[
40
+ str | None, Query(description="JSON string with filters")
41
+ ] = None,
42
+ ) -> Response:
43
+ ensemble = storage.get_ensemble(ensemble_id)
44
+ dataframe = data_for_response(
45
+ ensemble,
46
+ response_name,
47
+ json.loads(filter_on) if filter_on is not None else None,
48
+ )
49
+ if realization_index is not None:
50
+ dataframe = pd.DataFrame(dataframe.loc[realization_index]).T
51
+
52
+ response_dict = {}
53
+ for index, data in dataframe.iterrows():
54
+ data_df = pd.DataFrame(data).T
55
+ response_dict[index] = data_df
56
+
57
+ experiment = ensemble.experiment
58
+ response_type = experiment.response_key_to_response_type[response_name]
59
+ obs_keys = experiment.response_key_to_observation_key[response_type].get(
60
+ response_name, []
61
+ )
62
+ obs = _get_observations(
63
+ ensemble.experiment,
64
+ obs_keys,
65
+ json.loads(filter_on) if filter_on is not None else None,
66
+ )
67
+
68
+ if not obs_keys:
69
+ raise ValueError(f"No observations for key {response_name}")
70
+ if not obs:
71
+ raise ValueError(f"Cant fetch observations for key {response_name}")
72
+ o = obs[0]
73
+
74
+ def parse_index(x: Any) -> int | datetime:
75
+ try:
76
+ return int(x)
77
+ except ValueError:
78
+ return parse(x)
79
+
80
+ observation_df = pd.DataFrame(
81
+ data={"values": o["values"], "errors": o["errors"]},
82
+ index=[parse_index(x) for x in o["x_axis"]],
83
+ )
84
+ try:
85
+ result_df = calculate_signed_chi_squared_misfits(
86
+ response_dict, observation_df, summary_misfits
87
+ )
88
+ except Exception as misfits_exc:
89
+ raise exc.UnprocessableError(
90
+ f"Unable to compute misfits: {misfits_exc}"
91
+ ) from misfits_exc
92
+ return Response(
93
+ content=result_df.to_csv().encode(),
94
+ media_type="text/csv",
95
+ )
@@ -6,6 +6,7 @@ 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
9
10
  from ert.storage import Storage
10
11
 
11
12
  router = APIRouter(tags=["experiment"])
@@ -25,6 +26,7 @@ def get_experiments(
25
26
  id=experiment.id,
26
27
  name=experiment.name,
27
28
  ensemble_ids=[ens.id for ens in experiment.ensembles],
29
+ priors=create_priors(experiment),
28
30
  userdata={},
29
31
  parameters={
30
32
  group: config.model_dump()
@@ -60,6 +62,7 @@ def get_experiment_by_id(
60
62
  name=experiment.name,
61
63
  id=experiment.id,
62
64
  ensemble_ids=[ens.id for ens in experiment.ensembles],
65
+ priors=create_priors(experiment),
63
66
  userdata={},
64
67
  parameters={
65
68
  group: config.model_dump()
@@ -22,6 +22,7 @@ class ExperimentIn(_Experiment):
22
22
  class ExperimentOut(_Experiment):
23
23
  id: UUID
24
24
  ensemble_ids: list[UUID]
25
+ priors: Mapping[str, dict[str, Any]]
25
26
  userdata: Mapping[str, Any]
26
27
  parameters: Mapping[str, dict[str, Any]]
27
28
  responses: Mapping[str, list[dict[str, Any]]]
ert/gui/main_window.py CHANGED
@@ -116,10 +116,8 @@ class ErtMainWindow(QMainWindow):
116
116
 
117
117
  if is_dark_mode():
118
118
  self.side_frame.setStyleSheet("background-color: rgb(64, 64, 64);")
119
- logger.info("Running Ert with dark mode")
120
119
  else:
121
120
  self.side_frame.setStyleSheet("background-color: lightgray;")
122
- logger.info("Running Ert with light mode")
123
121
 
124
122
  if is_high_contrast_mode():
125
123
  msg_box = QMessageBox()
@@ -1,5 +1,4 @@
1
1
  import contextlib
2
- import logging
3
2
  from pathlib import Path
4
3
  from typing import cast
5
4
 
@@ -23,8 +22,6 @@ from PyQt6.QtWidgets import (
23
22
  QWidget,
24
23
  )
25
24
 
26
- logger = logging.getLogger(__name__)
27
-
28
25
 
29
26
  class ExportDialog(QDialog):
30
27
  """Base dialog for exporting ensemble-related data to files."""
@@ -88,7 +85,6 @@ class ExportDialog(QDialog):
88
85
  f"<span style='color: red;'>Could not export data: {e!s}</span><br>"
89
86
  )
90
87
  finally:
91
- logger.info(f"Export dialog used: '{self.windowTitle()}'")
92
88
  self._export_button.setEnabled(True)
93
89
 
94
90
  @Slot()
@@ -11,6 +11,7 @@ from polars import DataFrame
11
11
  from PyQt6.QtCore import Qt
12
12
  from PyQt6.QtCore import pyqtSlot as Slot
13
13
  from PyQt6.QtWidgets import (
14
+ QAbstractItemView,
14
15
  QFrame,
15
16
  QHBoxLayout,
16
17
  QLabel,
@@ -165,6 +166,9 @@ class _EnsembleWidget(QWidget):
165
166
  observations_frame.setLayout(observations_layout)
166
167
 
167
168
  self._parameters_table = QTableWidget()
169
+ self._parameters_table.setEditTriggers(
170
+ QAbstractItemView.EditTrigger.NoEditTriggers
171
+ )
168
172
  self._export_params_button = QPushButton("Export...")
169
173
  self._export_params_button.clicked.connect(self.onClickExportParameters)
170
174
 
@@ -249,8 +253,8 @@ class _EnsembleWidget(QWidget):
249
253
  df[x_axis_col]
250
254
  .map_elements(
251
255
  lambda x: response_config.display_column(x, x_axis_col),
256
+ return_dtype=pl.String,
252
257
  )
253
- .cast(pl.String)
254
258
  .alias("temp")
255
259
  ).filter(pl.col("temp").eq(observation_label))[
256
260
  [x for x in df.columns if x != "temp"]
@@ -19,7 +19,7 @@ from pandas.errors import ParserError
19
19
  from resfo_utilities import history_key
20
20
 
21
21
  from ert.config import ParameterConfig, ResponseMetadata
22
- from ert.services import create_ertserver_client
22
+ from ert.services import ErtServer
23
23
  from ert.storage.local_experiment import _parameters_adapter as parameter_config_adapter
24
24
  from ert.storage.realization_storage_state import RealizationStorageState
25
25
 
@@ -51,13 +51,13 @@ class PlotApiKeyDefinition(NamedTuple):
51
51
 
52
52
  class PlotApi:
53
53
  def __init__(self, ens_path: Path) -> None:
54
- self.ens_path: Path = ens_path
54
+ self.ens_path = ens_path
55
55
  self._all_ensembles: list[EnsembleObject] | None = None
56
56
  self._timeout = 120
57
57
 
58
58
  @property
59
59
  def api_version(self) -> str:
60
- with create_ertserver_client(self.ens_path) as client:
60
+ with ErtServer.session(project=self.ens_path) as client:
61
61
  try:
62
62
  response = client.get("/version", timeout=self._timeout)
63
63
  self._check_response(response)
@@ -83,7 +83,7 @@ class PlotApi:
83
83
  return self._all_ensembles
84
84
 
85
85
  self._all_ensembles = []
86
- with create_ertserver_client(self.ens_path) as client:
86
+ with ErtServer.session(project=self.ens_path) as client:
87
87
  try:
88
88
  response = client.get("/experiments", timeout=self._timeout)
89
89
  self._check_response(response)
@@ -139,7 +139,7 @@ class PlotApi:
139
139
  all_keys: dict[str, PlotApiKeyDefinition] = {}
140
140
  all_params = {}
141
141
 
142
- with create_ertserver_client(self.ens_path) as client:
142
+ with ErtServer.session(project=self.ens_path) as client:
143
143
  response = client.get("/experiments", timeout=self._timeout)
144
144
  self._check_response(response)
145
145
 
@@ -166,7 +166,7 @@ class PlotApi:
166
166
  def responses_api_key_defs(self) -> list[PlotApiKeyDefinition]:
167
167
  key_defs: dict[str, PlotApiKeyDefinition] = {}
168
168
 
169
- with create_ertserver_client(self.ens_path) as client:
169
+ with ErtServer.session(project=self.ens_path) as client:
170
170
  response = client.get("/experiments", timeout=self._timeout)
171
171
  self._check_response(response)
172
172
 
@@ -228,7 +228,7 @@ class PlotApi:
228
228
  response_key: str,
229
229
  filter_on: dict[str, Any] | None = None,
230
230
  ) -> pd.DataFrame:
231
- with create_ertserver_client(self.ens_path) as client:
231
+ with ErtServer.session(project=self.ens_path) as client:
232
232
  response = client.get(
233
233
  f"/ensembles/{ensemble_id}/responses/{PlotApi.escape(response_key)}",
234
234
  headers={"accept": "application/x-parquet"},
@@ -256,7 +256,7 @@ class PlotApi:
256
256
  return df
257
257
 
258
258
  def data_for_parameter(self, ensemble_id: str, parameter_key: str) -> pd.DataFrame:
259
- with create_ertserver_client(self.ens_path) as client:
259
+ with ErtServer.session(project=self.ens_path) as client:
260
260
  parameter = client.get(
261
261
  f"/ensembles/{ensemble_id}/parameters/{PlotApi.escape(parameter_key)}",
262
262
  headers={"accept": "application/x-parquet"},
@@ -298,7 +298,7 @@ class PlotApi:
298
298
  assert key_def.response_metadata is not None
299
299
  actual_response_key = key_def.response_metadata.response_key
300
300
  filter_on = key_def.filter_on
301
- with create_ertserver_client(self.ens_path) as client:
301
+ with ErtServer.session(project=self.ens_path) as client:
302
302
  response = client.get(
303
303
  f"/ensembles/{ensemble.id}/responses/{PlotApi.escape(actual_response_key)}/observations",
304
304
  timeout=self._timeout,
@@ -386,7 +386,7 @@ class PlotApi:
386
386
  if not ensemble:
387
387
  return np.array([])
388
388
 
389
- with create_ertserver_client(self.ens_path) as client:
389
+ with ErtServer.session(project=self.ens_path) as client:
390
390
  response = client.get(
391
391
  f"/ensembles/{ensemble.id}/parameters/{PlotApi.escape(key)}/std_dev",
392
392
  params={"z": z},
@@ -153,7 +153,6 @@ class PlotWidget(QWidget):
153
153
  # only for histogram plot see _sync_log_checkbox
154
154
  self._log_checkbox.setVisible(False)
155
155
  self._log_checkbox.setToolTip("Toggle data domain to log scale and back")
156
- self._log_checkbox.clicked.connect(self.logLogScaleButtonUsage)
157
156
 
158
157
  log_checkbox_row = QHBoxLayout()
159
158
  log_checkbox_row.addWidget(self._log_checkbox)
@@ -189,10 +188,6 @@ class PlotWidget(QWidget):
189
188
  def name(self) -> str:
190
189
  return self._name
191
190
 
192
- def logLogScaleButtonUsage(self) -> None:
193
- logger.info(f"Plotwidget utility used: 'Log scale button' in tab '{self.name}'")
194
- self._log_checkbox.clicked.disconnect() # Log only once
195
-
196
191
  def updatePlot(
197
192
  self,
198
193
  plot_context: "PlotContext",
@@ -25,7 +25,7 @@ from PyQt6.QtWidgets import (
25
25
  from ert.config.field import Field
26
26
  from ert.dark_storage.common import get_storage_api_version
27
27
  from ert.gui.ertwidgets import CopyButton, showWaitCursorWhileWaiting
28
- from ert.services import ServerBootFail
28
+ from ert.services._base_service import ServerBootFail
29
29
  from ert.utils import log_duration
30
30
 
31
31
  from .customize import PlotCustomizer
ert/services/__init__.py CHANGED
@@ -1,8 +1,4 @@
1
- from .ert_server import (
2
- ErtServer,
3
- ErtServerExit,
4
- ServerBootFail,
5
- create_ertserver_client,
6
- )
1
+ from .ert_server import ErtServer
2
+ from .webviz_ert_service import WebvizErt
7
3
 
8
- __all__ = ["ErtServer", "ErtServerExit", "ServerBootFail", "create_ertserver_client"]
4
+ __all__ = ["ErtServer", "WebvizErt"]