pvrpm.core package

Subpackages

Submodules

pvrpm.core.case module

class pvrpm.core.case.SamCase(sam_json_dir: str, config: str, num_realizations: int = 0, results_folder: str | None = None)[source]

Bases: object

SAM Case loader, verifier, and simulation

__getstate__() dict[source]

Converts the case into a dictionary for pickling

__setstate__(state: dict) None[source]

Creates the object from a dictionary

base_case_sim() None[source]

Runs the base case simulation for this case, with no failures and optimal lifetime losses

This also sets base case output parameters of this object

get_npv()[source]

Returns the NPV for the case after a simulation has been ran, regardless of financial model used.

output(name: str) None | float | dict | list | str[source]

Get an output variable by string name, without specifying the module the variable resides in.

This will search all of the module’s outputs. If the value is not found in all of the modules, an AttributeError is raised.

Parameters:

name (str) – Name of the output

Returns:

The value of the output variable

precalculate_tracker_losses()[source]

Precalculate_tracker_losses calculates an array of coefficients (one for every day of the year) that account for the “benefit” of trackers on that particular day. This is used to determine how much power is lost if a tracker fails.

simulate(verbose: int = 0) None[source]

Executes simulations for all modules in this case.

Parameters:

verbose (int) – 0 for no log messages, 1 for simulation log messages

value(name: str, value: Any | None = None) None | float | dict | list | str[source]

Get or set by string name a module value, without specifying the module the variable resides in.

If there is no value provided, the value is returned for the variable name.

This will search the module’s data and update the variable if found. If the value is not found in all of the modules, an AttributeError is raised.

Parameters:
  • name (str) – Name of the value

  • value (Any, optional) – Value to set variable to

Returns:

Value or the variable if value is None

Note

Some modules have the same keys, this function will return the first key found in the module order specified in the configuration. Because of the way modules share data in PySAM, setting the value in the first module will propagate it to the other modules.

pvrpm.core.components module

class pvrpm.core.components.Components(case: SamCase)[source]

Bases: object

Data container for each component in the simulation, as well as component and simulation level data

ac_availability() float[source]

Calculates the availability of AC power due to DC component outages, including inverters, disconnects, transformers, and the grid

Returns:

Decimal percentage of AC modules available

Return type:

float

static compound_failures(function: str, parameters: dict, num_fails: int)[source]

Compounds the failures using the provided function and it’s parameters to calculate the number of days to reduce the time to detection by

Possible functions and their parameters are:
  • step: Failures follow a step function, each step reducing the detection time by a static amount
    • threshold (float): fraction 0 <= threshold <= 1 that signifies the amount of modules that must fail before next step is reached. So if this is 0.2, every 0.2 * total_components components that fail will reduce detection time by step

    • step (int): The amount of days to reduce detection time for every step. So 2 steps reduces detection time by 2 * step

  • exponential: Failures compound on an exponential function
    • base (float): The base for the exponential function > 0

  • log: Failures compound on a logorithmic function
    • base (float): The base for the log function > 0

  • linear: Failures compound linearly
    • slope (float): Slope of the linear function > 0

  • constant: Each failure reduces the time to detection by a static fraction constant
    • constant (float): fraction 0 <= frac <= 1 that specifies how much of the overall time each failure reduces. So if fraction is 0.1, a failure will reduce time to detection by “time_to_detection * 0.1”

current_degradation() float[source]

Calculates the current module degradation, which is averaged for only operational modules, since the power production hit from degradation of non-operational modules would be double counted

Returns:

Average degradation of operational modules

Return type:

float

dc_availability() float[source]

Calculates the availability of the DC power due to DC component outages, including modules, strings, and combiners

Returns:

Decimal percentage of available DC modules

Return type:

float

initialize_components(component_level: str) DataFrame[source]

Initalizes all components for the first time

Parameters:

component_level (str) – The configuration key for this component level

Returns:

A dataframe containing the initalized values for this component level

Return type:

pd.DataFrame

