def __init__(self): super().__init__() self._screenshot_filename = f"{self.plugin_name()}.png" ASSETS_DIR = Path(pkg_resources.resource_filename("everviz", "assets")) WEBVIZ_ASSETS.add(ASSETS_DIR / "axis_customization.css")
import pkg_resources import json from pathlib import Path from webviz_config.webviz_assets import WEBVIZ_ASSETS ASSETS_DIR = Path(pkg_resources.resource_filename("ertviz", "assets")) WEBVIZ_ASSETS.add(ASSETS_DIR / "ert-style.css") with open(ASSETS_DIR / "ert-style.json") as f: ERTSTYLE = json.load(f) WEBVIZ_CONFIG = (Path(pkg_resources.resource_filename("ertviz", "assets")) / "webviz-config.yml")
def __init__( self, app: Dash, webviz_settings: WebvizSettings, basedir: Path, planned_wells_dir: Path = None, ): super().__init__() self.plotly_theme = webviz_settings.theme.plotly_theme self.uid = uuid4() WEBVIZ_ASSETS.add( Path(webviz_subsurface.__file__).parent / "_assets" / "css" / "modal.css") self.set_callbacks(app) self.basedir = basedir self.planned_wells_dir = planned_wells_dir self.modelfile_path = basedir / "model_file.xml" self.modelfile = get_path(self.modelfile_path) self.surfaces = load_surfaces(basedir, self.modelfile_path) self.planned_wellfiles = (json.load( find_files(planned_wells_dir, "*.txt")) if planned_wells_dir else None) self.wellfiles = json.load( find_files(basedir / "input" / "welldata", "*.txt")) self.wellfiles = [str(get_path(Path(w))) for w in self.wellfiles] self.allfiles = json.load(find_files(basedir)) self.allfiles.append(self.modelfile_path) self.allfiles += self.planned_wellfiles self.planned_wellfiles = [ str(get_path(Path(w))) for w in self.planned_wellfiles ] self.surface_attributes = {} for i, surface in enumerate(self.surfaces): self.surface_attributes[surface["name"]] = { "color": get_color(i), "order": i, "name": surface["name"], "topofzone": surface["topofzone"], "surface": surface["d_"], "surface_de": surface["de_"], "surface_dt": surface["dt_"], "surface_dr": surface["dr_"], "surface_dte": surface["dte_"], } self.surfacenames = [surface["name"] for surface in self.surfaces] # Log files zonation_status_file = get_zonation_status(basedir) well_points_file = get_well_points(basedir) zonelog_name = get_zonelog_name(self.modelfile) self.xsec = HuvXsection( self.surface_attributes, zonation_status_file, well_points_file, zonelog_name, ) target_points_file = get_target_points(basedir) self.df_well_target_points = FilterTable(target_points_file, well_points_file) # Wellfiles and planned wells self.planned_wells = {} if planned_wells_dir is not None: self.planned_wells = { wf: xtgeo.well_from_file(wfile=wf) for wf in self.planned_wellfiles } self.wells = { wf: xtgeo.well_from_file(wfile=wf) for wf in self.wellfiles } # Store current layers self.state = {"switch": False} self.layers_state = []
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)
def __init__( self, app, webviz_settings: WebvizSettings, ensembles: list, relpermfile: str = None, scalfile: Path = None, sheet_name: Optional[Union[str, int, list]] = None, ): super().__init__() WEBVIZ_ASSETS.add( Path(webviz_subsurface.__file__).parent / "_assets" / "css" / "block_options.css") self.ens_paths = { ens: webviz_settings.shared_settings["scratch_ensembles"][ens] for ens in ensembles } self.plotly_theme = webviz_settings.theme.plotly_theme self.relpermfile = relpermfile if self.relpermfile is not None: self.satfunc = load_csv(ensemble_paths=self.ens_paths, csv_file=relpermfile) self.satfunc = self.satfunc.rename( str.upper, axis="columns").rename(columns={"TYPE": "KEYWORD"}) if "KEYWORD" not in self.satfunc.columns: raise ValueError( "There has to be a KEYWORD or TYPE column with corresponding Eclipse keyword: " "e.g SWOF, SGOF and etc.") # pylint: disable=literal-comparison valid_columns = (["ENSEMBLE", "REAL", "KEYWORD", "SATNUM"] + RelativePermeability.SATURATIONS + [ key for key in RelativePermeability.SCAL_COLORMAP if key != "Missing" ]) self.satfunc = self.satfunc[[ col for col in self.satfunc.columns if col in valid_columns ]] else: self.satfunc = load_satfunc(self.ens_paths) if any(keyword in RelativePermeability.RELPERM_FAMILIES[1] for keyword in self.satfunc["KEYWORD"].unique()): self.family = 1 if any(keyword in RelativePermeability.RELPERM_FAMILIES[2] for keyword in self.satfunc["KEYWORD"].unique()): warnings.warn(( "Mix of keyword family 1 and 2, currently only support one family at the " "time. Dropping all data of family 2 ('SWFN', 'SGFN', 'SGWFN', 'SOF2', " "'SOF3', 'SOF32D') and continues with family 1 ('SWOF', 'SGOF', 'SLGOF')." ), ) self.satfunc = self.satfunc[self.satfunc["KEYWORD"].isin( RelativePermeability.RELPERM_FAMILIES["fam1"])] if "SGOF" in self.satfunc["KEYWORD"].unique(): if "SLGOF" in self.satfunc["KEYWORD"].unique(): warnings.warn(( "Mix of 'SGOF' and 'SLGOF' in ensembles, resulting in non-unique " "horizontal axis ('SG' and 'SL') for 'KRG', 'KROG' and 'PCOG'. " "Dropping all data with 'SLGOF'."), ) self.satfunc = self.satfunc[ self.satfunc["KEYWORD"] != "SLGOF"] self.sat_axes_maps = { "SW": ["KRW", "KROW", "PCOW"], "SG": ["KRG", "KROG", "PCOG"], } else: self.sat_axes_maps = { "SW": ["KRW", "KROW", "PCOW"], "SL": ["KRG", "KROG", "PCOG"], } elif not all(keyword in RelativePermeability.RELPERM_FAMILIES[2] for keyword in self.satfunc["KEYWORD"].unique()): raise ValueError( "Unrecognized saturation table keyword in data. This should not occur unless " "there has been changes to ecl2df. Update of this plugin might be required." ) else: self.family = 2 self.sat_axes_maps = { "SW": ["KRW", "PCOW"], "SG": ["KRG", "PCOG"], "SO": ["KROW", "KROG"], } self.scalfile = scalfile self.sheet_name = sheet_name self.scal = (load_scal_recommendation(self.scalfile, self.sheet_name) if self.scalfile is not None else None) self.set_callbacks(app)
def __init__( self, app: Dash, webviz_settings: WebvizSettings, csvfile: str = None, ensembles: list = None, aggregated_csvfile: Path = None, aggregated_parameterfile: Path = None, observation_file: Path = None, observation_group: str = "general", remap_observation_keys: Dict[str, str] = None, remap_observation_values: Dict[str, str] = None, colors: Dict = None, initial_data: Dict = None, initial_layout: Dict = None, ): super().__init__() provider = EnsembleTableProviderFactory.instance() self._initial_data = initial_data if initial_data else {} self._initial_layout = initial_layout if initial_layout else {} if ensembles is not None and csvfile is not None: ensembles_dict: Dict[str, str] = { ens_name: webviz_settings.shared_settings["scratch_ensembles"][ens_name] for ens_name in ensembles } self._parameterproviderset = ( provider. create_provider_set_from_per_realization_parameter_file( ensembles_dict)) self._tableproviderset = ( provider.create_provider_set_from_per_realization_csv_file( ensembles_dict, csvfile)) self._ensemble_names = ensembles elif aggregated_csvfile and aggregated_parameterfile is not None: self._tableproviderset = ( provider.create_provider_set_from_aggregated_csv_file( aggregated_csvfile)) self._parameterproviderset = ( provider.create_provider_set_from_aggregated_csv_file( aggregated_parameterfile)) self._ensemble_names = self._tableproviderset.ensemble_names() else: raise ValueError( "Specify either ensembles and csvfile or aggregated_csvfile " "and aggregated_parameterfile") all_parameters: list = [ self._parameterproviderset.ensemble_provider(ens).column_names() for ens in self._ensemble_names ] self._parameter_names: list = list(set().union(*all_parameters)) all_data_columns: list = [ self._tableproviderset.ensemble_provider(ens).column_names() for ens in self._ensemble_names ] self._data_column_names: list = list(set().union(*all_data_columns)) dfs = [] for ens in self._ensemble_names: df = self._parameterproviderset.ensemble_provider( ens).get_column_data(column_names=self._parameterproviderset. ensemble_provider(ens).column_names()) df["ENSEMBLE"] = ens dfs.append(df) parameterdf = pd.concat(dfs) self._realizations = sorted(list(parameterdf["REAL"].unique())) self._parameter_filter = ParameterFilter(self.uuid("parameter-filter"), parameterdf) self._observationfile = observation_file self._observationmodel = (ObservationModel( get_path(self._observationfile), observation_group, remap_observation_keys, remap_observation_values, ) if self._observationfile else None) WEBVIZ_ASSETS.add( Path(webviz_subsurface.__file__).parent / "_assets" / "js" / "clientside_functions.js") self._colors: Dict = unique_colors(self._ensemble_names, webviz_settings.theme) if colors is not None: self._colors.update(colors) self.set_callbacks(app)
def __init__( self, app: Dash, webviz_settings: WebvizSettings, ensembles: list, surface_attributes: list, surface_name_filter: List[str] = None, wellfolder: Path = None, wellsuffix: str = ".w", zonelog: str = None, mdlog: str = None, well_tvdmin: Union[int, float] = None, well_tvdmax: Union[int, float] = None, well_downsample_interval: int = None, calculate_percentiles: bool = False, initial_settings: Dict = None, ): super().__init__() self._initial_settings = initial_settings if initial_settings else {} WEBVIZ_ASSETS.add( Path(webviz_subsurface.__file__).parent / "_assets" / "css" / "structural_uncertainty.css") WEBVIZ_ASSETS.add( Path(webviz_subsurface.__file__).parent / "_assets" / "js" / "clientside_functions.js") self._calculate_percentiles = calculate_percentiles self._wellfolder = wellfolder self._wellsuffix = wellsuffix self._wellfiles: List = [] if wellfolder is not None: self._wellfiles = json.load(find_files(wellfolder, wellsuffix)) self._well_set_model = WellSetModel( self._wellfiles, zonelog=zonelog, mdlog=mdlog, tvdmin=well_tvdmin, tvdmax=well_tvdmax, downsample_interval=well_downsample_interval, ) self._use_wells = bool(self._wellfiles) if (self._initial_settings.get("intersection_data", {}).get("well") and not self._use_wells): raise KeyError( "Well is specified in initial settings but no well data is found!" ) self._surf_attrs = surface_attributes self._ensemble_paths = { ens: webviz_settings.shared_settings["scratch_ensembles"][ens] for ens in ensembles } # Create a table of surface files surface_table = find_surfaces(self._ensemble_paths) # Filter on provided surface attributes surface_table = surface_table[surface_table["attribute"].isin( self._surf_attrs)] # Filter on provided surface names self._surfacenames = (list(surface_table["name"].unique()) if surface_name_filter is None else surface_name_filter) surface_table = surface_table[surface_table["name"].isin( surface_name_filter)] if surface_table.empty: raise ValueError("No surfaces found with the given attributes") self.ensembles = list(surface_table["ENSEMBLE"].unique()) for _, attr_df in surface_table.groupby("attribute"): if set(attr_df["name"].unique()) != set(self._surfacenames): raise ValueError( "Surface attributes has different surfaces. This is not supported!" ) self._surface_ensemble_set_model = { ens: SurfaceSetModel(surf_ens_df) for ens, surf_ens_df in surface_table.groupby("ENSEMBLE") } self._realizations = sorted(list(surface_table["REAL"].unique())) self._zonelog = zonelog colors = [ "#1f77b4", # muted blue "#ff7f0e", # safety orange "#2ca02c", # cooked asparagus green "#d62728", # brick red "#9467bd", # muted purple "#8c564b", # chestnut brown "#e377c2", # raspberry yogurt pink "#7f7f7f", # middle gray "#bcbd22", # curry yellow-green "#17becf", # blue-teal ] self._surfacecolors = [{ "surfacename": surfacename, "ensemble": ens, "COLOR": self._initial_settings.get("colors", {}).get(surfacename, {}).get( ens, colors[idx % len(colors)]), } for idx, surfacename in enumerate(self._surfacenames) for ens in self.ensembles] self._color_picker = ColorPicker( app=app, uuid=self.uuid("colorpicker"), dframe=pd.DataFrame(self._surfacecolors), ) self.first_surface_geometry = self._surface_ensemble_set_model[ self.ensembles[0]].first_surface_geometry self.set_callbacks(app)
import pkg_resources import json from pathlib import Path from webviz_config.webviz_assets import WEBVIZ_ASSETS ASSETS_DIR = Path(pkg_resources.resource_filename("webviz_ert", "assets")) WEBVIZ_ASSETS.add(ASSETS_DIR / "bootstrap-grid.css") WEBVIZ_ASSETS.add(ASSETS_DIR / "ert-style.css") with open(ASSETS_DIR / "ert-style.json") as f: ERTSTYLE = json.load(f) WEBVIZ_CONFIG = ( Path(pkg_resources.resource_filename("webviz_ert", "assets")) / "webviz-config.yml")
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)