def test_on_time_updated(self):

        expr = mock.MagicMock(Expression)
        expr.expr.return_value = EXPR = object()

        pe = ProcessEvent(expr)

        CONDVAL = mock.MagicMock()
        pe.condition = mock.Mock(return_value=CONDVAL)
        CONDVAL.__invert__.return_value = NEG = object()

        PREVVAL = PhysicalField(2, 's')
        pe._prev_clock = PREV = mock.MagicMock(return_value=PREVVAL)
        clock = mock.MagicMock()
        DIFF_VAL = PhysicalField(5, 's')
        clock.__sub__ = mock.Mock(return_value=DIFF_VAL)

        EVENTVAL = PhysicalField(9, 's')
        pe.event_time = mock.MagicMock(return_value=EVENTVAL)
        pe.event_time.value = mock.MagicMock()
        pe.event_time.__iter__.return_value = [1, 2]

        pe.on_time_updated(clock)

        clock.copy().__sub__.assert_called_once()
        pe.condition.assert_called_once()
        pe.event_time.setValue.assert_called_once()

        CONDVAL.__invert__.assert_called_once()

        pe.event_time.value.__setitem__.assert_called_once_with(NEG, 0.0)
Beispiel #2
0
    def update_irradiance(self):
        """
        Update the set of data paths for irradiance intensities in :attr:`.irradiance_intensities`.
        """
        self.logger.debug('Updating irradiances')
        irradiances = set()

        irradiance = self.store[self.ENTRY_ENV].get(self.ENTRY_IRRADIANCE)
        if not irradiance:
            self.logger.debug('No irradiance info found')

        else:
            # load irradiance cycle info
            # irradiance might be hdf.Dataset or a dict
            linfo = dict(getattr(irradiance, 'attrs',
                                 irradiance.get('metadata')))
            self.diel_period = PhysicalField(linfo['hours_total'])
            self.diel_zenith = PhysicalField(linfo['zenith_time'])

            for chname in irradiance['channels']:
                data_path = self.PATH_IRRADIANCE_CHANNELS + '/'.join([
                    '',  # for a leading slash in the subpath
                    chname,
                    'intensity'
                    ])
                irradiances.add(data_path)
                self.logger.debug('Added irradiance intensity: {}'.format(data_path))

        self.irradiance_intensities = irradiances
        self.logger.debug('Updated irradiance intensities: {}'.format(self.irradiance_intensities))
Beispiel #3
0
    def finalize(self):
        """
        Call this to setup the equation. Once this is called, no more terms can be added.

        This does::

            self.obj = self.term_transient == sum(self.RHS_terms)

        Raises:
            RuntimeError: if no :attr:`.term_transient` defined or no RHS terms defined
        """
        if self.finalized:
            self.logger.warning('Equation already finalized')
            return

        if self.term_transient is None:
            raise RuntimeError('Cannot finalize equation without transient term!')

        RHS_terms = self.RHS_terms
        if not RHS_terms:
            raise RuntimeError('Cannot finalize equation without right-hand side terms')

        if self.source_exprs:
            self.sources_total = sum(self.source_exprs.values())
            #: the additive sum of all the sources
        else:
            self.sources_total = PhysicalField(np.zeros_like(self.var), self.var.unit.name() + '/s')

        self.obj = self.term_transient == sum(self.RHS_terms)
        self.update_tracked_budget(PhysicalField(0.0, 's'))
        self.finalized = True
        self.logger.info('Final equation: {}'.format(self.obj))
Beispiel #4
0
def unit_constructor(loader, node):

    value = loader.construct_scalar(node)
    if isinstance(value, str):
        ret = PhysicalField(str(value))
    elif isinstance(value, (tuple, list)):
        ret = PhysicalField(*value)
    else:
        raise ValueError(
            'Unknown input for PhysicalField: {} (type: {})'.format(
                value, type(value)))
    return ret
