solprop.spatial_absorber#

The transverse boundary of a free-space run: its specification and its design.

SEP 40. A free-space run (radial, Cartesian, line or planar) has a wall or a wrap at its grid’s outer size. By default it carries no real-space collar: a transverse-wavenumber safety ramp removes what reaches the grid’s outer wavenumbers, and the boundary monitor (solprop.monitor) watches the edge. A SpatialAbsorber asks for a real-space collar outside the requested aperture as well, for fast content the run declares; translation resolves it into a SpatialAbsorberDesign.

The design is a complex absorbing potential [Kosloff and Kosloff 1986; Muga et al. 2004]. For one frequency, with \(k_z(\kappa) = \sqrt{k^2 - \kappa^2}\) and a collar of power rate \(\alpha(x) = \alpha_0 P(x/w)\) over a width \(w\), a component of transverse wavenumber \(\kappa\) crossing the collar and coming back is attenuated in field by the local WKB integral with the exact symbol (SEP 40 section 4.2),

\[\Gamma(\kappa; \alpha_0) = 2 w \int_0^1 \left|\operatorname{Im} q\right| ds, \qquad q^2 = \kappa^2 + i k_z \alpha_0 P(s) + \alpha_0^2 P(s)^2 / 4,\]

which the phase-0 calibration on the discrete radial, Fourier and slab operators matched to between -1.3 % and +0.04 %. The strength \(\alpha_0\) is its root at the protected band’s edge \(\kappa_p\), \(\Gamma = \Gamma_\min\), over every frequency of the requested band; the run length does not enter it. Below the reflection threshold \(\kappa_\mathrm{lo}\) the collar reflects rather than absorbs (section 4.3), with the measured law \(\kappa_\mathrm{lo} w = 7.36\,(k_\max \alpha_0 w^2)^{0.182}\) for the Planck profile \(P = 1 - W\).

alpha is a power coefficient and attenuations are field nepers, as in solprop.boundaries (docs/dev/conventions.md section 2).

The manual page is docs/guide/free-space/model.md.

Module Attributes

DEFAULT_ATTENUATION

The collar's returned attenuation at the protected band's edge, field nepers.

THRESHOLD_LAW

(A, gamma) of the threshold kappa_lo w = A (k_max alpha0 w^2)^gamma.

PROFILE_FLOOR

The smallest kappa_p w at which the Planck collar's reflection at its band edge falls below 2e-4; a narrower design is refused (SEP 40 section 4.3).

LAUNCH_BAND_FACTOR

c_b: the launch-derived band's edge in units of the launch's rms wavenumber (SEP 40 Q5, measured in phase 0).

PROTECTED_CUTOFF

A protected band reaching beyond this fraction of k at any requested frequency is refused: near cutoff the ray picture fails and the collar's flux gain is unbounded.

RAMP_MARGIN

Nepers added to the safety ramp's attenuation, measured: inside the ramp the collar's own wavenumber broadening defeats the additive estimate by up to 1.7 Np.

RAMP_K_FLOOR

The floor on k_s as a fraction of k: k_z at 0.9 k, the protected band's limit, so that the ramp's strength stays finite as its band nears cutoff.

CROSSING_ADAPTIVE

c_x of the adaptive dz_max clamp: a step crosses at most half the collar.

CROSSING_FIXED

c_x a fixed in-stage schedule is checked at, measured under Runge-Kutta.

DAMPING_BOUND

The largest alpha0 h / 2 at which a fixed in-stage schedule damps accurately.

Functions

collar_attenuation(kappa, k, alpha0, width)

Return the field attenuation of a collar's round trip, by local WKB.

collar_profile(s)

Return the collar's profile P = 1 - W on its normalised depth.

collar_strength(kappa_p, k, width[, attenuation])

Return the collar's peak rate that attenuates the protected band's edge enough.

crossing_limit(kappa_p, k, width[, factor])

Return the longest step that cannot carry the protected band across the collar.

reflection_threshold(alpha0, width, k)

Return the wavenumber above which the collar reflects less than 2e-4.

rms_wavenumber(power, wavenumber)

Return an energy-weighted rms transverse wavenumber.

safety_ramp_strength(kappa_half, k, length)

Return the safety ramp's peak rate on one axis.

Classes

SpatialAbsorber([kappa, angle, ...])

The transverse boundary a free-space run asks for (SEP 40 section 5.2).

SpatialAbsorberDesign(specification, ...[, ...])

A transverse boundary resolved at translation (SEP 40 section 5.2).

solprop.spatial_absorber.CROSSING_ADAPTIVE: Final = 0.5#

c_x of the adaptive dz_max clamp: a step crosses at most half the collar.

