witwin-maxwell 0.3.0__py3-none-win_amd64.whl

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.
Files changed (107) hide show
  1. witwin/maxwell/__init__.py +164 -0
  2. witwin/maxwell/adapters/__init__.py +1 -0
  3. witwin/maxwell/adapters/gds.py +203 -0
  4. witwin/maxwell/adapters/tidy3d.py +1669 -0
  5. witwin/maxwell/adjoint_inputs.py +70 -0
  6. witwin/maxwell/compiler/__init__.py +11 -0
  7. witwin/maxwell/compiler/materials.py +1355 -0
  8. witwin/maxwell/compiler/monitors.py +164 -0
  9. witwin/maxwell/compiler/sources.py +222 -0
  10. witwin/maxwell/fdfd/__init__.py +3 -0
  11. witwin/maxwell/fdfd/adjoint/__init__.py +3 -0
  12. witwin/maxwell/fdfd/adjoint/bridge.py +180 -0
  13. witwin/maxwell/fdfd/ams.py +300 -0
  14. witwin/maxwell/fdfd/krylov.py +280 -0
  15. witwin/maxwell/fdfd/plotting.py +110 -0
  16. witwin/maxwell/fdfd/postprocess.py +33 -0
  17. witwin/maxwell/fdfd/solver.py +1057 -0
  18. witwin/maxwell/fdtd/__init__.py +4 -0
  19. witwin/maxwell/fdtd/adjoint/__init__.py +21 -0
  20. witwin/maxwell/fdtd/adjoint/bridge.py +746 -0
  21. witwin/maxwell/fdtd/adjoint/capabilities.py +29 -0
  22. witwin/maxwell/fdtd/adjoint/core.py +3614 -0
  23. witwin/maxwell/fdtd/adjoint/dispatch.py +684 -0
  24. witwin/maxwell/fdtd/adjoint/native.py +2561 -0
  25. witwin/maxwell/fdtd/adjoint/profiler.py +195 -0
  26. witwin/maxwell/fdtd/adjoint/reverse_common.py +207 -0
  27. witwin/maxwell/fdtd/adjoint/seeds.py +524 -0
  28. witwin/maxwell/fdtd/boundary/__init__.py +37 -0
  29. witwin/maxwell/fdtd/boundary/bloch.py +90 -0
  30. witwin/maxwell/fdtd/boundary/common.py +41 -0
  31. witwin/maxwell/fdtd/boundary/cpml.py +527 -0
  32. witwin/maxwell/fdtd/boundary/runtime.py +209 -0
  33. witwin/maxwell/fdtd/checkpoint.py +227 -0
  34. witwin/maxwell/fdtd/coords.py +32 -0
  35. witwin/maxwell/fdtd/cuda/__init__.py +11 -0
  36. witwin/maxwell/fdtd/cuda/backend.py +3532 -0
  37. witwin/maxwell/fdtd/cuda/build.py +292 -0
  38. witwin/maxwell/fdtd/cuda/extension.cpp +2053 -0
  39. witwin/maxwell/fdtd/cuda/kernels/adjoint.cu +5290 -0
  40. witwin/maxwell/fdtd/cuda/kernels/aniso.cu +1704 -0
  41. witwin/maxwell/fdtd/cuda/kernels/boundary.cu +424 -0
  42. witwin/maxwell/fdtd/cuda/kernels/common.cu +74 -0
  43. witwin/maxwell/fdtd/cuda/kernels/common.cuh +82 -0
  44. witwin/maxwell/fdtd/cuda/kernels/dispersive.cu +782 -0
  45. witwin/maxwell/fdtd/cuda/kernels/electric.cu +3613 -0
  46. witwin/maxwell/fdtd/cuda/kernels/magnetic.cu +781 -0
  47. witwin/maxwell/fdtd/cuda/kernels/observers.cu +347 -0
  48. witwin/maxwell/fdtd/cuda/kernels/projection.cu +211 -0
  49. witwin/maxwell/fdtd/cuda/kernels/sources.cu +1482 -0
  50. witwin/maxwell/fdtd/cuda/kernels/spectral.cu +297 -0
  51. witwin/maxwell/fdtd/cuda/launch.h +23 -0
  52. witwin/maxwell/fdtd/cuda/prebuilt/witwin_maxwell_fdtd_cuda.pyd +0 -0
  53. witwin/maxwell/fdtd/cuda/runtime/__init__.py +6 -0
  54. witwin/maxwell/fdtd/cuda/runtime/dispatcher.py +6 -0
  55. witwin/maxwell/fdtd/cuda/runtime/graph.py +33 -0
  56. witwin/maxwell/fdtd/cuda/tensors.h +35 -0
  57. witwin/maxwell/fdtd/dispersion.py +48 -0
  58. witwin/maxwell/fdtd/evaluation.py +290 -0
  59. witwin/maxwell/fdtd/excitation/__init__.py +27 -0
  60. witwin/maxwell/fdtd/excitation/injection.py +1102 -0
  61. witwin/maxwell/fdtd/excitation/modes.py +1675 -0
  62. witwin/maxwell/fdtd/excitation/spatial.py +469 -0
  63. witwin/maxwell/fdtd/excitation/temporal.py +510 -0
  64. witwin/maxwell/fdtd/excitation/tfsf_apply.py +264 -0
  65. witwin/maxwell/fdtd/excitation/tfsf_common.py +421 -0
  66. witwin/maxwell/fdtd/excitation/tfsf_specs.py +490 -0
  67. witwin/maxwell/fdtd/excitation/tfsf_state.py +778 -0
  68. witwin/maxwell/fdtd/io.py +31 -0
  69. witwin/maxwell/fdtd/material_pullback.py +371 -0
  70. witwin/maxwell/fdtd/meshing/__init__.py +3 -0
  71. witwin/maxwell/fdtd/meshing/autogrid.py +335 -0
  72. witwin/maxwell/fdtd/observers.py +1453 -0
  73. witwin/maxwell/fdtd/plotting.py +121 -0
  74. witwin/maxwell/fdtd/postprocess.py +205 -0
  75. witwin/maxwell/fdtd/runtime/__init__.py +119 -0
  76. witwin/maxwell/fdtd/runtime/initialization.py +236 -0
  77. witwin/maxwell/fdtd/runtime/materials.py +1563 -0
  78. witwin/maxwell/fdtd/runtime/module_cache.py +31 -0
  79. witwin/maxwell/fdtd/runtime/spectral.py +581 -0
  80. witwin/maxwell/fdtd/runtime/stepping.py +1987 -0
  81. witwin/maxwell/fdtd/solver.py +502 -0
  82. witwin/maxwell/geometry/__init__.py +5 -0
  83. witwin/maxwell/geometry/polyslab.py +299 -0
  84. witwin/maxwell/materials.py +3 -0
  85. witwin/maxwell/media.py +1467 -0
  86. witwin/maxwell/monitors.py +743 -0
  87. witwin/maxwell/ports.py +113 -0
  88. witwin/maxwell/postprocess/RCS.py +139 -0
  89. witwin/maxwell/postprocess/__init__.py +42 -0
  90. witwin/maxwell/postprocess/diffraction.py +219 -0
  91. witwin/maxwell/postprocess/directivity.py +152 -0
  92. witwin/maxwell/postprocess/emission.py +67 -0
  93. witwin/maxwell/postprocess/modal.py +324 -0
  94. witwin/maxwell/postprocess/nfft.py +266 -0
  95. witwin/maxwell/postprocess/scattering_parameters.py +358 -0
  96. witwin/maxwell/postprocess/stratton_chu.py +1193 -0
  97. witwin/maxwell/result.py +967 -0
  98. witwin/maxwell/scene.py +1338 -0
  99. witwin/maxwell/simulation.py +659 -0
  100. witwin/maxwell/sources.py +917 -0
  101. witwin/maxwell/visualization/__init__.py +28 -0
  102. witwin/maxwell/visualization/interactive.py +484 -0
  103. witwin/maxwell/visualization/plots.py +568 -0
  104. witwin_maxwell-0.3.0.dist-info/METADATA +184 -0
  105. witwin_maxwell-0.3.0.dist-info/RECORD +107 -0
  106. witwin_maxwell-0.3.0.dist-info/WHEEL +4 -0
  107. witwin_maxwell-0.3.0.dist-info/licenses/LICENSE +13 -0