Beispiel #5
0
 def simtime_lims(self, vals):
     if vals is None:
         lmin = PhysicalField(0.1, 's')
         # lmax = (self.simtime_total / 25.0).inUnitsOf('s').floor()
         lmax = PhysicalField(120, 's')
     else:
         lmin, lmax = [PhysicalField(float(_), 's') for _ in vals]
     if not (0 < lmin < lmax):
         raise ValueError(
             'simtime_lims ({}, {}) are not positive and in order'.format(
                 lmin, lmax))
     self._simtime_lims = (lmin, lmax)
     self.logger.debug('simtime_lims set: {}'.format(self._simtime_lims))
Beispiel #6
0
    def resume_existing_simulation(self, data_outpath=None):
        if not self.resume:
            self.logger.info(
                'resume={}, so will not resume from existing file'.format(
                    self.resume))
            return

        data_outpath = data_outpath or self.data_outpath

        if not os.path.exists(data_outpath):
            self.logger.debug('Outpath does not exist, cannot resume...')
            return

        from fipy import PhysicalField
        import h5py as hdf

        # open the store and read out the time info
        with hdf.File(data_outpath, 'r') as store:
            tds = store['/time/data']
            nt = len(tds)
            target_time = tds[self.resume]
            latest_time = tds[-1]
            time_unit = tds.attrs['unit']

        target_time = PhysicalField(target_time, time_unit)
        latest_time = PhysicalField(latest_time, time_unit)

        click.secho(
            'Model resume set: rewind from latest {} ({}) to {} ({})?'.format(
                latest_time, nt, target_time, self.resume),
            fg='red')

        if self.confirm:
            click.confirm(
                'Rewinding model clock can lead to data loss! Continue?',
                default=False,
                abort=True)

        try:
            with hdf.File(data_outpath, 'a') as store:
                self.model.restore_from(store, time_idx=self.resume)
            click.secho('Model restore successful. Clock = {}'.format(
                self.model.clock),
                        fg='green')
            self.simulation.simtime_step = 1
            # set a small simtime to start
        except:
            click.secho(
                'Simulation could not be restored from given data file!',
                fg='red')
            raise  # click.Abort()
Beispiel #7
0
    def test_init_float(self):
        # interpreted as millimeters
        cell_size = 0.3
        sediment_length = 30
        dbl_length = 3

        domain = SedimentDBLDomain(cell_size=cell_size,
                                   sediment_length=sediment_length,
                                   dbl_length=dbl_length)

        assert domain.cell_size.unit.name() == 'mm'

        assert domain.cell_size == PhysicalField(cell_size, 'mm')
        assert domain.sediment_length == PhysicalField(sediment_length, 'mm')
        assert domain.DBL_length == PhysicalField(dbl_length, 'mm')
Beispiel #8
0
    def test_init_physical(self):
        cell_size = PhysicalField('20 mum')
        sediment_length = PhysicalField('1. cm')
        dbl_length = PhysicalField('1.0 mm')
        domain = SedimentDBLDomain(cell_size=cell_size,
                                   sediment_length=sediment_length,
                                   dbl_length=dbl_length)

        # internally converted to mm
        assert domain.cell_size.unit.name() == 'mm'

        assert domain.cell_size == cell_size
        assert domain.sediment_length == sediment_length
        assert domain.DBL_length == dbl_length
        assert 'sed_mask' in domain
    def test_simtime_lims(self):
        sim = Simulation()
        # check the default values
        sim.simtime_lims = None
        sMin, sMax = sim.simtime_lims
        assert sMin == PhysicalField(0.1, 's')
        assert sMax == PhysicalField(120, 's')

        with pytest.raises(ValueError):
            sim.simtime_lims = (0.1, 0.001)

        sim.simtime_lims = (2, 3)
        sMin, sMax = sim.simtime_lims
        assert sMin == PhysicalField(2, 's')
        assert sMax == PhysicalField(3, 's')