solprop.spatial_absorber.CROSSING_FIXED: Final = 0.15#

c_x a fixed in-stage schedule is checked at, measured under Runge-Kutta.

solprop.spatial_absorber.DAMPING_BOUND: Final = 1.3#

The largest alpha0 h / 2 at which a fixed in-stage schedule damps accurately.

solprop.spatial_absorber.DEFAULT_ATTENUATION: Final = 8.517193191416238#

The collar’s returned attenuation at the protected band’s edge, field nepers.

log(5e3): a returned field below 2e-4 of the incident, one of the four equal shares of the 1e-3 aperture budget (SEP 40 sections 4.8 and 7.1).

solprop.spatial_absorber.LAUNCH_BAND_FACTOR: Final = 3.0#

c_b: the launch-derived band’s edge in units of the launch’s rms wavenumber (SEP 40 Q5, measured in phase 0).

solprop.spatial_absorber.PROFILE_FLOOR: Final = 22.3#

The smallest kappa_p w at which the Planck collar’s reflection at its band edge falls below 2e-4; a narrower design is refused (SEP 40 section 4.3).

solprop.spatial_absorber.PROTECTED_CUTOFF: Final = 0.9#

A protected band reaching beyond this fraction of k at any requested frequency is refused: near cutoff the ray picture fails and the collar’s flux gain is unbounded.

solprop.spatial_absorber.RAMP_K_FLOOR: Final = 0.44#

The floor on k_s as a fraction of k: k_z at 0.9 k, the protected band’s limit, so that the ramp’s strength stays finite as its band nears cutoff.

solprop.spatial_absorber.RAMP_MARGIN: Final = 2.0#

Nepers added to the safety ramp’s attenuation, measured: inside the ramp the collar’s own wavenumber broadening defeats the additive estimate by up to 1.7 Np.

class solprop.spatial_absorber.SpatialAbsorber(kappa=None, angle=None, discard_above=None, attenuation=8.517193191416238, safety_fraction=0.1, real_space=True)[source]#

Bases: object

The transverse boundary a free-space run asks for (SEP 40 section 5.2).

A specification, resolved at translation against the grid, the medium and the launch into a SpatialAbsorberDesign. By default (real_space=True) it asks for a real-space collar in the room the grid has outside its requested size (collar_fraction), as well as the safety ramp; real_space=False asks for the ramp alone, which is how a run with no spatial_absorber is resolved.

Attributes:
kappafloat or None

The protected band’s edge, |kappa| <= kappa, rad/m.

anglefloat or None

The protected band as an angle in vacuum behind a planar exit face, sin(theta) = c kappa / omega (SEP 23’s convention), rad, in (0, pi/2): a band that scales with frequency. Neither kappa nor angle takes the band from the launch, c_b sqrt(2) kappa_rms per axis (LAUNCH_BAND_FACTOR).

discard_abovefloat or None

A wavenumber above which the user declares no physical light, rad/m: the safety ramp then starts there. Never lowered by default.

attenuationfloat

Gamma_min, the collar’s returned attenuation at the band’s edge, field nepers. Default log(5e3).

safety_fractionfloat

The ramp’s width as a fraction of each axis’s largest wavenumber, in (0, 1). Default 0.1.

real_spacebool

Whether a real-space collar is asked for. Default True.

Raises:
GridError

If both kappa and angle are given, a value is out of range, or a band is declared with no collar to protect it.

Examples

>>> from solprop.spatial_absorber import SpatialAbsorber
>>> SpatialAbsorber(angle=25e-3).angle
0.025
>>> SpatialAbsorber(real_space=False, safety_fraction=0.2).real_space
False
angle: float | None = None#
attenuation: float = 8.517193191416238#
property band_source: Literal['kappa', 'angle', 'launch'][source]#

Return where the protected band comes from, in order of precedence.

Returns:
str

"kappa", "angle" or "launch".

discard_above: float | None = None#
kappa: float | None = None#
real_space: bool = True#
safety_fraction: float = 0.1#
class solprop.spatial_absorber.SpatialAbsorberDesign(specification, ramp_rate, ramp_strength, kappa_s, collar_rate=None, alpha0=0.0, width=(), kappa_p=(), kappa_lo=0.0, crossing_step=inf, unguarded_above=inf, check=<factory>, notes=<factory>)[source]#

Bases: object

A transverse boundary resolved at translation (SEP 40 section 5.2).

Immutable, and part of the translated problem: derived runs share it (solprop.driver.simulation.Simulation.with_input()), and the design check that belongs to a launch is rerun for a new one. Every derived value is recorded in the run’s output beside the specification, not hashed with it.

Attributes:
specificationSpatialAbsorber

What the run asked for.

ramp_ratenumpy.ndarray