Note: Individual components have these columns:
  • state (bool): Operational (True) or failed (False)

  • defective (bool): Whehter component has a defect. True means the component is also eligible for the defective failure mode

  • time_to_failure (float): Number of days until component fails

  • failure_type (str): The name of the failure type time_to_failure represents

  • time_to_repair (float): Number of days from failure until compoent is repaired

  • time_to_detection (float): Number of days until the component failure is detected and repairs start

  • repair_times (float): the total repair time for this repair

  • monitor_times (float): the total monitoring time before repairs start

  • time_left_on_warranty (int): Number of days left on warranty (if applicable)

  • cumulative_failures (int): Total number of failures for that component

  • cumulative_oow_failures (int): Total number of out of warranty failures (if applicable)

  • failure_by_type_n (int): Each failure will have its own column with the number of failures of this type

  • defective (bool): Whether this component is defective or not (for defective failures)

  • defective_failures (int): Total number of defective failures

  • avail_downtime (int): How many hours this component was available

  • degradation_factor (float): 1 - percentage component has degraded at this time (module only)

  • days_of_degradation (int): Time that the module has been degrading for

snapshot()[source]

Returns the current state of the simulation including all internal dataframes, arrays, and variables for this object

Returns:

A dictionary containing the simulation snapshot data

Return type:

dict

Note

The returned objects are copies of this components objects to avoid changing data in the simulation unintentionally

The returned dictionary has these keys and values:
  • module: DataFrame of simulation data for module level

  • string: DataFrame of simulation data for string level

  • combiner: DataFrame of simulation data for combiner level

  • inverter: DataFrame of simulation data for inverter level

  • disconnect: DataFrame of simulation data for disconnect level

  • transformer: DataFrame of simulation data for transformer level

  • grid: DataFrame of simulation data for grid level

  • tracker: DataFrame of simulation data for tracker level

  • misc_data: DataFrame containing costs (for each level), module degradation, dc and ac availability, and tracker loss/availability if tracking is used

summarize_failures(component_level: str)[source]

Returns the number of failures per day for every failure defined

Parameters:

component_level (str) – The configuration key for this component level

Returns:

Dictionary containing the failure mode mapped to an np array of fails per each day

Return type:

dict

tracker_power_loss(day: int) Tuple[float, float][source]

Calculates the current loss factor due to failed trackers

Parameters:

day (int) – Current day in the simulation

Returns:

The fraction of trackers operational and the loss factor for failed trackers

Return type:

Tuple[float, float]

update_fails(component_level: str, day: int)[source]

Changes state of a component to failed, incrementing failures and checking warranty only for failed components of each failure type

Parameters:
  • component_level (str) – The component level to check for failures

  • day (int) – Current day in the simulation

Note

Updates the underlying dataframes in place

update_indep_monitor(day: int)[source]

If independent monitoring is defined, check it for the current day in simulation

Parameters:

day (int) – Current day in the simulation

update_labor_rates(new_labor: float)[source]

Update labor rates for a all levels for all types of repairs

Parameters:

new_labor (float) – The new labor rate

update_monitor(component_level: str, day: int)[source]

Updates time to detection from component level, static, and cross component level monitoring based on number of failures

Monitoring defined for each level is unaffected, only cross level monitoring on levels with no monitoring at that level have times updated based on failures, since monitoring defined at the component level uses the defined monitoring distribution for those components instead of the cross level monitoring

Parameters:
  • component_level (str) – The component level to check for monitoring

  • day (int) – Current day in the simulation

Note

Updates the underlying dataframes in place

update_repairs(component_level: str, day: int)[source]

Changes the state of a component to operational once repairs are complete, only for components where the time to repair is zero

Parameters:
  • component_level (str) – The component level of this repair

  • day (int) – Current day in the simulation

Note

Updates the underlying dataframes in place

pvrpm.core.enums module

class pvrpm.core.enums.ConfigKeys[source]

Bases: object

