solprop.modes.base#

Derived quantities of any mode: beta, alpha, group delay, dispersion, ZDW.

Everything here is a function of a Mode, not a method on one, so a new mode family gets them for nothing. The conversions are the package’s (docs/dev/conventions.md section 2):

\[\beta = \frac{\omega}{c}\,\mathrm{Re}\,n_{\rm eff},\qquad \alpha = \frac{2\omega}{c}\,\mathrm{Im}\,n_{\rm eff},\]

so a positive imaginary part of the effective index is loss and alpha is a power loss coefficient per metre.

beta1() is the group delay per unit length that the co-moving frame rides on. It is analytic, straight from the mode’s dneff_domega: differencing beta with a seven-point stencil every stage would cost about seven index evaluations and leave 1e-10 of roughness that floors the whole cumulative operator at 1e-8 (docs/dev/stepper.md section 5).

beta2() is one step further. The second derivative of a gas susceptibility is not part of the material interface, so it is a Richardson-extrapolated central difference of the analytic beta1 – one derivative differenced instead of two, which is a very different thing from differencing beta itself, and reaches about 1e-10 relative rather than 1e-6. zdw() brackets its root.

Module Attributes

BETA2_RELATIVE_STEP

Relative frequency step of the Richardson difference behind beta2().

Functions

aeff(mode[, z])

Return the effective area of a mode.

alpha(mode, omega[, z])

Return the power loss coefficient of a mode.

beta(mode, omega[, z])

Return the propagation constant of a mode.

beta1(mode, omega[, z])

Return the group delay per unit length, analytically.

beta2(mode, omega[, z])

Return the group-velocity dispersion, by differencing the analytic beta1.

db_per_m(mode, omega[, z])

Return the loss of a mode in decibels per metre.

loss_length(mode, omega[, z])

Return the 1/e power loss length of a mode.

transmission(mode, omega, length[, start])

Return the power transmission of a length of waveguide.

zdw(mode[, z, wavelength_range])

Return the zero-dispersion wavelength of a mode.

solprop.modes.base.BETA2_RELATIVE_STEP: Final = 0.003#

Relative frequency step of the Richardson difference behind beta2().

Larger than a finite difference usually wants, and deliberately so. beta_1 is of order 1/c while its variation over the step is beta_2 h, which for a capillary is nine orders smaller, so the rounding error of the difference is eps beta_1 / h – it falls as the step grows. Truncation, (h^2/6) beta_4, rises. Measured against the closed-form second derivative of an evacuated capillary (tests/modes/test_base.py), the two cross near a relative step of 3e-3, where the result is good to 5e-9; at 1e-4 it is 1.6e-7 and at 1e-5 it is 2.3e-6.

That is three orders better than differencing beta itself, and it is enough: nothing in the solver consumes beta_2. The co-moving frame uses beta_1, which is analytic. Making this analytic too needs the second derivative of the gas susceptibility, which is not part of the material interface.

solprop.modes.base.aeff(mode, z=0.0)[source]#

Return the effective area of a mode.

A uniform accessor beside beta() and alpha(), so that code working over a mode set never has to mix method calls with function calls.

Parameters:
modeMode

The mode.

zfloat, optional

Position along the waveguide, m.

Returns:
float

A_eff in m^2.

Examples

>>> from solprop.modes import MarcatiliMode
>>> from solprop.modes.base import aeff
>>> f"{aeff(MarcatiliMode(75e-6, 'He', 1.0)):.6e}"
'8.421575e-09'
solprop.modes.base.alpha(mode, omega, z=0.0)[source]#

Return the power loss coefficient of a mode.

Parameters:
modeMode

The mode.

omeganumpy.ndarray

Angular frequencies in rad/s, shape (n_omega,).

zfloat, optional

Position along the waveguide, m.

Returns:
numpy.ndarray

alpha = 2 omega Im(n_eff) / c in 1/m, shape (n_omega,). Positive is loss, because Im n_eff > 0 is.

Examples

>>> import numpy as np
>>> from solprop.modes import MarcatiliMode
>>> from solprop.modes.base import alpha
>>> mode = MarcatiliMode(125e-6, "He", 1.0)
>>> f"{alpha(mode, np.array([2.356e15]))[0]:.6f}"
'0.141459'
solprop.modes.base.beta(mode, omega, z=0.0)[source]#

Return the propagation constant of a mode.

Parameters:
modeMode

The mode.

omeganumpy.ndarray

Angular frequencies in rad/s, shape (n_omega,).

zfloat, optional

Position along the waveguide, m.

Returns:
numpy.ndarray

beta = omega Re(n_eff) / c in 1/m, shape (n_omega,).

Examples

>>> import numpy as np
>>> from solprop.modes import MarcatiliMode
>>> from solprop.modes.base import beta
>>> mode = MarcatiliMode(125e-6, "He", 1.0)
>>> f"{beta(mode, np.array([2.356e15]))[0]:.6f}"
'7858997.479689'
solprop.modes.base.beta1(mode, omega, z=0.0)[source]#

Return the group delay per unit length, analytically.

Parameters:
modeMode

The mode.

omeganumpy.ndarray

Angular frequencies in rad/s, shape (n_omega,).

zfloat, optional

Position along the waveguide, m.

