Sources Module
Sources Module - Ray Generation for Raytracing
This module provides ray source classes for generating initial ray conditions. Each source type generates rays with specific spatial and angular distributions.
Available Sources
- RaySourceABC
Abstract base class defining the source interface.
- PointSource
Isotropic point source emitting in all directions.
- CollimatedBeam
Parallel beam with uniform or Gaussian intensity profile.
- DivergingBeam
Beam with angular divergence (fiber output, LED).
- UniformDivergingBeam
Diverging beam with uniform solid angle distribution.
- GaussianBeam
Gaussian beam following paraxial optics.
- ParallelBeamFromPositions
Parallel rays from explicit position array (atmospheric studies).
- CustomRaySource
Fully customizable rays with per-ray position, direction, wavelength, and intensity (chromatic dispersion, custom setups).
Interface
All sources implement the generate() method:
>>> rays = source.generate()
Returns a RayBatch with initialized positions, directions, wavelengths, and intensities.
Examples
>>> from surface_roughness.sources import CollimatedBeam, PointSource
>>>
>>> # Create a collimated laser beam
>>> beam = CollimatedBeam(
... center=(0, 0, -10),
... direction=(0, 0, 1),
... radius=0.001,
... num_rays=10000,
... wavelength=633e-9,
... power=5e-3
... )
>>> rays = beam.generate()
>>>
>>> # Create an isotropic point source
>>> point = PointSource(
... position=(0, 0, 0),
... num_rays=5000,
... wavelength=532e-9,
... power=1e-3
... )
>>> rays = point.generate()
>>>
>>> # Create parallel rays from explicit positions (for atmospheric studies)
>>> import numpy as np
>>> positions = np.array([
... [-1000, 0, 100],
... [-1000, 0, 200],
... [-1000, 0, 300],
... ])
>>> source = ParallelBeamFromPositions(
... positions=positions,
... direction=(1, 0, 0),
... wavelength=532e-9,
... )
>>> rays = source.generate()
- class lsurf.sources.RaySource(num_rays, wavelength, power=1.0)[source]
Bases:
ABCAbstract base class for ray sources.
A ray source defines initial conditions for a ray batch, including spatial distribution, angular distribution, wavelength spectrum, and intensity distribution.
- Parameters:
Notes
Derived classes must implement the generate() method which returns a fully initialized RayBatch.
The ray intensities should sum to the total power (conservation of energy). When generating rays, use _allocate_rays() to create the batch with proper initialization and _assign_wavelengths() to set wavelengths.
Examples
Creating a custom source:
>>> class MySource(RaySource): ... def __init__(self, position, num_rays, wavelength, power=1.0): ... super().__init__(num_rays, wavelength, power) ... self.position = np.array(position) ... ... def generate(self): ... rays = self._allocate_rays() ... rays.positions[:] = self.position ... # Set directions... ... self._assign_wavelengths(rays) ... return rays
- __init__(num_rays, wavelength, power=1.0)[source]
Initialize ray source.
- Parameters:
- Raises:
ValueError – If num_rays <= 0, power <= 0, wavelength <= 0, or wavelength range is invalid.
- abstractmethod generate()[source]
Generate ray batch with initial conditions.
Creates and initializes a RayBatch with positions, directions, wavelengths, and intensities according to the source configuration.
- Returns:
Initialized rays ready for propagation.
- Return type:
Notes
Implementations should ensure: - All rays are marked as active - Directions are normalized - Intensities sum to total power - Wavelengths are set appropriately
- class lsurf.sources.PointSource(position, num_rays, wavelength, power=1.0)[source]
Bases:
RaySourceIsotropic point source emitting in all directions.
Generates rays from a single point with directions uniformly distributed over the unit sphere.
- Parameters:
- position
Source position in meters.
- Type:
ndarray, shape (3,)
Notes
The angular distribution is uniform over the full 4π steradians. Each ray carries equal intensity (power / num_rays).
Examples
>>> # Monochromatic point source >>> source = PointSource( ... position=(0, 0, 0), ... num_rays=10000, ... wavelength=532e-9, ... power=1e-3 ... ) >>> rays = source.generate()
>>> # Polychromatic source (white light LED) >>> source = PointSource( ... position=(0, 0.1, 0), ... num_rays=5000, ... wavelength=(400e-9, 700e-9), ... power=0.5 ... )
- generate()[source]
Generate isotropic ray distribution.
Creates rays emanating from the source position with directions uniformly distributed over the unit sphere.
- Returns:
Ray batch with isotropic direction distribution.
- Return type:
Notes
Uses the standard method for uniform sphere sampling: - θ uniformly distributed in [0, 2π) - cos(φ) uniformly distributed in [-1, 1]
- class lsurf.sources.CollimatedBeam(center, direction, radius, num_rays, wavelength, power=1.0, profile='uniform')[source]
Bases:
RaySourceCollimated beam with parallel rays.
Generates rays with identical directions and positions distributed in a circular cross-section. Supports uniform and Gaussian intensity profiles.
- Parameters:
center (tuple of float) – Beam center position (x, y, z) in meters.
direction (tuple of float) – Beam propagation direction (dx, dy, dz), will be normalized.
radius (float) – Beam radius in meters.
num_rays (int) – Number of rays to generate.
wavelength (float or tuple of float) – Single wavelength (m) or (min, max) range.
power (float, optional) – Total beam power in watts. Default is 1.0.
profile ({'uniform', 'gaussian'}, optional) – Spatial intensity profile. Default is ‘uniform’.
- center
Beam center position.
- Type:
ndarray, shape (3,)
- direction
Normalized beam direction.
- Type:
ndarray, shape (3,)
Notes
For Gaussian profile, the radius corresponds to 2σ (where σ is the standard deviation of the Gaussian). Ray intensities are weighted according to the Gaussian distribution.
Ray timing is initialized so that all rays cross the reference plane (at center) at time=0. This ensures coherent phase fronts.
Examples
>>> # Uniform circular beam >>> source = CollimatedBeam( ... center=(0, 0, 0), ... direction=(0, 0, 1), ... radius=1e-3, ... num_rays=5000, ... wavelength=633e-9, ... power=5e-3 ... )
>>> # Gaussian beam profile >>> source = CollimatedBeam( ... center=(0, 0, 0), ... direction=(0, 0, 1), ... radius=2e-3, ... num_rays=10000, ... wavelength=1064e-9, ... power=1.0, ... profile='gaussian' ... )
- __init__(center, direction, radius, num_rays, wavelength, power=1.0, profile='uniform')[source]
Initialize collimated beam.
- Parameters:
center (tuple of float) – Beam center position (x, y, z) in meters.
direction (tuple of float) – Beam propagation direction, will be normalized.
radius (float) – Beam radius in meters.
num_rays (int) – Number of rays to generate.
wavelength (float or tuple of float) – Wavelength in meters or (min, max) range.
power (float, optional) – Total beam power in watts. Default is 1.0.
profile ({'uniform', 'gaussian'}, optional) – Spatial intensity profile. Default is ‘uniform’.
- Raises:
ValueError – If radius <= 0 or profile not in {‘uniform’, ‘gaussian’}.
- generate()[source]
Generate collimated beam.
Creates rays with parallel directions and positions sampled in a disk perpendicular to the beam direction.
- Returns:
Ray batch with collimated ray directions.
- Return type:
Notes
For uniform profile, positions are uniformly distributed in a disk. For Gaussian profile, positions follow a 2D Gaussian distribution and intensities are weighted accordingly.
- class lsurf.sources.DivergingBeam(origin, mean_direction, divergence_angle, num_rays, wavelength, power=1.0)[source]
Bases:
RaySourceBeam with angular divergence.
Generates rays from a single point with directions distributed within a cone around the mean direction. Suitable for modeling fiber optic outputs, LEDs, and similar diverging sources.
- Parameters:
origin (tuple of float) – Source position (x, y, z) in meters.
mean_direction (tuple of float) – Mean beam direction (dx, dy, dz), will be normalized.
divergence_angle (float) – Half-angle divergence in radians (cone half-angle). Must be in range (0, π/2).
num_rays (int) – Number of rays to generate.
wavelength (float or tuple of float) – Single wavelength (m) or (min, max) range.
power (float, optional) – Total source power in watts. Default is 1.0.
- origin
Source position.
- Type:
ndarray, shape (3,)
- mean_direction
Normalized mean beam direction.
- Type:
ndarray, shape (3,)
Notes
The angular distribution is uniform within the cone. For Lambertian sources (cosine distribution), a different implementation would be needed.
Examples
>>> # Fiber output with 0.1 rad NA >>> source = DivergingBeam( ... origin=(0, 0, 0), ... mean_direction=(0, 0, 1), ... divergence_angle=0.1, ... num_rays=5000, ... wavelength=1550e-9, ... power=1e-3 ... )
>>> # LED with wide angle >>> source = DivergingBeam( ... origin=(0, 0.1, 0), ... mean_direction=(0, 0, 1), ... divergence_angle=np.radians(30), # 30 degrees ... num_rays=10000, ... wavelength=(400e-9, 700e-9), ... power=0.5 ... )
- __init__(origin, mean_direction, divergence_angle, num_rays, wavelength, power=1.0)[source]
Initialize diverging beam.
- Parameters:
origin (tuple of float) – Source position (x, y, z) in meters.
mean_direction (tuple of float) – Mean beam direction, will be normalized.
divergence_angle (float) – Cone half-angle in radians.
num_rays (int) – Number of rays to generate.
wavelength (float or tuple of float) – Wavelength in meters or (min, max) range.
power (float, optional) – Total source power in watts. Default is 1.0.
- Raises:
ValueError – If divergence_angle not in (0, π/2).
- generate()[source]
Generate diverging beam.
Creates rays from the origin with directions uniformly distributed within a cone around the mean direction.
- Returns:
Ray batch with diverging direction distribution.
- Return type:
Notes
Uses spherical coordinates relative to the mean direction to generate uniformly distributed directions within the cone.
- class lsurf.sources.UniformDivergingBeam(origin, mean_direction, divergence_angle, num_rays, wavelength, power=1.0)[source]
Bases:
RaySourceBeam with angular divergence and uniform solid angle distribution.
Generates rays from a single point with directions uniformly distributed over the solid angle of a cone around the mean direction. Each element of solid angle receives equal ray density.
This is physically correct for sources that emit uniformly over a cone (e.g., idealized point sources with angular limits).
- Parameters:
origin (tuple of float) – Source position (x, y, z) in meters.
mean_direction (tuple of float) – Mean beam direction (dx, dy, dz), will be normalized.
divergence_angle (float) – Half-angle divergence in radians (cone half-angle). Must be in range (0, π/2).
num_rays (int) – Number of rays to generate.
wavelength (float or tuple of float) – Single wavelength (m) or (min, max) range.
power (float, optional) – Total source power in watts. Default is 1.0.
- origin
Source position.
- Type:
ndarray, shape (3,)
- mean_direction
Normalized mean beam direction.
- Type:
ndarray, shape (3,)
Notes
The key difference from DivergingBeam is the sampling of the polar angle:
DivergingBeam: phi ~ Uniform(0, divergence_angle) This creates higher ray density near the cone axis.
UniformDivergingBeam: cos(phi) ~ Uniform(cos(divergence_angle), 1) This creates uniform ray density per solid angle.
- The solid angle of a cone with half-angle θ is:
Ω = 2π(1 - cos(θ))
- For uniform sampling over this solid angle, we need:
dN/dΩ = constant
This requires sampling cos(phi) uniformly, not phi.
Examples
>>> # Uniform emission over 10 degree cone >>> source = UniformDivergingBeam( ... origin=(0, 0, 0), ... mean_direction=(0, 0, 1), ... divergence_angle=np.radians(10), ... num_rays=5000, ... wavelength=1550e-9, ... power=1e-3 ... )
>>> # Compare solid angle coverage >>> # Expected: uniform distribution in cos(phi)
- __init__(origin, mean_direction, divergence_angle, num_rays, wavelength, power=1.0)[source]
Initialize uniform diverging beam.
- Parameters:
origin (tuple of float) – Source position (x, y, z) in meters.
mean_direction (tuple of float) – Mean beam direction, will be normalized.
divergence_angle (float) – Cone half-angle in radians.
num_rays (int) – Number of rays to generate.
wavelength (float or tuple of float) – Wavelength in meters or (min, max) range.
power (float, optional) – Total source power in watts. Default is 1.0.
- Raises:
ValueError – If divergence_angle not in (0, π/2).
- property solid_angle: float
Solid angle of the cone in steradians.
- Returns:
Ω = 2π(1 - cos(θ)) where θ is the divergence half-angle.
- Return type:
- generate()[source]
Generate uniformly diverging beam.
Creates rays from the origin with directions uniformly distributed over the solid angle of a cone around the mean direction.
- Returns:
Ray batch with uniform solid angle distribution.
- Return type:
Notes
The sampling uses: - theta ~ Uniform(0, 2π) for azimuthal angle - cos(phi) ~ Uniform(cos(divergence_angle), 1) for polar angle
This ensures dN/dΩ = constant over the cone.
- class lsurf.sources.GaussianBeam(waist_position, direction, waist_radius, num_rays, wavelength, power=1.0)[source]
Bases:
RaySourceGaussian beam with specified waist and Rayleigh range.
Implements paraxial Gaussian beam using ray approximation. Each ray represents a wavefront normal, with intensity weighted according to the Gaussian profile.
- Parameters:
waist_position (tuple of float) – Position of beam waist (x, y, z) in meters.
direction (tuple of float) – Beam axis direction (dx, dy, dz), will be normalized.
waist_radius (float) – Beam waist radius (w₀) in meters.
num_rays (int) – Number of rays to generate.
wavelength (float) – Wavelength in meters. Must be monochromatic for Gaussian beam.
power (float, optional) – Total beam power in watts. Default is 1.0.
- waist_position
Beam waist position.
- Type:
ndarray, shape (3,)
- direction
Normalized beam direction.
- Type:
ndarray, shape (3,)
Notes
The Gaussian beam has intensity profile:
I(r) = I₀ exp(-2r²/w²)
where w is the beam radius at distance z from the waist:
w(z) = w₀ √(1 + (z/z_R)²)
and z_R = πw₀²/λ is the Rayleigh range.
This implementation generates rays at the waist position with parallel directions (plane wavefront at waist). Ray intensities are weighted according to the Gaussian profile.
Examples
>>> # 1 mm waist Nd:YAG laser >>> source = GaussianBeam( ... waist_position=(0, 0, 0), ... direction=(0, 0, 1), ... waist_radius=1e-3, ... num_rays=5000, ... wavelength=1064e-9, ... power=10e-3 ... ) >>> print(f"Rayleigh range: {source.rayleigh_range:.3f} m")
- __init__(waist_position, direction, waist_radius, num_rays, wavelength, power=1.0)[source]
Initialize Gaussian beam.
- Parameters:
waist_position (tuple of float) – Position of beam waist (x, y, z) in meters.
direction (tuple of float) – Beam axis direction, will be normalized.
waist_radius (float) – Beam waist radius w₀ in meters.
num_rays (int) – Number of rays to generate.
wavelength (float) – Wavelength in meters.
power (float, optional) – Total beam power in watts. Default is 1.0.
- Raises:
ValueError – If wavelength is a tuple (polychromatic not supported), or if waist_radius <= 0.
- generate()[source]
Generate Gaussian beam.
Creates rays at the waist position with Gaussian-distributed positions and parallel directions.
- Returns:
Ray batch with Gaussian intensity distribution.
- Return type:
Notes
Positions are sampled from a 2D Gaussian distribution with σ = w₀/2. Intensities are weighted by the Gaussian profile to accurately represent the beam’s energy distribution.
- beam_radius_at(z)[source]
Compute beam radius at distance z from waist.
- Parameters:
z (float) – Distance from waist along beam axis in meters.
- Returns:
Beam radius w(z) in meters.
- Return type:
Notes
w(z) = w₀ √(1 + (z/z_R)²)
- class lsurf.sources.ParallelBeamFromPositions(positions, direction, wavelength=5.32e-07, power=1.0)[source]
Bases:
RaySourceParallel rays from explicitly specified positions.
All rays share the same direction, making this ideal for: - Atmospheric refraction studies with specific impact parameters - Plane wave propagation through inhomogeneous media - Grid-based ray launching for wavefront analysis
Unlike CollimatedBeam which generates positions in a disk, this source accepts arbitrary position arrays, enabling custom spatial distributions.
- Parameters:
positions (array_like, shape (N, 3)) – Starting positions for each ray in meters.
direction (tuple of float) – Direction vector for all rays (will be normalized).
wavelength (float or tuple of float, optional) – Single wavelength (m) or (min, max) range. Default is 532 nm.
power (float, optional) – Total source power in watts. Default is 1.0.
- positions
Ray starting positions.
- Type:
ndarray, shape (N, 3)
- direction
Normalized ray direction.
- Type:
ndarray, shape (3,)
Examples
>>> # Rays at different impact parameters for atmospheric study >>> impact_params = np.linspace(0, 10000, 100) >>> positions = np.column_stack([ ... -np.sqrt((R + 100e3)**2 - (R + impact_params)**2), # x ... np.zeros_like(impact_params), # y ... impact_params # z ... ]) >>> source = ParallelBeamFromPositions(positions, direction=(1, 0, 0)) >>> rays = source.generate() >>> propagator.propagate(rays, total_distance=500e3, step_size=100)
>>> # Regular grid of rays >>> x, y = np.meshgrid(np.linspace(-1, 1, 10), np.linspace(-1, 1, 10)) >>> positions = np.column_stack([x.ravel(), y.ravel(), np.zeros(100)]) >>> source = ParallelBeamFromPositions(positions, direction=(0, 0, 1))
- __init__(positions, direction, wavelength=5.32e-07, power=1.0)[source]
Initialize parallel ray source.
- Parameters:
positions (array_like, shape (N, 3)) – Starting positions for each ray in meters.
direction (tuple of float) – Direction vector for all rays (will be normalized).
wavelength (float or tuple of float, optional) – Wavelength in meters or (min, max) range. Default is 532 nm.
power (float, optional) – Total source power in watts. Default is 1.0.
- Raises:
ValueError – If positions shape is invalid or direction is zero vector.
- class lsurf.sources.CustomRaySource(positions, directions, wavelengths, intensities=None)[source]
Bases:
objectFully customizable ray source with per-ray properties.
Each ray can have its own position, direction, wavelength, and intensity. This provides maximum flexibility for specialized simulations.
- Parameters:
positions (array_like, shape (N, 3)) – Starting position for each ray in meters.
directions (array_like, shape (N, 3)) – Direction vector for each ray (will be normalized).
wavelengths (array_like, shape (N,)) – Wavelength for each ray in meters.
intensities (array_like, shape (N,), optional) – Intensity/power for each ray in watts. If None, defaults to uniform distribution with total power of 1.0.
- positions
Ray starting positions.
- Type:
ndarray, shape (N, 3)
- directions
Normalized ray directions.
- Type:
ndarray, shape (N, 3)
- wavelengths
Per-ray wavelengths.
- Type:
ndarray, shape (N,)
- intensities
Per-ray intensities.
- Type:
ndarray, shape (N,)
Examples
>>> # Chromatic dispersion study - same start, different wavelengths >>> n_rays = 100 >>> positions = np.tile([0, 0, 1000], (n_rays, 1)) # All at same point >>> directions = np.tile([1, 0, 0], (n_rays, 1)) # All same direction >>> wavelengths = np.linspace(400e-9, 700e-9, n_rays) # Visible spectrum >>> source = CustomRaySource(positions, directions, wavelengths) >>> rays = source.generate()
>>> # Fan of rays for refraction analysis >>> angles = np.linspace(-0.1, 0.1, 50) # +/- 5.7 degrees >>> positions = np.zeros((50, 3)) >>> directions = np.column_stack([ ... np.cos(angles), ... np.zeros(50), ... np.sin(angles) ... ]) >>> wavelengths = np.full(50, 550e-9) >>> source = CustomRaySource(positions, directions, wavelengths)
- __init__(positions, directions, wavelengths, intensities=None)[source]
Initialize custom ray source.
- Parameters:
positions (array_like, shape (N, 3)) – Starting position for each ray in meters.
directions (array_like, shape (N, 3)) – Direction vector for each ray (will be normalized).
wavelengths (array_like, shape (N,)) – Wavelength for each ray in meters.
intensities (array_like, shape (N,), optional) – Intensity for each ray. If None, uniform distribution is used.
- Raises:
ValueError – If array shapes are inconsistent or invalid.
- property directions: ndarray[tuple[Any, ...], dtype[float32]]
Normalized ray directions, shape (N, 3).
- property wavelengths: ndarray[tuple[Any, ...], dtype[float32]]
Per-ray wavelengths in meters, shape (N,).
- generate()[source]
Generate ray batch with specified properties.
Creates a RayBatch with positions, directions, wavelengths, and intensities as specified in the constructor.
- Returns:
Ray batch ready for propagation.
- Return type:
- classmethod from_spectral_fan(origin, direction, wavelength_range, num_rays, total_power=1.0)[source]
Create rays with same position/direction but varying wavelengths.
Convenience factory for chromatic dispersion studies.
- Parameters:
origin (tuple of float) – Starting position for all rays (x, y, z) in meters.
direction (tuple of float) – Direction for all rays (will be normalized).
wavelength_range (tuple of float) – (min_wavelength, max_wavelength) in meters.
num_rays (int) – Number of rays to create.
total_power (float, optional) – Total power distributed uniformly. Default is 1.0 W.
- Returns:
Source with spectral distribution.
- Return type:
Examples
>>> # Visible spectrum from single point >>> source = CustomRaySource.from_spectral_fan( ... origin=(0, 0, 0), ... direction=(1, 0, 0), ... wavelength_range=(400e-9, 700e-9), ... num_rays=100, ... )
- classmethod from_angular_fan(origin, base_direction, angle_range, num_rays, wavelength=5.5e-07, total_power=1.0, fan_axis='vertical')[source]
Create rays from same position with varying angles.
Convenience factory for refraction/reflection studies.
- Parameters:
origin (tuple of float) – Starting position for all rays (x, y, z) in meters.
base_direction (tuple of float) – Central direction of the fan (will be normalized).
angle_range (tuple of float) – (min_angle, max_angle) deviation from base direction in radians.
num_rays (int) – Number of rays to create.
wavelength (float, optional) – Wavelength for all rays. Default is 550 nm.
total_power (float, optional) – Total power distributed uniformly. Default is 1.0 W.
fan_axis (str, optional) – ‘vertical’ for z-rotation, ‘horizontal’ for y-rotation. Default is ‘vertical’.
- Returns:
Source with angular distribution.
- Return type:
Examples
>>> # Fan of rays spanning +/- 10 degrees >>> source = CustomRaySource.from_angular_fan( ... origin=(0, 0, 1000), ... base_direction=(1, 0, 0), ... angle_range=(-0.17, 0.17), # ~10 degrees ... num_rays=50, ... )
Source Classes
|
Abstract base class for ray sources. |
|
Isotropic point source emitting in all directions. |
|
Collimated beam with parallel rays. |
|
Beam with angular divergence. |
|
Gaussian beam with specified waist and Rayleigh range. |
|
Parallel rays from explicitly specified positions. |