cuwave 0.7.1__tar.gz → 0.7.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cuwave-0.7.1 → cuwave-0.7.2}/PKG-INFO +1 -1
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/boundary.py +12 -0
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/decomposition.py +3 -1
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/elastic.py +87 -25
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/kernels/common.cuh +256 -10
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/kernels/elastic.cu +317 -338
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/kernels/elastic_sensitivity.cu +30 -37
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/kernels/maxwell.cu +29 -17
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/kernels/maxwell_sensitivity.cu +20 -8
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/kernels/scalar.cu +180 -75
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/kernels/scalar_sensitivity.cu +190 -152
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/maxwell.py +8 -3
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/scalar.py +13 -6
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/sensitivity.py +2 -1
- cuwave-0.7.2/cuwave/stencils.py +453 -0
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/wave.py +35 -12
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave.egg-info/PKG-INFO +1 -1
- {cuwave-0.7.1 → cuwave-0.7.2}/pyproject.toml +1 -1
- cuwave-0.7.1/cuwave/stencils.py +0 -80
- {cuwave-0.7.1 → cuwave-0.7.2}/LICENSE +0 -0
- {cuwave-0.7.1 → cuwave-0.7.2}/README.md +0 -0
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/__init__.py +0 -0
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/anisotropic.py +0 -0
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/evals.py +0 -0
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/geometry.py +0 -0
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/kernels/__init__.py +0 -0
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/kernels/anisotropic.cu +0 -0
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/kernels/anisotropic_sensitivity.cu +0 -0
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/nn.py +0 -0
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/optimization.py +0 -0
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/postprocessing.py +0 -0
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/regularization.py +0 -0
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/signals.py +0 -0
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave/utils.py +0 -0
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave.egg-info/SOURCES.txt +0 -0
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave.egg-info/dependency_links.txt +0 -0
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave.egg-info/requires.txt +0 -0
- {cuwave-0.7.1 → cuwave-0.7.2}/cuwave.egg-info/top_level.txt +0 -0
- {cuwave-0.7.1 → cuwave-0.7.2}/setup.cfg +0 -0
|
@@ -92,6 +92,18 @@ def face_mask(sim: Simulation, condition: BoundaryCondition) -> np.int32:
|
|
|
92
92
|
return np.int32(sum(1 << f for f in faces_with(sim, condition)))
|
|
93
93
|
|
|
94
94
|
|
|
95
|
+
def require_off_held_walls(sim: Simulation, position: cpt.NDArray[cp.int32]) -> None:
|
|
96
|
+
"""Raise if a source at `position` sits on the wall node of a `Dirichlet` face, which the step holds at zero."""
|
|
97
|
+
rows = position[-sim.ndim :]
|
|
98
|
+
for face in faces_with(sim, Dirichlet):
|
|
99
|
+
d, side = divmod(face, 2)
|
|
100
|
+
wall = sim.Nx[d] - 2 if side else 1
|
|
101
|
+
if bool(cp.any(rows[d] == wall)):
|
|
102
|
+
raise ValueError(
|
|
103
|
+
f"a source on the wall node {wall} of Dirichlet face {face}, held at zero"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
95
107
|
def canonical_boundary(
|
|
96
108
|
boundary: BoundaryCondition | Sequence | None,
|
|
97
109
|
ndim: int,
|
|
@@ -20,7 +20,7 @@ import cupy as cp
|
|
|
20
20
|
import cupy.typing as cpt
|
|
21
21
|
import numpy as np
|
|
22
22
|
|
|
23
|
-
from .boundary import _canonical_faces, pml_widths
|
|
23
|
+
from .boundary import _canonical_faces, pml_widths, require_off_held_walls
|
|
24
24
|
|
|
25
25
|
if TYPE_CHECKING:
|
|
26
26
|
from .wave import Simulation
|
|
@@ -295,6 +295,8 @@ def decomposable(function: Callable) -> Callable:
|
|
|
295
295
|
def run(*args, **kwargs):
|
|
296
296
|
bound = signature.bind(*args, **kwargs)
|
|
297
297
|
sim = bound.arguments["sim"]
|
|
298
|
+
# checked whole, since a subdomain's walls at the cuts are halo rows
|
|
299
|
+
require_off_held_walls(sim, bound.arguments["source"].position)
|
|
298
300
|
if sim.devices is None:
|
|
299
301
|
return function(*args, **kwargs)
|
|
300
302
|
if bound.arguments.get("record_every") is not None:
|
|
@@ -6,7 +6,8 @@ axes. Each is a pure per-axis staggered difference, which is what carries the cr
|
|
|
6
6
|
terms a per-axis flux cannot and keeps the cost linear in the stencil radius, where the
|
|
7
7
|
cell gather of `AnisotropicElasticWave` pays `(2r)**(2 ndim)`. The operator is
|
|
8
8
|
`-B^T C B` with the material sampled on the stress points, so it stays the exact
|
|
9
|
-
transpose at every order.
|
|
9
|
+
transpose at every order. Its walls are the closures of a staggered SBP pair, whose norms
|
|
10
|
+
weigh the stress and component points, so the walls keep the interior order.
|
|
10
11
|
"""
|
|
11
12
|
|
|
12
13
|
from __future__ import annotations
|
|
@@ -23,13 +24,13 @@ import numpy as np
|
|
|
23
24
|
import numpy.typing as npt
|
|
24
25
|
|
|
25
26
|
from .boundary import Clamped, Traction, face_mask, faces_with
|
|
27
|
+
from .stencils import CLOSURES, closure_band, closure_norms, closure_reach
|
|
26
28
|
from .wave import (
|
|
27
29
|
PAIRS,
|
|
28
30
|
Blocked,
|
|
29
31
|
LaunchSlots,
|
|
30
32
|
Simulation,
|
|
31
33
|
Source,
|
|
32
|
-
apply_cell_weights,
|
|
33
34
|
axis_geometry,
|
|
34
35
|
blocked_launches,
|
|
35
36
|
compile_kernels,
|
|
@@ -41,7 +42,6 @@ from .wave import (
|
|
|
41
42
|
point_average_adjoint,
|
|
42
43
|
tile_counts,
|
|
43
44
|
tile_lists,
|
|
44
|
-
wall_weights,
|
|
45
45
|
)
|
|
46
46
|
|
|
47
47
|
KERNEL_PATH = Path(__file__).parent / "kernels" / "elastic.cu"
|
|
@@ -138,6 +138,19 @@ def zener(relaxation: Relaxation, dt: float) -> Zener:
|
|
|
138
138
|
return Zener(rates, weights, float(np.real(1.0 / np.sqrt(modulus)) ** 2))
|
|
139
139
|
|
|
140
140
|
|
|
141
|
+
def _band_free_origin(n: int, tile: int, core: int, halo: int, band: int) -> int | None:
|
|
142
|
+
"""The latest first core start, 1 or before, that puts no blocked tile's edge inside a wall's closure band."""
|
|
143
|
+
for origin in range(1, 1 - core, -1):
|
|
144
|
+
count = -(-(n - 1 - origin) // core)
|
|
145
|
+
firsts = [origin + b * core - halo for b in range(1, count)]
|
|
146
|
+
lasts = [first + tile - 1 for first in [origin - halo, *firsts][:-1]]
|
|
147
|
+
if all(f <= 1 or f > band for f in firsts) and all(
|
|
148
|
+
e >= n - 2 or e < n - 2 - band for e in lasts
|
|
149
|
+
):
|
|
150
|
+
return origin
|
|
151
|
+
return None
|
|
152
|
+
|
|
153
|
+
|
|
141
154
|
# ------------------------------- discretization setup --------------------------------
|
|
142
155
|
@dataclass
|
|
143
156
|
class ElasticWave(Simulation):
|
|
@@ -206,6 +219,16 @@ class ElasticWave(Simulation):
|
|
|
206
219
|
if condition not in (Traction, Clamped):
|
|
207
220
|
raise ValueError(f"elastic faces are Traction or Clamped: {pair}")
|
|
208
221
|
|
|
222
|
+
# the order the walls close at, and room for both closures
|
|
223
|
+
if self.space_order not in CLOSURES:
|
|
224
|
+
raise ValueError(f"the walls close up to order 8: {self.space_order}")
|
|
225
|
+
least = 2 * closure_band(self.space_order, self.stencil) + 3
|
|
226
|
+
if min(self.Nx) < least:
|
|
227
|
+
raise ValueError(
|
|
228
|
+
f"Nx {self.Nx} at order {self.space_order} needs {least} nodes an "
|
|
229
|
+
f"axis for both closures"
|
|
230
|
+
)
|
|
231
|
+
|
|
209
232
|
# the relaxation, fitted to the step's dt
|
|
210
233
|
if self.relaxation is not None:
|
|
211
234
|
if self.pml is not None:
|
|
@@ -255,14 +278,32 @@ class ElasticWave(Simulation):
|
|
|
255
278
|
return 1.0 if self.zener is None else self.zener.unrelaxed
|
|
256
279
|
|
|
257
280
|
def inverse_inertia(self, indicator: cpt.NDArray) -> cpt.NDArray:
|
|
258
|
-
"""Nodal `1 / (gamma rho0
|
|
259
|
-
mass =
|
|
260
|
-
indicator
|
|
261
|
-
* self.density
|
|
262
|
-
* apply_cell_weights(self, cp.ones(self.Nx_padded, dtype=self.dtype))
|
|
263
|
-
)
|
|
281
|
+
"""Nodal `1 / (gamma rho0)`, the inertia the update damps by and a sponge scales its damping by."""
|
|
282
|
+
mass = indicator * self.density
|
|
264
283
|
return 1.0 / cp.maximum(mass, cp.finfo(self.dtype).tiny)
|
|
265
284
|
|
|
285
|
+
def point_weights(self, half: tuple[int, ...]) -> cpt.NDArray:
|
|
286
|
+
"""The norm W of the points on the half grid of the axes `half` and on the nodes of the rest."""
|
|
287
|
+
w = cp.ones(self.Nx_padded, dtype=self.dtype)
|
|
288
|
+
for d, line in enumerate(self._norm_lines(half)):
|
|
289
|
+
shape = [1] * self.ndim
|
|
290
|
+
shape[d] = -1
|
|
291
|
+
w *= cp.asarray(line.reshape(shape), dtype=self.dtype)
|
|
292
|
+
return w
|
|
293
|
+
|
|
294
|
+
def _norm_lines(self, half: tuple[int, ...]) -> list[npt.NDArray[np.float64]]:
|
|
295
|
+
"""Per axis the closure norms along its padded extent, 1 on the ghosts and past the walls."""
|
|
296
|
+
lines = []
|
|
297
|
+
for d, n in enumerate(self.Nx):
|
|
298
|
+
node, half_points = closure_norms(self.space_order, int(n) - 3)
|
|
299
|
+
line = np.ones(self.Nx_padded[d])
|
|
300
|
+
if d in half:
|
|
301
|
+
line[1 : n - 2] = half_points
|
|
302
|
+
else:
|
|
303
|
+
line[1 : n - 1] = node
|
|
304
|
+
lines.append(line)
|
|
305
|
+
return lines
|
|
306
|
+
|
|
266
307
|
def build_materials(self, indicator: cpt.NDArray) -> dict:
|
|
267
308
|
"""The design field alone: the kernels average it onto the stress and component points."""
|
|
268
309
|
mat = {"gamma": cp.ascontiguousarray(indicator, dtype=self.dtype)}
|
|
@@ -273,17 +314,17 @@ class ElasticWave(Simulation):
|
|
|
273
314
|
def point_inverse_inertia(
|
|
274
315
|
self, gamma: cpt.NDArray, lin_index: cpt.NDArray[cp.int32]
|
|
275
316
|
) -> cpt.NDArray:
|
|
276
|
-
"""`1 / (rho0 W mean gamma)` on the component points `lin_index`,
|
|
317
|
+
"""`1 / (rho0 W mean gamma)` on the component points `lin_index`, the inverse mass a point load divides by.
|
|
318
|
+
|
|
319
|
+
The kernels' `point_minv` leaves the norm W out, which the summation by parts
|
|
320
|
+
cancels against the stresses' (`_point_norms`), so it is this times W.
|
|
321
|
+
"""
|
|
277
322
|
component, node = lin_index // self.comp_stride, lin_index % self.comp_stride
|
|
278
323
|
rows = [node // s % n for s, n in zip(self.strides, self.Nx_padded)]
|
|
279
324
|
step = cp.asarray(self.strides, dtype=lin_index.dtype)[component]
|
|
280
325
|
flat = gamma.ravel()
|
|
281
326
|
mean = 0.5 * (flat[node] + flat[node + step])
|
|
282
|
-
|
|
283
|
-
for d, a in enumerate(rows):
|
|
284
|
-
for index in (1, self.Nx[d] - 2):
|
|
285
|
-
weight = cp.where((a == index) & (component != d), 0.5 * weight, weight)
|
|
286
|
-
mass = self.density * weight * mean
|
|
327
|
+
mass = self.density * self._point_norms(lin_index) * mean
|
|
287
328
|
minv = 1.0 / cp.maximum(mass, cp.finfo(self.dtype).tiny)
|
|
288
329
|
for face in faces_with(self, Clamped):
|
|
289
330
|
d = face // 2
|
|
@@ -291,6 +332,17 @@ class ElasticWave(Simulation):
|
|
|
291
332
|
minv = cp.where((rows[d] == wall) & (component != d), 0.0, minv)
|
|
292
333
|
return minv.astype(self.dtype)
|
|
293
334
|
|
|
335
|
+
def _point_norms(self, lin_index: cpt.NDArray[cp.int32]) -> cpt.NDArray:
|
|
336
|
+
"""The norm W on the component points `lin_index`: the half points' along its own axis."""
|
|
337
|
+
component, node = np.divmod(lin_index.get(), self.comp_stride)
|
|
338
|
+
nodal = self._norm_lines(())
|
|
339
|
+
half = self._norm_lines(tuple(range(self.ndim)))
|
|
340
|
+
weight = np.ones(len(node))
|
|
341
|
+
for d in range(self.ndim):
|
|
342
|
+
a = node // self.strides[d] % self.Nx_padded[d]
|
|
343
|
+
weight *= np.where(component == d, half[d][a], nodal[d][a])
|
|
344
|
+
return cp.asarray(weight, dtype=self.dtype)
|
|
345
|
+
|
|
294
346
|
def define_step(self, kernels: cp.RawModule, mat: dict) -> Callable:
|
|
295
347
|
"""Closure launching the stress kernel and then the update over (u0, u1, u2)."""
|
|
296
348
|
return self._define_pair(kernels, mat, adjoint=False)
|
|
@@ -341,6 +393,7 @@ class ElasticWave(Simulation):
|
|
|
341
393
|
ring = math.prod(t + self.space_order for t in tile)
|
|
342
394
|
return (
|
|
343
395
|
self.ndim >= 2
|
|
396
|
+
and min(tile) > closure_reach(self.space_order, self.stencil)
|
|
344
397
|
and self.pml is None
|
|
345
398
|
and self.relaxation is None
|
|
346
399
|
and fields > device["L2CacheSize"]
|
|
@@ -483,7 +536,15 @@ class ElasticWave(Simulation):
|
|
|
483
536
|
tile, rows, steps = block
|
|
484
537
|
halo = steps * reach
|
|
485
538
|
core = [t - 2 * halo for t in tile]
|
|
486
|
-
|
|
539
|
+
|
|
540
|
+
# the first cores moved until no tile edge cuts a wall's closure band
|
|
541
|
+
band = closure_band(self.space_order, self.stencil)
|
|
542
|
+
origin = [
|
|
543
|
+
_band_free_origin(*axis, halo, band) for axis in zip(self.Nx, tile, core)
|
|
544
|
+
]
|
|
545
|
+
if None in origin:
|
|
546
|
+
return None
|
|
547
|
+
cores = tile_counts(self.Nx, tile, halo, origin=origin)
|
|
487
548
|
|
|
488
549
|
# sources on points the step moves, sensors in the interior, none twice
|
|
489
550
|
sources = flatten_indices(self, source.position)
|
|
@@ -509,12 +570,12 @@ class ElasticWave(Simulation):
|
|
|
509
570
|
|
|
510
571
|
# a tile's sources wherever its nodes reach, its sensors where its core does
|
|
511
572
|
hits = [
|
|
512
|
-
covering_hits(points[0], tile, core, halo, cores),
|
|
513
|
-
owner_hits(points[1], points[1], core),
|
|
573
|
+
covering_hits(points[0], tile, core, halo, cores, origin=origin),
|
|
574
|
+
owner_hits(points[1], points[1], core, origin=origin),
|
|
514
575
|
]
|
|
515
576
|
lists = []
|
|
516
577
|
for hit, layer in zip(hits, layers):
|
|
517
|
-
lists += tile_lists(hit, tile, core, halo, cores, layer)
|
|
578
|
+
lists += tile_lists(hit, tile, core, halo, cores, layer, origin=origin)
|
|
518
579
|
|
|
519
580
|
# the kernel and its arguments, the pointers swapped per launch
|
|
520
581
|
flags = (f"-DSTEPS={steps}", f"-DTILE0={tile[0]}", f"-DTILE1={tile[1]}")
|
|
@@ -534,7 +595,7 @@ class ElasticWave(Simulation):
|
|
|
534
595
|
slots = LaunchSlots(len(args) - 10, len(args) - 7, len(args) - 1, None)
|
|
535
596
|
args += [face_mask(self, Clamped), np.int32(self.comp_stride)]
|
|
536
597
|
args += [self.dtype(1.0 / d) for d in self.dx]
|
|
537
|
-
args += axis_geometry(self, self.step_factors())
|
|
598
|
+
args += [*axis_geometry(self, self.step_factors()), *map(np.int32, origin)]
|
|
538
599
|
launches = [(kernel, tuple(cores[::-1]), None)]
|
|
539
600
|
return blocked_launches(steps, launches, (tile[1], rows), shared, args, slots)
|
|
540
601
|
|
|
@@ -614,9 +675,10 @@ class ElasticWave(Simulation):
|
|
|
614
675
|
minv = self.point_inverse_inertia(mat["gamma"], lin_index)
|
|
615
676
|
weight = minv * volume
|
|
616
677
|
if self.damping is not None:
|
|
678
|
+
# the kernel's beta, whose inertia leaves the norm out
|
|
617
679
|
node = lin_index % np.int32(self.comp_stride)
|
|
618
|
-
beta = 0.5 * minv *
|
|
619
|
-
weight = weight / (1.0 + beta)
|
|
680
|
+
beta = 0.5 * minv * self._point_norms(lin_index)
|
|
681
|
+
weight = weight / (1.0 + beta * mat["damping"].ravel()[node] * self.dt)
|
|
620
682
|
return (self.dtype(self.dt**2 * self.source_factor()) * weight).astype(
|
|
621
683
|
self.dtype
|
|
622
684
|
)
|
|
@@ -655,12 +717,12 @@ class ElasticWave(Simulation):
|
|
|
655
717
|
gamma = grads["design"]
|
|
656
718
|
g_mass = cp.zeros(self.Nx_padded, dtype=self.dtype)
|
|
657
719
|
for c in range(self.ncomp):
|
|
658
|
-
density = grads["mass"][c] *
|
|
720
|
+
density = grads["mass"][c] * self.point_weights((c,)) * self.density
|
|
659
721
|
g_mass += point_average_adjoint(self, density, c)
|
|
660
722
|
# the normal density sits on the nodes, so its chain rule is the weight alone
|
|
661
|
-
g_stiff =
|
|
723
|
+
g_stiff = grads["normal"] * self.point_weights(())
|
|
662
724
|
for p, axes in enumerate(PAIRS[self.ndim][self.ndim :]):
|
|
663
|
-
density = grads["shear"][p] *
|
|
725
|
+
density = grads["shear"][p] * self.point_weights(axes)
|
|
664
726
|
g_stiff += pair_average_adjoint(self, density, gamma, axes)
|
|
665
727
|
return {"mass": g_mass, "stiff": g_stiff}
|
|
666
728
|
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
// STENCIL_RADIUS
|
|
6
6
|
// OP_COEFFS
|
|
7
7
|
// STAG_COEFFS
|
|
8
|
+
// NODE_CLOSURE, HALF_CLOSURE, NODE_SPAN, HALF_SPAN, NODE_ROWS_C, HALF_ROWS_C
|
|
9
|
+
// DIRICHLET_FACES (default 0)
|
|
8
10
|
|
|
9
11
|
#ifdef USE_FLOAT
|
|
10
12
|
typedef float real_t;
|
|
@@ -12,8 +14,8 @@ typedef float real_t;
|
|
|
12
14
|
#define FMA_RN __fmaf_rn
|
|
13
15
|
#else
|
|
14
16
|
typedef double real_t;
|
|
15
|
-
#define MUL_RN
|
|
16
|
-
#define FMA_RN
|
|
17
|
+
#define MUL_RN __dmul_rn // pinned too, so code around it rounds nothing anew
|
|
18
|
+
#define FMA_RN __fma_rn
|
|
17
19
|
#endif
|
|
18
20
|
|
|
19
21
|
// ----------------------------- finite difference helpers
|
|
@@ -23,18 +25,105 @@ typedef double real_t;
|
|
|
23
25
|
|
|
24
26
|
#if STENCIL_RADIUS == 1
|
|
25
27
|
#define OP_W(r, k) ((real_t)1)
|
|
26
|
-
#define CLOSURE(a, N) 1
|
|
27
28
|
#define SG_W(r, k) ((real_t)1)
|
|
28
29
|
#else
|
|
29
30
|
// cache
|
|
30
31
|
__constant__ real_t OP_C[STENCIL_RADIUS][STENCIL_RADIUS] = OP_COEFFS;
|
|
31
32
|
#define OP_W(r, k) OP_C[(r) - 1][(k) - 1] // 1-indexed adjustment
|
|
32
|
-
// grade radius down towards wall so the stencil never reaches past ghost nodes
|
|
33
|
-
#define CLOSURE(a, N) min(STENCIL_RADIUS, min(a, (N) - 1 - (a)))
|
|
34
33
|
__constant__ real_t SG_C[STENCIL_RADIUS][STENCIL_RADIUS] = STAG_COEFFS;
|
|
35
34
|
#define SG_W(r, k) SG_C[(r) - 1][(k) - 1] // 1-indexed adjustment
|
|
36
35
|
#endif
|
|
37
36
|
|
|
37
|
+
// ---------------------------------- mirrored taps
|
|
38
|
+
#ifndef DIRICHLET_FACES
|
|
39
|
+
#define DIRICHLET_FACES 0
|
|
40
|
+
#endif
|
|
41
|
+
|
|
42
|
+
// whether node a is the wall node of a face of `odd` (bit 0 low, bit 1 high)
|
|
43
|
+
__device__ __forceinline__ bool held_wall(const int a, const int N,
|
|
44
|
+
const int odd) {
|
|
45
|
+
return (odd & 1 && a == 1) || (odd & 2 && a == N - 2);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// node b of an axis of N nodes, past the stored ghost its mirror about the
|
|
49
|
+
// wall node, odd past a face of `odd`
|
|
50
|
+
__device__ __forceinline__ int mirror_node(const int b, const int N,
|
|
51
|
+
const int odd, int &sign) {
|
|
52
|
+
sign = (b < 0 && odd & 1) || (b > N - 1 && odd & 2) ? -1 : 1;
|
|
53
|
+
return b < 0 ? 2 - b : b > N - 1 ? 2 * (N - 2) - b : b;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// a value read off a mirror with its sign
|
|
57
|
+
__device__ __forceinline__ real_t signed_value(const real_t v, const int sign) {
|
|
58
|
+
return sign > 0 ? v : sign < 0 ? -v : (real_t)0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// u at node a + j of the line through idx, read through mirror_node
|
|
62
|
+
__device__ __forceinline__ real_t mirrored(const real_t *__restrict__ u,
|
|
63
|
+
const int idx, const int s,
|
|
64
|
+
const int a, const int N,
|
|
65
|
+
const int odd, const int j) {
|
|
66
|
+
int sign;
|
|
67
|
+
const int b = mirror_node(a + j, N, odd, sign);
|
|
68
|
+
return signed_value(u[idx + (b - a) * s], sign);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// node b past the wall node its image, for a field no refill keeps beyond it:
|
|
72
|
+
// odd across a face of `odd`
|
|
73
|
+
__device__ __forceinline__ int node_image(const int b, const int N,
|
|
74
|
+
const int odd, int &sign) {
|
|
75
|
+
sign = (b < 1 && odd & 1) || (b > N - 2 && odd & 2) ? -1 : 1;
|
|
76
|
+
return b < 1 ? 2 - b : b > N - 2 ? 2 * (N - 2) - b : b;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// half point b, half a node up from node b, past the wall its image
|
|
80
|
+
__device__ __forceinline__ int half_image(const int b, const int N,
|
|
81
|
+
const int odd, int &sign) {
|
|
82
|
+
sign = (b < 1 && odd & 1) || (b > N - 3 && odd & 2) ? -1 : 1;
|
|
83
|
+
return b < 1 ? 1 - b : b > N - 3 ? 2 * N - 5 - b : b;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// a node-centred field at a + j of the line through idx, through node_image
|
|
87
|
+
__device__ __forceinline__ real_t node_imaged(const real_t *__restrict__ u,
|
|
88
|
+
const int idx, const int s,
|
|
89
|
+
const int a, const int N,
|
|
90
|
+
const int odd, const int j) {
|
|
91
|
+
int sign;
|
|
92
|
+
const int b = node_image(a + j, N, odd, sign);
|
|
93
|
+
return signed_value(u[idx + (b - a) * s], sign);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// a half-point field at a + j of the line through idx, through half_image
|
|
97
|
+
__device__ __forceinline__ real_t half_imaged(const real_t *__restrict__ u,
|
|
98
|
+
const int idx, const int s,
|
|
99
|
+
const int a, const int N,
|
|
100
|
+
const int odd, const int j) {
|
|
101
|
+
int sign;
|
|
102
|
+
const int b = half_image(a + j, N, odd, sign);
|
|
103
|
+
return signed_value(u[idx + (b - a) * s], sign);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// t[j - first] = a field at a + j, j = first .. first + 2R - 1, read in place
|
|
107
|
+
// where every tap lies on stored points `lo .. hi`, else through its image
|
|
108
|
+
template <bool HALF>
|
|
109
|
+
__device__ __forceinline__ void staged_taps(real_t *t,
|
|
110
|
+
const real_t *__restrict__ u,
|
|
111
|
+
const int idx, const int s,
|
|
112
|
+
const int a, const int N,
|
|
113
|
+
const int odd, const int first,
|
|
114
|
+
const int lo, const int hi) {
|
|
115
|
+
if (a + first >= lo && a + first + 2 * STENCIL_RADIUS - 1 <= hi) {
|
|
116
|
+
#pragma unroll
|
|
117
|
+
for (int j = 0; j < 2 * STENCIL_RADIUS; ++j)
|
|
118
|
+
t[j] = u[idx + (first + j) * s];
|
|
119
|
+
} else {
|
|
120
|
+
#pragma unroll
|
|
121
|
+
for (int j = 0; j < 2 * STENCIL_RADIUS; ++j)
|
|
122
|
+
t[j] = HALF ? half_imaged(u, idx, s, a, N, odd, first + j)
|
|
123
|
+
: node_imaged(u, idx, s, a, N, odd, first + j);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
38
127
|
// --------------------------------- staggered lattice
|
|
39
128
|
#define NPAIRS (NDIM * (NDIM - 1) / 2)
|
|
40
129
|
#define NVOIGT (NDIM + NPAIRS)
|
|
@@ -44,11 +133,6 @@ __constant__ real_t SG_C[STENCIL_RADIUS][STENCIL_RADIUS] = STAG_COEFFS;
|
|
|
44
133
|
#define PAIR_ROW(k, l) 2
|
|
45
134
|
#endif
|
|
46
135
|
|
|
47
|
-
// radius of the node-centred derivative of a staggered field, zero on the wall
|
|
48
|
-
__device__ __forceinline__ int rad_node(const int a, const int N) {
|
|
49
|
-
return min(STENCIL_RADIUS, min(a - 1, N - 2 - a));
|
|
50
|
-
}
|
|
51
|
-
|
|
52
136
|
// radius of a half-point derivative, whose taps may sit on the wall
|
|
53
137
|
__device__ __forceinline__ int rad_half(const int a, const int N) {
|
|
54
138
|
return min(STENCIL_RADIUS, min(a, N - 2 - a));
|
|
@@ -65,6 +149,154 @@ __device__ __forceinline__ bool clamped_face(const int clamped, const int d,
|
|
|
65
149
|
return (clamped >> (2 * d + side)) & 1;
|
|
66
150
|
}
|
|
67
151
|
|
|
152
|
+
// ---------------------------------- staggered closure
|
|
153
|
+
#ifdef NODE_CLOSURE
|
|
154
|
+
// the low wall's rows of the staggered SBP pair, row 0 the wall's; the high
|
|
155
|
+
// wall reads them mirrored and negated
|
|
156
|
+
__device__ const real_t NODE_ROWS[NODE_CLOSURE][NODE_SPAN] = NODE_ROWS_C;
|
|
157
|
+
__device__ const real_t HALF_ROWS[HALF_CLOSURE][HALF_SPAN] = HALF_ROWS_C;
|
|
158
|
+
#define CLOSURE_BAND \
|
|
159
|
+
(NODE_CLOSURE > HALF_CLOSURE ? NODE_CLOSURE : HALF_CLOSURE)
|
|
160
|
+
|
|
161
|
+
// the axes along which a node lies past both walls' closure bands, a bit each
|
|
162
|
+
__device__ __forceinline__ int deep_axes(const int *A, const int *NN) {
|
|
163
|
+
int deep = 0;
|
|
164
|
+
#pragma unroll
|
|
165
|
+
for (int d = 0; d < NDIM; ++d)
|
|
166
|
+
deep |= (A[d] > CLOSURE_BAND && A[d] < NN[d] - 2 - CLOSURE_BAND) << d;
|
|
167
|
+
return deep;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// every axis deep, or every one but the fast axis
|
|
171
|
+
#define DEEP_ALL ((1 << NDIM) - 1)
|
|
172
|
+
#define DEEP_SLOW (DEEP_ALL >> 1)
|
|
173
|
+
|
|
174
|
+
// the deep axes the warp's active lanes share, as DEEP_ALL, DEEP_SLOW or none,
|
|
175
|
+
// so the warp runs one path
|
|
176
|
+
__device__ __forceinline__ int warp_deep(const int *A, const int *NN) {
|
|
177
|
+
const unsigned lanes = __activemask();
|
|
178
|
+
const int deep = deep_axes(A, NN);
|
|
179
|
+
if (__all_sync(lanes, deep == DEEP_ALL))
|
|
180
|
+
return DEEP_ALL;
|
|
181
|
+
return __all_sync(lanes, (deep & DEEP_SLOW) == DEEP_SLOW) ? DEEP_SLOW : 0;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// f with the template mask of the deep axes a warp_deep result names
|
|
185
|
+
#define BY_DEEP(deep, f, ...) \
|
|
186
|
+
((deep) == DEEP_ALL ? f<DEEP_ALL>(__VA_ARGS__) \
|
|
187
|
+
: (deep) == DEEP_SLOW ? f<DEEP_SLOW>(__VA_ARGS__) \
|
|
188
|
+
: f(__VA_ARGS__))
|
|
189
|
+
|
|
190
|
+
// a line through a field: u(j) the value j points along the axis from idx
|
|
191
|
+
struct Line {
|
|
192
|
+
const real_t *__restrict__ p;
|
|
193
|
+
int idx, s;
|
|
194
|
+
__device__ real_t operator()(const int j) const { return p[idx + j * s]; }
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// the closure rows off either wall
|
|
198
|
+
template <class L>
|
|
199
|
+
__device__ __forceinline__ real_t node_rows(const L &u, const int lo,
|
|
200
|
+
const int hi) {
|
|
201
|
+
real_t acc = (real_t)0;
|
|
202
|
+
if (lo < NODE_CLOSURE) {
|
|
203
|
+
#pragma unroll
|
|
204
|
+
for (int m = 0; m < NODE_SPAN; ++m)
|
|
205
|
+
acc += NODE_ROWS[lo][m] * u(m - lo);
|
|
206
|
+
} else {
|
|
207
|
+
#pragma unroll
|
|
208
|
+
for (int m = 0; m < NODE_SPAN; ++m)
|
|
209
|
+
acc -= NODE_ROWS[hi][m] * u(hi - 1 - m);
|
|
210
|
+
}
|
|
211
|
+
return acc;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
template <class L>
|
|
215
|
+
__device__ __forceinline__ real_t half_rows(const L &u, const int lo,
|
|
216
|
+
const int hi) {
|
|
217
|
+
real_t acc = (real_t)0;
|
|
218
|
+
if (lo < HALF_CLOSURE) {
|
|
219
|
+
#pragma unroll
|
|
220
|
+
for (int i = 0; i < HALF_SPAN; ++i)
|
|
221
|
+
acc += HALF_ROWS[lo][i] * u(i - lo);
|
|
222
|
+
} else {
|
|
223
|
+
#pragma unroll
|
|
224
|
+
for (int i = 0; i < HALF_SPAN; ++i)
|
|
225
|
+
acc -= HALF_ROWS[hi][i] * u(hi + 1 - i);
|
|
226
|
+
}
|
|
227
|
+
return acc;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// d/dx at node a of a half-point field along a line; past a wall it reads the
|
|
231
|
+
// half-grid wall point as zero; deep (a constant where it is set) skips the
|
|
232
|
+
// closure rows
|
|
233
|
+
template <class L>
|
|
234
|
+
__device__ __forceinline__ real_t node_derivative(const L &u, const int a,
|
|
235
|
+
const int N,
|
|
236
|
+
const bool deep = false) {
|
|
237
|
+
const int lo = a - 1, hi = N - 2 - a;
|
|
238
|
+
if (!deep && (lo < NODE_CLOSURE || hi < NODE_CLOSURE))
|
|
239
|
+
return node_rows(u, lo, hi);
|
|
240
|
+
real_t acc = (real_t)0;
|
|
241
|
+
#pragma unroll
|
|
242
|
+
for (int k = 1; k <= STENCIL_RADIUS; ++k)
|
|
243
|
+
acc += SG_W(STENCIL_RADIUS, k) * (u(k - 1) - u(-k));
|
|
244
|
+
return acc;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// d/dx at the half point a (a + 1/2) of a nodal field along a line
|
|
248
|
+
template <class L>
|
|
249
|
+
__device__ __forceinline__ real_t half_derivative(const L &u, const int a,
|
|
250
|
+
const int N,
|
|
251
|
+
const bool deep = false) {
|
|
252
|
+
const int lo = a - 1, hi = N - 3 - a;
|
|
253
|
+
if (!deep && (lo < HALF_CLOSURE || hi < HALF_CLOSURE))
|
|
254
|
+
return half_rows(u, lo, hi);
|
|
255
|
+
real_t acc = (real_t)0;
|
|
256
|
+
#pragma unroll
|
|
257
|
+
for (int k = 1; k <= STENCIL_RADIUS; ++k)
|
|
258
|
+
acc += SG_W(STENCIL_RADIUS, k) * (u(k) - u(1 - k));
|
|
259
|
+
return acc;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// the divergence of a nodal stress at the half point a: half_derivative's rows,
|
|
263
|
+
// which are minus node_derivative's transpose in the norms, so the norms cancel
|
|
264
|
+
// against the inertia's; inside, the taps in the order the deep path rounds
|
|
265
|
+
template <class L>
|
|
266
|
+
__device__ __forceinline__ real_t node_divergence(const L &sg, const int a,
|
|
267
|
+
const int N,
|
|
268
|
+
const bool deep = false) {
|
|
269
|
+
const int lo = a - 1, hi = N - 3 - a;
|
|
270
|
+
if (!deep && (lo < HALF_CLOSURE || hi < HALF_CLOSURE))
|
|
271
|
+
return half_rows(sg, lo, hi);
|
|
272
|
+
real_t acc = (real_t)0;
|
|
273
|
+
#pragma unroll
|
|
274
|
+
for (int k = 1; k <= STENCIL_RADIUS; ++k) {
|
|
275
|
+
acc += SG_W(STENCIL_RADIUS, k) * sg(k);
|
|
276
|
+
acc -= SG_W(STENCIL_RADIUS, k) * sg(1 - k);
|
|
277
|
+
}
|
|
278
|
+
return acc;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// the divergence of a half-point stress at node a: node_derivative's rows, the
|
|
282
|
+
// stress at the wall point never read
|
|
283
|
+
template <class L>
|
|
284
|
+
__device__ __forceinline__ real_t half_divergence(const L &sg, const int a,
|
|
285
|
+
const int N,
|
|
286
|
+
const bool deep = false) {
|
|
287
|
+
const int lo = a - 1, hi = N - 2 - a;
|
|
288
|
+
if (!deep && (lo < NODE_CLOSURE || hi < NODE_CLOSURE))
|
|
289
|
+
return node_rows(sg, lo, hi);
|
|
290
|
+
real_t acc = (real_t)0;
|
|
291
|
+
#pragma unroll
|
|
292
|
+
for (int k = 1; k <= STENCIL_RADIUS; ++k) {
|
|
293
|
+
acc += SG_W(STENCIL_RADIUS, k) * sg(k - 1);
|
|
294
|
+
acc -= SG_W(STENCIL_RADIUS, k) * sg(-k);
|
|
295
|
+
}
|
|
296
|
+
return acc;
|
|
297
|
+
}
|
|
298
|
+
#endif
|
|
299
|
+
|
|
68
300
|
// --------------------------------------- pml slabs
|
|
69
301
|
#if NDIM == 1
|
|
70
302
|
#define PML_PARAMS const int N0
|
|
@@ -140,6 +372,20 @@ __device__ __forceinline__ int slab_index(const int a, const int off,
|
|
|
140
372
|
return d < NDIM - 1 ? row * plane + off : off * (lo + hi) + row;
|
|
141
373
|
}
|
|
142
374
|
|
|
375
|
+
// whether the node at idx is the wall node of a face DIRICHLET_FACES names
|
|
376
|
+
__device__ __forceinline__ bool held_node(const int idx, const int *n,
|
|
377
|
+
const int *s) {
|
|
378
|
+
bool held = false;
|
|
379
|
+
int rest = idx;
|
|
380
|
+
#pragma unroll
|
|
381
|
+
for (int e = 0; e < NDIM; ++e) {
|
|
382
|
+
const int ae = rest / s[e];
|
|
383
|
+
rest -= ae * s[e];
|
|
384
|
+
held |= held_wall(ae, n[e], DIRICHLET_FACES >> 2 * e);
|
|
385
|
+
}
|
|
386
|
+
return held;
|
|
387
|
+
}
|
|
388
|
+
|
|
143
389
|
// ------------------------------- pml memory accessors
|
|
144
390
|
// component k of a field j nodes along the axis from a node
|
|
145
391
|
struct Field {
|