Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
261 changes: 243 additions & 18 deletions Physlib/QuantumMechanics/Blackbody/PlancksLaw.lean
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
/-
Copyright (c) 2026 Samyak Rai. All rights reserved.
Copyright (c) 2026 Samyak Rai, Dwanith C. Jayanth. All rights reserved.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally we restrict the copyright to only one name.

Released under Apache 2.0 license as described in the file LICENSE.
Authors: Samyak Rai
Authors: Samyak Rai, Dwanith C. Jayanth
-/
module

public import Physlib.Thermodynamics.Temperature.Basic
public import Physlib.Relativity.SpeedOfLight
public import Physlib.QuantumMechanics.PlanckConstant
public import Mathlib.Analysis.SpecialFunctions.Exponential

/-!

Expand All @@ -22,32 +23,49 @@ its environment.

According to Planck's distribution law, the spectral energy radiance
(per unit frequency) for a black body at given temperature `T` as a function of frequency `ν`
is given by
is given by

`B(ν, T) = 2 h ν³ / c² · 1 / (e^{h ν / (k_B T)} - 1)`

where `h` is Planck's constant, `c` the speed of light, and `k_B` the Boltzmann constant.

and per unit wavelength `λ`, given by

`B(λ, T) = 2 h c² / λ⁵ · 1 / (e^{h c / (λ k_B T)} - 1)`

The two forms are related by `B(λ, T) = (c / λ²) B(ν = c/λ, T)`
(see `spectralRadianceWave_eq_freq`).

## ii. Key results

- `spectralRadiance` : The spectral radiance per unit frequency of blackbody radiation.
- `spectralRadiance_pos` : The spectral radiance is positive for positive frequency
and temperature.
- `spectralRadiance_absZero` : The spectral radiance is 0 at absolute zero.
- `spectralRadianceFreq` : Parametrized spectral radiance per unit frequency.
- `spectralRadianceWave` : Parametrized spectral radiance per unit wavelength.
- `spectralRadianceWave_eq_freq` : Correspondence between the two forms.
- `firstRadiationConstant` / `secondRadiationConstant` : Radiation constants `c₁L` and `c₂`.
- `spectralRadianceWave_eq_constants` : Planck's law in terms of radiation constants.

## iii. Table of contents

- A. The spectral radiance
- B. Parametrized spectral radiance per unit frequency
- C. Spectral radiance per unit wavelength
- D. Correspondence between the two forms
- E. First and second radiation constants

## iv. References

* https://en.wikipedia.org/wiki/Planck%27s_law
* M. Planck, "Ueber das Gesetz der Energieverteilung im Normalspectrum",
Ann. Phys. 309 (3), 553–563 (1901).

-/

@[expose] public section


Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I think it might be worth making a structure

structure BlackBody with 
    T : Temperature 

and then using this as an input to all of the things bellow.

namespace Blackbody

/-!
Expand All @@ -68,28 +86,235 @@ open Constants
Extended by zero outside the physical domain; zero is the unique continuous
extension since the Rayleigh–Jeans limit vanishes -/
noncomputable def spectralRadiance (c : SpeedOfLight) (ν : ℝ) (T : Temperature) : ℝ :=
if 0 < ν ∧ 0 < (T : ℝ) then
2 * h * ν ^ 3 / ((c : ℝ) ^ 2 * (Real.exp (h * ν / (kB * (T : ℝ))) - 1))
else 0
if 0 < ν ∧ 0 < (T : ℝ) then
2 * h * ν ^ 3 / ((c : ℝ) ^ 2 * (Real.exp (h * ν / (kB * (T : ℝ))) - 1))
else 0

