solprop.translate.modal_path#
Sample a guide whose geometry moves along z, and expand what a run reads of it.
A capillary’s modes are J_0(u r/a)/a, a pure
function of r/a, so a tapered capillary’s whole transform is its entrance transform
times one scalar. A solid guide’s modes change shape along a taper – a
step-index or graded-index mode moves with its normalised frequency – so the general
route’s synthesis S and projection Q move with z and every nonlinear overlap
follows them. This module is where that motion is sampled and expanded.
The node rule moves with the geometry. Each sampled position is solved on the
provider’s own rule at that position, asked for at one fixed frequency and one
fixed resolution (path_rule()): its panel edges sit on the material interfaces
there, so every solve is integrated exactly (a rule whose panel edge has left a
moving interface is wrong at first order in the displacement). Node q
of the rule at z is a point x_q(z) that moves smoothly with the interfaces, and
its material region does not change, so the field a map stores at node q is a
smooth function of z inside a section and the per-node region index the seam carries
is the same at every position. That is what lets the maps be expanded along z at all:
on a rule frozen at one position a node the interface sweeps over changes material
half way along, and the field there has a kink or a jump in z that no expansion in z
resolves. The connection’s derivative is a different quantity – the Eulerian
<psi_m | d_z psi_n> at fixed transverse position – and it keeps the union-split
rule of solprop.modes.connection.connection_nodes(), which is exactly what that
derivative needs; the two rules describe the same physical modes and differ only in
where they integrate them.
The sampling is per section, at the Chebyshev-Lobatto points of a degree the
tables’ own accuracy check demands. The sections are the provider’s declared
kink intervals, optionally subdivided (path_sections()); a section never spans a
kink, so everything sampled inside it is smooth. The degree is doubled until the
expansion reproduces a fresh solve at the positions the doubling adds – the
held-out points – to within the budget, which by default is the frequency
interpolation’s own measured error, so that sampling in z is never the coarser of the
two (sample_modal_path()).
The gauge is the provider’s own. Each position is solved and kept in the gauge the
provider returns, with no tracking alignment, because the connection
(solprop.modes.connection.band_connection()) is measured in that gauge: its
stencil is aligned to the provider’s own centre solve. A provider whose own gauge is
not continuous along the path – a sign that flips between two positions or two
frequencies, a phase that drifts – would put a gauge connection in the maps that the
measured connection does not carry, so it is refused by name
(path_snapshot()).
Module Attributes
Chebyshev degree along z a path's accuracy search starts at, per section. |
|
Highest Chebyshev degree along z a path's accuracy search tries per section. |
|
Smallest held-out error along z a path's accuracy search is asked to reach. |
|
Largest phase, rad, between two neighbouring samples' aligned overlap. |
Functions
|
Return knot values interpolated onto a run's bins, zero outside the band. |
|
Measure the moving-basis connection at every sampled position of a path. |
|
Return the provider's own rule at z, asked for exactly as rule was. |
|
Return the section edges of a moving provider's path, in m. |
|
Solve one position of a path over the band, in the provider's own gauge. |
|
Build the flat seam record of a moving guide, its maps expanded along z. |
|
Sample a moving guide along its sections, at the degree its accuracy demands. |
|
Return the Chebyshev coefficients along z of values sampled per section. |
|
Return the section of a record's maps a position lies in. |
|
Return the weights that combine a record's moving maps at one position. |
Classes
|
Hold a moving guide's snapshots at its sections' Chebyshev-Lobatto points. |
- solprop.translate.modal_path.GAUGE_PHASE_ATOL = 1e-09#
Largest phase, rad, between two neighbouring samples’ aligned overlap.
The connection is measured in the provider’s own gauge with its stencil aligned to the centre, which removes any phase the gauge accumulates along z; the maps are expanded in the same gauge without that alignment. They agree only where the provider’s gauge has no phase rate of its own, and the two qualified providers’ fields are real up to a fixed factor, so their overlaps are real to rounding. A provider whose gauge drifts would put
i phi'on the maps’ diagonal and not on the connection’s, which is refused rather than propagated.
- solprop.translate.modal_path.INITIAL_PATH_DEGREE = 4#
Chebyshev degree along z a path’s accuracy search starts at, per section.
Four is five solves per section before anything is measured. The search doubles from here, and the Chebyshev-Lobatto points nest bitwise under a doubling, so no solve is ever repeated (
solprop.translate.chebyshev.lobatto_points()).
- solprop.translate.modal_path.MAX_PATH_DEGREE = 32#
Highest Chebyshev degree along z a path’s accuracy search tries per section.
The same ceiling
solprop.translate.coupled.MAX_SECTION_DEGREEputs on a coupled flow’s sections, and for the same reason: a smooth section of a declared taper resolves at a low degree, and a degree this high means the section spans structure the provider should declare as a kink, or the caller should divide.
- class solprop.translate.modal_path.ModalPathTable(z_edges, degree, tables, z_error, gram_departure)[source]#
Bases:
DisplayedHold a moving guide’s snapshots at its sections’ Chebyshev-Lobatto points.
- Attributes:
- z_edgesnumpy.ndarray
Section edges in m, shape
(n_edges_path,), strictly increasing.- degreeint
The Chebyshev degree along z every section is sampled at, at least one; each section holds
degree + 1positions, its two ends included.- tablestuple of solprop.translate.modal_tables.DenseModalTable
One snapshot per position, section by section and increasing inside each, so a position on an edge between two sections appears twice, as the end of one and the start of the next.
- z_errorfloat
The measured held-out error of the expansion along z, dimensionless and non-negative: the largest relative maximum-norm departure of E, H or the generator from a fresh solve at the positions a degree doubling adds.
- gram_departurefloat
The largest
norm(W - I, 2)of the expanded basis at those same positions, on the rule there, dimensionless and non-negative.
- Raises:
- solprop.exceptions.ModeError
If the counts, the positions or the measurements are inconsistent.
- property n_intervals: int[source]#
Return how many sections the path is cut into.
- Returns:
- int
The section count.
- property positions: Float[ndarray, 'n_intervals_positions n_deg_positions'][source]#
Return every sampled position, section by section.
- Returns:
- numpy.ndarray
Positions in m, shape
(n_intervals, degree + 1).
- section_tables(index)[source]#
Return one section’s snapshots, increasing in z.
- Parameters:
- indexint
The section, in
[0, n_intervals).
- Returns:
- tuple of solprop.translate.modal_tables.DenseModalTable
Its
degree + 1snapshots.
- tables: tuple[DenseModalTable, ...]#
- z_edges: Float[ndarray, 'n_edges_path']#
- solprop.translate.modal_path.PATH_RTOL_FLOOR = 1e-10#
Smallest held-out error along z a path’s accuracy search is asked to reach.
The default target is the frequency interpolation’s own measured error, and a very well sampled band can measure below what a provider’s solves reproduce between two positions: the fields at a node are a sum over a quadrature and an eigenproblem, and their relative round-off is of order
1e-13to1e-12. Below this floor the search would be doubling to resolve round-off.
- solprop.translate.modal_path.in_frequency(knots, values, omega, inside)[source]#
Return knot values interpolated onto a run’s bins, zero outside the band.
The same not-a-knot cubic in frequency the dense tables interpolate their fields with (
solprop.translate.modal_tables.DenseModalTable), so a connection and the maps it is propagated beside are carried onto the run’s grid alike.- Parameters:
- knotsnumpy.ndarray
The knots in rad/s, increasing, shape (n_knots,).
- valuesnumpy.ndarray
Values at the knots, knot axis first.
- omeganumpy.ndarray
The run’s coarse bins in rad/s, shape (n_omega,).
- insidenumpy.ndarray
Which bins lie inside the knots, bool, shape (n_omega,).
- Returns:
- numpy.ndarray
The values with the frequency axis last, zero outside inside.
- solprop.translate.modal_path.path_connections(path, provider, *, sections=None, degenerate_blocks=(), basis=None, n_r=None, n_theta=None)[source]#
Measure the moving-basis connection at every sampled position of a path.
solprop.modes.connection.band_connection()at each position and every frequency knot, in the provider’s own gauge with the declared basis applied, which is the gauge the path’s snapshots are in (path_snapshot()). A position on a section’s end is measured one ulp inside it, so that its stencil is taken on that section’s own side of an edge: at a kink of the geometry the slope jumps and the connection with it, and the section that closes there must read its own one-sided limit (the rule of the coupled flow’ssolprop.translate.coupled.flow_weights(), applied where it is sampled).- Parameters:
- pathModalPathTable
The sampled path.
- providersolprop.modes.vector.ModalProvider
The moving guide.
- sectionsint or None, optional
How many of the path’s sections to measure, from the first;
Nonemeasures them all.- degenerate_blockssequence of sequence of int, optional
The declared degenerate blocks.
- basissolprop.modes.guide.ChannelBasis or None, optional
The declared constant unitary change of the channel coordinates.
- n_r, n_thetaint or None, optional
The resolution each stencil position’s rule is asked for.
- Returns:
- tuple
Per position of the measured sections, section by section, one
ModalConnectionper frequency knot.
- solprop.translate.modal_path.path_rule(provider, rule, z)[source]#
Return the provider’s own rule at z, asked for exactly as rule was.
The frequency and the resolution are read from rule’s own identity, so the rule at every position of a path is the one the provider would have built there on the same request: its panel edges on that position’s interfaces, its tail certified there. It must keep rule’s structure – the same node count and the same material region at every node – because node
qis one moving point along the whole path and the seam carries one region index per node.- Parameters:
- providersolprop.modes.vector.ModalProvider
The guide.
- rulesolprop.modes.vector.ModalNodeRule
The entrance rule, whose identity names
omega,n_randn_theta.- zfloat
Position in m inside the provider’s z domain.
- Returns:
- solprop.modes.vector.ModalNodeRule
The rule at z.
- Raises:
- solprop.exceptions.ModeError
If rule does not say how it was asked for, or the rule at z changes its node count or any node’s material region.
- solprop.translate.modal_path.path_sections(provider, sections=1)[source]#
Return the section edges of a moving provider’s path, in m.
The provider’s z domain split at every declared breakpoint strictly inside it – each a kink of the geometry, where the connection jumps with the slope – and each of those intervals divided into sections equal parts.
- Parameters:
- providersolprop.modes.vector.ModalProvider
The guide, whose
z_domainandz_breakpointsare read.- sectionsint, optional
Sections per kink interval, positive; one by default.
- Returns:
- numpy.ndarray
The edges in m, strictly increasing, from the domain’s start to its end.
- Raises:
- solprop.exceptions.ModeError
If sections is not a positive integer.
Examples
>>> import numpy as np >>> from solprop.modes.graded_index import ( ... GradedIndexProvider, PowerLawPermittivity, ... ) >>> from solprop.modes.material import IndexMaterial >>> from solprop.profiles import PiecewiseLinear >>> from solprop.translate.modal_path import path_sections >>> guide = GradedIndexProvider( ... permittivity=PowerLawPermittivity(IndexMaterial(1.45), 0.005), ... exterior=IndexMaterial(1.45 * np.sqrt(1 - 2 * 0.005)), ... radius=PiecewiseLinear( ... np.array([0.0, 4e-4, 1e-3]), np.array([25e-6, 24e-6, 22.5e-6]) ... ), ... family="LP", count=2, elements=16, extent=2.0, ... ) >>> [round(one, 6) for one in path_sections(guide, 2).tolist()] [0.0, 0.0002, 0.0004, 0.0007, 0.001]
- solprop.translate.modal_path.path_snapshot(provider, omega, z, nodes, *, degenerate_blocks=(), basis=None, gram_atol=0.0001)[source]#
Solve one position of a path over the band, in the provider’s own gauge.
Unlike
solprop.translate.modal_tables.sample_modal_table()the knots are not aligned to one another: they are kept exactly as the provider returns them, with the gauge’s continuity across the band checked instead (GAUGE_PHASE_ATOL), because the connection a moving guide is propagated with is measured in that gauge. A declared basis is applied after, as it is there.- Parameters:
- providersolprop.modes.vector.ModalProvider
The guide.
- omeganumpy.ndarray
At least four increasing positive frequencies in rad/s, shape (n_omega,).
- zfloat
Position in m inside the provider’s z domain.
- nodessolprop.modes.vector.ModalNodeRule
The rule at z (
path_rule()).- degenerate_blockssequence of sequence of int, optional
The declared degenerate blocks, retained on the table.
- basissolprop.modes.guide.ChannelBasis or None, optional
A declared constant unitary change of the channel coordinates.
- gram_atolfloat, optional
The table’s construction budget on its sampled Gram defect.
- Returns:
- solprop.translate.modal_tables.DenseModalTable
A snapshot table at z.
- Raises:
- solprop.exceptions.ModeError
If the provider’s gauge is not continuous across the band, or on anything the provider or the table refuses.
- solprop.translate.modal_path.path_transform_tables(path, omega, *, z_end, profile_error, omega_fine=None, policy_id='dense-knots-cubic-frequency-chebyshev-z')[source]#
Build the flat seam record of a moving guide, its maps expanded along z.
The moving counterpart of
solprop.translate.modal_tables.modal_transform_tables(). At every sampled position of every section the path covers up to z_end, the snapshot is evaluated on the run’s coarse grid exactly as a snapshot record’s is –Sthe stored electric profile,Q = W^-1 conj(e) w / 4with that position’s area weights – and each section’s samples become Chebyshev coefficients along z (section_coefficients()), which is the(n_intervals, n_deg)layout the record’s maps have always had. The dense reference, its partners, the coordinate dual, the node positions and the weights are stored at the section edges, which are the record’sz_edges; the region index is the path’s one per node.Outside the band the maps are explicitly zero, as a snapshot’s are.
- Parameters:
- pathModalPathTable
The sampled path.
- omeganumpy.ndarray
Coarse angular frequencies in rad/s, shape (n_omega,), increasing.
- z_endfloat
Where the run ends, m; the record keeps every section up to the one that contains it, so it covers the run and nothing is extrapolated.
- profile_errorarray_like or float
Declared non-negative profile error per section and coarse frequency, or one value for all.
- omega_finearray_like or None, optional
Fine angular frequencies in rad/s;
Noneuses the coarse grid.- policy_idstr, optional
Identity of the numeric policy the record was built under.
- Returns:
- solprop.translate.modal_tables.ModalTransformTables
The record, with maps of
n_intervalssections anddegree + 1coefficients each.
- Raises:
- solprop.exceptions.ModeError
If the path does not reach z_end, or the record does not validate.
- solprop.translate.modal_path.sample_modal_path(provider, omega, rule, z_edges, *, rtol, degree=None, measured=(0.0, 0.0), degenerate_blocks=(), basis=None, gram_atol=0.0001, known=None)[source]#
Sample a moving guide along its sections, at the degree its accuracy demands.
Each section is solved at the Chebyshev-Lobatto points of a degree, starting at
INITIAL_PATH_DEGREE, on the provider’s own rule at each position (path_rule()) and in its own gauge (path_snapshot()). The expansion along z is then checked against fresh solves at the points a doubling adds, which are the natural held-out points: they lie between every pair of the current ones and are exactly the solves the next degree needs, so the check costs nothing the doubling would not. The degree is doubled, for every section together because the seam’s degree axis is rectangular, until the largest relative departure of E, H and the generator is within rtol; the degree then kept is the one whose error was measured, not the doubled one, so that the certificate is a measurement of what is stored.With degree given, nothing is searched or measured: the path is sampled at that degree and carries the measured numbers it was certified with, which is how a run rebuilds the path its guide already qualified.
- Parameters:
- providersolprop.modes.vector.ModalProvider
The moving guide.
- omeganumpy.ndarray
The frequency knots in rad/s, shape (n_omega,), at least four.
- rulesolprop.modes.vector.ModalNodeRule
The entrance rule, which every position’s rule is asked for like (
path_rule()).- z_edgesnumpy.ndarray
Section edges in m (
path_sections()), shape (n_edges,).- rtolfloat
Budget on the held-out departure, positive and finite.
- degreeint or None, optional
A degree already qualified, which skips the search.
- measuredtuple of float, optional
The held-out error and Gram departure the qualified degree was measured at; carried, not re-measured.
- degenerate_blockssequence of sequence of int, optional
The declared degenerate blocks.
- basissolprop.modes.guide.ChannelBasis or None, optional
A declared constant unitary change of the channel coordinates.
- gram_atolfloat, optional
Each snapshot’s construction budget on its sampled Gram defect.
- knownmapping or None, optional
Snapshots already solved, keyed by position, reused rather than solved again.
- Returns:
- ModalPathTable
The snapshots and the measurements.
- Raises:
- solprop.exceptions.ModeError
If rtol is not positive and finite, the held-out departure is still above it at
MAX_PATH_DEGREE, or on anything a snapshot refuses.
- solprop.translate.modal_path.section_coefficients(values)[source]#
Return the Chebyshev coefficients along z of values sampled per section.
- Parameters:
- valuesnumpy.ndarray
Samples at each section’s Chebyshev-Lobatto points, increasing, shape
(n_intervals, degree + 1, *value_shape).
- Returns:
- numpy.ndarray
The coefficients
numpy.polynomial.chebyshev.chebval()evaluates onx = (2 z - lo - hi)/(hi - lo), same shape.
Examples
>>> import numpy as np >>> from solprop.translate.modal_path import section_coefficients >>> section_coefficients(np.array([[[1.0 + 0j], [1.0 + 0j]]])).round(12).tolist() [[[(1+0j)], [0j]]]
- solprop.translate.modal_path.transform_section(tables, z)[source]#
Return the section of a record’s maps a position lies in.
The maps are continuous across a section edge – the fields at a node are, even at a kink of the geometry, where only their slope jumps – so which side a position on an edge reads changes the value by the representation error alone and no one-sided rule is needed, unlike the coupled flow’s (
solprop.translate.coupled.flow_weights()). A position within1e-10of the span of an interior edge takes the section starting there, assolprop.translate.coupled.flow_section()does, and one a rounding outside the span the nearest end’s.- Parameters:
- tablessolprop.translate.modal_tables.ModalTransformTables
The record.
- zfloat
Position in m.
- Returns:
- int
The section index, in
[0, n_intervals).
- solprop.translate.modal_path.transform_weights(tables, z)[source]#
Return the weights that combine a record’s moving maps at one position.
The stage-bundle row of kernel schema 14 (
solprop.translate.stage.StageScalars.transform_weights): a backend evaluatesS(z) = sum_j w_j S_jandQ(z) = sum_j w_j Q_jover the maps’(n_intervals, n_deg)coefficient axes flattened, exactly as it evaluatesdPhiover its terms. Every weight outside the section containing z (transform_section()) is zero, so no section’s expansion is extrapolated.Nonewhere there is nothing to combine: no record, a record carrying no maps, or maps of one coefficient – one interval of degree zero, a constant geometry – whose one term is the map itself. That keeps the stage bundle of every problem whose maps do not move unchanged.- Parameters:
- tablessolprop.translate.modal_tables.ModalTransformTables or None
The record, or
None.- zfloat
Position in m.
- Returns:
- numpy.ndarray or None
Float64, shape
(n_intervals * n_deg,), orNone.
Examples
>>> from solprop.translate.modal_path import transform_weights >>> transform_weights(None, 0.5) is None True