perturbation.PerturbationResponse

perturbation.PerturbationResponse(
    freqs,
    X,
    L,
    est,
    n_solve,
    forcing,
    forcing_kinds,
    cidx=(0, 1, 2),
    terminals=None,
    node_names=(),
    geometry=None,
    frozen=(),
    scalar_names=(),
)

Stored independent perturbation fields; extracts N x N matrices on demand.

Attributes

Name Description
n Matrix dimension: the number of driven perturbation waves (2, 3, or more with scalars).
n_char Characteristic count per edge: 3 (f, g, h) for inert flow, plus one per transported scalar.

Methods

Name Description
acoustic_scattering_matrix 2x2 acoustic scattering matrix, incoming (f_a, g_b) -> outgoing (g_a, f_b).
acoustic_transfer_matrix The 2x2 acoustic (f, g) transfer matrix (incoming entropy = 0).
animate_field Animate the forced spatial field at one frequency over a phase cycle (slider + play).
contribution_labels Labels for :meth:contributions at edge: (output_labels, source_labels).
contributions Break the wave at edge into the contribution of each terminal’s incoming wave.
field_along_network Reconstruct the forced spatial field at one frequency along every root->leaf path.
intensity_along_network Acoustic intensity (or energy density) along the developed length, at one frequency.
multiport_scattering_labels Per-wave symbols for the multiport columns (incoming) and rows (outgoing).
multiport_scattering_matrix Whole-network scattering matrix mapping every terminal’s incoming wave to every outgoing one.
plot_contributions Plot the source attribution of the wave at edge: one panel per output wave.
plot_multiport_scattering_matrix Plot the whole-network multiport scattering matrix with terminal-tagged labels.
plot_scattering_matrix Plot the scattering matrix between a and b with station-tagged labels.
plot_transfer_matrix Plot the transfer matrix T_ba in basis, with edge-subscripted labels.
scattering_labels Ordered (station, char-index) tags of the SM’s incoming and outgoing waves.
scattering_matrix Scattering matrix between stations a (upstream) and b.
transfer_matrix Transfer matrix T_ba mapping the driven waves at a to those at b.
transfer_residual Seriality residual of the fitted transfer matrix between edges a and b.

acoustic_scattering_matrix

perturbation.PerturbationResponse.acoustic_scattering_matrix(a, b)

2x2 acoustic scattering matrix, incoming (f_a, g_b) -> outgoing (g_a, f_b).

Ordering matches the full :meth:scattering_matrix (reflection at a first, transmission to b second); tm_fg_to_sm2 yields the classic (f_b, g_a) ordering, so the two rows are swapped.

acoustic_transfer_matrix

perturbation.PerturbationResponse.acoustic_transfer_matrix(a, b)

The 2x2 acoustic (f, g) transfer matrix (incoming entropy = 0).

Reconstructed from the two acoustic forcings alone – well conditioned even at a quiescent mean state, where the full 3x3 degenerates because the entropy wave stops convecting (tau_0 -> inf). The acoustic columns pin the incoming entropy to zero, so this is the clean acoustic block whether or not the entropy wave is also driven (no entropy-noise contamination).

animate_field

perturbation.PerturbationResponse.animate_field(
    freq,
    *,
    incoming=None,
    variable='p',
    basis=None,
    root=None,
    n_x=160,
    n_frames=60,
    normalize=True,
    envelope=True,
    **layout,
)

Animate the forced spatial field at one frequency over a phase cycle (slider + play).

Pass a list of variable names, or a basis flavor (which expands to its three components), to overlay several quantities; all share the single forcing frequency, so they are phase-locked.

Parameters

Name Type Description Default
freq float Target frequency (Hz); the nearest value in :attr:freqs is used. required
incoming array_like of complex Per-source amplitudes (see :meth:field_along_network); default unit on the first source. None
variable str or sequence of str Plotted quantity, or several to overlay (see :meth:field_along_network); default "p". Ignored when basis is given. 'p'
basis str A flavor from :data:nefes.perturbation.operator.characteristics.BASIS_LABELS; overlays its three components and overrides variable. None
root int Developed-length origin element (default: a mean-flow inlet). None
n_x int Interior samples per duct (default 160) and phase frames per cycle (default 60). 160
n_frames int Interior samples per duct (default 160) and phase frames per cycle (default 60). 160
normalize bool Scale each overlaid quantity’s peak magnitude to 1 (default True). True
envelope bool Shade the +/- \|psi(x)\| span behind each animated line (default True). True
**layout Forwarded to Figure.update_layout. {}

