def add_realization_traces(self,
                               ensemble: str,
                               vector: str,
                               real_filter: pd.Series = None) -> list:
        """Renders line trace for each realization, includes history line if present"""
        dataframe = self.dataframe[self.dataframe["ENSEMBLE"] == ensemble]
        dataframe = (dataframe[dataframe["REAL"].isin(real_filter)]
                     if real_filter is not None else dataframe)
        traces = [{
            "line": {
                "shape": self.get_line_shape(vector)
            },
            "x": list(real_df["DATE"]),
            "y": list(real_df[vector]),
            "name": ensemble,
            "customdata": real,
            "legendgroup": ensemble,
            "marker": {
                "color": "red"
            },
            "showlegend": real_idx == 0,
        } for real_idx, (real, real_df) in enumerate(dataframe.groupby("REAL"))
                  ]

        if (historical_vector(vector=vector, smry_meta=self.metadata)
                in dataframe.columns):
            traces.append(
                self.add_history_trace(
                    dataframe,
                    historical_vector(vector=vector, smry_meta=self.metadata)))
        return traces
    def add_statistic_traces(self, ensembles: list, vector: str) -> list:
        """Calculate statistics for a given vector for relevant ensembles"""
        quantiles = [10, 90]
        traces = []
        ensembles = ensembles if isinstance(ensembles, list) else [ensembles]
        dataframe = self.dataframe[self.dataframe["ENSEMBLE"].isin(ensembles)]
        for ensemble, ens_df in dataframe.groupby("ENSEMBLE"):
            dframe = ens_df.drop(columns=["ENSEMBLE", "REAL"]).groupby("DATE")

            # Build a dictionary of dataframes to be concatenated
            dframes = {}
            dframes["mean"] = dframe.mean()
            for quantile in quantiles:
                quantile_str = "p" + str(quantile)
                dframes[quantile_str] = dframe.quantile(q=quantile / 100.0)
            dframes["maximum"] = dframe.max()
            dframes["minimum"] = dframe.min()
            traces.extend(
                add_fanchart_traces(
                    pd.concat(dframes, names=["STATISTIC"],
                              sort=False)[vector],
                    self.ens_colors.get(
                        ensemble,
                        self.ens_colors[list(self.ens_colors.keys())[0]]),
                    ensemble,
                    self.get_line_shape(vector),
                ))
        if (historical_vector(vector=vector, smry_meta=self.metadata)
                in dataframe.columns):
            traces.append(
                self.add_history_trace(
                    dataframe,
                    historical_vector(vector=vector, smry_meta=self.metadata)))
        return traces
    def add_ensset_realization_traces(self, ensembles: Union[str, list],
                                      vector: str) -> list:
        """Renders line trace for each realization grouped by ensemble,
        includes history line if present"""
        ensembles = ensembles if isinstance(ensembles, list) else [ensembles]
        dataframe = self.dataframe[self.dataframe["ENSEMBLE"].isin(ensembles)]
        traces = [{
            "line": {
                "shape": self.get_line_shape(vector)
            },
            "x": list(real_df["DATE"]),
            "y": list(real_df[vector]),
            "hovertext": f"Realization: {real}, Ensemble: {ensemble}",
            "name": ensemble,
            "legendgroup": ensemble,
            "marker": {
                "color":
                self.ens_colors.get(
                    ensemble, self.ens_colors[list(self.ens_colors.keys())[0]])
            },
            "showlegend": real_idx == 0,
        } for ens_no, (ensemble,
                       ens_df) in enumerate(dataframe.groupby("ENSEMBLE"))
                  for real_idx, (real,
                                 real_df) in enumerate(ens_df.groupby("REAL"))]

        if (historical_vector(vector=vector, smry_meta=self.metadata)
                in dataframe.columns):
            traces.append(
                self.add_history_trace(
                    dataframe,
                    historical_vector(vector=vector, smry_meta=self.metadata)))
        return traces
Ejemplo n.º 4
0
 def vectors(self) -> list:
     return [
         c
         for c in self.dataframe.columns
         if c not in ["REAL", "ENSEMBLE", "DATE"]
         and not historical_vector(c, self.metadata, False) in self.dataframe.columns
     ]
 def _determine_vector_names(dataframe: pd.DataFrame,
                             metadata: Optional[pd.DataFrame]) -> List[str]:
     """Determine which vectors we should make available"""
     vecnames = [
         c for c in dataframe.columns
         if c not in ["REAL", "ENSEMBLE", "DATE"]
         and not historical_vector(c, metadata, False) in dataframe.columns
     ]
     return vecnames
