Esempio n. 1
0
def multi_response_controller(parent: WebvizErtPluginABC,
                              app: dash.Dash) -> None:
    @app.callback(
        Output(
            {
                "index": MATCH,
                "id": parent.uuid("response-graphic"),
                "type": parent.uuid("graph"),
            },
            "figure",
        ),
        [
            Input({
                "index": MATCH,
                "type": parent.uuid("plot-type")
            }, "value"),
            Input(parent.uuid("ensemble-selection-store"),
                  "modified_timestamp"),
        ],
        [
            State(parent.uuid("ensemble-selection-store"), "data"),
            State({
                "index": MATCH,
                "type": parent.uuid("response-id-store")
            }, "data"),
        ],
    )
    def update_graph(
        plot_type: str,
        _: Any,
        selected_ensembles: Optional[Mapping[int, Dict]],
        response: Optional[str],
    ) -> go.Figure:
        if response in [None, ""] or not selected_ensembles:
            raise PreventUpdate

        def _generate_plot(ensemble_id: int,
                           color: str) -> Optional[ResponsePlotModel]:
            ensemble = load_ensemble(parent, ensemble_id)
            if response not in ensemble.responses:
                return None
            plot = _create_response_plot(ensemble.responses[response],
                                         plot_type, [], color)
            return plot

        response_plots = [
            _generate_plot(ensemble_id, data["color"])
            for ensemble_id, data in selected_ensembles.items()
        ]

        fig = go.Figure()
        for plot in filter(None, response_plots):
            for realization in plot._realization_plots:
                fig.add_trace(realization.repr)
        for plot in filter(None, response_plots):
            for observation in plot._observations:
                fig.add_trace(observation.repr)
        fig.update_layout(assets.ERTSTYLE["figure"]["layout"])
        return fig
def parameter_comparison_controller(parent: WebvizErtPluginABC,
                                    app: dash.Dash,
                                    suffix: str = "") -> None:
    @app.callback(
        Output(
            {
                "id": parent.uuid("parallel-coor"),
                "type": parent.uuid("graph")
            },
            "figure",
        ),
        [
            Input(parent.uuid("parameter-selection-store-" + suffix),
                  "modified_timestamp"),
            Input(parent.uuid("ensemble-selection-store"),
                  "modified_timestamp"),
        ],
        [
            State(parent.uuid("parameter-selection-store-" + suffix), "data"),
            State(parent.uuid("ensemble-selection-store"), "data"),
        ],
    )
    def update_parallel_coor(
        _: Any,
        __: Any,
        selected_parameters: Optional[List[str]],
        selected_ensembles: Optional[Mapping[int, Dict]],
    ) -> go.Figure:
        if not selected_ensembles or not selected_parameters:
            raise PreventUpdate
        selected_parameters = [] if not selected_parameters else selected_parameters

        data = {}
        colors = {}
        for idx, (ensemble_id, color) in enumerate(selected_ensembles.items()):
            ensemble = load_ensemble(parent, ensemble_id)
            ens_key = str(ensemble)
            df = ensemble.parameters_df(selected_parameters)
            df["ensemble_id"] = idx
            data[ens_key] = df.copy()
            colors[ens_key] = color["color"]
        parallel_plot = ParallelCoordinatesPlotModel(data, colors)

        return parallel_plot.repr
