solprop.physdata.nonlinear_data#

Third-order nonlinear data for gases: the Kerr coefficient at the carrier.

The nonlinearity is held as a per-particle quantity, exactly as the linear susceptibility is: gamma3() returns the third-order hyperpolarisability \(\gamma^{(3)}\) in m5/V2, and

\[\chi^{(3)} = \gamma^{(3)}\,\rho .\]

So the Kerr response of a gas is strictly linear in number density, and a pressure gradient scales it without re-deriving anything.

The nonlinear index follows from the susceptibility by

\[n_2 = \frac{3\chi^{(3)}}{4\varepsilon_0 c\,n_0^2},\]

with \(n_0\) the linear index at the carrier. n2_from_chi3() and chi3_from_n2() are that relation and its inverse, in the same convention as docs/dev/conventions.md section 4: an intensity \(I\) shifts the index by \(n_2 I\).

The coefficient is optmat’s (SEP 3, section 4.3): OptmatKerr adapts optmat’s fitted electronic hyperpolarizability, which is dispersive, and evaluates its Kerr process at the run’s carrier wavelength – one number per species and run, the carrier-kerr approximation, never a universal constant. The Taylor convention of the fit, \(p^{(3)} = \gamma_T E^3/6\), gives \(\gamma^{(3)} = \gamma_T/(6\varepsilon_0)\), which optmat’s public gamma3("kerr", wavelength) returns; optmat’s own gas n2, taken with \(n_0 = 1\), is never imported, so that n2_from_chi3() applies the exact \(n_0^2\). Air has no hyperpolarizability of its own: its populations each carry theirs.

SEP 6’s dispersive electronic policies take the whole fit instead: DispersiveKerrModel is optmat’s model of one gas in SI, with the per-leg bound and the band edges solprop derives from optmat’s support record, and ElectronicRegions classifies a run’s band against them (SEP 6, section 4.10).

Functions

carriers_agree(first, second)

Return whether two carrier wavelengths are the same, to rounding.

chi3(gas, *, pressure[, temperature, ...])

Return the third-order susceptibility of a gas at a carrier.

chi3_from_n2(n2_value, index)

Convert a nonlinear index to a third-order susceptibility.

gamma3(gas, *, wavelength)

Return a gas's per-particle Kerr coefficient at a carrier wavelength.

n2(gas, *, pressure[, temperature, ...])

Return the nonlinear index of a gas at a carrier.

n2_from_chi3(chi3_value, index)

Convert a third-order susceptibility to a nonlinear index.

selected_carrier(selection, wavelength)

Return the carrier a selection's Kerr coefficient is evaluated at.

Classes

CarrierKerr(material, model, digest, ...[, ...])

Record one gas's Kerr coefficient at the carrier, and what it leaves out.

DispersiveKerrModel(material, gamma3_static, ...)

Hold one gas's dispersive hyperpolarizability in SI, with its leg bound.

ElectronicRegions(material, polarisation, ...)

Classify a run's band against one gas's hyperpolarizability support.

OptmatKerr(gas)

Take a gas's electronic Kerr coefficient from optmat, at the carrier.

class solprop.physdata.nonlinear_data.CarrierKerr(material, model, digest, carrier_wavelength, gamma3, gamma3_thg, thg_ratio, thg_reason='')[source]#

Bases: object

Record one gas’s Kerr coefficient at the carrier, and what it leaves out.

The carrier-kerr approximation (SEP 3, section 4.3) freezes the dispersive electronic response at one wavelength and uses it for every process the cubic term drives, the third harmonic included. optmat’s distinct third-harmonic coefficient measures what that costs: the record keeps both, and their ratio where optmat evaluates the third harmonic within its fitted data.

Attributes:
materialstr

The gas.

modelstr

optmat’s model name, "hyperpolarizability/<gas>".

digeststr

Its digest.

carrier_wavelengthfloat

The vacuum carrier wavelength in m.

gamma3float

The Kerr coefficient there, m^5/V^2 per particle: what the run uses.

gamma3_thgfloat

optmat’s third-harmonic coefficient at the same fundamental, m^5/V^2.

thg_ratiofloat or None

gamma3_thg / gamma3; None where optmat’s third-harmonic evaluation leaves its fitted data, so that the ratio would only compare extrapolations.

thg_reasonstr

Empty for a reported ratio; otherwise optmat’s range warning.

carrier_wavelength: float#
digest: str#
gamma3: float#
gamma3_thg: float#
material: str#
model: str#
thg_ratio: float | None#
thg_reason: str = ''#
to_dict()[source]#

