Absorbed Power Density
Compute the absorbed power density in a dielectric cylinder illuminated by a plane wave
Absorbed Power Density
This example computes the absorbed power density in a SiO₂ cylinder illuminated by a plane wave. It demonstrates how to use DFT fields to calculate spatially-resolved absorption.
Reference: Based on the Meep absorbed power density tutorial.
Physics Background
Power Absorption
The absorbed power density in a lossy material is given by:
$$ P_{abs}(x,y) = 2\pi f \cdot \text{Im}[\mathbf{E}^* \cdot \mathbf{D}] $$
where:
- f is the frequency
- E is the electric field
- D is the displacement field
- Im denotes the imaginary part
Absorption Length
The absorption length L_abs characterizes how deeply light penetrates:
$$ L_{abs} = \frac{\lambda}{Im(n)} $$
Simulation Overview
This simulation:
- Creates a 2D cell with a SiO₂ cylinder
- Illuminates it with a plane wave
- Computes DFT fields inside the cylinder
- Calculates absorbed power density from E and D fields
- Validates by comparing with flux box measurement
Configuration Parameters
| Parameter | Value | Description |
|---|---|---|
resolution | 100 | Pixels per μm |
dpml | 1.0 | PML thickness |
r | 1.0 | Cylinder radius (μm) |
wavelength | 1.0 | Illumination wavelength |
material | SiO₂ | Cylinder material |
Complete Code
"""
Absorbed Power Density Simulation with OptixLog Integration
Computes the absorbed power density in a SiO2 cylinder illuminated by a plane wave.
"""
import os
import meep as mp
import matplotlib
matplotlib.use("agg")
import matplotlib.pyplot as plt
import numpy as np
from meep.materials import SiO2
from optixlog import Optixlog
def main():
api_key = os.getenv("OPTIX_API_KEY", "your_api_key_here")
client = Optixlog(api_key=api_key)
project = client.project(name="MeepExamples", create_if_not_exists=True)
# Simulation parameters
resolution = 100
dpml = 1.0
r = 1.0 # cylinder radius
dair = 2.0 # air padding
wvl = 1.0
fcen = 1 / wvl
run = project.run(
name="absorbed_power_density_SiO2",
config={
"simulation_type": "absorbed_power_density",
"material": "SiO2",
"resolution": resolution,
"cylinder_radius": r,
"wavelength": wvl,
}
)
# Log initial parameters
run.log(step=0, resolution=resolution, wavelength=wvl, cylinder_radius=r)
# Setup simulation
pml_layers = [mp.PML(thickness=dpml)]
s = 2 * (dpml + dair + r)
cell_size = mp.Vector3(s, s)
# Planewave source
sources = [
mp.Source(
mp.GaussianSource(fcen, fwidth=0.1 * fcen, is_integrated=True),
center=mp.Vector3(-0.5 * s + dpml),
size=mp.Vector3(0, s),
component=mp.Ez,
)
]
symmetries = [mp.Mirror(mp.Y)]
geometry = [mp.Cylinder(material=SiO2, center=mp.Vector3(), radius=r, height=mp.inf)]
sim = mp.Simulation(
resolution=resolution,
cell_size=cell_size,
boundary_layers=pml_layers,
sources=sources,
k_point=mp.Vector3(),
symmetries=symmetries,
geometry=geometry,
)
# Add DFT fields monitor
dft_fields = sim.add_dft_fields(
[mp.Dz, mp.Ez],
fcen, 0, 1,
center=mp.Vector3(),
size=mp.Vector3(2 * r, 2 * r),
yee_grid=True,
)
# Flux box for validation
flux_box = sim.add_flux(
fcen, 0, 1,
mp.FluxRegion(center=mp.Vector3(x=-r), size=mp.Vector3(0, 2 * r), weight=+1),
mp.FluxRegion(center=mp.Vector3(x=+r), size=mp.Vector3(0, 2 * r), weight=-1),
mp.FluxRegion(center=mp.Vector3(y=+r), size=mp.Vector3(2 * r, 0), weight=-1),
mp.FluxRegion(center=mp.Vector3(y=-r), size=mp.Vector3(2 * r, 0), weight=+1),
)
sim.run(until_after_sources=100)
# Calculate absorbed power density
Dz = sim.get_dft_array(dft_fields, mp.Dz, 0)
Ez = sim.get_dft_array(dft_fields, mp.Ez, 0)
absorbed_power_density = 2 * np.pi * fcen * np.imag(np.conj(Ez) * Dz)
# Compare with flux measurement
dxy = 1 / resolution**2
absorbed_power = np.sum(absorbed_power_density) * dxy
absorbed_flux = mp.get_fluxes(flux_box)[0]
err = abs(absorbed_power - absorbed_flux) / absorbed_flux
print(f"📊 Absorbed power (DFT fields): {absorbed_power:.6f}")
print(f"📊 Absorbed flux (flux box): {absorbed_flux:.6f}")
print(f"📊 Relative error: {err:.2e}")
# Log results
run.log(step=2,
absorbed_power_dft=absorbed_power,
absorbed_flux=absorbed_flux,
relative_error=err,
max_power_density=float(np.amax(absorbed_power_density)))
# Plot cell structure
fig1, ax1 = plt.subplots(figsize=(10, 8))
sim.plot2D(ax=ax1)
ax1.set_title("Simulation Cell with SiO2 Cylinder")
run.log_matplotlib("cell_structure", fig1)
plt.close(fig1)
# Plot absorbed power density map
fig2, ax2 = plt.subplots(figsize=(10, 8))
x = np.linspace(-r, r, Dz.shape[0])
y = np.linspace(-r, r, Dz.shape[1])
im = ax2.pcolormesh(
x, y,
np.transpose(absorbed_power_density),
cmap="inferno_r",
shading="gouraud",
vmin=0,
vmax=np.amax(absorbed_power_density),
)
ax2.set_xlabel("x (μm)")
ax2.set_ylabel("y (μm)")
ax2.set_aspect("equal")
labs = wvl / np.imag(np.sqrt(SiO2.epsilon(fcen)[0][0]))
ax2.set_title(f"Absorbed Power Density\nSiO2 Labs(λ={wvl} μm) = {labs:.2f} μm")
plt.colorbar(im, ax=ax2, label="Power Density (a.u.)")
plt.tight_layout()
run.log_matplotlib("power_density_map", fig2)
plt.close(fig2)
# Log final summary
run.log(step=3,
total_absorbed_power=absorbed_power,
absorption_length_um=labs,
simulation_completed=True)
print(f"\n✅ Simulation complete!")
if __name__ == "__main__":
main()Key Concepts
DFT Field Monitors
Use add_dft_fields to store time-averaged field data at specific frequencies:
dft_fields = sim.add_dft_fields(
[mp.Dz, mp.Ez], # Components to store
fcen, 0, 1, # Frequency, df, nfreq
center=mp.Vector3(),
size=mp.Vector3(2 * r, 2 * r),
yee_grid=True, # Store on Yee grid locations
)Extracting DFT Arrays
After simulation, extract the complex field arrays:
Dz = sim.get_dft_array(dft_fields, mp.Dz, 0)
Ez = sim.get_dft_array(dft_fields, mp.Ez, 0)Power Density Calculation
absorbed_power_density = 2 * np.pi * fcen * np.imag(np.conj(Ez) * Dz)Validation with Flux Box
Use a flux box to validate the integrated absorption:
flux_box = sim.add_flux(
fcen, 0, 1,
mp.FluxRegion(center=mp.Vector3(x=-r), size=mp.Vector3(0, 2*r), weight=+1),
mp.FluxRegion(center=mp.Vector3(x=+r), size=mp.Vector3(0, 2*r), weight=-1),
# ... additional faces
)Expected Results
Power Density Map
The absorbed power density shows:
- Higher absorption near the illuminated surface (left side)
- Exponential decay into the material
- Shadow region on the back side
Validation
The relative error between DFT integration and flux box should be < 1%.
OptixLog Integration
Logged Metrics
| Metric | Description |
|---|---|
absorbed_power_dft | Total power from DFT integration |
absorbed_flux | Total power from flux box |
relative_error | Agreement between methods |
max_power_density | Peak absorption location |
absorption_length_um | Material absorption length |
Logged Plots
- cell_structure — Simulation geometry visualization
- power_density_map — Spatially-resolved absorption
Running the Example
export OPTIX_API_KEY="your_api_key_here"
python absorbed_power_density.py