Esempio n. 3
0
def parameter_selector_controller(
    parent: WebvizErtPluginABC,
    app: dash.Dash,
    suffix: str = "",
    union_keys: bool = True,
    extra_input: bool = False,
) -> None:
    parameter_selector_multi_id = parent.uuid(
        f"parameter-selector-multi-{suffix}")
    parameter_type_store_id = parent.uuid(f"parameter-type-store-{suffix}")
    parameter_selector_filter_id = parent.uuid(
        f"parameter-selector-filter-{suffix}")
    parameter_deactivator_id = parent.uuid(f"parameter-deactivator-{suffix}")
    parameter_selection_store_id = parent.uuid(
        f"parameter-selection-store-{suffix}")
    options_inputs = [
        Input(parent.uuid("ensemble-selection-store"), "data"),
        Input(parameter_selector_filter_id, "value"),
        Input(parameter_deactivator_id, "value"),
    ]
    if extra_input:
        options_inputs.extend(
            [Input(parent.uuid("response-observations-check"), "value")])

    @app.callback(
        [
            Output(parameter_selector_multi_id, "options"),
            Output(parameter_selector_multi_id, "value"),
        ],
        options_inputs,
        [State(parameter_type_store_id, "data")],
    )
    def update_parameters_options(
        selected_ensembles: Optional[Mapping[str, Dict]],
        filter_search: str,
        selected: Optional[List[str]],
        *args: List[str],
    ) -> Tuple[List[Dict], List[str]]:
        if not selected_ensembles:
            raise PreventUpdate
        store_type = args[0]
        response_filter = []
        if extra_input:
            store_type = args[1]
            response_filter = args[0]
        selected = [] if not selected else selected
        ensembles = [
            load_ensemble(parent, ensemble_id)
            for ensemble_id in selected_ensembles
        ]
        options = None
        if store_type == "parameter":
            options = parameter_options(ensembles, union_keys=union_keys)
        elif store_type == "response":
            options = response_options(response_filter, ensembles)
        else:
            raise ValueError(f"Undefined parameter type {store_type}")

        options = [
            option for option in options if option["value"] not in selected
        ]
        if bool(filter_search):
            options = [
                option for option in options
                if _filter_match(filter_search, option["value"])
            ]

        return options, selected

    @app.callback(
        Output(parameter_selection_store_id, "data"),
        [
            Input(parameter_selector_multi_id, "value"),
            Input(parameter_selector_filter_id, "n_submit"),
        ],
        [
            State(parameter_deactivator_id, "value"),
            State(parameter_selector_multi_id, "options"),
        ],
    )
    def update_parameter_selection(
        parameters: List[str],
        _: int,
        selected_params: Optional[List[str]],
        par_opts: List[Dict],
    ) -> Optional[List[str]]:
        selected_params = [] if not selected_params else selected_params
        parameters = [] if not parameters else parameters

        ctx = dash.callback_context
        triggered_id = ctx.triggered[0]["prop_id"].split(".")[0]
        if triggered_id == parameter_selector_filter_id:
            parameters = [
                parm["value"] for parm in par_opts
                if parm["value"] not in selected_params
            ]
            if not parameters:
                raise PreventUpdate
        elif triggered_id == parameter_selector_multi_id:
            parameters = [
                parameter for parameter in parameters
                if parameter not in selected_params
            ]
        return selected_params + parameters

    @app.callback(
        [
            Output(parameter_deactivator_id, "options"),
            Output(parameter_deactivator_id, "value"),
        ],
        [
            Input(parameter_selection_store_id, "modified_timestamp"),
        ],
        [
            State(parameter_selection_store_id, "data"),
        ],
    )
    def update_parameter_options(
            _: Any, shown_parameters: Optional[List[str]]
    ) -> Tuple[List[Dict], List[str]]:
        shown_parameters = [] if not shown_parameters else shown_parameters
        selected_opts = [{
            "label": param,
            "value": param
        } for param in shown_parameters]
        return selected_opts, shown_parameters

    container_parameter_selector_multi_id = parent.uuid(
        f"container-parameter-selector-multi-{suffix}")
    parameter_selector_button_id = parent.uuid(
        f"parameter-selector-button-{suffix}")

    @app.callback(
        Output(container_parameter_selector_multi_id, "className"),
        [
            Input(parameter_selector_button_id, "n_clicks"),
        ],
        [
            State(container_parameter_selector_multi_id, "className"),
        ],
    )
    def toggle_selector_visibility(_: int, class_name: str) -> str:
        ctx = dash.callback_context
        triggered_id = ctx.triggered[0]["prop_id"].split(".")[0]
        if triggered_id == parameter_selector_button_id:
            if class_name == "ert-parameter-selector-container-hide":
                class_name = "ert-parameter-selector-container-show"
            else:
                class_name = "ert-parameter-selector-container-hide"
        return class_name
