Skip to content

Commit

Permalink
Changes to the WellAnalysis plugin (#1184)
Browse files Browse the repository at this point in the history
  • Loading branch information
lindjoha authored Dec 11, 2022
1 parent 6c55e12 commit c017009
Show file tree
Hide file tree
Showing 10 changed files with 408 additions and 272 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#1182](https://github.com/equinor/webviz-subsurface/pull/1182) - `ParameterResponseCorrelation` can now allow default None column_keys when using arrow file as input
- [#1122](https://github.com/equinor/webviz-subsurface/pull/1122) - `opm` and `ecl2df` are now optional, making `windows-subsurface` possible to install and import on non-unix based systems. **NOTE:** a lot of the functionality in `webviz-subsurface` is built on `opm` and `ecl2df`, and issues are therefore expected on eg Windows and macOS. Use with care.
- [#1146](https://github.com/equinor/webviz-subsurface/pull/1146) - Converted the `BhpQc` plugin to WLF (Webviz Layout Framework).
- [#1184](https://github.com/equinor/webviz-subsurface/pull/1184) - `WellAnalysis`: changes to the settings layout in the `WellOverview` view

### Added
- [#1162](https://github.com/equinor/webviz-subsurface/pull/1162) - `RelativePermeability` can now be initialized with only `pyscal` formatted input files instead of paths to ensembles using a new `scal_scenarios` input option.
- [#1176](https://github.com/equinor/webviz-subsurface/pull/1176) - `MapViewerFMU` now has an option to download visualized surfaces.
- [#1130](https://github.com/equinor/webviz-subsurface/pull/1130) - New plugin `VfpAnalysis` to vizualise and analyse VFP curves.
- [#1184](https://github.com/equinor/webviz-subsurface/pull/1184) - New option `Prod Until Date` in `WellAnalysis`.

### Fixed
- [#1171](https://github.com/equinor/webviz-subsurface/pull/1171) - Fixed bug in `WellAnalysis` that caused an error if the selected date did not not exist in some selected ensembles.
Expand Down
17 changes: 14 additions & 3 deletions webviz_subsurface/plugins/_well_analysis/_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,21 @@ def tour_steps(self) -> List[dict]:
},
{
"id": self.view(self.Ids.WELL_OVERVIEW)
.settings_group(WellOverviewView.Ids.SETTINGS)
.settings_group(WellOverviewView.Ids.CHART_TYPE)
.get_unique_id(),
"content": "Choose chart type, ensemble, type of production and other options. "
"You also change the layout of the chart.",
"content": "Choose chart type: bar, pie or area chart. ",
},
{
"id": self.view(self.Ids.WELL_OVERVIEW)
.settings_group(WellOverviewView.Ids.SELECTIONS)
.get_unique_id(),
"content": "Choose ensembles, type of production and other options.",
},
{
"id": self.view(self.Ids.WELL_OVERVIEW)
.settings_group(WellOverviewView.Ids.LAYOUT_OPTIONS)
.get_unique_id(),
"content": "Chart layout options.",
},
{
"id": self.view(self.Ids.WELL_OVERVIEW)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,28 +95,45 @@ def filter_on_well_attributes(
return filtered_wells

def get_summary_data(
self, well_sumvec: str, prod_after_date: Union[datetime.datetime, None]
self,
well_sumvec: str,
prod_from_date: Union[datetime.datetime, None],
prod_until_date: Union[datetime.datetime, None],
) -> pd.DataFrame:
"""Returns all summary data matching the well_sumvec. If the prod_after_date
"""Returns all summary data matching the well_sumvec. If the prod_from_date
is not None it will return all dates after that date and subtract the cumulative
production at that date.
production at that date. If prod_until_dates is not None it will filter out all
dates after that date.
"""
sumvecs = [f"{well_sumvec}:{well}" for well in self._wells]
df = self._smry[["REAL", "DATE"] + sumvecs]
max_date = df["DATE"].max()
min_date = df["DATE"].min()

if prod_from_date is not None:
df = df[df["DATE"] >= prod_from_date]

# If the prod_from_date exists in the ensemble, subtract the
# production at that date from all dates.
if min_date <= prod_from_date <= max_date:
df_date = df[df["DATE"] == prod_from_date].copy()
df_merged = df.merge(df_date, on=["REAL"], how="inner")
for vec in sumvecs:
df_merged[vec] = df_merged[f"{vec}_x"] - df_merged[f"{vec}_y"]
df = df_merged[["REAL", "DATE_x"] + sumvecs].rename(
{"DATE_x": "DATE"}, axis=1
)

if prod_until_date is not None:
df = df[df["DATE"] <= prod_until_date]

if prod_after_date is not None:
df = df[df["DATE"] >= prod_after_date]
df_date = df[df["DATE"] == df["DATE"].min()].copy()
df_merged = df.merge(df_date, on=["REAL"], how="inner")
for vec in sumvecs:
df_merged[vec] = df_merged[f"{vec}_x"] - df_merged[f"{vec}_y"]
df = df_merged[["REAL", "DATE_x"] + sumvecs].rename(
{"DATE_x": "DATE"}, axis=1
)
return df

def get_dataframe_melted(
self, well_sumvec: str, prod_after_date: Union[datetime.datetime, None]
self,
well_sumvec: str,
prod_from_date: Union[datetime.datetime, None],
prod_until_date: Union[datetime.datetime, None],
) -> pd.DataFrame:
"""Returns a dataframe on long form consisting of these columns:
* WELL
Expand All @@ -126,19 +143,26 @@ def get_dataframe_melted(
sumvecs = [f"{well_sumvec}:{well}" for well in self._wells]
df = self._smry[["REAL", "DATE"] + sumvecs]
max_date = df["DATE"].max()
df = df[df["DATE"] == max_date]

if prod_after_date is not None:
# Since the prod_after_dates can be chosen from the union of the date
# ranges from many ensembles, it can happen that the selecte date is
# > than the max_date for this ensemble. In that case we set it equal
# to the max date. The resulting sumvec value will then be zero for
# all wells.
prod_after_date = (
max_date if prod_after_date > max_date else prod_after_date
)
min_date = df["DATE"].min()

if prod_until_date is None:
prod_until_date = max_date
else:
# Set prod_until_date to min_date or max_date if it is outside the
# ensemble date range
prod_until_date = max(min(prod_until_date, max_date), min_date)

df = df[df["DATE"] == prod_until_date]

# If prod_from_date is None, do nothing
if prod_from_date is not None:
# Set prod_from_date to min_date or max_date if it is outside the
# ensemble date range
prod_from_date = max(min(prod_from_date, max_date), min_date)

# Subtract the production at the prod_from_date
df_date = self._smry[["REAL", "DATE"] + sumvecs]
df_date = df_date[df_date["DATE"] >= prod_after_date]
df_date = df_date[df_date["DATE"] >= prod_from_date]
df_date = df_date[df_date["DATE"] == df_date["DATE"].min()]
df_merged = df.merge(df_date, on=["REAL"], how="inner")
for vec in sumvecs:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from ._chart_type import WellOverviewChartType
from ._filters import WellOverviewFilters
from ._layout_options import WellOverviewLayoutOptions
from ._selections import WellOverviewSelections
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from typing import List

import webviz_core_components as wcc
from dash.development.base_component import Component
from webviz_config.utils import StrEnum
from webviz_config.webviz_plugin_subclasses import SettingsGroupABC

from ...._types import ChartType


class WellOverviewChartType(SettingsGroupABC):
class Ids(StrEnum):
CHARTTYPE = "charttype"

def __init__(self) -> None:
super().__init__("Chart Type")

def layout(self) -> List[Component]:
return [
wcc.RadioItems(
id=self.register_component_unique_id(self.Ids.CHARTTYPE),
options=[
{"label": "Bar chart", "value": ChartType.BAR},
{"label": "Pie chart", "value": ChartType.PIE},
{"label": "Stacked area chart", "value": ChartType.AREA},
],
value=ChartType.BAR,
vertical=True,
)
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from typing import List

import webviz_core_components as wcc
from dash.development.base_component import Component
from webviz_config.utils import StrEnum
from webviz_config.webviz_plugin_subclasses import SettingsGroupABC


class WellOverviewFilters(SettingsGroupABC):
class Ids(StrEnum):
SELECTED_WELLS = "selected-wells"

def __init__(self, wells: List[str]) -> None:
super().__init__("Filters")
self._wells = wells

def layout(self) -> List[Component]:
return [
wcc.SelectWithLabel(
label="Well",
size=min(10, len(self._wells)),
id=self.register_component_unique_id(
WellOverviewFilters.Ids.SELECTED_WELLS
),
options=[{"label": well, "value": well} for well in self._wells],
value=self._wells,
multi=True,
)
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from typing import List

import webviz_core_components as wcc
from dash import html
from dash.development.base_component import Component
from webviz_config.utils import StrEnum
from webviz_config.webviz_plugin_subclasses import SettingsGroupABC

from ...._types import ChartType


class WellOverviewLayoutOptions(SettingsGroupABC):
class Ids(StrEnum):
CHARTTYPE_SETTINGS = "charttype-settings"
CHARTTYPE_CHECKLIST = "charttype-checklist"

def __init__(self) -> None:
super().__init__("Layout Options")

def layout(self) -> List[Component]:
settings_id = self.register_component_unique_id(self.Ids.CHARTTYPE_SETTINGS)
checklist_id = self.register_component_unique_id(self.Ids.CHARTTYPE_CHECKLIST)
return [
html.Div(
children=[
html.Div(
id={"id": settings_id, "charttype": ChartType.BAR},
children=wcc.Checklist(
id={"id": checklist_id, "charttype": ChartType.BAR},
options=[
{"label": "Show legend", "value": "legend"},
{"label": "Overlay bars", "value": "overlay_bars"},
{
"label": "Show prod as text",
"value": "show_prod_text",
},
{
"label": "White background",
"value": "white_background",
},
],
value=["legend"],
),
),
html.Div(
id={"id": settings_id, "charttype": ChartType.PIE},
children=wcc.Checklist(
id={"id": checklist_id, "charttype": ChartType.PIE},
options=[
{"label": "Show legend", "value": "legend"},
{
"label": "Show prod as text",
"value": "show_prod_text",
},
],
value=[],
),
),
html.Div(
id={"id": settings_id, "charttype": ChartType.AREA},
children=wcc.Checklist(
id={"id": checklist_id, "charttype": ChartType.AREA},
options=[
{"label": "Show legend", "value": "legend"},
{
"label": "White background",
"value": "white_background",
},
],
value=["legend"],
),
),
],
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import datetime
from typing import List

import webviz_core_components as wcc
from dash.development.base_component import Component
from webviz_config.utils import StrEnum
from webviz_config.webviz_plugin_subclasses import SettingsGroupABC


class WellOverviewSelections(SettingsGroupABC):
class Ids(StrEnum):
ENSEMBLES = "ensembles"
RESPONSE = "response"
PROD_FROM_DATE = "prod-from-date"
PROD_UNTIL_DATE = "prod-until-date"

def __init__(self, ensembles: List[str], dates: List[datetime.datetime]) -> None:
super().__init__("Selections")

self._ensembles = ensembles
self._dates = dates

def layout(self) -> List[Component]:
return [
wcc.Dropdown(
label="Ensembles",
id=self.register_component_unique_id(self.Ids.ENSEMBLES),
options=[{"label": col, "value": col} for col in self._ensembles],
value=self._ensembles,
multi=True,
),
wcc.Dropdown(
label="Response",
id=self.register_component_unique_id(self.Ids.RESPONSE),
options=[
{"label": "Oil production", "value": "WOPT"},
{"label": "Gas production", "value": "WGPT"},
{"label": "Water production", "value": "WWPT"},
],
value="WOPT",
multi=False,
clearable=False,
),
wcc.Dropdown(
label="Production From Date",
id=self.register_component_unique_id(self.Ids.PROD_FROM_DATE),
options=[
{
"label": dte.strftime("%Y-%m-%d"),
"value": dte.strftime("%Y-%m-%d"),
}
for dte in self._dates
],
multi=False,
),
wcc.Dropdown(
label="Production Until Date",
id=self.register_component_unique_id(self.Ids.PROD_UNTIL_DATE),
options=[
{
"label": dte.strftime("%Y-%m-%d"),
"value": dte.strftime("%Y-%m-%d"),
}
for dte in self._dates
],
multi=False,
),
]
Loading

0 comments on commit c017009

Please sign in to comment.