Getting Started

This guide explains how to interact with L-SURF and covers the key concepts you need to understand for successful ray tracing simulations.

Interacting with L-SURF

L-SURF provides three ways to run simulations:

  1. Command Line Interface (CLI) - Recommended for production simulations

  2. Graphical User Interface (GUI) - Recommended for exploration and learning

  3. Python Scripts - For custom studies and advanced use cases

CLI and GUI: The Preferred Approach

For most users, the CLI and GUI are the recommended ways to interact with L-SURF:

  • GUI (lsurf gui): Best for learning, prototyping, and visualizing results. Provides immediate visual feedback and interactive configuration.

  • CLI (lsurf run): Best for production simulations, batch processing, and automation. Configurations are saved in portable YAML/TOML files.

# Start exploring with the GUI
lsurf gui

# Or build a configuration and run via CLI
lsurf build -o my_simulation.yaml
lsurf run my_simulation.yaml --progress

Numbered Example Scripts

The scripts/ directory contains numbered example scripts (01_basic.py, 02_sources.py, etc.) that demonstrate specific features and techniques.

These scripts are intended as educational examples and references, not as the primary way to run simulations. Use them when you need to:

  • Understand how a specific feature works

  • Create custom workflows not supported by the GUI/CLI

  • Run parameter sweeps or batch experiments

  • Integrate L-SURF into larger research pipelines

Note

For standard simulations, prefer the GUI or CLI over writing custom scripts. The GUI/CLI handle configuration management, validation, and output automatically.

Understanding Materials and Media

Material assignment is one of the most important concepts in L-SURF. Getting it right ensures physically accurate ray tracing results.

What is a Medium?

A medium is a named reference to a material. Materials define optical properties like refractive index, absorption, and scattering.

# Built-in materials
from lsurf import VACUUM, AIR_STP, WATER, BK7_GLASS

# Custom materials
from lsurf import HomogeneousMaterial, ExponentialAtmosphere

# Create a named medium
builder.register_medium("atmosphere", ExponentialAtmosphere())
builder.register_medium("ocean", WATER)

Why Use Named Media?

Named media provide several benefits:

  1. Consistency - Multiple surfaces referencing the same medium are guaranteed to use identical material properties

  2. Clarity - Names like “atmosphere” and “ocean” are self-documenting

  3. Validation - The builder can detect inconsistent material assignments

  4. Maintainability - Change a material in one place, update everywhere

Surface Material Assignment

Each optical surface has two materials: front and back.

  • Front - The material on the positive normal side

  • Back - The material on the negative normal side

from lsurf.surfaces import PlaneSurface, SurfaceRole

# A water surface with air above and water below
water_surface = PlaneSurface(
    point=(0, 0, 0),
    normal=(0, 0, 1),  # Points "up"
    role=SurfaceRole.OPTICAL,
    name="water_interface",
)

# When normal points up (+z):
# - Front (positive normal) = above the surface = atmosphere
# - Back (negative normal) = below the surface = ocean
builder.add_surface(water_surface, front="atmosphere", back="ocean")

The Background Medium

The background medium is the material where rays propagate between surfaces. It’s typically the “ambient” medium of your simulation.

# Rays travel through atmosphere until they hit a surface
builder.set_background("atmosphere")

Important

Rays always propagate through the background medium. When a ray crosses a surface, the material on that side determines refraction behavior, but propagation continues through the background.

Material Checking and Validation

L-SURF includes automatic validation to catch common configuration errors.

The Intersection Problem

When two surfaces intersect (cross each other in space), they create ambiguous regions where it’s unclear which material should apply.

Consider two perpendicular planes at the origin:

  • Plane X: normal (1, 0, 0), front=air, back=water

  • Plane Y: normal (0, 1, 0), front=air, back=glass

At the intersection line, which material is correct? This ambiguity causes validation to fail.

When Validation Fails

The geometry builder raises IntersectingSurfacesError when:

  1. Two surfaces intersect (are not parallel or nested), AND

  2. They have different material assignments

# This FAILS - intersecting planes with different materials
builder.add_surface(plane_x, front="air", back="water")
builder.add_surface(plane_y, front="air", back="glass")
geometry = builder.build()  # Raises IntersectingSurfacesError!

When Validation Passes

Intersecting surfaces are allowed when:

  1. They have the same material on all sides (no optical discontinuity)

# This WORKS - same material everywhere
builder.add_surface(plane_x, front="air", back="air")
builder.add_surface(plane_y, front="air", back="air")
  1. They are parallel (never intersect)

# This WORKS - parallel planes can have different materials
builder.add_surface(plane1, front="air", back="water")
builder.add_surface(plane2, front="water", back="glass")
  1. They are nested (concentric spheres)

# This WORKS - concentric spheres are nested, not intersecting
builder.add_surface(inner_sphere, front="air", back="water")
builder.add_surface(outer_sphere, front="water", back="air")

Skipping Validation

For advanced use cases, validation can be disabled:

# Skip validation (use with caution!)
geometry = builder.build(validate=False)

Warning

Disabling validation may result in physically inconsistent simulations. Only use this if you understand the implications.

Cell-Based Geometry (Advanced)

For complex geometries with intersecting surfaces, use the cell-based API:

# Define surfaces without materials
builder.add_surface_only(plane_x)
builder.add_surface_only(plane_y)

# Define cells (regions) explicitly
builder.add_cell("air", ("plane_x", True), ("plane_y", True))      # Quadrant 1
builder.add_cell("water", ("plane_x", False), ("plane_y", True))   # Quadrant 2
builder.add_cell("glass", ("plane_x", True), ("plane_y", False))   # Quadrant 3
builder.add_cell("vacuum", ("plane_x", False), ("plane_y", False)) # Quadrant 4

This explicitly defines which material applies in each region of space.

Using the “Check Geometry” Feature

In the GUI, the Check Geometry button validates your configuration:

  1. Verifies all optical surfaces have materials assigned

  2. Checks for intersecting surfaces with inconsistent materials

  3. Reports warnings and errors

Always check geometry before running large simulations to catch configuration problems early.

Common Material Configurations

Air-Water Interface

media:
  air:
    type: air
  water:
    type: water

surfaces:
  - name: water_surface
    type: plane
    front_medium: air    # Above (positive normal)
    back_medium: water   # Below (negative normal)
    params:
      point: [0, 0, 0]
      normal: [0, 0, 1]  # Points up

Atmospheric Simulation

media:
  atmosphere:
    type: exponential_atmosphere
    params:
      n_sea_level: 1.000293
      scale_height: 8500.0
  ocean:
    type: water

surfaces:
  - name: ocean
    type: sphere
    front_medium: atmosphere  # Above ocean
    back_medium: ocean        # In ocean
    params:
      center: [0, 0, -6371000]  # Earth center
      radius: 6371000           # Earth radius

Glass Optics

media:
  air:
    type: air
  glass:
    type: glass
    params:
      refractive_index: 1.52

surfaces:
  - name: lens_front
    type: sphere
    front_medium: air
    back_medium: glass
    params:
      center: [0, 0, 0.1]
      radius: 0.5

  - name: lens_back
    type: sphere
    front_medium: glass  # Inside lens
    back_medium: air     # Outside lens
    params:
      center: [0, 0, -0.1]
      radius: 0.5

Next Steps