Esempio n. 4
0
def multi_parameter_controller(parent: WebvizErtPluginABC,
                               app: dash.Dash) -> None:
    @app.callback(
        Output({
            "index": MATCH,
            "type": parent.uuid("bincount-store")
        }, "data"),
        [
            Input({
                "index": MATCH,
                "type": parent.uuid("hist-bincount")
            }, "value")
        ],
        [
            State({
                "index": MATCH,
                "type": parent.uuid("bincount-store")
            }, "data")
        ],
    )
    def update_bincount(hist_bincount: int, store_bincount: int) -> int:
        if not isinstance(hist_bincount, int):
            raise PreventUpdate
        if hist_bincount < 2:
            raise PreventUpdate
        if hist_bincount == store_bincount:
            raise PreventUpdate
        return hist_bincount

    @app.callback(
        [
            Output(
                {
                    "index": MATCH,
                    "id": parent.uuid("parameter-scatter"),
                    "type": parent.uuid("graph"),
                },
                "figure",
            ),
            Output({
                "index": MATCH,
                "type": parent.uuid("hist-bincount")
            }, "value"),
        ],
        [
            Input({
                "index": MATCH,
                "type": parent.uuid("hist-check")
            }, "value"),
            Input(
                {
                    "index": MATCH,
                    "type": parent.uuid("bincount-store")
                },
                "modified_timestamp",
            ),
            Input(parent.uuid("ensemble-selection-store"),
                  "modified_timestamp"),
            Input(parent.uuid("param-label-check"), "value"),
        ],
        [
            State(parent.uuid("ensemble-selection-store"), "data"),
            State({
                "index": MATCH,
                "type": parent.uuid("parameter-id-store")
            }, "data"),
            State({
                "index": MATCH,
                "type": parent.uuid("bincount-store")
            }, "data"),
        ],
    )
    def update_histogram(
        hist_check_values: List[str],
        _: Any,
        __: Any,
        legend: List[str],
        selected_ensembles: Optional[Mapping[int, Dict]],
        parameter: str,
        bin_count: int,
    ) -> Tuple[go.Figure, int]:
        if not selected_ensembles:
            raise PreventUpdate

        data = {}
        colors = {}
        names = {}
        priors = {}
        for ensemble_id, color in selected_ensembles.items():
            ensemble = load_ensemble(parent, ensemble_id)
            if ensemble.parameters and parameter in ensemble.parameters:
                key = str(ensemble)
                parameter_model = ensemble.parameters[parameter]
                data[key] = parameter_model.data_df()
                colors[key] = color["color"]
                names[key] = repr(ensemble) if "label" in legend else ""

                if parameter_model.priors and "prior" in hist_check_values:
                    priors[names[key]] = (parameter_model.priors, colors[key])

        parameter_plot = MultiHistogramPlotModel(
            data,
            names=names,
            colors=colors,
            hist="hist" in hist_check_values,
            kde="kde" in hist_check_values,
            priors=priors,
            bin_count=bin_count,
        )
        return parameter_plot.repr, parameter_plot.bin_count

    @app.callback(
        Output({
            "index": MATCH,
            "type": parent.uuid("hist-check")
        }, "options"),
        [
            Input({
                "index": MATCH,
                "type": parent.uuid("parameter-id-store")
            }, "data"),
        ],
        [
            State({
                "index": MATCH,
                "type": parent.uuid("hist-check")
            }, "options"),
            State(parent.uuid("ensemble-selection-store"), "data"),
        ],
    )
    def set_parameter_from_btn(
        parameter: str,
        plotting_options: List[Mapping[str, str]],
        selected_ensembles: Optional[Mapping[int, Dict]],
    ) -> List[Mapping[str, str]]:
        if not selected_ensembles:
            raise PreventUpdate
        has_priors = False
        for ensemble_id, _ in selected_ensembles.items():
            ensemble = load_ensemble(parent, ensemble_id)
            if ensemble.parameters and parameter in ensemble.parameters:
                parameter_model = ensemble.parameters[parameter]
                if parameter_model.priors:
                    has_priors = True
                    break
        prior_option = {"label": "prior", "value": "prior"}
        if has_priors and prior_option not in plotting_options:
            plotting_options.append(prior_option)
        if not has_priors and prior_option in plotting_options:
            plotting_options.remove(prior_option)
        return plotting_options
