Coverage for colorimetry/blackbody.py: 45%

40 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-11-16 22:49 +1300

1""" 

2Blackbody - Planckian Radiator 

3============================== 

4 

5Define objects to compute the spectral radiance of a planckian radiator 

6and its spectral distribution. 

7 

8References 

9---------- 

10- :cite:`CIETC1-482004i` : CIE TC 1-48. (2004). APPENDIX E. INFORMATION ON 

11 THE USE OF PLANCK'S EQUATION FOR STANDARD AIR. In CIE 015:2004 Colorimetry, 

12 3rd Edition (pp. 77-82). ISBN:978-3-901906-33-6 

13- :cite:`Wikipedia2003f` : Wikipedia. (2003). Rayleigh-Jeans law. Retrieved 

14 February 12, 2022, from https://en.wikipedia.org/wiki/Rayleigh-Jeans_law 

15""" 

16 

17from __future__ import annotations 

18 

19import typing 

20 

21import numpy as np 

22 

23from colour.colorimetry import ( 

24 SPECTRAL_SHAPE_DEFAULT, 

25 SpectralDistribution, 

26 SpectralShape, 

27) 

28from colour.constants import CONSTANT_BOLTZMANN, CONSTANT_LIGHT_SPEED 

29 

30if typing.TYPE_CHECKING: 

31 from colour.hints import ArrayLike, NDArrayFloat 

32 

33from colour.utilities import as_float, as_float_array 

34from colour.utilities.common import attest 

35 

36__author__ = "Colour Developers" 

37__copyright__ = "Copyright 2013 Colour Developers" 

38__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause" 

39__maintainer__ = "Colour Developers" 

40__email__ = "colour-developers@colour-science.org" 

41__status__ = "Production" 

42 

43__all__ = [ 

44 "CONSTANT_C1", 

45 "CONSTANT_C2", 

46 "CONSTANT_N", 

47 "planck_law", 

48 "blackbody_spectral_radiance", 

49 "sd_blackbody", 

50 "rayleigh_jeans_law", 

51 "sd_rayleigh_jeans", 

52] 

53 

54# 2 * math.pi * CONSTANT_PLANCK * CONSTANT_LIGHT_SPEED ** 2 

55CONSTANT_C1: float = 3.741771e-16 

56 

57# CONSTANT_PLANCK * CONSTANT_LIGHT_SPEED / CONSTANT_BOLTZMANN 

58CONSTANT_C2: float = 1.4388e-2 

59 

60CONSTANT_N: float = 1 

61 

62 

63def planck_law( 

64 wavelength: ArrayLike, 

65 temperature: ArrayLike, 

66 c1: float = CONSTANT_C1, 

67 c2: float = CONSTANT_C2, 

68 n: float = CONSTANT_N, 

69) -> NDArrayFloat: 

70 """ 

71 Compute the spectral radiance of a blackbody as a function of 

72 wavelength at specified thermodynamic temperature :math:`T[K]` in a 

73 medium with index of refraction :math:`n`. 

74 

75 Parameters 

76 ---------- 

77 wavelength 

78 Wavelength in meters. 

79 temperature 

80 Temperature :math:`T[K]` in kelvin degrees. 

81 c1 

82 The official value of :math:`c1` is provided by the Committee on 

83 Data for Science and Technology (CODATA) and is 

84 :math:`c1=3.741771 \\times 10^{-16}\\ \\mathrm{W/m^2}` *(Mohr and 

85 Taylor, 2000)*. 

86 c2 

87 Since :math:`T` is measured on the International Temperature 

88 Scale, the value of :math:`c2` used in colorimetry should follow 

89 that adopted in the current International Temperature Scale 

90 (ITS-90) *(Preston-Thomas, 1990; Mielenz et al., 1991)*, namely 

91 :math:`c2=1.4388 \\times 10^{-2}\\ \\mathrm{m \\cdot K}`. 

92 n 

93 Medium index of refraction. For dry air at 15°C and 101 325 Pa, 

94 containing 0.03 percent by volume of carbon dioxide, it is 

95 approximately 1.00028 throughout the visible region although 

96 *CIE 15:2004* recommends using :math:`n=1`. 

97 

98 Returns 

99 ------- 

100 :class:`numpy.ndarray` 

101 Radiance in *watts per steradian per square metre* 

102 (:math:`\\mathrm{W \\cdot sr^{-1} \\cdot m^{-2}}`). 

103 

104 Warnings 

105 -------- 

106 The :func:`colour.colorimetry.planck_law` definition behaviour with 

107 n-dimensional arrays is unusual: The ``wavelength`` and 

108 ``temperature`` parameters are first raveled using 

109 :func:`numpy.ravel`. Then, they are *broadcasted* together by 

110 transposing the ``temperature`` parameter. Finally, and for 

111 convenience, the return value is squeezed using :func:`numpy.squeeze`. 

112 

113 Notes 

114 ----- 

115 - The following implementation is expressed in terms of wavelength. 

116 - The SI unit of radiance is *watts per steradian per square 

117 metre* (:math:`\\mathrm{W \\cdot sr^{-1} \\cdot m^{-2}}`). 

118 

119 References 

120 ---------- 

121 :cite:`CIETC1-482004i` 

122 

123 Examples 

124 -------- 

125 >>> planck_law(500 * 1e-9, 5500) # doctest: +ELLIPSIS 

126 20472701909806.5... 

127 >>> planck_law(500 * 1e-9, [5000, 5500, 6000]) # doctest: +ELLIPSIS 

128 array([ 1.2106064...e+13, 2.0472701...e+13, 3.1754431...e+13]) 

129 """ 

