def test_constrain_with_unit(self, varunit, conunit): create = dict(value=3., unit=varunit, hasOld=True) name = 'MyVar' conval = PhysicalField(5, conunit) constraints = dict(top=conval, ) v = ModelVariable(name=name, create=create, constraints=constraints) domain = SedimentDBLDomain() v.set_domain(domain) try: varval = conval.inUnitsOf(varunit) except TypeError: # incompatible units varval = None if varval is None: with pytest.raises(TypeError): v.setup() else: v.setup() v.var.updateOld() assert v.var.faceValue[0] == conval
def seed(self, profile, **kwargs): """ Seed the value of the variable based on the profile and parameters Available profiles are: * "normal" * normal distribution of values from :data:`scipy.stats.norm` * `loc` and `scale` in units compatible with domain mesh * `coeff` to multiply the distribution with, in units compatible with that of :attr:`.var` * the normal distribution is created to have unit height for different `loc` and `scale` values * "linear" * uses :func:`~numpy.linspace` to fill the first dimension of :attr:`.var` * `start`: the start value given, else taken from constraint "top" * `stop`: the stop value given, else taken from constraint "bottom" * "lognormal" * lognormal distributuion from :data:`scipy.stats.lognorm` * `loc` and `scale` should be in units compatible with domain mesh * `shape` should be a float > 0 * the distribution is normalized to have max value = 1 Args: profile (str): The type of profile to use **kwargs: Parmeters for the profile Returns: None Raises: ValueError: if incompatible units encountered """ PROFILES = ('linear', 'normal', 'lognormal') if profile not in PROFILES: raise ValueError('Unknown profile {!r} not in {}'.format( profile, PROFILES)) if profile == 'normal': from scipy.stats import norm loc = kwargs['loc'] scale = kwargs['scale'] coeff = kwargs['coeff'] C = 1.0 / numerix.sqrt(2 * numerix.pi) # loc and scale should be in units of the domain mesh if hasattr(loc, 'unit'): loc_ = loc.inUnitsOf(self.domain.depths.unit).value else: loc_ = loc if hasattr(scale, 'unit'): scale_ = scale.inUnitsOf(self.domain.depths.unit).value else: scale_ = scale if hasattr(coeff, 'unit'): # check if compatible with variable unit try: c = coeff.inUnitsOf(self.var.unit) except TypeError: self.logger.error( 'Coeff {!r} not compatible with variable unit {!r}'. format(coeff, self.var.unit.name())) raise ValueError('Incompatible unit of coefficient') self.logger.info( 'Seeding with profile normal loc: {} scale: {} coeff: {}'. format(loc_, scale_, coeff)) normrv = norm(loc=loc_, scale=scale_) rvpdf = normrv.pdf(self.domain.depths) rvpdf /= rvpdf.max() val = coeff * rvpdf self.var.value = val elif profile == 'lognormal': from scipy.stats import lognorm loc = kwargs['loc'] scale = kwargs['scale'] coeff = kwargs['coeff'] lognorm_shape = kwargs.get('shape', 1.25) # loc and scale should be in units of the domain mesh if hasattr(loc, 'unit'): loc_ = loc.inUnitsOf(self.domain.depths.unit).value else: loc_ = loc if hasattr(scale, 'unit'): scale_ = scale.inUnitsOf(self.domain.depths.unit).value else: scale_ = scale if hasattr(coeff, 'unit'): # check if compatible with variable unit try: c = coeff.inUnitsOf(self.var.unit) except TypeError: self.logger.error( 'Coeff {!r} not compatible with variable unit {!r}'. format(coeff, self.var.unit.name())) raise ValueError('Incompatible unit of coefficient') self.logger.info( 'Seeding with profile lognormal loc: {} scale: {} shape: {} ' 'coeff: {}'.format(loc_, scale_, lognorm_shape, coeff)) rv = lognorm(lognorm_shape, loc=loc_, scale=scale_) rvpdf = rv.pdf(self.domain.depths) rvpdf = rvpdf / rvpdf.max() val = coeff * rvpdf self.var.value = val elif profile == 'linear': start = kwargs.get('start') stop = kwargs.get('stop') if start is None: start = self.constraints.get('top') if start is None: raise ValueError( 'Seed linear has no "start" or "top" constraint') else: start = PhysicalField(start, self.var.unit) self.logger.info('Linear seed using start as top value: ' '{}'.format(start)) if stop is None: stop = self.constraints.get('bottom') if stop is None: raise ValueError( 'Seed linear has no "stop" or "bottom" constraint') else: stop = PhysicalField(stop, self.var.unit) self.logger.info('Linear seed using stop as bottom value: ' '{}'.format(stop)) N = self.var.shape[0] if hasattr(start, 'unit'): start_ = start.inUnitsOf(self.var.unit).value else: start_ = start if hasattr(stop, 'unit'): stop_ = stop.inUnitsOf(self.var.unit).value else: stop_ = stop self.logger.info( 'Seeding with profile linear: start: {} stop: {}'.format( start_, stop_)) val = numerix.linspace(start_, stop_, N) self.var.value = val self.logger.debug('Seeded {!r} with {} profile'.format(self, profile))