cuwave 0.7.0__tar.gz → 0.7.1__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.0 → cuwave-0.7.1}/PKG-INFO +1 -1
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/anisotropic.py +110 -2
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/boundary.py +1 -1
- cuwave-0.7.1/cuwave/elastic.py +730 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/kernels/anisotropic.cu +192 -21
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/kernels/common.cuh +10 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/kernels/elastic.cu +515 -10
- cuwave-0.7.1/cuwave/kernels/maxwell.cu +700 -0
- cuwave-0.7.1/cuwave/kernels/scalar.cu +1269 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/maxwell.py +270 -15
- cuwave-0.7.1/cuwave/scalar.py +939 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/sensitivity.py +23 -3
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/wave.py +222 -7
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave.egg-info/PKG-INFO +1 -1
- {cuwave-0.7.0 → cuwave-0.7.1}/pyproject.toml +1 -1
- cuwave-0.7.0/cuwave/elastic.py +0 -387
- cuwave-0.7.0/cuwave/kernels/maxwell.cu +0 -367
- cuwave-0.7.0/cuwave/kernels/scalar.cu +0 -402
- cuwave-0.7.0/cuwave/scalar.py +0 -428
- {cuwave-0.7.0 → cuwave-0.7.1}/LICENSE +0 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/README.md +0 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/__init__.py +0 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/decomposition.py +0 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/evals.py +0 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/geometry.py +0 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/kernels/__init__.py +0 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/kernels/anisotropic_sensitivity.cu +0 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/kernels/elastic_sensitivity.cu +0 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/kernels/maxwell_sensitivity.cu +0 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/kernels/scalar_sensitivity.cu +0 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/nn.py +0 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/optimization.py +0 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/postprocessing.py +0 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/regularization.py +0 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/signals.py +0 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/stencils.py +0 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave/utils.py +0 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave.egg-info/SOURCES.txt +0 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave.egg-info/dependency_links.txt +0 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave.egg-info/requires.txt +0 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/cuwave.egg-info/top_level.txt +0 -0
- {cuwave-0.7.0 → cuwave-0.7.1}/setup.cfg +0 -0
|
@@ -13,7 +13,9 @@ general anisotropy.
|
|
|
13
13
|
|
|
14
14
|
from __future__ import annotations
|
|
15
15
|
|
|
16
|
+
import hashlib
|
|
16
17
|
import itertools
|
|
18
|
+
import math
|
|
17
19
|
from collections.abc import Callable
|
|
18
20
|
from dataclasses import dataclass
|
|
19
21
|
from pathlib import Path
|
|
@@ -28,15 +30,29 @@ from .elastic import voigt
|
|
|
28
30
|
from .wave import (
|
|
29
31
|
PAIRS,
|
|
30
32
|
PML_THREADS,
|
|
33
|
+
Blocked,
|
|
34
|
+
LaunchSlots,
|
|
31
35
|
Simulation,
|
|
36
|
+
Source,
|
|
32
37
|
apply_cell_weights,
|
|
33
38
|
axis_geometry,
|
|
39
|
+
blocked_launches,
|
|
40
|
+
compile_kernels,
|
|
41
|
+
covering_hits,
|
|
42
|
+
flatten_indices,
|
|
34
43
|
grid_block,
|
|
44
|
+
owner_hits,
|
|
35
45
|
slab_grid,
|
|
46
|
+
tile_counts,
|
|
47
|
+
tile_lists,
|
|
36
48
|
)
|
|
37
49
|
|
|
38
50
|
KERNEL_PATH = Path(__file__).parent / "kernels" / "anisotropic.cu"
|
|
39
51
|
SENSITIVITY_PATH = Path(__file__).parent / "kernels" / "anisotropic_sensitivity.cu"
|
|
52
|
+
# candidates (tile, thread rows, steps) of the 2D blocked step: the first with the fewest waves
|
|
53
|
+
BLOCKED = (((32, 32), 32, 6), ((32, 32), 32, 4), ((32, 64), 16, 6))
|
|
54
|
+
BLOCKED_WAVES = 1 # the waves of tiles the blocked step pays to
|
|
55
|
+
LARGE_BLOCKED = ((32, 128), 8, 4) # for fields past twice L2
|
|
40
56
|
|
|
41
57
|
|
|
42
58
|
# -------------------------------------- helpers --------------------------------------
|
|
@@ -169,6 +185,13 @@ class AnisotropicElasticWave(Simulation):
|
|
|
169
185
|
"""One displacement component per axis."""
|
|
170
186
|
return self.ndim
|
|
171
187
|
|
|
188
|
+
@property
|
|
189
|
+
def compile_flags(self) -> tuple[str, ...]:
|
|
190
|
+
"""The shared flags and `-DSTENCIL_KEY`, so modules alike but for the table never share `STENCIL`."""
|
|
191
|
+
table = np.append(np.asarray(self.dx, dtype=float), self.stiffness_matrix())
|
|
192
|
+
key = hashlib.sha256(table.tobytes()).hexdigest()[:16]
|
|
193
|
+
return super().compile_flags + (f"-DSTENCIL_KEY={key}",)
|
|
194
|
+
|
|
172
195
|
def __post_init__(self) -> None:
|
|
173
196
|
"""Validate the material, the 2D plane assumption and the order, then derive the grid."""
|
|
174
197
|
super().__post_init__()
|
|
@@ -259,8 +282,8 @@ class AnisotropicElasticWave(Simulation):
|
|
|
259
282
|
return mat
|
|
260
283
|
|
|
261
284
|
def step_kernel_args(self, mat: dict) -> tuple:
|
|
262
|
-
"""Material
|
|
263
|
-
args = (mat["minv"], mat["cell"]
|
|
285
|
+
"""Material and component stride for the step kernel, whose table sits in its constant memory."""
|
|
286
|
+
args = (mat["minv"], mat["cell"])
|
|
264
287
|
if self.damping is not None:
|
|
265
288
|
args += (mat["damping"], self.dtype(self.dt))
|
|
266
289
|
return args + (np.int32(self.comp_stride),)
|
|
@@ -287,6 +310,89 @@ class AnisotropicElasticWave(Simulation):
|
|
|
287
310
|
passes = ("pml_cell", "pml_force")
|
|
288
311
|
return self._define_pml(kernels, mat, passes)
|
|
289
312
|
|
|
313
|
+
def define_blocked_step(
|
|
314
|
+
self, mat: dict, source: Source, sensors: cpt.NDArray[cp.int32] | None
|
|
315
|
+
) -> Blocked | None:
|
|
316
|
+
"""The 2D step advancing several steps a launch, excitation and record included, else None.
|
|
317
|
+
|
|
318
|
+
Each thread block loads a tile of both components with a halo of a node a step,
|
|
319
|
+
gathers the cells and steps the tile in shared memory and writes back its core:
|
|
320
|
+
bitwise the step, the excitation and the record launched one by one. Taken where
|
|
321
|
+
launches bound the step (the candidate of `BLOCKED` with the fewest waves of
|
|
322
|
+
tiles, while those stay within `BLOCKED_WAVES`) or DRAM does (`LARGE_BLOCKED`,
|
|
323
|
+
fields past twice L2); else None, as in 1D and 3D, in float64, with a pml, a
|
|
324
|
+
source or a sensor off the interior, or two sources or sensors on one point.
|
|
325
|
+
"""
|
|
326
|
+
if self.ndim != 2 or self.precision != "float32" or self.pml is not None:
|
|
327
|
+
return None
|
|
328
|
+
if min(self.Nx) < 4:
|
|
329
|
+
return None
|
|
330
|
+
|
|
331
|
+
# launches bound a grid of a wave of tiles, DRAM one whose fields pass L2
|
|
332
|
+
device = cp.cuda.Device().attributes
|
|
333
|
+
sms = device["MultiProcessorCount"]
|
|
334
|
+
waves = [
|
|
335
|
+
-(-math.prod(tile_counts(self.Nx, b[0], b[2])) // sms) for b in BLOCKED
|
|
336
|
+
]
|
|
337
|
+
fields = (3 * self.ndim + 2) * math.prod(map(int, self.Nx)) * 4
|
|
338
|
+
if min(waves) <= BLOCKED_WAVES:
|
|
339
|
+
tile, rows, steps = BLOCKED[waves.index(min(waves))]
|
|
340
|
+
elif fields > 2 * device["L2CacheSize"]:
|
|
341
|
+
tile, rows, steps = LARGE_BLOCKED
|
|
342
|
+
else:
|
|
343
|
+
return None
|
|
344
|
+
core = [t - 2 * steps for t in tile]
|
|
345
|
+
cores = tile_counts(self.Nx, tile, steps)
|
|
346
|
+
|
|
347
|
+
# sources and sensors in the interior, none twice
|
|
348
|
+
sources = flatten_indices(self, source.position)
|
|
349
|
+
targets = flatten_indices(self, sensors) if sensors is not None else sources[:0]
|
|
350
|
+
upper = np.asarray(self.Nx)[:, None] - 2
|
|
351
|
+
points, layers = [], []
|
|
352
|
+
for index in (sources, targets):
|
|
353
|
+
flat = index.get()
|
|
354
|
+
layer, node = np.divmod(flat, self.comp_stride)
|
|
355
|
+
point = np.stack(np.divmod(node, self.Nx_padded[1]))
|
|
356
|
+
# in the interior, each point once, the index fitting an entry's half
|
|
357
|
+
if not ((point >= 1) & (point <= upper)).all():
|
|
358
|
+
return None
|
|
359
|
+
if len(flat) >= 2**16 or len(np.unique(flat)) != len(flat):
|
|
360
|
+
return None
|
|
361
|
+
points.append(point)
|
|
362
|
+
layers.append(layer)
|
|
363
|
+
|
|
364
|
+
# a tile's sources wherever its nodes reach, its sensors where its core does
|
|
365
|
+
hits = [
|
|
366
|
+
covering_hits(points[0], tile, core, steps, cores),
|
|
367
|
+
owner_hits(points[1], points[1], core),
|
|
368
|
+
]
|
|
369
|
+
lists = []
|
|
370
|
+
for hit, layer in zip(hits, layers):
|
|
371
|
+
lists += tile_lists(hit, tile, core, steps, cores, layer)
|
|
372
|
+
|
|
373
|
+
# the kernel, its stencil table copied in, u twice and the cell field
|
|
374
|
+
flags = (f"-DSTEPS={steps}", f"-DTILE0={tile[0]}", f"-DTILE1={tile[1]}")
|
|
375
|
+
kernels = compile_kernels(self, flags=(*flags, f"-DROWS={rows}"))
|
|
376
|
+
table = kernels.get_global("STENCIL")
|
|
377
|
+
cp.ndarray(mat["stencil"].shape, self.dtype, table)[...] = mat["stencil"]
|
|
378
|
+
kernel = kernels.get_function("blocked_kernel")
|
|
379
|
+
shared = 5 * (tile[0] + 2) * (tile[1] + 2) * 4
|
|
380
|
+
if shared > device["MaxSharedMemoryPerBlockOptin"]:
|
|
381
|
+
return None
|
|
382
|
+
kernel.max_dynamic_shared_size_bytes = shared
|
|
383
|
+
|
|
384
|
+
# the arguments, the pointers and the per-call values filled in per launch
|
|
385
|
+
weights = self.excitation_weights(mat, sources)
|
|
386
|
+
args = [None, None, None, None, mat["minv"], mat["cell"]]
|
|
387
|
+
if self.damping is not None:
|
|
388
|
+
args += [mat["damping"], self.dtype(self.dt)]
|
|
389
|
+
args += [None, weights, np.int32(len(sources)), None, np.int32(len(targets))]
|
|
390
|
+
args += [*lists, None]
|
|
391
|
+
slots = LaunchSlots(len(args) - 10, len(args) - 7, len(args) - 1, None)
|
|
392
|
+
args += [np.int32(self.comp_stride), *axis_geometry(self, self.step_factors())]
|
|
393
|
+
launches = [(kernel, tuple(cores[::-1]), None)]
|
|
394
|
+
return blocked_launches(steps, launches, (tile[1], rows), shared, args, slots)
|
|
395
|
+
|
|
290
396
|
def define_adjoint_step(
|
|
291
397
|
self, kernels: cp.RawModule, sens_kernels: cp.RawModule, mat: dict
|
|
292
398
|
) -> Callable:
|
|
@@ -296,6 +402,8 @@ class AnisotropicElasticWave(Simulation):
|
|
|
296
402
|
|
|
297
403
|
def _define_pml(self, kernels: cp.RawModule, mat: dict, passes: tuple) -> Callable:
|
|
298
404
|
"""The cell step, then each pass over every lined axis, all axes in one memory."""
|
|
405
|
+
table = kernels.get_global("STENCIL")
|
|
406
|
+
cp.ndarray(mat["stencil"].shape, self.dtype, table)[...] = mat["stencil"]
|
|
299
407
|
fd_step = super().define_step(kernels, mat)
|
|
300
408
|
if self.pml is None:
|
|
301
409
|
return fd_step
|
|
@@ -83,7 +83,7 @@ def faces_with(sim: Simulation, condition: BoundaryCondition) -> list[int]:
|
|
|
83
83
|
2 * d + side
|
|
84
84
|
for d, pair in enumerate(sim.boundary)
|
|
85
85
|
for side, marker in enumerate(pair)
|
|
86
|
-
if marker is
|
|
86
|
+
if marker == condition # a copied or unpickled condition is still the same
|
|
87
87
|
]
|
|
88
88
|
|
|
89
89
|
|