webviz-subsurface 0.2.35__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/_providers/ensemble_summary_provider/_provider_impl_arrow_lazy.py +1 -1
- 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/plugins/_relative_permeability.py +1 -1
- {webviz_subsurface-0.2.35.dist-info → webviz_subsurface-0.2.37.dist-info}/METADATA +2 -2
- {webviz_subsurface-0.2.35.dist-info → webviz_subsurface-0.2.37.dist-info}/RECORD +32 -21
- {webviz_subsurface-0.2.35.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.35.dist-info → webviz_subsurface-0.2.37.dist-info}/LICENSE +0 -0
- {webviz_subsurface-0.2.35.dist-info → webviz_subsurface-0.2.37.dist-info}/LICENSE.chromedriver +0 -0
- {webviz_subsurface-0.2.35.dist-info → webviz_subsurface-0.2.37.dist-info}/entry_points.txt +0 -0
- {webviz_subsurface-0.2.35.dist-info → webviz_subsurface-0.2.37.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Dict
|
|
3
|
+
|
|
4
|
+
from fmu.ensemble import ScratchEnsemble
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def realization_paths(ens_path: str) -> Dict[int, Path]:
|
|
8
|
+
scratch_ensemble = ScratchEnsemble("_", paths=ens_path).filter("OK")
|
|
9
|
+
return {i: Path(r.runpath()) for i, r in scratch_ensemble.realizations.items()}
|
|
@@ -11,7 +11,6 @@ from flask_caching import Cache
|
|
|
11
11
|
|
|
12
12
|
from webviz_subsurface._providers import (
|
|
13
13
|
EnsembleSurfaceProvider,
|
|
14
|
-
EnsembleTableProvider,
|
|
15
14
|
SimulatedSurfaceAddress,
|
|
16
15
|
StatisticalSurfaceAddress,
|
|
17
16
|
SurfaceAddress,
|
|
@@ -21,19 +20,29 @@ from webviz_subsurface._providers import (
|
|
|
21
20
|
from webviz_subsurface._providers.ensemble_surface_provider.ensemble_surface_provider import (
|
|
22
21
|
SurfaceStatistic,
|
|
23
22
|
)
|
|
24
|
-
from webviz_subsurface._utils.webvizstore_functions import read_csv
|
|
25
23
|
from webviz_subsurface.plugins._co2_leakage._utilities import plume_extent
|
|
26
24
|
from webviz_subsurface.plugins._co2_leakage._utilities.co2volume import (
|
|
25
|
+
generate_co2_statistics_figure,
|
|
27
26
|
generate_co2_time_containment_figure,
|
|
28
27
|
generate_co2_time_containment_one_realization_figure,
|
|
29
28
|
generate_co2_volume_figure,
|
|
30
29
|
)
|
|
30
|
+
from webviz_subsurface.plugins._co2_leakage._utilities.containment_data_provider import (
|
|
31
|
+
ContainmentDataProvider,
|
|
32
|
+
)
|
|
33
|
+
from webviz_subsurface.plugins._co2_leakage._utilities.ensemble_well_picks import (
|
|
34
|
+
EnsembleWellPicks,
|
|
35
|
+
)
|
|
31
36
|
from webviz_subsurface.plugins._co2_leakage._utilities.generic import (
|
|
32
37
|
Co2MassScale,
|
|
33
38
|
Co2VolumeScale,
|
|
39
|
+
FilteredMapAttribute,
|
|
34
40
|
GraphSource,
|
|
35
41
|
LayoutLabels,
|
|
36
42
|
MapAttribute,
|
|
43
|
+
MapGroup,
|
|
44
|
+
MapType,
|
|
45
|
+
MenuOptions,
|
|
37
46
|
)
|
|
38
47
|
from webviz_subsurface.plugins._co2_leakage._utilities.summary_graphs import (
|
|
39
48
|
generate_summary_figure,
|
|
@@ -42,21 +51,22 @@ from webviz_subsurface.plugins._co2_leakage._utilities.surface_publishing import
|
|
|
42
51
|
TruncatedSurfaceAddress,
|
|
43
52
|
publish_and_get_surface_metadata,
|
|
44
53
|
)
|
|
45
|
-
from webviz_subsurface.plugins.
|
|
46
|
-
|
|
54
|
+
from webviz_subsurface.plugins._co2_leakage._utilities.unsmry_data_provider import (
|
|
55
|
+
UnsmryDataProvider,
|
|
47
56
|
)
|
|
48
57
|
|
|
49
58
|
|
|
50
59
|
def property_origin(
|
|
51
|
-
attribute: MapAttribute, map_attribute_names:
|
|
60
|
+
attribute: MapAttribute, map_attribute_names: FilteredMapAttribute
|
|
52
61
|
) -> str:
|
|
53
|
-
if attribute
|
|
54
|
-
return
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
if MapType[MapAttribute(attribute).name].value == "PLUME":
|
|
63
|
+
return [
|
|
64
|
+
map_attribute_names[attr]
|
|
65
|
+
for attr in MapAttribute
|
|
66
|
+
if MapGroup[attr.name].value == MapGroup[MapAttribute(attribute).name].value
|
|
67
|
+
and MapType[attr.name] == "MAX"
|
|
68
|
+
][0]
|
|
69
|
+
return map_attribute_names[attribute]
|
|
60
70
|
|
|
61
71
|
|
|
62
72
|
@dataclass
|
|
@@ -77,7 +87,7 @@ class SurfaceData:
|
|
|
77
87
|
color_map_name: str,
|
|
78
88
|
readable_name_: str,
|
|
79
89
|
visualization_info: Dict[str, Any],
|
|
80
|
-
map_attribute_names:
|
|
90
|
+
map_attribute_names: FilteredMapAttribute,
|
|
81
91
|
) -> Tuple[Any, Optional[Any]]:
|
|
82
92
|
surf_meta, img_url, summed_mass = publish_and_get_surface_metadata(
|
|
83
93
|
server,
|
|
@@ -115,17 +125,14 @@ def derive_surface_address(
|
|
|
115
125
|
attribute: MapAttribute,
|
|
116
126
|
date: Optional[str],
|
|
117
127
|
realization: List[int],
|
|
118
|
-
map_attribute_names:
|
|
128
|
+
map_attribute_names: FilteredMapAttribute,
|
|
119
129
|
statistic: str,
|
|
120
130
|
contour_data: Optional[Dict[str, Any]],
|
|
121
131
|
) -> Union[SurfaceAddress, TruncatedSurfaceAddress]:
|
|
122
|
-
if attribute
|
|
132
|
+
if MapType[MapAttribute(attribute).name].value == "PLUME":
|
|
133
|
+
max_attr_name = f"MAX_{MapGroup[MapAttribute(attribute).name]}"
|
|
123
134
|
assert date is not None
|
|
124
|
-
basis = (
|
|
125
|
-
MapAttribute.MAX_SGAS
|
|
126
|
-
if attribute == MapAttribute.SGAS_PLUME
|
|
127
|
-
else MapAttribute.MAX_AMFG
|
|
128
|
-
)
|
|
135
|
+
basis = getattr(MapAttribute, max_attr_name)
|
|
129
136
|
return TruncatedSurfaceAddress(
|
|
130
137
|
name=surface_name,
|
|
131
138
|
datestr=date,
|
|
@@ -136,11 +143,7 @@ def derive_surface_address(
|
|
|
136
143
|
)
|
|
137
144
|
date = (
|
|
138
145
|
None
|
|
139
|
-
if attribute
|
|
140
|
-
in [
|
|
141
|
-
MapAttribute.MIGRATION_TIME_SGAS,
|
|
142
|
-
MapAttribute.MIGRATION_TIME_AMFG,
|
|
143
|
-
]
|
|
146
|
+
if MapType[MapAttribute(attribute).name].value == "MIGRATION_TIME"
|
|
144
147
|
else date
|
|
145
148
|
)
|
|
146
149
|
if len(realization) == 1:
|
|
@@ -161,12 +164,9 @@ def derive_surface_address(
|
|
|
161
164
|
|
|
162
165
|
def readable_name(attribute: MapAttribute) -> str:
|
|
163
166
|
unit = ""
|
|
164
|
-
if attribute
|
|
165
|
-
MapAttribute.MIGRATION_TIME_SGAS,
|
|
166
|
-
MapAttribute.MIGRATION_TIME_AMFG,
|
|
167
|
-
]:
|
|
167
|
+
if MapType[MapAttribute(attribute).name].value == "MIGRATION_TIME":
|
|
168
168
|
unit = " [year]"
|
|
169
|
-
elif attribute
|
|
169
|
+
elif MapType[MapAttribute(attribute).name].value == "PLUME":
|
|
170
170
|
unit = " [# real.]"
|
|
171
171
|
return f"{attribute.value}{unit}"
|
|
172
172
|
|
|
@@ -211,12 +211,9 @@ def get_plume_polygon(
|
|
|
211
211
|
|
|
212
212
|
|
|
213
213
|
def _find_legend_title(attribute: MapAttribute, unit: str) -> str:
|
|
214
|
-
if attribute
|
|
215
|
-
MapAttribute.MIGRATION_TIME_SGAS,
|
|
216
|
-
MapAttribute.MIGRATION_TIME_AMFG,
|
|
217
|
-
]:
|
|
214
|
+
if MapType[MapAttribute(attribute).name].value == "MIGRATION_TIME":
|
|
218
215
|
return "years"
|
|
219
|
-
if
|
|
216
|
+
if MapType[MapAttribute(attribute).name].value == "MASS":
|
|
220
217
|
return unit
|
|
221
218
|
return ""
|
|
222
219
|
|
|
@@ -234,7 +231,8 @@ def create_map_annotations(
|
|
|
234
231
|
and surface_data.color_map_range[0] is not None
|
|
235
232
|
and surface_data.color_map_range[1] is not None
|
|
236
233
|
):
|
|
237
|
-
|
|
234
|
+
max_value = surface_data.color_map_range[1]
|
|
235
|
+
num_digits = 4 if max_value < 1 else np.ceil(np.log(max_value) / np.log(10))
|
|
238
236
|
numbersize = max((6, min((17 - num_digits, 11))))
|
|
239
237
|
annotations.append(
|
|
240
238
|
wsc.ViewAnnotation(
|
|
@@ -283,12 +281,13 @@ def create_map_viewports() -> Dict:
|
|
|
283
281
|
|
|
284
282
|
# pylint: disable=too-many-arguments
|
|
285
283
|
def create_map_layers(
|
|
284
|
+
realizations: List[int],
|
|
286
285
|
formation: str,
|
|
287
286
|
surface_data: Optional[SurfaceData],
|
|
288
287
|
fault_polygon_url: Optional[str],
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
well_pick_provider: Optional[
|
|
288
|
+
containment_bounds_url: Optional[str],
|
|
289
|
+
haz_bounds_url: Optional[str],
|
|
290
|
+
well_pick_provider: Optional[EnsembleWellPicks],
|
|
292
291
|
plume_extent_data: Optional[geojson.FeatureCollection],
|
|
293
292
|
options_dialog_options: List[int],
|
|
294
293
|
selected_wells: List[str],
|
|
@@ -322,8 +321,9 @@ def create_map_layers(
|
|
|
322
321
|
"data": fault_polygon_url,
|
|
323
322
|
}
|
|
324
323
|
)
|
|
324
|
+
|
|
325
325
|
if (
|
|
326
|
-
|
|
326
|
+
containment_bounds_url is not None
|
|
327
327
|
and LayoutLabels.SHOW_CONTAINMENT_POLYGON in options_dialog_options
|
|
328
328
|
):
|
|
329
329
|
layers.append(
|
|
@@ -331,14 +331,15 @@ def create_map_layers(
|
|
|
331
331
|
"@@type": "GeoJsonLayer",
|
|
332
332
|
"name": "Containment Polygon",
|
|
333
333
|
"id": "license-boundary-layer",
|
|
334
|
-
"data":
|
|
334
|
+
"data": containment_bounds_url,
|
|
335
335
|
"stroked": False,
|
|
336
336
|
"getFillColor": [0, 172, 0, 120],
|
|
337
337
|
"visible": True,
|
|
338
338
|
}
|
|
339
339
|
)
|
|
340
|
+
|
|
340
341
|
if (
|
|
341
|
-
|
|
342
|
+
haz_bounds_url is not None
|
|
342
343
|
and LayoutLabels.SHOW_HAZARDOUS_POLYGON in options_dialog_options
|
|
343
344
|
):
|
|
344
345
|
layers.append(
|
|
@@ -346,48 +347,25 @@ def create_map_layers(
|
|
|
346
347
|
"@@type": "GeoJsonLayer",
|
|
347
348
|
"name": "Hazardous Polygon",
|
|
348
349
|
"id": "hazardous-boundary-layer",
|
|
349
|
-
"data":
|
|
350
|
+
"data": haz_bounds_url,
|
|
350
351
|
"stroked": False,
|
|
351
352
|
"getFillColor": [200, 0, 0, 120],
|
|
352
353
|
"visible": True,
|
|
353
354
|
}
|
|
354
355
|
)
|
|
356
|
+
|
|
355
357
|
if (
|
|
356
358
|
well_pick_provider is not None
|
|
357
359
|
and formation is not None
|
|
360
|
+
and len(realizations) > 0
|
|
358
361
|
and LayoutLabels.SHOW_WELLS in options_dialog_options
|
|
359
362
|
):
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
if len(well_data["features"]) == 0:
|
|
363
|
-
wellstring = "well: " if len(selected_wells) == 1 else "wells: "
|
|
364
|
-
wellstring += ", ".join(selected_wells)
|
|
365
|
-
warnings.warn(
|
|
366
|
-
f"Combination of formation: {formation} and "
|
|
367
|
-
f"{wellstring} not found in well picks file."
|
|
368
|
-
)
|
|
369
|
-
for i in range(len(well_data["features"])):
|
|
370
|
-
current_attribute = well_data["features"][i]["properties"]["attribute"]
|
|
371
|
-
well_data["features"][i]["properties"]["attribute"] = (
|
|
372
|
-
" " + current_attribute
|
|
373
|
-
)
|
|
374
|
-
layers.append(
|
|
375
|
-
{
|
|
376
|
-
"@@type": "GeoJsonLayer",
|
|
377
|
-
"name": "Well Picks",
|
|
378
|
-
"id": "well-picks-layer",
|
|
379
|
-
"data": well_data,
|
|
380
|
-
"visible": True,
|
|
381
|
-
"getText": "@@=properties.attribute",
|
|
382
|
-
"getTextSize": 12,
|
|
383
|
-
"getTextAnchor": "start",
|
|
384
|
-
"pointType": "circle+text",
|
|
385
|
-
"lineWidthMinPixels": 2,
|
|
386
|
-
"pointRadiusMinPixels": 2,
|
|
387
|
-
"pickable": True,
|
|
388
|
-
"parameters": {"depthTest": False},
|
|
389
|
-
}
|
|
363
|
+
layer = well_pick_provider.geojson_layer(
|
|
364
|
+
realizations[0], selected_wells, formation
|
|
390
365
|
)
|
|
366
|
+
if layer is not None:
|
|
367
|
+
layers.append(layer)
|
|
368
|
+
|
|
391
369
|
if plume_extent_data is not None:
|
|
392
370
|
layers.append(
|
|
393
371
|
{
|
|
@@ -403,28 +381,45 @@ def create_map_layers(
|
|
|
403
381
|
|
|
404
382
|
|
|
405
383
|
def generate_containment_figures(
|
|
406
|
-
table_provider:
|
|
384
|
+
table_provider: ContainmentDataProvider,
|
|
407
385
|
co2_scale: Union[Co2MassScale, Co2VolumeScale],
|
|
408
|
-
|
|
386
|
+
realizations: List[int],
|
|
409
387
|
y_limits: List[Optional[float]],
|
|
410
388
|
containment_info: Dict[str, Union[str, None, List[str], int]],
|
|
411
389
|
) -> Tuple[go.Figure, go.Figure, go.Figure]:
|
|
412
390
|
try:
|
|
413
|
-
fig0 =
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
391
|
+
fig0 = (
|
|
392
|
+
no_update
|
|
393
|
+
if not containment_info["update_first_figure"]
|
|
394
|
+
else generate_co2_volume_figure(
|
|
395
|
+
table_provider,
|
|
396
|
+
table_provider.realizations,
|
|
397
|
+
co2_scale,
|
|
398
|
+
containment_info,
|
|
399
|
+
)
|
|
418
400
|
)
|
|
419
|
-
fig1 =
|
|
401
|
+
fig1 = (
|
|
402
|
+
generate_co2_time_containment_figure(
|
|
403
|
+
table_provider,
|
|
404
|
+
realizations,
|
|
405
|
+
co2_scale,
|
|
406
|
+
containment_info,
|
|
407
|
+
)
|
|
408
|
+
if len(realizations) > 1
|
|
409
|
+
else generate_co2_time_containment_one_realization_figure(
|
|
410
|
+
table_provider,
|
|
411
|
+
co2_scale,
|
|
412
|
+
realizations[0],
|
|
413
|
+
y_limits,
|
|
414
|
+
containment_info,
|
|
415
|
+
)
|
|
416
|
+
)
|
|
417
|
+
fig2 = generate_co2_statistics_figure(
|
|
420
418
|
table_provider,
|
|
421
|
-
|
|
419
|
+
realizations,
|
|
422
420
|
co2_scale,
|
|
423
421
|
containment_info,
|
|
424
422
|
)
|
|
425
|
-
fig2 = generate_co2_time_containment_one_realization_figure(
|
|
426
|
-
table_provider, co2_scale, realization, y_limits, containment_info
|
|
427
|
-
)
|
|
428
423
|
except KeyError as exc:
|
|
429
424
|
warnings.warn(f"Could not generate CO2 figures: {exc}")
|
|
430
425
|
raise exc
|
|
@@ -432,58 +427,20 @@ def generate_containment_figures(
|
|
|
432
427
|
|
|
433
428
|
|
|
434
429
|
def generate_unsmry_figures(
|
|
435
|
-
table_provider_unsmry:
|
|
430
|
+
table_provider_unsmry: UnsmryDataProvider,
|
|
436
431
|
co2_mass_scale: Union[Co2MassScale, Co2VolumeScale],
|
|
437
|
-
table_provider_containment:
|
|
438
|
-
) ->
|
|
439
|
-
return (
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
co2_mass_scale,
|
|
444
|
-
table_provider_containment,
|
|
445
|
-
table_provider_containment.realizations(),
|
|
446
|
-
),
|
|
432
|
+
table_provider_containment: ContainmentDataProvider,
|
|
433
|
+
) -> go.Figure:
|
|
434
|
+
return generate_summary_figure(
|
|
435
|
+
table_provider_unsmry,
|
|
436
|
+
co2_mass_scale,
|
|
437
|
+
table_provider_containment,
|
|
447
438
|
)
|
|
448
439
|
|
|
449
440
|
|
|
450
|
-
def _parse_polygon_file(filename: str) -> Dict[str, Any]:
|
|
451
|
-
df = read_csv(filename)
|
|
452
|
-
if "x" in df.columns:
|
|
453
|
-
xyz = df[["x", "y"]].values
|
|
454
|
-
elif "X_UTME" in df.columns:
|
|
455
|
-
if "POLY_ID" in df.columns:
|
|
456
|
-
xyz = [gf[["X_UTME", "Y_UTMN"]].values for _, gf in df.groupby("POLY_ID")]
|
|
457
|
-
else:
|
|
458
|
-
xyz = df[["X_UTME", "Y_UTMN"]].values
|
|
459
|
-
else:
|
|
460
|
-
# Attempt to use the first two columns as the x and y coordinates
|
|
461
|
-
xyz = df.values[:, :2]
|
|
462
|
-
if isinstance(xyz, list):
|
|
463
|
-
poly_type = "MultiPolygon"
|
|
464
|
-
coords = [[arr.tolist()] for arr in xyz]
|
|
465
|
-
else:
|
|
466
|
-
poly_type = "Polygon"
|
|
467
|
-
coords = [xyz.tolist()]
|
|
468
|
-
as_geojson = {
|
|
469
|
-
"type": "FeatureCollection",
|
|
470
|
-
"features": [
|
|
471
|
-
{
|
|
472
|
-
"type": "Feature",
|
|
473
|
-
"properties": {},
|
|
474
|
-
"geometry": {
|
|
475
|
-
"type": poly_type,
|
|
476
|
-
"coordinates": coords,
|
|
477
|
-
},
|
|
478
|
-
}
|
|
479
|
-
],
|
|
480
|
-
}
|
|
481
|
-
return as_geojson
|
|
482
|
-
|
|
483
|
-
|
|
484
441
|
def process_visualization_info(
|
|
485
|
-
|
|
486
|
-
|
|
442
|
+
attribute: str,
|
|
443
|
+
thresholds: dict,
|
|
487
444
|
unit: str,
|
|
488
445
|
stored_info: Dict[str, Any],
|
|
489
446
|
cache: Cache,
|
|
@@ -491,17 +448,21 @@ def process_visualization_info(
|
|
|
491
448
|
"""
|
|
492
449
|
Clear surface cache if the threshold for visualization or mass unit is changed
|
|
493
450
|
"""
|
|
451
|
+
stored_info["attribute"] = attribute
|
|
494
452
|
stored_info["change"] = False
|
|
495
|
-
|
|
496
|
-
|
|
453
|
+
if (
|
|
454
|
+
MapType[MapAttribute(attribute).name].value not in ["PLUME", "MIGRATION_TIME"]
|
|
455
|
+
and unit != stored_info["unit"]
|
|
456
|
+
):
|
|
497
457
|
stored_info["unit"] = unit
|
|
498
458
|
stored_info["change"] = True
|
|
499
|
-
if
|
|
500
|
-
stored_info["
|
|
501
|
-
|
|
459
|
+
if thresholds is not None:
|
|
460
|
+
for att in stored_info["thresholds"].keys():
|
|
461
|
+
if stored_info["thresholds"][att] != thresholds[att]:
|
|
462
|
+
stored_info["change"] = True
|
|
463
|
+
stored_info["thresholds"][att] = thresholds[att]
|
|
502
464
|
if stored_info["change"]:
|
|
503
465
|
cache.clear()
|
|
504
|
-
# stored_info["n_clicks"] = n_clicks
|
|
505
466
|
return stored_info
|
|
506
467
|
|
|
507
468
|
|
|
@@ -510,21 +471,33 @@ def process_containment_info(
|
|
|
510
471
|
region: Optional[str],
|
|
511
472
|
phase: str,
|
|
512
473
|
containment: str,
|
|
474
|
+
plume_group: str,
|
|
513
475
|
color_choice: str,
|
|
514
476
|
mark_choice: Optional[str],
|
|
515
477
|
sorting: str,
|
|
516
|
-
|
|
478
|
+
lines_to_show: str,
|
|
479
|
+
date_option: str,
|
|
480
|
+
menu_options: MenuOptions,
|
|
517
481
|
) -> Dict[str, Union[str, None, List[str], int]]:
|
|
518
482
|
if mark_choice is None:
|
|
519
483
|
mark_choice = "phase"
|
|
520
484
|
zones = menu_options["zones"]
|
|
521
485
|
regions = menu_options["regions"]
|
|
486
|
+
plume_groups = menu_options["plume_groups"]
|
|
522
487
|
if len(zones) > 0:
|
|
523
488
|
zones = [zone_name for zone_name in zones if zone_name != "all"]
|
|
524
489
|
if len(regions) > 0:
|
|
525
490
|
regions = [reg_name for reg_name in regions if reg_name != "all"]
|
|
526
|
-
|
|
527
|
-
|
|
491
|
+
if len(plume_groups) > 0:
|
|
492
|
+
plume_groups = [pg_name for pg_name in plume_groups if pg_name != "all"]
|
|
493
|
+
|
|
494
|
+
def plume_sort_key(name: str) -> int:
|
|
495
|
+
if name == "undetermined":
|
|
496
|
+
return 999
|
|
497
|
+
return name.count("+")
|
|
498
|
+
|
|
499
|
+
plume_groups = sorted(plume_groups, key=plume_sort_key)
|
|
500
|
+
|
|
528
501
|
if "zone" in [mark_choice, color_choice]:
|
|
529
502
|
region = "all"
|
|
530
503
|
if "region" in [mark_choice, color_choice]:
|
|
@@ -536,45 +509,72 @@ def process_containment_info(
|
|
|
536
509
|
"regions": regions,
|
|
537
510
|
"phase": phase,
|
|
538
511
|
"containment": containment,
|
|
512
|
+
"plume_group": plume_group,
|
|
539
513
|
"color_choice": color_choice,
|
|
540
514
|
"mark_choice": mark_choice,
|
|
541
515
|
"sorting": sorting,
|
|
542
|
-
"phases": phases,
|
|
543
|
-
"containments":
|
|
516
|
+
"phases": [phase for phase in menu_options["phases"] if phase != "total"],
|
|
517
|
+
"containments": ["hazardous", "outside", "contained"],
|
|
518
|
+
"plume_groups": plume_groups,
|
|
519
|
+
"use_stats": lines_to_show == "stat",
|
|
520
|
+
"date_option": date_option,
|
|
544
521
|
}
|
|
545
522
|
|
|
546
523
|
|
|
547
|
-
def
|
|
548
|
-
|
|
524
|
+
def make_plot_ids(
|
|
525
|
+
ensemble: str,
|
|
549
526
|
source: GraphSource,
|
|
550
527
|
scale: Union[Co2MassScale, Co2VolumeScale],
|
|
551
528
|
containment_info: Dict,
|
|
552
529
|
realizations: List[int],
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
530
|
+
lines_to_show: str,
|
|
531
|
+
num_figs: int,
|
|
532
|
+
) -> List[str]:
|
|
533
|
+
zone_str = (
|
|
534
|
+
containment_info["zone"] if containment_info["zone"] is not None else "None"
|
|
535
|
+
)
|
|
536
|
+
region_str = (
|
|
537
|
+
containment_info["region"] if containment_info["region"] is not None else "None"
|
|
538
|
+
)
|
|
539
|
+
plume_group_str = (
|
|
540
|
+
containment_info["plume_group"]
|
|
541
|
+
if containment_info["plume_group"] is not None
|
|
542
|
+
else "None"
|
|
543
|
+
)
|
|
544
|
+
mark_choice_str = (
|
|
545
|
+
containment_info["mark_choice"]
|
|
546
|
+
if containment_info["mark_choice"] is not None
|
|
547
|
+
else "None"
|
|
548
|
+
)
|
|
549
|
+
plot_id = "-".join(
|
|
550
|
+
(
|
|
551
|
+
ensemble,
|
|
552
|
+
source,
|
|
553
|
+
scale,
|
|
554
|
+
zone_str,
|
|
555
|
+
region_str,
|
|
556
|
+
plume_group_str,
|
|
557
|
+
str(containment_info["phase"]),
|
|
558
|
+
str(containment_info["containment"]),
|
|
559
|
+
containment_info["color_choice"],
|
|
560
|
+
mark_choice_str,
|
|
561
|
+
containment_info["sorting"],
|
|
562
|
+
containment_info["date_option"],
|
|
574
563
|
)
|
|
575
|
-
|
|
564
|
+
)
|
|
565
|
+
ids = [plot_id]
|
|
566
|
+
ids += [plot_id + f"-{realizations}"] * (num_figs - 1)
|
|
567
|
+
ids[1] += f"-{lines_to_show}"
|
|
568
|
+
return ids
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
def set_plot_ids(
|
|
572
|
+
figs: List[go.Figure],
|
|
573
|
+
plot_ids: List[str],
|
|
574
|
+
) -> None:
|
|
575
|
+
for fig, plot_id in zip(figs, plot_ids):
|
|
576
|
+
if fig != no_update:
|
|
576
577
|
fig["layout"]["uirevision"] = plot_id
|
|
577
|
-
figs[-1]["layout"]["uirevision"] += f"-{realizations}"
|
|
578
578
|
|
|
579
579
|
|
|
580
580
|
def process_summed_mass(
|
|
@@ -589,11 +589,7 @@ def process_summed_mass(
|
|
|
589
589
|
) -> Tuple[Optional[SurfaceData], Dict[str, float]]:
|
|
590
590
|
summed_co2_key = f"{formation}-{realization[0]}-{datestr}-{attribute}-{unit}"
|
|
591
591
|
if len(realization) == 1:
|
|
592
|
-
if attribute
|
|
593
|
-
MapAttribute.MASS,
|
|
594
|
-
MapAttribute.DISSOLVED,
|
|
595
|
-
MapAttribute.FREE,
|
|
596
|
-
]:
|
|
592
|
+
if MapType[MapAttribute(attribute).name].value == "MASS":
|
|
597
593
|
if summed_mass is not None and summed_co2_key not in summed_co2:
|
|
598
594
|
summed_co2[summed_co2_key] = summed_mass
|
|
599
595
|
if summed_co2_key in summed_co2 and surf_data is not None:
|