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
Example #2
0
    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))