Caramel Corn CONSTANTS (v0.3.1)

This version is a major release with several new features which will break previous SimKit models. In particular, the following new features have been introduced starting with v0.3:

  • Parameter classes instead of dictionaries are used to differentiate user attributes from parameter declarations.

  • Use of Meta class options.

  • Calculator base class that can be overriden by user to modify how calculations are performed.

  • Separation of calculations into individual parameters that are all considered in the DAG.

  • Separation of formulas into individual parameters.

  • Introduction of simulation settings, which are instances of SimParameter, and the ability to have multiple settings per simulation.

Pint-0.8 Incompatibility

Unfortunately the newest version of Pint is not compatible with SimKit. This is a known issue, and you can track its resolution on GitHub. Until the issue is resolved please downgrade to Pint-0.7.2.

Parameters

Starting with SimKit-0.3, all layers now use a Parameter class to differentiate model parameters such as data, outputs, formulas, calculations, simulations and models, from user defined class attributes which should not be interpreted by SimKit. This affects some layers differently, specifically the Formula and Calc classes should now split formulas and calculations into different Parameter objects instead of grouping them together into one dictionary or list.

Migration Workarounds

The goal of these changes were to make SimKit classes easier to write and more flexible because they allow user defined attributes to be used arbitrarily. Here are some workarounds to help migrate v0.2.7 models to v0.3.

Outputs and Data Sources

The easiest way to migrate outputs and data sources is to wrap the existing dictionary for each data or output in either a DataParameter or OutputParameter object and use a double star operator, **, to splat the dictionary into keyword arguments:

from simkit.core.data_sources import DataSource, DataParameter


class PVPowerData(DataSource):
    """
    Data sources for PV Power demo.
    """
    # old v0.2.7
    # latitude = {"units": "degrees", "uncertainty": 1.0}

    # migration workaround just splat dictionary inside DataParameter

    # new v0.3
    latitude = DataParameter(
        **{"units": "degrees", "uncertainty": 1.0}
    )
    # splat dictionary is equivalent to using keyword arguments
    # latitude = DataParameter(units="degrees", uncertainty=1.0)

    # user attribute, not a parameter
    user_attr = 'user attributes are now OK in v0.3'

For more information on data sources see Tutorial 4: Data and the API section on Data Sources. And for more information on outputs see Tutorial 1: Outputs and the API section on Outputs.

Formulas

The only way to migrate formulas is to split them into individual parameters and put the module and package attributes into a nested Meta class:

from simkit.core.formulas import Formula, FormulaParameter


class UtilityFormulas(Formula):
    """
    Formulas for PV Power demo
    """
    # old v0.2.7
    # formulas = {
    #     "f_energy": {
    #         "args": ["ac_power", "times"],
    #         "units": [["watt_hour", None], ["W", None]]
    #     },
    #     "f_rollup": {
    #         "args": ["items", "times", "freq"],
    #         "units": ["=A", ["=A", None, None]]
    #     }
    # }
    # module = ".utils"
    # package = "formulas"

    # migration workaround split formulas into separate parameters
    # and put package and module attributes into nested Meta class

    # new v0.3
    f_energy = FormulaParameter(
        args=["ac_power", "times"],
        units=[["watt_hour", None], ["W", None]]
    )
    f_rollup = FormulaParameter(
        args=["items", "times", "freq"],
        units=["=A", ["=A", None, None]]
    )
    class Meta:
        module = ".utils"
        package = "formulas"

    # user attribute, not a parameter
    user_attr = 'user attributes are now OK in v0.3'

For more information on formulas see Tutorial 3: Formulas and the API section on Formulas.

Calculations

There is no easy workaround for migrating calculations to v0.3. Each calculation will need to be split up into separate parameters, each parameter now has an is_dynamic attribute, can require its own dependencies as list of other calculation parameters by name, and can also specify other options like always_calc or frequency. Also, since calculation parameters are now individually declared as Calculation class attributes, and not part of the old static and dynamic calculation lists, calculation parameters now need individual names.

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

class UtilityCalcs(Calc):
    """
    Calculations for PV Power demo
    """
    # old v0.2.7
    # dependencies = ["PerformanceCalcs"]
    # static = [
    #     {
    #         "formula": "f_energy",
    #         "args": {
    #             "outputs": {"ac_power": "Pac", "times": "timestamps"}
    #         },
    #         "returns": ["hourly_energy", "hourly_timeseries"]
    #     },
    #     {
    #         "formula": "f_rollup",
    #         "args": {
    #             "data": {"freq": "MONTHLY"},
    #             "outputs": {"items": "hourly_energy",
    #                         "times": "hourly_timeseries"}
    #         },
    #         "returns": ["monthly_energy"]
    #     },
    #     {
    #         "formula": "f_rollup",
    #         "args": {
    #             "data": {"freq": "YEARLY"},
    #             "outputs": {"items": "hourly_energy",
    #                         "times": "hourly_timeseries"}
    #         },
    #         "returns": ["annual_energy"]
    #     }
    # ]

    # no easy migration workaround split calculations into separate
    # parameters, replace static/dynamic lists with is_dynamic attribute
    # put default options in Meta class, override new Calculator class to
    # change how calculations are performed

    # new v0.3
    energy = CalcParameter(
        dependencies=["ac_power", "daterange"],
        formula="f_energy",
        args={"outputs": {"ac_power": "Pac", "times": "timestamps"}},
        returns=["hourly_energy", "hourly_timeseries"]
    )
    monthly_rollup = CalcParameter(
        dependencies=["energy"],
        formula="f_rollup",
        args={
            "data": {"freq": "MONTHLY"},
            "outputs": {"items": "hourly_energy",
                        "times": "hourly_timeseries"}
        },
        returns=["monthly_energy"]
    )
    yearly_rollup = CalcParameter(
        dependencies=["energy"],
        formula="f_rollup",
        args={"data": {"freq": "YEARLY"},
              "outputs": {"items": "hourly_energy",
                          "times": "hourly_timeseries"}},
        returns=["annual_energy"]
    )
    class Meta:
        is_dynamic = False
        calculator = Calculator

