solprop.boundaries#
Absorbing boundaries as absorption rates per unit distance.
Both axes of the grid are finite and periodic, so energy that reaches the edge of the
frequency or the time window does not leave the simulation – it wraps around, and the
answer is wrong. The collars of solprop.grid mark where the propagation is
absorbed instead of modelled; this module turns them into rates.
Multiplying the solution by the collar profile W once per accepted step would make
the total absorption W^n for however many steps the adaptive controller happened to
take. The answer would then depend on rtol: refining the tolerance sixteen-fold
erodes the spectral wings fifty-fold, and the limit it approaches is not the taper the
profile describes but a brick wall at the edge of the flat region. As a rate it is
exact instead, because traversing a distance attenuates by a factor that depends only on
the distance. The spectral collar becomes
the rate that reproduces one multiplication by the window over the reference length, and the temporal collar becomes the bounded profile
whose integral over the collar is what the capped logarithm gave and whose largest
value is a fifth of the cap (TEMPORAL_ALPHA_LENGTH).
alpha is a power coefficient, as everywhere else in solprop: power falls as
exp(-alpha z), the field as exp(-alpha z / 2), and the linear operator carries
-alpha/2 (docs/dev/conventions.md section 2). The factor of two in the spectral
definition is what makes exp(-alpha ell / 2) = W – one reference length applies the
historical profile to the field exactly once, which is how the profile was used. The
reference length is ell = length / boundary_N by default: apply the window
boundary_N times over the whole propagation.
The two rates are applied by different mechanisms, each chosen because it is exact
(see docs/dev/decisions.md):
alpha(omega)is diagonal in omega, so it is folded into the linear operator before the cumulative operator is integrated. The propagator then applies it exactly over whatever sub-interval the stepper takes, at no cost.alpha(t)is diagonal in t and cannot ride the propagator. It is applied as the term-alpha(t) E(t) / 2inside every stage evaluation (nonlinear_absorber_term()), the default because the alternative – the split-step factorexp(-alpha(t) h / 2)after each accepted step (split_step_factor()), exact for the absorber alone because the factors telescope – was measured to be a first-order splitting of the whole. Both are here.The hard band limit outside the active mask stays a 0/1 mask (
apply_band_mask()), which is idempotent and never had the problem.
Two lengths set the collars’ strengths (SEP 22 section 4.6). reference_length
sets the spectral rate and the ceiling every spectral rate is validated against;
collar_length, reference_length unless given, sets the in-stage collars, the
temporal 12.6 (1 - W(t)) / ell_c and the spatial 12.6 (1 - W(x, y)) / ell_c, and
their ceiling. A custom spectral rate (custom_absorber()) can then be as strong as
a measurement needs without moving the explicit in-stage collars or their stable step.
The manual page is docs/guide/numerics/boundaries.md.
Module Attributes
Default absorber strength: applications of the collar profile over the propagation. |
|
Cap on the spectral |
|
Depth of the temporal absorber at the edge of the time window, as |
|
Grid samples a collar edge must span before it counts as a taper. |
|
Field attenuation, in nepers, a walking packet must accumulate crossing the collar. |
|
How far DOPRI5's stability region reaches along the negative real axis. |
Functions
|
Build the absorbing boundaries of a grid. |
|
Zero a spectrum outside the active band. |
|
Apply the temporal absorber to a time-domain field over one accepted step. |
|
Convert a collar profile into a power absorption coefficient by its logarithm. |
|
Add a user's spectral power rate to a collar, keeping its in-stage collars. |
|
Return the temporal absorber as a term to add inside the nonlinear evaluation. |
|
Return the factor the temporal absorber multiplies the field by over one step. |
|
Return the longest step an explicit method can take with an in-stage collar. |
|
Convert the temporal collar into a bounded power absorption coefficient. |
|
Evaluate a transverse collar's power rate on a Cartesian grid's columns. |
|
Warn if the absorber is too weak for the walk-off the run can produce. |
Classes
|
The absorbing boundaries of one grid, as rates per unit distance. |
|
A transverse-wavevector (k-perp) safety ramp on a periodic Cartesian grid. |
- class solprop.boundaries.Absorber(spectral_rate, temporal_rate, temporal_rate_fine, band_mask, reference_length, collar_length=None, temporal=True)[source]#
Bases:
objectThe absorbing boundaries of one grid, as rates per unit distance.
- Attributes:
- spectral_ratenumpy.ndarray
Power absorption coefficient on the coarse frequency axis, 1/m, shape
(n_omega,). Exactly zero in the flat region and outside the active mask, where the linear operator is zero anyway. Folded into the linear operator as-alpha/2.- temporal_ratenumpy.ndarray
Power absorption coefficient on the coarse time axis, 1/m, shape
(n_t,).- temporal_rate_finenumpy.ndarray
The same on the fine time axis, 1/m, shape
(n_to,), for a response that is evaluated there.- band_masknumpy.ndarray
The hard band limit, shape
(n_mask,)matchingspectral_rate: true where the physics lives. Applied as a 0/1 multiply, which is idempotent.- reference_lengthfloat
The length
ellin metres over which the collar profile is applied once to the field. It sets the spectral rate’s ceiling,60 / ell.- collar_lengthfloat or None
The length
ell_cin metres that sets the in-stage collars’ strength: the temporal collar’s largest rate is12.6 / ell_c, and the spatial collar of a radial, Cartesian or planar run is12.6 (1 - W) / ell_c; it also sets the temporal rates’ ceiling,60 / ell_c.None, the default, means reference_length, as for every absorber before SEP 22; a value equal to reference_length is stored asNone, so that the record carries it only where the two differ. Left out of the run record atNone.- temporalbool
Whether the collar has a temporal part.
Falsefor a spectral-only collar (absorber()withtemporal=False), whose temporal rates are zero arrays; it is recorded only whenFalse. Any collar whose temporal rates are all zero, however it was built, is translated to no temporal absorber (solprop.translate.masks.temporal_rates()).
- Raises:
- GridError
If a length is not positive and finite, the mask and the spectral rate disagree in shape, a rate is negative, not finite, or above its ceiling, or a collar declared spectral-only carries a temporal rate.
Examples
>>> from solprop.boundaries import absorber >>> from solprop.grid import FieldGrid >>> grid = FieldGrid.from_limits(1.5, 800e-9, (90e-9, 4e-6), 400e-15) >>> boundary = absorber(grid) >>> boundary.reference_length 0.075 >>> float(boundary.spectral_rate[grid.inband_mask].max()) 0.0
- band_mask: Bool[ndarray, 'n_mask']#
- property in_stage_length: float[source]#
Return the length that sets the in-stage collars’ strength.
- Returns:
- float
collar_length, or reference_length when it is
None, m.
Examples
>>> from solprop.boundaries import absorber >>> from solprop.grid import FieldGrid >>> grid = FieldGrid.from_limits(1.5, 800e-9, (90e-9, 4e-6), 400e-15) >>> absorber(grid).in_stage_length 0.075
- spectral_rate: Float[ndarray, 'n_omega']#
- temporal_rate: Float[ndarray, 'n_t']#
- temporal_rate_fine: Float[ndarray, 'n_to']#
- solprop.boundaries.DEFAULT_BOUNDARY_N: Final = 20#
Default absorber strength: applications of the collar profile over the propagation.
It sets the reference length
ell = length / boundary_N, so the strength is a physical distance rather than a number of steps.
- solprop.boundaries.MAX_ALPHA_LENGTH: Final = 60.0#
Cap on the spectral
alpha * ell, and the ceiling every rate is validated against.The spectral collar reaches exactly zero at the band edge and
-2 log 0is an infinity, which would make the operatorinf * 0somewhere and hence a NaN. At 60 the field is attenuated byexp(-30), about 1e-13, over one reference length – far below anything a propagation can represent, so the cap costs nothing and keeps every rate finite. The temporal rate is bounded by construction atTEMPORAL_ALPHA_LENGTH, well inside this ceiling: were it the same capped logarithm, then under the in-stage absorber term the cap would set the stability limit and the step count.
- solprop.boundaries.MIN_COLLAR_ATTENUATION: Final = 6.907755278982137#
Field attenuation, in nepers, a walking packet must accumulate crossing the collar.
log(1e3): the field falls by a thousand and the power by sixty decibels. Below thatwarn_walkoff()says the absorber is too weak for the walk-off the run can produce.
- solprop.boundaries.MIN_COLLAR_SAMPLES: Final = 4#
Grid samples a collar edge must span before it counts as a taper.
With fewer the discrete profile steps from one to zero rather than tapering, the rate jumps to its cap in a single sample, and the boundary is the brick wall the rate form exists to avoid. A temporal collar built naively is exactly this degenerate for a
trangethat lands on a power of two; solprop’s grids guarantee a minimum width, and this is the guard that says so.
- solprop.boundaries.STABILITY_RADIUS: Final = 3.3#
How far DOPRI5’s stability region reaches along the negative real axis.
The textbook value for both order-5(4) pairs this package carries, and what
stable_step()turns into a step limit for the in-stage absorber. Measured on the 1 mm capillary of the driver tests, whose collar reached 1.2e6 m^-1 under the capped logarithm temporal profile: a fixed schedule diverges ath alpha / 2 = 3.75and completes at 3.13, which brackets it.
- solprop.boundaries.TEMPORAL_ALPHA_LENGTH: Final = 12.6#
Depth of the temporal absorber at the edge of the time window, as
alpha * ell.The temporal rate is
TEMPORAL_ALPHA_LENGTH * (1 - W(t)) / ell, the complement of the collar window (temporal_collar_rate()). At 12.6 its integral over the collar is what-2 log Wcapped atMAX_ALPHA_LENGTHgave – 6.3 per collar width per reference length, both edges, to a few per cent on the grids solprop builds – so the walk-off protection and the meaning ofboundary_Nare unchanged, while the largest rate is a fifth of the capped logarithm’s: the stability limit of the in-stage absorber term (stable_step()) is 4.8 times longer, and the error estimate does not see a rate of60 / ellon the collar’s last samples, which would cost the reduced M2 case 40 % of its steps. The capped logarithm puts 73 % of each edge’s attenuation in its outer 14 %; this profile spreads it over the edge, so a case whose field reaches the collar is absorbed earlier and answers differently. At the very edge the field falls byexp(-6.3), 1.8e-3, per reference length.
- class solprop.boundaries.TransverseCollar(strength, fraction=0.1)[source]#
Bases:
objectA transverse-wavevector (k-perp) safety ramp on a periodic Cartesian grid.
SEP 22 section 4.3. With \(\rho = \max(|k_x|/k_{x,\max}, |k_y|/k_{y,\max})\) the power rate is \(\alpha_\perp = S\,P(\rho)\), where \(P\) is zero for \(\rho \le 1 - f\) and rises as a raised cosine to one at the grid’s largest wavenumber. It enters the separable operator as a diagonal rate, which is exact under the cumulative flow and costs no transform (
docs/dev/kernel-contract.mdsection 3). It damps content that reaches the outer band and keeps rounding from accumulating at the Nyquist edge; it does not remove an alias that in-band content generates, which grid sizing by the one-half rule prevents (docs/guide/free-space/cartesian.md).- Attributes:
- strengthfloat
The rate \(S\) at the grid’s edge, a power coefficient in m^-1, finite and nonnegative.
S L / 2 >= log(1e3)removes 60 dB of field at the edge over an interaction lengthL.- fractionfloat
The ramp’s width \(f\) as a fraction of each axis’s largest wavenumber, in (0, 1). Default 0.1, the width of the spatial grids’ default collar.
- Raises:
- GridError
If the strength is negative or not finite, or the fraction is outside (0, 1).
Examples
>>> from solprop.boundaries import TransverseCollar >>> TransverseCollar(strength=2e4) TransverseCollar(strength=20000.0, fraction=0.1)
- solprop.boundaries.absorber(grid, *, boundary_N=20, boundary_length=None, temporal=True)[source]#
Build the absorbing boundaries of a grid.
- Parameters:
- gridTimeGrid
The grid, for its collars, its masks and its propagation length.
- boundary_Nfloat, optional
Absorber strength, as the number of times the collar profile is applied to the field over the whole propagation; the reference length is
length / boundary_N.- boundary_lengthfloat or None, optional
Reference length in metres, overriding
boundary_N.- temporalbool, optional
Falsebuilds a spectral-only collar (SEP 22): its temporal rates are zero arrays on both time axes, the band mask and the spectral rate are as before, and the record saystemporal=False. It is translated to no temporal absorber, so the kernels compile no temporal pass under either absorber scheme. The grid is unchanged.
- Returns:
- Absorber
The spectral and temporal rates, the hard band mask and the reference length.
- Raises:
- GridError
If the reference length is not positive and finite, or either collar is too narrow on its axis to be a taper.
Examples
>>> import numpy as np >>> from solprop.boundaries import absorber >>> from solprop.grid import FieldGrid >>> grid = FieldGrid.from_limits(1.5, 800e-9, (90e-9, 4e-6), 400e-15) >>> boundary = absorber(grid, boundary_N=20) >>> boundary.reference_length 0.075 >>> field_left = np.exp(-boundary.spectral_rate * boundary.reference_length / 2) >>> active = grid.active_mask >>> bool(np.allclose(field_left[active], grid.omega_window[active])) True >>> spectral_only = absorber(grid, temporal=False) >>> float(spectral_only.temporal_rate.max()), spectral_only.temporal (0.0, False)
- solprop.boundaries.apply_band_mask(spectrum, band_mask)[source]#
Zero a spectrum outside the active band.
The hard part of the boundary, kept as a 0/1 multiply rather than made a rate: it is idempotent, so applying it once per accepted step never accumulates, and it is the definition of the band the equation is solved on rather than an absorber.
- Parameters:
- spectrumnumpy.ndarray
Spectral amplitudes, shape
(..., n_omega).- band_masknumpy.ndarray
True where the physics lives, shape
(n_omega,).
- Returns:
- numpy.ndarray
The masked spectrum, with the shape of
spectrum.
Examples
>>> import numpy as np >>> from solprop.boundaries import apply_band_mask >>> mask = np.array([True, False, True]) >>> apply_band_mask(np.array([1.0, 2.0, 3.0]), mask).tolist() [1.0, 0.0, 3.0]
- solprop.boundaries.apply_split_step(field, temporal_rate, step)[source]#
Apply the temporal absorber to a time-domain field over one accepted step.
The reference implementation of the split-step absorber scheme, the alternative to the default in-stage term of
nonlinear_absorber_term(), and what that term is measured against.- Parameters:
- fieldnumpy.ndarray
Time-domain field, shape
(..., n_t); real on a field grid, complex on an envelope grid.- temporal_ratenumpy.ndarray
Power absorption coefficient on the time axis, 1/m, shape
(n_t,).- stepfloat
Distance travelled in metres.
- Returns:
- numpy.ndarray
The attenuated field, with the shape of
field.
Examples
>>> import numpy as np >>> from solprop.boundaries import apply_split_step, collar_rate >>> alpha = collar_rate(np.array([1.0, 0.5]), 0.1) >>> np.round(apply_split_step(np.ones((1, 2)), alpha, 0.1), 6).tolist() [[1.0, 0.5]]
- solprop.boundaries.collar_rate(window, reference_length)[source]#
Convert a collar profile into a power absorption coefficient by its logarithm.
The spectral collar’s rate; the temporal collar takes the bounded profile of
temporal_collar_rate()instead.alpha = -2 log(W) / ell, so that propagating throughellattenuates the field by exactlyW: the collar profiles were multiplied into the field, and this is the rate that reproduces one such multiplication over the reference length. Exactly zero whereW == 1, and capped atMAX_ALPHA_LENGTH / ellwhere the profile reaches zero.- Parameters:
- windownumpy.ndarray
Collar profile in
[0, 1], one in the interior and falling to zero at the boundary, shape(n,).- reference_lengthfloat
The length in metres over which the profile is applied once; positive, finite.
- Returns:
- numpy.ndarray
Power absorption coefficient in 1/m, shape
(n,), non-negative and finite.
- Raises:
- GridError
If the reference length is not positive and finite, or the profile leaves
[0, 1].
Examples
>>> import numpy as np >>> from solprop.boundaries import collar_rate >>> alpha = collar_rate(np.array([1.0, 0.5, 0.0]), 0.1) >>> np.round(alpha, 6).tolist() [0.0, 13.862944, 600.0] >>> float(np.exp(-alpha[1] * 0.1 / 2)) 0.5
- solprop.boundaries.custom_absorber(grid, spectral_rate, *, collar=None)[source]#
Add a user’s spectral power rate to a collar, keeping its in-stage collars.
The spectral rate is diagonal in frequency and folded into the linear operator, where it is exact and free whatever its strength (ADR-007); the in-stage collars are explicit and their strength bounds a replayed step (SEP 22 section 4.6). The user’s rate is therefore added to the collar’s spectral rate, and the collar’s temporal rates, band mask and in-stage strength are kept as they are: its
collar_lengthis the collar’s ownin_stage_length. Itsreference_lengthstays the collar’s when the summed rate fits under the collar’s ceiling60 / reference_length, and otherwise becomes60over the sum’s largest value, the longest length that admits it. The collar length is recorded only where it differs from the reference length, so a zero user rate returns a collar equal to the one given, field for field.- Parameters:
- gridTimeGrid
The grid the collar is for, for its frequency axis.
- spectral_ratefloat, numpy.ndarray or callable
The user’s power absorption coefficient, 1/m: a scalar, an array of shape
(n_omega,)on the grid’s coarse frequency axis, or a callable of the angular frequencyomegain rad/s (the grid’s own axis, an envelope grid’s in FFT order) returning that array. Finite and non-negative; it is zeroed outside the collar’s band mask, where the operator is zero.- collarAbsorber or None, optional
The collar it is added to, taken by value;
Noneisabsorber()of the grid with its defaults. Any collar is accepted as it is, a spectral-only one (absorber(grid, temporal=False)) among them.
- Returns:
- Absorber
The collar with the summed spectral rate.
- Raises:
- GridError
If the rate is not finite and non-negative, or not of the frequency axis’s shape.
Examples
>>> import numpy as np >>> from solprop.boundaries import absorber, custom_absorber >>> from solprop.grid import FieldGrid >>> grid = FieldGrid.from_limits(1.5, 800e-9, (90e-9, 4e-6), 400e-15) >>> collar = absorber(grid) >>> same = custom_absorber(grid, 0.0, collar=collar) >>> same.reference_length == collar.reference_length, same.collar_length (True, None) >>> strong = custom_absorber(grid, 1e5, collar=collar) >>> strong.reference_length < collar.reference_length True >>> strong.in_stage_length == collar.in_stage_length True >>> bool(np.array_equal(strong.temporal_rate, collar.temporal_rate)) True
- solprop.boundaries.nonlinear_absorber_term(field, temporal_rate)[source]#
Return the temporal absorber as a term to add inside the nonlinear evaluation.
-alpha(t) E(t) / 2: the derivative form of the same rate, and the reference implementation of the default in-stage absorber scheme. Integrating it exactly over a step would giveapply_split_step(); a Runge-Kutta stage integrates it to the order of the method, so the two agree to first order in the step and differ at second, which is what the comparison of the two schemes measures.- Parameters:
- fieldnumpy.ndarray
Time-domain field, shape
(..., n_t).- temporal_ratenumpy.ndarray
Power absorption coefficient on the time axis, 1/m, shape
(n_t,).
- Returns:
- numpy.ndarray
The contribution to
dE/dzin the time domain, with the shape offield.
Examples
>>> import numpy as np >>> from solprop.boundaries import collar_rate, nonlinear_absorber_term >>> alpha = collar_rate(np.array([0.8, 0.5]), 0.1) >>> np.round(nonlinear_absorber_term(np.ones(2), alpha), 6).tolist() [-2.231436, -6.931472]
- solprop.boundaries.split_step_factor(temporal_rate, step)[source]#
Return the factor the temporal absorber multiplies the field by over one step.
exp(-alpha(t) h / 2): the field, not the power, because that is what the state holds, andalphais a power coefficient. Successive factors telescope exactly toexp(-alpha(t) L / 2)however the solver subdivides the propagation, which is the whole point of rate semantics – and the property the tests assert.The factor of one half is required:
exp(-alpha(t) h)could not be right alongside the-alpha(t) E(t) / 2of the alternative scheme, or alongsidealpha = -2 log W / ell: with it, one reference length would apply the collar profile to the field twice, and the two schemes would differ at first order in the step rather than agreeing there.- Parameters:
- temporal_ratenumpy.ndarray
Power absorption coefficient on the time axis, 1/m, shape
(n_t,).- stepfloat
Distance travelled in metres, non-negative.
- Returns:
- numpy.ndarray
Multiplicative factor in
(0, 1], shape(n_t,).
- Raises:
- GridError
If the step is negative or not finite.
Examples
>>> import numpy as np >>> from solprop.boundaries import collar_rate, split_step_factor >>> alpha = collar_rate(np.array([1.0, 0.5]), 0.1) >>> np.round(split_step_factor(alpha, 0.1), 6).tolist() [1.0, 0.5]
- solprop.boundaries.stable_step(temporal_rate)[source]#
Return the longest step an explicit method can take with an in-stage collar.
The
"nonlinear"absorber scheme adds-alpha(t) E(t) / 2to the derivative, so the collar’s largest rate becomes an eigenvalue of the linearised problem and an explicit Runge-Kutta method must keeph alpha / 2inside its stability region. The split factor ofsplit_step_factor()integrates the same term exactly and is unconditionally stable, which is the one thing it is better at.DOPRI5’s and Tsit5’s stability region reaches
STABILITY_RADIUSalong the negative real axis, so the limit is2 R / max(alpha). Measured on the 1 mm capillary of the driver tests, whose collar reached 1.2e6 m^-1 under the capped logarithm then in force: a fixed schedule diverges ath alpha / 2 = 3.75and completes at 3.13, which brackets the textbook 3.3. With the bounded profile the largest rate isTEMPORAL_ALPHA_LENGTH / ell, so the limit is0.52 ell.The rate reaches its ceiling only at the very edge of the window, where the field is by design negligible, so an adaptive run is not usually held at this limit – the error norm is scaled by the state and an enormous derivative on an empty bin scores nothing. What the limit binds is a replayed schedule, and a floor dz_min set above it.
- Parameters:
- temporal_ratenumpy.ndarray
The collar’s power absorption coefficient on the time axis, 1/m, shape
(n_t,).
- Returns:
- float
The longest stable step in m, or
infwhen the collar is identically zero.
Examples
>>> import numpy as np >>> from solprop.boundaries import stable_step >>> round(stable_step(np.array([0.0, 6.6, 3.3])), 6) 1.0 >>> stable_step(np.zeros(4)) inf
- solprop.boundaries.temporal_collar_rate(axis, collar, reference_length)[source]#
Convert the temporal collar into a bounded power absorption coefficient.
alpha(t) = TEMPORAL_ALPHA_LENGTH * (1 - W(t)) / ell: exactly zero over the flat region, rising with every derivative continuous as the Planck window falls, and reachingTEMPORAL_ALPHA_LENGTH / ellat the edge of the window. The logarithm ofcollar_rate()is the right rate for the spectral collar, which rides the linear operator for free; for the temporal collar it would be a stiff term in every stage of the in-stage absorber, its cap would set the stability limit and the step count, and it puts nearly three quarters of the attenuation in the outer seventh of each edge. This profile carries the same integrated attenuation with a largest rate a fifth of the cap.- Parameters:
- axisnumpy.ndarray
Time axis to evaluate the rate on, s, shape
(n,).- collarsolprop.grid.Collar
The temporal collar geometry.
- reference_lengthfloat
The length
ellin metres; positive, finite.
- Returns:
- numpy.ndarray
Power absorption coefficient in 1/m, shape
(n,), in[0, TEMPORAL_ALPHA_LENGTH / ell].
- Raises:
- GridError
If the reference length is not positive and finite.
Examples
>>> import numpy as np >>> from solprop.boundaries import TEMPORAL_ALPHA_LENGTH, temporal_collar_rate >>> from solprop.grid import Collar >>> collar = Collar(-2.0, -1.0, 1.0, 2.0) >>> rate = temporal_collar_rate(np.array([-2.0, 0.0, 2.0]), collar, 0.1) >>> rate.tolist() == [TEMPORAL_ALPHA_LENGTH / 0.1, 0.0, TEMPORAL_ALPHA_LENGTH / 0.1] True
- solprop.boundaries.transverse_collar_rate(space, collar)[source]#
Evaluate a transverse collar’s power rate on a Cartesian grid’s columns.
- Parameters:
- spacesolprop.grid.FreeGrid
The periodic Cartesian grid, or a
solprop.grid.LineGrid, on which the collar acts onk_xalone. A radial grid is refused.- collarTransverseCollar
The strength and the ramp’s width.
- Returns:
- numpy.ndarray
The nonnegative power rate in m^-1, shape
(n_x*n_y,), in column orderix + n_x*iy.
- Raises:
- GridError
If space is radial, which is not periodic. The hybrid planar kind’s
k_y-only collar is not offered either (SEP 22, Q5); translation refuses it.
Examples
>>> import numpy as np >>> from solprop.boundaries import TransverseCollar, transverse_collar_rate >>> from solprop.grid import FreeGrid >>> space = FreeGrid(radius_x=1e-4, n_x=20, radius_y=1e-4, n_y=20) >>> rate = transverse_collar_rate(space, TransverseCollar(strength=1e4)) >>> rate.shape, float(rate.max()), float(rate[0]) ((400,), 10000.0, 0.0)
- solprop.boundaries.warn_walkoff(grid, group_delay_span, *, boundary_N=20, boundary_length=None)[source]#
Warn if the absorber is too weak for the walk-off the run can produce.
A packet whose group delay drifts by
group_delay_spanover the whole propagation crosses the temporal collar atgroup_delay_span / lengthseconds per metre, so it spendsdt / vmetres at each sample and accumulates a field attenuation of(length / group_delay_span) * integral(alpha) / 2nepers on the way through. BelowMIN_COLLAR_ATTENUATIONthe packet reaches the edge of the window still alive, wraps around, and aliases onto the rest of the field.The span is the caller’s, deliberately. A prediction from the group delay across the whole simulation band would, on a grid running to four microns, be dominated by band edges carrying nothing but shot noise, and so would fire on almost every run. A caller who passes the span of the band it cares about does not have that problem (
docs/dev/stepper.mdsection 6).- Parameters:
- gridTimeGrid
The grid, for its temporal collar and its propagation length.
- group_delay_spanfloat
Spread of group delay accumulated over the propagation, in seconds. Zero or negative means nothing walks anywhere and nothing is checked.
- boundary_Nfloat, optional
Absorber strength, as in
absorber().- boundary_lengthfloat or None, optional
Reference length in metres, overriding
boundary_N.
- Raises:
- GridError
If the span is not finite, or the reference length is invalid.
- Warns:
- SolpropWarning
If a packet drifting by that span would survive the collar.
Examples
>>> import warnings >>> from solprop.boundaries import warn_walkoff >>> from solprop.grid import FieldGrid >>> grid = FieldGrid.from_limits(1.5, 800e-9, (90e-9, 4e-6), 400e-15) >>> with warnings.catch_warnings(): ... warnings.simplefilter("error") ... warn_walkoff(grid, 50e-15)