Ejemplo n.º 6
0
 def get_historical_vector_df(self, vector: str,
                              ensemble: str) -> Optional[pd.DataFrame]:
     df = self._dataframe
     hist_vecname = historical_vector(vector, None)
     if hist_vecname and hist_vecname in df.columns:
         return (df[[hist_vecname,
                     "DATE"]].loc[(df["REAL"] == df["REAL"].unique()[0])
                                  & (df["ENSEMBLE"] == ensemble)].rename(
                                      columns={hist_vecname: vector}))
     return None
Ejemplo n.º 7
0
    def get_historical_vector_df(
        self, vector: str, ensemble: str
    ) -> Optional[pd.DataFrame]:
        hist_vecname = historical_vector(vector, smry_meta=None)

        if hist_vecname and hist_vecname in self.vectors:
            provider = self._provider_set[ensemble]
            return provider.get_vectors_df(
                [hist_vecname], None, realizations=provider.realizations()[:1]
            ).rename(columns={hist_vecname: vector})
        return None
 def __init__(
     self,
     dataframe: pd.DataFrame,
     theme: dict = None,
     metadata: Optional[pd.DataFrame] = None,
     line_shape_fallback: str = "linear",
 ) -> None:
     self._dataframe = dataframe
     self._prepare_and_validate_data()
     self.theme = theme
     self._metadata = metadata
     self.line_shape_fallback = set_simulation_line_shape_fallback(
         line_shape_fallback)
     self._vectors = [
         c for c in self.dataframe.columns
         if c not in ["REAL", "ENSEMBLE", "DATE"] and not historical_vector(
             c, self.metadata, False) in self.dataframe.columns
     ]
     self._vector_groups = self._split_vectors_by_type()
     self._dates = sorted(self._dataframe["DATE"].unique())
