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.
- ert/__main__.py +63 -94
- ert/analysis/_es_update.py +14 -11
- ert/config/_create_observation_dataframes.py +262 -23
- ert/config/_observations.py +153 -181
- ert/config/_read_summary.py +5 -4
- ert/config/ert_config.py +56 -1
- ert/config/parsing/observations_parser.py +0 -6
- ert/config/rft_config.py +1 -1
- ert/dark_storage/compute/__init__.py +0 -0
- ert/dark_storage/compute/misfits.py +42 -0
- ert/dark_storage/endpoints/__init__.py +2 -0
- ert/dark_storage/endpoints/compute/__init__.py +0 -0
- ert/dark_storage/endpoints/compute/misfits.py +95 -0
- ert/dark_storage/endpoints/experiments.py +3 -0
- ert/dark_storage/json_schema/experiment.py +1 -0
- ert/gui/main_window.py +0 -2
- ert/gui/tools/manage_experiments/export_dialog.py +0 -4
- ert/gui/tools/manage_experiments/storage_info_widget.py +5 -1
- ert/gui/tools/plot/plot_api.py +10 -10
- ert/gui/tools/plot/plot_widget.py +0 -5
- ert/gui/tools/plot/plot_window.py +1 -1
- ert/services/__init__.py +3 -7
- ert/services/_base_service.py +387 -0
- ert/services/_storage_main.py +22 -59
- ert/services/ert_server.py +24 -186
- ert/services/webviz_ert_service.py +20 -0
- ert/shared/storage/command.py +38 -0
- ert/shared/storage/extraction.py +42 -0
- ert/shared/version.py +3 -3
- ert/storage/local_ensemble.py +95 -2
- ert/storage/local_experiment.py +16 -0
- ert/storage/local_storage.py +1 -3
- ert/utils/__init__.py +0 -20
- {ert-19.0.0rc1.dist-info → ert-19.0.0rc3.dist-info}/METADATA +2 -2
- {ert-19.0.0rc1.dist-info → ert-19.0.0rc3.dist-info}/RECORD +46 -41
- {ert-19.0.0rc1.dist-info → ert-19.0.0rc3.dist-info}/WHEEL +1 -1
- everest/bin/everest_script.py +5 -5
- everest/bin/kill_script.py +2 -2
- everest/bin/monitor_script.py +2 -2
- everest/bin/utils.py +4 -4
- everest/detached/everserver.py +6 -6
- everest/gui/main_window.py +2 -2
- everest/util/__init__.py +19 -1
- ert/config/observation_config_migrations.py +0 -793
- ert/storage/migration/to22.py +0 -18
- {ert-19.0.0rc1.dist-info → ert-19.0.0rc3.dist-info}/entry_points.txt +0 -0
- {ert-19.0.0rc1.dist-info → ert-19.0.0rc3.dist-info}/licenses/COPYING +0 -0
- {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"]
|
ert/gui/tools/plot/plot_api.py
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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", "
|
|
4
|
+
__all__ = ["ErtServer", "WebvizErt"]
|