Workflow¶
mesh¶
lumi.mesh(path, component, layer_stack, materials, zmin, zmax)Writes .obj meshes of gdsfactory component extruded by layer_stack, clipped to [zmin, zmax], into simulation folder path. Call before setup.
setup¶
lumi.setup(path, sources, monitors, nres=6, wls=None, wl=None, f=None, fs=None, c0=None, targets=[], designs=[], approx_2D_mode=None, **kwargs)Writes simulation problem to folder path. Call mesh first, then solve.
Args:
wl and/or wls: center wavelength and wavelengths to compute results at, or
f and/or fs with c0: center frequency and frequencies, with speed of light c0 in your units eg 300 [mm GHz]. Results are then ordered by frequency
sources, monitors: from
source,monitor,gf_source,gf_monitorframes: from
plane_frame,box_frame,spherical_frame,points_frame,cage_framematerials: dict of name to
materialbackground: material name filling empty space
bbox: simulation box [[xmin, ymin, zmin], [xmax, ymax, zmax]]
boundaries: per axis “PML”, “PEC”, “PMC”, “periodic” or pair (min side, max side) eg (“PEC”, “PML”)
k_bloch: Bloch wavevector [kx, ky, kz] in rad per length unit on periodic axes, eg from
k_bloch. Fields pick up phase e^(i k L) across each period. Nonzero makes fields complex (2x memory)nres: grid points per wavelength in material. 6 is a good starting point for photonics; increase to check convergence
tmax: max simulation time [periods]. Required to solve; if unset, only setup runs
shutoff: stop before
tmaxonce field energy decays to this fraction. Needstmax. At least 1e-3 when nres <= 10PML_decay: PML field attenuation, scalar or per axis
courant: time step relative to max stable
subpixel_smoothing: subpixel averaging of material boundaries
views: from
view, for movieshot_every: save movie frame every _ periods
full_fields: save full 3D field snapshots instead of only view slices, so
movie(path, views=...)can re-render new slicesdevice: “gpu” (default) or “cpu”
approx_2D_mode: 2D approximation
designs, targets, optimizer: inverse design, from
design,target,optimizer
solve¶
lumi.solve(path, fields=False, gzip=False)Run simulation in folder path prepared by setup. Writes solution.json.
Runs on Lumi Colab server (see remote_solve; fields and gzip are passed to it).
load¶
lumi.load(path, show=True)Load solution dict from path. Monitor results in sol[“monitors”][name], eg [“modes”][mode number][“+” or “-”] as array over wavelengths (or frequencies if setup with fs). Frequency-domain arrays have wavelength as last axis: [“fields”][component] eg [x, y, wavelength], [“flux”], [“power”], [“farfield”] and [“directivity”] [theta, phi, wavelength], cage [“flux”][face]. Box monitor volume modes have no direction: [“modes”][mode number] is the mean mode amplitude after t0, |a|^2 the stored energy.
movie¶
lumi.movie(path, views=None, fps=None, src='frames')Show simulation movie in path, rendered by solve.
views (overrides views from setup) or fps re-render it from field snapshots, which cloud solves don’t keep.
Also re-renders if snapshots are newer than the movie.
src: “frames” for time snapshots, “history” for peak snapshot per optimization iteration.
view¶
lumi.view(field, name='', prop='n', x=None, y=None, z=None, field_color_intensity=1.0, show_grid=False, show_box=True, labels='auto', mirrors=[])Movie view: slices of field overlaid on material property prop, for setup(views=...).
Args:
field: field component eg “Ey”, “Hz”
name: movie file name, defaults to field and slice positions
prop: material property underneath eg “n”, “epsilon”
x, y, z: slice position along that axis, “half” for the simulation box center, or None for no slice. One panel per slice
field_color_intensity: scales field color saturation
show_grid: draw grid lines
show_box: draw simulation box outline
labels: “auto” to label sources and monitors, or dict of name to label
mirrors: reflect across symmetry boundaries for display eg [“z-”]
Materials¶
material¶
lumi.material(n=None, epsilon=None, mu=1, sigma=0, chi2=0, chi3=0, dispersion=[], placeholder=False, dx=None)Define material properties in relative units
Args:
epsilon: real relative permittivity; the instantaneous (infinite frequency) value when
dispersionis set. Usesigmaordispersionfor lossmu: relative permeability
sigma: conductivity (multiply by
377 * (characteristic wavelength in m)to convert from metric (S/m, 1 /(Ω m)) to relative units)chi2: Pockels second order nonlinear susceptibility
chi3: Kerr third order nonlinear susceptibility
dispersion: list of terms from
lorentzian,drudeorgain, eg[lumi.lorentzian(f, gamma, sigma)]. We use Meep convention https://meep .readthedocs .io /en /latest /Materials / #material -dispersion and relative units. n: real mesh density relative to vacuum, defaults to square root of relative permittivity. Sets epsilon if epsilon not given
dx: max grid spacing in this material, in geometry length units. Raises mesh density to
wavelength / (dx * nres)atsetup, never lowers it. Requires n or epsilon
PEC_material¶
lumi.PEC_material(n=1, dx=None)Perfect electric conductor material for modeling metals
placeholder_material¶
lumi.placeholder_material(n=1, dx=None)placeholder material contributes to meshing but not to physical properties, eg used for design region
lorentzian¶
lumi.lorentzian(f, gamma, sigma)Lorentzian dispersion term for material(dispersion=[...]): resonance frequency f, damping gamma, strength sigma (Meep convention, relative units)
drude¶
lumi.drude(f, gamma, sigma)Drude dispersion term for material(dispersion=[...]): plasma-like frequency f, damping gamma, strength sigma (Meep convention, relative units)
gain¶
lumi.gain(f, gamma, sigma)Lorentzian gain term for material(dispersion=[...]): like lorentzian with negative strength
Sources & monitors¶
source¶
lumi.source(name, origin, basis=None, frame=None, dim=None, mirrored=False, wl=None, f=None, bandwidth=None, duration=None, modenums=[0])Modulated Gaussian pulse source with gradual spectral roll off at bandwidth limits. Becomes continuous wave if bandwidth=0.
Args:
name: should match a port name eg “o1”
origin: position [x, y, z], or coordinate along
dimforfaceframesbasis: rows local x, local y, outward normal. source injects bidirectionally (along both ±normal)
frame: name of
plane_frameorfaceholding mode profiledim: outward normal along axis ±1, ±2, ±3 (x, y, z) instead of
basismirrored: reflect frame and modes across local x=0, eg for port on opposite side of a symmetric frame
wl or f: wavelength or frequency, specify either (not both). f requires c0 in setup
bandwidth: in same units as above
modenums: list of mode numbers to excite
monitor¶
lumi.monitor(name, origin, basis=None, frame=None, dim=None, mirrored=False, t0=0, x=None, y=None, z=None, direction='+', layer=None)Monitor recording results of frame at origin in basis.
Args:
name: should match a port name eg “o1”. Unique among monitors
origin: position [x, y, z], or coordinate along
dimforfaceframesbasis: rows local x, local y, outward normal. “+” results propagate along normal, “-” against
frame: frame name
dim: outward normal along axis ±1, ±2, ±3 (x, y, z) instead of
basismirrored: reflect frame and modes across local x=0
t0: start recording time-domain signals at t0 [periods]
gf_source¶
lumi.gf_source(port, c, layer_stack, offset=0, name=None, **kwargs)source at gdsfactory port port of component c, offset along outward normal. name defaults to port name. kwargs go to source.
gf_monitor¶
lumi.gf_monitor(port, c, layer_stack, offset=0, name=None, **kwargs)monitor at gdsfactory port port of component c. name defaults to port name. kwargs go to monitor.
gaussian_mode¶
lumi.gaussian_mode(D, n, dx, wl, theta=0, half=None, Dlim=None)Tilted Gaussian beam mode for plane_frame(modes=...), eg fiber.
Args:
D: mode field diameter (1/e^2 intensity)
n: refractive index of medium
dx: sampling
wl: vacuum wavelength
theta: tilt angle [rad] in medium in local xz plane
half: “y+” for half frame y>=0 (symmetry plane at y=0)
Dlim: frame full width, default 1.5*D
Frames¶
plane_frame¶
lumi.plane_frame(name, start, stop, fs_mode_solver=[], wls_mode_solver=[], modes='auto', nmodes=1, metallic_boundaries=None, boundaries=('PML', 'PML'), time_domain=None, frequency_domain=None)Plane port frame between local start, stop (2 coords in 3D). Mode solutions at given frequencies or wavelengths; sources and monitors use (or interpolate) modes at closest wavelengths. Results: modes, flux, power. Records frequency-domain results by default, or time-domain signals if time_domain=True (or frequency_domain=False).
Args:
fs_mode_solver or wls_mode_solver: frequencies (requires c0 in setup) or wavelengths to solve modes at, specify one (not both)
modes: “auto” to solve, else precomputed fields eg [[{‘Ex’: np_array, ‘Hy’: ...}, ...], ...]
nmodes: number of modes to solve for
metallic_boundaries: PEC outlines for mode solver: layer names or frame edges “x-”, “x+”, “y-”, “y+”
points_frame¶
lumi.points_frame(name, points, sensor=None, time_domain=None, frequency_domain=None)Points in monitor basis. Records frequency-domain results by default, or time-domain signals if time_domain=True (or frequency_domain=False).
Args:
sensor: “voltage”: ∫E·dl along polyline
points. “current”: ∮H·dl around closed looppoints. None: raw fields.
box_frame¶
lumi.box_frame(name, start, stop, dr, modes=None, wavelengths=[], time_domain=None, frequency_domain=None)Box volume frame between local start, stop (3 coords in 3D), sampled every dr (scalar or per axis). Results: fields in monitor basis. Records frequency-domain results by default, or time-domain signals if time_domain=True (or frequency_domain=False).
modes: optional volume modes eg resonator modes [[{‘Ex’: np_array, ..., ‘Hz’: ...}, ...], ...] per wavelength, sampled on the box (overrides
dr). Results then also include modes: mean projection after monitor t0 onto each mode normalized to unit stored energy, so |a|^2 is energy in the mode. No direction: use in targets as eg “o2@0”wavelengths: wavelengths of
modes. Empty uses the same modes at all wavelengths
cage_frame¶
lumi.cage_frame(name, start, stop, dr, theta=3.141592653589793, phi=6.283185307179586, dtheta=np.float64(0.08726646259971647), dphi=np.float64(0.08726646259971647), time_domain=None, frequency_domain=None)Cage: closed surface of box between local start, stop (3D only), faces sampled every dr (scalar or per axis). Results: flux per face (outward), power, farfield intensity and directivity on (theta, phi) grid. theta, phi: max angle (range from 0) or [min, max]. Records frequency-domain results by default, or time-domain signals if time_domain=True (or frequency_domain=False).
spherical_frame¶
lumi.spherical_frame(name, r, theta=3.141592653589793, phi=6.283185307179586, dtheta=np.float64(0.08726646259971647), dphi=np.float64(0.08726646259971647), time_domain=None, frequency_domain=None)Sphere (circle in 2D) of radius r about monitor origin, in monitor basis. theta, phi: max angle (range from 0) or [min, max]. Results: flux (radial), power, and if closed (full theta, phi), farfield intensity and directivity on (theta, phi) grid. Records frequency-domain results by default, or time-domain signals if time_domain=True (or frequency_domain=False).
Inverse design¶
design¶
lumi.design(name, layer, lmin, fill_material, void_material, component, layer_stack, materials, symdims=[], repdims=[3], anchordims=[], dependent=False, topopt=True, contour_layer=None)Design region to be optimized
Args:
name: design name eg “d1”
layer: placeholder layer for design region
lmin: minimum feature size
fill_material: eg ‘Si’
void_material: eg ‘SiO2’
symdims: eg [“x”,“y”,“diagonal”]
component, layer_stack, materials: as in setup
repdims: eg [3] for planar design region extruded along z
anchordims: signed dims eg [-1, 2] whose design edges (x-, y+) are closed instead of open
topopt: allow topology change
contour_layer: optional layer whose polygons mask the design region; only pixels inside are optimized, rest keep initial geometry
target¶
lumi.target(src, dst, target, target_func='abs2', error_func='abs', weight=1, f=None, wl=None, Q0=None)src: source port and mode eg “o1@0-” (mode 0 incoming)
dst: destination port and mode eg “o2@0+” (mode 0 outgoing), or volume mode of a box monitor eg “o2@0” (no direction): then abs2 is stored energy in the mode per incident spectral density at
src, independent of pulse shapetarget: target value
target_func: applied to S parameter before comparing to target eg “abs2” for power
weight: weight of this target in overall loss function
f or wl: frequency or wavelength, specify one (not both)
error_func: error function `abs, identity, -, relu, relu-’
Q0: with volume mode
dst, its Q from losses other thansrccoupling. Value becomes the steady state power fraction fromsrcinto the mode, 4 g g0 / (g + g0)^2, where g = abs2 above is the coupling rate and g0 = 2 pi f / Q0. Target 1 is critical coupling
optimizer¶
lumi.optimizer(iters, stoploss, startloss=1, lowloss=None, momentum=0.7, contrast=0.7, init_dA=0.03, ckptat=None, gradckptat=None)Args:
iters: maximum number of optimization iterations
stoploss: stop optimization if loss goes below this value
startloss: initial loss estimate
lowloss: loss at which contrast reaches 1. defaults to 0.2 * stoploss + 0.8 * startloss
- highiter: iteration at which contrast reaches 1. None ramps by loss only¶
momentum: optimizer momentum
contrast: initial binary contrast (0 to 1). slightly <1 enables topology change (new holes or islands). =1 freezes topology eg shape optimization. contrast automatically increases linearly to 1 as loss decreases to
lowlossinit_dA: fraction of design area changed by the first step. sets the initial step size
ckptat: save optimization checkpoint every _ iterations
gradckptat: gradient checkpointing
apply_design¶
lumi.apply_design(c0, sol)Replace design regions of component c0 with optimized designs from solution sol.
design_from_gds¶
lumi.design_from_gds(path, i=1)Import ith optimized design from path as gdsfactory component.
Utilities¶
straight¶
lumi.straight(length=10, width: float = 1, layer=(1, 0))Rectangular gdsfactory component with ports “o1” (west) and “o2” (east).
remote_solve¶
lumi.remote_solve(path, api=None, token=None, poll=3, fields=False, gzip=False)Run simulation in folder path on a Luminescent server. Uploads inputs, streams the log, downloads results into path.
The server renders the movie; field snapshots are not downloaded.
Port monitors’ field planes (large: every component at every wavelength) are downloaded only if fields.
Uploads and downloads are gzip-compressed only if gzip.
token defaults to env LUMI_TOKEN (or JUPYTERHUB_API_TOKEN in hosted notebooks).
Run from your own machine, a copy is also kept for 7 days in your Lumi Colab home at ~/tmp/runs_from_local/.
load_sparams¶
lumi.load_sparams(sparams)Convert {“re”, “im”} dicts to complex values.
version¶
lumi.version()Installed luminescent python package version. Its X.Y must match the solver binary’s X.Y.0.