Beispiel #10
0
    def test_restore_from(self, model):

        varpath = 'domain.var1'
        VAL = [1.5]  # make a single value array in PhysicalField
        UNIT = 'mol/l'

        model.get_object.return_value = var = mock.MagicMock(CellVariable)
        var.__getitem__.return_value = var0 = mock.MagicMock(CellVariable)
        model.domain.depths = depths = mock.MagicMock(CellVariable)
        depths.__getitem__.return_value = depth0 = mock.Mock()
        unitMock = var0 * depth0
        unitMock.inBaseUnits.return_value.unit = UNIT

        with mock.patch('fipy.tools.numerix.trapz') as trapzMock:
            trapzMock.return_value = 0.0

            eqn = ModelEquation(model=model, varpath=varpath, coeff=5)

            trackedMock = mock.MagicMock(dict)
            trackedMock.__getitem__.return_value = (VAL, dict(unit=UNIT))
            PV = PhysicalField(VAL, UNIT)
            FIELDS = eqn.Tracked._fields

            eqn.restore_from(dict(tracked_budget=trackedMock), tidx=None)

            assert eqn.tracked == (PV, ) * len(FIELDS)
Beispiel #11
0
def restore_var(input, tidx):
    """
    This is the inverse operation of :func:`snapshot_var`. It takes the output of that function
    and returns a PhysicalField quantity


    Returns:
        :class:`PhysicalField`

    """
    if tidx is None:
        tidx = slice(None, None)

    if isinstance(input, hdf.Group):
        value = input['data']
        unitstr = value.attrs['unit']

    elif isinstance(input, (tuple, list)):
        value, mdict = input
        unitstr = mdict['unit']

    else:
        raise ValueError('Unknown type {} to restore data from'.format(
            type(input)))

    return PhysicalField(value[tidx], unit=unitstr)
    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
Beispiel #13
0
    def test_evolution(self):

        sim = Simulation()

        sim._started = True

        with pytest.raises(RuntimeError):
            for ret in sim.evolution():
                pass

        sim._started = False

        model = mock.MagicMock(MicroBenthosModel)
        clock = mock.MagicMock(ModelClock)
        clock.return_value = PhysicalField(0, 'h')
        model.clock = clock
        model.full_eqn = feqn = mock.Mock()
        feqn.sweep.return_value = RES = sim.max_residual / 2

        sim.model = model

        evolution = sim.evolution()
        step, state = next(evolution)

        model.update_vars.call_count == 2
        # once before while loop starts and once for the first yield
        clock.copy.assert_called_once()

        assert step == 1
        assert isinstance(state, dict)

        step, state = next(evolution)
        clock.increment_time.assert_called_once()
Beispiel #14
0
    def setup_model(self):
        self.logger.debug('Setting model data: {}'.format(self.model))

        if self.model.store is None:
            self.logger.debug('Cannot setup model, since store is empty')
            return

        if self.fig is None:
            self._create_figure(**self._fig_kwds)

        depth_unit = 'mm'
        self.depths = D = np.array(
            self.model.depths.inUnitsOf(depth_unit).value)
        self.axMicrobes.set_ylabel(f'Depth ({depth_unit})')

        for ax in self.axes_depth:
            ax.axhspan(min(D), 0, color='aquamarine', alpha=0.4, zorder=0)
            ax.axhspan(0, max(D), color='xkcd:brown', alpha=0.4, zorder=0)

        self._init_artist_styles()

        self.create_artists()

        self.update_legends()

        self.logger.propagate = False

        self._clock = PhysicalField(0, 's')

        self.update_artists(tidx=0)

        self.fig.tight_layout()  #rect=self._depth_rect, pad=1.02)
