Skip to content

Commit

Permalink
Refactor process execution during a simulation (#59)
Browse files Browse the repository at this point in the history
* add executor classes

* add runtime decorator

* use process executor from model

* simplify xarray driver

* remove run_step FutureWarning in test fixtures and docs

* fix ordering in runtime methods shown in process repr

* add tests for process executor and runtime decorator

* remove Model runtime methods in favor of Model.execute

* add RuntimeContext (needs fixes and tests update)

* update and fix RuntimeContext + add and fix some tests

* add docs and update docstrings

* rename step_duration to step_delta

* update release notes

* fix depreciated run_step step delta argument
  • Loading branch information
benbovy committed Sep 26, 2019
1 parent e45d587 commit c5458be
Show file tree
Hide file tree
Showing 15 changed files with 368 additions and 117 deletions.
13 changes: 9 additions & 4 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,7 @@ interfaces.
.. autosummary::
:toctree: _api_generated/

Model.initialize
Model.run_step
Model.finalize_step
Model.finalize
Model.execute

Process
=======
Expand All @@ -130,6 +127,14 @@ Process introspection and variables
variable_info
filter_variables

Process runtime methods
-----------------------

.. autosummary::
:toctree: _api_generated/

runtime

Variable
========

Expand Down
41 changes: 23 additions & 18 deletions doc/create_model.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Let's first wrap the code above into a single class named
explain in detail the content of this class.

.. literalinclude:: scripts/advection_model.py
:lines: 3-32
:lines: 3-33

Process interface
~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -94,9 +94,8 @@ methods that will be called during simulation runtime:
simulation. Here it is used to set the x-coordinate values of the
grid and the initial values of ``u`` along the grid (Gaussian
pulse).
- ``.run_step()`` will be called at each time step iteration and have
the current time step duration as required argument. This is where
the Lax method is implemented.
- ``.run_step()`` will be called at each time step iteration. This is
where the Lax method is implemented.
- ``.finalize_step()`` will be called at each time step iteration too
but after having called ``run_step`` for all other processes (if
any). Its intended use is mainly to ensure that state variables like
Expand All @@ -106,6 +105,12 @@ A fourth method ``.finalize()`` could also be implemented, but it is
not needed in this case. This method is called once at the end of the
simulation, e.g., for some clean-up.

Each of these methods can be decorated with :func:`~xsimlab.runtime`
to pass some useful information during simulation runtime (e.g.,
current time step number, current time or time step duration), which
may be need for the computation. Without this decorator, runtime
methods must have no other parameter than ``self``.

Getting / setting variable values
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -133,7 +138,7 @@ need to provide a dictionary with the process class(es) that we want
to include in the model, e.g., with only the process created above:

.. literalinclude:: scripts/advection_model.py
:lines: 35
:lines: 36

That's it! Now we have different tools already available to inspect
the model (see section :doc:`inspect_model`). We can also use that
Expand Down Expand Up @@ -168,15 +173,15 @@ This process declares all grid-related variables and computes
x-coordinate values.

.. literalinclude:: scripts/advection_model.py
:lines: 38-47
:lines: 39-48

Grid x-coordinate values only need to be set once at the beginning of
the simulation ; there is no need to implement ``.run_step()`` here.

**ProfileU**

.. literalinclude:: scripts/advection_model.py
:lines: 50-62
:lines: 51-63

``u_vars`` is declared as a :func:`~xsimlab.group` variable, i.e., an
iterable of all variables declared elsewhere that belong the same
Expand All @@ -191,7 +196,7 @@ value from elsewhere.
**AdvectionLax**

.. literalinclude:: scripts/advection_model.py
:lines: 65-83
:lines: 66-85

``u_advected`` represents the effect of advection on the evolution of
:math:`u` and therefore belongs to the group 'u_vars'.
Expand All @@ -207,7 +212,7 @@ handle them like if these were the original variables. For example,
**InitUGauss**

.. literalinclude:: scripts/advection_model.py
:lines: 86-96
:lines: 88-98

A foreign variable can also be used to set values for variables that
are declared in other processes, as for ``u`` here with
Expand All @@ -218,7 +223,7 @@ are declared in other processes, as for ``u`` here with
We now have all the building blocks to create a more flexible model:

.. literalinclude:: scripts/advection_model.py
:lines: 99-102
:lines: 101-104

The order in which processes are given doesn't matter (it is a
dictionary). A computationally consistent order, as well as model
Expand All @@ -243,7 +248,7 @@ original, simple version.
For this we create a new process:

.. literalinclude:: scripts/advection_model.py
:lines: 105-130
:lines: 107-133

Some comments about this class:

Expand All @@ -263,13 +268,13 @@ profile instead of a Gaussian pulse. We create another (minimal)
process for that:

.. literalinclude:: scripts/advection_model.py
:lines: 133-141
:lines: 136-144

Using one command, we can then update the model with these new
features:

.. literalinclude:: scripts/advection_model.py
:lines: 144-145
:lines: 147-148

Compared to ``model2``, this new ``model3`` have a new process named
'source' and a replaced process 'init'.
Expand All @@ -280,7 +285,7 @@ It is also possible to create new models by removing one or more
processes from existing Model instances, e.g.,

.. literalinclude:: scripts/advection_model.py
:lines: 148
:lines: 151

In this latter case, users will have to provide initial values of
:math:`u` along the grid directly as an input array.
Expand All @@ -304,28 +309,28 @@ achieve this is to create a small new process class that sets
the values of ``spacing`` and ``length``:

.. literalinclude:: scripts/advection_model.py
:lines: 151-158
:lines: 154-161

However, one drawback of this "additive" approach is that the number
of processes in a model might become unnecessarily high:

.. literalinclude:: scripts/advection_model.py
:lines: 161-161
:lines: 164

Alternatively, it is possible to write a process class that inherits
from ``UniformGrid1D``, in which we can re-declare variables *and/or*
re-define "runtime" methods:

.. literalinclude:: scripts/advection_model.py
:lines: 164-172
:lines: 167-175

We can here directly update the model and replace the original process
``UniformGrid1D`` by the inherited class ``FixedGrid``. Foreign
variables that refer to ``UniformGrid1D`` will still correctly point
to the ``grid`` process in the updated model:

.. literalinclude:: scripts/advection_model.py
:lines: 175-175
:lines: 178

.. warning::

Expand Down
1 change: 1 addition & 0 deletions doc/examples/landscape-evolution-model.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -2102,6 +2102,7 @@
" self._u_rate = np.zeros((ny, nx))\n",
" self._u_rate[mask] = u_rate[mask]\n",
"\n",
" @xs.runtime(args='step_delta')\n",
" def run_step(self, dt):\n",
" self.uplift = self._u_rate * dt\n"
]
Expand Down
5 changes: 4 additions & 1 deletion doc/scripts/advection_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def initialize(self):
self.x = np.arange(0, self.length, self.spacing)
self.u = np.exp(-1 / self.scale**2 * (self.x - self.loc)**2)

@xs.runtime(args='step_delta')
def run_step(self, dt):
factor = (self.v * dt) / (2 * self.spacing)
u_left = np.roll(self.u, 1)
Expand Down Expand Up @@ -55,7 +56,7 @@ class ProfileU:
u = xs.variable(dims='x', intent='inout', description='quantity u',
attrs={'units': 'm'})

def run_step(self, *args):
def run_step(self):
self._delta_u = sum((v for v in self.u_vars))

def finalize_step(self):
Expand All @@ -73,6 +74,7 @@ class AdvectionLax:
u = xs.foreign(ProfileU, 'u')
u_advected = xs.variable(dims='x', intent='out', group='u_vars')

@xs.runtime(args='step_delta')
def run_step(self, dt):
factor = self.v / (2 * self.grid_spacing)

Expand Down Expand Up @@ -126,6 +128,7 @@ def source_rate(self):
src_array[self.nearest_node] = self.flux
return src_array

@xs.runtime(args='step_delta')
def run_step(self, dt):
self.u_source = self.source_rate * dt

Expand Down
14 changes: 13 additions & 1 deletion doc/whats_new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,26 @@ Breaking changes
model (:issue:`45`). Although it should work just fine in most
cases, there are potential caveats. This should be considered as an
experimental, possibly breaking change.
- ``Model.initialize``, ``Model.run_step``, ``Model.finalize_step``
and ``Model.finalize`` have been removed in favor of
``Model.execute`` (:issue:`59`).

Deprecations
~~~~~~~~~~~~

- ``run_step`` methods defined in process classes won't accept anymore
current step duration as a positional argument by default. Use the
``runtime`` decorator if you need current step duration (and/or
other runtime information) inside the method (:issue:`59`).

Enhancements
~~~~~~~~~~~~

- Ensure that there is no ``intent`` conflict between the variables
declared in a model. This check is explicit at Model creation and a
more meaningful error message is shown when it fails (:issue:`57`).

- Added ``runtime`` decorator to pass simulation runtime information
to the (runtime) methods defined in process classes (:issue:`59`).

v0.2.1 (7 November 2018)
------------------------
Expand Down
3 changes: 2 additions & 1 deletion xsimlab/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"""
from .xr_accessor import SimlabAccessor, create_setup
from .variable import variable, on_demand, foreign, group
from .process import filter_variables, process, process_info, variable_info
from .process import (filter_variables, process, process_info, runtime,
variable_info)
from .model import Model

from ._version import get_versions
Expand Down

0 comments on commit c5458be

Please sign in to comment.