solprop.modes.vector#

Represent internal-convention Maxwell samples on physical area quadrature.

These physics records do not extend the propagation kernel contract. Bound, lossless modes use the Hermitian Poynting product and true-power amplitudes.

Functions

flux_product(electric, magnetic, weights)

Integrate the Hermitian Poynting product.

Classes

ModalNodeRule(xy, weights, region_index, ...)

Own Cartesian quadrature nodes and their material regions.

ModalProvider(*args, **kwargs)

Supply internal-convention full-vector modes on common physical nodes.

ModalSample(omega, z, electric, magnetic, ...)

Own a full Maxwell mode set in the internal FFT convention.

class solprop.modes.vector.ModalNodeRule(xy, weights, region_index, identity)[source]#

Bases: Displayed

Own Cartesian quadrature nodes and their material regions.

Attributes:
xynumpy.ndarray

Finite Cartesian positions in m, shape (n_nodes_xy, 2).

weightsnumpy.ndarray

Positive area weights in m², shape (n_nodes_weights,).

region_indexnumpy.ndarray

Non-negative int32 region indices, shape (n_nodes_region_index,).

identitycollections.abc.Mapping

Serializable construction and tail-certificate metadata.

identity: Mapping[str, JsonValue]#
region_index: Int32[ndarray, 'n_nodes_region_index']#
weights: Float[ndarray, 'n_nodes_weights']#
xy: Float[ndarray, 'n_nodes_xy 2']#
class solprop.modes.vector.ModalProvider(*args, **kwargs)[source]#

Bases: Protocol

Supply internal-convention full-vector modes on common physical nodes.

The generator is unframed. ModalSample.generator carries the absolute propagation constants, -i diag(beta) for a diagonal provider, not a co-moving generator: the frame is subtracted exactly once, by the translation layer’s one shared frame for the whole mode set (a per-mode frame destroys walk-off; see docs/dev/decisions.md). A provider that removed a frame of its own would have it removed twice.

property identity: Mapping[str, JsonValue][source]#

Return physical identity including spectral_convention=’internal’.

node_rule(omega, z=0.0, *, n_r=None, n_theta=None)[source]#

Build the physical quadrature this provider’s fields are resolved on.

One signature for every provider: the rule depends on the frequency, because a bound field’s extent does, and on the position, because the geometry may. An implementation that does not resolve its domain in frequency still takes the argument and uses it to certify the power its rule discards.

Parameters:
omegafloat

Positive angular frequency in rad/s, inside omega_domain.

zfloat, optional

Position in m inside z_domain.

n_rint or None, optional

Radial Gauss nodes per panel, at least two; None asks for the count the implementation’s own geometry needs, which is the one a caller that must not invent a resolution wants. This protocol declares no number of its own: it has no geometry to choose one from.

n_thetaint or None, optional

Angular nodes; None lets the implementation choose the count its channels’ azimuthal orders require.

Returns:
ModalNodeRule

Nodes with declared material ownership and a tail certificate in the identity.

property omega_domain: tuple[float, float][source]#

Return the declared angular-frequency interval in rad/s.

property regions: tuple[MaterialRegion, ...][source]#

Return ordered material-region records.

solve(omega, z, *, nodes)[source]#

Evaluate the modes at one frequency and longitudinal position.

Parameters:
omegafloat

Positive angular frequency in rad/s within omega_domain.

zfloat

Longitudinal coordinate in m within z_domain.

nodesModalNodeRule

Physical quadrature with declared region ownership.

Returns:
ModalSample

Three-component E/H, generator and physical diagnostics. An implementation refuses an unsupported channel by name; it does not return a sample whose valid mask is partly False.

property z_breakpoints: tuple[float, ...][source]#

Return physical path breakpoints in m.

property z_domain: tuple[float, float][source]#

Return the declared longitudinal interval in m.

class solprop.modes.vector.ModalSample(omega, z, electric, magnetic, generator, flux_metric, boundary_residual, valid, backward_electric, backward_magnetic, reciprocal_gram, gauge_id, physical_id)[source]#