Returns

Name Type Description
plotly.graph_objects.Figure

contribution_labels

perturbation.PerturbationResponse.contribution_labels(edge)

Labels for :meth:contributions at edge: (output_labels, source_labels).

output_labels are the driven characteristics at edge (rows, f/g/h subscripted by the edge id – edge names are not meaningful); source_labels are the incoming wave each excitation drives, subscripted by its terminal node id and name (columns), so a plotted entry reads source → output (e.g. g₇:Outlet1 → f₅: the incoming wave at terminal Outlet1 contributing to f at edge 5).

contributions

perturbation.PerturbationResponse.contributions(edge, *, incoming=None)

Break the wave at edge into the contribution of each terminal’s incoming wave.

Every driven excitation is a unit incoming wave at one terminal with all others zero, so by linearity the perturbation field at edge is the exact superposition of one contribution per source. This is the physically honest “where does what I see at this edge come from” decomposition: each term is a genuine one-way path gain (a multiport scattering entry generalized to an internal edge), free of the common-driver confounding that makes a least-squares transfer matrix overstate one edge’s influence on another (the residual measures predictability, this measures contribution).

Parameters

Name Type Description Default
edge int Edge whose wave is decomposed. required
incoming array_like of complex Complex amplitude assigned to each source, as a 1-D array with one entry per source in the order of :meth:contribution_labels (the excitation-column order). A source is the incoming characteristic wave entering at one terminal – f driven into the inlet, g driven into an outlet, etc. – expressed in characteristic units. Use it to set the operating scenario you care about: e.g. [1, 0, 0] for unit forcing at the first terminal and silence elsewhere, or the actual/measured incoming amplitudes at each terminal. The contribution of source k is then its unit response scaled by incoming[k], and the columns sum to the total field for that scenario. Default (None): unit amplitude on every source, so each column is the bare per-source transfer function (gain) and the columns are directly comparable. None

Returns

Name Type Description
ndarray Shape (n_omega, n, n_source): entry [:, c, k] is the amplitude of driven characteristic c (see :attr:cidx) at edge produced by source k.

Raises

Name Type Description
ValueError If edge is out of range or incoming has the wrong length.

field_along_network

perturbation.PerturbationResponse.field_along_network(
    freq,
    *,
    incoming=None,
    variable='p',
    spec=None,
    root=None,
    n_x=160,
)

Reconstruct the forced spatial field at one frequency along every root->leaf path.

Picks the stored frequency nearest freq and superposes the driven sources by incoming, then reconstructs the continuous perturbation field inside every duct (see :func:nefes.perturbation.fields.modeshape.reconstruct_field).

Parameters

Name Type Description Default
freq float Target frequency (Hz); the nearest value in :attr:freqs is used. required
incoming array_like of complex One amplitude per driven source (column of :attr:X), in excitation order. Default: unit amplitude on the first source, zero on the rest. None
variable str Plotted quantity ("p", "u", "rho", "mdot", "f", "g", "h"); default "p". Ignored when spec is given. 'p'
spec tuple A (basis_flavor, component) pair (e.g. from :func:nefes.perturbation.fields.modeshape.resolve_specs) selecting any basis component directly; overrides variable. None
root int Developed-length origin element (default: a mean-flow inlet). None
n_x int Interior samples per duct (default 160). 160

Returns

Name Type Description
list of nefes.perturbation.fields.modeshape.PathField

Raises

Name Type Description
ValueError If the result carries no geometry, or incoming has the wrong length.

intensity_along_network

perturbation.PerturbationResponse.intensity_along_network(
    freq,
    *,
    incoming=None,
    energy_density=False,
    root=None,
    n_x=160,
)

Acoustic intensity (or energy density) along the developed length, at one frequency.

