Workflow¶
Draw the device as a gdsfactory component (or import a .gds) with ports, plus a
LayerStackassigning thickness and material per layer.Define
materials,frames,sourcesandmonitors.lumi.mesh(path, c, layer_stack, materials, zmin, zmax)writes 3D meshes into simulation folderpath.lumi.setup(path, ...)writes the simulation problem and solves port modes.lumi.solve(path)runs FDTD. Runs on Lumi Colab server ifLUMI_APIis set.lumi.movie(path)shows the field movie;sol = lumi.load(path)loads results.
See bend for a minimal example.
Conventions¶
Units & frequency¶
Length units are arbitrary but must be consistent: eg um for photonics, mm for RF.
Photonics:
wl(center) andwls(result wavelengths) in length units.RF:
fandfswithc0, speed of light in your units, egc0=300for mm and GHz.Results are arrays over
wlsorfs, in the order given.Times (
tmax,shot_every,t0) are in periods of the center frequency.One run computes the whole spectrum; more wavelengths add little runtime.
Material properties¶
lumi.material(n)orlumi.material(epsilon=...): relative permittivity, scalar or 3x3 tensor for anisotropic materials.Also
mu,sigma(relative units: multiply S/m by377 * (characteristic wavelength in m)),chi2,chi3,dispersion(Meep convention).nalso sets mesh density relative to vacuum, defaultsqrt(epsilon * mu). Raise it to refine the grid in a material, eg thin metals.lumi.PEC_material(n): perfect conductor for metals.lumi.placeholder_material(n): meshed but no physical properties, eg design regions.materialsis a dict of name to material;LayerStacklayers andbackgroundrefer to these names.
Grid & resolution¶
nres: grid points per wavelength in material. 6 is a good start for photonics; RF usually needs more to resolve thin features.Grid adapts to each material’s
n;subpixel_smoothingaverages material boundaries for accuracy.Check convergence by increasing
nresuntil results stop changing.Use symmetry to halve the domain: PMC or PEC boundary on one side, eg
("PMC", "PML").Leave
dtypeunset; it’s chosen automatically.
Frames¶
Coordinate frame with sampling regions or points, reusable by any number of sources and monitors at different positions via
frame="name". Pass all frames tosetup(frames=...).Local coordinates: origin at the source or monitor
origin, axes from its basis.mirrored=Trueflips local x, eg for a port on the opposite side of a symmetric device.Types and results:
plane_frame: port cross section with solved modes. Resultsmodes,flux,power. Sources need one.points_frame: fields at points, orsensor="voltage"/"current"integrals for RF.box_frame: fields in a sampled volume.cage_frame: closed box surface. Flux per face, power, far field.spherical_frame: sphere of radiusr. Flux, power, far field if closed.
Results are frequency domain at
wlsorfsby default;time_domain=Truerecords time signals instead.
Ports & S-parameters¶
Source and monitor names match gdsfactory port names, eg
"o1".gf_sourceandgf_monitorplace them at ports.General constructors
sourcemonitorPort basis rows: local x, local y, outward normal. Sources inject bidirectional.
Monitor results:
sol["monitors"][name]["modes"][mode number]["+" or "-"], where"+"is outgoing (along normal),"-"incoming.S-parameters are ratios of mode amplitudes, eg
S21 = sol["monitors"]["o2"]["modes"][0]["+"] / sol["monitors"]["o1"]["modes"][0]["-"]
S11 = sol["monitors"]["o1"]["modes"][0]["+"] / sol["monitors"]["o1"]["modes"][0]["-"]Inverse design targets use
"port@mode±", eglumi.target("o1@0-", "o2@0+", 1).
Inverse design¶
Optimizes the material layout in design regions to meet S-parameter targets. Not in the free tier. See GC_invd.
Draw the design region on its own layer in the component.
Give that layer the material
lumi.placeholder_material(n)in theLayerStack, with the lowestmesh_orderso it overrides other layers, and the vertical extent to be patterned.lumi.design(name, layer, lmin, fill_material, void_material, ...): region patterned withfill_materialandvoid_materialat minimum feature sizelmin.repdims=[3]extrudes a planar pattern along z;symdimsenforces symmetry.lumi.target(src, dst, target, wl=...): one per objective, eg"o2@0-"to"o1@0+"withtarget=1for full transmission. Loss is the weighted sum of target errors; use one target per wavelength for broadband.lumi.optimizer(iters, stoploss, ...): stops afteritersor belowstoploss.contrastbelow 1 allows topology change and rises quadratically to 1 (binary) as loss approacheslowloss, or by iteration 25.Pass
designs,targets,optimizertosetup, thensolveruns the optimization.
Outputs in the run folder: history.json (loss per iteration) and designs/<name>/mask.npy (optimized boolean fill mask).
Run folder & files¶
One folder per simulation, eg runs/bend:
geometry/: meshes frommeshproblem.json,modes/,info.json,visualization.json: fromsetupsolution.json: results fromsolve, read byloadfields/,*.gif,*.webp: field snapshots and movies, ifviewsset