"""Curated physical constants represented as uncertainty-aware quantities.
The measured values in this module are pinned to the 2022 CODATA recommended
values published in NIST Reference on Constants, Units, and Uncertainty,
version 9.0 (May 2024). The exact constants use the defining values of the
International System of Units (SI), 9th edition.
Only the fine-structure constant is introduced as an independent uncertain
input. Vacuum permeability, vacuum permittivity, and vacuum impedance are
derived from that shared input and the exact defining constants. Consequently,
SCUQ retains their physical dependence during uncertainty propagation.
References
----------
NIST
https://physics.nist.gov/constants
BIPM
https://www.bipm.org/en/measurement-units/si-defining-constants
"""
from __future__ import annotations
from .quantities import Quantity
from .si import (
COULOMB,
FARAD,
HENRY,
JOULE,
KELVIN,
METER,
MOLE,
OHM,
SECOND,
)
from .ucomponents import UncertainInput
from .units import ONE
CODATA_RELEASE = "2022 CODATA recommended values"
"""Pinned release used for measured physical constants."""
CODATA_SOURCE = "https://physics.nist.gov/constants"
"""Official NIST source for the pinned CODATA release."""
SI_DEFINITION_SOURCE = "https://www.bipm.org/en/measurement-units/si-defining-constants"
"""Official BIPM source for exact SI defining constants."""
[docs]
class PhysicalConstant(Quantity):
"""A :class:`Quantity` protected against in-place mutation.
Physical constants participate in ordinary SCUQ arithmetic exactly like
other quantities. Arithmetic returns ordinary :class:`Quantity` objects,
while augmented assignment and direct changes to the stored unit or value
raise :class:`TypeError`. This protects the module-level objects from
accidental process-wide modification.
Parameters
----------
unit : scuq.units.Unit
Canonical SI unit of the constant.
value : numbers.Number or scuq.ucomponents.UncertainComponent
Exact numeric value or uncertainty expression stored by the constant.
"""
[docs]
def __init__(self, unit, value):
object.__setattr__(self, "_physical_constant_locked", False)
super().__init__(unit, value)
object.__setattr__(self, "_physical_constant_locked", True)
def __setattr__(self, name, value):
if getattr(self, "_physical_constant_locked", False) and name in {
"_unit",
"_value",
}:
raise TypeError("physical constants cannot be modified")
object.__setattr__(self, name, value)
def _reject_inplace(self, _other):
raise TypeError("physical constants cannot be modified in place")
__iadd__ = _reject_inplace
__isub__ = _reject_inplace
__imul__ = _reject_inplace
__itruediv__ = _reject_inplace
__idiv__ = _reject_inplace
__ipow__ = _reject_inplace
[docs]
def __setstate__(self, state):
"""Restore a pickled constant while preserving its protection."""
unit, value = state
object.__setattr__(self, "_physical_constant_locked", False)
object.__setattr__(self, "_unit", unit)
object.__setattr__(self, "_value", value)
object.__setattr__(self, "_physical_constant_locked", True)
def _constant_from_quantity(unit, quantity):
"""Return a protected constant in ``unit`` from a compatible quantity."""
source_unit = quantity.get_default_unit()
return PhysicalConstant(unit, quantity.get_value(source_unit))
SPEED_OF_LIGHT = PhysicalConstant(METER / SECOND, 299_792_458.0)
"""Speed of light in vacuum, exactly 299 792 458 m/s in the SI."""
c = SPEED_OF_LIGHT
PLANCK_CONSTANT = PhysicalConstant(JOULE * SECOND, 6.626_070_15e-34)
"""Planck constant, exactly 6.626 070 15e-34 J s in the SI."""
h = PLANCK_CONSTANT
ELEMENTARY_CHARGE = PhysicalConstant(COULOMB, 1.602_176_634e-19)
"""Elementary charge, exactly 1.602 176 634e-19 C in the SI."""
e = ELEMENTARY_CHARGE
BOLTZMANN_CONSTANT = PhysicalConstant(JOULE / KELVIN, 1.380_649e-23)
"""Boltzmann constant, exactly 1.380 649e-23 J/K in the SI."""
k_B = BOLTZMANN_CONSTANT
AVOGADRO_CONSTANT = PhysicalConstant(ONE / MOLE, 6.022_140_76e23)
"""Avogadro constant, exactly 6.022 140 76e23 1/mol in the SI."""
N_A = AVOGADRO_CONSTANT
_FINE_STRUCTURE_INPUT = UncertainInput(7.297_352_564_3e-3, 1.1e-12)
FINE_STRUCTURE_CONSTANT = PhysicalConstant(ONE, _FINE_STRUCTURE_INPUT)
"""Fine-structure constant with its CODATA 2022 standard uncertainty."""
alpha = FINE_STRUCTURE_CONSTANT
_mu_0_expression = (
2
* FINE_STRUCTURE_CONSTANT
* PLANCK_CONSTANT
/ (ELEMENTARY_CHARGE**2 * SPEED_OF_LIGHT)
)
VACUUM_MAGNETIC_PERMEABILITY = _constant_from_quantity(HENRY / METER, _mu_0_expression)
"""Vacuum magnetic permeability derived from ``2 * alpha * h / (e**2 * c)``."""
mu_0 = VACUUM_MAGNETIC_PERMEABILITY
_epsilon_0_expression = 1 / (VACUUM_MAGNETIC_PERMEABILITY * SPEED_OF_LIGHT**2)
VACUUM_ELECTRIC_PERMITTIVITY = _constant_from_quantity(
FARAD / METER, _epsilon_0_expression
)
"""Vacuum electric permittivity derived from ``1 / (mu_0 * c**2)``."""
epsilon_0 = VACUUM_ELECTRIC_PERMITTIVITY
_Z_0_expression = VACUUM_MAGNETIC_PERMEABILITY * SPEED_OF_LIGHT
VACUUM_IMPEDANCE = _constant_from_quantity(OHM, _Z_0_expression)
"""Vacuum impedance derived from ``mu_0 * c``."""
Z_0 = VACUUM_IMPEDANCE
__all__ = [
"CODATA_RELEASE",
"CODATA_SOURCE",
"SI_DEFINITION_SOURCE",
"PhysicalConstant",
"SPEED_OF_LIGHT",
"PLANCK_CONSTANT",
"ELEMENTARY_CHARGE",
"BOLTZMANN_CONSTANT",
"AVOGADRO_CONSTANT",
"FINE_STRUCTURE_CONSTANT",
"VACUUM_MAGNETIC_PERMEABILITY",
"VACUUM_ELECTRIC_PERMITTIVITY",
"VACUUM_IMPEDANCE",
"c",
"h",
"e",
"k_B",
"N_A",
"alpha",
"mu_0",
"epsilon_0",
"Z_0",
]