Coverage for colour/colorimetry/blackbody.py: 100%
40 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-15 19:01 +1300
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-15 19:01 +1300
1"""
2Blackbody - Planckian Radiator
3==============================
5Define objects to compute the spectral radiance of a planckian radiator
6and its spectral distribution.
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"""
17from __future__ import annotations
19import typing
21import numpy as np
23from colour.colorimetry import (
24 SPECTRAL_SHAPE_DEFAULT,
25 SpectralDistribution,
26 SpectralShape,
27)
28from colour.constants import CONSTANT_BOLTZMANN, CONSTANT_LIGHT_SPEED
30if typing.TYPE_CHECKING:
31 from colour.hints import ArrayLike, NDArrayFloat
33from colour.utilities import as_float, as_float_array
34from colour.utilities.common import attest
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"
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]
54# 2 * math.pi * CONSTANT_PLANCK * CONSTANT_LIGHT_SPEED ** 2
55CONSTANT_C1: float = 3.741771e-16
57# CONSTANT_PLANCK * CONSTANT_LIGHT_SPEED / CONSTANT_BOLTZMANN
58CONSTANT_C2: float = 1.4388e-2
60CONSTANT_N: float = 1
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`.
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`.
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}}`).
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`.
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}}`).
119 References
120 ----------
121 :cite:`CIETC1-482004i`
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 """
131 l = as_float_array(wavelength) # noqa: E741
132 t = as_float_array(temperature)
134 attest(np.all(l > 0), "Wavelengths must be positive real numbers!")
136 l = np.ravel(l)[..., None] # noqa: E741
137 t = np.ravel(t)[None, ...]
139 d = 1 / np.expm1(c2 / (n * l * t))
140 p = ((c1 * n**-2 * l**-5) / np.pi) * d
142 return as_float(np.squeeze(p))
145blackbody_spectral_radiance = planck_law
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`).
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`.
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`).
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 """
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 )
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.
231 Parameters
232 ----------
233 wavelength
234 Wavelength in meters.
235 temperature
236 Temperature :math:`T[K]` in kelvin degrees.
238 Returns
239 -------
240 :class:`numpy.ndarray`
241 Radiance in *watts per steradian per square metre*
242 (:math:`W/sr/m^2`).
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`.
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`).
264 References
265 ----------
266 :cite:`Wikipedia2003f`
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 """
277 l = as_float_array(wavelength) # noqa: E741
278 t = as_float_array(temperature)
280 l = np.ravel(l)[..., None] # noqa: E741
281 t = np.ravel(t)[None, ...]
283 c = CONSTANT_LIGHT_SPEED
284 k_B = CONSTANT_BOLTZMANN
286 B = (2 * c * k_B * t) / (l**4)
288 return as_float(np.squeeze(B))
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.
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.
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`).
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*.
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 """
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 )