Skip to content

Commit

Permalink
Tornado improvements (#825)
Browse files Browse the repository at this point in the history
  • Loading branch information
tnatt authored Oct 25, 2021
1 parent 04a6615 commit dc84940
Show file tree
Hide file tree
Showing 21 changed files with 755 additions and 593 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [UNRELEASED] - YYYY-MM-DD

### Added
- [#825](https://github.com/equinor/webviz-subsurface/pull/825) - Added options to create separate tornado's for e.g Region/Zone in `VolumetricAnalysis`. As well as various improvements to the tornado figure.
- [#734](https://github.com/equinor/webviz-subsurface/pull/645) - New plugin, SeismicMisfit, for comparing observed and modelled seismic attributes. Multiple views, including misfit quantification and coverage plots.
- [#809](https://github.com/equinor/webviz-subsurface/pull/809) - `GroupTree` - added more statistical options (P10, P90, P50/Median, Max, Min). Some improvements to the menu layout and behaviour

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

def test_volumetrics_no_sens(dash_duo, app, shared_settings) -> None:
plugin = VolumetricAnalysis(
app,
shared_settings["HM_SETTINGS"],
ensembles=shared_settings["HM_ENSEMBLES"],
volfiles={"geogrid": "geogrid--vol.csv", "simgrid": "simgrid--vol.csv"},
Expand All @@ -24,7 +23,6 @@ def test_volumetrics_no_sens(dash_duo, app, shared_settings) -> None:

def test_volumetrics_sens(dash_duo, app, shared_settings) -> None:
plugin = VolumetricAnalysis(
app,
shared_settings["SENS_SETTINGS"],
ensembles=shared_settings["SENS_ENSEMBLES"],
volfiles={"geogrid": "geogrid--vol.csv", "simgrid": "simgrid--vol.csv"},
Expand Down
71 changes: 52 additions & 19 deletions webviz_subsurface/_components/tornado/_tornado_bar_chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@
import plotly.graph_objects as go

from webviz_subsurface._abbreviations.number_formatting import si_prefixed
from webviz_subsurface._utils.formatting import printable_int_list

from ._tornado_data import TornadoData


class TornadoBarChart:
"""Creates a plotly bar figure from a TornadoData instance"""

# pylint: disable=too-many-arguments
# pylint: disable=too-many-arguments, too-many-instance-attributes
def __init__(
self,
tornado_data: TornadoData,
Expand All @@ -25,6 +24,9 @@ def __init__(
spaced: bool = True,
use_true_base: bool = False,
show_realization_points: bool = True,
show_reference: bool = True,
color_by_sensitivity: bool = False,
sensitivity_color_map: dict = None,
) -> None:
self._tornadotable = tornado_data.tornadotable
self._realtable = self.make_points(tornado_data.real_df)
Expand All @@ -37,6 +39,7 @@ def __init__(
self._locked_si_prefix_relative: Optional[int]
self._scale = tornado_data.scale
self._use_true_base = use_true_base
self._show_reference = show_reference
if self._scale == "Percentage":
self._unit_x = "%"
self._locked_si_prefix_relative = 0
Expand All @@ -46,6 +49,15 @@ def __init__(
self._figure_height = figure_height
self._label_options = label_options
self._show_scatter = show_realization_points
self._color_by_sens = color_by_sensitivity
self._sens_color_map = sensitivity_color_map

def create_color_list(self, sensitivities: list) -> list:
return (
[self._sens_color_map.get(sensname, "grey") for sensname in sensitivities]
if self._sens_color_map is not None
else self._plotly_theme["layout"]["colorway"]
)

def make_points(self, realdf: pd.DataFrame) -> pd.DataFrame:
dfs = []
Expand Down Expand Up @@ -105,21 +117,30 @@ def bar_labels(self, case: str) -> List:
return [
f"<b>{self._set_si_prefix_relative(x)}</b>, "
f"True: {self._set_si_prefix(val)}, "
f"<br><b>Case: {label}</b>, "
f"Reals: {printable_int_list(reals)}"
if reals
else None
for x, label, val, reals in zip(
f"<br><b>Case: {label}</b> "
for x, label, val in zip(
self._tornadotable[f"{case}_tooltip"],
self._tornadotable[f"{case}_label"],
self._tornadotable[f"true_{case}"],
self._tornadotable[f"{case}_reals"],
)
]
return []

def hover_label(self) -> List:
return [
f"<b>Sensname: {sens}</b>:<br>"
f"Low: <b>{self._set_si_prefix_relative(low)}</b>, "
f"High: <b>{self._set_si_prefix_relative(high)}</b>, "
for low, high, sens in zip(
self._tornadotable["low"],
self._tornadotable["high"],
self._tornadotable["sensname"],
)
]

@property
def data(self) -> List:
colors = self.create_color_list(self._tornadotable["sensname"].unique())
return [
dict(
type="bar",
Expand All @@ -133,9 +154,13 @@ def data(self) -> List:
text=self.bar_labels("low"),
textposition="auto",
insidetextanchor="middle",
hoverinfo="none",
hoverinfo="text",
hovertext=self.hover_label(),
orientation="h",
marker={"line": {"width": 1.5, "color": "black"}},
marker={
"line": {"width": 1.5, "color": "black"},
"color": colors if self._color_by_sens else None,
},
),
dict(
type="bar",
Expand All @@ -149,9 +174,13 @@ def data(self) -> List:
text=self.bar_labels("high"),
textposition="auto",
insidetextanchor="middle",
hoverinfo="none",
hoverinfo="text",
hovertext=self.hover_label(),
orientation="h",
marker={"line": {"width": 1.5, "color": "black"}},
marker={
"line": {"width": 1.5, "color": "black"},
"color": colors if self._color_by_sens else None,
},
),
]

Expand All @@ -173,7 +202,8 @@ def scatter_data(self) -> List[Dict]:
"y": df["sensname"],
"x": self.calculate_scatter_value(df["VALUE"]),
"text": df["REAL"],
"hoverinfo": "none",
"hovertemplate": "REAL: <b>%{text}</b><br>"
+ "X: <b>%{x:.1f}</b> <extra></extra>",
"marker": {
"size": 15,
"color": self._plotly_theme["layout"]["colorway"][0]
Expand Down Expand Up @@ -208,7 +238,9 @@ def layout(self) -> Dict:
"title": self._scale,
"range": self.range,
"autorange": self._show_scatter or self._tornadotable.empty,
"showgrid": False,
"gridwidth": 1,
"gridcolor": "whitesmoke",
"showgrid": True,
"zeroline": False,
"linecolor": "black",
"showline": True,
Expand All @@ -227,7 +259,8 @@ def layout(self) -> Dict:
"tickfont": {"size": 15},
},
"showlegend": False,
"hovermode": "y",
"hovermode": "closest",
"hoverlabel": {"bgcolor": "white", "font_size": 16},
"annotations": [
{
"x": 0 if not self._use_true_base else self._reference_average,
Expand All @@ -239,7 +272,9 @@ def layout(self) -> Dict:
"showarrow": False,
"align": "center",
}
],
]
if self._show_reference
else None,
"shapes": [
{
"type": "line",
Expand All @@ -258,9 +293,7 @@ def layout(self) -> Dict:

@property
def figure(self) -> go.Figure:
data = self.data

fig = go.Figure({"data": data, "layout": self.layout})
fig = go.Figure({"data": self.data, "layout": self.layout})
if self._show_scatter:
fig.update_traces(marker_opacity=0.4, text=None)
for trace in self.scatter_data:
Expand Down
1 change: 1 addition & 0 deletions webviz_subsurface/_components/tornado/_tornado_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ def _cut_sensitivities_by_ref(self) -> None:

self._tornadotable = self._tornadotable.loc[
((self._tornadotable["low"] - self._tornadotable["high"]) != 0)
| (self._tornadotable["sensname"] == self._reference)
]

def _sort_sensitivities_by_max(self) -> None:
Expand Down
46 changes: 24 additions & 22 deletions webviz_subsurface/_components/tornado/tornado_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,10 @@ def settings_layout(self) -> html.Div:
"label": "Show realization points",
"value": "Show realization points",
},
{
"label": "Color bars by sensitivity",
"value": "Color bars by sensitivity",
},
],
value=[],
labelStyle={"display": "block"},
Expand Down Expand Up @@ -439,6 +443,7 @@ def _calc_tornado(
locked_si_prefix=data.get("locked_si_prefix", None),
use_true_base=scale == "True value",
show_realization_points="Show realization points" in plot_options,
color_by_sensitivity="Color bars by sensitivity" in plot_options,
)
tornado_table = TornadoTable(tornado_data=tornado_data)
return (
Expand All @@ -455,37 +460,34 @@ def _calc_tornado(
[
Input(self.ids("tornado-graph"), "clickData"),
Input(self.ids("reset"), "n_clicks"),
State(self.ids("high-low-storage"), "data"),
],
)
def _save_click_data(data: dict, nclicks: Optional[int]) -> str:
if callback_context.triggered is None:
def _save_click_data(
data: dict, nclicks: Optional[int], sens_reals: dict
) -> str:
if (
callback_context.triggered is None
or sens_reals is None
or data is None
):
raise PreventUpdate

ctx = callback_context.triggered[0]["prop_id"].split(".")[0]
if ctx == self.ids("reset") and nclicks:

return json.dumps(
{
"real_low": [],
"real_high": [],
"sens_name": None,
}
)
try:

real_low = next(
x["customdata"] for x in data["points"] if x["curveNumber"] == 0
)
real_high = next(
x["customdata"] for x in data["points"] if x["curveNumber"] == 1
)
sens_name = data["points"][0]["y"]
return json.dumps(
{
"real_low": real_low,
"real_high": real_high,
"sens_name": sens_name,
}
)
except TypeError as exc:
raise PreventUpdate from exc
sensname = data["points"][0]["y"]
real_high = sens_reals[sensname]["real_high"]
real_low = sens_reals[sensname]["real_low"]
return json.dumps(
{
"real_low": real_low,
"real_high": real_high,
"sens_name": sensname,
}
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
from .fipfile_qc_controller import fipfile_qc_controller
from .layout_controllers import layout_controllers
from .selections_controllers import selections_controllers
from .tornado_controllers import tornado_controllers
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from dash import Dash, Input, Output, State, callback_context, dash_table, html
from dash import Input, Output, State, callback, callback_context, dash_table, html
from dash.exceptions import PreventUpdate

from webviz_subsurface._figures import create_figure
Expand All @@ -24,11 +24,10 @@

# pylint: disable=too-many-locals
def comparison_controllers(
app: Dash,
get_uuid: Callable,
volumemodel: InplaceVolumesModel,
) -> None:
@app.callback(
@callback(
Output({"id": get_uuid("main-src-comp"), "wrapper": "table"}, "children"),
Input(get_uuid("selections"), "data"),
Input({"id": get_uuid("main-src-comp"), "element": "display-option"}, "value"),
Expand Down Expand Up @@ -56,7 +55,7 @@ def _update_page_src_comp(
display_option=display_option,
)

@app.callback(
@callback(
Output({"id": get_uuid("main-ens-comp"), "wrapper": "table"}, "children"),
Input(get_uuid("selections"), "data"),
Input({"id": get_uuid("main-ens-comp"), "element": "display-option"}, "value"),
Expand Down
Loading

0 comments on commit dc84940

Please sign in to comment.