Tutorial 2: Calculations

In the first tutorial we decided what outputs we wanted from our PV system power model. In the second tutorial we’ll create the calculations that yield those desired outputs. In SimKit, a calculation is a combination of formulas, data, and outputs that calculates outputs from formulas evaluated using data and outputs as arguments. Each step in the calculation maps formula input arguments to data and outputs and the return values to outputs. We already explained in Tutorial 1: Outputs how SimKit defines outputs, and next in Tutorial 3: Formulas and Tutorial 4: Data we’ll define what the terms data and formulas mean.

Calculation Class

Let’s keep using the performance.py module we created in Tutorial 1: Outputs. We’ll need to import the Calc class to create a new subclass for the calculations in our PV system power example. To calculate the hourly energy and corresponding timestamps we’ll need to integrate AC power over time and shift the timestamps to the end of each hour. Therefore in this example, the hourly energy at the given timestamp corresponds to the energy accumulated over the previous hour instead of the average power at that timestamp. We can also roll up the hourly energy to output monthly and annual values. We need to import the CalcParameter class to define these calculation steps. Assuming the AC power and corresponding timestamps are already outputs, and assuming that functions for energy integration and roll-ups are already formulas we can specify this calculation as follows:

from simkit.core.calculations import Calc, CalcParameter, Calculator


class UtilityCalcs(Calc):
    """
    Calculations for PV Power demo
    """
    energy = CalcParameter(
        is_dynamic=False,
        calculator=Calculator,
        dependencies=["ac_power", "daterange"],
        formula="f_energy",
        args={"outputs": {"ac_power": "Pac", "times": "timestamps"}},
        returns=["hourly_energy", "hourly_timeseries"]
    )
    monthly_rollup = CalcParameter(
        is_dynamic=False,
        calculator=Calculator,
        dependencies=["energy"],
        formula="f_rollup",
        args={
            "data": {"freq": "MONTHLY"},
            "outputs": {"items": "hourly_energy",
                        "times": "hourly_timeseries"}
        },
        returns=["monthly_energy"]
    )
    yearly_rollup = CalcParameter(
        is_dynamic=False,
        calculator=Calculator,
        dependencies=["energy"],
        formula="f_rollup",
        args={"data": {"freq": "YEARLY"},
              "outputs": {"items": "hourly_energy",
                          "times": "hourly_timeseries"}},
        returns=["annual_energy"]
    )

Calculation Attributes

In the snippet above, the calculation is called UtilityCalcs, and it defines three calculation parameters: energy, monthly_rollup, and yearly_rollup. Each calculation parameter has keyword arguments like dependencies, always_calc, and frequency, that describe attributes about the calculation parameter. All calculations parameters and their attributes are stored in the calculation registry which the simulation uses to run the model.

Formulas, Arguments, and Returns

Calculations are sets of calculation parameters that describe the steps required to calculate the desired outputs. Each step is a CalcParameter that must at least contain formula, args and returns. The value of each is a reference to the value in the corresponding registry. For example, in the UtilityCalc snippet above, the first calculation parameter, energy, uses the formula f_energy which is the key for the corresponding function in the FormulaRegistry. The arguments to the formula are given by args which is a dictionary that maps the formula inputs with either data form the DataRegistry or outputs from the OutputRegistry. The first key in args tells you which registry and the following dictionary maps the formula inputs to the registry keys. For example, f_energy takes two inputs: ac_power and times. To calculate energy we use the outputs: Pac and timestamps. Formulas can be used with different arguments to return different outputs by referring to different values in the data and output registries respectively. For example, notice how f_rollup is used twice, once with the freq argument set to the value of the data MONTHLY and return value set to the output monthly_energy and then again with data YEARLY and output annual_energy.

The following table lists the attributes that calculations can have. If given as positional arguments, then the order is the same as the table below; keyword arguments can be in any order.

Attribute

Description

Default

dependencies

list of required calculations

required

always_calc

calculations ignore simulation thresholds

False

frequency

dynamic calculations different from timestep

1-interval

formula

name of a function

required

args

dictionary of data and outputs

required

returns

name of outputs

required

calculator

calculator class used to calculate this

Calculator

is_dynamic

true if this is a periodic calculation

False

Static and Dynamic Calculations

The is_dynamic attribute indicates whether the calculation parameter has a time dependency or whether it is calculated once at the beginning of a simulation. The simulation first calculates parameters with is_dynamic==False then loops over calculations with is_dynamic==True for each timestep. The default value of is_dynamic is False.

Dynamic Calculations

Dynamic calculations depend on a previous timestep. To refer to arguments from previous timesteps use an index or to refer to a prior time use a quantity. In the example below, encapsulant browning depends on the previous timestep and the temperatures from the previous day.

encapsulant_browning = CalcParameter(
    formula="f_encapsulant_browning",
    args={
        "data": {"encapsulant": "encapsulant"},
        "outputs": {
            "prev_encapsulant_browning": ["encapsulant_browning", -1],
            "prev_day_cell_temp": ["Tcell", -1, "day"]
        }
    },
    returns=["encapsulant_browning"]
)

Calculators

The calculator attribute sets the Calculator class used to evaluate the calculation. The default is Calculator but can be overriden to change how the calculation is performed. A Calculator should implement a calculate() method that takes the following arguments:

  1. dictionary of parameter formula, args and return keys

  2. formula registry

  3. data registry

  4. outputs registry

For dynamic calculations, the calculate method should take these additional arguments:

  1. timestep, defaults to None for static calculations

  2. index, defaults to None for static calculations

New in version 0.3.1.

Meta Class Options

Calculation attributes can be specified for all parameters in a calculation by declaring them in a nested Meta class. If individual parameters also declare the same attributes, then the parameter value will override the Meta class value. For example, in the UtilityCalcs example, the attributes is_dynamic and calculator are declared for all three parameters. Instead these attributes could just be declared for all parameters in UtilityCalcs by putting them the Meta class.

class UtilityCalcs(Calc):
    """
    Calculations for PV Power demo
    """
    # same calculation parameters as above without is_dynamic and calculator

    # default attributes for all parameters
    class Meta:
        is_dynamic = False
        calculator = Calculator

Parameter File

Calculations can also be specified in a parameter file. For example copy the following into PVPower/calculations/utils.json:

{
  "energy": {
    "is_dynamic": false,
    "dependencies": ["ac_power", "daterange"],
    "formula": "f_energy",
    "args": {
      "outputs": {"ac_power": "Pac", "times": "timestamps"}
    },
    "returns": ["hourly_energy", "hourly_timeseries"]
  },
  "monthly_rollup": {
    "is_dynamic": false,
    "dependencies": ["energy"],
    "formula": "f_rollup",
    "args": {
      "data": {"freq": "MONTHLY"},
      "outputs": {"items": "hourly_energy", "times": "hourly_timeseries"}
    },
    "returns": ["monthly_energy"]
  },
  "yearly_rollup": {
    "is_dynamic": false,
    "dependencies": ["energy"],
    "formula": "f_rollup",
    "args": {
      "data": {"freq": "YEARLY"},
      "outputs": {"items": "hourly_energy", "times": "hourly_timeseries"}
    },
    "returns": ["annual_energy"]
  }
}

Just like the Output class, we tell SimKit about our calculations by specifying the parameter file in a Calc class. Create a new Python module in the pvpower package called performance.py, like we did above and add a Calc class for each calculation.

from simkit.core.calculations import Calc
import os
from pvpower import PROJ_PATH


class UtilityCalcs(Calc):
    outputs_file = 'utils.json'
    outputs_path = os.path.join(PROJ_PATH, 'calculations')