130 

131 l = as_float_array(wavelength) # noqa: E741 

132 t = as_float_array(temperature) 

133 

134 attest(np.all(l > 0), "Wavelengths must be positive real numbers!") 

135 

136 l = np.ravel(l)[..., None] # noqa: E741 

137 t = np.ravel(t)[None, ...] 

138 

139 d = 1 / np.expm1(c2 / (n * l * t)) 

140 p = ((c1 * n**-2 * l**-5) / np.pi) * d 

141 

142 return as_float(np.squeeze(p)) 

143 

144 

145blackbody_spectral_radiance = planck_law 

146 

147 

148def sd_blackbody( 

149 temperature: float, 

150 shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, 

151 c1: float = CONSTANT_C1, 

152 c2: float = CONSTANT_C2, 

153 n: float = CONSTANT_N, 

154) -> SpectralDistribution: 

155 """ 

156 Generate the spectral distribution of the planckian radiator for the 

157 specified temperature :math:`T[K]` with values in *watts per steradian 

158 per square metre per nanometre* (:math:`W/sr/m^2/nm`). 

159 

160 Parameters 

161 ---------- 

162 temperature 

163 Temperature :math:`T[K]` in kelvins. 

164 shape 

165 Spectral shape used to create the spectral distribution of the 

166 planckian radiator. 

167 c1 

168 The official value of :math:`c_1` is provided by the Committee on 

169 Data for Science and Technology (CODATA) and is 

170 :math:`c_1=3.741771 \\times 10^{16}\\ W/m^2` *(Mohr and Taylor, 

171 2000)*. 

172 c2 

173 Since :math:`T` is measured on the International Temperature 

174 Scale, the value of :math:`c_2` used in colorimetry should follow 

175 that adopted in the current International Temperature Scale 

176 (ITS-90) *(Preston-Thomas, 1990; Mielenz et al., 1991)*, namely 

177 :math:`c_2=1.4388 \\times 10^{-2}\\ m \\cdot K`. 

178 n 

179 Medium index of refraction. For dry air at 15°C and 101 325 Pa, 

180 containing 0.03 percent by volume of carbon dioxide, it is 

181 approximately 1.00028 throughout the visible region although 

182 *CIE 15:2004* recommends using :math:`n=1`. 

183 

184 Returns 

185 ------- 

186 :class:`colour.SpectralDistribution` 

187 Blackbody spectral distribution with values in *watts per 

188 steradian per square metre per nanometre* (:math:`W/sr/m^2/nm`). 

189 

190 Examples 

191 -------- 

192 >>> from colour.utilities import numpy_print_options 

193 >>> with numpy_print_options(suppress=True): 

194 ... sd_blackbody(5000, shape=SpectralShape(400, 700, 20)) 

195 ... # doctest: +ELLIPSIS 

196 SpectralDistribution([[ 400. , 8742.5713329...], 

197 [ 420. , 9651.6810212...], 

198 [ 440. , 10447.3423137...], 

199 [ 460. , 11121.8597759...], 

200 [ 480. , 11673.7121534...], 

201 [ 500. , 12106.0645344...], 

202 [ 520. , 12425.4166118...], 

203 [ 540. , 12640.4550541...], 

204 [ 560. , 12761.1284859...], 

205 [ 580. , 12797.9345572...], 

206 [ 600. , 12761.3938171...], 

207 [ 620. , 12661.6795247...], 

208 [ 640. , 12508.3723863...], 

209 [ 660. , 12310.3119640...], 

210 [ 680. , 12075.5205176...], 

211 [ 700. , 11811.1793602...]], 

212 SpragueInterpolator, 

213 {}, 

214 Extrapolator, 

215 {'method': 'Constant', 'left': None, 'right': None}) 

216 """ 

217 

218 return SpectralDistribution( 

219 planck_law(shape.wavelengths * 1e-9, temperature, c1, c2, n) * 1e-9, 

220 shape.wavelengths, 

221 name=f"{temperature}K Blackbody", 

222 ) 

223 

224 

225def rayleigh_jeans_law(wavelength: ArrayLike, temperature: ArrayLike) -> NDArrayFloat: 

