Lumerical Integration
Integrating OptixLog with Ansys Lumerical photonics tools via lumapi and the planned Companion App
Lumerical Integration
Ansys Lumerical is the industry standard for photonics simulation. OptixLog provides integration pathways to bring experiment tracking and collaboration to your Lumerical workflows.
Lumerical tools communicate via lumapi, an inter-process Python API. While powerful, it's not as seamless as native Python simulation tools. We're working on solutions to make integration easier.
The Reality of lumapi
Let's be honest: lumapi is not the ideal integration point. Here's why:
How lumapi Works
┌─────────────────┐ ┌─────────────────┐
│ Python Script │◄───────►│ Lumerical GUI │
│ (lumapi) │ IPC │ (Running) │
└─────────────────┘ └─────────────────┘lumapi launches Lumerical in the background and communicates via inter-process calls. This means:
- Lumerical must be installed — No cloud-only option
- License required — Each lumapi session consumes a license
- Platform quirks — Path setup varies by OS
- Performance overhead — IPC adds latency
- GUI dependency — Some operations require the GUI process
Our Honest Assessment
| Aspect | Rating | Notes |
|---|---|---|
| Setup complexity | ⚠️ Medium | Requires PATH configuration |
| Reliability | ✅ Good | Once configured, works well |
| Performance | ⚠️ Medium | IPC overhead on data transfer |
| Headless operation | ❌ Limited | Some features need GUI |
| Documentation | ✅ Good | Lumerical provides docs |
Despite these challenges, lumapi is the best option available today for programmatic Lumerical integration.
Integration Approaches
Option 1: lumapi + OptixLog SDK (Available Now)
For scripted workflows, integrate OptixLog directly:
import lumapi
import os
import numpy as np
from optixlog import Optixlog
# Initialize
client = Optixlog(api_key=os.getenv("OPTIX_API_KEY"))
project = client.project(name="LumericalProject", create_if_not_exists=True)
run = project.run(
name="waveguide_analysis",
config={
"tool": "Lumerical MODE",
"waveguide_width": 0.5,
"waveguide_height": 0.22
}
)
# Connect to Lumerical
with lumapi.MODE() as mode:
mode.load("waveguide.lms")
mode.run()
# Get results
neff = mode.getresult("FDE", "neff")
# Log to OptixLog
for i, n in enumerate(neff["neff"]):
run.log(step=i, mode_index=i, neff=float(np.real(n)))
print("✅ Results logged to OptixLog")Option 2: Companion App (Planned)
Coming Soon — The OptixLog Companion App is in active development.
For GUI-centric workflows, we're building a desktop companion app:
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Your Workstation │
│ │
│ ┌─────────────┐ ┌──────────────────────────────────────┐ │
│ │ Lumerical │ │ OptixLog Companion App │ │
│ │ FDTD/MODE │ │ ┌─────────────────────────────────┐ │ │
│ │ etc. │ │ │ System Tray Icon │ │ │
│ │ │ │ │ ════════════════ │ │ │
│ │ Save .fsp ──┼────┼──┼─► Watch Directory │ │ │
│ │ │ │ │ Parse Results │ │ │
│ │ │ │ │ [Sync to OptixLog] ◄── Click │ │ │
│ └─────────────┘ │ └─────────────────────────────────┘ │ │
│ └──────────────────────────────────────┘ │
│ │ │
└──────────────────────────────────────┼──────────────────────────┘
│ HTTPS
▼
┌──────────────────┐
│ OptixLog Cloud │
└──────────────────┘Planned Features
- System tray application (Windows
.exe, Linux AppImage) - Directory watching for simulation file changes
- Automatic result extraction from
.fsp,.lms,.ldevfiles - One-click sync to upload results
- Configuration UI for API key and project mapping
Configuration (Planned)
# ~/.optixlog/companion.yaml
connection:
api_key: "your_api_key"
api_url: "https://optixlog.com" # Or self-hosted
monitoring:
watch_paths:
- "C:/Users/engineer/Lumerical"
- "/home/engineer/simulations"
file_patterns:
- "*.fsp" # FDTD
- "*.lms" # MODE
- "*.ldev" # DEVICE
- "*.icp" # INTERCONNECT
sync:
auto_sync: false # Manual sync preferred
sync_on_save: false
batch_interval: 300 # seconds
project_mapping:
default: "Lumerical Experiments"
paths:
"C:/Users/engineer/Lumerical/RingResonators": "Ring Resonator Designs"
"C:/Users/engineer/Lumerical/Waveguides": "Waveguide Library"Lumerical Product Suite
FDTD
3D/2D electromagnetic solver for nanophotonic devices
MODE
Eigenmode solver for waveguides and couplers
INTERCONNECT
Photonic integrated circuit simulator
CML Compiler
Compact model library generation
Multiphysics
CHARGE, HEAT, DGTD, and FEEM solvers
Setting Up lumapi
import sys
# Add Lumerical's Python API to path
sys.path.append("C:/Program Files/Lumerical/v241/api/python")
import lumapiOr set PYTHONPATH:
set PYTHONPATH=%PYTHONPATH%;C:\Program Files\Lumerical\v241\api\pythonimport sys
sys.path.append("/opt/lumerical/v241/api/python")
import lumapiOr in .bashrc:
export PYTHONPATH=$PYTHONPATH:/opt/lumerical/v241/api/pythonimport sys
sys.path.append("/Applications/Lumerical/v241/api/python")
import lumapiVerify Installation
import lumapi
# Test connection
try:
with lumapi.FDTD() as fdtd:
print(f"✅ Connected to Lumerical FDTD")
print(f" Version: {fdtd.version()}")
except Exception as e:
print(f"❌ Connection failed: {e}")Basic Integration Pattern
Every Lumerical + OptixLog script follows this pattern:
import lumapi
import os
import matplotlib
matplotlib.use("agg")
import matplotlib.pyplot as plt
import numpy as np
from optixlog import Optixlog
def run_simulation():
# 1. Initialize OptixLog
client = Optixlog(api_key=os.getenv("OPTIX_API_KEY"))
project = client.project(name="LumericalProject", create_if_not_exists=True)
# 2. Define parameters
params = {
"wavelength": 1.55,
"waveguide_width": 0.5,
"simulation_time": 1000 # fs
}
# 3. Create run with config
run = project.run(
name="fdtd_simulation",
config=params
)
# 4. Run Lumerical simulation
with lumapi.FDTD() as fdtd:
# Load or build simulation
fdtd.load("device.fsp")
# Apply parameters
fdtd.setnamed("waveguide", "x span", params["waveguide_width"] * 1e-6)
# Run
fdtd.run()
# 5. Extract results
T = fdtd.getresult("transmission", "T")
# 6. Log metrics
run.log(step=0,
max_transmission=float(np.max(T["T"])),
bandwidth=calculate_bandwidth(T))
# 7. Log visualizations
fig, ax = plt.subplots()
ax.plot(T["lambda"] * 1e9, T["T"])
ax.set_xlabel("Wavelength (nm)")
ax.set_ylabel("Transmission")
run.log_matplotlib("transmission_spectrum", fig)
plt.close(fig)
print("✅ Simulation complete!")
if __name__ == "__main__":
run_simulation()What to Log
Configuration
run = project.run(
name="ring_resonator_v3",
config={
# Tool info
"tool": "Lumerical FDTD",
"version": "2024 R1",
"solver": "3D",
# Geometry
"ring_radius": 5.0, # μm
"waveguide_width": 0.5, # μm
"coupling_gap": 0.2, # μm
# Materials
"core_material": "Si (Silicon) - Palik",
"clad_material": "SiO2 (Glass) - Palik",
# Mesh
"mesh_accuracy": 3,
"override_mesh": True,
"dx": 0.02, # μm
# Simulation
"simulation_time": 2000, # fs
"auto_shutoff": 1e-5
}
)Simulation Progress
# Log mesh info
run.log(step=0,
mesh_cells=int(fdtd.getnamed("FDTD", "mesh cells")),
memory_mb=float(fdtd.getnamed("FDTD", "memory")))
# Log completion
run.log(step=1,
simulation_time_actual=float(fdtd.getnamed("FDTD", "simulation time")),
auto_shutoff_reached=True)Results
# Transmission/Reflection
T = fdtd.getresult("T_monitor", "T")
run.log(step=2,
max_T=float(np.max(T["T"])),
min_T=float(np.min(T["T"])),
center_wavelength=float(T["lambda"][np.argmax(T["T"])]))
# Mode data
neff = mode.getresult("FDE", "neff")
run.log(step=3,
fundamental_neff=float(np.real(neff["neff"][0])),
num_modes=len(neff["neff"]))Tips & Best Practices
1. Use Context Managers
# Good - auto-closes
with lumapi.FDTD() as fdtd:
fdtd.run()
# Avoid - may leave processes running
fdtd = lumapi.FDTD()
fdtd.run()
# fdtd.close() # Easy to forget2. Handle License Errors
try:
with lumapi.FDTD() as fdtd:
fdtd.run()
except lumapi.LumApiError as e:
if "license" in str(e).lower():
run.log(step=0, error="License unavailable", status="failed")
raise3. Extract Data Efficiently
# Get only what you need
T = fdtd.getresult("monitor", "T") # Just transmission
# Avoid large data transfers when possible
# Don't: E = fdtd.getresult("field_monitor", "E") # Can be huge4. Close Figures After Logging
fig, ax = plt.subplots()
ax.plot(wavelengths, transmission)
run.log_matplotlib("spectrum", fig)
plt.close(fig) # Important!Troubleshooting
lumapi Import Fails
# Check path is correct
import sys
print(sys.path)
# Verify Lumerical installation
import os
print(os.path.exists("/opt/lumerical/v241/api/python/lumapi.py"))Connection Timeout
# Increase timeout for slow starts
with lumapi.FDTD(hide=True) as fdtd: # hide=True for headless
passMemory Issues
# Clear memory between simulations
fdtd.switchtolayout()
fdtd.deleteall()Sign Up for Companion App Beta
Interested in the GUI-friendly Companion App?
We'll notify you when the beta is available for Windows and Linux.
Next Steps
- FDTD Integration — Nanophotonic device simulation
- MODE Integration — Waveguide eigenmode analysis
- INTERCONNECT Integration — Circuit simulation