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.
- witwin/maxwell/__init__.py +164 -0
- witwin/maxwell/adapters/__init__.py +1 -0
- witwin/maxwell/adapters/gds.py +203 -0
- witwin/maxwell/adapters/tidy3d.py +1669 -0
- witwin/maxwell/adjoint_inputs.py +70 -0
- witwin/maxwell/compiler/__init__.py +11 -0
- witwin/maxwell/compiler/materials.py +1355 -0
- witwin/maxwell/compiler/monitors.py +164 -0
- witwin/maxwell/compiler/sources.py +222 -0
- witwin/maxwell/fdfd/__init__.py +3 -0
- witwin/maxwell/fdfd/adjoint/__init__.py +3 -0
- witwin/maxwell/fdfd/adjoint/bridge.py +180 -0
- witwin/maxwell/fdfd/ams.py +300 -0
- witwin/maxwell/fdfd/krylov.py +280 -0
- witwin/maxwell/fdfd/plotting.py +110 -0
- witwin/maxwell/fdfd/postprocess.py +33 -0
- witwin/maxwell/fdfd/solver.py +1057 -0
- witwin/maxwell/fdtd/__init__.py +4 -0
- witwin/maxwell/fdtd/adjoint/__init__.py +21 -0
- witwin/maxwell/fdtd/adjoint/bridge.py +746 -0
- witwin/maxwell/fdtd/adjoint/capabilities.py +29 -0
- witwin/maxwell/fdtd/adjoint/core.py +3614 -0
- witwin/maxwell/fdtd/adjoint/dispatch.py +684 -0
- witwin/maxwell/fdtd/adjoint/native.py +2561 -0
- witwin/maxwell/fdtd/adjoint/profiler.py +195 -0
- witwin/maxwell/fdtd/adjoint/reverse_common.py +207 -0
- witwin/maxwell/fdtd/adjoint/seeds.py +524 -0
- witwin/maxwell/fdtd/boundary/__init__.py +37 -0
- witwin/maxwell/fdtd/boundary/bloch.py +90 -0
- witwin/maxwell/fdtd/boundary/common.py +41 -0
- witwin/maxwell/fdtd/boundary/cpml.py +527 -0
- witwin/maxwell/fdtd/boundary/runtime.py +209 -0
- witwin/maxwell/fdtd/checkpoint.py +227 -0
- witwin/maxwell/fdtd/coords.py +32 -0
- witwin/maxwell/fdtd/cuda/__init__.py +11 -0
- witwin/maxwell/fdtd/cuda/backend.py +3532 -0
- witwin/maxwell/fdtd/cuda/build.py +292 -0
- witwin/maxwell/fdtd/cuda/extension.cpp +2053 -0
- witwin/maxwell/fdtd/cuda/kernels/adjoint.cu +5290 -0
- witwin/maxwell/fdtd/cuda/kernels/aniso.cu +1704 -0
- witwin/maxwell/fdtd/cuda/kernels/boundary.cu +424 -0
- witwin/maxwell/fdtd/cuda/kernels/common.cu +74 -0
- witwin/maxwell/fdtd/cuda/kernels/common.cuh +82 -0
- witwin/maxwell/fdtd/cuda/kernels/dispersive.cu +782 -0
- witwin/maxwell/fdtd/cuda/kernels/electric.cu +3613 -0
- witwin/maxwell/fdtd/cuda/kernels/magnetic.cu +781 -0
- witwin/maxwell/fdtd/cuda/kernels/observers.cu +347 -0
- witwin/maxwell/fdtd/cuda/kernels/projection.cu +211 -0
- witwin/maxwell/fdtd/cuda/kernels/sources.cu +1482 -0
- witwin/maxwell/fdtd/cuda/kernels/spectral.cu +297 -0
- witwin/maxwell/fdtd/cuda/launch.h +23 -0
- witwin/maxwell/fdtd/cuda/prebuilt/witwin_maxwell_fdtd_cuda.pyd +0 -0
- witwin/maxwell/fdtd/cuda/runtime/__init__.py +6 -0
- witwin/maxwell/fdtd/cuda/runtime/dispatcher.py +6 -0
- witwin/maxwell/fdtd/cuda/runtime/graph.py +33 -0
- witwin/maxwell/fdtd/cuda/tensors.h +35 -0
- witwin/maxwell/fdtd/dispersion.py +48 -0
- witwin/maxwell/fdtd/evaluation.py +290 -0
- witwin/maxwell/fdtd/excitation/__init__.py +27 -0
- witwin/maxwell/fdtd/excitation/injection.py +1102 -0
- witwin/maxwell/fdtd/excitation/modes.py +1675 -0
- witwin/maxwell/fdtd/excitation/spatial.py +469 -0
- witwin/maxwell/fdtd/excitation/temporal.py +510 -0
- witwin/maxwell/fdtd/excitation/tfsf_apply.py +264 -0
- witwin/maxwell/fdtd/excitation/tfsf_common.py +421 -0
- witwin/maxwell/fdtd/excitation/tfsf_specs.py +490 -0
- witwin/maxwell/fdtd/excitation/tfsf_state.py +778 -0
- witwin/maxwell/fdtd/io.py +31 -0
- witwin/maxwell/fdtd/material_pullback.py +371 -0
- witwin/maxwell/fdtd/meshing/__init__.py +3 -0
- witwin/maxwell/fdtd/meshing/autogrid.py +335 -0
- witwin/maxwell/fdtd/observers.py +1453 -0
- witwin/maxwell/fdtd/plotting.py +121 -0
- witwin/maxwell/fdtd/postprocess.py +205 -0
- witwin/maxwell/fdtd/runtime/__init__.py +119 -0
- witwin/maxwell/fdtd/runtime/initialization.py +236 -0
- witwin/maxwell/fdtd/runtime/materials.py +1563 -0
- witwin/maxwell/fdtd/runtime/module_cache.py +31 -0
- witwin/maxwell/fdtd/runtime/spectral.py +581 -0
- witwin/maxwell/fdtd/runtime/stepping.py +1987 -0
- witwin/maxwell/fdtd/solver.py +502 -0
- witwin/maxwell/geometry/__init__.py +5 -0
- witwin/maxwell/geometry/polyslab.py +299 -0
- witwin/maxwell/materials.py +3 -0
- witwin/maxwell/media.py +1467 -0
- witwin/maxwell/monitors.py +743 -0
- witwin/maxwell/ports.py +113 -0
- witwin/maxwell/postprocess/RCS.py +139 -0
- witwin/maxwell/postprocess/__init__.py +42 -0
- witwin/maxwell/postprocess/diffraction.py +219 -0
- witwin/maxwell/postprocess/directivity.py +152 -0
- witwin/maxwell/postprocess/emission.py +67 -0
- witwin/maxwell/postprocess/modal.py +324 -0
- witwin/maxwell/postprocess/nfft.py +266 -0
- witwin/maxwell/postprocess/scattering_parameters.py +358 -0
- witwin/maxwell/postprocess/stratton_chu.py +1193 -0
- witwin/maxwell/result.py +967 -0
- witwin/maxwell/scene.py +1338 -0
- witwin/maxwell/simulation.py +659 -0
- witwin/maxwell/sources.py +917 -0
- witwin/maxwell/visualization/__init__.py +28 -0
- witwin/maxwell/visualization/interactive.py +484 -0
- witwin/maxwell/visualization/plots.py +568 -0
- witwin_maxwell-0.3.0.dist-info/METADATA +184 -0
- witwin_maxwell-0.3.0.dist-info/RECORD +107 -0
- witwin_maxwell-0.3.0.dist-info/WHEEL +4 -0
- witwin_maxwell-0.3.0.dist-info/licenses/LICENSE +13 -0
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import torch
|
|
5
|
+
|
|
6
|
+
from ...sources import (
|
|
7
|
+
SOURCE_TIME_KIND_CW,
|
|
8
|
+
SOURCE_TIME_KIND_GAUSSIAN_PULSE,
|
|
9
|
+
evaluate_source_time,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def build_source_term(
|
|
14
|
+
solver,
|
|
15
|
+
*,
|
|
16
|
+
field_name,
|
|
17
|
+
offsets,
|
|
18
|
+
patch=None,
|
|
19
|
+
phase_real=1.0,
|
|
20
|
+
phase_imag=0.0,
|
|
21
|
+
delay_patch=None,
|
|
22
|
+
activation_delay_patch=None,
|
|
23
|
+
cw_cos_patch=None,
|
|
24
|
+
cw_sin_patch=None,
|
|
25
|
+
source_index=None,
|
|
26
|
+
source_time=None,
|
|
27
|
+
omega=None,
|
|
28
|
+
):
|
|
29
|
+
grid_tensor = patch
|
|
30
|
+
if grid_tensor is None:
|
|
31
|
+
grid_tensor = delay_patch
|
|
32
|
+
if grid_tensor is None:
|
|
33
|
+
grid_tensor = cw_cos_patch
|
|
34
|
+
if grid_tensor is None:
|
|
35
|
+
grid_tensor = cw_sin_patch
|
|
36
|
+
if grid_tensor is None:
|
|
37
|
+
raise ValueError("source term requires at least one tensor payload.")
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
"field_name": field_name,
|
|
41
|
+
"offsets": offsets,
|
|
42
|
+
"patch": None if patch is None else patch.contiguous(),
|
|
43
|
+
"phase_real": float(phase_real),
|
|
44
|
+
"phase_imag": float(phase_imag),
|
|
45
|
+
"delay_patch": None if delay_patch is None else delay_patch.contiguous(),
|
|
46
|
+
"activation_delay_patch": (
|
|
47
|
+
None if activation_delay_patch is None else activation_delay_patch.contiguous()
|
|
48
|
+
),
|
|
49
|
+
"cw_cos_patch": None if cw_cos_patch is None else cw_cos_patch.contiguous(),
|
|
50
|
+
"cw_sin_patch": None if cw_sin_patch is None else cw_sin_patch.contiguous(),
|
|
51
|
+
"source_index": None if source_index is None else int(source_index),
|
|
52
|
+
"source_time": source_time,
|
|
53
|
+
"omega": None if omega is None else float(omega),
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def append_source_term(term_list, solver, **kwargs):
|
|
58
|
+
term_list.append(build_source_term(solver, **kwargs))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _resolve_time_shift_patch(term):
|
|
62
|
+
activation_delay_patch = term.get("activation_delay_patch")
|
|
63
|
+
return activation_delay_patch if activation_delay_patch is not None else term["delay_patch"]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _resolve_cw_signal(omega, source_time, time_value):
|
|
67
|
+
phase = float(omega) * float(time_value) + float(source_time["phase"])
|
|
68
|
+
amplitude = float(source_time["amplitude"])
|
|
69
|
+
return amplitude * np.cos(phase), amplitude * np.sin(phase)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _resolve_term_source(term, default_source_time, default_omega):
|
|
73
|
+
term_source_time = term.get("source_time") or default_source_time
|
|
74
|
+
term_omega = float(default_omega if term.get("omega") is None else term["omega"])
|
|
75
|
+
return term_source_time, term_omega
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _resolve_term_cache_key(term, term_source_time, term_omega):
|
|
79
|
+
source_index = term.get("source_index")
|
|
80
|
+
if source_index is not None:
|
|
81
|
+
return int(source_index)
|
|
82
|
+
return (id(term_source_time), float(term_omega))
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _launch_uniform_patch(solver, *, field_name, source_patch, offsets, signal):
|
|
86
|
+
offset_i, offset_j, offset_k = offsets
|
|
87
|
+
solver.fdtd_module.addSourcePatch3D(
|
|
88
|
+
field=getattr(solver, field_name),
|
|
89
|
+
sourcePatch=source_patch,
|
|
90
|
+
offsetI=int(offset_i),
|
|
91
|
+
offsetJ=int(offset_j),
|
|
92
|
+
offsetK=int(offset_k),
|
|
93
|
+
signal=float(signal),
|
|
94
|
+
).launchRaw()
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
_COMPONENT_TANGENTIAL_AXES = {
|
|
98
|
+
"x": ("y", "z"),
|
|
99
|
+
"y": ("x", "z"),
|
|
100
|
+
"z": ("x", "y"),
|
|
101
|
+
}
|
|
102
|
+
_COMPONENT_AXIS_CODE = {"x": 0, "y": 1, "z": 2}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _component_name(field_name):
|
|
106
|
+
return field_name[-1].lower()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _wrap_axes_for_kind(solver, field_name, kind):
|
|
110
|
+
tangential_axes = _COMPONENT_TANGENTIAL_AXES[_component_name(field_name)]
|
|
111
|
+
return tuple(
|
|
112
|
+
1 if solver.scene.boundary.axis_kind(axis) == kind else 0
|
|
113
|
+
for axis in tangential_axes
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _periodic_wrap_axes(solver, field_name):
|
|
118
|
+
return _wrap_axes_for_kind(solver, field_name, "periodic")
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _complex_wrap_axes(solver, field_name):
|
|
122
|
+
tangential_axes = _COMPONENT_TANGENTIAL_AXES[_component_name(field_name)]
|
|
123
|
+
return tuple(
|
|
124
|
+
1 if solver.scene.boundary.axis_kind(axis) in {"bloch", "periodic"} else 0
|
|
125
|
+
for axis in tangential_axes
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _launch_cw_patch(solver, *, field_name, term, signal_cos, signal_sin):
|
|
130
|
+
offset_i, offset_j, offset_k = term["offsets"]
|
|
131
|
+
solver.fdtd_module.addCwPhasedSourcePatch3D(
|
|
132
|
+
field=getattr(solver, field_name),
|
|
133
|
+
sourcePatchCos=term["cw_cos_patch"],
|
|
134
|
+
sourcePatchSin=term["cw_sin_patch"],
|
|
135
|
+
offsetI=int(offset_i),
|
|
136
|
+
offsetJ=int(offset_j),
|
|
137
|
+
offsetK=int(offset_k),
|
|
138
|
+
signalCos=float(signal_cos),
|
|
139
|
+
signalSin=float(signal_sin),
|
|
140
|
+
).launchRaw()
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _field_triplet_kwargs(solver, field_name):
|
|
144
|
+
if field_name[0] == "H":
|
|
145
|
+
return {
|
|
146
|
+
"ExReal": solver.Hx,
|
|
147
|
+
"ExImag": solver.Hx_imag,
|
|
148
|
+
"EyReal": solver.Hy,
|
|
149
|
+
"EyImag": solver.Hy_imag,
|
|
150
|
+
"EzReal": solver.Hz,
|
|
151
|
+
"EzImag": solver.Hz_imag,
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
"ExReal": solver.Ex,
|
|
155
|
+
"ExImag": solver.Ex_imag,
|
|
156
|
+
"EyReal": solver.Ey,
|
|
157
|
+
"EyImag": solver.Ey_imag,
|
|
158
|
+
"EzReal": solver.Ez,
|
|
159
|
+
"EzImag": solver.Ez_imag,
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _bloch_phase_kwargs(solver, field_name):
|
|
164
|
+
component = _component_name(field_name)
|
|
165
|
+
if component == "x":
|
|
166
|
+
return {
|
|
167
|
+
"phaseCosA": _phase_cos_for_axis(solver, "y"),
|
|
168
|
+
"phaseSinA": _phase_sin_for_axis(solver, "y"),
|
|
169
|
+
"phaseCosB": _phase_cos_for_axis(solver, "z"),
|
|
170
|
+
"phaseSinB": _phase_sin_for_axis(solver, "z"),
|
|
171
|
+
}
|
|
172
|
+
if component == "y":
|
|
173
|
+
return {
|
|
174
|
+
"phaseCosA": _phase_cos_for_axis(solver, "x"),
|
|
175
|
+
"phaseSinA": _phase_sin_for_axis(solver, "x"),
|
|
176
|
+
"phaseCosB": _phase_cos_for_axis(solver, "z"),
|
|
177
|
+
"phaseSinB": _phase_sin_for_axis(solver, "z"),
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
"phaseCosA": _phase_cos_for_axis(solver, "x"),
|
|
181
|
+
"phaseSinA": _phase_sin_for_axis(solver, "x"),
|
|
182
|
+
"phaseCosB": _phase_cos_for_axis(solver, "y"),
|
|
183
|
+
"phaseSinB": _phase_sin_for_axis(solver, "y"),
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _phase_cos_for_axis(solver, axis):
|
|
188
|
+
axis_index = {"x": 0, "y": 1, "z": 2}[axis]
|
|
189
|
+
if solver.scene.boundary.axis_kind(axis) == "periodic":
|
|
190
|
+
return 1.0
|
|
191
|
+
return solver.boundary_phase_cos[axis_index]
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _phase_sin_for_axis(solver, axis):
|
|
195
|
+
axis_index = {"x": 0, "y": 1, "z": 2}[axis]
|
|
196
|
+
if solver.scene.boundary.axis_kind(axis) == "periodic":
|
|
197
|
+
return 0.0
|
|
198
|
+
return solver.boundary_phase_sin[axis_index]
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _bloch_patch_common_kwargs(solver, field_name, term):
|
|
202
|
+
wrap_axis_a, wrap_axis_b = _complex_wrap_axes(solver, field_name)
|
|
203
|
+
offset_i, offset_j, offset_k = term["offsets"]
|
|
204
|
+
kernel_kwargs = {
|
|
205
|
+
"offsetI": int(offset_i),
|
|
206
|
+
"offsetJ": int(offset_j),
|
|
207
|
+
"offsetK": int(offset_k),
|
|
208
|
+
"axisCode": _COMPONENT_AXIS_CODE[_component_name(field_name)],
|
|
209
|
+
"wrapAxisA": int(wrap_axis_a),
|
|
210
|
+
"wrapAxisB": int(wrap_axis_b),
|
|
211
|
+
}
|
|
212
|
+
kernel_kwargs.update(_field_triplet_kwargs(solver, field_name))
|
|
213
|
+
kernel_kwargs.update(_bloch_phase_kwargs(solver, field_name))
|
|
214
|
+
return kernel_kwargs
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _launch_time_shifted_patch(solver, *, field_name, term, source_time, time_value):
|
|
218
|
+
offset_i, offset_j, offset_k = term["offsets"]
|
|
219
|
+
solver.fdtd_module.addTimeShiftedSourcePatch3D(
|
|
220
|
+
field=getattr(solver, field_name),
|
|
221
|
+
sourcePatch=term["patch"],
|
|
222
|
+
delayPatch=term["delay_patch"],
|
|
223
|
+
activationDelayPatch=_resolve_time_shift_patch(term),
|
|
224
|
+
offsetI=int(offset_i),
|
|
225
|
+
offsetJ=int(offset_j),
|
|
226
|
+
offsetK=int(offset_k),
|
|
227
|
+
timeKind=int(source_time["kind_code"]),
|
|
228
|
+
time=float(time_value),
|
|
229
|
+
frequency=float(source_time["frequency"]),
|
|
230
|
+
fwidth=float(source_time["fwidth"]),
|
|
231
|
+
amplitude=float(source_time["amplitude"]),
|
|
232
|
+
phase=float(source_time["phase"]),
|
|
233
|
+
delay=float(source_time["delay"]),
|
|
234
|
+
causalGate=1 if term.get("activation_delay_patch") is not None else 0,
|
|
235
|
+
).launchRaw()
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def _evaluate_time_shifted_signal_tensor(source_time, sample_time):
|
|
239
|
+
# Per-cell source-time waveform for a time-shifted patch, matching the
|
|
240
|
+
# ``evaluate_source_time`` device kernel branch-for-branch (kind 0 = CW,
|
|
241
|
+
# kind 1 = Gaussian pulse, anything else = Ricker) so a Bloch pulse injected
|
|
242
|
+
# through this Torch path is numerically identical to the non-Bloch kernel.
|
|
243
|
+
kind_code = int(source_time["kind_code"])
|
|
244
|
+
amplitude = float(source_time["amplitude"])
|
|
245
|
+
frequency = float(source_time["frequency"])
|
|
246
|
+
phase = float(source_time.get("phase", 0.0))
|
|
247
|
+
delay = float(source_time.get("delay", 0.0))
|
|
248
|
+
two_pi = 2.0 * np.pi
|
|
249
|
+
if kind_code == SOURCE_TIME_KIND_CW:
|
|
250
|
+
return amplitude * torch.cos(two_pi * frequency * sample_time + phase)
|
|
251
|
+
if kind_code == SOURCE_TIME_KIND_GAUSSIAN_PULSE:
|
|
252
|
+
inv_sigma = max(two_pi * float(source_time["fwidth"]), 1.0e-30)
|
|
253
|
+
tau = sample_time - delay
|
|
254
|
+
envelope = torch.exp(-0.5 * (tau * inv_sigma) ** 2)
|
|
255
|
+
return amplitude * envelope * torch.cos(two_pi * frequency * tau + phase)
|
|
256
|
+
tau = sample_time - delay
|
|
257
|
+
alpha = np.pi * frequency * tau
|
|
258
|
+
alpha_sq = alpha * alpha
|
|
259
|
+
return amplitude * (1.0 - 2.0 * alpha_sq) * torch.exp(-alpha_sq)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def _time_shifted_signal_patch(term, source_time, time_value):
|
|
263
|
+
# Collapse the delayed pulse to a real per-cell amplitude patch on-device.
|
|
264
|
+
# The scatter into the split real/imag Bloch field (interior injection plus
|
|
265
|
+
# the phase-rotated wrap copies) is still the native ``addSourcePatchBloch3D``
|
|
266
|
+
# kernel; only the envelope evaluation is Torch, exactly as the CW-phased
|
|
267
|
+
# Bloch path already precomputes its cos/sin combination before scattering.
|
|
268
|
+
sample_time = float(time_value) - term["delay_patch"]
|
|
269
|
+
signal_patch = _evaluate_time_shifted_signal_tensor(source_time, sample_time) * term["patch"]
|
|
270
|
+
activation_delay_patch = term.get("activation_delay_patch")
|
|
271
|
+
if activation_delay_patch is not None:
|
|
272
|
+
signal_patch = torch.where(
|
|
273
|
+
activation_delay_patch > float(time_value),
|
|
274
|
+
torch.zeros_like(signal_patch),
|
|
275
|
+
signal_patch,
|
|
276
|
+
)
|
|
277
|
+
return signal_patch.contiguous()
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def _launch_bloch_time_shifted_patch(solver, *, field_name, term, source_time, time_value):
|
|
281
|
+
signal_patch = _time_shifted_signal_patch(term, source_time, time_value)
|
|
282
|
+
kernel_kwargs = _bloch_patch_common_kwargs(solver, field_name, term)
|
|
283
|
+
kernel_kwargs.update(
|
|
284
|
+
{
|
|
285
|
+
"sourcePatch": signal_patch,
|
|
286
|
+
"signalReal": 1.0,
|
|
287
|
+
"signalImag": 0.0,
|
|
288
|
+
}
|
|
289
|
+
)
|
|
290
|
+
solver.fdtd_module.addSourcePatchBloch3D(**kernel_kwargs).launchRaw()
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def apply_generic_source_terms(solver, terms, *, source_time, omega, time_value, clamp_pec=True):
|
|
294
|
+
if not terms:
|
|
295
|
+
return
|
|
296
|
+
|
|
297
|
+
signal_cache = {}
|
|
298
|
+
cw_cache = {}
|
|
299
|
+
for term in terms:
|
|
300
|
+
field_name = term["field_name"]
|
|
301
|
+
term_source_time, term_omega = _resolve_term_source(term, source_time, omega)
|
|
302
|
+
cache_key = _resolve_term_cache_key(term, term_source_time, term_omega)
|
|
303
|
+
if term["cw_cos_patch"] is not None:
|
|
304
|
+
if cache_key not in cw_cache:
|
|
305
|
+
cw_cache[cache_key] = _resolve_cw_signal(term_omega, term_source_time, time_value)
|
|
306
|
+
signal_cos, signal_sin = cw_cache[cache_key]
|
|
307
|
+
if solver.scene.boundary.uses_kind("bloch"):
|
|
308
|
+
_launch_bloch_cw_patch(
|
|
309
|
+
solver,
|
|
310
|
+
field_name=field_name,
|
|
311
|
+
term=term,
|
|
312
|
+
signal_cos=signal_cos,
|
|
313
|
+
signal_sin=signal_sin,
|
|
314
|
+
)
|
|
315
|
+
continue
|
|
316
|
+
_launch_cw_patch(
|
|
317
|
+
solver,
|
|
318
|
+
field_name=field_name,
|
|
319
|
+
term=term,
|
|
320
|
+
signal_cos=signal_cos,
|
|
321
|
+
signal_sin=signal_sin,
|
|
322
|
+
)
|
|
323
|
+
continue
|
|
324
|
+
if term["delay_patch"] is not None:
|
|
325
|
+
if solver.scene.boundary.uses_kind("bloch"):
|
|
326
|
+
_launch_bloch_time_shifted_patch(
|
|
327
|
+
solver,
|
|
328
|
+
field_name=field_name,
|
|
329
|
+
term=term,
|
|
330
|
+
source_time=term_source_time,
|
|
331
|
+
time_value=time_value,
|
|
332
|
+
)
|
|
333
|
+
continue
|
|
334
|
+
_launch_time_shifted_patch(
|
|
335
|
+
solver,
|
|
336
|
+
field_name=field_name,
|
|
337
|
+
term=term,
|
|
338
|
+
source_time=term_source_time,
|
|
339
|
+
time_value=time_value,
|
|
340
|
+
)
|
|
341
|
+
continue
|
|
342
|
+
if cache_key not in signal_cache:
|
|
343
|
+
signal_cache[cache_key] = evaluate_source_time(term_source_time, time_value)
|
|
344
|
+
if solver.scene.boundary.uses_kind("bloch"):
|
|
345
|
+
_launch_bloch_patch(
|
|
346
|
+
solver,
|
|
347
|
+
field_name=field_name,
|
|
348
|
+
term=term,
|
|
349
|
+
signal=signal_cache[cache_key],
|
|
350
|
+
)
|
|
351
|
+
continue
|
|
352
|
+
if solver.scene.boundary.uses_kind("periodic"):
|
|
353
|
+
_launch_periodic_patch(
|
|
354
|
+
solver,
|
|
355
|
+
field_name=field_name,
|
|
356
|
+
term=term,
|
|
357
|
+
signal=signal_cache[cache_key],
|
|
358
|
+
)
|
|
359
|
+
continue
|
|
360
|
+
_launch_uniform_patch(
|
|
361
|
+
solver,
|
|
362
|
+
field_name=field_name,
|
|
363
|
+
source_patch=term["patch"],
|
|
364
|
+
offsets=term["offsets"],
|
|
365
|
+
signal=signal_cache[cache_key],
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
if clamp_pec:
|
|
369
|
+
solver._clamp_pec_boundaries()
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def _launch_periodic_patch(solver, *, field_name, term, signal):
|
|
373
|
+
wrap_axis_a, wrap_axis_b = _periodic_wrap_axes(solver, field_name)
|
|
374
|
+
scaled_signal = float(signal) * float(term["phase_real"])
|
|
375
|
+
if not wrap_axis_a and not wrap_axis_b:
|
|
376
|
+
_launch_uniform_patch(
|
|
377
|
+
solver,
|
|
378
|
+
field_name=field_name,
|
|
379
|
+
source_patch=term["patch"],
|
|
380
|
+
offsets=term["offsets"],
|
|
381
|
+
signal=scaled_signal,
|
|
382
|
+
)
|
|
383
|
+
return
|
|
384
|
+
offset_i, offset_j, offset_k = term["offsets"]
|
|
385
|
+
component = _component_name(field_name)
|
|
386
|
+
kernel_name = f"addSourcePatchE{component}Periodic3D"
|
|
387
|
+
field_arg_name = f"E{component}"
|
|
388
|
+
getattr(solver.fdtd_module, kernel_name)(
|
|
389
|
+
**{
|
|
390
|
+
field_arg_name: getattr(solver, field_name),
|
|
391
|
+
"sourcePatch": term["patch"],
|
|
392
|
+
"offsetI": int(offset_i),
|
|
393
|
+
"offsetJ": int(offset_j),
|
|
394
|
+
"offsetK": int(offset_k),
|
|
395
|
+
"signal": scaled_signal,
|
|
396
|
+
"wrapAxisA": int(wrap_axis_a),
|
|
397
|
+
"wrapAxisB": int(wrap_axis_b),
|
|
398
|
+
}
|
|
399
|
+
).launchRaw()
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def _launch_bloch_patch(solver, *, field_name, term, signal):
|
|
403
|
+
kernel_kwargs = _bloch_patch_common_kwargs(solver, field_name, term)
|
|
404
|
+
kernel_kwargs.update(
|
|
405
|
+
{
|
|
406
|
+
"sourcePatch": term["patch"],
|
|
407
|
+
"signalReal": float(signal) * float(term["phase_real"]),
|
|
408
|
+
"signalImag": float(signal) * float(term["phase_imag"]),
|
|
409
|
+
}
|
|
410
|
+
)
|
|
411
|
+
solver.fdtd_module.addSourcePatchBloch3D(**kernel_kwargs).launchRaw()
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def _launch_bloch_cw_patch(solver, *, field_name, term, signal_cos, signal_sin):
|
|
415
|
+
kernel_kwargs = _bloch_patch_common_kwargs(solver, field_name, term)
|
|
416
|
+
kernel_kwargs.update(
|
|
417
|
+
{
|
|
418
|
+
"sourcePatchCos": term["cw_cos_patch"],
|
|
419
|
+
"sourcePatchSin": term["cw_sin_patch"],
|
|
420
|
+
"signalCos": float(signal_cos),
|
|
421
|
+
"signalSin": float(signal_sin),
|
|
422
|
+
}
|
|
423
|
+
)
|
|
424
|
+
solver.fdtd_module.addCwPhasedSourcePatchBloch3D(**kernel_kwargs).launchRaw()
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def apply_compiled_source_terms(solver, terms, *, source_time, omega, time_value, signal=None):
|
|
428
|
+
if not terms:
|
|
429
|
+
return
|
|
430
|
+
|
|
431
|
+
default_key = _resolve_term_cache_key(
|
|
432
|
+
{"source_index": None},
|
|
433
|
+
source_time,
|
|
434
|
+
float(omega),
|
|
435
|
+
)
|
|
436
|
+
signal_cache = {}
|
|
437
|
+
cw_cache = {}
|
|
438
|
+
for term in terms:
|
|
439
|
+
field_name = term["field_name"]
|
|
440
|
+
term_source_time, term_omega = _resolve_term_source(term, source_time, omega)
|
|
441
|
+
cache_key = _resolve_term_cache_key(term, term_source_time, term_omega)
|
|
442
|
+
if term["cw_cos_patch"] is not None:
|
|
443
|
+
if cache_key not in cw_cache:
|
|
444
|
+
cw_cache[cache_key] = _resolve_cw_signal(term_omega, term_source_time, time_value)
|
|
445
|
+
signal_cos, signal_sin = cw_cache[cache_key]
|
|
446
|
+
if solver.scene.boundary.uses_kind("bloch"):
|
|
447
|
+
_launch_bloch_cw_patch(
|
|
448
|
+
solver,
|
|
449
|
+
field_name=field_name,
|
|
450
|
+
term=term,
|
|
451
|
+
signal_cos=signal_cos,
|
|
452
|
+
signal_sin=signal_sin,
|
|
453
|
+
)
|
|
454
|
+
continue
|
|
455
|
+
_launch_cw_patch(
|
|
456
|
+
solver,
|
|
457
|
+
field_name=field_name,
|
|
458
|
+
term=term,
|
|
459
|
+
signal_cos=signal_cos,
|
|
460
|
+
signal_sin=signal_sin,
|
|
461
|
+
)
|
|
462
|
+
continue
|
|
463
|
+
if term["delay_patch"] is not None:
|
|
464
|
+
if solver.scene.boundary.uses_kind("bloch"):
|
|
465
|
+
_launch_bloch_time_shifted_patch(
|
|
466
|
+
solver,
|
|
467
|
+
field_name=field_name,
|
|
468
|
+
term=term,
|
|
469
|
+
source_time=term_source_time,
|
|
470
|
+
time_value=time_value,
|
|
471
|
+
)
|
|
472
|
+
continue
|
|
473
|
+
_launch_time_shifted_patch(
|
|
474
|
+
solver,
|
|
475
|
+
field_name=field_name,
|
|
476
|
+
term=term,
|
|
477
|
+
source_time=term_source_time,
|
|
478
|
+
time_value=time_value,
|
|
479
|
+
)
|
|
480
|
+
continue
|
|
481
|
+
if cache_key not in signal_cache:
|
|
482
|
+
if signal is not None and cache_key == default_key:
|
|
483
|
+
signal_cache[cache_key] = signal
|
|
484
|
+
else:
|
|
485
|
+
signal_cache[cache_key] = evaluate_source_time(term_source_time, time_value)
|
|
486
|
+
if solver.scene.boundary.uses_kind("bloch"):
|
|
487
|
+
_launch_bloch_patch(
|
|
488
|
+
solver,
|
|
489
|
+
field_name=field_name,
|
|
490
|
+
term=term,
|
|
491
|
+
signal=signal_cache[cache_key],
|
|
492
|
+
)
|
|
493
|
+
continue
|
|
494
|
+
if solver.scene.boundary.uses_kind("periodic"):
|
|
495
|
+
_launch_periodic_patch(
|
|
496
|
+
solver,
|
|
497
|
+
field_name=field_name,
|
|
498
|
+
term=term,
|
|
499
|
+
signal=signal_cache[cache_key],
|
|
500
|
+
)
|
|
501
|
+
continue
|
|
502
|
+
_launch_uniform_patch(
|
|
503
|
+
solver,
|
|
504
|
+
field_name=field_name,
|
|
505
|
+
source_patch=term["patch"],
|
|
506
|
+
offsets=term["offsets"],
|
|
507
|
+
signal=float(signal_cache[cache_key]) * float(term["phase_real"]),
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
solver._clamp_pec_boundaries()
|