226 """ 

227 Approximate the spectral radiance of a blackbody as a function of 

228 wavelength at specified thermodynamic temperature :math:`T[K]` according 

229 to the *Rayleigh-Jeans* law. 

230 

231 Parameters 

232 ---------- 

233 wavelength 

234 Wavelength in meters. 

235 temperature 

236 Temperature :math:`T[K]` in kelvin degrees. 

237 

238 Returns 

239 ------- 

240 :class:`numpy.ndarray` 

241 Radiance in *watts per steradian per square metre* 

242 (:math:`W/sr/m^2`). 

243 

244 Warnings 

245 -------- 

246 The :func:`colour.colorimetry.rayleigh_jeans_law` definition behaviour 

247 with n-dimensional arrays is unusual: The ``wavelength`` and 

248 ``temperature`` parameters are first raveled using :func:`numpy.ravel`. 

249 Then, they are *broadcasted* together by transposing the 

250 ``temperature`` parameter. Finally, and for convenience, the return 

251 value is squeezed using :func:`numpy.squeeze`. 

252 

253 Notes 

254 ----- 

255 - The *Rayleigh-Jeans* law agrees with experimental results at large 

256 wavelengths (low frequencies) but strongly disagrees at short 

257 wavelengths (high frequencies). This inconsistency between 

258 observations and the predictions of classical physics is commonly 

259 known as the *ultraviolet catastrophe*. 

260 - The following implementation is expressed in terms of wavelength. 

261 - The SI unit of radiance is *watts per steradian per square metre* 

262 (:math:`W/sr/m^2`). 

263 

264 References 

265 ---------- 

266 :cite:`Wikipedia2003f` 

267 

268 Examples 

269 -------- 

270 >>> rayleigh_jeans_law(500 * 1e-9, 5500) # doctest: +ELLIPSIS 

271 728478884562351.5... 

272 >>> rayleigh_jeans_law(500 * 1e-9, [5000, 5500, 6000]) 

273 ... # doctest: +ELLIPSIS 

274 array([ 6.6225353...e+14, 7.2847888...e+14, 7.9470423...e+14]) 

275 """ 

276 

277 l = as_float_array(wavelength) # noqa: E741 

278 t = as_float_array(temperature) 

279 

280 l = np.ravel(l)[..., None] # noqa: E741 

281 t = np.ravel(t)[None, ...] 

282 

283 c = CONSTANT_LIGHT_SPEED 

284 k_B = CONSTANT_BOLTZMANN 

285 

286 B = (2 * c * k_B * t) / (l**4) 

287 

288 return as_float(np.squeeze(B)) 

289 

290 

291def sd_rayleigh_jeans( 

292 temperature: float, 

293 shape: SpectralShape = SPECTRAL_SHAPE_DEFAULT, 

294) -> SpectralDistribution: 

295 """ 

296 Generate the spectral distribution of the planckian radiator for the 

297 specified temperature :math:`T[K]` with values in *watts per steradian 

298 per square metre per nanometre* (:math:`W/sr/m^2/nm`) according to the 

299 *Rayleigh-Jeans* law. 

300 

301 Parameters 

302 ---------- 

303 temperature 

304 Temperature :math:`T[K]` in kelvins. 

305 shape 

306 Spectral shape used to create the spectral distribution of the 

307 planckian radiator. 

308 

309 Returns 

310 ------- 

311 :class:`colour.SpectralDistribution` 

312 Blackbody spectral distribution with values in *watts per steradian 

313 per square metre per nanometre* (:math:`W/sr/m^2/nm`). 

314 

315 Notes 

316 ----- 

317 - The *Rayleigh-Jeans* law agrees with experimental results at large 

318 wavelengths (low frequencies) but strongly disagrees at short 

319 wavelengths (high frequencies). This inconsistency between 

320 observations and the predictions of classical physics is commonly 

321 known as the *ultraviolet catastrophe*. 

322 

323 Examples 

324 -------- 

325 >>> from colour.utilities import numpy_print_options 

326 >>> with numpy_print_options(suppress=True): 

327 ... sd_rayleigh_jeans(5000, shape=SpectralShape(400, 700, 20)) 

328 ... # doctest: +ELLIPSIS 

329 SpectralDistribution([[ 400. , 1616829.9106941...], 

330 [ 420. , 1330169.9688456...], 

331 [ 440. , 1104316.5840408...], 

332 [ 460. , 924427.7490112...], 

333 [ 480. , 779721.2146480...], 

334 [ 500. , 662253.5314203...], 

335 [ 520. , 566097.0941823...], 

336 [ 540. , 486776.1157138...], 

337 [ 560. , 420874.0917050...], 

338 [ 580. , 365756.7299433...], 

339 [ 600. , 319373.8095198...], 

340 [ 620. , 280115.7588306...], 

341 [ 640. , 246708.6655722...], 

342 [ 660. , 218136.6091932...], 

343 [ 680. , 193583.6389284...], 

344 [ 700. , 172390.0279623...]], 

345 SpragueInterpolator, 

346 {}, 

347 Extrapolator, 

348 {'method': 'Constant', 'left': None, 'right': None}) 

349 """ 

350 

351 return SpectralDistribution( 

352 rayleigh_jeans_law(shape.wavelengths * 1e-9, temperature) * 1e-9, 

353 shape.wavelengths, 

354 name=f"{temperature}K Rayleigh-Jeans", 

355 )