solprop.physdata.sellmeier#

Refractive-index models for gases, with analytic frequency derivatives.

A gas is described by its per-particle susceptibility gamma(omega), in m^3, so that the linear susceptibility at a number density rho is simply

\[\begin{split}\\chi^{(1)}(\\omega) = \\gamma(\\omega)\\,\\rho\end{split}\]

and the refractive index is n = sqrt(1 + chi). Keeping the density outside the model is what lets n - 1 be formed directly rather than as (1 + x) - 1 (docs/dev/conventions.md section 5), and it makes a pressure gradient a scalar multiplier on one fixed spectral profile.

Every built-in gas’s law is optmat’s polarizability (OptmatSusceptibility, SEP 3), with its exact frequency derivative and its qualified wavelength intervals; gamma() returns it and the accessors below evaluate it at a state, with optmat’s equation of state. A model variant is chosen with a MaterialSelection, never a separate pseudo-gas.

The formula types (PoleSumSusceptibility, InversePoleIndex, WavenumberPoleIndex) are for a caller’s own published expression: each is a rational function of 1 / lambda^2, so its derivative with respect to angular frequency is available in closed form. That is a requirement, not a convenience: the co-moving frame needs beta_1 to about 1e-10 and a finite-difference derivative of a Sellmeier expression has a roughness floor near 5e-8 (see docs/dev/decisions.md). Each carries the wavelength range over which its source is valid, the state at which it reports its refractivity (ReferenceState) and a citation. Evaluating outside that range emits MaterialRangeWarning once per call.

Functions

chi1(gas, omega, *, pressure[, temperature])

Return the linear susceptibility of a gas.

dchi1_domega(gas, omega, *, pressure[, ...])

Return the analytic frequency derivative of the linear susceptibility.

drefractive_index_domega(gas, omega, *, pressure)

Return the analytic frequency derivative of the refractive index.

gamma(gas, *[, model])

Return the per-particle susceptibility model for a gas: optmat's.

refractive_index(gas, omega, *, pressure[, ...])

Return the refractive index of a gas.

refractive_index_minus_one(gas, omega, *, ...)

Return n - 1 for a gas, without cancellation.

Classes

GasSusceptibility(*, gas, source, ...)

A per-particle linear susceptibility model for one gas from one source.

InversePoleIndex(*, gas, source, ...)

A refractivity written as a sum of poles in 1 / lambda^2.

OptmatSusceptibility(*, gas, source, ...)

Take a gas's per-particle susceptibility from optmat's polarizability.

PoleSumSusceptibility(*, gas, source, ...)

A susceptibility written directly as a sum of poles in lambda^2.

ReferenceState(pressure_bar, temperature)

The thermodynamic state at which a measured refractivity was reported.

RefractivitySusceptibility(*, gas, source, ...)

A model whose source publishes the refractivity n - 1 rather than chi.

WavenumberPoleIndex(*, gas, source, ...)

A refractivity written as one pole in the squared wavenumber.

class solprop.physdata.sellmeier.GasSusceptibility(*, gas, source, reference_state, wavelength_range, citation)[source]#

Bases: ABC

A per-particle linear susceptibility model for one gas from one source.

Subclasses supply the published expression and its derivative with respect to u = 1 / lambda^2 (lambda in microns) at the source’s reference state; this base class turns that into the per-particle quantity gamma(omega) in m^3 and its analytic derivative, and checks the validity range.

Attributes:
gasGas

The species the coefficients describe.

sourcestr

The name used to select this model in gamma().

reference_stateReferenceState

The state at which the source reports its refractivity.

wavelength_rangetuple of float

Shortest and longest wavelength in m over which the source is valid.

citationstr

Where the coefficients come from, including the validity range and any deviation from the source.

property absorbing: bool[source]#

Return whether the model has an imaginary part anywhere.

Returns:
bool

False: a refractivity fit is real by construction.

