ert 16.0.9__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 (286) hide show
  1. _ert/events.py +19 -2
  2. _ert/forward_model_runner/client.py +6 -2
  3. _ert/forward_model_runner/fm_dispatch.py +9 -6
  4. _ert/forward_model_runner/reporting/event.py +1 -0
  5. _ert/forward_model_runner/runner.py +1 -2
  6. _ert/utils.py +12 -0
  7. ert/__main__.py +58 -38
  8. ert/analysis/_enif_update.py +8 -4
  9. ert/analysis/_es_update.py +19 -6
  10. ert/analysis/_update_commons.py +16 -6
  11. ert/base_model_context.py +1 -1
  12. ert/cli/main.py +17 -12
  13. ert/cli/monitor.py +7 -0
  14. ert/config/__init__.py +17 -6
  15. ert/config/_create_observation_dataframes.py +118 -21
  16. ert/config/_get_num_cpu.py +1 -1
  17. ert/config/_observations.py +91 -2
  18. ert/config/_read_summary.py +74 -328
  19. ert/config/design_matrix.py +62 -23
  20. ert/config/distribution.py +1 -1
  21. ert/config/ensemble_config.py +9 -17
  22. ert/config/ert_config.py +155 -58
  23. ert/config/everest_control.py +234 -0
  24. ert/config/{everest_constraints_config.py → everest_response.py} +27 -15
  25. ert/config/field.py +99 -90
  26. ert/config/forward_model_step.py +122 -17
  27. ert/config/gen_data_config.py +5 -10
  28. ert/config/gen_kw_config.py +11 -41
  29. ert/config/known_response_types.py +14 -0
  30. ert/config/parameter_config.py +1 -33
  31. ert/config/parsing/_option_dict.py +10 -2
  32. ert/config/parsing/config_errors.py +1 -1
  33. ert/config/parsing/config_keywords.py +2 -1
  34. ert/config/parsing/config_schema.py +23 -11
  35. ert/config/parsing/config_schema_deprecations.py +3 -3
  36. ert/config/parsing/config_schema_item.py +26 -11
  37. ert/config/parsing/context_values.py +3 -3
  38. ert/config/parsing/file_context_token.py +1 -1
  39. ert/config/parsing/observations_parser.py +6 -2
  40. ert/config/parsing/queue_system.py +9 -0
  41. ert/config/parsing/schema_item_type.py +1 -0
  42. ert/config/queue_config.py +42 -50
  43. ert/config/response_config.py +0 -8
  44. ert/config/rft_config.py +275 -0
  45. ert/config/summary_config.py +3 -8
  46. ert/config/surface_config.py +73 -26
  47. ert/config/workflow_fixtures.py +2 -1
  48. ert/config/workflow_job.py +135 -54
  49. ert/dark_storage/client/__init__.py +2 -2
  50. ert/dark_storage/client/_session.py +4 -4
  51. ert/dark_storage/client/client.py +2 -2
  52. ert/dark_storage/common.py +12 -3
  53. ert/dark_storage/compute/misfits.py +11 -7
  54. ert/dark_storage/endpoints/compute/misfits.py +6 -4
  55. ert/dark_storage/endpoints/ensembles.py +4 -0
  56. ert/dark_storage/endpoints/experiment_server.py +30 -24
  57. ert/dark_storage/endpoints/experiments.py +2 -2
  58. ert/dark_storage/endpoints/observations.py +8 -6
  59. ert/dark_storage/endpoints/parameters.py +4 -12
  60. ert/dark_storage/endpoints/responses.py +24 -5
  61. ert/dark_storage/json_schema/ensemble.py +3 -0
  62. ert/dark_storage/json_schema/experiment.py +1 -1
  63. ert/data/_measured_data.py +6 -5
  64. ert/ensemble_evaluator/__init__.py +8 -1
  65. ert/ensemble_evaluator/config.py +2 -1
  66. ert/ensemble_evaluator/evaluator.py +81 -29
  67. ert/ensemble_evaluator/event.py +6 -0
  68. ert/ensemble_evaluator/snapshot.py +3 -1
  69. ert/ensemble_evaluator/state.py +1 -0
  70. ert/field_utils/__init__.py +8 -0
  71. ert/field_utils/field_utils.py +228 -15
  72. ert/field_utils/grdecl_io.py +1 -1
  73. ert/field_utils/roff_io.py +1 -1
  74. ert/gui/__init__.py +5 -2
  75. ert/gui/ertnotifier.py +1 -1
  76. ert/gui/ertwidgets/__init__.py +23 -16
  77. ert/gui/ertwidgets/analysismoduleedit.py +2 -2
  78. ert/gui/ertwidgets/checklist.py +1 -1
  79. ert/gui/ertwidgets/closabledialog.py +2 -0
  80. ert/gui/ertwidgets/copyablelabel.py +2 -0
  81. ert/gui/ertwidgets/create_experiment_dialog.py +3 -1
  82. ert/gui/ertwidgets/ensembleselector.py +2 -2
  83. ert/gui/ertwidgets/listeditbox.py +2 -0
  84. ert/gui/ertwidgets/models/__init__.py +2 -0
  85. ert/gui/ertwidgets/models/activerealizationsmodel.py +5 -1
  86. ert/gui/ertwidgets/models/path_model.py +1 -1
  87. ert/gui/ertwidgets/models/targetensemblemodel.py +5 -1
  88. ert/gui/ertwidgets/models/text_model.py +4 -1
  89. ert/gui/ertwidgets/pathchooser.py +0 -3
  90. ert/gui/ertwidgets/searchbox.py +17 -4
  91. ert/gui/ertwidgets/stringbox.py +2 -0
  92. ert/gui/{suggestor → ertwidgets/suggestor}/_suggestor_message.py +13 -4
  93. ert/gui/{suggestor → ertwidgets/suggestor}/suggestor.py +63 -30
  94. ert/gui/main.py +41 -13
  95. ert/gui/main_window.py +3 -7
  96. ert/gui/model/fm_step_list.py +3 -0
  97. ert/gui/model/real_list.py +1 -0
  98. ert/gui/model/snapshot.py +1 -0
  99. ert/gui/simulation/combobox_with_description.py +3 -0
  100. ert/gui/simulation/ensemble_experiment_panel.py +8 -2
  101. ert/gui/simulation/ensemble_information_filter_panel.py +7 -2
  102. ert/gui/simulation/ensemble_smoother_panel.py +8 -2
  103. ert/gui/simulation/evaluate_ensemble_panel.py +17 -7
  104. ert/gui/simulation/experiment_panel.py +18 -6
  105. ert/gui/simulation/manual_update_panel.py +35 -10
  106. ert/gui/simulation/multiple_data_assimilation_panel.py +13 -9
  107. ert/gui/simulation/run_dialog.py +47 -20
  108. ert/gui/simulation/single_test_run_panel.py +6 -3
  109. ert/gui/simulation/view/progress_widget.py +2 -0
  110. ert/gui/simulation/view/realization.py +5 -1
  111. ert/gui/simulation/view/update.py +2 -0
  112. ert/gui/summarypanel.py +20 -1
  113. ert/gui/tools/event_viewer/panel.py +3 -4
  114. ert/gui/tools/event_viewer/tool.py +2 -0
  115. ert/gui/tools/load_results/load_results_panel.py +1 -1
  116. ert/gui/tools/load_results/load_results_tool.py +2 -0
  117. ert/gui/tools/manage_experiments/export_dialog.py +136 -0
  118. ert/gui/tools/manage_experiments/manage_experiments_panel.py +2 -0
  119. ert/gui/tools/manage_experiments/storage_info_widget.py +121 -16
  120. ert/gui/tools/manage_experiments/storage_widget.py +4 -3
  121. ert/gui/tools/plot/customize/color_chooser.py +5 -2
  122. ert/gui/tools/plot/customize/customize_plot_dialog.py +2 -0
  123. ert/gui/tools/plot/customize/default_customization_view.py +4 -0
  124. ert/gui/tools/plot/customize/limits_customization_view.py +3 -0
  125. ert/gui/tools/plot/customize/statistics_customization_view.py +3 -0
  126. ert/gui/tools/plot/customize/style_chooser.py +2 -0
  127. ert/gui/tools/plot/customize/style_customization_view.py +3 -0
  128. ert/gui/tools/plot/data_type_keys_widget.py +2 -0
  129. ert/gui/tools/plot/data_type_proxy_model.py +3 -0
  130. ert/gui/tools/plot/plot_api.py +50 -28
  131. ert/gui/tools/plot/plot_ensemble_selection_widget.py +17 -10
  132. ert/gui/tools/plot/plot_widget.py +15 -2
  133. ert/gui/tools/plot/plot_window.py +41 -19
  134. ert/gui/tools/plot/plottery/plot_config.py +2 -0
  135. ert/gui/tools/plot/plottery/plot_context.py +14 -0
  136. ert/gui/tools/plot/plottery/plots/__init__.py +2 -0
  137. ert/gui/tools/plot/plottery/plots/cesp.py +3 -1
  138. ert/gui/tools/plot/plottery/plots/distribution.py +6 -1
  139. ert/gui/tools/plot/plottery/plots/ensemble.py +13 -5
  140. ert/gui/tools/plot/plottery/plots/gaussian_kde.py +12 -2
  141. ert/gui/tools/plot/plottery/plots/histogram.py +3 -1
  142. ert/gui/tools/plot/plottery/plots/misfits.py +436 -0
  143. ert/gui/tools/plot/plottery/plots/observations.py +18 -4
  144. ert/gui/tools/plot/plottery/plots/statistics.py +62 -20
  145. ert/gui/tools/plot/plottery/plots/std_dev.py +3 -1
  146. ert/gui/tools/plot/widgets/clearable_line_edit.py +9 -0
  147. ert/gui/tools/plot/widgets/filter_popup.py +2 -0
  148. ert/gui/tools/plot/widgets/filterable_kw_list_model.py +3 -0
  149. ert/gui/tools/plugins/plugin.py +1 -1
  150. ert/gui/tools/plugins/plugins_tool.py +2 -0
  151. ert/gui/tools/plugins/process_job_dialog.py +3 -0
  152. ert/gui/tools/workflows/workflow_dialog.py +2 -0
  153. ert/gui/tools/workflows/workflows_tool.py +2 -0
  154. ert/libres_facade.py +5 -7
  155. ert/logging/__init__.py +4 -1
  156. ert/mode_definitions.py +2 -0
  157. ert/plugins/__init__.py +4 -6
  158. ert/plugins/hook_implementations/workflows/csv_export.py +2 -3
  159. ert/plugins/hook_implementations/workflows/gen_data_rft_export.py +10 -2
  160. ert/plugins/hook_specifications/__init__.py +0 -10
  161. ert/plugins/hook_specifications/jobs.py +0 -9
  162. ert/plugins/plugin_manager.py +53 -124
  163. ert/resources/forward_models/run_reservoirsimulator.py +8 -4
  164. ert/resources/forward_models/template_render.py +10 -10
  165. ert/resources/shell_scripts/delete_directory.py +2 -2
  166. ert/run_models/__init__.py +24 -6
  167. ert/run_models/_create_run_path.py +133 -38
  168. ert/run_models/ensemble_experiment.py +10 -4
  169. ert/run_models/ensemble_information_filter.py +8 -1
  170. ert/run_models/ensemble_smoother.py +9 -3
  171. ert/run_models/evaluate_ensemble.py +8 -6
  172. ert/run_models/event.py +7 -3
  173. ert/run_models/everest_run_model.py +337 -113
  174. ert/run_models/initial_ensemble_run_model.py +25 -24
  175. ert/run_models/manual_update.py +6 -3
  176. ert/run_models/manual_update_enif.py +37 -0
  177. ert/run_models/model_factory.py +78 -18
  178. ert/run_models/multiple_data_assimilation.py +22 -11
  179. ert/run_models/run_model.py +72 -73
  180. ert/run_models/single_test_run.py +7 -4
  181. ert/run_models/update_run_model.py +4 -2
  182. ert/runpaths.py +5 -6
  183. ert/sample_prior.py +9 -4
  184. ert/scheduler/__init__.py +10 -5
  185. ert/scheduler/driver.py +40 -0
  186. ert/scheduler/event.py +3 -1
  187. ert/scheduler/job.py +23 -13
  188. ert/scheduler/lsf_driver.py +15 -5
  189. ert/scheduler/openpbs_driver.py +10 -4
  190. ert/scheduler/scheduler.py +5 -0
  191. ert/scheduler/slurm_driver.py +20 -5
  192. ert/services/__init__.py +2 -2
  193. ert/services/_base_service.py +37 -20
  194. ert/services/_storage_main.py +20 -18
  195. ert/services/ert_server.py +317 -0
  196. ert/shared/_doc_utils/__init__.py +4 -2
  197. ert/shared/_doc_utils/ert_jobs.py +1 -4
  198. ert/shared/net_utils.py +43 -18
  199. ert/shared/storage/connection.py +3 -3
  200. ert/shared/version.py +3 -3
  201. ert/storage/__init__.py +14 -1
  202. ert/storage/local_ensemble.py +44 -13
  203. ert/storage/local_experiment.py +54 -34
  204. ert/storage/local_storage.py +90 -58
  205. ert/storage/migration/to10.py +3 -2
  206. ert/storage/migration/to11.py +9 -10
  207. ert/storage/migration/to12.py +19 -20
  208. ert/storage/migration/to13.py +28 -27
  209. ert/storage/migration/to14.py +3 -3
  210. ert/storage/migration/to15.py +25 -0
  211. ert/storage/migration/to16.py +38 -0
  212. ert/storage/migration/to17.py +42 -0
  213. ert/storage/migration/to18.py +11 -0
  214. ert/storage/migration/to19.py +34 -0
  215. ert/storage/migration/to20.py +23 -0
  216. ert/storage/migration/to21.py +25 -0
  217. ert/storage/migration/to6.py +3 -2
  218. ert/storage/migration/to7.py +12 -13
  219. ert/storage/migration/to8.py +9 -11
  220. ert/storage/migration/to9.py +5 -4
  221. ert/storage/realization_storage_state.py +7 -7
  222. ert/substitutions.py +12 -28
  223. ert/validation/active_range.py +7 -7
  224. ert/validation/ensemble_realizations_argument.py +4 -2
  225. ert/validation/rangestring.py +16 -16
  226. ert/workflow_runner.py +6 -3
  227. {ert-16.0.9.dist-info → ert-19.0.0rc2.dist-info}/METADATA +21 -15
  228. ert-19.0.0rc2.dist-info/RECORD +524 -0
  229. {ert-16.0.9.dist-info → ert-19.0.0rc2.dist-info}/WHEEL +1 -1
  230. everest/api/everest_data_api.py +14 -1
  231. everest/assets/everest_logo.svg +406 -0
  232. everest/bin/config_branch_script.py +30 -14
  233. everest/bin/everconfigdump_script.py +2 -10
  234. everest/bin/everest_script.py +53 -33
  235. everest/bin/everlint_script.py +3 -5
  236. everest/bin/kill_script.py +7 -5
  237. everest/bin/main.py +11 -24
  238. everest/bin/monitor_script.py +64 -35
  239. everest/bin/utils.py +58 -43
  240. everest/bin/visualization_script.py +23 -13
  241. everest/config/__init__.py +4 -1
  242. everest/config/control_config.py +81 -6
  243. everest/config/control_variable_config.py +4 -3
  244. everest/config/everest_config.py +102 -79
  245. everest/config/forward_model_config.py +5 -3
  246. everest/config/install_data_config.py +7 -5
  247. everest/config/install_job_config.py +45 -3
  248. everest/config/install_template_config.py +3 -3
  249. everest/config/optimization_config.py +19 -6
  250. everest/config/output_constraint_config.py +8 -2
  251. everest/config/server_config.py +6 -55
  252. everest/config/simulator_config.py +62 -17
  253. everest/config/utils.py +25 -105
  254. everest/config/validation_utils.py +34 -15
  255. everest/config_file_loader.py +30 -21
  256. everest/detached/__init__.py +0 -6
  257. everest/detached/client.py +7 -52
  258. everest/detached/everserver.py +19 -45
  259. everest/everest_storage.py +24 -40
  260. everest/gui/everest_client.py +2 -3
  261. everest/gui/main_window.py +2 -2
  262. everest/optimizer/everest2ropt.py +68 -42
  263. everest/optimizer/opt_model_transforms.py +15 -20
  264. everest/optimizer/utils.py +0 -29
  265. everest/plugins/hook_specs.py +0 -24
  266. everest/strings.py +1 -6
  267. everest/util/__init__.py +3 -1
  268. ert/config/everest_objective_config.py +0 -95
  269. ert/config/ext_param_config.py +0 -107
  270. ert/gui/tools/export/__init__.py +0 -3
  271. ert/gui/tools/export/export_panel.py +0 -83
  272. ert/gui/tools/export/export_tool.py +0 -67
  273. ert/gui/tools/export/exporter.py +0 -36
  274. ert/plugins/hook_specifications/ecl_config.py +0 -29
  275. ert/services/storage_service.py +0 -127
  276. ert/summary_key_type.py +0 -234
  277. ert-16.0.9.dist-info/RECORD +0 -521
  278. everest/bin/everexport_script.py +0 -53
  279. everest/config/sampler_config.py +0 -103
  280. everest/simulator/__init__.py +0 -88
  281. everest/simulator/everest_to_ert.py +0 -252
  282. /ert/gui/{suggestor → ertwidgets/suggestor}/__init__.py +0 -0
  283. /ert/gui/{suggestor → ertwidgets/suggestor}/_colors.py +0 -0
  284. {ert-16.0.9.dist-info → ert-19.0.0rc2.dist-info}/entry_points.txt +0 -0
  285. {ert-16.0.9.dist-info → ert-19.0.0rc2.dist-info}/licenses/COPYING +0 -0
  286. {ert-16.0.9.dist-info → ert-19.0.0rc2.dist-info}/top_level.txt +0 -0