The safety ramp’s power rate on the spectral columns, 1/m.

ramp_strengthfloat

Its peak, 1/m, the larger of the axes’.

kappa_stuple of float

Where the ramp starts, per axis (one radially), rad/m.

collar_ratenumpy.ndarray or None

The real-space collar’s power rate on the nodes, 1/m; None without one.

alpha0float

The collar’s peak rate, 1/m; zero without one.

widthtuple of float

The collar’s width per face’s axis, m; empty without one.

kappa_ptuple of float

The protected band’s edge per axis at the reddest requested frequency, rad/m; empty without a collar.

kappa_lofloat

The reflection threshold of the strongest face, rad/m; zero without a collar.

crossing_stepfloat

The adaptive crossing limit, m; infinite without a collar.

unguarded_abovefloat

The wavenumber above which content could in principle cross within a clamped step, (0.75 / c_x) kappa_p, rad/m; infinite without a collar.

checkdict

The design check’s record (SEP 40 section 4.4): its status, the launch’s return, the bare wall’s at the same size, the frequency nodes and its wall time; empty until it has run, and without a collar.

notesdict

Further derived values for the record: the intermediate band’s report, the reflected QDHT columns, the launch’s share in the ramp’s band.

alpha0: float = 0.0#
check: dict[str, object]#
collar_rate: Float[ndarray, 'n_nodes'] | None = None#
crossing_step: float = inf#
property has_collar: bool[source]#

Return whether the design carries a real-space collar.

Returns:
bool

True when collar_rate is set.

kappa_lo: float = 0.0#
kappa_p: tuple[float, ...] = ()#
kappa_s: tuple[float, ...]#
notes: dict[str, object]#
ramp_rate: Float[ndarray, 'n_columns']#
ramp_strength: float#
record()[source]#

Return the design’s derived values for the run’s record.

Returns:
dict

JSON-ready scalars and tuples: the ramp’s start per axis and strength, and with a collar its peak rate, widths, band edges, threshold, crossing step and unguarded band, then the design check’s record and the notes. The rates on the nodes and columns are in the operator’s own record.

specification: SpatialAbsorber#
unguarded_above: float = inf#
width: tuple[float, ...] = ()#
solprop.spatial_absorber.THRESHOLD_LAW: Final = (7.36, 0.182)#

(A, gamma) of the threshold kappa_lo w = A (k_max alpha0 w^2)^gamma.

Measured on the discrete operators for the Planck profile, for the wavenumber above which |r| <= 2e-4 (SEP 40 section 4.3, Part II). The exponent is the Born onset law’s, not the simple estimate’s one third.

solprop.spatial_absorber.collar_attenuation(kappa, k, alpha0, width)[source]#

Return the field attenuation of a collar’s round trip, by local WKB.

2 w int_0^1 |Im q| ds with the exact symbol q^2 = kappa^2 + i k_z alpha0 P + (alpha0 P)^2 / 4 and k_z taken at kappa (SEP 40 section 4.2). It counts the way out and the way back, through a radial collar to its wall or a periodic one to its image.

Parameters:
kappanumpy.ndarray or float

Transverse wavenumbers normal to the collar, rad/m.

knumpy.ndarray or float

The medium’s wavenumbers n omega / c at the same points, rad/m.

alpha0float

The collar’s peak power rate, 1/m.

widthfloat

Its width, m.

Returns:
numpy.ndarray

Field nepers, kappa and k broadcast together; zero where kappa >= k, which the design refuses before it gets here.

Examples

>>> from solprop.spatial_absorber import collar_attenuation
>>> gamma = collar_attenuation(1e5, 7.85e6, 1000.0, 300e-6)
>>> round(float(gamma), 2)
11.24
solprop.spatial_absorber.collar_profile(s)[source]#

Return the collar’s profile P = 1 - W on its normalised depth.

The complement of the grids’ Planck window (solprop.grid.planck_taper()), zero at and before the collar’s onset s = 0 and one at and beyond its far edge s = 1, with every derivative continuous (SEP 40 section 4.5).

Parameters:
snumpy.ndarray

Depth into the collar as a fraction of its width.

Returns:
numpy.ndarray

The profile, in [0, 1].

Examples

>>> import numpy as np
>>> from solprop.spatial_absorber import collar_profile
>>> collar_profile(np.array([-1.0, 0.0, 0.5, 1.0, 2.0])).tolist()
[0.0, 0.0, 0.5, 1.0, 1.0]
solprop.spatial_absorber.collar_strength(kappa_p, k, width, attenuation=8.517193191416238)[source]#

Return the collar’s peak rate that attenuates the protected band’s edge enough.