citation: str#
dsusceptibility_domega(omega)[source]#

Return the analytic frequency derivative of the per-particle susceptibility.

Parameters:
omegaarray_like

Angular frequency in rad/s, >= 0.

Returns:
ndarray

dgamma/domega in m^3 s/rad, exact to rounding rather than to a finite-difference floor.

Warns:
MaterialRangeWarning

If any wavelength lies outside the validity range of the source.

Examples

The derivative is positive: these gases are normally dispersive.

>>> from solprop.physdata import Gas, gamma
>>> float(gamma(Gas.Ar).dsusceptibility_domega(2.356e15)) > 0.0
True
gas: Gas#
property reference_density: float[source]#

Return the number density at the reference state of the source.

Returns:
float

Number density in m^-3.

reference_state: ReferenceState#
source: str#
susceptibility(omega)[source]#

Return the per-particle susceptibility.

Parameters:
omegaarray_like

Angular frequency in rad/s, >= 0. Zero gives the static limit.

Returns:
ndarray

gamma(omega) in m^3, such that chi1 = gamma * rho. 0-d for scalar input, otherwise the shape of omega.

Warns:
MaterialRangeWarning

If any wavelength lies outside the validity range of the source.

Examples

>>> from solprop.physdata import Gas, gamma
>>> model = gamma(Gas.Ar)
>>> f"{float(model.susceptibility(2.356e15)):.6e}"
'2.079791e-29'
wavelength_range: tuple[float, float]#
class solprop.physdata.sellmeier.InversePoleIndex(*, gas, source, reference_state, wavelength_range, citation, offset, b, c)[source]#

Bases: RefractivitySusceptibility

A refractivity written as a sum of poles in 1 / lambda^2.

n - 1 = offset + sum_i b_i / (c_i - 1 / lambda^2) with lambda in microns, the form used by Peck and Huang for hydrogen, by Zhang for oxygen (with a non-zero offset) and by Bideau-Mehu for the noble gases in the vacuum ultraviolet.

Attributes:
offsetfloat

Constant term of the refractivity at the reference state, dimensionless.

btuple of float

Pole strengths in um^-2.

ctuple of float

Pole positions in um^-2, one per entry of b.

b: tuple[float, ...]#
c: tuple[float, ...]#
offset: float#
class solprop.physdata.sellmeier.OptmatSusceptibility(*, gas, source, reference_state, wavelength_range, citation, model)[source]#

Bases: GasSusceptibility

Take a gas’s per-particle susceptibility from optmat’s polarizability.

The susceptibility per particle is \(\gamma = \alpha / \varepsilon_0\), with optmat’s SI refractive polarizability \(\alpha(\omega)\) (C m^2/V, magnetic contribution included), so that chi = gamma * rho is linear density scaling, the dilute limit of optmat’s own Lorentz–Lorenz law (dilute_scaling_error() measures the difference). The derivative is optmat’s exact \(\partial_\omega\alpha\), taken at fixed state.

The model holds over its qualified intervals of vacuum wavelength. A band outside them is evaluated all the same and warns once per evaluation context (MaterialRangeWarning), naming the parts outside and the reasons of any gaps. A transparent model returns a real susceptibility; an absorbing one a complex one with a non-negative imaginary part. Build one with resolve(). The adapter keeps its last few evaluations by frequency array, as OptmatGlass does, and two adapters of one model compare equal by its identity digest (SEP 3, Q26).

Attributes:
modelOptmatModel

The optmat dispersion model: a registry model, an AirModel, or a model from with_parameters.

Examples

>>> from solprop.physdata.sellmeier import OptmatSusceptibility
>>> argon = OptmatSusceptibility.resolve("Ar")
>>> argon.source
'fit/Ar'
>>> f"{float(argon.susceptibility(2.356e15)):.4e}"
'2.0798e-29'
property absorbing: bool[source]#

Return whether the optmat model carries an imaginary polarizability.