@@ -16,10 +16,12 @@ import numpy.typing as npt
16
16
  import pandas as pd
17
17
  from pandas.api.types import is_numeric_dtype
18
18
  from pandas.errors import ParserError
19
+ from resfo_utilities import history_key
19
20
 
20
- from ert.config import ParameterMetadata, ResponseMetadata
21
- from ert.services import StorageService
22
- from ert.summary_key_type import history_key
21
+ from ert.config import ParameterConfig, ResponseMetadata
22
+ from ert.services import ErtServer
23
+ from ert.storage.local_experiment import _parameters_adapter as parameter_config_adapter
24
+ from ert.storage.realization_storage_state import RealizationStorageState
23
25
 
24
26
  logger = logging.getLogger(__name__)
25
27
 
@@ -43,7 +45,7 @@ class PlotApiKeyDefinition(NamedTuple):
43
45
  dimensionality: int
44
46
  metadata: dict[Any, Any]
45
47
  filter_on: dict[Any, Any] | None = None
46
- parameter_metadata: ParameterMetadata | None = None
48
+ parameter: ParameterConfig | None = None
47
49
  response_metadata: ResponseMetadata | None = None
48
50
 
49
51
 
@@ -55,7 +57,7 @@ class PlotApi:
55
57
 
56
58
  @property
