GUI Simulation Guide: Creating Shapes and Running Simulations

Time: 45 minutes
Difficulty: Intermediate
Prerequisites: Basic Python, familiarity with electromagnetic simulations

Learning Objectives

By the end of this tutorial, you will:

  • ✓ Understand the Prismo GUI interface (Lumerical-style)

  • ✓ Create a new simulation using the GUI

  • ✓ Add geometric shapes (Box, Sphere, Cylinder, Polygon)

  • ✓ Assign materials to shapes

  • ✓ Add sources (PlaneWave, GaussianBeam, ModeSource)

  • ✓ Add monitors (FieldMonitor, FluxMonitor)

  • ✓ Use the 3D viewport with slice planes

  • ✓ Run simulations and view results

Overview

This guide will walk you through creating a complete simulation using Prismo’s GUI. We’ll build a waveguide structure with proper materials, sources, and monitors - similar to how you would use Lumerical FDTD Solutions.

The Prismo GUI provides:

  • 3D Viewport: Interactive 3D visualization of your simulation geometry with embedded viewport controls

  • Slice Planes: Cut through your geometry to inspect interior structures (XY, XZ, YZ planes)

  • Shape Dialog: Interactive dialog for creating geometric shapes (Box, Sphere, Cylinder) with material assignment

  • Results Viewer: Built-in viewer for visualizing field data, spectra, S-parameters, and time series

  • Simulation Control: Run, stop, and reset simulations with progress monitoring

Step 1: Launch the GUI

Start the Prismo GUI from the command line:

prismo gui

Or from Python:

from prismo.gui import MainWindow

window = MainWindow()
window.show()

The main window will open with:

  • Left Panel: Embedded 3D Viewport with slice plane controls

  • Right Panel:

    • Geometry panel with shape list and “Add Shape” button

    • Property plotter for material visualization

    • Results Viewer for simulation results

  • Top Menu: File operations, simulation control, view options

Step 2: Create a New Simulation

When you first launch the GUI, a default simulation is created automatically. To create a new simulation:

  1. Click File → New Simulation, or

  2. Click the New button in the toolbar

This creates a fresh simulation with default parameters:

  • Size: 10µm × 10µm × 1µm

  • Resolution: 20 points per micrometer

  • Boundary conditions: PML (Perfectly Matched Layer)

Step 3: Understanding the 3D Viewport

The 3D viewport displays your simulation geometry. To open the interactive 3D window:

  1. Click “Open 3D View” button in the viewport panel

  2. A separate window will open with the 3D visualization

The viewport supports:

  • Mouse Interaction:

    • Left-click drag: Rotate view

    • Right-click drag: Pan view

    • Scroll wheel: Zoom in/out

  • Slice Planes: Cut through geometry to see cross-sections

  • Reset Camera: Return to default viewing angle

Step 4: Using Slice Planes

Slice planes let you inspect the interior of your geometry, just like in Lumerical.

Enable Slice Planes

In the “Slice Planes” section:

  1. XY Plane (horizontal slice): Check the box to enable

  2. Use the Z Position slider to move the slice up/down

  3. XZ Plane (vertical along y): Enable and adjust Y Position

  4. YZ Plane (vertical along x): Enable and adjust X Position

You can enable multiple slice planes simultaneously to see different cross-sections.

Example: To see the middle of a waveguide:

# In the GUI, enable XY slice plane
# Set Z Position to 0.5e-6 (middle of 1µm thick structure)

Step 5: Creating Geometric Shapes

The GUI provides two ways to add shapes: through the interactive Shape Dialog or programmatically via Python code.

Adding Shapes via GUI (Shape Dialog)

The easiest way to add shapes is using the built-in Shape Dialog:

  1. Click the “Add Shape” button in the Geometry panel (right side)

  2. A dialog window will open with options to create:

    • Box (Rectangle): Rectangular structures like waveguides

    • Sphere (Ball): Spherical structures

    • Cylinder (Tube): Cylindrical waveguides or structures

  3. Configure Shape Parameters:

    • Position: Set center coordinates (X, Y, Z) in meters

    • Size/Dimensions:

      • For Box: Width (X), Height (Y), Depth (Z)

      • For Sphere: Radius

      • For Cylinder: Radius, Height, and Axis orientation

    • Material: Select from library materials (Si, SiO2, Au, etc.) or enter custom permittivity

  4. Click “Create Shape” to add it to your simulation

The shape will immediately appear in the 3D viewport, and you can see it listed in the Geometry panel.