Returns:
bool

The model’s own absorbing, or any constituent’s for an air model.

dilute_scaling_error(density, temperature, omega)[source]#

Compare linear density scaling with optmat’s finite-density index law.

(n - 1) from \(\chi = N\alpha/\varepsilon_0\) and \(n - 1 = \chi/(\sqrt{1+\chi}+1)\) is compared with optmat’s refractivity (Lorentz–Lorenz with the refractivity virial) at the same number density. The comparison is made at the density itself: the state handed to optmat is the ideal-gas one, p = N k_B T with ideal=True, whose density is density exactly, so no equation of state or its inverse is consulted. Wavelengths outside the model’s qualified intervals are left out, unless none is inside.

Parameters:
densityfloat

Number density in m^-3, > 0.

temperaturefloat

Temperature in K, > 0.

omeganumpy.ndarray

Angular frequencies in rad/s of the band, > 0.

Returns:
tuple of float

The relative error (n-1)_dilute / (n-1)_optmat - 1 of largest magnitude over the band, and the vacuum wavelength in m where it is.

Raises:
MaterialError

If optmat refuses the evaluation, or the band is empty.

Examples

>>> import numpy as np
>>> from solprop.physdata.sellmeier import OptmatSusceptibility
>>> argon = OptmatSusceptibility.resolve("Ar")
>>> error, _ = argon.dilute_scaling_error(2.47e26, 293.15, np.array([2.356e15]))
>>> f"{error:.2e}"
'-1.88e-03'
dsusceptibility_domega(omega)[source]#

Return optmat’s exact frequency derivative of the susceptibility.

Parameters:
omegaarray_like

Angular frequency in rad/s, >= 0.

Returns:
ndarray

dgamma/domega in m^3 s/rad.

Warns:
MaterialRangeWarning

If the band leaves the model’s qualified intervals.

property material_records: tuple[MaterialRecord, ...][source]#

Return the record of the optmat model, for a run’s manifest.

Returns:
tuple of MaterialRecord

The dispersion model’s identity.

model: OptmatModel#
property reference_density: float[source]#

Return the density of the reference state, the dilute limit.

Returns:
float

0.0 m^-3.

classmethod resolve(gas, model='recommended')[source]#

Resolve a gas’s optmat dispersion model and adapt it.

Parameters:
gasGas or str

The species. Gas.Air is optmat’s named composition’s summed polarizabilities, "dry air (N2, O2, Ar)" by default.

modelstr or OptmatModel, optional

"recommended", an optmat model name (for air, a composition name), or an optmat model object for gas, taken as it is.

Returns:
OptmatSusceptibility

The adapted model.

Raises:
MaterialError

If the gas is unknown, optmat has no such model, or the model describes another gas.

Warns:
MaterialStatusWarning

If the model is provisional or untested.

susceptibility(omega)[source]#

Return the per-particle susceptibility alpha / eps0.

Parameters:
omegaarray_like

Angular frequency in rad/s, >= 0. Zero gives the static limit.

Returns:
ndarray

gamma(omega) in m^3, such that chi1 = gamma * rho; complex, with Im gamma >= 0, for an absorbing model.

Warns:
MaterialRangeWarning

If the band leaves the model’s qualified intervals.

class solprop.physdata.sellmeier.PoleSumSusceptibility(*, gas, source, reference_state, wavelength_range, citation, b, c)[source]#

Bases: GasSusceptibility

A susceptibility written directly as a sum of poles in lambda^2.

chi(lambda) = sum_i b_i lambda^2 / (lambda^2 - c_i) with lambda in microns, the form used by Börzsönyi’s two-pole fits and by the three-pole fits extended into the vacuum ultraviolet. In terms of u = 1 / lambda^2 this is sum_i b_i / (1 - c_i u), which stays finite at zero frequency.

Attributes:
btuple of float

Pole strengths, dimensionless, being the susceptibility at the reference state.

