ExamplesMeep Examples

Material Dispersion

Extract the frequency-dependent dielectric function from dispersive material simulations

Material Dispersion Analysis

This example simulates a homogeneous dispersive material and extracts the dielectric function ε(ω) from the mode frequencies using a k-point sweep.

Overview

Material dispersion is critical for:

  • Pulse propagation: Group velocity and chirp
  • Resonant phenomena: Plasmonic and phononic effects
  • Metamaterials: Engineering effective permittivity
  • Chromatic aberration: Lens and waveguide design

This simulation uses Lorentzian susceptibilities to model realistic materials with resonances.

Simulation Parameters

ParameterDefault ValueDescription
resolution20Pixels per μm
fcen1.0Pulse center frequency
df2.0Pulse frequency width
kmin0.3Minimum k-vector
kmax2.2Maximum k-vector
k_interp99Number of k-points

Material Model

The dispersive material has two Lorentzian resonances:

ResonanceFrequencyGammaSigmaDescription
11.11e-50.5Strong polaritonic gap
20.50.12e-5Weak absorption

Background permittivity: ε_∞ = 2.25

The Lorentzian model: ε(ω) = ε_∞ + Σ σᵢω₀ᵢ²/(ω₀ᵢ² - ω² - iωγᵢ)

Python Code

"""
Material Dispersion Analysis with OptixLog Integration

Simulates homogeneous dispersive material and extracts the
dielectric function epsilon(omega) from mode frequencies.

Based on the Meep tutorial: material-dispersion.py
"""

import os
import optixlog
import meep as mp
import matplotlib
matplotlib.use("agg")
import matplotlib.pyplot as plt
import numpy as np

api_key = os.getenv("OPTIX_API_KEY", "")
api_url = os.getenv("OPTIX_API_URL", "https://optixlog.com")
project_name = os.getenv("OPTIX_PROJECT", "MeepExamples")


def main():
    """Main simulation function for material dispersion analysis."""
    
    if not optixlog.is_master_process():
        return
    
    try:
        client = optixlog.init(
            api_key=api_key,
            api_url=api_url,
            project=project_name,
            run_name="material_dispersion_analysis",
            config={
                "simulation_type": "dispersion",
                "description": "Material dispersion analysis",
            },
            create_project_if_not_exists=True
        )
        
        # Simulation parameters
        resolution = 20
        fcen = 1.0
        df = 2.0
        
        kmin = 0.3
        kmax = 2.2
        k_interp = 99
        
        # Material resonances
        resonance1_freq = 1.1
        resonance1_gamma = 1e-5
        resonance1_sigma = 0.5
        
        resonance2_freq = 0.5
        resonance2_gamma = 0.1
        resonance2_sigma = 2e-5
        
        background_epsilon = 2.25
        
        client.log(
            step=0,
            resolution=resolution,
            center_frequency=fcen,
            k_min=kmin,
            k_max=kmax,
            background_epsilon=background_epsilon,
            resonance1_frequency=resonance1_freq,
            resonance2_frequency=resonance2_freq
        )
        
        # Set up dispersive material
        susceptibilities = [
            mp.LorentzianSusceptibility(
                frequency=resonance1_freq,
                gamma=resonance1_gamma,
                sigma=resonance1_sigma
            ),
            mp.LorentzianSusceptibility(
                frequency=resonance2_freq,
                gamma=resonance2_gamma,
                sigma=resonance2_sigma
            ),
        ]
        
        default_material = mp.Medium(
            epsilon=background_epsilon,
            E_susceptibilities=susceptibilities
        )
        
        # Set up simulation (point cell)
        cell = mp.Vector3()
        sources = [
            mp.Source(
                mp.GaussianSource(fcen, fwidth=df),
                component=mp.Ez,
                center=mp.Vector3()
            )
        ]
        
        kpts = mp.interpolate(k_interp, [mp.Vector3(kmin), mp.Vector3(kmax)])
        
        sim = mp.Simulation(
            cell_size=cell,
            geometry=[],
            sources=sources,
            default_material=default_material,
            resolution=resolution,
        )
        
        all_freqs = sim.run_k_points(200, kpts)
        
        client.log(step=1, simulation_completed=True)
        
        # Process results
        k_data = []
        freq_real_data = []
        epsilon_data = []
        
        for fs, kx in zip(all_freqs, [v.x for v in kpts]):
            for f in fs:
                if f.real > 0:
                    eps_calc = (kx / f) ** 2
                    k_data.append(kx)
                    freq_real_data.append(f.real)
                    epsilon_data.append(eps_calc.real)
        
        if freq_real_data:
            client.log(
                step=2,
                total_data_points=len(freq_real_data),
                frequency_range_min=float(min(freq_real_data)),
                frequency_range_max=float(max(freq_real_data)),
                epsilon_range_min=float(min(epsilon_data)),
                epsilon_range_max=float(max(epsilon_data))
            )
        
    except Exception as e:
        print(f"Simulation Error: {e}")


if __name__ == "__main__":
    main()

How to Run

# Set your OptixLog API key
export OPTIX_API_KEY="your_api_key_here"

# Run the dispersion analysis
python material-dispersion.py

Results and Analysis

Dispersion Relation ω(k)

The simulation extracts the relationship between frequency and wavevector:

  • Light line: Reference for vacuum propagation
  • Polariton branches: Above and below the resonance
  • Band gap: Region where ε < 0 (no propagating modes)

Dielectric Function ε(ω)

From the dispersion relation, ε = (k/ω)² × c²:

  • Below resonance: ε > ε_∞, slower light
  • At resonance: Rapid ε variation, absorption
  • Above resonance: ε can be negative or approach ε_∞

Refractive Index n(ω)

The refractive index n = √ε shows:

  • Normal dispersion: n increases with ω (away from resonances)
  • Anomalous dispersion: n decreases near resonances

OptixLog Metrics

  • background_epsilon: High-frequency limit
  • resonance1_frequency, resonance2_frequency: Resonance locations
  • frequency_range_min/max: Extracted frequency range
  • epsilon_range_min/max: ε(ω) variation

Near resonances, the imaginary part of ε (absorption) becomes significant. Use small gamma for sharp resonances.

Physical Insights

Lorentzian Model

The Lorentzian susceptibility models bound electron oscillators:

  • Low frequency: Electrons follow field adiabatically
  • At resonance: Maximum energy transfer, absorption peak
  • High frequency: Electrons can't follow, ε → ε_∞

Kramers-Kronig Relations

Real and imaginary parts of ε are related:

  • Strong absorption → strong dispersion nearby
  • Helps validate simulation results

On this page