Return the record as plain values, for a run’s material manifest.

Returns:
dict

Every field.

class solprop.physdata.nonlinear_data.DispersiveKerrModel(material, gamma3_static, a, b, b_prime, leg_bound, band_edge_linear, band_edge_any, material_records)[source]#

Bases: object

Hold one gas’s dispersive hyperpolarizability in SI, with its leg bound.

optmat’s fit of the Taylor-convention second hyperpolarizability over a quartet of signed vacuum wavenumbers \(\nu_j = \omega_j/(2\pi c)\), output first and counted negative, so that the four sum to zero (SEP 6, section 4.5):

\[\gamma^{(3)} = \gamma^{(3)}_0\,[1 + a S_2 + b S_2^2 + b' S_4], \qquad S_2 = \sum_j \nu_j^2,\ S_4 = \sum_j \nu_j^4,\]

with \(\gamma^{(3)}_0 = \gamma_{0,\rm au}\,\mathrm{au}/(6\varepsilon_0)\) solprop’s per-particle coefficient in m^5/V^2 (section 4.2), and optmat’s A in 1e-10 cm^2 and B, B' in 1e-20 cm^4 taken to m^2 and m^4. Every leg enters through its bounded weight \(\tilde d(\nu) = \min(\nu^2, \nu_c^2)\), the rule of section 4.10, which changes no quartet within the fitted sums.

The leg bound and the band edges are derived from optmat’s support record of the model (optmat’s OEP 5): \(\nu_c = \sqrt{3 W_{2,\max}/4}\) (support.leg_max_in_w2) and \(\min(E_1, E_2/2)\) for one or for any polarisation (support.band_edge). solprop hard-codes none of them.

Attributes:
materialstr

The gas.

gamma3_staticfloat

\(\gamma^{(3)}_0\), the static per-particle coefficient, m^5/V^2.

afloat

The quadratic coefficient, m^2.

bfloat

The coefficient of \(S_2^2\), m^4.

b_primefloat

The coefficient of \(S_4\), m^4.

leg_boundfloat

\(\nu_c\), m^-1, positive.

band_edge_linearfloat or None

The band edge for one linear polarisation, m^-1; None where optmat’s record leaves it undefined (CO2, CH4, SF6, N2O and O3 at optmat 0.4).

band_edge_anyfloat or None

The band edge for any polarisation, m^-1, or None.

material_recordstuple of MaterialRecord

The records of optmat’s hyperpolarizability model and of its support record, in that order.

Raises:
MaterialError

If a coefficient is not finite, the static coefficient or the leg bound is not positive, or the Kerr diagonal is not positive up to the leg bound, where its fourth root would not be real.

Examples

>>> from solprop.physdata.nonlinear_data import OptmatKerr
>>> argon = OptmatKerr("Ar").dispersive_model()
>>> round(1e9 / argon.leg_bound, 1), round(1e9 / argon.band_edge(vector=False), 1)
(215.9, 189.4)
a: float#
b: float#
b_prime: float#
band_edge(*, vector)[source]#

Return the band edge a run takes, m^-1.

Parameters:
vectorbool

Whether the run carries two polarisation components, which takes the any-polarisation edge.

Returns:
float or None

The edge, or None where it is undefined.

band_edge_any: float | None#
band_edge_linear: float | None#
classmethod from_optmat(model)[source]#

Build the SI model of an optmat hyperpolarizability model.

Parameters:
modeloptmat.gas.nonlinear.HyperpolarizabilityModel

Any gas’s model, one solprop builds as a species or not.

Returns:
DispersiveKerrModel

The model in SI with its leg bound and band edges.

Raises:
MaterialError

If the model carries no four dispersion parameters or no support record, or its support record describes another model.

gamma3(legs)[source]#

Return the bounded per-particle coefficient of quartets, m^5/V^2.

Parameters:
legsnumpy.ndarray

Signed vacuum wavenumbers in m^-1, the last axis the quartet’s four legs; only their squares enter.

Returns:
numpy.ndarray

\(\gamma^{(3)}_0[1 + a S_2 + b S_2^2 + b' S_4]\) with every leg’s square bounded at \(\nu_c^2\).

gamma3_static: float#
kerr_ratio(weight)[source]#

Return the Kerr diagonal over its static value at a leg weight.

Parameters:
weightnumpy.ndarray

Leg weights \(\tilde d = \nu^2\), m^-2, any shape.

Returns:
numpy.ndarray

\(1 + 4 a \tilde d + (16 b + 4 b')\tilde d^2\), the diagonal \(S_2 = 4\tilde d\), \(S_4 = 4\tilde d^2\).

leg_bound: float#
leg_weight(omega)[source]#

Return the bounded leg weight at angular frequencies, m^-2.

Parameters:
omeganumpy.ndarray

Angular frequencies in rad/s, of either sign, any shape.

Returns:
numpy.ndarray

\(\min((\omega/2\pi c)^2, \nu_c^2)\).

material: str#
material_records: tuple[MaterialRecord, ...]#
property model_digest: str[source]#

Return the digest of optmat’s hyperpolarizability model.

Returns:
str

Sixty-four hexadecimal digits.

regions(omega_low, omega_high, *, vector)[source]#

Classify a band of angular frequencies (SEP 6, section 4.10).

Parameters:
omega_lowfloat

The lowest angular frequency of the band in rad/s, positive.

omega_highfloat

The highest, in rad/s, above omega_low.

vectorbool

Whether the run carries two polarisation components, which takes the any-polarisation band edge.

Returns:
ElectronicRegions

The supported, bounded and best-effort parts of the band.

Raises:
MaterialError

If the band is not a positive, finite, increasing pair.

Examples

>>> import math
>>> from solprop.constants import C
>>> from solprop.physdata.nonlinear_data import OptmatKerr
>>> helium = OptmatKerr("He").dispersive_model()
>>> omega = [2 * math.pi * C / wavelength for wavelength in (4e-6, 90e-9)]
>>> regions = helium.regions(*omega, vector=False)
>>> [round(x * 1e9, 1) for x in regions.bounded]
[120.3, 151.9]
>>> regions.best_effort is not None
True
property support_digest: str[source]#

Return the digest of optmat’s support record of the model.

Returns:
str

Sixty-four hexadecimal digits.

class solprop.physdata.nonlinear_data.ElectronicRegions(material, polarisation, band, leg_bound, band_edge, supported, bounded, best_effort)[source]#

Bases: object

Classify a run’s band against one gas’s hyperpolarizability support.

SEP 6, section 4.10, as qualified by its Phase 0 report. Every leg of a dispersive evaluator is held at the gas’s leg bound \(\nu_c\), and the run is never refused for the frequencies its field occupies; what is recorded is which part of the band falls in which region:

  • supported: every leg within \(\nu_c\) and inside the band edge, where the bound changes no quartet within the fitted sums;

  • bounded: beyond \(\nu_c\) and inside the band edge, where the legs are held at the bound – a bounded estimate, which can underestimate a response that rises below resonance but is not a certified bound on it;

  • best effort: beyond the band edge, or beyond \(\nu_c\) for a gas whose edge is undefined (no curated two-photon limit), where no off-resonant model is valid and the response is held at its value at the edge of the data.

Each region is one interval of vacuum wavelength, since the classification is monotonic in frequency.

Attributes:
materialstr

The gas.

polarisation{‘linear’, ‘any’}

Which band edge applied: one linear polarisation, or any (a vector run).

bandtuple of float

The classified band, shortest and longest vacuum wavelength in m.

leg_boundfloat

\(\nu_c\), m^-1.

band_edgefloat or None

The band edge that applied, m^-1; None where optmat’s record leaves it undefined.

supportedtuple of float or None

Shortest and longest wavelength in m of the supported part, or None.

boundedtuple of float or None

The same of the bounded part.

best_efforttuple of float or None

The same of the best-effort part.

band: tuple[float, float]#
band_edge: float | None#
best_effort: tuple[float, float] | None#
property beyond_support: bool[source]#

Return whether any part of the band is bounded or best effort.

Returns:
bool

True when a run’s legs reach beyond the gas’s leg bound or band edge.

bounded: tuple[float, float] | None#
describe()[source]#

Return a one-line account of the regions, for a run’s log.

Returns:
str

The gas, its band and each region’s wavelengths in nm.

leg_bound: float#
material: str#
polarisation: str#
supported: tuple[float, float] | None#
to_dict()[source]#

Return the classification as plain values, for a run’s manifest.

Returns:
dict

Every field; intervals as [shortest, longest] wavelengths in m.

class solprop.physdata.nonlinear_data.OptmatKerr(gas)[source]#

Bases: object

Take a gas’s electronic Kerr coefficient from optmat, at the carrier.

optmat fits each gas’s second hyperpolarizability \(\gamma_T(\omega)\) in the Taylor convention, \(p^{(3)} = \gamma_T E^3/6\), so that solprop’s per-particle coefficient in \(P^{(3)} = \varepsilon_0\gamma^{(3)}N E^3\) is

\[\gamma^{(3)}(\lambda_c) = \frac{\gamma_{T,\rm Kerr}(\lambda_c)} {6\varepsilon_0},\]

evaluated at the carrier \(\lambda_c\) (optmat’s public gamma3("kerr", wavelength)). The adapter holds the model, not a number: the carrier is the run’s, so one species serves runs at any carrier. No Raman amplitude is multiplied by it, and no local-field factor is added (SEP 3, section 4.3).

Attributes:
gasGas or str

The species; optmat has a model for every built-in gas but air, whose populations each carry their own.

Raises:
MaterialError

If optmat has no hyperpolarizability model of the gas.

Examples

>>> from solprop.physdata.nonlinear_data import OptmatKerr
>>> argon = OptmatKerr("Ar")
>>> f"{argon.gamma3(800e-9):.6e}"
'1.476195e-51'
at_carrier(wavelength)[source]#

Return the carrier record: the coefficient used and the THG discrepancy.

Parameters:
wavelengthfloat

Vacuum carrier wavelength in m, positive and finite.

Returns:
CarrierKerr

The Kerr coefficient at the carrier, optmat’s third-harmonic one at the same fundamental, and their ratio where the latter is within optmat’s fitted data.

Raises:
MaterialError

If the wavelength is not positive and finite.

Examples

>>> from solprop.physdata.nonlinear_data import OptmatKerr
>>> record = OptmatKerr("Ar").at_carrier(800e-9)
>>> f"{record.thg_ratio:.3f}"
'1.177'
dispersive_model()[source]#

Return the whole fit in SI, for a dispersive electronic policy (SEP 6).

Returns:
DispersiveKerrModel

The gas’s optmat model in SI, with its leg bound and band edges.

Raises:
MaterialError

If optmat’s model of the gas carries no dispersion parameters or support record.

Warns:
MaterialStatusWarning

Once per evaluation context, for a provisional or untested model or support record.

Examples

>>> from solprop.physdata.nonlinear_data import OptmatKerr
>>> f"{OptmatKerr('He').dispersive_model().gamma3_static:.6e}"
'5.064575e-53'
gamma3(wavelength)[source]#

Return the per-particle Kerr coefficient at a carrier wavelength.

Parameters:
wavelengthfloat

Vacuum carrier wavelength in m, positive and finite.

Returns:
float

gamma3 in m^5/V^2, such that chi3 = gamma3 * rho.

Raises:
MaterialError

If the wavelength is not positive and finite.

Warns:
MaterialRangeWarning

Once per evaluation context, outside the model’s fitted wavelengths (400 nm to 20 um for the built-in gases).

gas: Gas | str#
property key: Gas[source]#

Return the species as a registry member.

Returns:
Gas

The gas.

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

Return the record of the hyperpolarizability model.

Returns:
tuple of MaterialRecord

One record.

property model: Any[source]#

Return optmat’s hyperpolarizability model of the gas, resolved once.

Returns:
optmat.gas.nonlinear.HyperpolarizabilityModel

hyperpolarizability_models()[gas].

Raises:
MaterialError

If optmat has none for the gas.

solprop.physdata.nonlinear_data.carriers_agree(first, second)[source]#

Return whether two carrier wavelengths are the same, to rounding.

Parameters:
firstfloat

A vacuum wavelength in m.

secondfloat

Another, in m.

Returns:
bool

True when they differ by at most 1e-12 relative: a wavelength computed back from an angular frequency is the same carrier.

Examples

>>> from solprop.physdata.nonlinear_data import carriers_agree
>>> carriers_agree(800e-9, 800e-9 * (1 + 1e-15)), carriers_agree(800e-9, 1030e-9)
(True, False)
solprop.physdata.nonlinear_data.chi3(gas, *, pressure, temperature=293.15, selection=None, wavelength=None)[source]#

Return the third-order susceptibility of a gas at a carrier.

chi3 = gamma3 * rho: strictly linear in number density, with optmat’s Kerr coefficient at the carrier (OptmatKerr) and optmat’s equation of state.

Parameters:
gasGas or str

The species.

pressurefloat

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

temperaturefloat, optional

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

selectionMaterialSelection or None, optional

None is MaterialSelection(); its carrier_wavelength may name the carrier.

wavelengthfloat or None, optional

The carrier wavelength in m; it must agree with the selection’s carrier_wavelength when both are given, and one of the two is required.

Returns:
float

Third-order susceptibility in m^2/V^2.

Raises:
MaterialError

If the gas is unknown or has no Kerr coefficient (air), or there is no carrier.

Examples

>>> from solprop.physdata import Gas, chi3
>>> f"{chi3(Gas.Ar, pressure=1.0, wavelength=800e-9):.6e}"
'3.649772e-26'
solprop.physdata.nonlinear_data.chi3_from_n2(n2_value, index)[source]#

Convert a nonlinear index to a third-order susceptibility.

The inverse of n2_from_chi3().

Parameters:
n2_valuefloat

Nonlinear index in m^2/W.

indexfloat

Linear refractive index at the wavelength of interest, > 0.

Returns:
float

Third-order susceptibility in m^2/V^2.

Examples

>>> from solprop.physdata import chi3_from_n2, n2_from_chi3
>>> f"{chi3_from_n2(n2_from_chi3(3e-26, 1.0003), 1.0003):.12e}"
'3.000000000000e-26'
solprop.physdata.nonlinear_data.gamma3(gas, *, wavelength)[source]#

Return a gas’s per-particle Kerr coefficient at a carrier wavelength.

optmat’s electronic hyperpolarizability evaluated at the carrier (OptmatKerr); the coefficient is dispersive, so the carrier is required.

Parameters:
gasGas or str

A built-in gas other than air.

wavelengthfloat

Vacuum carrier wavelength in m, positive and finite.

Returns:
float

gamma3 in m^5/V^2, such that chi3 = gamma3 * rho.

Raises:
MaterialError

If the gas is unknown or is air, or the wavelength is not positive and finite.

Warns:
MaterialRangeWarning

Outside the model’s fitted wavelengths.

Examples

>>> from solprop.physdata import Gas, gamma3
>>> f"{gamma3(Gas.Ar, wavelength=800e-9):.6e}"
'1.476195e-51'
solprop.physdata.nonlinear_data.n2(gas, *, pressure, temperature=293.15, wavelength=None, selection=None)[source]#

Return the nonlinear index of a gas at a carrier.

The exact n2 = 3 chi3 / (4 eps0 c n0^2) with optmat’s Kerr coefficient, its density and its linear index n0, all at the carrier; optmat’s own gas n2, which takes n0 = 1, is not used.

Parameters:
gasGas or str

The species.

pressurefloat

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

temperaturefloat, optional

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

wavelengthfloat or None, optional

The carrier wavelength in m at which the dispersive Kerr coefficient and n0 are evaluated: it, or the selection’s carrier_wavelength, is required, and the two must agree when both are given.

selectionMaterialSelection or None, optional

None is MaterialSelection().

Returns:
float

Nonlinear index in m^2/W.

Raises:
MaterialError

If the gas is unknown or has no Kerr coefficient, or the carriers disagree or are both missing.

Warns:
MaterialRangeWarning

If the carrier lies outside the qualified range of the gas’s models.

Examples

Helium at 1 bar and 800 nm.

>>> from solprop.physdata import Gas, n2
>>> f"{n2(Gas.He, pressure=1.0, wavelength=800e-9):.3e}"
'3.635e-25'
solprop.physdata.nonlinear_data.n2_from_chi3(chi3_value, index)[source]#

Convert a third-order susceptibility to a nonlinear index.

n2 = 3 chi3 / (4 eps0 c n0^2).

Parameters:
chi3_valuefloat

Third-order susceptibility in m^2/V^2.

indexfloat

Linear refractive index at the wavelength of interest, > 0.

Returns:
float

Nonlinear index in m^2/W, in the convention where an intensity I shifts the refractive index by n2 * I.

Examples

>>> from solprop.physdata import n2_from_chi3
>>> f"{n2_from_chi3(3e-26, 1.0003):.6e}"
'8.471348e-24'
solprop.physdata.nonlinear_data.selected_carrier(selection, wavelength)[source]#

Return the carrier a selection’s Kerr coefficient is evaluated at.

Parameters:
selectionMaterialSelection

The selection; its carrier_wavelength may be None.

wavelengthfloat or None

The caller’s wavelength in m, or None.

Returns:
float

The one carrier in m both name.

Raises:
MaterialError

If neither names one, or the two disagree.

Examples

>>> from solprop.physdata import MaterialSelection
>>> from solprop.physdata.nonlinear_data import selected_carrier
>>> selected_carrier(MaterialSelection(carrier_wavelength=1030e-9), None)
1.03e-06