Bases: Displayed

Own a full Maxwell mode set in the internal FFT convention.

Attributes:
omegafloat

Positive angular frequency in rad/s.

zfloat

Finite longitudinal position in m.

electricnumpy.ndarray

E in V/m per sqrt(W), shape (n_nodes_electric, 3, n_modes_electric).

magneticnumpy.ndarray

H in A/m per sqrt(W), shape (n_nodes_magnetic, 3, n_modes_magnetic).

generatornumpy.ndarray

Internal generator in 1/m, square (n_modes_generator_out, n_modes_generator_in). Unframed: it carries the absolute propagation constants, and the co-moving frame is subtracted once by the translation layer (see ModalProvider). It is in the internal convention of docs/dev/conventions.md section 2, so a diagonal entry is G_cc = -i (omega/c) conj(n_eff): its imaginary part is -beta and its real part is -alpha/2. A passive channel therefore has Re G_cc <= 0, and Re G_cc > 0 is gain. This is the same sign convention the linear operator has, exp(G z) for a forward amplitude. It is not the optical convention of an IndexTable (solprop.modes.tabulated), which stores n_eff with Im n_eff > 0 for loss; the bridge between the two conjugates, as every optics/internal boundary does.

flux_metricnumpy.ndarray

Dimensionless positive Hermitian flux, shape (n_modes_flux_out, n_modes_flux_in).

boundary_residualnumpy.ndarray

Non-negative scaled boundary defects, shape (n_modes_boundary_residual,).

validnumpy.ndarray

Validity mask, shape (n_modes_valid,). Every provider in this package refuses an unsupported channel by name rather than masking it, so a returned sample has this mask all True; tracking refuses a sample that does not. The field exists so that a future provider which can genuinely return a partial set has somewhere to say so.

backward_electricnumpy.ndarray or None

Reciprocal E, shape (n_nodes_backward_electric, 3, n_modes_backward_electric).

backward_magneticnumpy.ndarray or None

Reciprocal H, shape (n_nodes_backward_magnetic, 3, n_modes_backward_magnetic).

reciprocal_gramnumpy.ndarray or None

Dimensionless reciprocal flux, shape (n_modes_reciprocal_out, n_modes_reciprocal_in).

gauge_idstr

Non-empty phase and coordinate convention identifier.

physical_idstr

Non-empty physical provider identifier.

backward_electric: Complex[ndarray, 'n_nodes_backward_electric 3 n_modes_backward_electric'] | None#
backward_magnetic: Complex[ndarray, 'n_nodes_backward_magnetic 3 n_modes_backward_magnetic'] | None#
boundary_residual: Float[ndarray, 'n_modes_boundary_residual']#
electric: Complex[ndarray, 'n_nodes_electric 3 n_modes_electric']#
flux_metric: Complex[ndarray, 'n_modes_flux_out n_modes_flux_in']#
gauge_id: str#
generator: Complex[ndarray, 'n_modes_generator_out n_modes_generator_in']#
magnetic: Complex[ndarray, 'n_nodes_magnetic 3 n_modes_magnetic']#
omega: float#
physical_id: str#
reciprocal_gram: Complex[ndarray, 'n_modes_reciprocal_out n_modes_reciprocal_in'] | None#
valid: Bool[ndarray, 'n_modes_valid']#
z: float#
solprop.modes.vector.flux_product(electric, magnetic, weights)[source]#

Integrate the Hermitian Poynting product.

Parameters:
electricnumpy.ndarray

E in V/m per sqrt(W), shape (n_nodes, 3, n_modes).

magneticnumpy.ndarray

H in A/m per sqrt(W), shape (n_nodes, 3, n_modes).

weightsnumpy.ndarray

Area weights in m², shape (n_nodes,).

Returns:
numpy.ndarray

Dimensionless Hermitian flux metric, shape (n_modes, n_modes).