This document tracks the current user-visible capabilities of the maxwell package.
- Update this file whenever a new user-visible feature, public API capability, or meaningful workflow is added, removed, or materially changed.
- Keep entries focused on capabilities that a user can discover through the public package, examples, or standard solver workflows.
- Prebuilt CUDA 12.8 wheels for CPython 3.10-3.14 on Linux x86_64 and Windows x86_64.
- Each wheel includes one Python-independent LibTorch Stable ABI FDTD library targeting PyTorch 2.10 or newer; the same binary is CI load-tested with PyTorch 2.10, 2.11, and 2.12 across CPython 3.10-3.14.
- Native CUDA images cover compute capabilities 7.0, 7.5, 8.0, 8.6, 8.9, 9.0, 10.0, 10.1, and 12.0, with compute 12.0 PTX retained for forward-compatible Blackwell execution.
- Linux release wheels are repaired and validated as
manylinux_2_35_x86_64artifacts before publication.
- Declarative simulation workflow:
Scene -> Simulation -> Result - PyTorch-native scene-module workflow:
SceneModule -> Simulation -> Result, with automaticto_scene()normalization insideSimulation - Top-level package exports for Maxwell-specific public objects under
maxwell, including the full-featured publicMaterial, the experimental RF/array contractsAxisPath,LumpedPort,TerminalRef,TerminalPort,WaveModeSpec,WavePort,Resistor,Capacitor,Inductor,SeriesRLC,ParallelRLC,PortExcitation,PortData,NetworkData,AntennaData,Ludwig3,BeamWeights,EmbeddedElementPatternData,ArrayBasisData,BeamData,BeamCodebook,MaxHoldComposite,MultipathEnvironment,MIMOData,PowerLossMonitor, andPowerLossData, the experimental linear-circuit graph contractsCircuit,CircuitNode,PortBinding,MNAConfig, independent/controlled sources, mutual inductors, scheduled ideal switches, transient source waveforms, and detachedFDTDResumeCheckpoint, and the experimental subgrid-wire contractsThinWire,WireConductor,WireEnd,WireMonitor, andWireData - Public simulation entrypoints:
Simulation.fdfd(...),Simulation.fdtd(...), andrun(...) - Typed simulation configuration records and enums exposed as
FDFDConfig,FDTDConfig,TimeConfig,SpectralSampler,SimulationMethod,SpectralWindowKind, andAbsorberKind - Public frequency vocabulary uses
frequency=for scalar selection andfrequencies=for one-or-many target frequencies;freqsis not exposed in public Maxwell APIs - Public result container with structured field/material access (
result.E.x,result.materials.eps.scalar), frequency-scoped selection throughResult.at(...), monitor access, stats, plotting, and save support; FDTD lumped-port runs expose live torch-nativePortDatathroughresult.port(name)/result.ports, circuit-coupled runs expose live device-residentCircuitDatathroughresult.circuit(name)/result.circuits, closed Huygens surfaces and driven ports feed typed antenna metrics throughresult.antenna(...), declared loss volumes resolve throughresult.power_loss(...)/result.monitor(name), and thin-wire monitors return device-resident, frequency-firstWireData, while detached Result v2 snapshots embed versioned CPU port, circuit, and wire-monitor payloads
Domainwith explicit 3D bounds andfrom_domain_range(...)GridSpec.uniform(dl)for isotropic grids, withdltreated as a maximum requested step: each axis usesceil(span / dl)physical cells and redistributes them uniformly asspan / count(the same rule as Tidy3D; for example, 1.28 m atdl=0.025 mbecomes 52 cells at approximately 0.024615 m); the resolved node array is endpoint-inclusive (count + 1Yee cell boundaries spanning theDomainexactly, identical to Tidy3D's exported grid boundaries and to theGridSpec.customconvention; on periodic/Bloch axes the last node is the wrap image of the first)GridSpec.anisotropic(...)for per-axis constantdx,dy,dzGridSpec.custom(x_coords, y_coords, z_coords)for nonuniform (graded) Yee grids from explicit node coordinates: any 1D float array-likes (list / NumPy / torch), validated per axis (strictly increasing, finite, at least two nodes) and stored as read-only float64 masters; a uniform axis is expressed by uniformly spaced coords, and theDomainbounds must equal the coordinate extentsGridSpec.auto(min_steps_per_wavelength=..., wavelength=None, max_ratio=..., override_structures=..., layer_refinement=...)for adaptive (AutoGrid) meshing: at prepare time every structure andMaterialRegionAABB face snaps to a cell boundary, each face-bounded interval is stepped atwavelength / (n_max * min_steps_per_wavelength)using the maximum overlapping refractive index at the meshing wavelength (material regions contributesqrt(max(eps_bounds) * max(mu_bounds))), intervals are filled with uniform or geometrically graded cells with a global adjacent-cell ratio bound ofmax_ratio, and the result materializes through the same nonuniform-grid path asGridSpec.custom(single downstream representation); the meshing wavelength defaults to the highest source characteristic frequency in the scene (broadband pulses mesh for their upper spectral content, matching FDTDauto_dt), or an explicitwavelength=; under PML faces the outermost absorber cells are uniformized piecewise between structure faces, preserving face snapping and in-band refinement targetsMeshOverrideStructure(geometry, dl)mesh overrides forGridSpec.auto: a scalar or per-axis(dx, dy, dz)maximum step enforced inside the geometry's axis-aligned bounding boxLayerRefinementSpec(min_cells=..., axes=None)forGridSpec.auto: any structure-bounded interval thinner thanmin_cellslocal target steps receives at leastmin_cellscells along the covered axesGridSpec.is_custom,GridSpec.is_auto,GridSpec.min_spacing, andGridSpec.axis_coords(...)for nonuniform-grid introspectionGridSpec.spacingreturns(dx, dy, dz)for uniform specs, withGridSpec.is_uniformfor uniform-grid checks; both raise onGridSpec.customandGridSpec.auto, as do the scalarScene.dx/dy/dz/Scene.grid_spacingproperties (usescene.x/y/z, the half-grid coordinates, orgrid.min_spacinginstead)BoundarySpec.none(),BoundarySpec.pml(...),BoundarySpec.periodic(),BoundarySpec.bloch(...),BoundarySpec.pec(), andBoundarySpec.pmc();Domain.boundsalways describes the physical domain, and PML cells are appended outside those bounds during scene preparation with the physical edge-cell spacing (matching Tidy3D)- Per-face boundary configuration through
BoundarySpec.faces(...)and directBoundarySpec(kind=..., x=..., x_low=..., ...)overrides, including global defaults plus per-axis or per-face specialization BoundarySpec.bloch_wavevector="auto"marker for solver-resolved Bloch phase workflows during FDTD preparation- Public
BoundaryKindliteral type for boundary-mode selection across declarative scene APIs - Shared
witwin.core.Structurerecords pairing geometry and material, withpriority,enabled, andtags Geometry.with_material(...)convenience path returns a sharedStructureMaterialRegionfor density-based PyTorch-native material overlays: Box occupancy participates in the same per-subsample arithmetic or polarized interface averaging as an equivalentStructure, each Yee axis blends from its own current background toward the density-defined scalareps/mu, density textures are sampled differentiably on-device, and the compiled model retains base/design tensors plus the nominal design mask- Scene assembly with
Scene.add_structure(...),Scene.add_source(...),Scene.add_monitor(...), andScene.add_material_region(...) - Experimental linear-circuit declarations through
Scene.add_circuit(...)andScene.compile_circuits(): aCircuitowns deterministic node/device order with a unique ground node, reuses the publicResistor/Capacitor/Inductortypes with circuit-node terminals, binds only existing lumped/terminal EM ports, and performs explicit floating-network, source-loop, branch-dependency, and dense-size diagnostics without a CPU solver fallback - Restricted linear netlist import through
Circuit.from_spice(...)/parse_spice(...), covering R/L/C/K, independent and E/G/F/H controlled sources, safe parameter arithmetic, sandboxed includes, flattened subcircuits,.ic, and PULSE/SIN/PWL waveforms; unknown devices/directives and executable expression syntax are hard errors, andCircuit.to_spice()provides deterministic canonical serialization - Experimental nonlinear circuit devices (Phase 0, standalone): device-terminal nonlinear elements
Diode(controlled Shockley junction with ideality, series resistance, and junction capacitance parameters),PiecewiseLinearIV,PolynomialIV, andVoltageDependentCapacitor(declared as a single-valued chargeQ(V)), each admitted throughCircuit.add(...)under the shared circuit-device contract.compile_nonlinear_devices(...)groups same-signature devices into fixed-shapeCompiledNonlinearDevicebatches exposing analytic conduction current/conductance and stored charge/capacitance, and thenewton_solve(...)GPU Newton core solves theNonlinearMNASystemresidual/Jacobian with a dual convergence gate (an iterate is accepted only when both the scaled KCL residual and the Newton update meet their own tolerances, so residual-only false convergence is impossible), stableexpm1/pnjlimjunction limiting that keeps hard forward drive finite, and a backtracking line search; a non-convergent or rootless system fails closed deterministically withinNonlinearSolveConfig.max_iterations(raise by default,record_and_stopfor diagnostics) with node/residual/iteration-trajectory localization, never returning an unconverged or non-finite iterate. Diode/behavioral I-V devices register as DC-connecting for floating-network diagnostics while the charge-only capacitor stays DC-open. Transistor surfacesBJT/MOSFETare reserved and fail closed until the independent Phase 5 go/no-go gate. FDTD coupling, adjoints, and benchmarks are later slices. - Experimental standalone nonlinear circuit transient (Phase 1, no FDTD coupling):
run_nonlinear_transient(...)runs Newton in the transient loop over a fixed or non-uniform time grid, folding each device's stored-charge companion into the residual/Jacobian with the same trapezoidal (2/dt) / backward-Euler (1/dt) bilinear-transform convention as the linear reactive elements (_charge_companion/advance_charge_state, so the diodejunction_capacitanceandVoltageDependentCapacitorQ(V)are consumed and the charge history advances only on an accepted step). The transient runs a backward-Euler first step (trapezoidal startup) so a prescribed non-DCinitial_statedoes not carry thei_cap^0 = 0first-step artifact.solve_dc_operating_point(...)finds the operating point with a geometric gmin continuation ladder ending exactly atgmin=0(no residual artificial conductance), treating capacitors as open at DC. A step in which aPiecewiseLinearIVdevice crosses a knot is re-solved with a local backward-Euler companion so the trapezoidal reactive companion does not ring on the conduction-slope kink, and that step's KCL residual and charge-history advance are taken under the same backward-Euler companion it was solved with. Underfailure='record_and_stop'a non-convergent step (or DC start) truncates the trajectory rather than committing the unconverged iterate:converged=Falsewithstopped_steprecording the failing index.multistart_dc(...)sweeps seeds and deduplicates the converged DC operating points into aMultistartReportthat distinguishes a unique operating point from multistability on an N-shaped negative-differential-resistance device (a benign non-convergence maps to-1; a non-finite device evaluation propagates as a device-model error rather than being masked). Verified CPU/fp64: diode DC I-V vs analytic Shockley< 1e-5across a bias sweep; half-wave and center-tapped full-wave rectifier steady-ripple output vs a frozen oversampled backward-Euler reference< 1%normalized RMS; every converged transient step meets the KCL residual tolerance; a linear-Q(V)capacitor discharge reproduces the analytic RC decay< 1e-4. - Experimental standalone CUDA linear-MNA runtime through
compile_mna_system(...): double- or single-precision DC operating points and trapezoidal/backward-Euler transients cover shared R/L/C devices, independent and E/G/F/H controlled sources, mutual inductance, scheduled switches, constrained or zero DAE initialization, local backward-Euler damping at startup/breakpoints, reusable piecewise factorizations, trainable CUDA parameters, and torch-nativeCircuitDatanode voltages, physical branch currents, device powers, energy residuals, and solver diagnostics - Experimental strong FDTD/circuit co-simulation for one circuit bound to one or more lumped/terminal ports through the ordinary
Scene -> Simulation.fdtd(...) -> Resultflow:compile_coupled_mna_system(...)condenses every exact Yee edge Norton operator into one GPU MNA/DAE solve at the same midpoint, supports controlled sources across bound ports and PULSE/SIN/PWL schedules, reuses the existing compiled terminal geometry and polarity, scatters solved currents before tail corrections, reuses piecewise LU factors and preallocated solve buffers, vectorizes fixed R/C networks, and publishes consistent port andCircuitDatasamples. Fixed built-in source/switch schedules support factor-class CUDA Graph replay, andcompile_batched_mna_factors(...)provides an internal fixed-shape[batch, unknown, unknown]cached GPU solve primitive.CircuitData.save/loaduses detached safe tensor-only snapshots; Result v2 persistence is a detached CPU snapshot that must be loaded only from trusted sources.PreparedSimulation.run_until(...)plus a fresh single-GPUprepare().run(resume_from=...)restores physical fields/CPML, port accumulators, circuit histories and result prefixes under a strict execution fingerprint. Spatial multi-GPU forward runs assign each wholly shard-owned bound port to one owner and the circuit graph to the owner of the minimum global reference edge, exchange one voltage/current scalar pair per remote port per step, keep same-shard ports on a zero-P2P fast path, and move finalPortData/CircuitDataonce toresult_device; full distributed field-shard checkpoint replay and distributed gradients remain deferred. Direct excitation orPortSweepof a bound port, a nonzero DC port current inconsistent with the zero initial Yee field, multi-circuit execution, resumable full-field/observer runs, and one port spanning an x split fail explicitly. - Experimental single-device FDTD thin-wire workflow keeps immutable, physical-radius
ThinWirecenterlines separate from volumeStructuregeometry throughScene.add_thin_wire(...)/ read-onlyScene.thin_wires; PEC straight, bent, branched, named-junction, and closed-loop paths compile into one cached energy-paired current/charge graph and run through native CUDA sampling, recurrence, and sorted-transpose deposition without thin-cylinder voxelization. Physical polyline spans own circuit state while cell-localWireFragmentrecords own conservative arbitrary-direction coupling, including uniform/custom/auto grids and legal interior paths under real-periodic boundaries; face-touching wraps and Bloch paths fail closed. Open and named-PEC-grounded endpoints are supported. StandardLumpedPortandTerminalPortdeclarations bind to wire nodes or a removed feed gap throughWireNodeRef/WirePortBinding, retain Yee-staggered voltage/current phasors, and share the ordinaryResult.port(...)and RF adjoint path.WireMonitorreturns frequency-first device-resident current/charge/loss selections as differentiableWireData; wireI/qparticipate in schema-v2 checkpoint/replay, and native standard/CPML reverse paths provide exact recurrence/deposition transposes plus fixed-stencil coordinate, physical-radius, local isotropic host-material, gap-weight, and source-amplitude gradients. The solver reports compressed state and a conservative joint Maxwell/wire CFL adjustment; differentiable runs require a fixed step below that joint bound. Spatial multi-GPU forward runs assign each cell-local sampling/deposition Yee edge to its owning shard and the whole compressedI/qrecurrence plus everyWireMonitorto the single owner of the minimum global sampling edge (declaration-order independent), reduce per-segment EMF partials to that owner with a deterministic rank-ordered sum, advanceI/qowner-only, broadcast the advanced current back so every owned edge deposits exactly once, and move finalWireDataonce toresult_device: a physical segment may span any number of shards, its fragments split at each partition boundary, and the shared joint Maxwell/wire dt is taken from the full-domain single-GPU wire runtime so the step matches bitwise. A wire crossing the x split reproduces the single-GPU forward bitwise at a short horizon (deterministic reductions and per-edge deposition are bit-exact) while over long runs individual field phases drift by chaotic float32 halo-curl accumulation as in any distributed FDTD, yet the discrete lossless-loop energy invariant across the split stays tight. A trainable wire under multi-GPU (the Phase 7 adjoint bridge does not checkpoint or replay wire state), a distributed CPML or Mur absorbing boundary, and a wire mixed with an embedded network or lumped circuit fail closed at prepare. Finite-conductor wires now have a first-class material lawWireConductor.finite(conductivity, permeability=...)and a per-unit-length series-impedance model (witwin.maxwell.compiler.wire_impedance): the exact analytic solid-round-wire skin-effect impedanceZ'(omega) = m/(2 pi a sigma) . I0(ma)/I1(ma)with the exact DC resistance1/(pi a^2 sigma), and a passive rational auxiliary-differential-equation fit of the excess impedance that reuses the shared network rational-fitting stack (fit_rational,RationalModel.to_state_space, bilineardiscretize, and the pole-aware positive-real passivity certificate) rather than duplicating any pole fitting. The analytic AC resistance meets the 2% analytic gate against an independent evaluation and the ADE realizes the broadband impedance with a numerically certified non-growing discretization (not positive-real by construction: the improper skin-effect impedance carries an out-of-band direct term, so dissipativity is asserted per segment by the checked in-band positive-resistance condition plus a combined current/ADE spectral-radius< 1certificate, not guaranteed by construction). A finite-conductor wire now runs: the passive series impedance folds into the wire current update as an energy-consistent trapezoidal companion (L dI/dt + R_dc I + ADE-excess = emf + potential drop) whose PEC limit is the byte-identical lossless leapfrog, and the build adaptively selects the highest fit order whose combined current/ADE companion is certified non-growing (physical in-band positivity plus spectral radius< 1) or fails closed. Theohmic_lossmonitor emits real per-frequency dissipation0.5 Re(Z'(f)) length |I(f)|^2(PEC segments report exactly zero), consistent with the recurrence's own dissipation. A finite conductor's field-coupled reverse/adjoint replay and checkpoint/resume fail closed (the ADE loss-state transpose and checkpoint schema, the field-coupled conductivity current sensitivitydI/dsigma, and distributed wire reverse/gradient communication remain deferred). The deterministic conductivity adjoint of the dissipation channel ships separately (see the next entry). - Finite-conductor thin-wire conductivity adjoint (dissipation channel): the exact closed-form conductivity sensitivity of the analytic scaled-Bessel internal impedance,
d Z'(omega)/d sigma = (i omega mu)/(4 pi sigma) . (1 - (I0(ma)/I1(ma))^2)(witwin.maxwell.compiler.wire_impedance.internal_impedance_conductivity_gradient), whose DC limit is exactlyd R_dc/d sigma = -1/(pi a^2 sigma^2). A PyTorch-native autograd path (witwin.maxwell.fdtd.wire_lossy.analytic_ac_resistance) makes a scalar conductivity leaf differentiate the per-unit-length AC resistanceRe(Z'(f; sigma)), so an ohmic-dissipation objective0.5 . Re(Z'(f; sigma)) . length . |I(f)|^2has an exact conductivity gradient (validated against a float64 central difference of the analytic model, no rational fit involved), andLossySegmentModel.conductivity_ac_resistance_gradient(frequencies)returns the per-segmentd Re(Z')/d sigmafor the built model. This deterministic channel deliberately avoids the field-coupled current sensitivity, which stays fail closed because the recurrence's ADE coefficients come from a nondeterministic shared rational fit. A distributed (multi-GPU) forward carrying a finite-conductor wire fails closed rather than silently running it as a lossless PEC wire, because the distributed owner runtime builds only the lossless current update. - Scene-level port assembly with type and unique-name validation through
Scene.add_port(...);ModePortcontinues to materialize throughScene.resolved_sources()/Scene.resolved_monitors(), while axis-alignedLumpedPortandTerminalPortdeclarations compile throughScene.compile_ports()into the same sparse Yee E-line/H-loop geometry without adding a second solver entrypoint. The firstTerminalPortcontract resolves two uniquely named, unrotated PECBoxstructures across facing surfaces, uses the center of their transverse footprint overlap for voltage integration, and constructs its current contour from that overlap at the explicit reference plane - Phase-0 RF conventions use explicit negative-to-positive voltage paths, right-hand-rule current loops, peak phasors with
exp(-i*omega*t), average power0.5*Re(V*conj(I)), and complex-reference Kurokawa power waves.PortDatakeeps an explicit frequency axis and reports V/I, a/b, impedance, incident/reflected/accepted power, return loss, and VSWR;NetworkDatafixes[frequency, output_port, input_port]ordering, complexz0[frequency, port], valid excitation columns, differentiable S/Z/Y conversion, and reference-impedance renormalization throughtorch.linalg.solve - Experimental single-device N-port workflow:
PortSweepexecutes one deterministic FDTD run per selected RF input, internally matches inactive ports, preserves the declared port order, rejects incomplete or inconsistent columns, and returns the complete complex matrix throughResult.network.NetworkDataadditionally supports differentiable reference-plane shifts and power-preserving single-ended/mixed-mode conversion.NetworkData.from_touchstone(...)strictly reads common Touchstone 1.x/2.0 RI/MA/DB S/Z/Y data with Hz/kHz/MHz/GHz units, explicit 2-port ordering, Full/Lower/Upper matrices, per-port reference impedances, source comments and line-numbered diagnostics; complete N-port networks write detached S/Z/Y data in the same formats - Experimental network rational-model workflow:
NetworkData.validate_physicality(...)reports sampled Kurokawa passivity and an explicitly finite-band causality heuristic, whileNetworkData.fit_rational(...)lowers complete S/Y/Z sweeps to a shared-pole realRationalModel. Public fit/state-space records provide auditable RMS/max error, unstable-pole and passivity diagnostics, pole-aware in-band interval verification, realA/B/C/Drealization withNports * orderstates, bilinear discretization with a strict|z| < 1-1e-7gate, persistence, and differentiable pre-fitted residues/direct terms; automatic pole relocation and passivity enforcement reject trainable inputs instead of detaching them - Experimental N-port network embedding:
NetworkBlock/TouchstoneNetworkconnect passive pre-fitted or prepare-time-fitted networks to ordered namedLumpedPort/ resolvedTerminalPortterminals through the existingScene -> Simulation -> Resultpath. The fixed-shape GPU runtime applies a prepare-time pivoted-LU factorization for the coupled same-step direct-feedthrough solve, advances all state on device, supports independent CUDA Graph replay, and returns ordered tensor-nativeEmbeddedNetworkDatavoltage/current, signed per-port power, network-total absorbed/generated power, state norm, model identity, warnings, and provenance throughresult.embedded_network(name). Optional bounded per-portdelay_seconds=(explicit or automatically extracted from scattering phase) de-embeds a passive rational S core and realizes integer plus fractional bidirectional power-wave delays with fixed GPU ring storage, integrated FDTD full-path< 3 degphase /< 2%response gates, a< 0.02re-embedding gate, and typed fit diagnostics. Pre-fittedRationalModelresidues/direct terms and nearby supported material parameters differentiate through checkpoint/replayed network state and the native CUDA Maxwell adjoint; explicit delay state, trainable poles/proportional terms, direct trainable state-space matrices, andWavePortfail explicitly. Spatial multi-GPU forward runs assign each wholly shard-owned connected terminal to one owner and the network graph to the owner of the minimum global reference edge, advance the sole authoritative state recurrence and same-step LU feedthrough solve on that owner, exchange one voltage/current scalar pair per remote connected port per step, keep same-shard ports on a zero-P2P fast path, and move finalPortData/EmbeddedNetworkDataonce toresult_device; one connected terminal spanning an x split, a scene mixing an embedded network with a bound circuit, more than one embedded network, an explicit per-port network delay on the distributed path, and trainable multi-GPU network embedding fail explicitly, and the distributed field-shard/adjoint boundaries remain deferred. APortExcitation,PortSweep, orport.terminationaimed at a network-connected terminal, and a requested output frequency outside a network's fitted band underextrapolation='reject', fail identically on the single-device and multi-GPU paths rather than being silently dropped. Embedded diagnostics persist through ordinary and shardedResult.save/load - Experimental single-device FDTD lumped-port workflow:
Simulation.fdtd(..., excitations=PortExcitation(...))applies a device-resident implicit-midpoint field/circuit correction and accumulates stagger-aware V/I phasors; passive port terminations support series/parallel RLC combinations, standalone R/C/L elements useScene.add_lumped_element(...)/Scene.compile_lumped_elements(), explicit feed-gap paths may bridge two distinct PEC terminal surfaces while embedded/same-conductor paths are rejected, pulsed port-only runs use a full step-zero spectral window, and all per-step indices, state, source evaluation, and DFT weights stay on the target device. The per-step series-port update runs an allocation-free in-place kernel schedule (no per-step device allocation, host<->device copy, or scalar sync), passive terminations skip the per-step drive-waveform evaluation, and the per-step energy/branch diagnostics are opt-in and off by default while the port voltage/current observers keep full fidelity - Unified port-field coupling convention: every model that couples a lumped device, an MNA circuit, or a fitted Touchstone/state-space network to the Yee grid drives the coupling from the same trapezoidal half-step interface voltage
0.5 * (V_after_previous + V_free)(the field free voltage averaged with the previous step's post-step port voltage). This is the unconditionally stable convention shared across the native lumped runtime, the strongly coupled MNA co-simulation, and the embedded-network solve, so cross-model agreement (an RLC termination expressed as a native lumped device, as an MNA circuit, or as a fitted one-port network) is exact at the shared convention rather than only close; a refinement-stability regression gate pins it by keeping a long fitted-network run finite and bounded where the older free-voltage interface went conditionally unstable PortData.save/loadandNetworkData.save/loaduse an explicit schema version, preserve port ordering/conventions/metadata, and intentionally restore detached inference tensors rather than a live autograd graph- Experimental single-device antenna workflow:
Result.antenna(surface=..., driven_port=...)converts every requested frequency of a first-class closed Huygens monitor into a full-sphere far field and returnsAntennaDatawith fixed[frequency, theta, phi]tensors, radiated/accepted/incident power, directivity, gain, realized gain, radiation/mismatch/system efficiency, EIRP, Ludwig-3 co/cross polarization, axial ratio, applied phase center/frame provenance, and the device-resident equivalent surface currents. Accepted and incident powers are taken directly from the namedPortData; field and port paths remain differentiable - Experimental array analytical contract: power-normalized complex embedded element patterns use fixed
[frequency, port, theta, phi]ordering and combine[port], exact[frequency, port], or batched[beam, frequency, port]incident power-wave weights entirely in PyTorch. The result reportsS @ a, per-port and total incident/reflected/accepted power, masked active reflection and complex-reference active impedance, total complex far field, realized gain, and EIRP without invoking a solver.Scene.compile_array_monitors(...)freezes closed-surface, angular-grid, port-order, and phase-center provenance and rejects independent-source, nonlinear, or time-varying scenes before superposition - Experimental full-wave array basis extraction: an FDTD
PortSweepretains compact closed-surface columns and measured incident power waves without N copies of full solver fields;Result.array_basis(...)produces content-fingerprinted, safely persistent embedded patterns and a Hermitian closed-surface Poynting power operator. Lumped/terminal ports support direct simultaneous LTI excitations for raw-complex broadside/endfire validation, WavePort sweeps support multifrequency modal-channel extraction, andArrayBasisData.combine(...)reports active network quantities, absolute radiated power, efficiency, realized gain, and EIRP with zero solver reruns - Experimental array codebook and scan workflows:
BeamCodebooknames a set of[beam, port]or[beam, frequency, port]incident power-wave vectors over one basis,BeamCodebook.from_scan_angles(...)builds progressive-phase steering weights from explicit element positions and a scan-angle list, andArrayBasisData.combine(codebook)evaluates every beam's active network quantities, far field, realized gain, and EIRP in one batched call. Combining any number of beams (64/256/1024) executes zero additional field-solver time steps (asserted by a solver step-counter fingerprint), andBeamData.max_hold(metric=...)reduces the beam axis to a per-direction envelope with a subgradient-carrying value and a non-differentiable winning-beam index; the content fingerprint (ArrayBasisData.cache_key) is weight-invariant so any beam vector reuses the same basis while geometry/material/port/frequency changes invalidate it - Experimental array MIMO metrics:
MultipathEnvironmentcarries the angular power spectrum, cross-polar ratio, and polarization correlation with explicit integration weights, andArrayBasisData.mimo(environment)integrates the dual-polarized embedded patterns into a Hermitian positive-semidefinite complex correlation matrix, envelope correlation coefficient (ECC), apparent diversity gain, and mean effective gain (Taga/Vaughan convention).ArrayBasisData.ecc_from_scattering()provides the distinct lossless S-parameter ECC approximation (Blanch form) and fails closed on non-passive or fully-reflective columns it cannot approximate (it cannot detect ohmic loss, which is invisible in S alone). All metrics stay in the torch autograd graph - Experimental array gradients: complex incident power-wave weights are fully differentiable through
combine()and the batched codebook/max-hold/MIMO metrics without any solver rerun (validated against high-precisiongradcheck); scene/material/geometry gradients through the basis are delivered byArrayBasisData.scene_gradient_vjp(...)(see the dedicated subsection below) - Shared
PowerLossMonitor/PowerLossDatacontract for explicitly named conduction, electric/magnetic dispersion, nonlinear, circuit, surface, and wire channels. Static bulk electric conduction is computed automatically as peak-phasor0.5*sigma_e*|E|^2on sparse Yee electric edges with nonuniform dual control volumes; explicit physical producers may supply volume W/m3, surface W/m2, line W/m, or integrated W channels. Results retain the explicit frequency axis, global Yee IDs, optional occupancy/material/geometry IDs, integration measures, autograd provenance, and a source-result fingerprint without inventing unavailable loss components - Experimental single-device RF adjoint workflow: active
LumpedPort/TerminalPort, finite positiveSeriesRLCor standalone R/C/L branches, and ordinary-Y embedded rational networks checkpoint and replay their auxiliary state alongside the native CUDA Maxwell reverse. Analytic local circuit/network VJPs cover port voltage/current, accepted/incident/reflected/available power, source amplitude, R/L/C values, rational residues/direct terms, material tensors, and supported smoothMaterialRegionparameters. Fixed single-modeWavePortdirect and sweep outputs differentiate through eligible material/design regions, whileNetworkDataalgebra andAntennaDataderived metrics remain torch-native - Experimental single-GPU FDTD/circuit adjoint: checkpoint replay rebuilds each strongly coupled MNA step with torch-native transpose solves and propagates through direct, SPICE-expression, or
SceneModule-derived R/L/C and independent-source waveform tensors, bound-port material/geometry inputs, port outputs, and every liveCircuitDatanode/branch, device-power, energy-balance, and tensor-diagnostic output. Direct and parsed derived tensors follow eager PyTorch semantics, so repeated finite-difference or optimization evaluations rebuild/reparse theCircuit(normally inSceneModule.to_scene()) instead of retaining a consumed graph or mutating an already materialized descendant. The adjoint requires the coupled DC solution and companion histories to be exactly zero, rejects a tensor seed at the initialCircuitDatasample, trainable DC source values or circuit initial conditions, trainable port reference impedance, and distributed execution, and keeps differentiable circuit runs off the RC/CUDA-Graph forward fast paths so no no-grad cache can hide a semantic dependency Scene.clone(...)for scene-preserving validation, benchmarking, and device-transfer workflowsScene(...)defaults todevice="cuda"and requires an explicitdevice="cpu"override for scene-only CPU workflows- Optional domain symmetry on
Scene(symmetry=(..., ..., ...))with per-axisNone/"PEC"/"PMC"on either domain face, via bare mode strings (low face by default) or explicit(mode, face)pairs such as("PEC", "high"), folding the domain about any axis at either face with matching result-side expansion - Optional subpixel material averaging via
Scene(subpixel_samples=...), acceptingint,(sx, sy, sz), or aSubpixelSpec(samples=..., averaging=..., pec=...)policy object; exact interface samples receive differentiable half occupancy so odd and even sample counts agree without a one-sided material bias, while periodic image unions preserve both endpoint continuity and structures that cross into the opposite-side interior SubpixelSpec(averaging="polarized")enables Kottke normal-projection subpixel averaging (harmonic permittivity/permeability along the interface normal, arithmetic tangentially) forepsandmucomponents, flowing through both FDFD and FDTD from the shared material compiler; multi-sample cells integrate each material component and its reciprocal separately before the normal projection, so nearly binary sub-samples retain the intended harmonic normal response instead of collapsing toward the arithmetic meanSubpixelSpec(pec="conformal")enables stable partial-fill conformal PEC edge treatment for in-domainMaterial.pec()structures in FDTD. The per-edge fill is the geometric coverage fraction of each Yee E edge (the PEC signed distance interpolated between the edge's two endpoint nodes), so it has compact support: exactly0on an edge the conductor surface does not reach and exactly1on an edge wholly inside it. A conductor face parallel to the grid therefore cuts no tangential edge and conformal reproduces thestaircasemask bit for bit; only genuinely cut (curved / oblique) surfaces take a fractional open fraction- Subpixel averaging (
samples > 1, arithmetic and polarized) is generalized to nonuniform grids: the per-sub-sample offsets are per-node fields scaled by the local Yee dual-cell width (d{axis}_dual64), soGridSpec.auto/GridSpec.customcombine with subpixel averaging instead of failing through the scalarScene.dx; every accumulated channel (eps/mu/sigma_e/off-diagonal components, Kerrchi3,chi2, TPA, the modulation quadratures and per-node frequency, and dispersive-pole weights) is offset consistently, and on a uniform grid the offsets reduce bit-exactly to the scalar-spacing path (aGridSpec.custombuilt from a uniform scene's node masters reproduces theGridSpec.uniformsubpixel model channel-for-channel) - The compiled PEC node occupancy (read by the mode solver, the modal ports, the terminal-contact checks and the material summaries) is generalized to nonuniform grids: its smoothing width is a per-node field, half the local Yee dual-cell width (min over axes), instead of the single global
0.5*min(min_spacing)that let a fine feature anywhere shrink the smoothing in a locally coarse region; the occupancy stays differentiable in the wall geometry, and on a uniform grid the width reduces bit-exactly to0.5*spacing(aGridSpec.custombuilt from a uniform scene's node masters reproduces theGridSpec.uniformPEC occupancy exactly). The conformal FDTD edge fill is computed separately from the signed distance directly on each Yee edge, so it is grid-spacing-exact onGridSpec.auto/GridSpec.customwithout any smoothing width - Solver-side compiled scene inspection via
Simulation.prepare(), whereprepared.solver.scenematerializes Yee-grid dimensions, lazy meshgrid allocation, material compilation, and orthogonal material cross sections without storing that state on the publicScene
- Analytic geometry primitives:
Box,Sphere,Cylinder,Ellipsoid,Cone,Pyramid,Prism,Torus,HollowBox - Extruded polygon geometry via
PolySlab(vertices, bounds, axis, sidewall_angle, reference_plane)with Tidy3D-style linear sidewall taper and even-odd interior rule for non-convex cross-sections, plusComplexPolySlab(loops, ...)for self-intersecting and multi-loop (hole-carving) cross-sections - Shared core geometry constructors use
position=...consistently across analytic primitives andMesh - Shared core geometry constructors default to
device=None, whileScene(...)owns device placement and defaults to CUDA - Rotation support for applicable analytic geometries
- Axis selection for directional geometries such as
Cylinder,Cone,Pyramid,Prism, andTorus - Triangle mesh geometry via
Mesh(vertices, faces, ...) - OBJ mesh loading via
Mesh.from_obj(...) Geometry.to_mesh(...)returns torch-native vertex and face tensors, with faces standardized totorch.int64- Mesh transforms: recentering, scaling, rotation, translation
- Mesh topology inspection: vertex count, face count, boundary edge count, non-manifold edge count, degenerate face count, inconsistent edge-orientation count, watertight flag
- Mesh fill modes:
auto,solid, andsurface
- Public
Material(eps_r, mu_r, sigma_e, name=None)inwitwin.maxwell, extending the sharedwitwin.core.Materialcontract with Maxwell-specific constitutive behavior - Optional electric and magnetic dispersive pole models on
Material:DebyePole,DrudePole, andLorentzPole - Dispersive poles expose
susceptibility(angular_frequency)andsusceptibility_at_freq(frequency)for explicit frequency-domain evaluation - Spatially-varying custom dispersive poles
CustomDebyePole,CustomDrudePole, andCustomLorentzPole: the oscillator-strength parameter (delta_epsfor Debye/Lorentz,plasma_frequencyfor Drude) is a 3D torch tensor mapped over the structure'sBoxextent with the same lower-inclusive/upper-exclusive node coverage and trilinear resampling asMaterialRegion.density, composed multiplicatively with the structure's soft geometry occupancy; the compiler lowers each custom pole to its peak-strength scalar reference pole plus a per-cell amplitude grid, so the existing native CUDA ADE kernels run unchanged and per-cellchi(x, omega) = weight(x) * chi_ref(omega)is exact - Custom poles expose
susceptibility(angular_frequency)/susceptibility_at_freq(frequency)returning the per-cell complex susceptibility grid, plusreference_pole()andamplitude()for the compile-layer lowering; FDTD auto-dtuses the peak (worst-case) pole parameters PerturbationMedium(base, perturbation=..., eps_sensitivity=...)shifts a baseMaterial's permittivity by an external perturbation field,eps(x) = eps_base + eps_sensitivity * perturbation(x)(for example a temperature or carrier-density map): the 3D torchperturbationgrid maps over the structure'sBoxextent exactly likeMaterialRegion.density, composes with the soft geometry occupancy, and is applied at compile time before ADE templating so dispersive bases see the shiftedeps_inf; the map is PyTorch-native and differentiable, and aperturbationtensor withrequires_grad=Trueis discovered as a trainable material input by the FDTD/FDFD gradient bridges (validated against per-element finite differences)- Axis-aligned diagonal anisotropy through
DiagonalTensor3forepsilon_tensor,mu_tensor, andsigma_e_tensor, plus symmetric positive-definite off-diagonal electric permittivity throughTensor3x3 - A
DiagonalTensor3epsilon_tensorcomposes with electric dispersive poles in the sameMaterial: each Yee axis carries its own background permittivityeps_inf_iwhile the shared isotropic pole susceptibility disperses all axes, giving the per-axis frequency permittivityeps_i(omega) = eps_inf_i + chi(omega)(birefringent background with material dispersion), validated against the analytic per-axis pole susceptibility and differentiable through the FDTD adjoint - A full off-diagonal
Tensor3x3epsilon_tensorcomposes with electric dispersive poles in the sameMaterial(a rotated birefringent dispersive crystal): the poles enter isotropically so the lab-frame response iseps(omega) = eps_inf_tensor + chi(omega) * I, and the FDTD forward applies the same per-edge instantaneous inverse permittivity tensoreps_inf^-1to bothcurl(H)(diagonal effective permittivity plus native off-diagonal coupling kernels) and the ADE polarization current (diagonal subtraction againsteps_effplus native off-diagonal current-coupling kernels), which diagonalizes exactly in the crystal principal frame and reproduces the ordinary and extraordinary indicesn_o(omega) = sqrt(eps_ordinary + chi(omega))andn_e(omega) = sqrt(eps_extraordinary + chi(omega)), validated within 2% by propagating both eigen-polarizations of a 45-degree rotated uniaxial Lorentz slab at two frequencies; this combination is FDTD forward-only, its adjoint stays guarded - A full off-diagonal
Tensor3x3epsilon_tensorstructure may overlap the CPML absorber (an anisotropic crystal reaching the boundary): a dedicated native CUDA aniso CPML kernel coordinate-stretches every off-diagonalcurl(H)derivative with a per-direction psi memory owned by the target Yee edge (the two transverse directions use the E-field node profiles, the edge's own half-point direction uses the H-field profile), which reduces exactly to the raw off-diagonal update outside the absorber and, inside it, restores the stability and absorption that the un-stretched coupling loses; validated by a homogeneous rotated birefringent crystal filling the domain whose post-pulse residual field energy stays within 2x of the isotropic-PML baseline (the un-stretched coupling diverges instead). The split-field graded-sigma absorbers (absorber="pml"/"absorber") have no per-direction auxiliary memory to stretch the coupling, so an anisotropic overlap there is rejected with a physics-worded error; the adjoint likewise rejects an anisotropic structure overlapping the CPML because the reverse replay does not yet stretch the off-diagonal coupling - A full off-diagonal
Tensor3x3epsilon_tensorcomposes with electric conductivity (sigma_e, or aDiagonalTensor3 sigma_e_tensor) in the sameMaterial(a lossy anisotropic crystal): the FDTD forward folds the loss through the exact per-edge semi-implicit tensor inverseB = dt * (eps_inf + dt/2 * diag(sigma))^-1, giving the updateE^{n+1} = E^n + B . (curl(H) - sigma . E^n)with the diagonal decay1 - sigma_i * B_iiand a native off-diagonal conduction-current subtraction-B_ij * sigma_j E_j^nthat couples the transverse components; for an isotropic conductivity this diagonalizes in the crystal principal frame, so a uniform field decays at exactly the analytic per-axis semi-implicit rate (validated to 1e-3, including the off-diagonal-generated cross component) and a plane wave through the slab absorbs at the analytic extraordinary complex indexn = sqrt(eps_e + i sigma/(omega eps0))(transmitted amplitude within 5%); this combination is FDTD forward-only, its adjoint stays guarded - A
DiagonalTensor3mu_tensorcomposes with magnetic dispersive poles in the sameMaterial(the magnetic mirror of the diagonal-anisotropic electric-dispersion combination): each Yee edge carries its own background permeabilitymu_inf_iwhile the shared isotropic magnetic pole susceptibility disperses all axes, giving the per-axis frequency permeabilitymu_i(omega) = mu_inf_i + chi_m(omega)(a birefringent magnetic background with material dispersion), validated against the analytic per-axis magnetic pole susceptibility read from the magnetic ADE state; a full off-diagonalTensor3x3magnetic tensor remains unsupported as a static tensor field, while a non-reciprocal off-diagonal permeability is instead provided by the dedicatedGyromagneticFerritematerial (below), which carries the gyrotropy in a local magnetization state rather than by wideningmu_tensor GyromagneticFerritefirst-class SI material for DC-biased ferrites (the framework's first non-reciprocal medium):GyromagneticFerrite(eps_r, saturation_magnetization, bias_field=(x,y,z), gilbert_damping, gyromagnetic_ratio, mu_infinity, sigma_e)defines a gyromagnetic (Polder) permeability from a linearized Landau-Lifshitz-Gilbert model, validated forsaturation_magnetization > 0,bias_field != 0,gilbert_damping >= 0, andgyromagnetic_ratio > 0;GyromagneticFerrite.from_cgs(saturation_4piMs_gauss=..., bias_Oe=...)accepts CGS datasheet quantities and records the exact SI conversion incgs_conversion, andGyromagneticFerrite.from_resonance(resonance_frequency=..., saturation_magnetization=..., linewidth=...)back-computes the bias and Gilbert damping from the gyromagnetic resonance;permeability_tensor_at_freq(frequency)/polder_tensor(angular_frequency)return the torch-native, differentiable complex 3x3 lab-frame Polder tensormu_r = mu*(I - b b^T) + mu_infinity*(b b^T) + i*kappa*[b]_x(exp(-i*omega*t)convention, Hermitian when lossless and passive-absorptive whengilbert_damping > 0, off-diagonalkappaand the Faraday rotation flipping sign under bias reversal while the diagonal is unchanged); the full sign/unit conventions, LLG->Polder derivation, implicit-midpoint discretization, discrete-energy passivity proof, and frozen acceptance budget are recorded indocs/reference/ferrite-physics-contract.md.Scene.compile_gyromagnetic_materials(dt=...)lowers every ferrite structure into aCompiledGyromagneticLayoutstructure-of-arrays (active-cell indices, raw occupancy, per-cell bias unit vector and right-handed orthonormal local basis with an axis-aligned z/x/y fast path,omega_0/omega_m/gilbert_damping/mu_infinity, the dt-independent 2x2 magnetization-ADE matrices, and -- oncedtis bound -- the implicit-midpoint Cayley propagator that is orthogonal when lossless and a strict contraction when lossy), with an explicit staircase partial-fill (the anti-symmetric gyrotropic tensor is never scalar-averaged) and a full off-diagonalmu_r(f)per-cell tensor accessorCompiledGyromagneticLayout.permeability_tensor(frequency); the layout is cached per scene and serializable, and its per-cell state-space / propagator matrices agree bit-for-bit with the Phase-0 verification oracle. The FDTD time-domain solver runs a ferrite scene directly through the standardScene -> Simulation -> Resultpath: the ferrite compiles as its diagonal background and the non-reciprocal off-diagonal permeability is produced by a local magnetization-ADE advanced at each ferrite cell (gyromagnetic_enabled), inserted into the magnetic update asH -= dM/mu_infinity(the magnetic mirror of the electric-side full-anisotropy correction). The forward advances an arbitrary bias -- an axis-aligned z/x/y fast path (where the two transverse magnetic components co-locate on the shared Yee overlap) plus a general per-cell path for an oblique or spatially-mixed bias -- is unconditionally stable and passive (the discrete magnetic energy does not grow at zero damping, monotonically decays for positive Gilbert damping), reproduces the frozen discrete Polder response and its bias-reversal non-reciprocity, composes with PML and electric conductivity, adds zero operations to a ferrite-free scene, and is CUDA-graph capturable (no per-step allocation or host synchronization); the field<->magnetization coupling is the implicit midpoint of the coupled system (the magnetization is driven by the time-centred pre/post-update H and the correction feeds back simultaneously via a precomputed per-cell 2x2 inverse), which is discretely non-growing in a lossless closed cavity unlike an explicit pre-update-advance / post-update-correct split; a general (non-axis-aligned) uniform bias and a scene mixing bias directions (mixed axes, opposed signs on one axis such as a +z/-z latching circulator, or differing magnitudes/materials) are supported through a per-cell general-bias path -- a pure coordinate rotation of the same contracted implicit-midpoint update (identity collocation reused), where the purely-local magnetization ADE makes a mixed-bias scene the direct sum of independent per-cell passive blocks with correct per-region handedness -- while only a Bloch-periodic ferrite run still fails closed (the real magnetization-ADE correction cannot carry the complex Bloch phase), and every non-FDTD-forward consumer of a ferrite scene fails closed rather than silently simulating a reciprocal medium: the frequency-domain (FDFD) solve, the differentiable adjoint (no reverse gyromagnetic core yet), and the multi-GPU distributed solve all reject a ferrite, while single-device checkpoint/resume round-trips the magnetization state
- General and mixed bias gyromagnetic ferrite forward (FDTD): an arbitrary uniform bias direction
b̂(not just a grid axis) now runs directly throughScene -> Simulation -> Result. The general-bias update is a pure per-cell coordinate rotation of the same linearized-LLG implicit-midpoint (Cayley) update the axis-aligned path uses -- no new integrator and no new coefficients -- gathering the transverse RF driveh_u = u·H,h_v = v·Hfrom all three labHcomponents (columnsu,vof the per-cell right-handed local frame[u|v|w],w = b̂) and scattering the back-reactiondM = dm_u·u + dm_v·vonto all three, with identity collocation reused from the axis-aligned slice; because[u|v]ᵀ[u|v] = I, the general path reduces to the axis-aligned fast path bit-for-bit for an axis-aligned bias (verified on both the magnetization ADE and the real CUDA field update), and it matches the frozen discrete Polder oracle forb̂ = (1,1,1)/√3toreference_polder_rtol. A spatially mixed-bias scene -- multiple ferrite structures with different bias axes, opposed signs on one axis (e.g. a+z/-zlatching circulator), or differing magnitudes/materials -- is supported through the same per-cell path: the magnetization ADE is purely local (fields couple only through the ordinary reciprocal Yee curl), so a mixed-bias scene is the exact direct sum of the independent single-material runs (verified bit-for-bit) with each region precessing around its ownb̂at the correct handedness. The forward is stable and passive for an oblique bias (energy-envelope non-growth in a closed lossless cavity) and reverses its Faraday-rotation (gyrotropic) direction under bias reversal while leaving the co-polarized response unchanged. Only a Bloch-periodic ferrite still fails closed (the real magnetization-ADE correction cannot carry the complex Bloch phase); the FDFD, adjoint, and multi-GPU consumers remain fail-closed as before.
Material.relative_permeability(frequency)for isotropic magnetic dispersion evaluation- Instantaneous isotropic Kerr nonlinearity on
Material(kerr_chi3=...) - Instantaneous second- and third-order nonlinearity through
NonlinearSusceptibility(chi2=..., chi3=...)composed viaMaterial(nonlinearity=...)(single descriptor or a tuple):chi2adds the per-componentP_i = eps0 * chi2 * E_i^2(second-harmonic generation, optical rectification) andchi3the isotropicP_i = eps0 * chi3 * |E|^2 * E_i, lowering to exactly the same runtime channel asMaterial(kerr_chi3=...)(the two are equivalent and additive, validated bitwise against the Kerr fast path); multiple descriptors sum, and nonlinear media may coexist with dispersive or anisotropic materials in other structures of the same scene (FDTD only) - A single
Materialmay carry both instantaneous nonlinearity (chi2/chi3/TwoPhotonAbsorption) and electric dispersive poles (Debye/Drude/Lorentz), the combination required forchi2second-harmonic generation where the dispersion setsn(w)vsn(2w)and hence the phase mismatchdk = (2w/c) * (n(2w) - n(w)): the runtime subtracts the ADE polarization current against the same field-dependent effective permittivityeps_eff = eps_lin + eps0 * (chi2 * E_i + chi3 * |E|^2)that the nonlinear displacement-current term uses (so the instantaneous nonlinear index shift and the dispersive response stay consistent), validated against the analyticsinc(dk*L/2)phase-matching factor (linear-in-L phase-matched SHG growth vs Maker-fringe suppression in a mismatched dispersive slab); this same-material combination is forward-only, its FDTD adjoint stays guarded (FDTD only) - Two-photon absorption through
TwoPhotonAbsorption(beta, n0=None)composed viaMaterial(nonlinearity=...): the TPA coefficientbeta[m/W] ofdI/dz = -beta * I^2enters the FDTD update as a field-dependent conductivitysigma_NL(x, t) = (4/3) * beta * (n0 * eps0 * c0)^2 * |E(x, t)|^2folded into the semi-implicit lossy decay term per step (the 4/3 factor matches the cycle-averaged CW dissipation toalpha = beta * I);n0defaults tosqrt(eps_r)of the host material, multiple descriptors sum, and TPA composes freely withchi2/chi3and static conductivity in the same material (validated by the intensity-dependent saturable transmission trend) - Convenience constructors
Material.debye(...),Material.drude(...), andMaterial.lorentz(...) - Experimental linear-gain media via gain-signed (negative oscillator strength,
delta_eps < 0)LorentzPole/Material.lorentz(...), gated behind an explicitallow_gain=Trueopt-in so an accidental sign flip still raises; opting in emits awarnings.warnstability notice because gain media can violate the usual FDTD Courant/stability margins - Space-time permittivity modulation through
ModulationSpec(frequency, amplitude, phase=0.0)composed viaMaterial(modulation=...): the static permittivity becomeseps(x, t) = eps_static(x) * (1 + amplitude(x) * cos(2*pi*frequency*t + phase(x)))for non-reciprocal devices, isolators, and frequency conversion;amplitude(modulation depth in[0, 0.5)) andphase[rad] are scalars or 3D grids mapped over the structure'sBoxextent (same node-coverage/trilinear-resampling convention asMaterialRegion.density, enabling traveling-wavephase(x)profiles), the compiler rasterizes the quadrature fieldsamplitude*cos(phase)/amplitude*sin(phase)once, and the FDTD runtime applies the charge-conserving updateE_new = decay * (m_prev/m_next) * E_old + (curl/m_next) * curl(H)inside dedicated native CUDA E-update kernel variants (updateElectricFieldE*Modulated3Dplus dense- and compressed/slab-CPML counterparts) driven by a per-edge modulation angular-frequency field plus a persistent two-element device clock (so no coefficient tensors or host phase scalars are rebuilt per step, CUDA Graph capture remains valid, and a single Scene may hold several distinct modulation frequencies at once — each modulated structure stamps its own angular frequency onto the cells it covers, letting disjoint structures modulate independently); a modulated slab reproduces the expectedomega +/- Omegasidebands in the transmitted spectrum, two disjoint slabs driven atOmega_AandOmega_Bproduce both sideband pairs at their own frequencies, and the compressed (slab) CPML memory mode reproduces the dense CPML modulated run bit-for-bit - Space-time modulation composes with electric/magnetic dispersion (
Debye/Drude/Lorentzpoles) and with the instantaneous nonlinear channels (kerr_chi3/chi2/TwoPhotonAbsorption) in the sameMaterialor across separate structures in a Scene (the electro-optic-modulator edge, e.g. a Pockels-modulated dispersive crystal): the scalar modulation factorm(x, t)scales theeps_infbackground only, so the ADE polarization current is folded through the same per-step1/m_nextfactor ascurl(H)(a dedicated native CUDAapplyPolarizationCurrentModulated3Dkernel divides the dispersive current byeps_inf * m_next, reducing bit-for-bit to the plain subtraction where the modulation depth vanishes), and the field-dependent nonlinear decay/curl coefficients feed the modulated E update directly; the modulated dispersive solve reduces to the pure-dispersive solve as the depth vanishes (withinO(depth)), generates theomega +/- Omegasidebands on top of a dispersive slab, converts a smaller fraction of carrier to sidebands than a non-dispersive medium of the same static index (the poles are not modulated), and the modulated Kerr solve reduces to the pure-modulation solve aschi3vanishes; this combination is FDTD forward-only, its adjoint stays guarded, and a modulated anisotropic tensor or a modulated static conductivity remains rejected with a physics-worded error - The pairwise composability of the FDTD material and environmental features is a documented, validated contract (
tests/materials/combinations/test_combination_matrix.py, the P5.2 combination matrix): across the material axes {dispersive, diagonal-aniso, full-aniso, nonlinear (chi2/chi3/TwoPhotonAbsorption), modulated,sigma_e} plus the environmental axes {Bloch boundary, CPML overlap}, every physically meaningful ordered pair compiles and runs to finite fields, while the combinations still out of reach raise a physics-worded error (never "not implemented yet") instead of producing silently wrong physics — nonlinear / full-aniso / modulated media under complex Bloch fields (the real-valued update kernels are undefined on phase-shifted complex fields), a modulated or nonlinear anisotropic permittivity tensor (would need a per-step re-inversion of a coupled 3x3 tensor), a modulated static conductivity, and a full off-diagonal tensor overlapping a split-field (non-CPML) PML. Diagonal and full anisotropy are not an independent pair: a permittivity is either diagonal or full and both occupy the singleepsilon_tensorslot Material.sellmeier(b_coefficients, c_coefficients, eps_inf=1.0, ...)for lossless Sellmeier dielectrics (for example Schott BK7 glass), which lowers eachB_i * lambda^2 / (lambda^2 - C_i)term to a zero-dampingLorentzPolewithdelta_eps = B_iandresonance_frequency = c / sqrt(C_i);c_coefficientsare the squared resonance wavelengths in SImeters^2- Frequency-dependent material evaluation through the shared material compiler for single-frequency workflows, including isotropic
sigma_e - Component-aware material compilation keeps scalar summary tensors for visualization / compatibility while exposing explicit per-axis material grids for solver backends and
Result.material(...) - Density-based
MaterialRegioncompilation using native PyTorch tensor interpolation, optional box filtering, and optional projection - Vacuum background by default (
eps_r = 1,mu_r = 1) - Multi-structure material composition on the scene grid
- Structure overlap resolution uses
Structure.priorityfirst, then append order among equal-priority structures - Occupancy-based material blending on the scene grid, with SDF-driven soft occupancy for shared
Box,Sphere,Cylinder,Torus, andHollowBox - Phase-1.5 primitive SDF coverage for shared
Ellipsoid,Cone,Pyramid, andPrism - Differentiable SDF occupancy for
PolySlabandComplexPolySlab, with gradients through polygon vertices, axis bounds, and sidewall angle - Differentiable mesh occupancy compilation through native-CUDA mesh signed-distance evaluation, including forward/backward distance-sign queries for watertight solid fill, geometry-state-aware static mesh SDF caching, cached BVH acceleration for larger static CUDA meshes, and shared surface-band modes
- Supersampled voxel averaging for smoother material interfaces on partial cells
- Polarized (Kottke normal-projection) subpixel averaging for
epsandmu, using SDF-gradient interface normals estimated by central finite differences on the node grid (differentiable in geometry parameters), reducing normal-field error at high-contrast interfaces - In-domain perfect-electric-conductor material via
Material.pec(name=None)/Material.is_pec, compiled to a differentiable union PEC occupancy grid and enforced in FDTD through per-edge open-fraction coefficient scaling (staircasehard edge, orconformalpartial fill from the per-edge geometric coverage fraction, which reduces to thestaircasemask bit for bit wherever the conductor surface does not cut the edge) - Zero-thickness conductive 2D sheets via
Medium2D(sigma_s=...)[S]: attached to aStructurewhose geometry is an axis-alignedBoxwith exactly one zero-size axis, the sheet snaps to the nearest node plane along its normal and lowers to the equivalent volumetric conductivitysigma_s / dcellon the two tangential Yee-edge conductivity components of that single layer (the normal component is untouched); sheet contributions are additive with bulk conductivity and other sheets, andMedium2D.sheet_conductivity(omega)/sheet_conductivity_at_freq(f)expose the analytic sheet conductivity for validation Graphene(chemical_potential=..., scattering_time=..., temperature=300.0, include_interband=False)2D sheet with the Kubo surface conductivity (chemical_potentialin eV,temperaturein K,scattering_timein s): the intraband (Drude-like) termsigma_s(omega) = A / (1/tau - i*omega)lowers to a tangential-only Drude pole on the snapped Yee layer (eps0*omega_p^2 = A/dcell); withinclude_interband=Truethe T>0 principal-value interband term (hbar*omega -> 2*|mu_c|absorption edge) is fitted at construction to a small set of Lorentz sheet terms (delta_eps = strength / (eps0*dcell)) and lowered to tangential Lorentz poles, matching the analytic Kubo conductivity to within a few percent across the optical band; both are advanced by the existing native CUDA Drude/Lorentz ADE kernels, and the intraband rate plus interband resonances feed the FDTD auto-dtboundMedium2D.sheet_lorentz_terms()exposes resonant(strength, omega_0, gamma)surface-conductivity terms (each-i*omega*strength*omega_0^2/(omega_0^2 - omega^2 - i*gamma*omega)[S]); unlike Drude sheet terms these represent a capacitive below-edge reactance (Im(sigma) < 0) and lower to volumetric Lorentz poles, and are additive with the static and Drude sheet channels insheet_conductivity(omega)LossyMetalMedium(conductivity=...)good-conductor with a normal-incidence surface-impedance (Leontovich) boundary runtime: the metal interior is masked and each step the two tangential E faces are updated from the vacuum-side tangential H via the resistive Leontovich relationE_t = R * (n x H), with the surface resistanceR = sqrt(omega0*mu0/(2*sigma))evaluated at the operating frequency (the reactive part ofZ_sis intentionally omitted: its explicit time-derivative overwrite is non-passive and unstable, and for a good conductor it shifts|Gamma|by< 1.3e-4); coverage is evaluated against the physicalDomain.bounds, so a half-space that ends at the physical boundary remains valid whenPreparedSceneappends external PML cells, and either low-side or high-side illuminated faces use the actual geometry-face Yee node. The resolved skin-depth interior never needs meshing (validated to <5% of the analytic reflection at >=10x fewer cells than a resolved volumetric metal). Analytic helperssurface_impedance(omega)/surface_impedance_at_freq(f)/skin_depth(f)remain exposed for design- The
LossyMetalMediumsurface-impedance boundary is generalized from axis-aligned metalBoxfaces to any staircased (voxelized) good conductor: a non-Boxconductor (a curved cylinder/sphere, all six exposed-face orientations, mixed orientations in one scene) is staircased from its node occupancy into masked per-face Leontovich writes, with each exposed voxel face illuminated only when its vacuum-side node lies inside the physical domain. Edges and corners are handled by the standard per-face-independent surface update (no new edge physics). This is validated at two levels beyond the flat-plate analytic reflection: (1) a wave-level skin-effect attenuation benchmark (benchmark/scenes/rf/lossy_waveguide_attenuation.py, RESULTS rowrf/lossy_waveguide_attenuation) where a lossy-wall TE10 rectangular waveguide's conductor attenuationalpha, extracted from the two-line|S21|ratio of a short and a long guide, tracks the analytic TE10alpha_c(Pozar 3.96) to a fraction of a percent across the band while a PEC-wall guide of the same geometry givesalpha ~ 0; and (2) a resolved-conductor physics gate (tests/validation/physics/test_sibc_cylinder_convergence.py) where the power absorbed by a staircased lossy-metal cylinder (skin depth unmeshed) reproduces the power absorbed by the same cylinder as a fully-resolved volumetric conductor (skin depth meshed on a grid-converged fine reference) within a documented tolerance, an order of magnitude closer than a PEC cylinder. True oblique/conformal (non-staircase) SIBC, a rotated metalBox, a generic rationalSurfaceImpedanceMediumon a curved conductor, and Bloch + SIBC remain fail-closed with a physical reason - Validation note (no new adapter capability): the pre-existing
LossyMetalMediumsurface export throughScene.to_tidy3d()(see the reference-backend adapter section) was exercised as an external reference for the lossy-waveguide attenuation benchmark. No adapter code changed; the forward-mode attenuation cross-check is recorded honestly (the external RF surface-impedance export under-applies the wall loss at a coarse export grid, a documented adapter-fidelity gap, while the analyticalpha_cand the FDTD two-linealpharemain the binding references)
CW,GaussianPulse, andRickerWaveletsource-time definitionsCustomSourceTimearbitrary temporal waveform from a sampled(times, amplitudes)table or a callablefn, evaluated on the solver time grid through the Python scalar injection path (no CUDA kernel change)PointDipolesource definition withsource_timeand selectableprofile="gaussian"|"ideal"PointDipole,ModeSource,ModeMonitor, andModePortuseposition=...as the public spatial-location argumentPointDipoleGaussian profiles are normalized by the local Yee control volumes so their integrated SI current moment is invariant to width and grid spacingPointDipole(profile="ideal")deposits the same unit current moment onto the neighboring Yee samples with linear position-preserving weightsPlaneWavesoft source for analytical plane-wave injection on an auto-placed source plane; zero-phase periodic boundaries are supported on transverse axes, while periodicity along the propagation direction, a nonzero tangential phase advance, and Bloch boundaries require the TFSF pathPlaneWavesoft source uses a single-plane directionalE/Hequivalent-current injector whose absolute incident-power scale is derived from first principles (surface-equivalence unit forward gain and the Yee numerical wave impedanceeta0): unit source amplitude radiates unit time-averaged power over the full computational aperture, including external PML, with the analytic1/sqrt(A*cos(theta)/(2*eta0))scale and the derived Yee half-cell Poynting factor1/sqrt(cos(k_normal*dl_normal/2)); there is no empirical calibration factor, and absolute native-monitor power matches analytic within 2% across frequencies and grid spacings- The soft
PlaneWavenumerical-dispersion phase correction is local to the launch footprint on nonuniform (graded) grids: the discrete-dispersion phase velocity that time-aligns the YeeE/Hsource planes (and phases the aperture for oblique incidence) is solved from the launch-plane cell spacing along the injection axis and the physical-aperture mean spacing along each tangential axis, instead of the global-minimum spacing whose finest cell can sit far from the source. On a grid whose interior is uniform along an axis, that axis returns its exact spacing, so the correction is bit-for-bit the previous global-minimum result and uniform grids are unchanged; on a ~2.6x-graded propagation axis with the source in the coarse region (~6 cells/wavelength there) the injected wavefront's phase-velocity error drops from ~4% to 0 (an exact match to the launch cell's own numerical wavenumber) GaussianBeamsoft source for analytical Gaussian-beam injection with configurable waist and focusAstigmaticGaussianBeamsoft source for elliptical Gaussian-beam injection with independent per-axis waist(w0_u, w0_v)and per-axis focal offsetsfocus_u/focus_v, reducing exactly toGaussianBeamfor isotropic inputs, normalized to one watt, and using the launch plane as the common pulsed-time origin so a remote waist does not truncate the pulse beforet=0UniformCurrentSource(size, polarization, source_time, center=...)for a uniform additive electric current filling an axis-aligned box region, deposited by the geometric overlap of the source box with each Yee component's dual control volumeCustomCurrentSource(current_dataset, source_time)injects arbitrary volume electric-current (Jx/Jy/Jz) and magnetic-current (Mx/My/Mz) data through component-aware Yee windows and trilinear sampling, retaining the source's endpoint extension onto the covering discretized grid; singleton dataset axes map to the nearest corresponding node- or half-step Yee sample instead of collapsing staggered components to an empty windowCustomFieldSource(field_dataset, source_time)replays a tangentialE/Hdistribution on one discrete TFSF face with the correct physical-H/update-equation sign conversion, producing the directional equivalent currentsJ = n x H,M = -n x E- Experimental
ModeSourcesoft source for axis-aligned FDTD waveguide launching, using a full-vector generalized 2D eigenmode solve with adjoint forward/backward Yee differences on structured apertures to prevent odd/even checkerboard copies, a centered TE/TM parity basis for uniform metallic guides, deterministic sparse initialization and smoothness tie-breaking, deterministic requested-polarization rotation inside numerically degenerate eigenspaces, power-inner-product duplicate rejection, and requested-polarization-family ordering for stable physicalmode_indexselection. For a guided (non-TEM) mode request on a closed metallic aperture, a transverse null-space branch atbeta = k0(a plane-wave-like profile with no transverse cutoff) is rejected structurally -- by a block-averaged, anti-checkerboard transverse-envelope test combined withbeta^2 ~ k0^2-- so the guided mode is returned at every grid tier instead of the spuriousbeta = k0eigenvalue; the rejection is gated on the requested wave family, sobeta = k0for a doubly-connected TEM line (solved on the separate electrostatic path) is unchanged. Full-plane component profiles retain zero-valued boundary nodes and use discrete one-watt Poynting normalization on transverse Yee planes, normal half-cell power compensation on resolved grids, and leapfrog-aligned propagation delays across staggered E/H launch faces in forward, replay, and adjoint execution. Dense and sparse forward backends coexist with the retained torch-differentiable scalar path for trainable scenes, while modal results expose per-candidate eigenpair/divergence/power/polarization/checkerboard diagnostics and the normalized power-overlap matrix - Experimental
ModeSource/ModeMonitor/ModePortnow accept broadband (GaussianPulse/Ricker) as well asCWsoft injection (the guided profile is solved once at the waveform center frequency and driven by the native time-shifted surface kernel), complex/lossy eigenmodes (a complex-symmetric scalar solve exposingeffective_index_complex/beta_complex, matched to~4e-5against the analytic lossy-slab dispersion), diagonal-anisotropic apertures solved with their true per-axiseps/mutensors instead of an isotropic average, and bent (curved-waveguide) ports via the Heiblum-Harris conformal map (bend_radius/bend_axis, threaded throughmode_specso the S-parameter / mode-overlap postprocess reconstructs the same bent reference mode) - Experimental
TFSF(bounds=...)injection descriptor forPlaneWave,GaussianBeam, andAstigmaticGaussianBeam, with validated axis-alignedPlaneWavesupport forCWandGaussianPulse, and validated CW obliquePlaneWavesupport - Experimental
TFSF.slab(axis="x"|"y"|"z", bounds=...)injection descriptor for grating-orientedPlaneWaveworkflows on any normal axis (both CW and broadbandGaussianPulse/RickerWaveletsource times), spanning the transverse Bloch unit cell (the two axes transverse to the chosen normal axis) during solver preparation for periodic grating illumination; the two slab faces normal toaxisare selected automatically and the oblique polarization is resolved from the incident direction, so the forward stepping, mixed Bloch/CPML update, and reverse-time grating adjoint all dispatch on the resolved single PML (normal) axis rather than assuming z. A pulsed source injects its per-cell time-delayed surface currents into the split real/imag Bloch field with the boundary wrap phase, so a single broadband run covers the band at a fixed transverse Bloch wavevector (automatic wavevector resolution still requires CW because it maps a single frequency to a wavevector) - Non-periodic (PML/absorbing transverse boundary)
TFSF.slabinjection for a normally-incidentPlaneWave(CWor pulsed): the two-face slab fills the whole transverse cross section, injecting a plane wave that propagates along the slab axis with the scattered-field region confined outside the two axis-normal faces (the standard 1D-style reflection/transmission setup through a layered stack). It reuses the axis-aligned auxiliary-line provider, so the forward and adjoint runtimes need no slab-specific branch; oblique incidence under non-periodic transverse boundaries is rejected with a physics-worded error (use Bloch transverse boundaries for the grating slab) - CUDA
PlaneWaveTFSF forward stepping uses native CUDA auxiliary-line updates and fused patch-application kernels to reduce per-step launch overhead GaussianBeamTFSFremains experimental and currently uses the analytical profile provider rather than the future angular-spectrum / discrete-face engine- Polarization specified by field name (
"Ex","Ey","Ez") or explicit 3-vector - Source amplitude and phase carried by
source_time; spatial sources keep width / beam parameters and optional name - Multiple sources per scene
- Source compilation uses
compile_fdfd_sources(...)/compile_fdtd_sources(...)list-based interfaces across both solvers
import witwin.maxwell as mw
scene = mw.Scene(
domain=mw.Domain(bounds=((-0.5, 0.5), (-0.5, 0.5), (-1.0, 1.0))),
grid=mw.GridSpec.uniform(0.05),
boundary=mw.BoundarySpec.faces(
default="pml",
num_layers=12,
strength=1.0,
x="bloch",
y="bloch",
z="pml",
bloch_wavevector="auto",
),
)
scene.add_source(
mw.PlaneWave(
direction=(0.25, 0.0, 0.9682458366),
polarization=(1.0, 0.0, -0.2581988897),
source_time=mw.CW(frequency=200e12),
injection=mw.TFSF.slab(axis="z", bounds=(-0.4, 0.4)),
)
)
scene.add_monitor(mw.FluxMonitor("transmission", axis="z", position=0.7))
result = mw.Simulation.fdtd(scene, frequencies=[200e12]).run()PointMonitorfor point sampling of selected electric or magnetic field componentsPlaneMonitorfor orthogonal plane sampling of selected electric or magnetic field componentsFinitePlaneMonitorfor first-class finite rectangular plane sampling with explicitposition=(x, y, z)andsize=(sx, sy, sz)on a zero-thickness axisClosedSurfaceMonitorfor first-class finite closed Huygens-surface workflows, includingClosedSurfaceMonitor.box(...)and custom multi-face axis-aligned surfaces built fromFinitePlaneMonitorfacesFluxMonitorfor plane-integrated power / flux extraction from tangentialE/Hfields; spectral flux planes snap to the nearest tangential-E Yee plane, symmetrically average the straddling H planes, crop external-PML samples back to the physicalDomain.bounds, and integrate cell-centred transverse samples with full control-volume widths (including endpoint cells), avoiding arbitrary-position phasor attenuation and one-cell aperture undercounting- Spectral point / plane / flux / closed-surface monitors accumulate the running-DFT with the physical Yee time stagger: electric observers sample at the plain step phase while magnetic observers carry an extra
-1/2step (-0.5*omega*dt) retard, so the time-averaged Poynting cross termS = 1/2 Re(E x H*)sees the correct+1/2-step E/H offset; the FDTD adjoint seed schedule applies the exact transpose of this per-field phase - Near-to-far-field (Stratton-Chu / NF2FF) surface quadrature integrates each equivalent-current sample over its full primal Yee control volume (cell-centred midpoint rule), so on a closed Huygens box the control volumes tile the box surface to machine precision and the radiated far field is box-size independent (surface-equivalence invariant), giving correct directional far fields, RCS, and closed-surface radiated power
FieldTimeMonitorfor raw time-domain FDTD field recording (point, plane, or volume region) withstart/stop/intervalsampling controls, returning the sample-time tensortand per-component GPU buffers throughResult.monitor(...)FluxTimeMonitorfor instantaneous time-domain Poynting flux (E x H) recorded on an axis-aligned plane withstart/stop/intervalsampling controls, returning the sample-time tensortand the flux time series throughResult.monitor(...); external-PML transverse samples receive zero integration weight so the default infinite plane remains the physical-domain aperture- Experimental
ModeMonitorfor first-class modal decomposition on an axis-aligned port plane, reusing the currentModeSourcemode specification and returning forward / backward modal amplitudes and power throughResult.monitor(...) - Experimental
DiffractionMonitorfor grating diffraction-order decomposition on an axis-aligned periodic unit-cell plane, returning per-order(m, n)complex tangential amplitudes, per-order Poynting power, propagating / evanescent flags, and diffraction angles throughResult.monitor(...), with reciprocal lattice and light-cone cutoff derived from the transverse periods and the resolved Bloch wavevector PermittivityMonitorandMediumMonitorfor first-class point / plane / volume sampling of the compiled material properties (eps, plusmuandsigma_eforMediumMonitor), resolved from the compiled material tensors at result time throughResult.monitor(...)without requesting any time stepping or extra DFT frequencies- Experimental
DipoleEmissionMonitorfor the power a namedPointDipoledelivers to the field (P = -(1/2) Re(conj(J) . E)), formed from the co-located electric-field DFT at the dipole cell and the injected source current spectrum, returning per-frequencypower_deliveredthroughResult.monitor(...); combine a structured run with a vacuum run throughmaxwell.postprocess.purcell_factor(...)to obtain the Purcell factor / local density of states - Optional per-monitor
frequencies=on plane and modal monitors / ports - Named monitor results returned through the unified
Resultobject - Multi-component plane monitors with aggregated
Result.monitor(...)payloads and collocated tangential grids for postprocessing workflows Scene.resolved_monitors()expandsClosedSurfaceMonitorinto its underlying finite face monitors while preserving the publicScene -> Simulation -> Resultworkflow- Multi-frequency monitor output with
Result.monitor(name, frequency=...)/freq_index=...selection - Optional
compute_flux=TrueonPlaneMonitorto emit integrated Poynting flux / power per frequency Result.raw_monitor(...)to access the underlying point / plane payload directly when a first-class modal monitor resolves to a higher-level modal result
- Experimental
ModePortscene object that declaratively couples an optionalModeSourceexcitation with a first-classModeMonitor, so modal ports still flow through the sameScene -> Simulation -> Resultpublic architecture ModePort(source_time=...)materializes a namedModeSourceplus a named modal monitor;ModePort(source_time=None)acts as a monitor-only modal portModePort(monitor_offset=...)can separate the launch plane from the sampled monitor plane along the port normal without introducing a second public solver entrypoint- Experimental RF
WavePort/WaveModeSpecdeclarations freeze an axis-aligned aperture, explicit propagation direction and coincident reference plane, deterministic per-port mode identities, a tangential polarization seed, and peak-phasor impedance definitions.Scene.compile_waveports()snaps the aperture and optional TEM/hybrid voltage path and current contour to one Yee grid.Simulation.fdtd(...)supports both a named single-modePortExcitation(..., mode_name=...)and independent-columnPortSweep; the latter returns a complete multimodeNetworkData, while both return modalPortDatawith explicit[M, F]propagation constants, characteristic impedances, and tracking confidence. Cross-frequency assignment combines propagation constants and modal overlap, including degenerate-subspace alignment. Uniform conductor-backed TEM apertures use a device-resident electrostatic mode solve and one-watt power-consistent V/I normalization; TE usesZ=omega*mu/beta, TM usesZ=beta/(omega*epsilon), and hybrid modes explicitly select voltage/current or power normalization. WavePort mode solves and FDTD columns execute sequentially on one CUDA device
Incomplete / experimental: FDFD is not feature-complete and is not a numerical reference for FDTD. The entries below describe only its currently implemented subset; use FDTD for the validated general workflow.
- Frequency-domain simulation through
Simulation.fdfd(...) - CUDA-only solver execution;
Simulation.fdfd(...)requiresScene(device="cuda") - Single-frequency public wrapper with
frequency= - Dispersive-material support via effective complex
epsilon_r(omega)at the simulation frequency - Isotropic conductive-material support via
sigma_efolded into effective complexepsilon_r(omega) - Axis-aligned diagonal electric anisotropy and diagonal
sigma_e_tensorsupport in the Yee-grid operator - Per-face
none/pmlboundary selection, including one-sided and mixed-axis PML layouts - Explicit fast-fail validation for magnetic response, Kerr/nonlinear media, full off-diagonal
Tensor3x3permittivity, and in-domain PEC: FDFD static parity for these (and FDFD nonuniform grids) is deferred by user decision (2026-07-11), so the guards state an honest deferral and route to FDTD, which models them - Configurable GMRES settings via
GMRES(max_iter, tol, restart, solver_type, preconditioner) - GPU-native preconditioners for the iterative solvers:
none,jacobi(default),ssor(relaxation viaGMRES(ssor_omega=...), default 0.8),ilu(ILU(0); unstable on the indefinite curl-curl operator — seebenchmark/FDFD_PERFORMANCE.md), andams(experimental in-repo Hiptmair–Xu auxiliary-space preconditioner with geometric multigrid; measured non-contractive on the indefinite time-harmonic operator — see the module docstring for its scope) - Double-precision iterative solves via
GMRES(precision="double"): the Krylov recurrences and preconditioner run in complex128 while assembly and returned fields stay complex64; removes the float32 round-off stagnation (measured:sqmr+ssorconverges to 1e-7 at 48³ where single precision stalls at ~2e-2) - Supported
solver_typevalues:gmres,cg,direct, plus in-repo GPU Krylov enginesbicgstab,tfqmr,idr(IDR(s)), andsqmr(simplified QMR; exploits the complex-symmetric system) - The FDFD system is assembled in a symmetrized UPML formulation (exactly complex-symmetric via a diagonal similarity), which also measures far lower PML reflection than the previous discretization; the adjoint solve reuses the forward factorization/preconditioner as a consequence
directsolver backed by NVIDIA cuDSS (pip install witwin-maxwell[direct]): factorize-once / solve-many with the factorization cached on the prepared solver and reused across source changes; complex64 LU plus iterative refinement- Direct-solve reuse composes with
solver.set_frequency(...), which releases the stale factorization automatically - Typed FDFD solver configuration through
FDFDConfig(solver=..., enable_plot=..., verbose=...) - Prepared execution via
Simulation.prepare()before running - Differentiable FDFD: scenes with trainable parameters (SceneModule parameters,
MaterialRegiondensities, trainable geometry tensors) route through an adjoint gradient bridge, soloss.backward()onResultfields propagates to material and geometry inputs; the adjoint solve reuses the cached system state (with the direct backend, a cached transpose factorization) - System-matrix caching on the prepared solver: repeated
solve()calls reuse the assembled matrix across source changes - Frequency switching on the prepared solver via
solver.set_frequency(...), reusing compiled material components when all materials are non-dispersive - Unified
Resultoutput containingEx,Ey, andEz - Solver stats including convergence flag, solver info, residual, and solver configuration
- Time-domain simulation through
Simulation.fdtd(...) - CUDA-only solver execution;
Simulation.fdtd(...)requiresScene(device="cuda") - Compiled native CUDA kernels are the only FDTD runtime backend; the Torch reference path has been retired from the runtime and relocated to the CUDA parity tests
- Engineering-preview multi-GPU joint solves through public
FDTDParallelConfigandSimulation.fdtd(..., parallel=...): Python remains the control plane, andDistributedFDTDowns an independent distributed coordinator/time loop for x-slab ownership, halos, streams, and events. The runtime is split into a rank-localShardEngine(deterministic local-scene build from the partition plan, local solver, per-step phases, owned-energy scalar, local monitor/DFT payloads) and a coordinator that expresses every cross-rank operation through transport primitives (exchange_electric/exchange_magnetic,reduce_owned_energy,gather_component_slabs,gather_monitor_payloads,gather_stats), so the same coordinator drives both the in-process CUDA P2P transport and a one-process-per-GPU NCCL transport: the per-step time loop is branch-free over the transport primitives, while construction, validation, and result-gather scope do branch on transport kind (rank-local engine build, a narrower NCCL capability envelope, and sized point-to-point vs. peer-copy gather). Single- and multi-GPU execution share the same native CUDA numerical core; the six bounded standard real-field operations and legacy full-domain wrappers invoke the same implementations with different x ranges - Monitor-first multi-GPU result semantics:
gather_fields=Falsereturns supported point/time and assembled plane/flux/mode payloads onresult_devicewithout a global field, whilegather_fields=Truepreflights and gathers global electric last-step or DFT fields. y/z-normalPlaneMonitor/FinitePlaneMonitor/FluxMonitor/ModeMonitorpayloads are tiled across owned x intervals; x-normal planes have one owner, with an explicit guard for ordinaryExplanes exactly on an internal split. GatheredResult.save/loadand manifest-basedsave_sharded/load_shardedprovide eager or lazy/gathered persistence without restoring live transport state - Experimental multi-GPU FDTD joint-solve adjoint for a trainable
BoxMaterialRegiondensity on the pure real standard (open/PEC) path: a trainable-densitySimulation.fdtd(..., parallel=...)scene routes through a distributed gradient bridge that captures per-shard forward checkpoints, runs one transposed reverse step per forward step (Phase 1 on every shard, a transposed magnetic halo, Phase 2, a transposed electric halo, Phase 3, then the per-shard source-term eps gradient), and gathers the per-shardgrad_epsowned slices into a single global tensor so the existing single-GPU material pullback runs once on the logical scene.loss.backward()on a point-monitor-spectrum or full-field-DFT objective propagates to the density. Two-GPU acceptance covers 1-vs-2-GPU objective and gradient parity (point-monitor-spectrum and full-field-DFT objectives), central finite differences on density texels on the x-split and interior to each shard with the source and objective on the interface node, a checkpoint-capture stream-ordering contract, and bitwise-reproducible gatheredgrad_eps. Trainable geometry/material-perturbation/circuit/RF-port parameters, the legacy graded-sigma absorbers ("pml"/"absorber"), dispersive/conductive/nonlinear/anisotropic/modulated media, field shutoff, multi-source normalization, non-Box density regions, and tiled plane/flux/mode-seeded objectives are rejected at prepare before any distributed allocation. (The CPML/stable-PML absorbing update is now a supported distributed-adjoint capability — see the Track E3 distributed CPML-trainable adjoint entry below — which supersedes the earlier envelope that rejected all absorbing boundaries) - Single-node one-process-per-GPU NCCL halo transport primitive (
fdtd/distributed/nccl_transport.py,transport="nccl"shape): a rank-local transport launched bytorchrunthat exchanges contiguous Yee x-plane halos with its chain neighbours throughtorch.distributed.batch_isend_irecv(forward electric/magnetic and their transposed reverse-halo adjoint accumulations into preallocated staging with deterministic orderedadd_), reduces a rank-local scalar withall_reduce, verifies cross-rank device homogeneity, and tears the process group down deterministically. Preflight fails fast on a missing/torchrun-mismatched launcher environment, a mismatched world size, non-Linux platforms, and an adopted process group whose world size, rank, or backend does not match the rank's expectation (rather than binding to a mismatched group; backend validation accepts a compositecuda:nccldevice-backend spec and fails closed otherwise). A two-ranktorchrunworker asserts bitwise halo round-trips, adjoint accumulation, ghost-zeroing, endpoint-ghost negative invariants, the matching-group adopt path, the shared-group teardown/vanished-group guard, and scalar all-reduce. The transport also exposes the engine-based coordinator primitives (exchange_electric/exchange_magneticover the local engine's contiguous Yee planes on its compute stream,reduce_owned_energyviaall_reduce, and a sized point-to-pointgather_component_slabsthat stitches each rank's owned x-slab onto rank 0 using the deterministic partition layouts). An end-to-end one-process-per-GPUtransport="nccl"forward solve is now qualified on two ranks: each rank builds its ownShardEngine, the coordinator runs the serialized time loop over the NCCL transport, and rank 0 gathers the global full-field DFT (Ex/Ey/Ez) and matches an independent single-GPU reference at the same tolerances as the in-process CUDA P2P leg (rtol 5e-5 / atol 5e-6), verified by a two-ranktorchrunconformance worker. A rank-death failure-matrix test confirms a dead peer surfaces as a bounded nonzero exit (launcher failure propagation /ProcessGroupNCCLwatchdog timeout bounded byFDTDParallelConfig.timeout_s) rather than a hang. Without atorchrunlauncher environment,transport="nccl"still raises the explicit launcher error from bothDistributedFDTDand the publicSimulation.prepare()path and never falls back to the in-process CUDA P2P transport (P2P monkeypatched to fail if reached). The NCCL forward path fails closed on monitors, coupled circuit/network/wire/port scenes, trainable density (adjoint), and field shutoff (each supported today only ontransport="cuda_p2p"), and multi-node execution remains out of scope. - Qualification remains limited to two RTX A6000 GPUs connected by NV4 and a guarded real-valued subset: end-to-end NCCL is qualified for the two-rank standard forward field solve only (no NCCL monitor gather, adjoint, coupled circuit/network/wire, or shutoff, and no three/four-GPU or multi-node NCCL); no three/four-GPU or PCIe-only qualification, Nsight trace, distributed CPML-trainable adjoint or tiled-monitor adjoint seed scatter, peer-aware CUDA Graph, advanced source families, x periodic/Bloch/symmetry, nonlinear/full-off-diagonal media, or SIBC claim is made. The final CUDA 13 acceptance observed 1.97385x strong speedup for the
257^3vacuum case and exact single/two-GPU diagnostics, while the129 x 65 x 65case remained below break-even; see the FDTD multi-GPU joint-solve guide for the complete support matrix, persistence contract, test checkpoints, and benchmark table
- Reverse-halo (adjoint transpose) exchanges on the one-process-per-GPU NCCL transport (
fdtd/distributed/nccl_transport.py): the transport now exposes the engine-basedprepare_adjoint_staging/exchange_magnetic_adjoint/exchange_electric_adjointcoordinator primitives alongside the forward halos, so a future distributed reverse driver can express its transposed Yee x-halos through the same branch-free primitive contract the forward loop uses. Each reverse exchange ships the rank-local ghost adjoint plane to its chain neighbour and zeroes it (preserving the ghost-adjoint-zero invariant the fused reverse kernels rely on) while receiving the opposite neighbour's ghost adjoint plane into a preallocated per-kind staging plane and accumulating it into the owned edge cell/node with a deterministic orderedadd_(no atomics, fixed rank order), introducing no per-step allocation. A two-ranktorchrunworker proves the reverse exchanges are the exact discrete transpose of the forward halos: it forms<A x, y>and<x, A^T y>for both the magnetic and electric halos (each inner product's two halves live on opposite ranks and are combined by anall_reduce), asserts bitwise equality (pure-copy halos ⇒ atol == 0), checks the ghost-zeroing invariant, and pins bitwise determinism across repeats; an env-gated falsification path that drops one accumulated owner makes the identity assertion fire on both halos (launcher asserts a nonzero exit), so the gate is not vacuous. The end-to-end one-process-per-GPU NCCL trainable-density reverse (per-rank distributed replay + reverse + grad_eps gather + rank-0 pullback) remains fail-closed (transport="nccl"trainable density still raises and points attransport="cuda_p2p"); this transport-level transpose contract is its verified foundation.
-
Opt-in per-rank step-rate instrumentation for distributed FDTD workers (
fdtd/distributed/instrumentation.py,StepRateInstrument): an env-gated timing hook (WITWIN_FDTD_STEP_TIMING, off by default;WITWIN_FDTD_STEP_TIMING_DIRselects the output directory) that brackets a rank's time loop (loop_begin/step_begin/step_end/loop_end) and, atfinalize, writes one machine-readablestep_timing_rank{r}.jsonper rank (schemawitwin.fdtd.step_timing/1: per-step wall mean/median/min/max/p95 in ms and aggregate steps-per-second, plus rank/world-size/device/step-count metadata). The disabled path is zero-cost: with the env var unset the bracket calls return immediately and never synchronize the device, so wrapping a production-shaped loop adds no per-step work — a unit test injects a countingsynchronizestand-in and asserts zero synchronizations across a full disabled loop (and that no artifact is written), while the enabled path synchronizes exactly2 + 2·stepstimes to make each recorded interval reflect completed GPU work rather than launch latency. The one-process-per-GPU NCCL forward worker runs the instrument as an opt-in collective pass after its parity solve (a no-op, byte-identical to the un-instrumented run, when the env var is unset), so an exclusive-GPU window can flip the env var and collect per-rank JSON; no wall-clock number is asserted by any test (only the zero-cost-off invariant and the JSON schema), and shared-GPU timing values are never recorded as claims. -
Ensemble multi-GPU execution through public
mw.MultiGPUExecution.ensemble(devices=..., placement=..., fail_fast=...)andmw.run_many(simulations, execution=...): a set of independentSimulationobjects is distributed across CUDA devices and returns a submission-orderedResultSequence(results[i]is the outcome ofsimulations[i]). A deterministicDevicePoolleases the first free device in declaration order (one large solver per GPU by default) so no task runs on a non-leased device and no device is over-subscribed; each task prepares and runs in its leased device context rather than materializing a full scene on a coordinator GPU. A per-task memory-estimation preflight fails a task before it runs when it cannot fit any leased device rather than migrating after OOM. Failures are structured, order-preservingDistributedFailureentries carrying the task index, device, kind (capacity/runtime/cancelled) and original exception chain: withfail_fast=Falseother independent tasks still complete and no exception is swallowed, and there is no cross-task state or gradient bleed. RF N-port sweeps reuse the same executor — aPortSweepNetworkRunManifestexpands into one independent single-active-port column per port, distributed over the pool and reassembled by the same matrix assembler into an identical orderedNetworkDatamatrix as serial execution, with per-column device provenance inResult.solver_stats["ensemble"].ExecutionRecordcarries per-task wall/device timing measurement hooks, but interpreting them as a task-level speedup is reserved for an exclusive-GPU window and is not asserted. Ensemble + trainable (adjoint throughrun_many), ensemble +FDTDParallelConfigjoint solve on the sameSimulation,SceneModuleinputs, and two tasks sharing oneSceneobject fail explicitly -
Single- or multi-frequency DFT extraction through
frequency=orfrequencies=[...] -
Source temporal frequency (
source_time.frequency) remains distinct from simulation / monitor extraction frequencies; Maxwell does not infer extraction frequencies implicitly -
ADE-based electric and magnetic dispersive-material updates for Debye, Drude, and Lorentz media
-
Static isotropic or diagonal electric conductivity (
sigma_e) in the time-domain update, folded into the per-component lossy-dielectricCa/Cbcoefficients via the standard semi-implicit trapezoidal scheme so simple lossy dielectrics no longer require a fitted Drude pole -
Static magnetic conductivity
Material(sigma_m=...)[Ohm/m], the magnetic dual ofsigma_e: the magnetic conduction currentsigma_m * Hon Faraday's law folds semi-implicitly into the per-component H-update decay/curl coefficientsDa = (1 - h)/(1 + h),Db = (dt/mu)/(1 + h)withh = 0.5 * sigma_m * dt / mu(the exact mirror of thesigma_eE-update fold), reducing to the lossless leapfrog wheresigma_m = 0and composing multiplicatively with the CPML split-field decay; a magnetically-lossy slab attenuates a plane wave at the analytic ratealpha = (omega/c) Im(sqrt(1 + i sigma_m/(omega mu0)))(within 2%), and a matched lossy layer satisfying the impedance-match conditionsigma_m/mu0 = sigma_e/eps0reflects below a same-thickness PML baseline (an unmatched electric-only layer of the same thickness reflects more).sigma_mfolds into eps-independent H coefficients that the reverse replay already reads, so it is transparent to the FDTD adjoint (an eps design differentiates alongside asigma_mstructure); FDFD and Tidy3D export reject it (magnetically-lossy media are outside their model) rather than silently dropping it -
Axis-aligned diagonal anisotropy for electric and magnetic material tensors on the Yee grid, plus native CUDA full-tensor electric-permittivity corrections for symmetric positive-definite
Tensor3x3media; a diagonal-anisotropicepsilon_tensorcombines with electric ADE dispersive poles in the same material, the per-axiseps_Ex/Ey/Ezbackground feeding each axis's shared pole susceptibility so the effective per-axis permittivity iseps_inf_i + chi(omega), and symmetrically a diagonal-anisotropicmu_tensorcombines with magnetic ADE dispersive poles, the per-axismu_Hx/Hy/Hzbackground feeding each axis's shared magnetic pole so the effective per-axis permeability ismu_inf_i + chi_m(omega) -
Nonuniform (
GridSpec.custom) Yee grids run on the single native CUDA kernel path through per-axis primal/dual spacing arrays, so a uniform grid is just the constant-array special case (aGridSpec.custombuilt from a uniform scene's node masters reproduces theGridSpec.uniformrun bitwise); covers all field-update variants (standard, CPML dense/compressed, Bloch, mixed Bloch+PML), physical-depth-graded CPML profiles with per-side layer thicknesses, per-face local-cell Mur deltas, physically placed sources and monitors via the float64 coordinate masters, anddtfrom the per-axis minimum spacing -
The reverse-time FDTD adjoint is the exact transpose of the nonuniform forward stencils (each reverse term reuses the same per-axis spacing element as its forward counterpart), so design-region gradients work unchanged on graded meshes
-
Instantaneous isotropic electric Kerr nonlinearity with GPU-resident dynamic update coefficients
-
General instantaneous nonlinearity through a dedicated native CUDA coefficient kernel: when a
chi2or two-photon-absorption channel is present the per-step pre-pass recomposes both the decay and curl coefficients fromeps_eff = eps_lin + eps0 * (chi2 * E_i + chi3 * |E|^2)andsigma = sigma_static + tpa_sigma * |E|^2through the semi-implicit lossy scheme (collocating off-axis field components onto each Yee edge exactly like the Kerr path, and reproducing the static coefficients exactly when the nonlinear channels vanish); pure-chi3 scenes keep the existing curl-only Kerr fast path -
When an instantaneous nonlinearity and electric ADE dispersion coexist (same or separate structures), the per-step ADE polarization-current subtraction reuses each Yee edge's field-dependent dynamic curl coefficient instead of the static inverse permittivity, so the dispersive current is divided by the same effective permittivity
eps_effthe nonlinear displacement-current term used that step (identical to the lineardt / eps_linsubtraction wherever the nonlinear channels vanish); this makeschi2second-harmonic generation phase-match correctly through the material's ownn(w)/n(2w)dispersion -
Automatic run length estimation with
TimeConfig.auto(...) -
Explicit run-step control with
TimeConfig(time_steps=...) -
Opt-in auto-shutoff early termination stops the forward loop once relative electric-field energy decays below
Simulation.fdtd(..., shutoff=..., shutoff_check_interval=100)(defaultshutoff=0.0disables it), with a settling/DFT/observer-aware floor so continuously driven CW and spectral runs are never truncated, plus a planned-window normalization restore so an early-stopped run's DFT/monitor spectra match the full-length run;Result.stats()reportsshutoff,shutoff_triggered,shutoff_step, andsteps_run -
Automatic
dttightening for broadband source-time objects such asGaussianPulseandRickerWavelet, and for electric or magnetic dispersive material poles such asDrude,Debye, andLorentzmedia -
CPML absorber configuration through
BoundarySpec.pml(...)plus typedSimulation.fdtd(...)config (absorber=...,cpml_config=...); the propagating-wave default is an impedance-matched cubic conductivity ramp (kappa=1,alpha=0, target reflection1e-6) calibrated for the common 8-12-cell layers, while custom complex-frequency stretching andstablepmlremain available for low-frequency or late-time-stability workloads -
CPML auxiliary
psistorage auto-selects between a dense fast path and slab-allocated low-memory storage, withcpml_config={"memory_mode": "auto"|"dense"|"slab"}and optionaldense_memory_limit_mibtuning -
Additional PML-face absorber variants selectable through
Simulation.fdtd(..., absorber=...):"absorber"(adiabatic graded-conductivity layer with no auxiliarypsimemory) and"stablepml"(a CPML profile tuned for late-time stability with a higher grading order and larger complex-frequency shift), alongside the existing"cpml"and"pml"options -
First-order Mur absorbing boundary as a per-face
BoundarySpec.mur()kind, applied by a native CUDA kernel after the electric update that updates persistent per-face boundary buffers in place (no per-step host arithmetic or allocation, so it is captured into the tail CUDA graph) -
Non-absorbing FDTD boundary conditions: periodic, Bloch phase-shifted periodic, PEC, and PMC
-
Per-face FDTD boundary selection across
pml,periodic,pec,pmc, andnone, including mixed-axis combinations such as periodic-in-yplus PML-in-x/z -
Grating-oriented
PlaneWaveTFSF slab forward stepping on any normal axis (the single PML axis with the two transverse axes Bloch), under both CW and broadband (GaussianPulse/RickerWavelet) source times, including explicit Bloch wavevectors and solver-resolved automatic Bloch phase from the incident CW plane wave -
Mixed Bloch + CPML FDTD stepping and native CUDA adjoints support every single-PML-axis / two-Bloch-axes permutation (absorbing axis
x,y, orz). The complex reverse propagates both Bloch phase wraps and CPML recursive state, and the any-axis grating TFSF reverse composes the same native specialization with its auxiliary-state pullback; material gradients are validated against central differences for non-z as well as z layouts -
Any-axis / either-face domain symmetry (PEC/PMC image plane on the chosen
loworhighface) combined with absorbing boundaries throughScene(symmetry=...)withBoundarySpec.pml(...); point-like sources in the folded-away half are rejected with a clear error -
Spectral window and normalization configuration through
SpectralSampler(window=..., normalize_source=...) -
Pulse-driven spectral extraction starts at the transient without steady-state apodization for
GaussianPulseandRickerWavelet, while CW extraction still skips startup transients -
Optional prepared execution via
Simulation.prepare() -
Optional full-field DFT output, including simultaneous accumulation of multiple target frequencies in one run
-
Single-frequency FDTD runs that do not request full-field DFT now return the last-step Yee fields instead of auto-enabling full-domain DFT work
-
Full-field FDTD DFT results stay in native PyTorch tensors through
Simulation/Result, with NumPy conversion deferred to plotting, export, and validation boundaries;Result.stats()reports full-field and observer DFT sample counts alongside elapsed time, ms/step, and steps/s -
Selective monitor/observer extraction for point and plane monitors
-
Simultaneous multi-frequency point / plane observer accumulation in one run
-
Point, plane, and flux monitor payloads stay torch-native through
Result.monitor(...), including multi-frequency selection -
First-class modal monitor and port results remain torch-native through
Result.monitor(...), while the underlying raw plane payload remains available throughResult.raw_monitor(...) -
GPU-accelerated soft source injection for
PointDipole,PlaneWave,GaussianBeam, and experimentalModeSource -
Reverse-time FDTD adjoints for trainable material, density, and supported analytic-geometry inputs through
Scene/SceneModule -> Simulation -> Result, including point/surface/current/field/mode sources, TFSF sources, electric conductivity, diagonal and supported full electric anisotropy, general instantaneouschi2/chi3/two-photon-absorption media, and electric plus magnetic Debye/Drude/Lorentz ADE state. Magnetic ADE carries both real and imaginary state under Bloch boundaries. Multiple sources retain independent waveform replay for multi-port objectives -
Internal checkpoint/replay support for adjoint-enabled FDTD runs, including CPML auxiliary state replay, TFSF auxiliary-line replay, and Bloch real/imag field checkpoints
-
Adjoint gradient pullback from Yee-grid coefficients through
Scene.compile_material_components()into trainable material-graph inputs, including exact per-axis attribution for diagonal anisotropy and native CUDA pullback of supported off-diagonal electric coupling; trainable geometry on the full tensor structure remains guarded because the off-diagonal coefficients have no geometry-gradient channel -
Solver stats including time steps,
dt, absorber, requested frequencies, per-frequency DFT sample counts, elapsed time, milliseconds per step, and steps per second -
Native CUDA extension builds on Windows can discover and load the Visual Studio x64 build environment automatically for accelerated FDTD kernels
-
Native CUDA extension platform wheels include the packaged FDTD CUDA extension, so ordinary
pip install witwin-maxwellusers can load the accelerated FDTD runtime without compiling locally -
Native CUDA extension builds resolve conda-distributed torch import libraries automatically, and
WITWIN_MAXWELL_FDTD_CUDA_PREBUILT=1loads an already-built extension from the configured build directory without invoking the build toolchain (required under profilers such as Nsight Systems) -
Native CUDA CPML field updates skip full-volume coefficient reads when the decay/curl coefficient tensors are spatially uniform, detected automatically once per solve (about 1.4x faster forward stepping on homogeneous scenes)
-
Default-on CUDA-graph capture of the per-step FDTD work (
Simulation.fdtd(..., cuda_graph=...), defaultTrue; passcuda_graph=Falseto force eager stepping): the magnetic/electric Yee updates (with CPML) are captured as one graph, and the post-source tail (PEC clamp, Mur ABC, and a GPU-driven running DFT) as a second graph, collapsing per-step kernel-launch and host overhead. The running DFT is driven from a precomputed device weight table indexed by a device step counter (no per-step host arithmetic or host->device transfer), so it captures cleanly and stays bit-identical to the host path (single- and multi-frequency); this GPU table path now also drives eager (non-captured) runs whenever the table can be built, with the per-step host path kept only for the complex-field (split-field Bloch) DFT the table cannot express. Capture now extends past plain real-field scenes to every per-step-deterministic field update, each proven bit-exact against the eager path: linear electric and magnetic ADE dispersion (Debye/Drude/Lorentz eps-poles and mu-poles, whose pole buffers stay a fixed point at zero field through warmup), instantaneous nonlinearity (Kerrchi3,chi2, and two-photon absorption, whose dynamic curl/decay coefficients recompute in place from the live field with no host input), complex split-field Bloch scenes including the mixed Bloch+CPML layout (one PML axis, two Bloch axes, imaginary fields and their psi memory snapshotted alongside the real path), and reference-provider TFSF plane-wave scenes (plane_wave_axis_aligned/plane_wave_ref_x_ezplus the non-periodic normal-incidence slab, which read the device-resident auxiliary incident line at fixed integer indices while the host waveform is evaluated eagerly outside the captured block). The classes that stay eager decline for a stated reason rather than silently: the oblique interpolated-aux plane wave (plane_wave_aux) accumulates through non-deterministic cross-warp atomic adds, so bit-exact capture is impossible; host-waveform sources (the discrete-CW / analytic-beam / grating-CW providers and soft magnetic surface sources) evaluate the incident waveform on the host each step and inject it as a launch scalar a captured graph would freeze; and time-modulated scenes carry per-step host phase scalars. Byte-for-byte identical to the eager path when declined (the adjoint bridge runs its own checkpointed forward loop and never setsuse_cuda_graph), and multi-GPU (parallel=) runs always keep eager stepping regardless of the default. Concurrent ensemble execution keeps eager stepping too: CUDA-graph capture is a process-global operation (one capture at a time, and PyTorch's default capture mode aborts synchronizing calls in every other thread), so anyexecute_plan/run_many/ ensemble network sweep whose plan can run more than one task at a time suspends capture for the whole plan, while a plan limited to one task at a time keeps the graph default. Capture is bound to the solver's own device, so runs on a GPU other than the process default capture and replay correctly rather than recording an empty graph. Most effective in the launch-bound small/adaptive-grid regime (measured ~8.25x/step on an 18^3 launch-bound dispersiveGridSpec.autoscene on an RTX 5080, 0.338 -> 0.041 ms/step, graph-on bit-exact, and negligible on large compute-bound uniform grids). On a plain uniform vacuum scene on an RTX A6000 the same shape is measured end to end (docs/assessments/cuda-graph-throughput-2026-07-22.json): +106.8% at 48^3, +2.70% at 64^3, +1.60% at 96^3, +0.94% at 128^3, +0.55% at 160^3, +0.12% at 288^3, with peak allocated memory +8.1% to +21.1%; eager stepping bottoms out at a CPU launch-bound floor of ~0.165 ms/step, so everything at or above 64^3 is GPU-bound and the graph gain there is under 3%;Result.stats()["cuda_graph_active"]/["tail_graph_active"]report whether capture engaged -
Differentiable FDTD is CUDA-only: preparation requires a CUDA scene and the packaged compiled extension, selects one internal native specialization, and fails before forward stepping when the capability is unavailable. There is no adjoint backend environment selector, analytic Torch reverse, autograd-VJP fallback, or CPU differentiation path
-
Native reverse specializations cover standard, CPML, conductive, nonlinear, anisotropic, Bloch, electric/magnetic ADE, mixed Bloch+CPML, TFSF, and grating-TFSF configurations. Python remains responsible only for checkpoint/replay orchestration, kernel launches, gradient ownership, and PyTorch autograd integration; per-cell reverse numerical operators execute in compiled CUDA kernels and accept strided tensor views
-
FDTD result stats include CPML auxiliary-memory mode and allocated-versus-dense
psibyte counts -
Support for odd grid sizes in Yee-component field outputs
- The experimental multi-GPU FDTD joint-solve adjoint for a trainable
BoxMaterialRegiondensity now extends past the open/PEC standard path to the CPML absorbing update (this supersedes the earlier "any absorbing PML boundary rejected" envelope for the CPML/stable-PML families). A trainable-densitySimulation.fdtd(..., parallel=..., absorber="cpml")(or"stablepml") scene reverses through the distributed bridge with psi-carrying forward replay half-steps and a psi-aware reverse loop (CPML electric phase, transposed magnetic halo, CPML magnetic phase, transposed electric halo, then the per-shard source-term eps gradient), carrying the twelve CPMLpsimemory cotangents seeded to zero and using nopsihalo: the x-CPML regions are pinned to the outer shards (asserted at prepare) so every cross-interface curl coupling rides the existing Yee field halos through theadj_dfolds. Two-GPU acceptance covers 1-vs-2-GPU objective (bit-identical) and gradient parity (rel ~1e-7), cross-shard interface finite differences, a load-bearing no-psi-halo confirmation (no-op'ing either field halo drives the parity gate red), and bitwise-reproducible gatheredgrad_eps. Only the legacy graded-sigma absorbers ("pml"/"absorber") stay rejected for a trainable distributed run, and dispersive/conductive/nonlinear/anisotropic/modulated media, field shutoff, multi-source normalization, non-Box density regions, and tiled plane/flux/mode-seeded objectives remain rejected at prepare before any distributed allocation - The
DistributedFDTDconstructor's static-capability guard fails closed on every trainable channel it cannot reverse, as defense in depth, even when the solver is constructed directly and the publicSimulationvalidator is bypassed: trainable geometry/material-perturbation/circuit/RF-port parameters and a trainableBoxdensity resolving to a graded-sigma absorber all raise at construction (before any hardware allocation) rather than run a forward whose backward the reverse-support guard would later refuse. The same rejections are enforced at the publicSimulation.fdtd(..., parallel=...)boundary - Forward one-process distributed monitor gather assembles supported monitor payloads on
result_devicewithout materializing a global field. The tiled-plane seam ownership rule is owned-exclusive: each global x index belongs to exactly one shard'sowned_global_slice, so each shard contributes only its owned strip (halo/ghost x cells cropped away by the owned-local slice) and the seam sample between two shards is assembled exactly once; the flux quadrature is recomputed on the result device from the merged owned components (cell-center weights derived from the reassembled global coordinates) and cropped to the physical domain, so no seam cell weight is double-counted. Point monitors and x-normal planes have a single owner; y/z-normalPlaneMonitor/FinitePlaneMonitor/FluxMonitor/ModeMonitorplanes tile along the x split. A double-counted owned strip is rejected by the owned-slice overlap guard (with the strictly-increasing-coordinate guard as a second line). Closed-surface, diffraction, time-domain flux, non-point time, material (permittivity/medium), and breakdown monitors remain fail-closed
- Structured field lookup through
result.E.x,result.E.y,result.H.z, and frequency/symmetry-scoped result views fromResult.at(...) - Monitor lookup through
Result.monitor(...) - Raw monitor lookup through
Result.raw_monitor(...) Result.monitor(...)andResult.raw_monitor(...)reassemble first-classClosedSurfaceMonitorpayloads from the resolved finite face monitors, and finite-plane payloads expose cropped coordinates plus face metadata- Frequency selection on structured field and monitor views through
Result.at(frequency=...)orResult.at(freq_index=...) - Structured material lookup through
result.materials.eps.scalar,result.materials.eps.x, andresult.materials.mu.z - Low-level tensor escape hatches through
Result.tensor(...)andResult.material(...) - Optional symmetry-expanded field and material access via
Result.at(expand_symmetry=True) - Summary metadata through
Result.stats() - Field and material plotting through
Result.plot.field(...)andResult.plot.material(...) - Serialized save via
Result.save(...) - Access to raw backend output through
Result.raw_output
maxwell.postprocess.PlanarEquivalentCurrentsfor planar Huygens-surface current datamaxwell.postprocess.EquivalentCurrentsSurfacefor multi-plane or closed-surface Huygens current collectionsmaxwell.postprocess.equivalent_surface_currents_from_fields(...)to build equivalent currents from tangentialE/Hplane fieldsmaxwell.postprocess.equivalent_surface_currents_from_monitor(...)to bridge a plane or first-classClosedSurfaceMonitoronResultdirectly into planar or multi-surface equivalent currentsmaxwell.postprocess.equivalent_surface_currents_from_monitors(...)to assemble multi-plane or closed-surface Huygens currents directly from multiplePlaneMonitoroutputs, with shared or per-monitor tangential cropping for finite closed surfacesmaxwell.postprocess.SurfaceEquivalentCurrentsandmaxwell.postprocess.equivalent_surface_currents_from_surface_samples(points, normals, areas, E, H, ...)build equivalent currents on a genuinely curved (arbitrary-normal) Huygens surface -- a sphere, ellipsoid, or any voxelized/staircased closed surface -- formingJ = n_hat x H,M = -n_hat x Eper quadrature point with its own outward normal, then feed the same Stratton-Chu / NF2FF propagators as the axis-aligned box-face path (a spherical Huygens surface reproduces a Hertzian dipole far field to< 1e-3relative L2 error with radius-independent surface invariance)maxwell.postprocess.StrattonChuPropagatorfor exact near-field propagation from planar equivalent currentsStrattonChuPropagatordefaults to CUDA and requires an explicitdevice="cpu"override for CPU postprocessingmaxwell.postprocess.NearFieldFarFieldTransformerfor planar near-field to far-field projection- Background-aware closed-surface Huygens NF2FF: a box embedded in a homogeneous non-vacuum exterior auto-samples the exterior material just outside every face and radiates its equivalent currents into that medium instead of vacuum, using the background wavenumber
k_b = n_b * omega / c(n_b = sqrt(eps_r*mu_r)) and intrinsic impedanceeta_b = eta0 * sqrt(mu_r/eps_r), with the near-field Stratton-Chu kernel scalingi*omega*eps -> i*omega*eps0*eps_randi*omega*mu -> i*omega*mu0*mu_r; the vacuum path (n_b = 1) stays bit-identical, and a layered-exterior RCS matches the analytic reference within about 4% while the former vacuum kernel distorts it by more than 15% - Postprocessing outputs stay torch-native end-to-end: equivalent currents, Stratton-Chu propagation, NFFT far fields, directivity, bistatic RCS, and flux-derived S-parameters preserve
torch.Tensorresults and autograd instead of detaching through NumPy maxwell.postprocess.compute_bistatic_rcs(...)andtransform_to_bistatic_rcs(...)for bistatic RCS derived from NFFT far fields and PlaneWave incident amplitudemaxwell.postprocess.compute_directivity(...)for radiation intensity, total radiated power, directivity, gain, radiation efficiency, and 3 dB beamwidth extraction from NFFT far fieldsmaxwell.postprocess.compute_s_parameters(...)for broadbandS11/S21extraction from flux-monitor results using reference-run or explicit incident-power normalizationcompute_s_parameters(incident_power="auto")now normalizes broadband (pulsedGaussianPulse/Ricker)PlaneWavesources without a second reference run: it reconstructs the injected incident spectrum by reproducing the monitor's own windowed running DFT of the known waveform and divides by the forward flux|E_inc(f)|^2 / (2*eta0) * areaper frequency, taking the illuminated cross-section from the TFSF-aperture area (box mode) or the monitor plane (soft source / full-width slab), so a single pulsed run yields per-frequency-normalized S-parameters (matching the measured incident flux within about 5-7% on a well-resolved grid)- Experimental
maxwell.postprocess.compute_mode_overlap(...)for plane-monitor or first-classModeMonitor/ModePortmodal decomposition against an axis-aligned scalarModeSourcereference, returning forward/backward modal amplitudes and power fractions - The modal S-parameter pipeline (the
compute_mode_overlapcomplex forward-amplitude extraction driving the solved guided dispersionbeta(f)) reconstructs an all-pass ring-resonatorS21spectrum against the closed-form coupled-mode transfer functionH = (t - a e^{i phi}) / (1 - t a e^{i phi})to within 3% in magnitude and phase across a full free spectral range, including on resonance (tests/postprocess/scattering/test_ring_resonator_s21.py) - Experimental
maxwell.postprocess.purcell_factor(structured, vacuum, name)for the vacuum-normalized Purcell factor / local density of states of aDipoleEmissionMonitor, validated undertests/monitors/emission/test_dipole_emission_monitor.pyagainst the analytic dipole-near-PEC-mirror curve (perpendicular orientation) and unity vacuum emission - Experimental
maxwell.postprocess.compute_diffraction_orders(...)andenumerate_diffraction_orders(...)for grating diffraction-order decomposition on a periodic unit-cell plane; regression-tested undertests/monitors/diffraction/test_diffraction_monitor.pyfor order-cutoff / light-cone physics, Parseval consistency between the summed order powers and the direct plane-flux integral, specular dominance at normal incidence, grating-driven redistribution of transmitted power into the+/-1orders, and cross-validation of the transmission/reflection coefficients against the independent Yee-gridFluxMonitorprimitive (with anxfailtripwire documenting that absoluteT + R = 1energy conservation is not recoverable under the FDTD spectral-monitor CW normalization) - End-to-end validation coverage for first-class finite closed-surface Huygens workflows in homogeneous free space: rectangular-box dipole near-field reconstruction/directivity, non-rectangular orthogonal-surface dipole directivity, and TFSF Rayleigh-sphere bistatic RCS are regression-tested against analytic or direct-FDTD references under
tests/validation/physics/test_postprocess_end_to_end_validation.py - First-class closed-surface postprocessing now rejects layered or otherwise inhomogeneous exterior media explicitly instead of silently applying a free-space Green's function outside its validity range
Material(mass_density=...)adds a tissue mass density in kg/m^3 (a positive scalar or a per-cell 3D grid spanning the structure Box extent).None(the default) excludes the material from SAR; a SAR request covering an electrically lossy material without a density fails closed. Convenience flagsMaterial.has_mass_densityandMaterial.is_electrically_lossy.Scene.compile_mass_density()rasterizes tissue mass onto material cell centers (the node grid) through the same soft-occupancy provenance the EM material compiler uses, returningCompiledMassDensity(rho_cell, occupancy, tissue_id, cell_volume, tissue_names);rho_cellis the occupancy-weighted effective density (true tissue density =rho_cell / occupancy) and the discretetissue_idlabels are stop-grad.Result.sar(monitor, averaging=..., normalization=...)reduces aPowerLossMonitor's absorbed-power density and the tissue mass model to point SAR. It is a pure result-domain reduction: it never runs a solver and fails explicitly when the named monitor, the frequency-domain fields, the tissue densities, or an electric volumetric loss channel are missing.- Point SAR consumes the shared
PowerLossDataelectric-loss volumetric density (conduction / electric_dispersion / nonlinear channels) colocated from the staggered Yee electric edges to material cell centers by a power-conserving half-weight scatter, so the region volume integral of absorbed power closes exactly against the edge-integrated channel power. SAR is reported per channel and as a total, in W/kg, with NaN at cells whose tissue fill is below a documented occupancy epsilon (no air-mass makeup, no zero-fill). SARResultcarries typed point-SAR fields, per-tissue statistics (absorbed power, mean/max SAR, tissue mass, cell count), coordinates, frequencies, the validity mass model, and full provenance (units, field/phasor convention, channel list, collocation description, grid hash, occupancy epsilon, averaging profile). Point SAR, the effective mass density, and per-tissue statistics stay in autograd (no hidden detach); discrete tissue/validity masks are stop-grad by design.SARAveraging(mass, profile, connectivity, boundary_policy, min_tissue_fraction)is the validated, serializable averaging request; the single versioned profile is"cubical-prefix-v1"(IEEE/IEC-inspired but explicitly not certified).PowerNormalization.source/.accepted_power/.input_powerdescribe multiplicative power scaling (source-amplitude scaling is available now; accepted/input-power scaling resolves in the normalization stage).SARResult.save(...)/SARResult.load(...)persist the SAR payload as typed data.
Result.sar(monitor, averaging=SARAveraging(mass=(1e-3, 10e-3)))computes local mass-averaged SAR under the"cubical-prefix-v1"profile: for each target averaging mass and every candidate tissue center cell, the smallest symmetric axis-aligned cube whose enclosed tissue mass reaches the target is selected, and the averaged SAR isenclosed_absorbed_power / enclosed_massrecorded with the ACTUAL enclosed mass. Enclosed cube quantities are read in O(1) from 3D inclusive prefix sums (integral images), so the search costs one lookup per half-width rather than an O(k^3) window sum.- Validity rules are enforced and reported as a mask (NaN, never silently padded):
boundary_policy="strict-interior"marks any center whose cube would clip the monitor region invalid; a cube whose tissue fill fraction is belowmin_tissue_fraction(default 0.1, the "no air-mass makeup" rule) is invalid; a target mass unreachable within the interior cube is invalid; averaged SAR is reported only at tissue-bearing center cells. SARResult.averaged_sar(mass)returns the[F, nx, ny, nz]mass-averaged SAR field andSARResult.averaging_masseslists the computed target masses.SARResult.peak(mass)returns a typedSARPeak(per-frequency peak value, center index and physical position, actual enclosed mass, cube half-width in cells, and physical cube edge length per axis);peak(...)andaveraged_sar(...)fail closed when no averaging request was made or an unrequested mass is asked for.- Fixed-window mass-averaged SAR (enclosed power over enclosed mass at the selected cube) stays in autograd; only the discrete half-width search and validity masks are stop-grad. The
argmaxpeak center is not differentiable by design (the reported peak value still carries the field's graph); a differentiablesoft_peaksurrogate is a later stage. "cubical-prefix-v1"differs from IEEE/IEC 62704-1 (documented, not certified): the averaging cube is a symmetric index-space cube with no cube-face expansion asymmetry, and there is no tissue-connectivity flood fill (connectivity="cube"only; other values raise). On a nonuniform grid the cube is symmetric in index space rather than a physical cube. The averaged fields and peaks ride the existingSARResult.save/loadtyped serialization.
PowerNormalization.accepted_power(port, watts)now resolves against the run: it reads the named port's measured accepted power at each SAR frequency and scales SAR bywatts / measured(a per-frequency scale that keeps the port's autograd graph). Fails closed when the port is absent, when a SAR frequency is not present in the port spectrum, or when the measured accepted power is not strictly positive.PowerNormalization.source(amplitude)scales byamplitude**2(exact square law).PowerNormalization.input_power(watts)fails closed with a clear message because this build exposes no total injected source-power diagnostic.combine_coherent_sar(results, monitor=..., weights=..., averaging=..., normalization=...)coherently combines same-frequency source runs by summing their complex electric spectra (with optional complex per-run weights) before the loss is formed, then performs a single SAR reduction, so field interference is captured exactly (in-phase sources add, opposite-phase sources cancel). Differentiable in the per-run fields; validates identical monitor, grid, and frequencies.combine_incoherent_sar(sar_results, averaging=...)combines source runs in the power domain by summing their absorbed-power densities, re-forming point SAR aspower / rho, and recomputing per-tissue statistics; passaveragingto recompute cubical-prefix-v1 mass-averaged SAR and peaks on the combined power. Validates identical grid hash, tissue model, frequencies, field convention, loss channels, and power normalization across operands and fails closed on any mismatch.SARResult.soft_peak(temperature, mass=...)is a differentiable, explicitly non-regulatory peak surrogate: a temperature-weighted softmax over the valid mass-averaged cube centers that approaches the hardpeak(...)as the temperature drops while staying in autograd (unlike theargmaxpeak). Themassargument is optional when a single averaging mass was computed.- SAR reduction provenance now records the resolved
power_scale(scalar or per-frequency), the per-axis dual cell sizes, and, for combined results, acombinationdescriptor (modecoherent/incoherent, operand count, coherent weights). Point SAR, fixed-cube averaged SAR, region statistics, and normalization are validated as differentiable in the field amplitude and in a per-cell density grid by central-difference gradient gates.
IncidentPowerDensityMonitor(name, axis=, position=, frequencies=, normal_direction=, spatial_average=)is a plane monitor variant for exposure incident power density. It carries the tangentialE/Hfields required to form the time-averaged normal Poynting componentS.n = 0.5*Re((E x conj(H)).n_hat)on the plane, reusing the same power-conserving plane-flux machinery asFluxMonitor. It reports whatever field is present at its plane; place it in the incident region (ahead of any scatterer) to read exposure incident power density.Result.incident_power_density(monitor, spatial_average=...)is a pure result-domain reduction returning a typedIncidentPowerDensity: the signed per-cellnormal_poynting, its magnitudepower_density = |S.n|in W/m^2, and the plane-integratedflux(W) that is identically equal to the co-locatedFluxMonitorintegral (both use the sharedplane_normal_poyntinghelper). Fails closed when the named monitor is not anIncidentPowerDensityMonitor. Plane-wave gates verify|S| = |E|^2/(2*eta)exactly.spatial_average(a moving-window area in m^2, e.g.4e-4for a 4 cm^2-class window) requests an area-weighted moving-window average of|S.n|under the versionedspatial-average-v1window: an axis-aligned square of sidesqrt(area)centred on each cell, computed in O(N) from 2D inclusive prefix sums, truncated to in-domain cells at the plane edge (edge_policy="truncate"). The window is an engineering convenience for exposure reporting and is explicitly not a certified standards averaging area; its area, side, shape, edge policy, andcertified: Falseare recorded in the result provenance.
- A redistributable canonical-geometry SAR benchmark family under
benchmark/scenes/sar/(uniform_lossy_cube,layered_slab,one_gram_cube,antenna_near_phantom), each exposing abuild_scene(...)builder plus aScenarioDefinition. Tissue dielectric/mass values are the published-class canonical numbers (900 MHz) documented in_tissue.py; no licensed anatomical model is distributed. Gates: the syntheticone_gram_cubeis a hand-computable 1 g average (27 cells weigh exactly 1 g; averaged SAR equals the analytic point SAR); the plane-waveuniform_lossy_cubeandlayered_slabruns check exact absorbed-power conservation closure, monotone mass averaging (10 g peak <= 1 g peak <= point peak), the peak sitting in the front skin layer (not low-loss fat), and golden regression anchors. - Recorded design blocker:
antenna_near_phantom(a driven half-wave dipole near a tissue block) cannot run in the current build because the FDTD port machinery fails closed on a conductive (lossy) background — both the thin-wire runtime and the lumped-port runtime require a conductance-aware update coefficient that does not yet exist. The scene ships with its fail-closed behaviour gated; the accepted-power -> SAR -> 1 g/10 g -> save/load chain is validated on synthetic ports and on the plane-wave phantom scenes.
benchmark.scenes.sar.layered_slab.build_conservation_scene(dx=..., device=...)is a periodic-transverse variant of the layered slab for a clean 1-D power-conservation balance. Under normal incidence on an infinite planar slab the physically correct transverse boundary is periodic (not PML), which makes the field transverse-uniform so the net power balance reduces to two z-planes:P_absorbed = flux(z_in, +z) - flux(z_out, +z). The absorbed power measured this way (surfaceE x H) is independent of the volumesigma |E|^2integral that SAR is built from, so their agreement is a wave-level conservation check rather than a self-consistency identity. The shippedbuild_scene(PML on every face, golden-anchored) is unchanged.- A three-grid convergence / conservation suite (
tests/sar/test_phantom_convergence.py) verifies the layered-slab surface-vs-volume absorbed-power closure (residual ~17% at dx=4 mm, shrinking monotonically to ~12.5% at dx=3 mm) and records the pointwise peak 1 g / 10 g SAR at three grids. The peak carries documented grid sensitivity (a source-normalized plane wave delivers a grid-dependent incident power density, and the peak is a pointwise max over a thin under-resolved skin layer), so the convergent, gate-bearing observable is the conservation closure, not the pointwise peak. python -m benchmark sar [scenes...]runs the SAR phantom exposure validation harness (benchmark/sar_validation.py): it drives the phantom family through the publicScene -> Simulation -> Resultpath and writes a## SAR exposure validationsection tobenchmark/RESULTS.mdplus a machine-readable JSON artifact per scene underdocs/assessments/sar-phantom-validation/. Each row self-labels its gate class with the verbatimdocs/reference/gate-classification.mdtaxonomy:sar/layered_slabis the bindingwave-levelconservation gate,sar/one_gram_cubeis ananalytic-identity,sar/uniform_lossy_cubeis a supporting self-consistencyanalytic-identity, andsar/antenna_near_phantomis reportedblocked. No external reference-solver run backs this family (every rowanalytic-only); the conservation law and analytic values are the binding first-line references.
Scene.to_tidy3d(frequencies=..., run_time=..., **kwargs)converts a maxwell Scene to atidy3d.Simulationfor cloud execution and cross-validation- Adapter module at
maxwell/adapters/tidy3d.pywith optionaltidy3dimport GaussianPulseexport reproduces Maxwell's delayed real waveform exactly: the envelope delay maps to Tidy3D'soffset, DC removal is disabled, and the carrier phase is converted as2*pi*f*delay - phase - pi/2to account for Tidy3D'sRe[i exp(i*phase) exp(-i*omega*t)]convention.CWexport supplies a deterministicfwidth = 0.1 * frequencyturn-on bandwidth so the Tidy3D source has a stable finite ramp that is tracked by benchmark cache keysPlaneWaveexport places the source inside the physical interior and uses Tidy3D's infinite-plane aperture convention instead of a finite rectangular source patch- Point and plane field-monitor export preserves the exact public
fields=(...)component selection instead of accepting Tidy3D's default six-component payload - TFSF export preserves physical incident-field amplitude: Maxwell's
SourceTime.amplitudeis a V/m electric field, while Tidy3D'sTFSFhas a fixed 1 W/um^2 incident normalization, so the adapter converts through the correspondingsqrt(2/(c*eps0))V/um field rather than treating the two amplitudes as dimensionless equivalents - Supported mappings: Domain, GridSpec, BoundarySpec (uniform or per-face PML/periodic/PEC/PMC/Bloch export), Structure (Box/Sphere/Cylinder/Cone), spatially-uniform unfiltered
MaterialRegionwithmu_r = 1, Material (simple conductive / Drude / Lorentz / Debye / mixed PoleResidue withmu_r = 1, plusMaterial.pec()mapped to Tidy3D's dedicatedPECMedium,Medium2D/Graphenesheets mapped to Tidy3D'sMedium2D, andLossyMetalMediummapped to Tidy3D'sLossyMetalMedium), PointDipole, PlaneWave, GaussianBeam, ModePort (through its resolved ModeSource/ModeMonitor), PointMonitor, PlaneMonitor, FinitePlaneMonitor, FluxMonitor, and translation-invariant symmetry Coneexport preserves the shared primitive's apex-based placement: the Tidy3D tapered cylinder is centered half a height above the apex, expands toward the positive axis from a top-reference radius, and rotated cones fall back to an exact triangle-mesh export- Explicit Bloch-boundary export converts Maxwell's physical wavevector components in radians/metre to Tidy3D's dimensionless
k * period / (2*pi)convention independently on each axis; unresolved automatic Bloch metadata is still rejected rather than exported with an ambiguous period - A dispersive
Material(Debye / Drude / Lorentz / mixed poles) that also carries a static electric conductivitysigma_eexports as a single Tidy3DPoleResidue: the dispersion is taken from the equivalent pole-residue of the base dispersive medium and the conductivity is folded in as a zero-frequency pole with residuesigma_e / (2 * eps0), which reproduces the physical loss term+i * sigma_e / (omega * eps0)under Tidy3D'se^{-i*omega*t}convention (validated againstMaterial.relative_permittivityand the analytic conductivity term viaeps_model) - Electric conductivity export uses the correct Tidy3D micrometre units: the SI
sigma_e[S/m] is divided by the metre-to-Tidy3D length scale soMedium.conductivity[S/um] yields the physicaleps'' = sigma_e / (omega * eps0) - Instantaneous Kerr (
chi3) and two-photon-absorption nonlinearity export onto Tidy3D's chi3-familyNonlinearSpec:kerr_chi3/NonlinearSusceptibility(chi3=...)map totd.NonlinearSusceptibilityandTwoPhotonAbsorption(beta, n0)maps totd.TwoPhotonAbsorption, attached via the medium'snonlinear_spec(also on the dispersiveDrude/Lorentz/Debye/PoleResidueexport so a same-material nonlinear + dispersive, optionally conductive, medium exports as one Tidy3D medium). Both frameworks shareP_NL = eps0 * chi3 * |E|^2 * E, sochi3[m^2/V^2] scales bylength_scale^2to Tidy3D's [um^2/V^2] (verified via Tidy3D's ownn2 = 3/(4 n0^2 eps0 c0) chi3relation reproducing the SIn2in [um^2/W]), TPAbeta[m/W] scales bylength_scaleto [um/W], andn0(defaulting tosqrt(eps_r)) is forwarded explicitly. Second-order (chi2/ SHG) susceptibility is rejected with a physics-worded error because Tidy3D's public nonlinear API is the chi3/Kerr/TPA family only - Anisotropic
Materialexport: an axis-alignedDiagonalTensor3epsilon_tensormaps to a Tidy3DAnisotropicMediumof three per-axis isotropic media, carrying per-axis conductivity (scalarsigma_eor aDiagonalTensor3 sigma_e_tensor, divided by the metre-to-Tidy3D length scale) and isotropic electric dispersion (the sharedDebye/Drude/Lorentz/mixed poles reused per axis over the per-axiseps_infbackground), verified against the physical per-axis permittivityeps_axis + i * sigma_axis / (omega * eps0)viaAnisotropicMedium.eps_diagonal; a symmetric-positive-definite full off-diagonalTensor3x3epsilon_tensormaps row-for-row to a non-dispersiveFullyAnisotropicMediumwith a scaled diagonal conductivity tensor. A magneticmu_tensorand a full off-diagonal tensor combined with dispersive poles are rejected with physics-worded errors (Tidy3D anisotropic media are electric-only withmu_r = 1, andFullyAnisotropicMediumis strictly non-dispersive) Medium2Dsheet export: a zero-thicknessMedium2Dmaps to a Tidy3DMedium2Dwhose two tangential surface media reproduceMedium2D.sheet_conductivity(omega)exactly. A surface conductance is in siemens [S], unit-system independent, so it is NOT length-scaled: a static sheet maps totd.Medium(conductivity=sigma_s); theGrapheneintraband Kubo channel (a single Drude sheet termweight/(rate - i*omega)) maps to atd.Drudewithplasma_frequency = sqrt(weight/eps0),gamma = rate; and any combination (static + Drude + fitted interband Lorentz sheet terms) folds into onetd.PoleResidue. The exportedMedium2D.sigma_model(omega)is validated to equalsheet_conductivity_at_freqfor static,Grapheneintraband (inductiveIm(sigma) > 0), andGrapheneinterband (below-edge capacitiveIm(sigma) < 0) sheetsLossyMetalMediumexport maps to Tidy3D'sLossyMetalMedium(surface-impedance boundary condition): the SI bulkconductivity[S/m] is divided by the metre-to-Tidy3D length scale to[S/um](the volumetricsigma_econvention), so the exported Leontovich surface impedanceZ_s(omega) = (1 - i) * sqrt(omega*mu0/(2*sigma))[ohm] matchesLossyMetalMedium.surface_impedance(validated against the real Tidy3D fit, including the inductiveIm(Z_s) < 0sign). Tidy3D vector-fitsZ_s(omega)over a requiredfrequency_range, so the export frequencies must be passed toScene.to_tidy3d()(a single operating frequency is widened into a non-degenerate fit band); omitting them raises a clearValueError- Time-modulated
Materialexport: a scalar-depthModulationSpec(frequency, amplitude, phase)on a non-dispersive isotropic medium maps to a Tidy3DMedium(modulation_spec=ModulationSpec(permittivity=SpaceTimeModulation(...))). maxwell's dimensionless depth is converted to Tidy3D's absolute permittivity deviation viaA_s = eps_static * amplitude(Tidy3D'sSpaceModulationamplitude is an absolutedelta_eps, not a fraction), the modulation frequency becomes theContinuousWaveTimeModulation.freq0, and the phase is negated (phi_s = -phase) because Tidy3D'se^{-i*omega*t}time factor is the conjugate of maxwell's+phaseconvention; the exporteddelta_eps(t) = eps_static * amplitude * cos(2*pi*f*t + phase)is validated against the real Tidy3Damp_time(t)/amp_spaceat multiple times (and the depth< 0.5cap keeps the modulated permittivity positive for Tidy3D's own validator). A modulatedMaterialthat also carries dispersion or an instantaneous nonlinearity is rejected with a physics-worded error (Tidy3D modulates only the non-dispersiveeps_inf/conductivity, whereas maxwell folds the same per-step factor through the dispersive polarization current and the Kerr/chi2/TPA coefficient), as is a spatially-varying (3D-tensor) amplitude/phase profile (defined relative to the owning structure'sBox, not resolvable into Tidy3D absolute coordinates at material-conversion time) - Custom dispersive pole and
PerturbationMediumexport: a spatially-uniform custom pole (CustomDebyePole/CustomDrudePole/CustomLorentzPole) lowers to its scalar reference pole (the peak equals the uniform value, so the lowering is exact) and exports through the ordinaryDrude/Lorentz/Debye/PoleResiduepath; a spatially-uniformPerturbationMediumlowers to a constant background shifteps_base + eps_sensitivity * valueapplied to the scalareps_r(or to eachDiagonalTensor3principal axis), carrying the base's poles/conductivity/nonlinearity/modulation through unchanged (validated so the exportedeps_modelreproduces the equivalent scalar-poleMaterial.relative_permittivity, and the perturbed background shift is exactlyeps_sensitivity * valueagainst the unperturbed export). A spatially-varying custom pole or perturbation has no homogeneous Tidy3D equivalent (its per-cell grid is defined relative to the owning structure'sBoxand cannot be resolved into Tidy3D absolute coordinates at material-conversion time, which would need aCustomPoleResidue/CustomMediumon aSpatialDataArray) and is rejected with a physics-worded error - Geometry export beyond the analytic primitives:
Ellipsoid(non-spherical),Torus,Pyramid,Prism, andHollowBoxtessellate to a Tidy3DTriangleMeshbuilt from the primitive's ownto_mesh()surface (vertices carry the structure position/rotation and scale by the metre-to-Tidy3D length factor; the integer face table is length-scale-invariant), so a primitive with no analytic Tidy3D counterpart round-trips geometrically instead of raising. An isotropicEllipsoidstill exports as Tidy3D's exact analyticSphere. APolySlabthat is un-rotated, origin-centred, and vertical (sidewall_angle = 0) maps to the faithful Tidy3DPolySlabprimitive (polygon vertices + axialslab_bounds+axis+reference_plane, all length-scaled); a tapered, rotated, or offset slab is baked into aTriangleMesh(itsto_meshalready applies the taper and transform) so no geometry is silently dropped - Source export beyond point/plane/beam sources:
ModeSourcemaps to Tidy3D'sModeSourcewith aModeSpecthat requests both polarization families throughmode_indexand applies aModeSortSpecderived from the requested tangential polarization fraction;AstigmaticGaussianBeammaps totd.AstigmaticGaussianBeamat the same launch plane and waist distances;UniformCurrentSource,CustomFieldSource, andCustomCurrentSourcecarry the required SI-to-micrometre amplitude scaling; and aPlaneWave/GaussianBeaminjected asTFSF(...)maps to Tidy3D's dedicatedtd.TFSF - Custom source export:
CustomFieldSourceandCustomCurrentSourcemap to Tidy3D'sCustomFieldSource/CustomCurrentSourcewith aFieldDatasetofScalarFieldDataArraycomponents on coordinates relative to the source center and a single injection frequency (taken from the attachedsource_time), matching Tidy3D's convention; field sources keep the tangentialE/Hcomponent names, while current sources reuse Tidy3D'sE/Hdataset slots forJ/M(Jx..Jz -> Ex..Ez,Mx..Mz -> Hx..Hz), validated against the real Tidy3D validators (single-frequency, interpolatable) and by reconstructing the maxwell absolute coordinates from center + relative offset - Nonuniform grid export: a
GridSpec.customorGridSpec.autoscene grid exports as per-axis Tidy3DCustomGridBoundariescarrying the exact Yee node coordinates (metre coordinates scaled by the metre-to-Tidy3D length factor), so the Tidy3D simulation discretizes on the same grid maxwell uses and the two solvers compare cell-for-cell instead of across two independent meshers. AGridSpec.autogrid is first resolved through maxwell's own mesher (resolve_auto_grid), which honours the index-aware step targets,override_structures, andlayer_refinementthat a lossy parameter map onto Tidy3D'sAutoGridwould drop; the resolved nodes reproduce the maxwell mesh exactly. Uniform / per-axis-anisotropic grids continue to export astd.UniformGrid. Verified against the real Tidy3DSimulation.grid.boundaries: the in-domain Yee boundaries equal the exported node coordinates (Tidy3D adds PML cells only outside the domain) - Monitor export beyond point/plane/flux monitors:
ModeMonitormaps to Tidy3D's dedicatedModeMonitorwith aModeSpecthat resolves both polarization families and applies the requested polarization-fraction ordering throughModeSortSpec;DiffractionMonitormaps totd.DiffractionMonitorwithtd.inftransverse extent;PermittivityMonitormaps totd.PermittivityMonitor; and time-domain field/flux monitors map without requiring export frequencies. Maxwell time-monitorstart/stopstep indices are converted to physical seconds using the exported scene's Courant step, whileintervalremains a sample-step stride
maxwell.adapters.gds.from_gds(path, layer=..., bounds=..., ...)imports GDS layout polygons asPolySlabgeometries with cell selection (explicit name or unique top-level cell), layer/datatype filtering, recursive reference flattening, extrusion parameters (axis,bounds,sidewall_angle,reference_plane), and unit conversion from the file's user unit to metres (overridable vialength_scale)maxwell.adapters.gds.to_gds_file(geometries, path, ...)writesPolySlab/ComplexPolySlabcross-sections as GDS polygons on a chosen layer/datatype (multi-loop geometry is even-odd merged so holes survive as keyhole cuts), with the library unit derived fromlength_scale(micrometre user units by default)- Adapter module at
maxwell/adapters/gds.pywith optionalgdstkimport
benchmark/package as the unified Maxwell-vs-Tidy3D benchmarking entrypoint- Benchmark material/source diagnostics sample the actual Tidy3D-exported geometry envelope on Maxwell's grid and plot its XOR against the public geometry, rather than labeling a cloned Maxwell material grid as Tidy3D; incident-power normalization uses boundary-matched PML or transverse-periodic vacuum references and scales physical
FluxDataby the plane-wave source amplitude squared - One-file-per-scenario definitions organized under
benchmark/scenes/dipole/,benchmark/scenes/planewave/, andbenchmark/scenes/media/, with the campaign scenarios inbenchmark/scenes/planned.pyand the per-family feature-coverage scenarios inbenchmark/scenes/coverage/ - Predefined ~128^3-grid benchmark scenarios covering vacuum dipoles, plane-wave slab/sphere scattering, dispersive resonators, multi-dielectric scenes, and
dipole_dielectric_sphere - 53-scenario feature-coverage benchmark suite under
benchmark/scenes/coverage/giving every directly comparable Tidy3D-exportable feature at least one load-bearing scenario, exported asCOVERAGE_SCENARIOSand registered into the same duplicate-checkedSCENARIOStable:coverage/sources.py:gaussian_beam_normal,gaussian_beam_defocused(focused/defocusedGaussianBeam),planewave_cw,dipole_cw_vacuum(CW→td.ContinuousWave),tfsf_vacuum,tfsf_dielectric_sphere(TFSF total/scattered-field split),mode_source_higher_order(mode_index=1),magnetic_current_vacuum(magnetic volume current →td.MagneticDipole/H*mapping), andricker_axis_x_anisotropic(Ricker waveform, negative-x injection, andeps_zz)coverage/media.py:drude_slab,lorentz_slab,lorentz_two_pole_slab,drude_lorentz_slab(multi-family pole folding toPoleResidue),debye_sphere,sellmeier_sphere,diag_aniso_sphere,pec_sphere(curved-interface dispersion, anisotropy, and PEC),lossy_metal_slab_high_sigma(surface-impedance/SIBC at 100× conductivity),kerr_slab_strong,modulated_slab_phase,static_medium2d_sheet,material_region_slab, anddispersive_kerr_slabcoverage/boundaries.py:pml_thin,pml_slab_through(material-loaded absorber),periodic_slab,bloch_oblique_te(in-plane polarization at 35°),symmetry_pec_center,symmetry_pmc_center(translation-invariantScene(symmetry=...)reduction),mixed_faces(per-face periodic/PEC/PML mix), andasymmetric_boundary_facescoverage/grid_geometry.py:cylinder_scatter,cone_scatter,ellipsoid_scatter,pyramid_scatter,prism_scatter,hollow_box_scatter,polyslab_pentagon(analytic, mesh, and PolySlab geometry export paths),autogrid_slab,nonuniform_custom_grid,anisotropic_uniform_grid(auto, graded-custom, and per-axis uniformGridSpec),explicit_mesh_scatter, andautogrid_override_refinementcoverage/postprocess.py:rcs_pec_sphere,rcs_dielectric_box(bistatic RCS on curved and faceted scatterers),directivity_two_dipoles(array-factor directivity),mode_monitor_straight_wg,mode_monitor_two_planes(single-moden_effand modal propagation ratio),mode_port_straight_wg,diffraction_normal_orders(0th and ±1st order efficiency),point_monitor_probe,permittivity_monitor_slab, andtime_monitor_vacuum- Point probes, two-plane mode monitors, ports, permittivity monitors, and time monitors contribute dedicated scalar observables instead of being incidental monitors beside a plane-field comparison
- P3-media Tidy3D cross-validation scenarios under
benchmark/scenes/media/exercising the phase-3 material physics that has a Tidy3D export equivalent:debye_slab(Debye dispersion →td.Debye),sigma_e_drude_slab(Drude + static conductivity → onetd.PoleResidue),anisotropic_slab(diagonal anisotropy →AnisotropicMedium),kerr_slab(Kerr χ³ →NonlinearSpec),modulated_slab(time modulation →Medium+ModulationSpec), andgraphene_sheet(Graphene/Medium2Dsheet →td.Medium2D) - Per-medium validation-coverage gate
tests/validation/benchmark/test_media_validation_coverage.pybacked by thebenchmark/media_coverage.pyregistry: it discovers every publicis_*/has_*capability flag onmedia.py(introspection, not a hardcoded class list) and fails if any capability lacks a declared validation path — a Tidy3D benchmark scenario, an FDFD cross-check, or a documented analytic-reference test — then verifies each claim (Tidy3D-path media must export through the adapter and reproduceMaterial.relative_permittivityfor the clean-identity cases; non-equivalent fallback media must genuinely raise on export, proving the analytic fallback is justified). The per-capability path table is recorded inbenchmark/RESULTS.mdunder## Validation coverage - HDF5-based Tidy3D reference caching under
benchmark/cache/ - Benchmark cache validation keyed to the exported Tidy3D scene configuration, with explicit mode, mesh, material-dispersion, directional-source, TFSF-source, source-time, and auto-grid export contract revisions for changes that alter SaaS results without changing declarative scene data (auto grids export Maxwell's own resolved mesh, so mesher changes invalidate their references loudly), so stale reference data is regenerated automatically; time-monitor caches also preserve their physical
tcoordinate - Benchmark-side Maxwell and Tidy3D runs share the same physical
Domain.bounds, both append PML outside it, and field/flux comparisons crop the external absorber samples back to the common physical domain during analysis - Shared benchmark scene helpers derive safe flux-monitor positions directly from the physical domain
- Error metrics: relative L2 error, best-fit-complex-scale shape L2, relative L-infinity error, normalized cross-correlation, and incident-power-normalized flux error; directional soft-surface sources remove one unit-modulus global source-reference phase (never amplitude) and use source geometry/direction to exclude only the discrete source sheet and upstream half-space. TFSF incident-power normalization is analytic in SI units (
0.5 * |E0|^2 * aperture * |direction_normal| / eta0) for both box and slab apertures, instead of inferring power from differently normalized cache payloads - Scenario-specific scalar comparisons share the same extracted solver data and report cavity resonance, grating diffraction efficiency by order, closed-surface dipole directivity/beamwidth, and sphere bistatic RCS alongside the coordinate-aligned field slices; Tidy3D and Maxwell closed-surface fields feed the same SI-unit near-to-far postprocessor so postprocessing differences are not mixed with solver differences
- Cached Tidy3D field payloads are filtered to the component set declared by the corresponding public Maxwell monitor before field or scalar postprocessing, preventing undeclared near-zero components exposed by Tidy3D's six-field monitor payload from being selected as observables
- Coordinate-aware plane-field alignment and interpolation onto the Tidy3D reference grid for cross-solver field comparison
- Flux benchmark comparison re-integrates Maxwell monitor fields over the same physical aperture used by the Tidy3D reference export with Yee cell-centred control-volume weights
- Material and source comparison plots align slices by physical coordinates, use the same soft PlaneWave injection-plane placement as the runtime/Tidy3D export, and geometry voxelization uses boundary tolerances to avoid one-pixel drift on benchmark domains
- Auto-generated Maxwell-vs-Tidy3D permittivity/source comparison plots plus
Ex/Ey/Ezfield comparisons onx/y/zcut planes underbenchmark/plots/; multi-frequency scenes display the worst-L2 frequency rather than silently fixing the image to the first channel, and every field panel reports its absolute peak so an auto-scaled near-zero cross-polarized component cannot be mistaken for a dominant-field failure. Each compared monitor also gets a coordinate-aligned complex-field diagnostic with globally phase-aligned magnitude/real/phase differences and center-line magnitude/real/phase traces; phase is shown only on the reference's significantly excited support, suppressing meaningless weak-field phase noise while retaining boundary-reflection and propagation-phase diagnostics. Frequency-converting scenes additionally emit one spectral electric-field row per requested frequency, using a single carrier-derived global phase factor for every sideband so relative modulation phase is never fitted away - Scalar benchmark observables produce paired visual comparisons as well as Markdown rows: complex S-parameters use magnitude/phase spectra, cavity resonances include the normalized point-probe spectrum and interpolated peak, and diffraction efficiency, bistatic RCS, and antenna directivity/beamwidth use solver-paired charts. Diffraction charts additionally report each order's absolute efficiency error in percentage points, annotate the retained relative error, and show the full order-distribution total-variation distance so near-zero orders cannot look dominant solely because of a small denominator. Time monitors retain solver-native physical time coordinates, interpolate normalized traces only over their common physical window, report zero-lag waveform L2 plus a bounded lag diagnostic, and generate paired physical-time trace plots; they are never stretched independently onto an artificial
[0, 1]sample axis - Auto-updated benchmark summary in
benchmark/RESULTS.md, grouped by benchmark scene folder with per-metric better-direction and target-range guidance plus FDTD ms/step, steps/s, total DFT samples, and driver-level peak GPU memory for Torch and CuPy allocations; each completed scenario is persisted incrementally so a later scenario failure does not discard earlier results - The generated summary always discloses what it did not measure: a
## Registered scenarios with no measured rowsection lists every scenario registered inbenchmark/runner.SCENARIOSthat carries no row in the tables above (with its solver and description), so a family that cannot execute on the generating host -- a missing optional dependency, a deliberate deferral, an aborted run -- is visible as unmeasured instead of silently absent. The section states in-line that such a row is not a pass, a fail, or a tolerance waiver but the absence of evidence, and because it is generated it cannot rot out of the file on the next regeneration - Benchmark runs enable source-spectrum normalization whenever all sources share one waveform, preserving user amplitude/phase while removing the arbitrary pulse envelope spectrum in the same convention as Tidy3D
- Validation-campaign controls for low-cost reference generation and solver filtering:
python -m benchmark --references-only --campaign-only, targeted cache replacement with--references-only --refresh-references <scenarios...>,--historical-only,--solver fdtd|fdfd, cache-key inventory, per-frequency field metrics (printed and persisted, with worst-frequency aggregation), incident-power-normalized flux errors, and a per-task Tidy3D cost ceiling - Unified FDFD validation scenarios use backend-supported CW point dipoles and compare normalized
|Ex|patterns for dielectric, conductive, dispersive Drude, and diagonal-anisotropic material responses against same-scene FDTD references; the campaign gate compiles every declared FDFD source so unsupported pseudo-scenarios cannot count as coverage - RF port validation harness via
python -m benchmark rf [scenes...]over the sixbenchmark/scenes/rf/scenes, with the binding metric measured from a real FDTDScene -> Simulation -> Resultrun wherever the two-port bench yields a usable S-matrix (never from the 2D mode eigensolve). Both wave benches are terminated by running their conductors/walls THROUGH the computational PML to the padded grid edges (the grid appends PML nodes outside the declared bounds, so2*(DOMAIN + num_layers*dx)is required and is verified against the prepared PEC occupancy, not the scene constant), and the network S-matrix is assembled by solvingB = S*Aacross the drive columns (the correct extraction whenever the passive port carries an incident wave; the per-driveb/aratio is the diagonal special case), recording the incident-matrix condition number per frequency. Current honest status (audit S1, round 4):coax_thruis a wave-level PASS -- terminated air-line TEM two-port witha_passive/a_driven0.17 (bench-quality diagnostic),|S11| < 0.02,|S21| ~ 1, max singular value ~1.0,cond(A) ~ 1.2, andbetafromarg(S21)/Lwithin 0.83% ofk0; the wave-level precondition is extraction conditioning plus post-solve passivity, and coax reciprocity is annotated as symmetric-trivial (mirror-symmetric fixture).rectangular_waveguideis a wave-level PASS on the Yee-staggered transverse full-vector operator: the selector injects a clean full-grid TE10 (sin(pi y/a)-correlation 1.0000), the terminated two-port S is well conditioned (cond(A) ~ 1.1) and passive (max singular value ~1.001), andbetafromarg(S21)/Ltracks the analytic dispersionsqrt(k0^2 - (pi/a)^2)to ~0.05% (median, interior band) inside a pre-registered 1% gate, with a fail-closedsin(pi y/a)-correlation regression guard (< 0.9 -> BLOCKED). A one-shot external-reference-solver cross-check (a TE10ModeSource-driven guide, cloud run) independently confirms the samebeta(omega)to ~1.2% median.lumped_open_short_matchis a wave-level PASS (rebuilt as a coax short-open-load calibration bench whose TEM feed is coupled to a de-embedded load plane, so the three standards are mutually distinguishable: matched|Gamma| <= -20 dB, short/open|Gamma| ~ 1, open in the +1 class and short in the -1 class after short-referenced de-embedding);microstrip_two_portanddifferential_pairare BLOCKED (contour-snap error first, andWaveModeSpec('tem')categorically inapplicable to their inhomogeneous substrate+air cross-sections);series_parallel_rlcis a wave-level PASS (rebuilt with the RLC as an in-line coax two-terminal element carrying the full axial line current, so the series|S11|notch tracksf0 = 1/(2*pi*sqrt(L C))with a documented ~13% parasitic downshift). See thee2a-rf-scenessubsection for the rebuilds. Modal-eigensolve quantities, when reported, are labelledmodal-eigensolvesupporting evidence and never gate. Each scene emits a machine-readable artifact (grid convergence, extraction conditioning/passivity, thea_passive/a_drivendiagnostic, and per-tier complexS(f)and porta/bso a frequency can be recomputed by hand; verbatim taxonomy gate classes and a separate status field) underdocs/assessments/rf-wave-validation-2026-07-18/and an## RF wave-level validationsection inbenchmark/RESULTS.md.python -m benchmark.rf_tidy3d_referencesperforms a real adapter-driven external-reference generation attempt (export -> runnable gate -> cost estimate/budget -> one cloud run ->.h5cache), and recordsreference: pending-generationwith the concrete reason when an export is not runnable or a cloud run fails, never fabricating a comparison, while the analytic reference keeps binding (see thee2c-rf-scenessubsection) - Wave-level RF gate tests under
tests/rf/wave_validation/replacing the retired plan-01 algebraic-identity gates: a propagating matched-load|S11|gate on a rectangular waveguide (matched thru reflects far less than a PEC short, measured from fields, with the short as falsification), and an asymmetric two-port reciprocity + field-derived power-balance gate (different port impedances/geometry soS12 == S21is physics not symmetry, with injected non-reciprocity and gain errors as falsification). The series-RLC companion-impedance formula check is retained but re-labelledanalytic-identity(non-gating); the wave-level RLC resonance gap has since been closed by the rebuilt in-line coax RLC bench (see the RF wave-level bench fixes section)
- FDFD solver performance benchmark via
python -m benchmark.fdfd_performance, sweeping cubic grid sizes (default 32³ through 128³) on a canonical dipole + dielectric-cube scene, with--solver/--precond/--precisionaxes - Per-size metrics: matrix assembly time, solve time, operator matvec count, convergence flag, explicit relative residual, and CuPy peak-GPU-memory high-water mark
- Results written to
benchmark/FDFD_PERFORMANCE.mdwith raw JSON runs archived underbenchmark/cache/fdfd_performance/ - Three-level dielectric-sphere FDTD grid-refinement study via
python -m benchmark.grid_convergence, with coordinate-aligned pairwise complex-field errors, observed convergence order, performance counters, Markdown results, and raw JSON archives
- Shared material compiler used by scene construction and backend preparation
- Reference-alignment helpers for FDTD field comparisons
- Error metrics for comparison against reference fields
- Direct physics validation suites under
tests/validation/physics/for vacuum plane-wave and dipole correctness, dielectric slab energy balance, boundary-condition validation (periodic,Bloch,PEC,PMC, mixedperiodic + PML,CPML), and TFSF leakage/scatter validation for both axis-aligned and oblique CW plane waves - Broadband-vs-CW grating acceptance (
tests/validation/physics/test_bloch_broadband_vs_cw.py): a single broadbandGaussianPulsegrating run reproduces the per-frequency monochromaticCWcomplex transmission of a thin non-resonant metasurface layer within 2% at three band frequencies, compared at normal incidence (thek_bloch -> 0limit, the regime where the CW reference settles to an extractable steady state), and the genuine oblique complex-field Bloch broadband injection confines the incident pulse to the total-field slab at each frequency - Dispersive-metasurface Bloch acceptance (
tests/validation/physics/test_dispersive_metasurface_bloch.py): a patterned (half-cell bar) Lorentz metasurface runs forward under oblique Bloch and, at unit Bloch phase (k L = 2*pi), reproduces its real-field periodic-equivalent run to float32 round-off; FDFD is unavailable as an independent cross-check because the FDFD runtime rejects periodic/Bloch faces, so the periodic-equivalent unit-phase reference is the exact cross-check of record - Validation workflows emit representative electric-field and centerline plots under
tests/test_output/validation/in addition to pass/fail assertions - Test coverage for public API, scene construction, mesh geometry, material compilation, CPML, observer extraction, and FDFD/FDTD consistency
- Guard-convergence gates in
tests/api/public/test_guard_census.py: an AST census caps the number ofNotImplementedErrorcapability guards inwitwin/against a committed budget (contract guards excluded via a documented list), and a phrase gate fails if anyNotImplementedErrorin the public forward path (media.py,compiler/,fdtd/runtime/,fdtd/boundary/,scene.py,simulation.py) carries a bare deferral phrase (not implemented yet/not supported yet/in v1) instead of a physical or mathematical reason; modules reworded by a later phase are allowlisted with the owning phase named
- The experimental lumped-port runtime currently executes one active
PortExcitationper FDTD run; automatic N-port sweeps are introduced by the network phase. Port excitation waveforms must be device-native CW, Gaussian-pulse, or Ricker forms, a time-domain source impedance must be real and positive, and the native FDTD adjoint does not yet replay port/RLC auxiliary state - Nonuniform (
GridSpec.custom) grids are FDTD-only in v1: FDFD and Tidy3D export both raiseNotImplementedErrorfor custom grids;GridSpec.autoresolves to a nonuniform grid at prepare time and inherits the same restrictions (FDFD unsupported, TFSF/mode-plane region-uniform bounds apply) - On nonuniform grids, TFSF-kind injections (
PlaneWaveTFSF box, grating TFSF slab) andModeSource/ModeMonitor/ModePortaccept a bounded amount of grading over the injection region / mode plane rather than requiring exact uniformity: a perfectly uniform region returns its exact cell spacing bit-for-bit (uniform grids unchanged), a mildly graded region is accepted using the region-mean spacing as the single effective delta, and an over-graded region raises with the predicted error quoted in the message. The TFSF bound caps the leading numerical-dispersion phase-velocity spread(k0*d)^2/24across the region (finest-to-coarsest cell) at1e-3for the vacuum injection wavek0 = omega/c(empirically the total-field/scattered-field leakage is about15 xthat spread, so within-bound grading keeps the null below ~1.5%); the mode-plane bound caps the fractional transverse-spacing variation(d_max - d_min)/d_meanat1e-2, the leading consistency error of the single-spacing 2D finite-difference mode operator. Soft (non-TFSF) surface sources andPointDipoleare fully generalized, and the softPlaneWavenumerical-dispersion phase correction uses the spacing local to the launch footprint (launch-plane cell along the injection axis, physical-aperture mean along each tangential axis), which is exact on uniform grids and tracks the launch cell's own numerical wavenumber on graded ones - On nonuniform grids the subpixel averaging window per node is the symmetric Yee dual-cell width (
0.5*(primal_left + primal_right)), i.e. a node-centered average; it reduces to the cell spacing on a uniform grid but is an approximation of a cell-exact quadrature on strongly graded meshes where the two neighbor cells differ substantially - Polarized subpixel averaging is applied to
epsandmuonly;sigma_e, Kerrchi3, and dispersive-pole weights stay arithmetic (the normal-projection rule is not well defined for conductivity/nonlinearity, and only the static base permittivity/permeability becomes normal-aware for dispersive media) - The polarized rule is baked at grid nodes and both runtimes keep their existing arithmetic node-to-Yee-edge averaging, so a residual O(dx) half-cell smoothing of the normal-aware correction remains versus evaluating the rule exactly at each Yee-edge location; this residual is far below the arithmetic/staircase error it replaces
- Conformal PEC (
SubpixelSpec(pec="conformal")) is FDTD-only: FDFD raisesNotImplementedErrorfor in-domain PEC materials, and the FDTD scheme is the stable partial-fill (edge-fraction E-suppression) variant of the Dey-Mittra family; classical area-scaled Dey-Mittra (H-side1/A_openwith an area floor), contour-path H-loop averaging, and surface-impedance boundaries remain future work. Two consequences of the E-suppression formulation are documented rather than fixed. (1) The open fraction multiplies the electric update every step, so a fillfon a cut edge is an effective conductivityeps*f/dtthere and a lossless PEC scatterer picks up spurious absorption: a closed PEC cavity holding a PEC sphere retains0.45of its energy after 5200 source-free steps underconformalversus1.00understaircase. (2) A flat wall parallel to the grid cuts no tangential edge, soconformaldoes not place such a wall sub-cell -- it reproducesstaircaseexactly there (that case needs the area-scaled magnetic update).staircaseremains the default everywhere, including the benchmark harness - Subpixel averaging normals and PEC fill fractions reuse per-structure signed-distance fields, so accuracy is bounded by SDF quality for primitives with kinked distance fields (faces/edges/corners)
Material.orientationremains unsupported. Full off-diagonalTensor3x3permittivity is FDTD-only and cannot currently combine with Bloch fields, Kerr media, or polarized subpixel averaging; it now composes with electric dispersive poles (a rotated birefringent dispersive crystal) and with electric conductivity (a lossy anisotropic crystal) in the same material, both FDTD forward-only. Static full-tensor scenes are differentiable through the FDTD adjoint bridge (a design differentiated through the static off-diagonal coupling), but a trainable geometry on the tensor structure itself, anisotropicmu_tensormedia, the full-tensor + electric-dispersion combination, and the full-tensor + conductivity combination stay guarded because the off-diagonal coupling, magnetic reverse updates, and coupled-tensor ADE/conduction reverse carry no off-diagonal material gradient channel.- A single
Materialmay combine instantaneous nonlinearity (kerr_chi3/NonlinearSusceptibility/TwoPhotonAbsorption) with electric dispersive poles (forchi2SHG phase matching) but not with anisotropic tensors; separate structures in the same scene may also mix them freely. Nonlinear media are excluded from Bloch / complex-field runs, full off-diagonal anisotropy, and FDFD, but instantaneous nonlinearity (Kerrchi3/chi2/ two-photon absorption) is now captured by the forward CUDA graph (the dynamic coefficient recompute carries no per-step host input); Tidy3D export covers the chi3-family (chi3Kerr and two-photon absorption) but rejectschi2(no Tidy3D public equivalent). The FDTD adjoint bridge covers pure instantaneous Kerr (chi3),chi2, and two-photon-absorption media (the general nonlinear coefficient recompute is replayed differentiably), but still rejects nonlinear media combined with static conductivity or with electric dispersion in the same material (the reverse replay divides the ADE polarization current by the static rather than the field-dependent effective permittivity, so the same-material nonlinear + dispersion combination is forward-only).NonlinearSusceptibilityis scalar (tensorial chi2 / chi3 are not supported) - Linear-gain support is limited to
allow_gain=Truegain-signedLorentzPolemedia (a documented negative-oscillator-strength path); saturable / rate-equation (2-level, 4-level) gain is a follow-up and is not implemented, gain runs carry no automatic stability guarantee, and the FDTD adjoint bridge does not cover gain media - Experimental
DiffractionMonitoris v1-limited: it assumes the monitor plane spans one full transverse unit cell (periods taken from the transverse domain lengths), targets FDTDCWgrating scenes with transverse Bloch/periodic boundaries, and reports per-order power as a share of the transmitted plane flux. Absolute per-order diffraction efficiency normalized against a separately measured incident power is not asserted, because the FDTD spectral-monitor CW normalization is steady-state/ratio-based rather than an absolute cross-run power balance - Experimental
DipoleEmissionMonitoris v1-limited: it reportspower_deliveredfrom the single co-located dipole cell (a point approximation of the profile-weighted current integral) and requires FDTDPointDipolesources; the Purcell factor is only obtained by explicitly normalizing against a separate vacuum run throughmaxwell.postprocess.purcell_factor(...), because the discrete effective source volume that sets the absolute power scale has no reliable closed form, so a lone run returnspurcell_factor=None - FDFD supports electric anisotropy only; static magnetic media and magnetic dispersion still fail explicitly
- FDTD supports static electric conductivity (
sigma_e) in the forward update but still lacks static magnetic conductivity (sigma_m); the FDTD adjoint bridge now differentiates real-fieldsigma_escenes by routing them to the torch-VJP reverse with a differentiable semi-implicit lossy-coefficient replica, but still rejectssigma_ecombined with nonlinear media (the instantaneous nonlinear kernel folds the loss into a field-dependent coefficient) or on Bloch boundaries (the complex-field replay carries the loss on the real update only) - Mixed Bloch boundary configurations outside the single-PML-axis / two-Bloch-axes family (any one absorbing axis with the other two Bloch) still fail explicitly: a layout that mixes Bloch and PML on the same axis, or leaves more than one absorbing axis, is rejected with a physics-worded error
- TFSF slab runtime support covers CW and broadband
PlaneWavegrating slabs on any normal axis (axis="x"|"y"|"z", Bloch on the two transverse axes) and normally-incidentPlaneWaveslabs with non-periodic transverse boundaries; oblique-incidence non-periodic slabs, non-PlaneWaveslab sources, and broadband automatic fixed-angle Bloch workflows still fail explicitly because automatic Bloch phase is single-frequency metadata - Automatic Bloch wavevectors are solver-preparation metadata and are rejected by unresolved Tidy3D export
- FDFD currently supports per-face boundary mixing only for
noneandpml - Domain symmetry (
Scene(symmetry=...)) is FDTD-only (FDFD rejects it), applies a single image plane per axis at the chosenlow/highface, and requires the user to supply the pre-folded half/quarter domain with a physically symmetric source layout. A configured PML on the symmetry face contributes zero layers, high-face uniform grids anchor their last sample to the image plane, and point dipoles exactly on an image plane receive the Yee-control-volume scaling needed to reproduce the expanded full-domain source. Tidy3D encodes symmetry about the domain center and has no per-face selection, so direct export is allowed only when structures/material regions and plane-wave sources are translation-invariant along each symmetry axis; localized or axis-varying symmetry scenes raiseNotImplementedErrorinstead of silently changing the image-plane location - The first-order
BoundarySpec.mur()absorbing boundary leaves a higher reflected residual than a PML absorber (single-snapshot residual energy roughly an order of magnitude above a reflecting PEC box, versus about two orders for PML), applies only to the real electric field so it cannot be combined with Bloch complex-field runs, and has no second-order variant in v1 - The
"absorber"and"stablepml"variants share the existing PML face plumbing (BoundarySpec.pml(...));"stablepml"overrides the CPML grading/alpha profile, so a user-suppliedcpml_configdoes not override the stable-profile grading keys - Tidy3D export rejects anisotropy, magnetic dispersion, and Kerr media explicitly in v1. The FDTD adjoint bridge supports diagonal (
DiagonalTensor3) epsilon anisotropy, scenes containing static magnetic-dispersive (mu-pole) media, and pure instantaneous Kerr (chi3) media, andchi2/ two-photon-absorption media (through a differentiable replica of the general nonlinear coefficient kernel), but still rejects full off-diagonal (Tensor3x3) permittivity,mu_tensoranisotropy, and trainable geometry on magnetic-dispersive structures (there is no mu material-gradient channel) explicitly - Custom spatially-varying dispersive poles vary only the oscillator strength per cell (
delta_eps/plasma_frequency); the rate parameters (tau,gamma,resonance_frequency) stay spatially uniform per pole so the ADE recursion constants remain scalar. They requireBoxstructure geometry,Material.relative_permittivity/relative_permeabilityraise for custom poles (evaluate the compiled scene model instead, soGridSpec.automeshes such structures from their staticeps_r), and Tidy3D export lowers a spatially-uniform custom pole to its equivalent homogeneous pole while rejecting a spatially-varying one with a physics-worded error (no Tidy3D absolute-coordinate resolution of the box-relative grid at material-conversion time) Medium2Dsheets require an axis-aligned unrotatedBoxgeometry with exactly one zero-size axis, snap to the nearest node plane along the sheet normal (staircase placement, no sub-cell interpolation); they export to Tidy3D'sMedium2D(surface conductivity cross-validated againstsheet_conductivity) but are rejected by the FDTD/FDFD adjoint bridgesGraphene(include_interband=True)fits the T>0 interband Kubo conductivity to Lorentz sheet terms only below the absorption edge (hbar*omega -> 2*|mu_c|); the fit targets and is validated over that optical band, so above-edge behavior (the universale^2/4*hbarreal-conductivity plateau) is extrapolated rather than fitted, and the constructor raises a clearValueErrorif an operating point (e.g.|mu_c|not large compared tokB*T) cannot be fitted within the band tolerance- Generalized axis-aligned surface-impedance boundary:
compile_surface_impedance_layoutextracts every illuminated axis-aligned face of every surface-impedance metal into aCompiledSurfaceImpedanceLayout(CompiledSurfaceMetal/CompiledSurfaceFace), replacing the single-plane v1 restriction. Finite blocks (all exposed faces), mid-domain double-sided plates, multiple metals, and multiple orientations are all supported; a face is illuminated when its box side does not sit flush against the physicalDomain.bounds(external PML cells never turn a boundary-backed metal into an interior face). Per-face area sums and a deterministic edge/corner owner order (minimum-rank owner writes last, so a shared corner edge has one deterministic owner) are computed in the layout; a PEC or 2D sheet coincident with a surface, or two different surface materials on one interface plane, fail closed through the single capability funnel as contradictory owners. Oblique/rotated or curved (conformal) surfaces and Bloch-periodic runs still fail closed with a physics-wordedNotImplementedErrornaming the phase that lifts them - Narrowband good-conductor
LossyMetalMediumis realized on that unified layout as an order-0 (pure-resistance) surface:Z_s = R = sqrt(omega0*mu0/(2*sigma))evaluated at the operating frequency, no auxiliary state. A full-plane order-0 face uses the fused native CUDA kernel (E_t = sign*R*(n x H)), which is bit-identical to the generic scalarD*(sign*H)path (multiplication bysign = +/-1is exact), so the fused kernel is a pure optimization of the same contract; a finite (sub-plane) face writes only its transverse window. The reactive part ofZ_sis omitted for the narrowband path (its explicit derivative overwrite is non-passive; it shifts|Gamma|by< 1.3e-4for a good conductor). The publicsurface_impedance(omega)helper and Tidy3D export still expose the full complexZ_s(omega0) = (1 + j)*R; Tidy3D export mapsLossyMetalMediumto Tidy3D's ownLossyMetalMedium - Broadband generic surface-impedance runtime: a
SurfaceImpedanceMediumcarrying a passiveRationalSurfaceImpedanceis stepped by a native per-edge Z-form auxiliary-differential-equation (ADE). The surfaceZ_s(omega)is resampled over its declaredfrequency_rangeand refit as a passiveZ-form rational through the shared fitter, then discretized with the bilinear (trapezoidal,|z| < 1) transform (fit_surface_impedance); passivity is a compile exit gate (a fit accurate but not certified passive is rejected before any step). Each surface edge advancesx <- A x + B uwith outputE = C x + D u,u = sign*(n x H)-- exactly the sharedDiscreteStateSpaceNetwork.step. The broadband good-conductor reflection matches the analytic Leontovich value within the frozenSurfaceAcceptanceBudget(analytic_reflection_relative_error), the surface is dissipative (|Gamma| < 1,min_local_surface_dissipation >= 0), the result is stable and consistent across three grid-refinement levels at moderate fit order and adequate resolution, and the torch-native per-edge ADE state advance is CUDA-graph capturable. Stability holds in the moderate-order / adequate-resolution regime the acceptance suite covers: the compile passivity certificate bounds the continuous surface response, not the coupled surface-plus-Yee discrete stability, so a certified-passive high-order Z-form fit at coarse resolution (e.g. order ~10 near ~40 cells/wavelength) can still diverge -- use a moderate fit order at adequate cells-per-wavelength. The FDTD adjoint bridge and multi-GPU distributed path reject any surface-impedance medium (the surface update and its ADE state advance carry no reverse gradient channel and no sharded owner yet); interoperability export of a genericSurfaceImpedanceMediumfails closed (no finite bulk-permittivity equivalent). Public model layer:SurfaceImpedanceModel/RationalSurfaceImpedance(with.fit) /SurfaceImpedanceMedium, all fail-closed unless stable, certified passive over the band, and scalar (1x1) or tangential (2x2);surface_impedance(omega)returnsZ_sregardless of the internal admittance/impedance representation. A runtime-independent torch reference oracle (analytic Fresnel/Leontovich with oblique TE/TM, the surface power-balance identity, and the discrete surface power form with the edge/corner unique-owner assembly proven nonnegative) pins the numerical contract - Staircased (voxelized) surface-impedance boundary: a good-conductor
LossyMetalMediumon any non-Boxgeometry (a curvedCylinder/Sphere, mixed orientations in one scene) is staircased from its node occupancy -- a node is metal when its center is inside the geometry, and every axis-aligned voxel face on the metal/vacuum boundary becomes a Leontovich surface-impedance face (_compile_voxel_surface_metal). Faces of one orientation at a node plane are grouped into a boolean transverse mask; the metal interior is terminated as a good conductor via the node->edge occupancy fill (the PEC-staircase treatment), and each face writesE_t = R*(n_hat x H)on its exposed footprint with a static-shape, CUDA-graph-capturabletorch.where. The same axis permutation of a plate scene reproduces the field to near-bitwise agreement (an exact Yee symmetry; the staircase adds only a ~1e-4 hard-threshold residual), the stateless resistive update stays finite and bounded on mixed-orientation and curved surfaces over long runs across the good-conductor regime, and a flat plate assembled entirely from voxel faces reproduces the analytic Leontovich reflection to <1% at 1/2/3 GHz versus|Gamma| ~ 0.999for a PEC plate. The staircase wires the narrowband good conductor (order-0 resistance) only; a generic rational surface on a curved conductor, a rotatedBox(grid-unaligned oblique normal), and Bloch runs still fail closed through the single capability funnel, which now funnels only the true conformal/oblique and Bloch cases -- the remaining SIBC gap is the non-staircase conformal surface, not the staircased curved conductor PerturbationMediumperturbs the electric permittivity only (nomu/sigma_esensitivity channels yet), requiresBoxstructure geometry, raises fromrelative_permittivity()/evaluate_at_frequency()because the shifted permittivity is spatially varying (GridSpec.automeshes such structures from the baseeps_r), and Tidy3D export lowers a spatially-uniform perturbation to a homogeneous permittivity shifteps_base + eps_sensitivity*value(preserving the base's dispersion/anisotropy) while rejecting a spatially-varying one with a physics-worded error- Space-time modulation (
ModulationSpec) is FDTD-only and modulates the electric permittivity only (nomu/sigma_emodulation); a modulatedMaterialnow composes with electric/magnetic dispersive poles and with the instantaneous nonlinear channels (kerr_chi3/chi2/TwoPhotonAbsorption), both in the sameMaterialand across separate structures in a scene, but a modulatedMaterialstill cannot carry an anisotropic tensor (the scalar modulation factor has no defined per-axis/crystal-tensor action) or a staticsigma_e(the semi-implicit loss fold would need to track the modulatedeps_inf * m(t)), and a modulated scene still cannot mix with a fully off-diagonal anisotropic structure. A scene may hold several distinct modulation frequencies at once (each modulated structure stamps its own angular frequency onto the cells it covers; overlapping differently-modulated structures resolve to the last structure's frequency, mirroring the quadrature-overlap rule, and the modulation depth cap below0.5stays per-structure because each cell carries at most one frequency), grid-valuedamplitude/phaserequireBoxstructure geometry, Bloch / complex-field runs are rejected, CUDA Graph capture uses the device-resident modulation clock, and the FDTD adjoint bridge rejects modulated media explicitly. Tidy3D export supports a scalar-depth non-dispersive modulated medium (mapped to Tidy3D'sModulationSpec) and rejects modulated dispersive/nonlinear media and spatially-varying (3D-tensor) modulation profiles with physics-worded errors.GridSpec.autoand static/frequency evaluation use the unmodulated carrier permittivityeps_static - Experimental
ModeSource,ModeMonitor, andModePortnow coverCWand broadband (GaussianPulse/Ricker) soft injection / monitoring, complex/lossy eigenmodes (a complex-symmetric solve exposingeffective_index_complex/beta_complex), diagonal-anisotropic apertures (true per-axiseps/mu), non-unitmu_rplanes, and bent (curved-waveguide) ports (bend_radius/bend_axis), and export to Tidy3D'sModeSource/ModeMonitor(see the Tidy3D Adapter section). The remaining genuine limitations are: the injection / monitor plane must be axis-aligned, so truly oblique or staircased angled planes are rejected by the plane-normal contract (the well-posed cylindrical-bend case is handled instead throughbend_radius/bend_axis); and the differentiable mode-solve path is Hermitian only, so gradients through a lossy (complex, non-Hermitian) permittivity are unavailable even though the forward complex solve and every real-permittivity gradient are supported. RFLumpedPortnow has a separate Phase-0 public/discrete contract; its FDTD source/load coupling, terminal ports, and RF wave ports are delivered by the subsequent RF runtime phases instead of being inferred fromModePort - Experimental
compute_mode_overlap(...)currently expects aligned tangentialE/Hfields on the target port plane and inherits the same axis-aligned modal-source assumptions as the currentModeSourceimplementation - Raw single-plane postprocessing from
PlaneMonitordata is still not a general 3D radiator/scatterer workflow; useFinitePlaneMonitor/ClosedSurfaceMonitorfor the validated near/far/RCS/directivity path - First-class closed-surface postprocessing currently requires a homogeneous exterior medium immediately outside every face; layered or otherwise inhomogeneous exteriors are rejected explicitly
Result.antenna(...)currently requires that homogeneous exterior to be lossless and one driven excitation column; the default full-sphere grid keeps the frequency dimension explicit.surface_currentsare the electric/magnetic Huygens equivalent currents sampled on the named near-field surface, not an inferred sub-cell conductor-current distribution- Automatic
PowerLossMonitorproduction currently covers static bulk electric conductivity from full-field FDTD spectra. Two-dimensional sheets are rejected because volume lowering discards their surface identity, and magnetic conductivity is rejected because the automatic path has no frequency-domain H loss channel. ADE, nonlinear, circuit, surface, and wire mechanisms appear only when their owning physics supplies explicit density or integrated power; absent channels are never reported as zero - RF differentiation is intentionally narrower than RF forward execution: differentiable lumped runs reject
ParallelRLC, observer-only contour ports, open internal resistance, trainable source/reference impedance, lumpedPortSweep, and conductive, dispersive, nonlinear, modulated, full-anisotropy, Bloch, or other complex-field coupling. DifferentiableWavePortruns require one fixed mode and fixed amplitude, keep the design away from the aperture and adjacent launch plane, and cannot mix with lumped ports or standalone R/C/L elements - Curved (arbitrary-normal) closed-surface far-field / near-field propagation is now supported and validated on the radiation side (
SurfaceEquivalentCurrents/equivalent_surface_currents_from_surface_samplesradiate given surface fields on a sphere, ellipsoid, or voxelized closed surface, checked against a Hertzian dipole to< 1e-3). The remaining gap is the monitor side: sampling FDTD near fields onto an off-Yee-grid curved surface (trilinear interpolation of the DFT'd fields) is still future work, so the FDTD monitor path continues to serve axis-aligned box and staircased polyhedral surfaces only - The soft-plane-wave absolute incident-power calibration is resolved: the amplitude scale is derived (surface-equivalence unit forward gain, Yee numerical impedance
eta0) rather than fitted, the two former empirical constants (_PLANE_WAVE_POWER_CALIBRATION,_PLANE_WAVE_DELAY_CALIBRATION_S) are removed, and the absolute power is validated against analytic unit power within 2% across three frequencies and two spacings UniformCurrentSource,CustomCurrentSource, andCustomFieldSourceinject additive electric/magnetic currents in the forward pass and are covered by the FDTD adjoint: a scene driven by them differentiates the design region through the injected field (the sourceJ/M/field datasets themselves are fixed forward inputs).CustomFieldSourceadditionally requires a planar (single-sample-axis)FieldDataset, injects toward+normalonly, and reproduces the plane wave to discretization-limited tolerance (finite-aperture edge diffraction and half-cell E/H staggering leave a small backward and cross-polarized residual)CustomSourceTimev1 is limited to the Python scalar soft/uniform injection path: it is supported onPointDipolebut rejected onPlaneWave,GaussianBeam,AstigmaticGaussianBeam, andModeSource(which all require the native time-shifted surface kernel;ModeSourcenow acceptsCW/GaussianPulse/Rickerthere and rejects onlyCustomSourceTime); callablefnwaveforms need an explicitcharacteristic_frequencyand are not supported in the FDTD adjoint (only sampled(times, amplitudes)tables are), and custom-waveform samples are not differentiable
- Dedicated cell-centred finite-volume electrostatic (Laplace/Poisson) solver reached through
Simulation.electrostatic(scene, boundary=..., solver=...).run(), returning a standardResult(method="electrostatic")whose typed output isresult.electrostatic(anElectrostaticResultData). It is an independent DC PDE runtime, not a low-frequency full-wave approximation, and keeps theScene -> Simulation -> Resultcontract. - New public objects
ElectrostaticTerminal(equipotential conductor:potential=,grounded=True, or floatingcharge=),ChargeDensity(volumetric free charge in C/m^3),ElectrostaticBoundarySpec(per-facedirichlet/neumann/symmetry, withgrounded_box(),dirichlet(value), andneumann()constructors), andElectrostaticSolverConfig(tolerance,max_iterations,dtype; float64 by default). - Terminals live in a solver-specific
Scene.add_electrostatic_terminal(...)collection (and free charge inScene.add_charge_density(...)) that never enters the RFScene.portsset, so an equipotential constraint is never reinterpreted as an RF impedance/power-wave port. - The operator uses harmonic-mean interface permittivity on each Yee face so the discrete Gauss law closes by construction; interior fixed-potential conductors are imposed by symmetric projection and solved with a GPU Jacobi-preconditioned conjugate gradient in float64. The solve records iteration count, relative residual, and the per-cell discrete-Gauss error, and raises with diagnostics on non-convergence.
ElectrostaticResultDataexposes the cell-centred potential [V], colocatedE[V/m] andD[C/m^2] fields, relative permittivity, per-cell free charge and volume, field energy [J], per-terminal conductor charge viaterminal_charge(name), the residual, and the Gauss error. Validated against parallel-plate, coaxial-cylinder, and concentric-sphere analytic solutions with three-level grid convergence, discrete Gauss closure, and the0.5 integral(E.D) = 0.5 sum(V Q)energy identity.- Floating conductors with a prescribed total charge (
ElectrostaticTerminal(..., charge=)) are resolved by exact linear superposition: the solver runs one base solve (fixed electrodes on, floating conductors grounded) plus one unit solve per floating conductor, then solves a small densek x ksystem so each floating conductor reaches the equipotential level that reproduces its prescribed charge. Multiple floating conductors, mixed fixed/floating scenes, and charge-neutral floating shields (which float to a consistent intermediate potential) are supported; a charged floating conductor in a fully insulated (pure-Neumann) enclosure is rejected as charge-incompatible, and an all-floating insulated problem is gauge-fixed bymean(phi)=0. - N-terminal Maxwell capacitance matrix extraction via
Simulation.capacitance(scene, terminals=..., reference=..., boundary=..., solver=...).run(), returning a standardResult(method="capacitance")with typedresult.capacitance(aCapacitanceData). It drives each active (non-reference) terminal to 1 V with the rest grounded, reusing one compiled operator across excitations, and reports the raw matrix with no silent symmetrization.CapacitanceDataexposesmatrix,terminal_order,reference,charges, per-excitationenergy,reciprocity_error(max|C - C^T| / max|C|), androw_sum_error(charge conservation under an insulating boundary), plus derivedcapacitance(a,b),mutual_capacitance(a,b),capacitance_to_reference(a), andtwo_terminal_capacitance(a,b)accessors. Validated by sphere-in-grounded-shell analytic capacitance with grid convergence, three-terminal matrix symmetry / positive-diagonal / non-positive-off-diagonal sign structure,0.5 V^T C Vvs field-energy equivalence, terminal-reordering invariance, and insulating-boundary row-sum conservation. - Fail-closed in this stage: grid-extending Scene boundaries (PML/periodic), PEC-material dielectrics (use a terminal), dispersive/tensor/complex permittivity, gauge-singular pure-Neumann problems with no conductor, incompatible floating-charge constraints, and capacitance requests with no charge return path (no reference terminal and no Dirichlet boundary) are all rejected with clear messages rather than silently mishandled.
- Differentiable electrostatics (implicit differentiation): the fixed-potential electrostatic solve and the capacitance extraction are PyTorch-native differentiable in the cell permittivity, the volumetric free charge, and the terminal voltages. The reduced solve is wrapped in a
torch.autograd.Functionwhose forward runs Jacobi-PCG underno_grad(no autograd tape) and whose backward solves the adjoint system on the same SPD reduced operator/preconditioner (A^T lambda = dL/dphi, closed-form pinned-cell multiplier) and forms the parameter gradients as a residual vector-Jacobian product, so no hand-codeddA/d(eps)stencil is needed. Result quantities (energy, per-terminalterminal_charge, the fields) and every capacitance-matrix entry (CapacitanceData.matrix) therefore backpropagate to the compiled cell-permittivity tensor, the compiled volumetric free-charge tensor, and the compiled fixed-potential tensor. Through the publicSceneAPI this reachesChargeDensity(density=<tensor>)directly (its magnitude is preserved as a live tensor and flows to the free charge).Materialpermittivity andElectrostaticTerminal.potentialare real-scalar scene inputs coerced to float on construction, so differentiating with respect to a permittivity or a terminal voltage means driving the corresponding compiledepsilon_r/fixed_valuetensor (the level the gradient gates exercise), not those scalar constructor fields. Verified by central-difference gates (relative error< 1e-4, float64) ondC_ij/d(eps), an interior-potential probed(phi)/d(eps)(the non-variational eps->phi path),d(energy)/d(free_charge)(a pure-implicit functional),d(energy)/d(eps_region), andd(phi)/d(V_terminal)(the pinned-cell adjoint path). Gradients through the floating-conductor prescribed-charge superposition solve are not implemented and fail closed (a floating terminal combined with a differentiable permittivity/charge raises rather than returning silently wrong gradients).
- Anisotropic (full symmetric-positive-definite tensor) static permittivity: a
StructurewhoseMaterialcarriesepsilon_tensor=DiagonalTensor3(...)or a fullepsilon_tensor=Tensor3x3(((exx,exy,exz),(exy,eyy,eyz),(exz,eyz,ezz)))now compiles into a per-cell relative-permittivity tensor field and solves throughSimulation.electrostatic(...)andSimulation.capacitance(...), extending the previous isotropic-only scalar operator. The compiler validates each material tensor as real, symmetric, and positive-definite (an asymmetric or indefinite tensor is rejected as a physically invalid lossless permittivity), and an isotropic scalar or an isotropic diagonal tensor keeps the exact scalar code path unchanged. - The SPD tensor operator keeps the existing conservative two-point harmonic-mean face flux for the diagonal permittivity entries and adds a symmetric cross-derivative coupling for the off-diagonal entries, derived as the gradient of a discrete quadratic energy
W(phi) = 0.5 phi^T A phi. The operator is therefore symmetric to floating-point tolerance (verified as a dense-matrix property and by random-vector<Ax,y> = <x,Ay>), positive-definite (positive symmetric eigenvalues / strictly positive field energy for a nonzero potential), and reduces exactly to the isotropic/per-axis face-flux path when the off-diagonal entries vanish (the cross operator maps every field to exactly zero).field_energy(phi)equals0.5 phi^T A phiincluding the cross terms, so the0.5 integral(E.D)energy identity and the discrete Gauss law continue to close.ElectrostaticResultData.Dis formed from the full tensor contractionD_i = eps0 sum_j eps_ij E_j, and the result carries the compiledepsilon_tensorfield. - Verified by a rotated-frame method-of-manufactured-solutions convergence study (a constant SPD tensor
diag(1,2,3)rotated 30 deg / 45 deg so all off-diagonal entries are active) showing second-order spatial convergence over a three-level grid refinement, a discrete Gauss-closure gate, and an anisotropic three-terminal Maxwell-capacitance reciprocity gate (reciprocity_error < 1e-6for a rotated anisotropic dielectric with asymmetrically placed electrodes; a mirror-symmetric two-terminal cell would report a symmetric matrix even with a broken operator, so the asymmetric placement genuinely probes the cross-flux symmetrization). - Fail-closed in this stage: a differentiable (trainable) tensor-permittivity or a trainable free charge alongside a tensor dielectric is rejected, because the off-diagonal cross-flux has no reverse-mode (implicit-diff) VJP yet, so the tensor solve is forward-only and refuses to silently detach a gradient. The cross-flux boundary treatment at a Dirichlet wall is first-order for a field with a strong tangential gradient at the wall (the interior scheme is second-order); improving that boundary layer and the open-boundary / truncation study are stage H2b.
- Controlled open-boundary handling via domain extension (stage H2b): an
open(infinite-domain) electrostatic boundary fails closed with a clear message (there is no exact radiation condition on the scalar potential at a finite Cartesian face; a boundary-element open boundary is a later phase). Instead, an isolated structure is modelled by enlarging the grounded-box enclosure until the capacitance converges.Simulation.capacitance(scene, ..., truncation_estimate=mw.TruncationEstimate(padding_cells=N))is an explicit opt-in that runs one additional enlarged grounded-box solve (the interior grid is held byte-identical; the grounded box is pushed out byNwhole cells on every side of every axis) and reports the finite-enclosure truncation error onresult.capacitance.truncation_estimate(aTruncationReport): the base and enlarged capacitance matrices, their difference,max_relative_delta(the relative sensitivity of the extracted capacitance to the enclosure size), the effective base/enlarged enclosure sizes, and a 1/L Richardson extrapolationrichardson_matrixto the infinite-domain limit with its residualrichardson_max_relative_shift. No second solve runs unlesstruncation_estimateis passed; it requires a Dirichlet (grounded-box) enclosure and a uniform grid, and fails closed with aValueErrorotherwise. Verified by a two-axis domain-extension convergence study (self-capacitance of an isolated conductor decreasing monotonically toward a stable RichardsonC_infas the enclosure grows at fixed grid, and Cauchy-convergent under grid refinement at fixed enclosure size). - Differentiability disposition (stage H2b, decided): electrostatic gradients through a full anisotropic (tensor) dielectric are deferred to the plan's differentiability phase, so a trainable input under a tensor dielectric fails closed on both the
Simulation.electrostatic(...)andSimulation.capacitance(...)public paths rather than returning a silently detached (wrong) gradient; the isotropic implicit-differentiation backward is unchanged and fully supported.
- Capability level is stress-only: these objects reproduce standard ESD current waveforms, inject them into a terminal port as an ideal (prescribed) current, and report local field stress, port V/I, charge, and action integral. They do NOT model source-impedance networks, discharge-gun geometry, arc channels, or device-failure probability, and a standard waveform class name does not by itself constitute standard certification.
ESDWaveform.iec_61000_4_2(level_voltage, discharge="contact", standard_revision=...)builds the IEC 61000-4-2 contact-discharge first-transient current as a four-parameter two-term Heidler sum (n = 1.8) with numerically computed per-term peak-normalization factors. Amplitudes scale linearly with the level voltage; the rise/decay time constants are voltage independent.standard_revisionmust name a supported revision ("ed2-contact", also the documented default when omitted) and is recorded in provenance. Onlydischarge="contact"is supported in this phase (air discharge is deferred, fail-closed). The current is torch-native (current(t)accepts a torch tensor).MeasuredWaveform(time, values, units="A", bandwidth=..., provenance=...)wraps user-measured current samples (strictly increasing time, amperes) with the same diagnostics/resampling interface and metadata provenance.waveform.diagnostics()returns anESDDiagnosticswith peak current, current at 30 ns and 60 ns, 10-90% rise time, total chargeintegral i dt, action integralintegral i^2 dt, and peak time; all match independent numerical quadrature of the same analytic current to tight tolerance.waveform.resample_to_grid(dt, t_end=...)performs charge-conserving binned integration onto andt-spaced grid (per-bin mean current(1/dt) integral_bin i dt, NOT naive point sampling), returning anESDResampledWaveformwhosecharge_ratiois 1 by construction at everydtand whosealiasing_metric(action-integral error) decreases toward zero asdtrefines.ESDCurrentSource(name, port=<TerminalPort name>, waveform=..., direction="+")binds a waveform to a resolvedTerminalPortand lowers (viaScene.resolved_sources()) to a uniform additive current source over the port gap footprint (an ideal current injection; the current density integrates to the target port current, polarization aligned to the port voltage-path axis and signed toward the positive terminal). Source-impedance networks are out of scope (Phase 3). Scenes with no ESD source are unaffected (the source lowering is a pure per-source expansion).Result.esd_waveform(name)returns a typedESDPortRecord(target diagnostics, the charge-conserving projection of the injected current onto the run time grid, and full provenance including standard revision, level voltage, capability level, and model version);Result.esd_waveform_names()lists ESD sources. For an ideal current injection into a capacitive terminal gap the measured gap voltage is the time integral of the injected current (V = Q/C), so its time derivative tracks the target current waveform. The record also carries ameasuredport record surfaced from the run's recorded terminal-port V/I (PortData) when present, enabling a target-vs-measured check; for the Phase-1 ideal-current injection path no terminal-port recorder runs, someasuredisNoneby design (documented on the accessor) and the injected current on the run grid is theresampledrecord.
- Capability level is stress-only and the source network is a circuit approximation of the standard network, NOT discharge-gun geometry, calibration-target, or system certification. This closes the Phase-3 gap of driving an ESD discharge through a source-impedance NETWORK rather than the ideal current injection of
ESDCurrentSource: the versioned ESD current waveform drives a time-dependent voltage source inside an MNA circuit whose Thevenin source impedance is the standard 330 ohm discharge resistor shunted by the 150 pF storage capacitance, and the network output node is bound to a sceneTerminalPort/LumpedPortthrough the existing strong (same-step Schur) FDTD+MNA coupling, so the delivered discharge current is shaped by the source network and the device-under-test back-reacts on the generator. waveform.to_circuit_waveform(t_end=..., samples=..., scale=...)resamples anyESDWaveform/MeasuredWaveformonto aPiecewiseLinearWaveformMNA source table(t, scale * i(t))over the support (or[start, t_end]); the dense table reproduces the smooth analytic current within tabulation error and its trapezoidal impulse converges to the analytic value.scalemaps the ampere-valued standard current onto the circuit-source quantity (a generator Thevenin resistance for a voltage source, or1.0for a current source).ESDVoltageSource(name, port=<port name>, waveform=..., discharge_resistance=330.0, storage_capacitance=150e-12)is the source-network ESD excitation.build_circuit(t_end=..., circuit_name=...)assembles the standard 330 ohm / 150 pF generatorCircuit(aVoltageSourcedriven bydischarge_resistance * i_esd(t)so the network short-circuit current equals the standard waveform, in series with the discharge resistor and the storage capacitor to ground), binds the named port, and stamps the generator provenance oncircuit.metadata['esd_generator']. Attach it viaScene(circuits=(...))and run the standardScene -> Simulation.fdtd -> Resultflow.ESD_STANDARD_DISCHARGE_RESISTANCE/ESD_STANDARD_STORAGE_CAPACITANCEexpose the default element values.Result.esd_generator(name)returns the generator-network provenance (versioned waveform metadata -- standard, revision, level voltage, model version -- plus the source-network element values and thesource_impedance_networkinjection tag), andResult.esd_generator_names()lists circuit-driven generators; the coupled port voltage/current time series lives on the associatedCircuitData(result.circuit(name)).- Validation coverage: an RC-load analytic cross-check (the coupled FDTD+MNA port voltage/current, ESD waveform through the 330 ohm/150 pF network into a resistive load, reproduced by a fully independent scipy state-equation integration of the hand-derived series-loop ODE with the measured/fit EM one-port; observed port-voltage cross-check rel. error ~7.8e-4 vs a 1e-2 gate, with a storage-capacitance-perturbation falsification driving it to ~4.5e-2); a coupled global energy-conservation gate in a closed PEC box (
S_source = dU_field + dU_circuit + D_circuitclosing to ~1.3e-4 of throughput with the raw-field/port-work field-link at ~5.2e-4, and a throughput-channel-imbalance falsification); generator provenance ride-through; and a circuit-driven end-to-end run (electrostatic pre-bias + circuit-driven ESD through theTerminalPort+ non-feedbackBreakdownMonitorstress). Known limitation (fail-closed, documented): the strong FDTD+MNA port coupling does not support conductive media, so the dynamic conductiveDielectricBreakdownfeedback cannot ride the circuit-driven port path in the current runtime (it stays on the ideal-current-injection path); the conductive combination raisesNotImplementedErroratprepare()and is pinned by a test.
- Capability level is stress-only: these monitors accumulate auditable field- and port-stress statistics during a standard FDTD run and compare them against declared material/component envelopes. They perform NO feedback into the field solve, do NOT switch conductivity, and do NOT model arc channels, latch-up, or device-failure probability. A recorded threshold exceedance is a stress indicator, never a failure prediction. Scenes with no such monitor are unaffected: adding a
BreakdownMonitorproduces bitwise-identical fields (verified) and there is no per-step cost when none is present. BreakdownMonitor(name, region=... | position=..., size=..., quantities=("electric_field","exposure","dissipated_energy"[, "damage"]), critical_field=..., minimum_duration=0.0, damage_exponent=None)accumulates, per cell and entirely on device (no host sync in the step loop), the runningmax|E|, the exceedance timeintegral H(|E| - critical_field) dt(Heaviside conventionH(0) = 1, i.e.|E| >= critical_fieldcounts), the longest contiguous exceedance duration, and an optional damage integralintegral (|E|/critical_field)^k dtaccrued only while exceeding. The scalar|E|is colocated onto Yee cell centers with the energy-consistent averaging (each component averaged along its two node-staggered axes); partial voxels are weighted by target-material occupancy only.Result.breakdown(name)returns a typedBreakdownStressDatawithpeak_field,peak_index,exceedance_duration,longest_exceedance_duration,qualifying_cell_count, occupancy-weightedexceedance_volume_time, optionaldamage_volume, per-cell device maps (max_field_map,exceedance_time_map,longest_exceedance_map,damage_map,qualifying_mask), alocations()index list of sustained-stress cells (longest contiguous run reachingminimum_duration), and provenance recording thresholds, the colocation convention, occupancy policy, model version, and capability level.Result.breakdown_names()lists breakdown records. No print-based event logs.ComponentRating(voltage=None, current=None, energy=None, pulse_width=None, model=None)declares an absolute-maximum envelope (at least one limit required; each populated limit must be positive), withmodelrecorded in provenance.ComponentStressMonitor(name, port=..., rating=..., voltage_series=..., current_series=...)binds a rating to recorded port time-series monitors. The boundportname is validated against the scene ports at prepare/compile time, so a typo'd port fails closed rather than binding silently.Result.component_stress(name)reduces the bound V(t)/I(t) series into a typedComponentStressData:power = V*I, cumulative dissipated energyintegral P dt(trapezoidal), peak voltage/current/power, total energy, a coarse measured pulse width, and an exceedance summary (rated vs measured, exceeded flag, margin) per channel versus the rating envelope. The float32 device reduction matches an independent float64 reference to tight tolerance. The bound voltage and current series must share a common time grid: mismatched sample counts or differing time axes are rejected (the voltage axis is never silently adopted).
ElectrostaticInitialCondition.from_result(dc_result, tolerance=1e-3)turns aResult(method="electrostatic")into an FDTD pre-bias, consumed bySimulation.fdtd(scene, ..., initial_condition=...). It maps the DC solution onto the Yee grid by interpolating the cell-centred electrostatic potential onto the primary nodes and taking Yee edge differencesE = -grad(phi); because the injected field is the discrete gradient of a nodal scalar, its discrete Yee curl is exactly zero, so H (and the CPML memory) start at zero and a lossless interior cell begins in a discrete FDTD steady state (H stays zero, E stays constant with no source). The initial magnetic field is zero (electrostatics carries no H pre-bias).- The electrostatic grid must be node-identical to the FDTD scene grid; a mismatch fails closed with a clear message. Compatible boundaries are the grid-non-extending ones (electrostatic
BoundarySpec.none()pairs with an FDTD PEC/PMC/periodic boundary); a PML boundary extends the FDTD grid and is therefore rejected by the grid-identity check. - After mapping, the run reports a dimensionless discrete-Gauss consistency residual
gauss_residual(the Yee divergence ofeps_fdtd * E_initversus the electrostatic free-charge density at the shared interior nodes, excluding conductor-adjacent nodes whose induced surface charge is not a free-charge source, normalised by the peak displacement-flux scale). Injection fails closed when the residual exceedstolerance(documented default1e-3), so a grossly inconsistent mapping is never silently seeded; a nonuniform / dielectric-interface pre-bias that carries a genuinely larger residual must raise the tolerance explicitly. - Fail-closed capability guards (
Simulation.fdtd(initial_condition=...)): a non-FDTD method, a distributed / multi-GPU run, a trainable / adjoint run, and a Bloch-periodic (complex-field) run are each rejected rather than silently dropping or mis-seeding the pre-bias. The validated case is a charged parallel-plate configuration whose plates are PEC-material structures. Result.electrostatic_prebiassurfaces the pre-bias provenance on the run result (DC solve iterations/residual/energy, per-terminal charges, the mapped-fieldgauss_residual, and the tolerance). Validation coverage: exact Yee-shape mapping, zero-discrete-curl of the mapped field, uniform-field recovery, grid-mismatch and PML-extension rejection, the fail-closed capability guards, an FDTD steady-state hold of a charged-plate pre-bias (bit-exact interior drift) with a checkerboard-corruption falsification, the Gauss-residual gate, and a cross-feature end-to-end run (pre-bias + IEC 61000-4-2 ESD terminal injection +DielectricBreakdown+BreakdownMonitor).
Capability level: deterministic-breakdown, uncalibrated (a comparison / conductive-path model, not a validated arc or device-failure predictor).
DielectricBreakdown(critical_field, post_breakdown_conductivity, minimum_duration=0.0, model="field_duration", state="latching", recovery=None, damage_parameters=None, ramp_time=None)is a public material descriptor composed into a scalarMaterial(breakdown=...). A breakdown-capable FDTD cell flips fromintacttoconductingonce its energy-consistent cell-center field magnitude|E|stays at or abovecritical_fieldfor a contiguousminimum_duration; on trigger the cell conductivity ramps linearly from its base value towardpost_breakdown_conductivityoverramp_time(default 10 time steps) and, understate="latching", stays conducting for the rest of the run. The conduction enters the standard semi-implicit lossy electric update, so it is unconditionally stable in the target conductivity; a prepare-time warning fires when0.5*sigma*dt/epsgrows large enough that the conducting cell becomes a poorly time-resolved PEC-like reflector.- v1 supports exactly
model="field_duration"andstate="latching"; any other model/state and the reservedrecovery/damage_parametersfields fail closed at construction. AMaterialcarrying a breakdown descriptor must be a scalar isotropic bulk medium: PEC, anisotropic-tensor, dispersive, instantaneous-nonlinear, time-modulated, and 2D-sheet materials are rejected. - Scenes with no breakdown material take the existing FDTD code path with zero added machinery (bitwise-identical fields): a cheap structure pre-scan gates all breakdown allocation, so a breakdown-free prepare never compiles the per-node layout. A scene whose breakdown cells never reach threshold is bitwise-identical to the same scene without the descriptor.
- Overlapping structures resolve breakdown capability last-writer-wins in the same priority order as the material compiler: a later, higher-priority non-breakdown structure that overwrites a breakdown region strips the breakdown descriptor from the cells it claims, so an overwritten cell never triggers a phantom breakdown.
Result.breakdown_datareturns a typedBreakdownResultData(present only for breakdown scenes): a deterministic event log ordered by(step, cell_index)ofBreakdownEvent(step, time, cell_index, position, material_id, field_before, state_before, state_after, deposited_energy_at_trigger), a per-cell final-state mask, per-cell cumulative breakdown-dissipated energyintegral(sigma_breakdown * |E|^2 dV dt), and the total dissipated energy.Result.breakdown_eventsexposes the event tuple directly. Events are collected in a bounded preallocated GPU buffer (capacity = breakdown-capable cell count, the exact latching upper bound) and transferred to host only at run end; a capacity overflow is a hard error, never a silent drop.- Deterministic single-GPU only this round: trainable scenes are rejected at
prepare()(the hard field-duration/latching switch is non-differentiable at the trigger time; a smooth surrogate is deferred), the frequency-domain solver is rejected (no dynamic conductivity update), and multi-GPU runs are rejected at prepare. Breakdown scenes run on the eager step path (no CUDA-graph capture of the coefficient-mutating step). - Validation coverage (
tests/breakdown/): manufactured golden trigger step (trigger_step = ceil(minimum_duration/dt) - 1), contiguous-timer reset, trigger-time dt convergence with a reported staircase error band, breakdown-dissipation closure against the analyticintegral(sigma_breakdown*|E|^2 dV dt), below-threshold six-field bitwise parity, no-breakdown plain-scene determinism and never-compiles-layout gates, structure-overlap last-writer-wins clearing, event-log determinism, and the trainable / multi-GPU / unsupported-model / buffer-overflow fail-closed guards.
Capability level: differentiable-surrogate (non-physical, non-regulatory). This is an optimization objective ONLY, not a breakdown model, a failure predictor, or a regulatory quantity. The hard field-duration/latching DielectricBreakdown feedback stays non-differentiable and trainable scenes that enable it are still rejected at prepare(); this surrogate is a separate non-feedback path that never drives the field solve.
SmoothBreakdownRisk(critical_field, width, reduction="sum", temperature=None, damage_exponent=None)defines a smooth, monotone functional of a recorded cell-center|E|(t)series. Itsevaluate(e_magnitude_series, dt, occupancy=None)computes, entirely in torch (autograd-preserving, no host sync), the soft exceedancep = sigmoid((|E| - critical_field)/width), the per-cell soft dwellsoft_duration = sum_t p dt, and reduces the occupancy-weighted per-cell dwell to a scalarrisk(reduction="sum"= soft over-stress dose in seconds,"mean", or"softmax"= temperature-weighted differentiable worst-cell dwell). An optionaldamage_exponentadds an auxiliarysoft_damagemap without changing the primaryrisk.evaluate_from_components(ex, ey, ez, dt)colocates raw node-overhang YeeEblocks through the exact energy-consistent averaging theBreakdownMonitoruses (colocate_electric_magnitude, now batched over a leading time axis), so the surrogate reads the same|E|as the physical stress accumulator, only softened.Result/monitor consumers receive a typedSmoothBreakdownRiskData(riskscalar, per-cellsoft_duration_map, optionalsoft_damage_map, diagnostic peaks, and provenance carryingnon_physical=True,non_regulatory=True, the sigmoid-margin definition, and the surrogate model version). The class name,capability_level, and provenance make the non-physical, non-regulatory nature unmistakable.- Validation coverage (
tests/breakdown/test_smooth_breakdown_risk.py): gradient flows from a source-amplitude and a material-screening parameter through a small differentiable|E|(t)scene to theriskscalar matching central differences to< 1e-4in float64 (with opposite-sign source vs material sensitivities), autograd reachestorch.nn.Parameterleaves, monotone increase ofriskin source amplitude,riskcollapse to numerically zero far below threshold (margin ~ -50 widths), batched colocation matching the per-step reduction and the analytic uniform-field magnitude, softmax reduction bracketed between the mean and the hard per-cell peak, occupancy zeroing, and fail-closed config/input validation. Recorded falsifications: detaching the field (kills the gradient), and a sigmoid sign flip (breaks both monotonicity and the far-below-threshold vanishing).
NetworkData.cascade(other, port_map=...)connects any set of ports of one N-port network to ports of another N-port network and reduces the joined ports out, returning a reducedNetworkData. The general multiport star connection is implemented from first principles asS'_EE = S_EE + S_EC * P * (I - S_CC * P)^-1 * S_CE(batched over frequency, differentiable, no third-party dependency). Connected ports must share a real reference impedance and the two networks must share a frequency grid; complex reference impedances, mismatched impedances, duplicate connections, empty maps, duplicate result names, and fully-connected (zero external port) results fail closed. Remaining ports keep this network's ports first, thenother's. Cascading through an ideal matched thru returns the original network and cascading two attenuators adds their attenuation in dB.NetworkData.terminate(port, gamma=... | impedance=...)closes one port with a reflection coefficient or a load impedance (scalar or per-frequency), returning the reduced network over the remaining ports viaS'_KK = S_KK + S_Kp * gamma * (1 - S_pp * gamma)^-1 * S_pK. For a two-port terminated on its second port this equals the closed-form input reflectionS11 + S12*gamma*S21/(1 - S22*gamma). Both helpers preserve autograd through the connection algebra and record their operation in the network transform history.- Independent raw-sample cross-check (
tests/rf/network/test_network_cascade_crosscheck.py): a bare three-port FDTD device S is measured by a port sweep, then a network's raw Touchstone samples (read directly, not the rational fit) are connected across two device ports withNetworkData.cascade; the resulting input reflection matches the same network embedded in the time domain (rational fit + state-space stepping) to< 1e-5across the band on a lossy and a reactive network. The reference path (raw samples + connection algebra) shares no code with the embedded path. - Multi-scenario passivity/conservation gates (
tests/rf/network/test_network_conservation.py): for a lossy two-port, a reactive two-port, and a four-port embedded run, each FDTD run gates terminal power balance (field-solve V + solved I vs field-solve V + model admittance), time-domain passivity (negligible generated energy and non-negative running cumulative net energy sampled over the pulse), and time-domain stability (net energy convergence and dynamic-state ring-down between run lengths T and 2T).
- The delay-free embedded-network same-step coupling solve now applies the constant direct-loop operator through two precomputed composite matvecs,
branch_current = (M^-1 C) @ state + (M^-1 D) @ v, instead of a per-step sequential pivoted-LU triangular substitution. The composite operators are formed once at prepare time by LU-solving the constantCandDagainst the loop matrixM = I + D * diag(Z_f)(not by naive matrix inversion), so the eager and CUDA-graph coupling paths become bitwise-identical and the per-step kernel-launch count of the connected 8-port/order-32 feedback block drops from 78 to 27 (65%). The op-stream before/after evidence is a reproducible artifact (docs/assessments/e4-network-coupling-op-stream-2026-07-19.json) and gate (tests/rf/network/test_network_coupling_op_stream.py); numerics are unchanged within the direct loop's floating-point roundoff bound and the existing multiport/ill-conditioned parity gates. - Embedded networks with explicit port delay now checkpoint and resume correctly: the frozen FDTD checkpoint schema captures the bidirectional reference-plane rings, the Thiran fractional-delay filter memory, and the shared ring cursor, so
run_until(k)+run(resume_from=...)reproduces the uninterrupted delayed-network fields and diagnostics bit-for-bit (tests/rf/network/test_network_delay_checkpoint.py). Previously a resumed delayed network silently restarted its reference planes from zero. - Differentiable adjoints of explicit-delay embedded networks remain fail-closed with a precise rejection reason (the bidirectional ring couples steps up to
max_delay_stepsapart, possibly across checkpoint segments, and the fractional-delay filter is an IIR recurrence, neither of which the segment-local network pullback reverses); forward runs including checkpoint/resume are fully supported. - WavePort embedding stays fail-closed with an accurate rejection message: an embedded state-space network couples through a scalar voltage/current terminal on a single lumped Yee edge (LumpedPort or resolved TerminalPort), but a WavePort is a modal port with no scalar time-domain terminal (V, I) contract. This is a missing design contract, not a bug.
- User-declared monitors (e.g.
PlaneMonitor) now ride throughWavePortdirect excitations andPortSweepResults instead of being silently dropped. A directPortExcitationof aWavePortis a single drive column, so its user monitors map unambiguously to that excitation and appear onResult.monitor(...)identical to a plain FDTD run of the injected mode. APortSweepdrives one channel per column: the flat top-levelResult.monitorscarries the first drive channel (recorded inResultmetadata asuser_monitor_drive_channel/user_monitor_frequency), and per-drive / per-frequency field payloads are preserved column-by-column inResult.array_run_data.column_results. The internal per-port ModeMonitors that extract the S-matrix stay hidden. This unblocks field-level inspection/falsification of the RF wave benches. benchmark/scenes/rf/lumped_open_short_match.py(coax_sol_scene) is rebuilt as a coax one-port short-open-load (SOL) calibration bench on the proven air coax line: a TEMWavePortfeed launches down the line to a de-embedded load plane terminated by a matched (reflectionless coax-through-PML, presenting Z0), a short (PEC plug), or an open (truncated inner rod below the outer-guide TM01 cutoff). The feed is now coupled to the load, so the three standards are mutually distinguishable (matched |Gamma| <= -20 dB; short/open |Gamma| ~ 1; open in the +1 class and short in the -1 class after short-referenced de-embedding), fixing the retired decoupled bench that read identical Gamma for every load. The open-end fringe capacitance shift is measured and documented.benchmark/scenes/rf/series_parallel_rlc.py(series_rlc_scene) is rebuilt to insert the series/parallel RLC as an in-line two-terminal element in the coax inner conductor ahead of a matched continuation, so the element carries the full axial line current and its resonance controls the feed reflection. The series |S11| notch tracks the analyticf0 = 1/(2*pi*sqrt(L C))(f_res*sqrt(C)constant to ~1%, moving by the analytic1/sqrt(C)ratio under a +/-20% C change) and the parallel anti-resonance peak moves monotonically with C, fixing the retired parasitic-dominated bench whose peak did not track C. The consistent ~13% parasitic downshift of the absolute resonance is measured and documented.
benchmark/scenes/antenna/half_wave_dipole.py(half_wave_dipole_scene) builds a center-fed thin-wire half-wave dipole (two collinear PEC arms joined by a node-boundLumpedPortgap feed) enclosed by aClosedSurfaceMonitorNF2FF box. A real FDTDScene -> Simulation -> Resultrun consumed throughResult.antenna(...)-- with NO monkeypatched surface currents or far field -- reproduces the canonical dipole: E-planesin^2(theta)-pattern correlation= 0.99 (measured 0.996 at the design frequency), peak directivity in the
2.15 dBiclass (measured 2.19 dBi, analytic 2.156), and radiated-vs-accepted power closure < 8% (measured ~4%). The input resistance sweeps through the thin-dipole73 Ohmradiation-resistance class within the band (measured 20 -> 88 Ohm, crossing 73 Ohm, with samples inside 60-90 Ohm). The input reactance carries a large positive delta-gap feed offset (the FDTD electrical resonance sits above the physical half-wave frequency); this is documented and deliberately not gated, rather than hidden.benchmark/scenes/antenna/patch.py(patch_antenna_scene) builds a probe-fed rectangular microstrip patch on a FINITE grounded dielectric slab (finite substrate + ground so the NF2FF box lies in a homogeneous air exterior; an infinite substrate running into the PML would leave no valid Huygens surface). All critical planes (groundz=0, patch undersidez=h, feed terminals) land on exact Yee nodes via integer-cellGridSpec.customcoordinates (arange*dx), becauseGridSpec.uniform's floatceilcell count can overshoot by one cell. The realResult.antenna(...)pipeline runs end to end and returns validAntennaData(six air-exterior faces per frequency, finite gains, positive radiated power, radiated-vs-accepted power closure). The matched-broadsideTM010resonance and theD >= 5 dBigate are a DOCUMENTED GAP recorded as a strict xfail: the probe on this thick finite-ground slab is reactance-dominated (|Gamma| ~ 1) and the pattern is off-broadside; feed/ground redesign and the external-reference cross-check are deferred to stage E2c.tests/rf/antenna/test_antenna_benchmark_e2e.pydrives both scenes through the real (non-monkeypatched)Result.antennapath on CUDA and enforces the gates above; the unit-level synthetic-surface reduction tests intest_result_antenna.pyare retained as the fast kernel coverage.
python -m benchmark.rf_tidy3d_references [scene ...]now performs a real adapter-driven external-reference-solver generation attempt for the RF / antenna scenes instead of only stamping pending markers. For each target it exports theScenethroughScene.to_tidy3d, gates on the export being physically runnable (at least one source), and only then estimates the cloud cost, enforces the per-scene FlexCredit budget, runs one cloud job, extracts the monitors, and writes the.h5cache plus a.generated.jsonrecord with the task id and cost. A non-runnable export (or any cloud failure) is recorded fail-closed as areference: pending-generationmarker carrying the concrete reason; it never fabricates a numerical cross-reference, and the analytic reference keeps binding.- The four owner-authorized targets (
rf/coax_thru,rf/lumped_open_short_match,antenna/half_wave_dipole,antenna/patch) currently fail-close at the runnable gate withsources=0: their excitation is port-driven (aWavePortTEM launch underPortSweep/PortExcitation, or aLumpedPortwire-gap / probe feed), and the adapter's source conversion has no port/lumped mapping, so the exported reference simulation has nothing to drive. Generation is refused BEFORE any cloud cost is incurred (zero FlexCredits spent); mapping port/lumped excitation to the reference solver is a deferred adapter feature. The per-scene outcome is recorded in an## RF / antenna external reference generationsection ofbenchmark/RESULTS.md. - The FDTD antenna scenes are now registered in the RF validation harness:
python -m benchmark rf antenna/half_wave_dipole antenna/patchruns the realResult.antennapath and writes an## Antenna wave-level validationsection tobenchmark/RESULTS.md(each scene family owns its own section, so an antenna-only run does not overwrite the RF section).antenna/half_wave_dipoleis a radiation-physics PASS (broadside directivity ~2.19 dBi, E-plane sin^2 pattern, radiated-vs-accepted power closure, radiation resistance through the 73 Ohm class);antenna/patchis a pipeline pass with a documented off-broadside physics gap. tests/rf/wave_validation/test_rf_reference_generation.pycovers the M3 wiring: the four targets export source-less and fail-close without fabricating a cache, and the runnable -> cloud -> cache branch is proven reachable (gate forced open with a stubbed cloud run) so the wiring is not a vacuous always-pending stub.
- The transverse full-vector mode eigensolver gains a genuinely Yee-staggered operator builder (
_build_yee_transverse_operator_sparse) that keeps each transverse electric component on its own Yee location (Euon the u-half / v-node grid,Evon the u-node / v-half grid) — the exact 2D restriction of the 3D Yee cell. LongitudinalEz/Hzare eliminated analytically to give a real symmetricP et = beta^2 eteigenproblem onet = (Eu, Ev), with the metallic walls treated symmetrically by construction (Dirichlet for the tangential component, natural/Neumann for the normal component). For a homogeneous cross-section it reproduces the closed-form discrete waveguide eigenpairs to machine precision (TE10/TE20sin-profile correlation >= 0.9999 andcheckerboard_fraction < 0.05, versus the 0.51–0.59 correlation cap and> 0.35checkerboard of the legacy centered branch), and its per-component stencil graph is connected (no odd/even sublattice decoupling). Non-magnetic (mu = 1) dielectric cross-sections only; diagonal per-component permittivity is supported at the correct Yee sample locations for the inhomogeneous (hybrid-mode) case. This builder is not yet wired into the mode selector (that integration, plus the microstrip / differential-pair hybrid gates and un-xfailing the TE10 selector pins, is the E1b deliverable).
- The homogeneous non-magnetic full-vector mode path (hollow metallic waveguides, uniformly dielectric-filled guides, and free-space
WavePortapertures) now solves on the Yee-staggered transverse operator end to end: the selector interpolates the reconstructed transverse fields onto the aperture node grid, so a closed metallic guide returns a genuineTE10whoseEzprofile is a clean full-gridsin(pi y/a)(sin-correlation>= 0.9999across thedx = 0.05 … 0.01tiers and at6 fc), replacing the retired centered branch whose sublattice decoupling capped the correlation at0.51–0.59. The propagation constant,sin-profile, and modal wave impedance now match the analyticTE10references, and the hardened selector filters (transverse-null-spacebeta -> k0rejection, checkerboard/duplicate diagnostics, forward-power and requested-polarization family selection, fail-closed raise on a genuinely absent requested mode) are preserved. A uniformly dielectric-filled aperture carries its real permittivity (the operator equals the vacuum operator plus a scalar(eps_r - 1) k0^2shift, sobeta^2 = eps_r k0^2 - kc^2, not the vacuum value). Routing to this path requiresmu = 1: inhomogeneous (dielectric-graded) and magnetic (mu_r != 1, uniform or graded) cross-sections continue on the diagonal-anisotropic operator, which threadsmuthrough the eliminated longitudinal fields. - The transverse operator's inhomogeneous hybrid capability is validated directly: a half-filled parallel-plate cross-section reproduces the 1D slab-loaded
LSESturm-Liouville spectrum to machine precision and converges to the analytic transverse-resonance (k1 cot(k1 d) + k2 cot(k2 (a-d)) = 0) propagation constant, with the mode uniform along the plates,Ev-polarized, and concentrated in the high-permittivity region.
- The Yee-staggered transverse mode operator now supports interior perfect-conductor masking: a transverse-field sample whose staggered Yee location falls inside a conductor is eliminated (Dirichlet 0) with the same symmetric row/column removal as the outer metallic walls, and a longitudinal node inside a conductor drops from the
eps_wwdivergence coupling — no penalty terms, no operator asymmetry. Conductor occupancy is rasterized onto the three staggered component grids with the same node→half placement the dielectric staggering uses, and a connectivity check reports the number of conductor-free regions and distinct conductors and fails closed on a degenerate pinch (a conductor-free node fully surrounded by conductor). This serves the guided (non-TEM, hybrid) interior-PEC modes; validated against a PEC-septum half-guide whose transverse cutoff rises analytically fromk~c = pi/2tok~c = piwhen the septum splits the guide (maskedbetawithin 0.5% ofsqrt(k0^2 - pi^2)). The curl-curlbeta^2operator does carry the gradient TEM branch in its spectrum (a curl-free-grad(phi)field withdiv(eps grad phi) = 0is an exact eigenvector atbeta^2 = eps k0^2); the shipped occupancy rasterization (threshold 0.5) eliminates the conductor-surface straddling normal-Esamples where the TEM field energy concentrates, so the TEM branch is absent from this masked reduced operator by that masking choice rather than as a structural property. A TEM request on the masked operator therefore fails closed and routes to the quasi-static engine (a strictly-interior keep-straddle rasterization would recover the exact TEM eigenvalue but would leave one-cell-thick conductor sheets unmasked). - A quasi-static electrostatic line-mode engine solves the TEM/quasi-TEM interior-PEC transmission-line families (coax, microstrip, differential pair) via the capacitance ratio
eps_eff = C / C0from a variable-coefficientdiv(eps grad phi) = 0solve, withbeta = k0 sqrt(eps_eff). Boundary-connected conductors are grounded and each isolated interior conductor is driven by a caller-supplied potential (single-signal lines, or even[1,1]/ odd[1,-1]for a pair). Verified: a uniform coax returnseps_eff = eps_rexactly andbeta = k0 sqrt(eps_r)matching the legacy electrostatic path; a shielded microstrip (eps_r = 4,W/h = 2) returnseps_effwithin 0.7% of the Hammerstad–Jensen (1980) closed form (pre-registered 3% gate); a differential pair returns two distinct, physical even/odd modes (eveneps_eff> odd) whose potential fields are mirror-symmetric / antisymmetric about the pair centreline.
Production quasi-TEM wave-level benches (microstrip / differential pair) and patch feed diagnosis (F2b)
- The inhomogeneous interior-PEC quasi-TEM mode is now wired into the production WavePort path: when the closed-form uniform-fill TEM solve fails closed on a substrate+air cross-section, a non-magnetic aperture falls through to the quasi-static electrostatic line-mode engine (
mode_solver_kind = "quasistatic_line_torch",eps_eff = C/C0), while a uniform (air) line keeps the closed-form electrostatic solve and a magnetic inhomogeneous line re-raises. The drive potentials for a coupled aperture are selected from the isolated-conductor count and mode index (single-signal[1]; even[1,1]/ odd[1,-1]for a two-signal pair). python -m benchmark rf rf/microstrip_two_portandrf/differential_pairare unblocked (were BLOCKED). Both scenes were rebuilt on the coax_thru precedent: the measurement ports sit near the origin so the single-precision current-contour planes stay on the Yee half-grid, the ground/substrate/strips run through the computational PML so the launched waves terminate, and integer-cell node arrays (GridSpec.custom(arange*dx)) put every conductor face and contour on exact Yee nodes/half-nodes. The terminated two-/four-port yields a well-conditioned (cond(A) ~ 1.2-1.3) S-matrix (B = S*Aextraction); against the shared 1.10 passivity precedent the microstrip two-port is passive (max sv ~1.09, recordedgap) while the differential pair's max sv ~1.18 exceeds it and is recordedfail, not forced. The differential pair converts to mixed-mode with|Sdd21| != |Scc21|(genuine even/odd coupling) and|Sdc21| ~ 0(mirror-symmetry). The absolute quasi-TEMeps_effcarries a documented, first-order resolution gap (the thin substrate under-loads the discrete field at feasibledx, converging toward Hammerstad with aperture resolution) — recorded, not forced to pass. Gates:tests/rf/wave_validation/test_microstrip_diffpair_wave_level.py.- The probe-fed patch antenna feed gained a galvanic PEC probe via (patch underside to a single-cell lumped-port gap above ground), cutting the feed reactance ~5x. A wide-band diagnosis (recorded in the F2b acceptance doc) shows the patch still does not resonate at feasible resolution (
Re(Zin) < 4Ohm, no resonance peak across 2-8 GHz, capacitive reactance, broadside-null via-monopole pattern); the matched-broadsideTM010gate stays a fail-closed strict xfail pending a wire-bound clean-gap feed + larger finite ground + on-resonance drive.
python -m benchmark rf rf/rectangular_waveguideis now a committed wave-level PASS on the Yee-staggered transverse operator. The terminated hollow-guide TE10 two-port S-matrix is assembled by solvingB = S*Aacross the drive columns and gated on extraction conditioning (cond(A) <= 10) plus post-solve passivity (max singular value<= 1.05), thenbeta(omega)fromarg(S21)/Lis compared against the analytic TE10 dispersionbeta = sqrt(k0^2 - (pi/a)^2)across 11 frequencies above cutoff. Measured (dx=0.02):sin(pi y/a)-correlation 1.0000,cond(A) ~ 1.09, max singular value ~1.0007,|S11|best ~2e-4,|S21| ~ 1, and beta median relative error ~0.05% against a pre-registered 1% tolerance (all three grid tiers pass). The mode-shape correlation check is retained as a fail-closed regression guard (< 0.9 records BLOCKED rather than reporting a spurious S-matrix). Committed gate:tests/rf/wave_validation/test_waveguide_wave_level.py(conditioning + passivity + beta, plus a reference-plane-length falsification).- The external-reference-solver generation path (
python -m benchmark.rf_tidy3d_references) gains a runnablerf/rectangular_waveguidetarget: a TE10ModeSource-driven guide with twoModeMonitorplanes exports through the interoperability adapter with a genuine reference source (sources=1), so one cloud job produces an S-parameter cross-reference. The reference forward-mode-amplitude phase constant confirms the analytic TE10beta(omega)to ~1.2% median over the band; the analytic dispersion remains the binding first-line reference. The remaining port/lumped-driven RF/antenna targets still fail-close at thesources=0runnable gate (deferred adapter source mapping) and spend no cloud credits.
- Validation evidence, not a new public feature: a multi-scenario coupled
FDTD + MNA global energy-residual suite
(
tests/rf/circuits/test_circuit_conservation.py) closes the whole-system energy balancesource injected = delta EM stored + circuit dissipated + circuit storedfor three strongly coupled scenarios driven from an in-circuit source in a closed (PEC, zero-outflow) vacuum box: (a) a resistive load on a driven lumped terminal port, (b) a resonant series RLC assembled from MNA primitives (not the nativeSeriesRLC), and (c) a controlled-source (VCVS) network. The EM-stored term is measured from the raw Yee E/H fields (0.5 eps E^2 + 0.5 mu H(n-1/2).H(n+1/2)), independent of the port record; the circuit terms come from the MNA companion state. The suite annotates gate classes honestly — the source/dissipation/circuit-store channels are consistency-class (Tellegen / companion algebra) and the genuine two-sided content is the field-link equalitydelta EM stored == -(port work)(raw fields vs MNA V/I record, no shared code) — and each headline channel has a recorded falsification. Pre-registered tolerances: global residual<= 5e-3of throughput, field-link residual<= 2e-2of peak field energy. - Validation evidence, not a new public feature (F1b, independent circuit
cross-check):
tests/rf/circuits/test_circuit_independent_crosscheck.pycharacterizes an EM one-port (open, PML-terminated box with aLumpedPort) by a passive port-admittance sweep, fits the measuredY_em(f)to a low-order stable rational (fit_rational, data-fitting only), derives the series-loop equivalent-circuit ODE state equations by hand, and integrates them withscipy.integrate.solve_ivpfor a different drive/resistance than the sweep. The independent transient reproduces the coupled FDTD + MNA run's port voltage to~1.2e-5(relative, headline gate) with no shared runtime code between the two paths, lifting the coupled circuit transient off the consistency class. A recorded falsification perturbs the MNA field-port companion conductance and shows the cross-check goes red.
- The interoperability adapter (
Scene.to_tidy3d) now maps RF port excitations to reference-solver drive constructs, so port-driven scenes export as genuinely runnable reference simulations (previously they exported withsources = 0and fail-closed at the generation runnable gate). AWavePortTEM aperture maps to a reference modal launch (ModeSource) of its fundamental mode plus a receivingModeMonitorat every wave-port aperture, so a terminated port line exports with S-parameter monitors. ALumpedPortdelta-gap feed — wire-bound (dipole wire-gap) or coordinate-bound (patch probe) — maps to its equivalent current injection: aUniformCurrentSourceelectric-current filament spanning the feed gap along the negative→positive voltage-path axis, interpreted as a unit total feed current. The near-field-to-far-fieldClosedSurfaceMonitorbox lowers to its six face field monitors through the existing monitor path, so the antenna near-field surface exports intact. A single export uses one documented drive convention (drive the first declared port, index 0); a full N-port scattering reference is one export per driven port. Ports carry nosource_time, so the adapter synthesizes a broadband Gaussian drive covering the requested frequency band. Gates:tests/api/adapters/tidy3d/test_port_source_mapping.py. - The external-reference generation path (
python -m benchmark.rf_tidy3d_references) now generates all five RF/antenna reference caches —rf/rectangular_waveguide,rf/coax_thru,rf/lumped_open_short_match,antenna/half_wave_dipole,antenna/patch— one authorized cloud job each (0.025 FlexCredits each), with task ids and costs recorded inbenchmark/RESULTS.mdand the F2c acceptance doc. A new--from-markersmode rebuilds theRESULTS.mdaggregate table from the on-disk generation markers without any cloud call, so per-scene runs can be re-aggregated without re-spending. The analytic transmission-line / waveguide / dipole references remain the binding first-line gate.
ArrayBasisData.scene_gradient_vjp(columns=..., weights=..., parameters=..., objective=...)aggregates the per-column adjoints of the linear beam combineE = sum_n w_n e_nback onto trainable scene parameters (a designBox/MaterialRegiondensity), delivering scene/material/geometry gradients that the retained detached-column basis could not. The caller re-runs each drive column's forward under autograd and passes the resulting live embedded-pattern columns ((e_theta_n, e_phi_n)of shape[F, T, P]); the method forms the combined far field, takes the combined-field cotangentcot_E = autograd.grad(L, E), seeds each column with the derived weight conjugationconj(w_n) * cot_E(= w_n^* . (dL/dE)^*, exactly the PyTorch complex-product backward of the combine), and sums the per-column vector-Jacobian products in a caller-controllable deterministicreduction_order. The seeded per-column sum is bit-identical to end-to-end autograd of the combined objective for a fixed order and agrees across orders to floating-point round-off; a central-difference gate on a genuine two-column FDTD array (trainableMaterialRegiondensity, NF2FF far-field columns) matches the aggregated gradient at the FDTD-adjoint tolerance. Weights accept a frequency-flat[N]or exact[F, N]incident power-wave vector; either anobjectivecallable on the combined field or pre-computedfield_cotangentsmay drive the VJP. Weight gradients throughcombine()are unchanged (regression-gated). Detached columns, a no-contribution parameter set, batched[B, F, N]weights, wrong column counts, and shape/dtype/device mismatches fail closed withValueError/TypeError. The 2-GPU ensemble aggregation of the same per-column VJPs is the F3b follow-on.
aggregate_scene_gradient_vjp(basis, columns=..., parameters=..., weights=..., objective=...)reduces per-column scene-gradient VJPs whose columns may live on different devices, returning anAggregatedSceneGradient(the reduced gradient plus provenance: the fixed reduction order, the reduction device, the per-column devices, and the port order). The combined field and its cotangent are formed on the reduction device from detached column values; each column is seeded withconj(w_n) * cot_Eon its own device, back-propagated there, moved to the reduction device, and summed in a fixed public-port order.parametersis a per-column sequence of trainable leaf(s) — the same object for every column in the single-device case, or a per-device replica of the shared design in the multi-GPU case; the summed per-column VJPs are the gradient of that shared design. The reduction reproduces the single-deviceArrayBasisData.scene_gradient_vjpbit-for-bit for the same order.ensemble_scene_gradient_vjp(basis, column_forward=..., weights=..., execution=mw.MultiGPUExecution.ensemble(devices=...), objective=...)distributes the per-column forwards over the ensemble device pool as independent tasks (eachcolumn_forward(index, device)runs columnindex's forward ondeviceunder autograd and returns its live embedded far-field column plus the trainable leaf(s)), then reduces withaggregate_scene_gradient_vjp. The per-column adjoint is not routed throughrun_many(which refuses trainable simulations); the pool only places and orders the forwards, and the seeded backward + deterministic reduction run on the caller thread. 1-GPU-vs-2-GPU aggregated-gradient parity is bitwise on homogeneous GPUs (measured maxabsdiff 0 on both a synthetic float64 map and a real two-column FDTD array with a trainableMaterialRegiondensity split one column per GPU). A non-MultiGPUExecutionexecution, a failed column forward, detached columns, an inconsistent per-column parameter structure, and the F3a validation contracts all fail closed. The NCCL joint-solve adjoint remains out of scope (independent Simulations only); no timing/throughput scaling is claimed.
- FDTD material coefficients are now sampled edge-native: the diagonal
background permittivity / permeability and the static electric / magnetic
conductivities are evaluated directly at each Yee component's own staggered
location (
Ex/Ey/Ezedges,Hx/Hy/Hzfaces), with the SDF occupancy, the interface normal, and anyMaterialRegiondensity sampled there and the polarized (Kottke) or arithmetic subpixel blend formed at that location. This replaces the previous node-centered blend followed by an arithmetic node->edge average (the "smear"), which applied the interface operator at the wrong place and then interpolated it. The node-centered model is still produced as the canonical representation for summaries, monitors, the mode solver, and the SAR / mass models; only the FDTD update coefficients switch to the edge fields. This is the standard path for isotropic, axis-aligned diagonal-anisotropic, andPerturbationMediumfamilies (with or without static conductivity, dispersion, nonlinearity, or modulation layered on the edge-native background). Full off-diagonal anisotropy, 2D sheets, and surface-impedance metals fail closed to the node->edge path (unchanged capability scope; no guard added or removed). - The differentiable material VJP follows the forward: when edge-native sampling is active the permittivity sensitivity back-propagates directly through the edge fields (no node->edge transpose), so geometry, region-density, and diagonal-anisotropy gradients stay consistent by construction.
- The benchmark harness default is
pec="staircase"(F4 briefly made itpec="conformal"; that was reverted in K1 after the conformal PEC edge fill was given compact support and the residual spurious absorption on cut edges was measured). Scenes that want the conformal boundary opt in per scene with an explicitSubpixelSpec. Dielectric scenes are unaffected either way.
- A trainable Box-
MaterialRegiondensity scene now backpropagates over a single-node one-process-per-GPU NCCL launch (transport="nccl",torchrun --nproc-per-node=<gpus>), not only the in-processtransport="cuda_p2p"runtime. The per-rank collective reverse driver (run_nccl_distributed_reverse) runs the distributed forward with per-rank checkpoints, replays each checkpoint segment with NCCL forward-replay dict halos, seeds a separable local objective per rank (a point monitor is owned by exactly one rank; every other rank seeds zero and receives adjoint only through the transposed NCCL halos), runs the transposed reverse with this track's NCCL adjoint halos, gathers the per-rankgrad_epsowned slabs to rank 0, and runs the single-GPU material pullback once on rank 0. The open-boundary standard update and the x-CPML absorbing update (including objectives whose sensitivity threads the CPML psi memory recursion) are both supported. - Objective + gathered-
grad_epsgradient parity against the single-process single-GPU adjoint is gated on two processes for the open-boundary standard geometry and the psi-active x-CPML geometry (loss rtol 5e-5 / atol 5e-6; grad rtol 1e-4 with an atol floor 1e-6*max|grad|), the gatheredgrad_epsis bitwise reproducible across repeated backward passes, and an unsupported-adjoint scene (e.g. a trainable density on a legacy graded-sigma absorber) is rejected cleanly and symmetrically on every rank without deadlocking. - The reverse gradient is load-safe: the headline parity gates (standard / x-CPML / seam-spanning plane) hold at the same honest 1e-4-class tolerance while both GPUs are saturated by a co-tenant burner (a committed stress gate spawns the load), and a rank-0-only gather-capacity failure raises collectively on every rank without hanging. This closes a caching-allocator cross-stream reuse hazard in which the reverse/replay NCCL halos previously ran on a non-default stream while their per-step adjoint planes were allocated on the default stream, deterministically corrupting the partition-seam gradient under concurrent GPU load; the halos now run on the current (default) stream so allocation-stream == use-stream.
- The forward-only NCCL fence still rejects a trainable/monitor scene on the plain NCCL forward path; the relaxation is internal to the verified adjoint driver.
- A y/z-normal
PlaneMonitorobjective is now also supported on the NCCL adjoint driver: the plane is tiled across the x seam, so asum|spectrum|^2objective restricted to each rank's owned plane strip (the forward monitor-merge owned-local x slice) is separable -- the world sum reproduces the single-process full-plane objective with every seam cell counted on exactly one rank, and each rank seeds only its owned strip (no cross-rank cotangent scatter). Two-process acceptance gates the owned-strip objective and its gatheredgrad_epsagainst the single-GPU full-plane adjoint on a plane spanning the seam, plus a seam-ownership falsification (summing each rank's full local strip double-counts the live seam cell and reddens parity). Flux / mode / finite-plane / x-normal plane objectives stay fail-closed (they need seam-crossing tangential-field assembly whose cotangent scatter is not wired), and the in-processtransport="cuda_p2p"bridge continues to reject every tiled monitor. - The NCCL adjoint driver reproduces the single-GPU reference to ~2e-7 of scale
both on exclusive GPUs and under a saturating co-tenant, so no shared-GPU caveat
applies to it. The in-process
transport="cuda_p2p"bridge is now load-safe too: its own cross-stream drift was a distinct hazard from the NCCL path -- a checkpoint-capture happens-before race, not the allocator-reuse class. The mid-forward checkpoint clone read the persistent field storage on the device default stream while the forward field updates run on each shard's compute stream, so nothing ordered the next update after the clone and under load the update tore the snapshot, drifting the replayed seam gradient (~8e-2) while the forward output stayed bitwise clean. Cloning the checkpoint on the shard's compute stream serializes previous-update -> clone -> next-update on one stream, restoring ~2e-7 parity under a saturating co-tenant burner. A committed stressed gate holds the standard and x-CPML 1-vs-2-GPU parity over six rounds under load at the unchanged honest tolerances, with a falsification that reverts the clone to the default stream and shows the seam drift return. Seedocs/assessments/i1-p2p-race-acceptance-2026-07-21.md.
SubpixelSpec(pec="conformal")now derives its per-edge fill from the geometric coverage fraction of each Yee E edge — the union PEC signed distance interpolated between the edge's two endpoint nodes — instead of the two-node average of thetanh-smoothed node occupancy. Because the conformal open fraction multiplies the electric update every step, a fillfis an effective conductivityeps*f/dton that edge, so the fill must be zero wherever the conductor is not. The new fill is exactly0on an edge the surface does not reach and exactly1on an edge wholly inside, which removes the multi-cell lossy shell the smoothed occupancy painted around every conductor (5298 vacuum edges per component on a 0.2 m cube atdx = 0.02) and restores the hard short on the face itself (the previous0.53fill left a 405 ohm/sq sheet reflecting ~32% instead of ~100%). A grid-aligned PEC slab underconformalnow reproduces thestaircaseresult bit for bit, at both the compiled-mask level and the field level.- Capability change:
conformalno longer places a flat, grid-parallel wall sub-cell — such a wall cuts no tangential edge, so conformal is exactly staircase there. Sub-cell resolution of genuinely cut (curved / oblique) conductors is retained and improved: the compiled conformal mask reproduces a sphere's volume to 0.74% where staircase gives 1.85% (r = 0.11,dx = 0.02). Placing a flat grid-parallel wall sub-cell requires the area-scaled (Dey–Mittra) magnetic update, which remains future work. - Documented residual: the soft short is still lossy on genuinely cut edges. A
closed PEC cavity holding a PEC sphere retains
0.450of its energy after 5200 source-free steps underconformalversus1.000understaircase(the pre-fix smoothed-occupancy path retained0.125).staircaseis therefore the default everywhere, including the benchmark harness;conformalis opt-in per scene. - Gates:
tests/materials/compiler/test_pec_conformal_alignment.py(compiled masks, CPU) andtests/validation/physics/test_pec_conformal.py(field level, CUDA). Evidence and falsifications indocs/assessments/k1-conformal-pec-fix-2026-07-22.md.
- GitHub Actions builds Linux CUDA wheels inside manylinux_2_28 and Windows wheels on Windows Server 2022. Release fatbins include native SM87 SASS alongside the maintained CUDA 12.8 architecture set.
- A
witwin-maxwell-v*tag runs the complete Stable ABI compatibility matrix, publishes the verified wheel and source artifacts through trusted publishing, and creates the corresponding release with those artifacts attached.