Source code for marmot.marmot_plot_main

# -*- coding: utf-8 -*-
"""Main plotting source code, creates output figures and data-tables.

marmot_plot_main.py is the main plotting script within Marmot which calls on 
supporting files to read in data, create the plot, and then return the plot and 
data to marmot_plot_main.py. The supporting modules can be viewed within the repo 
plottingmodules folder and have descriptive names such as total_generation.py, 
generation_stack.py, curtailment.py etc.

@author: Daniel Levie
"""
# ========================================================================================
# Import Python Libraries
# ========================================================================================

import importlib
import sys
import time
from pathlib import Path
from typing import Union, List

import matplotlib.pyplot as plt
import pandas as pd

try:
    import marmot.utils.mconfig as mconfig
except ModuleNotFoundError:
    from utils.definitions import INCORRECT_ENTRY_POINT

    print(INCORRECT_ENTRY_POINT.format(Path(__file__).name))
    sys.exit()
from marmot.metamanagers.read_metadata import MetaData
from marmot.plottingmodules.plotutils.plot_data_helper import GenCategories
from marmot.plottingmodules.plotutils.plot_exceptions import (
    DataSavedInModule,
    InputSheetError,
    MissingInputData,
    MissingMetaData,
    MissingZoneData,
    UnderDevelopment,
    UnsupportedAggregation,
)
from marmot.plottingmodules.plotutils.styles import (
    ColorList,
    GeneratorColorDict,
    PlotMarkers,
)
from marmot.utils.definitions import INPUT_DIR, Module_CLASS_MAPPING
from marmot.utils.loggersetup import SetupLogger

# A bug in pandas requires this to be included, otherwise df.to_string truncates
# long strings.
# Fix available in Pandas 1.0 but leaving here in case user version not up to date
pd.set_option("display.max_colwidth", 1000)