57
59
  def api_version(self) -> str:
58
- with StorageService.session(project=self.ens_path) as client:
60
+ with ErtServer.session(project=self.ens_path) as client:
59
61
  try:
60
62
  response = client.get("/version", timeout=self._timeout)
61
63
  self._check_response(response)
@@ -81,7 +83,7 @@ class PlotApi:
81
83
  return self._all_ensembles
82
84
 
83
85
  self._all_ensembles = []
84
- with StorageService.session(project=self.ens_path) as client:
86
+ with ErtServer.session(project=self.ens_path) as client:
85
87
  try:
86
88
  response = client.get("/experiments", timeout=self._timeout)
87
89
  self._check_response(response)
@@ -92,18 +94,27 @@ class PlotApi:
92
94
  f"/ensembles/{ensemble_id}", timeout=self._timeout
93
95
  )
94
96
  self._check_response(response)
95
- response_json = response.json()
97
+ response_json: dict[str, Any] = response.json()
96
98
  ensemble_name: str = response_json["userdata"]["name"]
97
99
  experiment_name: str = response_json["userdata"][
98
100
  "experiment_name"
99
101
  ]
100
102
  ensemble_started_at = response_json["userdata"]["started_at"]
103
+ ensemble_undefined = False
104
+ if realization_storage_states := response_json.get(
105
+ "realization_storage_states"
106
+ ):
107
+ ensemble_undefined = (
108
+ RealizationStorageState.PARAMETERS_LOADED
109
+ not in set(realization_storage_states)
110
+ )
101
111
  self._all_ensembles.append(
102
112
  EnsembleObject(
103
113
  name=ensemble_name,
104
114
  id=ensemble_id,
105
115
  experiment_name=experiment_name,
106
- hidden=ensemble_name.startswith("."),
116
+ hidden=ensemble_name.startswith(".")
117
+ or ensemble_undefined,
107
118
  started_at=ensemble_started_at,
108
119
  )
109
120
  )
