Example YAML Configuration

Please read all the comments carefully, there are many options for each section of PVRPM.

### Run Setup ###
# results folder doesn't need to exist, it will be created if it doesnt already
# For windows use single backslash. Do not need to escape spaces in folder names
results_folder: /path/to/results/folder
num_realizations: 2 # number of realizations to run
conf_interval: 90 #XX % confidence interval around the mean will be calculated for summary results

### Case Setup ###
num_combiners: 2 # total number of DC combiner boxes
num_transformers: 1 # total number of Transformers
num_trackers: 0 # total number of trackers

### Financial Inputs ###
present_day_labor_rate: 100 # dollars per hour
inflation: 2.5 # in percent

### Failure Tracker Algorithm ###
use_worst_case_tracker: false


### Component Information ###
  # The structure of the user-entered component information is as follows:
  #**NOTE: components currently include: module, string, combiner, inverter, disconnect, transformer, grid, and (optional) tracker

#    component.
#      name = a string containing the name of the component type, used for error reporting
#      can_fail = true if component is allowed to fail
#      can_repair = true if component is allowed to be repaired after failing
#      can_monitor = true if component's failures use component level monitoring
#                    This can be false and replaced with other monitoring methods, see below for independent and cross level monitoring sections
#      ### if can_repair, can_monitor or can_fail is false, you can remove their respective section below (failures, monitoring and repairs)
#      warranty (can remove this section if no warranty)
#        days = number of days that the warranty is for (e.g. a 20 year warranty would be 20 * 365 days)
#      failures (list as many failures as needed)
#        distribution = distribution type of this failure mode
#        parameters = parameters for this failure mode
#          mean: mean in days for this failure mode's distribution
#          std: standard deviation for this failure mode's distribution (not all distributions need std)
#        labor_time = number of hours of labor it takes to repair this type of failure
#        cost = parts cost to repair this type of failure
#        cost_per_watt (optional) = USD per watt cost of failure FOR INVERTERS ONLY. This will OVERRIDE the cost if specified for a failure
#        by multiplying this value by the inverter size. If you want to set a static cost, use the cost parameter as normal
#        fraction (optional) = If fraction is defined, then this failure mode is a defective failure mode, and "fraction" represents the fraction of this type of component that are defective. This fraction of components with this failure mode is maintained throughout the simulation.
#        decay_fraction (optional) = Works similiarly to fraction, except the fraction of affected components decays. For example, if 0.5 is defined, then half of the components have this failure mode. Once those are repaired, half of the repaired components will have this failure mode, and so on.
#      concurrent_failures (these failures are all tracked in parallel, meaning a component can fail with these failure modes while another failure mode like above is still counting down)
#         list failures same as failures above
#      monitoring: this section specfies how long each failure takes to be detected by monitoring. This occurs before repair time begins. This should either be 1 section for all failures or a section for each failure
#        distribution = distribution type of monitoring times
#        parameters = parameters of the monitoring distribution
#          mean: mean in days for this monitoring mode's distribution
#          std: standard deviation for this monitoring mode's distribution (not all distributions need std)
#      repairs (list a repair for each failure, or only 1 repair for all failures)
#        distribution = distribution type of repair times
#        parameters = parameters of the repair distribution
#          mean: mean in days for this repair mode's distribution
#          std: standard deviation for this repair mode's distribution (not all distributions need std)
#        **NOTE: If there is only ONE repair distribution (repair[0]), then ALL failure modes will use that distribution! Otherwise, # repair modes must equal # failure modes.
#      concurrent_repairs (repairs for concurrent failure modes, same setup as repairs above)
#      degradation (MODULES ONLY) (remove if no degradation) (%/year)

### Distribution types
# PVRPM has some distributions built in, where only a mean and standard deviation is needed to
# properly model the failure or repair. Under the hood, PVRPM uses scipy.stats distribution
# functions to model these. However, scipy.stats documentation for each function is not very
# clear on how to convert the mean and std into usable values for the distribution, which
# is why PVRPM will wrap them for you.

# However, not every single scipy distribution is wrapped by PVRPM. These are the distributions wrapped by PVRPM (use these as the distribution option):
#  - exponential
#  - normal
#  - uniform
#  - lognormal
#  - weibull (can also provide shape for this distribution, see below)
# If using one of these distributions, you can simply provide the mean and std in days.
# **For the weibull distribution**: instead of standard deviation (std) you can provide the mean and shape
# See here: https://en.wikipedia.org/wiki/Weibull_distribution the shape parameter is k, and lambda is calculated by solving for it using the gamma function and the provided mean.
# Otherwise, if you provide the STD for the weibull distribution, it should be a large number otherwise you'll get values extremely close to the mean only

