ExamplesMeep Examples

Binary Grating Phase Map

Compute transmission and phase characteristics of binary gratings across duty cycle and wavelength ranges

Binary Grating Phase Map

This example simulates binary grating transmission and phase characteristics using Meep, sweeping across duty cycle values to generate a comprehensive phase map. The results are logged to OptixLog for visualization and tracking.

Overview

Binary gratings are fundamental optical elements used in:

  • Beam steering and wavefront shaping
  • Metasurface design for flat optics
  • Diffractive optical elements
  • Phase encoding for holography

This simulation computes the wavelength-dependent transmission and phase for various grating duty cycles, creating a phase map that's essential for metasurface design.

Simulation Parameters

ParameterDefault ValueDescription
resolution60Pixels per μm
gp0.35 μmGrating period
gh0.6 μmGrating height
wvl_min0.4 μmMinimum wavelength
wvl_max0.6 μmMaximum wavelength
nfreq21Number of frequency bins
duty_cycle0.1 - 0.9Range of duty cycles

Physical Setup

The simulation consists of:

  1. Substrate: Glass (n=1.5) substrate layer
  2. Grating: Binary glass pillars with variable duty cycle
  3. Air gap: Region above the grating
  4. Periodic boundaries: Y-direction periodicity for infinite grating

The simulation uses eigenmode decomposition to extract both the amplitude and phase of the transmitted fundamental mode.

Python Code

"""
Binary Grating Phase Map Simulation with OptixLog Integration

This script simulates binary grating transmission and phase characteristics using Meep
and logs the results to OptixLog for visualization and tracking.

Usage:
    # Set your OptixLog API key
    export OPTIX_API_KEY="proj_your_api_key_here"
    
    # Run the simulation
    python binary_grating_phasemap.py                    # Default parameters
    python binary_grating_phasemap.py -gp 0.4 -gh 0.7    # Custom grating parameters
    python binary_grating_phasemap.py -oddz              # Odd Z symmetry
"""

import argparse
import sys
import os

sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'Coupler', 'sdk'))

import matplotlib
matplotlib.use("agg")
import matplotlib.pyplot as plt
import numpy as np
import numpy.matlib

import meep as mp
import optixlog

resolution = 60  # pixels/μm

dpml = 1.0  # PML thickness
dsub = 3.0  # substrate thickness
dpad = 3.0  # padding between grating and PML

wvl_min = 0.4  # min wavelength
wvl_max = 0.6  # max wavelength
fmin = 1 / wvl_max  # min frequency
fmax = 1 / wvl_min  # max frequency
fcen = 0.5 * (fmin + fmax)  # center frequency
df = fmax - fmin  # frequency width
nfreq = 21  # number of frequency bins

k_point = mp.Vector3(0, 0, 0)

glass = mp.Medium(index=1.5)


