def __init__(self): #: outputs initial value self.initial_value = {} #: size of outputs self.size = {} #: outputs uncertainty self.uncertainty = {} #: variance self.variance = {} #: jacobian self.jacobian = {} #: outputs isconstant flag self.isconstant = {} #: outputs isproperty flag self.isproperty = {} #: name of corresponding time series, ``None`` if no time series self.timeseries = {} #: name of :class:`Output` superclass self.output_source = {} #: calculation outputs self.outputs = {} for k, v in self.parameters.items(): self.initial_value[k] = v.get('init') # returns None if missing self.size[k] = v.get('size') or 1 # minimum size is 1 self.uncertainty[k] = None # uncertainty for outputs is calculated self.isconstant[k] = v.get('isconstant', False) # True or False self.isproperty[k] = v.get('isproperty', False) # True or False units = str(v.get('units', '')) # default is non-dimensional # NOTE: np.empty is faster than zeros! self.outputs[k] = Q_(np.zeros((1, self.size[k])), UREG(units)) # NOTE: Initial values are assigned and outputs resized when # simulation "start" method is called from the model. self.timeseries[k] = v.get('timeseries') # None if not time series self.output_source[k] = self.__class__.__name__ # output source
def apply_units_to_cache(self, data): """ Apply units to data read using :class:`JSONReader`. :param data: cached data :return: data with units applied :rtype: :class:`~pint.unit.Quantity` """ for k, val in self.parameters.iteritems(): if 'units' in val: data[k] = Q_(data[k], val.get('units')) return data
def test_pv_context(): """ Test Pint PV context - specifically suns to power flux and v.v. """ esun = Q_(876.5, UREG.W / UREG.m / UREG.m) eq_(esun.to('suns', 'pv'), 0.8765 * UREG.suns) esun = Q_(0.8765, UREG.suns) ok_(esun.dimensionless) eq_(esun.to('W / m ** 2', 'pv'), 876.5 * UREG.W / UREG.m / UREG.m)
def apply_units_to_cache(self, data): """ Applies units to data when a proxy reader is used. For example if the data is cached as JSON and retrieved using the :class:`~simkit.core.data_readers.JSONReader`, then units can be applied from the original parameter schema. :param data: Data read by proxy reader. :return: data with units applied """ # if units key exists then apply for k, v in self.parameters.iteritems(): if v and v.get('units'): data[k] = Q_(data[k], v.get('units')) return data
def calculate(self, calc, formula_reg, data_reg, out_reg, timestep=None, idx=None): """ Calculate looping over specified repeat arguments. :param calc: Calculation to loop over. :param formula_reg: Formula registry :param data_reg: Data registry :param out_reg: Outputs registry :param timestep: timestep used for dynamic calcs :param idx: index used in dynamic calcs """ # the superclass Calculator.calculate() method base_calculator = super(LazyLoopingCalculator, self).calculate # call base calculator and return if there are no repeat args if not self.repeat_args: base_calculator(calc, formula_reg, data_reg, out_reg, timestep, idx) return # make dictionaries of the calculation data and outputs argument maps # this maps what the formulas and registries call the repeats arguments data_rargs, out_rargs = {}, {} # allocate dictionaries for repeat args calc_data = calc['args'].get('data') calc_outs = calc['args'].get('outputs') # get dictionaries of repeat args from calculation data and outputs for rarg in self.repeat_args: # rarg could be either data or output so try both try: data_rargs[rarg] = calc_data[rarg] except (KeyError, TypeError): out_rargs[rarg] = calc_outs[rarg] # get values of repeat data and outputs from registries rargs = dict(index_registry(data_rargs, data_reg, timestep, idx), **index_registry(out_rargs, out_reg, timestep, idx)) rargkeys, rargvals = zip(*rargs.iteritems()) # split keys and values rargvals = zip(*rargvals) # reshuffle values, should be same size? # allocate dictionary of empty numpy arrays for each return value returns = calc['returns'] # return keys retvals = {rv: [] for rv in returns} # empty dictionary of return vals retvalu = {rv: None for rv in returns} # dictionary of return units ret_var = {rv: {rv: [] for rv in returns} for rv in returns} # variances ret_unc = {rv: {rv: [] for rv in returns} for rv in returns} # uncertainty ret_jac = dict.fromkeys(returns) # jacobian # get calc data and outputs keys to copy from registries try: calc_data_keys = calc_data.values() except (AttributeError, TypeError): calc_data_keys = [] # if there are no data, leave it empty try: calc_outs_keys = calc_outs.values() except (AttributeError, TypeError): calc_outs_keys = [] # if there are no outputs, leave it empty # copy returns and this calculations output arguments from output reg data_reg_copy = reg_copy(data_reg, calc_data_keys) out_reg_copy = reg_copy(out_reg, returns + calc_outs_keys) # loop over first repeat arg values and enumerate numpy indices as n for vals in rargvals: rargs_keys = dict(zip(rargkeys, vals)) # this is the magic or garbage depending on how you look at it, # change the registry copies to only contain the values for this # iteration of the repeats # TODO: instead of using copies rewrite index_registry to do this # copies means that calculations can't use a registry backend that # uses shared memory, which will limit ability to run asynchronously for k, v in data_rargs.iteritems(): data_reg_copy[v] = rargs_keys[k] for k, v in out_rargs.iteritems(): out_reg_copy[v] = rargs_keys[k] # run base calculator to get retvals, var, unc and jac base_calculator(calc, formula_reg, data_reg_copy, out_reg_copy, timestep, idx) # re-assign retvals for this index of repeats for rv, rval in retvals.iteritems(): rval.append(out_reg_copy[rv].m) # append magnitude to returns retvalu[rv] = out_reg_copy[rv].u # save units for this repeat # re-assign variance for this index of repeats if out_reg_copy.variance.get(rv) is None: continue for rv2, rval2 in ret_var.iteritems(): rval2[rv].append(out_reg_copy.variance[rv2][rv]) # uncertainty only on diagonal of variance if rv == rv2: ret_unc[rv][rv2].append(out_reg_copy.uncertainty[rv][rv2]) else: # FIXME: inefficient to get length every iteration! unc_size = len(out_reg_copy.uncertainty[rv][rv]) ret_unc[rv][rv2].append(Q_([0.]*unc_size, 'percent')) # jacobian is dictionary of returns versus arguments if ret_jac[rv] is None: # first time through create dictionary of sensitivities ret_jac[rv] = {o: v for o, v in out_reg_copy.jacobian[rv].iteritems()} else: # next time through, vstack the sensitivities to existing for o, v in out_reg_copy.jacobian[rv].iteritems(): ret_jac[rv][o] = np.vstack((ret_jac[rv][o], v)) LOGGER.debug('ret_jac:\n%r', ret_jac) # TODO: handle jacobian for repeat args and for dynamic simulations # apply units if they were for k in retvals: if retvalu[k] is not None: if retvalu[k] == out_reg[k].u: retvals[k] = Q_(retvals[k], retvalu[k]) else: retvals[k] = Q_(retvals[k], retvalu[k]).to(out_reg[k].u) # put return values into output registry if idx is None: out_reg.update(retvals) out_reg.variance.update(ret_var) out_reg.uncertainty.update(ret_unc) out_reg.jacobian.update(ret_jac) else: for k, v in retvals: out_reg[k][idx] = v