solprop.translate.modal#
The transform between modal amplitudes and the field at quadrature nodes.
A nonlinear response is a statement about the field at a point of the cross-section, in
volts per metre; the propagated state is a set of modal amplitudes in the square root of
watts. Two matrices carry the state between them — synthesis onto the nodes, projection
back onto the modes with the quadrature weights folded in — and a scalar path along z
rescales both when the waveguide tapers. Section 4 of docs/dev/kernel-contract.md is
the class Transform, field for field.
Mode-averaged propagation explicitly selects an effective-area transform. It has
one node, but a resolved quadrature with one node remains a modal transform. In
the mode-averaged case the matrices are one by one and
the conversion factor is 1 / sqrt(eps0 c A_eff / 2) (conventions.md section 4).
The oversampling pad scale lives here. The state is zero-padded onto the fine
spectral grid before the inverse transform, and the factor n_to / n_t that keeps the
two time-domain signals equal at coincident samples (conventions.md section 3) is
folded into the synthesis rather than into a response coefficient. It has to be: a
response coefficient absorbs a field scale only when the response is homogeneous in the
field, which the Kerr term is and an ionisation rate emphatically is not. Folded into
the synthesis, the field every response sees is the physical one in volts per metre. The
crop scale on the way back is folded into the prefactor instead
(solprop.translate.responses), where it meets no such condition.
Module Attributes
Bytes of node-chunk buffers a problem may hold, before the chunk size is reduced. |
|
Node-chunk bytes a free-space, line or planar problem may hold by default (SEP 28). |
|
What a transform represents (kernel schema 26, SEP 19). |
|
Node-chunk buffers the fused pass holds: |
Functions
|
Build the transform of a general modal guide: the tables and placeholders. |
|
Build the one-node transform of a mode-averaged problem. |
|
Build the fixed-quadrature transform of a multimode problem. |
Classes
|
The synthesis and projection matrices and their scaling along z. |
- solprop.translate.modal.BUFFERS_PER_NODE = 3#
Node-chunk buffers the fused pass holds:
E,Pand the Raman drive.
- solprop.translate.modal.DEFAULT_NODE_BUDGET = 67108864#
Bytes of node-chunk buffers a problem may hold, before the chunk size is reduced.
Architecture section 13 chooses the whole node set when it fits a per-problem budget and the largest chunk that fits otherwise. Sixty-four mebibytes reproduces the chunk of about sixteen nodes that section 8 tabulates for the comb-to-continuum target (32768 time samples, six modes, Raman) while leaving a small problem un-chunked. It is an estimate until the benchmark suite measures the crossover.
The free-space, line and planar grids default to
DEFAULT_SPATIAL_NODE_BUDGET.
- solprop.translate.modal.DEFAULT_SPATIAL_NODE_BUDGET = 1073741824#
Node-chunk bytes a free-space, line or planar problem may hold by default (SEP 28).
A spatial evaluation pays a fixed cost per node chunk: its launches, its temporal transforms’ thread wake-ups, and its gather and scatter. A field volume is gigabytes, so a larger group is cheap in memory. With the per-chunk copies and clears removed, one gibibyte was the fastest of 64, 256 and 1024 MiB for the 384- and 768-square Kerr beams on Zen 5 and on Apple silicon, 2.3 to 2.4 times faster than 64 MiB on the native backend’s march; on an H200, IP-DOPRI5 was 1.3 times faster at one gibibyte than at 64 MiB.
- class solprop.translate.modal.Transform(synthesis, projection, projection_coarse, taper_scale, node_chunk, aeff, geometry=None, node_weights=None, modal_tables=None, _n_pol=1, *, kind)[source]#
Bases:
objectThe synthesis and projection matrices and their scaling along z.
Section 4 of the kernel contract. The matrices are built once, at the reference position, and taper_scale carries whatever z dependence a scale-invariant waveguide has: the normalised mode field goes as
1 / aat nodes placed atr = a xiand the weights asa**2, so multiplying the synthesis bya(0) / a(z)and dividing the projection by it is exact rather than an approximation (solprop.modes.quadrature.ModeMatrices.rescaled()).- Attributes:
- synthesisnumpy.ndarray
Mode fields at the nodes, float64, shape
(n_nodes * n_pol, n_modes), in V/(m sqrt(W)), with the oversampling pad scalen_to / n_tfolded in. The column index isi * n_pol + p.- projectionnumpy.ndarray
The same fields with the quadrature weights folded in, float64, shape
(n_modes, n_nodes * n_pol).- projection_coarsenumpy.ndarray
The embedded coarse-rule projection, same shape, for the quadrature error estimate. It is an alarm, never a controller.
- taper_scalesolprop.translate.chebyshev.PiecewiseChebyshev or None
The scalar rescaling
a(0) / a(z)as a table along z, orNonewhen the cross-section does not change.- node_chunkint
Nodes the response pass handles at a time, at least 1 and at most
n_nodes.- aeffsolprop.translate.chebyshev.PiecewiseChebyshev
Effective area in m^2 as a table along z. It is the normalisation itself when
kind == 'mode_averaged'and a diagnostic otherwise.- geometrysolprop.translate.spatial.SpatialGeometry or None
Spatial data for radial/free/slab propagation;
Nonefor modal amplitudes. A free transform carries empty matrices and uses the declared FFT shape; a slab transform carries the x half of the map, the slab rule’s(n_x, n_p)synthesis and its projections, and an FFT in y.- node_weightsnumpy.ndarray or None
Physical transverse quadrature weights in m² at z=0, shape (n_nodes,), for resolved modal populations. Divide by taper_scale² at later planes. Spatial transforms use geometry.spatial_weights; mode-averaged transforms use aeff.
- modal_tablessolprop.translate.modal_tables.ModalTransformTables or None
Kernel schema 12: the full E/H reference of a general modal guide, with its own node rule, separate synthesis and projection maps and optional factors.
Nonefor every route that goes through the real static matrices above. When it is present the general route reads these tables instead, and the static matrices are declared placeholders: they must be the single zero column(1, n_modes)and its two(n_modes, 1)transposes, withnode_chunk = 1, no taper and no node weights. They are mandatory fields of the contract that this route does not use, and a placeholder that is checked cannot be mistaken for a second, silently wrong transform. The route’s own chunking is the ledger ofsolprop.backends.numpy_ref.general_modal, notnode_chunk.- kind{‘mode_averaged’, ‘modal’, ‘radial’, ‘free’, ‘slab’}
Kernel schema 26: what the transform represents, stored rather than inferred from the node count, so that a one-node quadrature is an ordinary modal transform. A spatial kind must equal its geometry’s kind; a mode-averaged transform has one node, one component and neither tables nor node weights.
- Raises:
- TranslationError
If the matrix shapes disagree, or
node_chunkis out of range.
Examples
>>> from solprop.grid import EnvelopeGrid >>> from solprop.nonlinear.gnlse import SpecifiedMode >>> from solprop.translate.modal import mode_averaged_transform >>> grid = EnvelopeGrid.from_limits(1.0, 1030e-9, (700e-9, 2e-6), 2e-12) >>> mode = SpecifiedMode.from_taylor( ... [0.0], omega_ref=grid.omega0, aeff=1e-10 ... ) >>> transform = mode_averaged_transform(mode, grid, z_max=1.0) >>> transform.n_nodes, transform.node_chunk, transform.taper_scale is None (1, 1, True) >>> transform.kind 'mode_averaged'
- aeff: PiecewiseChebyshev#
- geometry: SpatialGeometry | None = None#
- modal_tables: ModalTransformTables | None = None#
- property mode_averaged: bool[source]#
Return whether this is the effective-area case.
Read from the stored kind, never inferred from the node count: a one-node quadrature (SEP 19’s field-scale rule at
n_r=1) is an ordinary modal transform, and a general modal guide’s placeholder column is not a node.- Returns:
- bool
Truewhenkind == "mode_averaged".
Examples
>>> from solprop.grid import EnvelopeGrid >>> from solprop.nonlinear.gnlse import SpecifiedMode >>> from solprop.translate.modal import mode_averaged_transform >>> grid = EnvelopeGrid.from_limits(1.0, 1030e-9, (700e-9, 2e-6), 2e-12) >>> mode = SpecifiedMode.from_taylor([0.0], omega_ref=grid.omega0) >>> mode_averaged_transform(mode, grid, z_max=1.0).mode_averaged True
- property n_modes: int[source]#
Return the number of modes the transform carries.
- Returns:
- int
The width of
synthesis.
Examples
>>> from solprop.grid import EnvelopeGrid >>> from solprop.nonlinear.gnlse import SpecifiedMode >>> from solprop.translate.modal import mode_averaged_transform >>> grid = EnvelopeGrid.from_limits(1.0, 1030e-9, (700e-9, 2e-6), 2e-12) >>> mode = SpecifiedMode.from_taylor([0.0], omega_ref=grid.omega0) >>> mode_averaged_transform(mode, grid, z_max=1.0).n_modes 1
- property n_nodes: int[source]#
Return the number of quadrature nodes, one for a mode-averaged run.
- Returns:
- int
synthesis.shape[0] // n_pol; the rows carry node and polarisation together, so this uses the stored chunk arithmetic rather than a guess.
Examples
>>> from solprop.grid import EnvelopeGrid >>> from solprop.nonlinear.gnlse import SpecifiedMode >>> from solprop.translate.modal import mode_averaged_transform >>> grid = EnvelopeGrid.from_limits(1.0, 1030e-9, (700e-9, 2e-6), 2e-12) >>> mode = SpecifiedMode.from_taylor([0.0], omega_ref=grid.omega0) >>> mode_averaged_transform(mode, grid, z_max=1.0).n_nodes 1
- property n_pol: int[source]#
Return the transverse components the transform resolves at each node.
- Returns:
- int
1 when the rows of synthesis are one component per node – a scalar run, or the mode-averaged case – and 2 when both Cartesian components are carried, which is the polarisation-resolved multimode set.
Examples
>>> from solprop.grid import EnvelopeGrid >>> from solprop.nonlinear.gnlse import SpecifiedMode >>> from solprop.translate.modal import mode_averaged_transform >>> grid = EnvelopeGrid.from_limits(1.0, 1030e-9, (700e-9, 2e-6), 2e-12) >>> mode = SpecifiedMode.from_taylor([0.0], omega_ref=grid.omega0) >>> mode_averaged_transform(mode, grid, z_max=1.0).n_pol 1
- projection: Float[ndarray, 'n_modes_p n_columns']#
- projection_coarse: Float[ndarray, 'n_modes_c n_columns_c']#
- synthesis: Float[ndarray, 'n_rows n_modes']#
- taper_scale: PiecewiseChebyshev | None#
- solprop.translate.modal.TransformKind#
What a transform represents (kernel schema 26, SEP 19).
"mode_averaged"is the effective-area route ofmode_averaged_transform(), the only kind whose response prefactor folds in an area conversion."modal"is every resolved modal transform – a quadrature of any node count, including one, and the general modal tables. The spatial kinds match their geometry.alias of
Literal[‘mode_averaged’, ‘modal’, ‘radial’, ‘free’, ‘slab’]
- solprop.translate.modal.general_modal_transform(modes, tables, *, z_max, margin=0.05, tol=1e-12)[source]#
Build the transform of a general modal guide: the tables and placeholders.
The general modal route synthesises and projects through the frequency-dependent complex maps of tables, not through a real static matrix, so section 4’s three matrices are the checked zero placeholders
Transformdescribes and every other field of the group is absent. The one real entry isaeff, which is a diagnostic here as it is for any multimode transform: the scalar surrogate area of the first channel, which is what the legacy mode-averaged interfaces mean by an area and not the nonlinear coefficient of a vector channel.- Parameters:
- modessequence of Mode
The guide’s channels, in state order; only the first channel’s
aeffis read.- tablessolprop.translate.modal_tables.ModalTransformTables
The general guide’s E/H reference and execution maps, already tabulated on the run’s own canonical grids.
- z_maxfloat
Length the run covers, m, positive and finite.
- marginfloat, optional
Fraction of z_max the tables extend beyond the end.
- tolfloat, optional
Target bound on the uniform error of the area table.
- Returns:
- Transform
Section 4 of the kernel contract, carrying
modal_tables.
- Raises:
- TranslationError
If z_max or margin is invalid, or no channel was given.
- solprop.translate.modal.mode_averaged_transform(mode, grid, *, z_max, margin=0.05, tol=1e-12)[source]#
Build the one-node transform of a mode-averaged problem.
The single “node” is the mode itself: the field a response sees is the propagated amplitude times
1 / sqrt(eps0 c A_eff / 2)(solprop.nonlinear.normalisation.sqrtw_to_vm()), and the polarisation the response returns is brought back by the reciprocal, which is already folded intosolprop.nonlinear.normalisation.mode_average_prefactor(). So the projection is the identity and the synthesis carries the conversion — times the oversampling pad scale, for the reason in the module docstring.A varying effective area is carried by taper_scale rather than by rebuilding anything: with
tau(z) = sqrt(A_eff(0) / A_eff(z))the synthesis atzistau(z)times the stored one and the projection is the stored one divided by it, which reproduces the conversion at every position exactly. For a scale-invariant mode that is thea(0) / a(z)ofsolprop.modes.quadrature.taper_scale(), so mode-averaged and multimode tapers use one mechanism.- Parameters:
- modeMode
The mode; only
aeffand its declaredcore_radiusbreakpoints are used.- gridsolprop.grid.TimeGrid
The grid; supplies the oversampling pad scale.
- z_maxfloat
Length the run covers, m, positive and finite.
- marginfloat, optional
Fraction of z_max the tables extend beyond the end, matching the cumulative operator’s.
- tolfloat, optional
Target bound on the uniform error of the tables relative to their value scale.
- Returns:
- Transform
Section 4 of the kernel contract, with
n_nodes == 1.
- Raises:
- TranslationError
If z_max or margin is invalid.
Examples
>>> from solprop.grid import EnvelopeGrid >>> from solprop.nonlinear.gnlse import SpecifiedMode >>> from solprop.nonlinear.normalisation import sqrtw_to_vm >>> from solprop.translate.modal import mode_averaged_transform >>> grid = EnvelopeGrid.from_limits(1.0, 1030e-9, (700e-9, 2e-6), 2e-12) >>> mode = SpecifiedMode.from_taylor( ... [0.0], omega_ref=grid.omega0, aeff=1e-10 ... ) >>> transform = mode_averaged_transform(mode, grid, z_max=1.0) >>> float(transform.synthesis[0, 0]) == sqrtw_to_vm(1e-10) * grid.pad_scale True
- solprop.translate.modal.multimode_transform(modes, quadrature, grid, *, z_max, components=('x', 'y'), node_budget=67108864, margin=0.05, tol=1e-12)[source]#
Build the fixed-quadrature transform of a multimode problem.
The matrices are
solprop.modes.quadrature.mode_matrix()’s, built once at the reference position, with the oversampling pad scale folded into the synthesis; the projection is the dual basis asmode_matrixbuilds it, so the pair a backend applies inverts topad_scaletimes the identity on an orthonormal set. When the set is scale invariant and its radius varies, taper_scale tabulatesa(0) / a(z)so the matrices are rescaled rather than rebuilt; a set whose cross-section changes shape would need rebuilding and is refused here rather than approximated.- Parameters:
- modessequence of Mode
The ordered mode set.
- quadraturesolprop.modes.quadrature.TransverseQuadrature
The fixed rule, with the Kronrod extension when an error estimate is wanted.
- gridsolprop.grid.TimeGrid
The grid; supplies the pad scale and the fine time length the chunk is sized from.
- z_maxfloat
Length the run covers, m, positive and finite.
- componentssequence of str, optional
Transverse components carried:
("x", "y")for a vector run, one of them for a scalar run.- node_budgetint, optional
Bytes the node-chunk buffers may occupy. Default
DEFAULT_NODE_BUDGET.- marginfloat, optional
Fraction of z_max the tables extend beyond the end.
- tolfloat, optional
Target bound on the uniform error of the tables relative to their value scale.
- Returns:
- Transform
Section 4 of the kernel contract.
- Raises:
- TranslationError
If z_max, margin or node_budget is invalid, or the set varies along z in a way the rescaling cannot express.
Examples
>>> from solprop.grid import EnvelopeGrid >>> from solprop.modes import MarcatiliMode, transverse_quadrature >>> from solprop.translate.modal import multimode_transform >>> grid = EnvelopeGrid.from_limits(1.0, 1030e-9, (700e-9, 2e-6), 2e-12) >>> modes = [MarcatiliMode(125e-6, "He", 1.0, m=m) for m in (1, 2)] >>> rule = transverse_quadrature(modes, n_r=16, radial_rule="gauss_kronrod") >>> transform = multimode_transform(modes, rule, grid, z_max=1.0, ... components=("x",)) >>> transform.n_modes, transform.n_nodes (2, 17)