optmat.gas.raman#

The Raman lines of the molecular gases, ready for a propagation code (TC-439).

A molecule’s delayed (Raman) third-order response is a sum of damped oscillators, one per Raman line, each driven by the intensity. This module turns curated spectroscopic data into those lines: the frequency of every line, its coupling amplitude and its linewidth at a stated temperature and density. The tensor response is assembled by optmat.gas.raman_response; a propagation code chooses how to sample or integrate it.

The convention of the amplitudes. For a field linearly polarised along \(x\), the Raman polarisation of a gas of number density \(N\) is

\[P_x(t) = \varepsilon_0 N E_x(t) \int_0^\infty h(\tau) E_x^2(t - \tau)\, d\tau, \qquad h(\tau) = \sum_k A_k \sin(\Omega_k \tau)\, e^{-\Gamma_k \tau},\]

with \(A_k\) in m^5 V^-2 s^-1, \(\Omega_k\) the line’s angular frequency and \(\Gamma_k = \pi \Delta\nu_k\) its dephasing rate (\(\Delta\nu_k\) the full width at half maximum in Hz). A propagation adapter maps the lines one to one.

  • Rotational S branch (\(J \to J + 2\) of a linear molecule):

    \[A_J = \frac{2}{15} \frac{\Delta\alpha^2}{\hbar \varepsilon_0} \frac{(J + 1)(J + 2)}{2J + 3} (n_J - n_{J+2}),\]

    with \(\Delta\alpha\) the polarizability anisotropy (C m^2/V) and \(n_J = g_J e^{-E_J / k_B T} / Z\) the population of one of the \(2J + 1\) sublevels (Placzek-Teller; equivalently, \(p_J/(2J+1) = n_J\)).

  • Vibrational Q branch (\(v = 0 \to 1\), one line per rotational level):

    \[A_J = \frac{|\langle 0 | \bar\alpha | 1 \rangle|^2}{\hbar \varepsilon_0} P_J,\]

    with \(\langle 0 | \bar\alpha | 1 \rangle\) the transition polarizability of the mean (isotropic) polarizability (C m^2/V) and \(P_J\) the fraction of molecules in level \(J\) (all sublevels). For a harmonic mode \(|\langle 0|\bar\alpha|1\rangle|^2 = (\partial\bar\alpha/\partial q)^2 \hbar / (2 \mu \Omega)\), so \(A = (\partial\bar\alpha/\partial q)^2 / (2\mu\Omega \varepsilon_0)\), with a consistent vibrational coordinate and reduced mass.

Energies come from the curated constants in cm^-1: the rotational levels \(E_J = B J(J+1) - D [J(J+1)]^2 + H [J(J+1)]^3\), and the Q-branch line \(\nu_J = \nu_0 + (B_1 - B_0) J(J+1) - (D_1 - D_0) [J(J+1)]^2\).

Module Attributes

RAMAN_FILE

The per-gas choices of the Raman parameters, each with its reason.

Functions

band_lines(gas, temperature, density[, ...])

Return a gas's Raman lines band by band, with their widths, at a state.

default_j_max(constants, temperature)

Return the rotational level at about 50 k_B T, where the population is ~1e-20.

raman_lines(gas, temperature, density[, ...])

Return a gas's Raman lines, with their widths, at a state.

raman_parameters(gas)

Return the curated Raman parameters of a molecular gas.

rotational_lines(constants, spin, ...[, ...])

Return the rotational S-branch lines of a linear molecule.

rotational_nonlinear_index(lines, density)

Return the rotational Raman nonlinear index for a pulse long against the lines.

rotational_populations(constants, spin, ...)

Return the fraction of molecules in each rotational level.

spin_statistics(gas)

Return the nuclear-spin weights of a linear molecule's rotational levels.

stokes_gain(lines, pump_wavelength, density)

Return the steady-state Stokes gain of each line, for a pump at its centre.

vibrational_lines(band_origin, ...[, lower, ...])

Return the vibrational Q-branch lines of a mode.

Classes

AnisotropyTable(j, value)

A polarizability element that depends on the rotational level.

BandLines(band, lines[, dispersion_factor])

The lines of one Raman band of a gas at a state.

RamanLines(branch, j, frequency, amplitude)