Beispiel #15
0
    def process(self, num, state):
        """
        Write the progress information to the progress bar

        This includes info about residual, duration (dt), and number of sweeps in the timestep
        and the global progress through the model clock.
        """

        time, tdict = state['time']['data']
        # curr = PhysicalField(time, tdict['unit']).inUnitsOf(simtime_total.unit)
        curr = PhysicalField(time, tdict['unit']).inUnitsOf(self._clock_unit)
        dt = curr - self._prev_t
        dt_unitless = round(float((curr - self._prev_t).value), 4)
        # print(curr, self._prev_t, dt, dt_unitless)

        # clock_info = '{0:.2f}/{1:.2f} {2}'.format(
        #     float(curr.value),
        #     float(simtime_total.value),
        #     simtime_total.unit.name(),
        # )

        residual = state['metrics']['residual']['data'][0]
        sweeps = state['metrics']['num_sweeps']['data'][0]

        self._pbar.set_postfix(
            # clock=clock_info,
            dt=self.srepr(dt.inUnitsOf('s'), prec=4),
            res='{:.2g} / {:.2g}'.format(residual, self.sim.max_residual),
            sweeps='{:02d}/{}'.format(sweeps, self.sim.max_sweeps,)
            )
        self._pbar.update(dt_unitless)
        self._prev_t = curr
Beispiel #16
0
    def read_data_from(self, path, tidx=None):
        """
        Get the data from the path in the snapshot

        Args:
            path (str): Input used for :meth:`.get_node`
            tidx (None): This is ignored, since a model snapshot only contains one timepoint

        Returns:
            a :class:`PhysicalField` of the data with units at the node

        """
        self.logger.debug('Getting data from {}'.format(path))
        node = self.get_node(path)

        try:
            data, meta = node['data']
        except KeyError:
            try:
                data, meta = node['data_static']
            except KeyError:
                self.logger.error(
                    'Could not find "data" or "data_static" in {}'.format(
                        path))
                raise ValueError(
                    'Path {} does not exist in snapshot'.format(path))

        unit = meta['unit']

        # if tidx is None:
        #   return PhysicalField(np.atleast_1d(data), unit)
        # else:
        #     return PhysicalField(np.atleast_1d(data[tidx]), unit)

        return PhysicalField(np.atleast_1d(data), unit)
Beispiel #17
0
    def __init__(self,
                 name,
                 k0=PhysicalField(0, '1/cm'),
                 k_mods=None,
                 **kwargs):
        """
        A scalar irradiance channel.

        This creates variables for the channel intensities, for the attenuation values.

        Args:
            name (str): The channel name

            k0 (float, PhysicalField): The base attenuation for this channel through the sediment
                with units of (1/cm)

            k_mods (None, list): ``(var, coeff)`` pairs that modify `k0` based on the value of the
                variable pointed at by `var` (example: `"microbes.cyano.biomass"`) and multiplied
                with a `coeff`. `coeff` must have the units such that `var * coeff` has the units
                of `k0`.

        Raises:
            ValueError: if the units of `k0` are not compatible with 1/cm

        """
        self.logger = kwargs.get('logger') or logging.getLogger(__name__)
        self.logger.debug('Init in {}'.format(self.__class__.__name__))
        kwargs['logger'] = self.logger
        super(IrradianceChannel, self).__init__(**kwargs)

        self.name = name

        #: CellVariable to hold the intensities of the irradiance channel through the domain
        self.intensities = None

        try:
            #: the base attenuation through the sediment
            self.k0 = PhysicalField(k0, '1/cm')
        except TypeError:
            raise ValueError('Invalid value for k0: {}'.format(k0))

        #: variable that represents the net attenuation
        self.k_var = None
        #: list of modulations for the attenuation in :attr:`.k0`
        self.k_mods = k_mods or []
        self._mods_added = {}
        self.logger.debug('Created irradiance channel {}'.format(self))
    def test_seed_linear_from_constraints(self, params, constraints, error):

        seed = dict(profile='linear')
        if params:
            seed['params'] = params

        unit = 'km/kg'
        N = 10

        v = ModelVariable(
            name='mvar',
            create=dict(value=3.2, unit=unit),
            seed=seed,
            constraints=constraints,
        )
        v.domain = mock.Mock(SedimentDBLDomain)
        v.domain.create_var.return_value = PhysicalField([34] * N, unit)
        v.domain.mesh = mock.Mock()
        v.domain.idx_surface = mock.Mock()
        v.constrain = mock.Mock()

        if error:
            with pytest.raises(error):
                v.setup()

        else:
            v.setup()

            if params is None:
                params = {}

            if constraints is None:
                constraints = {}

            startval = PhysicalField(
                params.get('start', constraints.get('top')),
                unit).inUnitsOf(unit).value
            stopval = PhysicalField(
                params.get('stop', constraints.get('bottom')),
                unit).inUnitsOf(unit).value

            expected = PhysicalField(numerix.linspace(startval, stopval, N),
                                     unit)

            assert numerix.array_equal(v.var, expected)
