webviz-subsurface 0.2.36__py3-none-any.whl → 0.2.37__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/_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 +531 -377
- webviz_subsurface/plugins/_co2_leakage/_utilities/_misc.py +9 -0
- webviz_subsurface/plugins/_co2_leakage/_utilities/callbacks.py +169 -173
- webviz_subsurface/plugins/_co2_leakage/_utilities/co2volume.py +329 -84
- webviz_subsurface/plugins/_co2_leakage/_utilities/containment_data_provider.py +147 -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 +189 -96
- 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 +29 -21
- webviz_subsurface/plugins/_co2_leakage/_utilities/unsmry_data_provider.py +108 -0
- webviz_subsurface/plugins/_co2_leakage/views/mainview/mainview.py +30 -18
- webviz_subsurface/plugins/_co2_leakage/views/mainview/settings.py +805 -343
- {webviz_subsurface-0.2.36.dist-info → webviz_subsurface-0.2.37.dist-info}/METADATA +2 -2
- {webviz_subsurface-0.2.36.dist-info → webviz_subsurface-0.2.37.dist-info}/RECORD +30 -19
- {webviz_subsurface-0.2.36.dist-info → webviz_subsurface-0.2.37.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.37.dist-info}/LICENSE +0 -0
- {webviz_subsurface-0.2.36.dist-info → webviz_subsurface-0.2.37.dist-info}/LICENSE.chromedriver +0 -0
- {webviz_subsurface-0.2.36.dist-info → webviz_subsurface-0.2.37.dist-info}/entry_points.txt +0 -0
- {webviz_subsurface-0.2.36.dist-info → webviz_subsurface-0.2.37.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,80 @@ 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,
|
|
33
|
+
)
|
|
34
|
+
from webviz_subsurface.plugins._co2_leakage._utilities.polygon_handler import (
|
|
35
|
+
PolygonHandler,
|
|
23
36
|
)
|
|
24
|
-
from webviz_subsurface.plugins.
|
|
25
|
-
|
|
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
|
-
|
|
78
|
+
webviz_settings: WebvizSettings,
|
|
79
|
+
ensembles: List[str],
|
|
80
|
+
mapping: Optional[Dict[str, str]],
|
|
81
|
+
) -> FilteredMapAttribute:
|
|
35
82
|
if mapping is None:
|
|
36
83
|
# Based on name convention of xtgeoapp_grd3dmaps:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
MapAttribute.DISSOLVED: "co2-mass-aqu-phase",
|
|
44
|
-
MapAttribute.FREE: "co2-mass-gas-phase",
|
|
45
|
-
}
|
|
46
|
-
return {MapAttribute[key]: value for key, value in mapping.items()}
|
|
84
|
+
mapping = build_mapping(webviz_settings, ensembles)
|
|
85
|
+
final_attributes = {
|
|
86
|
+
(MapAttribute[key].value if key in MapAttribute.__members__ else key): value
|
|
87
|
+
for key, value in mapping.items()
|
|
88
|
+
}
|
|
89
|
+
return FilteredMapAttribute(final_attributes)
|
|
47
90
|
|
|
48
91
|
|
|
49
92
|
def init_surface_providers(
|
|
@@ -60,105 +103,155 @@ def init_surface_providers(
|
|
|
60
103
|
|
|
61
104
|
|
|
62
105
|
def init_well_pick_provider(
|
|
63
|
-
|
|
106
|
+
ensemble_paths: Dict[str, str],
|
|
107
|
+
well_pick_path: Optional[str],
|
|
64
108
|
map_surface_names_to_well_pick_names: Optional[Dict[str, str]],
|
|
65
|
-
) -> Dict[str,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
109
|
+
) -> Dict[str, EnsembleWellPicks]:
|
|
110
|
+
if well_pick_path is None:
|
|
111
|
+
return {}
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
ens: EnsembleWellPicks(
|
|
115
|
+
ens_p, well_pick_path, map_surface_names_to_well_pick_names
|
|
116
|
+
)
|
|
117
|
+
for ens, ens_p in ensemble_paths.items()
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def init_polygon_provider_handlers(
|
|
122
|
+
server: PolygonServer,
|
|
123
|
+
ensemble_paths: Dict[str, str],
|
|
124
|
+
options: Optional[BoundarySettings],
|
|
125
|
+
) -> Dict[str, PolygonHandler]:
|
|
126
|
+
filled_options: BoundarySettings = {
|
|
127
|
+
"polygon_file_pattern": "share/results/polygon/*.csv",
|
|
128
|
+
"attribute": "boundary",
|
|
129
|
+
"hazardous_name": "hazardous",
|
|
130
|
+
"containment_name": "containment",
|
|
131
|
+
}
|
|
132
|
+
if options is not None:
|
|
133
|
+
filled_options.update(options)
|
|
134
|
+
return {
|
|
135
|
+
ens: PolygonHandler(
|
|
136
|
+
server,
|
|
137
|
+
ens_path,
|
|
138
|
+
filled_options,
|
|
139
|
+
)
|
|
140
|
+
for ens, ens_path in ensemble_paths.items()
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def init_unsmry_data_providers(
|
|
145
|
+
ensemble_roots: Dict[str, str],
|
|
146
|
+
table_rel_path: Optional[str],
|
|
147
|
+
) -> Dict[str, UnsmryDataProvider]:
|
|
148
|
+
if table_rel_path is None:
|
|
149
|
+
return {}
|
|
150
|
+
factory = EnsembleTableProviderFactory.instance()
|
|
151
|
+
providers = {
|
|
152
|
+
ens: _init_ensemble_table_provider(factory, ens, ens_path, table_rel_path)
|
|
153
|
+
for ens, ens_path in ensemble_roots.items()
|
|
154
|
+
}
|
|
155
|
+
return {k: UnsmryDataProvider(v) for k, v in providers.items() if v is not None}
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def init_containment_data_providers(
|
|
83
159
|
ensemble_roots: Dict[str, str],
|
|
84
160
|
table_rel_path: str,
|
|
85
|
-
) -> Dict[str,
|
|
86
|
-
providers = {}
|
|
161
|
+
) -> Dict[str, ContainmentDataProvider]:
|
|
87
162
|
factory = EnsembleTableProviderFactory.instance()
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
text += f"\n CSV-files: {table_rel_path}"
|
|
96
|
-
text += f"\n Max size : {max_size_mb:.2f} MB"
|
|
97
|
-
LOGGER.warning(text)
|
|
163
|
+
providers = {
|
|
164
|
+
ens: _init_ensemble_table_provider(factory, ens, ens_path, table_rel_path)
|
|
165
|
+
for ens, ens_path in ensemble_roots.items()
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
k: ContainmentDataProvider(v) for k, v in providers.items() if v is not None
|
|
169
|
+
}
|
|
98
170
|
|
|
171
|
+
|
|
172
|
+
def _init_ensemble_table_provider(
|
|
173
|
+
factory: EnsembleTableProviderFactory,
|
|
174
|
+
ens: str,
|
|
175
|
+
ens_path: str,
|
|
176
|
+
table_rel_path: str,
|
|
177
|
+
) -> Optional[EnsembleTableProvider]:
|
|
178
|
+
try:
|
|
179
|
+
return factory.create_from_per_realization_arrow_file(ens_path, table_rel_path)
|
|
180
|
+
except (KeyError, ValueError) as exc:
|
|
99
181
|
try:
|
|
100
|
-
|
|
182
|
+
return factory.create_from_per_realization_csv_file(
|
|
101
183
|
ens_path, table_rel_path
|
|
102
184
|
)
|
|
103
|
-
except (KeyError, ValueError) as
|
|
185
|
+
except (KeyError, ValueError) as exc2:
|
|
104
186
|
LOGGER.warning(
|
|
105
|
-
f'
|
|
187
|
+
f'\nTried reading "{table_rel_path}" for ensemble "{ens}" as csv with'
|
|
188
|
+
f" error \n- {exc2}, \nand as arrow with error \n- {exc}"
|
|
106
189
|
)
|
|
107
|
-
return
|
|
190
|
+
return None
|
|
108
191
|
|
|
109
192
|
|
|
110
|
-
def
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
max_size = max(max_size, size_in_mb)
|
|
119
|
-
return max_size
|
|
193
|
+
def init_realizations(ensemble_paths: Dict[str, str]) -> Dict[str, List[int]]:
|
|
194
|
+
realization_per_ens = {}
|
|
195
|
+
for ens, ens_path in ensemble_paths.items():
|
|
196
|
+
scratch_ensemble = ScratchEnsemble("dummyEnsembleName", paths=ens_path).filter(
|
|
197
|
+
"OK"
|
|
198
|
+
)
|
|
199
|
+
realization_per_ens[ens] = sorted(list(scratch_ensemble.realizations.keys()))
|
|
200
|
+
return realization_per_ens
|
|
120
201
|
|
|
121
202
|
|
|
122
203
|
def init_menu_options(
|
|
123
204
|
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]]]] = {}
|
|
205
|
+
mass_table: Dict[str, ContainmentDataProvider],
|
|
206
|
+
actual_volume_table: Dict[str, ContainmentDataProvider],
|
|
207
|
+
unsmry_providers: Dict[str, UnsmryDataProvider],
|
|
208
|
+
) -> Dict[str, Dict[GraphSource, MenuOptions]]:
|
|
209
|
+
options: Dict[str, Dict[GraphSource, MenuOptions]] = {}
|
|
130
210
|
for ens in ensemble_roots.keys():
|
|
131
211
|
options[ens] = {}
|
|
132
|
-
|
|
133
|
-
[GraphSource.CONTAINMENT_MASS
|
|
134
|
-
|
|
135
|
-
[
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
"zones": [],
|
|
141
|
-
"regions": [],
|
|
142
|
-
"phases": ["total", "gas", "aqueous"],
|
|
143
|
-
}
|
|
212
|
+
if ens in mass_table:
|
|
213
|
+
options[ens][GraphSource.CONTAINMENT_MASS] = mass_table[ens].menu_options
|
|
214
|
+
if ens in actual_volume_table:
|
|
215
|
+
options[ens][GraphSource.CONTAINMENT_ACTUAL_VOLUME] = actual_volume_table[
|
|
216
|
+
ens
|
|
217
|
+
].menu_options
|
|
218
|
+
if ens in unsmry_providers:
|
|
219
|
+
options[ens][GraphSource.UNSMRY] = unsmry_providers[ens].menu_options
|
|
144
220
|
return options
|
|
145
221
|
|
|
146
222
|
|
|
147
|
-
def
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
]
|
|
223
|
+
def init_dictionary_of_content(
|
|
224
|
+
menu_options: Dict[str, Dict[GraphSource, MenuOptions]],
|
|
225
|
+
has_maps: bool,
|
|
226
|
+
) -> Dict[str, bool]:
|
|
227
|
+
options = next(iter(menu_options.values()))
|
|
228
|
+
content = {
|
|
229
|
+
"mass": GraphSource.CONTAINMENT_MASS in options,
|
|
230
|
+
"volume": GraphSource.CONTAINMENT_ACTUAL_VOLUME in options,
|
|
231
|
+
"unsmry": GraphSource.UNSMRY in options,
|
|
232
|
+
}
|
|
233
|
+
content["any_table"] = max(content.values())
|
|
234
|
+
content["maps"] = has_maps
|
|
235
|
+
content["zones"] = False
|
|
236
|
+
content["regions"] = False
|
|
237
|
+
content["plume_groups"] = False
|
|
238
|
+
if content["mass"] or content["volume"]:
|
|
239
|
+
content["zones"] = max(
|
|
240
|
+
len(inner_dict["zones"]) > 0
|
|
241
|
+
for outer_dict in menu_options.values()
|
|
242
|
+
for inner_dict in outer_dict.values()
|
|
243
|
+
)
|
|
244
|
+
content["regions"] = max(
|
|
245
|
+
len(inner_dict["regions"]) > 0
|
|
246
|
+
for outer_dict in menu_options.values()
|
|
247
|
+
for inner_dict in outer_dict.values()
|
|
248
|
+
)
|
|
249
|
+
content["plume_groups"] = max(
|
|
250
|
+
len(inner_dict["plume_groups"]) > 0
|
|
251
|
+
for outer_dict in menu_options.values()
|
|
252
|
+
for inner_dict in outer_dict.values()
|
|
253
|
+
)
|
|
254
|
+
return content
|
|
162
255
|
|
|
163
256
|
|
|
164
257
|
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
|
+
)
|