# You can also override this, and change the distribution to match the function name of any of the distributions listed here:
# https://docs.scipy.org/doc/scipy/reference/stats.html
# If you do this, then the "parameters" option will then be a list of kwargs to the scipy function you select. For example, if you want to use the gamma distribution:
#  distribution: gamma
#  parameters:
#    a: 1.99
#    scale: 100 # 1 / beta
###

module:
  name: module # can be anything you want
  can_fail: true
  can_repair: true
  can_monitor: true # leave true to use monitoring distributions

  warranty:
    days: 7300 # years converted to days

  failures:
    normal_failures: # this key name can be anything you want
      distribution: normal
      parameters: # parameters for distribution chosen above, either mean and std for a built in distribution or kwargs to the scipy function
        mean: 1460 # years converted to days
        std: 365 # days
      labor_time: 2 # in hours
      cost: 322 # in USD
    routine_failures: # this key name can be anything you want
      distribution: normal
      parameters:
        mean: 365 # mean in days, or you can do 1 / (num_failures / year * 365)
        std: 365
      labor_time: 2 # in hours
      cost: 322 # in USD
      fraction: 0.1 # > 0 and < 1, fraction of these components that are normal failures, maintained throughout the simulation
    defective_failures: # this key name can be anything you want
      distribution: exponential
      parameters:
        mean: 100 # mean in days, or you can do 1 / (num_failures / year * 365)
      labor_time: 2 # in hours
      cost: 322 # in USD
      decay_fraction: 0.2 # > 0 and < 1, fraction of these components that are defective

  concurrent_failures: # this happens all in parallel, independent of each other
    cell_failure: # this key name can be anything you want
      distribution: normal
      parameters: # parameters for distribution chosen above, either mean and std for a built in distribution or kwargs to the scipy function
        mean: 365 # years converted to days
        std: 365 # days
      labor_time: 2 # in hours
      cost: 322 # in USD
      decay_fraction: 0.2
    wiring_failure: # this key name can be anything you want
      distribution: normal
      parameters:
        mean: 365 # mean in days, or you can do 1 / (num_failures / year * 365)
        std: 365
      labor_time: 2 # in hours
      cost: 322 # in USD
      fraction: 0.1 # > 0 and < 1, fraction of these components that are normal failures, maintained throughout the simulation

  monitoring:
    all_monitoring: # this key name can be anything you want
      distribution: exponential
      parameters:
        mean: 5

  repairs:
    all_repairs: # this key name can be anything you want
      distribution: lognormal
      parameters:
        mean: 60 # in days
        std: 20 # in days

  concurrent_repairs:
    cell_repair: # this key name can be anything you want
      distribution: lognormal
      parameters:
        mean: 7 # in days
        std: 3 # in days

    wire_repair: # this key name can be anything you want
      distribution: lognormal
      parameters:
        mean: 3 # in days
        std: 3 # in days

  degradation: 20 # modules only, how much a module degrades per year in percent

string:
  name: string
  can_fail: true
  can_repair: true
  can_monitor: true

  failures:
    failure: # this key name can be anything you want
      distribution: exponential
      parameters:
        mean: 182.5 # mean in days, or you can do 1 / (num_failures / year * 365)
      labor_time: 1 # in hours
      cost: 20 # in USD

  monitoring:
    normal_monitoring: # this key name can be anything you want
      distribution: exponential
      parameters:
        mean: 5

  repairs:
    all_repairs: # this key name can be anything you want
      distribution: lognormal
      parameters:
        mean: 7 # in days
        std: 3 # in days

combiner:
  name: combiner
  can_fail: true
  can_repair: true
  can_monitor: true

  failures:
    failure: # this key name can be anything you want
      distribution: normal
      parameters:
        mean: 730
        std: 182.5
      labor_time: 2 # in hours
      cost: 976 # in USD

  monitoring:
    normal_monitoring: # this key name can be anything you want
      distribution: exponential
      parameters:
        mean: 5

  repairs:
    all_repairs: # this key name can be anything you want
      distribution: exponential
      parameters:
        mean: 3 # in days

inverter:
  name: inverter
  can_fail: true
  can_repair: true
  can_monitor: true

  failures:
    component_failure: # this key name can be anything you want
      distribution: exponential
      parameters:
        mean: 365
      labor_time: 0 # in hours
      cost_per_watt: 0.07 # in USD, cents/watt. This will be multiplied by the inverter_size (listed in SAM). Overrides cost
    routine_failure:
      distribution: exponential
      parameters:
        mean: 365
      labor_time: 0
      cost: 1000 # static cost, not multiplied by inverter size
    catastrophic_failure:
      distribution: normal
      parameters:
        mean: 500
        std: 365.25
      labor_time: 0
      cost_per_watt: 0.35 # in USD, cents/watt. This will be multiplied by the inverter_size (listed in SAM). Overrides cost

  monitoring:
    all_monitoring: # this key name can be anything you want
      distribution: exponential
      parameters:
        mean: 5

  repairs:
    component_repair: # this key name can be anything you want
      distribution: lognormal
      parameters:
        mean: 3 # in days
        std: 1.5
    routine_repair:
      distribution: exponential
      parameters:
        mean: 0.5
    catastrophic_repair:
      distribution: lognormal
      parameters:
        mean: 3
        std: 1.5