Beispiel #19
0
    def constrain(self, loc, value):
        """
        Constrain the variable at the given location to the given value

        Args:
            loc (str): One of ``("top", "bottom", "dbl", "sediment")``

            value (float, :class:`PhysicalField`): a numeric value for the constraint

        Returns:
            None

        Raises:
            TypeError: if the units for `value` are incompatible with :attr:`.var` units

            ValueError: for improper values for `loc`

            ValueError: if value is not a 0-dimension array

            RuntimeError: if variable doesn't exist

        """
        if self.var is None:
            raise RuntimeError('Variable {} does not exist!'.format(self.name))

        self.logger.debug("Setting constraint for {!r}: {} = {}".format(
            self.var, loc, value))

        loc, grad_type = self._parse_constraint_loc(loc)

        if loc in ('top', 'bottom'):
            mask = self._LOCs[loc]

        else:
            mask = numerix.zeros(self.var.shape, dtype=bool)

            try:
                L = self._LOCs[loc]
                self.logger.debug('Constraint mask loc: {}'.format(L))
                mask[L] = 1
            except KeyError:
                raise ValueError('loc={} not in {}'.format(
                    loc, tuple(self._LOCs.keys())))

        if isinstance(value, PhysicalField):
            value = value.inUnitsOf(self.var.unit)
        else:
            value = PhysicalField(value, self.var.unit)

        self.logger.info('Constraining {} (grad={}) at {} = {}'.format(
            self.var, grad_type, loc, value))
        if grad_type:
            var_entity = getattr(self.var, grad_type)
        else:
            var_entity = self.var

        var_entity.constrain(value, mask)
Beispiel #20
0
def test_dump_unit():
    inp = "40 mol/l"
    P = PhysicalField(inp)
    # PhysicalField with str input coerces value to float

    expected = "!unit '{} {}'".format(P.value, P.unit.name())
    s = yaml.dump(P).strip()

    assert s == expected
Beispiel #21
0
def test_load_unit():

    val = "35 mol/l"
    s = """
    km: !unit {}
    """.format(val)

    val_ = yaml.unsafe_load(s)['km']

    assert val_ == PhysicalField(val)
Beispiel #22
0
    def _check_with_unit_name(self, field, value):
        """
        Checks that the string can be used as units
        """
        self.logger.debug('Validating unit_name: {}'.format(value))

        try:
            PhysicalField(1, value)
        except TypeError:
            self._error(field, 'Must be str of physical units')
Beispiel #23
0
 def write_frame(self, state):
     """
     Save a frame into the output directory for the current state
     """
     time, tdict = state['time']['data']
     clock = int(PhysicalField(time, tdict['unit']).numericValue)
     fname = 'frame_{:010d}.png'.format(clock)
     path = os.path.join(self.frames_outdir, fname)
     self.plot.fig.savefig(path)
     self.logger.debug('Wrote frame: {}'.format(fname))
Beispiel #24
0
    def test_update_time(self):
        # update to clock time
        e = Entity()
        with pytest.raises(TypeError):
            e.on_time_updated()

        e.on_time_updated(2)
        e.on_time_updated(3.5)
        e.on_time_updated(PhysicalField('35 s'))
        # should this raise an error? No, because this is only reacts to the model clock
        e.on_time_updated(PF('5 kg'))