@@ -128,23 +139,26 @@ class PlotApi:
128
139
  all_keys: dict[str, PlotApiKeyDefinition] = {}
129
140
  all_params = {}
130
141
 
131
- with StorageService.session(project=self.ens_path) as client:
142
+ with ErtServer.session(project=self.ens_path) as client:
132
143
  response = client.get("/experiments", timeout=self._timeout)
133
144
  self._check_response(response)
134
145
 
135
146
  for experiment in response.json():
136
- for param_metadatas in experiment["parameters"].values():
137
- for metadata in param_metadatas:
138
- param_key = metadata["key"]
139
- all_keys[param_key] = PlotApiKeyDefinition(
140
- key=param_key,
141
- index_type=None,
142
- observations=False,
143
- dimensionality=metadata["dimensionality"],
144
- metadata=metadata["userdata"],
145
- parameter_metadata=ParameterMetadata(**metadata),
146
- )
147
- all_params[param_key] = all_keys[param_key]
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]
148
162
 
149
163
  return list(all_keys.values())
150
164
 
@@ -152,7 +166,7 @@ class PlotApi:
152
166
  def responses_api_key_defs(self) -> list[PlotApiKeyDefinition]:
153
167
  key_defs: dict[str, PlotApiKeyDefinition] = {}
154
168
 
