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

SwatInit QC to WLF #1104

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions webviz_subsurface/plugins/_swatinit_qc/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from ._plugin import SwatinitQC
from ._swatint import SwatinitQcDataModel
5 changes: 5 additions & 0 deletions webviz_subsurface/plugins/_swatinit_qc/_error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from dash import html


def error(error_message: str) -> html.Div:
return html.Div(children=error_message, style={"color": "red"})
97 changes: 61 additions & 36 deletions webviz_subsurface/plugins/_swatinit_qc/_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,13 @@
import webviz_core_components as wcc
from webviz_config import WebvizPluginABC, WebvizSettings

from ._business_logic import SwatinitQcDataModel
from ._callbacks import plugin_callbacks
from ._layout import plugin_main_layout
from ._error import error
from ._plugin_ids import PlugInIDs
from ._swatint import SwatinitQcDataModel
from .views import OverviewTabLayout, TabMaxPcInfoLayout, TabQqPlotLayout


class SwatinitQC(WebvizPluginABC):
"""This plugin is used to visualize the output from [check_swatinit]\
(https://fmu-docs.equinor.com/docs/subscript/scripts/check_swatinit.html) which is a QC tool
for Water Initialization in Eclipse runs when the `SWATINIT` keyword has been used. It is used to
quantify how much the volume changes from `SWATINIT` to `SWAT` at time zero in the dynamical model,
and help understand why it changes.

---
* **`csvfile`:** Path to an csvfile from check_swatinit. The path should be relative to the runpath
if ensemble and realization is given as input, if not the path needs to be absolute.
* **`ensemble`:** Which ensemble in `shared_settings` to visualize.
* **`realization`:** Which realization to pick from the ensemble
* **`faultlines`**: A csv file containing faultpolygons to be visualized together with the map view.
Export format from [xtgeo.xyz.polygons.dataframe](
https://xtgeo.readthedocs.io/en/latest/apiref/xtgeo.xyz.polygons.html#xtgeo.xyz.polygons.Polygons.dataframe
) \
[(example file)](\
https://github.com/equinor/webviz-subsurface-testdata/blob/master/01_drogon_ahm/\
realization-0/iter-0/share/results/polygons/toptherys--gl_faultlines_extract_postprocess.csv).

---
The `csvfile` can be generated by running the [CHECK_SWATINIT](https://fmu-docs.equinor.com/\
docs/ert/reference/forward_models.html?highlight=swatinit#CHECK_SWATINIT) forward model in ERT,
or with the "check_swatinit" command line tool.

"""