The spatial companion of :meth:field_along_network: instead of a primitive variable it reconstructs the Myers acoustic intensity I(x) [W/m^2] (downstream positive) – or the energy density e(x) [J/m^3] when energy_density=True – along every root->leaf path, so one can read where acoustic power flows and where it is produced or absorbed.

Parameters

Name Type Description Default
freq float Target frequency (Hz); the nearest value in :attr:freqs is used. required
incoming array_like of complex One amplitude per driven source (column of :attr:X); default unit on the first source. None
energy_density bool Return the energy density instead of the intensity (default False). False
root int Developed-length origin element (default: a mean-flow inlet). None
n_x int Interior samples per duct (default 160). 160

Returns

Name Type Description
list of nefes.perturbation.fields.modeshape.PathField values is the real intensity (or energy density).

multiport_scattering_labels

perturbation.PerturbationResponse.multiport_scattering_labels()

Per-wave symbols for the multiport columns (incoming) and rows (outgoing).

Each wave is its characteristic symbol (f/g/h) subscripted by the node id of the terminal it lives on (e.g. f<sub>0</sub>). The bare id keeps the matrix labels uncrowded – a multiport entry reads f₀ → g₀ – while still uniquely identifying the terminal (cross-reference :attr:node_names for the element name).

multiport_scattering_matrix

perturbation.PerturbationResponse.multiport_scattering_matrix()

Whole-network scattering matrix mapping every terminal’s incoming wave to every outgoing one.

The rigorous boundary-independent descriptor of a network with more than two terminals (where pairwise edge transfer matrices across a branch do not exist). Generally rectangular: with entropy the incoming set is #terminals + #inflow-terminals and the outgoing set #terminals + #outflow-terminals (acoustic-only it is the square N x N, N = #terminals). Columns follow the excitation order, rows are terminal-major; both are tagged by :meth:multiport_scattering_labels.

Returns

Name Type Description
ndarray Shape (n_omega, n_outgoing, n_incoming).

Raises

Name Type Description
ValueError If the network was not driven at every terminal (rebuild with forcing=None).

plot_contributions

perturbation.PerturbationResponse.plot_contributions(
    edge,
    freqs=None,
    *,
    incoming=None,
    normalize='auto',
    **kwargs,
)

Plot the source attribution of the wave at edge: one panel per output wave.

The honest “where does what I see at this edge come from” view. There is one panel per driven characteristic at edge (f/g/h), and within each panel one curve per source (each terminal’s incoming wave), magnitude over phase, so the sources can be compared directly against one another for that output wave – which dominates, where they cross over. Unlike a transfer matrix this is well defined across branch points: it decomposes a contribution rather than asserting one edge determines another.

Parameters

Name Type Description Default
edge int Edge whose wave is decomposed. required
freqs array_like x-axis values (default: self.freqs, in Hz). None
incoming array_like Per-source incoming amplitudes for a specific scenario (see :meth:contributions); default is unit amplitude on every source. None
normalize (auto, True, False) Scale each panel by its dominant source’s peak magnitude (a scalar – never by the anchor’s frequency curve, which would blow up at its nulls), so the leading source peaks at 1 and the rest read as honest fractions of it. "auto" (default) normalizes only when incoming is None (per-source gains have no absolute scale), and shows absolute magnitudes once an incoming scenario fixes the amplitudes. "auto"
**kwargs Forwarded to :func:nefes.plotting.plot_complex_matrix. {}

Returns

Name Type Description
plotly.graph_objects.Figure

plot_multiport_scattering_matrix

perturbation.PerturbationResponse.plot_multiport_scattering_matrix(
    freqs=None,
    **kwargs,
)

Plot the whole-network multiport scattering matrix with terminal-tagged labels.

Wraps :meth:multiport_scattering_matrix and labels every entry by its own terminal-subscripted waves (e.g. f₀ → g₀ for the inlet reflection, f₀ → f₇ for transmission to terminal 7).

Parameters

Name Type Description Default
freqs array_like x-axis values (default: self.freqs, in Hz). None
**kwargs Forwarded to :func:nefes.plotting.plot_scattering_matrix. {}

Returns

Name Type Description
plotly.graph_objects.Figure

plot_scattering_matrix

perturbation.PerturbationResponse.plot_scattering_matrix(
    a,
    b,
    freqs=None,
    *,
    basis='char',
    **kwargs,
)

