From 82d7029b6f6e93a924d79ce223ccd765cf4ad94c Mon Sep 17 00:00:00 2001 From: "Jincheng Liu (OG SUB RPE)" Date: Fri, 8 Jul 2022 13:39:40 +0200 Subject: [PATCH 01/15] build new running time analysis fmu plugin --- .../_running_time_analysis_fmu/__init__.py | 0 .../_plugin.py} | 355 ++---------------- .../_running_time_analysis_fmu/_plugin_ids.py | 0 3 files changed, 35 insertions(+), 320 deletions(-) create mode 100644 webviz_subsurface/plugins/_running_time_analysis_fmu/__init__.py rename webviz_subsurface/plugins/{_running_time_analysis_fmu.py => _running_time_analysis_fmu/_plugin.py} (50%) create mode 100644 webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin_ids.py diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/__init__.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py similarity index 50% rename from webviz_subsurface/plugins/_running_time_analysis_fmu.py rename to webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py index 1581e9260..53e924b8c 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py @@ -3,71 +3,57 @@ import numpy as np import pandas as pd -import webviz_core_components as wcc -from dash import Dash, Input, Output, html from webviz_config import WebvizPluginABC, WebvizSettings from webviz_config.common_cache import CACHE from webviz_config.webviz_store import webvizstore -from .._datainput.fmu_input import load_ensemble_set, load_parameters +from ..._datainput.fmu_input import load_ensemble_set, load_parameters class RunningTimeAnalysisFMU(WebvizPluginABC): """Can e.g. be used to investigate which jobs that are important for the running -time of realizations, and if specific parameter combinations increase running time or chance of -realization failure. Systematic realization failure could introduce bias to assisted history -matching. - -Visualizations: -* Running time matrix, a heatmap of job running times relative to: - * Same job in ensemble - * Slowest job in ensemble - * Slowest job in realization -* Parameter parallel coordinates plot: - * Analyze running time and successful/failed run together with input parameters. - ---- - -* **`ensembles`:** Which ensembles in `shared_settings` to include in check. Only required input. -* **`filter_shorter`:** Filters jobs with maximum run time in ensemble less than X seconds \ - (default: 10). Can be checked on/off interactively, this only sets the filtering value. -* **`status_file`:** Name of json file local per realization with job status \ - (default: `status.json`). -* **`visual_parameters`:** List of default visualized parameteres in parallel coordinates plot \ - (default: all parameters). - ---- - -Parameters are picked up automatically from `parameters.txt` in individual realizations in -defined ensembles using `fmu-ensemble`. - -The `status.json` file is the standard status file when running -[`ERT`](https://github.com/Equinor/ert) runs. If defining a different name, it still has to be -on the same format [(example file)](https://github.com/equinor/webviz-subsurface-testdata/\ -blob/master/reek_history_match/realization-0/iter-0/status.json). -""" - - COLOR_MATRIX_BY_LABELS = [ - "Same job in ensemble", - "Slowest job in realization", - "Slowest job in ensemble", - ] - - COLOR_PARCOORD_BY_LABELS = [ - "Successful/failed realization", - "Running time of realization", - ] + time of realizations, and if specific parameter combinations increase running time or chance of + realization failure. Systematic realization failure could introduce bias to assisted history + matching. + + Visualizations: + * Running time matrix, a heatmap of job running times relative to: + * Same job in ensemble + * Slowest job in ensemble + * Slowest job in realization + * Parameter parallel coordinates plot: + * Analyze running time and successful/failed run together with input parameters. + + --- + + * **`ensembles`:** Which ensembles in `shared_settings` to include in check. Only required input. + * **`filter_shorter`:** Filters jobs with maximum run time in ensemble less than X seconds \ + (default: 10). Can be checked on/off interactively, this only sets the filtering value. + * **`status_file`:** Name of json file local per realization with job status \ + (default: `status.json`). + * **`visual_parameters`:** List of default visualized parameteres in parallel coordinates plot \ + (default: all parameters). + + --- + + Parameters are picked up automatically from `parameters.txt` in individual realizations in + defined ensembles using `fmu-ensemble`. + + The `status.json` file is the standard status file when running + [`ERT`](https://github.com/Equinor/ert) runs. If defining a different name, it still has to be + on the same format [(example file)](https://github.com/equinor/webviz-subsurface-testdata/\ + blob/master/reek_history_match/realization-0/iter-0/status.json). + """ def __init__( self, - app: Dash, webviz_settings: WebvizSettings, ensembles: list, filter_shorter: Union[int, float] = 10, status_file: str = "status.json", visual_parameters: Optional[list] = None, - ): - super().__init__() + ) -> None: + super().__init__(stretch=True) self.filter_shorter = filter_shorter self.ens_paths = { ens: webviz_settings.shared_settings["scratch_ensembles"][ens] @@ -89,26 +75,6 @@ def __init__( self.visual_parameters = ( visual_parameters if visual_parameters else self.parameters ) - self.set_callbacks(app) - - @property - def tour_steps(self) -> List[dict]: - return [ - { - "id": self.uuid("mode"), - "content": ( - "Switch between job running time matrix and parameter parallel coordinates." - ), - }, - { - "id": self.uuid("ensemble"), - "content": ("Display the realizations from the selected ensemble. "), - }, - { - "id": self.uuid("relative_runtime"), - "content": ("Make the colorscale relative to the selected option."), - }, - ] @property def parameters(self) -> List[str]: @@ -120,257 +86,6 @@ def parameters(self) -> List[str]: .columns ) - @property - def plot_fig(self) -> html.Div: - return html.Div( - style={ - "overflowX": "hidden", - "width": "100%", - }, - children=[ - html.Div( - id=self.uuid("plot_fig"), - style={"overflowX": "auto", "width": "100%"}, - children=wcc.Graph(id=self.uuid("fig")), - ), - # Blank div: Makes sure that horizontal scrollbar is moved straight under the - # figure instead of the figure div getting padded by whitespace down to height of - # outer div. - html.Div(style={"width": "100%"}), - ], - ) - - @property - def control_div(self) -> html.Div: - return html.Div( - children=[ - wcc.Selectors( - label="Mode", - children=[ - wcc.RadioItems( - id=self.uuid("mode"), - options=[ - { - "label": "Running time matrix", - "value": "running_time_matrix", - }, - { - "label": "Parameter parallel coordinates", - "value": "parallel_coordinates", - }, - ], - value="running_time_matrix", - ), - ], - ), - wcc.Selectors( - label="Ensemble", - children=[ - wcc.Dropdown( - id=self.uuid("ensemble"), - options=[ - {"label": ens, "value": ens} for ens in self.ensembles - ], - value=self.ensembles[0], - clearable=False, - ), - ], - ), - wcc.Selectors( - label="Coloring", - children=[ - html.Div( - id=self.uuid("matrix_color"), - children=[ - wcc.Dropdown( - label="Color jobs relative to running time of:", - id=self.uuid("relative_runtime"), - options=[ - {"label": rel, "value": rel} - for rel in RunningTimeAnalysisFMU.COLOR_MATRIX_BY_LABELS - ], - value=RunningTimeAnalysisFMU.COLOR_MATRIX_BY_LABELS[ - 0 - ], - clearable=False, - ), - ], - ), - html.Div( - id=self.uuid("parcoords_color"), - style={"display": "none"}, - children=[ - wcc.Dropdown( - label="Color realizations relative to:", - id=self.uuid("relative_real"), - options=[ - {"label": rel, "value": rel} - for rel in RunningTimeAnalysisFMU.COLOR_PARCOORD_BY_LABELS - ], - value=RunningTimeAnalysisFMU.COLOR_PARCOORD_BY_LABELS[ - 0 - ], - clearable=False, - ), - ], - ), - ], - ), - wcc.Selectors( - label="Filtering", - children=[ - html.Div( - id=self.uuid("parameter_dropdown"), - style={"display": "none"}, - children=[ - wcc.SelectWithLabel( - id=self.uuid("parameters"), - style={"overflowX": "auto", "fontSize": "0.97rem"}, - options=[ - {"label": param, "value": param} - for param in self.parameters - ], - multi=True, - value=self.visual_parameters, - size=min(50, len(self.visual_parameters)), - ), - ], - ), - html.Div( - id=self.uuid("filter_short_checkbox"), - children=[ - wcc.Checklist( - label="Filter jobs", - id=self.uuid("filter_short"), - options=[ - { - "label": "Slowest in ensemble less than " - f"{self.filter_shorter}s", - "value": "filter_short", - }, - ], - value=["filter_short"], - ), - ], - ), - ], - ), - ] - ) - - @property - def layout(self) -> wcc.FlexBox: - return wcc.FlexBox( - children=[ - wcc.Frame( - style={"flex": "1", "height": "90vh"}, children=self.control_div - ), - wcc.Frame( - color="white", - highlight=False, - style={"flex": "6", "height": "90vh"}, - children=self.plot_fig, - ), - ], - ) - - def set_callbacks(self, app: Dash) -> None: - @app.callback( - Output(self.uuid("fig"), "figure"), - [ - Input(self.uuid("ensemble"), "value"), - Input(self.uuid("mode"), "value"), - Input(self.uuid("relative_runtime"), "value"), - Input(self.uuid("relative_real"), "value"), - Input(self.uuid("parameters"), "value"), - Input(self.uuid("filter_short"), "value"), - ], - ) - def _update_fig( - ens: str, - mode: str, - rel_runtime: str, - rel_real: str, - params: Union[str, List[str]], - filter_short: List[str], - ) -> dict: - """Update main figure - Dependent on `mode` it will call rendering of the chosen form of visualization - """ - if mode == "running_time_matrix" and "filter_short" in filter_short: - return render_matrix( - self.job_status_df[ - (self.job_status_df["ENSEMBLE"] == ens) - & (self.job_status_df["JOB_MAX_RUNTIME"] >= self.filter_shorter) - ], - rel_runtime, - self.plotly_theme, - ) - if mode == "running_time_matrix": - return render_matrix( - self.job_status_df[(self.job_status_df["ENSEMBLE"] == ens)], - rel_runtime, - self.plotly_theme, - ) - - # Otherwise: parallel coordinates - # Ensure selected parameters is a list - params = params if isinstance(params, list) else [params] - # Color by success or runtime, for runtime drop unsuccesful - colormap_labels: Union[List[str], None] - if rel_real == "Successful/failed realization": - plot_df = self.real_status_df[self.real_status_df["ENSEMBLE"] == ens] - colormap = make_colormap( - self.plotly_theme["layout"]["colorway"], discrete=2 - ) - color_by_col = "STATUS_BOOL" - colormap_labels = ["Failed", "Success"] - else: - plot_df = self.real_status_df[ - (self.real_status_df["ENSEMBLE"] == ens) - & (self.real_status_df["STATUS_BOOL"] == 1) - ] - colormap = self.plotly_theme["layout"]["colorscale"]["sequential"] - color_by_col = "RUNTIME" - colormap_labels = None - - # Call rendering of parallel coordinate plot - return render_parcoord( - plot_df, - params, - self.plotly_theme, - colormap, - color_by_col, - colormap_labels, - ) - - @app.callback( - [ - Output(self.uuid("matrix_color"), "style"), - Output(self.uuid("parcoords_color"), "style"), - Output(self.uuid("parameter_dropdown"), "style"), - Output(self.uuid("filter_short_checkbox"), "style"), - ], - [Input(self.uuid("mode"), "value")], - ) - def _update_mode(mode: str) -> Tuple[dict, dict, dict, dict]: - """Switch displayed mode between running time matrix and parallel coordinates""" - if mode == "running_time_matrix": - style = ( - {"display": "block"}, - {"display": "none"}, - {"display": "none"}, - {"display": "block"}, - ) - else: - style = ( - {"display": "none"}, - {"display": "block"}, - {"display": "block"}, - {"display": "none"}, - ) - return style - def add_webvizstore(self) -> List[Tuple[Callable, list]]: return [ ( diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin_ids.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin_ids.py new file mode 100644 index 000000000..e69de29bb From a8ea43fc9a7c2867cd0cef37ef40b69477e046d1 Mon Sep 17 00:00:00 2001 From: "Jincheng Liu (OG SUB RPE)" Date: Mon, 11 Jul 2022 12:44:07 +0200 Subject: [PATCH 02/15] first plugin --- .../_running_time_analysis_fmu/__init__.py | 1 + .../_running_time_analysis_fmu/_plugin.py | 198 ++++--------- .../_running_time_analysis_fmu/_plugin_ids.py | 19 ++ .../_shared_settings.py | 186 ++++++++++++ .../_running_time_analysis_fmu/_view.py | 266 ++++++++++++++++++ 5 files changed, 520 insertions(+), 150 deletions(-) create mode 100644 webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py create mode 100644 webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/__init__.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/__init__.py index e69de29bb..3c7fe1ca7 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/__init__.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/__init__.py @@ -0,0 +1 @@ +from ._plugin import RunningTimeAnalysisFMU \ No newline at end of file diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py index 53e924b8c..da8dcdd44 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py @@ -1,13 +1,18 @@ import json -from typing import Callable, List, Optional, Tuple, Union +from typing import Callable, List, Optional, Tuple, Type, Union import numpy as np import pandas as pd +from dash import html, Input, Output, callback +from dash.development.base_component import Component from webviz_config import WebvizPluginABC, WebvizSettings from webviz_config.common_cache import CACHE from webviz_config.webviz_store import webvizstore from ..._datainput.fmu_input import load_ensemble_set, load_parameters +from ._plugin_ids import PluginIds +from ._shared_settings import RunningTimeAnalysisFmuSettings +from ._view import RunTimeAnalysisGraph class RunningTimeAnalysisFMU(WebvizPluginABC): @@ -59,6 +64,11 @@ def __init__( ens: webviz_settings.shared_settings["scratch_ensembles"][ens] for ens in ensembles } + + print("plugin ID is", self._plugin_unique_id.to_string()) + + print("active ID is",self._active_view_id) + self.plotly_theme = webviz_settings.theme.plotly_theme self.ensembles = ensembles self.status_file = status_file @@ -75,6 +85,43 @@ def __init__( self.visual_parameters = ( visual_parameters if visual_parameters else self.parameters ) + self.plugin_parameters = self.parameters + + self.add_store(PluginIds.Stores.MODE, WebvizPluginABC.StorageType.SESSION) + self.add_store(PluginIds.Stores.ENSEMBLE, WebvizPluginABC.StorageType.SESSION) + self.add_store(PluginIds.Stores.COLORING, WebvizPluginABC.StorageType.SESSION) + self.add_store(PluginIds.Stores.FILTERING_SHORT, WebvizPluginABC.StorageType.SESSION) + self.add_store(PluginIds.Stores.FILTERING_PARAMS, WebvizPluginABC.StorageType.SESSION) + self.add_store(PluginIds.Stores.ACTIVE_ID, WebvizPluginABC.StorageType.SESSION) + + + self.add_shared_settings_group( + RunningTimeAnalysisFmuSettings(self.ensembles, self.visual_parameters,self.plugin_parameters, self.filter_shorter), + PluginIds.SharedSettings.SHARED_SETTINGS_GROUP, + ) + + self.add_view( + RunTimeAnalysisGraph(self.plotly_theme, self.job_status_df, self.real_status_df, self.filter_shorter), + PluginIds.RunTimeAnalysisView.RUN_TIME_FMU, + PluginIds.RunTimeAnalysisView.GROUP_NAME + ) + + + def _set_callbacks(self) -> None: + @callback( + Output(self.get_store_unique_id(PluginIds.Stores.ACTIVE_ID), 'data'), + Input(self._plugin_unique_id.to_string(), 'active_view_id') + ) + def update_store(value:str) -> str: + print(value) + return value + + + + + @property + def layout(self) -> Type[Component]: + return html.Div() @property def parameters(self) -> List[str]: @@ -109,138 +156,6 @@ def add_webvizstore(self) -> List[Tuple[Callable, list]]: ] -@CACHE.memoize(timeout=CACHE.TIMEOUT) -def render_matrix(status_df: pd.DataFrame, rel: str, theme: dict) -> dict: - """Render matrix - Returns figure object as heatmap for the chosen ensemble and scaling method. - """ - if rel == "Same job in ensemble": - z = list(status_df["JOB_SCALED_RUNTIME"]) - elif rel == "Slowest job in realization": - z = list(status_df["REAL_SCALED_RUNTIME"]) - else: - z = list(status_df["ENS_SCALED_RUNTIME"]) - data = { - "type": "heatmap", - "x": list(status_df["REAL"]), - "y": list(status_df["JOB_ID"]), - "z": z, - "zmin": 0, - "zmax": 1, - "text": list(status_df["HOVERINFO"]), - "hoverinfo": "text", - "colorscale": theme["layout"]["colorscale"]["sequential"], - "colorbar": { - "tickvals": [ - 0, - 0.5, - 1, - ], - "ticktext": [ - "0 %", - "50 %", - "100 %", - ], - "xanchor": "left", - }, - } - layout = {} - layout.update(theme["layout"]) - layout.update( - { - "paper_bgcolor": "rgba(0,0,0,0)", - "plot_bgcolor": "rgba(0,0,0,0)", - "margin": { - "t": 50, - "b": 50, - "l": 50, - }, - "xaxis": { - "ticks": "", - "title": "Realizations", - "showgrid": False, - "side": "top", - }, - "yaxis": { - "ticks": "", - "showticklabels": True, - "tickmode": "array", - "tickvals": list(status_df["JOB_ID"]), - "ticktext": list(status_df["JOB"]), - "showgrid": False, - "automargin": True, - "autorange": "reversed", - "type": "category", - }, - "height": max(350, len(status_df["JOB_ID"].unique()) * 15), - "width": max(400, len(status_df["REAL"].unique()) * 12 + 250), - } - ) - - return {"data": [data], "layout": layout} - - -@CACHE.memoize(timeout=CACHE.TIMEOUT) -def render_parcoord( - plot_df: pd.DataFrame, - params: List[str], - theme: dict, - colormap: Union[List[str], List[list]], - color_col: str, - colormap_labels: Union[List[str], None] = None, -) -> dict: - """Renders parallel coordinates plot""" - # Create parcoords dimensions (one per parameter) - dimensions = [ - {"label": param, "values": plot_df[param].values.tolist()} for param in params - ] - - # Parcoords data dict - data: dict = { - "line": { - "color": plot_df[color_col].values.tolist(), - "colorscale": colormap, - "showscale": True, - }, - "dimensions": dimensions, - "labelangle": -90, - "labelside": "bottom", - "type": "parcoords", - } - if color_col == "STATUS_BOOL": - data["line"].update( - { - "cmin": -0.5, - "cmax": 1.5, - "colorbar": { - "tickvals": [0, 1], - "ticktext": colormap_labels, - "title": "Status", - "xanchor": "right", - "x": -0.02, - "len": 0.3, - }, - }, - ) - else: - data["line"].update( - { - "colorbar": { - "title": "Running time", - "xanchor": "right", - "x": -0.02, - }, - }, - ) - - layout = {} - layout.update(theme["layout"]) - # Ensure sufficient spacing between each dimension and margin for labels - width = len(dimensions) * 100 + 250 - margin_b = max([len(param) for param in params]) * 8 - layout.update({"width": width, "height": 800, "margin": {"b": margin_b, "t": 30}}) - return {"data": [data], "layout": layout} - @CACHE.memoize(timeout=CACHE.TIMEOUT) @webvizstore @@ -402,20 +317,3 @@ def ensemble_post_processing() -> list: return pd.concat([job_status_df, real_status_df], keys=["job", "real"], sort=False) -@CACHE.memoize(timeout=CACHE.TIMEOUT) -def make_colormap(color_array: list, discrete: int = None) -> list: - """ - Returns a colormap: - * If the `discrete` variable is set to an integer x, the colormap will be a discrete map of - size x evenly sampled from the given color_array. - * If discrete not defined or `None`: assumes continuous colormap and returns the given - color_array. - """ - if discrete is None: - colormap = color_array - else: - colormap = [] - for i in range(0, discrete): - colormap.append([i / discrete, color_array[i]]) - colormap.append([(i + 1) / discrete, color_array[i]]) - return colormap diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin_ids.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin_ids.py index e69de29bb..f4550cdc6 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin_ids.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin_ids.py @@ -0,0 +1,19 @@ +class PluginIds: + + # pylint: disable=too-few-public-methods + class Stores: + MODE = "mode" + ENSEMBLE = "ensemble" + COLORING = "coloring" + FILTERING_SHORT = "filtering-short" + FILTERING_PARAMS = "filtering-params" + ACTIVE_ID = "active-view-id" + + # pylint: disable=too-few-public-methods + class SharedSettings: + SHARED_SETTINGS_GROUP = "shared-settings-group" + + # pylint: disable=too-few-public-methods + class RunTimeAnalysisView: + GROUP_NAME = "run-time-analysis-fmu" + RUN_TIME_FMU = "run-time-fmu" diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py new file mode 100644 index 000000000..adb4c7055 --- /dev/null +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py @@ -0,0 +1,186 @@ +from typing import List, Tuple, Union + +import webviz_core_components as wcc +from dash import Input, Output, callback +from dash.development.base_component import Component +from webviz_config.webviz_plugin_subclasses import SettingsGroupABC + +from ._plugin_ids import PluginIds + +class RunningTimeAnalysisFmuSettings(SettingsGroupABC): + + # pylint: disable=too-few-public-methods + class Ids: + MODE = "mode-1" + ENSEMBLE = "ensemble-1" + COLORING = "coloring-1" + FILTERING = "filtering" + + COLOR_MATRIX_BY_LABELS = [ + "Same job in ensemble", + "Slowest job in realization", + "Slowest job in ensemble", + ] + COLOR_PARCOORD_BY_LABELS = [ + "Successful/failed realization", + "Running time of realization", + ] + def __init__(self, ensembles: list, visual_parameters :list, plugin_paratamters:List[str], filter_shorter: Union[int, float] = 10) -> None: + super().__init__("Data filter") + self.ensembles = ensembles + self.filter_shorter = filter_shorter + self.parameters = plugin_paratamters + self.visual_parameters = visual_parameters + + def layout(self) -> List[Component]: + return [ + wcc.RadioItems( + label = "Mode", + id = self.register_component_unique_id(self.Ids.MODE), + options=[ + { + "label": "Running time matrix", + "value": "running_time_matrix", + }, + { + "label": "Parameter parallel coordinates", + "value": "parallel_coordinates", + }, + ], + value="running_time_matrix", + ), + wcc.Dropdown( + label = "Ensemble", + id=self.register_component_unique_id(self.Ids.ENSEMBLE), + options=[ + {"label": ens, "value": ens} for ens in self.ensembles + ], + value=self.ensembles[0], + clearable=False, + ), + wcc.Dropdown( + label="Color jobs relative to running time of:", + id=self.register_component_unique_id(self.Ids.COLORING), + options=[ + {"label": rel, "value": rel} + for rel in self.COLOR_MATRIX_BY_LABELS + ], + value=self.COLOR_MATRIX_BY_LABELS[0], + clearable=False, + ), + wcc.FlexBox( + id = self.register_component_unique_id(self.Ids.FILTERING), + children = wcc.Checklist( + label="Filtering", + id=self.register_component_unique_id("filter_short"), + options=[ + { + "label": "Slowest in ensemble less than " + f"{self.filter_shorter}s", + "value": "filter_short", + }, + ], + value=["filter_short"], + ), + ) + ] + + def set_callbacks(self) -> None: + + @callback( + Output(self.get_store_unique_id(PluginIds.Stores.MODE), 'data'), + Input(self.component_unique_id(self.Ids.MODE).to_string(), 'value') + ) + def _update_mode_store( + selected_mode: str + ) -> str: + return selected_mode + + @callback( + Output(self.get_store_unique_id(PluginIds.Stores.ENSEMBLE), 'data'), + Input(self.component_unique_id(self.Ids.ENSEMBLE).to_string(), 'value') + ) + def _update_ensemble_store( + selected_ensemble: str + ) -> str: + return selected_ensemble + + @callback( + Output(self.get_store_unique_id(PluginIds.Stores.COLORING), 'data'), + Input(self.component_unique_id(self.Ids.COLORING).to_string(), 'value') + ) + def _update_coloring_store( + selected_coloring: str + ) -> str: + return selected_coloring + + @callback( + Output(self.get_store_unique_id(PluginIds.Stores.FILTERING_SHORT), 'data'), + Input(self.component_unique_id("filter_short").to_string(), 'value') + ) + def _update_fshort_store( + selected_filtering_short: str + ) -> str: + return selected_filtering_short + + @callback( + Output(self.get_store_unique_id(PluginIds.Stores.FILTERING_PARAMS), 'data'), + Input(self.component_unique_id("filter_params").to_string(), 'value') + ) + def _update_fparms_store( + selected_fparams: str + ) -> str: + return selected_fparams + + @callback( + Output(self.component_unique_id(self.Ids.FILTERING).to_string(),"children"), + Output(self.component_unique_id(self.Ids.COLORING).to_string(),'label'), + Output(self.component_unique_id(self.Ids.COLORING).to_string(),'options'), + Output(self.component_unique_id(self.Ids.COLORING).to_string(),'value'), + Input(self.component_unique_id(self.Ids.MODE).to_string(),'value') + ) + def _update_color(selected_mode: str) -> Tuple: + children = None + label = None + optons = None + value = None + if selected_mode == "running_time_matrix": + children = wcc.Checklist( + label="Filtering jobs", + id=self.register_component_unique_id("filter_short"), + options=[ + { + "label": "Slowest in ensemble less than " + f"{self.filter_shorter}s", + "value": "filter_short", + }, + ], + value=["filter_short"], + ) + label = "Color jobs relative to running time of:" + options = [ + {"label": rel, "value": rel} + for rel in self.COLOR_MATRIX_BY_LABELS + ] + value = self.COLOR_MATRIX_BY_LABELS[0] + else: + children = wcc.SelectWithLabel( + label= "Filtering", + id=self.register_component_unique_id("filter_params"), + style={"overflowX": "auto", "fontSize": "0.97rem"}, + options=[ + {"label": param, "value": param} + for param in self.parameters + ], + multi=True, + value=self.visual_parameters, + size=min(50, len(self.visual_parameters)), + ) + label = "Color realizations relative to:" + options = [ + {"label": rel, "value": rel} + for rel in self.COLOR_PARCOORD_BY_LABELS + ] + value = self.COLOR_PARCOORD_BY_LABELS[0] + + return (children, label,options,value) diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py new file mode 100644 index 000000000..fe4b9144a --- /dev/null +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py @@ -0,0 +1,266 @@ +from typing import Callable, List, Optional, Tuple, Type, Union + +import pandas as pd +import webviz_core_components as wcc +import webviz_subsurface_components +from dash import Input, Output, callback +from dash.development.base_component import Component +from matplotlib.pyplot import figure +from webviz_config.common_cache import CACHE +from webviz_config.webviz_plugin_subclasses import ViewABC + +from ._plugin_ids import PluginIds +from ._shared_settings import RunningTimeAnalysisFmuSettings +from ._view_element import Graph + + +class RunTimeAnalysisGraph(ViewABC): + # pylint: disable=too-few-public-methods + class Ids: + GRAPH = "graph" + RUNTIMEANALYSIS = "group-tree" + + def __init__( + self, + plotly_theme: dict, + job_status_df: pd.DataFrame, + real_status_df: pd.DataFrame, + filter_shorter: Union[int, float] = 10 + ) -> None: + super().__init__("name") + self.add_column(RunTimeAnalysisGraph.Ids.RUNTIMEANALYSIS) + #column.add_view_element(wcc.Graph( ), RunTimeAnalysisGraph.Ids.GRAPH) + self.plotly_theme = plotly_theme + self.job_status_df = job_status_df + self.real_status_df = real_status_df + self.filter_shorter= filter_shorter + + def set_callbacks(self) -> None: + @callback( + Output( + self.layout_element(RunTimeAnalysisGraph.Ids.RUNTIMEANALYSIS) + .get_unique_id() + .to_string(), + "children", + ), + Input(self.get_store_unique_id(PluginIds.Stores.MODE), 'data'), + Input(self.get_store_unique_id(PluginIds.Stores.ENSEMBLE), 'data'), + Input(self.get_store_unique_id(PluginIds.Stores.COLORING), 'data'), + Input(self.get_store_unique_id(PluginIds.Stores.FILTERING_SHORT), 'data'), + Input(self.get_store_unique_id(PluginIds.Stores.FILTERING_PARAMS), 'data') + ) + def _update_fig( + mode: str, + ens: str, + coloring: str, + filter_short: List[str], + params: Union[str, List[str]], + ) -> dict: + """Update main figure + Dependent on `mode` it will call rendering of the chosen form of visualization + """ + plot_info = None + if mode == "running_time_matrix": + if "filter_short" in filter_short: + plot_info = render_matrix( + self.job_status_df[ + (self.job_status_df["ENSEMBLE"] == ens) + & (self.job_status_df["JOB_MAX_RUNTIME"] >= self.filter_shorter) + ], + coloring, + self.plotly_theme, + ) + else: + plot_info = render_matrix( + self.job_status_df[(self.job_status_df["ENSEMBLE"] == ens)], + coloring, + self.plotly_theme, + ) + + else: + + # Otherwise: parallel coordinates + # Ensure selected parameters is a list + params = params if isinstance(params, list) else [params] + # Color by success or runtime, for runtime drop unsuccesful + colormap_labels: Union[List[str], None] + if coloring == "Successful/failed realization": + plot_df = self.real_status_df[self.real_status_df["ENSEMBLE"] == ens] + colormap = make_colormap( + self.plotly_theme["layout"]["colorway"], discrete=2 + ) + color_by_col = "STATUS_BOOL" + colormap_labels = ["Failed", "Success"] + else: + plot_df = self.real_status_df[ + (self.real_status_df["ENSEMBLE"] == ens) + & (self.real_status_df["STATUS_BOOL"] == 1) + ] + colormap = self.plotly_theme["layout"]["colorscale"]["sequential"] + color_by_col = "RUNTIME" + colormap_labels = None + + # Call rendering of parallel coordinate plot + plot_info = render_parcoord( + plot_df, + params, + self.plotly_theme, + colormap, + color_by_col, + colormap_labels, + ) + return wcc.Graph( + id = "run-time-analysis-fmu-graph", + figure = plot_info + ) + + +@CACHE.memoize(timeout=CACHE.TIMEOUT) +def render_matrix(status_df: pd.DataFrame, rel: str, theme: dict) -> dict: + """Render matrix + Returns figure object as heatmap for the chosen ensemble and scaling method. + """ + if rel == "Same job in ensemble": + z = list(status_df["JOB_SCALED_RUNTIME"]) + elif rel == "Slowest job in realization": + z = list(status_df["REAL_SCALED_RUNTIME"]) + else: + z = list(status_df["ENS_SCALED_RUNTIME"]) + data = { + "type": "heatmap", + "x": list(status_df["REAL"]), + "y": list(status_df["JOB_ID"]), + "z": z, + "zmin": 0, + "zmax": 1, + "text": list(status_df["HOVERINFO"]), + "hoverinfo": "text", + "colorscale": theme["layout"]["colorscale"]["sequential"], + "colorbar": { + "tickvals": [ + 0, + 0.5, + 1, + ], + "ticktext": [ + "0 %", + "50 %", + "100 %", + ], + "xanchor": "left", + }, + } + layout = {} + layout.update(theme["layout"]) + layout.update( + { + "paper_bgcolor": "rgba(0,0,0,0)", + "plot_bgcolor": "rgba(0,0,0,0)", + "margin": { + "t": 50, + "b": 50, + "l": 50, + }, + "xaxis": { + "ticks": "", + "title": "Realizations", + "showgrid": False, + "side": "top", + }, + "yaxis": { + "ticks": "", + "showticklabels": True, + "tickmode": "array", + "tickvals": list(status_df["JOB_ID"]), + "ticktext": list(status_df["JOB"]), + "showgrid": False, + "automargin": True, + "autorange": "reversed", + "type": "category", + }, + "height": max(350, len(status_df["JOB_ID"].unique()) * 15), + "width": max(400, len(status_df["REAL"].unique()) * 12 + 250), + } + ) + + return {"data": [data], "layout": layout} + + +@CACHE.memoize(timeout=CACHE.TIMEOUT) +def render_parcoord( + plot_df: pd.DataFrame, + params: List[str], + theme: dict, + colormap: Union[List[str], List[list]], + color_col: str, + colormap_labels: Union[List[str], None] = None, +) -> dict: + """Renders parallel coordinates plot""" + # Create parcoords dimensions (one per parameter) + dimensions = [ + {"label": param, "values": plot_df[param].values.tolist()} for param in params + ] + + # Parcoords data dict + data: dict = { + "line": { + "color": plot_df[color_col].values.tolist(), + "colorscale": colormap, + "showscale": True, + }, + "dimensions": dimensions, + "labelangle": -90, + "labelside": "bottom", + "type": "parcoords", + } + if color_col == "STATUS_BOOL": + data["line"].update( + { + "cmin": -0.5, + "cmax": 1.5, + "colorbar": { + "tickvals": [0, 1], + "ticktext": colormap_labels, + "title": "Status", + "xanchor": "right", + "x": -0.02, + "len": 0.3, + }, + }, + ) + else: + data["line"].update( + { + "colorbar": { + "title": "Running time", + "xanchor": "right", + "x": -0.02, + }, + }, + ) + + layout = {} + layout.update(theme["layout"]) + # Ensure sufficient spacing between each dimension and margin for labels + width = len(dimensions) * 100 + 250 + margin_b = max([len(param) for param in params]) * 8 + layout.update({"width": width, "height": 800, "margin": {"b": margin_b, "t": 30}}) + return {"data": [data], "layout": layout} + +@CACHE.memoize(timeout=CACHE.TIMEOUT) +def make_colormap(color_array: list, discrete: int = None) -> list: + """ + Returns a colormap: + * If the `discrete` variable is set to an integer x, the colormap will be a discrete map of + size x evenly sampled from the given color_array. + * If discrete not defined or `None`: assumes continuous colormap and returns the given + color_array. + """ + if discrete is None: + colormap = color_array + else: + colormap = [] + for i in range(0, discrete): + colormap.append([i / discrete, color_array[i]]) + colormap.append([(i + 1) / discrete, color_array[i]]) + return colormap From d0117e0e878ea7e8b85c9121cd54822af760d8f3 Mon Sep 17 00:00:00 2001 From: "Jincheng Liu (OG SUB RPE)" Date: Mon, 11 Jul 2022 12:47:04 +0200 Subject: [PATCH 03/15] delete unused file --- webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py | 1 - 1 file changed, 1 deletion(-) diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py index fe4b9144a..e7f45abc8 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py @@ -11,7 +11,6 @@ from ._plugin_ids import PluginIds from ._shared_settings import RunningTimeAnalysisFmuSettings -from ._view_element import Graph class RunTimeAnalysisGraph(ViewABC): From abbe3a605af0adb4278d1163a51baae24bfbfd54 Mon Sep 17 00:00:00 2001 From: "Jincheng Liu (OG SUB RPE)" Date: Mon, 11 Jul 2022 12:49:25 +0200 Subject: [PATCH 04/15] delete unused class --- webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py | 2 +- .../plugins/_running_time_analysis_fmu/_shared_settings.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py index da8dcdd44..8f6c89df0 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py @@ -3,7 +3,7 @@ import numpy as np import pandas as pd -from dash import html, Input, Output, callback +from dash import Input, Output, callback, html from dash.development.base_component import Component from webviz_config import WebvizPluginABC, WebvizSettings from webviz_config.common_cache import CACHE diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py index adb4c7055..19fc84f0b 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py @@ -7,6 +7,7 @@ from ._plugin_ids import PluginIds + class RunningTimeAnalysisFmuSettings(SettingsGroupABC): # pylint: disable=too-few-public-methods From 156005934d1804fe5c3067ceee6722f86cc13a5b Mon Sep 17 00:00:00 2001 From: "Jincheng Liu (OG SUB RPE)" Date: Mon, 11 Jul 2022 15:32:39 +0200 Subject: [PATCH 05/15] remove constant --- .../_running_time_analysis_fmu/_plugin.py | 5 +-- .../_running_time_analysis_fmu/_plugin_ids.py | 1 + .../_shared_settings.py | 37 ++++++++++++++++-- .../_running_time_analysis_fmu/_view.py | 38 ++++++++++++++----- 4 files changed, 65 insertions(+), 16 deletions(-) diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py index 8f6c89df0..12dbe0ee3 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py @@ -65,10 +65,6 @@ def __init__( for ens in ensembles } - print("plugin ID is", self._plugin_unique_id.to_string()) - - print("active ID is",self._active_view_id) - self.plotly_theme = webviz_settings.theme.plotly_theme self.ensembles = ensembles self.status_file = status_file @@ -92,6 +88,7 @@ def __init__( self.add_store(PluginIds.Stores.COLORING, WebvizPluginABC.StorageType.SESSION) self.add_store(PluginIds.Stores.FILTERING_SHORT, WebvizPluginABC.StorageType.SESSION) self.add_store(PluginIds.Stores.FILTERING_PARAMS, WebvizPluginABC.StorageType.SESSION) + self.add_store(PluginIds.Stores.REMOVE_CONSTANT, WebvizPluginABC.StorageType.SESSION) self.add_store(PluginIds.Stores.ACTIVE_ID, WebvizPluginABC.StorageType.SESSION) diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin_ids.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin_ids.py index f4550cdc6..b8aa21034 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin_ids.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin_ids.py @@ -7,6 +7,7 @@ class Stores: COLORING = "coloring" FILTERING_SHORT = "filtering-short" FILTERING_PARAMS = "filtering-params" + REMOVE_CONSTANT = "remove-constant" ACTIVE_ID = "active-view-id" # pylint: disable=too-few-public-methods diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py index 19fc84f0b..54cca65e2 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py @@ -1,5 +1,7 @@ from typing import List, Tuple, Union +import numpy as np +import pandas as pd import webviz_core_components as wcc from dash import Input, Output, callback from dash.development.base_component import Component @@ -26,7 +28,12 @@ class Ids: "Successful/failed realization", "Running time of realization", ] - def __init__(self, ensembles: list, visual_parameters :list, plugin_paratamters:List[str], filter_shorter: Union[int, float] = 10) -> None: + def __init__( + self, ensembles: list, + visual_parameters :list, + plugin_paratamters:List[str], + filter_shorter: Union[int, float] = 10 + ) -> None: super().__init__("Data filter") self.ensembles = ensembles self.filter_shorter = filter_shorter @@ -132,6 +139,15 @@ def _update_fparms_store( selected_fparams: str ) -> str: return selected_fparams + + @callback( + Output(self.get_store_unique_id(PluginIds.Stores.REMOVE_CONSTANT), 'data'), + Input(self.component_unique_id("remove_constant").to_string(), 'value') + ) + def _update_remove_store( + selected_remove: str + ) -> str: + return selected_remove @callback( Output(self.component_unique_id(self.Ids.FILTERING).to_string(),"children"), @@ -165,8 +181,19 @@ def _update_color(selected_mode: str) -> Tuple: ] value = self.COLOR_MATRIX_BY_LABELS[0] else: - children = wcc.SelectWithLabel( - label= "Filtering", + children = [ + wcc.Checklist( + label= "Filtering", + id=self.register_component_unique_id("remove_constant"), + options=[ + { + "label": " Remove constant ", + "value": "remove_constant", + }, + ], + value=[], + ), + wcc.SelectWithLabel( id=self.register_component_unique_id("filter_params"), style={"overflowX": "auto", "fontSize": "0.97rem"}, options=[ @@ -177,6 +204,7 @@ def _update_color(selected_mode: str) -> Tuple: value=self.visual_parameters, size=min(50, len(self.visual_parameters)), ) + ] label = "Color realizations relative to:" options = [ {"label": rel, "value": rel} @@ -185,3 +213,6 @@ def _update_color(selected_mode: str) -> Tuple: value = self.COLOR_PARCOORD_BY_LABELS[0] return (children, label,options,value) + + + \ No newline at end of file diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py index e7f45abc8..392fe982e 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py @@ -1,6 +1,7 @@ from typing import Callable, List, Optional, Tuple, Type, Union import pandas as pd +import numpy as np import webviz_core_components as wcc import webviz_subsurface_components from dash import Input, Output, callback @@ -28,7 +29,6 @@ def __init__( ) -> None: super().__init__("name") self.add_column(RunTimeAnalysisGraph.Ids.RUNTIMEANALYSIS) - #column.add_view_element(wcc.Graph( ), RunTimeAnalysisGraph.Ids.GRAPH) self.plotly_theme = plotly_theme self.job_status_df = job_status_df self.real_status_df = real_status_df @@ -46,7 +46,8 @@ def set_callbacks(self) -> None: Input(self.get_store_unique_id(PluginIds.Stores.ENSEMBLE), 'data'), Input(self.get_store_unique_id(PluginIds.Stores.COLORING), 'data'), Input(self.get_store_unique_id(PluginIds.Stores.FILTERING_SHORT), 'data'), - Input(self.get_store_unique_id(PluginIds.Stores.FILTERING_PARAMS), 'data') + Input(self.get_store_unique_id(PluginIds.Stores.FILTERING_PARAMS), 'data'), + Input(self.get_store_unique_id(PluginIds.Stores.REMOVE_CONSTANT), 'data'), ) def _update_fig( mode: str, @@ -54,6 +55,7 @@ def _update_fig( coloring: str, filter_short: List[str], params: Union[str, List[str]], + remove_constant: str ) -> dict: """Update main figure Dependent on `mode` it will call rendering of the chosen form of visualization @@ -106,14 +108,14 @@ def _update_fig( self.plotly_theme, colormap, color_by_col, - colormap_labels, + remove_constant, + colormap_labels, ) return wcc.Graph( id = "run-time-analysis-fmu-graph", figure = plot_info ) - @CACHE.memoize(timeout=CACHE.TIMEOUT) def render_matrix(status_df: pd.DataFrame, rel: str, theme: dict) -> dict: """Render matrix @@ -192,13 +194,31 @@ def render_parcoord( theme: dict, colormap: Union[List[str], List[list]], color_col: str, - colormap_labels: Union[List[str], None] = None, + remove_constant: str, + colormap_labels: Union[List[str], None] = None, ) -> dict: + + # return {"data": [data], "layout": layout} """Renders parallel coordinates plot""" # Create parcoords dimensions (one per parameter) - dimensions = [ - {"label": param, "values": plot_df[param].values.tolist()} for param in params - ] + + dimensions = [] + dimentions_params = [] + + + if remove_constant == ["remove_constant"]: + for param in params: + if len(np.unique(plot_df[param].values)) >1: + dimentions_params.append(param) + + dimensions = [ + {"label": param, "values": plot_df[param].values.tolist()} for param in dimentions_params + ] + + else: + dimensions = [ + {"label": param, "values": plot_df[param].values.tolist()} for param in params + ] # Parcoords data dict data: dict = { @@ -210,7 +230,7 @@ def render_parcoord( "dimensions": dimensions, "labelangle": -90, "labelside": "bottom", - "type": "parcoords", + "type": "parcoords" } if color_col == "STATUS_BOOL": data["line"].update( From 09fe9e9919baf6aedb01f159101df51edaee0502 Mon Sep 17 00:00:00 2001 From: "Jincheng Liu (OG SUB RPE)" Date: Tue, 12 Jul 2022 12:22:52 +0200 Subject: [PATCH 06/15] filp the axises --- .../_shared_settings.py | 5 +- .../_running_time_analysis_fmu/_view.py | 306 +++++++++++++----- 2 files changed, 221 insertions(+), 90 deletions(-) diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py index 54cca65e2..ce4b02951 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py @@ -212,7 +212,4 @@ def _update_color(selected_mode: str) -> Tuple: ] value = self.COLOR_PARCOORD_BY_LABELS[0] - return (children, label,options,value) - - - \ No newline at end of file + return (children, label,options,value) \ No newline at end of file diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py index 392fe982e..889742a20 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py @@ -28,7 +28,8 @@ def __init__( filter_shorter: Union[int, float] = 10 ) -> None: super().__init__("name") - self.add_column(RunTimeAnalysisGraph.Ids.RUNTIMEANALYSIS) + column = self.add_column() + column.make_row(RunTimeAnalysisGraph.Ids.RUNTIMEANALYSIS) self.plotly_theme = plotly_theme self.job_status_df = job_status_df self.real_status_df = real_status_df @@ -56,7 +57,7 @@ def _update_fig( filter_short: List[str], params: Union[str, List[str]], remove_constant: str - ) -> dict: + ) -> List[dict]: """Update main figure Dependent on `mode` it will call rendering of the chosen form of visualization """ @@ -78,43 +79,54 @@ def _update_fig( self.plotly_theme, ) - else: + return wcc.Graph( + id = "run-time-analysis-fmu-graph", + figure = plot_info + ) - # Otherwise: parallel coordinates - # Ensure selected parameters is a list - params = params if isinstance(params, list) else [params] - # Color by success or runtime, for runtime drop unsuccesful - colormap_labels: Union[List[str], None] - if coloring == "Successful/failed realization": - plot_df = self.real_status_df[self.real_status_df["ENSEMBLE"] == ens] - colormap = make_colormap( - self.plotly_theme["layout"]["colorway"], discrete=2 - ) - color_by_col = "STATUS_BOOL" - colormap_labels = ["Failed", "Success"] - else: - plot_df = self.real_status_df[ - (self.real_status_df["ENSEMBLE"] == ens) - & (self.real_status_df["STATUS_BOOL"] == 1) - ] - colormap = self.plotly_theme["layout"]["colorscale"]["sequential"] - color_by_col = "RUNTIME" - colormap_labels = None - # Call rendering of parallel coordinate plot - plot_info = render_parcoord( - plot_df, - params, - self.plotly_theme, - colormap, - color_by_col, - remove_constant, - colormap_labels, + # Otherwise: parallel coordinates + # Ensure selected parameters is a list + params = params if isinstance(params, list) else [params] + # Color by success or runtime, for runtime drop unsuccesful + colormap_labels: Union[List[str], None] + if coloring == "Successful/failed realization": + plot_df = self.real_status_df[self.real_status_df["ENSEMBLE"] == ens] + colormap = make_colormap( + self.plotly_theme["layout"]["colorway"], discrete=2 ) - return wcc.Graph( - id = "run-time-analysis-fmu-graph", - figure = plot_info + color_by_col = "STATUS_BOOL" + colormap_labels = ["Failed", "Success"] + else: + plot_df = self.real_status_df[ + (self.real_status_df["ENSEMBLE"] == ens) + & (self.real_status_df["STATUS_BOOL"] == 1) + ] + colormap = self.plotly_theme["layout"]["colorscale"]["sequential"] + color_by_col = "RUNTIME" + colormap_labels = None + + # Call rendering of parallel coordinate plot + # plot_info = render_parcoord( + # plot_df, + # params, + # self.plotly_theme, + # colormap, + # color_by_col, + # remove_constant, + # colormap_labels, + # ) + plot_info = render_parcoord( + plot_df, + params, + self.plotly_theme, + colormap, + color_by_col, + remove_constant, + colormap_labels, ) + return plot_info + @CACHE.memoize(timeout=CACHE.TIMEOUT) def render_matrix(status_df: pd.DataFrame, rel: str, theme: dict) -> dict: @@ -202,7 +214,7 @@ def render_parcoord( """Renders parallel coordinates plot""" # Create parcoords dimensions (one per parameter) - dimensions = [] + dimensions = {} dimentions_params = [] @@ -210,61 +222,183 @@ def render_parcoord( for param in params: if len(np.unique(plot_df[param].values)) >1: dimentions_params.append(param) - - dimensions = [ - {"label": param, "values": plot_df[param].values.tolist()} for param in dimentions_params - ] - else: - dimensions = [ - {"label": param, "values": plot_df[param].values.tolist()} for param in params - ] + dimentions_params = params - # Parcoords data dict - data: dict = { - "line": { - "color": plot_df[color_col].values.tolist(), - "colorscale": colormap, - "showscale": True, - }, - "dimensions": dimensions, - "labelangle": -90, - "labelside": "bottom", - "type": "parcoords" - } - if color_col == "STATUS_BOOL": - data["line"].update( - { - "cmin": -0.5, - "cmax": 1.5, - "colorbar": { - "tickvals": [0, 1], - "ticktext": colormap_labels, - "title": "Status", - "xanchor": "right", - "x": -0.02, - "len": 0.3, - }, - }, - ) - else: - data["line"].update( - { - "colorbar": { - "title": "Running time", - "xanchor": "right", - "x": -0.02, - }, + + for param in dimentions_params: + dimensions [param] = plot_df[param].values.tolist() + + # dimensions = [ + # {"label": param, "values": plot_df[param].values.tolist()} for param in params + # ] + output_element = [] + print(plot_df[color_col].values.tolist()) + + for param in dimentions_params: + data = { + 'x': list(range(0,100)), + 'y': dimensions[param], + 'type': 'line', + 'name': param, + 'line': { + # "color": plot_df[color_col].values.tolist(), + # "colorscale": colormap, + # "showscale": True, + }, + } + layout = {} + layout.update(theme["layout"]) + width = len(dimensions) * 100 + 250 + margin_b = max([len(param) for param in params]) * 8 + layout.update({ + "paper_bgcolor": "rgba(0,0,0,0)", + "plot_bgcolor": "rgba(0,0,0,0)", + "margin": {"b": 0, "t": 0, 'r': '6vh'}, + 'xaxis': { + 'visible': False, + 'showticklabels': False}, + 'yaxis': { + 'visible': True, + 'showticklabels': True}, }, + ) + layout.update ({ + 'title':{ + 'text': param, + 'y': '0.5', + 'x':'0', + 'font':{'size':'8'} + } + }) + + figure = {'data':[data], 'layout':layout} + output_element.append( + wcc.Graph( + id = "run-time-analysis-fmu-graph"+param, + figure = figure, + style={"height": '10vh'}, + ) ) - layout = {} - layout.update(theme["layout"]) - # Ensure sufficient spacing between each dimension and margin for labels - width = len(dimensions) * 100 + 250 - margin_b = max([len(param) for param in params]) * 8 - layout.update({"width": width, "height": 800, "margin": {"b": margin_b, "t": 30}}) - return {"data": [data], "layout": layout} + # if color_col == "STATUS_BOOL": + # data["line"].update( + # { + # "cmin": -0.5, + # "cmax": 1.5, + # "colorbar": { + # "tickvals": [0, 1], + # "ticktext": colormap_labels, + # "title": "Status", + # "xanchor": "right", + # "x": -0.02, + # "len": 0.3, + # }, + # }, + # ) + # else: + # data["line"].update( + # { + # "colorbar": { + # "title": "Running time", + # "xanchor": "right", + # "x": -0.02, + # }, + # }, + # ) + + + + + + return output_element + + # # Parcoords data dict + # data: dict = { + # "line": { + # "color": plot_df[color_col].values.tolist(), + # "colorscale": colormap, + # "showscale": True, + # }, + # "dimensions": dimensions, + # "labelangle": -90, + # "labelside": "bottom", + # "type": "line" + # } + # if color_col == "STATUS_BOOL": + # data["line"].update( + # { + # "cmin": -0.5, + # "cmax": 1.5, + # "colorbar": { + # "tickvals": [0, 1], + # "ticktext": colormap_labels, + # "title": "Status", + # "xanchor": "right", + # "x": -0.02, + # "len": 0.3, + # }, + # }, + # ) + # else: + # data["line"].update( + # { + # "colorbar": { + # "title": "Running time", + # "xanchor": "right", + # "x": -0.02, + # }, + # }, + # ) + + + + # # Parcoords data dict + # data: dict = { + # "line": { + # "color": plot_df[color_col].values.tolist(), + # "colorscale": colormap, + # "showscale": True, + # }, + # "dimensions": dimensions, + # "labelangle": -90, + # "labelside": "bottom", + # "type": "parcoords" + # } + # if color_col == "STATUS_BOOL": + # data["line"].update( + # { + # "cmin": -0.5, + # "cmax": 1.5, + # "colorbar": { + # "tickvals": [0, 1], + # "ticktext": colormap_labels, + # "title": "Status", + # "xanchor": "right", + # "x": -0.02, + # "len": 0.3, + # }, + # }, + # ) + # else: + # data["line"].update( + # { + # "colorbar": { + # "title": "Running time", + # "xanchor": "right", + # "x": -0.02, + # }, + # }, + # ) + + # layout = {} + # layout.update(theme["layout"]) + # # Ensure sufficient spacing between each dimension and margin for labels + # width = len(dimensions) * 100 + 250 + # margin_b = max([len(param) for param in params]) * 8 + # layout.update({"width": width, "height": 800, "margin": {"b": margin_b, "t": 30}}) + + #return {"data": [data], "layout": layout} @CACHE.memoize(timeout=CACHE.TIMEOUT) def make_colormap(color_array: list, discrete: int = None) -> list: From 6fa945815b472a183bbaa37352bb45c51e642cae Mon Sep 17 00:00:00 2001 From: "Jincheng Liu (OG SUB RPE)" Date: Tue, 26 Jul 2022 11:20:18 +0200 Subject: [PATCH 07/15] change the filtering filters --- .../_view_elements.py | 168 ++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 webviz_subsurface/plugins/_running_time_analysis_fmu/_view_elements.py diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view_elements.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view_elements.py new file mode 100644 index 000000000..969293612 --- /dev/null +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view_elements.py @@ -0,0 +1,168 @@ +from typing import List, Union + +import numpy as np +import pandas as pd +from webviz_config.common_cache import CACHE + + +@CACHE.memoize(timeout=CACHE.TIMEOUT) +def render_matrix(status_df: pd.DataFrame, rel: str, theme: dict) -> dict: + """Render matrix + Returns figure object as heatmap for the chosen ensemble and scaling method. + """ + if rel == "Same job in ensemble": + z = list(status_df["JOB_SCALED_RUNTIME"]) + elif rel == "Slowest job in realization": + z = list(status_df["REAL_SCALED_RUNTIME"]) + else: + z = list(status_df["ENS_SCALED_RUNTIME"]) + data = { + "type": "heatmap", + "x": list(status_df["REAL"]), + "y": list(status_df["JOB_ID"]), + "z": z, + "zmin": 0, + "zmax": 1, + "text": list(status_df["HOVERINFO"]), + "hoverinfo": "text", + "colorscale": theme["layout"]["colorscale"]["sequential"], + "colorbar": { + "tickvals": [ + 0, + 0.5, + 1, + ], + "ticktext": [ + "0 %", + "50 %", + "100 %", + ], + "xanchor": "left", + }, + } + layout = {} + layout.update(theme["layout"]) + layout.update( + { + "paper_bgcolor": "rgba(0,0,0,0)", + "plot_bgcolor": "rgba(0,0,0,0)", + "margin": { + "t": 50, + "b": 50, + "l": 50, + }, + "xaxis": { + "ticks": "", + "title": "Realizations", + "showgrid": False, + "side": "top", + }, + "yaxis": { + "ticks": "", + "showticklabels": True, + "tickmode": "array", + "tickvals": list(status_df["JOB_ID"]), + "ticktext": list(status_df["JOB"]), + "showgrid": False, + "automargin": True, + "autorange": "reversed", + "type": "category", + }, + "height": max(350, len(status_df["JOB_ID"].unique()) * 15), + "width": max(400, len(status_df["REAL"].unique()) * 12 + 250), + } + ) + + return {"data": [data], "layout": layout} + + +@CACHE.memoize(timeout=CACHE.TIMEOUT) +def render_parcoord( + plot_df: pd.DataFrame, + params: List[str], + theme: dict, + colormap: Union[List[str], List[list]], + color_col: str, + remove_constant: str, + colormap_labels: Union[List[str], None] = None, +) -> dict: + """Renders parallel coordinates plot""" + # Create parcoords dimensions (one per parameter) + dimentions_params = [] + if remove_constant == ["remove_constant"]: + for param in params: + if len(np.unique(plot_df[param].values)) > 1: + dimentions_params.append(param) + else: + dimentions_params = params + + dimensions = [ + {"label": param, "values": plot_df[param].values.tolist()} + for param in dimentions_params + ] + + # Parcoords data dict + data: dict = { + "line": { + "color": plot_df[color_col].values.tolist(), + "colorscale": colormap, + "showscale": True, + }, + "dimensions": dimensions, + "labelangle": -90, + "labelside": "bottom", + "type": "parcoords", + } + if color_col == "STATUS_BOOL": + data["line"].update( + { + "cmin": -0.5, + "cmax": 1.5, + "colorbar": { + "tickvals": [0, 1], + "ticktext": colormap_labels, + "title": "Status", + "xanchor": "right", + "x": -0.02, + "len": 0.3, + }, + }, + ) + else: + data["line"].update( + { + "colorbar": { + "title": "Running time", + "xanchor": "right", + "x": -0.02, + }, + }, + ) + + layout = {} + layout.update(theme["layout"]) + # Ensure sufficient spacing between each dimension and margin for labels + width = len(dimensions) * 100 + 250 + margin_b = max([len(param) for param in params]) * 8 + layout.update({"width": width, "height": 800, "margin": {"b": margin_b, "t": 30}}) + + return {"data": [data], "layout": layout} + + +@CACHE.memoize(timeout=CACHE.TIMEOUT) +def make_colormap(color_array: list, discrete: int = None) -> list: + """ + Returns a colormap: + * If the `discrete` variable is set to an integer x, the colormap will be a discrete map of + size x evenly sampled from the given color_array. + * If discrete not defined or `None`: assumes continuous colormap and returns the given + color_array. + """ + if discrete is None: + colormap = color_array + else: + colormap = [] + for i in range(0, discrete): + colormap.append([i / discrete, color_array[i]]) + colormap.append([(i + 1) / discrete, color_array[i]]) + return colormap From 76371f1e754d5ed635cb43712a76eaef9e0be97b Mon Sep 17 00:00:00 2001 From: "Jincheng Liu (OG SUB RPE)" Date: Tue, 26 Jul 2022 11:23:35 +0200 Subject: [PATCH 08/15] change the filtering filters --- .../_running_time_analysis_fmu/_plugin.py | 75 ++- .../_running_time_analysis_fmu/_plugin_ids.py | 11 - .../_shared_settings.py | 305 +++++------ .../_running_time_analysis_fmu/_view.py | 480 +++++------------- .../_view_elements.py | 59 +++ 5 files changed, 361 insertions(+), 569 deletions(-) diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py index 12dbe0ee3..466e23584 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py @@ -1,17 +1,14 @@ import json -from typing import Callable, List, Optional, Tuple, Type, Union +from typing import Callable, List, Optional, Tuple, Union import numpy as np import pandas as pd -from dash import Input, Output, callback, html -from dash.development.base_component import Component from webviz_config import WebvizPluginABC, WebvizSettings from webviz_config.common_cache import CACHE from webviz_config.webviz_store import webvizstore from ..._datainput.fmu_input import load_ensemble_set, load_parameters from ._plugin_ids import PluginIds -from ._shared_settings import RunningTimeAnalysisFmuSettings from ._view import RunTimeAnalysisGraph @@ -83,42 +80,43 @@ def __init__( ) self.plugin_parameters = self.parameters - self.add_store(PluginIds.Stores.MODE, WebvizPluginABC.StorageType.SESSION) - self.add_store(PluginIds.Stores.ENSEMBLE, WebvizPluginABC.StorageType.SESSION) - self.add_store(PluginIds.Stores.COLORING, WebvizPluginABC.StorageType.SESSION) - self.add_store(PluginIds.Stores.FILTERING_SHORT, WebvizPluginABC.StorageType.SESSION) - self.add_store(PluginIds.Stores.FILTERING_PARAMS, WebvizPluginABC.StorageType.SESSION) - self.add_store(PluginIds.Stores.REMOVE_CONSTANT, WebvizPluginABC.StorageType.SESSION) - self.add_store(PluginIds.Stores.ACTIVE_ID, WebvizPluginABC.StorageType.SESSION) - - - self.add_shared_settings_group( - RunningTimeAnalysisFmuSettings(self.ensembles, self.visual_parameters,self.plugin_parameters, self.filter_shorter), - PluginIds.SharedSettings.SHARED_SETTINGS_GROUP, - ) + # self.add_store(PluginIds.Stores.MODE, WebvizPluginABC.StorageType.SESSION) + # self.add_store(PluginIds.Stores.ENSEMBLE, WebvizPluginABC.StorageType.SESSION) + # self.add_store(PluginIds.Stores.COLORING, WebvizPluginABC.StorageType.SESSION) + # self.add_store( + # PluginIds.Stores.FILTERING_SHORT, WebvizPluginABC.StorageType.SESSION + # ) + # self.add_store( + # PluginIds.Stores.FILTERING_PARAMS, WebvizPluginABC.StorageType.SESSION + # ) + # self.add_store( + # PluginIds.Stores.REMOVE_CONSTANT, WebvizPluginABC.StorageType.SESSION + # ) + # self.add_store(PluginIds.Stores.ACTIVE_ID, WebvizPluginABC.StorageType.SESSION) + + # self.add_shared_settings_group( + # RunningTimeAnalysisFmuSettings( + # self.ensembles, + # self.visual_parameters, + # self.plugin_parameters, + # self.filter_shorter, + # ), + # PluginIds.SharedSettings.SHARED_SETTINGS_GROUP, + # ) self.add_view( - RunTimeAnalysisGraph(self.plotly_theme, self.job_status_df, self.real_status_df, self.filter_shorter), - PluginIds.RunTimeAnalysisView.RUN_TIME_FMU, - PluginIds.RunTimeAnalysisView.GROUP_NAME - ) - - - def _set_callbacks(self) -> None: - @callback( - Output(self.get_store_unique_id(PluginIds.Stores.ACTIVE_ID), 'data'), - Input(self._plugin_unique_id.to_string(), 'active_view_id') + RunTimeAnalysisGraph( + self.plotly_theme, + self.job_status_df, + self.real_status_df, + self.ensembles, + self.visual_parameters, + self.plugin_parameters, + self.filter_shorter, + ), + PluginIds.RunTimeAnalysisView.RUN_TIME_FMU, + PluginIds.RunTimeAnalysisView.GROUP_NAME, ) - def update_store(value:str) -> str: - print(value) - return value - - - - - @property - def layout(self) -> Type[Component]: - return html.Div() @property def parameters(self) -> List[str]: @@ -153,7 +151,6 @@ def add_webvizstore(self) -> List[Tuple[Callable, list]]: ] - @CACHE.memoize(timeout=CACHE.TIMEOUT) @webvizstore # pylint: disable=too-many-locals @@ -312,5 +309,3 @@ def ensemble_post_processing() -> list: ) # Has to be stored in one df due to webvizstore, see issue #206 in webviz-config return pd.concat([job_status_df, real_status_df], keys=["job", "real"], sort=False) - - diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin_ids.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin_ids.py index b8aa21034..a21d98e91 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin_ids.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin_ids.py @@ -1,15 +1,4 @@ class PluginIds: - - # pylint: disable=too-few-public-methods - class Stores: - MODE = "mode" - ENSEMBLE = "ensemble" - COLORING = "coloring" - FILTERING_SHORT = "filtering-short" - FILTERING_PARAMS = "filtering-params" - REMOVE_CONSTANT = "remove-constant" - ACTIVE_ID = "active-view-id" - # pylint: disable=too-few-public-methods class SharedSettings: SHARED_SETTINGS_GROUP = "shared-settings-group" diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py index ce4b02951..8f5e034ba 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py @@ -3,38 +3,44 @@ import numpy as np import pandas as pd import webviz_core_components as wcc -from dash import Input, Output, callback +from dash import Input, Output, State, callback from dash.development.base_component import Component +from dash.exceptions import PreventUpdate from webviz_config.webviz_plugin_subclasses import SettingsGroupABC -from ._plugin_ids import PluginIds - class RunningTimeAnalysisFmuSettings(SettingsGroupABC): - + # pylint: disable=too-few-public-methods class Ids: MODE = "mode-1" - ENSEMBLE = "ensemble-1" - COLORING = "coloring-1" + ENSEMBLE = "ensemble" + COLORING = "coloring" FILTERING = "filtering" + FILTER_SHORT = "filter-short" + REMOVE_CONSTANT = "remove-constant" + FILTER_PARAMS = "filter-params" COLOR_MATRIX_BY_LABELS = [ - "Same job in ensemble", - "Slowest job in realization", - "Slowest job in ensemble", + "Same job in ensemble", + "Slowest job in realization", + "Slowest job in ensemble", ] COLOR_PARCOORD_BY_LABELS = [ - "Successful/failed realization", - "Running time of realization", + "Successful/failed realization", + "Running time of realization", ] + def __init__( - self, ensembles: list, - visual_parameters :list, - plugin_paratamters:List[str], - filter_shorter: Union[int, float] = 10 - ) -> None: + self, + real_status_df: pd.DataFrame, + ensembles: list, + visual_parameters: list, + plugin_paratamters: List[str], + filter_shorter: Union[int, float] = 10, + ) -> None: super().__init__("Data filter") + self.real_status_df = real_status_df self.ensembles = ensembles self.filter_shorter = filter_shorter self.parameters = plugin_paratamters @@ -43,26 +49,24 @@ def __init__( def layout(self) -> List[Component]: return [ wcc.RadioItems( - label = "Mode", - id = self.register_component_unique_id(self.Ids.MODE), + label="Mode", + id=self.register_component_unique_id(self.Ids.MODE), options=[ - { - "label": "Running time matrix", - "value": "running_time_matrix", - }, - { - "label": "Parameter parallel coordinates", - "value": "parallel_coordinates", - }, - ], + { + "label": "Running time matrix", + "value": "running_time_matrix", + }, + { + "label": "Parameter parallel coordinates", + "value": "parallel_coordinates", + }, + ], value="running_time_matrix", ), wcc.Dropdown( - label = "Ensemble", + label="Ensemble", id=self.register_component_unique_id(self.Ids.ENSEMBLE), - options=[ - {"label": ens, "value": ens} for ens in self.ensembles - ], + options=[{"label": ens, "value": ens} for ens in self.ensembles], value=self.ensembles[0], clearable=False, ), @@ -70,146 +74,145 @@ def layout(self) -> List[Component]: label="Color jobs relative to running time of:", id=self.register_component_unique_id(self.Ids.COLORING), options=[ - {"label": rel, "value": rel} - for rel in self.COLOR_MATRIX_BY_LABELS + {"label": rel, "value": rel} for rel in self.COLOR_MATRIX_BY_LABELS ], value=self.COLOR_MATRIX_BY_LABELS[0], clearable=False, ), - wcc.FlexBox( - id = self.register_component_unique_id(self.Ids.FILTERING), - children = wcc.Checklist( - label="Filtering", - id=self.register_component_unique_id("filter_short"), - options=[ - { - "label": "Slowest in ensemble less than " - f"{self.filter_shorter}s", - "value": "filter_short", - }, - ], - value=["filter_short"], - ), - ) + wcc.Selectors( + label="Filtering jobs", + id=self.register_component_unique_id(self.Ids.FILTERING), + children=[ + wcc.Checklist( + id=self.register_component_unique_id(self.Ids.FILTER_SHORT), + labelStyle={"display": "block"}, + options=[ + { + "label": "Slowest in ensemble less than " + f"{self.filter_shorter}s", + "value": "filter_short", + }, + ], + value=["filter_short"], + ), + wcc.Checklist( + id=self.register_component_unique_id(self.Ids.REMOVE_CONSTANT), + labelStyle={"display": "none"}, + options=[ + { + "label": " Remove constant ", + "value": "remove_constant", + }, + ], + value=[], + ), + wcc.SelectWithLabel( + id=self.register_component_unique_id(self.Ids.FILTER_PARAMS), + style={"display": "none"}, + options=[ + {"label": param, "value": param} + for param in self.parameters + ], + multi=True, + value=self.visual_parameters, + size=min(50, len(self.visual_parameters)), + ), + ], + ), ] def set_callbacks(self) -> None: - @callback( - Output(self.get_store_unique_id(PluginIds.Stores.MODE), 'data'), - Input(self.component_unique_id(self.Ids.MODE).to_string(), 'value') + Output( + self.component_unique_id(self.Ids.FILTER_PARAMS).to_string(), "options" + ), + Output( + self.component_unique_id(self.Ids.FILTER_PARAMS).to_string(), "value" + ), + Input( + self.component_unique_id(self.Ids.REMOVE_CONSTANT).to_string(), "value" + ), + State(self.component_unique_id(self.Ids.ENSEMBLE).to_string(), "value"), + Input(self.component_unique_id(self.Ids.COLORING).to_string(), "value"), ) - def _update_mode_store( - selected_mode: str - ) -> str: - return selected_mode + def _update_filter_parameters( + remove_constant: str, ens: str, coloring: str + ) -> Tuple: + if remove_constant == None: + raise PreventUpdate - @callback( - Output(self.get_store_unique_id(PluginIds.Stores.ENSEMBLE), 'data'), - Input(self.component_unique_id(self.Ids.ENSEMBLE).to_string(), 'value') - ) - def _update_ensemble_store( - selected_ensemble: str - ) -> str: - return selected_ensemble + dimentions_params = [] - @callback( - Output(self.get_store_unique_id(PluginIds.Stores.COLORING), 'data'), - Input(self.component_unique_id(self.Ids.COLORING).to_string(), 'value') - ) - def _update_coloring_store( - selected_coloring: str - ) -> str: - return selected_coloring + if coloring == "Successful/failed realization": + plot_df = self.real_status_df[self.real_status_df["ENSEMBLE"] == ens] + else: + plot_df = self.real_status_df[ + (self.real_status_df["ENSEMBLE"] == ens) + & (self.real_status_df["STATUS_BOOL"] == 1) + ] + if remove_constant == ["remove_constant"]: + for param in self.parameters: + if len(np.unique(plot_df[param].values)) > 1: + dimentions_params.append(param) + else: + dimentions_params = self.parameters - @callback( - Output(self.get_store_unique_id(PluginIds.Stores.FILTERING_SHORT), 'data'), - Input(self.component_unique_id("filter_short").to_string(), 'value') - ) - def _update_fshort_store( - selected_filtering_short: str - ) -> str: - return selected_filtering_short + filter_parame_options = [ + {"label": param, "value": param} for param in dimentions_params + ] + filter_parame_value = dimentions_params - @callback( - Output(self.get_store_unique_id(PluginIds.Stores.FILTERING_PARAMS), 'data'), - Input(self.component_unique_id("filter_params").to_string(), 'value') - ) - def _update_fparms_store( - selected_fparams: str - ) -> str: - return selected_fparams + return (filter_parame_options, filter_parame_value) @callback( - Output(self.get_store_unique_id(PluginIds.Stores.REMOVE_CONSTANT), 'data'), - Input(self.component_unique_id("remove_constant").to_string(), 'value') - ) - def _update_remove_store( - selected_remove: str - ) -> str: - return selected_remove - - @callback( - Output(self.component_unique_id(self.Ids.FILTERING).to_string(),"children"), - Output(self.component_unique_id(self.Ids.COLORING).to_string(),'label'), - Output(self.component_unique_id(self.Ids.COLORING).to_string(),'options'), - Output(self.component_unique_id(self.Ids.COLORING).to_string(),'value'), - Input(self.component_unique_id(self.Ids.MODE).to_string(),'value') + Output( + self.component_unique_id(self.Ids.FILTER_SHORT).to_string(), + "labelStyle", + ), + Output( + self.component_unique_id(self.Ids.REMOVE_CONSTANT).to_string(), + "labelStyle", + ), + Output( + self.component_unique_id(self.Ids.FILTER_PARAMS).to_string(), + "style", + ), + Output(self.component_unique_id(self.Ids.COLORING).to_string(), "label"), + Output(self.component_unique_id(self.Ids.COLORING).to_string(), "options"), + Output(self.component_unique_id(self.Ids.COLORING).to_string(), "value"), + Input(self.component_unique_id(self.Ids.MODE).to_string(), "value"), ) def _update_color(selected_mode: str) -> Tuple: - children = None label = None - optons = None value = None + if selected_mode == "running_time_matrix": - children = wcc.Checklist( - label="Filtering jobs", - id=self.register_component_unique_id("filter_short"), - options=[ - { - "label": "Slowest in ensemble less than " - f"{self.filter_shorter}s", - "value": "filter_short", - }, - ], - value=["filter_short"], - ) label = "Color jobs relative to running time of:" options = [ - {"label": rel, "value": rel} - for rel in self.COLOR_MATRIX_BY_LABELS - ] + {"label": rel, "value": rel} for rel in self.COLOR_MATRIX_BY_LABELS + ] value = self.COLOR_MATRIX_BY_LABELS[0] - else: - children = [ - wcc.Checklist( - label= "Filtering", - id=self.register_component_unique_id("remove_constant"), - options=[ - { - "label": " Remove constant ", - "value": "remove_constant", - }, - ], - value=[], - ), - wcc.SelectWithLabel( - id=self.register_component_unique_id("filter_params"), - style={"overflowX": "auto", "fontSize": "0.97rem"}, - options=[ - {"label": param, "value": param} - for param in self.parameters - ], - multi=True, - value=self.visual_parameters, - size=min(50, len(self.visual_parameters)), - ) - ] - label = "Color realizations relative to:" - options = [ - {"label": rel, "value": rel} - for rel in self.COLOR_PARCOORD_BY_LABELS - ] - value = self.COLOR_PARCOORD_BY_LABELS[0] - - return (children, label,options,value) \ No newline at end of file + + return ( + {"display": "block"}, + {"display": "none"}, + {"display": "none"}, + label, + options, + value, + ) + + label = "Color realizations relative to:" + options = [ + {"label": rel, "value": rel} for rel in self.COLOR_PARCOORD_BY_LABELS + ] + value = self.COLOR_PARCOORD_BY_LABELS[0] + + return ( + {"display": "none"}, + {"display": "block"}, + {"display": "block"}, + label, + options, + value, + ) diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py index 889742a20..95fc58add 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py @@ -1,54 +1,99 @@ -from typing import Callable, List, Optional, Tuple, Type, Union +from typing import List, Union import pandas as pd -import numpy as np import webviz_core_components as wcc -import webviz_subsurface_components from dash import Input, Output, callback from dash.development.base_component import Component -from matplotlib.pyplot import figure -from webviz_config.common_cache import CACHE from webviz_config.webviz_plugin_subclasses import ViewABC -from ._plugin_ids import PluginIds from ._shared_settings import RunningTimeAnalysisFmuSettings +from ._view_elements import make_colormap, render_matrix, render_parcoord class RunTimeAnalysisGraph(ViewABC): # pylint: disable=too-few-public-methods class Ids: - GRAPH = "graph" - RUNTIMEANALYSIS = "group-tree" + RUN_TIME_SETTINGS = "run-time-settings" + RUNTIME_ANALYSIS = "run-time-analysis" def __init__( - self, - plotly_theme: dict, - job_status_df: pd.DataFrame, - real_status_df: pd.DataFrame, - filter_shorter: Union[int, float] = 10 - ) -> None: - super().__init__("name") - column = self.add_column() - column.make_row(RunTimeAnalysisGraph.Ids.RUNTIMEANALYSIS) + self, + plotly_theme: dict, + job_status_df: pd.DataFrame, + real_status_df: pd.DataFrame, + ensembles: list, + visual_parameters: list, + plugin_paratamters: List[str], + filter_shorter: Union[int, float] = 10, + ) -> None: + super().__init__("Group tree") + self.plotly_theme = plotly_theme self.job_status_df = job_status_df self.real_status_df = real_status_df - self.filter_shorter= filter_shorter + self.filter_shorter = filter_shorter + self.cooridinates_params = None + self.ensembles = ensembles + self.visual_parameters = visual_parameters + self.plugin_parameters = plugin_paratamters + + self.add_column(self.Ids.RUNTIME_ANALYSIS) + + self.add_settings_group( + RunningTimeAnalysisFmuSettings( + self.real_status_df, + self.ensembles, + self.visual_parameters, + self.plugin_parameters, + self.filter_shorter, + ), + self.Ids.RUN_TIME_SETTINGS, + ) def set_callbacks(self) -> None: @callback( Output( - self.layout_element(RunTimeAnalysisGraph.Ids.RUNTIMEANALYSIS) + self.layout_element(RunTimeAnalysisGraph.Ids.RUNTIME_ANALYSIS) .get_unique_id() .to_string(), "children", ), - Input(self.get_store_unique_id(PluginIds.Stores.MODE), 'data'), - Input(self.get_store_unique_id(PluginIds.Stores.ENSEMBLE), 'data'), - Input(self.get_store_unique_id(PluginIds.Stores.COLORING), 'data'), - Input(self.get_store_unique_id(PluginIds.Stores.FILTERING_SHORT), 'data'), - Input(self.get_store_unique_id(PluginIds.Stores.FILTERING_PARAMS), 'data'), - Input(self.get_store_unique_id(PluginIds.Stores.REMOVE_CONSTANT), 'data'), + Input( + self.settings_group(self.Ids.RUN_TIME_SETTINGS) + .component_unique_id(RunningTimeAnalysisFmuSettings.Ids.MODE) + .to_string(), + "value", + ), + Input( + self.settings_group(self.Ids.RUN_TIME_SETTINGS) + .component_unique_id(RunningTimeAnalysisFmuSettings.Ids.ENSEMBLE) + .to_string(), + "value", + ), + Input( + self.settings_group(self.Ids.RUN_TIME_SETTINGS) + .component_unique_id(RunningTimeAnalysisFmuSettings.Ids.COLORING) + .to_string(), + "value", + ), + Input( + self.settings_group(self.Ids.RUN_TIME_SETTINGS) + .component_unique_id(RunningTimeAnalysisFmuSettings.Ids.FILTER_SHORT) + .to_string(), + "value", + ), + Input( + self.settings_group(self.Ids.RUN_TIME_SETTINGS) + .component_unique_id(RunningTimeAnalysisFmuSettings.Ids.FILTER_PARAMS) + .to_string(), + "value", + ), + Input( + self.settings_group(self.Ids.RUN_TIME_SETTINGS) + .component_unique_id(RunningTimeAnalysisFmuSettings.Ids.REMOVE_CONSTANT) + .to_string(), + "value", + ), ) def _update_fig( mode: str, @@ -56,364 +101,65 @@ def _update_fig( coloring: str, filter_short: List[str], params: Union[str, List[str]], - remove_constant: str - ) -> List[dict]: + remove_constant: str, + ) -> Component: """Update main figure Dependent on `mode` it will call rendering of the chosen form of visualization """ plot_info = None + if mode == "running_time_matrix": if "filter_short" in filter_short: - plot_info = render_matrix( + plot_info = render_matrix( self.job_status_df[ (self.job_status_df["ENSEMBLE"] == ens) - & (self.job_status_df["JOB_MAX_RUNTIME"] >= self.filter_shorter) + & ( + self.job_status_df["JOB_MAX_RUNTIME"] + >= self.filter_shorter + ) ], coloring, self.plotly_theme, ) - else: + else: plot_info = render_matrix( self.job_status_df[(self.job_status_df["ENSEMBLE"] == ens)], coloring, self.plotly_theme, ) - return wcc.Graph( - id = "run-time-analysis-fmu-graph", - figure = plot_info - ) - - - # Otherwise: parallel coordinates - # Ensure selected parameters is a list - params = params if isinstance(params, list) else [params] - # Color by success or runtime, for runtime drop unsuccesful - colormap_labels: Union[List[str], None] - if coloring == "Successful/failed realization": - plot_df = self.real_status_df[self.real_status_df["ENSEMBLE"] == ens] - colormap = make_colormap( - self.plotly_theme["layout"]["colorway"], discrete=2 - ) - color_by_col = "STATUS_BOOL" - colormap_labels = ["Failed", "Success"] else: - plot_df = self.real_status_df[ - (self.real_status_df["ENSEMBLE"] == ens) - & (self.real_status_df["STATUS_BOOL"] == 1) - ] - colormap = self.plotly_theme["layout"]["colorscale"]["sequential"] - color_by_col = "RUNTIME" - colormap_labels = None - - # Call rendering of parallel coordinate plot - # plot_info = render_parcoord( - # plot_df, - # params, - # self.plotly_theme, - # colormap, - # color_by_col, - # remove_constant, - # colormap_labels, - # ) - plot_info = render_parcoord( - plot_df, - params, - self.plotly_theme, - colormap, - color_by_col, - remove_constant, - colormap_labels, - ) - return plot_info - - -@CACHE.memoize(timeout=CACHE.TIMEOUT) -def render_matrix(status_df: pd.DataFrame, rel: str, theme: dict) -> dict: - """Render matrix - Returns figure object as heatmap for the chosen ensemble and scaling method. - """ - if rel == "Same job in ensemble": - z = list(status_df["JOB_SCALED_RUNTIME"]) - elif rel == "Slowest job in realization": - z = list(status_df["REAL_SCALED_RUNTIME"]) - else: - z = list(status_df["ENS_SCALED_RUNTIME"]) - data = { - "type": "heatmap", - "x": list(status_df["REAL"]), - "y": list(status_df["JOB_ID"]), - "z": z, - "zmin": 0, - "zmax": 1, - "text": list(status_df["HOVERINFO"]), - "hoverinfo": "text", - "colorscale": theme["layout"]["colorscale"]["sequential"], - "colorbar": { - "tickvals": [ - 0, - 0.5, - 1, - ], - "ticktext": [ - "0 %", - "50 %", - "100 %", - ], - "xanchor": "left", - }, - } - layout = {} - layout.update(theme["layout"]) - layout.update( - { - "paper_bgcolor": "rgba(0,0,0,0)", - "plot_bgcolor": "rgba(0,0,0,0)", - "margin": { - "t": 50, - "b": 50, - "l": 50, - }, - "xaxis": { - "ticks": "", - "title": "Realizations", - "showgrid": False, - "side": "top", - }, - "yaxis": { - "ticks": "", - "showticklabels": True, - "tickmode": "array", - "tickvals": list(status_df["JOB_ID"]), - "ticktext": list(status_df["JOB"]), - "showgrid": False, - "automargin": True, - "autorange": "reversed", - "type": "category", - }, - "height": max(350, len(status_df["JOB_ID"].unique()) * 15), - "width": max(400, len(status_df["REAL"].unique()) * 12 + 250), - } - ) - - return {"data": [data], "layout": layout} - - -@CACHE.memoize(timeout=CACHE.TIMEOUT) -def render_parcoord( - plot_df: pd.DataFrame, - params: List[str], - theme: dict, - colormap: Union[List[str], List[list]], - color_col: str, - remove_constant: str, - colormap_labels: Union[List[str], None] = None, -) -> dict: - - # return {"data": [data], "layout": layout} - """Renders parallel coordinates plot""" - # Create parcoords dimensions (one per parameter) - - dimensions = {} - dimentions_params = [] - - - if remove_constant == ["remove_constant"]: - for param in params: - if len(np.unique(plot_df[param].values)) >1: - dimentions_params.append(param) - else: - dimentions_params = params - - - for param in dimentions_params: - dimensions [param] = plot_df[param].values.tolist() - - # dimensions = [ - # {"label": param, "values": plot_df[param].values.tolist()} for param in params - # ] - output_element = [] - print(plot_df[color_col].values.tolist()) - - for param in dimentions_params: - data = { - 'x': list(range(0,100)), - 'y': dimensions[param], - 'type': 'line', - 'name': param, - 'line': { - # "color": plot_df[color_col].values.tolist(), - # "colorscale": colormap, - # "showscale": True, - }, - } - layout = {} - layout.update(theme["layout"]) - width = len(dimensions) * 100 + 250 - margin_b = max([len(param) for param in params]) * 8 - layout.update({ - "paper_bgcolor": "rgba(0,0,0,0)", - "plot_bgcolor": "rgba(0,0,0,0)", - "margin": {"b": 0, "t": 0, 'r': '6vh'}, - 'xaxis': { - 'visible': False, - 'showticklabels': False}, - 'yaxis': { - 'visible': True, - 'showticklabels': True}, - }, - ) - layout.update ({ - 'title':{ - 'text': param, - 'y': '0.5', - 'x':'0', - 'font':{'size':'8'} - } - }) - - figure = {'data':[data], 'layout':layout} - output_element.append( - wcc.Graph( - id = "run-time-analysis-fmu-graph"+param, - figure = figure, - style={"height": '10vh'}, - ) - ) - - # if color_col == "STATUS_BOOL": - # data["line"].update( - # { - # "cmin": -0.5, - # "cmax": 1.5, - # "colorbar": { - # "tickvals": [0, 1], - # "ticktext": colormap_labels, - # "title": "Status", - # "xanchor": "right", - # "x": -0.02, - # "len": 0.3, - # }, - # }, - # ) - # else: - # data["line"].update( - # { - # "colorbar": { - # "title": "Running time", - # "xanchor": "right", - # "x": -0.02, - # }, - # }, - # ) - - - - - - return output_element - - # # Parcoords data dict - # data: dict = { - # "line": { - # "color": plot_df[color_col].values.tolist(), - # "colorscale": colormap, - # "showscale": True, - # }, - # "dimensions": dimensions, - # "labelangle": -90, - # "labelside": "bottom", - # "type": "line" - # } - # if color_col == "STATUS_BOOL": - # data["line"].update( - # { - # "cmin": -0.5, - # "cmax": 1.5, - # "colorbar": { - # "tickvals": [0, 1], - # "ticktext": colormap_labels, - # "title": "Status", - # "xanchor": "right", - # "x": -0.02, - # "len": 0.3, - # }, - # }, - # ) - # else: - # data["line"].update( - # { - # "colorbar": { - # "title": "Running time", - # "xanchor": "right", - # "x": -0.02, - # }, - # }, - # ) - - - - # # Parcoords data dict - # data: dict = { - # "line": { - # "color": plot_df[color_col].values.tolist(), - # "colorscale": colormap, - # "showscale": True, - # }, - # "dimensions": dimensions, - # "labelangle": -90, - # "labelside": "bottom", - # "type": "parcoords" - # } - # if color_col == "STATUS_BOOL": - # data["line"].update( - # { - # "cmin": -0.5, - # "cmax": 1.5, - # "colorbar": { - # "tickvals": [0, 1], - # "ticktext": colormap_labels, - # "title": "Status", - # "xanchor": "right", - # "x": -0.02, - # "len": 0.3, - # }, - # }, - # ) - # else: - # data["line"].update( - # { - # "colorbar": { - # "title": "Running time", - # "xanchor": "right", - # "x": -0.02, - # }, - # }, - # ) - - # layout = {} - # layout.update(theme["layout"]) - # # Ensure sufficient spacing between each dimension and margin for labels - # width = len(dimensions) * 100 + 250 - # margin_b = max([len(param) for param in params]) * 8 - # layout.update({"width": width, "height": 800, "margin": {"b": margin_b, "t": 30}}) - - #return {"data": [data], "layout": layout} - -@CACHE.memoize(timeout=CACHE.TIMEOUT) -def make_colormap(color_array: list, discrete: int = None) -> list: - """ - Returns a colormap: - * If the `discrete` variable is set to an integer x, the colormap will be a discrete map of - size x evenly sampled from the given color_array. - * If discrete not defined or `None`: assumes continuous colormap and returns the given - color_array. - """ - if discrete is None: - colormap = color_array - else: - colormap = [] - for i in range(0, discrete): - colormap.append([i / discrete, color_array[i]]) - colormap.append([(i + 1) / discrete, color_array[i]]) - return colormap + # Otherwise: parallel coordinates + # Ensure selected parameters is a list + params = params if isinstance(params, list) else [params] + # Color by success or runtime, for runtime drop unsuccesful + colormap_labels: Union[List[str], None] + if coloring == "Successful/failed realization": + plot_df = self.real_status_df[ + self.real_status_df["ENSEMBLE"] == ens + ] + colormap = make_colormap( + self.plotly_theme["layout"]["colorway"], discrete=2 + ) + color_by_col = "STATUS_BOOL" + colormap_labels = ["Failed", "Success"] + else: + plot_df = self.real_status_df[ + (self.real_status_df["ENSEMBLE"] == ens) + & (self.real_status_df["STATUS_BOOL"] == 1) + ] + colormap = self.plotly_theme["layout"]["colorscale"]["sequential"] + color_by_col = "RUNTIME" + colormap_labels = None + + # Call rendering of parallel coordinate plot + plot_info = render_parcoord( + plot_df, + params, + self.plotly_theme, + colormap, + color_by_col, + remove_constant, + colormap_labels, + ) + return wcc.Graph(figure=plot_info) diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view_elements.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view_elements.py index 969293612..7db8bbf02 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view_elements.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view_elements.py @@ -149,6 +149,65 @@ def render_parcoord( return {"data": [data], "layout": layout} +# dimensions = {} +# # dimentions_params = [] + +# if remove_constant == ["remove_constant"]: +# for param in params: +# if len(np.unique(plot_df[param].values)) > 1: +# dimentions_params.append(param) +# else: +# dimentions_params = params + +# for param in dimentions_params: +# dimensions[param] = plot_df[param].values.tolist() + +# # dimensions = [ +# # {"label": param, "values": plot_df[param].values.tolist()} for param in params +# # ] +# # +# # print(plot_df[color_col].values.tolist()) +# output_element = [] +# for param in dimentions_params: +# data = { +# "x": list(range(0, 100)), +# "y": dimensions[param], +# "type": "line", +# "name": param, +# "line": { +# # "color": plot_df[color_col].values.tolist(), +# # "colorscale": colormap, +# # "showscale": True, +# }, +# } +# layout = {} +# layout.update(theme["layout"]) +# width = len(dimensions) * 100 + 250 +# margin_b = max([len(param) for param in params]) * 8 +# layout.update( +# { +# "paper_bgcolor": "rgba(0,0,0,0)", +# "plot_bgcolor": "rgba(0,0,0,0)", +# "margin": {"b": 0, "t": 0, "r": "6vh"}, +# "xaxis": {"visible": False, "showticklabels": False}, +# "yaxis": {"visible": True, "showticklabels": True}, +# }, +# ) +# layout.update( +# {"title": {"text": param, "y": "0.5", "x": "0", "font": {"size": "8"}}} +# ) + +# figure = {"data": [data], "layout": layout} +# output_element.append( +# wcc.Graph( +# id="run-time-analysis-fmu-graph" + param, +# figure=figure, +# style={"height": "10vh"}, +# ) +# ) +# return output_element + + @CACHE.memoize(timeout=CACHE.TIMEOUT) def make_colormap(color_array: list, discrete: int = None) -> list: """ From ed67e632b580980a1271fbc9d07e79953b583e6b Mon Sep 17 00:00:00 2001 From: "Jincheng Liu (OG SUB RPE)" Date: Tue, 26 Jul 2022 12:30:56 +0200 Subject: [PATCH 09/15] add tour steps --- .../_running_time_analysis_fmu/_plugin.py | 26 +++++++++++++++++++ .../_running_time_analysis_fmu/_view.py | 2 +- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py index 466e23584..e6c996dd2 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py @@ -9,6 +9,7 @@ from ..._datainput.fmu_input import load_ensemble_set, load_parameters from ._plugin_ids import PluginIds +from ._shared_settings import RunningTimeAnalysisFmuSettings from ._view import RunTimeAnalysisGraph @@ -118,6 +119,31 @@ def __init__( PluginIds.RunTimeAnalysisView.GROUP_NAME, ) + @property + def tour_steps(self) -> List[dict]: + return [ + { + "id": self.view(PluginIds.RunTimeAnalysisView.RUN_TIME_FMU) + .settings_group(RunTimeAnalysisGraph.Ids.RUN_TIME_SETTINGS) + .component_unique_id(RunningTimeAnalysisFmuSettings.Ids.MODE), + "content": ( + "Switch between job running time matrix and parameter parallel coordinates." + ), + }, + { + "id": self.view(PluginIds.RunTimeAnalysisView.RUN_TIME_FMU) + .settings_group(RunTimeAnalysisGraph.Ids.RUN_TIME_SETTINGS) + .component_unique_id(RunningTimeAnalysisFmuSettings.Ids.ENSEMBLE), + "content": ("Display the realizations from the selected ensemble. "), + }, + { + "id": self.view(PluginIds.RunTimeAnalysisView.RUN_TIME_FMU) + .settings_group(RunTimeAnalysisGraph.Ids.RUN_TIME_SETTINGS) + .component_unique_id(RunningTimeAnalysisFmuSettings.Ids.COLORING), + "content": ("Make the colorscale relative to the selected option."), + }, + ] + @property def parameters(self) -> List[str]: """Returns numerical input parameters""" diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py index 95fc58add..2f013880a 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py @@ -26,7 +26,7 @@ def __init__( plugin_paratamters: List[str], filter_shorter: Union[int, float] = 10, ) -> None: - super().__init__("Group tree") + super().__init__("Settings") self.plotly_theme = plotly_theme self.job_status_df = job_status_df From 0bc8735fcb0a911762179638fe76685d7d1ff0ab Mon Sep 17 00:00:00 2001 From: "Jincheng Liu (OG SUB RPE)" Date: Tue, 26 Jul 2022 12:37:02 +0200 Subject: [PATCH 10/15] reformating --- .../plugins/_running_time_analysis_fmu/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/__init__.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/__init__.py index 3c7fe1ca7..467f67a6e 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/__init__.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/__init__.py @@ -1 +1 @@ -from ._plugin import RunningTimeAnalysisFMU \ No newline at end of file +from ._plugin import RunningTimeAnalysisFMU From 2b2029d1daaffd74fa32fe24d6be7c9ad4af90eb Mon Sep 17 00:00:00 2001 From: "Jincheng Liu (OG SUB RPE)" Date: Tue, 26 Jul 2022 12:44:16 +0200 Subject: [PATCH 11/15] reformatting --- .../plugins/_running_time_analysis_fmu/_plugin.py | 3 ++- .../plugins/_running_time_analysis_fmu/_shared_settings.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py index e6c996dd2..3cbda0b3b 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py @@ -29,7 +29,8 @@ class RunningTimeAnalysisFMU(WebvizPluginABC): --- - * **`ensembles`:** Which ensembles in `shared_settings` to include in check. Only required input. + * **`ensembles`:** Which ensembles in `shared_settings` to include in check. + Only required input. * **`filter_shorter`:** Filters jobs with maximum run time in ensemble less than X seconds \ (default: 10). Can be checked on/off interactively, this only sets the filtering value. * **`status_file`:** Name of json file local per realization with job status \ diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py index 8f5e034ba..fddda3f32 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py @@ -138,7 +138,7 @@ def set_callbacks(self) -> None: def _update_filter_parameters( remove_constant: str, ens: str, coloring: str ) -> Tuple: - if remove_constant == None: + if remove_constant is None: raise PreventUpdate dimentions_params = [] From 4fbe341e9dda695bb9675d9af4de580022df0776 Mon Sep 17 00:00:00 2001 From: "Jincheng Liu (OG SUB RPE)" Date: Tue, 26 Jul 2022 12:49:16 +0200 Subject: [PATCH 12/15] reformatting --- webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py index 3cbda0b3b..73e9a2d56 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py @@ -29,7 +29,7 @@ class RunningTimeAnalysisFMU(WebvizPluginABC): --- - * **`ensembles`:** Which ensembles in `shared_settings` to include in check. + * **`ensembles`:** Which ensembles in `shared_settings` to include in check. Only required input. * **`filter_shorter`:** Filters jobs with maximum run time in ensemble less than X seconds \ (default: 10). Can be checked on/off interactively, this only sets the filtering value. From afd77553dd7037da7375adc5aa2130182efe684f Mon Sep 17 00:00:00 2001 From: "Jincheng Liu (OG SUB RPE)" Date: Wed, 27 Jul 2022 10:53:22 +0200 Subject: [PATCH 13/15] flip axis --- .../_running_time_analysis_fmu/_view.py | 74 ++++++++++--------- .../_view_elements.py | 36 +++++++-- 2 files changed, 68 insertions(+), 42 deletions(-) diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py index 2f013880a..017248917 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py @@ -1,3 +1,4 @@ +from distutils.command.config import config from typing import List, Union import pandas as pd @@ -37,7 +38,8 @@ def __init__( self.visual_parameters = visual_parameters self.plugin_parameters = plugin_paratamters - self.add_column(self.Ids.RUNTIME_ANALYSIS) + column = self.add_column() + column.make_row(self.Ids.RUNTIME_ANALYSIS, flex_grow=4) self.add_settings_group( RunningTimeAnalysisFmuSettings( @@ -128,38 +130,42 @@ def _update_fig( self.plotly_theme, ) + return wcc.Graph(figure=plot_info) + # Otherwise: parallel coordinates + # Ensure selected parameters is a list + params = params if isinstance(params, list) else [params] + # Color by success or runtime, for runtime drop unsuccesful + colormap_labels: Union[List[str], None] + if coloring == "Successful/failed realization": + plot_df = self.real_status_df[self.real_status_df["ENSEMBLE"] == ens] + colormap = make_colormap( + self.plotly_theme["layout"]["colorway"], discrete=2 + ) + color_by_col = "STATUS_BOOL" + colormap_labels = ["Failed", "Success"] else: - # Otherwise: parallel coordinates - # Ensure selected parameters is a list - params = params if isinstance(params, list) else [params] - # Color by success or runtime, for runtime drop unsuccesful - colormap_labels: Union[List[str], None] - if coloring == "Successful/failed realization": - plot_df = self.real_status_df[ - self.real_status_df["ENSEMBLE"] == ens - ] - colormap = make_colormap( - self.plotly_theme["layout"]["colorway"], discrete=2 - ) - color_by_col = "STATUS_BOOL" - colormap_labels = ["Failed", "Success"] - else: - plot_df = self.real_status_df[ - (self.real_status_df["ENSEMBLE"] == ens) - & (self.real_status_df["STATUS_BOOL"] == 1) - ] - colormap = self.plotly_theme["layout"]["colorscale"]["sequential"] - color_by_col = "RUNTIME" - colormap_labels = None + plot_df = self.real_status_df[ + (self.real_status_df["ENSEMBLE"] == ens) + & (self.real_status_df["STATUS_BOOL"] == 1) + ] + colormap = self.plotly_theme["layout"]["colorscale"]["sequential"] + color_by_col = "RUNTIME" + colormap_labels = None - # Call rendering of parallel coordinate plot - plot_info = render_parcoord( - plot_df, - params, - self.plotly_theme, - colormap, - color_by_col, - remove_constant, - colormap_labels, - ) - return wcc.Graph(figure=plot_info) + # Call rendering of parallel coordinate plot + plot_info = render_parcoord( + plot_df, + params, + self.plotly_theme, + colormap, + color_by_col, + remove_constant, + colormap_labels, + ) + + return wcc.Graph( + figure=plot_info, style={"transform": "rotate(90deg)", "width": 900} + ) + + +# diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view_elements.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view_elements.py index 7db8bbf02..03d4e44d7 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view_elements.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view_elements.py @@ -121,10 +121,14 @@ def render_parcoord( "colorbar": { "tickvals": [0, 1], "ticktext": colormap_labels, - "title": "Status", - "xanchor": "right", - "x": -0.02, - "len": 0.3, + "tickangle": -90, + "title": {"text": "Status", "side": "top"}, + "orientation": "h", + "yanchor": "bottom", + "y": 0.6, + "xanchor": "left", + "x": 0, + "len": 0.1, }, }, ) @@ -132,9 +136,19 @@ def render_parcoord( data["line"].update( { "colorbar": { - "title": "Running time", - "xanchor": "right", - "x": -0.02, + "tickvals": [0, 1], + "ticktext": colormap_labels, + "tickangle": -90, + "title": {"text": "Running time", "side": "top"}, + "orientation": "h", + "yanchor": "bottom", + "y": 0.6, + "xanchor": "left", + "x": 0, + "len": 0.1, + # "title": "Running time", + # "xanchor": "right", + # "x": -0.02, }, }, ) @@ -144,7 +158,13 @@ def render_parcoord( # Ensure sufficient spacing between each dimension and margin for labels width = len(dimensions) * 100 + 250 margin_b = max([len(param) for param in params]) * 8 - layout.update({"width": width, "height": 800, "margin": {"b": margin_b, "t": 30}}) + layout.update( + { + "width": width, + "height": 800, + "margin": {"b": margin_b, "t": 30}, + }, + ) return {"data": [data], "layout": layout} From 7d1c37f5cf0d0ddb6db86b94c27fc3b1a55d8721 Mon Sep 17 00:00:00 2001 From: "Jincheng Liu (OG SUB RPE)" Date: Wed, 27 Jul 2022 11:02:25 +0200 Subject: [PATCH 14/15] formatting --- webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py | 1 - 1 file changed, 1 deletion(-) diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py index 017248917..b4231aa3f 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py @@ -1,4 +1,3 @@ -from distutils.command.config import config from typing import List, Union import pandas as pd From 816aef43d734aabd4c3fbf25ae9b82a0be01e924 Mon Sep 17 00:00:00 2001 From: "Jincheng Liu (OG SUB RPE)" Date: Tue, 2 Aug 2022 10:46:26 +0200 Subject: [PATCH 15/15] rebuild the figure --- .../_running_time_analysis_fmu/_plugin.py | 34 +++---- .../_running_time_analysis_fmu/_plugin_ids.py | 4 + .../_shared_settings.py | 16 ++-- .../_running_time_analysis_fmu/_view.py | 30 ++++--- .../_view_elements.py | 89 ++----------------- 5 files changed, 51 insertions(+), 122 deletions(-) diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py index 73e9a2d56..176f0fadc 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin.py @@ -1,8 +1,10 @@ import json -from typing import Callable, List, Optional, Tuple, Union +from typing import Callable, List, Optional, Tuple, Type, Union import numpy as np import pandas as pd +from dash import html +from dash.development.base_component import Component from webviz_config import WebvizPluginABC, WebvizSettings from webviz_config.common_cache import CACHE from webviz_config.webviz_store import webvizstore @@ -82,29 +84,9 @@ def __init__( ) self.plugin_parameters = self.parameters - # self.add_store(PluginIds.Stores.MODE, WebvizPluginABC.StorageType.SESSION) - # self.add_store(PluginIds.Stores.ENSEMBLE, WebvizPluginABC.StorageType.SESSION) - # self.add_store(PluginIds.Stores.COLORING, WebvizPluginABC.StorageType.SESSION) - # self.add_store( - # PluginIds.Stores.FILTERING_SHORT, WebvizPluginABC.StorageType.SESSION - # ) - # self.add_store( - # PluginIds.Stores.FILTERING_PARAMS, WebvizPluginABC.StorageType.SESSION - # ) - # self.add_store( - # PluginIds.Stores.REMOVE_CONSTANT, WebvizPluginABC.StorageType.SESSION - # ) - # self.add_store(PluginIds.Stores.ACTIVE_ID, WebvizPluginABC.StorageType.SESSION) - - # self.add_shared_settings_group( - # RunningTimeAnalysisFmuSettings( - # self.ensembles, - # self.visual_parameters, - # self.plugin_parameters, - # self.filter_shorter, - # ), - # PluginIds.SharedSettings.SHARED_SETTINGS_GROUP, - # ) + self.add_store( + PluginIds.Stores.VIEW_ELEMENT_HEIGHT, WebvizPluginABC.StorageType.SESSION + ) self.add_view( RunTimeAnalysisGraph( @@ -120,6 +102,10 @@ def __init__( PluginIds.RunTimeAnalysisView.GROUP_NAME, ) + @property + def layout(self) -> Type[Component]: + return html.Div("No view is loaded.") + @property def tour_steps(self) -> List[dict]: return [ diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin_ids.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin_ids.py index a21d98e91..786b53f78 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin_ids.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_plugin_ids.py @@ -1,4 +1,8 @@ class PluginIds: + # pylint: disable=too-few-public-methods + class Stores: + VIEW_ELEMENT_HEIGHT = "view-element-height" + # pylint: disable=too-few-public-methods class SharedSettings: SHARED_SETTINGS_GROUP = "shared-settings-group" diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py index fddda3f32..c5d9272e4 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_shared_settings.py @@ -16,6 +16,7 @@ class Ids: MODE = "mode-1" ENSEMBLE = "ensemble" COLORING = "coloring" + COLOR_LABEL = "color-label" FILTERING = "filtering" FILTER_SHORT = "filter-short" REMOVE_CONSTANT = "remove-constant" @@ -70,8 +71,10 @@ def layout(self) -> List[Component]: value=self.ensembles[0], clearable=False, ), + wcc.Label( + id=self.register_component_unique_id(self.Ids.COLOR_LABEL), + ), wcc.Dropdown( - label="Color jobs relative to running time of:", id=self.register_component_unique_id(self.Ids.COLORING), options=[ {"label": rel, "value": rel} for rel in self.COLOR_MATRIX_BY_LABELS @@ -177,14 +180,16 @@ def _update_filter_parameters( self.component_unique_id(self.Ids.FILTER_PARAMS).to_string(), "style", ), - Output(self.component_unique_id(self.Ids.COLORING).to_string(), "label"), + Output( + self.component_unique_id(self.Ids.COLOR_LABEL).to_string(), "children" + ), Output(self.component_unique_id(self.Ids.COLORING).to_string(), "options"), Output(self.component_unique_id(self.Ids.COLORING).to_string(), "value"), Input(self.component_unique_id(self.Ids.MODE).to_string(), "value"), ) def _update_color(selected_mode: str) -> Tuple: - label = None - value = None + # label = None + # value = None if selected_mode == "running_time_matrix": label = "Color jobs relative to running time of:" @@ -202,7 +207,6 @@ def _update_color(selected_mode: str) -> Tuple: value, ) - label = "Color realizations relative to:" options = [ {"label": rel, "value": rel} for rel in self.COLOR_PARCOORD_BY_LABELS ] @@ -212,7 +216,7 @@ def _update_color(selected_mode: str) -> Tuple: {"display": "none"}, {"display": "block"}, {"display": "block"}, - label, + "Color realizations relative to:", options, value, ) diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py index b4231aa3f..20741ef2a 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view.py @@ -107,11 +107,10 @@ def _update_fig( """Update main figure Dependent on `mode` it will call rendering of the chosen form of visualization """ - plot_info = None - if mode == "running_time_matrix": - if "filter_short" in filter_short: - plot_info = render_matrix( + if mode == "running_time_matrix" and "filter_short" in filter_short: + return wcc.Graph( + figure=render_matrix( self.job_status_df[ (self.job_status_df["ENSEMBLE"] == ens) & ( @@ -122,19 +121,22 @@ def _update_fig( coloring, self.plotly_theme, ) - else: - plot_info = render_matrix( + ) + if mode == "running_time_matrix": + return wcc.Graph( + figure=render_matrix( self.job_status_df[(self.job_status_df["ENSEMBLE"] == ens)], coloring, self.plotly_theme, ) + ) - return wcc.Graph(figure=plot_info) # Otherwise: parallel coordinates # Ensure selected parameters is a list params = params if isinstance(params, list) else [params] # Color by success or runtime, for runtime drop unsuccesful colormap_labels: Union[List[str], None] + if coloring == "Successful/failed realization": plot_df = self.real_status_df[self.real_status_df["ENSEMBLE"] == ens] colormap = make_colormap( @@ -163,8 +165,14 @@ def _update_fig( ) return wcc.Graph( - figure=plot_info, style={"transform": "rotate(90deg)", "width": 900} + figure=plot_info, + style={ + "display": "block", + "transform": "rotate(90deg)", + "width": 900, + "margin": { + "b": 0, + "r": 30, + }, + }, ) - - -# diff --git a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view_elements.py b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view_elements.py index 03d4e44d7..06972db2b 100644 --- a/webviz_subsurface/plugins/_running_time_analysis_fmu/_view_elements.py +++ b/webviz_subsurface/plugins/_running_time_analysis_fmu/_view_elements.py @@ -35,7 +35,7 @@ def render_matrix(status_df: pd.DataFrame, rel: str, theme: dict) -> dict: "ticktext": [ "0 %", "50 %", - "100 %", + "100%", ], "xanchor": "left", }, @@ -122,13 +122,9 @@ def render_parcoord( "tickvals": [0, 1], "ticktext": colormap_labels, "tickangle": -90, - "title": {"text": "Status", "side": "top"}, - "orientation": "h", - "yanchor": "bottom", - "y": 0.6, - "xanchor": "left", - "x": 0, - "len": 0.1, + "title": {"text": "Status", "side": "right"}, + "xanchor": "right", + "x": -0.005, }, }, ) @@ -136,19 +132,10 @@ def render_parcoord( data["line"].update( { "colorbar": { - "tickvals": [0, 1], - "ticktext": colormap_labels, + "title": {"text": "Running time", "side": "right"}, "tickangle": -90, - "title": {"text": "Running time", "side": "top"}, - "orientation": "h", - "yanchor": "bottom", - "y": 0.6, - "xanchor": "left", - "x": 0, - "len": 0.1, - # "title": "Running time", - # "xanchor": "right", - # "x": -0.02, + "xanchor": "right", + "x": -0.005, }, }, ) @@ -163,71 +150,11 @@ def render_parcoord( "width": width, "height": 800, "margin": {"b": margin_b, "t": 30}, - }, + } ) - return {"data": [data], "layout": layout} -# dimensions = {} -# # dimentions_params = [] - -# if remove_constant == ["remove_constant"]: -# for param in params: -# if len(np.unique(plot_df[param].values)) > 1: -# dimentions_params.append(param) -# else: -# dimentions_params = params - -# for param in dimentions_params: -# dimensions[param] = plot_df[param].values.tolist() - -# # dimensions = [ -# # {"label": param, "values": plot_df[param].values.tolist()} for param in params -# # ] -# # -# # print(plot_df[color_col].values.tolist()) -# output_element = [] -# for param in dimentions_params: -# data = { -# "x": list(range(0, 100)), -# "y": dimensions[param], -# "type": "line", -# "name": param, -# "line": { -# # "color": plot_df[color_col].values.tolist(), -# # "colorscale": colormap, -# # "showscale": True, -# }, -# } -# layout = {} -# layout.update(theme["layout"]) -# width = len(dimensions) * 100 + 250 -# margin_b = max([len(param) for param in params]) * 8 -# layout.update( -# { -# "paper_bgcolor": "rgba(0,0,0,0)", -# "plot_bgcolor": "rgba(0,0,0,0)", -# "margin": {"b": 0, "t": 0, "r": "6vh"}, -# "xaxis": {"visible": False, "showticklabels": False}, -# "yaxis": {"visible": True, "showticklabels": True}, -# }, -# ) -# layout.update( -# {"title": {"text": param, "y": "0.5", "x": "0", "font": {"size": "8"}}} -# ) - -# figure = {"data": [data], "layout": layout} -# output_element.append( -# wcc.Graph( -# id="run-time-analysis-fmu-graph" + param, -# figure=figure, -# style={"height": "10vh"}, -# ) -# ) -# return output_element - - @CACHE.memoize(timeout=CACHE.TIMEOUT) def make_colormap(color_array: list, discrete: int = None) -> list: """