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.