"""Immutable vacuum-wavelength registry used by global line recipes."""
from __future__ import annotations
from dataclasses import dataclass
import re
from typing import Dict, Optional, Tuple
def _normalize(value: str) -> str:
text = str(value).strip().lower()
replacements = {
"α": "alpha",
"β": "beta",
"γ": "gamma",
"δ": "delta",
"λ": "",
"å": "a",
}
for source, target in replacements.items():
text = text.replace(source, target)
return re.sub(r"[^a-z0-9]+", "", text)
@dataclass(frozen=True)
class LineDefinition:
id: str
aliases: Tuple[str, ...]
label: str
vacuum_wavelength: float
transition_type: str
default_roles: Tuple[str, ...]
default_profile: str = "gaussian"
blend_members: Tuple[str, ...] = ()
notes: Tuple[str, ...] = ()
reference: Optional[str] = None
_NIST = "NIST Atomic Spectra Database (vacuum wavelength)"
_NIST_APPROX = "NIST Atomic Spectra Database; rounded vacuum wavelength"
def _line(
id: str,
wavelength: float,
label: str,
transition_type: str,
*,
aliases=(),
roles=("narrow",),
blend_members=(),
notes=(),
reference=_NIST,
) -> LineDefinition:
return LineDefinition(
id=id,
aliases=tuple(aliases),
label=label,
vacuum_wavelength=float(wavelength),
transition_type=transition_type,
default_roles=tuple(roles),
blend_members=tuple(blend_members),
notes=tuple(notes),
reference=reference,
)
_DEFINITIONS = (
_line("lya_1216", 1215.67, "Lyα", "recombination", aliases=("lya", "lyalpha"), roles=("broad", "narrow")),
_line("nv_1239", 1238.82, "N V 1239", "permitted", aliases=("nv1238",)),
_line("nv_1243", 1242.80, "N V 1243", "permitted", aliases=("nv1242",)),
_line(
"nv_blend",
1240.14,
"N V",
"blend",
aliases=("nv", "nv1240"),
roles=("broad",),
blend_members=("nv_1239", "nv_1243"),
),
_line("siiv_1394", 1393.76, "Si IV 1394", "permitted", aliases=("siiv1393",)),
_line("oiv_1401", 1401.16, "O IV] 1401", "forbidden", aliases=("oiv1401", "oiv]1401")),
_line("siiv_1403", 1402.77, "Si IV 1403", "permitted", aliases=("siiv1402",)),
_line("civ_1548", 1548.20, "C IV 1548", "permitted", aliases=("civ1548",), roles=("broad", "narrow")),
_line("civ_1551", 1550.77, "C IV 1551", "permitted", aliases=("civ1550",), roles=("broad", "narrow")),
_line("civ_blend", 1549.06, "C IV", "blend", aliases=("civ", "civ1549"), roles=("broad",), blend_members=("civ_1548", "civ_1551")),
_line("heii_1640", 1640.42, "He II", "recombination", aliases=("heii1640",), roles=("broad", "narrow")),
_line("ciii_1909", 1908.73, "C III]", "forbidden", aliases=("ciii1909", "ciii]1909")),
_line("mgii_2796", 2796.35, "Mg II 2796", "permitted", aliases=("mgii2796",), roles=("broad",)),
_line("mgii_2804", 2803.53, "Mg II 2804", "permitted", aliases=("mgii2803",), roles=("broad",)),
_line("mgii_blend", 2798.75, "Mg II", "blend", aliases=("mgii", "mgii2798"), roles=("broad",), blend_members=("mgii_2796", "mgii_2804")),
_line("nev_3427", 3426.84, "[Ne V] 3427", "forbidden", aliases=("nev3426", "nev3427")),
_line("oii_3727", 3727.09, "[O II] 3727", "forbidden", aliases=("oii3726", "oii3727")),
_line("oii_3730", 3729.88, "[O II] 3730", "forbidden", aliases=("oii3729", "oii3730")),
_line("oii_blend", 3728.48, "[O II]", "blend", aliases=("oii", "oii3728"), blend_members=("oii_3727", "oii_3730")),
_line("neiii_3870", 3869.86, "[Ne III] 3870", "forbidden", aliases=("neiii3869", "neiii3870")),
_line("hdelta", 4102.93, "Hδ", "recombination", aliases=("hd", "hdelta4103"), roles=("broad", "narrow")),
_line("hgamma", 4341.68, "Hγ", "recombination", aliases=("hg", "hgamma4342"), roles=("broad", "narrow")),
_line("heii_4687", 4687.02, "He II", "recombination", aliases=("heii4686", "heii4687"), roles=("broad", "narrow")),
_line("hbeta", 4862.68, "Hβ", "recombination", aliases=("hb", "hbeta4863", "hbeta4861"), roles=("broad", "narrow")),
_line("oiii_4960", 4960.30, "[O III] 4960", "forbidden", aliases=("oiii4959", "oiii4960")),
_line("oiii_5008", 5008.24, "[O III] 5008", "forbidden", aliases=("oiii5007", "oiii5008", "OIII5007")),
_line("hei_5877", 5877.25, "He I", "permitted", aliases=("hei5876", "hei5877"), roles=("broad", "narrow")),
_line("nii_6550", 6549.85, "[N II] 6550", "forbidden", aliases=("nii6549", "nii6550")),
_line("halpha", 6564.61, "Hα", "recombination", aliases=("ha", "halpha6565", "halpha6563"), roles=("broad", "narrow")),
_line("nii_6585", 6585.28, "[N II] 6585", "forbidden", aliases=("nii6583", "nii6585")),
_line("sii_6718", 6718.29, "[S II] 6718", "forbidden", aliases=("sii6716", "sii6718")),
_line("sii_6733", 6732.67, "[S II] 6733", "forbidden", aliases=("sii6731", "sii6733")),
_line("oi_8449", 8448.68, "O I", "permitted", aliases=("oi8446", "oi8449"), roles=("broad", "narrow")),
_line("siii_9071", 9071.1, "[S III] 9071", "forbidden", aliases=("siii9069", "siii9071"), reference=_NIST_APPROX),
_line("siii_9533", 9533.2, "[S III] 9533", "forbidden", aliases=("siii9531", "siii9533"), reference=_NIST_APPROX),
_line("padelta", 10052.1, "Paδ", "recombination", aliases=("pad", "padelta10052"), roles=("broad", "narrow"), reference=_NIST_APPROX),
_line("hei_10833", 10833.3, "He I", "permitted", aliases=("hei10830", "hei10833"), roles=("broad", "narrow"), reference=_NIST_APPROX),
_line("pagamma", 10941.1, "Paγ", "recombination", aliases=("pag", "pagamma10941"), roles=("broad", "narrow"), reference=_NIST_APPROX),
_line("oi_11290", 11290.0, "O I", "permitted", aliases=("oi11287", "oi11290"), roles=("broad", "narrow"), reference=_NIST_APPROX),
_line("pabeta", 12821.6, "Paβ", "recombination", aliases=("pab", "pabeta12822"), roles=("broad", "narrow"), reference=_NIST_APPROX),
)
_BY_ID: Dict[str, LineDefinition] = {item.id: item for item in _DEFINITIONS}
_ALIASES: Dict[str, str] = {}
for _definition in _DEFINITIONS:
for _alias in (_definition.id, _definition.label, *_definition.aliases):
_ALIASES[_normalize(_alias)] = _definition.id
[docs]
def list() -> Tuple[LineDefinition, ...]:
"""Return all registered line definitions in wavelength order."""
return tuple(sorted(_DEFINITIONS, key=lambda item: item.vacuum_wavelength))
[docs]
def resolve(value: str) -> str:
"""Resolve a canonical line ID from a normalized or historical alias."""
key = _normalize(value)
if key not in _ALIASES:
raise ValueError(f"unknown_line_id: {value!r}")
return _ALIASES[key]
[docs]
def get(value: str) -> LineDefinition:
"""Return one immutable line definition."""
return _BY_ID[resolve(value)]