155
- with StorageService.session(project=self.ens_path) as client:
169
+ with ErtServer.session(project=self.ens_path) as client:
156
170
  response = client.get("/experiments", timeout=self._timeout)
157
171
  self._check_response(response)
158
172
 
@@ -214,7 +228,7 @@ class PlotApi:
214
228
  response_key: str,
215
229
  filter_on: dict[str, Any] | None = None,
216
230
  ) -> pd.DataFrame:
217
- with StorageService.session(project=self.ens_path) as client:
231
+ with ErtServer.session(project=self.ens_path) as client:
218
232
  response = client.get(
219
233
  f"/ensembles/{ensemble_id}/responses/{PlotApi.escape(response_key)}",
220
234
  headers={"accept": "application/x-parquet"},
@@ -231,7 +245,10 @@ class PlotApi:
231
245
  try:
232
246
  df.columns = pd.to_datetime(df.columns, format="%Y-%m-%d %H:%M:%S")
233
247
  except (ParserError, ValueError):
234
- df.columns = [int(s) for s in df.columns]
248
+ try:
249
+ df.columns = [int(s) for s in df.columns]
250
+ except ValueError:
251
+ df.columns = [float(s) for s in df.columns]
235
252
 
236
253
  try:
237
254
  return df.astype(float)
@@ -239,7 +256,7 @@ class PlotApi:
239
256
  return df
240
257
 
241
258
  def data_for_parameter(self, ensemble_id: str, parameter_key: str) -> pd.DataFrame:
242
- with StorageService.session(project=self.ens_path) as client:
259
+ with ErtServer.session(project=self.ens_path) as client:
243
260
  parameter = client.get(
244
261
  f"/ensembles/{ensemble_id}/parameters/{PlotApi.escape(parameter_key)}",
245
262
  headers={"accept": "application/x-parquet"},
@@ -281,7 +298,7 @@ class PlotApi:
281
298
  assert key_def.response_metadata is not None
282
299
  actual_response_key = key_def.response_metadata.response_key
283
300
  filter_on = key_def.filter_on
284
- with StorageService.session(project=self.ens_path) as client:
301
+ with ErtServer.session(project=self.ens_path) as client:
285
302
  response = client.get(
286
303
  f"/ensembles/{ensemble.id}/responses/{PlotApi.escape(actual_response_key)}/observations",
287
304
  timeout=self._timeout,
@@ -305,12 +322,17 @@ class PlotApi:
305
322
  f"ensemble_name={ensemble.name}, e={e}"
306
323
  ) from e
307
324
 
325
+ key_index: list[int | float | pd.Timestamp]
308
326
  for obs in observations:
309
327
  try:
310
328
  int(obs["x_axis"][0])
311
329
  key_index = [int(v) for v in obs["x_axis"]]
312
330
  except ValueError:
313
- key_index = [pd.Timestamp(v) for v in obs["x_axis"]]
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"]]
314
336
 
315
337
  observations_dfs.append(
316
338
  pd.DataFrame(
@@ -364,7 +386,7 @@ class PlotApi:
364
386
  if not ensemble:
365
387
  return np.array([])
366
388
 
367
- with StorageService.session(project=self.ens_path) as client:
389
+ with ErtServer.session(project=self.ens_path) as client:
368
390
  response = client.get(
369
391
  f"/ensembles/{ensemble.id}/parameters/{PlotApi.escape(key)}/std_dev",
370
392
  params={"z": z},
@@ -28,6 +28,7 @@ from PyQt6.QtWidgets import (
28
28
  QVBoxLayout,
29
29
  QWidget,
30
30
  )
31
+ from typing_extensions import override
31
32
 
32
33
  from .plot_api import EnsembleObject
33
34
 
@@ -39,22 +40,24 @@ class EnsembleSelectionWidget(QWidget):
39
40
  self, ensembles: list[EnsembleObject], number_of_plot_colors: int
40
41
  ) -> None:
41
42
  QWidget.__init__(self)
42
- self.__dndlist = EnsembleSelectListWidget(ensembles, number_of_plot_colors)
43
-
44
- self.__ensemble_layout = QVBoxLayout()
45
- self.__ensemble_layout.setSpacing(0)
46
- self.__ensemble_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
47
- self.__ensemble_layout.addWidget(self.__dndlist)
48
- self.setLayout(self.__ensemble_layout)
49
- self.__dndlist.ensembleSelectionListChanged.connect(
43
+ self._selected_ensembles = EnsembleSelectListWidget(
44
+ ensembles, number_of_plot_colors
45
+ )
46
+
47
+ self._ensemble_layout = QVBoxLayout()
48
+ self._ensemble_layout.setSpacing(0)
49
+ self._ensemble_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
50
+ self._ensemble_layout.addWidget(self._selected_ensembles)
51
+ self.setLayout(self._ensemble_layout)
52
+ self._selected_ensembles.ensembleSelectionListChanged.connect(
50
53
  self.ensembleSelectionChanged.emit
51
54
  )
52
55
 
53
56
  def get_selected_ensembles(self) -> list[EnsembleObject]:
54
- return self.__dndlist.get_checked_ensembles()
57
+ return self._selected_ensembles.get_checked_ensembles()
55
58
 
56
59
  def get_selected_ensembles_color_indexes(self) -> list[int]:
57
- return self.__dndlist.get_checked_color_indexes()
60
+ return self._selected_ensembles.get_checked_color_indexes()
58
61
 
59
62
 
60
63
  class EnsembleSelectListWidgetItemDataRole(IntEnum):
@@ -121,6 +124,7 @@ class EnsembleSelectListWidget(QListWidget):
121
124
 
122
125
  return list(_iter())
123
126
 
127
+ @override
124
128
  def mouseMoveEvent(self, e: QMouseEvent | None) -> None:
125
129
  super().mouseMoveEvent(e)
126
130
  if e is not None and self.itemAt(e.pos()):
@@ -128,6 +132,7 @@ class EnsembleSelectListWidget(QListWidget):
128
132
  else:
129
133
  self.setCursor(QCursor(Qt.CursorShape.ArrowCursor))
130
134
 
135
+ @override
131
136
  def dropEvent(self, event: QDropEvent | None) -> None:
132
137
  super().dropEvent(event)
133
138
  self.ensembleSelectionListChanged.emit()
@@ -171,9 +176,11 @@ class CustomItemDelegate(QStyledItemDelegate):
171
176
  super().__init__()
172
177
  self.swap_pixmap = QIcon("img:reorder.svg").pixmap(QSize(20, 20))
173
178
 
179
+ @override
174
180
  def sizeHint(self, option: Any, index: Any) -> QSize:
175
181
  return QSize(-1, 30)
176
182
 
183
+ @override
177
184
  def paint(
178
185
  self, painter: QPainter | None, option: QStyleOptionViewItem, index: QModelIndex
179
186
  ) -> None:
@@ -23,8 +23,9 @@ from PyQt6.QtWidgets import (
23
23
  QWidget,
24
24
  QWidgetAction,
25
25
  )
26
+ from typing_extensions import override
26
27
 
27
- from .plot_api import EnsembleObject
28
+ from .plot_api import EnsembleObject, PlotApiKeyDefinition
28
29
 
29
30
  if TYPE_CHECKING:
30
31
  from .plottery import PlotContext
@@ -33,6 +34,7 @@ if TYPE_CHECKING:
33
34
  from .plottery.plots.ensemble import EnsemblePlot
34
35
  from .plottery.plots.gaussian_kde import GaussianKDEPlot
35
36
  from .plottery.plots.histogram import HistogramPlot
37
+ from .plottery.plots.misfits import MisfitsPlot
36
38
  from .plottery.plots.statistics import StatisticsPlot
37
39
  from .plottery.plots.std_dev import StdDevPlot
38
40
 
@@ -79,19 +81,23 @@ class CustomNavigationToolbar(NavigationToolbar2QT):
79
81
 
80
82
  action.triggered.connect(lambda _, a=action: self.logToolbarUsage(a.text()))
81
83
 
84
+ @override
82
85
  def logToolbarUsage(self, action_name: str) -> None:
83
86
  logger.info(f"Plotwindow toolbar used: {action_name}")
84
87
 
88
+ @override
85
89
  @Slot(bool)
86
90
  def showLayerWidget(self, show: bool) -> None:
87
91
  self._layer_action.setVisible(show)
88
92
 
93
+ @override
89
94
  @Slot()
90
95
  def resetLayerWidget(
91
96
  self,
92
97
  ) -> None:
93
98
  self._layer_action.defaultWidget().setCurrentIndex(0)
94
99
 
100
+ @override
95
101
  @Slot(int)
96
102
  def updateLayerWidget(self, layers: int) -> None:
97
103
  if layers != len(self._model.stringList()):
@@ -117,6 +123,7 @@ class PlotWidget(QWidget):
117
123
  "DistributionPlot",
118
124
  "CrossEnsembleStatisticsPlot",
119
125
  "StdDevPlot",
126
+ "MisfitsPlot",
120
127
  ],
121
128
  parent: QWidget | None = None,
122
129
  ) -> None:
@@ -168,7 +175,11 @@ class PlotWidget(QWidget):
168
175
  self._figure.clear()
169
176
 
170
177
  def _sync_log_checkbox(self) -> None:
171
- if type(self._plotter).__name__ == "HistogramPlot":
178
+ if type(self._plotter).__name__ in {
179
+ "HistogramPlot",
180
+ "DistributionPlot",
181
+ "GaussianKDEPlot",
182
+ }:
172
183
  self._log_checkbox.setVisible(True)
173
184
  else:
174
185
  self._log_checkbox.setVisible(False)
@@ -183,6 +194,7 @@ class PlotWidget(QWidget):
183
194
  ensemble_to_data_map: dict[EnsembleObject, pd.DataFrame],
184
195
  observations: pd.DataFrame,
185
196
  std_dev_images: dict[str, npt.NDArray[np.float32]],
197
+ key_def: PlotApiKeyDefinition | None = None,
186
198
  ) -> None:
187
199
  self.resetPlot()
188
200
  try:
@@ -195,6 +207,7 @@ class PlotWidget(QWidget):
195
207
  ensemble_to_data_map,
196
208
  observations,
197
209
  std_dev_images,
210
+ key_def,
198
211
  )
199
212
  self._canvas.draw()
200
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 = 2
56
- STD_DEV_DEFAULT = 6
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())
@@ -269,10 +273,13 @@ class PlotWindow(QMainWindow):
269
273
  response_key=key_def.response_metadata.response_key,