Ejemplo n.º 9
0
    def __init__(
        self,
        app: dash.Dash,
        webviz_settings: WebvizSettings,
        ensembles: Optional[list] = None,
        rel_file_pattern: str = "share/results/unsmry/*.arrow",
        perform_presampling: bool = False,
        obsfile: Path = None,
        options: dict = None,
        sampling: str = Frequency.MONTHLY.value,
        predefined_expressions: str = None,
        user_defined_vector_definitions: str = None,
        line_shape_fallback: str = "linear",
    ) -> None:
        super().__init__()

        # NOTE: Temporary css, pending on new wcc modal component.
        # See: https://github.com/equinor/webviz-core-components/issues/163
        WEBVIZ_ASSETS.add(
            Path(webviz_subsurface.__file__).parent / "_assets" / "css" /
            "modal.css")

        self._webviz_settings = webviz_settings
        self._obsfile = obsfile

        # Retrieve user defined vector descriptions from configuration and validate
        self._user_defined_vector_descriptions_path = (
            None if user_defined_vector_definitions is None else
            webviz_settings.shared_settings["user_defined_vector_definitions"]
            [user_defined_vector_definitions])
        self._user_defined_vector_definitions: Dict[
            str, wsc.
            VectorDefinition] = create_user_defined_vector_descriptions_from_config(
                get_path(self._user_defined_vector_descriptions_path) if self.
                _user_defined_vector_descriptions_path else None)
        self._custom_vector_definitions = copy.deepcopy(
            self._user_defined_vector_definitions)

        self._line_shape_fallback = set_simulation_line_shape_fallback(
            line_shape_fallback)

        # Must define valid freqency!
        if Frequency.from_string_value(sampling) is None:
            raise ValueError(
                'Sampling frequency conversion is "None", i.e. Raw sampling, and '
                "is not supported by plugin yet!")
        self._sampling = Frequency(sampling)
        self._presampled_frequency = None

        # TODO: Update functionality when allowing raw data and csv file input
        # NOTE: If csv is implemented-> handle/disable statistics, PER_INTVL_, PER_DAY_, delta
        # ensemble, etc.
        if ensembles is not None:
            ensemble_paths: Dict[str, Path] = {
                ensemble_name:
                webviz_settings.shared_settings["scratch_ensembles"]
                [ensemble_name]
                for ensemble_name in ensembles
            }
            if perform_presampling:
                self._presampled_frequency = self._sampling
                self._input_provider_set = create_presampled_provider_set_from_paths(
                    ensemble_paths, rel_file_pattern,
                    self._presampled_frequency)
            else:
                self._input_provider_set = create_lazy_provider_set_from_paths(
                    ensemble_paths, rel_file_pattern)
        else:
            raise ValueError('Incorrect argument, must provide "ensembles"')

        if not self._input_provider_set:
            raise ValueError(
                "Initial provider set is undefined, and ensemble summary providers"
                " are not instanciated for plugin")

        self._theme = webviz_settings.theme

        self._observations = {}
        if self._obsfile:
            self._observations = check_and_format_observations(
                get_path(self._obsfile))

        # NOTE: Initially keep set of all vector names - can make dynamic if wanted?
        vector_names = self._input_provider_set.all_vector_names()
        non_historical_vector_names = [
            vector for vector in vector_names
            if historical_vector(vector, None, False) not in vector_names
        ]

        # NOTE: Initially: With set of vector names, the vector selector data is static
        # Can be made dynamic based on selected ensembles - i.e. vectors present among
        # selected providers?
        self._vector_selector_base_data: list = []
        self._vector_calculator_data: list = []
        for vector in non_historical_vector_names:
            add_vector_to_vector_selector_data(
                self._vector_selector_base_data,
                vector,
            )

            # Only vectors from providers are provided to vector calculator
            add_vector_to_vector_selector_data(
                self._vector_calculator_data,
                vector,
            )

            metadata = (self._input_provider_set.vector_metadata(vector)
                        if self._input_provider_set else None)
            if metadata and metadata.is_total:
                # Get the likely name for equivalent rate vector and make dropdown options.
                # Requires that the time_index was either defined or possible to infer.
                per_day_vec = create_per_day_vector_name(vector)
                per_intvl_vec = create_per_interval_vector_name(vector)

                add_vector_to_vector_selector_data(
                    self._vector_selector_base_data,
                    per_day_vec,
                )
                add_vector_to_vector_selector_data(
                    self._vector_selector_base_data,
                    per_intvl_vec,
                )

                # Add vector base to custom vector definition if not existing
                vector_base = vector.split(":")[0]
                _definition = wsc.VectorDefinitions.get(vector_base, None)
                _type = _definition["type"] if _definition else "others"

                per_day_vec_base = per_day_vec.split(":")[0]
                per_intvl_vec_base = per_intvl_vec.split(":")[0]
                if per_day_vec_base not in self._custom_vector_definitions:
                    self._custom_vector_definitions[
                        per_day_vec_base] = wsc.VectorDefinition(
                            type=_type,
                            description=simulation_vector_description(
                                per_day_vec_base,
                                self._user_defined_vector_definitions),
                        )
                if per_intvl_vec_base not in self._custom_vector_definitions:
                    self._custom_vector_definitions[
                        per_intvl_vec_base] = wsc.VectorDefinition(
                            type=_type,
                            description=simulation_vector_description(
                                per_intvl_vec_base,
                                self._user_defined_vector_definitions),
                        )

        # Retreive predefined expressions from configuration and validate
        self._predefined_expressions_path = (
            None if predefined_expressions is None else webviz_settings.
            shared_settings["predefined_expressions"][predefined_expressions])
        self._predefined_expressions = expressions_from_config(
            get_path(self._predefined_expressions_path) if self.
            _predefined_expressions_path else None)
        for expression in self._predefined_expressions:
            valid, message = validate_predefined_expression(
                expression, self._vector_selector_base_data)
            if not valid:
                warnings.warn(message)
            expression["isValid"] = valid

        # Add expressions to custom vector definitions
        self._custom_vector_definitions_base = copy.deepcopy(
            self._custom_vector_definitions)
        _custom_vector_definitions_from_expressions = (
            get_vector_definitions_from_expressions(
                self._predefined_expressions))
        for key, value in _custom_vector_definitions_from_expressions.items():
            if key not in self._custom_vector_definitions:
                self._custom_vector_definitions[key] = value

        # Create initial vector selector data with predefined expressions
        self._initial_vector_selector_data = copy.deepcopy(
            self._vector_selector_base_data)
        add_expressions_to_vector_selector_data(
            self._initial_vector_selector_data, self._predefined_expressions)

        plot_options = options if options else {}
        self._initial_visualization_selection = VisualizationOptions(
            plot_options.get("visualization", "statistics"))

        # Initial selected vectors - NB: {vector1, vector2, vector3} is deprecated!
        initial_vectors: List[str] = plot_options.get("vectors", [])

        # TODO: Remove when depretaced code is not utilized anymore
        if "vectors" in plot_options and any(
                elm in plot_options
                for elm in ["vector1", "vector2", "vector3"]):
            warnings.warn(
                'Providing new user input option "vectors" and deprecated user input options '
                '"vector1", "vector2" and "vector3" simultaneously. Initially selected vectors '
                'for plugin are set equal to new user input option "vectors".')
        if not initial_vectors:
            initial_vectors = [
                plot_options[elm] for elm in ["vector1", "vector2", "vector3"]
                if elm in plot_options
            ][:3]

        # Check if initially selected vectors exist in data, raise ValueError if not
        missing_vectors = [
            elm for elm in initial_vectors
            if not is_vector_name_in_vector_selector_data(
                elm, self._initial_vector_selector_data)
        ]
        if missing_vectors:
            raise ValueError(
                f"Cannot find: {', '.join(missing_vectors)} to plot initially in "
                "SimulationTimeSeries. Check that the vector(s) exist in your data."
            )

        if len(initial_vectors) > 3:
            warnings.warn(
                'User input option "vectors" contains more than 3 vectors. Only the first 3 listed '
                "vectors are kept for initially selected vectors - the remaining are neglected."
            )
        self._initial_vectors = initial_vectors[:3]

        # Set callbacks
        self.set_callbacks(app)