For more information on calculations see Tutorial 2: Calculations and the API section on Calculations.

Static and Dynamic

In v0.3, static and dynamic calculations are now determined by each parameter’s is_dynamic attribute, which defaults to False if not given. Therefore there is no static or dynamic list of serial calculations, and the calculation class does not have static and dynamic class attributes anymore.

Dependencies

Since calculation parameter names can be listed in the dependencies of other calculation parameters, when the order of calculations is determined in the simulation layer, each calculation parameter is now considered separately instead of as a group of serial steps, as in v0.2.7. This means that SimKit now has more granular control to determine which calculations can be performed in parallel.

A default set of dependencies for all parameters in the calculation can be listed as a Meta class option. If an individual parameter is missing the dependencies keyword, then the default is used from the Meta class.

Calculation Meta Class Options

Other calculation options like always_calc and frequency are also now listed in a Meta class. If not specified individually in the calculation parameter, then the value from the Meta class is used.

Calculator Class

Another significant change for calculations is that individual calculations can now specify a calculator class. A default calculator for all calculation parameters can also be specified in the Meta class. A calculator is a new base class that implements a calculate method but can be overriden to change how calculations are performed. If not given then the default calculator for all calculation parameters is the base Calculator class.

Simulations

Migrating simulations is easy. Just take all of the class properties and drop them into an instance of SimParameter, which can be named anything you want, but represents a set of settings you can use to simulate the model. Therefore, you could potentially have more than one set of settings by defining more than one SimParameter. By default the first SimParameter is used for settings if not specified in the model when declaring the model layers.

from simkit.core.simulations import Simulation, SimParameter


class PVPowerSim(Simulation):
    """
    PV Power Demo Simulations
    """
    # old v0.2.7
    # ID = "Tuscon_SAPM"
    # path = "~/SimKit_Simulations"
    # thresholds = None
    # interval = [1, "hour"]
    # sim_length = [0, "hours"]
    # write_frequency = 0
    # write_fields = {
    #     "data": ["latitude", "longitude", "Tamb", "Uwind"],
    #     "outputs": [
    #         "monthly_energy", "annual_energy"
    #     ]
    # }
    # display_frequency = 12
    # display_fields = {
    #     "data": ["latitude", "longitude", "Tamb", "Uwind"],
    #     "outputs": [
    #         "monthly_energy", "annual_energy"
    #     ]
    # }
    # commands = ['start', 'pause', 'run', 'load']

    # new v0.3
    settings = SimParameter(
        ID="Tuscon_SAPM",
        path="~/SimKit_Simulations",
        thresholds=None,
        interval=[1, "hour"],
        sim_length=[0, "hours"],
        write_frequency=0,
        write_fields={
            "data": ["latitude", "longitude", "Tamb", "Uwind"],
            "outputs": ["monthly_energy", "annual_energy"]
        },
        display_frequency=12,
        display_fields={
            "data": ["latitude", "longitude", "Tamb", "Uwind"],
            "outputs": ["monthly_energy", "annual_energy"]
        },
        commands=['start', 'pause']
    )

For more information on simulations see Tutorial 5: Models and Simulations and the API section on Simulations.

Models

Migrating models to v0.3 is also straightforward. Just take all of the layers and declare them by name as ModelParameters. The value of each layer is given as the source keyword argument of the ModelParameter. The modelpath should be in the model’s Meta class. Instead of using the default map of layers to Layer classes, if desired optionally provide the name of the Layer class as the layer keyword argument for each ModelParameter.

from pvpower import PROJ_PATH
from simkit.core.models import Model, ModelParameter

class NewSAPM(Model):
    """
    PV Power Demo model
    """
    # old v0.2.7
    # modelpath = PROJ_PATH  # folder containing project, not model
    # data = [PVPowerData]
    # outputs = [PVPowerOutputs, PerformanceOutputs, IrradianceOutputs]
    # formulas = [UtilityFormulas, PerformanceFormulas, IrradianceFormulas]
    # calculations = [UtilityCalcs, PerformanceCalcs, IrradianceCalcs]
    # simulations = [PVPowerSim]

    # new v0.3
    data = ModelParameter(
        layer='Data', sources=[(PVPowerData, {'filename': 'Tuscon.json'})]
    )
    outputs = ModelParameter(
        layer='Outputs',
        sources=[PVPowerOutputs, PerformanceOutputs, IrradianceOutputs]
    )
    formulas = ModelParameter(
        layer='Formulas',
        sources=[UtilityFormulas, PerformanceFormulas, IrradianceFormulas]
    )
    calculations = ModelParameter(
        layer='Calculations',
        sources=[UtilityCalcs, PerformanceCalcs, IrradianceCalcs]
    )
    simulations = ModelParameter(layer='Simulations', sources=[PVPowerSim])


    class Meta:
        modelpath = PROJ_PATH  # folder containing project, not model

For more information on models see Tutorial 5: Models and Simulations and the API section on Models.

Meta Class Options

Another major change is that any SimKit class options that aren’t parameters should now be put in a nested class called Meta. This should be familiar to Django, SQLAlchemy and Marshmallow users, and in fact much of SimKit is heavily inspired by those other projects.