ModuleSource
Provides a definition of sources of light. A source can be either a composite source (a sum of sources) or a simple source. A simple source is defined by 4 parameters:
- Wavelength (monochromatic)
- Angular power distribution
- Ray origins distribution
- Ray directions distribution A reason to use composite sources is for when the power and ray distributions depend on the wavelength. Another source option is the source containing just a list of rays that can be prepared manually. Finally another source option is one used for alignment, containing a single ray.
Created in 2019
@author: Anthony Guillaume and Stefan Haessler
1""" 2Provides a definition of sources of light. 3A source can be either a composite source (a sum of sources) or a simple source. 4A simple source is defined by 4 parameters: 5- Wavelength (monochromatic) 6- Angular power distribution 7- Ray origins distribution 8- Ray directions distribution 9A reason to use composite sources is for when the power and ray distributions depend on the wavelength. 10Another source option is the source containing just a list of rays that can be prepared manually. 11Finally another source option is one used for alignment, containing a single ray. 12 13 14Created in 2019 15 16@author: Anthony Guillaume and Stefan Haessler 17""" 18 19# %% Modules 20import ARTcore.ModuleOpticalRay as mray 21import ARTcore.ModuleGeometry as mgeo 22import ARTcore.ModuleProcessing as mp 23 24from ARTcore.DepGraphDefinitions import UniformSpectraCalculator 25 26import numpy as np 27from abc import ABC, abstractmethod 28import logging 29 30logger = logging.getLogger(__name__) 31 32# %% Abstract base classes for sources 33class Source(ABC): 34 """ 35 Abstract base class for sources. 36 Fundamentally a source just has to be able to return a list of rays. 37 To do so, we make it callable. 38 """ 39 @abstractmethod 40 def __call__(self,N): 41 pass 42 43class PowerDistribution(ABC): 44 """ 45 Abstract base class for angular power distributions. 46 """ 47 @abstractmethod 48 def __call__(self, Origins, Directions): 49 """ 50 Return the power of the rays coming 51 from the origin points Origins to the directions Directions. 52 """ 53 pass 54class RayOriginsDistribution(ABC): 55 """ 56 Abstract base class for ray origins distributions. 57 """ 58 @abstractmethod 59 def __call__(self, N): 60 """ 61 Return the origins of N rays. 62 """ 63 pass 64 65class RayDirectionsDistribution(ABC): 66 """ 67 Abstract base class for ray directions distributions. 68 """ 69 @abstractmethod 70 def __call__(self, N): 71 """ 72 Return the directions of N rays. 73 """ 74 pass 75 76class Spectrum(ABC): 77 """ 78 Abstract base class for spectra. 79 """ 80 @abstractmethod 81 def __call__(self, N): 82 """ 83 Return the wavelengths of N rays. 84 """ 85 pass 86 87# %% Specific power distributions 88class SpatialGaussianPowerDistribution(PowerDistribution): 89 """ 90 Spatial Gaussian power distribution, depending only on ray origin points, and not on directions. 91 92 Attributes 93 ---------- 94 Power : float 95 Peak power of the distribution in arbitrary units (user can keep track of their own units.) 96 97 W0 : float 98 1/e^2 beam waist in mm 99 """ 100 def __init__(self, Power, W0): 101 self.Power = Power 102 self.W0 = W0 103 104 def __call__(self, Origins, Directions): 105 """ 106 Return the power of the rays coming 107 from the origin points Origins to the directions Directions. 108 109 Parameters 110 ---------- 111 Origins : mgeo.PointArray 112 PointArray as defined in ModuleGeometry, with points of origin of the rays. 113 114 Directions : mgeo.VectorArray 115 VectorArray as defined in ModuleGeometry, with ray vectors. 116 117 Returns 118 ------- 119 Powers: numpy.array 120 Array of powers for each ray. 121 122 """ 123 return self.Power * np.exp(-2 * np.linalg.norm(Origins, axis=1) ** 2 / self.W0 ** 2) 124 125class AngularGaussianPowerDistribution(PowerDistribution): 126 """ 127 Angular Gaussian power distribution, depending only on ray directions, but not origin points. 128 129 Attributes 130 ---------- 131 Power : float 132 Peak power of the distribution in arbitrary units (user can keep track of their own units.) 133 134 Divergence : float 135 1/e^2 divergence half-angle in radians. 136 """ 137 def __init__(self, Power, Divergence): 138 self.Power = Power 139 self.Divergence = Divergence 140 141 def __call__(self, Origins, Directions): 142 """ 143 Return the power of the rays coming 144 from the origin points Origins to the directions Directions. 145 146 Parameters 147 ---------- 148 Origins : mgeo.PointArray 149 PointArray as defined in ModuleGeometry, with points of origin of the rays. 150 151 Directions : mgeo.VectorArray 152 VectorArray as defined in ModuleGeometry, with ray vectors. 153 154 Returns 155 ------- 156 Powers: numpy.array 157 Array of powers for each ray. 158 159 """ 160 return self.Power * np.exp(-2 * np.arccos(np.dot(Directions, [0, 0, 1])) ** 2 / self.Divergence ** 2) 161 162class GaussianPowerDistribution(PowerDistribution): 163 """ 164 Gaussian power distribution, depending on ray origin points and directions. 165 166 Attributes 167 ---------- 168 Power : float 169 Peak power of the distribution in arbitrary units (user can keep track of their own units.) 170 171 W0 : float 172 1/e^2 beam waist in mm 173 174 Divergence : float 175 1/e^2 divergence half-angle in radians. 176 """ 177 def __init__(self, Power, W0, Divergence): 178 self.Power = Power 179 self.W0 = W0 180 self.Divergence = Divergence 181 182 def __call__(self, Origins, Directions): 183 """ 184 Return the power of the rays coming 185 from the origin points Origins to the directions Directions. 186 187 Parameters 188 ---------- 189 Origins : mgeo.PointArray 190 PointArray as defined in ModuleGeometry, with points of origin of the rays. 191 192 Directions : mgeo.VectorArray 193 VectorArray as defined in ModuleGeometry, with ray vectors. 194 195 Returns 196 ------- 197 Powers: numpy.array 198 Array of powers for each ray. 199 200 """ 201 return self.Power * np.exp(-2 * (Origins-mgeo.Origin).norm ** 2 / self.W0 ** 2) * np.exp(-2 * np.array(np.arccos(np.dot(Directions, mgeo.Vector([1, 0, 0])))) ** 2 / self.Divergence ** 2) 202 203class UniformPowerDistribution(PowerDistribution): 204 """ 205 Uniform power distribution. 206 """ 207 def __init__(self, Power): 208 self.Power = Power 209 210 def __call__(self, Origins, Directions): 211 """ 212 Return the power of the rays coming 213 from the origin points Origins to the directions Directions. 214 """ 215 return self.Power * np.ones(len(Origins)) 216 217# %% Specific ray origins distributions 218class PointRayOriginsDistribution(RayOriginsDistribution): 219 """ 220 Point ray origins distribution. 221 """ 222 def __init__(self, Origin): 223 self.Origin = Origin 224 225 def __call__(self, N): 226 """ 227 Return the origins of N rays. 228 """ 229 return mgeo.PointArray([self.Origin for i in range(N)]) 230 231class DiskRayOriginsDistribution(RayOriginsDistribution): 232 """ 233 Disk ray origins distribution. Uses the Vogel spiral to initialize the rays. 234 """ 235 def __init__(self, Origin, Radius, Normal = mgeo.Vector([0, 0, 1])): 236 self.Origin = Origin 237 self.Radius = Radius 238 self.Normal = Normal 239 240 def __call__(self, N): 241 """ 242 Return the origins of N rays. 243 """ 244 MatrixXY = mgeo.SpiralVogel(N, self.Radius) 245 q = mgeo.QRotationVector2Vector(mgeo.Vector([0, 0, 1]), self.Normal) 246 return mgeo.PointArray([self.Origin + mgeo.Vector([MatrixXY[i, 0], MatrixXY[i, 1], 0]) for i in range(N)]).rotate(q) 247 248# %% Specific ray directions distributions 249class UniformRayDirectionsDistribution(RayDirectionsDistribution): 250 """ 251 Uniform ray directions distribution. 252 """ 253 def __init__(self, Direction): 254 self.Direction = Direction 255 256 def __call__(self, N): 257 """ 258 Return the directions of N rays. 259 """ 260 return mgeo.VectorArray([self.Direction for i in range(N)]) 261 262class ConeRayDirectionsDistribution(RayDirectionsDistribution): 263 """ 264 Cone ray directions distribution. Uses the Vogel spiral to initialize the rays. 265 """ 266 def __init__(self, Direction, Angle): 267 self.Direction = Direction 268 self.Angle = Angle 269 270 def __call__(self, N): 271 """ 272 Return the directions of N rays. 273 """ 274 Height = 1 275 Radius = Height * np.tan(self.Angle) 276 MatrixXY = mgeo.SpiralVogel(N, Radius) 277 q = mgeo.QRotationVector2Vector(mgeo.Vector([0, 0, 1]), self.Direction) 278 return mgeo.VectorArray([[MatrixXY[i, 0], MatrixXY[i, 1], Height] for i in range(N)]).rotate(q).normalized() 279 280 281# %% Specific spectra 282class SingleWavelengthSpectrum(Spectrum): 283 """ 284 Single wavelength spectrum. 285 """ 286 def __init__(self, Wavelength): 287 self.Wavelength = Wavelength 288 289 def __call__(self, N): 290 """ 291 Return the wavelengths of N rays. 292 """ 293 return np.ones(N) * self.Wavelength 294 295class UniformSpectrum(Spectrum): 296 """ 297 Uniform spectrum. 298 Can be specified as any combination of min, max, central or width in either eV or nm. 299 """ 300 def __init__(self, eVMax = None, eVMin = None, eVCentral = None, 301 lambdaMax = None, lambdaMin = None, lambdaCentral = None, 302 eVWidth = None, lambdaWidth = None): 303 # Using the DepSolver, we calculate the minimum and maximum wavelengths 304 values, steps = UniformSpectraCalculator().calculate_values( 305 lambda_min = lambdaMin, 306 lambda_max = lambdaMax, 307 lambda_center = lambdaCentral, 308 lambda_width = lambdaWidth, 309 eV_min = eVMin, 310 eV_max = eVMax, 311 eV_center = eVCentral, 312 eV_width = eVWidth 313 ) 314 self.lambdaMin = values['lambda_min'] 315 self.lambdaMax = values['lambda_max'] 316 def __call__(self, N): 317 """ 318 Return the wavelengths of N rays. 319 """ 320 return np.linspace(self.lambdaMin, self.lambdaMax, N) 321 322 323# %% Simple sources 324class SimpleSource(Source): 325 """ 326 A simple monochromatic source defined by 4 parameters: 327 - Wavelength (in nm) 328 - Power distribution 329 - Ray origins distribution 330 - Ray directions distribution 331 """ 332 333 def __init__(self, Wavelength, PowerDistribution, RayOriginsDistribution, RayDirectionsDistribution): 334 self.Wavelength = Wavelength 335 self.PowerDistribution = PowerDistribution 336 self.RayOriginsDistribution = RayOriginsDistribution 337 self.RayDirectionsDistribution = RayDirectionsDistribution 338 339 def __call__(self, N): 340 """ 341 Return a list of N rays from the simple source. 342 """ 343 Origins = self.RayOriginsDistribution(N) 344 rng = np.random.default_rng() 345 rng.shuffle(Origins) 346 Directions = self.RayDirectionsDistribution(N) 347 Powers = self.PowerDistribution(Origins, Directions) 348 349 RayList = [] 350 for i in range(N): 351 RayList.append(mray.Ray(Origins[i], Directions[i], wavelength=self.Wavelength, number=i, intensity=Powers[i])) 352 return mray.RayList.from_list(RayList) 353 354 355class ListSource(Source): 356 """ 357 A source containing just a list of rays that can be prepared manually. 358 """ 359 def __init__(self, Rays): 360 self.Rays = Rays 361 362 def __call__(self, N): 363 """ 364 Return the list of rays. 365 """ 366 if N < len(self.Rays): 367 return self.Rays[:N] 368 elif N>len(self.Rays): 369 logger.warning("Requested number of rays is greater than the number of rays in the source. Returning the whole source.") 370 return self.Rays 371 return mray.RayList.from_list(self.Rays)
34class Source(ABC): 35 """ 36 Abstract base class for sources. 37 Fundamentally a source just has to be able to return a list of rays. 38 To do so, we make it callable. 39 """ 40 @abstractmethod 41 def __call__(self,N): 42 pass
Abstract base class for sources. Fundamentally a source just has to be able to return a list of rays. To do so, we make it callable.
44class PowerDistribution(ABC): 45 """ 46 Abstract base class for angular power distributions. 47 """ 48 @abstractmethod 49 def __call__(self, Origins, Directions): 50 """ 51 Return the power of the rays coming 52 from the origin points Origins to the directions Directions. 53 """ 54 pass
Abstract base class for angular power distributions.
55class RayOriginsDistribution(ABC): 56 """ 57 Abstract base class for ray origins distributions. 58 """ 59 @abstractmethod 60 def __call__(self, N): 61 """ 62 Return the origins of N rays. 63 """ 64 pass
Abstract base class for ray origins distributions.
66class RayDirectionsDistribution(ABC): 67 """ 68 Abstract base class for ray directions distributions. 69 """ 70 @abstractmethod 71 def __call__(self, N): 72 """ 73 Return the directions of N rays. 74 """ 75 pass
Abstract base class for ray directions distributions.
77class Spectrum(ABC): 78 """ 79 Abstract base class for spectra. 80 """ 81 @abstractmethod 82 def __call__(self, N): 83 """ 84 Return the wavelengths of N rays. 85 """ 86 pass
Abstract base class for spectra.
89class SpatialGaussianPowerDistribution(PowerDistribution): 90 """ 91 Spatial Gaussian power distribution, depending only on ray origin points, and not on directions. 92 93 Attributes 94 ---------- 95 Power : float 96 Peak power of the distribution in arbitrary units (user can keep track of their own units.) 97 98 W0 : float 99 1/e^2 beam waist in mm 100 """ 101 def __init__(self, Power, W0): 102 self.Power = Power 103 self.W0 = W0 104 105 def __call__(self, Origins, Directions): 106 """ 107 Return the power of the rays coming 108 from the origin points Origins to the directions Directions. 109 110 Parameters 111 ---------- 112 Origins : mgeo.PointArray 113 PointArray as defined in ModuleGeometry, with points of origin of the rays. 114 115 Directions : mgeo.VectorArray 116 VectorArray as defined in ModuleGeometry, with ray vectors. 117 118 Returns 119 ------- 120 Powers: numpy.array 121 Array of powers for each ray. 122 123 """ 124 return self.Power * np.exp(-2 * np.linalg.norm(Origins, axis=1) ** 2 / self.W0 ** 2)
Spatial Gaussian power distribution, depending only on ray origin points, and not on directions.
Attributes
Power : float
Peak power of the distribution in arbitrary units (user can keep track of their own units.)
W0 : float
1/e^2 beam waist in mm
126class AngularGaussianPowerDistribution(PowerDistribution): 127 """ 128 Angular Gaussian power distribution, depending only on ray directions, but not origin points. 129 130 Attributes 131 ---------- 132 Power : float 133 Peak power of the distribution in arbitrary units (user can keep track of their own units.) 134 135 Divergence : float 136 1/e^2 divergence half-angle in radians. 137 """ 138 def __init__(self, Power, Divergence): 139 self.Power = Power 140 self.Divergence = Divergence 141 142 def __call__(self, Origins, Directions): 143 """ 144 Return the power of the rays coming 145 from the origin points Origins to the directions Directions. 146 147 Parameters 148 ---------- 149 Origins : mgeo.PointArray 150 PointArray as defined in ModuleGeometry, with points of origin of the rays. 151 152 Directions : mgeo.VectorArray 153 VectorArray as defined in ModuleGeometry, with ray vectors. 154 155 Returns 156 ------- 157 Powers: numpy.array 158 Array of powers for each ray. 159 160 """ 161 return self.Power * np.exp(-2 * np.arccos(np.dot(Directions, [0, 0, 1])) ** 2 / self.Divergence ** 2)
Angular Gaussian power distribution, depending only on ray directions, but not origin points.
Attributes
Power : float
Peak power of the distribution in arbitrary units (user can keep track of their own units.)
Divergence : float
1/e^2 divergence half-angle in radians.
163class GaussianPowerDistribution(PowerDistribution): 164 """ 165 Gaussian power distribution, depending on ray origin points and directions. 166 167 Attributes 168 ---------- 169 Power : float 170 Peak power of the distribution in arbitrary units (user can keep track of their own units.) 171 172 W0 : float 173 1/e^2 beam waist in mm 174 175 Divergence : float 176 1/e^2 divergence half-angle in radians. 177 """ 178 def __init__(self, Power, W0, Divergence): 179 self.Power = Power 180 self.W0 = W0 181 self.Divergence = Divergence 182 183 def __call__(self, Origins, Directions): 184 """ 185 Return the power of the rays coming 186 from the origin points Origins to the directions Directions. 187 188 Parameters 189 ---------- 190 Origins : mgeo.PointArray 191 PointArray as defined in ModuleGeometry, with points of origin of the rays. 192 193 Directions : mgeo.VectorArray 194 VectorArray as defined in ModuleGeometry, with ray vectors. 195 196 Returns 197 ------- 198 Powers: numpy.array 199 Array of powers for each ray. 200 201 """ 202 return self.Power * np.exp(-2 * (Origins-mgeo.Origin).norm ** 2 / self.W0 ** 2) * np.exp(-2 * np.array(np.arccos(np.dot(Directions, mgeo.Vector([1, 0, 0])))) ** 2 / self.Divergence ** 2)
Gaussian power distribution, depending on ray origin points and directions.
Attributes
Power : float
Peak power of the distribution in arbitrary units (user can keep track of their own units.)
W0 : float
1/e^2 beam waist in mm
Divergence : float
1/e^2 divergence half-angle in radians.
204class UniformPowerDistribution(PowerDistribution): 205 """ 206 Uniform power distribution. 207 """ 208 def __init__(self, Power): 209 self.Power = Power 210 211 def __call__(self, Origins, Directions): 212 """ 213 Return the power of the rays coming 214 from the origin points Origins to the directions Directions. 215 """ 216 return self.Power * np.ones(len(Origins))
Uniform power distribution.
219class PointRayOriginsDistribution(RayOriginsDistribution): 220 """ 221 Point ray origins distribution. 222 """ 223 def __init__(self, Origin): 224 self.Origin = Origin 225 226 def __call__(self, N): 227 """ 228 Return the origins of N rays. 229 """ 230 return mgeo.PointArray([self.Origin for i in range(N)])
Point ray origins distribution.
232class DiskRayOriginsDistribution(RayOriginsDistribution): 233 """ 234 Disk ray origins distribution. Uses the Vogel spiral to initialize the rays. 235 """ 236 def __init__(self, Origin, Radius, Normal = mgeo.Vector([0, 0, 1])): 237 self.Origin = Origin 238 self.Radius = Radius 239 self.Normal = Normal 240 241 def __call__(self, N): 242 """ 243 Return the origins of N rays. 244 """ 245 MatrixXY = mgeo.SpiralVogel(N, self.Radius) 246 q = mgeo.QRotationVector2Vector(mgeo.Vector([0, 0, 1]), self.Normal) 247 return mgeo.PointArray([self.Origin + mgeo.Vector([MatrixXY[i, 0], MatrixXY[i, 1], 0]) for i in range(N)]).rotate(q)
Disk ray origins distribution. Uses the Vogel spiral to initialize the rays.
250class UniformRayDirectionsDistribution(RayDirectionsDistribution): 251 """ 252 Uniform ray directions distribution. 253 """ 254 def __init__(self, Direction): 255 self.Direction = Direction 256 257 def __call__(self, N): 258 """ 259 Return the directions of N rays. 260 """ 261 return mgeo.VectorArray([self.Direction for i in range(N)])
Uniform ray directions distribution.
263class ConeRayDirectionsDistribution(RayDirectionsDistribution): 264 """ 265 Cone ray directions distribution. Uses the Vogel spiral to initialize the rays. 266 """ 267 def __init__(self, Direction, Angle): 268 self.Direction = Direction 269 self.Angle = Angle 270 271 def __call__(self, N): 272 """ 273 Return the directions of N rays. 274 """ 275 Height = 1 276 Radius = Height * np.tan(self.Angle) 277 MatrixXY = mgeo.SpiralVogel(N, Radius) 278 q = mgeo.QRotationVector2Vector(mgeo.Vector([0, 0, 1]), self.Direction) 279 return mgeo.VectorArray([[MatrixXY[i, 0], MatrixXY[i, 1], Height] for i in range(N)]).rotate(q).normalized()
Cone ray directions distribution. Uses the Vogel spiral to initialize the rays.
283class SingleWavelengthSpectrum(Spectrum): 284 """ 285 Single wavelength spectrum. 286 """ 287 def __init__(self, Wavelength): 288 self.Wavelength = Wavelength 289 290 def __call__(self, N): 291 """ 292 Return the wavelengths of N rays. 293 """ 294 return np.ones(N) * self.Wavelength
Single wavelength spectrum.
296class UniformSpectrum(Spectrum): 297 """ 298 Uniform spectrum. 299 Can be specified as any combination of min, max, central or width in either eV or nm. 300 """ 301 def __init__(self, eVMax = None, eVMin = None, eVCentral = None, 302 lambdaMax = None, lambdaMin = None, lambdaCentral = None, 303 eVWidth = None, lambdaWidth = None): 304 # Using the DepSolver, we calculate the minimum and maximum wavelengths 305 values, steps = UniformSpectraCalculator().calculate_values( 306 lambda_min = lambdaMin, 307 lambda_max = lambdaMax, 308 lambda_center = lambdaCentral, 309 lambda_width = lambdaWidth, 310 eV_min = eVMin, 311 eV_max = eVMax, 312 eV_center = eVCentral, 313 eV_width = eVWidth 314 ) 315 self.lambdaMin = values['lambda_min'] 316 self.lambdaMax = values['lambda_max'] 317 def __call__(self, N): 318 """ 319 Return the wavelengths of N rays. 320 """ 321 return np.linspace(self.lambdaMin, self.lambdaMax, N)
Uniform spectrum. Can be specified as any combination of min, max, central or width in either eV or nm.
301 def __init__(self, eVMax = None, eVMin = None, eVCentral = None, 302 lambdaMax = None, lambdaMin = None, lambdaCentral = None, 303 eVWidth = None, lambdaWidth = None): 304 # Using the DepSolver, we calculate the minimum and maximum wavelengths 305 values, steps = UniformSpectraCalculator().calculate_values( 306 lambda_min = lambdaMin, 307 lambda_max = lambdaMax, 308 lambda_center = lambdaCentral, 309 lambda_width = lambdaWidth, 310 eV_min = eVMin, 311 eV_max = eVMax, 312 eV_center = eVCentral, 313 eV_width = eVWidth 314 ) 315 self.lambdaMin = values['lambda_min'] 316 self.lambdaMax = values['lambda_max']
325class SimpleSource(Source): 326 """ 327 A simple monochromatic source defined by 4 parameters: 328 - Wavelength (in nm) 329 - Power distribution 330 - Ray origins distribution 331 - Ray directions distribution 332 """ 333 334 def __init__(self, Wavelength, PowerDistribution, RayOriginsDistribution, RayDirectionsDistribution): 335 self.Wavelength = Wavelength 336 self.PowerDistribution = PowerDistribution 337 self.RayOriginsDistribution = RayOriginsDistribution 338 self.RayDirectionsDistribution = RayDirectionsDistribution 339 340 def __call__(self, N): 341 """ 342 Return a list of N rays from the simple source. 343 """ 344 Origins = self.RayOriginsDistribution(N) 345 rng = np.random.default_rng() 346 rng.shuffle(Origins) 347 Directions = self.RayDirectionsDistribution(N) 348 Powers = self.PowerDistribution(Origins, Directions) 349 350 RayList = [] 351 for i in range(N): 352 RayList.append(mray.Ray(Origins[i], Directions[i], wavelength=self.Wavelength, number=i, intensity=Powers[i])) 353 return mray.RayList.from_list(RayList)
A simple monochromatic source defined by 4 parameters:
- Wavelength (in nm)
- Power distribution
- Ray origins distribution
- Ray directions distribution
334 def __init__(self, Wavelength, PowerDistribution, RayOriginsDistribution, RayDirectionsDistribution): 335 self.Wavelength = Wavelength 336 self.PowerDistribution = PowerDistribution 337 self.RayOriginsDistribution = RayOriginsDistribution 338 self.RayDirectionsDistribution = RayDirectionsDistribution
356class ListSource(Source): 357 """ 358 A source containing just a list of rays that can be prepared manually. 359 """ 360 def __init__(self, Rays): 361 self.Rays = Rays 362 363 def __call__(self, N): 364 """ 365 Return the list of rays. 366 """ 367 if N < len(self.Rays): 368 return self.Rays[:N] 369 elif N>len(self.Rays): 370 logger.warning("Requested number of rays is greater than the number of rays in the source. Returning the whole source.") 371 return self.Rays 372 return mray.RayList.from_list(self.Rays)
A source containing just a list of rays that can be prepared manually.