Source code for simkit.core.calculations

# -*- coding: utf-8 -*-

"""
This module provides base classes for calculations. All calculations should
inherit from one of the calcs in this module.
"""

from simkit.core import logging, CommonBase, Registry, UREG, Parameter
from simkit.core.calculators import Calculator

LOGGER = logging.getLogger(__name__)


[docs]class CalcParameter(Parameter): """ Fields for calculations. """ _attrs = ['dependencies', 'always_calc', 'frequency', 'formula', 'args', 'returns', 'calculator', 'is_dynamic']
[docs]class CalcRegistry(Registry): """ A registry for calculations. Each key is a calculation. The value of each calculation is split into 2 dictionaries: "static" and "dynamic". Static calculations occur once at the beginning of a simulation and dynamic calculations occur at every interval. The contents of either the "static" or "dynamic" key is an ordered list of formulas, their arguments and return values. Calculations can list `dependencies <http://xkcd.com/754/>`_ that must be calculated first. Calculations marked as `always_calc` will not be limited by thresholds set in simulations. The frequency determines how often to dynamic calculations occur. Frequency can be given in intervals or can list a quantity of time, _EG:_ ``2 * UREG.hours``. """ #: meta names meta_names = ['dependencies', 'always_calc', 'frequency', 'calculator', 'is_dynamic', 'calc_source'] def register(self, new_calc, *args, **kwargs): """ Register calculations and meta data. * ``dependencies`` - list of prerequisite calculations * ``always_calc`` - ``True`` if calculation ignores thresholds * ``frequency`` - frequency of calculation in intervals or units of time :param new_calc: register new calculation """ kwargs.update(zip(self.meta_names, args)) # dependencies should be a list of other calculations if isinstance(kwargs['dependencies'], basestring): kwargs['dependencies'] = [kwargs['dependencies']] # call super method, now meta can be passed as args or kwargs. super(CalcRegistry, self).register(new_calc, **kwargs)
[docs]class CalcBase(CommonBase): """ Base calculation meta class. """ _path_attr = 'calcs_path' _file_attr = 'calcs_file' _param_cls = CalcParameter def __new__(mcs, name, bases, attr): # use only with Calc subclasses if not CommonBase.get_parents(bases, CalcBase): return super(CalcBase, mcs).__new__(mcs, name, bases, attr) # set _meta combined from bases attr = mcs.set_meta(bases, attr) # set param file full path if calculations path and file specified or # try to set parameters from class attributes except private/magic attr = mcs.set_param_file_or_parameters(attr) return super(CalcBase, mcs).__new__(mcs, name, bases, attr)
[docs]class Calc(object): """ A class for all calculations. """ __metaclass__ = CalcBase def __init__(self): meta = getattr(self, CalcBase._meta_attr) parameters = getattr(self, CalcBase._param_attr) #: ``True`` if always calculated (day and night) self.always_calc = dict.fromkeys( parameters, getattr(meta, 'always_calc', False) ) freq = getattr(meta, 'frequency', [1, '']) #: frequency calculation is calculated in intervals or units of time self.frequency = dict.fromkeys(parameters, freq[0] * UREG(str(freq[1]))) #: dependencies self.dependencies = dict.fromkeys( parameters, getattr(meta, 'dependencies', []) ) #: name of :class:`Calc` superclass self.calc_source = dict.fromkeys(parameters, self.__class__.__name__) #: calculator self.calculator = dict.fromkeys( parameters, getattr(meta, 'calculator', Calculator) ) #: ``True`` if calculations are dynamic, ``False`` if static self.is_dynamic = dict.fromkeys( parameters, getattr(meta, 'is_dynamic', False) ) #: calculations self.calcs = {} for k, v in parameters.iteritems(): self.calcs[k] = { key: v[key] for key in ('formula', 'args', 'returns') } keys = ('dependencies', 'always_calc', 'frequency', 'calculator', 'is_dynamic') for key in keys: value = v.get(key) if value is not None: getattr(self, key)[k] = value