Simulation Module ================= The simulation module provides the main ``Simulation`` class for running ray tracing simulations, along with ``SimulationConfig`` for configuration and ``SimulationResult`` for results. .. module:: lsurf.simulation Overview -------- The simulation module contains: 1. **Simulation** - Main class for running ray tracing 2. **SimulationConfig** - Configuration parameters 3. **SimulationResult** - Results container with detected rays and statistics 4. **SimulationStatistics** - Detailed statistics about the simulation run Simulation ---------- .. autoclass:: lsurf.simulation.Simulation :members: :undoc-members: SimulationConfig ---------------- .. autoclass:: lsurf.simulation.SimulationConfig :members: :undoc-members: Configuration Parameters ~~~~~~~~~~~~~~~~~~~~~~~~ The ``SimulationConfig`` dataclass accepts the following parameters: **Propagation Parameters** ``step_size`` : float, default=100.0 Maximum integration step size in meters. Rays advance by at most this distance per propagation step. ``min_step_size`` : float, default=3e-4 Minimum step size in meters for adaptive stepping (0.3mm). This provides approximately 1 picosecond time resolution near surfaces. ``adaptive_stepping`` : bool, default=True Whether to use adaptive step sizing near surfaces. When enabled, steps decrease as rays approach surfaces for more precise intersection timing. ``surface_proximity_factor`` : float, default=0.5 When within proximity threshold, step_size = distance × factor. ``surface_proximity_threshold`` : float, default=10.0 Distance (meters) within which adaptive stepping activates. ``max_steps_per_leg`` : int, default=10000 Maximum propagation steps before forcing a surface check. **Termination Parameters** ``max_bounces`` : int, default=10 Maximum surface interactions before ray termination. ``min_intensity`` : float, default=1e-10 Intensity threshold below which rays are terminated. ``bounding_radius`` : float, default=500000.0 Radius of bounding sphere in meters. Rays outside are terminated. ``bounding_center`` : tuple, default=(0.0, 0.0, -6.371e6) Center of bounding sphere (default is Earth center). **Physics Parameters** ``apply_absorption`` : bool, default=True Whether to apply Beer-Lambert absorption during propagation. ``polarization`` : str, default="unpolarized" Polarization state: ``"s"``, ``"p"``, or ``"unpolarized"``. ``track_polarization_vector`` : bool, default=False Whether to track 3D polarization vectors through interactions. **Tracking Parameters** ``track_surface_hits`` : bool, default=False Whether to store intermediate surface hit positions. Useful for visualization of ray paths. ``track_refracted_rays`` : bool, default=False Whether to continue propagating refracted rays from optical surfaces. When False, only reflected rays continue. **Performance Parameters** ``use_gpu`` : bool, default=True Whether to use GPU acceleration if available. SimulationResult ---------------- .. autoclass:: lsurf.simulation.SimulationResult :members: :undoc-members: Result Attributes ~~~~~~~~~~~~~~~~~ ``detected`` : RecordedRays All rays detected by detector surfaces. Contains positions, directions, times, intensities, and wavelengths. ``remaining`` : RayBatch Rays still active after simulation (not detected/absorbed/terminated). ``statistics`` : SimulationStatistics Detailed statistics about the simulation run. ``detections_per_surface`` : dict[str, int] Mapping from detector name to number of hits. ``surface_hits`` : dict[str, list] or None Intermediate surface hit positions (if ``track_surface_hits=True``). SimulationStatistics -------------------- .. autoclass:: lsurf.simulation.SimulationStatistics :members: :undoc-members: Statistics Attributes ~~~~~~~~~~~~~~~~~~~~~ ``total_rays_initial`` : int Number of rays at simulation start. ``total_rays_created`` : int Total rays including those created by splitting. ``rays_detected`` : int Rays that hit detector surfaces. ``rays_absorbed`` : int Rays absorbed by absorber surfaces. ``rays_terminated_bounds`` : int Rays terminated for leaving bounding sphere. ``rays_terminated_intensity`` : int Rays terminated for falling below intensity threshold. ``rays_terminated_max_bounces`` : int Rays terminated for exceeding max_bounces. ``bounces_completed`` : int Number of bounce iterations completed. ``max_depth_reached`` : int Maximum ray generation depth reached. Usage Example ------------- .. code-block:: python from lsurf.geometry import GeometryBuilder from lsurf.simulation import Simulation, SimulationConfig from lsurf.surfaces import SphereSurface, PlaneSurface, SurfaceRole import lsurf as sr # Build geometry (see geometry module) geometry = ( GeometryBuilder() .register_medium("atmosphere", sr.ExponentialAtmosphere()) .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, max_bounces=5, adaptive_stepping=True, min_step_size=3e-4, # 0.3mm for ~1ps timing polarization="unpolarized", ) # Create simulation sim = Simulation(geometry, config) # Generate rays source = sr.CollimatedBeam( center=(0, 0, 1000), direction=(0.17, 0, -0.98), radius=10.0, num_rays=50000, wavelength=532e-9, ) rays = source.generate() # Run simulation result = sim.run(rays) # Process results print(f"Detected: {result.statistics.rays_detected}") print(f"Absorbed: {result.statistics.rays_absorbed}") print(f"Bounces: {result.statistics.bounces_completed}") # Access detected rays detected = result.detected print(f"Detection times: {detected.times[:10]}") print(f"Detection positions: {detected.positions[:10]}") # Per-detector breakdown for name, count in result.detections_per_surface.items(): print(f" {name}: {count} hits") Adaptive Stepping ----------------- The adaptive stepping feature provides high timing precision near surfaces while maintaining efficiency during long propagation paths: .. list-table:: Adaptive Step Size vs Time Resolution :header-rows: 1 * - Distance to Surface - Step Size - Time Resolution * - > 10m - 100m - ~333ns * - 5m - 2.5m - ~8ns * - 1m - 0.5m - ~1.7ns * - 0.1m - 0.05m - ~167ps * - < 0.6mm - 0.3mm (min) - ~1ps Logging ------- Enable logging to monitor simulation progress: .. code-block:: python import lsurf as sr # INFO level: simulation summaries sr.configure_logging("INFO") # DEBUG level: per-bounce details sr.configure_logging("DEBUG") Example output at INFO level:: INFO - Simulation initialized: 1 optical, 1 detector, 0 absorber surfaces INFO - Starting simulation: 50000 rays, max 5 bounces, step_size=100 m INFO - Simulation complete: 2 bounces, 48523 detected, 0 absorbed, 1477 remaining INFO - Detector 'detector_35km': 48523 hits