solprop.nonlinear.optmat_raman#

Adapt optmat’s gas Raman responses onto solprop’s existing Raman terms (SEP 3).

optmat gives a molecular gas’s, or a gas mixture’s, delayed response as a sum of lines,

\[h(\tau; N) = \Theta(\tau) \sum_k A_k \sin(\Omega_k(N)\tau)\, e^{-\Gamma_k(N)\tau}, \qquad \Gamma_k = \pi\, \Delta\nu_k(N),\]

for one composition, one temperature and one pump wavelength, with the amplitudes in m^5 V^-2 s^-1 per molecule of the whole composition: a constituent’s lines carry its mole fraction, so a mixture’s response is driven by its total density. That is solprop’s own convention (solprop.nonlinear.raman), so no unit changes here.

Lines that share a width law and a shift form one damping group (optmat’s groups()). Each group and tensor family becomes one existing RamanTensorTerm: an undamped row u(t) = sum_k A_k sin(Omega_k t) (RamanGroupLines) and the group’s decay rate as a function of density (GroupDephasing), which the translation layer composes with the density path. The isotropic amplitudes become an isotropic term and the anisotropic ones a rank-two term, so a copolarised probe sees a + b and a cross-polarised one a (Hellwarth’s functions). The mapping is exact for every gas whose lines do not shift, at any density path. H2’s Q lines shift with density: at a constant density their row is built at that density, which is exact; along a gradient they are refused unless the selection names the approximation raman_shift="reference-density", whose phase bound is recorded.

A mixture’s response is built once, for the composition of all its labelled populations, and is driven, and damped, by the summed density of those labels (OptmatRamanResponse.labels). optmat builds a mixture’s response only from the collision partners whose broadening it has curated, and a mixture with any other partner is refused here by name, naming the pair (SEP 3, D16). The derivation and the limits are on the manual page docs/guide/media/raman-data.md, “Raman responses from optmat”.

Module Attributes

COMPOSITION_TOLERANCE

Largest relative change of a constituent's mole fraction along the run.

Functions

gas_raman(gas[, options, thg, density, ...])

Build a pure gas's Raman response from optmat, not bound to a mixture.

mixture_raman(mixture, raman, *, selection)

Build the Raman response of a mixture's whole composition from optmat.

raman_status(gas)

Return the status of optmat's curated Raman response of a built-in gas.

Classes

GroupDephasing(linewidth, doppler_width, group)

The decay of one optmat damping group's coherence with the total density.

OptmatRamanResponse([models, fraction, thg, ...])

A Raman response adapted from optmat, driven by its composition's density.

RamanAssembly(material, composition, ...[, ...])

Record how an optmat Raman response was mapped onto solprop's terms.

RamanGroupLines(frequencies, amplitudes, ...)

The undamped row of one damping group and tensor family.

RamanShift(policy, density, frequency_error)

How a density-dependent line shift is held in a response's rows.

solprop.nonlinear.optmat_raman.COMPOSITION_TOLERANCE: Final = 0.01#

Largest relative change of a constituent’s mole fraction along the run.

A response is built for one composition. Populations whose densities are proportional keep it; the partial-pressure rule (SEP 3, D17) makes them proportional only to the gases’ differing non-ideality, which moves argon’s fraction of dry air by 1.6 x 10^-3 of itself between 10 bar and vacuum at 293.15 K (nitrogen’s by 8 x 10^-5). Beyond this tolerance the composition varies along z and the response is refused by name rather than built at a composition the run does not have.

class solprop.nonlinear.optmat_raman.GroupDephasing(linewidth, doppler_width, group)[source]#

Bases: Dephasing

The decay of one optmat damping group’s coherence with the total density.

The rate is optmat’s group law, \(\Gamma(N) = \gamma_c + \min(\gamma_d / N, \gamma_D) + \gamma_\beta N\), evaluated by optmat itself (RamanGroup.decay_rate). It is defined for every \(N \ge 0\): the Dicke term is capped at the forward-scattering Doppler width, so at vacuum it is \(\pi(c + \Delta\nu_D)\) with a Dicke term and \(\pi c\) without one – zero for nitrogen’s and oxygen’s lines, whose widths are pure broadening. No floor or clamp is applied here.

