Quick Start
Build your first scene and run a simulation with WiTwin Maxwell
Overview#
Every WiTwin Maxwell workflow follows the same pattern:
- Define a
Scenewith domain, grid, and boundary - Add structures, sources, and monitors
- Create a
Simulationand callsim.run() - Read the result
Your First Simulation#
The example below places a dielectric cube in a domain, adds a point dipole source and a probe monitor, runs an FDTD simulation, and backpropagates from the probe value to the cube position.
import torch
import witwin.maxwell as mw
# Trainable cube position
box_x = torch.tensor(0.10, device="cuda", requires_grad=True)
box_position = torch.stack((box_x, box_x.new_tensor(0.0), box_x.new_tensor(0.06)))
# Build scene
scene = mw.Scene(
domain=mw.Domain(bounds=((-0.24, 0.24), (-0.24, 0.24), (-0.24, 0.24))),
grid=mw.GridSpec.uniform(0.12),
boundary=mw.BoundarySpec.pml(num_layers=2, strength=1.0),
device="cuda",
subpixel_samples=5,
)
scene.add_structure(
mw.Structure(
name="cube",
geometry=mw.Box(position=box_position, size=(0.18, 0.18, 0.18)),
material=mw.Material(eps_r=20.0),
)
)
scene.add_source(
mw.PointDipole(
position=(0.0, 0.0, -0.06),
polarization="Ez",
width=0.04,
source_time=mw.GaussianPulse(
frequency=1.0e9,
fwidth=0.25e9,
amplitude=50.0,
),
)
)
scene.add_monitor(mw.PointMonitor("probe", (0.0, 0.0, 0.06), fields=("Ez",)))
# Run simulation
sim = mw.Simulation.fdtd(
scene,
frequencies=[1.0e9],
run_time=mw.TimeConfig(time_steps=32),
spectral_sampler=mw.SpectralSampler(window="none"),
full_field_dft=True,
)
result = sim.run()
# Backpropagate from probe
probe = result.monitor("probe")["data"]
loss = torch.abs(probe) ** 2
loss.backward()
print("probe =", probe)
print("d(loss)/d(box_x) =", box_x.grad)What Just Happened#
Sceneholds the domain, grid, boundary, structures, sources, and monitorsDomaindefines the simulation volume boundsGridSpec.uniform()sets the spatial resolutionBoundarySpec.pml()adds perfectly matched layer absorbing boundariesPointDipoleinjects an EM source with a Gaussian pulse waveformPointMonitorrecords the field at a specific pointSimulation.fdtd()configures and dispatches the FDTD solverresult.monitor("probe")reads the recorded field dataloss.backward()propagates gradients back tobox_x
Plotting Field Slices#
After running a simulation you can plot a vertical field slice:
result.plot.field(axis="y", position=0.0, component="abs", field_log_scale=True)Next Steps#
- Scene API — full scene construction reference
- Sources API — all source types
- Simulation API — simulation configuration reference