/-- The spectral radiance of blackbody radiation is positive for positive frequency
and positive temperature. -/
lemma spectralRadiance_pos (c : SpeedOfLight) (ν : ℝ) (T : Temperature)
(ν_pos : 0 < ν) (T_pos : 0 < T.val) : 0 < spectralRadiance c ν T := by
have if_cond : 0 < ν ∧ 0 < (T : ℝ) := ⟨ν_pos, by exact_mod_cast T_pos⟩
rw [spectralRadiance, ite_eq_left if_cond]
refine div_pos ?numerator ?denominator
· exact mul_pos (mul_pos (by norm_num) h_pos) (pow_pos ν_pos 3)
· have expo_term : 0 < h * ν / (kB * (T : ℝ)) :=
div_pos (mul_pos h_pos ν_pos) (mul_pos kB_pos (by exact_mod_cast T_pos))
exact mul_pos (pow_pos c.val_pos 2)
(sub_pos.mpr (by simpa using Real.exp_strictMono expo_term))
have if_cond : 0 < ν ∧ 0 < (T : ℝ) := ⟨ν_pos, by exact_mod_cast T_pos⟩
rw [spectralRadiance, ite_eq_left if_cond]
refine div_pos ?numerator ?denominator
· exact mul_pos (mul_pos (by norm_num) h_pos) (pow_pos ν_pos 3)
· have expo_term : 0 < h * ν / (kB * (T : ℝ)) :=
div_pos (mul_pos h_pos ν_pos) (mul_pos kB_pos (by exact_mod_cast T_pos))
exact mul_pos (pow_pos c.val_pos 2)
(sub_pos.mpr (by simpa using Real.exp_strictMono expo_term))

/-- Explicit promise for Spectral Radiance vanishing at absolute zero Temperature. -/
lemma spectralRadiance_absZero (c : SpeedOfLight) (ν : ℝ) :
spectralRadiance c ν ⟨0⟩ = 0 := by
rw [spectralRadiance, ite_eq_right]
rintro ⟨ν_pos, T_zero⟩
exact lt_irrefl _ T_zero
rw [spectralRadiance, ite_eq_right]
rintro ⟨ν_pos, T_zero⟩
exact lt_irrefl _ T_zero

/-!
## B. Parametrized spectral radiance per unit frequency
-/

/-- Spectral radiance per unit frequency of blackbody radiation at frequency `ν`
and temperature `T`:

`B(ν, T) = 2 h ν³ / c² · 1 / (e ^ (h ν / (kB T)) - 1)`,

extended by zero outside the physical domain. -/
noncomputable def spectralRadianceFreq (h c kB ν T : ℝ) : ℝ :=

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't remake this definition since it is essentially equivalent to spectralRadiance.

if 0 < ν ∧ 0 < T then
2 * h * ν ^ 3 / (c ^ 2 * (Real.exp (h * ν / (kB * T)) - 1))
else 0

/-- Correspondence between `spectralRadiance` and `spectralRadianceFreq`. -/
lemma spectralRadiance_eq_spectralRadianceFreq (c : SpeedOfLight) (ν : ℝ) (T : Temperature) :
spectralRadiance c ν T = spectralRadianceFreq h (c : ℝ) kB ν (T : ℝ) := rfl

/-- The spectral radiance per unit frequency is positive for positive frequency
and positive temperature. -/
lemma spectralRadianceFreq_pos (h c kB ν T : ℝ) (hh : 0 < h) (hc : 0 < c)
(hk : 0 < kB) (hν : 0 < ν) (hT : 0 < T) :
0 < spectralRadianceFreq h c kB ν T := by
unfold spectralRadianceFreq
rw [if_pos ⟨hν, hT⟩]
apply div_pos
· exact mul_pos (mul_pos zero_lt_two hh) (pow_pos hν 3)
· apply mul_pos (pow_pos hc 2)
have harg : 0 < h * ν / (kB * T) :=
div_pos (mul_pos hh hν) (mul_pos hk hT)
have h1e : 1 < Real.exp (h * ν / (kB * T)) := Real.one_lt_exp_iff.mpr harg
linarith

/-- The spectral radiance per unit frequency vanishes at absolute zero. -/
@[simp]
lemma spectralRadianceFreq_absZero (h c kB ν : ℝ) :
spectralRadianceFreq h c kB ν 0 = 0 := by
unfold spectralRadianceFreq
simp

/-- The spectral radiance per unit frequency vanishes at zero frequency. -/
@[simp]
lemma spectralRadianceFreq_zeroFreq (h c kB T : ℝ) :
spectralRadianceFreq h c kB 0 T = 0 := by
unfold spectralRadianceFreq
simp

/-!
## C. Spectral radiance per unit wavelength
-/

/-- Spectral radiance per unit wavelength of blackbody radiation at wavelength
`λ` and temperature `T`:

`B(λ, T) = 2 h c² / λ⁵ · 1 / (e ^ (h c / (λ kB T)) - 1)`,

extended by zero outside the physical domain. -/
noncomputable def spectralRadianceWave (h c kB lam T : ℝ) : ℝ :=
if 0 < lam ∧ 0 < T then
2 * h * c ^ 2 / lam ^ 5 / (Real.exp (h * c / (lam * kB * T)) - 1)
else 0

