A source adds a time-dependent excitation to one field component at a grid location. It starts a wave by injecting values into the otherwise evolving electromagnetic field.
Simulation::add_source(i, j, component, waveform) registers the position, component, and a callable waveform. For example, an $E_z$ source can use the built-in Gaussian pulse:
sim.add_source(128, 128, fdtd::TMz::Component::Ez, fdtd::TMz::Gaussian::from_steps(sim.dt(), 20));
The injection is additive: it adds to the updated field rather than overwriting it. Sources targeting the same component and cell are summed before their samples are passed to the backend. Different sources can therefore overlap naturally.
Waveforms are evaluated at the electric or magnetic sample time, preserving the half-step offset used by leapfrog stepping. Repeated runs continue sampling from the current simulation time.
Registration rejects positions outside the grid, inside the PML, or on samples that the selected field update does not advance. This prevents an excitation from being placed where boundary handling would discard it.