The root of min_omega Gamma(kappa_p(omega), omega; alpha0) = attenuation, a monotone scalar root (SEP 40 section 4.2). For a declared wavenumber kappa_p is one number and the smallest k_z decides; for a declared angle it varies with frequency, and the root is solved over the band rather than pairing the bluest wavenumber with the reddest k_z.

Parameters:
kappa_pnumpy.ndarray or float

The protected band’s edge at each frequency, rad/m, or one value for all.

knumpy.ndarray

The medium’s wavenumbers over the requested band, rad/m.

widthfloat

The collar’s width, m.

attenuationfloat, optional

The returned attenuation required, field nepers.

Returns:
float

alpha0, 1/m.

Raises:
GridError

If a frequency has kappa_p >= k, or the root cannot be bracketed.

Examples

>>> import numpy as np
>>> from solprop.spatial_absorber import collar_attenuation, collar_strength
>>> k = np.array([7.85e6, 1.0e7])
>>> alpha0 = collar_strength(1e5, k, 300e-6)
>>> round(float(collar_attenuation(1e5, k, alpha0, 300e-6).min()), 6)
8.517193
solprop.spatial_absorber.crossing_limit(kappa_p, k, width, factor=0.5)[source]#

Return the longest step that cannot carry the protected band across the collar.

h <= c_x w min_omega k_z(kappa_p) / kappa_p (SEP 40 section 4.7): a component moves h kappa / k_z across over a step.

Parameters:
kappa_pnumpy.ndarray or float

The protected band’s edge per frequency, or one value, rad/m.

knumpy.ndarray

The medium’s wavenumbers over the requested band, rad/m.

widthfloat

The collar’s width, m.

factorfloat, optional

c_x: CROSSING_ADAPTIVE for the adaptive clamp, CROSSING_FIXED for a fixed schedule.

Returns:
float

The step, m.

Examples

>>> import numpy as np
>>> from solprop.spatial_absorber import crossing_limit
>>> round(crossing_limit(1e5, np.array([7.85e6]), 300e-6) * 1e3, 3)
11.774
solprop.spatial_absorber.reflection_threshold(alpha0, width, k)[source]#

Return the wavenumber above which the collar reflects less than 2e-4.

kappa_lo w = A (k_max alpha0 w^2)^gamma (THRESHOLD_LAW), with k_max the largest k_z at kappa_lo over the requested band, found by iterating from the largest k (SEP 40 section 4.3).

Parameters:
alpha0float

The collar’s peak power rate, 1/m.

widthfloat

Its width, m.

knumpy.ndarray

The medium’s wavenumbers over the requested band, rad/m.

Returns:
float

kappa_lo, rad/m.

Examples

>>> import numpy as np
>>> from solprop.spatial_absorber import reflection_threshold
>>> round(reflection_threshold(698.0, 300e-6, np.array([1.25e7])) * 300e-6, 2)
24.76
solprop.spatial_absorber.rms_wavenumber(power, wavenumber)[source]#

Return an energy-weighted rms transverse wavenumber.

sqrt(sum P kappa^2 / sum P) over the columns, with power each column’s energy (quadrature weight times |E|^2, summed over the requested band).

Parameters:
powernumpy.ndarray

Each column’s energy, any consistent unit.

wavenumbernumpy.ndarray

Each column’s wavenumber along the axis, or |kappa_perp| radially, rad/m.

Returns:
float

The wavenumber, rad/m; zero for no energy.

Examples

>>> import numpy as np
>>> from solprop.spatial_absorber import rms_wavenumber
>>> rms_wavenumber(np.array([1.0, 1.0]), np.array([1.0, 3.0]))
2.23606797749979
solprop.spatial_absorber.safety_ramp_strength(kappa_half, k, length, attenuation=8.517193191416238)[source]#

Return the safety ramp’s peak rate on one axis.

alpha_s = 2 (Gamma_min + 2) kappa_half / (k_s ell): content in the ramp’s upper half, where the raised cosine is at least half its peak, is attenuated by Gamma_min + 2 nepers over the distance ell in which it would cross and come back, with k_s the smallest over the band of max(k_z(kappa_half), 0.44 k) (SEP 40 section 4.4).

Parameters:
kappa_halffloat

The ramp’s midpoint wavenumber, rad/m.

knumpy.ndarray

The medium’s wavenumbers over the requested band, rad/m.

lengthfloat

ell: the collar’s width when there is one, else the requested size, m.

attenuationfloat, optional

Gamma_min, field nepers.

Returns:
float

The ramp’s peak power rate, 1/m.

Examples

>>> import numpy as np
>>> from solprop.spatial_absorber import safety_ramp_strength
>>> round(safety_ramp_strength(6e5, np.array([7.85e6]), 300e-6))
5375