Raman lines in the convention of the module docstring.

RamanParameters(gas, rotational, vibrational)

The curated Raman parameters of a gas.

RotationalBand(constants, spin, anisotropy, ...)

The rotational Raman band of a linear molecule.

RotationalConstants(b[, d, h])

The constants of one vibrational level's rotational ladder, cm^-1.

SpinStatistics(even, odd, reason)

The nuclear-spin weights of the even and odd rotational levels.

VibrationalBand(mode, band_origin, ...[, ...])

One vibrational Raman band (its Q branch).

class optmat.gas.raman.AnisotropyTable(j, value)[source]#

Bases: object

A polarizability element that depends on the rotational level.

For H2 the anisotropy of the J -> J + 2 transition rises by 10 % from J = 0 to J = 6 (centrifugal stretching), and the Q(J) transition polarizability by 2 % from J = 0 to 5. Between tabulated levels it is interpolated linearly; beyond them it is held at the end values.

Attributes:
jtuple of int

The lower levels, increasing.

valuetuple of float

The anisotropy of each, C m^2/V.

j: tuple[int, ...]#
value: tuple[float, ...]#
class optmat.gas.raman.BandLines(band, lines, dispersion_factor=1.0)[source]#

Bases: object

The lines of one Raman band of a gas at a state.

Attributes:
bandRotationalBand or VibrationalBand

The band, with its curated parameters.

linesRamanLines

Its lines, with their widths, in the convention of this module.

dispersion_factorfloat

The factor applied to the band’s transition elements for the pump wavelength asked for (1 for the rotational band, which is not dispersed).

band: RotationalBand | VibrationalBand#
dispersion_factor: float = 1.0#
lines: RamanLines#
optmat.gas.raman.RAMAN_FILE = PosixPath('/home/docs/checkouts/optmat/checkouts/7/src/optmat/data/gas/raman.toml')#

The per-gas choices of the Raman parameters, each with its reason.

class optmat.gas.raman.RamanLines(branch, j, frequency, amplitude, fwhm=None)[source]#

Bases: object

Raman lines in the convention of the module docstring.

Attributes:
branchtuple of str

S (rotational) or Q (vibrational), per line.

jnumpy.ndarray

The lower level’s rotational quantum number (-1 where the line is not resolved by J).

frequencynumpy.ndarray

Angular frequency Omega, rad/s.

amplitudenumpy.ndarray

A, m^5 V^-2 s^-1.

fwhmnumpy.ndarray or None

The full width at half maximum of each line at the state asked for, Hz (Gamma = pi FWHM); None where no state was given.

amplitude: Float[ndarray, 'n']#
branch: tuple[str, ...]#
frequency: Float[ndarray, 'n']#
fwhm: Float[ndarray, 'n'] | None = None#
j: Int[ndarray, 'n']#
class optmat.gas.raman.RamanParameters(gas, rotational, vibrational, reasons=<factory>, missing='')[source]#

Bases: object

The curated Raman parameters of a gas.

Attributes:
gasGas

The gas.

rotationalRotationalBand or None

The rotational band; None for a spherical top.

vibrationaltuple of VibrationalBand

The Raman-active vibrational bands curated.

reasonsdict of str to str

Why each choice was made (from raman.toml).

missingstr

What is known to be missing, or an empty string.

gas: Gas#
missing: str = ''#
reasons: Mapping[str, str]#
rotational: RotationalBand | None#
vibrational: tuple[VibrationalBand, ...]#
class optmat.gas.raman.RotationalBand(constants, spin, anisotropy, widths, sources)[source]#

Bases: object

The rotational Raman band of a linear molecule.

Attributes:
constantsRotationalConstants

The ground vibrational level’s constants, cm^-1.

spinSpinStatistics

The nuclear-spin weights.

anisotropyfloat or AnisotropyTable

The polarizability anisotropy, C m^2/V.

widthsLinewidthLaw or LineTableLaw or ExponentialGapLaw

The width law of the lines.

sourcesdict of str to str

The dataset or formula behind each parameter.

anisotropy: float | AnisotropyTable#
constants: RotationalConstants#
sources: Mapping[str, str]#
spin: SpinStatistics#
widths: LinewidthLaw | LineTableLaw | ExponentialGapLaw#
class optmat.gas.raman.RotationalConstants(b, d=0.0, h=0.0)[source]#

