Sources and Monitors

This guide covers all available electromagnetic sources and field monitoring capabilities in Prismo.

Overview

Prismo provides several types of sources for exciting electromagnetic fields:

  • Point sources: Dipoles and point excitations

  • Gaussian beams: Focused beams with Gaussian profiles

  • Plane waves: Uniform waves (basic and TFSF)

  • Custom sources: User-defined field patterns

And comprehensive monitoring capabilities:

  • Field monitors: Record E and H fields

  • Time-domain: Full time evolution

  • Frequency-domain: DFT at specific frequencies


Sources

Point Sources

Electric Dipole

An electric dipole radiates electromagnetic waves from a point source:

from prismo import ElectricDipole

dipole = ElectricDipole(
    position=(1.0e-6, 1.0e-6, 0.0),    # Position in meters
    polarization="y",                   # "x", "y", or "z"
    frequency=200e12,                   # 200 THz
    pulse=True,                         # Pulsed excitation
    pulse_width=10e-15,                 # 10 fs pulse
    amplitude=1.0,
    phase=0.0,                          # Phase offset in radians
)
sim.add_source(dipole)

Parameters:

  • position: (x, y, z) coordinates in meters

  • polarization: Direction of electric field oscillation

  • frequency: Center frequency in Hz

  • pulse: True for Gaussian pulse, False for continuous wave

  • pulse_width: Temporal width for Gaussian pulse (seconds)

  • amplitude: Peak amplitude (V/m)

  • phase: Phase offset in radians

Use cases:

  • Single-molecule emission

  • Antenna radiation patterns

  • Near-field studies

Magnetic Dipole

Similar to electric dipole but excites magnetic field:

from prismo import MagneticDipole

mag_dipole = MagneticDipole(
    position=(1.0e-6, 1.0e-6, 0.0),
    polarization="z",
    frequency=200e12,
    pulse=False,  # Continuous wave
    amplitude=1.0,
)
sim.add_source(mag_dipole)

Use cases:

  • Magnetic materials

  • Coil/loop antennas

  • Complementary to electric dipoles

Gaussian Beam Sources

Gaussian beams provide focused electromagnetic fields:

from prismo import GaussianBeamSource

beam = GaussianBeamSource(
    center=(1.0e-6, 1.5e-6, 0.0),      # Beam center
    size=(0.0, 1.0e-6, 0.0),           # Source region (line source)
    direction="x",                      # Propagation direction
    polarization="y",                   # E-field polarization
    frequency=193.4e12,                 # 193.4 THz (1550 nm)
    beam_waist=0.5e-6,                 # 500 nm waist
    pulse=True,
    pulse_width=10e-15,
    amplitude=1.0,
)
sim.add_source(beam)

Parameters:

  • center: Beam center position (m)

  • size: Source region dimensions (m)

  • direction: Propagation direction (“x”, “y”, “z”)

  • polarization: E-field polarization (perpendicular to direction)

  • frequency: Center frequency (Hz)

  • beam_waist: Minimum beam radius (m)

  • pulse: Pulsed or continuous

  • pulse_width: Pulse duration (s)

Calculated properties:

  • Wavelength: λ = c/f

  • Rayleigh range: zᵣ = πw₀²/λ

  • Wave number: k = 2π/λ

Use cases:

  • Focused illumination

  • Coupling to waveguides

  • Beam propagation studies

Plane Wave Sources

Basic Plane Wave

Uniform plane wave excitation:

from prismo import PlaneWaveSource

plane_wave = PlaneWaveSource(
    center=(0.5e-6, 1.0e-6, 0.0),
    size=(1.5e-6, 0.0, 0.0),          # Line source along x
    direction="+y",                    # Propagate in +y
    polarization="z",                  # Ez polarization
    frequency=193.4e12,
    pulse=False,                       # Continuous wave
    amplitude=1.0,
)
sim.add_source(plane_wave)

Direction options:

  • "+x", "-x": Positive/negative x direction

  • "+y", "-y": Positive/negative y direction

  • "+z", "-z": Positive/negative z direction


Waveforms

All sources support different temporal waveforms:

Gaussian Pulse

from prismo.sources import GaussianPulse

waveform = GaussianPulse(
    frequency=200e12,      # Center frequency
    pulse_width=10e-15,    # Temporal width (FWHM)
    amplitude=1.0,
    phase=0.0,
)

Equation:

E(t) = A exp(-((t-t₀)/τ)²) cos(2πf₀t + φ)

Continuous Wave

from prismo.sources import ContinuousWave

waveform = ContinuousWave(
    frequency=200e12,
    amplitude=1.0,
    phase=0.0,
)

Equation:

E(t) = A cos(2πf₀t + φ)

Ricker Wavelet

Second derivative of Gaussian (Mexican hat):

from prismo.sources import RickerWavelet

waveform = RickerWavelet(
    frequency=200e12,
    pulse_width=10e-15,
    amplitude=1.0,
)

Custom Waveform

Define your own waveform:

from prismo.sources import CustomWaveform
import numpy as np

def my_waveform(t):
    """Custom temporal profile."""
    return np.sin(2*np.pi*200e12*t) * np.exp(-t/50e-15)

