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,490 @@
1
+ from __future__ import annotations
2
+
3
+ import math
4
+
5
+ import torch
6
+
7
+
8
+ AXIS_INDEX = {"x": 0, "y": 1, "z": 2}
9
+ E_CURL_ATTR = {"Ex": "cex_curl", "Ey": "cey_curl", "Ez": "cez_curl"}
10
+ H_CURL_ATTR = {"Hx": "chx_curl", "Hy": "chy_curl", "Hz": "chz_curl"}
11
+ _FACE_SPEC_RANGES = {
12
+ ("x", "low"): slice(0, 2),
13
+ ("x", "high"): slice(2, 4),
14
+ ("y", "low"): slice(4, 6),
15
+ ("y", "high"): slice(6, 8),
16
+ ("z", "low"): slice(8, 10),
17
+ ("z", "high"): slice(10, 12),
18
+ }
19
+
20
+
21
+ def make_discrete_spec(
22
+ *,
23
+ field_name,
24
+ incident_name,
25
+ offsets,
26
+ shape,
27
+ sample_offsets,
28
+ sample_shape,
29
+ delta_axis,
30
+ sign,
31
+ sample_kind,
32
+ ):
33
+ return {
34
+ "field_name": field_name,
35
+ "incident_name": incident_name,
36
+ "offsets": offsets,
37
+ "shape": shape,
38
+ "sample_offsets": sample_offsets,
39
+ "sample_shape": sample_shape,
40
+ "delta_axis": delta_axis,
41
+ "sign": float(sign),
42
+ "sample_kind": sample_kind,
43
+ }
44
+
45
+
46
+ def build_discrete_tfsf_specs(lower, upper):
47
+ ix0, iy0, iz0 = lower
48
+ ix1, iy1, iz1 = upper
49
+
50
+ magnetic_specs = [
51
+ make_discrete_spec(
52
+ field_name="Hy",
53
+ incident_name="Ez",
54
+ offsets=(ix0 - 1, iy0, iz0),
55
+ shape=(1, iy1 - iy0 + 1, iz1 - iz0),
56
+ sample_offsets=(ix0, iy0, iz0),
57
+ sample_shape=(1, iy1 - iy0 + 1, iz1 - iz0),
58
+ delta_axis="x",
59
+ sign=+1.0,
60
+ sample_kind="electric",
61
+ ),
62
+ make_discrete_spec(
63
+ field_name="Hz",
64
+ incident_name="Ey",
65
+ offsets=(ix0 - 1, iy0, iz0),
66
+ shape=(1, iy1 - iy0, iz1 - iz0 + 1),
67
+ sample_offsets=(ix0, iy0, iz0),
68
+ sample_shape=(1, iy1 - iy0, iz1 - iz0 + 1),
69
+ delta_axis="x",
70
+ sign=-1.0,
71
+ sample_kind="electric",
72
+ ),
73
+ make_discrete_spec(
74
+ field_name="Hy",
75
+ incident_name="Ez",
76
+ offsets=(ix1, iy0, iz0),
77
+ shape=(1, iy1 - iy0 + 1, iz1 - iz0),
78
+ sample_offsets=(ix1, iy0, iz0),
79
+ sample_shape=(1, iy1 - iy0 + 1, iz1 - iz0),
80
+ delta_axis="x",
81
+ sign=-1.0,
82
+ sample_kind="electric",
83
+ ),
84
+ make_discrete_spec(
85
+ field_name="Hz",
86
+ incident_name="Ey",
87
+ offsets=(ix1, iy0, iz0),
88
+ shape=(1, iy1 - iy0, iz1 - iz0 + 1),
89
+ sample_offsets=(ix1, iy0, iz0),
90
+ sample_shape=(1, iy1 - iy0, iz1 - iz0 + 1),
91
+ delta_axis="x",
92
+ sign=+1.0,
93
+ sample_kind="electric",
94
+ ),
95
+ make_discrete_spec(
96
+ field_name="Hx",
97
+ incident_name="Ez",
98
+ offsets=(ix0, iy0 - 1, iz0),
99
+ shape=(ix1 - ix0 + 1, 1, iz1 - iz0),
100
+ sample_offsets=(ix0, iy0, iz0),
101
+ sample_shape=(ix1 - ix0 + 1, 1, iz1 - iz0),
102
+ delta_axis="y",
103
+ sign=-1.0,
104
+ sample_kind="electric",
105
+ ),
106
+ make_discrete_spec(
107
+ field_name="Hz",
108
+ incident_name="Ex",
109
+ offsets=(ix0, iy0 - 1, iz0),
110
+ shape=(ix1 - ix0, 1, iz1 - iz0 + 1),
111
+ sample_offsets=(ix0, iy0, iz0),
112
+ sample_shape=(ix1 - ix0, 1, iz1 - iz0 + 1),
113
+ delta_axis="y",
114
+ sign=+1.0,
115
+ sample_kind="electric",
116
+ ),
117
+ make_discrete_spec(
118
+ field_name="Hx",
119
+ incident_name="Ez",
120
+ offsets=(ix0, iy1, iz0),
121
+ shape=(ix1 - ix0 + 1, 1, iz1 - iz0),
122
+ sample_offsets=(ix0, iy1, iz0),
123
+ sample_shape=(ix1 - ix0 + 1, 1, iz1 - iz0),
124
+ delta_axis="y",
125
+ sign=+1.0,
126
+ sample_kind="electric",
127
+ ),
128
+ make_discrete_spec(
129
+ field_name="Hz",
130
+ incident_name="Ex",
131
+ offsets=(ix0, iy1, iz0),
132
+ shape=(ix1 - ix0, 1, iz1 - iz0 + 1),
133
+ sample_offsets=(ix0, iy1, iz0),
134
+ sample_shape=(ix1 - ix0, 1, iz1 - iz0 + 1),
135
+ delta_axis="y",
136
+ sign=-1.0,
137
+ sample_kind="electric",
138
+ ),
139
+ make_discrete_spec(
140
+ field_name="Hx",
141
+ incident_name="Ey",
142
+ offsets=(ix0, iy0, iz0 - 1),
143
+ shape=(ix1 - ix0 + 1, iy1 - iy0, 1),
144
+ sample_offsets=(ix0, iy0, iz0),
145
+ sample_shape=(ix1 - ix0 + 1, iy1 - iy0, 1),
146
+ delta_axis="z",
147
+ sign=+1.0,
148
+ sample_kind="electric",
149
+ ),
150
+ make_discrete_spec(
151
+ field_name="Hy",
152
+ incident_name="Ex",
153
+ offsets=(ix0, iy0, iz0 - 1),
154
+ shape=(ix1 - ix0, iy1 - iy0 + 1, 1),
155
+ sample_offsets=(ix0, iy0, iz0),
156
+ sample_shape=(ix1 - ix0, iy1 - iy0 + 1, 1),
157
+ delta_axis="z",
158
+ sign=-1.0,
159
+ sample_kind="electric",
160
+ ),
161
+ make_discrete_spec(
162
+ field_name="Hx",
163
+ incident_name="Ey",
164
+ offsets=(ix0, iy0, iz1),
165
+ shape=(ix1 - ix0 + 1, iy1 - iy0, 1),
166
+ sample_offsets=(ix0, iy0, iz1),
167
+ sample_shape=(ix1 - ix0 + 1, iy1 - iy0, 1),
168
+ delta_axis="z",
169
+ sign=-1.0,
170
+ sample_kind="electric",
171
+ ),
172
+ make_discrete_spec(
173
+ field_name="Hy",
174
+ incident_name="Ex",
175
+ offsets=(ix0, iy0, iz1),
176
+ shape=(ix1 - ix0, iy1 - iy0 + 1, 1),
177
+ sample_offsets=(ix0, iy0, iz1),
178
+ sample_shape=(ix1 - ix0, iy1 - iy0 + 1, 1),
179
+ delta_axis="z",
180
+ sign=+1.0,
181
+ sample_kind="electric",
182
+ ),
183
+ ]
184
+
185
+ electric_specs = [
186
+ make_discrete_spec(
187
+ field_name="Ey",
188
+ incident_name="Hz",
189
+ offsets=(ix0, iy0, iz0),
190
+ shape=(1, iy1 - iy0, iz1 - iz0 + 1),
191
+ sample_offsets=(ix0 - 1, iy0, iz0),
192
+ sample_shape=(1, iy1 - iy0, iz1 - iz0 + 1),
193
+ delta_axis="x",
194
+ sign=+1.0,
195
+ sample_kind="magnetic",
196
+ ),
197
+ make_discrete_spec(
198
+ field_name="Ez",
199
+ incident_name="Hy",
200
+ offsets=(ix0, iy0, iz0),
201
+ shape=(1, iy1 - iy0 + 1, iz1 - iz0),
202
+ sample_offsets=(ix0 - 1, iy0, iz0),
203
+ sample_shape=(1, iy1 - iy0 + 1, iz1 - iz0),
204
+ delta_axis="x",
205
+ sign=-1.0,
206
+ sample_kind="magnetic",
207
+ ),
208
+ make_discrete_spec(
209
+ field_name="Ey",
210
+ incident_name="Hz",
211
+ offsets=(ix1, iy0, iz0),
212
+ shape=(1, iy1 - iy0, iz1 - iz0 + 1),
213
+ sample_offsets=(ix1, iy0, iz0),
214
+ sample_shape=(1, iy1 - iy0, iz1 - iz0 + 1),
215
+ delta_axis="x",
216
+ sign=-1.0,
217
+ sample_kind="magnetic",
218
+ ),
219
+ make_discrete_spec(
220
+ field_name="Ez",
221
+ incident_name="Hy",
222
+ offsets=(ix1, iy0, iz0),
223
+ shape=(1, iy1 - iy0 + 1, iz1 - iz0),
224
+ sample_offsets=(ix1, iy0, iz0),
225
+ sample_shape=(1, iy1 - iy0 + 1, iz1 - iz0),
226
+ delta_axis="x",
227
+ sign=+1.0,
228
+ sample_kind="magnetic",
229
+ ),
230
+ make_discrete_spec(
231
+ field_name="Ex",
232
+ incident_name="Hz",
233
+ offsets=(ix0, iy0, iz0),
234
+ shape=(ix1 - ix0, 1, iz1 - iz0 + 1),
235
+ sample_offsets=(ix0, iy0 - 1, iz0),
236
+ sample_shape=(ix1 - ix0, 1, iz1 - iz0 + 1),
237
+ delta_axis="y",
238
+ sign=-1.0,
239
+ sample_kind="magnetic",
240
+ ),
241
+ make_discrete_spec(
242
+ field_name="Ez",
243
+ incident_name="Hx",
244
+ offsets=(ix0, iy0, iz0),
245
+ shape=(ix1 - ix0 + 1, 1, iz1 - iz0),
246
+ sample_offsets=(ix0, iy0 - 1, iz0),
247
+ sample_shape=(ix1 - ix0 + 1, 1, iz1 - iz0),
248
+ delta_axis="y",
249
+ sign=+1.0,
250
+ sample_kind="magnetic",
251
+ ),
252
+ make_discrete_spec(
253
+ field_name="Ex",
254
+ incident_name="Hz",
255
+ offsets=(ix0, iy1, iz0),
256
+ shape=(ix1 - ix0, 1, iz1 - iz0 + 1),
257
+ sample_offsets=(ix0, iy1, iz0),
258
+ sample_shape=(ix1 - ix0, 1, iz1 - iz0 + 1),
259
+ delta_axis="y",
260
+ sign=+1.0,
261
+ sample_kind="magnetic",
262
+ ),
263
+ make_discrete_spec(
264
+ field_name="Ez",
265
+ incident_name="Hx",
266
+ offsets=(ix0, iy1, iz0),
267
+ shape=(ix1 - ix0 + 1, 1, iz1 - iz0),
268
+ sample_offsets=(ix0, iy1, iz0),
269
+ sample_shape=(ix1 - ix0 + 1, 1, iz1 - iz0),
270
+ delta_axis="y",
271
+ sign=-1.0,
272
+ sample_kind="magnetic",
273
+ ),
274
+ make_discrete_spec(
275
+ field_name="Ex",
276
+ incident_name="Hy",
277
+ offsets=(ix0, iy0, iz0),
278
+ shape=(ix1 - ix0, iy1 - iy0 + 1, 1),
279
+ sample_offsets=(ix0, iy0, iz0 - 1),
280
+ sample_shape=(ix1 - ix0, iy1 - iy0 + 1, 1),
281
+ delta_axis="z",
282
+ sign=+1.0,
283
+ sample_kind="magnetic",
284
+ ),
285
+ make_discrete_spec(
286
+ field_name="Ey",
287
+ incident_name="Hx",
288
+ offsets=(ix0, iy0, iz0),
289
+ shape=(ix1 - ix0 + 1, iy1 - iy0, 1),
290
+ sample_offsets=(ix0, iy0, iz0 - 1),
291
+ sample_shape=(ix1 - ix0 + 1, iy1 - iy0, 1),
292
+ delta_axis="z",
293
+ sign=-1.0,
294
+ sample_kind="magnetic",
295
+ ),
296
+ make_discrete_spec(
297
+ field_name="Ex",
298
+ incident_name="Hy",
299
+ offsets=(ix0, iy0, iz1),
300
+ shape=(ix1 - ix0, iy1 - iy0 + 1, 1),
301
+ sample_offsets=(ix0, iy0, iz1),
302
+ sample_shape=(ix1 - ix0, iy1 - iy0 + 1, 1),
303
+ delta_axis="z",
304
+ sign=-1.0,
305
+ sample_kind="magnetic",
306
+ ),
307
+ make_discrete_spec(
308
+ field_name="Ey",
309
+ incident_name="Hx",
310
+ offsets=(ix0, iy0, iz1),
311
+ shape=(ix1 - ix0 + 1, iy1 - iy0, 1),
312
+ sample_offsets=(ix0, iy0, iz1),
313
+ sample_shape=(ix1 - ix0 + 1, iy1 - iy0, 1),
314
+ delta_axis="z",
315
+ sign=+1.0,
316
+ sample_kind="magnetic",
317
+ ),
318
+ ]
319
+ return electric_specs, magnetic_specs
320
+
321
+
322
+ def build_slab_tfsf_specs(lower, upper, *, axis: str):
323
+ axis_name = str(axis).lower()
324
+ if axis_name not in AXIS_INDEX:
325
+ raise ValueError("TFSF slab axis must be 'x', 'y', or 'z'.")
326
+ electric_specs, magnetic_specs = build_discrete_tfsf_specs(lower, upper)
327
+ low_slice = _FACE_SPEC_RANGES[(axis_name, "low")]
328
+ high_slice = _FACE_SPEC_RANGES[(axis_name, "high")]
329
+ return (
330
+ electric_specs[low_slice] + electric_specs[high_slice],
331
+ magnetic_specs[low_slice] + magnetic_specs[high_slice],
332
+ )
333
+
334
+
335
+ def magnetic_unit_vector(direction, polarization):
336
+ mx = -(float(direction[1]) * float(polarization[2]) - float(direction[2]) * float(polarization[1]))
337
+ my = -(float(direction[2]) * float(polarization[0]) - float(direction[0]) * float(polarization[2]))
338
+ mz = -(float(direction[0]) * float(polarization[1]) - float(direction[1]) * float(polarization[0]))
339
+ magnitude = math.sqrt(mx * mx + my * my + mz * mz)
340
+ if magnitude <= 1e-12:
341
+ raise ValueError("Plane-wave magnetic direction is ill-defined.")
342
+ return {
343
+ "Hx": mx / magnitude,
344
+ "Hy": my / magnitude,
345
+ "Hz": mz / magnitude,
346
+ }
347
+
348
+
349
+ def magnetic_physical_vector(direction, polarization, impedance):
350
+ unit = magnetic_unit_vector(direction, polarization)
351
+ return {name: value / float(impedance) for name, value in unit.items()}
352
+
353
+
354
+ def normalize_vector_components(vector, *, name):
355
+ magnitude = math.sqrt(sum(float(component) * float(component) for component in vector))
356
+ if magnitude <= 1e-12:
357
+ raise ValueError(f"{name} must have non-zero magnitude.")
358
+ return tuple(float(component) / magnitude for component in vector)
359
+
360
+
361
+ def cross_vector(a, b):
362
+ return (
363
+ float(a[1]) * float(b[2]) - float(a[2]) * float(b[1]),
364
+ float(a[2]) * float(b[0]) - float(a[0]) * float(b[2]),
365
+ float(a[0]) * float(b[1]) - float(a[1]) * float(b[0]),
366
+ )
367
+
368
+
369
+ def discrete_plane_wave_vectors(direction, polarization, k_numeric: float, deltas):
370
+ effective_wavevector = []
371
+ for axis, component in zip("xyz", direction):
372
+ delta = float(deltas[axis])
373
+ effective_wavevector.append(math.sin(0.5 * k_numeric * float(component) * delta) / delta)
374
+ kappa_hat = normalize_vector_components(effective_wavevector, name="effective_wavevector")
375
+
376
+ projection = sum(float(p) * float(k) for p, k in zip(polarization, kappa_hat))
377
+ electric = tuple(float(p) - projection * float(k) for p, k in zip(polarization, kappa_hat))
378
+ electric = normalize_vector_components(electric, name="discrete_polarization")
379
+ magnetic = normalize_vector_components(
380
+ tuple(-float(component) for component in cross_vector(kappa_hat, electric)),
381
+ name="discrete_magnetic",
382
+ )
383
+ return (
384
+ {"Ex": electric[0], "Ey": electric[1], "Ez": electric[2]},
385
+ {"Hx": magnetic[0], "Hy": magnetic[1], "Hz": magnetic[2]},
386
+ )
387
+
388
+
389
+ def is_reference_plane_wave_x_ez(source) -> bool:
390
+ direction = source["direction"]
391
+ polarization = source["polarization"]
392
+ return (
393
+ abs(float(direction[0]) - 1.0) <= 1e-9
394
+ and abs(float(direction[1])) <= 1e-9
395
+ and abs(float(direction[2])) <= 1e-9
396
+ and abs(float(polarization[0])) <= 1e-9
397
+ and abs(float(polarization[1])) <= 1e-9
398
+ and abs(float(polarization[2]) - 1.0) <= 1e-9
399
+ )
400
+
401
+
402
+ def axis_aligned_direction(direction):
403
+ dominant_axis = None
404
+ dominant_sign = 0
405
+ for axis, component in zip("xyz", direction):
406
+ value = float(component)
407
+ if abs(value) > 1e-9:
408
+ if dominant_axis is not None:
409
+ return None
410
+ dominant_axis = axis
411
+ dominant_sign = 1 if value > 0.0 else -1
412
+ if dominant_axis is None:
413
+ return None
414
+ return dominant_axis, dominant_sign
415
+
416
+
417
+ def line_index_tensor(solver, start: int, length: int):
418
+ return torch.arange(start, start + length, device=solver.device, dtype=torch.int64)
419
+
420
+
421
+ def constant_line_index_tensor(solver, index: int):
422
+ return torch.tensor([index], device=solver.device, dtype=torch.int64)
423
+
424
+
425
+ def make_reference_term(
426
+ solver,
427
+ *,
428
+ field_name,
429
+ offsets,
430
+ coeff_patch,
431
+ sample_kind,
432
+ sample_indices,
433
+ component_scale,
434
+ ):
435
+ scalar_sample_index = None
436
+ if int(sample_indices.numel()) == 1:
437
+ scalar_sample_index = int(sample_indices.reshape(-1)[0].item())
438
+ return {
439
+ "field_name": field_name,
440
+ "offsets": offsets,
441
+ "coeff_patch": coeff_patch.contiguous(),
442
+ "sample_kind": sample_kind,
443
+ "sample_indices": sample_indices.contiguous(),
444
+ "scalar_sample_index": scalar_sample_index,
445
+ "sample_view": (int(sample_indices.numel()), 1, 1),
446
+ "component_scale": float(component_scale),
447
+ }
448
+
449
+
450
+ def axis_aligned_sample_view(axis: str, length: int):
451
+ if axis == "x":
452
+ return (int(length), 1, 1)
453
+ if axis == "y":
454
+ return (1, int(length), 1)
455
+ return (1, 1, int(length))
456
+
457
+
458
+ def sample_axis_code(sample_view):
459
+ if int(sample_view[0]) > 1:
460
+ return 0
461
+ if int(sample_view[1]) > 1:
462
+ return 1
463
+ return 2
464
+
465
+
466
+ def reference_sample_axis_code(term):
467
+ if int(term["sample_indices"].numel()) > 1:
468
+ return sample_axis_code(term["sample_view"])
469
+
470
+ shape = tuple(int(length) for length in term["coeff_patch"].shape)
471
+ if shape[0] == 1:
472
+ return 0
473
+ if shape[1] == 1:
474
+ return 1
475
+ return 2
476
+
477
+
478
+ def axis_aligned_sample_indices(solver, axis: str, direction_sign: int, sample_kind: str, offsets, shape):
479
+ axis_index = AXIS_INDEX[axis]
480
+ start = int(offsets[axis_index])
481
+ length = int(shape[axis_index])
482
+ indices = torch.arange(start, start + length, device=solver.device, dtype=torch.int64)
483
+ if direction_sign > 0:
484
+ return indices
485
+
486
+ if sample_kind == "electric":
487
+ max_index = {"x": solver.Nx - 1, "y": solver.Ny - 1, "z": solver.Nz - 1}[axis]
488
+ else:
489
+ max_index = {"x": solver.Nx - 2, "y": solver.Ny - 2, "z": solver.Nz - 2}[axis]
490
+ return max_index - indices