BASE = 'base'
CAN_FAIL = 'can_fail'
CAN_MONITOR = 'can_monitor'
CAN_REPAIR = 'can_repair'
COMBINER = 'combiner'
COMBINER_PER_INVERTER = 'num_combiners_per_inverter'
COMP_FUNC = 'compounding_function'
COMP_MONITOR = 'component_level_monitoring'
COMP_PARAM = 'compound_parameters'
CONF_INTERVAL = 'conf_interval'
CONST = 'constant'
COST = 'cost'
COST_PER_WATT = 'cost_per_watt'
DAYS = 'days'
DECAY_FRAC = 'decay_fraction'
DEGRADE = 'degradation'
DISCONNECT = 'disconnect'
DIST = 'distribution'
EXPON = 'exponential'
FAILURE = 'failures'
FAIL_PER_THRESH = 'failure_per_threshold'
FAIL_THRESH = 'global_threshold'
FRAC = 'fraction'
GRID = 'grid'
INDEP_MONITOR = 'indep_monitoring'
INFLATION = 'inflation'
INTERVAL = 'interval'
INVERTER = 'inverter'
INVERTER_PER_TRANS = 'num_inverters_per_transformer'
INVERTER_SIZE = 'inverter_size'
LABOR = 'labor_time'
LABOR_RATE = 'present_day_labor_rate'
LEVELS = 'levels'
LIFETIME_YRS = 'system_lifetime_yrs'
LINEAR = 'linear'
LOG = 'log'
LOGNORM = 'lognormal'
MEAN = 'mean'
MODULE = 'module'
MODULES_PER_STR = 'num_modules_per_string'
MODULE_ORDER = 'module_order'
MONITORING = 'monitoring'
MULTI_SUBARRAY = 'has_multiple_subarrays'
NAME = 'name'
NORMAL = 'normal'
NUM_COMBINERS = 'num_combiners'
NUM_COMPONENT = 'count'
NUM_REALIZATION = 'num_realizations'
NUM_TRACKERS = 'num_trackers'
NUM_TRANSFORMERS = 'num_transformers'
PARAM = 'parameters'
PARTIAL_FAIL = 'concurrent_failures'
PARTIAL_REPAIR = 'concurrent_repairs'
REPAIR = 'repairs'
RESULTS_FOLDER = 'results_folder'
SHAPE = 'shape'
SLOPE = 'slope'
STD = 'std'
STEP = 'step'
STRING = 'string'
STR_PER_COMBINER = 'num_strings_per_combiner'
THRESH = 'threshold'
TRACKER = 'tracker'
TRACKING = 'is_tracking_system'
TRANSFORMER = 'transformer'
UNIFORM = 'uniform'
WARRANTY = 'warranty'
WEIBULL = 'weibull'
WORST_TRACKER = 'use_worst_case_tracker'
component_keys = ['module', 'string', 'combiner', 'inverter', 'disconnect', 'transformer', 'grid', 'tracker']
compound_funcs = ['step', 'log', 'linear', 'exponential', 'constant']
compound_keys = ['distribution', 'parameters']
compound_levels = ['string', 'combiner', 'inverter', 'disconnect', 'transformer', 'grid']
dists = ['normal', 'exponential', 'weibull', 'lognormal', 'uniform']
failure_keys = ['distribution', 'parameters', 'labor_time', 'cost']
indep_monitor_keys = ['cost', 'levels', 'labor_time']
losses = ['annual_poa_shading_loss_percent', 'annual_poa_soiling_loss_percent', 'annual_poa_cover_loss_percent', 'annual_dc_module_loss_percent', 'annual_dc_mppt_clip_loss_percent', 'annual_dc_mismatch_loss_percent', 'annual_dc_diodes_loss_percent', 'annual_dc_wiring_loss_percent', 'annual_dc_tracking_loss_percent', 'annual_dc_nameplate_loss_percent', 'annual_dc_optimizer_loss_percent', 'annual_dc_perf_adj_loss_percent', 'annual_ac_inv_clip_loss_percent', 'annual_ac_inv_pso_loss_percent', 'annual_ac_inv_pnt_loss_percent', 'annual_ac_inv_eff_loss_percent', 'ac_loss', 'annual_transmission_loss_percent', 'annual_ac_perf_adj_loss_percent', 'annual_xfmr_loss_percent']
monitoring_keys = ['distribution', 'parameters']
needed_keys = ['module_order', 'num_realizations', 'num_combiners', 'num_transformers', 'num_trackers', 'results_folder', 'conf_interval', 'present_day_labor_rate', 'inflation', 'use_worst_case_tracker', 'module', 'string', 'combiner', 'inverter', 'disconnect', 'transformer', 'grid']
partial_failure_keys = ['distribution', 'parameters', 'labor_time', 'cost']
partial_repair_keys = ['distribution', 'parameters']
repair_keys = ['distribution', 'parameters']

