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:
- Spectrum
- 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- Spectrum 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 22from ARTcore.ModuleGeometry import Point, Vector, Origin 23import ARTcore.ModuleProcessing as mp 24 25from ARTcore.DepGraphDefinitions import UniformSpectraCalculator 26 27import numpy as np 28from abc import ABC, abstractmethod 29import logging 30 31logger = logging.getLogger(__name__) 32 33# %% Abstract base classes for sources 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 43 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 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 65 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 76 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 87 88# %% Specific power distributions 89class SpatialGaussianPowerDistribution(PowerDistribution): 90 """ 91 Spatial Gaussian power distribution. 92 """ 93 def __init__(self, Power, W0): 94 self.Power = Power 95 self.W0 = W0 96 97 def __call__(self, Origins, Directions): 98 """ 99 Return the power of the rays coming 100 from the origin points Origins to the directions Directions. 101 """ 102 return self.Power * np.exp(-2 * np.linalg.norm(Origins, axis=1) ** 2 / self.W0 ** 2) 103 104class AngularGaussianPowerDistribution(PowerDistribution): 105 """ 106 Angular Gaussian power distribution. 107 """ 108 def __init__(self, Power, Divergence): 109 self.Power = Power 110 self.Divergence = Divergence 111 112 def __call__(self, Origins, Directions): 113 """ 114 Return the power of the rays coming 115 from the origin points Origins to the directions Directions. 116 """ 117 return self.Power * np.exp(-2 * np.arccos(np.dot(Directions, [0, 0, 1])) ** 2 / self.Divergence ** 2) 118 119class GaussianPowerDistribution(PowerDistribution): 120 """ 121 Gaussian power distribution. 122 """ 123 def __init__(self, Power, W0, Divergence): 124 self.Power = Power 125 self.W0 = W0 126 self.Divergence = Divergence 127 128 def __call__(self, Origins, Directions): 129 """ 130 Return the power of the rays coming 131 from the origin points Origins to the directions Directions. 132 """ 133 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) 134 135class UniformPowerDistribution(PowerDistribution): 136 """ 137 Uniform power distribution. 138 """ 139 def __init__(self, Power): 140 self.Power = Power 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 return self.Power * np.ones(len(Origins)) 148 149# %% Specific ray origins distributions 150class PointRayOriginsDistribution(RayOriginsDistribution): 151 """ 152 Point ray origins distribution. 153 """ 154 def __init__(self, Origin): 155 self.Origin = Origin 156 157 def __call__(self, N): 158 """ 159 Return the origins of N rays. 160 """ 161 return mgeo.PointArray([self.Origin for i in range(N)]) 162 163class DiskRayOriginsDistribution(RayOriginsDistribution): 164 """ 165 Disk ray origins distribution. Uses the Vogel spiral to initialize the rays. 166 """ 167 def __init__(self, Origin, Radius, Normal = mgeo.Vector([0, 0, 1])): 168 self.Origin = Origin 169 self.Radius = Radius 170 self.Normal = Normal 171 172 def __call__(self, N): 173 """ 174 Return the origins of N rays. 175 """ 176 MatrixXY = mgeo.SpiralVogel(N, self.Radius) 177 q = mgeo.QRotationVector2Vector(mgeo.Vector([0, 0, 1]), self.Normal) 178 return mgeo.PointArray([self.Origin + mgeo.Vector([MatrixXY[i, 0], MatrixXY[i, 1], 0]) for i in range(N)]).rotate(q) 179 180# %% Specific ray directions distributions 181class UniformRayDirectionsDistribution(RayDirectionsDistribution): 182 """ 183 Uniform ray directions distribution. 184 """ 185 def __init__(self, Direction): 186 self.Direction = Direction 187 188 def __call__(self, N): 189 """ 190 Return the directions of N rays. 191 """ 192 return mgeo.VectorArray([self.Direction for i in range(N)]) 193 194class ConeRayDirectionsDistribution(RayDirectionsDistribution): 195 """ 196 Cone ray directions distribution. Uses the Vogel spiral to initialize the rays. 197 """ 198 def __init__(self, Direction, Angle): 199 self.Direction = Direction 200 self.Angle = Angle 201 202 def __call__(self, N): 203 """ 204 Return the directions of N rays. 205 """ 206 Height = 1 207 Radius = Height * np.tan(self.Angle) 208 MatrixXY = mgeo.SpiralVogel(N, Radius) 209 q = mgeo.QRotationVector2Vector(mgeo.Vector([0, 0, 1]), self.Direction) 210 return mgeo.VectorArray([[MatrixXY[i, 0], MatrixXY[i, 1], Height] for i in range(N)]).rotate(q).normalized() 211 212 213# %% Specific spectra 214class SingleWavelengthSpectrum(Spectrum): 215 """ 216 Single wavelength spectrum. 217 """ 218 def __init__(self, Wavelength): 219 self.Wavelength = Wavelength 220 221 def __call__(self, N): 222 """ 223 Return the wavelengths of N rays. 224 """ 225 return np.ones(N) * self.Wavelength 226 227class UniformSpectrum(Spectrum): 228 """ 229 Uniform spectrum. 230 Can be specified as a r 231 """ 232 def __init__(self, eVMax = None, eVMin = None, eVCentral = None, 233 lambdaMax = None, lambdaMin = None, lambdaCentral = None, 234 eVWidth = None, lambdaWidth = None): 235 # Using the DepSolver, we calculate the minimum and maximum wavelengths 236 values, steps = UniformSpectraCalculator().calculate_values( 237 lambda_min = lambdaMin, 238 lambda_max = lambdaMax, 239 lambda_center = lambdaCentral, 240 lambda_width = lambdaWidth, 241 eV_min = eVMin, 242 eV_max = eVMax, 243 eV_center = eVCentral, 244 eV_width = eVWidth 245 ) 246 self.lambdaMin = values['lambda_min'] 247 self.lambdaMax = values['lambda_max'] 248 def __call__(self, N): 249 """ 250 Return the wavelengths of N rays. 251 """ 252 return np.linspace(self.lambdaMin, self.lambdaMax, N) 253 254 255# %% Simple sources 256class SimpleSource(Source): 257 """ 258 A simple source is defined by 4 parameters: 259 - Spectrum 260 - Power distribution 261 - Ray origins distribution 262 - Ray directions distribution 263 """ 264 265 def __init__(self, Spectrum, PowerDistribution, RayOriginsDistribution, RayDirectionsDistribution): 266 self.Spectrum = Spectrum 267 self.PowerDistribution = PowerDistribution 268 self.RayOriginsDistribution = RayOriginsDistribution 269 self.RayDirectionsDistribution = RayDirectionsDistribution 270 271 def __call__(self, N): 272 """ 273 Return a list of N rays from the simple source. 274 """ 275 Wavelengths = self.Spectrum(N) 276 Origins = self.RayOriginsDistribution(N) 277 Directions = self.RayDirectionsDistribution(N) 278 Powers = self.PowerDistribution(Origins, Directions) 279 RayList = [] 280 for i in range(N): 281 RayList.append(mray.Ray(Origins[i], Directions[i], wavelength=Wavelengths[i], number=i, intensity=Powers[i])) 282 return mray.RayList.from_list(RayList) 283 284 285class ListSource(Source): 286 """ 287 A source containing just a list of rays that can be prepared manually. 288 """ 289 def __init__(self, Rays): 290 self.Rays = Rays 291 292 def __call__(self, N): 293 """ 294 Return the list of rays. 295 """ 296 if N < len(self.Rays): 297 return self.Rays[:N] 298 elif N>len(self.Rays): 299 logger.warning("Requested number of rays is greater than the number of rays in the source. Returning the whole source.") 300 return self.Rays 301 return mray.RayList.from_list(self.Rays)
35class Source(ABC): 36 """ 37 Abstract base class for sources. 38 Fundamentally a source just has to be able to return a list of rays. 39 To do so, we make it callable. 40 """ 41 @abstractmethod 42 def __call__(self,N): 43 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.
45class PowerDistribution(ABC): 46 """ 47 Abstract base class for angular power distributions. 48 """ 49 @abstractmethod 50 def __call__(self, Origins, Directions): 51 """ 52 Return the power of the rays coming 53 from the origin points Origins to the directions Directions. 54 """ 55 pass
Abstract base class for angular power distributions.
56class RayOriginsDistribution(ABC): 57 """ 58 Abstract base class for ray origins distributions. 59 """ 60 @abstractmethod 61 def __call__(self, N): 62 """ 63 Return the origins of N rays. 64 """ 65 pass
Abstract base class for ray origins distributions.
67class RayDirectionsDistribution(ABC): 68 """ 69 Abstract base class for ray directions distributions. 70 """ 71 @abstractmethod 72 def __call__(self, N): 73 """ 74 Return the directions of N rays. 75 """ 76 pass
Abstract base class for ray directions distributions.
78class Spectrum(ABC): 79 """ 80 Abstract base class for spectra. 81 """ 82 @abstractmethod 83 def __call__(self, N): 84 """ 85 Return the wavelengths of N rays. 86 """ 87 pass
Abstract base class for spectra.
90class SpatialGaussianPowerDistribution(PowerDistribution): 91 """ 92 Spatial Gaussian power distribution. 93 """ 94 def __init__(self, Power, W0): 95 self.Power = Power 96 self.W0 = W0 97 98 def __call__(self, Origins, Directions): 99 """ 100 Return the power of the rays coming 101 from the origin points Origins to the directions Directions. 102 """ 103 return self.Power * np.exp(-2 * np.linalg.norm(Origins, axis=1) ** 2 / self.W0 ** 2)
Spatial Gaussian power distribution.
105class AngularGaussianPowerDistribution(PowerDistribution): 106 """ 107 Angular Gaussian power distribution. 108 """ 109 def __init__(self, Power, Divergence): 110 self.Power = Power 111 self.Divergence = Divergence 112 113 def __call__(self, Origins, Directions): 114 """ 115 Return the power of the rays coming 116 from the origin points Origins to the directions Directions. 117 """ 118 return self.Power * np.exp(-2 * np.arccos(np.dot(Directions, [0, 0, 1])) ** 2 / self.Divergence ** 2)
Angular Gaussian power distribution.
120class GaussianPowerDistribution(PowerDistribution): 121 """ 122 Gaussian power distribution. 123 """ 124 def __init__(self, Power, W0, Divergence): 125 self.Power = Power 126 self.W0 = W0 127 self.Divergence = Divergence 128 129 def __call__(self, Origins, Directions): 130 """ 131 Return the power of the rays coming 132 from the origin points Origins to the directions Directions. 133 """ 134 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.
136class UniformPowerDistribution(PowerDistribution): 137 """ 138 Uniform power distribution. 139 """ 140 def __init__(self, Power): 141 self.Power = Power 142 143 def __call__(self, Origins, Directions): 144 """ 145 Return the power of the rays coming 146 from the origin points Origins to the directions Directions. 147 """ 148 return self.Power * np.ones(len(Origins))
Uniform power distribution.
151class PointRayOriginsDistribution(RayOriginsDistribution): 152 """ 153 Point ray origins distribution. 154 """ 155 def __init__(self, Origin): 156 self.Origin = Origin 157 158 def __call__(self, N): 159 """ 160 Return the origins of N rays. 161 """ 162 return mgeo.PointArray([self.Origin for i in range(N)])
Point ray origins distribution.
164class DiskRayOriginsDistribution(RayOriginsDistribution): 165 """ 166 Disk ray origins distribution. Uses the Vogel spiral to initialize the rays. 167 """ 168 def __init__(self, Origin, Radius, Normal = mgeo.Vector([0, 0, 1])): 169 self.Origin = Origin 170 self.Radius = Radius 171 self.Normal = Normal 172 173 def __call__(self, N): 174 """ 175 Return the origins of N rays. 176 """ 177 MatrixXY = mgeo.SpiralVogel(N, self.Radius) 178 q = mgeo.QRotationVector2Vector(mgeo.Vector([0, 0, 1]), self.Normal) 179 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.
182class UniformRayDirectionsDistribution(RayDirectionsDistribution): 183 """ 184 Uniform ray directions distribution. 185 """ 186 def __init__(self, Direction): 187 self.Direction = Direction 188 189 def __call__(self, N): 190 """ 191 Return the directions of N rays. 192 """ 193 return mgeo.VectorArray([self.Direction for i in range(N)])
Uniform ray directions distribution.
195class ConeRayDirectionsDistribution(RayDirectionsDistribution): 196 """ 197 Cone ray directions distribution. Uses the Vogel spiral to initialize the rays. 198 """ 199 def __init__(self, Direction, Angle): 200 self.Direction = Direction 201 self.Angle = Angle 202 203 def __call__(self, N): 204 """ 205 Return the directions of N rays. 206 """ 207 Height = 1 208 Radius = Height * np.tan(self.Angle) 209 MatrixXY = mgeo.SpiralVogel(N, Radius) 210 q = mgeo.QRotationVector2Vector(mgeo.Vector([0, 0, 1]), self.Direction) 211 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.
215class SingleWavelengthSpectrum(Spectrum): 216 """ 217 Single wavelength spectrum. 218 """ 219 def __init__(self, Wavelength): 220 self.Wavelength = Wavelength 221 222 def __call__(self, N): 223 """ 224 Return the wavelengths of N rays. 225 """ 226 return np.ones(N) * self.Wavelength
Single wavelength spectrum.
228class UniformSpectrum(Spectrum): 229 """ 230 Uniform spectrum. 231 Can be specified as a r 232 """ 233 def __init__(self, eVMax = None, eVMin = None, eVCentral = None, 234 lambdaMax = None, lambdaMin = None, lambdaCentral = None, 235 eVWidth = None, lambdaWidth = None): 236 # Using the DepSolver, we calculate the minimum and maximum wavelengths 237 values, steps = UniformSpectraCalculator().calculate_values( 238 lambda_min = lambdaMin, 239 lambda_max = lambdaMax, 240 lambda_center = lambdaCentral, 241 lambda_width = lambdaWidth, 242 eV_min = eVMin, 243 eV_max = eVMax, 244 eV_center = eVCentral, 245 eV_width = eVWidth 246 ) 247 self.lambdaMin = values['lambda_min'] 248 self.lambdaMax = values['lambda_max'] 249 def __call__(self, N): 250 """ 251 Return the wavelengths of N rays. 252 """ 253 return np.linspace(self.lambdaMin, self.lambdaMax, N)
Uniform spectrum. Can be specified as a r
233 def __init__(self, eVMax = None, eVMin = None, eVCentral = None, 234 lambdaMax = None, lambdaMin = None, lambdaCentral = None, 235 eVWidth = None, lambdaWidth = None): 236 # Using the DepSolver, we calculate the minimum and maximum wavelengths 237 values, steps = UniformSpectraCalculator().calculate_values( 238 lambda_min = lambdaMin, 239 lambda_max = lambdaMax, 240 lambda_center = lambdaCentral, 241 lambda_width = lambdaWidth, 242 eV_min = eVMin, 243 eV_max = eVMax, 244 eV_center = eVCentral, 245 eV_width = eVWidth 246 ) 247 self.lambdaMin = values['lambda_min'] 248 self.lambdaMax = values['lambda_max']
257class SimpleSource(Source): 258 """ 259 A simple source is defined by 4 parameters: 260 - Spectrum 261 - Power distribution 262 - Ray origins distribution 263 - Ray directions distribution 264 """ 265 266 def __init__(self, Spectrum, PowerDistribution, RayOriginsDistribution, RayDirectionsDistribution): 267 self.Spectrum = Spectrum 268 self.PowerDistribution = PowerDistribution 269 self.RayOriginsDistribution = RayOriginsDistribution 270 self.RayDirectionsDistribution = RayDirectionsDistribution 271 272 def __call__(self, N): 273 """ 274 Return a list of N rays from the simple source. 275 """ 276 Wavelengths = self.Spectrum(N) 277 Origins = self.RayOriginsDistribution(N) 278 Directions = self.RayDirectionsDistribution(N) 279 Powers = self.PowerDistribution(Origins, Directions) 280 RayList = [] 281 for i in range(N): 282 RayList.append(mray.Ray(Origins[i], Directions[i], wavelength=Wavelengths[i], number=i, intensity=Powers[i])) 283 return mray.RayList.from_list(RayList)
A simple source is defined by 4 parameters:
- Spectrum
- Power distribution
- Ray origins distribution
- Ray directions distribution
286class ListSource(Source): 287 """ 288 A source containing just a list of rays that can be prepared manually. 289 """ 290 def __init__(self, Rays): 291 self.Rays = Rays 292 293 def __call__(self, N): 294 """ 295 Return the list of rays. 296 """ 297 if N < len(self.Rays): 298 return self.Rays[:N] 299 elif N>len(self.Rays): 300 logger.warning("Requested number of rays is greater than the number of rays in the source. Returning the whole source.") 301 return self.Rays 302 return mray.RayList.from_list(self.Rays)
A source containing just a list of rays that can be prepared manually.