ctuple of float

Pole positions in um^2, one per entry of b. A negative entry is a fitting parameter with no resonance, not a physical pole.

b: tuple[float, ...]#
c: tuple[float, ...]#
class solprop.physdata.sellmeier.ReferenceState(pressure_bar, temperature)[source]#

Bases: object

The thermodynamic state at which a measured refractivity was reported.

A published Sellmeier expression gives the refractivity of a gas at one temperature and pressure. Dividing by the number density there turns it into the per-particle quantity this module works with, so getting the state right matters: 1 bar against 1 atm is a 1.3 % error in every susceptibility.

Attributes:
pressure_barfloat

Absolute pressure of the reported measurement, in bar.

temperaturefloat

Temperature of the reported measurement, in K.

pressure_bar: float#
temperature: float#
class solprop.physdata.sellmeier.RefractivitySusceptibility(*, gas, source, reference_state, wavelength_range, citation)[source]#

Bases: GasSusceptibility

A model whose source publishes the refractivity n - 1 rather than chi.

The susceptibility follows as chi = (1 + (n - 1))^2 - 1. Keeping that step in one place is deliberate: omitting either the subtraction or the squaring when implementing the He et al. expression is an easy error, and omitting both is wrong by a factor of about 1200.

class solprop.physdata.sellmeier.WavenumberPoleIndex(*, gas, source, reference_state, wavelength_range, citation, a, b, c)[source]#

Bases: RefractivitySusceptibility

A refractivity written as one pole in the squared wavenumber.

(n - 1) * 1e8 = a + b / (c - nu^2) with the wavenumber nu = 1e4 / lambda[um] in cm^-1, the form used by He et al. (2021) for methane, nitrous oxide and sulfur hexafluoride.

Attributes:
afloat

Constant term of (n - 1) * 1e8 at the reference state.

bfloat

Pole strength in cm^-2.

cfloat

Pole position in cm^-2.

a: float#
b: float#
c: float#
solprop.physdata.sellmeier.chi1(gas, omega, *, pressure, temperature=293.15)[source]#

Return the linear susceptibility of a gas.

chi1 = gamma(omega) * rho, with the number density from the equation of state.

Parameters:
gasGas or str

The species.

omegaarray_like

Angular frequency in rad/s, >= 0.

pressurefloat

Absolute pressure in bar (the documented non-SI user-interface unit), >= 0.

temperaturefloat, optional

Temperature in K, > 0. Defaults to 293.15 K.

Returns:
ndarray

Dimensionless susceptibility, 0-d for scalar input.

Warns:
MaterialRangeWarning

If any wavelength lies outside the validity range of the source.

Examples

>>> from solprop.physdata import Gas, chi1
>>> f"{float(chi1(Gas.Ar, 2.356e15, pressure=1.0)):.6e}"
'5.142114e-04'
solprop.physdata.sellmeier.dchi1_domega(gas, omega, *, pressure, temperature=293.15)[source]#

Return the analytic frequency derivative of the linear susceptibility.

Parameters:
gasGas or str

The species.

omegaarray_like

Angular frequency in rad/s, >= 0.

pressurefloat

Absolute pressure in bar, >= 0.

temperaturefloat, optional

Temperature in K, > 0. Defaults to 293.15 K.

Returns:
ndarray

dchi1/domega in s/rad, 0-d for scalar input.

Warns:
MaterialRangeWarning

If any wavelength lies outside the validity range of the source.

Examples

>>> from solprop.physdata import Gas, dchi1_domega
>>> f"{float(dchi1_domega(Gas.Ar, 2.356e15, pressure=1.0)):.6e}"
'3.630134e-21'
solprop.physdata.sellmeier.drefractive_index_domega(gas, omega, *, pressure, temperature=293.15)[source]#

Return the analytic frequency derivative of the refractive index.