Bases: object

The constants of one vibrational level’s rotational ladder, cm^-1.

Attributes:
bfloat

Rotational constant B.

dfloat

Centrifugal distortion D.

hfloat

Sextic distortion H.

b: float#
d: float = 0.0#
energy(j)[source]#

Return the rotational energies of levels J.

Parameters:
jnumpy.ndarray

Rotational quantum numbers.

Returns:
numpy.ndarray

B x - D x^2 + H x^3 with x = J (J + 1), cm^-1.

h: float = 0.0#
class optmat.gas.raman.SpinStatistics(even, odd, reason)[source]#

Bases: object

The nuclear-spin weights of the even and odd rotational levels.

Attributes:
evenint

Statistical weight of the even-J (or even-N) levels.

oddint

Statistical weight of the odd levels.

reasonstr

Where the weights come from.

even: int#
odd: int#
reason: str#
weight(j)[source]#

Return the weight of each rotational level.

Parameters:
jnumpy.ndarray

Rotational quantum numbers.

Returns:
numpy.ndarray

even or odd for each.

class optmat.gas.raman.VibrationalBand(mode, band_origin, transition_polarizability, lower, upper, widths, sources, resonance=None, reference_wavenumber=0.0, transition_anisotropy=None, depolarization=None, key='')[source]#

Bases: object

One vibrational Raman band (its Q branch).

Attributes:
modestr

The mode, as the curated data describe it.

band_originfloat

nu_0, cm^-1.

transition_polarizabilityfloat

<0| alpha_mean |1>, C m^2/V.

lower, upperRotationalConstants or None

The rotational constants of the two vibrational levels (None for an unresolved band).

widthsLinewidthLaw or LineTableLaw or ExponentialGapLaw

The width law of the lines.

sourcesdict of str to str

The dataset or formula behind each parameter.

resonancefloat or None

The effective resonance nu_i of a single-resonance dispersion law, cm^-1: the element scales as 1 / (nu_i^2 - nu_p^2) with the pump wavenumber nu_p (Bischel & Black 1983). None where no law is curated.

reference_wavenumberfloat

The pump wavenumber at which transition_polarizability holds, cm^-1 (0 for a static value).

transition_anisotropyAnisotropyTable or None

<0J| alpha_par - alpha_perp |1J> per rotational level, C m^2/V, where the band’s elements are curated as components (H2, D2); None otherwise.

depolarizationfloat or None

The depolarization ratio of the Q branch for linearly polarised light at 90 degrees, rho = 3 g^2 / (45 a^2 + 4 g^2), where one is curated (the value that reduced the cross sections, or the band’s depolarization key); None where none is.

keystr

A short, stable name of the band (v1, nu3, fermi_1388): the band of the Raman response’s lines and of their identifiers; the mode where none is curated.

band_origin: float#
depolarization: float | None = None#
dispersion_factor(wavelength)[source]#

Return the factor on the transition polarizability at a pump wavelength.

Parameters:
wavelengthfloat or None

Vacuum wavelength of the pump, m; None for the element as curated.

Returns:
float

(nu_i^2 - nu_ref^2) / (nu_i^2 - nu_p^2), or 1 without a law or a wavelength.

Raises:
DataError

If the pump is at or beyond the effective resonance.

key: str = ''#
lower: RotationalConstants | None#
mode: str#
reference_wavenumber: float = 0.0#
resonance: float | None = None#
sources: Mapping[str, str]#
transition_anisotropy: AnisotropyTable | None = None#
transition_polarizability: float | AnisotropyTable#
upper: RotationalConstants | None#
widths: LinewidthLaw | LineTableLaw | ExponentialGapLaw#
optmat.gas.raman.band_lines(gas, temperature, density, fractions=None, *, wavelength=None, population_floor=1e-12)[source]#

Return a gas’s Raman lines band by band, with their widths, at a state.

The parameters are those of raman_lines(), which joins these lines.

Parameters:
gasGas or str

The gas.

temperaturefloat

Temperature, K, > 0.

densityfloat

Total number density, m^-3, > 0.

fractionsmapping of str to float, optional