/-- The spectral radiance per unit wavelength is positive for positive wavelength
and positive temperature. -/
lemma spectralRadianceWave_pos (h c kB lam T : ℝ) (hh : 0 < h) (hc : 0 < c)
(hk : 0 < kB) (hlam : 0 < lam) (hT : 0 < T) :
0 < spectralRadianceWave h c kB lam T := by
unfold spectralRadianceWave
rw [if_pos ⟨hlam, hT⟩]
have harg : 0 < h * c / (lam * kB * T) :=
div_pos (mul_pos hh hc) (mul_pos (mul_pos hlam hk) hT)
have h1e : 1 < Real.exp (h * c / (lam * kB * T)) := Real.one_lt_exp_iff.mpr harg
have hE : 0 < Real.exp (h * c / (lam * kB * T)) - 1 := sub_pos.mpr h1e
have hnum : 0 < 2 * h * c ^ 2 / lam ^ 5 :=
div_pos (mul_pos (mul_pos zero_lt_two hh) (pow_pos hc 2)) (pow_pos hlam 5)
exact div_pos hnum hE

/-- The spectral radiance per unit wavelength vanishes at absolute zero. -/
@[simp]
lemma spectralRadianceWave_absZero (h c kB lam : ℝ) :
spectralRadianceWave h c kB lam 0 = 0 := by
unfold spectralRadianceWave
simp

/-- The spectral radiance per unit wavelength vanishes at zero wavelength. -/
@[simp]
lemma spectralRadianceWave_zeroWave (h c kB T : ℝ) :
spectralRadianceWave h c kB 0 T = 0 := by
unfold spectralRadianceWave
simp

/-!
## D. Correspondence between the two forms

Since `B(λ, T) dλ = -B(ν(λ), T) dν` with `ν = c / λ` and `|dν / dλ| = c / λ²`,
the wavelength form equals `c / λ²` times the frequency form evaluated at
`ν = c / λ`.
-/

/-- Correspondence between the wavelength and frequency forms of Planck's law:
`B(λ, T) = (c / λ²) B(ν = c / λ, T)`. -/
lemma spectralRadianceWave_eq_freq (h c kB lam T : ℝ) (hh : 0 < h) (hc : 0 < c)
(hk : 0 < kB) (hlam : 0 < lam) (hT : 0 < T) :
spectralRadianceWave h c kB lam T
= (c / lam ^ 2) * spectralRadianceFreq h c kB (c / lam) T := by
have h1 : 0 < lam ∧ 0 < T := ⟨hlam, hT⟩
have h2 : 0 < c / lam ∧ 0 < T := ⟨div_pos hc hlam, hT⟩
unfold spectralRadianceWave spectralRadianceFreq
rw [if_pos h1, if_pos h2]
have hlam' : lam ≠ 0 := ne_of_gt hlam
have hc' : c ≠ 0 := ne_of_gt hc
have hkT : kB * T ≠ 0 := mul_ne_zero (ne_of_gt hk) (ne_of_gt hT)
have hE : Real.exp (h * c / (lam * kB * T)) - 1 ≠ 0 := by
have harg : 0 < h * c / (lam * kB * T) :=
div_pos (mul_pos hh hc) (mul_pos (mul_pos hlam hk) hT)
have h1e : 1 < Real.exp (h * c / (lam * kB * T)) := Real.one_lt_exp_iff.mpr harg
exact ne_of_gt (sub_pos.mpr h1e)
have hexp : h * (c / lam) / (kB * T) = h * c / (lam * kB * T) := by
field_simp
rw [hexp]
field_simp

/-!
## E. First and second radiation constants

The wavelength variant uses only the combinations `2 h c²` and `h c / kB`,
called the first and second radiation constants.
-/

/-- The first radiation constant `c₁L = 2 h c²`. -/
noncomputable def firstRadiationConstant (h c : ℝ) : ℝ := 2 * h * c ^ 2

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

c : SpeedOfLight. We also use the value of kB and h given via the imports. In theory we could define a set value of c aswell.


/-- The second radiation constant `c₂ = h c / kB`. -/
noncomputable def secondRadiationConstant (h c kB : ℝ) : ℝ := h * c / kB

/-- The first radiation constant is positive. -/
lemma firstRadiationConstant_pos (h c : ℝ) (hh : 0 < h) (hc : 0 < c) :
0 < firstRadiationConstant h c := by
unfold firstRadiationConstant
exact mul_pos (mul_pos zero_lt_two hh) (pow_pos hc 2)