Attributes:
linewidthLinewidth

The group’s width law c + d/n + beta n in Hz as optmat curates it, without the Doppler cap, for the record.

doppler_widthfloat

The forward-scattering Doppler width in Hz that caps the Dicke term; zero without one.

groupoptmat.gas.raman_response.RamanGroup

optmat’s group, which evaluates the law. A live handle, not recorded.

Examples

>>> from optmat.gas.raman_response import raman_response
>>> from solprop.nonlinear.optmat_raman import GroupDephasing
>>> group = raman_response("H2", 293.15).groups()[0]
>>> dephasing = GroupDephasing.of(group, raman_response("H2", 293.15))
>>> dephasing.decay_rate(0.0) == float(group.decay_rate(0.0)) > 0.0
True
decay_rate(density)[source]#

Return the group’s coherence decay rate at a total number density.

Parameters:
densityfloat

Total number density of the composition in m^-3, finite and >= 0.

Returns:
float

Gamma = pi FWHM in s^-1, finite at every density, vacuum included.

Raises:
MaterialError

If the density is negative or not finite.

doppler_width: float#
group: Any#
classmethod of(group, response)[source]#

Return the dephasing of one group of an optmat response.

Parameters:
groupoptmat.gas.raman_response.RamanGroup

The group.

responseoptmat.gas.raman_response.GasRamanResponse

The response it belongs to, whose line arrays carry the law in Hz.

Returns:
GroupDephasing

The group’s decay law.

class solprop.nonlinear.optmat_raman.OptmatRamanResponse(models=None, fraction=None, thg=True, species=None, *, tensor_terms=None, labels=(), assembly=None, source=None)[source]#

Bases: RamanResponse

A Raman response adapted from optmat, driven by its composition’s density.

A RamanResponse whose terms are optmat’s damping groups, one per group and tensor family, and which records how it was built. Build one with mixture_raman() or gas_raman().

Parameters:
modelsNone, optional

Refused: the terms are declared by tensor_terms.

fractionNone, optional

Refused: the response is absolute.

thgbool, optional

Whether a field grid keeps the third harmonic. Default True.

speciesstr or None, optional

The leaf’s label.

tensor_termstuple of RamanTensorTerm

The group rows.

labelstuple of str, optional

The composition’s labels.

assemblyRamanAssembly or None, optional

How the response was mapped onto terms.

sourceoptmat.gas.raman_response.GasRamanResponse or None, optional

The response optmat built.

Attributes:
tensor_termstuple of RamanTensorTerm

Isotropic and rank-two terms, one per damping group and tensor family.

fractionNone

Always None: the response is absolute.

thgbool

Whether a field grid keeps the third harmonic of the drive.

speciesstr or None

The label the compiled leaf carries: the first Raman-active label of the composition, or None for a single population.

labelstuple of str

The labelled populations of the composition, whose summed density drives the response and sets its decay rates; empty for a response not bound to a mixture, which takes the run’s one density.

assemblyRamanAssembly

How the response was mapped onto terms.

sourceoptmat.gas.raman_response.GasRamanResponse

optmat’s response. A live handle, recorded through material_records.

Raises:
MaterialError

If a fraction is given, or a term’s model is not an optmat group row.

assembly: RamanAssembly | None = None#
labels: tuple[str, ...] = ()#
property material_records: tuple[MaterialRecord, ...][source]#

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

Returns:
tuple of MaterialRecord

The response’s identity, or none for a response without its source.

source: Any = None#
class solprop.nonlinear.optmat_raman.RamanAssembly(material, composition, temperature, wavelength, groups, terms, shift=None, densities=None, composition_spread=0.0)[source]#

Bases: object

Record how an optmat Raman response was mapped onto solprop’s terms.

Attributes:
materialstr

What optmat’s response is of: a gas, or the species of a mixture joined by +.

compositiontuple of tuple of (str, float)

The species and the mole fractions the response was built for.

temperaturefloat

The reference temperature in K: the populations and the widths’ temperature laws.

wavelengthfloat

The pump wavelength in m at which the vibrational strengths are dispersed.

groupsint

The damping groups the response keeps.

termsint

Its rows after the tensor split, all-zero rows dropped.

shiftRamanShift or None

