webviz-subsurface 0.2.39__py3-none-any.whl → 0.2.41__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.
- webviz_subsurface/_figures/timeseries_figure.py +1 -1
- webviz_subsurface/_providers/ensemble_summary_provider/_provider_impl_arrow_lazy.py +3 -1
- webviz_subsurface/_providers/ensemble_summary_provider/_provider_impl_arrow_presampled.py +3 -1
- webviz_subsurface/_providers/ensemble_table_provider/ensemble_table_provider_impl_arrow.py +3 -1
- webviz_subsurface/_utils/dataframe_utils.py +1 -1
- webviz_subsurface/_version.py +34 -0
- webviz_subsurface/plugins/_bhp_qc/views/_view_functions.py +5 -5
- webviz_subsurface/plugins/_co2_migration/__init__.py +1 -0
- webviz_subsurface/plugins/{_co2_leakage → _co2_migration}/_plugin.py +86 -46
- webviz_subsurface/plugins/{_co2_leakage → _co2_migration}/_utilities/callbacks.py +53 -30
- webviz_subsurface/plugins/{_co2_leakage → _co2_migration}/_utilities/co2volume.py +283 -40
- webviz_subsurface/plugins/{_co2_leakage → _co2_migration}/_utilities/color_tables.py +1 -1
- webviz_subsurface/plugins/{_co2_leakage → _co2_migration}/_utilities/containment_data_provider.py +6 -4
- webviz_subsurface/plugins/{_co2_leakage → _co2_migration}/_utilities/containment_info.py +6 -0
- webviz_subsurface/plugins/{_co2_leakage → _co2_migration}/_utilities/ensemble_well_picks.py +1 -1
- webviz_subsurface/plugins/{_co2_leakage → _co2_migration}/_utilities/generic.py +59 -6
- webviz_subsurface/plugins/{_co2_leakage → _co2_migration}/_utilities/initialization.py +73 -10
- webviz_subsurface/plugins/{_co2_leakage → _co2_migration}/_utilities/polygon_handler.py +1 -1
- webviz_subsurface/plugins/{_co2_leakage → _co2_migration}/_utilities/summary_graphs.py +20 -18
- webviz_subsurface/plugins/{_co2_leakage → _co2_migration}/_utilities/surface_publishing.py +18 -20
- webviz_subsurface/plugins/{_co2_leakage → _co2_migration}/_utilities/unsmry_data_provider.py +8 -8
- webviz_subsurface/plugins/{_co2_leakage → _co2_migration}/views/mainview/mainview.py +98 -44
- webviz_subsurface/plugins/{_co2_leakage → _co2_migration}/views/mainview/settings.py +7 -5
- webviz_subsurface/plugins/_disk_usage.py +19 -8
- webviz_subsurface/plugins/_line_plotter_fmu/controllers/build_figure.py +4 -4
- webviz_subsurface/plugins/_map_viewer_fmu/layout.py +2 -1
- webviz_subsurface/plugins/_map_viewer_fmu/map_viewer_fmu.py +1 -1
- webviz_subsurface/plugins/_parameter_analysis/_utils/_parameters_model.py +5 -5
- webviz_subsurface/plugins/_property_statistics/property_statistics.py +1 -1
- webviz_subsurface/plugins/_relative_permeability.py +6 -6
- webviz_subsurface/plugins/_reservoir_simulation_timeseries_regional.py +12 -12
- webviz_subsurface/plugins/_running_time_analysis_fmu.py +6 -1
- webviz_subsurface/plugins/_seismic_misfit.py +2 -3
- webviz_subsurface/plugins/_simulation_time_series/_views/_subplot_view/_utils/vector_statistics.py +4 -4
- webviz_subsurface/plugins/_structural_uncertainty/views/intersection_and_map.py +1 -1
- webviz_subsurface/plugins/_swatinit_qc/_business_logic.py +1 -1
- webviz_subsurface-0.2.41.dist-info/METADATA +822 -0
- {webviz_subsurface-0.2.39.dist-info → webviz_subsurface-0.2.41.dist-info}/RECORD +51 -102
- {webviz_subsurface-0.2.39.dist-info → webviz_subsurface-0.2.41.dist-info}/WHEEL +1 -1
- {webviz_subsurface-0.2.39.dist-info → webviz_subsurface-0.2.41.dist-info}/entry_points.txt +1 -1
- {webviz_subsurface-0.2.39.dist-info → webviz_subsurface-0.2.41.dist-info}/top_level.txt +0 -1
- tests/integration_tests/test_parameter_filter.py +0 -28
- tests/integration_tests/test_surface_selector.py +0 -53
- tests/unit_tests/abbreviations_tests/test_reservoir_simulation.py +0 -94
- tests/unit_tests/data_input/__init__.py +0 -0
- tests/unit_tests/data_input/test_calc_from_cumulatives.py +0 -178
- tests/unit_tests/data_input/test_image_processing.py +0 -11
- tests/unit_tests/mocks/__init__.py +0 -0
- tests/unit_tests/mocks/ensemble_summary_provider_dummy.py +0 -67
- tests/unit_tests/model_tests/__init__.py +0 -0
- tests/unit_tests/model_tests/test_ensemble_model.py +0 -176
- tests/unit_tests/model_tests/test_ensemble_set_model.py +0 -105
- tests/unit_tests/model_tests/test_gruptree_model.py +0 -89
- tests/unit_tests/model_tests/test_property_statistics_model.py +0 -42
- tests/unit_tests/model_tests/test_surface_set_model.py +0 -48
- tests/unit_tests/model_tests/test_well_attributes_model.py +0 -110
- tests/unit_tests/model_tests/test_well_set_model.py +0 -70
- tests/unit_tests/plugin_tests/__init__.py +0 -0
- tests/unit_tests/plugin_tests/test_grouptree.py +0 -175
- tests/unit_tests/plugin_tests/test_simulation_time_series/__init__.py +0 -0
- tests/unit_tests/plugin_tests/test_simulation_time_series/mocks/__init__.py +0 -0
- tests/unit_tests/plugin_tests/test_simulation_time_series/mocks/derived_vectors_accessor_ensemble_summary_provider_mock.py +0 -60
- tests/unit_tests/plugin_tests/test_simulation_time_series/test_utils/__init__.py +0 -0
- tests/unit_tests/plugin_tests/test_simulation_time_series/test_utils/test_create_vector_traces_utils.py +0 -530
- tests/unit_tests/plugin_tests/test_simulation_time_series/test_utils/test_dataframe_utils.py +0 -119
- tests/unit_tests/plugin_tests/test_simulation_time_series/test_utils/test_datetime_utils.py +0 -51
- tests/unit_tests/plugin_tests/test_simulation_time_series/test_utils/test_delta_ensemble_utils.py +0 -222
- tests/unit_tests/plugin_tests/test_simulation_time_series/test_utils/test_derived_delta_ensemble_vectors_accessor_impl.py +0 -319
- tests/unit_tests/plugin_tests/test_simulation_time_series/test_utils/test_derived_ensemble_vectors_accessor_impl.py +0 -271
- tests/unit_tests/plugin_tests/test_simulation_time_series/test_utils/test_derived_ensemble_vectors_accessor_utils.py +0 -78
- tests/unit_tests/plugin_tests/test_simulation_time_series/test_utils/test_derived_vector_accessor.py +0 -57
- tests/unit_tests/plugin_tests/test_simulation_time_series/test_utils/test_ensemble_summary_provider_set_utils.py +0 -213
- tests/unit_tests/plugin_tests/test_simulation_time_series/test_utils/test_from_timeseries_cumulatives.py +0 -322
- tests/unit_tests/plugin_tests/test_simulation_time_series/test_utils/test_history_vectors.py +0 -201
- tests/unit_tests/plugin_tests/test_simulation_time_series/test_utils/test_trace_line_shape.py +0 -56
- tests/unit_tests/plugin_tests/test_simulation_time_series/test_utils/test_vector_statistics.py +0 -171
- tests/unit_tests/plugin_tests/test_tornado_data.py +0 -130
- tests/unit_tests/plugin_tests/test_well_completions.py +0 -158
- tests/unit_tests/provider_tests/__init__.py +0 -0
- tests/unit_tests/provider_tests/test_ensemble_summary_provider.py +0 -255
- tests/unit_tests/provider_tests/test_ensemble_summary_provider_impl_arrow_lazy.py +0 -388
- tests/unit_tests/provider_tests/test_ensemble_summary_provider_impl_arrow_presampled.py +0 -160
- tests/unit_tests/provider_tests/test_ensemble_summary_provider_resampling.py +0 -320
- tests/unit_tests/provider_tests/test_ensemble_table_provider.py +0 -190
- tests/unit_tests/utils_tests/__init__.py +0 -0
- tests/unit_tests/utils_tests/test_dataframe_utils.py +0 -281
- tests/unit_tests/utils_tests/test_ensemble_summary_provider_set/__init__.py +0 -0
- tests/unit_tests/utils_tests/test_ensemble_summary_provider_set/test_ensemble_summary_provider_set.py +0 -306
- tests/unit_tests/utils_tests/test_formatting.py +0 -10
- tests/unit_tests/utils_tests/test_simulation_timeseries.py +0 -51
- webviz_subsurface/plugins/_co2_leakage/__init__.py +0 -1
- webviz_subsurface/plugins/_co2_leakage/_utilities/__init__.py +0 -0
- webviz_subsurface/plugins/_co2_leakage/views/__init__.py +0 -0
- webviz_subsurface/plugins/_co2_leakage/views/mainview/__init__.py +0 -0
- webviz_subsurface-0.2.39.dist-info/METADATA +0 -147
- /webviz_subsurface/plugins/{_co2_leakage → _co2_migration}/_error.py +0 -0
- /webviz_subsurface/plugins/{_co2_leakage → _co2_migration}/_types.py +0 -0
- {tests/integration_tests → webviz_subsurface/plugins/_co2_migration/_utilities}/__init__.py +0 -0
- /webviz_subsurface/plugins/{_co2_leakage → _co2_migration}/_utilities/_misc.py +0 -0
- /webviz_subsurface/plugins/{_co2_leakage → _co2_migration}/_utilities/fault_polygons_handler.py +0 -0
- /webviz_subsurface/plugins/{_co2_leakage → _co2_migration}/_utilities/plume_extent.py +0 -0
- {tests/unit_tests → webviz_subsurface/plugins/_co2_migration/views}/__init__.py +0 -0
- {tests/unit_tests/abbreviations_tests → webviz_subsurface/plugins/_co2_migration/views/mainview}/__init__.py +0 -0
- {webviz_subsurface-0.2.39.dist-info → webviz_subsurface-0.2.41.dist-info/licenses}/LICENSE +0 -0
- {webviz_subsurface-0.2.39.dist-info → webviz_subsurface-0.2.41.dist-info/licenses}/LICENSE.chromedriver +0 -0
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# pylint: disable=too-many-lines
|
|
2
|
+
# pylint: disable=C0103
|
|
2
3
|
# NBNB-AS: We should address this pylint message soon
|
|
4
|
+
import re
|
|
3
5
|
import warnings
|
|
4
6
|
from datetime import datetime as dt
|
|
5
7
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
@@ -11,13 +13,13 @@ import plotly.graph_objects as go
|
|
|
11
13
|
|
|
12
14
|
from webviz_subsurface._providers import EnsembleTableProvider
|
|
13
15
|
from webviz_subsurface._utils.enum_shim import StrEnum
|
|
14
|
-
from webviz_subsurface.plugins.
|
|
16
|
+
from webviz_subsurface.plugins._co2_migration._utilities.containment_data_provider import (
|
|
15
17
|
ContainmentDataProvider,
|
|
16
18
|
)
|
|
17
|
-
from webviz_subsurface.plugins.
|
|
19
|
+
from webviz_subsurface.plugins._co2_migration._utilities.containment_info import (
|
|
18
20
|
ContainmentInfo,
|
|
19
21
|
)
|
|
20
|
-
from webviz_subsurface.plugins.
|
|
22
|
+
from webviz_subsurface.plugins._co2_migration._utilities.generic import (
|
|
21
23
|
Co2MassScale,
|
|
22
24
|
Co2VolumeScale,
|
|
23
25
|
)
|
|
@@ -30,14 +32,35 @@ class _Columns(StrEnum):
|
|
|
30
32
|
VOLUME_OUTSIDE = "volume_outside"
|
|
31
33
|
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
class Colors(StrEnum):
|
|
36
|
+
# pylint: disable=invalid-name
|
|
37
|
+
total = "#222222"
|
|
38
|
+
contained = "#00aa00"
|
|
39
|
+
outside = "#006ddd"
|
|
40
|
+
hazardous = "#dd4300"
|
|
41
|
+
dissolved_water = "#208eb7"
|
|
42
|
+
dissolved_oil = "#A0522D"
|
|
43
|
+
gas = "#C41E3A"
|
|
44
|
+
free = "#FF2400"
|
|
45
|
+
trapped = "#880808"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Marks(StrEnum):
|
|
49
|
+
dissolved_water = "/"
|
|
50
|
+
dissolved_oil = "x"
|
|
51
|
+
gas = ""
|
|
52
|
+
free = ""
|
|
53
|
+
trapped = "."
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class Lines(StrEnum):
|
|
57
|
+
dissolved_water = "dash"
|
|
58
|
+
dissolved_oil = "longdash"
|
|
59
|
+
gas = "dot"
|
|
60
|
+
free = "dot"
|
|
61
|
+
trapped = "dashdot"
|
|
62
|
+
|
|
63
|
+
|
|
41
64
|
_COLOR_ZONES = [
|
|
42
65
|
"#e91451",
|
|
43
66
|
"#daa218",
|
|
@@ -89,16 +112,13 @@ def _read_dataframe(
|
|
|
89
112
|
return df
|
|
90
113
|
|
|
91
114
|
|
|
92
|
-
def _get_colors(
|
|
93
|
-
if split
|
|
94
|
-
return [
|
|
95
|
-
if split == "phase":
|
|
96
|
-
if num_cols == 2:
|
|
97
|
-
return [_COLOR_GAS, _COLOR_DISSOLVED]
|
|
98
|
-
return [_COLOR_FREE, _COLOR_TRAPPED, _COLOR_DISSOLVED]
|
|
115
|
+
def _get_colors(color_options: List[str], split: str) -> List[str]:
|
|
116
|
+
if split in {"containment", "phase"}:
|
|
117
|
+
return [Colors[option] for option in color_options]
|
|
99
118
|
options = list(_COLOR_ZONES)
|
|
100
119
|
if split == "region":
|
|
101
120
|
options.reverse()
|
|
121
|
+
num_cols = len(color_options)
|
|
102
122
|
if len(options) >= num_cols:
|
|
103
123
|
return options[:num_cols]
|
|
104
124
|
num_lengths = int(np.ceil(num_cols / len(options)))
|
|
@@ -106,7 +126,8 @@ def _get_colors(num_cols: int = 3, split: str = "zone") -> List[str]:
|
|
|
106
126
|
return new_cols[:num_cols]
|
|
107
127
|
|
|
108
128
|
|
|
109
|
-
def _get_marks(
|
|
129
|
+
def _get_marks(mark_options: List[str], mark_choice: str) -> List[str]:
|
|
130
|
+
num_marks = len(mark_options)
|
|
110
131
|
if mark_choice == "none":
|
|
111
132
|
return [""] * num_marks
|
|
112
133
|
if mark_choice == "containment":
|
|
@@ -121,7 +142,7 @@ def _get_marks(num_marks: int, mark_choice: str) -> List[str]:
|
|
|
121
142
|
)
|
|
122
143
|
return base_pattern[:num_marks]
|
|
123
144
|
# mark_choice == "phase":
|
|
124
|
-
return [
|
|
145
|
+
return [Marks[option] for option in mark_options]
|
|
125
146
|
|
|
126
147
|
|
|
127
148
|
def _get_line_types(mark_options: List[str], mark_choice: str) -> List[str]:
|
|
@@ -138,7 +159,7 @@ def _get_line_types(mark_options: List[str], mark_choice: str) -> List[str]:
|
|
|
138
159
|
)
|
|
139
160
|
return [options[i % 6] for i in range(len(mark_options))]
|
|
140
161
|
# mark_choice == "phase":
|
|
141
|
-
return [
|
|
162
|
+
return [Lines[option] for option in mark_options]
|
|
142
163
|
|
|
143
164
|
|
|
144
165
|
def _prepare_pattern_and_color_options(
|
|
@@ -150,10 +171,10 @@ def _prepare_pattern_and_color_options(
|
|
|
150
171
|
no_mark = mark_choice == "none"
|
|
151
172
|
mark_options = [] if no_mark else getattr(containment_info, f"{mark_choice}s")
|
|
152
173
|
color_options = getattr(containment_info, f"{color_choice}s")
|
|
174
|
+
marks = _get_marks(mark_options, mark_choice)
|
|
175
|
+
colors = _get_colors(color_options, color_choice)
|
|
153
176
|
num_colors = len(color_options)
|
|
154
177
|
num_marks = num_colors if no_mark else len(mark_options)
|
|
155
|
-
marks = _get_marks(num_marks, mark_choice)
|
|
156
|
-
colors = _get_colors(num_colors, color_choice)
|
|
157
178
|
if no_mark:
|
|
158
179
|
cat_ord = {"type": color_options}
|
|
159
180
|
df["type"] = df[color_choice]
|
|
@@ -186,7 +207,7 @@ def _prepare_pattern_and_color_options_statistics_plot(
|
|
|
186
207
|
num_colors = len(color_options)
|
|
187
208
|
num_marks = num_colors if no_mark else len(mark_options)
|
|
188
209
|
line_types = _get_line_types(mark_options, mark_choice)
|
|
189
|
-
colors = _get_colors(
|
|
210
|
+
colors = _get_colors(color_options, color_choice)
|
|
190
211
|
|
|
191
212
|
if mark_choice == "phase":
|
|
192
213
|
mark_options = ["total"] + mark_options
|
|
@@ -273,9 +294,8 @@ def _prepare_line_type_and_color_options(
|
|
|
273
294
|
if mark_choice != "none":
|
|
274
295
|
mark_options = list(getattr(containment_info, f"{mark_choice}s"))
|
|
275
296
|
color_options = list(getattr(containment_info, f"{color_choice}s"))
|
|
276
|
-
num_colors = len(color_options)
|
|
277
297
|
line_types = _get_line_types(mark_options, mark_choice)
|
|
278
|
-
colors = _get_colors(
|
|
298
|
+
colors = _get_colors(color_options, color_choice)
|
|
279
299
|
|
|
280
300
|
filter_mark = True
|
|
281
301
|
if mark_choice in ["containment", "phase"]:
|
|
@@ -523,8 +543,14 @@ def generate_co2_volume_figure(
|
|
|
523
543
|
custom_data=["type", "prop"],
|
|
524
544
|
)
|
|
525
545
|
fig.update_traces(
|
|
526
|
-
hovertemplate=
|
|
527
|
-
|
|
546
|
+
hovertemplate=(
|
|
547
|
+
"<span style='font-family:Courier New;'>"
|
|
548
|
+
"Type : %{customdata[0]}<br>"
|
|
549
|
+
"Amount : %{x:.3f}<br>"
|
|
550
|
+
"Realization: %{y}<br>"
|
|
551
|
+
"Proportion : %{customdata[1]}"
|
|
552
|
+
"</span><extra></extra>"
|
|
553
|
+
),
|
|
528
554
|
)
|
|
529
555
|
if legendonly_traces is not None:
|
|
530
556
|
_toggle_trace_visibility(fig.data, legendonly_traces)
|
|
@@ -577,8 +603,14 @@ def generate_co2_time_containment_one_realization_figure(
|
|
|
577
603
|
custom_data=["type", "prop"],
|
|
578
604
|
)
|
|
579
605
|
fig.update_traces(
|
|
580
|
-
hovertemplate=
|
|
581
|
-
|
|
606
|
+
hovertemplate=(
|
|
607
|
+
"<span style='font-family:Courier New;'>"
|
|
608
|
+
"Type : %{customdata[0]}<br>"
|
|
609
|
+
"Date : %{x}<br>"
|
|
610
|
+
"Amount : %{y:.3f}<br>"
|
|
611
|
+
"Proportion: %{customdata[1]}"
|
|
612
|
+
"</span><extra></extra>"
|
|
613
|
+
),
|
|
582
614
|
)
|
|
583
615
|
_add_hover_info_in_field(fig, df, cat_ord, colors)
|
|
584
616
|
fig.layout.yaxis.range = y_limits
|
|
@@ -623,7 +655,7 @@ def _add_hover_info_in_field(
|
|
|
623
655
|
for date in dates
|
|
624
656
|
}
|
|
625
657
|
prev_vals = {date: 0 for date in dates}
|
|
626
|
-
date_dict = spaced_dates(dates, 4)
|
|
658
|
+
date_dict = spaced_dates(dates, 4) # type: ignore[arg-type]
|
|
627
659
|
for name, color in zip(cat_ord["type"], colors):
|
|
628
660
|
sub_df = df[df["type"] == name]
|
|
629
661
|
for date in dates:
|
|
@@ -640,8 +672,13 @@ def _add_hover_info_in_field(
|
|
|
640
672
|
y=y_vals,
|
|
641
673
|
mode="lines",
|
|
642
674
|
line=go.scatter.Line(color=color),
|
|
643
|
-
text=
|
|
644
|
-
|
|
675
|
+
text=(
|
|
676
|
+
"<span style='font-family:Courier New;'>"
|
|
677
|
+
f"Type : {name}<br>"
|
|
678
|
+
f"Date : {date_strings[date]}<br>"
|
|
679
|
+
f"Amount : {amount:.3f}<br>"
|
|
680
|
+
f"Proportion: {prop}"
|
|
681
|
+
),
|
|
645
682
|
opacity=0,
|
|
646
683
|
hoverinfo="text",
|
|
647
684
|
hoveron="points",
|
|
@@ -768,8 +805,13 @@ def generate_co2_time_containment_figure(
|
|
|
768
805
|
fig.add_scatter(y=[0.0], **dummy_args, **args)
|
|
769
806
|
|
|
770
807
|
hover_template = (
|
|
771
|
-
"
|
|
772
|
-
"
|
|
808
|
+
"<span style='font-family:Courier New;'>"
|
|
809
|
+
"Type : %{meta[1]}<br>"
|
|
810
|
+
"Date : %{x}<br>"
|
|
811
|
+
"Amount : %{y:.3f}<br>"
|
|
812
|
+
"Realization: %{meta[0]}<br>"
|
|
813
|
+
"Proportion : %{customdata}"
|
|
814
|
+
"</span><extra></extra>"
|
|
773
815
|
)
|
|
774
816
|
|
|
775
817
|
if containment_info.use_stats:
|
|
@@ -795,8 +837,12 @@ def generate_co2_time_containment_figure(
|
|
|
795
837
|
)
|
|
796
838
|
realizations = ["p10", "mean", "p90"] # type: ignore
|
|
797
839
|
hover_template = (
|
|
798
|
-
"
|
|
840
|
+
"<span style='font-family:Courier New;'>"
|
|
841
|
+
"Type : %{meta[1]}<br>"
|
|
842
|
+
"Date : %{x}<br>"
|
|
843
|
+
"Amount : %{y:.3f}<br>"
|
|
799
844
|
"Statistic: %{meta[0]}"
|
|
845
|
+
"</span><extra></extra>"
|
|
800
846
|
)
|
|
801
847
|
for rlz in realizations:
|
|
802
848
|
lwd = 1.5 if rlz in ["p10", "p90"] else 2.5
|
|
@@ -823,6 +869,9 @@ def generate_co2_time_containment_figure(
|
|
|
823
869
|
"meta": [rlz, name],
|
|
824
870
|
"hovertemplate": hover_template,
|
|
825
871
|
}
|
|
872
|
+
# Note: The current implementation of CSV export in extract_df_from_fig()
|
|
873
|
+
# uses 'meta' to extract realization number and name.
|
|
874
|
+
# Make sure to keep it in sync when modifying.
|
|
826
875
|
if not containment_info.use_stats:
|
|
827
876
|
args["customdata"] = sub_df[sub_df["name"] == name]["prop"]
|
|
828
877
|
if name in inactive_cols_at_startup:
|
|
@@ -859,8 +908,7 @@ def generate_co2_statistics_figure(
|
|
|
859
908
|
mark_choice,
|
|
860
909
|
)
|
|
861
910
|
|
|
862
|
-
|
|
863
|
-
df = df.drop(columns=["REAL", "realization"]).reset_index(drop=True)
|
|
911
|
+
df = df.drop(columns=["REAL"]).reset_index(drop=True)
|
|
864
912
|
fig = px.ecdf(
|
|
865
913
|
df,
|
|
866
914
|
x="amount",
|
|
@@ -872,6 +920,7 @@ def generate_co2_statistics_figure(
|
|
|
872
920
|
line_dash="type" if mark_choice != "none" else None,
|
|
873
921
|
line_dash_sequence=line_types,
|
|
874
922
|
category_orders=cat_ord,
|
|
923
|
+
hover_data=["realization"],
|
|
875
924
|
)
|
|
876
925
|
|
|
877
926
|
if legend_only_traces is None:
|
|
@@ -880,9 +929,18 @@ def generate_co2_statistics_figure(
|
|
|
880
929
|
else:
|
|
881
930
|
_toggle_trace_visibility(fig.data, legend_only_traces)
|
|
882
931
|
|
|
932
|
+
# Note: The current implementation of CSV export in extract_df_from_fig()
|
|
933
|
+
# uses the customdata+hovertemplate to extract realization number.
|
|
934
|
+
# Make sure to keep it in sync when modifying.
|
|
883
935
|
fig.update_traces(
|
|
884
|
-
hovertemplate=
|
|
885
|
-
|
|
936
|
+
hovertemplate=(
|
|
937
|
+
"<span style='font-family:Courier New;'>"
|
|
938
|
+
"Type : %{data.name}<br>"
|
|
939
|
+
"Amount : %{x:.3f}<br>"
|
|
940
|
+
"Probability: %{y:.3f}<br>"
|
|
941
|
+
"Realization: %{customdata[0]}"
|
|
942
|
+
"</span><extra></extra>"
|
|
943
|
+
),
|
|
886
944
|
)
|
|
887
945
|
fig.layout.yaxis.range = [-0.02, 1.02]
|
|
888
946
|
fig.layout.legend.tracegroupgap = 0
|
|
@@ -951,6 +1009,10 @@ def generate_co2_box_plot_figure(
|
|
|
951
1009
|
)
|
|
952
1010
|
)
|
|
953
1011
|
|
|
1012
|
+
# Note: The current implementation of CSV export in extract_df_from_fig()
|
|
1013
|
+
# uses the hovertemplate text string to extract the data.
|
|
1014
|
+
# Changing the hovertemplate text string might break the CSV export.
|
|
1015
|
+
|
|
954
1016
|
fig.add_trace(
|
|
955
1017
|
go.Bar(
|
|
956
1018
|
x=[count],
|
|
@@ -1081,3 +1143,184 @@ def _toggle_trace_visibility(traces: List, legendonly_names: List[str]) -> None:
|
|
|
1081
1143
|
t.visible = "legendonly"
|
|
1082
1144
|
else:
|
|
1083
1145
|
t.visible = True
|
|
1146
|
+
|
|
1147
|
+
|
|
1148
|
+
def parse_hover_template(hover_template: str) -> dict:
|
|
1149
|
+
parsed_hover_dict = {}
|
|
1150
|
+
for item in hover_template.split("<br>"):
|
|
1151
|
+
# Remove HTML tags (like <span style='font-family:Courier New;'>)
|
|
1152
|
+
clean_item = re.sub(r"<[^>]*>", "", item)
|
|
1153
|
+
clean_item = clean_item.strip()
|
|
1154
|
+
|
|
1155
|
+
if ":" in clean_item:
|
|
1156
|
+
key, value = clean_item.split(":", 1)
|
|
1157
|
+
key = key.strip()
|
|
1158
|
+
value = value.strip()
|
|
1159
|
+
parsed_hover_dict[key] = value
|
|
1160
|
+
return parsed_hover_dict
|
|
1161
|
+
|
|
1162
|
+
|
|
1163
|
+
def _extract_data_from_box_trace(trace: go.Box) -> dict[str, Any]:
|
|
1164
|
+
if hasattr(trace, "hovertemplate"):
|
|
1165
|
+
hover_dict = parse_hover_template(trace.hovertemplate)
|
|
1166
|
+
trace_name = hover_dict.get("Type", "Unknown")
|
|
1167
|
+
trace_name = trace_name.replace(",", "_").replace(" ", "")
|
|
1168
|
+
return {
|
|
1169
|
+
"type": trace_name,
|
|
1170
|
+
"min": float(hover_dict.get("Min", -1)),
|
|
1171
|
+
"lower_whisker": float(hover_dict.get("Lower whisker", -1)),
|
|
1172
|
+
"p90": float(hover_dict.get("p90 (not shown)", -1)),
|
|
1173
|
+
"q1": float(hover_dict.get("Q1", -1)),
|
|
1174
|
+
"median": float(hover_dict.get("Median", -1)),
|
|
1175
|
+
"q3": float(hover_dict.get("Q3", -1)),
|
|
1176
|
+
"p10": float(hover_dict.get("p10 (not shown)", -1)),
|
|
1177
|
+
"top_whisker": float(hover_dict.get("Top whisker", -1)),
|
|
1178
|
+
"max": float(hover_dict.get("Max", -1)),
|
|
1179
|
+
}
|
|
1180
|
+
return {}
|
|
1181
|
+
|
|
1182
|
+
|
|
1183
|
+
def _extract_data_from_general_trace(
|
|
1184
|
+
trace: Union[go.Box, go.Scatter], plot_choice: str
|
|
1185
|
+
) -> List[Dict[str, Any]]:
|
|
1186
|
+
records = []
|
|
1187
|
+
if plot_choice == "containment_time_multiple":
|
|
1188
|
+
meta_data = getattr(trace, "meta", None)
|
|
1189
|
+
realization = (
|
|
1190
|
+
meta_data[0] if meta_data is not None and len(meta_data) > 0 else -1
|
|
1191
|
+
)
|
|
1192
|
+
trace_name = (
|
|
1193
|
+
meta_data[1] if meta_data is not None and len(meta_data) > 1 else "Unknown"
|
|
1194
|
+
)
|
|
1195
|
+
else:
|
|
1196
|
+
trace_name = getattr(trace, "name", "Unknown")
|
|
1197
|
+
|
|
1198
|
+
if plot_choice in ["probability", "containment_state"]:
|
|
1199
|
+
if trace.x is not None and "_inputArray" in trace.x:
|
|
1200
|
+
x_data = trace.x["_inputArray"]
|
|
1201
|
+
x_data = {int(k): v for k, v in x_data.items() if str(k).isdigit()}
|
|
1202
|
+
elif plot_choice in [
|
|
1203
|
+
"containment_time_single",
|
|
1204
|
+
"containment_time_multiple",
|
|
1205
|
+
]:
|
|
1206
|
+
if trace.x is not None:
|
|
1207
|
+
x_data = dict(enumerate(trace.x))
|
|
1208
|
+
if plot_choice in [
|
|
1209
|
+
"probability",
|
|
1210
|
+
"containment_time_single",
|
|
1211
|
+
"containment_time_multiple",
|
|
1212
|
+
]:
|
|
1213
|
+
if trace.y is not None and "_inputArray" in trace.y:
|
|
1214
|
+
y_data = trace.y["_inputArray"]
|
|
1215
|
+
y_data = {int(k): v for k, v in y_data.items() if str(k).isdigit()}
|
|
1216
|
+
elif plot_choice == "containment_state":
|
|
1217
|
+
if trace.y is not None:
|
|
1218
|
+
y_data = {k: int(v) for k, v in enumerate(trace.y)}
|
|
1219
|
+
xy_data = {k: (x_data[k], y_data[k]) for k in x_data if k in y_data}
|
|
1220
|
+
|
|
1221
|
+
if plot_choice == "probability":
|
|
1222
|
+
custom_data = []
|
|
1223
|
+
if (
|
|
1224
|
+
hasattr(trace, "customdata")
|
|
1225
|
+
and trace.customdata is not None
|
|
1226
|
+
and hasattr(trace, "hovertemplate")
|
|
1227
|
+
and trace.hovertemplate is not None
|
|
1228
|
+
):
|
|
1229
|
+
match = re.search(r"(\w+)\s*:\s*%\{customdata\[0\]\}", trace.hovertemplate)
|
|
1230
|
+
col_name = match.group(1).lower() if match else "customdata"
|
|
1231
|
+
if col_name == "realization":
|
|
1232
|
+
if "_inputArray" in trace.customdata:
|
|
1233
|
+
custom_data = [x["0"] for x in trace.customdata["_inputArray"]]
|
|
1234
|
+
|
|
1235
|
+
trace_name = trace_name.replace(",", "_").replace(" ", "")
|
|
1236
|
+
for j, (x_val, y_val) in xy_data.items():
|
|
1237
|
+
if plot_choice == "probability":
|
|
1238
|
+
record = {
|
|
1239
|
+
"type": trace_name,
|
|
1240
|
+
"amount": x_val,
|
|
1241
|
+
"probability": y_val,
|
|
1242
|
+
}
|
|
1243
|
+
if custom_data:
|
|
1244
|
+
record["realization"] = custom_data[j]
|
|
1245
|
+
elif plot_choice == "containment_state":
|
|
1246
|
+
record = {
|
|
1247
|
+
"type": trace_name,
|
|
1248
|
+
"amount": x_val,
|
|
1249
|
+
"realization": y_val,
|
|
1250
|
+
}
|
|
1251
|
+
elif plot_choice == "containment_time_single":
|
|
1252
|
+
record = {
|
|
1253
|
+
"type": trace_name,
|
|
1254
|
+
"date": x_val,
|
|
1255
|
+
"amount": y_val,
|
|
1256
|
+
}
|
|
1257
|
+
elif plot_choice == "containment_time_multiple":
|
|
1258
|
+
record = {
|
|
1259
|
+
"type": trace_name,
|
|
1260
|
+
"date": x_val,
|
|
1261
|
+
"amount": y_val,
|
|
1262
|
+
}
|
|
1263
|
+
if realization in ["p10", "p90", "mean"]:
|
|
1264
|
+
col_name = "statistic"
|
|
1265
|
+
else:
|
|
1266
|
+
col_name = "realization"
|
|
1267
|
+
record[col_name] = realization
|
|
1268
|
+
records.append(record)
|
|
1269
|
+
return records
|
|
1270
|
+
|
|
1271
|
+
|
|
1272
|
+
def extract_df_from_fig(fig_data: tuple, plot_choice: str) -> pd.DataFrame:
|
|
1273
|
+
if plot_choice == "containment_time":
|
|
1274
|
+
# Distinguish between single and multiple realizations selected:
|
|
1275
|
+
if hasattr(fig_data[0], "stackgroup") and fig_data[0].stackgroup is not None:
|
|
1276
|
+
plot_choice = "containment_time_single"
|
|
1277
|
+
else:
|
|
1278
|
+
plot_choice = "containment_time_multiple"
|
|
1279
|
+
|
|
1280
|
+
data_records = []
|
|
1281
|
+
for trace in fig_data:
|
|
1282
|
+
if hasattr(trace, "visible") and trace.visible == "legendonly":
|
|
1283
|
+
continue # Skip hidden traces
|
|
1284
|
+
if plot_choice == "containment_time_multiple":
|
|
1285
|
+
is_data_point_trace = (
|
|
1286
|
+
hasattr(trace, "showlegend")
|
|
1287
|
+
and trace.showlegend is False
|
|
1288
|
+
and hasattr(trace, "name")
|
|
1289
|
+
and trace.name == ""
|
|
1290
|
+
)
|
|
1291
|
+
if not is_data_point_trace:
|
|
1292
|
+
# Only keep subset of traces that we want
|
|
1293
|
+
continue
|
|
1294
|
+
elif plot_choice == "containment_time_single":
|
|
1295
|
+
is_line_trace = (
|
|
1296
|
+
hasattr(trace, "showlegend")
|
|
1297
|
+
and trace.showlegend is False
|
|
1298
|
+
and hasattr(trace, "mode")
|
|
1299
|
+
and trace.mode == "lines"
|
|
1300
|
+
)
|
|
1301
|
+
if is_line_trace:
|
|
1302
|
+
continue
|
|
1303
|
+
elif plot_choice == "box":
|
|
1304
|
+
is_invisible_trace = (
|
|
1305
|
+
hasattr(trace, "showlegend")
|
|
1306
|
+
and trace.showlegend is False
|
|
1307
|
+
and hasattr(trace, "opacity")
|
|
1308
|
+
and trace.opacity == 0
|
|
1309
|
+
)
|
|
1310
|
+
if not is_invisible_trace:
|
|
1311
|
+
# Keep only the invisible box traces
|
|
1312
|
+
# They have all the data stored in the hover box,
|
|
1313
|
+
# while the visible traces only got lower/upper whisker and median
|
|
1314
|
+
continue
|
|
1315
|
+
|
|
1316
|
+
if plot_choice == "box":
|
|
1317
|
+
record = _extract_data_from_box_trace(trace)
|
|
1318
|
+
if record:
|
|
1319
|
+
data_records.append(record)
|
|
1320
|
+
else:
|
|
1321
|
+
if hasattr(trace, "x") and hasattr(trace, "y"):
|
|
1322
|
+
records = _extract_data_from_general_trace(trace, plot_choice)
|
|
1323
|
+
if records:
|
|
1324
|
+
data_records += records
|
|
1325
|
+
|
|
1326
|
+
return pd.DataFrame(data_records)
|
|
@@ -6,7 +6,7 @@ from webviz_subsurface.plugins._map_viewer_fmu.color_tables import default_color
|
|
|
6
6
|
ColorTables = List[Dict[str, Any]]
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
def
|
|
9
|
+
def co2migration_color_tables() -> ColorTables:
|
|
10
10
|
tables = default_color_tables + uniform_color_tables()
|
|
11
11
|
rev_tables = _reversed_color_tables(tables)
|
|
12
12
|
return [t for tup in zip(tables, rev_tables) for t in tup]
|
webviz_subsurface/plugins/{_co2_leakage → _co2_migration}/_utilities/containment_data_provider.py
RENAMED
|
@@ -3,7 +3,7 @@ from typing import List, Optional, Union
|
|
|
3
3
|
import pandas as pd
|
|
4
4
|
|
|
5
5
|
from webviz_subsurface._providers import EnsembleTableProvider
|
|
6
|
-
from webviz_subsurface.plugins.
|
|
6
|
+
from webviz_subsurface.plugins._co2_migration._utilities.generic import (
|
|
7
7
|
Co2MassScale,
|
|
8
8
|
Co2VolumeScale,
|
|
9
9
|
MenuOptions,
|
|
@@ -105,10 +105,12 @@ class ContainmentDataProvider:
|
|
|
105
105
|
|
|
106
106
|
plume_groups = sorted(plume_groups, key=plume_sort_key)
|
|
107
107
|
|
|
108
|
+
phases = ["total", "gas", "dissolved_water"]
|
|
108
109
|
if "free_gas" in list(df["phase"]):
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
idx = phases.index("gas")
|
|
111
|
+
phases = phases[:idx] + ["free_gas", "trapped_gas"] + phases[idx + 1 :]
|
|
112
|
+
if "dissolved_oil" in list(df["phase"]):
|
|
113
|
+
phases.append("dissolved_oil")
|
|
112
114
|
|
|
113
115
|
dates = df["date"].unique()
|
|
114
116
|
dates.sort()
|
|
@@ -9,6 +9,12 @@ class StatisticsTabOption(StrEnum):
|
|
|
9
9
|
BOX_PLOT = "box-plot"
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
class MainTabOption(StrEnum):
|
|
13
|
+
CONTAINMENT_STATE = "containment-state"
|
|
14
|
+
CONTAINMENT_OVER_TIME = "containment-over-time"
|
|
15
|
+
STATISTICS = "statistics"
|
|
16
|
+
|
|
17
|
+
|
|
12
18
|
# pylint: disable=too-many-instance-attributes
|
|
13
19
|
@dataclass(frozen=True) # NBNB-AS: Removed slots=True (python>=3.10)
|
|
14
20
|
class ContainmentInfo:
|
|
@@ -4,7 +4,7 @@ from pathlib import Path
|
|
|
4
4
|
from typing import Any, Dict, List, Optional
|
|
5
5
|
|
|
6
6
|
from webviz_subsurface._utils.webvizstore_functions import read_csv
|
|
7
|
-
from webviz_subsurface.plugins.
|
|
7
|
+
from webviz_subsurface.plugins._co2_migration._utilities._misc import realization_paths
|
|
8
8
|
from webviz_subsurface.plugins._map_viewer_fmu._tmp_well_pick_provider import (
|
|
9
9
|
WellPickProvider,
|
|
10
10
|
)
|