Returns:
numpy.ndarray

beta_1 = d(beta)/d(omega) = (Re n_eff + omega d(Re n_eff)/d(omega)) / c in s/m, shape (n_omega,), from the mode’s analytic derivative.

Examples

>>> import numpy as np
>>> from solprop.modes import MarcatiliMode
>>> from solprop.modes.base import beta1
>>> mode = MarcatiliMode(125e-6, "He", 1.0)
>>> f"{beta1(mode, np.array([2.356e15]))[0]:.10e}"
'3.3357582351e-09'
solprop.modes.base.beta2(mode, omega, z=0.0)[source]#

Return the group-velocity dispersion, by differencing the analytic beta1.

Departs from the analytic derivatives elsewhere in this module, and says so: the second derivative of a gas susceptibility is not part of the material interface, so this is a Richardson-extrapolated central difference of beta1() at a relative step of BETA2_RELATIVE_STEP. It reaches about 1e-10 relative, against the 1e-6 of differencing beta twice, and only the co-moving frame needs better – which uses beta1, not this.

Parameters:
modeMode

The mode.

omeganumpy.ndarray

Angular frequencies in rad/s, shape (n_omega,), > 0.

zfloat, optional

Position along the waveguide, m.

Returns:
numpy.ndarray

beta_2 = d^2(beta)/d(omega)^2 in s^2/m, shape (n_omega,).

Raises:
ModeError

If any frequency is not finite or not positive, since the step is relative.

Examples

>>> import numpy as np
>>> from solprop.modes import MarcatiliMode
>>> from solprop.modes.base import beta2
>>> mode = MarcatiliMode(125e-6, "He", 1.0)
>>> f"{beta2(mode, np.array([2.356e15]))[0]:.6e}"
'-7.491419e-30'
solprop.modes.base.db_per_m(mode, omega, z=0.0)[source]#

Return the loss of a mode in decibels per metre.

Parameters:
modeMode

The mode.

omeganumpy.ndarray

Angular frequencies in rad/s, shape (n_omega,).

zfloat, optional

Position along the waveguide, m.

Returns:
numpy.ndarray

10 alpha / ln(10) in dB/m, shape (n_omega,).

Examples

>>> import numpy as np
>>> from solprop.modes import MarcatiliMode
>>> from solprop.modes.base import db_per_m
>>> mode = MarcatiliMode(125e-6, "He", 1.0)
>>> f"{db_per_m(mode, np.array([2.356e15]))[0]:.6f}"
'0.614348'
solprop.modes.base.loss_length(mode, omega, z=0.0)[source]#

Return the 1/e power loss length of a mode.

Parameters:
modeMode

The mode.

omeganumpy.ndarray

Angular frequencies in rad/s, shape (n_omega,).

zfloat, optional

Position along the waveguide, m.

Returns:
numpy.ndarray

1 / alpha in m, shape (n_omega,); infinite where the mode is lossless.

Examples

>>> import numpy as np
>>> from solprop.modes import MarcatiliMode
>>> from solprop.modes.base import loss_length
>>> mode = MarcatiliMode(125e-6, "He", 1.0)
>>> f"{loss_length(mode, np.array([2.356e15]))[0]:.4f}"
'7.0692'
solprop.modes.base.transmission(mode, omega, length, start=0.0)[source]#

Return the power transmission of a length of waveguide.

Parameters:
modeMode

The mode.

omeganumpy.ndarray

Angular frequencies in rad/s, shape (n_omega,).

lengthfloat

Length propagated in m, >= 0.

startfloat, optional

Position the length starts at, m.

Returns:
numpy.ndarray

exp(-int alpha dz) over [start, start + length], shape (n_omega,). The integral is taken numerically because a taper or a gradient makes alpha a function of position; for a uniform waveguide it is exactly exp(-alpha L).

Raises:
ModeError

If length is negative.

Examples

>>> import numpy as np
>>> from solprop.modes import MarcatiliMode
>>> from solprop.modes.base import transmission
>>> mode = MarcatiliMode(125e-6, "He", 1.0)
>>> f"{transmission(mode, np.array([2.356e15]), 1.0)[0]:.6f}"
'0.868091'
solprop.modes.base.zdw(mode, z=0.0, *, wavelength_range=(1e-07, 3e-06))[source]#

Return the zero-dispersion wavelength of a mode.

Parameters:
modeMode

The mode.

zfloat, optional

Position along the waveguide, m.

wavelength_rangetuple of float, optional

Shortest and longest wavelength in m to bracket the root in. Defaults to 100 nm to 3 um. A bracket wider than the validity range of a material model behind the mode makes that model warn, once per evaluation: the ends are only probes, but they are still extrapolation.

Returns:
float

The wavelength in m where beta_2 vanishes.

Raises:
ModeError

If the range is not ordered and positive, or if beta_2 does not change sign across it: a mode with no zero-dispersion point in the window is a fact the caller has to see, not a missing value to propagate.

Examples

>>> from solprop.modes import MarcatiliMode
>>> from solprop.modes.base import zdw
>>> mode = MarcatiliMode(125e-6, "He", 0.4, model="reduced", cladding=1.45)
>>> f"{zdw(mode, wavelength_range=(150e-9, 900e-9)) * 1e9:.1f}"
'376.7'