Esempio n. 5
0
def observation_response_controller(parent: WebvizErtPluginABC, app: dash.Dash) -> None:
    @app.callback(
        Output(parent.uuid("response-selector"), "options"),
        [Input(parent.uuid("ensemble-selection-store"), "data")],
    )
    def set_response_options(
        selected_ensembles: Optional[Mapping[str, Dict]]
    ) -> List[Dict]:
        if not selected_ensembles:
            raise PreventUpdate
        ensembles = [
            load_ensemble(parent, ensemble_id) for ensemble_id in selected_ensembles
        ]
        return response_options(response_filters=["obs"], ensembles=ensembles)

    @app.callback(
        Output(parent.uuid("response-selector"), "value"),
        [Input(parent.uuid("response-selector"), "options")],
        [State(parent.uuid("response-selector"), "value")],
    )
    def set_responses_value(
        available_options: List[Dict], previous_selected_response: str
    ) -> str:
        if available_options and previous_selected_response in [
            opt["value"] for opt in available_options
        ]:
            return previous_selected_response
        if available_options and not previous_selected_response:
            return available_options[0]["value"]
        return ""

    @app.callback(
        Output(
            {"id": parent.uuid("response-graphic"), "type": parent.uuid("graph")},
            "figure",
        ),
        [
            Input(parent.uuid("response-selector"), "value"),
            Input(parent.uuid("yaxis-type"), "value"),
            Input(parent.uuid("misfits-type"), "value"),
        ],
        [State(parent.uuid("ensemble-selection-store"), "data")],
    )
    def update_graph(
        response: Optional[str],
        yaxis_type: List[str],
        misfits_type: str,
        selected_ensembles: Optional[Mapping[str, Dict]],
    ) -> go.Figure:
        if not response or response == "" or not selected_ensembles:
            raise PreventUpdate

        if misfits_type == "Summary":
            data_dict = {}
            colors = {}
            for ensemble_id, color in selected_ensembles.items():
                ensemble = load_ensemble(parent, ensemble_id)
                summary_df = ensemble.responses[response].summary_misfits_df(
                    selection=None
                )  # What about selections?
                if summary_df is not None:
                    data_dict[parent.ensembles[ensemble_id]._name] = summary_df
                colors[parent.ensembles[ensemble_id]._name] = color["color"]
            if data_dict:
                plot = MultiHistogramPlotModel(
                    data_dict,
                    names={name: name for name in data_dict},
                    colors=colors,
                    hist=True,
                    kde=False,
                )
                return plot.repr

        def _generate_plot(ensemble_id: str, color: str) -> ResponsePlotModel:
            ensemble = load_ensemble(parent, ensemble_id)
            plot = _create_misfits_plot(ensemble.responses[response], [], color)
            return plot

        response_plots = [
            _generate_plot(ensemble_id, data["color"])
            for ensemble_id, data in selected_ensembles.items()
        ]

        fig = go.Figure()
        for plt in response_plots:
            for trace in plt.repr.data:
                fig.add_trace(trace)
        fig.update_yaxes(type=yaxis_type)
        return fig