disconnect: # A/C disconnect
  name: disconnect
  can_fail: true
  can_repair: true
  can_monitor: true

  failures:
    failure: # this key name can be anything you want
      distribution: weibull
      parameters:
        mean: 1095
        std: 1200 # use a large STD for weibull
      labor_time: 4 # in hours
      cost: 500 # in USD

  monitoring:
    normal_monitoring: # this key name can be anything you want
      distribution: exponential
      parameters:
        mean: 5

  repairs:
    all_repairs: # this key name can be anything you want
      distribution: lognormal
      parameters:
        mean: 1 # in days
        std: 0.5

transformer:
  name: transformer
  can_fail: true
  can_repair: true
  can_monitor: true

  failures:
    failure: # this key name can be anything you want
      distribution: weibull
      parameters:
        mean: 365 # can optionally provide the shape which is K parameter on wikipedia page
        shape: 0.3477 # lambda is calculated from the mean
      labor_time: 10 # in hours
      cost: 32868 # in USD

  monitoring:
    normal_monitoring: # this key name can be anything you want
      distribution: exponential
      parameters:
        mean: 5

  repairs:
    all_repairs: # this key name can be anything you want
      distribution: lognormal
      parameters:
        mean: 0.25 # in days
        std: 0.5

grid:
  name: grid
  can_fail: true
  can_repair: true
  can_monitor: true

  failures:
    failure: # this key name can be anything you want
      distribution: weibull
      parameters:
        mean: 100
        shape: 0.75
      labor_time: 0 # in hours
      cost: 0 # in USD

  monitoring:
    normal_monitoring: # this key name can be anything you want
      distribution: exponential
      parameters:
        mean: 5

  repairs:
    all_repairs: # this key name can be anything you want
      distribution: exponential
      parameters:
        mean: 0.5 # in days

# uncomment if using trackers
#tracker: # only required for tracking systems, remove it not using trackers
#  name: tracker
#  can_fail: true
#  can_repair: true
#  can_monitor: true
#
#  failures:
#    failure: # this key name can be anything you want
#      distribution: exponential
#      parameters:
#        mean: 500
#      labor_time: 0 # in hours
#      cost: 2000 # in USD
#
#  monitoring:
#    normal_monitoring: # this key name can be anything you want
#      distribution: exponential
#      parameters:
#        mean: 5
#
#  repairs:
#    all_repairs: # this key name can be anything you want
#      distribution: lognormal
#      parameters:
#        mean: 30 # in days
#        std: 10


### Independent Monitoring Practices ###
# This section defines monitoring practices (like IR drone scans) that happen at an invertal or threshold with a fix cost to reduce time to detection to 0 (i.e detect all failed components which are still in the detection phase from monitoring). or to the distribution sample from the defined distribution
# This occurs independent of any component level
# Here you can define the interval, cost, and what component levels this services will detect all failures on at each interval
# Remove this section if not using it
# You can also OPTIONALLY define a distribution that states how long it takes after the monitoring occurs for the failed components to be detected, instead of it being 0. You may omit the distribution to have time to detection go to 0 when monitoring occurs
indep_monitoring:
  drone_ir:  # this name can be anything you want!
    interval: 1095 # interval in days when this static monitoring occurs
    cost: 50000 # cost of this monitoring in USD
    labor_time: 1 # in hours
    distribution: normal
    parameters:
      mean: 14
      std: 5
    levels: # the component levels this detects on
      - module
      - string
      - combiner

  drone_ir2: # list as many static monitoring methods as you want
    interval: 365 # this monitoring will happen every 365 days, alongside the threshold.
    # a indep monitoring triggered by a threshold RESETs the countdown to the interval
    global_threshold: 0.1 # if DC availability drops by this threshold amount, then this indep monitoring will occur
    # DC availability is the DC power reaching the inverter(s), which is affected by combiners, strings, and module failures
    failure_per_threshold: 0.2 # this threshold is PER LEVEL, if the availability of ANY of the defined levels drops by this threshold amount, this indep monitoring will occur
    cost: 100000
    labor_time: 1 # in hours
    levels:
      - module
      - combiner
      - string

  drone_ir3: # list as many static monitoring methods as you want
    failure_per_threshold: 0.2 # this threshold is PER LEVEL, people if the availability of ANY of the defined levels drops by this threshold amount, this indep monitoring will occur
    cost: 1500
    labor_time: 1 # in hours
    levels:
      - module

