Example 06: Detector Scan (Planar Surface) ========================================== **Location:** ``scripts/06_detector_scan.py`` This example demonstrates scanning a detector through angular positions to measure ray distributions from a planar water surface. Overview -------- Features demonstrated: * Detector position scanning * Time-of-arrival measurements * Angular distribution analysis * Specular reflection peak detection * Multi-panel visualization Geometry -------- .. code-block:: text Detector arc (0-90°) ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ ○ Beam → ○ ═════════════════ ← Water surface (z=0) 45° * Water surface: Horizontal plane at z=0 * Beam: 45° grazing angle, 5 mm radius, 5000 rays * Detector: 2 cm radius sphere, scans 0-90° in 1° steps Key Code Sections ----------------- Detector Scanning Loop ~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python detector_angles_deg = np.arange(0, 91, 1) detection_counts = np.zeros(len(detector_angles_deg)) for idx, angle_deg in enumerate(detector_angles_deg): angle_rad = np.radians(angle_deg) detector_pos = ( detector_distance * np.cos(angle_rad), 0, detector_distance * np.sin(angle_rad), ) detector = sr.SphericalDetector( center=detector_pos, radius=detector_radius, ) events = detector.detect(reflected_rays) detection_counts[idx] = len(events) Timing Analysis ~~~~~~~~~~~~~~~ For each detection, record arrival time and angle: .. code-block:: python if num_detected > 0: times = np.array([e.time for e in events]) mean_arrival_times[idx] = np.mean(times) std_arrival_times[idx] = np.std(times) # Angles to detector normal detector_normal = -np.array(detector_pos) / np.linalg.norm(detector_pos) angles = [] for e in events: ray_dir = e.direction / np.linalg.norm(e.direction) cos_angle = np.clip(np.dot(ray_dir, detector_normal), -1, 1) angles.append(np.degrees(np.arccos(cos_angle))) Visualization ~~~~~~~~~~~~~ Two plots are generated: 1. **Ray paths** (``06_detector_scan_rays.png``): - Incident and reflected ray paths - Surface geometry - Intersection points 2. **Detector scan results** (``06_detector_scan.png``): - Detection count vs. angle - Intensity vs. angle - Mean arrival time vs. angle - Timing spread vs. angle - Angular distribution histograms - Time distribution histograms Expected Output --------------- Console output:: ====================================================================== Detector Position Scan - Planar Water Surface ====================================================================== Planar Surface: Material: Water (n ≈ 1.33) Position: z = 0 Beam: Rays: 5000 Grazing angle: 45° Radius: 5.0 mm Height: 5.0 cm Detector: Distance: 100 cm Radius: 2.0 cm Scanning 91 detector positions... Results: Expected specular angle: 45° Peak detection: 5000 rays at 43° Total detections: 12,876 Detection efficiency: 7.2% Physics ------- Specular Reflection ~~~~~~~~~~~~~~~~~~~ For a planar surface, the law of reflection predicts: .. math:: \theta_r = \theta_i where :math:`\theta_i` is the incident angle and :math:`\theta_r` is the reflection angle. For a 45° grazing angle: * Incident angle to normal: 45° * Reflection angle to normal: 45° * Expected detector peak: 45° above horizon Detection Efficiency ~~~~~~~~~~~~~~~~~~~~ Only rays within the detector solid angle are recorded: .. math:: \Omega_{detector} = \frac{\pi r_{detector}^2}{d_{detector}^2} For the parameters in this example: * :math:`r_{detector} = 0.02` m * :math:`d_{detector} = 1.0` m * :math:`\Omega \approx 0.0013` sr (0.01% of sphere) Time Spread ~~~~~~~~~~~ The timing spread arises from: 1. **Beam geometry:** Rays start at different positions 2. **Path length variation:** Different distances to detector 3. **Multiple reflections:** (negligible for planar surface) Running the Example ------------------- From the ``scripts/`` directory:: python 06_detector_scan.py Execution time: ~5 seconds (GPU) or ~20 seconds (CPU) Generated plots: * ``plots/06_detector_scan_rays.png`` * ``plots/06_detector_scan.png`` Exercises --------- 1. **Vary beam angle:** Change ``grazing_angle_deg`` to 30° or 60° 2. **Detector resolution:** Reduce ``detector_step`` to 0.5° for finer sampling 3. **Larger detector:** Increase ``detector_radius`` to 0.05 m 4. **Different materials:** Replace water with ``BK7_GLASS`` See Also -------- * :doc:`07_detector_scan_waves` - Same scan with wave surface * :py:class:`lsurf.detectors.SphericalDetector` - Detector API * :py:func:`lsurf.visualization.plot_detector_scan_results` - Visualization function