Mole fractions of the collision partners; the pure gas by default.

wavelengthfloat, optional

Vacuum wavelength of the optical field, m, for the vibrational strengths’ dispersion.

population_floorfloat, optional

Lines from levels less populated than this fraction are left out.

Returns:
tuple of BandLines

The rotational band (if any), then each vibrational band.

optmat.gas.raman.default_j_max(constants, temperature)[source]#

Return the rotational level at about 50 k_B T, where the population is ~1e-20.

Parameters:
constantsRotationalConstants

The rotational constants, cm^-1.

temperaturefloat

Temperature, K.

Returns:
int

sqrt(50 k_B T / B) + 5, at least 10.

Examples

>>> default_j_max(RotationalConstants(2.0), 295.0)
76
optmat.gas.raman.raman_lines(gas, temperature, density, fractions=None, *, wavelength=None, population_floor=1e-12)[source]#

Return a gas’s Raman lines, with their widths, at a state.

Parameters:
gasGas or str

The gas.

temperaturefloat

Temperature, K, > 0.

densityfloat

Total number density, m^-3, > 0 (the widths depend on it; the amplitudes are per molecule).

fractionsmapping of str to float, optional

Mole fractions of the collision partners (self for the gas itself); the pure gas by default.

wavelengthfloat, optional

Vacuum wavelength of the optical field, m: the vibrational strengths are scaled by their dispersion law where one is curated (TC-542). By default the strengths are as curated (static, or at the stated reference wavelength).

population_floorfloat, optional

Lines from levels less populated than this fraction are left out.

Returns:
RamanLines

The rotational S-branch lines, then each vibrational band’s Q-branch lines, in the convention of this module.

optmat.gas.raman.raman_parameters(gas)[source]#

Return the curated Raman parameters of a molecular gas.

Parameters:
gasGas or str

The gas.

Returns:
RamanParameters

Its bands, with the source of every parameter.

Raises:
DataError

If the gas has none (an atom, or a molecule not yet curated).

optmat.gas.raman.rotational_lines(constants, spin, anisotropy, temperature, *, j_max=None, population_floor=1e-12)[source]#

Return the rotational S-branch lines of a linear molecule.

Parameters:
constantsRotationalConstants

The ground vibrational level’s constants, cm^-1.

spinSpinStatistics

The nuclear-spin weights.

anisotropyfloat or AnisotropyTable

Polarizability anisotropy Delta alpha, C m^2/V, or its value per lower level.

temperaturefloat

Temperature, K, > 0.

j_maxint, optional

The highest lower level considered; by default the level near 50 k_B T (default_j_max()).

population_floorfloat, optional

Lines whose lower and upper populations are both below this fraction are dropped.

Returns:
RamanLines

One line per J -> J + 2 with a nonzero population difference.

Raises:
DataError

If the population at the highest level kept is not negligible.

Examples

With illustrative constants close to nitrogen’s (B = 2 cm^-1 and a polarizability anisotropy of 4.6 au), the line of largest amplitude at 295 K starts from J = 8 (an even level, which nitrogen’s spin statistics favour 2:1):

>>> from optmat.constants import AU_POLARIZABILITY
>>> anisotropy = 4.6 * AU_POLARIZABILITY
>>> lines = rotational_lines(
...     RotationalConstants(2.0), spin_statistics("N2"), anisotropy, 295.0
... )
>>> int(lines.j[np.argmax(lines.amplitude)])
8
optmat.gas.raman.rotational_nonlinear_index(lines, density)[source]#

Return the rotational Raman nonlinear index for a pulse long against the lines.

A pulse long against every rotational period sees the time integral of the response, R0 = sum_J A_J Omega_J / (Omega_J^2 + Gamma_J^2). In the convention of this module the index change is then N R0 I / (2 eps0 c) (n0 = 1). The response is slow, so a probe sees the same shift as the pulse itself (only the slowly varying part of E^2 drives it), unlike the instantaneous electronic response, whose probe coefficient is twice the self one.

The integral is that of the coherent (Delta J = +-2) response. The isothermal, classical orientational response Delta alpha^2 / (22.5 eps0 k_B T) is 4/3 larger at high temperature: it also contains the redistribution of the M sublevels within a level, which collisions establish but a coherent drive does not (TC-493).