Ejemplo n.º 10
0
 def get_non_historical_vector_names(self) -> list:
     return [
         vector
         for vector in self._vector_names
         if historical_vector(vector, None, False) not in self._vector_names
     ]
Ejemplo n.º 11
0
    def __init__(
        self,
        app: dash.Dash,
        webviz_settings: WebvizSettings,
        ensembles: Optional[list] = None,
        rel_file_pattern: str = "share/results/unsmry/*.arrow",
        perform_presampling: bool = False,
        obsfile: Path = None,
        options: dict = None,
        sampling: str = Frequency.MONTHLY.value,
        predefined_expressions: str = None,
        line_shape_fallback: str = "linear",
    ) -> None:
        super().__init__()

        # NOTE: Temporary css, pending on new wcc modal component.
        # See: https://github.com/equinor/webviz-core-components/issues/163
        WEBVIZ_ASSETS.add(
            Path(webviz_subsurface.__file__).parent / "_assets" / "css" /
            "modal.css")

        self._webviz_settings = webviz_settings
        self._obsfile = obsfile

        self._line_shape_fallback = set_simulation_line_shape_fallback(
            line_shape_fallback)

        # Must define valid freqency!
        if Frequency.from_string_value(sampling) is None:
            raise ValueError(
                'Sampling frequency conversion is "None", i.e. Raw sampling, and '
                "is not supported by plugin yet!")
        self._sampling = Frequency(sampling)
        self._presampled_frequency = None

        # TODO: Update functionality when allowing raw data and csv file input
        # NOTE: If csv is implemented-> handle/disable statistics, INTVL_, AVG_, delta
        # ensemble, etc.
        if ensembles is not None:
            ensemble_paths: Dict[str, Path] = {
                ensemble_name:
                webviz_settings.shared_settings["scratch_ensembles"]
                [ensemble_name]
                for ensemble_name in ensembles
            }
            if perform_presampling:
                self._presampled_frequency = self._sampling
                self._input_provider_set = create_presampled_provider_set_from_paths(
                    ensemble_paths, rel_file_pattern,
                    self._presampled_frequency)
            else:
                self._input_provider_set = create_lazy_provider_set_from_paths(
                    ensemble_paths, rel_file_pattern)
        else:
            raise ValueError('Incorrect argument, must provide "ensembles"')

        if not self._input_provider_set:
            raise ValueError(
                "Initial provider set is undefined, and ensemble summary providers"
                " are not instanciated for plugin")

        self._theme = webviz_settings.theme

        self._observations = {}
        if self._obsfile:
            self._observations = check_and_format_observations(
                get_path(self._obsfile))

        # NOTE: Initially keep set of all vector names - can make dynamic if wanted?
        vector_names = self._input_provider_set.all_vector_names()
        non_historical_vector_names = [
            vector for vector in vector_names
            if historical_vector(vector, None, False) not in vector_names
        ]

        # NOTE: Initially: With set of vector names, the vector selector data is static
        # Can be made dynamic based on selected ensembles - i.e. vectors present among
        # selected providers?
        self._vector_selector_base_data: list = []
        self._vector_calculator_data: list = []
        for vector in non_historical_vector_names:
            split = vector.split(":")
            add_vector_to_vector_selector_data(
                self._vector_selector_base_data,
                vector,
                simulation_vector_description(split[0]),
            )
            add_vector_to_vector_selector_data(
                self._vector_calculator_data,
                vector,
                simulation_vector_description(split[0]),
            )

            metadata = (self._input_provider_set.vector_metadata(vector)
                        if self._input_provider_set else None)
            if metadata and metadata.is_total:
                # Get the likely name for equivalent rate vector and make dropdown options.
                # Requires that the time_index was either defined or possible to infer.
                avgrate_vec = rename_vector_from_cumulative(vector=vector,
                                                            as_rate=True)
                interval_vec = rename_vector_from_cumulative(vector=vector,
                                                             as_rate=False)

                avgrate_split = avgrate_vec.split(":")
                interval_split = interval_vec.split(":")

                add_vector_to_vector_selector_data(
                    self._vector_selector_base_data,
                    avgrate_vec,
                    f"{simulation_vector_description(avgrate_split[0])} ({avgrate_vec})",
                )
                add_vector_to_vector_selector_data(
                    self._vector_selector_base_data,
                    interval_vec,
                    f"{simulation_vector_description(interval_split[0])} ({interval_vec})",
                )

        # Retreive predefined expressions from configuration and validate
        self._predefined_expressions_path = (
            None if predefined_expressions is None else webviz_settings.
            shared_settings["predefined_expressions"][predefined_expressions])
        self._predefined_expressions = expressions_from_config(
            get_path(self._predefined_expressions_path) if self.
            _predefined_expressions_path else None)
        for expression in self._predefined_expressions:
            valid, message = validate_predefined_expression(
                expression, self._vector_selector_base_data)
            if not valid:
                warnings.warn(message)
            expression["isValid"] = valid

        # Create initial vector selector data with predefined expressions
        self._initial_vector_selector_data = copy.deepcopy(
            self._vector_selector_base_data)
        add_expressions_to_vector_selector_data(
            self._initial_vector_selector_data, self._predefined_expressions)

        plot_options = options if options else {}
        self._initial_visualization_selection = VisualizationOptions(
            plot_options.get("visualization", "statistics"))
        self._initial_vectors: List[str] = []
        if "vectors" not in plot_options:
            self._initial_vectors = []
        for vector in [
                vector for vector in ["vector1", "vector2", "vector3"]
                if vector in plot_options
        ]:
            self._initial_vectors.append(plot_options[vector])
        self._initial_vectors = self._initial_vectors[:3]

        # Set callbacks
        self.set_callbacks(app)