def response_correlation_controller(parent: WebvizErtPluginABC,
                                    app: dash.Dash) -> None:
    @app.callback(
        [
            Output(
                {
                    "id": parent.uuid("response-correlation"),
                    "type": parent.uuid("graph"),
                },
                "figure",
            ),
            Output(
                {
                    "id": parent.uuid("response-heatmap"),
                    "type": parent.uuid("graph"),
                },
                "figure",
            ),
        ],
        [
            Input(parent.uuid("correlation-store-xindex"), "data"),
            Input(parent.uuid("correlation-store-selection"), "data"),
            Input(parent.uuid("correlation-metric"), "value"),
        ],
        [
            State(parent.uuid("parameter-selection-store-param"), "data"),
            State(parent.uuid("parameter-selection-store-resp"), "data"),
            State(parent.uuid("ensemble-selection-store"), "data"),
        ],
    )
    def update_correlation_plot(
        corr_xindex: Dict,
        corr_param_resp: Dict,
        correlation_metric: str,
        parameters: List[str],
        responses: List[str],
        ensembles: Optional[Mapping[str, Dict]],
    ) -> Optional[Tuple[go.Figure, go.Figure]]:
        if not (ensembles and parameters and responses
                and corr_param_resp["response"] in responses):
            raise PreventUpdate

        selected_response = corr_param_resp["response"]
        data = {}
        colors = {}
        heatmaps = []

        df_index = None
        for ensemble_id, color in ensembles.items():
            ensemble = load_ensemble(parent, ensemble_id)
            parameter_df = ensemble.parameters_df(parameters)
            for response in responses:
                response_df = ensemble.responses[response].data_df()
                x_index = corr_xindex.get(response, 0)
                parameter_df[response] = response_df.iloc[x_index]

            corrdf = parameter_df.corr(method=correlation_metric)
            corrdf = corrdf.drop(responses, axis=0).fillna(0)
            if df_index is None:
                df_index = corrdf[selected_response].abs().sort_values(
                ).index.copy()
            corrdf = corrdf.reindex(df_index)
            # create heatmap
            data_heatmap = {
                "type":
                "heatmap",
                "x":
                corrdf[responses].columns,
                "y":
                corrdf[responses].index,
                "z": [
                    corrdf[responses].loc[parameter].values
                    for parameter in corrdf.index
                ],
                "zmin":
                -1,
                "zmax":
                1,
                "colorscale":
                "rdylbu",
                "reversescale":
                True,
            }
            heatmaps.append(data_heatmap)
            ens_key = repr(ensemble)
            data[ens_key] = corrdf[selected_response]
            data[ens_key].index.name = selected_response
            colors[ens_key] = color["color"]
        if data and heatmaps:
            correlation_plot = BarChartPlotModel(data, colors)
            heatmap_plot = make_subplots(
                rows=1,
                cols=len(heatmaps),
                subplot_titles=[
                    f"ENS_{idx + 1}" for idx, _ in enumerate(heatmaps)
                ],
            )
            for idx, heatmap in enumerate(heatmaps):
                heatmap_plot.add_trace(heatmap, 1, 1 + idx)
                heatmap_plot.update_yaxes(showticklabels=False,
                                          row=1,
                                          col=1 + idx)
                heatmap_plot.update_xaxes(showticklabels=False,
                                          row=1,
                                          col=1 + idx)
            _layout = assets.ERTSTYLE["figure"]["layout"].copy()
            _layout.update({
                "clickmode":
                "event+select",
                "showlegend":
                False,
                "annotations": [{
                    "font": {
                        "color": colors[ens]
                    }
                } for ens in colors],
            })
            heatmap_plot.update_layout(_layout)
            return correlation_plot.repr, heatmap_plot
        raise PreventUpdate

    @app.callback(
        Output(
            {
                "id": parent.uuid("response-overview"),
                "type": parent.uuid("graph")
            },
            "figure",
        ),
        [
            Input(parent.uuid("correlation-store-xindex"),
                  "modified_timestamp"),
            Input(parent.uuid("parameter-selection-store-resp"),
                  "modified_timestamp"),
            Input(parent.uuid("ensemble-selection-store"),
                  "modified_timestamp"),
            Input(parent.uuid("correlation-store-selection"),
                  "modified_timestamp"),
        ],
        [
            State(parent.uuid("parameter-selection-store-resp"), "data"),
            State(parent.uuid("ensemble-selection-store"), "data"),
            State(parent.uuid("correlation-store-xindex"), "data"),
            State(parent.uuid("correlation-store-selection"), "data"),
        ],
    )
    def update_response_overview_plot(
        _: Any,
        __: Any,
        ___: Any,
        ____: Any,
        responses: Optional[List[str]],
        ensembles: Optional[Mapping[str, Dict]],
        corr_xindex: Dict,
        corr_param_resp: Dict,
    ) -> Optional[go.Figure]:
        if not (ensembles and responses
                and corr_param_resp["response"] in responses):
            raise PreventUpdate
        selected_response = corr_param_resp["response"]
        _plots = []
        _obs_plots: List[PlotModel] = []

        for ensemble_id, data in ensembles.items():
            ensemble = load_ensemble(parent, ensemble_id)
            response = ensemble.responses[selected_response]
            x_axis = response.axis
            if x_axis is not None:
                if str(x_axis[0]).isnumeric():
                    style = deepcopy(
                        assets.ERTSTYLE["response-plot"]["response-index"])
                else:
                    style = deepcopy(
                        assets.ERTSTYLE["response-plot"]["response"])
            data_df = response.data_df().copy()

            style.update({"marker": {"color": data["color"]}})
            style.update({"line": {"color": data["color"]}})
            _plots += [
                PlotModel(
                    x_axis=x_axis,
                    y_axis=data_df[realization],
                    text=realization,
                    name=realization,
                    **style,
                ) for realization in data_df
            ]

            if response.observations:
                for obs in response.observations:
                    _obs_plots.append(_get_observation_plots(obs.data_df()))

        fig = go.Figure()
        for plot in _plots:
            fig.add_trace(plot.repr)

        _layout = assets.ERTSTYLE["figure"]["layout"].copy()
        _layout.update(dict(showlegend=False))
        fig.update_layout(_layout)

        x_index = corr_xindex.get(selected_response, 0)
        if x_axis is not None:
            fig.add_shape(
                type="line",
                x0=x_axis[x_index],
                y0=0,
                x1=x_axis[x_index],
                y1=1,
                yref="paper",
                line=dict(color="rgb(30, 30, 30)", dash="dash", width=3),
            )
        # draw observations on top
        for plot in _obs_plots:
            fig.add_trace(plot.repr)

        fig.update_layout(clickmode="event+select")
        return fig

    @app.callback(
        [
            Output(
                {
                    "id": parent.uuid("response-scatterplot"),
                    "type": parent.uuid("graph"),
                },
                "figure",
            ),
            Output(parent.uuid("response-info-text"), "children"),
        ],
        [
            Input(parent.uuid("correlation-store-xindex"), "data"),
            Input(parent.uuid("correlation-store-selection"), "data"),
        ],
        [
            State(parent.uuid("parameter-selection-store-param"), "data"),
            State(parent.uuid("parameter-selection-store-resp"), "data"),
            State(parent.uuid("ensemble-selection-store"), "data"),
        ],
    )
    def update_response_parameter_scatterplot(
        corr_xindex: Dict,
        corr_param_resp: Dict,
        parameters: List[str],
        responses: List[str],
        ensembles: Optional[Mapping[str, Dict]],
    ) -> Optional[Tuple[go.Figure, Component]]:

        if not (parameters and ensembles and responses
                and corr_param_resp["parameter"] in parameters
                and corr_param_resp["response"] in responses):
            raise PreventUpdate

        selected_parameter = corr_param_resp["parameter"]
        selected_response = corr_param_resp["response"]
        _plots = []
        _resp_plots = {}
        _param_plots = {}
        _colors = {}

        for ensemble_id, data in ensembles.items():
            ensemble = load_ensemble(parent, ensemble_id)

            if ensemble.parameters and ensemble.responses:
                y_data = ensemble.parameters[selected_parameter].data_df()
                response = ensemble.responses[selected_response]

                if response.axis is not None:
                    x_index = corr_xindex.get(selected_response, 0)
                    x_data = response.data_df().iloc[x_index]
                    style = deepcopy(
                        assets.ERTSTYLE["response-plot"]["response-index"])
                    style["marker"]["color"] = data["color"]
                    _colors[str(ensemble)] = data["color"]
                    _plots += [
                        PlotModel(
                            x_axis=x_data.values.flatten(),
                            y_axis=y_data.values.flatten(),
                            text="Mean",
                            name=
                            f"{repr(ensemble)}: {selected_response}x{selected_parameter}@{response.axis[x_index]}",
                            **style,
                        )
                    ]
                    _resp_plots[str(ensemble)] = x_data.values.flatten()
                    _param_plots[str(ensemble)] = y_data.values.flatten()

        fig = make_subplots(
            rows=4,
            cols=2,
            specs=[
                [{
                    "colspan": 2,
                    "rowspan": 3
                }, None],
                [None, None],
                [None, None],
                [{
                    "rowspan": 1
                }, {
                    "rowspan": 1
                }],
            ],
        )
        for plot in _plots:
            fig.add_trace(plot.repr, 1, 1)

        for key in _param_plots:
            fig.add_trace(
                {
                    "type": "histogram",
                    "x": _param_plots[key],
                    "showlegend": False,
                    "marker_color": _colors[key],
                },
                4,
                1,
            )
        for key in _resp_plots:
            fig.add_trace(
                {
                    "type": "histogram",
                    "x": _resp_plots[key],
                    "showlegend": False,
                    "marker_color": _colors[key],
                },
                4,
                2,
            )
        fig.update_layout(assets.ERTSTYLE["figure"]["layout"])
        fig.update_layout(showlegend=False)
        final_text = []
        for response_name in responses:
            x_axis = ensemble.responses[response_name].axis
            if x_axis is not None:
                x_value = x_axis[corr_xindex.get(response_name, 0)]
                if response_name == selected_response:
                    res_text = f"**{response_name} @ {x_value}**, "
                else:
                    res_text = f"{response_name} @ {x_value}, "
                final_text.append(dcc.Markdown(res_text))
        final_text += [
            dcc.Markdown(f"parameter: **{corr_param_resp['parameter']}**")
        ]
        return fig, html.Div(final_text)

    @app.callback(
        Output(parent.uuid("correlation-store-xindex"), "data"),
        [
            Input(
                {
                    "id": parent.uuid("response-overview"),
                    "type": parent.uuid("graph")
                },
                "clickData",
            ),
            Input(parent.uuid("parameter-selection-store-resp"), "data"),
        ],
        [
            State(parent.uuid("correlation-store-xindex"), "data"),
            State(parent.uuid("correlation-store-selection"), "data"),
        ],
    )
    def update_corr_index(click_data: Dict, responses: List[str],
                          corr_xindex: Dict,
                          corr_param_resp: Dict) -> Optional[Dict]:
        if not responses:
            raise PreventUpdate

        ctx = dash.callback_context
        triggered_id = ctx.triggered[0]["prop_id"].split(".")[0]

        if triggered_id == parent.uuid("parameter-selection-store-resp"):
            return {
                response: corr_xindex.get(response, 0)
                for response in responses
            }
        if click_data:
            corr_xindex[corr_param_resp["response"]] = click_data["points"][0][
                "pointIndex"]
        return corr_xindex

    @app.callback(
        Output(parent.uuid("correlation-store-selection"), "data"),
        [
            Input(
                {
                    "id": parent.uuid("response-heatmap"),
                    "type": parent.uuid("graph"),
                },
                "clickData",
            ),
            Input(parent.uuid("parameter-selection-store-resp"), "data"),
            Input(parent.uuid("parameter-selection-store-param"), "data"),
        ],
        [
            State(parent.uuid("correlation-store-selection"), "data"),
        ],
    )
    def update_corr_param_resp(
        click_data: Dict,
        responses: List[str],
        parameters: List[str],
        corr_param_resp: Dict,
    ) -> Optional[Dict]:
        ctx = dash.callback_context
        triggered_id = ctx.triggered[0]["prop_id"].split(".")[0]
        if triggered_id == parent.uuid(
                "parameter-selection-store-resp") and responses:
            corr_param_resp["response"] = (corr_param_resp["response"]
                                           if corr_param_resp["response"]
                                           in responses else responses[0])
        elif (triggered_id == parent.uuid("parameter-selection-store-param")
              and parameters):
            corr_param_resp["parameter"] = (corr_param_resp["parameter"]
                                            if corr_param_resp["parameter"]
                                            in parameters else parameters[0])
        elif click_data:
            corr_param_resp["parameter"] = click_data["points"][0]["y"]
            corr_param_resp["response"] = click_data["points"][0]["x"]
        return corr_param_resp
