optmat.gas.raman_response#
The Raman response function of a molecular gas or a gas mixture (TC-685 to TC-690).
A molecule’s delayed third-order response is a sum of damped oscillators, one per Raman
line (optmat.gas.raman). This module assembles them into the response function
itself, with its polarisation (tensor) structure and its dependence on the density,
so that a propagation code only samples it.
The convention (TC-442, TC-685). For a field linearly polarised along \(x\), the Raman polarisation of a gas of number density \(N\) is
with \(A_k\) in m^5 V^-2 s^-1 per molecule of the gas (of the mixture: a
constituent’s lines carry its mole fraction), \(\Omega_k\) in rad/s and
\(\Gamma_k = \pi \Delta\nu_k\), \(\Delta\nu_k\) the line’s full width at half
maximum in Hz. Its transform, with the sign convention of
optmat.gas.raman.stokes_gain(), is
so that \(H(\Omega_k) \approx i A_k / (2\Gamma_k)\) at a resonance and \(H(0) = \int h\, d\tau\).
The tensor form (Hellwarth). For any polarisation,
and \(h = a + b\) for a single linear polarisation. Each line has an isotropic (rank-0) share, from the mean polarizability \(\bar\alpha\), and an anisotropic (rank-2) share, from its anisotropy. The isotropic share enters \(a\) only; the anisotropic share \(h_2\) has \(a = -h_2/2\), \(b = 3h_2/2\) (the induced change of the anisotropic polarizability is the traceless part of \(E E\)). A weak probe polarised across the pump sees \(a\): the rotational lines give it \(-1/2\) of the parallel response, the isotropic vibrational lines all of it.
The rotational S lines are anisotropic (\(\Delta\alpha\)).
A vibrational Q(J) line has both shares. Its anisotropic share is \((4/45)\, b_{J,J}\, \gamma'^2 P_J / (\hbar\varepsilon_0)\), with the Placzek-Teller coefficient \(b_{J,J} = J(J+1)/[(2J-1)(2J+3)]\) and \(\gamma'\) the transition anisotropy: from its components where they are curated (H2, D2), else from the Q branch’s depolarization ratio \(\rho = 3 b\gamma'^2 / (45\bar\alpha'^2 + 4 b\gamma'^2)\) (TC-686).
The vibrational O and S branches (\(\Delta J = \mp 2\) with \(v = 0 \to 1\)), anisotropic only, are left out: they carry \(3\times\) the anisotropic Q share, 8 % of the Q branch for N2, 19 % for O2 and 3 % for H2 and D2, spread over some 100 cm^-1 about the band (TC-686).
The widths and the density. Every curated width law has the form
\(\Delta\nu = c + d/n + \beta n\) at a fixed temperature and composition (n in
amagat; optmat.gas.linewidths), so the response keeps the three coefficients
of each line (TC-687). A line’s frequency shifts with the density where a shift is
curated (H2’s Q branch; TC-688). The amplitudes depend on the temperature and the pump
wavelength only. The response is therefore fixed by (temperature, composition,
wavelength), and GasRamanResponse.decay_rates() and
GasRamanResponse.frequencies() give its density dependence cheaply, for any array
of densities: the part a propagation code evaluates along a density gradient. Lines
whose widths follow one law have the same damping_group: they can be summed into
one damped kernel without approximation.
Ranges (ModelRangeWarning, TC-688). The lines are independent Lorentzians
with collisional (impact) widths, which holds between two densities, found for the
strongest line of each band:
below, the motion of the molecules sets the width. For a line with a Dicke term the diffusion law holds above Herring et al.’s cutoff \(\rho_c = 3.33\, d / \Delta\nu_D\) (their Eq. (3)), with \(\Delta\nu_D\) the Doppler width for forward scattering (their Eq. (4)); below it \(d/n\) is capped at \(\Delta\nu_D\), a Lorentzian stand-in for the Doppler limit. For a line without one, the limit is the density at which its collisional width falls to \(\Delta\nu_D\);
above, the line’s width reaches the spacing to its nearest neighbour: the overlap and mixing of the lines (the collisional narrowing of a Q branch) is not modelled (TC-527);
the temperatures of each band’s width measurements (warned when the response is built).
What is not here. The sampling of the response on a time grid, its taper and padding, and any grouping or low-rank tabulation for speed belong to the propagation code (the manual page The Raman response). The response is a coherent (Kubo) response: the redistribution of the M sublevels within a level, which a slow isothermal measurement also sees, is not part of it (TC-493).
References#
R. W. Hellwarth, “Third-order optical susceptibilities of liquids and solids”, Prog. Quantum Electron. 5, 1 (1977).
G. Placzek and E. Teller, “Die Rotationsstruktur der Ramanbanden mehratomiger Moleküle”, Z. Phys. 81, 209 (1933).
Long, The Raman Effect (Wiley, 2002), chapters 5 and 6.
Module Attributes
The version of this module's evaluation code, in the identity of every model that evaluates through it; raised whenever the code changes its output (OEP 1, section 4.8). |
|
The range, shift and mixture choices of the responses, each with its reason. |
|
A part of the response. |
Functions
Return the Placzek-Teller coefficient of the Q lines, \(b_{J,J}\). |
|
|
Return the Raman response of a gas or a mixture at a temperature. |
Return the status a gas's Raman response would have, without building it. |
Classes
|
The Raman response of a gas or a mixture at one temperature and composition. |
|
One damping group of a Raman response: lines that share a width law and shift. |
- optmat.gas.raman_response.Component#
A part of the response.
parallel: \(h = a + b\), the response to a single linear polarisation.isotropicandanisotropic: the rank-0 and rank-2 shares ofparallel.aandb: Hellwarth’s two functions;ais the response a weak probe polarised across the pump sees.alias of
Literal[‘parallel’, ‘isotropic’, ‘anisotropic’, ‘a’, ‘b’]
- optmat.gas.raman_response.EVALUATION_VERSION: Final = 1#
The version of this module’s evaluation code, in the identity of every model that evaluates through it; raised whenever the code changes its output (OEP 1, section 4.8).
- class optmat.gas.raman_response.GasRamanResponse(composition, temperature, wavelength, species, band, branch, j, frequency, isotropic, anisotropic, width_constant, width_dicke, width_broadening, doppler_width, shift, damping_group, density_range, temperature_range, sources=<factory>, reasons=<factory>, *, status='user', status_reason='', modified=(), base='', material='', population_floor=nan)[source]#
Bases:
IdentifiedThe Raman response of a gas or a mixture at one temperature and composition.
Built by
raman_response(), or byfrom_lines()from a caller’s lines. Every array has one entry per line (a term of the response); the lines are grouped by species and band, the rotational S lines first. The damping groups and the density range are derived from the lines by one rule (from_lines()).Its overrides (
with_parameters()) act on the assembled lines:amplitude_scale, a factor on every amplitude;width_scale, a factor onc,d,betaand the Doppler cap alike, which keeps the groups; andwidth_law,{band: (c, d, beta, cap)}for every line of a band ("rotation", a mode, or"<species> <band>"in a mixture). A different temperature, composition, pump wavelength or population floor is a newraman_response(), not an override.- Attributes:
- compositiontuple of tuple of (str, float)
The species and their mole fractions (a pure gas is one species at 1).
- temperaturefloat
Temperature, K.
- wavelengthfloat or None
The pump wavelength at which the vibrational strengths are dispersed, m; None for the strengths as curated.
- speciestuple of str
The species of each line.
- bandtuple of str
The band of each line (
rotationor the vibrational mode).- branchtuple of str
SorQ.- jnumpy.ndarray
The lower rotational level (-1 for an unresolved band).
- frequencynumpy.ndarray
\(\Omega\) at zero density, rad/s.
- isotropic, anisotropicnumpy.ndarray
The rank-0 and rank-2 shares of each line’s amplitude for a single linear polarisation, per molecule of the gas, m^5 V^-2 s^-1.
- width_constantnumpy.ndarray
The density-independent width \(c\), Hz.
- width_dickenumpy.ndarray
The Dicke coefficient \(d\), Hz amagat.
- width_broadeningnumpy.ndarray
The broadening coefficient \(\beta\), Hz/amagat.
- doppler_widthnumpy.ndarray
The forward-scattering Doppler width that caps a Dicke term, Hz (0 where the line has no Dicke term).
- shiftnumpy.ndarray
The line’s frequency shift per unit density, Hz/amagat.
- damping_groupnumpy.ndarray
Lines with the same number share their width and shift at every density.
- density_rangetuple of float
The number densities between which the lines are independent collisional Lorentzians (module docstring), m^-3.
- temperature_rangetuple of float
The temperatures covered by the width measurements of every band, K.
- sourcesdict of str to str
The datasets and formulas behind the response, by what they give.
- reasonsdict of str to str
Why each choice was made.
- materialstr
What the response is of: a species, a named mixture (
"dry air") or, for mole fractions given directly, the species joined by+.- population_floorfloat
The relative population below which rotational levels were left out.
- statusstr
validated, orprovisionalwhere a constituent’s widths are a stopgap (CO2 and N2O, TC-542);userfor a response a caller built.
- DERIVED: ClassVar[tuple[str, ...]] = ('damping_group', 'density_range')#
Fields derived from the inputs, which an override may not name.
- INPUTS: ClassVar[tuple[str, ...]] = ('amplitude_scale', 'width_law', 'width_scale')#
The parameters
with_parameters()may change.
- property amplitude: Float[ndarray, 'n']#
The amplitude of each line for a single linear polarisation, m^5 V^-2 s^-1.
- Returns:
- numpy.ndarray
isotropic + anisotropic.
- anisotropic: Float[ndarray, 'n']#
- check_densities(density)[source]#
Warn once for the densities a consumer will evaluate the groups at.
The group view (
groups()) does not warn; a propagation code declares its densities here instead.- Parameters:
- densityarray_like
Number densities, m^-3, >= 0.
- Raises:
- DataError
If a density is negative or not finite.
- Warns:
- ModelRangeWarning
If any is outside
density_range.
- component_amplitudes(component='parallel')[source]#
Return each line’s amplitude in one part of the response.
- Parameters:
- component{“parallel”, “isotropic”, “anisotropic”, “a”, “b”}, optional
The part (
Component).
- Returns:
- numpy.ndarray
The amplitudes, m^5 V^-2 s^-1.
- Raises:
- DataError
If the component is not one of these.
- damping_group: Int[ndarray, 'n']#
- decay_rates(density)[source]#
Return every line’s dephasing rate \(\Gamma = \pi \Delta\nu\).
- Parameters:
- densityarray_like
Total number densities, m^-3, > 0.
- Returns:
- numpy.ndarray
\(\Gamma\), 1/s, shape
(len(self),) + shape(density).
- Warns:
- ModelRangeWarning
As
fwhm().
Examples
>>> from optmat.gas.linewidths import AMAGAT >>> response = raman_response("H2", 295.0, wavelength=800e-9) >>> rates = response.decay_rates(np.array([2.0, 10.0]) * AMAGAT) >>> rates.shape == (len(response), 2) True
- doppler_width: Float[ndarray, 'n']#
- frequencies(density)[source]#
Return every line’s angular frequency \(\Omega\) at number densities.
- Parameters:
- densityarray_like
Total number densities, m^-3, > 0.
- Returns:
- numpy.ndarray
\(\Omega\), rad/s, shape
(len(self),) + shape(density).
- Warns:
- ModelRangeWarning
As
fwhm().
- frequency: Float[ndarray, 'n']#
- classmethod from_lines(species, temperature, frequency, isotropic, anisotropic, width_constant, width_dicke, width_broadening, doppler_width, shift, *, band=None, branch=None, j=None, mass=None, composition=None, wavelength=None, temperature_range=None, citation='', material='', population_floor=nan, sources=None, reasons=None)[source]#
Return a Raman response from its lines, as
raman_response()does.The damping groups number the lines by first appearance of their species, band and width law
(c, d, beta, cap, shift). The density range is each band’s (TC-688) for its strongest line, with that line’s forward-scattering Doppler width (Herring et al.’s Eq. 4, from the species’ mass): the narrowest over the bands.- Parameters:
- speciesstr or sequence of str
The species of every line, or of each.
- temperaturefloat
- frequencyarray_like
Each line’s frequency at zero density, rad/s.
- isotropic, anisotropicarray_like
The two shares of each line’s amplitude, m^5 V^-2 s^-1.
- width_constant, width_dicke, width_broadeningarray_like
c(Hz),d(Hz amagat) andbeta(Hz/amagat).- doppler_widtharray_like
The Doppler cap of each line’s Dicke term, Hz; 0 without one.
- shiftarray_like
The frequency shift per unit density, Hz/amagat.
- band, branchsequence of str or None, optional
Each line’s band (
"rotation"by default) and branch ("S").- jarray_like or None, optional
Each line’s lower level; -1 (unresolved) by default.
- massfloat or Mapping or None, optional
The molecular mass in u, one or by species; the curated one of a curated species by default.
- compositionMapping or None, optional
Species to mole fraction; each species at its share of the lines’ by default (one species: 1).
- wavelengthfloat or None, optional
The pump wavelength the strengths are dispersed at, m.
- temperature_rangetuple of float or None, optional
The temperatures the widths hold over, K; the temperature by default.
- citationstr, optional
Where the lines come from.
- materialstr, optional
What the response is of; the species joined by
+by default.- population_floorfloat, optional
The population below which levels were left out, if one was.
- sources, reasonsMapping or None, optional
The response’s sources and reasons.
- Returns:
- GasRamanResponse
The response, status
user.
- Raises:
- ValueError
If the arrays differ in length, a Dicke term has no cap, or a species’ mass is unknown.
Examples
>>> response = GasRamanResponse.from_lines( ... "X2", 295.0, [1e13], [0.0], [1e-40], [1e9], [0.0], [1e8], [0.0], [0.0], ... mass=30.0) >>> response.identity.material, int(response.damping_group[0]) ('X2', 0)
- fwhm(density)[source]#
Return every line’s full width at half maximum at number densities.
- Parameters:
- densityarray_like
Total number densities, m^-3, > 0.
- Returns:
- numpy.ndarray
FWHM, Hz, shape
(len(self),) + shape(density).
- Warns:
- ModelRangeWarning
Outside
density_range.
- groups()[source]#
Return the damping groups, with their laws in SI (OEP 1, section 6.7).
- Returns:
- tuple of RamanGroup
In the order of
damping_group; their responses sum toresponse().
Examples
>>> h2 = raman_response("H2", 295.0) >>> [g.line_ids[0] for g in h2.groups() if g.shifted][:1] ['H2/v1/Q/0']
- isotropic: Float[ndarray, 'n']#
- j: Int[ndarray, 'n']#
- property line_id: tuple[str, ...]#
A stable identifier of each line:
species/band/branch/J.Jis the lower level, or-for an unresolved band ("N2/rotation/S/6","H2/v1/Q/1").- Returns:
- tuple of str
One per line.
- lines(density)[source]#
Return the lines at a number density, for
optmat.gas.raman’s formulas.- Parameters:
- densityfloat
Total number density, m^-3, > 0.
- Returns:
- RamanLines
Frequencies, amplitudes (parallel) and widths at that density.
- Warns:
- ModelRangeWarning
As
fwhm().
- property references: tuple[str, ...]#
The curated sources the response rests on.
- Returns:
- tuple of str
Source ids, sorted.
- response(time, density, component='parallel')[source]#
Return the response function \(h(\tau)\) at a number density.
- Parameters:
- timearray_like
Delay after the impulse, s (the response is zero before it).
- densityfloat
Total number density, m^-3, > 0.
- component{“parallel”, “isotropic”, “anisotropic”, “a”, “b”}, optional
The part of the response (
Component).
- Returns:
- numpy.ndarray
\(h\), m^5 V^-2 s^-1, with the shape of
time.
- Warns:
- ModelRangeWarning
As
fwhm().
Examples
Nitrogen’s response first peaks 75 fs after the impulse, as the molecules begin to align:
>>> from optmat.gas.linewidths import AMAGAT >>> response = raman_response("N2", 295.0) >>> t = np.linspace(0.0, 0.2e-12, 2001) >>> f"{t[np.argmax(response.response(t, AMAGAT))] * 1e15:.0f}" '75'
- rotational_nonlinear_index(density)[source]#
Return the rotational nonlinear index for a long pulse.
optmat.gas.raman.rotational_nonlinear_index()oflines().- Parameters:
- densityfloat
Total number density, m^-3, > 0.
- Returns:
- float
n2, m^2/W.
- Warns:
- ModelRangeWarning
As
fwhm().
- shift: Float[ndarray, 'n']#
- spectrum(omega, density, component='parallel')[source]#
Return \(H(\omega) = \int_0^\infty h(\tau) e^{i\omega\tau} d\tau\).
- Parameters:
- omegaarray_like
Angular frequency, rad/s.
- densityfloat
Total number density, m^-3, > 0.
- component{“parallel”, “isotropic”, “anisotropic”, “a”, “b”}, optional
The part of the response (
Component).
- Returns:
- numpy.ndarray
\(H\), m^5 V^-2, complex, with the shape of
omega. Its imaginary part is positive at a Stokes resonance (gain).
- Warns:
- ModelRangeWarning
As
fwhm().
- stokes_gain(pump_wavelength, density)[source]#
Return each line’s steady-state Stokes gain, pump and Stokes polarised alike.
optmat.gas.raman.stokes_gain()oflines().- Parameters:
- pump_wavelengthfloat
Vacuum wavelength of the pump, m.
- densityfloat
Total number density, m^-3, > 0.
- Returns:
- numpy.ndarray
The gain of each line, m/W.
- Warns:
- ModelRangeWarning
As
fwhm().
- width_broadening: Float[ndarray, 'n']#
- width_constant: Float[ndarray, 'n']#
- width_dicke: Float[ndarray, 'n']#
- optmat.gas.raman_response.RAMAN_RESPONSE_FILE: Final = PosixPath('/home/docs/checkouts/optmat/checkouts/7/src/optmat/data/gas/raman_response.toml')#
The range, shift and mixture choices of the responses, each with its reason.
- class optmat.gas.raman_response.RamanGroup(index, lines, line_ids, gamma_constant, gamma_dicke, gamma_doppler, gamma_broadening, shift, frequency, isotropic, anisotropic)[source]#
Bases:
objectOne damping group of a Raman response: lines that share a width law and shift.
The group’s decay rate at a total number density \(N\) (m^-3) is \(\Gamma_g(N) = \gamma_c + \min(\gamma_d/N,\ \gamma_D) + \gamma_\beta N\), 1/s, and every line’s angular frequency \(\omega_k + \sigma_g N\), so that the group’s response is \(e^{-\Gamma_g t}\sum_k A_k \sin((\omega_k + \sigma_g N) t)\) (OEP 1, section 4.5). Where \(\sigma_g = 0\) the row of amplitudes and frequencies is fixed and only the decay depends on the density.
- Attributes:
- indexint
The group’s number (
damping_group).- linestuple of int
The indices of its lines in the response.
- line_idstuple of str
Their identifiers (
GasRamanResponse.line_id).- gamma_constantfloat
\(\gamma_c = \pi c\), 1/s.
- gamma_dickefloat
\(\gamma_d = \pi d N_\text{amg}\), 1/s m^-3; 0 without a Dicke term.
- gamma_dopplerfloat
\(\gamma_D = \pi \Delta\nu_D\), the Dicke term’s cap, 1/s; 0 without one.
- gamma_broadeningfloat
\(\gamma_\beta = \pi \beta / N_\text{amg}\), 1/s m^3.
- shiftfloat
\(\sigma_g = 2\pi s_g / N_\text{amg}\), rad s^-1 m^3.
- frequencynumpy.ndarray
The lines’ angular frequencies at zero density, rad/s.
- isotropic, anisotropicnumpy.ndarray
Their amplitudes’ two shares, m^5 V^-2 s^-1.
- anisotropic: Float[ndarray, 'm']#
- decay_rate(density)[source]#
Return the group’s decay rate, down to zero density.
At
N = 0it is the law’s limit: the capped Dicke term tends to its cap. It does not warn outside the response’s validated range; seeGasRamanResponse.check_densities().- Parameters:
- densityarray_like
Total number densities, m^-3, >= 0.
- Returns:
- numpy.ndarray
The decay rate, 1/s, the shape of
density.
- Raises:
- DataError
If a density is negative or not finite.
- frequency: Float[ndarray, 'm']#
- isotropic: Float[ndarray, 'm']#
- response(time, density, component='parallel')[source]#
Return the group’s share of the response at a density.
- Parameters:
- timearray_like
Delay after the impulse, s (zero before it).
- densityfloat
Total number density, m^-3, >= 0.
- component{“parallel”, “isotropic”, “anisotropic”, “a”, “b”}, optional
The part of the response.
- Returns:
- numpy.ndarray
The group’s response, m^5 V^-2 s^-1, the shape of
time.
- optmat.gas.raman_response.placzek_teller_q(j)[source]#
Return the Placzek-Teller coefficient of the Q lines, \(b_{J,J}\).
The share of a level’s anisotropic scattering that keeps \(J\), \(J(J+1)/[(2J-1)(2J+3)]\); with \(b_{J+2,J}\) and \(b_{J-2,J}\) the three sum to one. It is 0 for \(J = 0\) and tends to 1/4.
- Parameters:
- jnumpy.ndarray
Rotational levels, >= 0.
- Returns:
- numpy.ndarray
\(b_{J,J}\).
Examples
>>> [f"{b:.4f}" for b in placzek_teller_q(np.array([0, 1, 2, 50]))] ['0.0000', '0.4000', '0.2857', '0.2501']
- optmat.gas.raman_response.raman_response(gas, temperature, *, wavelength=None, population_floor=1e-12)[source]#
Return the Raman response of a gas or a mixture at a temperature.
- Parameters:
- gasGas or str or mapping of str to float
A molecular gas (
"N2"), a mixture named inraman_response.toml("dry air"), or mole fractions by species (atoms take part as collision partners only).- temperaturefloat
Temperature, K, > 0: the populations and the widths.
- wavelengthfloat, optional
Vacuum wavelength of the pump, m, at which the vibrational strengths are dispersed (Bischel & Black’s law, TC-542); by default as curated.
- population_floorfloat, optional
Lines from levels less populated than this fraction are left out.
- Returns:
- GasRamanResponse
The response, with its density dependence.
- Raises:
- DataError
If a species has no Raman parameters or choices, or a collision partner has no broadening coefficient.
- Warns:
- ModelRangeWarning
If the temperature is outside that of the width measurements.
Examples
>>> from optmat.gas.linewidths import AMAGAT >>> air = raman_response("dry air", 295.0, wavelength=800e-9) >>> sorted(set(air.species)) ['N2', 'O2'] >>> f"{air.rotational_nonlinear_index(AMAGAT) * 1e23:.2f}" '3.22'
- optmat.gas.raman_response.raman_response_status(gas)[source]#
Return the status a gas’s Raman response would have, without building it.
The status and its reason are those of
raman_response()’s result at any temperature and wavelength, read from the curated choices alone: a propagation code can ask whether a gas’s response is validated or provisional, or whether it has none, without computing the lines. The collision partners are not checked: a mixture whose constituents lack broadening coefficients by one another (CO2 with N2) has a status here, andraman_response()refuses it.- Parameters:
- gasGas or str or mapping of str to float
As for
raman_response().
- Returns:
- tuple of (str, str) or None
The status (
validated,provisional, …) and its reason (empty forvalidated); None where no constituent is Raman-active, as for the noble gases, which have no Raman response at all.
- Raises:
- DataError
As
raman_response()does for a molecule without curated choices or a composition it refuses.
Examples
>>> raman_response_status("N2") ('validated', '') >>> raman_response_status("CO2")[0] 'provisional' >>> raman_response_status("Ar") is None True