Ejemplo n.º 12
0
def create_history_vectors_df(
    provider: EnsembleSummaryProvider,
    vector_names: List[str],
    resampling_frequency: Optional[Frequency],
) -> pd.DataFrame:
    """Get dataframe with existing historical vector data for provided vectors.

    The returned dataframe contains columns with name of vector and corresponding historical
    data

    `Input:`
    * ensemble: str - Ensemble name
    * vector_names: List[str] - list of vectors to get historical data for
    [vector1, ... , vectorN]

    `Output:`
    * dataframe with non-historical vector names in columns and their historical data in rows.
    `Columns` in dataframe: ["DATE", "REAL", vector1, ..., vectorN]

    ---------------------
    `NOTE:`
    * Raise ValueError if vector does not exist for ensemble
    * If historical data does not exist for provided vector, vector is excluded from
    the returned dataframe.
    * Column names are not the historical vector name, but the original vector name,
    i.e. `WOPTH:OP_1` data is placed in colum with name `WOPT:OP_1`
    """
    if len(vector_names) < 1:
        raise ValueError("Empty list of vector names!")

    provider_vectors = provider.vector_names()
    resampling_frequency = (resampling_frequency
                            if provider.supports_resampling() else None)

    # Verify for provider
    for elm in vector_names:
        if elm not in provider_vectors:
            raise ValueError(
                f'Vector "{elm}" not present among vectors for provider')

    # Dict with historical vector name as key, and non-historical vector name as value
    historical_vector_and_vector_name_dict: Dict[str, str] = {}
    for vector in vector_names:
        # TODO: Create new historical_vector according to new provider metadata?
        historical_vector_name = historical_vector(vector=vector,
                                                   smry_meta=None)
        if historical_vector_name and historical_vector_name in provider.vector_names(
        ):
            historical_vector_and_vector_name_dict[
                historical_vector_name] = vector

    # Get lowest valid realization number
    realization = min(provider.realizations(), default=None)
    if not historical_vector_and_vector_name_dict or realization is None:
        return pd.DataFrame()

    historical_vector_names = list(
        historical_vector_and_vector_name_dict.keys())
    historical_vectors_df = provider.get_vectors_df(historical_vector_names,
                                                    resampling_frequency,
                                                    realizations=[realization])
    return historical_vectors_df.rename(
        columns=historical_vector_and_vector_name_dict)