Adding Shapes Programmatically

You can also add shapes via Python code for more control or automation:

Box (Rectangular Structure)

from prismo import Simulation, Box, Material

# Create simulation
sim = Simulation(
    size=(10.0e-6, 5.0e-6, 1.0e-6),
    resolution=20.0e6,
)

# Define material
silicon = Material(name="Si", epsilon_r=12.0)

# Create a waveguide box
waveguide = Box(
    material=silicon,
    center=(5.0e-6, 2.5e-6, 0.5e-6),  # Center position
    size=(8.0e-6, 0.5e-6, 0.22e-6),   # Length, width, height
)

# Add to simulation
sim.add_shape(waveguide)

# Update GUI viewport (if using GUI)
# viewport.sync_with_simulation(sim)

Sphere

from prismo import Sphere

# Create a spherical structure
sphere = Sphere(
    material=silicon,
    center=(5.0e-6, 2.5e-6, 0.5e-6),
    radius=0.5e-6,
)

sim.add_shape(sphere)

Cylinder

from prismo import Cylinder

# Create a cylindrical waveguide
cylinder = Cylinder(
    material=silicon,
    center=(5.0e-6, 2.5e-6, 0.5e-6),
    radius=0.25e-6,
    height=1.0e-6,
    axis="z",  # Orientation: "x", "y", or "z"
)

sim.add_shape(cylinder)

Polygon (Custom 2D Shape)

from prismo import Polygon
import numpy as np

# Define polygon vertices (2D, will be extruded)
vertices = np.array([
    [1.0e-6, 1.0e-6],
    [3.0e-6, 1.0e-6],
    [3.0e-6, 2.0e-6],
    [2.0e-6, 3.0e-6],
    [1.0e-6, 2.0e-6],
])

polygon = Polygon(
    material=silicon,
    vertices=vertices,
    z_min=0.0,      # Bottom of extrusion
    z_max=0.22e-6,  # Top of extrusion
)

sim.add_shape(polygon)

Multiple Materials

You can add multiple shapes with different materials:

from prismo import Material

# Define materials
silicon = Material(name="Si", epsilon_r=12.0)
oxide = Material(name="SiO2", epsilon_r=2.25)

# Substrate
substrate = Box(
    material=oxide,
    center=(5.0e-6, 2.5e-6, 0.0),
    size=(10.0e-6, 5.0e-6, 0.5e-6),
)

# Waveguide on top
waveguide = Box(
    material=silicon,
    center=(5.0e-6, 2.5e-6, 0.61e-6),
    size=(8.0e-6, 0.5e-6, 0.22e-6),
)

sim.add_shape(substrate)
sim.add_shape(waveguide)

Step 6: Adding Sources

Sources excite electromagnetic fields in your simulation.

Plane Wave Source

from prismo import PlaneWaveSource

# Create a plane wave source
source = PlaneWaveSource(
    center=(1.0e-6, 2.5e-6, 0.5e-6),
    size=(0.0, 5.0e-6, 1.0e-6),  # Cross-sectional area
    direction="x",                # Propagation direction
    polarization="y",             # E-field polarization
    frequency=193.4e12,           # 1550 nm (193.4 THz)
    amplitude=1.0,
)

sim.add_source(source)

Gaussian Beam Source

from prismo import GaussianBeamSource

# Create a Gaussian beam
source = GaussianBeamSource(
    center=(1.0e-6, 2.5e-6, 0.5e-6),
    size=(0.0, 1.0e-6, 0.0),     # Line source
    direction="x",
    polarization="y",
    frequency=193.4e12,
    beam_waist=0.5e-6,           # Beam waist radius
    pulse=True,                  # Use Gaussian pulse
    pulse_width=10e-15,          # Pulse width (10 fs)
    amplitude=1.0,
)

sim.add_source(source)

Mode Source (Waveguide Mode Launcher)

from prismo import ModeSource

# Create a mode source that launches a waveguide mode
source = ModeSource(
    center=(1.0e-6, 2.5e-6, 0.5e-6),
    size=(0.0, 1.0e-6, 0.22e-6),  # Cross-section
    direction="x",
    mode_index=0,                 # Fundamental mode
    frequency=193.4e12,
    amplitude=1.0,
)

sim.add_source(source)

Step 7: Adding Monitors

Monitors record field data during the simulation.

Field Monitor

Record electric and magnetic fields:

from prismo import FieldMonitor

