Quick Start
This guide will get you started with L-SURF in minutes.
Full Simulation Pipeline
The recommended approach uses GeometryBuilder to define your simulation geometry
and Simulation to run the ray tracing:
import lsurf as sr
from lsurf.geometry import GeometryBuilder
from lsurf.simulation import Simulation, SimulationConfig
from lsurf.surfaces import SphereSurface, PlaneSurface, SurfaceRole
# Constants
EARTH_RADIUS = 6.371e6 # meters
# Create materials
atmosphere = sr.ExponentialAtmosphere(n_sea_level=1.000293)
# Create surfaces with roles
ocean = SphereSurface(
center=(0, 0, -EARTH_RADIUS),
radius=EARTH_RADIUS,
role=SurfaceRole.OPTICAL, # Reflects/refracts rays
name="ocean",
)
detector = PlaneSurface(
point=(0, 0, 35000),
normal=(0, 0, 1),
role=SurfaceRole.DETECTOR, # Records rays
name="detector_35km",
)
# Build geometry with named media
geometry = (
GeometryBuilder()
.register_medium("atmosphere", atmosphere)
.register_medium("ocean", sr.WATER)
.set_background("atmosphere")
.add_surface(ocean, front="atmosphere", back="ocean")
.add_detector(detector)
.build()
)
# Configure simulation
config = SimulationConfig(
step_size=100.0, # Maximum step size (meters)
max_bounces=5, # Maximum surface interactions
min_intensity=1e-10, # Terminate weak rays
)
# Create and run simulation
sim = Simulation(geometry, config)
source = sr.CollimatedBeam(
center=(0, 0, 1000),
direction=(0.17, 0, -0.98),
radius=10.0,
num_rays=10000,
wavelength=532e-9,
)
result = sim.run(source.generate())
print(f"Detected: {result.statistics.rays_detected}")
print(f"Absorbed: {result.statistics.rays_absorbed}")
Simple Surface Interaction
For quick tests without the full simulation framework:
import lsurf as sr
# Create a flat water surface at z=0
surface = sr.PlanarSurface(
point=(0, 0, 0),
normal=(0, 0, 1),
material_front=sr.AIR_STP,
material_back=sr.WATER,
role=sr.SurfaceRole.OPTICAL,
name="water",
)
# Create a collimated beam at 45° incidence
source = sr.CollimatedBeam(
center=(0, 0, 0.1),
direction=(0.707, 0, -0.707),
radius=0.01,
num_rays=1000,
wavelength=532e-9,
)
rays = source.generate()
# Process surface interaction
reflected, refracted = sr.process_surface_interaction(
rays, surface,
wavelength=532e-9,
generate_reflected=True,
generate_refracted=True,
)
print(f"Reflected rays: {reflected.num_rays}")
print(f"Refracted rays: {refracted.num_rays}")
Key Concepts
GeometryBuilder
The GeometryBuilder provides a fluent interface for constructing simulations:
Register media - Name your materials for consistency across surfaces
Set background - Define the ambient propagation medium
Add surfaces - Add optical surfaces with front/back media
Add detectors - Add detection surfaces
Build - Create the immutable
Geometryobject
geometry = (
GeometryBuilder()
.register_medium("air", sr.AIR_STP)
.register_medium("glass", sr.BK7_GLASS)
.set_background("air")
.add_surface(lens, front="air", back="glass")
.add_detector(screen)
.build()
)
SimulationConfig
Configure simulation behavior with SimulationConfig:
config = SimulationConfig(
step_size=100.0, # Max step size (meters)
min_step_size=3e-4, # Min step (0.3mm → ~1ps resolution)
adaptive_stepping=True, # Reduce step near surfaces
max_bounces=10, # Max surface interactions
min_intensity=1e-10, # Terminate weak rays
bounding_radius=500_000.0, # Terminate rays outside this
polarization="unpolarized", # 's', 'p', or 'unpolarized'
use_gpu=True, # Use GPU if available
)
Surface Roles
Surfaces have roles that determine their behavior:
SurfaceRole.OPTICAL - Reflects and/or refracts rays (Fresnel)
SurfaceRole.DETECTOR - Records rays that hit (terminates them)
SurfaceRole.ABSORBER - Terminates rays without recording
Surfaces
L-SURF supports several surface types:
PlaneSurface - Flat surfaces (mirrors, windows, detectors)
SphereSurface - Curved spherical surfaces (lenses, curved Earth)
GerstnerWaveSurface - Ocean wave surfaces (CPU)
CurvedWaveSurface - Large-scale curved ocean surfaces (CPU)
GPUCurvedWaveSurface - GPU-accelerated curved wave surfaces
LocalRecordingSphereSurface - Spherical detector at altitude
Materials
Built-in materials:
AIR_STP- Standard temperature/pressure air (n ≈ 1.000293)WATER- Pure water (n ≈ 1.33)BK7_GLASS- Common optical glass (n ≈ 1.52)VACUUM- Perfect vacuum (n = 1.0)ExponentialAtmosphere- Height-dependent refractive index
Ray Sources
Generate rays from various sources:
PointSource - Point source with spherical emission
CollimatedBeam - Parallel beam of rays
DivergingBeam - Diverging conical beam
GaussianBeam - Gaussian intensity profile
Simulation Results
The SimulationResult contains:
result = sim.run(rays)
# Access detected rays
detected = result.detected # RecordedRays object
print(f"Detected {detected.num_rays} rays")
print(f"Positions: {detected.positions.shape}")
print(f"Times: {detected.times}")
# Statistics
stats = result.statistics
print(f"Total created: {stats.total_rays_created}")
print(f"Detected: {stats.rays_detected}")
print(f"Absorbed: {stats.rays_absorbed}")
print(f"Bounces: {stats.bounces_completed}")
# Per-detector counts
for name, count in result.detections_per_surface.items():
print(f" {name}: {count} hits")
Logging
Enable logging to see simulation progress:
import lsurf as sr
# INFO level shows simulation summaries
sr.configure_logging("INFO")
# DEBUG level shows per-bounce details
sr.configure_logging("DEBUG")
Next Steps
Examples - Walk through example applications
User Guide - Detailed user guide
API Reference - Complete API reference