Beispiel #25
0
    def var_quantity(self):
        """
        Calculate the integral quantity of the variable in the domain

        Returns:
            PhysicalField: depth integrated amount
        """
        # self.logger.debug('Calculating actual var quantity')
        q = np.trapz(self.var.numericValue, self.model.domain.depths.numericValue)
        unit = (self.var[0] * self.model.domain.depths[0]).inBaseUnits().unit
        return PhysicalField(q, unit)
Beispiel #26
0
    def check_create(**params):
        """
        Check that the given `params` are valid for creating the variable once the domain is
        available

        Args:
            name (str): a required identifier string

            unit (str): a string like ("kg/m**3") that defines the physical units of the variable

            value (float, :class:`~numpy.ndarray`, :class:`~fipy.PhysicalField`): the value to set
                for the variable. If a :class:`PhysicalField` is supplied, then its (base) unit is
                used as `unit` and overrides any supplied `unit`.

        Returns:
            dict: the params dictionary to be used

        Raises:
            ValueError: if no `name` is supplied

            ValueError: if `unit` is not a valid input for :class:`PhysicalField`


        Note:
            Due to limitation in :mod:`fipy` (v3.1.3) that meshes do not accept arrays
            :class:`PhysicalField` as inputs, the variables defined here are cast into base units
            since the domain mesh is created in meters.

        """

        name = params.get('name')
        if name:
            raise ValueError(
                'Create params should not contain name. Will be set from init name.'
            )

        from fipy import PhysicalField
        value = params.get('value', 0.0)
        if hasattr(value, 'unit'):
            unit = value.unit
        else:
            unit = params.get('unit')

        try:
            p = PhysicalField(value, unit)
        except:
            raise ValueError('{!r} is not a valid unit!'.format(unit))

        pbase = p.inBaseUnits()
        params['unit'] = pbase.unit.name()
        params['value'] = pbase.value

        return params
    def test_create_var(self, value, unit, hasOld):
        # creation casts it into base units
        create = dict(value=value, unit=unit, hasOld=hasOld)
        name = 'myVar'
        v = ModelVariable(name=name, create=create)

        domain = SedimentDBLDomain()
        v.set_domain(domain)
        v.setup()
        assert v.var is domain[name]
        assert v.var.name == v.name
        assert v.var.shape == domain.mesh.shape
        assert (v.var() == PhysicalField(value, unit).inBaseUnits()).all()
Beispiel #28
0
    def test_simtime_total(self):
        sim = Simulation()

        with pytest.raises(ValueError):
            sim.simtime_total = 0
        with pytest.raises(ValueError):
            sim.simtime_total = -3

        V = 3
        sim.simtime_total = V
        assert sim.simtime_total == PhysicalField(V, 'h')

        with pytest.raises(ValueError):
            sim.simtime_total = sim.simtime_step / 2.0
Beispiel #29
0
    def test_simtime_days(self):
        sim = Simulation(simtime_days=3)
        sTot = sim.simtime_total

        model = mock.MagicMock(MicroBenthosModel)
        clock = mock.MagicMock(ModelClock)
        model.clock = clock
        model.full_eqn = mock.Mock()
        model.get_object.return_value = I = mock.Mock()
        I.hours_total = 5
        sim.model = model

        model.get_object.assert_called_once_with('env.irradiance')
        assert sim.simtime_total == PhysicalField(5 * 3, 'h')
Beispiel #30
0
    def read_data_from(self, path, tidx=None):
        """
        Read out the data in `path` (a :class:`h5py:Dataset`) into a :class:`PhysicalField`

        If `tidx` is `None`, then no slicing of the dataset is done

        """
        path = path.replace('.', '/')

        if not path.endswith('/data'):
            path += '/data'
        ds = self.store[path]
        try:
            ds.id.refresh()
        except AttributeError:
            pass

        self.logger.debug('Found {}: {}'.format(path, ds))
        ds_unit = str(ds.attrs['unit'])
        if tidx is None:
            return PhysicalField(ds, ds_unit)
        else:
            return PhysicalField(ds[tidx], ds_unit)