270
274
  filter_on=key_def.filter_on,
271
275
  )
272
- elif key_def.parameter_metadata is not None:
276
+ elif (
277
+ key_def.parameter is not None
278
+ and key_def.parameter.type == "gen_kw"
279
+ ):
273
280
  ensemble_to_data_map[ensemble] = self._api.data_for_parameter(
274
281
  ensemble_id=ensemble.id,
275
- parameter_key=key_def.parameter_metadata.key,
282
+ parameter_key=key_def.parameter.name,
276
283
  )
277
284
  except BaseException as e:
278
285
  handle_exception(e)
@@ -287,10 +294,10 @@ class PlotWindow(QMainWindow):
287
294
  handle_exception(e)
288
295
 
289
296
  std_dev_images: dict[str, npt.NDArray[np.float32]] = {}
290
- if "FIELD" in key_def.metadata["data_origin"]:
291
- plot_widget.showLayerWidget.emit(True)
292
297
 
293
- layers = key_def.metadata["ertbox_params"]["nz"]
298
+ if isinstance(key_def.parameter, Field):
299
+ plot_widget.showLayerWidget.emit(True)
300
+ layers = key_def.parameter.ertbox_params.nz
294
301
  plot_widget.updateLayerWidget.emit(layers)
295
302
 
296
303
  if layer is None:
@@ -332,6 +339,18 @@ class PlotWindow(QMainWindow):
332
339
  handle_exception(e)
333
340
  plot_context.history_data = None
334
341
 
342
+ if (
343
+ key_def.response_metadata is not None
344
+ and key_def.response_metadata.response_type == "rft"
345
+ ):
346
+ plot_context.setXLabel(key.split(":")[-1])
347
+ plot_context.setYLabel("TVD")
348
+ plot_context.depth_y_axis = True
349
+ for ekey, data in list(ensemble_to_data_map.items()):
350
+ ensemble_to_data_map[ekey] = data.interpolate(
351
+ method="linear", axis="columns"
352
+ )
353
+
335
354
  for data in ensemble_to_data_map.values():
336
355
  data = data.T
337
356
 
@@ -342,7 +361,11 @@ class PlotWindow(QMainWindow):
342
361
  self._updateCustomizer(plot_widget, self._preferred_ensemble_x_axis_format)
343
362
 
344
363
  plot_widget.updatePlot(
345
- plot_context, ensemble_to_data_map, observations, std_dev_images
364
+ plot_context,
365
+ ensemble_to_data_map,
366
+ observations,
367
+ std_dev_images,
368
+ key_def,
346
369
  )
347
370
 
348
371
  def _updateCustomizer(
@@ -371,15 +394,14 @@ class PlotWindow(QMainWindow):
371
394
  def addPlotWidget(
372
395
  self,
373
396
  name: str,
374
- plotter: (
375
- EnsemblePlot
376
- | StatisticsPlot
377
- | HistogramPlot
378
- | GaussianKDEPlot
379
- | DistributionPlot
380
- | CrossEnsembleStatisticsPlot
381
- | StdDevPlot
382
- ),
397
+ plotter: EnsemblePlot
398
+ | StatisticsPlot
399
+ | HistogramPlot
400
+ | GaussianKDEPlot
401
+ | DistributionPlot
402
+ | CrossEnsembleStatisticsPlot
403
+ | StdDevPlot
404
+ | MisfitsPlot,
383
405
  enabled: bool = True,
384
406
  ) -> None:
385
407
  plot_widget = PlotWidget(name, plotter)
@@ -418,6 +440,7 @@ class PlotWindow(QMainWindow):
418
440
  widget
419
441
  for widget in self._plot_widgets
420
442
  if widget._plotter.dimensionality == key_def.dimensionality
443
+ and (key_def.observations or not widget._plotter.requires_observations)
421
444
  ]
