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,347 @@
1
+ #include "../launch.h"
2
+ #include "../tensors.h"
3
+ #include "common.cuh"
4
+
5
+ namespace {
6
+
7
+ template <typename IndexT>
8
+ __global__ void accumulate_point_observer_kernel(
9
+ int64_t total,
10
+ unsigned int ny,
11
+ unsigned int nz,
12
+ const float* __restrict__ field,
13
+ const IndexT* __restrict__ point_i,
14
+ const IndexT* __restrict__ point_j,
15
+ const IndexT* __restrict__ point_k,
16
+ float weighted_cos,
17
+ float weighted_sin,
18
+ float* __restrict__ real_accum,
19
+ float* __restrict__ imag_accum) {
20
+ const int64_t index = static_cast<int64_t>(blockIdx.x) * blockDim.x + threadIdx.x;
21
+ if (index >= total) {
22
+ return;
23
+ }
24
+ const long long field_offset = offset3d(
25
+ static_cast<unsigned int>(point_i[index]),
26
+ static_cast<unsigned int>(point_j[index]),
27
+ static_cast<unsigned int>(point_k[index]),
28
+ ny,
29
+ nz);
30
+ const float value = field[field_offset];
31
+ real_accum[index] += value * weighted_cos;
32
+ imag_accum[index] += value * weighted_sin;
33
+ }
34
+
35
+ template <int Axis>
36
+ __global__ void accumulate_plane_observer_kernel(
37
+ unsigned int nx,
38
+ unsigned int ny,
39
+ unsigned int nz,
40
+ const float* __restrict__ field,
41
+ int plane_index,
42
+ float weighted_cos,
43
+ float weighted_sin,
44
+ float* __restrict__ real_accum,
45
+ float* __restrict__ imag_accum) {
46
+ unsigned int i = 0;
47
+ unsigned int j = 0;
48
+ unsigned int k = 0;
49
+ int64_t linear = 0;
50
+ if constexpr (Axis == 0) {
51
+ k = blockIdx.x * blockDim.x + threadIdx.x;
52
+ j = blockIdx.y * blockDim.y + threadIdx.y;
53
+ if (j >= ny || k >= nz) {
54
+ return;
55
+ }
56
+ i = static_cast<unsigned int>(plane_index);
57
+ linear = static_cast<int64_t>(j) * nz + k;
58
+ } else if constexpr (Axis == 1) {
59
+ k = blockIdx.x * blockDim.x + threadIdx.x;
60
+ i = blockIdx.y * blockDim.y + threadIdx.y;
61
+ if (i >= nx || k >= nz) {
62
+ return;
63
+ }
64
+ j = static_cast<unsigned int>(plane_index);
65
+ linear = static_cast<int64_t>(i) * nz + k;
66
+ } else {
67
+ j = blockIdx.x * blockDim.x + threadIdx.x;
68
+ i = blockIdx.y * blockDim.y + threadIdx.y;
69
+ if (i >= nx || j >= ny) {
70
+ return;
71
+ }
72
+ k = static_cast<unsigned int>(plane_index);
73
+ linear = static_cast<int64_t>(i) * ny + j;
74
+ }
75
+ const float value = field[offset3d(i, j, k, ny, nz)];
76
+ real_accum[linear] += value * weighted_cos;
77
+ imag_accum[linear] += value * weighted_sin;
78
+ }
79
+
80
+ // Single-block reduction of the instantaneous Poynting flux through a plane:
81
+ // flux = scale * sum_pq (Ea*Hb - Eb*Ha) * weight
82
+ // The four tangential field planes are already Yee-averaged onto the common
83
+ // (P, Q) grid and share `weights`'s layout; the scalar result is written
84
+ // straight into out[out_index] (a preallocated time-series slot), so a sampled
85
+ // step needs no temporaries and no host round-trip. block_size must be a power
86
+ // of two for the tree reduction.
87
+ __global__ void plane_flux_reduce_kernel(
88
+ int64_t plane_size,
89
+ float scale,
90
+ const float* __restrict__ ea,
91
+ const float* __restrict__ eb,
92
+ const float* __restrict__ ha,
93
+ const float* __restrict__ hb,
94
+ const float* __restrict__ weights,
95
+ float* __restrict__ out,
96
+ int64_t out_index) {
97
+ extern __shared__ float sdata[];
98
+ const unsigned int tid = threadIdx.x;
99
+ float local = 0.0f;
100
+ for (int64_t idx = tid; idx < plane_size; idx += blockDim.x) {
101
+ local += (ea[idx] * hb[idx] - eb[idx] * ha[idx]) * weights[idx];
102
+ }
103
+ sdata[tid] = local;
104
+ __syncthreads();
105
+ for (unsigned int stride = blockDim.x >> 1; stride > 0; stride >>= 1) {
106
+ if (tid < stride) {
107
+ sdata[tid] += sdata[tid + stride];
108
+ }
109
+ __syncthreads();
110
+ }
111
+ if (tid == 0) {
112
+ out[out_index] = scale * sdata[0];
113
+ }
114
+ }
115
+
116
+ } // namespace
117
+
118
+ namespace {
119
+
120
+ void check_index_vector(const torch::stable::Tensor& tensor, const char* name) {
121
+ check_cuda_tensor(tensor, name);
122
+ check_contiguous_tensor(tensor, name);
123
+ STD_TORCH_CHECK(
124
+ tensor.scalar_type() == torch::headeronly::ScalarType::Int || tensor.scalar_type() == torch::headeronly::ScalarType::Long,
125
+ name,
126
+ " must be int32 or int64");
127
+ STD_TORCH_CHECK(tensor.dim() == 1, name, " must be rank 1");
128
+ }
129
+
130
+ template <typename IndexT>
131
+ void launch_point_observers(
132
+ int64_t total,
133
+ unsigned int ny,
134
+ unsigned int nz,
135
+ const float* __restrict__ field,
136
+ const IndexT* __restrict__ point_i,
137
+ const IndexT* __restrict__ point_j,
138
+ const IndexT* __restrict__ point_k,
139
+ float weighted_cos,
140
+ float weighted_sin,
141
+ float* __restrict__ real_accum,
142
+ float* __restrict__ imag_accum,
143
+ int block_size) {
144
+ accumulate_point_observer_kernel<IndexT><<<linear_grid(total, block_size), block_size, 0, current_cuda_stream()>>>(
145
+ total,
146
+ ny,
147
+ nz,
148
+ field,
149
+ point_i,
150
+ point_j,
151
+ point_k,
152
+ weighted_cos,
153
+ weighted_sin,
154
+ real_accum,
155
+ imag_accum);
156
+ }
157
+
158
+ template <int Axis>
159
+ void launch_plane_observer(
160
+ unsigned int nx,
161
+ unsigned int ny,
162
+ unsigned int nz,
163
+ const float* __restrict__ field,
164
+ int plane_index,
165
+ float weighted_cos,
166
+ float weighted_sin,
167
+ float* __restrict__ real_accum,
168
+ float* __restrict__ imag_accum) {
169
+ const dim3 block(64, 4, 1);
170
+ const unsigned int dim_x = Axis == 2 ? ny : nz;
171
+ const unsigned int dim_y = Axis == 0 ? ny : nx;
172
+ const dim3 grid((dim_x + block.x - 1) / block.x, (dim_y + block.y - 1) / block.y, 1);
173
+ accumulate_plane_observer_kernel<Axis><<<grid, block, 0, current_cuda_stream()>>>(
174
+ nx,
175
+ ny,
176
+ nz,
177
+ field,
178
+ plane_index,
179
+ weighted_cos,
180
+ weighted_sin,
181
+ real_accum,
182
+ imag_accum);
183
+ }
184
+
185
+ } // namespace
186
+
187
+ void accumulate_point_observers_cuda(
188
+ const torch::stable::Tensor& field,
189
+ const torch::stable::Tensor& point_i,
190
+ const torch::stable::Tensor& point_j,
191
+ const torch::stable::Tensor& point_k,
192
+ torch::stable::Tensor real_accum,
193
+ torch::stable::Tensor imag_accum,
194
+ double weighted_cos,
195
+ double weighted_sin) {
196
+ check_float32_tensor(field, "field");
197
+ check_float32_tensor(real_accum, "real_accum");
198
+ check_float32_tensor(imag_accum, "imag_accum");
199
+ check_contiguous_tensor(field, "field");
200
+ check_contiguous_tensor(real_accum, "real_accum");
201
+ check_contiguous_tensor(imag_accum, "imag_accum");
202
+ STD_TORCH_CHECK(field.dim() == 3, "field must be rank 3");
203
+ check_index_vector(point_i, "point_i");
204
+ check_index_vector(point_j, "point_j");
205
+ check_index_vector(point_k, "point_k");
206
+ STD_TORCH_CHECK(point_i.scalar_type() == point_j.scalar_type(), "point_j must match point_i dtype");
207
+ STD_TORCH_CHECK(point_i.scalar_type() == point_k.scalar_type(), "point_k must match point_i dtype");
208
+ check_same_cuda_device(field, point_i, "point_i");
209
+ check_same_cuda_device(field, point_j, "point_j");
210
+ check_same_cuda_device(field, point_k, "point_k");
211
+ check_same_cuda_device(field, real_accum, "real_accum");
212
+ check_same_cuda_device(field, imag_accum, "imag_accum");
213
+ STD_TORCH_CHECK(real_accum.sizes().equals(imag_accum.sizes()), "observer accumulators must match");
214
+ STD_TORCH_CHECK(point_i.numel() == real_accum.numel(), "point_i size must match accumulators");
215
+ STD_TORCH_CHECK(point_j.numel() == real_accum.numel(), "point_j size must match accumulators");
216
+ STD_TORCH_CHECK(point_k.numel() == real_accum.numel(), "point_k size must match accumulators");
217
+ torch::stable::accelerator::DeviceGuard guard(field.get_device_index());
218
+ const auto sizes = field.sizes();
219
+ const int64_t point_count = real_accum.numel();
220
+ const int block_size = point_count <= 64 ? 64 : (point_count >= 262144 ? 512 : 256);
221
+ if (point_i.scalar_type() == torch::headeronly::ScalarType::Int) {
222
+ launch_point_observers<int>(
223
+ point_count,
224
+ static_cast<unsigned int>(sizes[1]),
225
+ static_cast<unsigned int>(sizes[2]),
226
+ field.mutable_data_ptr<float>(),
227
+ point_i.mutable_data_ptr<int>(),
228
+ point_j.mutable_data_ptr<int>(),
229
+ point_k.mutable_data_ptr<int>(),
230
+ static_cast<float>(weighted_cos),
231
+ static_cast<float>(weighted_sin),
232
+ real_accum.mutable_data_ptr<float>(),
233
+ imag_accum.mutable_data_ptr<float>(),
234
+ block_size);
235
+ } else {
236
+ launch_point_observers<int64_t>(
237
+ point_count,
238
+ static_cast<unsigned int>(sizes[1]),
239
+ static_cast<unsigned int>(sizes[2]),
240
+ field.mutable_data_ptr<float>(),
241
+ point_i.mutable_data_ptr<int64_t>(),
242
+ point_j.mutable_data_ptr<int64_t>(),
243
+ point_k.mutable_data_ptr<int64_t>(),
244
+ static_cast<float>(weighted_cos),
245
+ static_cast<float>(weighted_sin),
246
+ real_accum.mutable_data_ptr<float>(),
247
+ imag_accum.mutable_data_ptr<float>(),
248
+ block_size);
249
+ }
250
+ WITWIN_CUDA_CHECK();
251
+ }
252
+
253
+ void accumulate_plane_observer_cuda(
254
+ const torch::stable::Tensor& field,
255
+ torch::stable::Tensor real_accum,
256
+ torch::stable::Tensor imag_accum,
257
+ int64_t axis,
258
+ int64_t plane_index,
259
+ double weighted_cos,
260
+ double weighted_sin) {
261
+ check_float32_tensor(field, "field");
262
+ check_float32_tensor(real_accum, "real_accum");
263
+ check_float32_tensor(imag_accum, "imag_accum");
264
+ check_contiguous_tensor(field, "field");
265
+ check_contiguous_tensor(real_accum, "real_accum");
266
+ check_contiguous_tensor(imag_accum, "imag_accum");
267
+ STD_TORCH_CHECK(field.dim() == 3, "field must be rank 3");
268
+ STD_TORCH_CHECK(axis >= 0 && axis <= 2, "axis must be 0, 1, or 2");
269
+ STD_TORCH_CHECK(plane_index >= 0 && plane_index < field.size(axis), "plane_index is out of range");
270
+ check_same_cuda_device(field, real_accum, "real_accum");
271
+ check_same_cuda_device(field, imag_accum, "imag_accum");
272
+ STD_TORCH_CHECK(real_accum.sizes().equals(imag_accum.sizes()), "observer accumulators must match");
273
+ const int64_t expected = axis == 0
274
+ ? field.size(1) * field.size(2)
275
+ : (axis == 1 ? field.size(0) * field.size(2) : field.size(0) * field.size(1));
276
+ STD_TORCH_CHECK(real_accum.numel() == expected, "plane observer accumulator size does not match selected plane");
277
+ torch::stable::accelerator::DeviceGuard guard(field.get_device_index());
278
+ const auto sizes = field.sizes();
279
+ const auto nx = static_cast<unsigned int>(sizes[0]);
280
+ const auto ny = static_cast<unsigned int>(sizes[1]);
281
+ const auto nz = static_cast<unsigned int>(sizes[2]);
282
+ const auto plane = static_cast<int>(plane_index);
283
+ const auto cos_weight = static_cast<float>(weighted_cos);
284
+ const auto sin_weight = static_cast<float>(weighted_sin);
285
+ if (axis == 0) {
286
+ launch_plane_observer<0>(
287
+ nx, ny, nz, field.mutable_data_ptr<float>(), plane, cos_weight, sin_weight,
288
+ real_accum.mutable_data_ptr<float>(), imag_accum.mutable_data_ptr<float>());
289
+ } else if (axis == 1) {
290
+ launch_plane_observer<1>(
291
+ nx, ny, nz, field.mutable_data_ptr<float>(), plane, cos_weight, sin_weight,
292
+ real_accum.mutable_data_ptr<float>(), imag_accum.mutable_data_ptr<float>());
293
+ } else {
294
+ launch_plane_observer<2>(
295
+ nx, ny, nz, field.mutable_data_ptr<float>(), plane, cos_weight, sin_weight,
296
+ real_accum.mutable_data_ptr<float>(), imag_accum.mutable_data_ptr<float>());
297
+ }
298
+ WITWIN_CUDA_CHECK();
299
+ }
300
+
301
+ void plane_flux_reduce_cuda(
302
+ const torch::stable::Tensor& ea,
303
+ const torch::stable::Tensor& eb,
304
+ const torch::stable::Tensor& ha,
305
+ const torch::stable::Tensor& hb,
306
+ const torch::stable::Tensor& weights,
307
+ torch::stable::Tensor out,
308
+ int64_t out_index,
309
+ double scale) {
310
+ check_float32_tensor(ea, "ea");
311
+ check_float32_tensor(eb, "eb");
312
+ check_float32_tensor(ha, "ha");
313
+ check_float32_tensor(hb, "hb");
314
+ check_float32_tensor(weights, "weights");
315
+ check_float32_tensor(out, "out");
316
+ check_contiguous_tensor(ea, "ea");
317
+ check_contiguous_tensor(eb, "eb");
318
+ check_contiguous_tensor(ha, "ha");
319
+ check_contiguous_tensor(hb, "hb");
320
+ check_contiguous_tensor(weights, "weights");
321
+ check_contiguous_tensor(out, "out");
322
+ const int64_t plane_size = weights.numel();
323
+ STD_TORCH_CHECK(ea.numel() == plane_size, "ea size must match weights");
324
+ STD_TORCH_CHECK(eb.numel() == plane_size, "eb size must match weights");
325
+ STD_TORCH_CHECK(ha.numel() == plane_size, "ha size must match weights");
326
+ STD_TORCH_CHECK(hb.numel() == plane_size, "hb size must match weights");
327
+ STD_TORCH_CHECK(out.dim() == 1, "out must be rank 1");
328
+ STD_TORCH_CHECK(out_index >= 0 && out_index < out.numel(), "out_index is out of range");
329
+ check_same_cuda_device(ea, eb, "eb");
330
+ check_same_cuda_device(ea, ha, "ha");
331
+ check_same_cuda_device(ea, hb, "hb");
332
+ check_same_cuda_device(ea, weights, "weights");
333
+ check_same_cuda_device(ea, out, "out");
334
+ torch::stable::accelerator::DeviceGuard guard(ea.get_device_index());
335
+ const int block_size = 256;
336
+ plane_flux_reduce_kernel<<<1, block_size, block_size * sizeof(float), current_cuda_stream()>>>(
337
+ plane_size,
338
+ static_cast<float>(scale),
339
+ ea.mutable_data_ptr<float>(),
340
+ eb.mutable_data_ptr<float>(),
341
+ ha.mutable_data_ptr<float>(),
342
+ hb.mutable_data_ptr<float>(),
343
+ weights.mutable_data_ptr<float>(),
344
+ out.mutable_data_ptr<float>(),
345
+ out_index);
346
+ WITWIN_CUDA_CHECK();
347
+ }
@@ -0,0 +1,211 @@
1
+ #include "../launch.h"
2
+ #include "../tensors.h"
3
+ #include "common.cuh"
4
+
5
+ namespace {
6
+
7
+ void check_field3d(const torch::stable::Tensor& field, const char* name) {
8
+ check_float32_tensor(field, name);
9
+ check_contiguous_tensor(field, name);
10
+ STD_TORCH_CHECK(field.dim() == 3, name, " must be a contiguous 3D float32 CUDA tensor");
11
+ }
12
+
13
+ template <int Axis>
14
+ __device__ __forceinline__ void face_indices(
15
+ int coord_a,
16
+ int coord_b,
17
+ int nx,
18
+ int ny,
19
+ int nz,
20
+ long long& low,
21
+ long long& high) {
22
+ if constexpr (Axis == 0) {
23
+ const int j = coord_a;
24
+ const int k = coord_b;
25
+ low = offset3d(0, j, k, ny, nz);
26
+ high = low + static_cast<long long>(nx - 1) * ny * nz;
27
+ return;
28
+ }
29
+ if constexpr (Axis == 1) {
30
+ const int i = coord_a;
31
+ const int k = coord_b;
32
+ low = offset3d(i, 0, k, ny, nz);
33
+ high = low + static_cast<long long>(ny - 1) * nz;
34
+ return;
35
+ }
36
+ const int i = coord_a;
37
+ const int j = coord_b;
38
+ low = offset3d(i, j, 0, ny, nz);
39
+ high = low + nz - 1;
40
+ }
41
+
42
+ template <int Axis>
43
+ __global__ void project_periodic_boundary_kernel(
44
+ int nx,
45
+ int ny,
46
+ int nz,
47
+ float* __restrict__ field) {
48
+ const int coord_b = blockIdx.x * blockDim.x + threadIdx.x;
49
+ const int coord_a = blockIdx.y * blockDim.y + threadIdx.y;
50
+ const int dim_b = Axis == 2 ? ny : nz;
51
+ const int dim_a = Axis == 0 ? ny : nx;
52
+ if (coord_a >= dim_a || coord_b >= dim_b) {
53
+ return;
54
+ }
55
+ long long low = 0;
56
+ long long high = 0;
57
+ face_indices<Axis>(coord_a, coord_b, nx, ny, nz, low, high);
58
+ const float average = 0.5f * (field[low] + field[high]);
59
+ field[low] = average;
60
+ field[high] = average;
61
+ }
62
+
63
+ template <int Axis, bool RealPhase>
64
+ __global__ void project_bloch_boundary_kernel(
65
+ int nx,
66
+ int ny,
67
+ int nz,
68
+ float phase_cos,
69
+ float phase_sin,
70
+ float* __restrict__ real_field,
71
+ float* __restrict__ imag_field) {
72
+ const int coord_b = blockIdx.x * blockDim.x + threadIdx.x;
73
+ const int coord_a = blockIdx.y * blockDim.y + threadIdx.y;
74
+ const int dim_b = Axis == 2 ? ny : nz;
75
+ const int dim_a = Axis == 0 ? ny : nx;
76
+ if (coord_a >= dim_a || coord_b >= dim_b) {
77
+ return;
78
+ }
79
+ long long low = 0;
80
+ long long high = 0;
81
+ face_indices<Axis>(coord_a, coord_b, nx, ny, nz, low, high);
82
+
83
+ const float low_r = real_field[low];
84
+ const float low_i = imag_field[low];
85
+ const float high_r = real_field[high];
86
+ const float high_i = imag_field[high];
87
+ const float c = phase_cos;
88
+
89
+ float projected_low_r;
90
+ float projected_low_i;
91
+ float projected_high_r;
92
+ float projected_high_i;
93
+ if constexpr (RealPhase) {
94
+ projected_low_r = 0.5f * (low_r + c * high_r);
95
+ projected_low_i = 0.5f * (low_i + c * high_i);
96
+ projected_high_r = c * projected_low_r;
97
+ projected_high_i = c * projected_low_i;
98
+ } else {
99
+ const float s = phase_sin;
100
+ projected_low_r = 0.5f * (low_r + c * high_r + s * high_i);
101
+ projected_low_i = 0.5f * (low_i + c * high_i - s * high_r);
102
+ projected_high_r = c * projected_low_r - s * projected_low_i;
103
+ projected_high_i = s * projected_low_r + c * projected_low_i;
104
+ }
105
+
106
+ real_field[low] = projected_low_r;
107
+ imag_field[low] = projected_low_i;
108
+ real_field[high] = projected_high_r;
109
+ imag_field[high] = projected_high_i;
110
+ }
111
+
112
+ template <int Axis>
113
+ void launch_periodic_projection(int nx, int ny, int nz, float* __restrict__ field) {
114
+ const dim3 block(16, 16, 1);
115
+ const unsigned int dim_b = Axis == 2 ? static_cast<unsigned int>(ny) : static_cast<unsigned int>(nz);
116
+ const unsigned int dim_a = Axis == 0 ? static_cast<unsigned int>(ny) : static_cast<unsigned int>(nx);
117
+ const dim3 grid((dim_b + block.x - 1) / block.x, (dim_a + block.y - 1) / block.y, 1);
118
+ project_periodic_boundary_kernel<Axis><<<grid, block, 0, current_cuda_stream()>>>(
119
+ nx,
120
+ ny,
121
+ nz,
122
+ field);
123
+ }
124
+
125
+ template <int Axis, bool RealPhase>
126
+ void launch_bloch_projection(
127
+ int nx,
128
+ int ny,
129
+ int nz,
130
+ float phase_cos,
131
+ float phase_sin,
132
+ float* __restrict__ field_real,
133
+ float* __restrict__ field_imag) {
134
+ const dim3 block(16, 16, 1);
135
+ const unsigned int dim_b = Axis == 2 ? static_cast<unsigned int>(ny) : static_cast<unsigned int>(nz);
136
+ const unsigned int dim_a = Axis == 0 ? static_cast<unsigned int>(ny) : static_cast<unsigned int>(nx);
137
+ const dim3 grid((dim_b + block.x - 1) / block.x, (dim_a + block.y - 1) / block.y, 1);
138
+ project_bloch_boundary_kernel<Axis, RealPhase><<<grid, block, 0, current_cuda_stream()>>>(
139
+ nx,
140
+ ny,
141
+ nz,
142
+ phase_cos,
143
+ phase_sin,
144
+ field_real,
145
+ field_imag);
146
+ }
147
+
148
+ } // namespace
149
+
150
+ void project_periodic_boundary_cuda(torch::stable::Tensor field, int64_t axis) {
151
+ check_field3d(field, "field");
152
+ STD_TORCH_CHECK(axis >= 0 && axis < 3, "axis must be in [0, 3)");
153
+ const torch::stable::accelerator::DeviceGuard device_guard(field.get_device_index());
154
+ const int nx = static_cast<int>(field.size(0));
155
+ const int ny = static_cast<int>(field.size(1));
156
+ const int nz = static_cast<int>(field.size(2));
157
+ if (axis == 0) {
158
+ launch_periodic_projection<0>(nx, ny, nz, field.mutable_data_ptr<float>());
159
+ } else if (axis == 1) {
160
+ launch_periodic_projection<1>(nx, ny, nz, field.mutable_data_ptr<float>());
161
+ } else {
162
+ launch_periodic_projection<2>(nx, ny, nz, field.mutable_data_ptr<float>());
163
+ }
164
+ WITWIN_CUDA_CHECK();
165
+ }
166
+
167
+ void project_bloch_boundary_cuda(
168
+ torch::stable::Tensor field_real,
169
+ torch::stable::Tensor field_imag,
170
+ int64_t axis,
171
+ double phase_cos,
172
+ double phase_sin) {
173
+ check_field3d(field_real, "field_real");
174
+ check_field3d(field_imag, "field_imag");
175
+ check_same_cuda_device(field_real, field_imag, "field_imag");
176
+ STD_TORCH_CHECK(field_real.sizes().equals(field_imag.sizes()), "field_imag must match field_real shape");
177
+ STD_TORCH_CHECK(axis >= 0 && axis < 3, "axis must be in [0, 3)");
178
+ const torch::stable::accelerator::DeviceGuard device_guard(field_real.get_device_index());
179
+ const int nx = static_cast<int>(field_real.size(0));
180
+ const int ny = static_cast<int>(field_real.size(1));
181
+ const int nz = static_cast<int>(field_real.size(2));
182
+ const float phase_cos_f = static_cast<float>(phase_cos);
183
+ const float phase_sin_f = static_cast<float>(phase_sin);
184
+ const bool real_phase = phase_sin_f == 0.0f;
185
+ if (axis == 0) {
186
+ if (real_phase) {
187
+ launch_bloch_projection<0, true>(
188
+ nx, ny, nz, phase_cos_f, phase_sin_f, field_real.mutable_data_ptr<float>(), field_imag.mutable_data_ptr<float>());
189
+ } else {
190
+ launch_bloch_projection<0, false>(
191
+ nx, ny, nz, phase_cos_f, phase_sin_f, field_real.mutable_data_ptr<float>(), field_imag.mutable_data_ptr<float>());
192
+ }
193
+ } else if (axis == 1) {
194
+ if (real_phase) {
195
+ launch_bloch_projection<1, true>(
196
+ nx, ny, nz, phase_cos_f, phase_sin_f, field_real.mutable_data_ptr<float>(), field_imag.mutable_data_ptr<float>());
197
+ } else {
198
+ launch_bloch_projection<1, false>(
199
+ nx, ny, nz, phase_cos_f, phase_sin_f, field_real.mutable_data_ptr<float>(), field_imag.mutable_data_ptr<float>());
200
+ }
201
+ } else {
202
+ if (real_phase) {
203
+ launch_bloch_projection<2, true>(
204
+ nx, ny, nz, phase_cos_f, phase_sin_f, field_real.mutable_data_ptr<float>(), field_imag.mutable_data_ptr<float>());
205
+ } else {
206
+ launch_bloch_projection<2, false>(
207
+ nx, ny, nz, phase_cos_f, phase_sin_f, field_real.mutable_data_ptr<float>(), field_imag.mutable_data_ptr<float>());
208
+ }
209
+ }
210
+ WITWIN_CUDA_CHECK();
211
+ }