pvrpm.core.exceptions module

exception pvrpm.core.exceptions.CaseError[source]

Bases: Exception

pvrpm.core.logger module

pvrpm.core.logger.init_logger()[source]

Initalizes logger for module

pvrpm.core.simulation module

pvrpm.core.simulation.cf_interval(alpha: float, std: float, num_samples: int) float[source]

Calculates the two tails margin of error given the desired input. The margin of error is the value added and subtracted by the sample mean to obtain the confidence interval

Sample sizes less then equal to 30 use t score, greater then 30 use z score

Parameters:
  • alpha (float) – The significance level for the interval

  • std (float) – The standard deviation of the data

  • num_samples (int) – The number of samples in the data

Returns:

The margin of error

Return type:

float

pvrpm.core.simulation.gen_results(case: SamCase, results: List[Components]) List[DataFrame][source]

Generates results for the given SAM case and list of component objects containing the results of each realization.

Parameters:
  • case (SamCase) – The loaded and verified case to use with the simulation

  • results (list(Components)) – List of component objects that contain the results for each realization

Returns:

List of dataframes containing the results.

Return type:

list(pd.DataFrame)

Note

The order of the returned dataframes is:
  • Summary Results

  • Degradation Results

  • DC Power

  • AC Power

  • Yearly Costs

pvrpm.core.simulation.graph_results(case: SamCase, results: List[Components], save_path: str | None = None) None[source]

Generate graphs from a list of Component objects from each realization

Parameters:
  • case (SamCase) – The loaded and verified case to use with the simulation

  • results (list(Components)) – List of component objects that contain the results for each realization

  • save_path (str, Optional) – Path to save graphs to, if provided

pvrpm.core.simulation.pvrpm_sim(case: SamCase, save_results: bool = False, save_graphs: bool = False, progress_bar: bool = False, debug: int = 0, threads: int = 1) List[Components][source]

Run the PVRPM simulation on a specific case. Results will be saved to the folder specified in the configuration.

Parameters:
  • case (SamCase) – The loaded and verified case to use with the simulation

  • save_results (bool, Optional) – Whether to save output csv results

  • save_graphs (bool, Optional) – Whether to save output graphs

  • progress_bar (bool, Optional) – Whether to display progress bar for each realization

  • debug (int, Optional) – Whether to save simulation state every debug days (0 to turn off)

  • threads (int, Optional) – Number of threads to use for paralizing realizations

Returns:

Returns the list of results Component objects for each realization

Return type:

list(Components)

pvrpm.core.simulation.run_system_realization(case: SamCase, seed: bool = False, realization_num: int = 0, progress_bar: bool = False, debug: int = 0) Components[source]

Run a full realization for calculating costs

Parameters:
  • case (SamCase) – The loaded and verified case to use with the simulation

  • seed (bool, Optional) – Whether to seed the random number generator, for multiprocessing

  • realization_num (int, Optional) – Current realization number, used for multiprocessing

  • progress_bar (bool, Optional) – Whether to display progress bar during the realization

  • debug (int, Optional) – Whether to save simulation state every debug days (0 to turn off)

Returns:

The components object which contains all the data for this realization

Return type:

Components

pvrpm.core.simulation.simulate_day(case: SamCase, comp: Components, day: int)[source]

Updates and increments the simulation by a day, performing all neccesary component updates.