# Create a field monitor
monitor = FieldMonitor(
    center=(5.0e-6, 2.5e-6, 0.5e-6),
    size=(8.0e-6, 3.0e-6, 0.22e-6),
    components=["Ey", "Hz"],      # Components to record
    time_domain=True,             # Record time-domain data
    frequencies=[193.4e12],       # Also do DFT at this frequency
    name="field_monitor",
)

sim.add_monitor(monitor)

Monitor Types by Size:

  • Point Monitor: size=(0, 0, 0) - Single location

  • Line Monitor: size=(Lx, 0, 0) - 1D profile

  • Plane Monitor: size=(Lx, Ly, 0) - 2D slice

  • Volume Monitor: size=(Lx, Ly, Lz) - Full 3D region

Flux Monitor

Record power flow:

from prismo import FluxMonitor

# Monitor power transmission
flux_monitor = FluxMonitor(
    center=(9.0e-6, 2.5e-6, 0.5e-6),
    size=(0.0, 1.0e-6, 0.22e-6),
    direction="x",                # Direction of power flow
    frequencies=[193.4e12],
    name="transmission",
)

sim.add_monitor(flux_monitor)

Multiple Monitors

You can add multiple monitors to track different regions:

# Input monitor
input_monitor = FieldMonitor(
    center=(1.0e-6, 2.5e-6, 0.5e-6),
    size=(0.0, 1.0e-6, 0.22e-6),
    components="all",
    frequencies=[193.4e12],
    name="input",
)

# Output monitor
output_monitor = FieldMonitor(
    center=(9.0e-6, 2.5e-6, 0.5e-6),
    size=(0.0, 1.0e-6, 0.22e-6),
    components="all",
    frequencies=[193.4e12],
    name="output",
)

sim.add_monitor(input_monitor)
sim.add_monitor(output_monitor)

Step 8: Setting Simulation Parameters

Before running, configure simulation parameters:

Simulation Size and Resolution

sim = Simulation(
    size=(10.0e-6, 5.0e-6, 1.0e-6),  # Physical size (m)
    resolution=20.0e6,                # Points per meter
    boundary_conditions="pml",        # PML absorbing boundaries
    pml_layers=10,                    # Number of PML layers
    courant_factor=0.9,               # Time step safety factor
)

Resolution Guidelines:

  • Higher resolution = more accurate but slower

  • Typical: 20-50 points per wavelength

  • For 1550 nm light: ~20-50 points per micrometer

Boundary Conditions

  • PML: Absorbing boundaries (recommended)

  • Periodic: Periodic boundary conditions

  • Reflecting: Perfect electric/magnetic conductors

Step 9: Viewing Geometry in the GUI

After adding shapes, sources, and monitors:

  1. Sync Viewport: The GUI automatically updates when you sync:

# In your code, after adding shapes
viewport.sync_with_simulation(sim)
  1. Open 3D View: Click “Open 3D View” to see:

    • Shapes: Colored by material (permittivity-based colors)

    • Sources: Green wireframe boxes or red spheres

    • Monitors: Yellow wireframe boxes

  2. Use Slice Planes:

    • Enable XY plane to see horizontal cross-section

    • Adjust Z position to slice through different heights

    • Enable multiple planes for different views

Step 10: Running the Simulation

From Code

import time

# Define progress callback
def progress(step, total_steps, sim_time, elapsed_time):
    if step % 100 == 0:
        print(f"Step {step}/{total_steps} ({step/total_steps*100:.1f}%)")

# Run simulation
sim_time = 100e-15  # 100 femtoseconds
sim.run(sim_time, progress_callback=progress)

print(f"Simulation completed in {sim.current_time*1e15:.1f} fs")

From GUI

  1. Click Simulation → Run or the Run button

  2. Progress will be shown in the status bar

  3. Click Stop to interrupt the simulation

Step 11: Viewing Results

After the simulation completes, you can view results both in the GUI and programmatically.

Viewing Results in GUI (Results Viewer)

The GUI includes a built-in Results Viewer for interactive visualization:

  1. Open Results Viewer: Expand the “Results Viewer” section in the right panel

  2. Load Results: You have two options:

    • Load from Monitor: Click “Load from Monitor” to load data directly from a monitor in your simulation

    • Load from File: Click “Load Results…” to load previously saved results from CSV or Parquet files

  3. Visualize Data: The Results Viewer supports:

    • Field Plots: 2D field distributions with colormaps

    • Spectra: Frequency-domain power spectra

    • S-Parameters: Reflection and transmission coefficients

    • Time Series: Time-domain field evolution

  4. Interactive Controls:

    • Select different field components (Ex, Ey, Ez, Hx, Hy, Hz)

    • Adjust colormap and scaling

    • Zoom and pan in plots