def ensemble_selector_controller(parent: WebvizErtPluginABC,
                                 app: dash.Dash) -> None:
    @app.callback(
        Output(parent.uuid("ensemble-selector"), "elements"),
        [
            Input(parent.uuid("ensemble-selection-store"), "data"),
            Input(parent.uuid("ensemble-selector-button"), "n_clicks"),
        ],
        [
            State(parent.uuid("ensemble-selector"), "elements"),
        ],
    )
    def update_ensemble_selector_graph(
        selected_ensembles: Optional[Mapping[int, Dict]],
        _: int,
        elements: Optional[List[Dict]],
    ) -> List[Dict]:
        selected_ensembles = {} if not selected_ensembles else selected_ensembles
        ctx = dash.callback_context
        triggered_id = ctx.triggered[0]["prop_id"].split(".")[0]

        if (triggered_id == parent.uuid("ensemble-selection-store")
                and elements is not None):
            datas = elements
        else:
            ensemble_dict = get_ensembles(project_id=parent.project_identifier)
            for ensemble_schema in ensemble_dict:
                ensemble_id = ensemble_schema["id"]
                load_ensemble(parent, ensemble_id)
            datas = _construct_graph(parent.ensembles)

        for element in datas:

            if "id" in element["data"]:
                element["data"].update(
                    selected_ensembles.get(element["data"]["id"], {}))
                element["selected"] = element["data"][
                    "id"] in selected_ensembles
        return datas

    @app.callback(
        Output(parent.uuid("ensemble-selection-store"), "data"),
        [
            Input(parent.uuid("ensemble-selector"), "selectedNodeData"),
        ],
    )
    def update_ensemble_selection(
        selected_nodes: Optional[List[Dict]], ) -> Mapping[int, Dict]:
        color_wheel = assets.ERTSTYLE["ensemble-selector"]["color_wheel"]
        if not selected_nodes:
            raise PreventUpdate
        data = {
            ensemble["id"]: {
                "color": color_wheel[index % len(color_wheel)],
            }
            for index, ensemble in enumerate(selected_nodes)
        }

        return data

    @app.callback(
        [
            Output(parent.uuid("ensemble-selector"), "className"),
            Output(parent.uuid("ensemble-selector-container"), "className"),
            Output(parent.uuid("ensemble-selector-button"), "children"),
            Output(parent.uuid("ensemble-view-store"), "data"),
        ],
        [
            Input(parent.uuid("ensemble-selector-button"), "n_clicks"),
        ],
        [
            State(parent.uuid("ensemble-selector"), "className"),
            State(parent.uuid("ensemble-selector-container"), "className"),
            State(parent.uuid("ensemble-view-store"), "data"),
        ],
    )
    def update_ensemble_selector_view_size(
            _: int, class_name: str, class_name_container: str,
            maximized: bool) -> List[Union[str, str, str, bool]]:

        ctx = dash.callback_context
        triggered_id = ctx.triggered[0]["prop_id"].split(".")[0]

        if triggered_id == parent.uuid("ensemble-selector-button"):
            maximized = not maximized

        if maximized:
            old_class_name = "ert-ensemble-selector-small"
            new_class_name = "ert-ensemble-selector-large"
            old_container_class_name = "ert-ensemble-selector-container-small"
            new_container_class_name = "ert-ensemble-selector-container-large"
            new_button_text = "Minimize"
        else:
            old_class_name = "ert-ensemble-selector-large"
            new_class_name = "ert-ensemble-selector-small"
            old_container_class_name = "ert-ensemble-selector-container-large"
            new_container_class_name = "ert-ensemble-selector-container-small"
            new_button_text = "Expand"

        return [
            class_name.replace(old_class_name, new_class_name),
            class_name_container.replace(old_container_class_name,
                                         new_container_class_name),
            new_button_text,
            maximized,
        ]
