webviz-subsurface 0.2.36__py3-none-any.whl → 0.2.38__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/__init__.py +1 -1
- webviz_subsurface/_components/color_picker.py +1 -1
- webviz_subsurface/_datainput/well_completions.py +2 -1
- webviz_subsurface/_providers/ensemble_polygon_provider/__init__.py +3 -0
- webviz_subsurface/_providers/ensemble_polygon_provider/_polygon_discovery.py +97 -0
- webviz_subsurface/_providers/ensemble_polygon_provider/_provider_impl_file.py +226 -0
- webviz_subsurface/_providers/ensemble_polygon_provider/ensemble_polygon_provider.py +53 -0
- webviz_subsurface/_providers/ensemble_polygon_provider/ensemble_polygon_provider_factory.py +99 -0
- webviz_subsurface/_providers/ensemble_polygon_provider/polygon_server.py +125 -0
- webviz_subsurface/plugins/_co2_leakage/_plugin.py +577 -293
- webviz_subsurface/plugins/_co2_leakage/_types.py +7 -0
- webviz_subsurface/plugins/_co2_leakage/_utilities/_misc.py +9 -0
- webviz_subsurface/plugins/_co2_leakage/_utilities/callbacks.py +226 -186
- webviz_subsurface/plugins/_co2_leakage/_utilities/co2volume.py +591 -128
- webviz_subsurface/plugins/_co2_leakage/_utilities/containment_data_provider.py +147 -0
- webviz_subsurface/plugins/_co2_leakage/_utilities/containment_info.py +31 -0
- webviz_subsurface/plugins/_co2_leakage/_utilities/ensemble_well_picks.py +105 -0
- webviz_subsurface/plugins/_co2_leakage/_utilities/generic.py +170 -2
- webviz_subsurface/plugins/_co2_leakage/_utilities/initialization.py +199 -97
- webviz_subsurface/plugins/_co2_leakage/_utilities/polygon_handler.py +60 -0
- webviz_subsurface/plugins/_co2_leakage/_utilities/summary_graphs.py +77 -173
- webviz_subsurface/plugins/_co2_leakage/_utilities/surface_publishing.py +122 -21
- webviz_subsurface/plugins/_co2_leakage/_utilities/unsmry_data_provider.py +108 -0
- webviz_subsurface/plugins/_co2_leakage/views/mainview/mainview.py +44 -19
- webviz_subsurface/plugins/_co2_leakage/views/mainview/settings.py +944 -359
- {webviz_subsurface-0.2.36.dist-info → webviz_subsurface-0.2.38.dist-info}/METADATA +2 -2
- {webviz_subsurface-0.2.36.dist-info → webviz_subsurface-0.2.38.dist-info}/RECORD +33 -20
- {webviz_subsurface-0.2.36.dist-info → webviz_subsurface-0.2.38.dist-info}/WHEEL +1 -1
- /webviz_subsurface/plugins/_co2_leakage/_utilities/{fault_polygons.py → fault_polygons_handler.py} +0 -0
- {webviz_subsurface-0.2.36.dist-info → webviz_subsurface-0.2.38.dist-info}/LICENSE +0 -0
- {webviz_subsurface-0.2.36.dist-info → webviz_subsurface-0.2.38.dist-info}/LICENSE.chromedriver +0 -0
- {webviz_subsurface-0.2.36.dist-info → webviz_subsurface-0.2.38.dist-info}/entry_points.txt +0 -0
- {webviz_subsurface-0.2.36.dist-info → webviz_subsurface-0.2.38.dist-info}/top_level.txt +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import glob
|
|
2
1
|
import logging
|
|
3
2
|
import os
|
|
4
3
|
import warnings
|
|
5
4
|
from pathlib import Path
|
|
6
5
|
from typing import Dict, List, Optional
|
|
7
6
|
|
|
7
|
+
from fmu.ensemble import ScratchEnsemble
|
|
8
8
|
from webviz_config import WebvizSettings
|
|
9
9
|
|
|
10
10
|
from webviz_subsurface._providers import (
|
|
@@ -13,37 +13,87 @@ from webviz_subsurface._providers import (
|
|
|
13
13
|
EnsembleTableProvider,
|
|
14
14
|
EnsembleTableProviderFactory,
|
|
15
15
|
)
|
|
16
|
-
from webviz_subsurface.
|
|
17
|
-
from webviz_subsurface.
|
|
18
|
-
|
|
16
|
+
from webviz_subsurface._providers.ensemble_polygon_provider import PolygonServer
|
|
17
|
+
from webviz_subsurface._providers.ensemble_surface_provider._surface_discovery import (
|
|
18
|
+
discover_per_realization_surface_files,
|
|
19
|
+
)
|
|
20
|
+
from webviz_subsurface.plugins._co2_leakage._utilities.containment_data_provider import (
|
|
21
|
+
ContainmentDataProvider,
|
|
22
|
+
)
|
|
23
|
+
from webviz_subsurface.plugins._co2_leakage._utilities.ensemble_well_picks import (
|
|
24
|
+
EnsembleWellPicks,
|
|
19
25
|
)
|
|
20
26
|
from webviz_subsurface.plugins._co2_leakage._utilities.generic import (
|
|
27
|
+
BoundarySettings,
|
|
28
|
+
FilteredMapAttribute,
|
|
21
29
|
GraphSource,
|
|
22
30
|
MapAttribute,
|
|
31
|
+
MapNamingConvention,
|
|
32
|
+
MenuOptions,
|
|
23
33
|
)
|
|
24
|
-
from webviz_subsurface.plugins.
|
|
25
|
-
|
|
34
|
+
from webviz_subsurface.plugins._co2_leakage._utilities.polygon_handler import (
|
|
35
|
+
PolygonHandler,
|
|
36
|
+
)
|
|
37
|
+
from webviz_subsurface.plugins._co2_leakage._utilities.unsmry_data_provider import (
|
|
38
|
+
UnsmryDataProvider,
|
|
26
39
|
)
|
|
27
40
|
|
|
28
41
|
LOGGER = logging.getLogger(__name__)
|
|
42
|
+
LOGGER_TO_SUPPRESS = logging.getLogger(
|
|
43
|
+
"webviz_subsurface._providers.ensemble_summary_provider._arrow_unsmry_import"
|
|
44
|
+
)
|
|
45
|
+
LOGGER_TO_SUPPRESS.setLevel(logging.ERROR) # We replace the given warning with our own
|
|
29
46
|
WARNING_THRESHOLD_CSV_FILE_SIZE_MB = 100.0
|
|
30
47
|
|
|
31
48
|
|
|
49
|
+
def build_mapping(
|
|
50
|
+
webviz_settings: WebvizSettings,
|
|
51
|
+
ensembles: List[str],
|
|
52
|
+
) -> Dict[str, str]:
|
|
53
|
+
available_attrs_per_ensemble = [
|
|
54
|
+
discover_per_realization_surface_files(
|
|
55
|
+
webviz_settings.shared_settings["scratch_ensembles"][ens],
|
|
56
|
+
"share/results/maps",
|
|
57
|
+
)
|
|
58
|
+
for ens in ensembles
|
|
59
|
+
]
|
|
60
|
+
full_attr_list = [
|
|
61
|
+
[attr.attribute for attr in ens] for ens in available_attrs_per_ensemble
|
|
62
|
+
]
|
|
63
|
+
unique_attributes = set()
|
|
64
|
+
for ens_attr in full_attr_list:
|
|
65
|
+
unique_attributes.update(ens_attr)
|
|
66
|
+
unique_attributes_list = list(unique_attributes)
|
|
67
|
+
mapping = {}
|
|
68
|
+
for attr in unique_attributes_list:
|
|
69
|
+
for name_convention in MapNamingConvention:
|
|
70
|
+
if attr == name_convention.value:
|
|
71
|
+
attribute_key = MapAttribute[name_convention.name].name
|
|
72
|
+
mapping[attribute_key] = attr
|
|
73
|
+
break
|
|
74
|
+
return mapping
|
|
75
|
+
|
|
76
|
+
|
|
32
77
|
def init_map_attribute_names(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
78
|
+
webviz_settings: WebvizSettings,
|
|
79
|
+
ensembles: List[str],
|
|
80
|
+
input_mapping: Optional[Dict[str, str]],
|
|
81
|
+
) -> FilteredMapAttribute:
|
|
82
|
+
default_mapping = build_mapping(webviz_settings, ensembles)
|
|
83
|
+
final_mapping = dict(default_mapping)
|
|
84
|
+
if input_mapping is not None:
|
|
85
|
+
for key, value in input_mapping.items():
|
|
86
|
+
if key in final_mapping and final_mapping[key] != value:
|
|
87
|
+
LOGGER.info(
|
|
88
|
+
f"Conflict on attribute '{key}': prioritizing '{value}' (from input attributes)"
|
|
89
|
+
f" over '{final_mapping[key]}' (from default attributes)"
|
|
90
|
+
)
|
|
91
|
+
final_mapping[key] = value
|
|
92
|
+
final_attributes = {
|
|
93
|
+
(MapAttribute[key].value if key in MapAttribute.__members__ else key): value
|
|
94
|
+
for key, value in final_mapping.items()
|
|
95
|
+
}
|
|
96
|
+
return FilteredMapAttribute(final_attributes)
|
|
47
97
|
|
|
48
98
|
|
|
49
99
|
def init_surface_providers(
|
|
@@ -60,105 +110,157 @@ def init_surface_providers(
|
|
|
60
110
|
|
|
61
111
|
|
|
62
112
|
def init_well_pick_provider(
|
|
63
|
-
|
|
113
|
+
ensemble_paths: Dict[str, str],
|
|
114
|
+
well_pick_path: Optional[str],
|
|
64
115
|
map_surface_names_to_well_pick_names: Optional[Dict[str, str]],
|
|
65
|
-
) -> Dict[str,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
for ens in ensembles:
|
|
69
|
-
well_pick_path = well_pick_dict[ens]
|
|
70
|
-
if well_pick_path is None:
|
|
71
|
-
well_pick_provider[ens] = None
|
|
72
|
-
else:
|
|
73
|
-
try:
|
|
74
|
-
well_pick_provider[ens] = WellPickProvider(
|
|
75
|
-
read_csv(well_pick_path), map_surface_names_to_well_pick_names
|
|
76
|
-
)
|
|
77
|
-
except OSError:
|
|
78
|
-
well_pick_provider[ens] = None
|
|
79
|
-
return well_pick_provider
|
|
116
|
+
) -> Dict[str, EnsembleWellPicks]:
|
|
117
|
+
if well_pick_path is None:
|
|
118
|
+
return {}
|
|
80
119
|
|
|
120
|
+
return {
|
|
121
|
+
ens: EnsembleWellPicks(
|
|
122
|
+
ens_p, well_pick_path, map_surface_names_to_well_pick_names
|
|
123
|
+
)
|
|
124
|
+
for ens, ens_p in ensemble_paths.items()
|
|
125
|
+
}
|
|
81
126
|
|
|
82
|
-
|
|
127
|
+
|
|
128
|
+
def init_polygon_provider_handlers(
|
|
129
|
+
server: PolygonServer,
|
|
130
|
+
ensemble_paths: Dict[str, str],
|
|
131
|
+
options: Optional[BoundarySettings],
|
|
132
|
+
) -> Dict[str, PolygonHandler]:
|
|
133
|
+
filled_options: BoundarySettings = {
|
|
134
|
+
"polygon_file_pattern": "share/results/polygons/*.csv",
|
|
135
|
+
"attribute": "boundary",
|
|
136
|
+
"hazardous_name": "hazardous",
|
|
137
|
+
"containment_name": "containment",
|
|
138
|
+
}
|
|
139
|
+
if options is not None:
|
|
140
|
+
filled_options.update(options)
|
|
141
|
+
return {
|
|
142
|
+
ens: PolygonHandler(
|
|
143
|
+
server,
|
|
144
|
+
ens_path,
|
|
145
|
+
filled_options,
|
|
146
|
+
)
|
|
147
|
+
for ens, ens_path in ensemble_paths.items()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def init_unsmry_data_providers(
|
|
83
152
|
ensemble_roots: Dict[str, str],
|
|
84
|
-
table_rel_path: str,
|
|
85
|
-
) -> Dict[str,
|
|
86
|
-
|
|
153
|
+
table_rel_path: Optional[str],
|
|
154
|
+
) -> Dict[str, UnsmryDataProvider]:
|
|
155
|
+
if table_rel_path is None:
|
|
156
|
+
return {}
|
|
87
157
|
factory = EnsembleTableProviderFactory.instance()
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
158
|
+
providers = {
|
|
159
|
+
ens: _init_ensemble_table_provider(factory, ens, ens_path, table_rel_path)
|
|
160
|
+
for ens, ens_path in ensemble_roots.items()
|
|
161
|
+
}
|
|
162
|
+
return {k: UnsmryDataProvider(v) for k, v in providers.items() if v is not None}
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def init_containment_data_providers(
|
|
166
|
+
ensemble_roots: Dict[str, str],
|
|
167
|
+
table_rel_path: Optional[str],
|
|
168
|
+
) -> Dict[str, ContainmentDataProvider]:
|
|
169
|
+
if table_rel_path is None:
|
|
170
|
+
return {}
|
|
171
|
+
factory = EnsembleTableProviderFactory.instance()
|
|
172
|
+
providers = {
|
|
173
|
+
ens: _init_ensemble_table_provider(factory, ens, ens_path, table_rel_path)
|
|
174
|
+
for ens, ens_path in ensemble_roots.items()
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
k: ContainmentDataProvider(v) for k, v in providers.items() if v is not None
|
|
178
|
+
}
|
|
98
179
|
|
|
180
|
+
|
|
181
|
+
def _init_ensemble_table_provider(
|
|
182
|
+
factory: EnsembleTableProviderFactory,
|
|
183
|
+
ens: str,
|
|
184
|
+
ens_path: str,
|
|
185
|
+
table_rel_path: str,
|
|
186
|
+
) -> Optional[EnsembleTableProvider]:
|
|
187
|
+
try:
|
|
188
|
+
return factory.create_from_per_realization_arrow_file(ens_path, table_rel_path)
|
|
189
|
+
except (KeyError, ValueError) as exc:
|
|
99
190
|
try:
|
|
100
|
-
|
|
191
|
+
return factory.create_from_per_realization_csv_file(
|
|
101
192
|
ens_path, table_rel_path
|
|
102
193
|
)
|
|
103
|
-
except (KeyError, ValueError) as
|
|
194
|
+
except (KeyError, ValueError) as exc2:
|
|
104
195
|
LOGGER.warning(
|
|
105
|
-
f'
|
|
196
|
+
f'\nTried reading "{table_rel_path}" for ensemble "{ens}" as csv with'
|
|
197
|
+
f" error \n- {exc2}, \nand as arrow with error \n- {exc}"
|
|
106
198
|
)
|
|
107
|
-
return
|
|
199
|
+
return None
|
|
108
200
|
|
|
109
201
|
|
|
110
|
-
def
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
max_size = max(max_size, size_in_mb)
|
|
119
|
-
return max_size
|
|
202
|
+
def init_realizations(ensemble_paths: Dict[str, str]) -> Dict[str, List[int]]:
|
|
203
|
+
realization_per_ens = {}
|
|
204
|
+
for ens, ens_path in ensemble_paths.items():
|
|
205
|
+
scratch_ensemble = ScratchEnsemble("dummyEnsembleName", paths=ens_path).filter(
|
|
206
|
+
"OK"
|
|
207
|
+
)
|
|
208
|
+
realization_per_ens[ens] = sorted(list(scratch_ensemble.realizations.keys()))
|
|
209
|
+
return realization_per_ens
|
|
120
210
|
|
|
121
211
|
|
|
122
212
|
def init_menu_options(
|
|
123
213
|
ensemble_roots: Dict[str, str],
|
|
124
|
-
mass_table: Dict[str,
|
|
125
|
-
actual_volume_table: Dict[str,
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
options: Dict[str, Dict[str, Dict[str, List[str]]]] = {}
|
|
214
|
+
mass_table: Dict[str, ContainmentDataProvider],
|
|
215
|
+
actual_volume_table: Dict[str, ContainmentDataProvider],
|
|
216
|
+
unsmry_providers: Dict[str, UnsmryDataProvider],
|
|
217
|
+
) -> Dict[str, Dict[GraphSource, MenuOptions]]:
|
|
218
|
+
options: Dict[str, Dict[GraphSource, MenuOptions]] = {}
|
|
130
219
|
for ens in ensemble_roots.keys():
|
|
131
220
|
options[ens] = {}
|
|
132
|
-
|
|
133
|
-
[GraphSource.CONTAINMENT_MASS
|
|
134
|
-
|
|
135
|
-
[
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
"zones": [],
|
|
141
|
-
"regions": [],
|
|
142
|
-
"phases": ["total", "gas", "aqueous"],
|
|
143
|
-
}
|
|
221
|
+
if ens in mass_table:
|
|
222
|
+
options[ens][GraphSource.CONTAINMENT_MASS] = mass_table[ens].menu_options
|
|
223
|
+
if ens in actual_volume_table:
|
|
224
|
+
options[ens][GraphSource.CONTAINMENT_ACTUAL_VOLUME] = actual_volume_table[
|
|
225
|
+
ens
|
|
226
|
+
].menu_options
|
|
227
|
+
if ens in unsmry_providers:
|
|
228
|
+
options[ens][GraphSource.UNSMRY] = unsmry_providers[ens].menu_options
|
|
144
229
|
return options
|
|
145
230
|
|
|
146
231
|
|
|
147
|
-
def
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
]
|
|
232
|
+
def init_dictionary_of_content(
|
|
233
|
+
menu_options: Dict[str, Dict[GraphSource, MenuOptions]],
|
|
234
|
+
has_maps: bool,
|
|
235
|
+
) -> Dict[str, bool]:
|
|
236
|
+
options = next(iter(menu_options.values()))
|
|
237
|
+
content = {
|
|
238
|
+
"mass": GraphSource.CONTAINMENT_MASS in options,
|
|
239
|
+
"volume": GraphSource.CONTAINMENT_ACTUAL_VOLUME in options,
|
|
240
|
+
"unsmry": GraphSource.UNSMRY in options,
|
|
241
|
+
}
|
|
242
|
+
content["any_table"] = max(content.values())
|
|
243
|
+
content["maps"] = has_maps
|
|
244
|
+
content["zones"] = False
|
|
245
|
+
content["regions"] = False
|
|
246
|
+
content["plume_groups"] = False
|
|
247
|
+
if content["mass"] or content["volume"]:
|
|
248
|
+
content["zones"] = max(
|
|
249
|
+
len(inner_dict["zones"]) > 0
|
|
250
|
+
for outer_dict in menu_options.values()
|
|
251
|
+
for inner_dict in outer_dict.values()
|
|
252
|
+
)
|
|
253
|
+
content["regions"] = max(
|
|
254
|
+
len(inner_dict["regions"]) > 0
|
|
255
|
+
for outer_dict in menu_options.values()
|
|
256
|
+
for inner_dict in outer_dict.values()
|
|
257
|
+
)
|
|
258
|
+
content["plume_groups"] = max(
|
|
259
|
+
len(inner_dict["plume_groups"]) > 0
|
|
260
|
+
for outer_dict in menu_options.values()
|
|
261
|
+
for inner_dict in outer_dict.values()
|
|
262
|
+
)
|
|
263
|
+
return content
|
|
162
264
|
|
|
163
265
|
|
|
164
266
|
def _process_file(file: Optional[str], ensemble_path: str) -> Optional[str]:
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
|
|
3
|
+
from webviz_subsurface._providers.ensemble_polygon_provider import (
|
|
4
|
+
EnsemblePolygonProviderFactory,
|
|
5
|
+
PolygonServer,
|
|
6
|
+
)
|
|
7
|
+
from webviz_subsurface._providers.ensemble_polygon_provider.ensemble_polygon_provider import (
|
|
8
|
+
PolygonsAddress,
|
|
9
|
+
)
|
|
10
|
+
from webviz_subsurface.plugins._co2_leakage._utilities.generic import BoundarySettings
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PolygonHandler:
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
server: PolygonServer,
|
|
17
|
+
ensemble_path: str,
|
|
18
|
+
boundary_options: BoundarySettings,
|
|
19
|
+
) -> None:
|
|
20
|
+
self._server = server
|
|
21
|
+
self._attribute = boundary_options["attribute"]
|
|
22
|
+
self._hazardous_name = boundary_options["hazardous_name"]
|
|
23
|
+
self._containment_name = boundary_options["containment_name"]
|
|
24
|
+
polygon_provider_factory = EnsemblePolygonProviderFactory.instance()
|
|
25
|
+
self._provider = polygon_provider_factory.create_from_ensemble_polygon_files(
|
|
26
|
+
ensemble_path,
|
|
27
|
+
boundary_options["polygon_file_pattern"],
|
|
28
|
+
)
|
|
29
|
+
server.add_provider(self._provider)
|
|
30
|
+
|
|
31
|
+
def extract_hazardous_poly_url(self, realization: List[int]) -> Optional[str]:
|
|
32
|
+
return self._extract_polygon_url(
|
|
33
|
+
self._hazardous_name, self._attribute, realization
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
def extract_containment_poly_url(self, realization: List[int]) -> Optional[str]:
|
|
37
|
+
return self._extract_polygon_url(
|
|
38
|
+
self._containment_name, self._attribute, realization
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def _extract_polygon_url(
|
|
42
|
+
self,
|
|
43
|
+
name: str,
|
|
44
|
+
attribute: str,
|
|
45
|
+
realization: List[int],
|
|
46
|
+
) -> Optional[str]:
|
|
47
|
+
if attribute is None:
|
|
48
|
+
return None
|
|
49
|
+
if len(realization) == 0:
|
|
50
|
+
return None
|
|
51
|
+
# NB! This always returns the url corresponding to the first realization
|
|
52
|
+
address = PolygonsAddress(
|
|
53
|
+
attribute=attribute,
|
|
54
|
+
name=name,
|
|
55
|
+
realization=realization[0],
|
|
56
|
+
)
|
|
57
|
+
return self._server.encode_partial_url(
|
|
58
|
+
provider_id=self._provider.provider_id(),
|
|
59
|
+
polygons_address=address,
|
|
60
|
+
)
|