Plot the scattering matrix between a and b with station-tagged labels.

Convenience wrapper that converts the matrix to basis (via :meth:scattering_matrix) and labels it to match, supplying both the station edges and the incoming/outgoing wave partition so each entry is titled by its own station-subscripted waves (e.g. f₁ -> g₁ for a reflection at edge a).

Parameters

Name Type Description Default
a int Upstream / downstream edge ids of the cut. required
b int Upstream / downstream edge ids of the cut. required
freqs array_like x-axis values (default: self.freqs, in Hz). None
basis str Wave flavor ("char" or "riemann" – diagonal in the waves). Default "char". 'char'
**kwargs Forwarded to :func:nefes.plotting.plot_scattering_matrix. {}

Returns

Name Type Description
plotly.graph_objects.Figure

plot_transfer_matrix

perturbation.PerturbationResponse.plot_transfer_matrix(
    a,
    b,
    freqs=None,
    *,
    basis='char',
    **kwargs,
)

Plot the transfer matrix T_ba in basis, with edge-subscripted labels.

Convenience wrapper that converts the matrix to basis (via :meth:transfer_matrix) and labels it to match, supplying the station edges so each entry reads var_a -> var_b (e.g. f₁ -> f₂) instead of the ambiguous f -> f. Unlike the free :func:nefes.plotting.plot_transfer_matrix, the basis here genuinely changes the matrix values, not just the labels.

Parameters

Name Type Description Default
a int Upstream / downstream edge ids; the matrix maps the waves at a to those at b. required
b int Upstream / downstream edge ids; the matrix maps the waves at a to those at b. required
freqs array_like x-axis values (default: self.freqs, in Hz). None
basis str Variable flavor (characteristics.BASIS_LABELS; e.g. "char", "primitive", "network"). Default "char". 'char'
**kwargs Forwarded to :func:nefes.plotting.plot_transfer_matrix. {}

Returns

Name Type Description
plotly.graph_objects.Figure

scattering_labels

perturbation.PerturbationResponse.scattering_labels(a, b)

Ordered (station, char-index) tags of the SM’s incoming and outgoing waves.

scattering_matrix

perturbation.PerturbationResponse.scattering_matrix(a, b, basis='char')

Scattering matrix between stations a (upstream) and b.

Dimension is self.n (2 acoustic-only, 3 with entropy). Incoming waves (a’s downstream-running + b’s upstream-running) map to the outgoing ones; ordering follows matrices.scattering_labels. basis may only be a flavor diagonal in the characteristics (char or riemann); mixed flavors are undefined for a scattering matrix.

It delegates to :meth:transfer_matrix, so the same :class:TransferMatrixWarning fires when a and b straddle an internal branch point or the response is under-determined (see :meth:transfer_residual).

transfer_matrix

perturbation.PerturbationResponse.transfer_matrix(a, b, basis='char')

Transfer matrix T_ba mapping the driven waves at a to those at b.

The dimension is self.n (2 for the default acoustic excitation, 3 with entropy). Read along each edge’s own arrow; basis selects the variable flavor (characteristics.BASIS_LABELS). Shape (n_omega, n, n).

Always returns a matrix. If a and b lie on opposite sides of an internal branch point no transfer matrix exists, so the returned T is only the least-squares best fit and a :class:TransferMatrixWarning is emitted – inspect :meth:transfer_residual and prefer :meth:multiport_scattering_matrix. A second warning flavor fires when the response is too under-determined to tell either way. In both cases a :class:TransferMatrixWarning is emitted.

transfer_residual

perturbation.PerturbationResponse.transfer_residual(a, b)

Seriality residual of the fitted transfer matrix between edges a and b.

The max-over-frequency relative residual of T = Wb @ pinv(Wa) (see :meth:_seriality_residual). Near zero means a genuine transfer matrix exists (the edges are in series); a large value means they straddle an internal branch point and :meth:transfer_matrix returns only a least-squares best fit. Computed in the characteristic basis, so it is independent of the basis the matrix is later expressed in. Beware: when the response is under-determined (:meth:_underdetermined) this is structurally ~0 and does not certify seriality.

Returns

Name Type Description
float
Back to top