def grating(gp, gh, gdc, oddz):
    sx = dpml + dsub + gh + dpad + dpml
    sy = gp

    cell_size = mp.Vector3(sx, sy, 0)
    pml_layers = [mp.PML(thickness=dpml, direction=mp.X)]

    src_pt = mp.Vector3(-0.5 * sx + dpml + 0.5 * dsub, 0, 0)
    sources = [
        mp.Source(
            mp.GaussianSource(fcen, fwidth=df),
            component=mp.Ez if oddz else mp.Hz,
            center=src_pt,
            size=mp.Vector3(0, sy, 0),
        )
    ]

    symmetries = [mp.Mirror(mp.Y, phase=+1 if oddz else -1)]

    sim = mp.Simulation(
        resolution=resolution,
        cell_size=cell_size,
        boundary_layers=pml_layers,
        k_point=k_point,
        default_material=glass,
        sources=sources,
        symmetries=symmetries,
    )

    mon_pt = mp.Vector3(0.5 * sx - dpml - 0.5 * dpad, 0, 0)
    flux_mon = sim.add_flux(
        fcen, df, nfreq, mp.FluxRegion(center=mon_pt, size=mp.Vector3(0, sy, 0))
    )

    sim.run(until_after_sources=100)

    input_flux = mp.get_fluxes(flux_mon)

    sim.reset_meep()

    geometry = [
        mp.Block(
            material=glass,
            size=mp.Vector3(dpml + dsub, mp.inf, mp.inf),
            center=mp.Vector3(-0.5 * sx + 0.5 * (dpml + dsub), 0, 0),
        ),
        mp.Block(
            material=glass,
            size=mp.Vector3(gh, gdc * gp, mp.inf),
            center=mp.Vector3(-0.5 * sx + dpml + dsub + 0.5 * gh, 0, 0),
        ),
    ]

    sim = mp.Simulation(
        resolution=resolution,
        cell_size=cell_size,
        boundary_layers=pml_layers,
        geometry=geometry,
        k_point=k_point,
        sources=sources,
        symmetries=symmetries,
    )

    mode_mon = sim.add_flux(
        fcen, df, nfreq, mp.FluxRegion(center=mon_pt, size=mp.Vector3(0, sy, 0))
    )

    sim.run(until_after_sources=300)

    freqs = mp.get_eigenmode_freqs(mode_mon)
    res = sim.get_eigenmode_coefficients(
        mode_mon, [1], eig_parity=mp.ODD_Z + mp.EVEN_Y if oddz else mp.EVEN_Z + mp.ODD_Y
    )
    coeffs = res.alpha

    mode_wvl = [1 / freqs[nf] for nf in range(nfreq)]
    mode_tran = [abs(coeffs[0, nf, 0]) ** 2 / input_flux[nf] for nf in range(nfreq)]
    mode_phase = [np.angle(coeffs[0, nf, 0]) for nf in range(nfreq)]

    return mode_wvl, mode_tran, mode_phase


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="Binary Grating Phase Map Simulation with OptixLog Integration"
    )
    parser.add_argument("-gp", type=float, default=0.35, help="grating periodicity (μm)")
    parser.add_argument("-gh", type=float, default=0.6, help="grating height (μm)")
    parser.add_argument("-oddz", action="store_true", default=False, help="use odd Z symmetry")
    args = parser.parse_args()

    # Initialize OptixLog client
    config = {
        "simulation_type": "binary_grating_phasemap",
        "grating_period": args.gp,
        "grating_height": args.gh,
        "odd_z_symmetry": args.oddz,
        "wavelength_range": [wvl_min, wvl_max],
    }

    client = optixlog.init(
        api_key=os.getenv("OPTIX_API_KEY"),
        project="Binary Grating Phase Map",
        run_name=f"gp{args.gp}_gh{args.gh}",
        config=config,
        create_project_if_not_exists=True
    )

    # Define duty cycle range
    gdc = np.arange(0.1, 1.0, 0.1)

    mode_tran = np.empty((gdc.size, nfreq))
    mode_phase = np.empty((gdc.size, nfreq))
    
    for n in range(gdc.size):
        mode_wvl, mode_tran[n, :], mode_phase[n, :] = grating(
            args.gp, args.gh, gdc[n], args.oddz
        )
        
        client.log(n + 1,
            duty_cycle=gdc[n],
            max_transmittance=float(np.max(mode_tran[n, :])),
            phase_range=float(np.max(mode_phase[n, :]) - np.min(mode_phase[n, :]))
        )

    # Create phase map visualization
    plt.figure(figsize=(16, 8), dpi=150)

    plt.subplot(1, 2, 1)
    plt.pcolormesh(mode_wvl, gdc, mode_tran, cmap="hot_r", shading="gouraud")
    plt.xlabel("wavelength (μm)")
    plt.ylabel("grating duty cycle")
    plt.title("Transmittance")
    plt.colorbar()

    plt.subplot(1, 2, 2)
    plt.pcolormesh(mode_wvl, gdc, mode_phase, cmap="RdBu", shading="gouraud")
    plt.xlabel("wavelength (μm)")
    plt.ylabel("grating duty cycle")
    plt.title("Phase (radians)")
    plt.colorbar()

    plt.tight_layout()
    plt.savefig("binary_grating_phasemap.png", dpi=150, bbox_inches="tight")

How to Run

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

# Run with default parameters
python binary_grating_phasemap.py

# Custom grating parameters
python binary_grating_phasemap.py -gp 0.4 -gh 0.7

# Use odd Z symmetry (TM polarization)
python binary_grating_phasemap.py -oddz

Results and Analysis

Phase Map Visualization

The simulation generates two color maps:

  1. Transmittance Map: Shows how efficiently light passes through the grating

    • X-axis: Wavelength (0.4 - 0.6 μm)
    • Y-axis: Duty cycle (0.1 - 0.9)
    • Color: Transmittance (0 to 1)
  2. Phase Map: Shows the phase shift imparted by the grating

    • X-axis: Wavelength
    • Y-axis: Duty cycle
    • Color: Phase (-π to +π radians)

Key Findings

  • Full 2π phase coverage: By varying duty cycle, you can achieve complete phase control
  • High transmittance: Near-unity transmission maintained across most of the parameter space
  • Wavelength dependence: Phase shift varies with wavelength, important for broadband designs

OptixLog Metrics

The following metrics are logged for each simulation:

  • duty_cycle: Current grating duty cycle
  • max_transmittance: Maximum transmission across wavelengths
  • min_transmittance: Minimum transmission
  • phase_range: Total phase coverage at this duty cycle

Use the phase map to select duty cycle values that provide desired phase shifts while maintaining high transmission efficiency.

On this page