Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CO2Leakage: Visual improvements, split plots into tabs, add slider, add options for which category to use for color/markings #1269

Merged
merged 38 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
28a704d
Modify zone- and region-views to show containment for a specific phase.
FredrikNevjenNR Mar 8, 2024
298cf71
Experiment with options for ordering and coloring containment plots.
FredrikNevjenNR Mar 11, 2024
26907a5
Add feedback button.
FredrikNevjenNR Mar 15, 2024
8cacbf1
Formatting fix for the feedback button.
FredrikNevjenNR Mar 15, 2024
4e69a5b
Extend containment plot options.
FredrikNevjenNR Mar 15, 2024
0d1f0ea
Add percentages to hover information in containment plots.
FredrikNevjenNR Mar 18, 2024
489c66f
Formatting fixes.
FredrikNevjenNR Mar 18, 2024
85f2807
Fix colorbar being cut off.
FredrikNevjenNR Mar 19, 2024
8631463
Prevent division by 0 in percentages.
FredrikNevjenNR Mar 19, 2024
8afc49d
Merge pull request #16 from AudunSektnanNR/branch_fn_the_sixth
FredrikNevjenNR Mar 19, 2024
de1f132
Separate containment plots into tabs.
FredrikNevjenNR Mar 19, 2024
d6c3b70
Add slider to change map and plot sizes.
FredrikNevjenNR Mar 21, 2024
43109a6
Visual improvements, cleaner menus.
FredrikNevjenNR Mar 22, 2024
399d131
Formatting fixes.
FredrikNevjenNR Mar 22, 2024
049c93c
Add color and mark selection and implement missing combinations
FredrikNevjenNR Apr 4, 2024
7310cdf
Remove replaced code, formatting, minor refactoring.
FredrikNevjenNR Apr 5, 2024
5c65650
Add proper ids to GUI-elements.
FredrikNevjenNR Apr 5, 2024
eebbb88
Add options to mark menu earlier, some formatting.
FredrikNevjenNR Apr 5, 2024
7d12a04
Merge pull request #17 from AudunSektnanNR/colorbymarkby
FredrikNevjenNR Apr 5, 2024
bc12ea3
Fix case without zone and/or regions.
AudunSektnanNR Apr 15, 2024
5fe29aa
Merge branch 'equinor:master' into develop
AudunSektnanNR Apr 15, 2024
e0a9f26
Update changelog
AudunSektnanNR Apr 15, 2024
d3997e2
Fix pylint.
AudunSektnanNR Apr 15, 2024
65aa3a4
Fix pylint.
AudunSektnanNR Apr 15, 2024
f061f88
Fix pylint.
AudunSektnanNR Apr 15, 2024
38aac5d
Fix one mypy
AudunSektnanNR Apr 16, 2024
24216e1
Fix second mypy.
AudunSektnanNR Apr 16, 2024
8dd333a
CCS-117: Fix crash for case with csv-files but no surfaces (#18)
AudunSektnanNR Apr 22, 2024
c393d6c
Add option to use only color, no marking.
FredrikNevjenNR Apr 24, 2024
c5e3fd7
Disable region choice if zone is selected and vice versa.
FredrikNevjenNR Apr 24, 2024
cd3a67f
Merge pull request #19 from AudunSektnanNR/colorbymarkby
FredrikNevjenNR Apr 25, 2024
5bf421b
Merge branch 'master' into develop
AudunSektnanNR Apr 25, 2024
87a1646
Black fix.
FredrikNevjenNR Apr 25, 2024
91946ed
Add hover info mid-field for the third plot.
FredrikNevjenNR Apr 26, 2024
183e15c
Merge pull request #20 from AudunSektnanNR/hover_info_in_field
FredrikNevjenNR Apr 26, 2024
cf0775e
Minor bugfix.
FredrikNevjenNR May 2, 2024
f000f04
Merge branch 'master' into develop
AudunSektnanNR Jun 19, 2024
48b1998
pylint fix.
AudunSektnanNR Jun 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- [#1266](https://github.com/equinor/webviz-subsurface/pull/1266) - Added waterfall plot for analysing volume changes in `VolumetricAnalysis`.

### Changed
- [#1269](https://github.com/equinor/webviz-subsurface/pull/1269) - Visual improvements to `CO2Leakage`: split plots into tabs, add slider, add options for which category to use for color/markings.

## [0.2.24] - 2024-01-25

### Added
Expand Down
99 changes: 69 additions & 30 deletions webviz_subsurface/plugins/_co2_leakage/_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
process_visualization_info,
property_origin,
readable_name,
set_plot_ids,
)
from webviz_subsurface.plugins._co2_leakage._utilities.fault_polygons import (
FaultPolygonsHandler,
Expand Down Expand Up @@ -170,7 +171,7 @@ def __init__(
ensemble_paths,
self._co2_table_providers,
self._co2_actual_volume_table_providers,
self._ensemble_surface_providers,
self._co2_table_providers,
)
except Exception as err:
self._error_message = f"Plugin initialization failed: {err}"
Expand Down Expand Up @@ -229,25 +230,18 @@ def _ensemble_dates(self, ens: str) -> List[str]:
raise ValueError(f"Failed to fetch dates for attribute '{att_name}'")
return dates

# Might want to do some refactoring if this gets too big
# pylint: disable=too-many-statements
def _set_callbacks(self) -> None:
# Cannot avoid many arguments since all the parameters are needed
# to determine what to plot
# pylint: disable=too-many-arguments
# pylint: disable=too-many-locals
@callback(
Output(
self._settings_component(ViewSettings.Ids.CONTAINMENT_VIEW), "value"
),
Output(self._view_component(MapViewElement.Ids.BAR_PLOT), "figure"),
Output(self._view_component(MapViewElement.Ids.TIME_PLOT), "figure"),
Output(
self._view_component(MapViewElement.Ids.TIME_PLOT_ONE_REAL), "figure"
),
Output(self._view_component(MapViewElement.Ids.BAR_PLOT), "style"),
Output(self._view_component(MapViewElement.Ids.TIME_PLOT), "style"),
Output(
self._view_component(MapViewElement.Ids.TIME_PLOT_ONE_REAL), "style"
),
Input(self._settings_component(ViewSettings.Ids.ENSEMBLE), "value"),
Input(self._settings_component(ViewSettings.Ids.GRAPH_SOURCE), "value"),
Input(self._settings_component(ViewSettings.Ids.CO2_SCALE), "value"),
Expand All @@ -258,7 +252,11 @@ def _set_callbacks(self) -> None:
Input(self._settings_component(ViewSettings.Ids.Y_MAX_GRAPH), "value"),
Input(self._settings_component(ViewSettings.Ids.ZONE), "value"),
Input(self._settings_component(ViewSettings.Ids.REGION), "value"),
Input(self._settings_component(ViewSettings.Ids.CONTAINMENT_VIEW), "value"),
Input(self._settings_component(ViewSettings.Ids.PHASE), "value"),
Input(self._settings_component(ViewSettings.Ids.CONTAINMENT), "value"),
Input(self._settings_component(ViewSettings.Ids.COLOR_BY), "value"),
Input(self._settings_component(ViewSettings.Ids.MARK_BY), "value"),
Input(self._settings_component(ViewSettings.Ids.SORT_PLOT), "value"),
)
@callback_typecheck
def update_graphs(
Expand All @@ -272,15 +270,23 @@ def update_graphs(
y_max_val: Optional[float],
zone: Optional[str],
region: Optional[str],
containment_view: str,
) -> Tuple[Dict, go.Figure, go.Figure]:
out = {"figs": [no_update] * 3, "styles": [{"display": "none"}] * 3}
phase: str,
containment: str,
color_choice: str,
mark_choice: Optional[str],
sorting: str,
) -> Tuple[Dict, go.Figure, go.Figure, go.Figure]:
# pylint: disable=too-many-locals
figs = [no_update] * 3
cont_info = process_containment_info(
zone,
region,
containment_view,
phase,
containment,
color_choice,
mark_choice,
sorting,
self._zone_and_region_options[ensemble][source],
source,
)
if source in [
GraphSource.CONTAINMENT_MASS,
Expand All @@ -290,12 +296,11 @@ def update_graphs(
y_min_val if len(y_min_auto) == 0 else None,
y_max_val if len(y_max_auto) == 0 else None,
]
out["styles"] = [{}] * 3
if (
source == GraphSource.CONTAINMENT_MASS
and ensemble in self._co2_table_providers
):
out["figs"][: len(out["figs"])] = generate_containment_figures(
figs[: len(figs)] = generate_containment_figures(
self._co2_table_providers[ensemble],
co2_scale,
realizations[0],
Expand All @@ -306,18 +311,14 @@ def update_graphs(
source == GraphSource.CONTAINMENT_ACTUAL_VOLUME
and ensemble in self._co2_actual_volume_table_providers
):
out["figs"][: len(out["figs"])] = generate_containment_figures(
figs[: len(figs)] = generate_containment_figures(
self._co2_actual_volume_table_providers[ensemble],
co2_scale,
realizations[0],
y_limits,
cont_info,
)
for fig in out["figs"]:
fig["layout"][
"uirevision"
] = f"{source}-{co2_scale}-{cont_info['zone']}-{cont_info['region']}"
out["figs"][-1]["layout"]["uirevision"] += f"-{realizations}"
set_plot_ids(figs, source, co2_scale, cont_info, realizations)
elif source == GraphSource.UNSMRY:
if self._unsmry_providers is not None:
if ensemble in self._unsmry_providers:
Expand All @@ -326,14 +327,13 @@ def update_graphs(
co2_scale,
self._co2_table_providers[ensemble],
)
out["figs"][: len(u_figs)] = u_figs
out["styles"][: len(u_figs)] = [{}] * len(u_figs)
figs = list(u_figs)
else:
LOGGER.warning(
"""UNSMRY file has not been specified as input.
Please use unsmry_relpath in the configuration."""
)
return cont_info["containment_view"], *out["figs"], *out["styles"] # type: ignore
return figs # type: ignore

@callback(
Output(self._view_component(MapViewElement.Ids.DATE_SLIDER), "marks"),
Expand All @@ -352,8 +352,7 @@ def set_dates(ensemble: str) -> Tuple[Dict[int, Dict[str, Any]], Optional[int]]:
}
for i, d in enumerate(date_list)
}
initial_date = max(dates.keys())
return dates, initial_date
return dates, max(dates.keys())

@callback(
Output(self._view_component(MapViewElement.Ids.DATE_WRAPPER), "style"),
Expand Down Expand Up @@ -401,7 +400,7 @@ def set_well_options(
)

# Cannot avoid many arguments and/or locals since all layers of the DeckGL map
# needs to be updated simultaneously
# need to be updated simultaneously
# pylint: disable=too-many-arguments,too-many-locals
@callback(
Output(self._view_component(MapViewElement.Ids.DECKGL_MAP), "layers"),
Expand Down Expand Up @@ -566,3 +565,43 @@ def open_close_feedback(_n_clicks: Optional[int]) -> bool:
if _n_clicks is not None:
return _n_clicks > 0
raise PreventUpdate

@callback(
Output(self._view_component(MapViewElement.Ids.TOP_ELEMENT), "style"),
Output(self._view_component(MapViewElement.Ids.BOTTOM_ELEMENT), "style"),
Output(self._view_component(MapViewElement.Ids.BAR_PLOT), "style"),
Output(self._view_component(MapViewElement.Ids.TIME_PLOT), "style"),
Output(
self._view_component(MapViewElement.Ids.TIME_PLOT_ONE_REAL), "style"
),
Input(self._settings_component(ViewSettings.Ids.ENSEMBLE), "value"),
Input(self._view_component(MapViewElement.Ids.SIZE_SLIDER), "value"),
State(self._view_component(MapViewElement.Ids.TOP_ELEMENT), "style"),
State(self._view_component(MapViewElement.Ids.BOTTOM_ELEMENT), "style"),
Input(self._settings_component(ViewSettings.Ids.GRAPH_SOURCE), "value"),
)
def resize_plots(
ensemble: str,
slider_value: float,
top_style: Dict,
bottom_style: Dict,
source: GraphSource,
) -> List[Dict]:
bottom_style["height"] = f"{slider_value}vh"
top_style["height"] = f"{80 - slider_value}vh"

styles = [{"height": f"{slider_value * 0.9 - 4}vh", "width": "90%"}] * 3
if source == GraphSource.UNSMRY and self._unsmry_providers is None:
styles = [{"display": "none"}] * 3
elif (
source == GraphSource.CONTAINMENT_MASS
and ensemble not in self._co2_table_providers
):
styles = [{"display": "none"}] * 3
elif (
source == GraphSource.CONTAINMENT_ACTUAL_VOLUME
and ensemble not in self._co2_actual_volume_table_providers
):
styles = [{"display": "none"}] * 3

return [top_style, bottom_style] + styles
101 changes: 71 additions & 30 deletions webviz_subsurface/plugins/_co2_leakage/_utilities/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import numpy as np
import plotly.graph_objects as go
import webviz_subsurface_components as wsc
from dash import no_update
from flask_caching import Cache

from webviz_subsurface._providers import (
Expand All @@ -30,7 +31,6 @@
from webviz_subsurface.plugins._co2_leakage._utilities.generic import (
Co2MassScale,
Co2VolumeScale,
ContainmentViews,
GraphSource,
LayoutLabels,
MapAttribute,
Expand Down Expand Up @@ -213,7 +213,13 @@ def create_map_annotations(
unit: str,
) -> List[wsc.ViewAnnotation]:
annotations = []
if surface_data is not None:
if (
surface_data is not None
and surface_data.color_map_range[0] is not None
and surface_data.color_map_range[1] is not None
):
num_digits = np.ceil(np.log(surface_data.color_map_range[1]) / np.log(10))
numbersize = max((6, min((17 - num_digits, 11))))
annotations.append(
wsc.ViewAnnotation(
id="1_view",
Expand All @@ -227,6 +233,8 @@ def create_map_annotations(
openColorSelector=False,
legendScaleSize=0.1,
legendFontSize=20,
tickFontSize=numbersize,
numberOfTicks=2,
colorTables=colortables,
),
wsc.ViewFooter(children=formation),
Expand Down Expand Up @@ -383,7 +391,7 @@ def generate_containment_figures(
co2_scale: Union[Co2MassScale, Co2VolumeScale],
realization: int,
y_limits: List[Optional[float]],
containment_info: Dict[str, Union[str, None, List[str]]],
containment_info: Dict[str, Union[str, None, List[str], int]],
) -> Tuple[go.Figure, go.Figure, go.Figure]:
try:
fig0 = generate_co2_volume_figure(
Expand Down Expand Up @@ -484,36 +492,69 @@ def process_visualization_info(
def process_containment_info(
zone: Optional[str],
region: Optional[str],
view: Optional[str],
phase: str,
containment: str,
color_choice: str,
mark_choice: Optional[str],
sorting: str,
zone_and_region_options: Dict[str, List[str]],
source: str,
) -> Dict[str, Union[str, None, List[str]]]:
) -> Dict[str, Union[str, None, List[str], int]]:
if mark_choice is None:
mark_choice = "phase"
zones = zone_and_region_options["zones"]
regions = zone_and_region_options["regions"]
if source in [
GraphSource.CONTAINMENT_MASS,
GraphSource.CONTAINMENT_ACTUAL_VOLUME,
]:
if view == ContainmentViews.CONTAINMENTSPLIT:
return {"zone": zone, "region": region, "containment_view": view}
if view == ContainmentViews.ZONESPLIT and len(zones) > 0:
zones = [zone_name for zone_name in zones if zone_name != "all"]
elif view == ContainmentViews.REGIONSPLIT and len(regions) > 0:
regions = [reg_name for reg_name in regions if reg_name != "all"]
else:
return {
"zone": zone,
"region": region,
"containment_view": ContainmentViews.CONTAINMENTSPLIT,
}
return {
"zone": zone,
"region": region,
"containment_view": view,
"zones": zones,
"regions": regions,
}
return {"containment_view": ContainmentViews.CONTAINMENTSPLIT}
if len(zones) > 0:
zones = [zone_name for zone_name in zones if zone_name != "all"]
if len(regions) > 0:
regions = [reg_name for reg_name in regions if reg_name != "all"]
containments = ["hazardous", "outside", "contained"]
phases = ["gas", "aqueous"]
return {
"zone": zone,
"region": region,
"zones": zones,
"regions": regions,
"phase": phase,
"containment": containment,
"color_choice": color_choice,
"mark_choice": mark_choice,
"sorting": sorting,
"phases": phases,
"containments": containments,
}


def set_plot_ids(
figs: List[go.Figure],
source: GraphSource,
scale: Union[Co2MassScale, Co2VolumeScale],
containment_info: Dict,
realizations: List[int],
) -> None:
if figs[0] != no_update:
zone_str = (
containment_info["zone"] if containment_info["zone"] is not None else "None"
)
region_str = (
containment_info["region"]
if containment_info["region"] is not None
else "None"
)
plot_id = "-".join(
(
source,
scale,
zone_str,
region_str,
str(containment_info["phase"]),
str(containment_info["containment"]),
containment_info["color_choice"],
containment_info["mark_choice"],
)
)
for fig in figs:
fig["layout"]["uirevision"] = plot_id
figs[-1]["layout"]["uirevision"] += f"-{realizations}"


def process_summed_mass(
Expand Down
Loading