How a line shift is held, or None when no kept group shifts.

densitiestuple of float or None

The smallest and largest non-zero total densities in m^-3 declared to the response’s range check; the smallest is 0.0 when the path falls to vacuum, whose every density above zero is then declared. None when none was declared.

composition_spreadfloat

The largest relative departure of a constituent’s mole fraction from the composition along the run; zero for a pure gas.

composition: tuple[tuple[str, float], ...]#
composition_spread: float = 0.0#
densities: tuple[float, float] | None = None#
groups: int#
material: str#
shift: RamanShift | None = None#
temperature: float#
terms: int#
to_dict()[source]#

Return the record as plain values.

Returns:
dict

Every field, the shift as its own record.

wavelength: float#
class solprop.nonlinear.optmat_raman.RamanGroupLines(frequencies, amplitudes, dephasing, line_ids, share)[source]#

Bases: object

The undamped row of one damping group and tensor family.

u(t) = sum_k A_k sin(Omega_k t) for t > 0 and zero otherwise, in m^5 V^-2 s^-1: a RamanModel whose dephasing is the group’s (GroupDephasing), which the translation layer applies per density. The translation’s causal taper multiplies the row; nothing here does.

Attributes:
frequenciesnumpy.ndarray

The lines’ angular frequencies in rad/s, shape (n_lines,): at zero density for an unshifted group, and at the density the shift is held at for a shifted one (RamanShift).

amplitudesnumpy.ndarray

The lines’ amplitudes of this tensor family in m^5 V^-2 s^-1, per molecule of the composition, shape (n_lines,).

dephasingGroupDephasing

The group’s decay law.

line_idstuple of str

optmat’s stable identifier of each line, species/band/branch/J.

share{‘isotropic’, ‘anisotropic’}

Which of the lines’ two amplitude shares the row carries.

Raises:
MaterialError

If the arrays disagree in length, a frequency is not positive and finite, or an amplitude is not finite.

amplitudes: Float[ndarray, 'n_amplitudes']#
dephasing: GroupDephasing#
frequencies: Float[ndarray, 'n_lines']#
line_ids: tuple[str, ...]#
share: Literal['isotropic', 'anisotropic']#
undamped(t)[source]#

Return the undamped row on a time axis, in m^5 V^-2 s^-1.

Parameters:
tnumpy.ndarray

Delays in s, shape (n_to,).

Returns:
numpy.ndarray

sum_k A_k sin(Omega_k t) where t > 0 and zero elsewhere, shape (n_to,).

class solprop.nonlinear.optmat_raman.RamanShift(policy, density, frequency_error)[source]#

Bases: object

How a density-dependent line shift is held in a response’s rows.

Attributes:
policy{‘constant-density’, ‘reference-density’}

constant-density: the density is constant, and the rows are built at it, exactly. reference-density: the selection’s named approximation, the rows held at the density-weighted reference density along a gradient.

densityfloat

The total number density in m^-3 the shifted rows are built at.

frequency_errorfloat

\(\max_g |\sigma_g| \max_z |N(z) - N_\mathrm{ref}|\) in rad/s, the largest error of a line’s angular frequency over the run; zero at constant density. Over a window \(T_w\) the phase error is bounded by its product with \(T_w\) (phase_bound()).

density: float#
frequency_error: float#
phase_bound(window)[source]#

Return the largest phase error of a shifted line over a time window.

Parameters:
windowfloat

The time window in s, positive.

Returns:
float

frequency_error * window in rad.

Examples

>>> from solprop.nonlinear.optmat_raman import RamanShift
>>> RamanShift("reference-density", 1e25, 2e9).phase_bound(1e-12)
0.002
policy: Literal['constant-density', 'reference-density']#
to_dict()[source]#

Return the record as plain values.

Returns:
dict

policy, density_m-3 and frequency_error_rad_s.

solprop.nonlinear.optmat_raman.gas_raman(gas, options=None, *, selection, thg=True, density=None, wavelength=None)[source]#

Build a pure gas’s Raman response from optmat, not bound to a mixture.

Parameters:
gasstr

The species, a molecule optmat curates Raman lines for.

optionsMolecularOptions or None, optional

