ert 18.0.9__py3-none-any.whl → 19.0.0rc0__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/forward_model_runner/client.py +6 -2
- ert/__main__.py +20 -6
- ert/analysis/_es_update.py +6 -19
- ert/cli/main.py +7 -3
- ert/config/__init__.py +3 -4
- ert/config/_create_observation_dataframes.py +57 -8
- ert/config/_get_num_cpu.py +1 -1
- ert/config/_observations.py +77 -1
- ert/config/distribution.py +1 -1
- ert/config/ensemble_config.py +3 -3
- ert/config/ert_config.py +50 -8
- ert/config/{ext_param_config.py → everest_control.py} +8 -12
- ert/config/everest_response.py +3 -5
- ert/config/field.py +76 -14
- ert/config/forward_model_step.py +12 -9
- ert/config/gen_data_config.py +3 -4
- ert/config/gen_kw_config.py +2 -12
- ert/config/parameter_config.py +1 -16
- ert/config/parsing/_option_dict.py +10 -2
- ert/config/parsing/config_keywords.py +1 -0
- ert/config/parsing/config_schema.py +8 -0
- ert/config/parsing/config_schema_deprecations.py +14 -3
- ert/config/parsing/config_schema_item.py +12 -3
- ert/config/parsing/context_values.py +3 -3
- ert/config/parsing/file_context_token.py +1 -1
- ert/config/parsing/observations_parser.py +6 -2
- ert/config/parsing/queue_system.py +9 -0
- ert/config/queue_config.py +0 -1
- ert/config/response_config.py +0 -1
- ert/config/rft_config.py +78 -33
- ert/config/summary_config.py +1 -2
- ert/config/surface_config.py +59 -16
- ert/dark_storage/common.py +1 -1
- ert/dark_storage/compute/misfits.py +4 -1
- ert/dark_storage/endpoints/compute/misfits.py +4 -2
- ert/dark_storage/endpoints/experiment_server.py +12 -9
- ert/dark_storage/endpoints/experiments.py +2 -2
- ert/dark_storage/endpoints/observations.py +4 -2
- ert/dark_storage/endpoints/parameters.py +2 -18
- ert/dark_storage/endpoints/responses.py +10 -5
- ert/dark_storage/json_schema/experiment.py +1 -1
- ert/data/_measured_data.py +6 -5
- ert/ensemble_evaluator/config.py +2 -1
- ert/field_utils/field_utils.py +1 -1
- ert/field_utils/grdecl_io.py +9 -26
- ert/field_utils/roff_io.py +1 -1
- ert/gui/__init__.py +5 -2
- ert/gui/ertnotifier.py +1 -1
- ert/gui/ertwidgets/pathchooser.py +0 -3
- ert/gui/ertwidgets/suggestor/suggestor.py +63 -30
- ert/gui/main.py +27 -5
- ert/gui/main_window.py +0 -5
- ert/gui/simulation/experiment_panel.py +12 -7
- ert/gui/simulation/run_dialog.py +2 -16
- ert/gui/summarypanel.py +0 -19
- ert/gui/tools/manage_experiments/export_dialog.py +136 -0
- ert/gui/tools/manage_experiments/storage_info_widget.py +110 -9
- ert/gui/tools/plot/plot_api.py +24 -15
- ert/gui/tools/plot/plot_widget.py +10 -2
- ert/gui/tools/plot/plot_window.py +26 -18
- ert/gui/tools/plot/plottery/plots/__init__.py +2 -0
- ert/gui/tools/plot/plottery/plots/cesp.py +3 -1
- ert/gui/tools/plot/plottery/plots/distribution.py +6 -1
- ert/gui/tools/plot/plottery/plots/ensemble.py +3 -1
- ert/gui/tools/plot/plottery/plots/gaussian_kde.py +12 -2
- ert/gui/tools/plot/plottery/plots/histogram.py +3 -1
- ert/gui/tools/plot/plottery/plots/misfits.py +436 -0
- ert/gui/tools/plot/plottery/plots/observations.py +18 -4
- ert/gui/tools/plot/plottery/plots/statistics.py +3 -1
- ert/gui/tools/plot/plottery/plots/std_dev.py +3 -1
- ert/plugins/hook_implementations/workflows/csv_export.py +2 -3
- ert/plugins/plugin_manager.py +4 -0
- ert/resources/forward_models/run_reservoirsimulator.py +8 -3
- ert/run_models/_create_run_path.py +3 -3
- ert/run_models/everest_run_model.py +13 -11
- ert/run_models/initial_ensemble_run_model.py +2 -2
- ert/run_models/run_model.py +30 -1
- ert/services/_base_service.py +6 -5
- ert/services/ert_server.py +4 -4
- ert/shared/_doc_utils/__init__.py +4 -2
- ert/shared/net_utils.py +43 -18
- ert/shared/version.py +3 -3
- ert/storage/__init__.py +2 -0
- ert/storage/local_ensemble.py +13 -7
- ert/storage/local_experiment.py +2 -2
- ert/storage/local_storage.py +41 -25
- ert/storage/migration/to11.py +1 -1
- ert/storage/migration/to18.py +0 -1
- ert/storage/migration/to19.py +34 -0
- ert/storage/migration/to20.py +23 -0
- ert/storage/migration/to21.py +25 -0
- ert/workflow_runner.py +2 -1
- {ert-18.0.9.dist-info → ert-19.0.0rc0.dist-info}/METADATA +1 -1
- {ert-18.0.9.dist-info → ert-19.0.0rc0.dist-info}/RECORD +112 -112
- {ert-18.0.9.dist-info → ert-19.0.0rc0.dist-info}/WHEEL +1 -1
- everest/bin/everlint_script.py +0 -2
- everest/bin/utils.py +2 -1
- everest/bin/visualization_script.py +4 -11
- everest/config/control_config.py +4 -4
- everest/config/control_variable_config.py +2 -2
- everest/config/everest_config.py +9 -0
- everest/config/utils.py +2 -2
- everest/config/validation_utils.py +7 -1
- everest/config_file_loader.py +0 -2
- everest/detached/client.py +3 -3
- everest/everest_storage.py +0 -2
- everest/gui/everest_client.py +2 -2
- everest/optimizer/everest2ropt.py +4 -4
- everest/optimizer/opt_model_transforms.py +2 -2
- ert/config/violations.py +0 -0
- ert/gui/tools/export/__init__.py +0 -3
- ert/gui/tools/export/export_panel.py +0 -83
- ert/gui/tools/export/export_tool.py +0 -69
- ert/gui/tools/export/exporter.py +0 -36
- {ert-18.0.9.dist-info → ert-19.0.0rc0.dist-info}/entry_points.txt +0 -0
- {ert-18.0.9.dist-info → ert-19.0.0rc0.dist-info}/licenses/COPYING +0 -0
- {ert-18.0.9.dist-info → ert-19.0.0rc0.dist-info}/top_level.txt +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import contextlib
|
|
1
2
|
import json
|
|
2
3
|
from enum import IntEnum
|
|
3
4
|
|
|
@@ -6,13 +7,17 @@ import seaborn as sns
|
|
|
6
7
|
import yaml
|
|
7
8
|
from matplotlib.backends.backend_qt5agg import FigureCanvas # type: ignore
|
|
8
9
|
from matplotlib.figure import Figure
|
|
10
|
+
from polars import DataFrame
|
|
9
11
|
from PyQt6.QtCore import Qt
|
|
10
12
|
from PyQt6.QtCore import pyqtSlot as Slot
|
|
11
13
|
from PyQt6.QtWidgets import (
|
|
12
14
|
QFrame,
|
|
13
15
|
QHBoxLayout,
|
|
14
16
|
QLabel,
|
|
17
|
+
QPushButton,
|
|
15
18
|
QStackedLayout,
|
|
19
|
+
QTableWidget,
|
|
20
|
+
QTableWidgetItem,
|
|
16
21
|
QTabWidget,
|
|
17
22
|
QTextEdit,
|
|
18
23
|
QTreeWidget,
|
|
@@ -21,8 +26,11 @@ from PyQt6.QtWidgets import (
|
|
|
21
26
|
QWidget,
|
|
22
27
|
)
|
|
23
28
|
|
|
29
|
+
from ert import LibresFacade
|
|
24
30
|
from ert.storage import Ensemble, Experiment, RealizationStorageState
|
|
25
31
|
|
|
32
|
+
from .export_dialog import ExportDialog
|
|
33
|
+
|
|
26
34
|
|
|
27
35
|
class _WidgetType(IntEnum):
|
|
28
36
|
EMPTY_WIDGET = 0
|
|
@@ -42,6 +50,8 @@ class _EnsembleWidgetTabs(IntEnum):
|
|
|
42
50
|
ENSEMBLE_TAB = 0
|
|
43
51
|
STATE_TAB = 1
|
|
44
52
|
OBSERVATIONS_TAB = 2
|
|
53
|
+
PARAMETERS_TAB = 3
|
|
54
|
+
MISFIT_TAB = 4
|
|
45
55
|
|
|
46
56
|
|
|
47
57
|
class _ExperimentWidget(QWidget):
|
|
@@ -126,12 +136,15 @@ class _EnsembleWidget(QWidget):
|
|
|
126
136
|
|
|
127
137
|
info_frame.setLayout(info_layout)
|
|
128
138
|
|
|
139
|
+
state_frame = QFrame()
|
|
140
|
+
state_layout = QHBoxLayout()
|
|
129
141
|
self._state_text_edit = QTextEdit()
|
|
130
142
|
self._state_text_edit.setReadOnly(True)
|
|
131
143
|
self._state_text_edit.setObjectName("ensemble_state_text")
|
|
144
|
+
state_layout.addWidget(self._state_text_edit)
|
|
145
|
+
state_frame.setLayout(state_layout)
|
|
132
146
|
|
|
133
147
|
observations_frame = QFrame()
|
|
134
|
-
|
|
135
148
|
self._observations_tree_widget = QTreeWidget(self)
|
|
136
149
|
self._observations_tree_widget.currentItemChanged.connect(
|
|
137
150
|
self._currentItemChanged
|
|
@@ -151,15 +164,35 @@ class _EnsembleWidget(QWidget):
|
|
|
151
164
|
observations_layout.addWidget(self._canvas)
|
|
152
165
|
observations_frame.setLayout(observations_layout)
|
|
153
166
|
|
|
167
|
+
self._parameters_table = QTableWidget()
|
|
168
|
+
self._export_params_button = QPushButton("Export...")
|
|
169
|
+
self._export_params_button.clicked.connect(self.onClickExportParameters)
|
|
170
|
+
|
|
171
|
+
parameters_frame = self.create_export_frame(
|
|
172
|
+
self._parameters_table, self._export_params_button
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
self._misfit_table = QTableWidget()
|
|
176
|
+
self._export_misfit_button = QPushButton("Export...")
|
|
177
|
+
self._export_misfit_button.clicked.connect(self.onClickExportMisfit)
|
|
178
|
+
|
|
179
|
+
misfit_frame = self.create_export_frame(
|
|
180
|
+
self._misfit_table, self._export_misfit_button
|
|
181
|
+
)
|
|
182
|
+
|
|
154
183
|
self._tab_widget = QTabWidget()
|
|
155
184
|
self._tab_widget.insertTab(
|
|
156
185
|
_EnsembleWidgetTabs.ENSEMBLE_TAB, info_frame, "Ensemble"
|
|
157
186
|
)
|
|
187
|
+
self._tab_widget.insertTab(_EnsembleWidgetTabs.STATE_TAB, state_frame, "State")
|
|
158
188
|
self._tab_widget.insertTab(
|
|
159
|
-
_EnsembleWidgetTabs.
|
|
189
|
+
_EnsembleWidgetTabs.OBSERVATIONS_TAB, observations_frame, "Observations"
|
|
160
190
|
)
|
|
161
191
|
self._tab_widget.insertTab(
|
|
162
|
-
_EnsembleWidgetTabs.
|
|
192
|
+
_EnsembleWidgetTabs.PARAMETERS_TAB, parameters_frame, "Parameters"
|
|
193
|
+
)
|
|
194
|
+
self._tab_widget.insertTab(
|
|
195
|
+
_EnsembleWidgetTabs.MISFIT_TAB, misfit_frame, "Misfit"
|
|
163
196
|
)
|
|
164
197
|
self._tab_widget.currentChanged.connect(self._currentTabChanged)
|
|
165
198
|
|
|
@@ -168,6 +201,17 @@ class _EnsembleWidget(QWidget):
|
|
|
168
201
|
|
|
169
202
|
self.setLayout(layout)
|
|
170
203
|
|
|
204
|
+
def create_export_frame(self, table: QTableWidget, button: QPushButton) -> QFrame:
|
|
205
|
+
export_frame = QFrame()
|
|
206
|
+
export_layout = QVBoxLayout()
|
|
207
|
+
vertical_header = table.verticalHeader()
|
|
208
|
+
assert vertical_header is not None
|
|
209
|
+
vertical_header.setVisible(False)
|
|
210
|
+
export_layout.addWidget(table)
|
|
211
|
+
export_layout.addWidget(button)
|
|
212
|
+
export_frame.setLayout(export_layout)
|
|
213
|
+
return export_frame
|
|
214
|
+
|
|
171
215
|
def _currentItemChanged(
|
|
172
216
|
self, selected: QTreeWidgetItem, _: QTreeWidgetItem
|
|
173
217
|
) -> None:
|
|
@@ -356,12 +400,53 @@ class _EnsembleWidget(QWidget):
|
|
|
356
400
|
0, Qt.SortOrder.AscendingOrder
|
|
357
401
|
)
|
|
358
402
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
403
|
+
for i in range(self._observations_tree_widget.topLevelItemCount()):
|
|
404
|
+
item = self._observations_tree_widget.topLevelItem(i)
|
|
405
|
+
assert item is not None
|
|
406
|
+
if item.childCount() > 0:
|
|
407
|
+
self._observations_tree_widget.setCurrentItem(item.child(0))
|
|
408
|
+
break
|
|
409
|
+
|
|
410
|
+
elif index in {
|
|
411
|
+
_EnsembleWidgetTabs.PARAMETERS_TAB,
|
|
412
|
+
_EnsembleWidgetTabs.MISFIT_TAB,
|
|
413
|
+
}:
|
|
414
|
+
assert self._ensemble is not None
|
|
415
|
+
|
|
416
|
+
df: pl.DataFrame = pl.DataFrame()
|
|
417
|
+
with contextlib.suppress(Exception):
|
|
418
|
+
if index == _EnsembleWidgetTabs.PARAMETERS_TAB:
|
|
419
|
+
df = self._ensemble.load_scalar_keys(transformed=True)
|
|
420
|
+
else:
|
|
421
|
+
df = self.get_misfit_df()
|
|
422
|
+
|
|
423
|
+
table = (
|
|
424
|
+
self._parameters_table
|
|
425
|
+
if index == _EnsembleWidgetTabs.PARAMETERS_TAB
|
|
426
|
+
else self._misfit_table
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
table.setUpdatesEnabled(False)
|
|
430
|
+
table.setSortingEnabled(False)
|
|
431
|
+
table.setRowCount(df.height)
|
|
432
|
+
table.setColumnCount(df.width)
|
|
433
|
+
table.setHorizontalHeaderLabels(df.columns)
|
|
434
|
+
|
|
435
|
+
rows = df.rows()
|
|
436
|
+
for r, row in enumerate(rows):
|
|
437
|
+
for c, v in enumerate(row):
|
|
438
|
+
table.setItem(r, c, QTableWidgetItem("" if v is None else str(v)))
|
|
439
|
+
|
|
440
|
+
table.resizeColumnsToContents()
|
|
441
|
+
table.setUpdatesEnabled(True)
|
|
442
|
+
|
|
443
|
+
def get_misfit_df(self) -> DataFrame:
|
|
444
|
+
assert self._ensemble is not None
|
|
445
|
+
df = LibresFacade.load_all_misfit_data(self._ensemble)
|
|
446
|
+
realization_column = pl.Series(df.index)
|
|
447
|
+
df = pl.from_pandas(df)
|
|
448
|
+
df.insert_column(0, realization_column)
|
|
449
|
+
return df
|
|
365
450
|
|
|
366
451
|
@Slot(Ensemble)
|
|
367
452
|
def setEnsemble(self, ensemble: Ensemble) -> None:
|
|
@@ -372,6 +457,22 @@ class _EnsembleWidget(QWidget):
|
|
|
372
457
|
|
|
373
458
|
self._tab_widget.setCurrentIndex(0)
|
|
374
459
|
|
|
460
|
+
@Slot()
|
|
461
|
+
def onClickExportMisfit(self) -> None:
|
|
462
|
+
assert self._ensemble is not None
|
|
463
|
+
misfit_df = self.get_misfit_df()
|
|
464
|
+
export_dialog = ExportDialog(misfit_df, "Export misfit", parent=self)
|
|
465
|
+
export_dialog.show()
|
|
466
|
+
|
|
467
|
+
@Slot()
|
|
468
|
+
def onClickExportParameters(self) -> None:
|
|
469
|
+
assert self._ensemble is not None
|
|
470
|
+
parameters_df = self._ensemble.load_scalar_keys(transformed=True)
|
|
471
|
+
export_dialog = ExportDialog(
|
|
472
|
+
parameters_df, window_title="Export parameters", parent=self
|
|
473
|
+
)
|
|
474
|
+
export_dialog.show()
|
|
475
|
+
|
|
375
476
|
|
|
376
477
|
class _RealizationWidget(QWidget):
|
|
377
478
|
def __init__(self) -> None:
|
ert/gui/tools/plot/plot_api.py
CHANGED
|
@@ -18,8 +18,9 @@ from pandas.api.types import is_numeric_dtype
|
|
|
18
18
|
from pandas.errors import ParserError
|
|
19
19
|
from resfo_utilities import history_key
|
|
20
20
|
|
|
21
|
-
from ert.config import
|
|
21
|
+
from ert.config import ParameterConfig, ResponseMetadata
|
|
22
22
|
from ert.services import ErtServer
|
|
23
|
+
from ert.storage.local_experiment import _parameters_adapter as parameter_config_adapter
|
|
23
24
|
from ert.storage.realization_storage_state import RealizationStorageState
|
|
24
25
|
|
|
25
26
|
logger = logging.getLogger(__name__)
|
|
@@ -44,7 +45,7 @@ class PlotApiKeyDefinition(NamedTuple):
|
|
|
44
45
|
dimensionality: int
|
|
45
46
|
metadata: dict[Any, Any]
|
|
46
47
|
filter_on: dict[Any, Any] | None = None
|
|
47
|
-
|
|
48
|
+
parameter: ParameterConfig | None = None
|
|
48
49
|
response_metadata: ResponseMetadata | None = None
|
|
49
50
|
|
|
50
51
|
|
|
@@ -143,18 +144,21 @@ class PlotApi:
|
|
|
143
144
|
self._check_response(response)
|
|
144
145
|
|
|
145
146
|
for experiment in response.json():
|
|
146
|
-
for
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
147
|
+
for metadata in experiment["parameters"].values():
|
|
148
|
+
param_cfg = parameter_config_adapter.validate_python(metadata)
|
|
149
|
+
if group := metadata.get("group"):
|
|
150
|
+
param_key = f"{group}:{metadata['name']}"
|
|
151
|
+
else:
|
|
152
|
+
param_key = metadata["name"]
|
|
153
|
+
all_keys[param_key] = PlotApiKeyDefinition(
|
|
154
|
+
key=param_key,
|
|
155
|
+
index_type=None,
|
|
156
|
+
observations=False,
|
|
157
|
+
dimensionality=metadata["dimensionality"],
|
|
158
|
+
metadata={"data_origin": metadata["type"]},
|
|
159
|
+
parameter=param_cfg,
|
|
160
|
+
)
|
|
161
|
+
all_params[param_key] = all_keys[param_key]
|
|
158
162
|
|
|
159
163
|
return list(all_keys.values())
|
|
160
164
|
|
|
@@ -318,12 +322,17 @@ class PlotApi:
|
|
|
318
322
|
f"ensemble_name={ensemble.name}, e={e}"
|
|
319
323
|
) from e
|
|
320
324
|
|
|
325
|
+
key_index: list[int | float | pd.Timestamp]
|
|
321
326
|
for obs in observations:
|
|
322
327
|
try:
|
|
323
328
|
int(obs["x_axis"][0])
|
|
324
329
|
key_index = [int(v) for v in obs["x_axis"]]
|
|
325
330
|
except ValueError:
|
|
326
|
-
|
|
331
|
+
try:
|
|
332
|
+
float(obs["x_axis"][0])
|
|
333
|
+
key_index = [float(v) for v in obs["x_axis"]]
|
|
334
|
+
except ValueError:
|
|
335
|
+
key_index = [pd.Timestamp(v) for v in obs["x_axis"]]
|
|
327
336
|
|
|
328
337
|
observations_dfs.append(
|
|
329
338
|
pd.DataFrame(
|
|
@@ -25,7 +25,7 @@ from PyQt6.QtWidgets import (
|
|
|
25
25
|
)
|
|
26
26
|
from typing_extensions import override
|
|
27
27
|
|
|
28
|
-
from .plot_api import EnsembleObject
|
|
28
|
+
from .plot_api import EnsembleObject, PlotApiKeyDefinition
|
|
29
29
|
|
|
30
30
|
if TYPE_CHECKING:
|
|
31
31
|
from .plottery import PlotContext
|
|
@@ -34,6 +34,7 @@ if TYPE_CHECKING:
|
|
|
34
34
|
from .plottery.plots.ensemble import EnsemblePlot
|
|
35
35
|
from .plottery.plots.gaussian_kde import GaussianKDEPlot
|
|
36
36
|
from .plottery.plots.histogram import HistogramPlot
|
|
37
|
+
from .plottery.plots.misfits import MisfitsPlot
|
|
37
38
|
from .plottery.plots.statistics import StatisticsPlot
|
|
38
39
|
from .plottery.plots.std_dev import StdDevPlot
|
|
39
40
|
|
|
@@ -122,6 +123,7 @@ class PlotWidget(QWidget):
|
|
|
122
123
|
"DistributionPlot",
|
|
123
124
|
"CrossEnsembleStatisticsPlot",
|
|
124
125
|
"StdDevPlot",
|
|
126
|
+
"MisfitsPlot",
|
|
125
127
|
],
|
|
126
128
|
parent: QWidget | None = None,
|
|
127
129
|
) -> None:
|
|
@@ -173,7 +175,11 @@ class PlotWidget(QWidget):
|
|
|
173
175
|
self._figure.clear()
|
|
174
176
|
|
|
175
177
|
def _sync_log_checkbox(self) -> None:
|
|
176
|
-
if type(self._plotter).__name__
|
|
178
|
+
if type(self._plotter).__name__ in {
|
|
179
|
+
"HistogramPlot",
|
|
180
|
+
"DistributionPlot",
|
|
181
|
+
"GaussianKDEPlot",
|
|
182
|
+
}:
|
|
177
183
|
self._log_checkbox.setVisible(True)
|
|
178
184
|
else:
|
|
179
185
|
self._log_checkbox.setVisible(False)
|
|
@@ -188,6 +194,7 @@ class PlotWidget(QWidget):
|
|
|
188
194
|
ensemble_to_data_map: dict[EnsembleObject, pd.DataFrame],
|
|
189
195
|
observations: pd.DataFrame,
|
|
190
196
|
std_dev_images: dict[str, npt.NDArray[np.float32]],
|
|
197
|
+
key_def: PlotApiKeyDefinition | None = None,
|
|
191
198
|
) -> None:
|
|
192
199
|
self.resetPlot()
|
|
193
200
|
try:
|
|
@@ -200,6 +207,7 @@ class PlotWidget(QWidget):
|
|
|
200
207
|
ensemble_to_data_map,
|
|
201
208
|
observations,
|
|
202
209
|
std_dev_images,
|
|
210
|
+
key_def,
|
|
203
211
|
)
|
|
204
212
|
self._canvas.draw()
|
|
205
213
|
self._sync_log_checkbox()
|
|
@@ -22,6 +22,7 @@ from PyQt6.QtWidgets import (
|
|
|
22
22
|
QWidget,
|
|
23
23
|
)
|
|
24
24
|
|
|
25
|
+
from ert.config.field import Field
|
|
25
26
|
from ert.dark_storage.common import get_storage_api_version
|
|
26
27
|
from ert.gui.ertwidgets import CopyButton, showWaitCursorWhileWaiting
|
|
27
28
|
from ert.services._base_service import ServerBootFail
|
|
@@ -39,6 +40,7 @@ from .plottery.plots import (
|
|
|
39
40
|
EnsemblePlot,
|
|
40
41
|
GaussianKDEPlot,
|
|
41
42
|
HistogramPlot,
|
|
43
|
+
MisfitsPlot,
|
|
42
44
|
StatisticsPlot,
|
|
43
45
|
StdDevPlot,
|
|
44
46
|
)
|
|
@@ -50,10 +52,11 @@ ENSEMBLE = "Ensemble"
|
|
|
50
52
|
HISTOGRAM = "Histogram"
|
|
51
53
|
STATISTICS = "Statistics"
|
|
52
54
|
STD_DEV = "Std Dev"
|
|
55
|
+
MISFITS = "Misfits"
|
|
53
56
|
|
|
54
57
|
RESPONSE_DEFAULT = 0
|
|
55
|
-
GEN_KW_DEFAULT =
|
|
56
|
-
STD_DEV_DEFAULT =
|
|
58
|
+
GEN_KW_DEFAULT = 3
|
|
59
|
+
STD_DEV_DEFAULT = 7
|
|
57
60
|
|
|
58
61
|
|
|
59
62
|
logger = logging.getLogger(__name__)
|
|
@@ -188,6 +191,7 @@ class PlotWindow(QMainWindow):
|
|
|
188
191
|
|
|
189
192
|
self.addPlotWidget(ENSEMBLE, EnsemblePlot())
|
|
190
193
|
self.addPlotWidget(STATISTICS, StatisticsPlot())
|
|
194
|
+
self.addPlotWidget(MISFITS, MisfitsPlot())
|
|
191
195
|
self.addPlotWidget(HISTOGRAM, HistogramPlot())
|
|
192
196
|
self.addPlotWidget(GAUSSIAN_KDE, GaussianKDEPlot())
|
|
193
197
|
self.addPlotWidget(DISTRIBUTION, DistributionPlot())
|
|
@@ -270,12 +274,12 @@ class PlotWindow(QMainWindow):
|
|
|
270
274
|
filter_on=key_def.filter_on,
|
|
271
275
|
)
|
|
272
276
|
elif (
|
|
273
|
-
key_def.
|
|
274
|
-
and
|
|
277
|
+
key_def.parameter is not None
|
|
278
|
+
and key_def.parameter.type == "gen_kw"
|
|
275
279
|
):
|
|
276
280
|
ensemble_to_data_map[ensemble] = self._api.data_for_parameter(
|
|
277
281
|
ensemble_id=ensemble.id,
|
|
278
|
-
parameter_key=key_def.
|
|
282
|
+
parameter_key=key_def.parameter.name,
|
|
279
283
|
)
|
|
280
284
|
except BaseException as e:
|
|
281
285
|
handle_exception(e)
|
|
@@ -290,10 +294,10 @@ class PlotWindow(QMainWindow):
|
|
|
290
294
|
handle_exception(e)
|
|
291
295
|
|
|
292
296
|
std_dev_images: dict[str, npt.NDArray[np.float32]] = {}
|
|
293
|
-
if "FIELD" in key_def.metadata["data_origin"]:
|
|
294
|
-
plot_widget.showLayerWidget.emit(True)
|
|
295
297
|
|
|
296
|
-
|
|
298
|
+
if isinstance(key_def.parameter, Field):
|
|
299
|
+
plot_widget.showLayerWidget.emit(True)
|
|
300
|
+
layers = key_def.parameter.ertbox_params.nz
|
|
297
301
|
plot_widget.updateLayerWidget.emit(layers)
|
|
298
302
|
|
|
299
303
|
if layer is None:
|
|
@@ -357,7 +361,11 @@ class PlotWindow(QMainWindow):
|
|
|
357
361
|
self._updateCustomizer(plot_widget, self._preferred_ensemble_x_axis_format)
|
|
358
362
|
|
|
359
363
|
plot_widget.updatePlot(
|
|
360
|
-
plot_context,
|
|
364
|
+
plot_context,
|
|
365
|
+
ensemble_to_data_map,
|
|
366
|
+
observations,
|
|
367
|
+
std_dev_images,
|
|
368
|
+
key_def,
|
|
361
369
|
)
|
|
362
370
|
|
|
363
371
|
def _updateCustomizer(
|
|
@@ -386,15 +394,14 @@ class PlotWindow(QMainWindow):
|
|
|
386
394
|
def addPlotWidget(
|
|
387
395
|
self,
|
|
388
396
|
name: str,
|
|
389
|
-
plotter:
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
),
|
|
397
|
+
plotter: EnsemblePlot
|
|
398
|
+
| StatisticsPlot
|
|
399
|
+
| HistogramPlot
|
|
400
|
+
| GaussianKDEPlot
|
|
401
|
+
| DistributionPlot
|
|
402
|
+
| CrossEnsembleStatisticsPlot
|
|
403
|
+
| StdDevPlot
|
|
404
|
+
| MisfitsPlot,
|
|
398
405
|
enabled: bool = True,
|
|
399
406
|
) -> None:
|
|
400
407
|
plot_widget = PlotWidget(name, plotter)
|
|
@@ -433,6 +440,7 @@ class PlotWindow(QMainWindow):
|
|
|
433
440
|
widget
|
|
434
441
|
for widget in self._plot_widgets
|
|
435
442
|
if widget._plotter.dimensionality == key_def.dimensionality
|
|
443
|
+
and (key_def.observations or not widget._plotter.requires_observations)
|
|
436
444
|
]
|
|
437
445
|
|
|
438
446
|
# Enabling/disabling tab triggers the
|
|
@@ -3,6 +3,7 @@ from .distribution import DistributionPlot
|
|
|
3
3
|
from .ensemble import EnsemblePlot
|
|
4
4
|
from .gaussian_kde import GaussianKDEPlot
|
|
5
5
|
from .histogram import HistogramPlot
|
|
6
|
+
from .misfits import MisfitsPlot
|
|
6
7
|
from .statistics import StatisticsPlot
|
|
7
8
|
from .std_dev import StdDevPlot
|
|
8
9
|
|
|
@@ -12,6 +13,7 @@ __all__ = [
|
|
|
12
13
|
"EnsemblePlot",
|
|
13
14
|
"GaussianKDEPlot",
|
|
14
15
|
"HistogramPlot",
|
|
16
|
+
"MisfitsPlot",
|
|
15
17
|
"StatisticsPlot",
|
|
16
18
|
"StdDevPlot",
|
|
17
19
|
]
|
|
@@ -8,7 +8,7 @@ from matplotlib.lines import Line2D
|
|
|
8
8
|
from matplotlib.patches import Rectangle
|
|
9
9
|
from typing_extensions import TypedDict
|
|
10
10
|
|
|
11
|
-
from ert.gui.tools.plot.plot_api import EnsembleObject
|
|
11
|
+
from ert.gui.tools.plot.plot_api import EnsembleObject, PlotApiKeyDefinition
|
|
12
12
|
|
|
13
13
|
from .plot_tools import ConditionalAxisFormatter, PlotTools
|
|
14
14
|
|
|
@@ -37,6 +37,7 @@ class CcsData(TypedDict):
|
|
|
37
37
|
class CrossEnsembleStatisticsPlot:
|
|
38
38
|
def __init__(self) -> None:
|
|
39
39
|
self.dimensionality = 1
|
|
40
|
+
self.requires_observations = False
|
|
40
41
|
|
|
41
42
|
@staticmethod
|
|
42
43
|
def plot(
|
|
@@ -45,6 +46,7 @@ class CrossEnsembleStatisticsPlot:
|
|
|
45
46
|
ensemble_to_data_map: dict[EnsembleObject, pd.DataFrame],
|
|
46
47
|
observation_data: pd.DataFrame,
|
|
47
48
|
std_dev_images: dict[str, npt.NDArray[np.float32]],
|
|
49
|
+
key_def: PlotApiKeyDefinition | None = None,
|
|
48
50
|
) -> None:
|
|
49
51
|
plotCrossEnsembleStatistics(
|
|
50
52
|
figure, plot_context, ensemble_to_data_map, observation_data
|
|
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING
|
|
|
5
5
|
import numpy as np
|
|
6
6
|
import pandas as pd
|
|
7
7
|
|
|
8
|
-
from ert.gui.tools.plot.plot_api import EnsembleObject
|
|
8
|
+
from ert.gui.tools.plot.plot_api import EnsembleObject, PlotApiKeyDefinition
|
|
9
9
|
|
|
10
10
|
from .plot_tools import ConditionalAxisFormatter, PlotTools
|
|
11
11
|
|
|
@@ -20,6 +20,7 @@ if TYPE_CHECKING:
|
|
|
20
20
|
class DistributionPlot:
|
|
21
21
|
def __init__(self) -> None:
|
|
22
22
|
self.dimensionality = 1
|
|
23
|
+
self.requires_observations = False
|
|
23
24
|
|
|
24
25
|
@staticmethod
|
|
25
26
|
def plot(
|
|
@@ -28,6 +29,7 @@ class DistributionPlot:
|
|
|
28
29
|
ensemble_to_data_map: dict[EnsembleObject, pd.DataFrame],
|
|
29
30
|
observation_data: pd.DataFrame,
|
|
30
31
|
std_dev_images: dict[str, npt.NDArray[np.float32]],
|
|
32
|
+
key_def: PlotApiKeyDefinition | None = None,
|
|
31
33
|
) -> None:
|
|
32
34
|
plotDistribution(figure, plot_context, ensemble_to_data_map, observation_data)
|
|
33
35
|
|
|
@@ -80,6 +82,9 @@ def plotDistribution(
|
|
|
80
82
|
)
|
|
81
83
|
config.setLegendEnabled(False)
|
|
82
84
|
|
|
85
|
+
if plot_context.log_scale:
|
|
86
|
+
axes.set_yscale("log")
|
|
87
|
+
|
|
83
88
|
PlotTools.finalizePlot(
|
|
84
89
|
plot_context, figure, axes, default_x_label="Ensemble", default_y_label="Value"
|
|
85
90
|
)
|
|
@@ -15,13 +15,14 @@ if TYPE_CHECKING:
|
|
|
15
15
|
from matplotlib.axes import Axes
|
|
16
16
|
from matplotlib.figure import Figure
|
|
17
17
|
|
|
18
|
-
from ert.gui.tools.plot.plot_api import EnsembleObject
|
|
18
|
+
from ert.gui.tools.plot.plot_api import EnsembleObject, PlotApiKeyDefinition
|
|
19
19
|
from ert.gui.tools.plot.plottery import PlotConfig, PlotContext
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class EnsemblePlot:
|
|
23
23
|
def __init__(self) -> None:
|
|
24
24
|
self.dimensionality = 2
|
|
25
|
+
self.requires_observations = False
|
|
25
26
|
|
|
26
27
|
def plot(
|
|
27
28
|
self,
|
|
@@ -30,6 +31,7 @@ class EnsemblePlot:
|
|
|
30
31
|
ensemble_to_data_map: dict[EnsembleObject, pd.DataFrame],
|
|
31
32
|
observation_data: pd.DataFrame,
|
|
32
33
|
std_dev_images: dict[str, npt.NDArray[np.float32]],
|
|
34
|
+
key_def: PlotApiKeyDefinition | None = None,
|
|
33
35
|
) -> None:
|
|
34
36
|
config = plot_context.plotConfig()
|
|
35
37
|
axes = figure.add_subplot(111)
|
|
@@ -6,7 +6,7 @@ import numpy as np
|
|
|
6
6
|
import pandas as pd
|
|
7
7
|
from scipy.stats import gaussian_kde
|
|
8
8
|
|
|
9
|
-
from ert.gui.tools.plot.plot_api import EnsembleObject
|
|
9
|
+
from ert.gui.tools.plot.plot_api import EnsembleObject, PlotApiKeyDefinition
|
|
10
10
|
|
|
11
11
|
from .plot_tools import ConditionalAxisFormatter, PlotTools
|
|
12
12
|
|
|
@@ -21,6 +21,7 @@ if TYPE_CHECKING:
|
|
|
21
21
|
class GaussianKDEPlot:
|
|
22
22
|
def __init__(self) -> None:
|
|
23
23
|
self.dimensionality = 1
|
|
24
|
+
self.requires_observations = False
|
|
24
25
|
|
|
25
26
|
@staticmethod
|
|
26
27
|
def plot(
|
|
@@ -29,10 +30,16 @@ class GaussianKDEPlot:
|
|
|
29
30
|
ensemble_to_data_map: dict[EnsembleObject, pd.DataFrame],
|
|
30
31
|
observation_data: pd.DataFrame,
|
|
31
32
|
std_dev_images: dict[str, npt.NDArray[np.float32]],
|
|
33
|
+
key_def: PlotApiKeyDefinition | None = None,
|
|
32
34
|
) -> None:
|
|
33
35
|
plotGaussianKDE(figure, plot_context, ensemble_to_data_map, observation_data)
|
|
34
36
|
|
|
35
37
|
|
|
38
|
+
def _array_is_constant(data: pd.Series | pd.DataFrame) -> bool:
|
|
39
|
+
array = data.to_numpy()
|
|
40
|
+
return array.shape[0] == 0 or (array[0] == array).all()
|
|
41
|
+
|
|
42
|
+
|
|
36
43
|
def plotGaussianKDE(
|
|
37
44
|
figure: Figure,
|
|
38
45
|
plot_context: PlotContext,
|
|
@@ -55,11 +62,14 @@ def plotGaussianKDE(
|
|
|
55
62
|
if data.empty:
|
|
56
63
|
continue
|
|
57
64
|
data = data[0]
|
|
58
|
-
if data
|
|
65
|
+
if not _array_is_constant(data):
|
|
59
66
|
_plotGaussianKDE(
|
|
60
67
|
axes, config, data, f"{ensemble.experiment_name} : {ensemble.name}"
|
|
61
68
|
)
|
|
62
69
|
|
|
70
|
+
if plot_context.log_scale:
|
|
71
|
+
axes.set_xscale("log")
|
|
72
|
+
|
|
63
73
|
PlotTools.finalizePlot(
|
|
64
74
|
plot_context, figure, axes, default_x_label="Value", default_y_label="Density"
|
|
65
75
|
)
|
|
@@ -8,7 +8,7 @@ import numpy as np
|
|
|
8
8
|
import pandas as pd
|
|
9
9
|
from matplotlib.patches import Rectangle
|
|
10
10
|
|
|
11
|
-
from ert.gui.tools.plot.plot_api import EnsembleObject
|
|
11
|
+
from ert.gui.tools.plot.plot_api import EnsembleObject, PlotApiKeyDefinition
|
|
12
12
|
from ert.shared.status.utils import convert_to_numeric
|
|
13
13
|
|
|
14
14
|
from .plot_tools import ConditionalAxisFormatter, PlotTools
|
|
@@ -24,6 +24,7 @@ if TYPE_CHECKING:
|
|
|
24
24
|
class HistogramPlot:
|
|
25
25
|
def __init__(self) -> None:
|
|
26
26
|
self.dimensionality = 1
|
|
27
|
+
self.requires_observations = False
|
|
27
28
|
|
|
28
29
|
@staticmethod
|
|
29
30
|
def plot(
|
|
@@ -32,6 +33,7 @@ class HistogramPlot:
|
|
|
32
33
|
ensemble_to_data_map: dict[EnsembleObject, pd.DataFrame],
|
|
33
34
|
observation_data: pd.DataFrame,
|
|
34
35
|
std_dev_images: dict[str, npt.NDArray[np.float32]],
|
|
36
|
+
key_def: PlotApiKeyDefinition | None = None,
|
|
35
37
|
) -> None:
|
|
36
38
|
plotHistogram(figure, plot_context, ensemble_to_data_map)
|
|
37
39
|
|