/-- The second radiation constant is positive. -/
lemma secondRadiationConstant_pos (h c kB : ℝ) (hh : 0 < h) (hc : 0 < c)
(hk : 0 < kB) :
0 < secondRadiationConstant h c kB := by
unfold secondRadiationConstant
exact div_pos (mul_pos hh hc) hk

/-- Planck's law per unit wavelength in terms of the radiation constants:
`B(λ, T) = (c₁L / λ⁵) / (e ^ (c₂ / (λ T)) - 1)`. -/
lemma spectralRadianceWave_eq_constants (h c kB lam T : ℝ) (hh : 0 < h)
(hc : 0 < c) (hk : 0 < kB) (hlam : 0 < lam) (hT : 0 < T) :
spectralRadianceWave h c kB lam T
= firstRadiationConstant h c / lam ^ 5
/ (Real.exp (secondRadiationConstant h c kB / (lam * T)) - 1) := by
have h1 : 0 < lam ∧ 0 < T := ⟨hlam, hT⟩
unfold spectralRadianceWave firstRadiationConstant secondRadiationConstant
rw [if_pos h1]
have hlam' : lam ≠ 0 := ne_of_gt hlam
have hk' : kB ≠ 0 := ne_of_gt hk
have hT' : T ≠ 0 := ne_of_gt hT
have hE : Real.exp (h * c / (lam * kB * T)) - 1 ≠ 0 := by
have harg : 0 < h * c / (lam * kB * T) :=
div_pos (mul_pos hh hc) (mul_pos (mul_pos hlam hk) hT)
have h1e : 1 < Real.exp (h * c / (lam * kB * T)) := Real.one_lt_exp_iff.mpr harg
exact ne_of_gt (sub_pos.mpr h1e)
have hexp : h * c / kB / (lam * T) = h * c / (lam * kB * T) := by
field_simp
rw [hexp]

/-- The spectral radiance per unit frequency is non-negative on the physical domain. -/
lemma spectralRadianceFreq_nonneg (h c kB ν T : ℝ) (hh : 0 < h) (hc : 0 < c)
(hk : 0 < kB) (hν : 0 < ν) (hT : 0 < T) :
0 ≤ spectralRadianceFreq h c kB ν T :=
le_of_lt (spectralRadianceFreq_pos h c kB ν T hh hc hk hν hT)

/-- The spectral radiance per unit wavelength is non-negative on the physical domain. -/
lemma spectralRadianceWave_nonneg (h c kB lam T : ℝ) (hh : 0 < h) (hc : 0 < c)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Results like this should appear next to the corresponding definition.

(hk : 0 < kB) (hlam : 0 < lam) (hT : 0 < T) :
0 ≤ spectralRadianceWave h c kB lam T :=
le_of_lt (spectralRadianceWave_pos h c kB lam T hh hc hk hlam hT)

/-- The spectral radiance per unit frequency vanishes when frequency is
non-positive (the if-guard `0 < ν ∧ 0 < T` fails). -/
lemma spectralRadianceFreq_eq_zero_of_nonpos_freq (h c kB ν T : ℝ) (hν : ν ≤ 0) :
spectralRadianceFreq h c kB ν T = 0 := by
unfold spectralRadianceFreq
rw [if_neg (not_and_of_not_left _ (not_lt.mpr hν))]

/-- The spectral radiance per unit wavelength vanishes when wavelength is
non-positive (the if-guard `0 < lam ∧ 0 < T` fails). -/
lemma spectralRadianceWave_eq_zero_of_nonpos_wave (h c kB lam T : ℝ) (hlam : lam ≤ 0) :
spectralRadianceWave h c kB lam T = 0 := by
unfold spectralRadianceWave
rw [if_neg (not_and_of_not_left _ (not_lt.mpr hlam))]

/-- The first radiation constant equals `2 * h * c ^ 2`. -/
@[simp]
lemma firstRadiationConstant_eq (h c : ℝ) :

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I probably would not attribute this and the lemma below with @[simp].

firstRadiationConstant h c = 2 * h * c ^ 2 := rfl

/-- The second radiation constant equals `h * c / kB`. -/
@[simp]
lemma secondRadiationConstant_eq (h c kB : ℝ) :
secondRadiationConstant h c kB = h * c / kB := rfl

end Blackbody
Loading