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
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
from typing import List, Optional, Union
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
from webviz_subsurface._providers import EnsembleTableProvider
|
|
6
|
+
from webviz_subsurface.plugins._co2_leakage._utilities.generic import (
|
|
7
|
+
Co2MassScale,
|
|
8
|
+
Co2VolumeScale,
|
|
9
|
+
MenuOptions,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ContainmentDataValidationError(Exception):
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ContainmentDataProvider:
|
|
18
|
+
def __init__(self, table_provider: EnsembleTableProvider):
|
|
19
|
+
ContainmentDataProvider._validate(table_provider)
|
|
20
|
+
self._provider = table_provider
|
|
21
|
+
self._menu_options = ContainmentDataProvider._get_menu_options(self._provider)
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def menu_options(self) -> MenuOptions:
|
|
25
|
+
return self._menu_options
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def realizations(self) -> List[int]:
|
|
29
|
+
return self._provider.realizations()
|
|
30
|
+
|
|
31
|
+
def extract_dataframe(
|
|
32
|
+
self, realization: int, scale: Union[Co2MassScale, Co2VolumeScale]
|
|
33
|
+
) -> pd.DataFrame:
|
|
34
|
+
df = self._provider.get_column_data(
|
|
35
|
+
self._provider.column_names(), [realization]
|
|
36
|
+
)
|
|
37
|
+
scale_factor = self._find_scale_factor(scale)
|
|
38
|
+
if scale_factor == 1.0:
|
|
39
|
+
return df
|
|
40
|
+
df["amount"] /= scale_factor
|
|
41
|
+
return df
|
|
42
|
+
|
|
43
|
+
def extract_condensed_dataframe(
|
|
44
|
+
self,
|
|
45
|
+
co2_scale: Union[Co2MassScale, Co2VolumeScale],
|
|
46
|
+
) -> pd.DataFrame:
|
|
47
|
+
df = self._provider.get_column_data(self._provider.column_names())
|
|
48
|
+
df = df.loc[
|
|
49
|
+
(df["zone"] == "all")
|
|
50
|
+
& (df["region"] == "all")
|
|
51
|
+
& (df["plume_group"] == "all")
|
|
52
|
+
]
|
|
53
|
+
if co2_scale == Co2MassScale.MTONS:
|
|
54
|
+
df.loc[:, "amount"] /= 1e9
|
|
55
|
+
elif co2_scale == Co2MassScale.NORMALIZE:
|
|
56
|
+
df.loc[:, "amount"] /= df["amount"].max()
|
|
57
|
+
return df
|
|
58
|
+
|
|
59
|
+
def _find_scale_factor(
|
|
60
|
+
self,
|
|
61
|
+
scale: Union[Co2MassScale, Co2VolumeScale],
|
|
62
|
+
) -> float:
|
|
63
|
+
if scale == Co2MassScale.KG:
|
|
64
|
+
return 0.001
|
|
65
|
+
if scale in (Co2MassScale.TONS, Co2VolumeScale.CUBIC_METERS):
|
|
66
|
+
return 1.0
|
|
67
|
+
if scale == Co2MassScale.MTONS:
|
|
68
|
+
return 1e6
|
|
69
|
+
if scale == Co2VolumeScale.BILLION_CUBIC_METERS:
|
|
70
|
+
return 1e9
|
|
71
|
+
if scale in (Co2MassScale.NORMALIZE, Co2VolumeScale.NORMALIZE):
|
|
72
|
+
df = self._provider.get_column_data(["amount"])
|
|
73
|
+
return df["amount"].max()
|
|
74
|
+
return 1.0
|
|
75
|
+
|
|
76
|
+
@staticmethod
|
|
77
|
+
def _get_menu_options(provider: EnsembleTableProvider) -> MenuOptions:
|
|
78
|
+
col_names = provider.column_names()
|
|
79
|
+
realization = provider.realizations()[0]
|
|
80
|
+
# NBNB: Check that these are the same for all realizations????
|
|
81
|
+
# NBNB: WARNING and empty for zones / regions, and Error if phases are different?
|
|
82
|
+
df = provider.get_column_data(col_names, [realization])
|
|
83
|
+
zones = ["all"]
|
|
84
|
+
if "zone" in df:
|
|
85
|
+
for zone in list(df["zone"]):
|
|
86
|
+
if zone not in zones:
|
|
87
|
+
zones.append(zone)
|
|
88
|
+
regions = ["all"]
|
|
89
|
+
if "region" in df:
|
|
90
|
+
for region in list(df["region"]):
|
|
91
|
+
if region not in regions:
|
|
92
|
+
regions.append(region)
|
|
93
|
+
plume_groups = ["all"]
|
|
94
|
+
if "plume_group" in df:
|
|
95
|
+
for plume_group in list(df["plume_group"]):
|
|
96
|
+
if plume_group not in plume_groups and plume_group is not None:
|
|
97
|
+
plume_groups.append(plume_group)
|
|
98
|
+
|
|
99
|
+
def plume_sort_key(name: Optional[str]) -> int:
|
|
100
|
+
if name is None:
|
|
101
|
+
return 999 # Not sure why/when this can happen, just a precaution
|
|
102
|
+
if name == "undetermined":
|
|
103
|
+
return 998
|
|
104
|
+
return name.count("+")
|
|
105
|
+
|
|
106
|
+
plume_groups = sorted(plume_groups, key=plume_sort_key)
|
|
107
|
+
|
|
108
|
+
if "free_gas" in list(df["phase"]):
|
|
109
|
+
phases = ["total", "free_gas", "trapped_gas", "dissolved"]
|
|
110
|
+
else:
|
|
111
|
+
phases = ["total", "gas", "dissolved"]
|
|
112
|
+
|
|
113
|
+
dates = df["date"].unique()
|
|
114
|
+
dates.sort()
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
"zones": zones if len(zones) > 1 else [],
|
|
118
|
+
"regions": regions if len(regions) > 1 else [],
|
|
119
|
+
"phases": phases,
|
|
120
|
+
"plume_groups": plume_groups if len(plume_groups) > 1 else [],
|
|
121
|
+
"dates": dates,
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
@staticmethod
|
|
125
|
+
def _validate(provider: EnsembleTableProvider) -> None:
|
|
126
|
+
col_names = provider.column_names()
|
|
127
|
+
required_columns = [
|
|
128
|
+
"date",
|
|
129
|
+
"amount",
|
|
130
|
+
"phase",
|
|
131
|
+
"containment",
|
|
132
|
+
"zone",
|
|
133
|
+
"region",
|
|
134
|
+
"plume_group",
|
|
135
|
+
]
|
|
136
|
+
missing_columns = [col for col in required_columns if col not in col_names]
|
|
137
|
+
realization = provider.realizations()[0]
|
|
138
|
+
if len(missing_columns) == 0:
|
|
139
|
+
return
|
|
140
|
+
raise ContainmentDataValidationError(
|
|
141
|
+
f"EnsembleTableProvider validation error for provider {provider} in "
|
|
142
|
+
f"realization {realization} (and possibly other csv-files).\n"
|
|
143
|
+
f" Expected columns: {', '.join(missing_columns)}\n"
|
|
144
|
+
f" Found columns: {', '.join(col_names)}\n"
|
|
145
|
+
f" (Missing columns: {', '.join(missing_columns)})"
|
|
146
|
+
f"Provided files are possibly from an outdated version of ccs-scripts?"
|
|
147
|
+
)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
|
|
4
|
+
from webviz_subsurface._utils.enum_shim import StrEnum
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class StatisticsTabOption(StrEnum):
|
|
8
|
+
PROBABILITY_PLOT = "probability-plot"
|
|
9
|
+
BOX_PLOT = "box-plot"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# pylint: disable=too-many-instance-attributes
|
|
13
|
+
@dataclass(frozen=True) # NBNB-AS: Removed slots=True (python>=3.10)
|
|
14
|
+
class ContainmentInfo:
|
|
15
|
+
zone: Optional[str]
|
|
16
|
+
region: Optional[str]
|
|
17
|
+
zones: List[str]
|
|
18
|
+
regions: Optional[List[str]]
|
|
19
|
+
phase: Optional[str]
|
|
20
|
+
containment: Optional[str]
|
|
21
|
+
plume_group: Optional[str]
|
|
22
|
+
color_choice: str
|
|
23
|
+
mark_choice: str
|
|
24
|
+
sorting: str
|
|
25
|
+
phases: List[str]
|
|
26
|
+
containments: List[str]
|
|
27
|
+
plume_groups: List[str]
|
|
28
|
+
use_stats: bool
|
|
29
|
+
date_option: str
|
|
30
|
+
statistics_tab_option: StatisticsTabOption
|
|
31
|
+
box_show_points: str
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from functools import cached_property
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any, Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
from webviz_subsurface._utils.webvizstore_functions import read_csv
|
|
7
|
+
from webviz_subsurface.plugins._co2_leakage._utilities._misc import realization_paths
|
|
8
|
+
from webviz_subsurface.plugins._map_viewer_fmu._tmp_well_pick_provider import (
|
|
9
|
+
WellPickProvider,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
LOGGER = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class EnsembleWellPicks:
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
ens_path: str,
|
|
19
|
+
well_picks_path: str,
|
|
20
|
+
map_surface_names_to_well_pick_names: Optional[Dict[str, str]],
|
|
21
|
+
):
|
|
22
|
+
self._absolute_well_pick_provider: Optional[WellPickProvider] = None
|
|
23
|
+
self._per_real_well_pick_providers: Dict[int, WellPickProvider] = {}
|
|
24
|
+
|
|
25
|
+
if Path(well_picks_path).is_absolute():
|
|
26
|
+
self._absolute_well_pick_provider = _try_get_well_pick_provider(
|
|
27
|
+
read_csv(well_picks_path),
|
|
28
|
+
map_surface_names_to_well_pick_names,
|
|
29
|
+
)
|
|
30
|
+
else:
|
|
31
|
+
realizations = realization_paths(ens_path)
|
|
32
|
+
for r, r_path in realizations.items():
|
|
33
|
+
try:
|
|
34
|
+
self._per_real_well_pick_providers[r] = WellPickProvider(
|
|
35
|
+
read_csv(r_path / well_picks_path),
|
|
36
|
+
map_surface_names_to_well_pick_names,
|
|
37
|
+
)
|
|
38
|
+
except (FileNotFoundError, OSError) as e:
|
|
39
|
+
LOGGER.warning(
|
|
40
|
+
f"Failed to find well picks for realization {r} at {r_path}: {e}"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
@cached_property
|
|
44
|
+
def well_names(self) -> List[str]:
|
|
45
|
+
if self._absolute_well_pick_provider is not None:
|
|
46
|
+
return self._absolute_well_pick_provider.well_names()
|
|
47
|
+
|
|
48
|
+
return list(
|
|
49
|
+
dict.fromkeys(
|
|
50
|
+
w
|
|
51
|
+
for v in self._per_real_well_pick_providers.values()
|
|
52
|
+
for w in v.well_names()
|
|
53
|
+
).keys()
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
def geojson_layer(
|
|
57
|
+
self, realization: int, selected_wells: List[str], formation: str
|
|
58
|
+
) -> Optional[Dict[str, Any]]:
|
|
59
|
+
if self._absolute_well_pick_provider is not None:
|
|
60
|
+
wpp = self._absolute_well_pick_provider
|
|
61
|
+
elif realization in self._per_real_well_pick_providers:
|
|
62
|
+
wpp = self._per_real_well_pick_providers[realization]
|
|
63
|
+
else:
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
well_data = dict(wpp.get_geojson(selected_wells, formation))
|
|
67
|
+
if "features" in well_data:
|
|
68
|
+
if len(well_data["features"]) == 0:
|
|
69
|
+
wellstring = "well: " if len(selected_wells) == 1 else "wells: "
|
|
70
|
+
wellstring += ", ".join(selected_wells)
|
|
71
|
+
LOGGER.warning(
|
|
72
|
+
f"Combination of formation: {formation} and "
|
|
73
|
+
f"{wellstring} not found in well picks file."
|
|
74
|
+
)
|
|
75
|
+
for i in range(len(well_data["features"])):
|
|
76
|
+
current_attribute = well_data["features"][i]["properties"]["attribute"]
|
|
77
|
+
well_data["features"][i]["properties"]["attribute"] = (
|
|
78
|
+
" " + current_attribute
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
"@@type": "GeoJsonLayer",
|
|
83
|
+
"name": "Well Picks",
|
|
84
|
+
"id": "well-picks-layer",
|
|
85
|
+
"data": well_data,
|
|
86
|
+
"visible": True,
|
|
87
|
+
"getText": "@@=properties.attribute",
|
|
88
|
+
"getTextSize": 12,
|
|
89
|
+
"getTextAnchor": "start",
|
|
90
|
+
"pointType": "circle+text",
|
|
91
|
+
"lineWidthMinPixels": 2,
|
|
92
|
+
"pointRadiusMinPixels": 2,
|
|
93
|
+
"pickable": True,
|
|
94
|
+
"parameters": {"depthTest": False},
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _try_get_well_pick_provider(
|
|
99
|
+
p: Path, name_mapping: Optional[Dict[str, str]]
|
|
100
|
+
) -> Optional[WellPickProvider]:
|
|
101
|
+
try:
|
|
102
|
+
return WellPickProvider(read_csv(p), name_mapping)
|
|
103
|
+
except OSError as e:
|
|
104
|
+
LOGGER.warning(f"Failed to read well picks file '{p}': {e}")
|
|
105
|
+
return None
|
|
@@ -1,22 +1,145 @@
|
|
|
1
|
+
from __future__ import ( # Change to import Self from typing if we update to Python >3.11
|
|
2
|
+
annotations,
|
|
3
|
+
)
|
|
4
|
+
|
|
5
|
+
from typing import Dict, List, TypedDict
|
|
6
|
+
|
|
1
7
|
from webviz_subsurface._utils.enum_shim import StrEnum
|
|
2
8
|
|
|
3
9
|
|
|
4
10
|
class MapAttribute(StrEnum):
|
|
5
11
|
MIGRATION_TIME_SGAS = "Migration time (SGAS)"
|
|
6
12
|
MIGRATION_TIME_AMFG = "Migration time (AMFG)"
|
|
13
|
+
MIGRATION_TIME_XMF2 = "Migration time (XMF2)"
|
|
7
14
|
MAX_SGAS = "Maximum SGAS"
|
|
8
15
|
MAX_AMFG = "Maximum AMFG"
|
|
16
|
+
MAX_XMF2 = "Maximum XMF2"
|
|
17
|
+
MAX_SGSTRAND = "Maximum SGSTRAND"
|
|
18
|
+
MAX_SGTRH = "Maximum SGTRH"
|
|
9
19
|
SGAS_PLUME = "Plume (SGAS)"
|
|
10
20
|
AMFG_PLUME = "Plume (AMFG)"
|
|
21
|
+
XMF2_PLUME = "Plume (XMF2)"
|
|
22
|
+
SGSTRAND_PLUME = "Plume (SGSTRAND)"
|
|
23
|
+
SGTRH_PLUME = "Plume (SGTRH)"
|
|
11
24
|
MASS = "Mass"
|
|
12
25
|
DISSOLVED = "Dissolved mass"
|
|
13
|
-
FREE = "Free mass"
|
|
26
|
+
FREE = "Free gas mass"
|
|
27
|
+
FREE_GAS = "Free gas phase mass"
|
|
28
|
+
TRAPPED_GAS = "Trapped gas phase mass"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class MapGroup(StrEnum):
|
|
32
|
+
MIGRATION_TIME_SGAS = "SGAS"
|
|
33
|
+
MIGRATION_TIME_AMFG = "AMFG"
|
|
34
|
+
MIGRATION_TIME_XMF2 = "XMF2"
|
|
35
|
+
MAX_SGAS = "SGAS"
|
|
36
|
+
MAX_AMFG = "AMFG"
|
|
37
|
+
MAX_XMF2 = "XMF2"
|
|
38
|
+
MAX_SGSTRAND = "SGSTRAND"
|
|
39
|
+
MAX_SGTRH = "SGTRH"
|
|
40
|
+
SGAS_PLUME = "SGAS"
|
|
41
|
+
AMFG_PLUME = "AMFG"
|
|
42
|
+
XMF2_PLUME = "XMF2"
|
|
43
|
+
SGSTRAND_PLUME = "SGSTRAND"
|
|
44
|
+
SGTRH_PLUME = "SGTRH"
|
|
45
|
+
MASS = "CO2 MASS"
|
|
46
|
+
DISSOLVED = "CO2 MASS"
|
|
47
|
+
FREE = "CO2 MASS"
|
|
48
|
+
FREE_GAS = "CO2 MASS"
|
|
49
|
+
TRAPPED_GAS = "CO2 MASS"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
map_group_labels = {
|
|
53
|
+
"SGAS": "Gas phase",
|
|
54
|
+
"AMFG": "Dissolved phase",
|
|
55
|
+
"XMF2": "Dissolved phase",
|
|
56
|
+
"SGSTRAND": "Trapped gas phase",
|
|
57
|
+
"SGTRH": "Trapped gas phase",
|
|
58
|
+
"CO2 MASS": "CO2 mass",
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class MapType(StrEnum):
|
|
63
|
+
MIGRATION_TIME_SGAS = "MIGRATION_TIME"
|
|
64
|
+
MIGRATION_TIME_AMFG = "MIGRATION_TIME"
|
|
65
|
+
MIGRATION_TIME_XMF2 = "MIGRATION_TIME"
|
|
66
|
+
MAX_SGAS = "MAX"
|
|
67
|
+
MAX_AMFG = "MAX"
|
|
68
|
+
MAX_XMF2 = "MAX"
|
|
69
|
+
MAX_SGSTRAND = "MAX"
|
|
70
|
+
MAX_SGTRH = "MAX"
|
|
71
|
+
SGAS_PLUME = "PLUME"
|
|
72
|
+
AMFG_PLUME = "PLUME"
|
|
73
|
+
XMF2_PLUME = "PLUME"
|
|
74
|
+
SGSTRAND_PLUME = "PLUME"
|
|
75
|
+
SGTRH_PLUME = "PLUME"
|
|
76
|
+
MASS = "MASS"
|
|
77
|
+
DISSOLVED = "MASS"
|
|
78
|
+
FREE = "MASS"
|
|
79
|
+
FREE_GAS = "MASS"
|
|
80
|
+
TRAPPED_GAS = "MASS"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class MapNamingConvention(StrEnum):
|
|
84
|
+
MIGRATION_TIME_SGAS = "migrationtime_sgas"
|
|
85
|
+
MIGRATION_TIME_AMFG = "migrationtime_amfg"
|
|
86
|
+
MIGRATION_TIME_XMF2 = "migrationtime_xmf2"
|
|
87
|
+
MAX_SGAS = "max_sgas"
|
|
88
|
+
MAX_AMFG = "max_amfg"
|
|
89
|
+
MAX_XMF2 = "max_xmf2"
|
|
90
|
+
MAX_SGSTRAND = "max_sgstrand"
|
|
91
|
+
MAX_SGTRH = "max_sgtrh"
|
|
92
|
+
MASS = "co2_mass_total"
|
|
93
|
+
DISSOLVED = "co2_mass_dissolved_phase"
|
|
94
|
+
FREE = "co2_mass_gas_phase"
|
|
95
|
+
FREE_GAS = "co2_mass_free_gas_phase"
|
|
96
|
+
TRAPPED_GAS = "co2_mass_trapped_gas_phase"
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class FilteredMapAttribute:
|
|
100
|
+
def __init__(self, mapping: Dict):
|
|
101
|
+
self.mapping = mapping
|
|
102
|
+
map_types = {
|
|
103
|
+
key: MapType[key].value
|
|
104
|
+
for key in MapAttribute.__members__
|
|
105
|
+
if MapAttribute[key].value in self.mapping
|
|
106
|
+
}
|
|
107
|
+
map_groups = {
|
|
108
|
+
key: MapGroup[key].value
|
|
109
|
+
for key in MapAttribute.__members__
|
|
110
|
+
if MapAttribute[key].value in self.mapping
|
|
111
|
+
}
|
|
112
|
+
map_attrs_with_plume = [
|
|
113
|
+
map_groups[key] for key, value in map_types.items() if value == "MAX"
|
|
114
|
+
]
|
|
115
|
+
plume_request = {
|
|
116
|
+
f"Plume ({item})": f"{item.lower()}_plume" for item in map_attrs_with_plume
|
|
117
|
+
}
|
|
118
|
+
self.mapping.update(plume_request)
|
|
119
|
+
self.filtered_values = self.filter_map_attribute()
|
|
120
|
+
|
|
121
|
+
def filter_map_attribute(self) -> Dict:
|
|
122
|
+
return {
|
|
123
|
+
MapAttribute[key]: self.mapping[MapAttribute[key].value]
|
|
124
|
+
for key in MapAttribute.__members__
|
|
125
|
+
if MapAttribute[key].value in self.mapping
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
def __getitem__(self, key: MapAttribute) -> MapAttribute:
|
|
129
|
+
if isinstance(key, MapAttribute):
|
|
130
|
+
return self.filtered_values[key]
|
|
131
|
+
raise KeyError(f"Key must be a MapAttribute, " f"got {type(key)} instead.")
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def values(self) -> Dict:
|
|
135
|
+
return self.filtered_values
|
|
14
136
|
|
|
15
137
|
|
|
16
138
|
class Co2MassScale(StrEnum):
|
|
17
139
|
NORMALIZE = "Fraction"
|
|
140
|
+
KG = "kg"
|
|
141
|
+
TONS = "tons"
|
|
18
142
|
MTONS = "M tons"
|
|
19
|
-
KG = "Kg"
|
|
20
143
|
|
|
21
144
|
|
|
22
145
|
class Co2VolumeScale(StrEnum):
|
|
@@ -42,6 +165,8 @@ class LayoutLabels(StrEnum):
|
|
|
42
165
|
COMMON_SELECTIONS = "Options and global filters"
|
|
43
166
|
FEEDBACK = "User feedback"
|
|
44
167
|
VISUALIZATION_UPDATE = "Update threshold"
|
|
168
|
+
VISUALIZATION_THRESHOLDS = "Manage visualization filter"
|
|
169
|
+
ALL_REAL = "Select all"
|
|
45
170
|
|
|
46
171
|
|
|
47
172
|
# pylint: disable=too-few-public-methods
|
|
@@ -56,6 +181,13 @@ class LayoutStyle:
|
|
|
56
181
|
"background-color": "lightgrey",
|
|
57
182
|
}
|
|
58
183
|
|
|
184
|
+
ALL_REAL_BUTTON = {
|
|
185
|
+
"marginLeft": "10px",
|
|
186
|
+
"height": "25px",
|
|
187
|
+
"line-height": "25px",
|
|
188
|
+
"background-color": "lightgrey",
|
|
189
|
+
}
|
|
190
|
+
|
|
59
191
|
FEEDBACK_BUTTON = {
|
|
60
192
|
"marginBottom": "10px",
|
|
61
193
|
"width": "100%",
|
|
@@ -70,3 +202,39 @@ class LayoutStyle:
|
|
|
70
202
|
"line-height": "30px",
|
|
71
203
|
"background-color": "lightgrey",
|
|
72
204
|
}
|
|
205
|
+
|
|
206
|
+
THRESHOLDS_BUTTON = {
|
|
207
|
+
"marginTop": "10px",
|
|
208
|
+
"width": "100%",
|
|
209
|
+
"height": "30px",
|
|
210
|
+
"line-height": "30px",
|
|
211
|
+
"padding": "0",
|
|
212
|
+
"background-color": "lightgrey",
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class MenuOptions(TypedDict):
|
|
217
|
+
zones: List[str]
|
|
218
|
+
regions: List[str]
|
|
219
|
+
phases: List[str]
|
|
220
|
+
plume_groups: List[str]
|
|
221
|
+
dates: List[str]
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class MapThresholds:
|
|
225
|
+
def __init__(self, mapping: FilteredMapAttribute):
|
|
226
|
+
self.standard_thresholds = {
|
|
227
|
+
MapAttribute[key.name].value: 0.0
|
|
228
|
+
for key in mapping.filtered_values.keys()
|
|
229
|
+
if MapType[MapAttribute[key.name].name].value
|
|
230
|
+
not in ["PLUME", "MIGRATION_TIME"]
|
|
231
|
+
}
|
|
232
|
+
if MapAttribute.MAX_AMFG in self.standard_thresholds.keys():
|
|
233
|
+
self.standard_thresholds[MapAttribute.MAX_AMFG] = 0.0005
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
class BoundarySettings(TypedDict):
|
|
237
|
+
polygon_file_pattern: str
|
|
238
|
+
attribute: str
|
|
239
|
+
hazardous_name: str
|
|
240
|
+
containment_name: str
|