Skip to content

Commit c5458be

Browse files
authored
Refactor process execution during a simulation (#59)
* 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
1 parent e45d587 commit c5458be

15 files changed

+368
-117
lines changed

doc/api.rst

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,7 @@ interfaces.
104104
.. autosummary::
105105
:toctree: _api_generated/
106106

107-
Model.initialize
108-
Model.run_step
109-
Model.finalize_step
110-
Model.finalize
107+
Model.execute
111108

112109
Process
113110
=======
@@ -130,6 +127,14 @@ Process introspection and variables
130127
variable_info
131128
filter_variables
132129

130+
Process runtime methods
131+
-----------------------
132+
133+
.. autosummary::
134+
:toctree: _api_generated/
135+
136+
runtime
137+
133138
Variable
134139
========
135140

doc/create_model.rst

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Let's first wrap the code above into a single class named
4747
explain in detail the content of this class.
4848

4949
.. literalinclude:: scripts/advection_model.py
50-
:lines: 3-32
50+
:lines: 3-33
5151

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

108+
Each of these methods can be decorated with :func:`~xsimlab.runtime`
109+
to pass some useful information during simulation runtime (e.g.,
110+
current time step number, current time or time step duration), which
111+
may be need for the computation. Without this decorator, runtime
112+
methods must have no other parameter than ``self``.
113+
109114
Getting / setting variable values
110115
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
111116

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

135140
.. literalinclude:: scripts/advection_model.py
136-
:lines: 35
141+
:lines: 36
137142

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

170175
.. literalinclude:: scripts/advection_model.py
171-
:lines: 38-47
176+
:lines: 39-48
172177

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

176181
**ProfileU**
177182

178183
.. literalinclude:: scripts/advection_model.py
179-
:lines: 50-62
184+
:lines: 51-63
180185

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

193198
.. literalinclude:: scripts/advection_model.py
194-
:lines: 65-83
199+
:lines: 66-85
195200

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

209214
.. literalinclude:: scripts/advection_model.py
210-
:lines: 86-96
215+
:lines: 88-98
211216

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

220225
.. literalinclude:: scripts/advection_model.py
221-
:lines: 99-102
226+
:lines: 101-104
222227

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

245250
.. literalinclude:: scripts/advection_model.py
246-
:lines: 105-130
251+
:lines: 107-133
247252

248253
Some comments about this class:
249254

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

265270
.. literalinclude:: scripts/advection_model.py
266-
:lines: 133-141
271+
:lines: 136-144
267272

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

271276
.. literalinclude:: scripts/advection_model.py
272-
:lines: 144-145
277+
:lines: 147-148
273278

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

282287
.. literalinclude:: scripts/advection_model.py
283-
:lines: 148
288+
:lines: 151
284289

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

306311
.. literalinclude:: scripts/advection_model.py
307-
:lines: 151-158
312+
:lines: 154-161
308313

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

312317
.. literalinclude:: scripts/advection_model.py
313-
:lines: 161-161
318+
:lines: 164
314319

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

319324
.. literalinclude:: scripts/advection_model.py
320-
:lines: 164-172
325+
:lines: 167-175
321326

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

327332
.. literalinclude:: scripts/advection_model.py
328-
:lines: 175-175
333+
:lines: 178
329334

330335
.. warning::
331336

doc/examples/landscape-evolution-model.ipynb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2102,6 +2102,7 @@
21022102
" self._u_rate = np.zeros((ny, nx))\n",
21032103
" self._u_rate[mask] = u_rate[mask]\n",
21042104
"\n",
2105+
" @xs.runtime(args='step_delta')\n",
21052106
" def run_step(self, dt):\n",
21062107
" self.uplift = self._u_rate * dt\n"
21072108
]

doc/scripts/advection_model.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def initialize(self):
2222
self.x = np.arange(0, self.length, self.spacing)
2323
self.u = np.exp(-1 / self.scale**2 * (self.x - self.loc)**2)
2424

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

58-
def run_step(self, *args):
59+
def run_step(self):
5960
self._delta_u = sum((v for v in self.u_vars))
6061

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

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

@@ -126,6 +128,7 @@ def source_rate(self):
126128
src_array[self.nearest_node] = self.flux
127129
return src_array
128130

131+
@xs.runtime(args='step_delta')
129132
def run_step(self, dt):
130133
self.u_source = self.source_rate * dt
131134

doc/whats_new.rst

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,26 @@ Breaking changes
1515
model (:issue:`45`). Although it should work just fine in most
1616
cases, there are potential caveats. This should be considered as an
1717
experimental, possibly breaking change.
18+
- ``Model.initialize``, ``Model.run_step``, ``Model.finalize_step``
19+
and ``Model.finalize`` have been removed in favor of
20+
``Model.execute`` (:issue:`59`).
21+
22+
Deprecations
23+
~~~~~~~~~~~~
24+
25+
- ``run_step`` methods defined in process classes won't accept anymore
26+
current step duration as a positional argument by default. Use the
27+
``runtime`` decorator if you need current step duration (and/or
28+
other runtime information) inside the method (:issue:`59`).
1829

1930
Enhancements
2031
~~~~~~~~~~~~
2132

2233
- Ensure that there is no ``intent`` conflict between the variables
2334
declared in a model. This check is explicit at Model creation and a
2435
more meaningful error message is shown when it fails (:issue:`57`).
25-
36+
- Added ``runtime`` decorator to pass simulation runtime information
37+
to the (runtime) methods defined in process classes (:issue:`59`).
2638

2739
v0.2.1 (7 November 2018)
2840
------------------------

xsimlab/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"""
55
from .xr_accessor import SimlabAccessor, create_setup
66
from .variable import variable, on_demand, foreign, group
7-
from .process import filter_variables, process, process_info, variable_info
7+
from .process import (filter_variables, process, process_info, runtime,
8+
variable_info)
89
from .model import Model
910

1011
from ._version import get_versions

0 commit comments

Comments
 (0)