422
445
 
423
446
  # Enabling/disabling tab triggers the
@@ -431,8 +454,6 @@ class PlotWindow(QMainWindow):
431
454
  self._central_tab.setTabEnabled(
432
455
  self._central_tab.indexOf(plot_widget), plot_widget in available_widgets
433
456
  )
434
- self._central_tab.currentChanged.connect(self.currentTabChanged)
435
-
436
457
  current_widget = self._central_tab.currentWidget()
437
458
 
438
459
  if 0 < self._prev_key_dimensionality != key_def.dimensionality:
@@ -447,6 +468,7 @@ class PlotWindow(QMainWindow):
447
468
  self._current_tab_index = -1
448
469
 
449
470
  self._central_tab.setCurrentWidget(current_widget)
471
+ self._central_tab.currentChanged.connect(self.currentTabChanged)
450
472
  self._prev_tab_widget_index = self._central_tab.currentIndex()
451
473
  self._prev_key_dimensionality = key_def.dimensionality
452
474
  self.updatePlot()
@@ -88,6 +88,8 @@ class PlotConfig:
88
88
 
89
89
  self._std_dev_factor = 1 # sigma 1 is default std dev
90
90
 
91
+ self.depth_y_axis = False
92
+
91
93
  def getNumberOfColors(self) -> int:
92
94
  return len(self._line_color_cycle_colors)
93
95
 
@@ -47,6 +47,14 @@ class PlotContext:
47
47
  self._y_axis: str | None = None
48
48
  self._log_scale = False
49
49
 
50
+ @property
51
+ def depth_y_axis(self) -> bool:
52
+ return self._plot_config.depth_y_axis
53
+
54
+ @depth_y_axis.setter
55
+ def depth_y_axis(self, value: bool) -> None:
56
+ self._plot_config.depth_y_axis = value
57
+
50
58
  def plotConfig(self) -> PlotConfig:
51
59
  return self._plot_config
52
60
 
@@ -93,6 +101,12 @@ class PlotContext:
93
101
  )
94
102
  self._y_axis = value
95
103
 
104
+ def setXLabel(self, value: str) -> None:
105
+ self._plot_config.setXLabel(value)
106
+
107
+ def setYLabel(self, value: str) -> None:
108
+ self._plot_config.setYLabel(value)
109
+
96
110
  @property
97
111
  def log_scale(self) -> bool:
98
112
  return self._log_scale
@@ -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
  )
@@ -4,8 +4,7 @@ from typing import TYPE_CHECKING
4
4
 
5
5
  import numpy as np
6
6
  import pandas as pd
7
-
8
- from ert.summary_key_type import is_rate
7
+ from resfo_utilities import is_rate
9
8
 
10
9
  from .history import plotHistory
11
10
  from .observations import plotObservations
@@ -16,13 +15,14 @@ if TYPE_CHECKING:
16
15
  from matplotlib.axes import Axes
17
16
  from matplotlib.figure import Figure
18
17
 
19
- from ert.gui.tools.plot.plot_api import EnsembleObject
18
+ from ert.gui.tools.plot.plot_api import EnsembleObject, PlotApiKeyDefinition
20
19
  from ert.gui.tools.plot.plottery import PlotConfig, PlotContext
21
20
 
22
21
 
23
22
  class EnsemblePlot:
24
23
  def __init__(self) -> None:
25
24
  self.dimensionality = 2
25
+ self.requires_observations = False
26
26
 
27
27
  def plot(
28
28
  self,
@@ -31,6 +31,7 @@ class EnsemblePlot:
31
31
  ensemble_to_data_map: dict[EnsembleObject, pd.DataFrame],
32
32
  observation_data: pd.DataFrame,
33
33
  std_dev_images: dict[str, npt.NDArray[np.float32]],
34
+ key_def: PlotApiKeyDefinition | None = None,
34
35
  ) -> None:
35
36
  config = plot_context.plotConfig()
36
37
  axes = figure.add_subplot(111)
@@ -87,9 +88,16 @@ class EnsemblePlot:
87
88
  if len(data) == 1 and not style.marker:
88
89
  style.marker = "."
89
90
 
91
+ if plot_config.depth_y_axis:
92
+ x = data.to_numpy()
93
+ y = data.index.to_numpy()
94
+ axes.yaxis.set_inverted(True)
95
+ else:
96
+ y = data.to_numpy()
97
+ x = data.index.to_numpy()
90
98
  lines = axes.plot(
91
- data.index.to_numpy(),
92
- data.to_numpy(),
99
+ x,
100
+ y,
93
101
  color=style.color,
94
102
  alpha=style.alpha,
95
103
  marker=style.marker,