You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: sections/core-concepts/index.qmd
+10-4Lines changed: 10 additions & 4 deletions
Original file line number
Diff line number
Diff line change
@@ -17,6 +17,9 @@ classDiagram
17
17
}
18
18
class UniformCartesian {
19
19
+spacing
20
+
}
21
+
class Mesh {
22
+
+gridsize
20
23
+origin
21
24
}
22
25
class FieldLayout {
@@ -38,6 +41,7 @@ classDiagram
38
41
FieldLayout --> NDIndex
39
42
Field --> FieldLayout
40
43
Field --> UniformCartesian
44
+
UniformCartesian --> Mesh
41
45
ParticleBase --> FieldLayout
42
46
```
43
47
@@ -142,7 +146,7 @@ flowchart LR
142
146
Index1["Index y"]
143
147
Index2["Index z"]
144
148
Domain["NDIndex<Dim>"]
145
-
Mesh["UniformCartesian<T, Dim>"]
149
+
Mesh["UniformCartesian<T, Dim> (Mesh)"]
146
150
Layout["FieldLayout<Dim>"]
147
151
Field["Field<T, Dim, Mesh, Centering>"]
148
152
@@ -157,8 +161,10 @@ flowchart LR
157
161
158
162
## `ParameterList`
159
163
160
-
Solver and backend options are commonly passed through `ippl::ParameterList`. The allowed value types are `double`, `float`, `bool`, `std::string`, `unsigned int`, `int`, and nested `ParameterList`.
161
164
165
+
Solvers take many options, such as type of algorithm used, etc. These also include options for the FFT provided by heffte. These are commonly passed through `ippl::ParameterList`.
166
+
167
+
The ParameterList is a collection of options, identified with a key (`std::string`) and given a value. The allowed value types in the ParameterList are `double`, `float`, `bool`, `std::string`, `unsigned int`, `int`. The following is an example of a ParameterList with some heffte options:
`add` rejects duplicate keys. `get` without a default throws if the key is missing, while `get(key, defaultValue)` returns the default for absent keys.
176
+
To add options to the ParameterList, we use `add(key, value)`. `add` rejects duplicate keys. The value of a key can be updated using `update(key, value)`.
177
+
To get the value of an option in the ParameterList, we can use the `get<T>(key)` function. `get(key)` without a default throws if the key is missing, while `get(key, defaultValue)` returns the default for absent keys.
@@ -99,33 +126,47 @@ The `Field` template inherits from `BareField` and adds a mesh pointer plus fiel
99
126
| `view_type` | Kokkos view type used for field storage. |
100
127
| `BConds_t` | Boundary-condition container type for the field. |
101
128
102
-
The constructor form `ScalarField rho(mesh, layout);` is also used in tests. The default ghost depth is one cell unless a different `nghost` value is supplied to the constructor or `initialize`.
129
+
The constructor form `ScalarField field(mesh, layout);` is also used in tests. The default ghost depth is one cell unless a different `nghost` value is supplied to the constructor or `initialize`.
103
130
104
131
## Accessing data in kernels
105
132
106
-
The Gaussian Poisson test shows the standard local-to-global mapping inside a field kernel:
107
-
133
+
Field data can be accessed within a Kokkos kernel. For the most common parallel pattern, `Kokkos::parallel_for`, there exists an `ippl::parallel_for` to loop over fields in a dimension independent manner. As an example, the following loops over a field and assigns values:
108
134
```cpp
109
-
auto view = rho.getView();
110
-
constint nghost = rho.getNghost();
111
-
constauto& ldom = layout.getLocalNDIndex();
112
-
113
-
Kokkos::parallel_for(
114
-
"Assign rho field", rho.getFieldRangePolicy(),
115
-
KOKKOS_LAMBDA(const int i, const int j, const int k) {
116
-
const int ig = i + ldom[0].first() - nghost;
117
-
const int jg = j + ldom[1].first() - nghost;
118
-
const int kg = k + ldom[2].first() - nghost;
119
-
120
-
const double x = (ig + 0.5) * hr[0] + origin[0];
121
-
const double y = (jg + 0.5) * hr[1] + origin[1];
122
-
const double z = (kg + 0.5) * hr[2] + origin[2];
123
-
124
-
view(i, j, k) = gaussian(x, y, z);
135
+
auto view = field.getView();
136
+
const int nghost = field.getNghost();
137
+
138
+
using index_array_type = typename ippl::RangePolicy<Dim>::index_array_type;
139
+
ippl::parallel_for(
140
+
"Assign field values", field.getFieldRangePolicy(),
141
+
KOKKOS_LAMBDA(const index_array_type& args) {
142
+
// ippl::apply accesses the view at the given indices and obtains a
143
+
// reference; see src/Expression/IpplOperations.h
144
+
ippl::apply(view, args) = ...;
125
145
});
126
146
```
127
147
128
-
This pattern is worth documenting carefully because it makes three contracts explicit: local indices include ghost cells, `FieldLayout` provides the owned global offset, and cell-centered coordinates use the `0.5` shift.
148
+
When doing operations which require the physical position of a point in the global domain (x, y, z), we need to map from the local index of the Field data in the Kokkos View (local to each MPI rank), to the global position in the full domain.
149
+
150
+
The standard way to do this is using the domain information in the layout and the mesh spacing. For example, when assigning field values according to a function, such as a sinusoidal:
151
+
```cpp
152
+
auto view = field.getView();
153
+
constint nghost = field.getNghost();
154
+
155
+
constauto& ldom = layout.getLocalNDIndex();
156
+
constauto hr = mesh.getMeshSpacing();
157
+
constauto origin = mesh.getOrigin();
158
+
159
+
using index_array_type = typename ippl::RangePolicy<Dim>::index_array_type;
160
+
ippl::parallel_for(
161
+
"Assign field values", field.getFieldRangePolicy(),
The layout (`FieldLayout`) contains information about the offset of the sub-domain owned by this MPI rank with respect to the global origin, given by `ldom.first()`. The local indexing also contains the ghost cells, hence why it needs to be subtracted to obtain a global index.
169
+
The `+ 0.5` in the global position computation is needed due to cell centering.
129
170
130
171
## Layout and neighbors
131
172
@@ -157,6 +198,7 @@ Current field boundary conditions are stored in `BConds<Field, Dim>`, an array-l
157
198
|`3`| upper face in dimension 1 |
158
199
|`2 * d`| lower face in dimension `d`|
159
200
|`2 * d + 1`| upper face in dimension `d`|
201
+
The dimensions go from 0 up to Dim-1.
160
202
161
203
Supported field boundary-condition classes are:
162
204
@@ -180,7 +222,7 @@ for (size_t face = 0; face < 2 * Dim; ++face) {
Mixed boundary conditions are also possible. `test/field/TestFieldBC.cpp` uses periodic faces in the x direction, no boundary condition on the lower y face, a constant value on the upper y face, zero on the lower z face, and extrapolation on the upper z face:
`setFieldBC` stores the container and calls `findBCNeighbors` on the field. Differential operators such as `grad`, `div`, `laplace`, `curl`, and `hess` call `fillHalo()` and apply the field boundary conditions before building the expression object.
@@ -205,10 +247,10 @@ rho.setFieldBC(bc);
205
247
Ghost cells provide local data for stencil operations at subdomain boundaries. A field has a ghost depth `nghost`, available through `getNghost()`. Halo exchange is invoked explicitly with:
206
248
207
249
```cpp
208
-
rho.fillHalo();
250
+
field.fillHalo();
209
251
```
210
252
211
-
Field operations that require neighbor values call halo filling internally. For custom kernels, users must decide whether the operation reads only owned cells or also reads ghost cells. If the kernel reads stencil neighbors across rank boundaries, call `fillHalo()` and apply boundary conditions first.
253
+
Field operations that require neighbor values call `fillHalo`internally. For custom kernels, users must decide whether the operation needs only owned cells of the MPI rank or also ghost cells. If the kernel needs neighbors across rank boundaries, e.g. for stencil operations, call `fillHalo()` and apply boundary conditions first.
212
254
213
255
## Expressions and reductions
214
256
@@ -217,56 +259,55 @@ Fields support scalar assignment, field expressions, math functions, and reducti
217
259
| Operation | Example |
218
260
|---|---|
219
261
| scalar assignment |`rho = 1.0;`|
220
-
| deep copy |`auto copy = rho.deepCopy();`|
262
+
| deep copy |`auto rho_copy = rho.deepCopy();`|
221
263
| arithmetic |`rho = rho - exact;`|
222
264
| sum |`double q = rho.sum();`|
223
265
| norm |`double err = norm(rho) / norm(exact);`|
224
266
| volume integral |`rho.getVolumeIntegral();`|
225
267
| volume average |`rho.getVolumeAverage();`|
226
-
| gradient |`efield = grad(rho);`|
268
+
| gradient |`efield = grad(phi);`|
227
269
| divergence |`rho = div(efield);`|
228
270
| curl |`result = curl(vectorField);`|
229
271
| Hessian |`matrixField = hess(rho);`|
230
272
| Laplacian |`lap = laplace(rho);`|
231
273
232
-
The differential operators use mesh spacing from `UniformCartesian`. For example, `laplace`builds coefficients from `1 / h_d^2` in each dimension, while `grad` and `div` use centered differences scaled by `0.5 / h_d`.
274
+
The differential operators use the spacing obtained from the `UniformCartesian` mesh. For example, `laplace`has a pre-factor of `1 / h[d]^2` in each dimension d.
233
275
234
276
## Laplacian example
235
277
236
-
`test/field/TestLaplace.cpp` is the compact example for differential field operators. It builds a periodic scalar field on `[-1, 1]^3`,
278
+
`test/field/TestLaplace.cpp` is the compact example for differential field operators. It builds a 3-dimensinal periodic scalar field on `[-1, 1]^3`,
237
279
238
-
\[
280
+
$$
239
281
u(x,y,z) = \sin(\pi x)\sin(\pi y)\sin(\pi z),
240
-
\]
282
+
$$
241
283
242
-
then compares `laplace(field)` with
284
+
then compares `laplace(field)` with the exact form
243
285
244
-
\[
286
+
$$
245
287
\nabla^2 u = -3\pi^2 \sin(\pi x)\sin(\pi y)\sin(\pi z).
246
-
\]
288
+
$$
247
289
248
290
The relevant operator call is deliberately simple:
This example is a good manual candidate because it touches domain construction, mesh spacing, periodic boundary conditions, local-to-global coordinate conversion, expression assignment, reductions, timing, and explicit `ippl::fence()` calls inside a benchmark loop.
265
-
266
307
## Sub-layouts
267
308
268
-
The test suite contains parallel coverage for both full layouts and sub-layouts. The user-level contract is the same: a field is tied to a mesh and a layout, while the layout determines local domains, neighbor relationships, and halo ranges. Sub-layout documentation should be expanded once the `SubFieldLayout` examples are promoted into the manual.
309
+
TO-DO
269
310
270
311
## Documentation tasks
271
312
272
-
Remaining migration work from the legacy LaTeX manual: older `where` examples, a compact table of mathematical functions supported by expressions, and a side-by-side update of legacy constructor forms to current constructor forms.
313
+
Remaining migration work from the legacy LaTeX manual: older `where` examples, a compact table of mathematical functions supported by expressions, and a side-by-side update of legacy constructor forms to current constructor forms.
Copy file name to clipboardExpand all lines: sections/overview/index.qmd
+5-2Lines changed: 5 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,7 @@
2
2
3
3
IPPL provides reusable components for scientific simulations that need distributed fields, particles, interpolation, FFTs, and solvers. The library is designed around templates for dimension, precision, and execution space, so the same user code can target serial, OpenMP, CUDA, or HIP builds when the backend stack is available.
0 commit comments