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,1482 @@
1
+ #include <cmath>
2
+ #include <type_traits>
3
+
4
+ #include "../launch.h"
5
+ #include "../tensors.h"
6
+ #include "common.cuh"
7
+
8
+ namespace {
9
+
10
+ struct Shape3D {
11
+ int x;
12
+ int y;
13
+ int z;
14
+ };
15
+
16
+ dim3 patch_block3d() {
17
+ return dim3(32, 4, 2);
18
+ }
19
+
20
+ dim3 patch_grid3d(Shape3D shape, dim3 block) {
21
+ return dim3(
22
+ static_cast<unsigned int>((shape.z + block.x - 1) / block.x),
23
+ static_cast<unsigned int>((shape.y + block.y - 1) / block.y),
24
+ static_cast<unsigned int>((shape.x + block.z - 1) / block.z));
25
+ }
26
+
27
+ __device__ __forceinline__ long long offset_shape(int i, int j, int k, Shape3D shape) {
28
+ return (static_cast<long long>(i) * shape.y + j) * shape.z + k;
29
+ }
30
+
31
+ template <int Axis>
32
+ __device__ __forceinline__ long long offset_replace_axis(int i, int j, int k, int value, Shape3D shape) {
33
+ if constexpr (Axis == 0) {
34
+ return offset_shape(value, j, k, shape);
35
+ } else if constexpr (Axis == 1) {
36
+ return offset_shape(i, value, k, shape);
37
+ } else {
38
+ return offset_shape(i, j, value, shape);
39
+ }
40
+ }
41
+
42
+ template <int AxisA, int AxisB>
43
+ __device__ __forceinline__ long long offset_replace_two_axes(
44
+ int i,
45
+ int j,
46
+ int k,
47
+ int value_a,
48
+ int value_b,
49
+ Shape3D shape) {
50
+ if constexpr (AxisA == 0) {
51
+ i = value_a;
52
+ } else if constexpr (AxisA == 1) {
53
+ j = value_a;
54
+ } else {
55
+ k = value_a;
56
+ }
57
+ if constexpr (AxisB == 0) {
58
+ i = value_b;
59
+ } else if constexpr (AxisB == 1) {
60
+ j = value_b;
61
+ } else {
62
+ k = value_b;
63
+ }
64
+ return offset_shape(i, j, k, shape);
65
+ }
66
+
67
+ __device__ __forceinline__ bool in_bounds(int i, int j, int k, Shape3D shape) {
68
+ return i >= 0 && i < shape.x && j >= 0 && j < shape.y && k >= 0 && k < shape.z;
69
+ }
70
+
71
+ template <int TimeKind>
72
+ __device__ __forceinline__ float evaluate_source_time(
73
+ float sample_time,
74
+ float angular_frequency,
75
+ float gaussian_inv_sigma,
76
+ float ricker_pi_frequency,
77
+ float amplitude,
78
+ float phase,
79
+ float delay) {
80
+ if constexpr (TimeKind == 0) {
81
+ return amplitude * cosf(angular_frequency * sample_time + phase);
82
+ }
83
+ if constexpr (TimeKind == 1) {
84
+ const float tau = sample_time - delay;
85
+ const float normalized = tau * gaussian_inv_sigma;
86
+ const float envelope = expf(-0.5f * normalized * normalized);
87
+ return amplitude * envelope * cosf(angular_frequency * tau + phase);
88
+ }
89
+ const float tau = sample_time - delay;
90
+ const float alpha = ricker_pi_frequency * tau;
91
+ const float alpha_sq = alpha * alpha;
92
+ return amplitude * (1.0f - 2.0f * alpha_sq) * expf(-alpha_sq);
93
+ }
94
+
95
+ __device__ __forceinline__ float2 phase_positive(float phase_cos, float phase_sin, float2 value) {
96
+ return make_float2(
97
+ phase_cos * value.x - phase_sin * value.y,
98
+ phase_sin * value.x + phase_cos * value.y);
99
+ }
100
+
101
+ __device__ __forceinline__ float2 phase_negative(float phase_cos, float phase_sin, float2 value) {
102
+ return make_float2(
103
+ phase_cos * value.x + phase_sin * value.y,
104
+ phase_cos * value.y - phase_sin * value.x);
105
+ }
106
+
107
+ __device__ __forceinline__ void add_real_direct(float* __restrict__ field, Shape3D shape, int i, int j, int k, float value) {
108
+ if (in_bounds(i, j, k, shape)) {
109
+ field[offset_shape(i, j, k, shape)] += value;
110
+ }
111
+ }
112
+
113
+ template <bool CheckBounds>
114
+ __device__ __forceinline__ void add_real_patch_value(
115
+ float* __restrict__ field,
116
+ Shape3D shape,
117
+ int i,
118
+ int j,
119
+ int k,
120
+ float value) {
121
+ if constexpr (CheckBounds) {
122
+ add_real_direct(field, shape, i, j, k, value);
123
+ } else {
124
+ field[offset_shape(i, j, k, shape)] += value;
125
+ }
126
+ }
127
+
128
+ bool patch_contained(Shape3D field_shape, Shape3D patch_shape, int64_t offset_i, int64_t offset_j, int64_t offset_k) {
129
+ return offset_i >= 0 && offset_j >= 0 && offset_k >= 0
130
+ && offset_i + static_cast<int64_t>(patch_shape.x) <= field_shape.x
131
+ && offset_j + static_cast<int64_t>(patch_shape.y) <= field_shape.y
132
+ && offset_k + static_cast<int64_t>(patch_shape.z) <= field_shape.z;
133
+ }
134
+
135
+ template <bool CheckBounds>
136
+ __global__ void add_source_patch_kernel(
137
+ Shape3D field_shape,
138
+ Shape3D patch_shape,
139
+ int offset_i,
140
+ int offset_j,
141
+ int offset_k,
142
+ float signal,
143
+ float* __restrict__ field,
144
+ const float* __restrict__ patch) {
145
+ const int k = static_cast<int>(blockIdx.x * blockDim.x + threadIdx.x);
146
+ const int j = static_cast<int>(blockIdx.y * blockDim.y + threadIdx.y);
147
+ const int i = static_cast<int>(blockIdx.z * blockDim.z + threadIdx.z);
148
+ if (!in_bounds(i, j, k, patch_shape)) {
149
+ return;
150
+ }
151
+ const long long linear = offset_shape(i, j, k, patch_shape);
152
+ const float value = signal * patch[linear];
153
+ add_real_patch_value<CheckBounds>(field, field_shape, offset_i + i, offset_j + j, offset_k + k, value);
154
+ }
155
+
156
+ template <bool CheckBounds>
157
+ __global__ void add_cw_phased_source_patch_kernel(
158
+ Shape3D field_shape,
159
+ Shape3D patch_shape,
160
+ int offset_i,
161
+ int offset_j,
162
+ int offset_k,
163
+ float signal_cos,
164
+ float signal_sin,
165
+ float* __restrict__ field,
166
+ const float* __restrict__ patch_cos,
167
+ const float* __restrict__ patch_sin) {
168
+ const int k = static_cast<int>(blockIdx.x * blockDim.x + threadIdx.x);
169
+ const int j = static_cast<int>(blockIdx.y * blockDim.y + threadIdx.y);
170
+ const int i = static_cast<int>(blockIdx.z * blockDim.z + threadIdx.z);
171
+ if (!in_bounds(i, j, k, patch_shape)) {
172
+ return;
173
+ }
174
+ const long long linear = offset_shape(i, j, k, patch_shape);
175
+ const float value = signal_cos * patch_cos[linear] + signal_sin * patch_sin[linear];
176
+ add_real_patch_value<CheckBounds>(field, field_shape, offset_i + i, offset_j + j, offset_k + k, value);
177
+ }
178
+
179
+ template <int TimeKind, bool CheckBounds>
180
+ __global__ void add_time_shifted_source_patch_kernel(
181
+ Shape3D field_shape,
182
+ Shape3D patch_shape,
183
+ int offset_i,
184
+ int offset_j,
185
+ int offset_k,
186
+ float time,
187
+ float angular_frequency,
188
+ float gaussian_inv_sigma,
189
+ float ricker_pi_frequency,
190
+ float amplitude,
191
+ float phase,
192
+ float delay,
193
+ int causal_gate,
194
+ float* __restrict__ field,
195
+ const float* __restrict__ patch,
196
+ const float* __restrict__ delay_patch,
197
+ const float* __restrict__ activation_delay_patch) {
198
+ const int k = static_cast<int>(blockIdx.x * blockDim.x + threadIdx.x);
199
+ const int j = static_cast<int>(blockIdx.y * blockDim.y + threadIdx.y);
200
+ const int i = static_cast<int>(blockIdx.z * blockDim.z + threadIdx.z);
201
+ if (!in_bounds(i, j, k, patch_shape)) {
202
+ return;
203
+ }
204
+ const long long linear = offset_shape(i, j, k, patch_shape);
205
+ if (causal_gate != 0) {
206
+ if (time < activation_delay_patch[linear]) {
207
+ return;
208
+ }
209
+ }
210
+ const float sample_time = time - delay_patch[linear];
211
+ const float signal = evaluate_source_time<TimeKind>(
212
+ sample_time,
213
+ angular_frequency,
214
+ gaussian_inv_sigma,
215
+ ricker_pi_frequency,
216
+ amplitude,
217
+ phase,
218
+ delay);
219
+ add_real_patch_value<CheckBounds>(
220
+ field, field_shape, offset_i + i, offset_j + j, offset_k + k, signal * patch[linear]);
221
+ }
222
+
223
+ template <int TimeKind, bool CheckBounds>
224
+ void launch_time_shifted_source_patch(
225
+ Shape3D field_shape,
226
+ Shape3D patch_shape,
227
+ int offset_i,
228
+ int offset_j,
229
+ int offset_k,
230
+ float time,
231
+ float angular_frequency,
232
+ float gaussian_inv_sigma,
233
+ float ricker_pi_frequency,
234
+ float amplitude,
235
+ float phase,
236
+ float delay,
237
+ int causal_gate,
238
+ float* __restrict__ field,
239
+ const float* __restrict__ patch,
240
+ const float* __restrict__ delay_patch,
241
+ const float* __restrict__ activation_delay_patch) {
242
+ const dim3 block = patch_block3d();
243
+ add_time_shifted_source_patch_kernel<TimeKind, CheckBounds><<<patch_grid3d(patch_shape, block), block, 0, current_cuda_stream()>>>(
244
+ field_shape,
245
+ patch_shape,
246
+ offset_i,
247
+ offset_j,
248
+ offset_k,
249
+ time,
250
+ angular_frequency,
251
+ gaussian_inv_sigma,
252
+ ricker_pi_frequency,
253
+ amplitude,
254
+ phase,
255
+ delay,
256
+ causal_gate,
257
+ field,
258
+ patch,
259
+ delay_patch,
260
+ activation_delay_patch);
261
+ }
262
+
263
+ template <int AxisA, int AxisB, bool WrapA, bool WrapB>
264
+ __global__ void add_periodic_source_patch_kernel(
265
+ Shape3D field_shape,
266
+ Shape3D patch_shape,
267
+ int offset_i,
268
+ int offset_j,
269
+ int offset_k,
270
+ float signal,
271
+ float* __restrict__ field,
272
+ const float* __restrict__ patch) {
273
+ const int k = static_cast<int>(blockIdx.x * blockDim.x + threadIdx.x);
274
+ const int j = static_cast<int>(blockIdx.y * blockDim.y + threadIdx.y);
275
+ const int i = static_cast<int>(blockIdx.z * blockDim.z + threadIdx.z);
276
+ if (!in_bounds(i, j, k, patch_shape)) {
277
+ return;
278
+ }
279
+ const long long linear = offset_shape(i, j, k, patch_shape);
280
+ const int ci = offset_i + i;
281
+ const int cj = offset_j + j;
282
+ const int ck = offset_k + k;
283
+ if (!in_bounds(ci, cj, ck, field_shape)) {
284
+ return;
285
+ }
286
+ const int axis_a_coord = AxisA == 0 ? ci : (AxisA == 1 ? cj : ck);
287
+ const int axis_b_coord = AxisB == 0 ? ci : (AxisB == 1 ? cj : ck);
288
+ const int axis_a_size = AxisA == 0 ? field_shape.x : (AxisA == 1 ? field_shape.y : field_shape.z);
289
+ const int axis_b_size = AxisB == 0 ? field_shape.x : (AxisB == 1 ? field_shape.y : field_shape.z);
290
+ const float delta = signal * patch[linear];
291
+ const long long field_linear = offset_shape(ci, cj, ck, field_shape);
292
+ const bool boundary_a = WrapA && (axis_a_coord == 0 || axis_a_coord + 1 >= axis_a_size);
293
+ const bool boundary_b = WrapB && (axis_b_coord == 0 || axis_b_coord + 1 >= axis_b_size);
294
+ if constexpr (!WrapA && !WrapB) {
295
+ field[field_linear] += delta;
296
+ return;
297
+ } else {
298
+ if (!boundary_a && !boundary_b) {
299
+ field[field_linear] += delta;
300
+ return;
301
+ }
302
+ atomicAdd(field + field_linear, delta);
303
+ }
304
+
305
+ const int pair_a = axis_a_coord == 0 ? axis_a_size - 1 : 0;
306
+ const int pair_b = axis_b_coord == 0 ? axis_b_size - 1 : 0;
307
+ if (boundary_a) {
308
+ atomicAdd(field + offset_replace_axis<AxisA>(ci, cj, ck, pair_a, field_shape), delta);
309
+ }
310
+ if (boundary_b) {
311
+ atomicAdd(field + offset_replace_axis<AxisB>(ci, cj, ck, pair_b, field_shape), delta);
312
+ }
313
+ if (boundary_a && boundary_b) {
314
+ atomicAdd(field + offset_replace_two_axes<AxisA, AxisB>(ci, cj, ck, pair_a, pair_b, field_shape), delta);
315
+ }
316
+ }
317
+
318
+ template <int AxisA, int AxisB, bool WrapA, bool WrapB>
319
+ void launch_periodic_source_patch(
320
+ Shape3D field_shape,
321
+ Shape3D patch_shape,
322
+ int offset_i,
323
+ int offset_j,
324
+ int offset_k,
325
+ float signal,
326
+ float* __restrict__ field,
327
+ const float* __restrict__ patch) {
328
+ const dim3 block = patch_block3d();
329
+ add_periodic_source_patch_kernel<AxisA, AxisB, WrapA, WrapB><<<patch_grid3d(patch_shape, block), block, 0, current_cuda_stream()>>>(
330
+ field_shape,
331
+ patch_shape,
332
+ offset_i,
333
+ offset_j,
334
+ offset_k,
335
+ signal,
336
+ field,
337
+ patch);
338
+ }
339
+
340
+ template <int AxisA, int AxisB>
341
+ void dispatch_periodic_source_wraps(
342
+ Shape3D field_shape,
343
+ Shape3D patch_shape,
344
+ int offset_i,
345
+ int offset_j,
346
+ int offset_k,
347
+ float signal,
348
+ int wrap_a,
349
+ int wrap_b,
350
+ float* __restrict__ field,
351
+ const float* __restrict__ patch) {
352
+ if (wrap_a != 0 && wrap_b != 0) {
353
+ launch_periodic_source_patch<AxisA, AxisB, true, true>(
354
+ field_shape, patch_shape, offset_i, offset_j, offset_k, signal, field, patch);
355
+ } else if (wrap_a != 0) {
356
+ launch_periodic_source_patch<AxisA, AxisB, true, false>(
357
+ field_shape, patch_shape, offset_i, offset_j, offset_k, signal, field, patch);
358
+ } else if (wrap_b != 0) {
359
+ launch_periodic_source_patch<AxisA, AxisB, false, true>(
360
+ field_shape, patch_shape, offset_i, offset_j, offset_k, signal, field, patch);
361
+ } else {
362
+ launch_periodic_source_patch<AxisA, AxisB, false, false>(
363
+ field_shape, patch_shape, offset_i, offset_j, offset_k, signal, field, patch);
364
+ }
365
+ }
366
+
367
+ template <int AxisA, int AxisB>
368
+ __global__ void add_bloch_source_patch_kernel(
369
+ Shape3D field_shape,
370
+ Shape3D patch_shape,
371
+ int offset_i,
372
+ int offset_j,
373
+ int offset_k,
374
+ float signal_real,
375
+ float signal_imag,
376
+ float phase_cos_a,
377
+ float phase_sin_a,
378
+ float phase_cos_b,
379
+ float phase_sin_b,
380
+ int wrap_a,
381
+ int wrap_b,
382
+ float* __restrict__ real,
383
+ float* __restrict__ imag,
384
+ const float* __restrict__ patch) {
385
+ const int k = static_cast<int>(blockIdx.x * blockDim.x + threadIdx.x);
386
+ const int j = static_cast<int>(blockIdx.y * blockDim.y + threadIdx.y);
387
+ const int i = static_cast<int>(blockIdx.z * blockDim.z + threadIdx.z);
388
+ if (!in_bounds(i, j, k, patch_shape)) {
389
+ return;
390
+ }
391
+ const long long linear = offset_shape(i, j, k, patch_shape);
392
+ const int ci = offset_i + i;
393
+ const int cj = offset_j + j;
394
+ const int ck = offset_k + k;
395
+ if (!in_bounds(ci, cj, ck, field_shape)) {
396
+ return;
397
+ }
398
+
399
+ const int axis_a_coord = AxisA == 0 ? ci : (AxisA == 1 ? cj : ck);
400
+ const int axis_b_coord = AxisB == 0 ? ci : (AxisB == 1 ? cj : ck);
401
+ const int axis_a_size = AxisA == 0 ? field_shape.x : (AxisA == 1 ? field_shape.y : field_shape.z);
402
+ const int axis_b_size = AxisB == 0 ? field_shape.x : (AxisB == 1 ? field_shape.y : field_shape.z);
403
+ const float amplitude = patch[linear];
404
+ const float2 delta = make_float2(signal_real * amplitude, signal_imag * amplitude);
405
+ const long long field_linear = offset_shape(ci, cj, ck, field_shape);
406
+ const bool boundary_a = wrap_a != 0 && (axis_a_coord == 0 || axis_a_coord + 1 >= axis_a_size);
407
+ const bool boundary_b = wrap_b != 0 && (axis_b_coord == 0 || axis_b_coord + 1 >= axis_b_size);
408
+ if (!boundary_a && !boundary_b) {
409
+ real[field_linear] += delta.x;
410
+ imag[field_linear] += delta.y;
411
+ return;
412
+ }
413
+ atomicAdd(real + field_linear, delta.x);
414
+ atomicAdd(imag + field_linear, delta.y);
415
+
416
+ const int pair_a = axis_a_coord == 0 ? axis_a_size - 1 : 0;
417
+ const int pair_b = axis_b_coord == 0 ? axis_b_size - 1 : 0;
418
+ if (boundary_a) {
419
+ const float2 value = axis_a_coord == 0
420
+ ? phase_positive(phase_cos_a, phase_sin_a, delta)
421
+ : phase_negative(phase_cos_a, phase_sin_a, delta);
422
+ const long long offset = offset_replace_axis<AxisA>(ci, cj, ck, pair_a, field_shape);
423
+ atomicAdd(real + offset, value.x);
424
+ atomicAdd(imag + offset, value.y);
425
+ }
426
+ if (boundary_b) {
427
+ const float2 value = axis_b_coord == 0
428
+ ? phase_positive(phase_cos_b, phase_sin_b, delta)
429
+ : phase_negative(phase_cos_b, phase_sin_b, delta);
430
+ const long long offset = offset_replace_axis<AxisB>(ci, cj, ck, pair_b, field_shape);
431
+ atomicAdd(real + offset, value.x);
432
+ atomicAdd(imag + offset, value.y);
433
+ }
434
+ if (boundary_a && boundary_b) {
435
+ float2 value = axis_a_coord == 0
436
+ ? phase_positive(phase_cos_a, phase_sin_a, delta)
437
+ : phase_negative(phase_cos_a, phase_sin_a, delta);
438
+ value = axis_b_coord == 0
439
+ ? phase_positive(phase_cos_b, phase_sin_b, value)
440
+ : phase_negative(phase_cos_b, phase_sin_b, value);
441
+ const long long offset = offset_replace_two_axes<AxisA, AxisB>(ci, cj, ck, pair_a, pair_b, field_shape);
442
+ atomicAdd(real + offset, value.x);
443
+ atomicAdd(imag + offset, value.y);
444
+ }
445
+ }
446
+
447
+ template <int AxisA, int AxisB>
448
+ void launch_bloch_source_patch(
449
+ Shape3D field_shape,
450
+ Shape3D patch_shape,
451
+ int offset_i,
452
+ int offset_j,
453
+ int offset_k,
454
+ float signal_real,
455
+ float signal_imag,
456
+ float phase_cos_a,
457
+ float phase_sin_a,
458
+ float phase_cos_b,
459
+ float phase_sin_b,
460
+ int wrap_a,
461
+ int wrap_b,
462
+ float* __restrict__ real,
463
+ float* __restrict__ imag,
464
+ const float* __restrict__ patch) {
465
+ const dim3 block = patch_block3d();
466
+ add_bloch_source_patch_kernel<AxisA, AxisB><<<patch_grid3d(patch_shape, block), block, 0, current_cuda_stream()>>>(
467
+ field_shape,
468
+ patch_shape,
469
+ offset_i,
470
+ offset_j,
471
+ offset_k,
472
+ signal_real,
473
+ signal_imag,
474
+ phase_cos_a,
475
+ phase_sin_a,
476
+ phase_cos_b,
477
+ phase_sin_b,
478
+ wrap_a,
479
+ wrap_b,
480
+ real,
481
+ imag,
482
+ patch);
483
+ }
484
+
485
+ __global__ void add_scaled_slice_source_patch_kernel(
486
+ Shape3D field_shape,
487
+ Shape3D patch_shape,
488
+ int sample_index,
489
+ int offset_i,
490
+ int offset_j,
491
+ int offset_k,
492
+ float scale,
493
+ float* __restrict__ field,
494
+ const float* __restrict__ patch,
495
+ const float* __restrict__ incident) {
496
+ const int k = static_cast<int>(blockIdx.x * blockDim.x + threadIdx.x);
497
+ const int j = static_cast<int>(blockIdx.y * blockDim.y + threadIdx.y);
498
+ const int i = static_cast<int>(blockIdx.z * blockDim.z + threadIdx.z);
499
+ if (!in_bounds(i, j, k, patch_shape)) {
500
+ return;
501
+ }
502
+ const long long linear = offset_shape(i, j, k, patch_shape);
503
+ add_real_direct(
504
+ field,
505
+ field_shape,
506
+ offset_i + i,
507
+ offset_j + j,
508
+ offset_k + k,
509
+ scale * incident[sample_index] * patch[linear]);
510
+ }
511
+
512
+ template <int SampleAxis>
513
+ __global__ void add_scaled_line_source_patch_kernel(
514
+ Shape3D field_shape,
515
+ Shape3D patch_shape,
516
+ int offset_i,
517
+ int offset_j,
518
+ int offset_k,
519
+ float scale,
520
+ float* __restrict__ field,
521
+ const float* __restrict__ patch,
522
+ const float* __restrict__ incident,
523
+ const int* __restrict__ sample_indices) {
524
+ const int k = static_cast<int>(blockIdx.x * blockDim.x + threadIdx.x);
525
+ const int j = static_cast<int>(blockIdx.y * blockDim.y + threadIdx.y);
526
+ const int i = static_cast<int>(blockIdx.z * blockDim.z + threadIdx.z);
527
+ if (!in_bounds(i, j, k, patch_shape)) {
528
+ return;
529
+ }
530
+ const long long linear = offset_shape(i, j, k, patch_shape);
531
+ int sample_linear = k;
532
+ if constexpr (SampleAxis == 0) {
533
+ sample_linear = i;
534
+ } else if constexpr (SampleAxis == 1) {
535
+ sample_linear = j;
536
+ }
537
+ const int sample_index = sample_indices[sample_linear];
538
+ add_real_direct(
539
+ field,
540
+ field_shape,
541
+ offset_i + i,
542
+ offset_j + j,
543
+ offset_k + k,
544
+ scale * incident[sample_index] * patch[linear]);
545
+ }
546
+
547
+ template <int SampleAxis>
548
+ void launch_scaled_line_source_patch(
549
+ Shape3D field_shape,
550
+ Shape3D patch_shape,
551
+ int offset_i,
552
+ int offset_j,
553
+ int offset_k,
554
+ float scale,
555
+ float* __restrict__ field,
556
+ const float* __restrict__ patch,
557
+ const float* __restrict__ incident,
558
+ const int* __restrict__ sample_indices) {
559
+ const dim3 block = patch_block3d();
560
+ add_scaled_line_source_patch_kernel<SampleAxis><<<patch_grid3d(patch_shape, block), block, 0, current_cuda_stream()>>>(
561
+ field_shape,
562
+ patch_shape,
563
+ offset_i,
564
+ offset_j,
565
+ offset_k,
566
+ scale,
567
+ field,
568
+ patch,
569
+ incident,
570
+ sample_indices);
571
+ }
572
+
573
+ __device__ __forceinline__ float interpolated_incident(
574
+ const float* __restrict__ incident,
575
+ int incident_count,
576
+ float position,
577
+ float origin,
578
+ float inv_ds) {
579
+ const float max_index = static_cast<float>(incident_count - 1);
580
+ float coord = inv_ds > 0.0f ? (position - origin) * inv_ds : 0.0f;
581
+ coord = fminf(fmaxf(coord, 0.0f), max_index);
582
+ const int lower = static_cast<int>(coord);
583
+ const int upper = min(lower + 1, incident_count - 1);
584
+ const float frac = coord - static_cast<float>(lower);
585
+ return incident[lower] + frac * (incident[upper] - incident[lower]);
586
+ }
587
+
588
+ __global__ void add_interpolated_source_patch_kernel(
589
+ Shape3D field_shape,
590
+ Shape3D patch_shape,
591
+ int incident_count,
592
+ float origin,
593
+ float inv_ds,
594
+ int offset_i,
595
+ int offset_j,
596
+ int offset_k,
597
+ float scale,
598
+ float* __restrict__ field,
599
+ const float* __restrict__ patch,
600
+ const float* __restrict__ incident,
601
+ const float* __restrict__ sample_positions) {
602
+ const int k = static_cast<int>(blockIdx.x * blockDim.x + threadIdx.x);
603
+ const int j = static_cast<int>(blockIdx.y * blockDim.y + threadIdx.y);
604
+ const int i = static_cast<int>(blockIdx.z * blockDim.z + threadIdx.z);
605
+ if (!in_bounds(i, j, k, patch_shape)) {
606
+ return;
607
+ }
608
+ const long long linear = offset_shape(i, j, k, patch_shape);
609
+ const float sampled = interpolated_incident(incident, incident_count, sample_positions[linear], origin, inv_ds);
610
+ add_real_direct(
611
+ field,
612
+ field_shape,
613
+ offset_i + i,
614
+ offset_j + j,
615
+ offset_k + k,
616
+ scale * sampled * patch[linear]);
617
+ }
618
+
619
+ template <typename OffsetT>
620
+ __device__ __forceinline__ void add_to_field_code_offset(
621
+ int field_code,
622
+ float* __restrict__ field_x,
623
+ float* __restrict__ field_y,
624
+ float* __restrict__ field_z,
625
+ OffsetT offset,
626
+ float value) {
627
+ if (field_code == 0) {
628
+ atomicAdd(field_x + offset, value);
629
+ } else if (field_code == 1) {
630
+ atomicAdd(field_y + offset, value);
631
+ } else {
632
+ atomicAdd(field_z + offset, value);
633
+ }
634
+ }
635
+
636
+ __device__ __forceinline__ float warp_reduce_sum_active(float value, unsigned int mask) {
637
+ const unsigned int lane = threadIdx.x & 31u;
638
+ #pragma unroll
639
+ for (int delta = 16; delta > 0; delta >>= 1) {
640
+ const float other = __shfl_down_sync(mask, value, delta);
641
+ if ((lane + static_cast<unsigned int>(delta)) < 32u && ((mask >> (lane + delta)) & 1u) != 0u) {
642
+ value += other;
643
+ }
644
+ }
645
+ return value;
646
+ }
647
+
648
+ __device__ __forceinline__ void add_to_field_code_offset_warp_aggregated(
649
+ int field_code,
650
+ float* __restrict__ field_x,
651
+ float* __restrict__ field_y,
652
+ float* __restrict__ field_z,
653
+ int offset,
654
+ float value) {
655
+ const unsigned int mask = __activemask();
656
+ const unsigned int lane = threadIdx.x & 31u;
657
+ const unsigned long long key =
658
+ (static_cast<unsigned long long>(static_cast<unsigned int>(field_code)) << 32)
659
+ | static_cast<unsigned int>(offset);
660
+ const unsigned long long prev_key = __shfl_up_sync(mask, key, 1);
661
+ const unsigned long long next_key = __shfl_down_sync(mask, key, 1);
662
+ const bool adjacent_repeat =
663
+ ((lane > 0u) && prev_key == key)
664
+ || ((lane < 31u) && next_key == key);
665
+ if (!__any_sync(mask, adjacent_repeat)) {
666
+ add_to_field_code_offset(field_code, field_x, field_y, field_z, offset, value);
667
+ return;
668
+ }
669
+
670
+ const unsigned int peers = __match_any_sync(mask, key);
671
+ if ((peers & (peers - 1u)) == 0u) {
672
+ add_to_field_code_offset(field_code, field_x, field_y, field_z, offset, value);
673
+ return;
674
+ }
675
+ if (peers == mask) {
676
+ const float sum = warp_reduce_sum_active(value, mask);
677
+ if (lane == static_cast<unsigned int>(__ffs(peers) - 1)) {
678
+ add_to_field_code_offset(field_code, field_x, field_y, field_z, offset, sum);
679
+ }
680
+ return;
681
+ }
682
+
683
+ float sum = 0.0f;
684
+ unsigned int remaining = peers;
685
+ while (remaining != 0u) {
686
+ const int source_lane = __ffs(remaining) - 1;
687
+ sum += __shfl_sync(peers, value, source_lane);
688
+ remaining &= remaining - 1u;
689
+ }
690
+ if (static_cast<int>(lane) == (__ffs(peers) - 1)) {
691
+ add_to_field_code_offset(field_code, field_x, field_y, field_z, offset, sum);
692
+ }
693
+ }
694
+
695
+ template <typename OffsetT>
696
+ __global__ void add_batched_reference_source_patches_kernel(
697
+ int64_t total,
698
+ float* __restrict__ field_x,
699
+ float* __restrict__ field_y,
700
+ float* __restrict__ field_z,
701
+ const float* __restrict__ coeff_data,
702
+ const float* __restrict__ incident,
703
+ const int* __restrict__ field_codes_per_coeff,
704
+ const OffsetT* __restrict__ field_offsets,
705
+ const int* __restrict__ sample_indices_per_coeff) {
706
+ const int64_t linear = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
707
+ if (linear >= total) {
708
+ return;
709
+ }
710
+ const int sample_index = sample_indices_per_coeff[linear];
711
+ const float value = coeff_data[linear] * incident[sample_index];
712
+ add_to_field_code_offset(
713
+ field_codes_per_coeff[linear],
714
+ field_x,
715
+ field_y,
716
+ field_z,
717
+ field_offsets[linear],
718
+ value);
719
+ }
720
+
721
+ __global__ void add_batched_reference_source_patches_warp_kernel(
722
+ int64_t total,
723
+ float* __restrict__ field_x,
724
+ float* __restrict__ field_y,
725
+ float* __restrict__ field_z,
726
+ const float* __restrict__ coeff_data,
727
+ const float* __restrict__ incident,
728
+ const int* __restrict__ field_codes_per_coeff,
729
+ const int* __restrict__ field_offsets,
730
+ const int* __restrict__ sample_indices_per_coeff) {
731
+ const int64_t linear = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
732
+ if (linear >= total) {
733
+ return;
734
+ }
735
+ const int sample_index = sample_indices_per_coeff[linear];
736
+ const float value = coeff_data[linear] * incident[sample_index];
737
+ add_to_field_code_offset_warp_aggregated(
738
+ field_codes_per_coeff[linear],
739
+ field_x,
740
+ field_y,
741
+ field_z,
742
+ field_offsets[linear],
743
+ value);
744
+ }
745
+
746
+ template <typename OffsetT>
747
+ __global__ void add_batched_interpolated_source_patches_kernel(
748
+ int64_t total,
749
+ int incident_count,
750
+ float origin,
751
+ float inv_ds,
752
+ float* __restrict__ field_x,
753
+ float* __restrict__ field_y,
754
+ float* __restrict__ field_z,
755
+ const float* __restrict__ coeff_data,
756
+ const float* __restrict__ incident,
757
+ const float* __restrict__ sample_positions,
758
+ const int* __restrict__ field_codes_per_coeff,
759
+ const OffsetT* __restrict__ field_offsets) {
760
+ const int64_t linear = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
761
+ if (linear >= total) {
762
+ return;
763
+ }
764
+ const float sampled = interpolated_incident(incident, incident_count, sample_positions[linear], origin, inv_ds);
765
+ const float value = coeff_data[linear] * sampled;
766
+ add_to_field_code_offset(
767
+ field_codes_per_coeff[linear],
768
+ field_x,
769
+ field_y,
770
+ field_z,
771
+ field_offsets[linear],
772
+ value);
773
+ }
774
+
775
+ __global__ void add_batched_interpolated_source_patches_warp_kernel(
776
+ int64_t total,
777
+ int incident_count,
778
+ float origin,
779
+ float inv_ds,
780
+ float* __restrict__ field_x,
781
+ float* __restrict__ field_y,
782
+ float* __restrict__ field_z,
783
+ const float* __restrict__ coeff_data,
784
+ const float* __restrict__ incident,
785
+ const float* __restrict__ sample_positions,
786
+ const int* __restrict__ field_codes_per_coeff,
787
+ const int* __restrict__ field_offsets) {
788
+ const int64_t linear = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
789
+ if (linear >= total) {
790
+ return;
791
+ }
792
+ const float sampled = interpolated_incident(incident, incident_count, sample_positions[linear], origin, inv_ds);
793
+ const float value = coeff_data[linear] * sampled;
794
+ add_to_field_code_offset_warp_aggregated(
795
+ field_codes_per_coeff[linear],
796
+ field_x,
797
+ field_y,
798
+ field_z,
799
+ field_offsets[linear],
800
+ value);
801
+ }
802
+
803
+ __global__ void update_auxiliary_magnetic_kernel(
804
+ int64_t total,
805
+ float* __restrict__ magnetic,
806
+ const float* __restrict__ electric,
807
+ const float* __restrict__ decay,
808
+ const float* __restrict__ curl) {
809
+ const int64_t index = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
810
+ if (index >= total) {
811
+ return;
812
+ }
813
+ magnetic[index] = decay[index] * magnetic[index] - curl[index] * (electric[index + 1] - electric[index]);
814
+ }
815
+
816
+ __global__ void update_auxiliary_electric_kernel(
817
+ int64_t total,
818
+ int source_index,
819
+ float source_value,
820
+ float* __restrict__ electric,
821
+ const float* __restrict__ magnetic,
822
+ const float* __restrict__ decay,
823
+ const float* __restrict__ curl) {
824
+ const int64_t index = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
825
+ if (index >= total) {
826
+ return;
827
+ }
828
+ if (index + 1 == total) {
829
+ electric[index] = 0.0f;
830
+ return;
831
+ }
832
+ if (index == source_index) {
833
+ electric[index] = source_value;
834
+ return;
835
+ }
836
+ if (index > 0) {
837
+ electric[index] = decay[index] * electric[index] - curl[index] * (magnetic[index] - magnetic[index - 1]);
838
+ }
839
+ }
840
+
841
+ Shape3D shape3d(const torch::stable::Tensor& tensor) {
842
+ STD_TORCH_CHECK(tensor.dim() == 3, "tensor must be 3D");
843
+ return {static_cast<int>(tensor.size(0)), static_cast<int>(tensor.size(1)), static_cast<int>(tensor.size(2))};
844
+ }
845
+
846
+ void check_float_3d(const torch::stable::Tensor& tensor, const char* name) {
847
+ check_float32_tensor(tensor, name);
848
+ check_contiguous_tensor(tensor, name);
849
+ STD_TORCH_CHECK(tensor.dim() == 3, name, " must be 3D");
850
+ }
851
+
852
+ void check_float_1d(const torch::stable::Tensor& tensor, const char* name) {
853
+ check_float32_tensor(tensor, name);
854
+ check_contiguous_tensor(tensor, name);
855
+ STD_TORCH_CHECK(tensor.dim() == 1, name, " must be 1D");
856
+ }
857
+
858
+ void check_int32_tensor(const torch::stable::Tensor& tensor, const char* name) {
859
+ check_cuda_tensor(tensor, name);
860
+ check_contiguous_tensor(tensor, name);
861
+ STD_TORCH_CHECK(tensor.scalar_type() == torch::headeronly::ScalarType::Int, name, " must be int32");
862
+ }
863
+
864
+ void check_int32_or_int64_tensor(const torch::stable::Tensor& tensor, const char* name) {
865
+ check_cuda_tensor(tensor, name);
866
+ check_contiguous_tensor(tensor, name);
867
+ STD_TORCH_CHECK(
868
+ tensor.scalar_type() == torch::headeronly::ScalarType::Int || tensor.scalar_type() == torch::headeronly::ScalarType::Long,
869
+ name,
870
+ " must be int32 or int64");
871
+ }
872
+
873
+ void check_int64_tensor(const torch::stable::Tensor& tensor, const char* name) {
874
+ check_cuda_tensor(tensor, name);
875
+ check_contiguous_tensor(tensor, name);
876
+ STD_TORCH_CHECK(tensor.scalar_type() == torch::headeronly::ScalarType::Long, name, " must be int64");
877
+ }
878
+
879
+ void check_same_shape(const torch::stable::Tensor& reference, const torch::stable::Tensor& tensor, const char* name) {
880
+ check_same_cuda_device(reference, tensor, name);
881
+ STD_TORCH_CHECK(tensor.sizes().equals(reference.sizes()), name, " must match reference shape");
882
+ }
883
+
884
+ } // namespace
885
+
886
+ void add_source_patch_cuda(
887
+ torch::stable::Tensor field,
888
+ const torch::stable::Tensor& patch,
889
+ int64_t offset_i,
890
+ int64_t offset_j,
891
+ int64_t offset_k,
892
+ double signal) {
893
+ check_float_3d(field, "field");
894
+ check_float_3d(patch, "patch");
895
+ check_same_cuda_device(field, patch, "patch");
896
+ torch::stable::accelerator::DeviceGuard guard(field.get_device_index());
897
+ const Shape3D field_shape = shape3d(field);
898
+ const Shape3D patch_shape = shape3d(patch);
899
+ const dim3 block = patch_block3d();
900
+ const auto launch = [&](auto check_bounds_tag) {
901
+ constexpr bool check_bounds = decltype(check_bounds_tag)::value;
902
+ add_source_patch_kernel<check_bounds><<<patch_grid3d(patch_shape, block), block, 0, current_cuda_stream()>>>(
903
+ field_shape,
904
+ patch_shape,
905
+ static_cast<int>(offset_i),
906
+ static_cast<int>(offset_j),
907
+ static_cast<int>(offset_k),
908
+ static_cast<float>(signal),
909
+ field.mutable_data_ptr<float>(),
910
+ patch.mutable_data_ptr<float>());
911
+ };
912
+ if (patch_contained(field_shape, patch_shape, offset_i, offset_j, offset_k)) {
913
+ launch(std::false_type{});
914
+ } else {
915
+ launch(std::true_type{});
916
+ }
917
+ WITWIN_CUDA_CHECK();
918
+ }
919
+
920
+ void add_cw_phased_source_patch_cuda(
921
+ torch::stable::Tensor field,
922
+ const torch::stable::Tensor& patch_cos,
923
+ const torch::stable::Tensor& patch_sin,
924
+ int64_t offset_i,
925
+ int64_t offset_j,
926
+ int64_t offset_k,
927
+ double signal_cos,
928
+ double signal_sin) {
929
+ check_float_3d(field, "field");
930
+ check_float_3d(patch_cos, "patch_cos");
931
+ check_float_3d(patch_sin, "patch_sin");
932
+ check_same_cuda_device(field, patch_cos, "patch_cos");
933
+ check_same_shape(patch_cos, patch_sin, "patch_sin");
934
+ torch::stable::accelerator::DeviceGuard guard(field.get_device_index());
935
+ const Shape3D field_shape = shape3d(field);
936
+ const Shape3D patch_shape = shape3d(patch_cos);
937
+ const dim3 block = patch_block3d();
938
+ const auto launch = [&](auto check_bounds_tag) {
939
+ constexpr bool check_bounds = decltype(check_bounds_tag)::value;
940
+ add_cw_phased_source_patch_kernel<check_bounds><<<patch_grid3d(patch_shape, block), block, 0, current_cuda_stream()>>>(
941
+ field_shape,
942
+ patch_shape,
943
+ static_cast<int>(offset_i),
944
+ static_cast<int>(offset_j),
945
+ static_cast<int>(offset_k),
946
+ static_cast<float>(signal_cos),
947
+ static_cast<float>(signal_sin),
948
+ field.mutable_data_ptr<float>(),
949
+ patch_cos.mutable_data_ptr<float>(),
950
+ patch_sin.mutable_data_ptr<float>());
951
+ };
952
+ if (patch_contained(field_shape, patch_shape, offset_i, offset_j, offset_k)) {
953
+ launch(std::false_type{});
954
+ } else {
955
+ launch(std::true_type{});
956
+ }
957
+ WITWIN_CUDA_CHECK();
958
+ }
959
+
960
+ void add_time_shifted_source_patch_cuda(
961
+ torch::stable::Tensor field,
962
+ const torch::stable::Tensor& patch,
963
+ const torch::stable::Tensor& delay_patch,
964
+ const torch::stable::Tensor& activation_delay_patch,
965
+ int64_t offset_i,
966
+ int64_t offset_j,
967
+ int64_t offset_k,
968
+ int64_t time_kind,
969
+ double time,
970
+ double frequency,
971
+ double fwidth,
972
+ double amplitude,
973
+ double phase,
974
+ double delay,
975
+ int64_t causal_gate) {
976
+ check_float_3d(field, "field");
977
+ check_float_3d(patch, "patch");
978
+ check_float_3d(delay_patch, "delay_patch");
979
+ check_float_3d(activation_delay_patch, "activation_delay_patch");
980
+ check_same_cuda_device(field, patch, "patch");
981
+ check_same_shape(patch, delay_patch, "delay_patch");
982
+ check_same_shape(patch, activation_delay_patch, "activation_delay_patch");
983
+ torch::stable::accelerator::DeviceGuard guard(field.get_device_index());
984
+ constexpr float two_pi = 6.283185307179586f;
985
+ constexpr float pi = 3.141592653589793f;
986
+ const float frequency_f = static_cast<float>(frequency);
987
+ const float angular_frequency = two_pi * frequency_f;
988
+ const float gaussian_inv_sigma = fmaxf(two_pi * static_cast<float>(fwidth), 1.0e-30f);
989
+ const float ricker_pi_frequency = pi * frequency_f;
990
+ const Shape3D field_shape = shape3d(field);
991
+ const Shape3D patch_shape = shape3d(patch);
992
+ const bool contained = patch_contained(field_shape, patch_shape, offset_i, offset_j, offset_k);
993
+ const auto launch = [&](auto time_kind_tag, auto check_bounds_tag) {
994
+ constexpr int time_kind_value = decltype(time_kind_tag)::value;
995
+ constexpr bool check_bounds = decltype(check_bounds_tag)::value;
996
+ launch_time_shifted_source_patch<time_kind_value, check_bounds>(
997
+ field_shape,
998
+ patch_shape,
999
+ static_cast<int>(offset_i),
1000
+ static_cast<int>(offset_j),
1001
+ static_cast<int>(offset_k),
1002
+ static_cast<float>(time),
1003
+ angular_frequency,
1004
+ gaussian_inv_sigma,
1005
+ ricker_pi_frequency,
1006
+ static_cast<float>(amplitude),
1007
+ static_cast<float>(phase),
1008
+ static_cast<float>(delay),
1009
+ static_cast<int>(causal_gate),
1010
+ field.mutable_data_ptr<float>(),
1011
+ patch.mutable_data_ptr<float>(),
1012
+ delay_patch.mutable_data_ptr<float>(),
1013
+ activation_delay_patch.mutable_data_ptr<float>());
1014
+ };
1015
+ if (time_kind == 0) {
1016
+ if (contained) {
1017
+ launch(std::integral_constant<int, 0>{}, std::false_type{});
1018
+ } else {
1019
+ launch(std::integral_constant<int, 0>{}, std::true_type{});
1020
+ }
1021
+ } else if (time_kind == 1) {
1022
+ if (contained) {
1023
+ launch(std::integral_constant<int, 1>{}, std::false_type{});
1024
+ } else {
1025
+ launch(std::integral_constant<int, 1>{}, std::true_type{});
1026
+ }
1027
+ } else {
1028
+ if (contained) {
1029
+ launch(std::integral_constant<int, 2>{}, std::false_type{});
1030
+ } else {
1031
+ launch(std::integral_constant<int, 2>{}, std::true_type{});
1032
+ }
1033
+ }
1034
+ WITWIN_CUDA_CHECK();
1035
+ }
1036
+
1037
+ void add_source_patch_periodic_cuda(
1038
+ torch::stable::Tensor field,
1039
+ const torch::stable::Tensor& patch,
1040
+ int64_t offset_i,
1041
+ int64_t offset_j,
1042
+ int64_t offset_k,
1043
+ double signal,
1044
+ int64_t axis_a,
1045
+ int64_t axis_b,
1046
+ int64_t wrap_a,
1047
+ int64_t wrap_b) {
1048
+ check_float_3d(field, "field");
1049
+ check_float_3d(patch, "patch");
1050
+ check_same_cuda_device(field, patch, "patch");
1051
+ STD_TORCH_CHECK(axis_a >= 0 && axis_a < 3, "axis_a must be in [0, 3)");
1052
+ STD_TORCH_CHECK(axis_b >= 0 && axis_b < 3, "axis_b must be in [0, 3)");
1053
+ STD_TORCH_CHECK(axis_a != axis_b, "axis_a and axis_b must be distinct");
1054
+ torch::stable::accelerator::DeviceGuard guard(field.get_device_index());
1055
+ const auto launch = [&](auto axis_a_tag, auto axis_b_tag) {
1056
+ constexpr int axis_a_value = decltype(axis_a_tag)::value;
1057
+ constexpr int axis_b_value = decltype(axis_b_tag)::value;
1058
+ dispatch_periodic_source_wraps<axis_a_value, axis_b_value>(
1059
+ shape3d(field),
1060
+ shape3d(patch),
1061
+ static_cast<int>(offset_i),
1062
+ static_cast<int>(offset_j),
1063
+ static_cast<int>(offset_k),
1064
+ static_cast<float>(signal),
1065
+ static_cast<int>(wrap_a),
1066
+ static_cast<int>(wrap_b),
1067
+ field.mutable_data_ptr<float>(),
1068
+ patch.mutable_data_ptr<float>());
1069
+ };
1070
+ if (axis_a == 0 && axis_b == 1) {
1071
+ launch(std::integral_constant<int, 0>{}, std::integral_constant<int, 1>{});
1072
+ } else if (axis_a == 0 && axis_b == 2) {
1073
+ launch(std::integral_constant<int, 0>{}, std::integral_constant<int, 2>{});
1074
+ } else if (axis_a == 1 && axis_b == 0) {
1075
+ launch(std::integral_constant<int, 1>{}, std::integral_constant<int, 0>{});
1076
+ } else if (axis_a == 1 && axis_b == 2) {
1077
+ launch(std::integral_constant<int, 1>{}, std::integral_constant<int, 2>{});
1078
+ } else if (axis_a == 2 && axis_b == 0) {
1079
+ launch(std::integral_constant<int, 2>{}, std::integral_constant<int, 0>{});
1080
+ } else {
1081
+ launch(std::integral_constant<int, 2>{}, std::integral_constant<int, 1>{});
1082
+ }
1083
+ WITWIN_CUDA_CHECK();
1084
+ }
1085
+
1086
+ void add_source_patch_bloch_cuda(
1087
+ torch::stable::Tensor ex_real,
1088
+ torch::stable::Tensor ex_imag,
1089
+ torch::stable::Tensor ey_real,
1090
+ torch::stable::Tensor ey_imag,
1091
+ torch::stable::Tensor ez_real,
1092
+ torch::stable::Tensor ez_imag,
1093
+ const torch::stable::Tensor& patch,
1094
+ int64_t offset_i,
1095
+ int64_t offset_j,
1096
+ int64_t offset_k,
1097
+ double signal_real,
1098
+ double signal_imag,
1099
+ int64_t axis_code,
1100
+ double phase_cos_a,
1101
+ double phase_sin_a,
1102
+ double phase_cos_b,
1103
+ double phase_sin_b,
1104
+ int64_t wrap_axis_a,
1105
+ int64_t wrap_axis_b) {
1106
+ check_float_3d(ex_real, "ex_real");
1107
+ check_float_3d(ex_imag, "ex_imag");
1108
+ check_float_3d(ey_real, "ey_real");
1109
+ check_float_3d(ey_imag, "ey_imag");
1110
+ check_float_3d(ez_real, "ez_real");
1111
+ check_float_3d(ez_imag, "ez_imag");
1112
+ check_float_3d(patch, "patch");
1113
+ check_same_cuda_device(ex_real, ex_imag, "ex_imag");
1114
+ check_same_cuda_device(ex_real, ey_real, "ey_real");
1115
+ check_same_cuda_device(ex_real, ey_imag, "ey_imag");
1116
+ check_same_cuda_device(ex_real, ez_real, "ez_real");
1117
+ check_same_cuda_device(ex_real, ez_imag, "ez_imag");
1118
+ check_same_cuda_device(ex_real, patch, "patch");
1119
+ check_same_shape(ex_real, ex_imag, "ex_imag");
1120
+ check_same_shape(ey_real, ey_imag, "ey_imag");
1121
+ check_same_shape(ez_real, ez_imag, "ez_imag");
1122
+ STD_TORCH_CHECK(axis_code >= 0 && axis_code < 3, "axis_code must be in [0, 3)");
1123
+ torch::stable::accelerator::DeviceGuard guard(ex_real.get_device_index());
1124
+ if (axis_code == 0) {
1125
+ launch_bloch_source_patch<1, 2>(
1126
+ shape3d(ex_real),
1127
+ shape3d(patch),
1128
+ static_cast<int>(offset_i),
1129
+ static_cast<int>(offset_j),
1130
+ static_cast<int>(offset_k),
1131
+ static_cast<float>(signal_real),
1132
+ static_cast<float>(signal_imag),
1133
+ static_cast<float>(phase_cos_a),
1134
+ static_cast<float>(phase_sin_a),
1135
+ static_cast<float>(phase_cos_b),
1136
+ static_cast<float>(phase_sin_b),
1137
+ static_cast<int>(wrap_axis_a),
1138
+ static_cast<int>(wrap_axis_b),
1139
+ ex_real.mutable_data_ptr<float>(),
1140
+ ex_imag.mutable_data_ptr<float>(),
1141
+ patch.mutable_data_ptr<float>());
1142
+ } else if (axis_code == 1) {
1143
+ launch_bloch_source_patch<0, 2>(
1144
+ shape3d(ey_real),
1145
+ shape3d(patch),
1146
+ static_cast<int>(offset_i),
1147
+ static_cast<int>(offset_j),
1148
+ static_cast<int>(offset_k),
1149
+ static_cast<float>(signal_real),
1150
+ static_cast<float>(signal_imag),
1151
+ static_cast<float>(phase_cos_a),
1152
+ static_cast<float>(phase_sin_a),
1153
+ static_cast<float>(phase_cos_b),
1154
+ static_cast<float>(phase_sin_b),
1155
+ static_cast<int>(wrap_axis_a),
1156
+ static_cast<int>(wrap_axis_b),
1157
+ ey_real.mutable_data_ptr<float>(),
1158
+ ey_imag.mutable_data_ptr<float>(),
1159
+ patch.mutable_data_ptr<float>());
1160
+ } else {
1161
+ launch_bloch_source_patch<0, 1>(
1162
+ shape3d(ez_real),
1163
+ shape3d(patch),
1164
+ static_cast<int>(offset_i),
1165
+ static_cast<int>(offset_j),
1166
+ static_cast<int>(offset_k),
1167
+ static_cast<float>(signal_real),
1168
+ static_cast<float>(signal_imag),
1169
+ static_cast<float>(phase_cos_a),
1170
+ static_cast<float>(phase_sin_a),
1171
+ static_cast<float>(phase_cos_b),
1172
+ static_cast<float>(phase_sin_b),
1173
+ static_cast<int>(wrap_axis_a),
1174
+ static_cast<int>(wrap_axis_b),
1175
+ ez_real.mutable_data_ptr<float>(),
1176
+ ez_imag.mutable_data_ptr<float>(),
1177
+ patch.mutable_data_ptr<float>());
1178
+ }
1179
+ WITWIN_CUDA_CHECK();
1180
+ }
1181
+
1182
+ void add_scaled_slice_source_patch_cuda(
1183
+ torch::stable::Tensor field,
1184
+ const torch::stable::Tensor& patch,
1185
+ const torch::stable::Tensor& incident,
1186
+ int64_t sample_index,
1187
+ int64_t offset_i,
1188
+ int64_t offset_j,
1189
+ int64_t offset_k,
1190
+ double scale) {
1191
+ check_float_3d(field, "field");
1192
+ check_float_3d(patch, "patch");
1193
+ check_float_1d(incident, "incident");
1194
+ check_same_cuda_device(field, patch, "patch");
1195
+ check_same_cuda_device(field, incident, "incident");
1196
+ STD_TORCH_CHECK(sample_index >= 0 && sample_index < incident.numel(), "sample_index is out of range");
1197
+ torch::stable::accelerator::DeviceGuard guard(field.get_device_index());
1198
+ const Shape3D patch_shape = shape3d(patch);
1199
+ const dim3 block = patch_block3d();
1200
+ add_scaled_slice_source_patch_kernel<<<patch_grid3d(patch_shape, block), block, 0, current_cuda_stream()>>>(
1201
+ shape3d(field),
1202
+ patch_shape,
1203
+ static_cast<int>(sample_index),
1204
+ static_cast<int>(offset_i),
1205
+ static_cast<int>(offset_j),
1206
+ static_cast<int>(offset_k),
1207
+ static_cast<float>(scale),
1208
+ field.mutable_data_ptr<float>(),
1209
+ patch.mutable_data_ptr<float>(),
1210
+ incident.mutable_data_ptr<float>());
1211
+ WITWIN_CUDA_CHECK();
1212
+ }
1213
+
1214
+ void add_scaled_line_source_patch_cuda(
1215
+ torch::stable::Tensor field,
1216
+ const torch::stable::Tensor& patch,
1217
+ const torch::stable::Tensor& incident,
1218
+ const torch::stable::Tensor& sample_indices,
1219
+ int64_t sample_axis,
1220
+ int64_t offset_i,
1221
+ int64_t offset_j,
1222
+ int64_t offset_k,
1223
+ double scale) {
1224
+ check_float_3d(field, "field");
1225
+ check_float_3d(patch, "patch");
1226
+ check_float_1d(incident, "incident");
1227
+ check_int32_tensor(sample_indices, "sample_indices");
1228
+ check_same_cuda_device(field, patch, "patch");
1229
+ check_same_cuda_device(field, incident, "incident");
1230
+ check_same_cuda_device(field, sample_indices, "sample_indices");
1231
+ STD_TORCH_CHECK(sample_axis >= 0 && sample_axis < 3, "sample_axis must be in [0, 3)");
1232
+ STD_TORCH_CHECK(sample_indices.numel() == patch.size(sample_axis), "sample_indices length must match selected patch axis");
1233
+ torch::stable::accelerator::DeviceGuard guard(field.get_device_index());
1234
+ if (sample_axis == 0) {
1235
+ launch_scaled_line_source_patch<0>(
1236
+ shape3d(field),
1237
+ shape3d(patch),
1238
+ static_cast<int>(offset_i),
1239
+ static_cast<int>(offset_j),
1240
+ static_cast<int>(offset_k),
1241
+ static_cast<float>(scale),
1242
+ field.mutable_data_ptr<float>(),
1243
+ patch.mutable_data_ptr<float>(),
1244
+ incident.mutable_data_ptr<float>(),
1245
+ sample_indices.mutable_data_ptr<int>());
1246
+ } else if (sample_axis == 1) {
1247
+ launch_scaled_line_source_patch<1>(
1248
+ shape3d(field),
1249
+ shape3d(patch),
1250
+ static_cast<int>(offset_i),
1251
+ static_cast<int>(offset_j),
1252
+ static_cast<int>(offset_k),
1253
+ static_cast<float>(scale),
1254
+ field.mutable_data_ptr<float>(),
1255
+ patch.mutable_data_ptr<float>(),
1256
+ incident.mutable_data_ptr<float>(),
1257
+ sample_indices.mutable_data_ptr<int>());
1258
+ } else {
1259
+ launch_scaled_line_source_patch<2>(
1260
+ shape3d(field),
1261
+ shape3d(patch),
1262
+ static_cast<int>(offset_i),
1263
+ static_cast<int>(offset_j),
1264
+ static_cast<int>(offset_k),
1265
+ static_cast<float>(scale),
1266
+ field.mutable_data_ptr<float>(),
1267
+ patch.mutable_data_ptr<float>(),
1268
+ incident.mutable_data_ptr<float>(),
1269
+ sample_indices.mutable_data_ptr<int>());
1270
+ }
1271
+ WITWIN_CUDA_CHECK();
1272
+ }
1273
+
1274
+ void add_interpolated_source_patch_cuda(
1275
+ torch::stable::Tensor field,
1276
+ const torch::stable::Tensor& patch,
1277
+ const torch::stable::Tensor& incident,
1278
+ const torch::stable::Tensor& sample_positions,
1279
+ double origin,
1280
+ double ds,
1281
+ int64_t offset_i,
1282
+ int64_t offset_j,
1283
+ int64_t offset_k,
1284
+ double scale) {
1285
+ check_float_3d(field, "field");
1286
+ check_float_3d(patch, "patch");
1287
+ check_float_1d(incident, "incident");
1288
+ check_float_3d(sample_positions, "sample_positions");
1289
+ check_same_cuda_device(field, patch, "patch");
1290
+ check_same_cuda_device(field, incident, "incident");
1291
+ check_same_shape(patch, sample_positions, "sample_positions");
1292
+ torch::stable::accelerator::DeviceGuard guard(field.get_device_index());
1293
+ const float inv_ds = ds > 0.0 ? static_cast<float>(1.0 / ds) : 0.0f;
1294
+ const Shape3D patch_shape = shape3d(patch);
1295
+ const dim3 block = patch_block3d();
1296
+ add_interpolated_source_patch_kernel<<<patch_grid3d(patch_shape, block), block, 0, current_cuda_stream()>>>(
1297
+ shape3d(field),
1298
+ patch_shape,
1299
+ static_cast<int>(incident.numel()),
1300
+ static_cast<float>(origin),
1301
+ inv_ds,
1302
+ static_cast<int>(offset_i),
1303
+ static_cast<int>(offset_j),
1304
+ static_cast<int>(offset_k),
1305
+ static_cast<float>(scale),
1306
+ field.mutable_data_ptr<float>(),
1307
+ patch.mutable_data_ptr<float>(),
1308
+ incident.mutable_data_ptr<float>(),
1309
+ sample_positions.mutable_data_ptr<float>());
1310
+ WITWIN_CUDA_CHECK();
1311
+ }
1312
+
1313
+ void add_batched_reference_source_patches_cuda(
1314
+ torch::stable::Tensor field_x,
1315
+ torch::stable::Tensor field_y,
1316
+ torch::stable::Tensor field_z,
1317
+ const torch::stable::Tensor& coeff_data,
1318
+ const torch::stable::Tensor& incident,
1319
+ const torch::stable::Tensor& field_codes_per_coeff,
1320
+ const torch::stable::Tensor& field_offsets,
1321
+ const torch::stable::Tensor& sample_indices_per_coeff) {
1322
+ check_float_3d(field_x, "field_x");
1323
+ check_float_3d(field_y, "field_y");
1324
+ check_float_3d(field_z, "field_z");
1325
+ check_float_1d(coeff_data, "coeff_data");
1326
+ check_float_1d(incident, "incident");
1327
+ check_int32_tensor(field_codes_per_coeff, "field_codes_per_coeff");
1328
+ check_int32_or_int64_tensor(field_offsets, "field_offsets");
1329
+ check_int32_tensor(sample_indices_per_coeff, "sample_indices_per_coeff");
1330
+ check_same_cuda_device(field_x, field_y, "field_y");
1331
+ check_same_cuda_device(field_x, field_z, "field_z");
1332
+ check_same_cuda_device(field_x, coeff_data, "coeff_data");
1333
+ check_same_cuda_device(field_x, incident, "incident");
1334
+ check_same_cuda_device(field_x, field_codes_per_coeff, "field_codes_per_coeff");
1335
+ check_same_cuda_device(field_x, field_offsets, "field_offsets");
1336
+ check_same_cuda_device(field_x, sample_indices_per_coeff, "sample_indices_per_coeff");
1337
+ STD_TORCH_CHECK(field_codes_per_coeff.numel() == coeff_data.numel(), "field_codes_per_coeff length must match coeff_data");
1338
+ STD_TORCH_CHECK(field_offsets.numel() == coeff_data.numel(), "field_offsets length must match coeff_data");
1339
+ STD_TORCH_CHECK(sample_indices_per_coeff.numel() == coeff_data.numel(), "sample_indices_per_coeff length must match coeff_data");
1340
+ torch::stable::accelerator::DeviceGuard guard(field_x.get_device_index());
1341
+ if (field_offsets.scalar_type() == torch::headeronly::ScalarType::Int) {
1342
+ add_batched_reference_source_patches_warp_kernel<<<linear_grid(coeff_data.numel()), 256, 0, current_cuda_stream()>>>(
1343
+ coeff_data.numel(),
1344
+ field_x.mutable_data_ptr<float>(),
1345
+ field_y.mutable_data_ptr<float>(),
1346
+ field_z.mutable_data_ptr<float>(),
1347
+ coeff_data.mutable_data_ptr<float>(),
1348
+ incident.mutable_data_ptr<float>(),
1349
+ field_codes_per_coeff.mutable_data_ptr<int>(),
1350
+ field_offsets.mutable_data_ptr<int>(),
1351
+ sample_indices_per_coeff.mutable_data_ptr<int>());
1352
+ } else {
1353
+ add_batched_reference_source_patches_kernel<int64_t><<<linear_grid(coeff_data.numel()), 256, 0, current_cuda_stream()>>>(
1354
+ coeff_data.numel(),
1355
+ field_x.mutable_data_ptr<float>(),
1356
+ field_y.mutable_data_ptr<float>(),
1357
+ field_z.mutable_data_ptr<float>(),
1358
+ coeff_data.mutable_data_ptr<float>(),
1359
+ incident.mutable_data_ptr<float>(),
1360
+ field_codes_per_coeff.mutable_data_ptr<int>(),
1361
+ field_offsets.mutable_data_ptr<int64_t>(),
1362
+ sample_indices_per_coeff.mutable_data_ptr<int>());
1363
+ }
1364
+ WITWIN_CUDA_CHECK();
1365
+ }
1366
+
1367
+ void add_batched_interpolated_source_patches_cuda(
1368
+ torch::stable::Tensor field_x,
1369
+ torch::stable::Tensor field_y,
1370
+ torch::stable::Tensor field_z,
1371
+ const torch::stable::Tensor& coeff_data,
1372
+ const torch::stable::Tensor& incident,
1373
+ const torch::stable::Tensor& sample_positions,
1374
+ const torch::stable::Tensor& field_codes_per_coeff,
1375
+ const torch::stable::Tensor& field_offsets,
1376
+ double origin,
1377
+ double ds) {
1378
+ check_float_3d(field_x, "field_x");
1379
+ check_float_3d(field_y, "field_y");
1380
+ check_float_3d(field_z, "field_z");
1381
+ check_float_1d(coeff_data, "coeff_data");
1382
+ check_float_1d(incident, "incident");
1383
+ check_float_1d(sample_positions, "sample_positions");
1384
+ check_int32_tensor(field_codes_per_coeff, "field_codes_per_coeff");
1385
+ check_int32_or_int64_tensor(field_offsets, "field_offsets");
1386
+ check_same_cuda_device(field_x, field_y, "field_y");
1387
+ check_same_cuda_device(field_x, field_z, "field_z");
1388
+ check_same_cuda_device(field_x, coeff_data, "coeff_data");
1389
+ check_same_cuda_device(field_x, incident, "incident");
1390
+ check_same_cuda_device(field_x, sample_positions, "sample_positions");
1391
+ check_same_cuda_device(field_x, field_codes_per_coeff, "field_codes_per_coeff");
1392
+ check_same_cuda_device(field_x, field_offsets, "field_offsets");
1393
+ STD_TORCH_CHECK(sample_positions.numel() == coeff_data.numel(), "sample_positions length must match coeff_data");
1394
+ STD_TORCH_CHECK(field_codes_per_coeff.numel() == coeff_data.numel(), "field_codes_per_coeff length must match coeff_data");
1395
+ STD_TORCH_CHECK(field_offsets.numel() == coeff_data.numel(), "field_offsets length must match coeff_data");
1396
+ torch::stable::accelerator::DeviceGuard guard(field_x.get_device_index());
1397
+ const float inv_ds = ds > 0.0 ? static_cast<float>(1.0 / ds) : 0.0f;
1398
+ if (field_offsets.scalar_type() == torch::headeronly::ScalarType::Int) {
1399
+ add_batched_interpolated_source_patches_warp_kernel<<<linear_grid(coeff_data.numel()), 256, 0, current_cuda_stream()>>>(
1400
+ coeff_data.numel(),
1401
+ static_cast<int>(incident.numel()),
1402
+ static_cast<float>(origin),
1403
+ inv_ds,
1404
+ field_x.mutable_data_ptr<float>(),
1405
+ field_y.mutable_data_ptr<float>(),
1406
+ field_z.mutable_data_ptr<float>(),
1407
+ coeff_data.mutable_data_ptr<float>(),
1408
+ incident.mutable_data_ptr<float>(),
1409
+ sample_positions.mutable_data_ptr<float>(),
1410
+ field_codes_per_coeff.mutable_data_ptr<int>(),
1411
+ field_offsets.mutable_data_ptr<int>());
1412
+ } else {
1413
+ add_batched_interpolated_source_patches_kernel<int64_t><<<linear_grid(coeff_data.numel()), 256, 0, current_cuda_stream()>>>(
1414
+ coeff_data.numel(),
1415
+ static_cast<int>(incident.numel()),
1416
+ static_cast<float>(origin),
1417
+ inv_ds,
1418
+ field_x.mutable_data_ptr<float>(),
1419
+ field_y.mutable_data_ptr<float>(),
1420
+ field_z.mutable_data_ptr<float>(),
1421
+ coeff_data.mutable_data_ptr<float>(),
1422
+ incident.mutable_data_ptr<float>(),
1423
+ sample_positions.mutable_data_ptr<float>(),
1424
+ field_codes_per_coeff.mutable_data_ptr<int>(),
1425
+ field_offsets.mutable_data_ptr<int64_t>());
1426
+ }
1427
+ WITWIN_CUDA_CHECK();
1428
+ }
1429
+
1430
+ void update_auxiliary_magnetic_cuda(
1431
+ torch::stable::Tensor magnetic,
1432
+ const torch::stable::Tensor& electric,
1433
+ const torch::stable::Tensor& decay,
1434
+ const torch::stable::Tensor& curl) {
1435
+ check_float_1d(magnetic, "magnetic");
1436
+ check_float_1d(electric, "electric");
1437
+ check_float_1d(decay, "decay");
1438
+ check_float_1d(curl, "curl");
1439
+ check_same_cuda_device(magnetic, electric, "electric");
1440
+ check_same_cuda_device(magnetic, decay, "decay");
1441
+ check_same_cuda_device(magnetic, curl, "curl");
1442
+ STD_TORCH_CHECK(decay.numel() == magnetic.numel(), "decay must match magnetic length");
1443
+ STD_TORCH_CHECK(curl.numel() == magnetic.numel(), "curl must match magnetic length");
1444
+ STD_TORCH_CHECK(electric.numel() == magnetic.numel() + 1, "electric length must be magnetic length + 1");
1445
+ torch::stable::accelerator::DeviceGuard guard(magnetic.get_device_index());
1446
+ update_auxiliary_magnetic_kernel<<<linear_grid(magnetic.numel()), 256, 0, current_cuda_stream()>>>(
1447
+ magnetic.numel(),
1448
+ magnetic.mutable_data_ptr<float>(),
1449
+ electric.mutable_data_ptr<float>(),
1450
+ decay.mutable_data_ptr<float>(),
1451
+ curl.mutable_data_ptr<float>());
1452
+ WITWIN_CUDA_CHECK();
1453
+ }
1454
+
1455
+ void update_auxiliary_electric_cuda(
1456
+ torch::stable::Tensor electric,
1457
+ const torch::stable::Tensor& magnetic,
1458
+ const torch::stable::Tensor& decay,
1459
+ const torch::stable::Tensor& curl,
1460
+ int64_t source_index,
1461
+ double source_value) {
1462
+ check_float_1d(electric, "electric");
1463
+ check_float_1d(magnetic, "magnetic");
1464
+ check_float_1d(decay, "decay");
1465
+ check_float_1d(curl, "curl");
1466
+ check_same_cuda_device(electric, magnetic, "magnetic");
1467
+ check_same_cuda_device(electric, decay, "decay");
1468
+ check_same_cuda_device(electric, curl, "curl");
1469
+ STD_TORCH_CHECK(decay.numel() == electric.numel(), "decay must match electric length");
1470
+ STD_TORCH_CHECK(curl.numel() == electric.numel(), "curl must match electric length");
1471
+ STD_TORCH_CHECK(magnetic.numel() + 1 == electric.numel(), "magnetic length must be electric length - 1");
1472
+ torch::stable::accelerator::DeviceGuard guard(electric.get_device_index());
1473
+ update_auxiliary_electric_kernel<<<linear_grid(electric.numel()), 256, 0, current_cuda_stream()>>>(
1474
+ electric.numel(),
1475
+ static_cast<int>(source_index),
1476
+ static_cast<float>(source_value),
1477
+ electric.mutable_data_ptr<float>(),
1478
+ magnetic.mutable_data_ptr<float>(),
1479
+ decay.mutable_data_ptr<float>(),
1480
+ curl.mutable_data_ptr<float>());
1481
+ WITWIN_CUDA_CHECK();
1482
+ }