The term flags and the temperature in K; None keeps every term at solprop.constants.ROOM_TEMPERATURE. min_j and max_j must keep their defaults: optmat keeps every level above its population floor.

selectionMaterialSelection

The selection: raman and the carrier.

thgbool, optional

Whether a field grid keeps the third harmonic. Default True.

densityfloat or None, optional

The constant number density in m^-3 of the fill, > 0: declared to the response’s range check, and the density H2’s shifted lines are built at. None declares none, and refuses a gas whose lines shift.

wavelengthfloat or None, optional

The run’s reference wavelength in m, used when the selection names no carrier.

Returns:
OptmatRamanResponse

The response, taking the run’s one density.

Raises:
MaterialError

If optmat has no Raman lines for the gas, the options cannot be honoured, the lines shift and no density is given, or the carrier is unknown.

Examples

>>> from solprop.nonlinear.optmat_raman import gas_raman
>>> from solprop.physdata.selection import MaterialSelection
>>> response = gas_raman("N2", selection=MaterialSelection(carrier_wavelength=8e-7))
>>> response.assembly.groups, response.assembly.terms
(43, 72)
solprop.nonlinear.optmat_raman.mixture_raman(mixture, raman, *, selection, thg=True, wavelength=None, length=None, explicit=False)[source]#

Build the Raman response of a mixture’s whole composition from optmat.

Every labelled population is a member of the composition, Raman-active or not: the response is optmat’s for those mole fractions, at the reference temperature of solprop.nonlinear.mixtures.raman_temperature() (over the summed density), dispersed to the carrier, and driven and damped by the summed density of the labels. The selected labels choose whose lines are kept, and their MolecularOptions which bands.

Parameters:
mixtureMixture

The fill; every population optmat-resolved.

ramanbool, sequence of str or Mapping

True keeps every Raman-active species; a sequence or mapping selects labels, and a mapping’s options select bands.

selectionMaterialSelection

The selection: raman ("recommended", "provisional" or an optmat response object), raman_shift and the carrier.

thgbool, optional

Whether a field grid keeps the third harmonic. Default True.

wavelengthfloat or None, optional

The run’s reference wavelength in m, used when the selection names no carrier.

lengthfloat or None, optional

The run’s length in m, over which the composition, the reference temperature and density, and the densities checked are taken; None takes the densities’ own span.

explicitbool, optional

Whether raman named labels, so that one without a response is refused.

Returns:
OptmatRamanResponse or None

The response, or None when no selected population is a molecule, or the fill is vacuum along the whole run.

Raises:
MaterialError

If a population is not optmat-resolved beside a Raman-active one, optmat has no broadening for a pair of constituents (SEP 3, D16), the composition varies along z, a provisional response is not admitted, a shift is not admitted on a gradient, or the options cannot be honoured.

Warns:
MaterialRangeWarning

Once per run each, outside the response’s temperature or density range.

MaterialStatusWarning

Once per run, for an admitted provisional response.

RamanTemperatureWarning

For a temperature path held at its density-weighted reference.

Examples

>>> from solprop.nonlinear.optmat_raman import mixture_raman
>>> from solprop.physdata import Mixture
>>> from solprop.physdata.selection import MaterialSelection
>>> selection = MaterialSelection(carrier_wavelength=800e-9)
>>> air = Mixture.air(1.0, selection=selection)
>>> response = mixture_raman(air, True, selection=selection)
>>> response.labels, response.assembly.groups
(('N2', 'O2', 'Ar'), 82)
solprop.nonlinear.optmat_raman.raman_status(gas)[source]#

Return the status of optmat’s curated Raman response of a built-in gas.

The status is a property of the curated data, not of the state: it is read from optmat’s curated choices (raman_response_status) without building the lines. The capillary driver’s raman=None takes it to decide whether a fill carries a Raman response by default.

Parameters:
gasstr

A built-in gas’s name.

Returns:
str

"atom" for a species optmat knows as an atom (no Raman-active mode); optmat’s status of the response otherwise ("validated", or "provisional" for N2O’s bands, SEP 3, D13); "none" for a molecule optmat has no response of.

Examples

>>> from solprop.nonlinear.optmat_raman import raman_status
>>> raman_status("N2"), raman_status("N2O"), raman_status("He")
('validated', 'provisional', 'atom')