def __init__(
self,
webviz_settings: WebvizSettings,
Expand All @@ -44,7 +19,7 @@ def __init__(
realization: Optional[int] = None,
faultlines: Path = None,
) -> None:
super().__init__()
super().__init__(stretch=True)

self._datamodel = SwatinitQcDataModel(
webviz_settings=webviz_settings,
Expand All @@ -53,15 +28,65 @@ def __init__(
realization=realization,
faultlines=faultlines,
)
self.add_webvizstore()
self.set_callbacks()
self.error_message = ""

# Stores used in Overview tab
self.add_store(
PlugInIDs.Stores.Overview.BUTTON, WebvizPluginABC.StorageType.SESSION
)

# Stores used in Water tab
self.add_store(
PlugInIDs.Stores.Water.QC_VIZ, WebvizPluginABC.StorageType.SESSION
)
self.add_store(
PlugInIDs.Stores.Water.EQLNUM, WebvizPluginABC.StorageType.SESSION
)
self.add_store(
PlugInIDs.Stores.Water.COLOR_BY, WebvizPluginABC.StorageType.SESSION
)
self.add_store(
PlugInIDs.Stores.Water.MAX_POINTS, WebvizPluginABC.StorageType.SESSION
)
self.add_store(
PlugInIDs.Stores.Water.QC_FLAG, WebvizPluginABC.StorageType.SESSION
)
self.add_store(
PlugInIDs.Stores.Water.SATNUM, WebvizPluginABC.StorageType.SESSION
)

# Stores used in Capilaty tab
self.add_store(
PlugInIDs.Stores.Capilary.SPLIT_TABLE_BY,
WebvizPluginABC.StorageType.SESSION,
)
self.add_store(
PlugInIDs.Stores.Capilary.MAX_PC_SCALE, WebvizPluginABC.StorageType.SESSION
)
self.add_store(
PlugInIDs.Stores.Capilary.EQLNUM, WebvizPluginABC.StorageType.SESSION
)

# Adding each tab as a view element
self.add_view(
OverviewTabLayout(self._datamodel),
PlugInIDs.SwatinitViews.OVERVIEW,
PlugInIDs.SwatinitViews.GROUP_NAME,
)
self.add_view(
TabQqPlotLayout(self._datamodel),
PlugInIDs.SwatinitViews.WATER,
PlugInIDs.SwatinitViews.GROUP_NAME,
)
self.add_view(
TabMaxPcInfoLayout(self._datamodel),
PlugInIDs.SwatinitViews.WATER,
PlugInIDs.SwatinitViews.GROUP_NAME,
)

@property
def layout(self) -> wcc.Tabs:
return plugin_main_layout(self.uuid, self._datamodel)

def set_callbacks(self) -> None:
plugin_callbacks(self.uuid, self._datamodel)
return error(self.error_message)

def add_webvizstore(self) -> List[Tuple[Callable, List[dict]]]:
return self._datamodel.webviz_store
53 changes: 53 additions & 0 deletions webviz_subsurface/plugins/_swatinit_qc/_plugin_ids.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# pylint: disable=too-few-public-methods
class PlugInIDs:
class Tabs:
QC_PLOTS = "Water Initializaion QC plots"
MAX_PC_SCALING = "Capillary pressure scaling"
OVERVIEW = "Overview and Information"

class Stores:
class Shared:
PICK_VIEW = "pick-view"

class Overview:
BUTTON = "button"

class Water:
QC_VIZ = "qc-viz"
EQLNUM = "eqlnum"
COLOR_BY = "color_by"
MAX_POINTS = "max-points"
QC_FLAG = "qc-flag"
SATNUM = "satnum"

class Capilary:
SPLIT_TABLE_BY = "split-table-by"
MAX_PC_SCALE = "max-pc-scale"
EQLNUM = "eqlnum"

class SharedSettings:
PICK_VIEW = "pick-view"
FILTERS = "filters"

class SettingsGroups:
WATER_SEELECTORS = "water-selectors"
WATER_FILTERS = "water-filters"
CAPILAR_SELECTORS = "capilar-selectors"
CAPILAR_FILTERS = "capilar-filters"

class QcFlags:
FINE_EQUIL = "FINE_EQUIL"
HC_BELOW_FWL = "HC_BELOW_FWL"
PC_SCALED = "PC_SCALED"
PPCWMAX = "PPCWMAX"
SWATINIT_1 = "SWATINIT_1"
SWL_TRUNC = "SWL_TRUNC"
UNKNOWN = "UNKNOWN"
WATER = "WATER"

class SwatinitViews:
GROUP_NAME = "swatinit-group"

OVERVIEW = "overview"
WATER = "water"
CAPILAR = "capilar"
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from ._capilar_layout import CapilarViewelement
from ._dash_table import DashTable
from ._fullscreen import FullScreen
from ._layout_style import LayoutStyle
from ._map_figure import MapFigure
from ._overview_layout import OverviewViewelement
from ._properties_vs_depth import PropertiesVsDepthSubplots
from ._water_layout import WaterViewelement
from ._waterfall_plot import WaterfallPlot
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
from cgi import print_arguments
from typing import List

import pandas as pd
import plotly.graph_objects as go
import webviz_core_components as wcc
from dash import dash_table, dcc
from dash.development.base_component import Component
from webviz_config.webviz_plugin_subclasses import ViewElementABC

from .._swatint import SwatinitQcDataModel
from ..views.capilar_tab.settings import CapilarFilters
from ._dash_table import DashTable
from ._fullscreen import FullScreen
from ._layout_style import LayoutStyle
from ._map_figure import MapFigure

# This can be moved into a view_elements folder in views > capilar_tab


class CapilarViewelement(ViewElementABC):
"""All elements visible in the 'Caplillary pressure scaling'-tab
gathered in one viewelement"""

class IDs:
# pylint: disable=too-few-public-methods
INFO_TEXT = "info-text"
MAP = "map"
TABLE = "table"

def __init__(
self,
datamodel: SwatinitQcDataModel,
) -> None:
super().__init__()
self.datamodel = datamodel

self.init_eqlnums = self.datamodel.eqlnums[:1]
continous_filters = (
[self.datamodel.dframe[col].min(), self.datamodel.dframe[col].max()]
for col in self.datamodel.filters_continuous
)

continous_filters_ids = (
[
{
"id": CapilarFilters.IDs.RANGE_FILTERS,
"col": col,
}
]
for col in self.datamodel.filters_continuous
)

print({"EQLNUM": self.init_eqlnums})
self.dframe = self.datamodel.get_dataframe(
filters={"EQLNUM": self.init_eqlnums},
range_filters=zip_filters(continous_filters, continous_filters_ids),
)

df_for_map = datamodel.resample_dataframe(self.dframe, max_points=10000)
self.selectors = self.datamodel.SELECTORS

self.map_figure = MapFigure(
dframe=df_for_map,
color_by="EQLNUM",
faultlinedf=datamodel.faultlines_df,
colormap=datamodel.create_colormap("EQLNUM"),
).figure

def inner_layout(self) -> List[Component]:
return [
wcc.Header("Maximum capillary pressure scaling", style=LayoutStyle.HEADER),
wcc.FlexBox(
style={"margin-top": "10px", "height": "40vh"},
children=[
wcc.FlexColumn(
dcc.Markdown(pc_columns_description()),
id=self.register_component_unique_id(
CapilarViewelement.IDs.INFO_TEXT
),
flex=7,
style={"margin-right": "40px"},
),
wcc.FlexColumn(
FullScreen(
wcc.Graph(
style={"height": "100%", "min-height": "35vh"},
figure=self.map_figure,
id=self.register_component_unique_id(
CapilarViewelement.IDs.MAP
),
)
),
flex=3,
),
],
),
self.max_pc_table,
]

@property
def max_pc_table(self) -> dash_table:
return DashTable(
id=self.register_component_unique_id(CapilarViewelement.IDs.TABLE),
data=self.dframe.to_dict("records"),
columns=[
{
"name": i,
"id": i,
"type": "numeric" if i not in self.selectors else "text",
"format": {"specifier": ".4~r"} if i not in self.selectors else {},
}
for i in self.dframe.columns
],
height="48vh",
sort_action="native",
fixed_rows={"headers": True},
style_cell={
"minWidth": LayoutStyle.TABLE_CELL_WIDTH,
"maxWidth": LayoutStyle.TABLE_CELL_WIDTH,
"width": LayoutStyle.TABLE_CELL_WIDTH,
},
style_data_conditional=[
{
"if": {
"filter_query": f"{{{self.datamodel.COLNAME_THRESHOLD}}} > 0",
},
**LayoutStyle.TABLE_HIGHLIGHT,
},
],
)


# pylint: disable=anomalous-backslash-in-string
def pc_columns_description() -> str:
return f"""
> **Column descriptions**
> - **PCOW_MAX** - Maximum capillary pressure from the input SWOF/SWFN tables
> - **PC_SCALING** - Maximum capillary pressure scaling applied
> - **PPCW** - Maximum capillary pressure after scaling
> - **{SwatinitQcDataModel.COLNAME_THRESHOLD}** - Column showing how many percent of the pc-scaled dataset that match the user-selected threshold
*PPCW = PCOW_MAX \* PC_SCALING*
A threshold for the maximum capillary scaling can be set in the menu.
The table will show how many percent of the dataset that exceeds this value, and cells above the threshold will be shown in the map ➡️
"""


def zip_filters(filter_values: list, filter_ids: list) -> dict:
for values, id_val in zip(filter_values, filter_ids):
print("val: ", values)
print("id: ", id_val)
return {id_val["col"]: values for values, id_val in zip(filter_values, filter_ids)}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from typing import Any, List

from dash import dash_table

from ._layout_style import LayoutStyle


class DashTable(dash_table.DataTable):
def __init__(
self, data: List[dict], columns: List[dict], height: str = "none", **kwargs: Any
) -> None:
super().__init__(
data=data,
columns=columns,
style_table={"height": height, **LayoutStyle.TABLE_STYLE},
style_as_list_view=True,
css=LayoutStyle.TABLE_CSS,
style_header=LayoutStyle.TABLE_HEADER,
**kwargs,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from typing import Any, List

import webviz_core_components as wcc


class FullScreen(wcc.WebvizPluginPlaceholder):
def __init__(self, children: List[Any]) -> None:
super().__init__(buttons=["expand"], children=children)
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class LayoutStyle:
MAIN_HEIGHT = "87vh"
HEADER = {
"font-size": "15px",
"color": "black",
"text-transform": "uppercase",
"border-color": "black",
}
TABLE_HEADER = {"fontWeight": "bold"}
TABLE_STYLE = {"max-height": MAIN_HEIGHT, "overflowY": "auto"}
TABLE_CELL_WIDTH = 95
TABLE_CELL_HEIGHT = "10px"
TABLE_HIGHLIGHT = {"backgroundColor": "rgb(230, 230, 230)", "fontWeight": "bold"}
TABLE_CSS = [
{
"selector": ".dash-spreadsheet tr",
"rule": f"height: {TABLE_CELL_HEIGHT};",
},
]
Loading