Skip to content

Commit fd05c20

Browse files
authored
Merge branch 'master' into moar-omp-off
2 parents e234ee0 + 2d355c2 commit fd05c20

31 files changed

+68
-1420
lines changed

.github/workflows/documentation.yml

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,14 @@ on:
77

88
jobs:
99
build:
10-
1110
runs-on: ubuntu-latest
1211

1312
steps:
1413
- uses: actions/checkout@v3
15-
- name: Set up Python
16-
uses: actions/setup-python@v4
17-
with:
18-
# TODO: 3.x should be fine, but for now we enforce 3.10 to work around
19-
# the "fatal error: longintrepr.h: No such file or directory" error
20-
# triggered by revolve/cython in 3.11
21-
python-version: '3.10'
22-
- name: Install Sphinx
23-
run: |
24-
python -m pip install --upgrade pip
25-
pip install sphinx sphinx_rtd_theme
26-
pip install -e .
27-
28-
- name: Generate documentation
29-
working-directory: docs
30-
run: make html
3114

32-
- name: Deploy
33-
uses: peaceiris/actions-gh-pages@v3
15+
- name: Repository Dispatch
16+
uses: peter-evans/repository-dispatch@v2
3417
with:
35-
personal_token: ${{ secrets.PERSONAL_TOKEN }}
36-
publish_branch: gh-pages
37-
publish_dir: ./docs/_build/html
18+
token: ${{ secrets.DEPLOY_DOC_PAT }}
19+
repository: devitocodes/devitoproject.org
20+
event-type: deploy-docs

FAQ.md

Lines changed: 63 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
## How can I see the code generated by Devito?
4040
After you build an ```op=Operator(...)``` implementing one or more equations, you can use ```print(op)``` to see the generated low level code. The example below builds an operator that takes a 1/2 cell forward shifted derivative of the ```Function``` **f** and puts the result in the ```Function``` **g**.
4141

42-
```
42+
```python
4343
import numpy as np
4444
import devito
4545
from devito import Grid, Function, Eq, Operator
@@ -54,7 +54,7 @@ print(op)
5454

5555
And the output:
5656