dn/domega = (dchi1/domega) / (2 n). Together with n this gives the group index and the group delay per unit length, beta_1 = (n + omega dn/domega) / c, which the co-moving frame needs to about 1e-10.

Parameters:
gasGas or str

The species.

omegaarray_like

Angular frequency in rad/s, >= 0.

pressurefloat

Absolute pressure in bar (the documented non-SI user-interface unit), >= 0.

temperaturefloat, optional

Temperature in K, > 0. Defaults to 293.15 K.

Returns:
ndarray

dn/domega in s/rad, 0-d for scalar input.

Warns:
MaterialRangeWarning

If any wavelength lies outside the validity range of the source.

Examples

>>> from solprop.physdata import Gas, drefractive_index_domega
>>> f"{float(drefractive_index_domega(Gas.Ar, 2.356e15, pressure=1.0)):.6e}"
'1.814601e-21'
solprop.physdata.sellmeier.gamma(gas, *, model='recommended')[source]#

Return the per-particle susceptibility model for a gas: optmat’s.

The returned object exposes susceptibility(omega) in m^3, its exact derivative dsusceptibility_domega(omega), the wavelength_range its qualified intervals span, and the citation.

Parameters:
gasGas or str

The species.

modelstr or OptmatModel, optional

"recommended" (the default), an optmat model name of this gas, such as "borzsonyi_2008/sellmeier_Ar", or an optmat model object.

Returns:
GasSusceptibility

The OptmatSusceptibility.

Raises:
MaterialError

If the gas is unknown, optmat has no such model, or it describes another gas.

Examples

>>> from solprop.physdata import Gas, gamma
>>> gamma(Gas.Kr).source
'fit/Kr'
>>> gamma(Gas.Kr, model="borzsonyi_2008/sellmeier_Kr").source
'borzsonyi_2008/sellmeier_Kr'
solprop.physdata.sellmeier.refractive_index(gas, omega, *, pressure, temperature=293.15)[source]#

Return the refractive index of a gas.

n = sqrt(1 + chi1), real: these models carry no absorption. Where the quantity of interest is n - 1, use refractive_index_minus_one(), which avoids the cancellation.

Parameters:
gasGas or str

The species.

omegaarray_like

Angular frequency in rad/s, >= 0.

pressurefloat

Absolute pressure in bar (the documented non-SI user-interface unit), >= 0.

temperaturefloat, optional

Temperature in K, > 0. Defaults to 293.15 K.

Returns:
ndarray

Refractive index, 0-d for scalar input.

Warns:
MaterialRangeWarning

If any wavelength lies outside the validity range of the source.

Examples

Argon at 800 nm and 1 bar.

>>> from solprop.physdata import Gas, refractive_index
>>> f"{float(refractive_index(Gas.Ar, 2.356e15, pressure=1.0)):.9f}"
'1.000257073'
solprop.physdata.sellmeier.refractive_index_minus_one(gas, omega, *, pressure, temperature=293.15)[source]#

Return n - 1 for a gas, without cancellation.

Computed as chi1 / (1 + sqrt(1 + chi1)), which is exact for the small susceptibilities of a gas; forming sqrt(1 + chi1) - 1 instead would lose about four significant figures at 1 bar (docs/dev/conventions.md section 5).

Parameters:
gasGas or str

The species.

omegaarray_like

Angular frequency in rad/s, >= 0.

pressurefloat

Absolute pressure in bar (the documented non-SI user-interface unit), >= 0.

temperaturefloat, optional

Temperature in K, > 0. Defaults to 293.15 K.

Returns:
ndarray

n - 1, 0-d for scalar input.

Warns:
MaterialRangeWarning

If any wavelength lies outside the validity range of the source.

Examples

>>> from solprop.physdata import Gas, refractive_index_minus_one
>>> f"{float(refractive_index_minus_one(Gas.Ar, 2.356e15, pressure=1.0)):.6e}"
'2.570726e-04'