Esempio n. 8
0
def plot_view_controller(parent: WebvizErtPluginABC, app: dash.Dash) -> None:
    webviz_ert.controllers.multi_response_controller(parent, app)
    webviz_ert.controllers.multi_parameter_controller(parent, app)

    @app.callback(
        Output(parent.uuid("plot-selection-store"), "data"),
        [
            Input(parent.uuid("parameter-selection-store-param"),
                  "modified_timestamp"),
            Input(parent.uuid("parameter-selection-store-resp"),
                  "modified_timestamp"),
        ],
        [
            State(parent.uuid("parameter-selection-store-param"), "data"),
            State(parent.uuid("parameter-selection-store-resp"), "data"),
            State(parent.uuid("plot-selection-store"), "data"),
        ],
    )
    def update_plot_selection(
        _: Any,
        __: Any,
        parameters: Optional[List[str]],
        responses: Optional[List[str]],
        current_selection: Optional[List[Mapping[str, str]]],
    ) -> List[Mapping[str, str]]:
        parameters = [] if not parameters else parameters
        responses = [] if not responses else responses
        current_selection = [] if not current_selection else current_selection
        for plot in current_selection.copy():
            if plot["name"] not in parameters and plot["name"] not in responses:
                current_selection.remove(plot)
        for param in parameters:
            new_plot = {"name": param, "type": "parameter"}
            if new_plot not in current_selection:
                current_selection.append(new_plot)
        for response in responses:
            new_plot = {"name": response, "type": "response"}
            if new_plot not in current_selection:
                current_selection.append(new_plot)

        return current_selection

    @app.callback(
        [
            Output(parent.uuid("plotting-content"), "children"),
            Output(parent.uuid("plotting-content-store"), "data"),
        ],
        [
            Input(parent.uuid("plot-selection-store"), "data"),
        ],
        [State(parent.uuid("plotting-content-store"), "data")],
    )
    def create_grid(
        plots: List[Dict], children: Union[None, Component, List[Component]]
    ) -> Tuple[dbc.Row, List[Component]]:
        if not plots:
            return [], []
        if children is None:
            children = []
        elif type(children) is not list:
            children = [children]
        new_children = []
        for plot in plots:
            child = _get_child(plot["name"], children)
            if child is not None:
                new_children.append(child)
            else:
                if plot["type"] == "response":
                    p = response_view(parent=parent, index=plot["name"])
                elif plot["type"] == "parameter":
                    p = parameter_view(parent=parent, index=plot["name"])
                else:
                    raise ValueError(f"Plots of undefined type {plot['type']}")
                new_children.append(
                    html.Div(id=parent.uuid(plot["name"]), children=p))

        col_width = max(6, 12 // max(1, len(new_children)))
        bootstrapped_children = [
            dbc.Col(child,
                    xl=col_width,
                    lg=12,
                    style=assets.ERTSTYLE["dbc-column"])
            for child in new_children
        ]
        return dbc.Row(bootstrapped_children), new_children