57-
```
57+
```C
5858
#define _POSIX_C_SOURCE 200809L
5959
#include "stdlib.h"
6060
#include "math.h"
@@ -107,7 +107,8 @@ int Kernel(struct dataobj *restrict f_vec, struct dataobj *restrict g_vec, const
107107
Set the environment variable `DEVITO_LOGGING=DEBUG`. When an Operator gets compiled, the used compilation command will be emitted to stdout.
108108
109109
If nothing seems to change, it is possible that no compilation is happening under-the-hood as all kernels have already been compiled in a previous run. You will then have to clear up the Devito kernel cache. From the Devito root directory, run:
110-
```
110+
111+
```bash
111112
python scripts/clear_devito_cache.py
112113
```
113114

@@ -151,7 +152,8 @@ Take a look [here](https://github.com/devitocodes/devito/tree/master/examples/pe
151152
Devito applies several performance optimizations to improve the number of operations ("operation count") in complex expressions. These optimizations are designed to do a really good job but also be reasonably fast. One such pass attempts to factorize as many common terms as possible in expressions in order to reduce the operation count. We will construct a demonstrative example below that has a common term that is _not_ factored out by the Devito optimization. The difference in floating-point operations per output point for the factoring of that term is about 10 percent, and the generated C is different, but numerical outputs of running the two different operators are indistinguishable to machine precision. In terms of actual performance, the (few) missed factorization opportunities may not necessarily be a relevant issue: as long as the code is not heavily compute-bound, the runtimes may only be slightly higher than in the optimally-factorized version.
152153

153154
#### Operator 1:
154-
```
155+
156+
```python
155157
ux_update = t.spacing**2 * b * \
156158
((c33 * u_x.dx(x0=x+x.spacing/2)).dx(x0=x-x.spacing/2) +
157159
(c55 * u_x.dz(x0=z+z.spacing/2)).dz(x0=z-z.spacing/2) +
@@ -162,8 +164,10 @@ stencil_x = Eq(u_x.forward, ux_update)
162164
print("\n", stencil_x)
163165
op = Operator([stencil_x])
164166
```
167+
165168
#### Operator 2:
166-
```
169+
170+
```python
167171
ux_update = \
168172
t.spacing**2 * b * (c33 * u_x.dx(x0=x+x.spacing/2)).dx(x0=x-x.spacing/2) + \
169173
t.spacing**2 * b * (c55 * u_x.dz(x0=z+z.spacing/2)).dz(x0=z-z.spacing/2) + \
@@ -176,7 +180,8 @@ op = Operator([stencil_x])
176180
```
177181

178182
#### Output 1:
179-
```
183+
184+
```bash
180185
Eq(u_x(t + dt, x, z), dt**2*(Derivative(c13(x, z)*Derivative(u_z(t, x, z), z), x) + Derivative(c33(x, z)*Derivative(u_x(t, x, z), x), x) + Derivative(c55(x, z)*Derivative(u_x(t, x, z), z), z) + Derivative(c55(x, z)*Derivative(u_z(t, x, z), x), z))*b(x, z) + (-dt*wOverQ(x, z) + 2)*u_x(t, x, z) + (dt*wOverQ(x, z) - 1)*u_x(t - dt, x, z))
181186
Operator `Kernel` generated in 1.26 s
182187
* lowering.Expressions: 0.61 s (48.7 %)
@@ -186,7 +191,8 @@ Flops reduction after symbolic optimization: [1160 --> 136]
186191
```
187192

188193
#### Output 2:
189-
```
194+
195+
```bash
190196
Eq(u_x(t + dt, x, z), dt**2*b(x, z)*Derivative(c13(x, z)*Derivative(u_z(t, x, z), z), x) + dt**2*b(x, z)*Derivative(c33(x, z)*Derivative(u_x(t, x, z), x), x) + dt**2*b(x, z)*Derivative(c55(x, z)*Derivative(u_x(t, x, z), z), z) + dt**2*b(x, z)*Derivative(c55(x, z)*Derivative(u_z(t, x, z), x), z) + (-dt*wOverQ(x, z) + 2)*u_x(t, x, z) + (dt*wOverQ(x, z) - 1)*u_x(t - dt, x, z))
191197
Operator `Kernel` generated in 1.12 s
192198
* lowering.Expressions: 0.59 s (53.0 %)
@@ -221,7 +227,8 @@ You will note that this method uses placeholders for the material parameter arra
221227

222228
### How to get the list of Devito environment variables
223229
You can get the list of environment variables with the following python code:
224-
```
230+
231+
```python
225232
from devito import print_defaults
226233
print_defaults()
227234
```
@@ -304,7 +311,8 @@ Set `DEVITO_IGNORE_UNKNOWN_PARAMS=1` to avoid Devito raising an exception if one
304311

305312
## How do you run the unit tests from the command line
306313
In addition to the [tutorials]( https://www.devitoproject.org/devito/tutorials.html), the unit tests provide an excellent way to see how the Devito API works with small self-contained examples. You can exercise individual unit tests with the following python code:
307-
```
314+
315+
```bash
308316
pytest <test.py>
309317
pytest -vs <test.py> [more detailed log]
310318
```
@@ -315,7 +323,7 @@ pytest -vs <test.py> [more detailed log]
315323
## What is the difference between f() and f[] notation
316324
Devito offers a functional language to express finite difference operators. This is introduced [here](https://github.com/devitocodes/devito/blob/master/examples/userapi/01_dsl.ipynb) and systematically used throughout our examples and tutorials. The language relies on what in jargon we call the "f() notation".
317325

318-
```
326+
```python
319327
>>> from devito import Grid, Function
320328
>>> grid = Grid(shape=(5, 6))
321329
>>> f = Function(name='f', grid=grid, space_order=2)
@@ -327,15 +335,15 @@ Derivative(f(x, y), x)
327335

328336
Sometimes, one wishes to escape the constraints of the language. Instead of taking derivatives, other special operations are required. Or perhaps, a specific grid point needs to be accessed. In such a case, one could use the "f[] notation" or "indexed notation". Following on from the example above:
329337

330-
```
338+
```python
331339
>>> x, y = grid.dimensions
332340
>>> f[x + 1000, y]
333341
f[x + 1000, y]
334342
```
335343

336344
The indexed object can be used at will to construct `Eq`s, and they can be mixed up with objects stemming from the "f() notation".
337345

338-
```
346+
```python
339347
>>> f.dx + f[x + 1000, y]
340348
Derivative(f(x, y), x) + f[x + 1000, y]
341349
```
@@ -378,19 +386,23 @@ The indexed notation, or "f[] notation", is discussed [here](#What-is-the-differ
378386

379387
## What's up with object\.data
380388
The `.data` property which is associated with objects such as `Constant`, `Function` and `SparseFunction` (along with their derivatives) represents the 'numerical' value of the 'data' associated with that particular object. For example, a `Constant` will have a single numerical value associated with it as shown in the following snippet
381-
```
389+
390+
```python
382391
from devito import Constant
383392

384393
c = Constant(name='c')
385394
c.data = 2.7
386395

387396
print(c.data)
388397
```
389-
```
398+
399+
```default
390400
2.7
391401
```
402+
392403
Then, a `Function` defined on a `Grid` will have a data value associated with each of the grid points (as shown in the snippet below) and so forth.
393-
```
404+
405+
```python
394406
import numpy as np
395407
from devito import Grid, Function
396408

@@ -400,7 +412,8 @@ f.data[:] = np.arange(16).reshape(grid.shape)
400412

401413
print(f.data)
402414
```
403-
```
415+
416+
```default
404417
[[ 0. 1. 2. 3.]
405418
[ 4. 5. 6. 7.]
406419
[ 8. 9. 10. 11.]
@@ -412,27 +425,36 @@ print(f.data)
412425

413426
## How do I create and N-dimensional grid
414427
Grids are often created via, e.g.,
415-
```
428+
429+
```python
416430
grid = Grid(shape=(5, 5))
417431
```
432+
418433
where printing the `grid` object then returns:
419-
```
434+
435+
```default
420436
Grid[extent=(1.0, 1.0), shape=(5, 5), dimensions=(x, y)]
421-
```
422-
Here we see the `grid` has been created with the 'default' dimensions `x` and `y`. If a grid is created and passed a shape of `(5, 5, 5)` we'll see that in addition it has a `z` dimension. However, what if we want to create a grid with, say, a shape of `(5, 5, 5, 5)`? For this case, we've now run out of the dimensions defined by default and hence need to create our own dimensions to achieve this. This can be done via, e.g.,
423437
```
438+
439+
Here we see the `grid` has been created with the 'default' dimensions `x` and `y`. If a grid is created and passed a shape of `(5, 5, 5)` we'll see that in addition it has a `z` dimension. However, what if we want to create a grid with, say, a shape of `(5, 5, 5, 5)`? For this case, we've now run out of the dimensions defined by default and hence need to create our own dimensions to achieve this. This can be done via, e.g.,
440+
441+
```python
424442
a = SpaceDimension('a')
425443
b = SpaceDimension('b')
426444
c = SpaceDimension('c')
427445
d = SpaceDimension('d')
428446
grid = Grid(shape=(5, 5, 5, 5), dimensions=(a, b, c, d))
429447
```
448+
430449
where now, printng `grid` we get
431-
```
450+
451+
```default
432452
Grid[extent=(1.0, 1.0, 1.0, 1.0), shape=(5, 5, 5, 5), dimensions=(a, b, c, d)]
433453
```
454+
434455
and `grid.shape` returns
435-
```
456+
457+
```default
436458
(5, 5, 5, 5)
437459
```
438460

@@ -463,7 +485,8 @@ Loop fission (to maximize parallelism)
463485
## As time increases in the finite difference evolution, are wavefield arrays "swapped" as you might see in c/c++ code
464486

465487
In c/c++ code using two wavefield arrays for second order acoustics, you might see code like the following to “swap” the wavefield arrays at each time step:
466-
```
488+
489+
```C
467490
float *p_tmp = p_old;
468491
p_old = p_cur;
469492
p_cur = p_tmp;
@@ -491,7 +514,7 @@ First, classes such as `Function` or `SparseTimeFunction` are inherently complex
491514

492515
Second, you must know that these objects are subjected to so-called reconstruction during compilation. Objects are immutable inside Devito; therefore, even a straightforward symbolic transformation such as `f[x] -> f[y]` boils down to performing a reconstruction, that is, creating a whole new object. Since `f` carries around several attributes (e.g., shape, grid, dimensions), each time Devito performs a reconstruction, we only want to specify which attributes are changing -- not all of them, as it would make the code ugly and incredibly complicated. The solution to this problem is that all the base symbolic types inherit from a common base class called `Reconstructable`; a `Reconstructable` object has two special class attributes, called `__rargs__` and `__rkwargs__`. If a subclass adds a new positional or keyword argument to its `__init_finalize__`, it must also be added to `__rargs__` or `__rkwargs__`, respectively. This will provide Devito with enough information to perform a reconstruction when it's needed during compilation. The following example should clarify:
493516

494-
```
517+
```python
495518
class Foo(Reconstructable):
496519
__rargs__ = ('a', 'b')
497520
__rkwargs__ = ('c',)
@@ -515,7 +538,7 @@ class Bar(Foo):
515538

516539
You are unlikely to care about how reconstruction works in practice, but here are a few examples for `a = Foo(3, 5)` to give you more context.
517540

518-
```
541+
```python
519542
a._rebuild() -> "x(3, 5, 4)" (i.e., copy of `a`).
520543
a._rebuild(4) -> "x(4, 5, 4)"
521544
a._rebuild(4, 7) -> "x(4, 7, 4)"
@@ -534,7 +557,7 @@ There is currently no API to achieve this straightforwardly. However, there are
534557
* via env vars: use a [CustomCompiler](https://github.com/opesci/devito/blob/v4.0/devito/compiler.py#L446) -- just leave the `DEVITO_ARCH` environment variable unset or set it to `'custom'`. Then, `export CFLAGS="..."` to tell Devito to use the exported flags in place of the default ones.
535558
* programmatically: subclass one of the compiler classes and set `self.cflags` to whatever you need. Do not forget to add the subclass to the [compiler registry](https://github.com/opesci/devito/blob/v4.0/devito/compiler.py#L472). For example, you could do
536559

537-
```
560+
```python
538561
from devito import configuration, compiler_registry
539562
from devito.compiler import GNUCompiler
540563

@@ -576,7 +599,8 @@ Until Devito v3.5 included, domain decomposition occurs along the fastest axis.
576599
## How should I use MPI on multi-socket machines
577600

578601
In general you should use one MPI rank per NUMA node on a multi-socket machine. You can find the number of numa nodes with the `lscpu` command. For example, here is the relevant part of the output from the `lscpu` command on an AMD 7502 2 socket machine with 2 NUMA nodes:
579-
```
602+
603+
```default
580604
Architecture: x86_64
581605
CPU(s): 64
582606
On-line CPU(s) list: 0-63
@@ -597,7 +621,7 @@ NUMA node1 CPU(s): 32-63
597621
There are a few things you may want to check
598622

599623
* To refer to the actual ("global") shape of the domain, you should always use `grid.shape` (or analogously through a `Function`, `f.grid.shape`). And unless you know well what you're doing, you should never use the function shape, namely `f.shape` or `f.data.shape`, as that will return the "local" domain shape, that is the data shape after domain decomposition, which might differ across the various MPI ranks.
600-
* <... to be completed ...>
624+
601625

602626
[top](#Frequently-Asked-Questions)
603627

@@ -613,17 +637,23 @@ This is likely due to an out-of-bounds (OOB) array access while running the gene
613637
## Can I manually modify the C code generated by Devito and test these modifications
614638

615639
Yes, as of Devito v3.5 it is possible to modify the generated C code and run it inside Devito. First you need to get the C file generated for a given `Operator`. Run your code in `DEBUG` mode:
616-
```
640+
641+
```bash
617642
DEVITO_LOGGING=DEBUG python your_code.py
618643
```
644+
619645
The generated code path will be shown as in the excerpt below:
620-
```
646+
647+
```default
621648
CustomCompiler: compiled `/tmp/devito-jitcache-uid1000/ed41e9373af1bc129471b7ae45e1c3740b60a856.c` [0.29 s]
622649
```
650+
623651
You can now open the C file, do the modifications you like, and save them. Finally, rerun the same program but this time with the _Devito JIT backdoor_ enabled:
624-
```
652+
653+
```bash
625654
DEVITO_JIT_BACKDOOR=1 python your_code.py
626655
```
656+
627657
This will force Devito to recompile and link the modified C code.
628658

629659
If you have a large codebase with many `Operator`s, here's a [trick](https://github.com/devitocodes/devito/wiki/Efficient-use-of-DEVITO_JIT_BACKDOOR-in-large-codes-with-many-Operators) to speed up your hacking with the JIT backdoor.
@@ -675,7 +705,7 @@ About the GPts/s metric, that is number of gigapoints per seconds. The "points"
675705

676706
An excerpt of the performance profile emitted by Devito upon running an Operator is provided below. In this case, the Operator has two sections, ``section0`` and ``section1``, and ``section1`` consists of two consecutive 6D iteration spaces whose size is given between angle brackets.
677707

678-
```
708+
```default
679709
Global performance: [OI=0.16, 8.00 GFlops/s, 0.04 GPts/s]
680710
Local performance:
681711
* section0<136,136,136> run in 0.10 s [OI=0.16, 0.14 GFlops/s]
@@ -695,7 +725,7 @@ The floating-point operations are counted once all of the symbolic flop-reducing
695725

696726
To calculate the GFlops/s performance, Devito multiplies the floating-point operations calculated at compile time by the size of the iteration space, and it does that at the granularity of individual expressions. For example, consider the following snippet:
697727

698-
```
728+
```default
699729
<section0 start>
700730
for x = x_m to x_M
701731
for y = y_m to y_M

0 commit comments

Comments
 (0)