Parameters:
linesRamanLines

Lines with their widths (from raman_lines()); only the S branch is used.

densityfloat

Number density of the gas, m^-3.

Returns:
float

n2, m^2/W.

Raises:
DataError

If the lines carry no widths.

optmat.gas.raman.rotational_populations(constants, spin, temperature, j_max)[source]#

Return the fraction of molecules in each rotational level.

A truncated distortion series (B x - D x^2) turns over at high J, where it no longer describes the molecule; the ladder is cut at its last increasing level, which the callers then require to be negligibly populated.

Parameters:
constantsRotationalConstants

The ground vibrational level’s constants.

spinSpinStatistics

The nuclear-spin weights.

temperaturefloat

Temperature, K, > 0.

j_maxint

The highest level wanted.

Returns:
tuple of numpy.ndarray

J = 0 ... min(j_max, the turning level) and the fraction P_J of molecules in each (all 2J + 1 sublevels), summing to one.

optmat.gas.raman.spin_statistics(gas)[source]#

Return the nuclear-spin weights of a linear molecule’s rotational levels.

Parameters:
gasGas or str

A linear molecule.

Returns:
SpinStatistics

The weights.

Raises:
DataError

If the gas is not a linear molecule (an atom, or a spherical top, which has no rotational Raman spectrum).

Examples

>>> weights = spin_statistics("N2")
>>> weights.even, weights.odd
(6, 3)
>>> weights = spin_statistics("O2")
>>> weights.even, weights.odd
(0, 1)
optmat.gas.raman.stokes_gain(lines, pump_wavelength, density)[source]#

Return the steady-state Stokes gain of each line, for a pump at its centre.

With a real pump field E_p and Stokes field E_s polarised alike, the response H(Delta) = int h(tau) exp(i Delta tau) d tau of one line at the Raman resonance Delta = Omega is i A / (2 Gamma). The Stokes polarisation (1/2) eps0 N |E_p|^2 E_s H* then gives the intensity gain g = omega_s N A / (2 c^2 eps0 Gamma), with Gamma = pi FWHM and omega_s = omega_p - Omega. That is the plane-wave, narrow-band (steady-state) gain, dI_s / dz = g I_p I_s.

Parameters:
linesRamanLines

Lines with their widths (from raman_lines()).

pump_wavelengthfloat

Vacuum wavelength of the pump, m.

densityfloat

Number density of the Raman-active gas, m^-3.

Returns:
numpy.ndarray

The gain of each line, m/W.

Raises:
DataError

If the lines carry no widths.

optmat.gas.raman.vibrational_lines(band_origin, transition_polarizability, *, lower=None, upper=None, spin=None, temperature=296.0, j_max=None, population_floor=1e-12)[source]#

Return the vibrational Q-branch lines of a mode.

Parameters:
band_originfloat

The band origin nu_0, cm^-1.

transition_polarizabilityfloat or AnisotropyTable

<0| alpha_mean |1>, C m^2/V, or its value per rotational level (the table’s J is the level of the Q(J) line).

lower, upperRotationalConstants, optional

The rotational constants of v = 0 and v = 1. Without them (a spherical top, or an unresolved band) the branch is one line at the band origin.

spinSpinStatistics, optional

The nuclear-spin weights; needed with the constants.

temperaturefloat, optional

Temperature, K.

j_maxint, optional

The highest rotational level considered; by default the level near 50 k_B T (default_j_max()).

population_floorfloat, optional

Levels below this fraction are dropped.

Returns:
RamanLines

The Q(J) lines, or one line.

Raises:
DataError

If constants are given without spin weights, or the population at the highest level kept is not negligible.

Examples

With illustrative constants close to hydrogen’s (B = 60 cm^-1 in v = 0 and 57 cm^-1 in v = 1), Q(1) lies 2 (B_1 - B_0) = -6 cm^-1 from Q(0):

>>> lines = vibrational_lines(
...     4160.0, 1e-41, lower=RotationalConstants(60.0),
...     upper=RotationalConstants(57.0), spin=spin_statistics("H2"))
>>> f"{(lines.frequency[1] - lines.frequency[0]) / (2 * np.pi * C * 100):.1f}"
'-6.0'