Retrieve Field Data Programmatically

# Get time-domain field data
time_points, field_data = monitor.get_time_data("Ey")

# Get frequency-domain data
field_at_freq = monitor.get_frequency_data("Ey", frequency=193.4e12)

# Plot with matplotlib
import matplotlib.pyplot as plt

plt.figure(figsize=(10, 6))
plt.imshow(field_data[-1], cmap="RdBu_r", origin="lower")
plt.colorbar(label="Ey (V/m)")
plt.xlabel("x (grid points)")
plt.ylabel("y (grid points)")
plt.title("Electric Field Ey at Final Time")
plt.show()

Retrieve Flux Data

# Get power transmission
transmission = flux_monitor.get_frequency_domain_power(frequency=193.4e12)
print(f"Transmission: {transmission:.4f}")

Saving and Loading Results

You can save results to files for later analysis:

# Export to CSV
from prismo.io.exporters import CSVExporter

exporter = CSVExporter()
exporter.export_spectrum(monitor, "results.csv")

# Later, load in GUI using "Load Results..." button

Complete Example

Here’s a complete example combining everything:

from prismo import (
    Simulation, Box, Material, 
    GaussianBeamSource, FieldMonitor, FluxMonitor
)

# Create simulation
sim = Simulation(
    size=(10.0e-6, 5.0e-6, 1.0e-6),
    resolution=20.0e6,
)

# Materials
silicon = Material(name="Si", epsilon_r=12.0)
oxide = Material(name="SiO2", epsilon_r=2.25)

# Substrate
substrate = Box(
    material=oxide,
    center=(5.0e-6, 2.5e-6, 0.0),
    size=(10.0e-6, 5.0e-6, 0.5e-6),
)

# Waveguide
waveguide = Box(
    material=silicon,
    center=(5.0e-6, 2.5e-6, 0.61e-6),
    size=(8.0e-6, 0.5e-6, 0.22e-6),
)

sim.add_shape(substrate)
sim.add_shape(waveguide)

# Source
freq = 193.4e12  # 1550 nm
source = GaussianBeamSource(
    center=(1.0e-6, 2.5e-6, 0.5e-6),
    size=(0.0, 1.0e-6, 0.0),
    direction="x",
    polarization="y",
    frequency=freq,
    beam_waist=0.5e-6,
    pulse=True,
    pulse_width=10e-15,
)

sim.add_source(source)

# Monitors
field_monitor = FieldMonitor(
    center=(5.0e-6, 2.5e-6, 0.5e-6),
    size=(8.0e-6, 3.0e-6, 0.22e-6),
    components=["Ey"],
    time_domain=True,
    frequencies=[freq],
)

flux_monitor = FluxMonitor(
    center=(9.0e-6, 2.5e-6, 0.5e-6),
    size=(0.0, 1.0e-6, 0.22e-6),
    direction="x",
    frequencies=[freq],
)

sim.add_monitor(field_monitor)
sim.add_monitor(flux_monitor)

# Run simulation
sim.run(100e-15)

# View results
time_points, field_data = field_monitor.get_time_data("Ey")
print(f"Field monitor recorded {len(time_points)} time steps")

Tips and Best Practices

  1. Start Simple: Begin with a single shape and source to verify setup

  2. Use Appropriate Resolution:

    • Too low: Inaccurate results

    • Too high: Slow simulation

    • Rule of thumb: 20-30 points per wavelength

  3. Monitor Placement:

    • Place monitors away from sources to avoid artifacts

    • Use multiple monitors to track field evolution

  4. Slice Planes:

    • Use slice planes to verify geometry is correct

    • Check material boundaries before running

  5. Material Properties:

    • Verify epsilon_r values match your materials

    • Use library materials when possible: prismo.get_material("Si")

  6. Simulation Time:

    • Run long enough for fields to propagate and settle

    • Typical: 10-100 femtoseconds for photonic structures

Troubleshooting

Problem: Viewport shows nothing

  • Solution: Ensure you’ve added shapes and synced with simulation

Problem: Slice planes don’t appear

  • Solution: Make sure “Open 3D View” is clicked and slice plane checkbox is enabled

Problem: Simulation is very slow

  • Solution: Reduce resolution or simulation size

Problem: Fields look incorrect

  • Solution: Check material properties, source frequency, and monitor placement

Next Steps

Now that you understand the basics:

Additional Resources