ExamplesMeep Examples

Angular Reflectance

Compute reflectance of a dielectric interface as a function of wavelength and angle

Angular Reflectance Simulation

This example computes the reflectance of a dielectric interface as a function of wavelength at oblique incidence, comparing with Fresnel theory.

Overview

Angular reflectance analysis is essential for:

  • Anti-reflection coatings: Designing AR layers
  • Total internal reflection: Waveguide design
  • Brewster angle: Polarization control
  • Thin-film optics: Multilayer design

Simulation Parameters

ParameterDefault ValueDescription
resolution200Pixels per μm
thetaIncidence angle
wvl_min0.4 μmMinimum wavelength
wvl_max0.8 μmMaximum wavelength
n_dielectric3.5Substrate index
nfreq50Frequency points

Physical Setup

  1. Air half-space: Incident medium (n=1)
  2. Dielectric substrate: High-index material (n=3.5)
  3. Oblique incidence: Variable k-vector angle
  4. Flux normalization: Reference and sample runs

The simulation uses a 1D cell for normal incidence (θ=0) and a 3D cell with Bloch periodicity for oblique incidence.

Python Code

"""
Angular Reflectance Simulation with OptixLog Integration

Computes the reflectance of a dielectric interface as a function
of wavelength at oblique incidence.
"""

import os
import math
import argparse
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", "")
project_name = os.getenv("OPTIX_PROJECT", "MeepExamples")


def compute_reflectance(resolution, theta):
    """Compute reflectance spectrum for given angle."""
    dpml = 1.0
    sz = 10 + 2 * dpml
    cell_size = mp.Vector3(0, 0, sz)
    pml_layers = [mp.PML(dpml)]
    
    wvl_min, wvl_max = 0.4, 0.8
    fmin, fmax = 1 / wvl_max, 1 / wvl_min
    fcen = 0.5 * (fmin + fmax)
    df = fmax - fmin
    nfreq = 50
    
    theta_r = math.radians(theta)
    k = mp.Vector3(math.sin(theta_r), 0, math.cos(theta_r)).scale(fmin)
    dimensions = 1 if theta_r == 0 else 3
    
    sources = [mp.Source(mp.GaussianSource(fcen, fwidth=df), component=mp.Ex, center=mp.Vector3(0, 0, -0.5 * sz + dpml))]
    
    # Reference simulation (empty)
    sim = mp.Simulation(cell_size=cell_size, boundary_layers=pml_layers, sources=sources, k_point=k, dimensions=dimensions, resolution=resolution)
    refl = sim.add_flux(fcen, df, nfreq, mp.FluxRegion(center=mp.Vector3(0, 0, -0.25 * sz)))
    sim.run(until_after_sources=mp.stop_when_fields_decayed(50, mp.Ex, mp.Vector3(0, 0, -0.5 * sz + dpml), 1e-9))
    
    empty_flux = mp.get_fluxes(refl)
    empty_data = sim.get_flux_data(refl)
    sim.reset_meep()
    
    # With dielectric
    n_dielectric = 3.5
    geometry = [mp.Block(size=mp.Vector3(mp.inf, mp.inf, 0.5 * sz), center=mp.Vector3(0, 0, 0.25 * sz), material=mp.Medium(index=n_dielectric))]
    
    sim = mp.Simulation(cell_size=cell_size, geometry=geometry, boundary_layers=pml_layers, sources=sources, k_point=k, dimensions=dimensions, resolution=resolution)
    refl = sim.add_flux(fcen, df, nfreq, mp.FluxRegion(center=mp.Vector3(0, 0, -0.25 * sz)))
    sim.load_minus_flux_data(refl, empty_data)
    sim.run(until_after_sources=mp.stop_when_fields_decayed(50, mp.Ex, mp.Vector3(0, 0, -0.5 * sz + dpml), 1e-9))
    
    refl_flux = mp.get_fluxes(refl)
    freqs = mp.get_flux_freqs(refl)
    
    return {'wavelengths': [1/f for f in freqs], 'reflectances': [-refl_flux[i]/empty_flux[i] for i in range(nfreq)]}


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("-res", type=int, default=200)
    parser.add_argument("-theta", type=float, default=0)
    args = parser.parse_args()
    
    if not optixlog.is_master_process():
        return
    
    try:
        client = optixlog.init(api_key=os.getenv("OPTIX_API_KEY"), project="MeepExamples", run_name=f"angular_reflectance_theta{args.theta}", config={"simulation_type": "reflectance"}, create_project_if_not_exists=True)
        
        client.log(step=0, resolution=args.res, incidence_angle=args.theta)
        
        results = compute_reflectance(args.res, args.theta)
        
        client.log(step=1, max_reflectance=float(max(results['reflectances'])), mean_reflectance=float(np.mean(results['reflectances'])))
        
    except Exception as e:
        print(f"Error: {e}")


if __name__ == "__main__":
    main()

How to Run

# Normal incidence
python refl-angular.py -theta 0

# Oblique incidence
python refl-angular.py -theta 30
python refl-angular.py -theta 60

Results and Analysis

Reflectance Spectrum

Results show:

  • Normal incidence: R = ((n-1)/(n+1))² ≈ 30% for n=3.5
  • Oblique angles: Varies with polarization and angle
  • Brewster angle: Zero reflectance for p-polarization

Fresnel Comparison

The s-polarization reflectance:

Rs = |n₁cosθ₁ - n₂cosθ₂|² / |n₁cosθ₁ + n₂cosθ₂|²

OptixLog Metrics

  • incidence_angle_degrees: Input angle
  • fresnel_theory_reflectance: Analytical prediction
  • meep_mean_reflectance: FDTD result

On this page