[docs]class MarmotPlot(SetupLogger): """Main module class to be instantiated to run the plotter. MarmotPlot handles the selection of plotting module to create the desired figure and saving of outputs. It also handles the area aggregation selection """ def __init__( self, Scenarios: Union[str, list], AGG_BY: str, model_solutions_folder: Union[str, Path], gen_names_dict: Union[str, Path, pd.DataFrame, dict], ordered_gen_categories: Union[str, Path, pd.DataFrame], color_dictionary: Union[str, Path, pd.DataFrame, dict], marmot_plot_select: Union[str, Path, pd.DataFrame], marmot_solutions_folder: Union[str, Path] = None, scenario_diff: Union[str, list] = None, zone_region_sublist: Union[str, list] = None, xlabels: Union[str, list] = None, ylabels: Union[str, list] = None, ticklabels: Union[str, list] = None, region_mapping: Union[str, Path, pd.DataFrame] = pd.DataFrame(), tech_subset: Union[str, list] = None, **kwargs, ): """ Args: Scenarios (Union[str, list]): Name of scenarios to process. AGG_BY (str): Informs region type to aggregate by when creating plots. model_solutions_folder (Union[str, Path]): Directory containing model simulation results subfolders and their files. gen_names_dict (Union[str, Path, pd.DataFrame, dict]): Path to, Dataframe or dict of generator technologies to rename. ordered_gen_categories (Union[str, Path, pd.DataFrame]): Path to or Dataframe containing ordered generation and columns to specify technology subsets. color_dictionary (Union[str, Path, pd.DataFrame, dict]): Path to, Dataframe or dict containing list of colors to assign to each generator category. marmot_plot_select (Union[str, Path, pd.DataFrame]): Path to or DataFrame containing information on plots to create and certain settings. marmot_solutions_folder (Union[str, Path], optional): Directory to save Marmot solution files. Defaults to None. scenario_diff (Union[str, list], optional): 2 value string or list, used to compare 2 scenarios. Defaults to None. zone_region_sublist (Union[str, list], optional): Subset of regions to plot from AGG_BY. Defaults to None. xlabels (Union[str, list], optional): x axis labels for facet plots. Defaults to None. ylabels (Union[str, list], optional): y axis labels for facet plots. Defaults to None. ticklabels (Union[str, list], optional): custom ticklabels for plots, not available for every plot type. Defaults to None. region_mapping (Union[str, Path, pd.DataFrame], optional): Path to or Dataframe to map custom regions/zones to create custom aggregations. Aggregations are created by grouping PLEXOS regions. Defaults to pd.DataFrame(). tech_subset (Union[str, list], optional): Tech subset category to plot. The tech_subset value should be a column in the ordered_gen_categories.csv. If left None all techs will be plotted Defaults to None. **kwargs These parameters will be passed to the marmot.utils.loggersetup.SetupLogger class. """ super().__init__("plotter", **kwargs) # Instantiation of SetupLogger self.Scenarios = self.convert_str_to_list(Scenarios) self.AGG_BY = AGG_BY self.model_solutions_folder = Path(model_solutions_folder) self.gen_names_dict = gen_names_dict self.ordered_gen_categories = ordered_gen_categories self.color_dictionary = color_dictionary self.marmot_plot_select = marmot_plot_select if marmot_solutions_folder is None: self.marmot_solutions_folder = self.model_solutions_folder else: self.marmot_solutions_folder = Path(marmot_solutions_folder) self.scenario_diff = self.convert_str_to_list(scenario_diff) self.zone_region_sublist = self.convert_str_to_list(zone_region_sublist) self.xlabels = self.convert_str_to_list(xlabels) self.ylabels = self.convert_str_to_list(ylabels) self.custom_xticklabels = self.convert_str_to_list(ticklabels) self.region_mapping = region_mapping self.tech_subset = tech_subset self._ordered_gen_list = None @property def ordered_gen_list(self) -> List[str]: """List of ordered generator technolgies. Oder is specified in the ordered_gen_categories input. Returns: List[str]: Ordered list of generator technolgies """ if self._ordered_gen_list is None: # Subset ordered_gen to user desired generation if self.tech_subset: if self.tech_subset not in self.ordered_gen_categories.columns: self.logger.warning( f"{self.tech_subset} column was not found " "in the ordered_gen_categories.csv. " "All generator technologies will be plotted" ) self._ordered_gen_list = ( self.ordered_gen_categories["Ordered_Gen"].str.strip().tolist() ) else: ordered_gen = self.ordered_gen_categories.loc[ self.ordered_gen_categories[self.tech_subset] == True ] self._ordered_gen_list = ( ordered_gen["Ordered_Gen"].str.strip().tolist() ) self.logger.info(f"Tech Aggregation selected: {self.tech_subset}") else: self._ordered_gen_list = ( self.ordered_gen_categories["Ordered_Gen"].str.strip().tolist() ) # If Other category does not exist in ordered_gen, create entry if "Other" not in self._ordered_gen_list: self._ordered_gen_list.append("Other") return self._ordered_gen_list @property def gen_names_dict(self) -> dict: """Dictionary of existing gen technology names to new names. Used to rename technologies. Returns: dict: Keys: Existing names, Values: New names """ return self._gen_names_dict @gen_names_dict.setter def gen_names_dict(self, gen_names_dict) -> None: if isinstance(gen_names_dict, (str, Path)): try: gen_names_dict = pd.read_csv(gen_names_dict) except FileNotFoundError: msg = ( "Could not find specified gen_names_dict csv file; " "check file name and path." ) self.logger.error(msg) raise FileNotFoundError(msg) if isinstance(gen_names_dict, pd.DataFrame): if len(gen_names_dict.axes[1]) == 2: self._gen_names_dict = ( gen_names_dict.set_index(gen_names_dict.columns[0]) .squeeze() .to_dict() ) else: msg = ( "Expected exactly 2 columns for gen_names_dict input, " f"{len(input.axes[1])} columns were in the DataFrame." ) self.logger.error(msg) raise ValueError(msg) elif isinstance(gen_names_dict, dict): self._gen_names_dict = gen_names_dict else: msg = ( "Expected a DataFrame, dict, or a file path to csv for the gen_names_dict input but " f"recieved a {type(gen_names_dict)}" ) self.logger.error(msg) raise NotImplementedError(msg) @property def ordered_gen_categories(self) -> pd.DataFrame: """DataFrame containing generator order and category information Has at least one column named Ordered_Gen. Other columns define different generator technology category groupings. Returns: pd.DataFrame: ordered_gen_categories DataFrame """ return self._ordered_gen_categories @ordered_gen_categories.setter def ordered_gen_categories(self, ordered_gen_categories) -> None: if isinstance(ordered_gen_categories, (str, Path)): try: ordered_gen_categories = pd.read_csv(ordered_gen_categories) except FileNotFoundError: msg = ( "Could not find specified ordered_gen_categories csv file; " "check file name and path." ) self.logger.error(msg) raise FileNotFoundError(msg) if isinstance(ordered_gen_categories, pd.DataFrame): if "Ordered_Gen" in ordered_gen_categories.columns: self._ordered_gen_categories = ordered_gen_categories else: msg = "Misssing 'Ordered_Gen' column from ordered_gen_categories input." self.logger.error(msg) raise ValueError(msg) else: msg = ( "Expected a DataFrame or a file path to csv for the ordered_gen_categories input but " f"recieved a {type(ordered_gen_categories)}" ) self.logger.error(msg) raise NotImplementedError(msg) # Compare gen_names_dict to ordered_gen_categories if ( set(self.gen_names_dict.values()).issubset( self._ordered_gen_categories["Ordered_Gen"].str.strip().tolist() ) ) == False: missing_gen = set(self.gen_names_dict.values()) - ( set(self._ordered_gen_categories["Ordered_Gen"].str.strip().tolist()) ) self.logger.warning( "The following tech categories from the " "gen_names_dict input do not exist in " "ordered_gen_categorie input!: " f"{missing_gen}" ) @property def color_dictionary(self) -> dict: """Dictionary of gen technology names to plotting colors. Returns: dict: Keys gen technologies, Values colors """ return self._color_dictionary @color_dictionary.setter def color_dictionary(self, color_dictionary) -> None: if isinstance(color_dictionary, (str, Path)): try: color_dictionary = pd.read_csv(color_dictionary) except FileNotFoundError: msg = ( "Could not find specified color dictionary csv file; " "check file name and path." ) self.logger.error(msg) raise FileNotFoundError(msg) if isinstance(color_dictionary, pd.DataFrame): if len(color_dictionary.axes[1]) == 2: self._color_dictionary = GeneratorColorDict.set_colors_from_df( color_dictionary ).color_dict else: msg = ( "Expected exactly 2 columns for color_dictionary input, " f"{len(color_dictionary.axes[1])} columns were in the DataFrame." ) self.logger.error(msg) raise ValueError(msg) elif isinstance(color_dictionary, dict): self._color_dictionary = GeneratorColorDict(color_dictionary).color_dict else: msg = ( "Expected a DataFrame, dict, or file path to csv for the color_dictionary input but " f"recieved a {type(color_dictionary)}" ) self.logger.error(msg) raise NotImplementedError(msg) @property def marmot_plot_select(self) -> pd.DataFrame: """DataFrame containing information on plots to create and certain settings. Returns: pd.DataFrame: """ return self._marmot_plot_select @marmot_plot_select.setter def marmot_plot_select(self, marmot_plot_select) -> None: if isinstance(marmot_plot_select, (str, Path)): try: self._marmot_plot_select = pd.read_csv(marmot_plot_select) except FileNotFoundError: msg = ( "Could not find specified marmot_plot_select csv file; " "check file name and path." ) self.logger.error(msg) raise FileNotFoundError(msg) elif isinstance(marmot_plot_select, pd.DataFrame): self._marmot_plot_select = marmot_plot_select else: msg = ( "Expected a DataFrame or a file path to csv for the marmot_plot_select input but " f"recieved a {type(marmot_plot_select)}" ) self.logger.error(msg) raise NotImplementedError(msg) @property def region_mapping(self) -> pd.DataFrame: """Region mapping Dataframe to map custom aggregations. Returns: pd.DataFrame: """ return self._region_mapping @region_mapping.setter def region_mapping(self, region_mapping) -> None: if isinstance(region_mapping, (str, Path)): try: region_mapping = pd.read_csv(region_mapping) except FileNotFoundError: msg = ( "Could not find specified region_mapping csv file; " "check file name and path." ) self.logger.error(msg) raise FileNotFoundError(msg) if isinstance(region_mapping, pd.DataFrame): self._region_mapping = region_mapping.astype(str) if "category" in region_mapping.columns: # delete category columns if exists self._region_mapping = self._region_mapping.drop(["category"], axis=1) else: msg = ( "Expected a DataFrame or a file path to csv for the region_mapping input but " f"recieved a {type(region_mapping)}" ) self.logger.error(msg) raise NotImplementedError(msg)
[docs] @staticmethod def convert_str_to_list(string_object: str) -> List[str]: """Converts a comma separated string to a list. Args: string_object (str): A comma separated string Returns: List[str]: list of strings. """ if isinstance(string_object, str): list_obj = [x.strip() for x in string_object.split(",")] else: list_obj = string_object return list_obj
[docs] def get_geographic_regions(self, meta: MetaData) -> List[str]: """Gets the geographic regions to plot based on the geographic aggregation. The aggregation is determined with the AGG_BY attribute. region and zone (PLEXOS) will pull model defined aggregations. Other aggregations will use values from the region mapping file. The zone_region_sublist attribute can be used to reduce the set of geographic regions to plot. Args: meta (MetaData): instance of MetaData class Returns: List[str]: List of geographic regions to plot """ if self.AGG_BY in {"zone", "zones", "Zone", "Zones"}: self.AGG_BY = "zone" zones = pd.concat([meta.zones(scenario) for scenario in self.Scenarios]) if zones.empty == True: self.logger.warning( "Input Sheet Data Incorrect! Your model does " "not contain Zones, enter a different aggregation" ) sys.exit() Zones = zones["name"].unique() if self.zone_region_sublist: zsub = [] for zone in self.zone_region_sublist: if zone in Zones: zsub.append(zone) else: self.logger.info( "metadata does not contain zone: " f"{zone}, SKIPPING ZONE" ) if zsub: Zones = zsub else: self.logger.warning( f"None of: {self.zone_region_sublist} " "in model Zones. Plotting all Zones" ) elif self.AGG_BY in {"region", "regions", "Region", "Regions"}: self.AGG_BY = "region" regions = pd.concat([meta.regions(scenario) for scenario in self.Scenarios]) if regions.empty == True: self.logger.warning( "Input Sheet Data Incorrect! Your model does " "not contain Regions, enter a different aggregation" ) sys.exit() Zones = regions["region"].unique() if self.zone_region_sublist: zsub = [] for region in self.zone_region_sublist: if region in Zones: zsub.append(region) else: self.logger.info( "metadata does not contain region: " f"{region}, SKIPPING REGION" ) if zsub: Zones = zsub else: self.logger.warning( f"None of: {self.zone_region_sublist} " "in model Regions. Plotting all Regions" ) elif not self.region_mapping.empty: self.logger.info( "Plotting Custom region aggregation from " "region_mapping File" ) regions = pd.concat([meta.regions(scenario) for scenario in self.Scenarios]) self.region_mapping = regions.merge( self.region_mapping, how="left", on="region" ) self.region_mapping.dropna(axis=1, how="all", inplace=True) try: Zones = self.region_mapping[self.AGG_BY].unique() except KeyError: self.logger.warning( f"AGG_BY = '{self.AGG_BY}' is not in the " "region_mapping File, enter a different aggregation" ) sys.exit() # remove any nan that might end up in list Zones = [x for x in Zones if str(x) != "nan"] if self.zone_region_sublist: zsub = [] for region in self.zone_region_sublist: if region in Zones: zsub.append(region) else: self.logger.info( "region_mapping File does not contain region: " f"{region}, SKIPPING REGION" ) if zsub: Zones = zsub else: self.logger.warning( f"None of: {self.zone_region_sublist} " "in region_mapping File. Plotting all " f"Regions of aggregation '{self.AGG_BY}'" ) else: self.logger.warning( "AGG_BY is not defined correctly, aggregation " "specified was not found, system will now exit" ) sys.exit() return Zones
[docs] def run_plotter(self): """Main method to call to begin plotting figures. This method takes no input variables, all required variables are passed in via the __init__ method. """ self.logger.info(f"Area Aggregation selected: {self.AGG_BY}") if self.zone_region_sublist: self.logger.info( f"Only plotting {self.AGG_BY}: " f"{self.zone_region_sublist}" ) processed_hdf5_folder = self.marmot_solutions_folder.joinpath( "Processed_HDF5_folder" ) figure_format = mconfig.parser("figure_file_format") if figure_format == "nan": figure_format = "png" # Create an instance of MetaData. meta = MetaData(processed_hdf5_folder, region_mapping=self.region_mapping) Zones = self.get_geographic_regions(meta) # ================================================================================ # Start Main plotting loop # ================================================================================ # Filter for chosen figures to plot plot_selection = self.marmot_plot_select.loc[ self.marmot_plot_select["Plot Graph"] == True ] plot_selection = plot_selection.sort_values(by=["Marmot Module", "Method"]) list_modules = plot_selection["Marmot Module"].unique() start_timer = time.time() for module in list_modules: module_plots = plot_selection.loc[plot_selection["Marmot Module"] == module] # List of required arguments argument_list = [ Zones, self.Scenarios, self.AGG_BY, self.ordered_gen_list, self.marmot_solutions_folder, ] # dictionary of keyword arguments passed to plotting modules; # key names match the instance variables in each module argument_dict = { "gen_names_dict": self.gen_names_dict, "gen_categories": GenCategories().set_categories( self.ordered_gen_categories ), "marmot_color_dict": self.color_dictionary, "scenario_diff": self.scenario_diff, "ylabels": self.ylabels, "xlabels": self.xlabels, "custom_xticklabels": self.custom_xticklabels, "color_list": ColorList().colors, "marker_style": PlotMarkers().markers, "region_mapping": self.region_mapping, "tech_subset": self.tech_subset, } # Import plot module from plottingmodules package plot_module = importlib.import_module("marmot.plottingmodules." + module) # Instantiate the module class class_name = getattr(plot_module, Module_CLASS_MAPPING[module]) instantiate_mplot = class_name(*argument_list, **argument_dict) # Create output folder for each plotting module figures: Path = instantiate_mplot.figure_folder.joinpath( f"{self.AGG_BY}_{module}" ) figures.mkdir(exist_ok=True) # Main loop to process each figure and pass # plot specific variables to methods for _, row in module_plots.iterrows(): print("\n\n\n") self.logger.info(f"Plot = {row['Figure Output Name']}") if pd.isna(row.iloc[2]): prop = None else: prop = row.iloc[2] # Modifies timezone string before plotting if pd.isna(row.iloc[6]): timezone_string: str = "Date" else: timezone_string: str = f"Date ({row.iloc[6]})" if pd.isna(row.iloc[4]): days_before = 2 else: days_before = float(row.iloc[4]) if pd.isna(row.iloc[5]): days_after = 2 else: days_after = float(row.iloc[5]) if pd.notna(row["Custom Data File"]): custom_data_file_path = Path(row["Custom Data File"]) else: custom_data_file_path = None if ( pd.notna(row["Timeseries Plot Resolution"]) and row["Timeseries Plot Resolution"] == "Annual" ): data_resolution: str = "_Annual" else: data_resolution: str = "" if row["Group by Scenario or Year-Scenario"] == "Year-Scenario": scenario_groupby: str = "Year-Scenario" else: scenario_groupby: str = "Scenario" # Get figure method and run plot try: figure_method = getattr(instantiate_mplot, row["Method"]) except AttributeError: self.logger.warning( f"{Module_CLASS_MAPPING[module]} has no attribute '{row['Method']}'" ) continue Figure_Out = figure_method( figure_name=row.iloc[0], prop=prop, y_axis_max=float(row.iloc[3]), start=days_before, end=days_after, timezone=timezone_string, start_date_range=row.iloc[7], end_date_range=row.iloc[8], custom_data_file_path=custom_data_file_path, data_resolution=data_resolution, scenario_groupby=scenario_groupby, ) if isinstance(Figure_Out, MissingInputData): self.logger.info( "Add Inputs With Formatter Before " "Attempting to Plot!\n" ) continue if isinstance(Figure_Out, DataSavedInModule): self.logger.info( f"Plotting Completed for " f'{row["Figure Output Name"]}\n' ) self.logger.info("Plots & Data Saved Within Module!\n") continue if isinstance(Figure_Out, UnderDevelopment): self.logger.info("Plot is Under Development, Plotting Skipped!\n") continue if isinstance(Figure_Out, InputSheetError): self.logger.info("Input Sheet Data Incorrect!\n") continue if isinstance(Figure_Out, MissingMetaData): self.logger.info( "Required Meta Data Not Available For " "This Plot!\n" ) continue if isinstance(Figure_Out, UnsupportedAggregation): self.logger.info( f"Aggregation Type: '{self.AGG_BY}' " "not supported for This plot!\n" ) continue for zone_input in Zones: if isinstance(Figure_Out[zone_input], MissingZoneData): self.logger.info(f"No Data to Plot in {zone_input}") else: # Save figures Figure_Out[zone_input]["fig"].savefig( figures.joinpath( f"{zone_input}_" f'{row["Figure Output Name"]}' f".{figure_format}" ), dpi=600, bbox_inches="tight", ) # Save .csv's. if Figure_Out[zone_input]["data_table"].empty: self.logger.info( f'{row["Figure Output Name"]} ' "does not return a data table" ) else: Figure_Out[zone_input]["data_table"].to_csv( figures.joinpath( f"{zone_input}_" f'{row["Figure Output Name"]}.csv' ) ) self.logger.info( "Plotting Completed for " f'{row["Figure Output Name"]}\n' ) # plt.tight_layout() # plt.show() plt.close("all") end_timer = time.time() time_elapsed = end_timer - start_timer self.logger.info(f"Main Plotting loop took {round(time_elapsed/60,2)} minutes") self.logger.info("All Plotting COMPLETED") meta.close_h5()
[docs]def main(): """Run the plotting code and create desired plots and data-tables based on user input files.""" # ==================================================================================== # Load Input Properties # ==================================================================================== Marmot_user_defined_inputs = pd.read_csv( INPUT_DIR.joinpath(mconfig.parser("user_defined_inputs_file")), usecols=["Input", "User_defined_value"], index_col="Input", skipinitialspace=True, ) marmot_plot_select = pd.read_csv( INPUT_DIR.joinpath(mconfig.parser("plot_select_file")) ) # Folder to save your processed solutions if pd.isna( Marmot_user_defined_inputs.loc["Marmot_Solutions_folder", "User_defined_value"] ): marmot_solutions_folder = None else: marmot_solutions_folder = Marmot_user_defined_inputs.loc[ "Marmot_Solutions_folder", "User_defined_value" ].strip() Scenarios = Marmot_user_defined_inputs.loc["Scenarios", "User_defined_value"] # These variables (along with region_mapping) are used to initialize MetaData model_solutions_folder = Marmot_user_defined_inputs.loc[ "Model_Solutions_folder", "User_defined_value" ].strip() # For plots using the difference of the values between two scenarios. # Max two entries, the second scenario is subtracted from the first. if pd.isna( Marmot_user_defined_inputs.loc["Scenario_Diff_plot", "User_defined_value"] ): scenario_diff = None else: scenario_diff = Marmot_user_defined_inputs.loc[ "Scenario_Diff_plot", "User_defined_value" ] Mapping_folder = INPUT_DIR.joinpath("mapping_folder") if pd.isna( Marmot_user_defined_inputs.loc["Region_Mapping.csv_name", "User_defined_value"] ): region_mapping = pd.DataFrame() else: region_mapping = Mapping_folder.joinpath( Marmot_user_defined_inputs.loc[ "Region_Mapping.csv_name", "User_defined_value" ] ) gen_names_dict = Mapping_folder.joinpath( Marmot_user_defined_inputs.loc["gen_names.csv_name", "User_defined_value"] ) ordered_gen_cat_file = Mapping_folder.joinpath( Marmot_user_defined_inputs.loc[ "ordered_gen_categories_file", "User_defined_value" ] ) color_dictionary = Mapping_folder.joinpath( Marmot_user_defined_inputs.loc["color_dictionary_file", "User_defined_value"] ) AGG_BY = Marmot_user_defined_inputs.loc["AGG_BY", "User_defined_value"].strip() if pd.notna(Marmot_user_defined_inputs.loc["TECH_SUBSET", "User_defined_value"]): tech_subset = Marmot_user_defined_inputs.loc[ "TECH_SUBSET", "User_defined_value" ].strip() else: tech_subset = None # Facet Grid Labels (Based on Scenarios) if pd.isna( Marmot_user_defined_inputs.loc["zone_region_sublist", "User_defined_value"] ): zone_region_sublist = None else: zone_region_sublist = Marmot_user_defined_inputs.loc[ "zone_region_sublist", "User_defined_value" ] if pd.isna(Marmot_user_defined_inputs.loc["Facet_ylabels", "User_defined_value"]): ylabels = None else: ylabels = Marmot_user_defined_inputs.loc["Facet_ylabels", "User_defined_value"] if pd.isna(Marmot_user_defined_inputs.loc["Facet_xlabels", "User_defined_value"]): xlabels = None else: xlabels = Marmot_user_defined_inputs.loc["Facet_xlabels", "User_defined_value"] # option to change tick labels on plot if pd.isna(Marmot_user_defined_inputs.loc["Tick_labels", "User_defined_value"]): ticklabels = None else: ticklabels = Marmot_user_defined_inputs.loc["Tick_labels", "User_defined_value"] initiate = MarmotPlot( Scenarios, AGG_BY, model_solutions_folder, gen_names_dict, ordered_gen_cat_file, color_dictionary, marmot_plot_select, marmot_solutions_folder=marmot_solutions_folder, scenario_diff=scenario_diff, zone_region_sublist=zone_region_sublist, xlabels=xlabels, ylabels=ylabels, ticklabels=ticklabels, region_mapping=region_mapping, tech_subset=tech_subset, ) initiate.run_plotter()
if __name__ == "__main__": main()