@@ -0,0 +1,1704 @@
1
+ // Off-diagonal (full-tensor) anisotropic permittivity correction kernels.
2
+ //
3
+ // The base electric update already applies the diagonal entry of the per-edge
4
+ // inverse permittivity tensor through the standard curl coefficient. These
5
+ // kernels add the off-diagonal coupling terms
6
+ // E_i += coeff_ij * <curlH_j>_i
7
+ // where <curlH_j>_i is the four-neighbor average of the off-axis curl(H)
8
+ // component evaluated at its own Yee edges and collocated onto the target
9
+ // edge. Periodic axes wrap both the neighbor sites and the finite-difference
10
+ // stencils; on non-periodic axes, samples whose stencil would leave the grid
11
+ // are skipped (the off-diagonal coefficients vanish near PML/absorber faces by
12
+ // construction, so the fixed 1/4 weight stays exact where it matters).
13
+ //
14
+ // The kernels are launched only when the solver's full-anisotropy feature flag
15
+ // is set, so the base field-update path keeps its memory traffic untouched.
16
+
17
+ #include "../launch.h"
18
+ #include "../tensors.h"
19
+ #include "common.cuh"
20
+
21
+ #include <algorithm>
22
+ #include <utility>
23
+
24
+ namespace {
25
+
26
+ __global__ void capture_aniso_conduction_current_kernel(
27
+ int64_t total,
28
+ int64_t nx,
29
+ int64_t ny,
30
+ int64_t nz,
31
+ const float* __restrict__ sigma_x,
32
+ const float* __restrict__ sigma_y,
33
+ const float* __restrict__ sigma_z,
34
+ const float* __restrict__ ex,
35
+ const float* __restrict__ ey,
36
+ const float* __restrict__ ez,
37
+ float* __restrict__ jx,
38
+ float* __restrict__ jy,
39
+ float* __restrict__ jz) {
40
+ const int64_t linear = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
41
+ if (linear >= total) {
42
+ return;
43
+ }
44
+ if (linear < nx) {
45
+ jx[linear] = sigma_x[linear] * ex[linear];
46
+ }
47
+ if (linear < ny) {
48
+ jy[linear] = sigma_y[linear] * ey[linear];
49
+ }
50
+ if (linear < nz) {
51
+ jz[linear] = sigma_z[linear] * ez[linear];
52
+ }
53
+ }
54
+
55
+ dim3 aniso_block3d() {
56
+ return dim3(128, 2, 1);
57
+ }
58
+
59
+ dim3 aniso_grid3d(int64_t nx, int64_t ny, int64_t nz, dim3 block) {
60
+ return dim3(
61
+ static_cast<unsigned int>((nz + block.x - 1) / block.x),
62
+ static_cast<unsigned int>((ny + block.y - 1) / block.y),
63
+ static_cast<unsigned int>((nx + block.z - 1) / block.z));
64
+ }
65
+
66
+ // Wrap an edge-type coordinate (array length n_nodes - 1) that may be off by
67
+ // one period. Returns -1 when the sample must be skipped.
68
+ __device__ __forceinline__ int wrap_edge_coord(int coord, int n_nodes, bool periodic) {
69
+ const int count = n_nodes - 1;
70
+ if (coord >= 0 && coord < count) {
71
+ return coord;
72
+ }
73
+ if (!periodic) {
74
+ return -1;
75
+ }
76
+ return coord < 0 ? coord + count : coord - count;
77
+ }
78
+
79
+ // Map a node-type coordinate (array length n_nodes) onto its unique plane:
80
+ // on a periodic axis the last node duplicates node 0.
81
+ __device__ __forceinline__ int map_node_coord(int coord, int n_nodes, bool periodic) {
82
+ if (periodic && coord == n_nodes - 1) {
83
+ return 0;
84
+ }
85
+ return coord;
86
+ }
87
+
88
+ // Backward-difference edge pair (coord-1, coord) at a node-type coordinate.
89
+ // Returns false when the stencil leaves a non-periodic axis.
90
+ __device__ __forceinline__ bool backward_edge_pair(
91
+ int coord,
92
+ int n_nodes,
93
+ bool periodic,
94
+ int& low,
95
+ int& high) {
96
+ high = coord;
97
+ low = coord - 1;
98
+ if (high > n_nodes - 2) {
99
+ if (!periodic) {
100
+ return false;
101
+ }
102
+ high -= n_nodes - 1;
103
+ }
104
+ if (low < 0) {
105
+ if (!periodic) {
106
+ return false;
107
+ }
108
+ low += n_nodes - 1;
109
+ }
110
+ return true;
111
+ }
112
+
113
+ // curl(H)_x = dHz/dy - dHy/dz at the Ex edge (sx edge-x, sy node-y, sz node-z).
114
+ __device__ __forceinline__ bool curl_h_x_at(
115
+ const float* __restrict__ hy,
116
+ const float* __restrict__ hz,
117
+ int sx,
118
+ int sy,
119
+ int sz,
120
+ int nx_nodes,
121
+ int ny_nodes,
122
+ int nz_nodes,
123
+ bool periodic_x,
124
+ bool periodic_y,
125
+ bool periodic_z,
126
+ const float* __restrict__ inv_dy,
127
+ const float* __restrict__ inv_dz,
128
+ float& value) {
129
+ sx = wrap_edge_coord(sx, nx_nodes, periodic_x);
130
+ if (sx < 0) {
131
+ return false;
132
+ }
133
+ sy = map_node_coord(sy, ny_nodes, periodic_y);
134
+ sz = map_node_coord(sz, nz_nodes, periodic_z);
135
+ int y_low;
136
+ int y_high;
137
+ int z_low;
138
+ int z_high;
139
+ if (!backward_edge_pair(sy, ny_nodes, periodic_y, y_low, y_high) ||
140
+ !backward_edge_pair(sz, nz_nodes, periodic_z, z_low, z_high)) {
141
+ return false;
142
+ }
143
+ const float d_y = (hz[offset3d(sx, y_high, sz, ny_nodes - 1, nz_nodes)] -
144
+ hz[offset3d(sx, y_low, sz, ny_nodes - 1, nz_nodes)]) * inv_dy[sy];
145
+ const float d_z = (hy[offset3d(sx, sy, z_high, ny_nodes, nz_nodes - 1)] -
146
+ hy[offset3d(sx, sy, z_low, ny_nodes, nz_nodes - 1)]) * inv_dz[sz];
147
+ value = d_y - d_z;
148
+ return true;
149
+ }
150
+
151
+ // curl(H)_y = dHx/dz - dHz/dx at the Ey edge (sx node-x, sy edge-y, sz node-z).
152
+ __device__ __forceinline__ bool curl_h_y_at(
153
+ const float* __restrict__ hx,
154
+ const float* __restrict__ hz,
155
+ int sx,
156
+ int sy,
157
+ int sz,
158
+ int nx_nodes,
159
+ int ny_nodes,
160
+ int nz_nodes,
161
+ bool periodic_x,
162
+ bool periodic_y,
163
+ bool periodic_z,
164
+ const float* __restrict__ inv_dx,
165
+ const float* __restrict__ inv_dz,
166
+ float& value) {
167
+ sy = wrap_edge_coord(sy, ny_nodes, periodic_y);
168
+ if (sy < 0) {
169
+ return false;
170
+ }
171
+ sx = map_node_coord(sx, nx_nodes, periodic_x);
172
+ sz = map_node_coord(sz, nz_nodes, periodic_z);
173
+ int x_low;
174
+ int x_high;
175
+ int z_low;
176
+ int z_high;
177
+ if (!backward_edge_pair(sx, nx_nodes, periodic_x, x_low, x_high) ||
178
+ !backward_edge_pair(sz, nz_nodes, periodic_z, z_low, z_high)) {
179
+ return false;
180
+ }
181
+ const float d_z = (hx[offset3d(sx, sy, z_high, ny_nodes - 1, nz_nodes - 1)] -
182
+ hx[offset3d(sx, sy, z_low, ny_nodes - 1, nz_nodes - 1)]) * inv_dz[sz];
183
+ const float d_x = (hz[offset3d(x_high, sy, sz, ny_nodes - 1, nz_nodes)] -
184
+ hz[offset3d(x_low, sy, sz, ny_nodes - 1, nz_nodes)]) * inv_dx[sx];
185
+ value = d_z - d_x;
186
+ return true;
187
+ }
188
+
189
+ // curl(H)_z = dHy/dx - dHx/dy at the Ez edge (sx node-x, sy node-y, sz edge-z).
190
+ __device__ __forceinline__ bool curl_h_z_at(
191
+ const float* __restrict__ hx,
192
+ const float* __restrict__ hy,
193
+ int sx,
194
+ int sy,
195
+ int sz,
196
+ int nx_nodes,
197
+ int ny_nodes,
198
+ int nz_nodes,
199
+ bool periodic_x,
200
+ bool periodic_y,
201
+ bool periodic_z,
202
+ const float* __restrict__ inv_dx,
203
+ const float* __restrict__ inv_dy,
204
+ float& value) {
205
+ sz = wrap_edge_coord(sz, nz_nodes, periodic_z);
206
+ if (sz < 0) {
207
+ return false;
208
+ }
209
+ sx = map_node_coord(sx, nx_nodes, periodic_x);
210
+ sy = map_node_coord(sy, ny_nodes, periodic_y);
211
+ int x_low;
212
+ int x_high;
213
+ int y_low;
214
+ int y_high;
215
+ if (!backward_edge_pair(sx, nx_nodes, periodic_x, x_low, x_high) ||
216
+ !backward_edge_pair(sy, ny_nodes, periodic_y, y_low, y_high)) {
217
+ return false;
218
+ }
219
+ const float d_x = (hy[offset3d(x_high, sy, sz, ny_nodes, nz_nodes - 1)] -
220
+ hy[offset3d(x_low, sy, sz, ny_nodes, nz_nodes - 1)]) * inv_dx[sx];
221
+ const float d_y = (hx[offset3d(sx, y_high, sz, ny_nodes - 1, nz_nodes - 1)] -
222
+ hx[offset3d(sx, y_low, sz, ny_nodes - 1, nz_nodes - 1)]) * inv_dy[sy];
223
+ value = d_x - d_y;
224
+ return true;
225
+ }
226
+
227
+ // --- Split (per-direction) curl(H) parts for the CPML off-diagonal update ---
228
+ //
229
+ // The CPML off-diagonal correction coordinate-stretches each spatial derivative
230
+ // separately, so it needs the two directional derivatives that make up an
231
+ // off-axis curl(H) component instead of their difference. These helpers mirror
232
+ // the curl_h_*_at stencils above exactly but return each part; when the stencil
233
+ // leaves a non-periodic axis they return false so the neighbor is skipped
234
+ // identically to the raw path (its contribution to both parts is zero).
235
+
236
+ // curl(H)_x = dHz/dy - dHy/dz at the Ex edge: outputs d_dy = dHz/dy, d_dz = dHy/dz.
237
+ __device__ __forceinline__ bool curl_h_x_parts(
238
+ const float* __restrict__ hy,
239
+ const float* __restrict__ hz,
240
+ int sx,
241
+ int sy,
242
+ int sz,
243
+ int nx_nodes,
244
+ int ny_nodes,
245
+ int nz_nodes,
246
+ bool periodic_x,
247
+ bool periodic_y,
248
+ bool periodic_z,
249
+ const float* __restrict__ inv_dy,
250
+ const float* __restrict__ inv_dz,
251
+ float& d_dy,
252
+ float& d_dz) {
253
+ sx = wrap_edge_coord(sx, nx_nodes, periodic_x);
254
+ if (sx < 0) {
255
+ return false;
256
+ }
257
+ sy = map_node_coord(sy, ny_nodes, periodic_y);
258
+ sz = map_node_coord(sz, nz_nodes, periodic_z);
259
+ int y_low;
260
+ int y_high;
261
+ int z_low;
262
+ int z_high;
263
+ if (!backward_edge_pair(sy, ny_nodes, periodic_y, y_low, y_high) ||
264
+ !backward_edge_pair(sz, nz_nodes, periodic_z, z_low, z_high)) {
265
+ return false;
266
+ }
267
+ d_dy = (hz[offset3d(sx, y_high, sz, ny_nodes - 1, nz_nodes)] -
268
+ hz[offset3d(sx, y_low, sz, ny_nodes - 1, nz_nodes)]) * inv_dy[sy];
269
+ d_dz = (hy[offset3d(sx, sy, z_high, ny_nodes, nz_nodes - 1)] -
270
+ hy[offset3d(sx, sy, z_low, ny_nodes, nz_nodes - 1)]) * inv_dz[sz];
271
+ return true;
272
+ }
273
+
274
+ // curl(H)_y = dHx/dz - dHz/dx at the Ey edge: outputs d_dz = dHx/dz, d_dx = dHz/dx.
275
+ __device__ __forceinline__ bool curl_h_y_parts(
276
+ const float* __restrict__ hx,
277
+ const float* __restrict__ hz,
278
+ int sx,
279
+ int sy,
280
+ int sz,
281
+ int nx_nodes,
282
+ int ny_nodes,
283
+ int nz_nodes,
284
+ bool periodic_x,
285
+ bool periodic_y,
286
+ bool periodic_z,
287
+ const float* __restrict__ inv_dx,
288
+ const float* __restrict__ inv_dz,
289
+ float& d_dz,
290
+ float& d_dx) {
291
+ sy = wrap_edge_coord(sy, ny_nodes, periodic_y);
292
+ if (sy < 0) {
293
+ return false;
294
+ }
295
+ sx = map_node_coord(sx, nx_nodes, periodic_x);
296
+ sz = map_node_coord(sz, nz_nodes, periodic_z);
297
+ int x_low;
298
+ int x_high;
299
+ int z_low;
300
+ int z_high;
301
+ if (!backward_edge_pair(sx, nx_nodes, periodic_x, x_low, x_high) ||
302
+ !backward_edge_pair(sz, nz_nodes, periodic_z, z_low, z_high)) {
303
+ return false;
304
+ }
305
+ d_dz = (hx[offset3d(sx, sy, z_high, ny_nodes - 1, nz_nodes - 1)] -
306
+ hx[offset3d(sx, sy, z_low, ny_nodes - 1, nz_nodes - 1)]) * inv_dz[sz];
307
+ d_dx = (hz[offset3d(x_high, sy, sz, ny_nodes - 1, nz_nodes)] -
308
+ hz[offset3d(x_low, sy, sz, ny_nodes - 1, nz_nodes)]) * inv_dx[sx];
309
+ return true;
310
+ }
311
+
312
+ // curl(H)_z = dHy/dx - dHx/dy at the Ez edge: outputs d_dx = dHy/dx, d_dy = dHx/dy.
313
+ __device__ __forceinline__ bool curl_h_z_parts(
314
+ const float* __restrict__ hx,
315
+ const float* __restrict__ hy,
316
+ int sx,
317
+ int sy,
318
+ int sz,
319
+ int nx_nodes,
320
+ int ny_nodes,
321
+ int nz_nodes,
322
+ bool periodic_x,
323
+ bool periodic_y,
324
+ bool periodic_z,
325
+ const float* __restrict__ inv_dx,
326
+ const float* __restrict__ inv_dy,
327
+ float& d_dx,
328
+ float& d_dy) {
329
+ sz = wrap_edge_coord(sz, nz_nodes, periodic_z);
330
+ if (sz < 0) {
331
+ return false;
332
+ }
333
+ sx = map_node_coord(sx, nx_nodes, periodic_x);
334
+ sy = map_node_coord(sy, ny_nodes, periodic_y);
335
+ int x_low;
336
+ int x_high;
337
+ int y_low;
338
+ int y_high;
339
+ if (!backward_edge_pair(sx, nx_nodes, periodic_x, x_low, x_high) ||
340
+ !backward_edge_pair(sy, ny_nodes, periodic_y, y_low, y_high)) {
341
+ return false;
342
+ }
343
+ d_dx = (hy[offset3d(x_high, sy, sz, ny_nodes, nz_nodes - 1)] -
344
+ hy[offset3d(x_low, sy, sz, ny_nodes, nz_nodes - 1)]) * inv_dx[sx];
345
+ d_dy = (hx[offset3d(sx, y_high, sz, ny_nodes - 1, nz_nodes - 1)] -
346
+ hx[offset3d(sx, y_low, sz, ny_nodes - 1, nz_nodes - 1)]) * inv_dy[sy];
347
+ return true;
348
+ }
349
+
350
+ // One CPML coordinate-stretch of a collocated off-diagonal driver term. The
351
+ // psi accumulator lives at the target E edge (single update per step, no race);
352
+ // b/c/inv_kappa are the axis profiles sampled at that edge's index for the axis.
353
+ // Outside the absorber (c == 0, b == 1, inv_kappa == 1) this returns the driver
354
+ // unchanged and psi stays zero, so the correction reduces to the raw update.
355
+ __device__ __forceinline__ float aniso_cpml_stretch(
356
+ float driver,
357
+ float* __restrict__ psi,
358
+ long long linear,
359
+ float b,
360
+ float c,
361
+ float inv_kappa) {
362
+ const float psi_new = b * psi[linear] + c * driver;
363
+ psi[linear] = psi_new;
364
+ return driver * inv_kappa + psi_new;
365
+ }
366
+
367
+ // Ex(i, j, k) lives at ((i+1/2), j, k) on an (nx_nodes, ny_nodes, nz_nodes)
368
+ // node grid; field shape is (nx_nodes-1, ny_nodes, nz_nodes).
369
+ __global__ void update_electric_ex_full_aniso_kernel(
370
+ unsigned int nx,
371
+ unsigned int ny,
372
+ unsigned int nz,
373
+ const float* __restrict__ hx,
374
+ const float* __restrict__ hy,
375
+ const float* __restrict__ hz,
376
+ const float* __restrict__ coeff_y,
377
+ const float* __restrict__ coeff_z,
378
+ const float* __restrict__ inv_dx,
379
+ const float* __restrict__ inv_dy,
380
+ const float* __restrict__ inv_dz,
381
+ int periodic_x,
382
+ int periodic_y,
383
+ int periodic_z,
384
+ float* __restrict__ ex) {
385
+ const unsigned int k = blockIdx.x * blockDim.x + threadIdx.x;
386
+ const unsigned int j = blockIdx.y * blockDim.y + threadIdx.y;
387
+ const unsigned int i = blockIdx.z * blockDim.z + threadIdx.z;
388
+ if (i >= nx || j >= ny || k >= nz) {
389
+ return;
390
+ }
391
+ const long long linear = offset3d(i, j, k, ny, nz);
392
+ const float c_y = coeff_y[linear];
393
+ const float c_z = coeff_z[linear];
394
+ if (c_y == 0.0f && c_z == 0.0f) {
395
+ return;
396
+ }
397
+ const int nx_nodes = static_cast<int>(nx) + 1; // ex shape (Nx-1, Ny, Nz)
398
+ const int ny_nodes = static_cast<int>(ny);
399
+ const int nz_nodes = static_cast<int>(nz);
400
+ const bool per_x = periodic_x != 0;
401
+ const bool per_y = periodic_y != 0;
402
+ const bool per_z = periodic_z != 0;
403
+ const int ii = static_cast<int>(i);
404
+ const int jj = static_cast<int>(j);
405
+ const int kk = static_cast<int>(k);
406
+
407
+ // <curlH_y> over the four surrounding Ey edges (sx in {i, i+1}, sy in {j-1, j}).
408
+ float acc_y = 0.0f;
409
+ if (c_y != 0.0f) {
410
+ for (int sx = ii; sx <= ii + 1; ++sx) {
411
+ for (int sy = jj - 1; sy <= jj; ++sy) {
412
+ float sample;
413
+ if (curl_h_y_at(hx, hz, sx, sy, kk, nx_nodes, ny_nodes, nz_nodes,
414
+ per_x, per_y, per_z, inv_dx, inv_dz, sample)) {
415
+ acc_y += sample;
416
+ }
417
+ }
418
+ }
419
+ }
420
+
421
+ // <curlH_z> over the four surrounding Ez edges (sx in {i, i+1}, sz in {k-1, k}).
422
+ float acc_z = 0.0f;
423
+ if (c_z != 0.0f) {
424
+ for (int sx = ii; sx <= ii + 1; ++sx) {
425
+ for (int sz = kk - 1; sz <= kk; ++sz) {
426
+ float sample;
427
+ if (curl_h_z_at(hx, hy, sx, jj, sz, nx_nodes, ny_nodes, nz_nodes,
428
+ per_x, per_y, per_z, inv_dx, inv_dy, sample)) {
429
+ acc_z += sample;
430
+ }
431
+ }
432
+ }
433
+ }
434
+
435
+ ex[linear] += 0.25f * (c_y * acc_y + c_z * acc_z);
436
+ }
437
+
438
+ // Ey(i, j, k) lives at (i, (j+1/2), k); field shape is (Nx, Ny-1, Nz).
439
+ __global__ void update_electric_ey_full_aniso_kernel(
440
+ unsigned int nx,
441
+ unsigned int ny,
442
+ unsigned int nz,
443
+ const float* __restrict__ hx,
444
+ const float* __restrict__ hy,
445
+ const float* __restrict__ hz,
446
+ const float* __restrict__ coeff_x,
447
+ const float* __restrict__ coeff_z,
448
+ const float* __restrict__ inv_dx,
449
+ const float* __restrict__ inv_dy,
450
+ const float* __restrict__ inv_dz,
451
+ int periodic_x,
452
+ int periodic_y,
453
+ int periodic_z,
454
+ float* __restrict__ ey) {
455
+ const unsigned int k = blockIdx.x * blockDim.x + threadIdx.x;
456
+ const unsigned int j = blockIdx.y * blockDim.y + threadIdx.y;
457
+ const unsigned int i = blockIdx.z * blockDim.z + threadIdx.z;
458
+ if (i >= nx || j >= ny || k >= nz) {
459
+ return;
460
+ }
461
+ const long long linear = offset3d(i, j, k, ny, nz);
462
+ const float c_x = coeff_x[linear];
463
+ const float c_z = coeff_z[linear];
464
+ if (c_x == 0.0f && c_z == 0.0f) {
465
+ return;
466
+ }
467
+ const int nx_nodes = static_cast<int>(nx); // ey shape (Nx, Ny-1, Nz)
468
+ const int ny_nodes = static_cast<int>(ny) + 1;
469
+ const int nz_nodes = static_cast<int>(nz);
470
+ const bool per_x = periodic_x != 0;
471
+ const bool per_y = periodic_y != 0;
472
+ const bool per_z = periodic_z != 0;
473
+ const int ii = static_cast<int>(i);
474
+ const int jj = static_cast<int>(j);
475
+ const int kk = static_cast<int>(k);
476
+
477
+ // <curlH_x> over the four surrounding Ex edges (sx in {i-1, i}, sy in {j, j+1}).
478
+ float acc_x = 0.0f;
479
+ if (c_x != 0.0f) {
480
+ for (int sx = ii - 1; sx <= ii; ++sx) {
481
+ for (int sy = jj; sy <= jj + 1; ++sy) {
482
+ float sample;
483
+ if (curl_h_x_at(hy, hz, sx, sy, kk, nx_nodes, ny_nodes, nz_nodes,
484
+ per_x, per_y, per_z, inv_dy, inv_dz, sample)) {
485
+ acc_x += sample;
486
+ }
487
+ }
488
+ }
489
+ }
490
+
491
+ // <curlH_z> over the four surrounding Ez edges (sy in {j, j+1}, sz in {k-1, k}).
492
+ float acc_z = 0.0f;
493
+ if (c_z != 0.0f) {
494
+ for (int sy = jj; sy <= jj + 1; ++sy) {
495
+ for (int sz = kk - 1; sz <= kk; ++sz) {
496
+ float sample;
497
+ if (curl_h_z_at(hx, hy, ii, sy, sz, nx_nodes, ny_nodes, nz_nodes,
498
+ per_x, per_y, per_z, inv_dx, inv_dy, sample)) {
499
+ acc_z += sample;
500
+ }
501
+ }
502
+ }
503
+ }
504
+
505
+ ey[linear] += 0.25f * (c_x * acc_x + c_z * acc_z);
506
+ }
507
+
508
+ // Ez(i, j, k) lives at (i, j, (k+1/2)); field shape is (Nx, Ny, Nz-1).
509
+ __global__ void update_electric_ez_full_aniso_kernel(
510
+ unsigned int nx,
511
+ unsigned int ny,
512
+ unsigned int nz,
513
+ const float* __restrict__ hx,
514
+ const float* __restrict__ hy,
515
+ const float* __restrict__ hz,
516
+ const float* __restrict__ coeff_x,
517
+ const float* __restrict__ coeff_y,
518
+ const float* __restrict__ inv_dx,
519
+ const float* __restrict__ inv_dy,
520
+ const float* __restrict__ inv_dz,
521
+ int periodic_x,
522
+ int periodic_y,
523
+ int periodic_z,
524
+ float* __restrict__ ez) {
525
+ const unsigned int k = blockIdx.x * blockDim.x + threadIdx.x;
526
+ const unsigned int j = blockIdx.y * blockDim.y + threadIdx.y;
527
+ const unsigned int i = blockIdx.z * blockDim.z + threadIdx.z;
528
+ if (i >= nx || j >= ny || k >= nz) {
529
+ return;
530
+ }
531
+ const long long linear = offset3d(i, j, k, ny, nz);
532
+ const float c_x = coeff_x[linear];
533
+ const float c_y = coeff_y[linear];
534
+ if (c_x == 0.0f && c_y == 0.0f) {
535
+ return;
536
+ }
537
+ const int nx_nodes = static_cast<int>(nx); // ez shape (Nx, Ny, Nz-1)
538
+ const int ny_nodes = static_cast<int>(ny);
539
+ const int nz_nodes = static_cast<int>(nz) + 1;
540
+ const bool per_x = periodic_x != 0;
541
+ const bool per_y = periodic_y != 0;
542
+ const bool per_z = periodic_z != 0;
543
+ const int ii = static_cast<int>(i);
544
+ const int jj = static_cast<int>(j);
545
+ const int kk = static_cast<int>(k);
546
+
547
+ // <curlH_x> over the four surrounding Ex edges (sx in {i-1, i}, sz in {k, k+1}).
548
+ float acc_x = 0.0f;
549
+ if (c_x != 0.0f) {
550
+ for (int sx = ii - 1; sx <= ii; ++sx) {
551
+ for (int sz = kk; sz <= kk + 1; ++sz) {
552
+ float sample;
553
+ if (curl_h_x_at(hy, hz, sx, jj, sz, nx_nodes, ny_nodes, nz_nodes,
554
+ per_x, per_y, per_z, inv_dy, inv_dz, sample)) {
555
+ acc_x += sample;
556
+ }
557
+ }
558
+ }
559
+ }
560
+
561
+ // <curlH_y> over the four surrounding Ey edges (sy in {j-1, j}, sz in {k, k+1}).
562
+ float acc_y = 0.0f;
563
+ if (c_y != 0.0f) {
564
+ for (int sy = jj - 1; sy <= jj; ++sy) {
565
+ for (int sz = kk; sz <= kk + 1; ++sz) {
566
+ float sample;
567
+ if (curl_h_y_at(hx, hz, ii, sy, sz, nx_nodes, ny_nodes, nz_nodes,
568
+ per_x, per_y, per_z, inv_dx, inv_dz, sample)) {
569
+ acc_y += sample;
570
+ }
571
+ }
572
+ }
573
+ }
574
+
575
+ ez[linear] += 0.25f * (c_x * acc_x + c_y * acc_y);
576
+ }
577
+
578
+ // --- CPML-consistent off-diagonal correction (structure overlapping the PML) -
579
+ //
580
+ // The raw kernels above add the collocated off-axis curl(H) with plain finite
581
+ // differences, which is only exact where the CPML coordinate stretch is
582
+ // inactive. When an anisotropic structure reaches into the absorber the
583
+ // off-diagonal coupling must be stretched exactly like the diagonal curl. These
584
+ // kernels split each off-axis curl(H) into its two directional derivatives,
585
+ // collocate each onto the target E edge, and apply the CPML stretch per
586
+ // direction with a psi accumulator owned by that edge. The transverse
587
+ // directions use the E-field (node) profiles at the edge's node index; the
588
+ // edge's own direction sits on a half point, so it uses the H-field (half)
589
+ // profile at the edge index. The three per-direction psi buffers are zero
590
+ // outside the absorber, so this reduces exactly to the raw correction there.
591
+
592
+ __global__ void update_electric_ex_full_aniso_cpml_kernel(
593
+ unsigned int nx,
594
+ unsigned int ny,
595
+ unsigned int nz,
596
+ const float* __restrict__ hx,
597
+ const float* __restrict__ hy,
598
+ const float* __restrict__ hz,
599
+ const float* __restrict__ coeff_y,
600
+ const float* __restrict__ coeff_z,
601
+ const float* __restrict__ inv_dx,
602
+ const float* __restrict__ inv_dy,
603
+ const float* __restrict__ inv_dz,
604
+ const float* __restrict__ inv_kappa_x,
605
+ const float* __restrict__ cpml_b_x,
606
+ const float* __restrict__ cpml_c_x,
607
+ const float* __restrict__ inv_kappa_y,
608
+ const float* __restrict__ cpml_b_y,
609
+ const float* __restrict__ cpml_c_y,
610
+ const float* __restrict__ inv_kappa_z,
611
+ const float* __restrict__ cpml_b_z,
612
+ const float* __restrict__ cpml_c_z,
613
+ int periodic_x,
614
+ int periodic_y,
615
+ int periodic_z,
616
+ float* __restrict__ psi_x,
617
+ float* __restrict__ psi_y,
618
+ float* __restrict__ psi_z,
619
+ float* __restrict__ ex) {
620
+ const unsigned int k = blockIdx.x * blockDim.x + threadIdx.x;
621
+ const unsigned int j = blockIdx.y * blockDim.y + threadIdx.y;
622
+ const unsigned int i = blockIdx.z * blockDim.z + threadIdx.z;
623
+ if (i >= nx || j >= ny || k >= nz) {
624
+ return;
625
+ }
626
+ const long long linear = offset3d(i, j, k, ny, nz);
627
+ const float c_y = coeff_y[linear];
628
+ const float c_z = coeff_z[linear];
629
+ if (c_y == 0.0f && c_z == 0.0f) {
630
+ return;
631
+ }
632
+ const int nx_nodes = static_cast<int>(nx) + 1; // ex shape (Nx-1, Ny, Nz)
633
+ const int ny_nodes = static_cast<int>(ny);
634
+ const int nz_nodes = static_cast<int>(nz);
635
+ const bool per_x = periodic_x != 0;
636
+ const bool per_y = periodic_y != 0;
637
+ const bool per_z = periodic_z != 0;
638
+ const int ii = static_cast<int>(i);
639
+ const int jj = static_cast<int>(j);
640
+ const int kk = static_cast<int>(k);
641
+
642
+ // curl(H)_y over the four surrounding Ey edges: split into dHx/dz and dHz/dx.
643
+ float acc_yz = 0.0f; // dHx/dz
644
+ float acc_yx = 0.0f; // dHz/dx
645
+ if (c_y != 0.0f) {
646
+ for (int sx = ii; sx <= ii + 1; ++sx) {
647
+ for (int sy = jj - 1; sy <= jj; ++sy) {
648
+ float d_dz;
649
+ float d_dx;
650
+ if (curl_h_y_parts(hx, hz, sx, sy, kk, nx_nodes, ny_nodes, nz_nodes,
651
+ per_x, per_y, per_z, inv_dx, inv_dz, d_dz, d_dx)) {
652
+ acc_yz += d_dz;
653
+ acc_yx += d_dx;
654
+ }
655
+ }
656
+ }
657
+ }
658
+
659
+ // curl(H)_z over the four surrounding Ez edges: split into dHy/dx and dHx/dy.
660
+ float acc_zx = 0.0f; // dHy/dx
661
+ float acc_zy = 0.0f; // dHx/dy
662
+ if (c_z != 0.0f) {
663
+ for (int sx = ii; sx <= ii + 1; ++sx) {
664
+ for (int sz = kk - 1; sz <= kk; ++sz) {
665
+ float d_dx;
666
+ float d_dy;
667
+ if (curl_h_z_parts(hx, hy, sx, jj, sz, nx_nodes, ny_nodes, nz_nodes,
668
+ per_x, per_y, per_z, inv_dx, inv_dy, d_dx, d_dy)) {
669
+ acc_zx += d_dx;
670
+ acc_zy += d_dy;
671
+ }
672
+ }
673
+ }
674
+ }
675
+
676
+ const float driver_x = 0.25f * (-c_y * acc_yx + c_z * acc_zx);
677
+ const float driver_y = 0.25f * (-c_z * acc_zy);
678
+ const float driver_z = 0.25f * (c_y * acc_yz);
679
+
680
+ const float out_x = aniso_cpml_stretch(driver_x, psi_x, linear, cpml_b_x[i], cpml_c_x[i], inv_kappa_x[i]);
681
+ const float out_y = aniso_cpml_stretch(driver_y, psi_y, linear, cpml_b_y[j], cpml_c_y[j], inv_kappa_y[j]);
682
+ const float out_z = aniso_cpml_stretch(driver_z, psi_z, linear, cpml_b_z[k], cpml_c_z[k], inv_kappa_z[k]);
683
+ ex[linear] += out_x + out_y + out_z;
684
+ }
685
+
686
+ __global__ void update_electric_ey_full_aniso_cpml_kernel(
687
+ unsigned int nx,
688
+ unsigned int ny,
689
+ unsigned int nz,
690
+ const float* __restrict__ hx,
691
+ const float* __restrict__ hy,
692
+ const float* __restrict__ hz,
693
+ const float* __restrict__ coeff_x,
694
+ const float* __restrict__ coeff_z,
695
+ const float* __restrict__ inv_dx,
696
+ const float* __restrict__ inv_dy,
697
+ const float* __restrict__ inv_dz,
698
+ const float* __restrict__ inv_kappa_x,
699
+ const float* __restrict__ cpml_b_x,
700
+ const float* __restrict__ cpml_c_x,
701
+ const float* __restrict__ inv_kappa_y,
702
+ const float* __restrict__ cpml_b_y,
703
+ const float* __restrict__ cpml_c_y,
704
+ const float* __restrict__ inv_kappa_z,
705
+ const float* __restrict__ cpml_b_z,
706
+ const float* __restrict__ cpml_c_z,
707
+ int periodic_x,
708
+ int periodic_y,
709
+ int periodic_z,
710
+ float* __restrict__ psi_x,
711
+ float* __restrict__ psi_y,
712
+ float* __restrict__ psi_z,
713
+ float* __restrict__ ey) {
714
+ const unsigned int k = blockIdx.x * blockDim.x + threadIdx.x;
715
+ const unsigned int j = blockIdx.y * blockDim.y + threadIdx.y;
716
+ const unsigned int i = blockIdx.z * blockDim.z + threadIdx.z;
717
+ if (i >= nx || j >= ny || k >= nz) {
718
+ return;
719
+ }
720
+ const long long linear = offset3d(i, j, k, ny, nz);
721
+ const float c_x = coeff_x[linear];
722
+ const float c_z = coeff_z[linear];
723
+ if (c_x == 0.0f && c_z == 0.0f) {
724
+ return;
725
+ }
726
+ const int nx_nodes = static_cast<int>(nx); // ey shape (Nx, Ny-1, Nz)
727
+ const int ny_nodes = static_cast<int>(ny) + 1;
728
+ const int nz_nodes = static_cast<int>(nz);
729
+ const bool per_x = periodic_x != 0;
730
+ const bool per_y = periodic_y != 0;
731
+ const bool per_z = periodic_z != 0;
732
+ const int ii = static_cast<int>(i);
733
+ const int jj = static_cast<int>(j);
734
+ const int kk = static_cast<int>(k);
735
+
736
+ // curl(H)_x over the four surrounding Ex edges: split into dHz/dy and dHy/dz.
737
+ float acc_xy = 0.0f; // dHz/dy
738
+ float acc_xz = 0.0f; // dHy/dz
739
+ if (c_x != 0.0f) {
740
+ for (int sx = ii - 1; sx <= ii; ++sx) {
741
+ for (int sy = jj; sy <= jj + 1; ++sy) {
742
+ float d_dy;
743
+ float d_dz;
744
+ if (curl_h_x_parts(hy, hz, sx, sy, kk, nx_nodes, ny_nodes, nz_nodes,
745
+ per_x, per_y, per_z, inv_dy, inv_dz, d_dy, d_dz)) {
746
+ acc_xy += d_dy;
747
+ acc_xz += d_dz;
748
+ }
749
+ }
750
+ }
751
+ }
752
+
753
+ // curl(H)_z over the four surrounding Ez edges: split into dHy/dx and dHx/dy.
754
+ float acc_zx = 0.0f; // dHy/dx
755
+ float acc_zy = 0.0f; // dHx/dy
756
+ if (c_z != 0.0f) {
757
+ for (int sy = jj; sy <= jj + 1; ++sy) {
758
+ for (int sz = kk - 1; sz <= kk; ++sz) {
759
+ float d_dx;
760
+ float d_dy;
761
+ if (curl_h_z_parts(hx, hy, ii, sy, sz, nx_nodes, ny_nodes, nz_nodes,
762
+ per_x, per_y, per_z, inv_dx, inv_dy, d_dx, d_dy)) {
763
+ acc_zx += d_dx;
764
+ acc_zy += d_dy;
765
+ }
766
+ }
767
+ }
768
+ }
769
+
770
+ const float driver_x = 0.25f * (c_z * acc_zx);
771
+ const float driver_y = 0.25f * (c_x * acc_xy - c_z * acc_zy);
772
+ const float driver_z = 0.25f * (-c_x * acc_xz);
773
+
774
+ const float out_x = aniso_cpml_stretch(driver_x, psi_x, linear, cpml_b_x[i], cpml_c_x[i], inv_kappa_x[i]);
775
+ const float out_y = aniso_cpml_stretch(driver_y, psi_y, linear, cpml_b_y[j], cpml_c_y[j], inv_kappa_y[j]);
776
+ const float out_z = aniso_cpml_stretch(driver_z, psi_z, linear, cpml_b_z[k], cpml_c_z[k], inv_kappa_z[k]);
777
+ ey[linear] += out_x + out_y + out_z;
778
+ }
779
+
780
+ __global__ void update_electric_ez_full_aniso_cpml_kernel(
781
+ unsigned int nx,
782
+ unsigned int ny,
783
+ unsigned int nz,
784
+ const float* __restrict__ hx,
785
+ const float* __restrict__ hy,
786
+ const float* __restrict__ hz,
787
+ const float* __restrict__ coeff_x,
788
+ const float* __restrict__ coeff_y,
789
+ const float* __restrict__ inv_dx,
790
+ const float* __restrict__ inv_dy,
791
+ const float* __restrict__ inv_dz,
792
+ const float* __restrict__ inv_kappa_x,
793
+ const float* __restrict__ cpml_b_x,
794
+ const float* __restrict__ cpml_c_x,
795
+ const float* __restrict__ inv_kappa_y,
796
+ const float* __restrict__ cpml_b_y,
797
+ const float* __restrict__ cpml_c_y,
798
+ const float* __restrict__ inv_kappa_z,
799
+ const float* __restrict__ cpml_b_z,
800
+ const float* __restrict__ cpml_c_z,
801
+ int periodic_x,
802
+ int periodic_y,
803
+ int periodic_z,
804
+ float* __restrict__ psi_x,
805
+ float* __restrict__ psi_y,
806
+ float* __restrict__ psi_z,
807
+ float* __restrict__ ez) {
808
+ const unsigned int k = blockIdx.x * blockDim.x + threadIdx.x;
809
+ const unsigned int j = blockIdx.y * blockDim.y + threadIdx.y;
810
+ const unsigned int i = blockIdx.z * blockDim.z + threadIdx.z;
811
+ if (i >= nx || j >= ny || k >= nz) {
812
+ return;
813
+ }
814
+ const long long linear = offset3d(i, j, k, ny, nz);
815
+ const float c_x = coeff_x[linear];
816
+ const float c_y = coeff_y[linear];
817
+ if (c_x == 0.0f && c_y == 0.0f) {
818
+ return;
819
+ }
820
+ const int nx_nodes = static_cast<int>(nx); // ez shape (Nx, Ny, Nz-1)
821
+ const int ny_nodes = static_cast<int>(ny);
822
+ const int nz_nodes = static_cast<int>(nz) + 1;
823
+ const bool per_x = periodic_x != 0;
824
+ const bool per_y = periodic_y != 0;
825
+ const bool per_z = periodic_z != 0;
826
+ const int ii = static_cast<int>(i);
827
+ const int jj = static_cast<int>(j);
828
+ const int kk = static_cast<int>(k);
829
+
830
+ // curl(H)_x over the four surrounding Ex edges: split into dHz/dy and dHy/dz.
831
+ float acc_xy = 0.0f; // dHz/dy
832
+ float acc_xz = 0.0f; // dHy/dz
833
+ if (c_x != 0.0f) {
834
+ for (int sx = ii - 1; sx <= ii; ++sx) {
835
+ for (int sz = kk; sz <= kk + 1; ++sz) {
836
+ float d_dy;
837
+ float d_dz;
838
+ if (curl_h_x_parts(hy, hz, sx, jj, sz, nx_nodes, ny_nodes, nz_nodes,
839
+ per_x, per_y, per_z, inv_dy, inv_dz, d_dy, d_dz)) {
840
+ acc_xy += d_dy;
841
+ acc_xz += d_dz;
842
+ }
843
+ }
844
+ }
845
+ }
846
+
847
+ // curl(H)_y over the four surrounding Ey edges: split into dHx/dz and dHz/dx.
848
+ float acc_yz = 0.0f; // dHx/dz
849
+ float acc_yx = 0.0f; // dHz/dx
850
+ if (c_y != 0.0f) {
851
+ for (int sy = jj - 1; sy <= jj; ++sy) {
852
+ for (int sz = kk; sz <= kk + 1; ++sz) {
853
+ float d_dz;
854
+ float d_dx;
855
+ if (curl_h_y_parts(hx, hz, ii, sy, sz, nx_nodes, ny_nodes, nz_nodes,
856
+ per_x, per_y, per_z, inv_dx, inv_dz, d_dz, d_dx)) {
857
+ acc_yz += d_dz;
858
+ acc_yx += d_dx;
859
+ }
860
+ }
861
+ }
862
+ }
863
+
864
+ const float driver_x = 0.25f * (-c_y * acc_yx);
865
+ const float driver_y = 0.25f * (c_x * acc_xy);
866
+ const float driver_z = 0.25f * (-c_x * acc_xz + c_y * acc_yz);
867
+
868
+ const float out_x = aniso_cpml_stretch(driver_x, psi_x, linear, cpml_b_x[i], cpml_c_x[i], inv_kappa_x[i]);
869
+ const float out_y = aniso_cpml_stretch(driver_y, psi_y, linear, cpml_b_y[j], cpml_c_y[j], inv_kappa_y[j]);
870
+ const float out_z = aniso_cpml_stretch(driver_z, psi_z, linear, cpml_b_z[k], cpml_c_z[k], inv_kappa_z[k]);
871
+ ez[linear] += out_x + out_y + out_z;
872
+ }
873
+
874
+ // --- Off-diagonal ADE polarization-current subtraction ---------------------
875
+ //
876
+ // For a full (off-diagonal) anisotropic permittivity the Ampere update is
877
+ // E += dt * eps_inf^-1 . (curl H - J_p),
878
+ // so the same per-edge inverse permittivity tensor that couples curl(H) also
879
+ // couples the polarization current J_p across components. These kernels apply
880
+ // the off-diagonal part of that tensor to the accumulated per-component
881
+ // polarization current, mirroring the curl(H) collocation stencil above but
882
+ // reading the stored current field directly and subtracting:
883
+ // E_i -= 0.25 * (coeff_ij * <J_j>_i + coeff_ik * <J_k>_i).
884
+ // The coefficients coeff_ij == dt * inv_ij / eps0 are exactly the ones used for
885
+ // the curl(H) off-diagonal correction, so magnitude and sign stay consistent.
886
+
887
+ // Read an Ex-edge field value at (sx edge-x, sy node-y, sz node-z).
888
+ __device__ __forceinline__ bool read_ex_edge_value(
889
+ const float* __restrict__ field,
890
+ int sx,
891
+ int sy,
892
+ int sz,
893
+ int nx_nodes,
894
+ int ny_nodes,
895
+ int nz_nodes,
896
+ bool periodic_x,
897
+ bool periodic_y,
898
+ bool periodic_z,
899
+ float& value) {
900
+ sx = wrap_edge_coord(sx, nx_nodes, periodic_x);
901
+ if (sx < 0) {
902
+ return false;
903
+ }
904
+ sy = map_node_coord(sy, ny_nodes, periodic_y);
905
+ sz = map_node_coord(sz, nz_nodes, periodic_z);
906
+ value = field[offset3d(sx, sy, sz, ny_nodes, nz_nodes)];
907
+ return true;
908
+ }
909
+
910
+ // Read an Ey-edge field value at (sx node-x, sy edge-y, sz node-z).
911
+ __device__ __forceinline__ bool read_ey_edge_value(
912
+ const float* __restrict__ field,
913
+ int sx,
914
+ int sy,
915
+ int sz,
916
+ int nx_nodes,
917
+ int ny_nodes,
918
+ int nz_nodes,
919
+ bool periodic_x,
920
+ bool periodic_y,
921
+ bool periodic_z,
922
+ float& value) {
923
+ sy = wrap_edge_coord(sy, ny_nodes, periodic_y);
924
+ if (sy < 0) {
925
+ return false;
926
+ }
927
+ sx = map_node_coord(sx, nx_nodes, periodic_x);
928
+ sz = map_node_coord(sz, nz_nodes, periodic_z);
929
+ value = field[offset3d(sx, sy, sz, ny_nodes - 1, nz_nodes)];
930
+ return true;
931
+ }
932
+
933
+ // Read an Ez-edge field value at (sx node-x, sy node-y, sz edge-z).
934
+ __device__ __forceinline__ bool read_ez_edge_value(
935
+ const float* __restrict__ field,
936
+ int sx,
937
+ int sy,
938
+ int sz,
939
+ int nx_nodes,
940
+ int ny_nodes,
941
+ int nz_nodes,
942
+ bool periodic_x,
943
+ bool periodic_y,
944
+ bool periodic_z,
945
+ float& value) {
946
+ sz = wrap_edge_coord(sz, nz_nodes, periodic_z);
947
+ if (sz < 0) {
948
+ return false;
949
+ }
950
+ sx = map_node_coord(sx, nx_nodes, periodic_x);
951
+ sy = map_node_coord(sy, ny_nodes, periodic_y);
952
+ value = field[offset3d(sx, sy, sz, ny_nodes, nz_nodes - 1)];
953
+ return true;
954
+ }
955
+
956
+ __global__ void apply_aniso_offdiag_current_ex_kernel(
957
+ unsigned int nx,
958
+ unsigned int ny,
959
+ unsigned int nz,
960
+ const float* __restrict__ jy,
961
+ const float* __restrict__ jz,
962
+ const float* __restrict__ coeff_y,
963
+ const float* __restrict__ coeff_z,
964
+ int periodic_x,
965
+ int periodic_y,
966
+ int periodic_z,
967
+ float* __restrict__ ex) {
968
+ const unsigned int k = blockIdx.x * blockDim.x + threadIdx.x;
969
+ const unsigned int j = blockIdx.y * blockDim.y + threadIdx.y;
970
+ const unsigned int i = blockIdx.z * blockDim.z + threadIdx.z;
971
+ if (i >= nx || j >= ny || k >= nz) {
972
+ return;
973
+ }
974
+ const long long linear = offset3d(i, j, k, ny, nz);
975
+ const float c_y = coeff_y[linear];
976
+ const float c_z = coeff_z[linear];
977
+ if (c_y == 0.0f && c_z == 0.0f) {
978
+ return;
979
+ }
980
+ const int nx_nodes = static_cast<int>(nx) + 1; // ex shape (Nx-1, Ny, Nz)
981
+ const int ny_nodes = static_cast<int>(ny);
982
+ const int nz_nodes = static_cast<int>(nz);
983
+ const bool per_x = periodic_x != 0;
984
+ const bool per_y = periodic_y != 0;
985
+ const bool per_z = periodic_z != 0;
986
+ const int ii = static_cast<int>(i);
987
+ const int jj = static_cast<int>(j);
988
+ const int kk = static_cast<int>(k);
989
+
990
+ float acc_y = 0.0f;
991
+ if (c_y != 0.0f) {
992
+ for (int sx = ii; sx <= ii + 1; ++sx) {
993
+ for (int sy = jj - 1; sy <= jj; ++sy) {
994
+ float sample;
995
+ if (read_ey_edge_value(jy, sx, sy, kk, nx_nodes, ny_nodes, nz_nodes,
996
+ per_x, per_y, per_z, sample)) {
997
+ acc_y += sample;
998
+ }
999
+ }
1000
+ }
1001
+ }
1002
+
1003
+ float acc_z = 0.0f;
1004
+ if (c_z != 0.0f) {
1005
+ for (int sx = ii; sx <= ii + 1; ++sx) {
1006
+ for (int sz = kk - 1; sz <= kk; ++sz) {
1007
+ float sample;
1008
+ if (read_ez_edge_value(jz, sx, jj, sz, nx_nodes, ny_nodes, nz_nodes,
1009
+ per_x, per_y, per_z, sample)) {
1010
+ acc_z += sample;
1011
+ }
1012
+ }
1013
+ }
1014
+ }
1015
+
1016
+ ex[linear] -= 0.25f * (c_y * acc_y + c_z * acc_z);
1017
+ }
1018
+
1019
+ __global__ void apply_aniso_offdiag_current_ey_kernel(
1020
+ unsigned int nx,
1021
+ unsigned int ny,
1022
+ unsigned int nz,
1023
+ const float* __restrict__ jx,
1024
+ const float* __restrict__ jz,
1025
+ const float* __restrict__ coeff_x,
1026
+ const float* __restrict__ coeff_z,
1027
+ int periodic_x,
1028
+ int periodic_y,
1029
+ int periodic_z,
1030
+ float* __restrict__ ey) {
1031
+ const unsigned int k = blockIdx.x * blockDim.x + threadIdx.x;
1032
+ const unsigned int j = blockIdx.y * blockDim.y + threadIdx.y;
1033
+ const unsigned int i = blockIdx.z * blockDim.z + threadIdx.z;
1034
+ if (i >= nx || j >= ny || k >= nz) {
1035
+ return;
1036
+ }
1037
+ const long long linear = offset3d(i, j, k, ny, nz);
1038
+ const float c_x = coeff_x[linear];
1039
+ const float c_z = coeff_z[linear];
1040
+ if (c_x == 0.0f && c_z == 0.0f) {
1041
+ return;
1042
+ }
1043
+ const int nx_nodes = static_cast<int>(nx); // ey shape (Nx, Ny-1, Nz)
1044
+ const int ny_nodes = static_cast<int>(ny) + 1;
1045
+ const int nz_nodes = static_cast<int>(nz);
1046
+ const bool per_x = periodic_x != 0;
1047
+ const bool per_y = periodic_y != 0;
1048
+ const bool per_z = periodic_z != 0;
1049
+ const int ii = static_cast<int>(i);
1050
+ const int jj = static_cast<int>(j);
1051
+ const int kk = static_cast<int>(k);
1052
+
1053
+ float acc_x = 0.0f;
1054
+ if (c_x != 0.0f) {
1055
+ for (int sx = ii - 1; sx <= ii; ++sx) {
1056
+ for (int sy = jj; sy <= jj + 1; ++sy) {
1057
+ float sample;
1058
+ if (read_ex_edge_value(jx, sx, sy, kk, nx_nodes, ny_nodes, nz_nodes,
1059
+ per_x, per_y, per_z, sample)) {
1060
+ acc_x += sample;
1061
+ }
1062
+ }
1063
+ }
1064
+ }
1065
+
1066
+ float acc_z = 0.0f;
1067
+ if (c_z != 0.0f) {
1068
+ for (int sy = jj; sy <= jj + 1; ++sy) {
1069
+ for (int sz = kk - 1; sz <= kk; ++sz) {
1070
+ float sample;
1071
+ if (read_ez_edge_value(jz, ii, sy, sz, nx_nodes, ny_nodes, nz_nodes,
1072
+ per_x, per_y, per_z, sample)) {
1073
+ acc_z += sample;
1074
+ }
1075
+ }
1076
+ }
1077
+ }
1078
+
1079
+ ey[linear] -= 0.25f * (c_x * acc_x + c_z * acc_z);
1080
+ }
1081
+
1082
+ __global__ void apply_aniso_offdiag_current_ez_kernel(
1083
+ unsigned int nx,
1084
+ unsigned int ny,
1085
+ unsigned int nz,
1086
+ const float* __restrict__ jx,
1087
+ const float* __restrict__ jy,
1088
+ const float* __restrict__ coeff_x,
1089
+ const float* __restrict__ coeff_y,
1090
+ int periodic_x,
1091
+ int periodic_y,
1092
+ int periodic_z,
1093
+ float* __restrict__ ez) {
1094
+ const unsigned int k = blockIdx.x * blockDim.x + threadIdx.x;
1095
+ const unsigned int j = blockIdx.y * blockDim.y + threadIdx.y;
1096
+ const unsigned int i = blockIdx.z * blockDim.z + threadIdx.z;
1097
+ if (i >= nx || j >= ny || k >= nz) {
1098
+ return;
1099
+ }
1100
+ const long long linear = offset3d(i, j, k, ny, nz);
1101
+ const float c_x = coeff_x[linear];
1102
+ const float c_y = coeff_y[linear];
1103
+ if (c_x == 0.0f && c_y == 0.0f) {
1104
+ return;
1105
+ }
1106
+ const int nx_nodes = static_cast<int>(nx); // ez shape (Nx, Ny, Nz-1)
1107
+ const int ny_nodes = static_cast<int>(ny);
1108
+ const int nz_nodes = static_cast<int>(nz) + 1;
1109
+ const bool per_x = periodic_x != 0;
1110
+ const bool per_y = periodic_y != 0;
1111
+ const bool per_z = periodic_z != 0;
1112
+ const int ii = static_cast<int>(i);
1113
+ const int jj = static_cast<int>(j);
1114
+ const int kk = static_cast<int>(k);
1115
+
1116
+ float acc_x = 0.0f;
1117
+ if (c_x != 0.0f) {
1118
+ for (int sx = ii - 1; sx <= ii; ++sx) {
1119
+ for (int sz = kk; sz <= kk + 1; ++sz) {
1120
+ float sample;
1121
+ if (read_ex_edge_value(jx, sx, jj, sz, nx_nodes, ny_nodes, nz_nodes,
1122
+ per_x, per_y, per_z, sample)) {
1123
+ acc_x += sample;
1124
+ }
1125
+ }
1126
+ }
1127
+ }
1128
+
1129
+ float acc_y = 0.0f;
1130
+ if (c_y != 0.0f) {
1131
+ for (int sy = jj - 1; sy <= jj; ++sy) {
1132
+ for (int sz = kk; sz <= kk + 1; ++sz) {
1133
+ float sample;
1134
+ if (read_ey_edge_value(jy, ii, sy, sz, nx_nodes, ny_nodes, nz_nodes,
1135
+ per_x, per_y, per_z, sample)) {
1136
+ acc_y += sample;
1137
+ }
1138
+ }
1139
+ }
1140
+ }
1141
+
1142
+ ez[linear] -= 0.25f * (c_x * acc_x + c_y * acc_y);
1143
+ }
1144
+
1145
+ void check_full_aniso_inputs(
1146
+ const torch::stable::Tensor& field,
1147
+ const torch::stable::Tensor& hx,
1148
+ const torch::stable::Tensor& hy,
1149
+ const torch::stable::Tensor& hz,
1150
+ const torch::stable::Tensor& coeff_a,
1151
+ const torch::stable::Tensor& coeff_b,
1152
+ const torch::stable::Tensor& inv_dx,
1153
+ const torch::stable::Tensor& inv_dy,
1154
+ const torch::stable::Tensor& inv_dz,
1155
+ int64_t nx_nodes,
1156
+ int64_t ny_nodes,
1157
+ int64_t nz_nodes,
1158
+ const char* name) {
1159
+ for (const torch::stable::Tensor* tensor :
1160
+ {&field, &hx, &hy, &hz, &coeff_a, &coeff_b, &inv_dx, &inv_dy, &inv_dz}) {
1161
+ check_float32_tensor(*tensor, name);
1162
+ check_contiguous_tensor(*tensor, name);
1163
+ check_same_cuda_device(field, *tensor, name);
1164
+ }
1165
+ STD_TORCH_CHECK(field.dim() == 3, name, " must be rank 3");
1166
+ STD_TORCH_CHECK(coeff_a.sizes().equals(field.sizes()), name, " coeff must match field shape");
1167
+ STD_TORCH_CHECK(coeff_b.sizes().equals(field.sizes()), name, " coeff must match field shape");
1168
+ STD_TORCH_CHECK(
1169
+ hx.dim() == 3 && hx.size(0) == nx_nodes && hx.size(1) == ny_nodes - 1 && hx.size(2) == nz_nodes - 1,
1170
+ name, " hx has an incompatible Yee-grid shape");
1171
+ STD_TORCH_CHECK(
1172
+ hy.dim() == 3 && hy.size(0) == nx_nodes - 1 && hy.size(1) == ny_nodes && hy.size(2) == nz_nodes - 1,
1173
+ name, " hy has an incompatible Yee-grid shape");
1174
+ STD_TORCH_CHECK(
1175
+ hz.dim() == 3 && hz.size(0) == nx_nodes - 1 && hz.size(1) == ny_nodes - 1 && hz.size(2) == nz_nodes,
1176
+ name, " hz has an incompatible Yee-grid shape");
1177
+ STD_TORCH_CHECK(inv_dx.dim() == 1 && inv_dx.size(0) == nx_nodes, name, " inv_dx length must be the node count");
1178
+ STD_TORCH_CHECK(inv_dy.dim() == 1 && inv_dy.size(0) == ny_nodes, name, " inv_dy length must be the node count");
1179
+ STD_TORCH_CHECK(inv_dz.dim() == 1 && inv_dz.size(0) == nz_nodes, name, " inv_dz length must be the node count");
1180
+ }
1181
+
1182
+ void check_aniso_cpml_axis(
1183
+ const torch::stable::Tensor& field,
1184
+ const torch::stable::Tensor& inv_kappa,
1185
+ const torch::stable::Tensor& cpml_b,
1186
+ const torch::stable::Tensor& cpml_c,
1187
+ int axis,
1188
+ const char* name) {
1189
+ for (const torch::stable::Tensor* tensor : {&inv_kappa, &cpml_b, &cpml_c}) {
1190
+ check_float32_tensor(*tensor, name);
1191
+ check_contiguous_tensor(*tensor, name);
1192
+ check_same_cuda_device(field, *tensor, name);
1193
+ STD_TORCH_CHECK(
1194
+ tensor->dim() == 1 && tensor->size(0) == field.size(axis),
1195
+ name, " CPML profile length must match the field extent along its axis");
1196
+ }
1197
+ }
1198
+
1199
+ void check_aniso_cpml_psi(
1200
+ const torch::stable::Tensor& field,
1201
+ const torch::stable::Tensor& psi_x,
1202
+ const torch::stable::Tensor& psi_y,
1203
+ const torch::stable::Tensor& psi_z,
1204
+ const char* name) {
1205
+ for (const torch::stable::Tensor* tensor : {&psi_x, &psi_y, &psi_z}) {
1206
+ check_float32_tensor(*tensor, name);
1207
+ check_contiguous_tensor(*tensor, name);
1208
+ check_same_cuda_device(field, *tensor, name);
1209
+ STD_TORCH_CHECK(tensor->sizes().equals(field.sizes()), name, " psi buffer must match field shape");
1210
+ }
1211
+ }
1212
+
1213
+ void check_current_field(
1214
+ const torch::stable::Tensor& field,
1215
+ const torch::stable::Tensor& reference,
1216
+ int64_t s0,
1217
+ int64_t s1,
1218
+ int64_t s2,
1219
+ const char* name) {
1220
+ check_float32_tensor(field, name);
1221
+ check_contiguous_tensor(field, name);
1222
+ check_same_cuda_device(reference, field, name);
1223
+ STD_TORCH_CHECK(
1224
+ field.dim() == 3 && field.size(0) == s0 && field.size(1) == s1 && field.size(2) == s2,
1225
+ name, " has an incompatible Yee-grid shape");
1226
+ }
1227
+
1228
+ void check_aniso_offdiag_current_inputs(
1229
+ const torch::stable::Tensor& field,
1230
+ const torch::stable::Tensor& coeff_a,
1231
+ const torch::stable::Tensor& coeff_b,
1232
+ const char* name) {
1233
+ for (const torch::stable::Tensor* tensor : {&field, &coeff_a, &coeff_b}) {
1234
+ check_float32_tensor(*tensor, name);
1235
+ check_contiguous_tensor(*tensor, name);
1236
+ check_same_cuda_device(field, *tensor, name);
1237
+ }
1238
+ STD_TORCH_CHECK(field.dim() == 3, name, " must be rank 3");
1239
+ STD_TORCH_CHECK(coeff_a.sizes().equals(field.sizes()), name, " coeff must match field shape");
1240
+ STD_TORCH_CHECK(coeff_b.sizes().equals(field.sizes()), name, " coeff must match field shape");
1241
+ }
1242
+
1243
+ } // namespace
1244
+
1245
+ void capture_aniso_conduction_current_cuda(
1246
+ const torch::stable::Tensor& sigma_x,
1247
+ const torch::stable::Tensor& sigma_y,
1248
+ const torch::stable::Tensor& sigma_z,
1249
+ const torch::stable::Tensor& ex,
1250
+ const torch::stable::Tensor& ey,
1251
+ const torch::stable::Tensor& ez,
1252
+ torch::stable::Tensor jx,
1253
+ torch::stable::Tensor jy,
1254
+ torch::stable::Tensor jz) {
1255
+ const std::pair<const torch::stable::Tensor*, const char*> inputs[] = {
1256
+ {&sigma_x, "sigma_x"}, {&sigma_y, "sigma_y"}, {&sigma_z, "sigma_z"},
1257
+ {&ex, "ex"}, {&ey, "ey"}, {&ez, "ez"},
1258
+ {&jx, "jx"}, {&jy, "jy"}, {&jz, "jz"}};
1259
+ for (const auto& entry : inputs) {
1260
+ check_float32_tensor(*entry.first, entry.second);
1261
+ check_contiguous_tensor(*entry.first, entry.second);
1262
+ check_same_cuda_device(ex, *entry.first, entry.second);
1263
+ }
1264
+ STD_TORCH_CHECK(sigma_x.sizes().equals(ex.sizes()) && jx.sizes().equals(ex.sizes()),
1265
+ "sigma_x and jx must match ex");
1266
+ STD_TORCH_CHECK(sigma_y.sizes().equals(ey.sizes()) && jy.sizes().equals(ey.sizes()),
1267
+ "sigma_y and jy must match ey");
1268
+ STD_TORCH_CHECK(sigma_z.sizes().equals(ez.sizes()) && jz.sizes().equals(ez.sizes()),
1269
+ "sigma_z and jz must match ez");
1270
+ const int64_t total = std::max(ex.numel(), std::max(ey.numel(), ez.numel()));
1271
+ if (total == 0) {
1272
+ return;
1273
+ }
1274
+ const torch::stable::accelerator::DeviceGuard guard(ex.get_device_index());
1275
+ capture_aniso_conduction_current_kernel<<<linear_grid(total), 256, 0, current_cuda_stream()>>>(
1276
+ total, ex.numel(), ey.numel(), ez.numel(),
1277
+ sigma_x.mutable_data_ptr<float>(), sigma_y.mutable_data_ptr<float>(), sigma_z.mutable_data_ptr<float>(),
1278
+ ex.mutable_data_ptr<float>(), ey.mutable_data_ptr<float>(), ez.mutable_data_ptr<float>(),
1279
+ jx.mutable_data_ptr<float>(), jy.mutable_data_ptr<float>(), jz.mutable_data_ptr<float>());
1280
+ WITWIN_CUDA_CHECK();
1281
+ }
1282
+
1283
+ void update_electric_ex_full_aniso_cuda(
1284
+ torch::stable::Tensor ex,
1285
+ const torch::stable::Tensor& hx,
1286
+ const torch::stable::Tensor& hy,
1287
+ const torch::stable::Tensor& hz,
1288
+ const torch::stable::Tensor& coeff_y,
1289
+ const torch::stable::Tensor& coeff_z,
1290
+ const torch::stable::Tensor& inv_dx,
1291
+ const torch::stable::Tensor& inv_dy,
1292
+ const torch::stable::Tensor& inv_dz,
1293
+ int64_t periodic_x,
1294
+ int64_t periodic_y,
1295
+ int64_t periodic_z) {
1296
+ const int64_t nx_nodes = ex.size(0) + 1;
1297
+ const int64_t ny_nodes = ex.size(1);
1298
+ const int64_t nz_nodes = ex.size(2);
1299
+ check_full_aniso_inputs(ex, hx, hy, hz, coeff_y, coeff_z, inv_dx, inv_dy, inv_dz,
1300
+ nx_nodes, ny_nodes, nz_nodes, "ex");
1301
+ torch::stable::accelerator::DeviceGuard guard(ex.get_device_index());
1302
+ const dim3 block = aniso_block3d();
1303
+ update_electric_ex_full_aniso_kernel<<<
1304
+ aniso_grid3d(ex.size(0), ex.size(1), ex.size(2), block), block, 0, current_cuda_stream()>>>(
1305
+ static_cast<unsigned int>(ex.size(0)),
1306
+ static_cast<unsigned int>(ex.size(1)),
1307
+ static_cast<unsigned int>(ex.size(2)),
1308
+ hx.mutable_data_ptr<float>(),
1309
+ hy.mutable_data_ptr<float>(),
1310
+ hz.mutable_data_ptr<float>(),
1311
+ coeff_y.mutable_data_ptr<float>(),
1312
+ coeff_z.mutable_data_ptr<float>(),
1313
+ inv_dx.mutable_data_ptr<float>(),
1314
+ inv_dy.mutable_data_ptr<float>(),
1315
+ inv_dz.mutable_data_ptr<float>(),
1316
+ static_cast<int>(periodic_x),
1317
+ static_cast<int>(periodic_y),
1318
+ static_cast<int>(periodic_z),
1319
+ ex.mutable_data_ptr<float>());
1320
+ WITWIN_CUDA_CHECK();
1321
+ }
1322
+
1323
+ void update_electric_ey_full_aniso_cuda(
1324
+ torch::stable::Tensor ey,
1325
+ const torch::stable::Tensor& hx,
1326
+ const torch::stable::Tensor& hy,
1327
+ const torch::stable::Tensor& hz,
1328
+ const torch::stable::Tensor& coeff_x,
1329
+ const torch::stable::Tensor& coeff_z,
1330
+ const torch::stable::Tensor& inv_dx,
1331
+ const torch::stable::Tensor& inv_dy,
1332
+ const torch::stable::Tensor& inv_dz,
1333
+ int64_t periodic_x,
1334
+ int64_t periodic_y,
1335
+ int64_t periodic_z) {
1336
+ const int64_t nx_nodes = ey.size(0);
1337
+ const int64_t ny_nodes = ey.size(1) + 1;
1338
+ const int64_t nz_nodes = ey.size(2);
1339
+ check_full_aniso_inputs(ey, hx, hy, hz, coeff_x, coeff_z, inv_dx, inv_dy, inv_dz,
1340
+ nx_nodes, ny_nodes, nz_nodes, "ey");
1341
+ torch::stable::accelerator::DeviceGuard guard(ey.get_device_index());
1342
+ const dim3 block = aniso_block3d();
1343
+ update_electric_ey_full_aniso_kernel<<<
1344
+ aniso_grid3d(ey.size(0), ey.size(1), ey.size(2), block), block, 0, current_cuda_stream()>>>(
1345
+ static_cast<unsigned int>(ey.size(0)),
1346
+ static_cast<unsigned int>(ey.size(1)),
1347
+ static_cast<unsigned int>(ey.size(2)),
1348
+ hx.mutable_data_ptr<float>(),
1349
+ hy.mutable_data_ptr<float>(),
1350
+ hz.mutable_data_ptr<float>(),
1351
+ coeff_x.mutable_data_ptr<float>(),
1352
+ coeff_z.mutable_data_ptr<float>(),
1353
+ inv_dx.mutable_data_ptr<float>(),
1354
+ inv_dy.mutable_data_ptr<float>(),
1355
+ inv_dz.mutable_data_ptr<float>(),
1356
+ static_cast<int>(periodic_x),
1357
+ static_cast<int>(periodic_y),
1358
+ static_cast<int>(periodic_z),
1359
+ ey.mutable_data_ptr<float>());
1360
+ WITWIN_CUDA_CHECK();
1361
+ }
1362
+
1363
+ void update_electric_ez_full_aniso_cuda(
1364
+ torch::stable::Tensor ez,
1365
+ const torch::stable::Tensor& hx,
1366
+ const torch::stable::Tensor& hy,
1367
+ const torch::stable::Tensor& hz,
1368
+ const torch::stable::Tensor& coeff_x,
1369
+ const torch::stable::Tensor& coeff_y,
1370
+ const torch::stable::Tensor& inv_dx,
1371
+ const torch::stable::Tensor& inv_dy,
1372
+ const torch::stable::Tensor& inv_dz,
1373
+ int64_t periodic_x,
1374
+ int64_t periodic_y,
1375
+ int64_t periodic_z) {
1376
+ const int64_t nx_nodes = ez.size(0);
1377
+ const int64_t ny_nodes = ez.size(1);
1378
+ const int64_t nz_nodes = ez.size(2) + 1;
1379
+ check_full_aniso_inputs(ez, hx, hy, hz, coeff_x, coeff_y, inv_dx, inv_dy, inv_dz,
1380
+ nx_nodes, ny_nodes, nz_nodes, "ez");
1381
+ torch::stable::accelerator::DeviceGuard guard(ez.get_device_index());
1382
+ const dim3 block = aniso_block3d();
1383
+ update_electric_ez_full_aniso_kernel<<<
1384
+ aniso_grid3d(ez.size(0), ez.size(1), ez.size(2), block), block, 0, current_cuda_stream()>>>(
1385
+ static_cast<unsigned int>(ez.size(0)),
1386
+ static_cast<unsigned int>(ez.size(1)),
1387
+ static_cast<unsigned int>(ez.size(2)),
1388
+ hx.mutable_data_ptr<float>(),
1389
+ hy.mutable_data_ptr<float>(),
1390
+ hz.mutable_data_ptr<float>(),
1391
+ coeff_x.mutable_data_ptr<float>(),
1392
+ coeff_y.mutable_data_ptr<float>(),
1393
+ inv_dx.mutable_data_ptr<float>(),
1394
+ inv_dy.mutable_data_ptr<float>(),
1395
+ inv_dz.mutable_data_ptr<float>(),
1396
+ static_cast<int>(periodic_x),
1397
+ static_cast<int>(periodic_y),
1398
+ static_cast<int>(periodic_z),
1399
+ ez.mutable_data_ptr<float>());
1400
+ WITWIN_CUDA_CHECK();
1401
+ }
1402
+
1403
+ void update_electric_ex_full_aniso_cpml_cuda(
1404
+ torch::stable::Tensor ex,
1405
+ const torch::stable::Tensor& hx,
1406
+ const torch::stable::Tensor& hy,
1407
+ const torch::stable::Tensor& hz,
1408
+ const torch::stable::Tensor& coeff_y,
1409
+ const torch::stable::Tensor& coeff_z,
1410
+ const torch::stable::Tensor& inv_dx,
1411
+ const torch::stable::Tensor& inv_dy,
1412
+ const torch::stable::Tensor& inv_dz,
1413
+ const torch::stable::Tensor& inv_kappa_x,
1414
+ const torch::stable::Tensor& b_x,
1415
+ const torch::stable::Tensor& c_x,
1416
+ const torch::stable::Tensor& inv_kappa_y,
1417
+ const torch::stable::Tensor& b_y,
1418
+ const torch::stable::Tensor& c_y,
1419
+ const torch::stable::Tensor& inv_kappa_z,
1420
+ const torch::stable::Tensor& b_z,
1421
+ const torch::stable::Tensor& c_z,
1422
+ int64_t periodic_x,
1423
+ int64_t periodic_y,
1424
+ int64_t periodic_z,
1425
+ torch::stable::Tensor psi_x,
1426
+ torch::stable::Tensor psi_y,
1427
+ torch::stable::Tensor psi_z) {
1428
+ const int64_t nx_nodes = ex.size(0) + 1;
1429
+ const int64_t ny_nodes = ex.size(1);
1430
+ const int64_t nz_nodes = ex.size(2);
1431
+ check_full_aniso_inputs(ex, hx, hy, hz, coeff_y, coeff_z, inv_dx, inv_dy, inv_dz,
1432
+ nx_nodes, ny_nodes, nz_nodes, "ex_cpml");
1433
+ check_aniso_cpml_axis(ex, inv_kappa_x, b_x, c_x, 0, "ex_cpml x");
1434
+ check_aniso_cpml_axis(ex, inv_kappa_y, b_y, c_y, 1, "ex_cpml y");
1435
+ check_aniso_cpml_axis(ex, inv_kappa_z, b_z, c_z, 2, "ex_cpml z");
1436
+ check_aniso_cpml_psi(ex, psi_x, psi_y, psi_z, "ex_cpml psi");
1437
+ torch::stable::accelerator::DeviceGuard guard(ex.get_device_index());
1438
+ const dim3 block = aniso_block3d();
1439
+ update_electric_ex_full_aniso_cpml_kernel<<<
1440
+ aniso_grid3d(ex.size(0), ex.size(1), ex.size(2), block), block, 0, current_cuda_stream()>>>(
1441
+ static_cast<unsigned int>(ex.size(0)),
1442
+ static_cast<unsigned int>(ex.size(1)),
1443
+ static_cast<unsigned int>(ex.size(2)),
1444
+ hx.mutable_data_ptr<float>(),
1445
+ hy.mutable_data_ptr<float>(),
1446
+ hz.mutable_data_ptr<float>(),
1447
+ coeff_y.mutable_data_ptr<float>(),
1448
+ coeff_z.mutable_data_ptr<float>(),
1449
+ inv_dx.mutable_data_ptr<float>(),
1450
+ inv_dy.mutable_data_ptr<float>(),
1451
+ inv_dz.mutable_data_ptr<float>(),
1452
+ inv_kappa_x.mutable_data_ptr<float>(),
1453
+ b_x.mutable_data_ptr<float>(),
1454
+ c_x.mutable_data_ptr<float>(),
1455
+ inv_kappa_y.mutable_data_ptr<float>(),
1456
+ b_y.mutable_data_ptr<float>(),
1457
+ c_y.mutable_data_ptr<float>(),
1458
+ inv_kappa_z.mutable_data_ptr<float>(),
1459
+ b_z.mutable_data_ptr<float>(),
1460
+ c_z.mutable_data_ptr<float>(),
1461
+ static_cast<int>(periodic_x),
1462
+ static_cast<int>(periodic_y),
1463
+ static_cast<int>(periodic_z),
1464
+ psi_x.mutable_data_ptr<float>(),
1465
+ psi_y.mutable_data_ptr<float>(),
1466
+ psi_z.mutable_data_ptr<float>(),
1467
+ ex.mutable_data_ptr<float>());
1468
+ WITWIN_CUDA_CHECK();
1469
+ }
1470
+
1471
+ void update_electric_ey_full_aniso_cpml_cuda(
1472
+ torch::stable::Tensor ey,
1473
+ const torch::stable::Tensor& hx,
1474
+ const torch::stable::Tensor& hy,
1475
+ const torch::stable::Tensor& hz,
1476
+ const torch::stable::Tensor& coeff_x,
1477
+ const torch::stable::Tensor& coeff_z,
1478
+ const torch::stable::Tensor& inv_dx,
1479
+ const torch::stable::Tensor& inv_dy,
1480
+ const torch::stable::Tensor& inv_dz,
1481
+ const torch::stable::Tensor& inv_kappa_x,
1482
+ const torch::stable::Tensor& b_x,
1483
+ const torch::stable::Tensor& c_x,
1484
+ const torch::stable::Tensor& inv_kappa_y,
1485
+ const torch::stable::Tensor& b_y,
1486
+ const torch::stable::Tensor& c_y,
1487
+ const torch::stable::Tensor& inv_kappa_z,
1488
+ const torch::stable::Tensor& b_z,
1489
+ const torch::stable::Tensor& c_z,
1490
+ int64_t periodic_x,
1491
+ int64_t periodic_y,
1492
+ int64_t periodic_z,
1493
+ torch::stable::Tensor psi_x,
1494
+ torch::stable::Tensor psi_y,
1495
+ torch::stable::Tensor psi_z) {
1496
+ const int64_t nx_nodes = ey.size(0);
1497
+ const int64_t ny_nodes = ey.size(1) + 1;
1498
+ const int64_t nz_nodes = ey.size(2);
1499
+ check_full_aniso_inputs(ey, hx, hy, hz, coeff_x, coeff_z, inv_dx, inv_dy, inv_dz,
1500
+ nx_nodes, ny_nodes, nz_nodes, "ey_cpml");
1501
+ check_aniso_cpml_axis(ey, inv_kappa_x, b_x, c_x, 0, "ey_cpml x");
1502
+ check_aniso_cpml_axis(ey, inv_kappa_y, b_y, c_y, 1, "ey_cpml y");
1503
+ check_aniso_cpml_axis(ey, inv_kappa_z, b_z, c_z, 2, "ey_cpml z");
1504
+ check_aniso_cpml_psi(ey, psi_x, psi_y, psi_z, "ey_cpml psi");
1505
+ torch::stable::accelerator::DeviceGuard guard(ey.get_device_index());
1506
+ const dim3 block = aniso_block3d();
1507
+ update_electric_ey_full_aniso_cpml_kernel<<<
1508
+ aniso_grid3d(ey.size(0), ey.size(1), ey.size(2), block), block, 0, current_cuda_stream()>>>(
1509
+ static_cast<unsigned int>(ey.size(0)),
1510
+ static_cast<unsigned int>(ey.size(1)),
1511
+ static_cast<unsigned int>(ey.size(2)),
1512
+ hx.mutable_data_ptr<float>(),
1513
+ hy.mutable_data_ptr<float>(),
1514
+ hz.mutable_data_ptr<float>(),
1515
+ coeff_x.mutable_data_ptr<float>(),
1516
+ coeff_z.mutable_data_ptr<float>(),
1517
+ inv_dx.mutable_data_ptr<float>(),
1518
+ inv_dy.mutable_data_ptr<float>(),
1519
+ inv_dz.mutable_data_ptr<float>(),
1520
+ inv_kappa_x.mutable_data_ptr<float>(),
1521
+ b_x.mutable_data_ptr<float>(),
1522
+ c_x.mutable_data_ptr<float>(),
1523
+ inv_kappa_y.mutable_data_ptr<float>(),
1524
+ b_y.mutable_data_ptr<float>(),
1525
+ c_y.mutable_data_ptr<float>(),
1526
+ inv_kappa_z.mutable_data_ptr<float>(),
1527
+ b_z.mutable_data_ptr<float>(),
1528
+ c_z.mutable_data_ptr<float>(),
1529
+ static_cast<int>(periodic_x),
1530
+ static_cast<int>(periodic_y),
1531
+ static_cast<int>(periodic_z),
1532
+ psi_x.mutable_data_ptr<float>(),
1533
+ psi_y.mutable_data_ptr<float>(),
1534
+ psi_z.mutable_data_ptr<float>(),
1535
+ ey.mutable_data_ptr<float>());
1536
+ WITWIN_CUDA_CHECK();
1537
+ }
1538
+
1539
+ void update_electric_ez_full_aniso_cpml_cuda(
1540
+ torch::stable::Tensor ez,
1541
+ const torch::stable::Tensor& hx,
1542
+ const torch::stable::Tensor& hy,
1543
+ const torch::stable::Tensor& hz,
1544
+ const torch::stable::Tensor& coeff_x,
1545
+ const torch::stable::Tensor& coeff_y,
1546
+ const torch::stable::Tensor& inv_dx,
1547
+ const torch::stable::Tensor& inv_dy,
1548
+ const torch::stable::Tensor& inv_dz,
1549
+ const torch::stable::Tensor& inv_kappa_x,
1550
+ const torch::stable::Tensor& b_x,
1551
+ const torch::stable::Tensor& c_x,
1552
+ const torch::stable::Tensor& inv_kappa_y,
1553
+ const torch::stable::Tensor& b_y,
1554
+ const torch::stable::Tensor& c_y,
1555
+ const torch::stable::Tensor& inv_kappa_z,
1556
+ const torch::stable::Tensor& b_z,
1557
+ const torch::stable::Tensor& c_z,
1558
+ int64_t periodic_x,
1559
+ int64_t periodic_y,
1560
+ int64_t periodic_z,
1561
+ torch::stable::Tensor psi_x,
1562
+ torch::stable::Tensor psi_y,
1563
+ torch::stable::Tensor psi_z) {
1564
+ const int64_t nx_nodes = ez.size(0);
1565
+ const int64_t ny_nodes = ez.size(1);
1566
+ const int64_t nz_nodes = ez.size(2) + 1;
1567
+ check_full_aniso_inputs(ez, hx, hy, hz, coeff_x, coeff_y, inv_dx, inv_dy, inv_dz,
1568
+ nx_nodes, ny_nodes, nz_nodes, "ez_cpml");
1569
+ check_aniso_cpml_axis(ez, inv_kappa_x, b_x, c_x, 0, "ez_cpml x");
1570
+ check_aniso_cpml_axis(ez, inv_kappa_y, b_y, c_y, 1, "ez_cpml y");
1571
+ check_aniso_cpml_axis(ez, inv_kappa_z, b_z, c_z, 2, "ez_cpml z");
1572
+ check_aniso_cpml_psi(ez, psi_x, psi_y, psi_z, "ez_cpml psi");
1573
+ torch::stable::accelerator::DeviceGuard guard(ez.get_device_index());
1574
+ const dim3 block = aniso_block3d();
1575
+ update_electric_ez_full_aniso_cpml_kernel<<<
1576
+ aniso_grid3d(ez.size(0), ez.size(1), ez.size(2), block), block, 0, current_cuda_stream()>>>(
1577
+ static_cast<unsigned int>(ez.size(0)),
1578
+ static_cast<unsigned int>(ez.size(1)),
1579
+ static_cast<unsigned int>(ez.size(2)),
1580
+ hx.mutable_data_ptr<float>(),
1581
+ hy.mutable_data_ptr<float>(),
1582
+ hz.mutable_data_ptr<float>(),
1583
+ coeff_x.mutable_data_ptr<float>(),
1584
+ coeff_y.mutable_data_ptr<float>(),
1585
+ inv_dx.mutable_data_ptr<float>(),
1586
+ inv_dy.mutable_data_ptr<float>(),
1587
+ inv_dz.mutable_data_ptr<float>(),
1588
+ inv_kappa_x.mutable_data_ptr<float>(),
1589
+ b_x.mutable_data_ptr<float>(),
1590
+ c_x.mutable_data_ptr<float>(),
1591
+ inv_kappa_y.mutable_data_ptr<float>(),
1592
+ b_y.mutable_data_ptr<float>(),
1593
+ c_y.mutable_data_ptr<float>(),
1594
+ inv_kappa_z.mutable_data_ptr<float>(),
1595
+ b_z.mutable_data_ptr<float>(),
1596
+ c_z.mutable_data_ptr<float>(),
1597
+ static_cast<int>(periodic_x),
1598
+ static_cast<int>(periodic_y),
1599
+ static_cast<int>(periodic_z),
1600
+ psi_x.mutable_data_ptr<float>(),
1601
+ psi_y.mutable_data_ptr<float>(),
1602
+ psi_z.mutable_data_ptr<float>(),
1603
+ ez.mutable_data_ptr<float>());
1604
+ WITWIN_CUDA_CHECK();
1605
+ }
1606
+
1607
+ void apply_aniso_offdiag_current_ex_cuda(
1608
+ torch::stable::Tensor ex,
1609
+ const torch::stable::Tensor& jy,
1610
+ const torch::stable::Tensor& jz,
1611
+ const torch::stable::Tensor& coeff_y,
1612
+ const torch::stable::Tensor& coeff_z,
1613
+ int64_t periodic_x,
1614
+ int64_t periodic_y,
1615
+ int64_t periodic_z) {
1616
+ const int64_t nx_nodes = ex.size(0) + 1;
1617
+ const int64_t ny_nodes = ex.size(1);
1618
+ const int64_t nz_nodes = ex.size(2);
1619
+ check_aniso_offdiag_current_inputs(ex, coeff_y, coeff_z, "ex_offdiag_current");
1620
+ check_current_field(jy, ex, nx_nodes, ny_nodes - 1, nz_nodes, "ex_offdiag_current jy");
1621
+ check_current_field(jz, ex, nx_nodes, ny_nodes, nz_nodes - 1, "ex_offdiag_current jz");
1622
+ torch::stable::accelerator::DeviceGuard guard(ex.get_device_index());
1623
+ const dim3 block = aniso_block3d();
1624
+ apply_aniso_offdiag_current_ex_kernel<<<
1625
+ aniso_grid3d(ex.size(0), ex.size(1), ex.size(2), block), block, 0, current_cuda_stream()>>>(
1626
+ static_cast<unsigned int>(ex.size(0)),
1627
+ static_cast<unsigned int>(ex.size(1)),
1628
+ static_cast<unsigned int>(ex.size(2)),
1629
+ jy.mutable_data_ptr<float>(),
1630
+ jz.mutable_data_ptr<float>(),
1631
+ coeff_y.mutable_data_ptr<float>(),
1632
+ coeff_z.mutable_data_ptr<float>(),
1633
+ static_cast<int>(periodic_x),
1634
+ static_cast<int>(periodic_y),
1635
+ static_cast<int>(periodic_z),
1636
+ ex.mutable_data_ptr<float>());
1637
+ WITWIN_CUDA_CHECK();
1638
+ }
1639
+
1640
+ void apply_aniso_offdiag_current_ey_cuda(
1641
+ torch::stable::Tensor ey,
1642
+ const torch::stable::Tensor& jx,
1643
+ const torch::stable::Tensor& jz,
1644
+ const torch::stable::Tensor& coeff_x,
1645
+ const torch::stable::Tensor& coeff_z,
1646
+ int64_t periodic_x,
1647
+ int64_t periodic_y,
1648
+ int64_t periodic_z) {
1649
+ const int64_t nx_nodes = ey.size(0);
1650
+ const int64_t ny_nodes = ey.size(1) + 1;
1651
+ const int64_t nz_nodes = ey.size(2);
1652
+ check_aniso_offdiag_current_inputs(ey, coeff_x, coeff_z, "ey_offdiag_current");
1653
+ check_current_field(jx, ey, nx_nodes - 1, ny_nodes, nz_nodes, "ey_offdiag_current jx");
1654
+ check_current_field(jz, ey, nx_nodes, ny_nodes, nz_nodes - 1, "ey_offdiag_current jz");
1655
+ torch::stable::accelerator::DeviceGuard guard(ey.get_device_index());
1656
+ const dim3 block = aniso_block3d();
1657
+ apply_aniso_offdiag_current_ey_kernel<<<
1658
+ aniso_grid3d(ey.size(0), ey.size(1), ey.size(2), block), block, 0, current_cuda_stream()>>>(
1659
+ static_cast<unsigned int>(ey.size(0)),
1660
+ static_cast<unsigned int>(ey.size(1)),
1661
+ static_cast<unsigned int>(ey.size(2)),
1662
+ jx.mutable_data_ptr<float>(),
1663
+ jz.mutable_data_ptr<float>(),
1664
+ coeff_x.mutable_data_ptr<float>(),
1665
+ coeff_z.mutable_data_ptr<float>(),
1666
+ static_cast<int>(periodic_x),
1667
+ static_cast<int>(periodic_y),
1668
+ static_cast<int>(periodic_z),
1669
+ ey.mutable_data_ptr<float>());
1670
+ WITWIN_CUDA_CHECK();
1671
+ }
1672
+
1673
+ void apply_aniso_offdiag_current_ez_cuda(
1674
+ torch::stable::Tensor ez,
1675
+ const torch::stable::Tensor& jx,
1676
+ const torch::stable::Tensor& jy,
1677
+ const torch::stable::Tensor& coeff_x,
1678
+ const torch::stable::Tensor& coeff_y,
1679
+ int64_t periodic_x,
1680
+ int64_t periodic_y,
1681
+ int64_t periodic_z) {
1682
+ const int64_t nx_nodes = ez.size(0);
1683
+ const int64_t ny_nodes = ez.size(1);
1684
+ const int64_t nz_nodes = ez.size(2) + 1;
1685
+ check_aniso_offdiag_current_inputs(ez, coeff_x, coeff_y, "ez_offdiag_current");
1686
+ check_current_field(jx, ez, nx_nodes - 1, ny_nodes, nz_nodes, "ez_offdiag_current jx");
1687
+ check_current_field(jy, ez, nx_nodes, ny_nodes - 1, nz_nodes, "ez_offdiag_current jy");
1688
+ torch::stable::accelerator::DeviceGuard guard(ez.get_device_index());
1689
+ const dim3 block = aniso_block3d();
1690
+ apply_aniso_offdiag_current_ez_kernel<<<
1691
+ aniso_grid3d(ez.size(0), ez.size(1), ez.size(2), block), block, 0, current_cuda_stream()>>>(
1692
+ static_cast<unsigned int>(ez.size(0)),
1693
+ static_cast<unsigned int>(ez.size(1)),
1694
+ static_cast<unsigned int>(ez.size(2)),
1695
+ jx.mutable_data_ptr<float>(),
1696
+ jy.mutable_data_ptr<float>(),
1697
+ coeff_x.mutable_data_ptr<float>(),
1698
+ coeff_y.mutable_data_ptr<float>(),
1699
+ static_cast<int>(periodic_x),
1700
+ static_cast<int>(periodic_y),
1701
+ static_cast<int>(periodic_z),
1702
+ ez.mutable_data_ptr<float>());
1703
+ WITWIN_CUDA_CHECK();
1704
+ }