waveform = CustomWaveform(callable=my_waveform)

Monitors

Field Monitor

Record electromagnetic field data during simulation:

from prismo import FieldMonitor

monitor = FieldMonitor(
    center=(1.0e-6, 1.0e-6, 0.0),        # Monitor center
    size=(1.8e-6, 1.8e-6, 0.0),          # Monitor region
    components=["Ey", "Hz"],              # Components to record
    time_domain=True,                     # Enable time-domain recording
    frequencies=[150e12, 200e12],         # Frequency-domain DFT
    name="main_monitor",
)
sim.add_monitor(monitor)

Parameters:

  • center: Monitor center position (m)

  • size: Monitor region size (m)

  • components: Field components to record

    • Individual: ["Ex", "Ey", "Ez", "Hx", "Hy", "Hz"]

    • Groups: "E" (all electric), "H" (all magnetic), "all"

  • time_domain: Record every time step (bool)

  • frequencies: List of frequencies for DFT (Hz)

  • name: Optional identifier

Monitor Types by Size:

# Point monitor (single location)
point_monitor = FieldMonitor(
    center=(1.0e-6, 1.0e-6, 0.0),
    size=(0.0, 0.0, 0.0),
    components=["Ey"],
)

# Line monitor (1D profile)
line_monitor = FieldMonitor(
    center=(1.0e-6, 1.0e-6, 0.0),
    size=(1.8e-6, 0.0, 0.0),  # Along x
    components=["Ey"],
)

# Plane monitor (2D slice)
plane_monitor = FieldMonitor(
    center=(1.0e-6, 1.0e-6, 0.0),
    size=(1.8e-6, 1.8e-6, 0.0),  # xy plane
    components=["Ey"],
)

# Volume monitor (full 3D)
volume_monitor = FieldMonitor(
    center=(1.0e-6, 1.0e-6, 0.5e-6),
    size=(1.8e-6, 1.8e-6, 0.8e-6),  # 3D region
    components="all",
)

Retrieving Monitor Data

Time-Domain Data

# Run simulation
sim.run(100e-15)

# Get time-domain data
time_points, field_data = monitor.get_time_data("Ey")

print(f"Time points shape: {time_points.shape}")
print(f"Field data shape: {field_data.shape}")

# field_data shape: (time_steps, ny, nx) for 2D
#                   (time_steps, nz, ny, nx) for 3D

Frequency-Domain Data

# Get frequency-domain data (from DFT)
freq_field = monitor.get_frequency_data("Ey", 150e12)

# freq_field is complex: amplitude and phase
amplitude = np.abs(freq_field)
phase = np.angle(freq_field)

All Frequencies

# Get all recorded frequencies
freq_data = monitor.get_all_frequency_data("Ey")

for freq, field in freq_data.items():
    print(f"Frequency: {freq/1e12:.1f} THz")
    print(f"Field shape: {field.shape}")

Best Practices

Source Placement

  1. Avoid PML regions: Place sources away from absorbing boundaries

  2. TFSF regions: Should fit comfortably within computational domain

  3. Dipoles: At least 1 wavelength from boundaries

Component Selection

For 2D simulations:

  • TM modes: Use Ez, Hx, Hy

  • TE modes: Use Hz, Ex, Ey

For plane waves:

  • Polarization must be perpendicular to propagation direction

Monitor Optimization

  1. Selective recording: Only record needed components

  2. Region size: Match to area of interest

  3. Frequency-domain: Use DFT instead of storing all time steps

  4. Sampling: Consider using every Nth time step for large simulations

Time Duration

Choose simulation time based on:

  • Pulse sources: 5-10× pulse width

  • Continuous wave: Multiple periods for steady state

  • Resonances: Wait for transients to decay


Advanced Topics

Multiple Sources

Add multiple sources with different properties:

# Two interfering dipoles
dipole1 = ElectricDipole(position=(0.5e-6, 1.0e-6, 0.0), 
                          polarization="y", frequency=200e12)
dipole2 = ElectricDipole(position=(1.5e-6, 1.0e-6, 0.0), 
                          polarization="y", frequency=200e12, phase=np.pi)

sim.add_source(dipole1)
sim.add_source(dipole2)

Enabling/Disabling Sources

# Disable a source temporarily
source.enabled = False

# Re-enable
source.enabled = True

Monitor Data Analysis

# Calculate total energy
time_points, ey_data = monitor.get_time_data("Ey")
energy = np.sum(ey_data**2) * sim.dx * sim.dy * sim.dt

# Find peak field
max_field = np.max(np.abs(ey_data))
peak_time = time_points[np.argmax(np.abs(ey_data[:, ny//2, nx//2]))]

# Spectral analysis
from scipy.fft import fft, fftfreq
signal = ey_data[:, ny//2, nx//2]
spectrum = np.abs(fft(signal))
freqs = fftfreq(len(signal), d=sim.dt)

Examples

See the examples/ directory for complete demonstrations:

  • tfsf_plane_wave.py: TFSF plane wave

  • basic_waveguide.py: Gaussian beam in waveguide

  • plane_wave_validation.py: Multiple source types

Next Steps