### Cross level component monitoring ###
# This next section will define optional cross level component monitoring. This means that you can define monitoring at higher component
# levels for the component levels below it (i.e inverter monitoring modules). These come with extra parameters to define failure dependence,
# meaning how more failures contribute to quicker detection times from monitoring. This is done by user defined functions and parameters.
# This is only available for all levels except tracker, grid, and module. The monitoring defined here is also overrided by monitoring at the level defined above, meaning that if you define monitoring for modules above under the module section, monitoring of modules defined below will be ignored.
# Also, each level can only be monitored by one higher component level, meaning that if you define monitoring of modules at both the string and inverter level, PVRPM will only use the monitoring distribution and compounding of the monitoring at the string level for modules.

# component_level: The level in which monitors levels below it
#   component_monitoring: The level being monitored, must be a level below the component level defined above
#     YOU MUST PROVIDE EITHER GLOBAL_THRESHOLD OR FAILURE_PER_THRESHOLD, or you can provide both
#     global_threshold (float): fraction on [0, 1] that defines how many of the components must fail across ALL MONITORED COMPONENTS before monitoring can start detecting failures. Component failures will never be detected, and therefore not repaired, until this fraction of failed components is met.
#     failure_per_threshold (float): fraction on [0, 1] that defines how many components must fail under EACH COMPONENT BEING MONITORED AT THIS LEVEL. This means that if there are 8 combiners that monitor 64 strings, every combiner will monitor 8 strings, and the defined failure_per_threshold must fail under that combiner in order for those failures to start being detected, as such you can define a total number of failures across all the monitored components and also the number of failures per monitor level component.
#     As another example, if there 8 combiners monitoring 64 strings, and enough strings fail under combiner 1 to detect those failures, but not enough to meet the global_threshold total, if strings fail under combiner 2 they wont be detected until they either reach failure_per_threshold for that combiner or the global global_threshold
#     compounding_function (str): The function used to defined how more failures reduce the time to detection from monitoring (NOT IMPLEMENTED)
#     compound_parameters: parameters for the function above, see below for list of functions and their parameters (NOT IMPLEMENTED)
#     distribution = distribution type of monitoring times
#        parameters = parameters of the monitoring distribution
#          mean: mean in days for this monitoring mode's distribution
#          std: standard deviation for this monitoring mode's distribution (not all distributions need std)

# this section below is optional, remove if you aren't using it
component_level_monitoring:
  # lists what monitoring each component level has for levels BELOW it
  # this is for cross level monitoring only, for defining monitoring at each level use the monitoring distributions above
  string: # component level that has the monitoring for levels below
    module: # componenet level that is below's key. same keys used above: module, string, combiner, inverter, disconnect, grid, transformer
      global_threshold: 0.2 # fraction on [0, 1] that specifies how many of this component type must fail before detection can occur.
                             # this means that until this threshold is met, component failures can never be detected
                             # In the simulation, the time to detection doesn't count down until this threshold is met, which at that point the compouning function will be used along with the distribution as normal
      failure_per_threshold: 0.1 # the fraction of components that must fail per string for failures to be detected at that specified string, or if the total number of failures reach the global_threshold above
      distribution: normal # distribution that defines how long this monitoring takes to detect a failure at this level (independent)
                           # the value calculated from this distribution will be reduced by the compounding factor for every failure in this level
      parameters:
        mean: 1200
        std: 365

  combiner:
    string:
        failure_per_threshold: 0.2 # this fraction of strings must fail on a specific combiner for detection for those to start
        # failures are not compounded globally in this case, only per each combiner
        distribution: normal # distribution that defines how long this monitoring takes to detect a failure at this level (independent)
        parameters:
          mean: 1825
          std: 365

    module: # since module level monitoring is defined for strings, this will be ignored as the higher level takes precedant
      global_threshold: 0.4 # fraction on [0, 1] that specifies how many of this component type must fail before detection can occur.
      failure_per_threshold: 0.3
      distribution: normal # distribution that defines how long this monitoring takes to detect a failure at this level (independent)
      parameters:
        mean: 3650
        std: 365

  inverter:
    combiner:
      global_threshold: 0.1 # fraction on [0, 1] that specifies how many of this component type must fail before detection can occur.
      distribution: lognormal
      parameters:
        mean: 365
        std: 365

    string:
      global_threshold: 0.2 # fraction on [0, 1] that specifies how many of this component type must fail before detection can occur.
      distribution: exponential
      parameters:
        mean: 3650

    module:
      global_threshold: 0.5 # fraction on [0, 1] that specifies how many of this component type must fail before detection can occur.
      distribution: lognormal
      parameters:
        mean: 3650
        std: 365