Parameters:
  • case (SamCase) – The current Sam Case of the simulation

  • comp (Components) – The components class containing all the outputs for this simulation

  • day (int) – Current day in the simulation

pvrpm.core.utils module

pvrpm.core.utils.component_degradation(percent_per_day: float, t: int) float[source]

Calculate the degradation of a component given the time since last replacement

Parameters:
  • percent_per_day (float) – The percent degradation per day of the module

  • t (int) – Time since the module was last replaced, or if its a new module, installed

Returns:

The performance of the module, between 0 and 1

Return type:

float

Note

This gives the overall module performance based on degradation, so if the module has degraded 2 percent so far, this function returns 0.98

pvrpm.core.utils.filename_to_module(filename: str) object[source]

Takes the filename of an exported json file from SAM, extracts the module name, and returns a callback to that module that can be used to create an object

Parameters:

filename (str) – Filename of the exported case

Returns:

PySAM object the file represents

Return type:

PySAM

pvrpm.core.utils.get_components_per(array_to_split: array, split_indicies: array, per: int)[source]

Splits a 1D array into a 2D array split up by split indicies, with per amount for each row

This will handle when the data cannot be evenly split.

Parameters:
  • array_to_split (np.array) – 1D array to split.

  • split_indicies (np.array) – List of row indicies for the new array

  • per (int) – The amount to fill each row with

Returns:

The newly shaped array

Return type:

np.array

pvrpm.core.utils.get_higher_components(top_level: str, start_level: str, case, start_level_df: DataFrame | None = None) Tuple[array, array, int][source]

Calculates the indicies of the top level that correspond to the given level df indicies and returns the given level indicies count per top level component and the total number of start_level components per top_level component

Parameters:
  • top_level (str) – The string name of the component level to calculate indicies for

  • start_level (str) – The string name of the component level to start at

  • case (SamCase) – The case object for this simulation

  • start_level_df (pd.DataFrame, Optional) – The dataframe of the component level for which to find the corresponding top level indicies for

Returns:

If start_level_df is given, returns the top level indicies, the number of start_level components in start_level_df per top level index, and the total number of start_level components per top_level component. If start_level_df is None this only returns the total number of start_level components per top_level component.

Return type:

tuple(np.array, np.array, int)

pvrpm.core.utils.getattr_override(obj: Any, attr: str) Any[source]
pvrpm.core.utils.load_pysam_modules()[source]

Loads ALL of PySAM’s modules manually and globalizes them

This is needed because PySAM is a wrapper for the ssc and sdk of SAM, which includes dynamic modules that are not properly defined for pybind, so using pkgutil’s walk_packages function does not work (import error). Since the modules need to be loaded in order for getattr to find it, this must be done once when the program starts

pvrpm.core.utils.sample(distribution: str, parameters: dict, num_samples: int) array[source]

Sample data from a distribution. If distribution is a supported distribution, parameters should be a dictionary with keys “mean” and “std”. Otherwise, distribution should be a scipy stats function and parameters be the kwargs for the distribution.

Supported Distributions (only requires mean and std):
  • lognormal

  • normal

  • uniform (one std around mean)

  • weibull

  • exponential

Parameters:
  • distribution (str) – Name of the distribution function

  • parameters (dict) – Kwargs for the distribution (for a supported distribution should only be the mean and std)

  • num_samples (int) – Number of samples to return from distribution

Returns:

obj:(list): List of floats containing samples from the distribution

pvrpm.core.utils.summarize_dc_energy(dc_power_output: tuple, split: int) array[source]

Calculates the DC energy (kWh) based on an input array of timeseries DC power (kW) for the system lifetime (likely the ‘dc_net’ output from SAM)

Can be used to summarize similar hourly, daily data to yearly

Parameters:
  • dc_power_output (tuple) – Tuple output from SAM simulation

  • split (int) – The frequency to split the data too, typically this is the number of years the system was simulated for (system_lifetime_yrs)

Returns:

Numpy array of length system_lifetime_yrs containing the yearly energy in kWh

Return type:

np.array

Module contents