magnelio.post#
Post-processing components — S-parameter computation helpers. The result objects themselves come back from the analyses; this component holds the free functions and containers for custom pipelines.
- class magnelio.post.FarFieldResult(f, theta, phi, E_theta, E_phi, accepted_power=None, physical_mask=None, physical_sphere_fraction=1.0)#
Far-field pattern of one frequency.
The complex patterns are the r-independent far-zone amplitudes: the physical field at distance r is
E(r) = (E_theta θ̂ + E_phi φ̂) · e^{-jkr} / r. Like every frequency-domain field of the library they are effective (RMS) phasors per √W of incident CW power, which makesrealized_gainthe directly measured quantity and keeps the intensity free of a peak-phasor ½.- Parameters:
- f#
Frequency [Hz].
- Type:
float
- theta, phi
Evaluation angles [rad]; θ from +z (ISO convention), φ from +x.
- Type:
ndarray
- E_theta, E_phi
Far-zone amplitudes [V].
- Type:
(n_theta, n_phi) complex ndarray
- accepted_power#
Accepted input power [W] behind
gain;Noneuntil wired by the analysis.- Type:
float or None
- physical_mask#
False in directions behind an infinite ground plane;
Nonewhen the whole sphere is physical.- Type:
(n_theta, n_phi) bool ndarray or None
- physical_sphere_fraction#
Solid-angle share of the physical region (0.5 per ground plane). The image expansion makes the pattern mirror-symmetric about every such plane, so the physical-region power integral is exactly this fraction of the smooth full-sphere integral — which avoids the half-cell quadrature bias a hard mask edge would cost.
- Type:
float
- cut(*, plane='phi', angle=0.0, quantity='realized_gain')#
One pattern cut for polar plotting.
- Parameters:
plane ({"phi", "theta"}) –
"phi"fixes the azimuth: the cut runs over θ at the azimuth angle (and continues over the back half at angle + π, giving the full 0…2π polar trace)."theta"fixes the polar angle and runs over φ.angle (float) – The fixed angle [rad].
quantity (str) –
"realized_gain"(default),"gain","directivity"or"U".
- Returns:
angles, values – Cut angles [rad] and the (linear) quantity along the cut.
- Return type:
ndarray
- plot_3d(*, quantity='realized_gain', **kwargs)#
3D radiation surface of the pattern.
Extra keyword arguments go to
magnelio.plots.plot_pattern_3d()(db=,floor_db=,ax=,cmap=,title=).- Returns:
fig (matplotlib.figure.Figure)
ax (mpl_toolkits.mplot3d.axes3d.Axes3D)
- Parameters:
quantity (str)
- plot_cut(*, plane='phi', angle=0.0, quantity='realized_gain', **kwargs)#
Polar plot of one pattern cut (see
cut()).Extra keyword arguments go to
magnelio.plots.plot_pattern_cut()(db=,floor_db=,ax=,label=,title=).- Returns:
fig (matplotlib.figure.Figure)
ax (matplotlib.projections.polar.PolarAxes)
- Parameters:
plane (str)
angle (float)
quantity (str)
- property P_rad: float#
∮ U dΩ over the physical sphere.
- Type:
Total radiated power [W]
- property radiation_efficiency: float#
P_rad / P_accepted — 1.0 for a lossless model.
- class magnelio.post.SParameterResult(f_axis, channels, excitations, matrix)#
Structured multi-port S-parameter spectrum.
A single
SParameterResultholds the full S-matrixS[f, observed, excited]of a multi-port network at a fixed list of frequencies, along with the channel labels and the subset of channels that were actually excited.Channels are addressed by
(port_name, mode_idx)tuples; the convenience accessors (S(),db()) take the more ergonomicport_namestrings plus optional mode indices.- Parameters:
- f_axis#
Real, strictly positive frequencies [Hz], shape
(Nf,).- Type:
np.ndarray
- channels#
Observed channels in canonical order. Same ordering as the rows of
matrix.- Type:
tuple of (str, int)
- excitations#
Excited channels in canonical order — a subset of
channels. Same ordering as the columns ofmatrix.- Type:
tuple of (str, int)
- matrix#
Complex S-matrix.
matrix[k, i, j]isS(observed=i, excited=j)at frequencyf_axis[k].- Type:
np.ndarray, shape (Nf, n_channels, n_excitations)
Notes
The wrapper enforces channel-key consistency at construction; the matrix shape must match
(Nf, len(channels), len(excitations)).Single-excitation runs produce an SParameterResult with
n_excitations = 1; that is the natural form for one Phase-2 FIT-TD simulation result. Usemerge()to combine K such results into the full K-excitation S-matrix.- classmethod from_multiple_excitations(runs, f_axis, *, channel_order=None)#
Aggregate K single-excitation runs into a multi-column result.
Each
runs[k] = (excited_k, s_dict_k)contributes one column of the S-matrix at column indexk. Alls_dict_kmust share the same observed-channel set (otherwise the resulting matrix would have NaN holes — refuse rather than fill).- Parameters:
runs (list of ((str, int), dict[(str, int), np.ndarray])) – List of (excited, s_dict) pairs, in the order to assemble the columns. All s_dicts must share the same key set.
f_axis (np.ndarray) – Frequency axis common to all runs, shape
(Nf,).channel_order (tuple of (str, int), optional) – Override the canonical observed-channel ordering. Must enumerate every key shared by all s_dicts. Default: keys of the first s_dict in iteration order.
- Return type:
- classmethod from_single_excitation(s_dict, excited, f_axis, *, channel_order=None)#
Wrap a single-column
compute_s_parameters()dict.- Parameters:
s_dict (dict[(str, int), np.ndarray]) – Direct output of
compute_s_parameters(). Each value is a frequency-domain S-parameter spectrum of shape(Nf,).excited ((str, int)) –
(port_name, mode_idx)of the source for this run. Must be a key ofs_dict.f_axis (np.ndarray) – Frequency axis the spectra were sampled at, shape
(Nf,).channel_order (tuple of (str, int), optional) – Override the canonical ordering of observed channels. Default: keys of
s_dictin their dict-iteration order (insertion-order in CPython ≥ 3.7).
- Return type:
- classmethod merge(results)#
Combine K single-excitation
SParameterResultobjects.All inputs must share the same
f_axisand the samechannelsordering. Each input’s single excitation becomes one column of the merged matrix; column order follows the input list.- Parameters:
results (list[SParameterResult])
- Return type:
- S(out_port, in_port, *, mode_out=0, mode_in=0)#
Return the spectrum
S(out_port, in_port).Conventionally
S_{out,in}is the wave amplitude received at the out port-mode in response to a unit-amplitude wave injected at the in port-mode.- Parameters:
out_port (str) – Port labels. Must match channel keys.
in_port (str) – Port labels. Must match channel keys.
mode_out (int, optional) – Mode indices on each port (default 0).
mode_in (int, optional) – Mode indices on each port (default 0).
- Return type:
- channel_index(port, mode=0)#
Index of
(port, mode)intochannels.- Parameters:
port (str)
mode (int)
- Return type:
int
- db(out_port, in_port, *, mode_out=0, mode_in=0, floor_db=-200.0)#
Return
20·log10|S(out, in)|with a floor atfloor_db.The floor avoids
-infat frequencies where the S-parameter is at the FFT round-off (|S| ≈ 1e-12or smaller). Default-200 dBis below any physically meaningful response.- Parameters:
out_port (str)
in_port (str)
mode_out (int)
mode_in (int)
floor_db (float)
- Return type:
- excitation_index(port, mode=0)#
Index of
(port, mode)intoexcitations.- Parameters:
port (str)
mode (int)
- Return type:
int
- to_skrf(name='magnelio')#
Return the complete S-matrix as a
skrf.Network.Requires
scikit-rf(install extramagnelio[interop]) and a complete square matrix; multi-mode ports map to one network port per channel, in canonical channel order.- Return type:
skrf.Network
- Parameters:
name (str)
- to_touchstone(path)#
Write the complete S-matrix as a Touchstone
.sNpfile.Requires every observed channel to have been excited (a square matrix); raises otherwise. Touchstone ports are the channels in canonical order — a multi-mode port occupies one Touchstone port per mode; the mapping is recorded in the file’s comment header. Data are the power-wave (generalised) S-parameters on the per-mode reference impedances; the nominal
R 50of the option line does not renormalise them.- Parameters:
path (str or pathlib.Path) – Output file; conventionally
<name>.s{N}p.- Return type:
None
- property is_complete: bool#
True iff every observed channel was also excited (K×K matrix).
- magnelio.post.compute_band_s_parameters(recorder_signals, ports, excited, f_axis, *, a_threshold=1e-12)#
S-parameters of a pulsed broadband run through band DTBC ports.
The per-frequency true-mode decomposition applied to a single pulsed record: per
f_axispoint the true discrete modes of each port cross-section are solved on the stored inward chain, their exact V/I responses through the port’s fixed recording profiles are synthesised (cw_wave_phasors()— the de-stagger and the discrete wave impedance are contained in the phasors), and the recorded spectra are decomposed by the joint linear systemV_c(f) = sum_j a_j v_in[c, j] + b_j v_out[c, j] I_c(f) = sum_j a_j i_in[c, j] + b_j i_out[c, j]
over all recording channels
cand all modesjpropagating atf.S[(port, c)] = b_(port, c) / a_excited.Cost note: each frequency point runs one sparse mode solve per port (measured 30 ms – 3.6 s depending on cross-section size) plus one curl application per (channel, mode) pair — independent of the 3D run, which is needed only once for the whole axis.
- Parameters:
recorder_signals (dict) – Output of
PortSignalRecorder.finalize(), keyed by(port_label, mode_idx).ports (list) – The
PortOperatorBandDTBCinstances of the run (each must carryband_data).excited ((str, int)) –
(port_label, channel)of the pulsed source.f_axis (np.ndarray) – Evaluation frequencies [Hz]; must lie inside every port’s subspace band (content outside the band is not certified).
a_threshold (float, default 1e-12) – Relative
|a_excited|floor below which S is NaN.
- Returns:
Complex S spectrum per (port, channel) against the excited channel. Channels whose family does not propagate at a frequency carry NaN there.
- Return type:
dict[(str, int), np.ndarray]
- magnelio.post.compute_s_parameters(recorder_signals, port_modes, excited, reference_signal, f_axis, *, a_threshold=1e-12, taper_signals=False, port_normal_dx=None, port_line_params=None)#
Compute power-wave S-parameters from recorded V/I time-series.
- Parameters:
recorder_signals (dict) – Output of
PortSignalRecorder.finalize(). Keys are(port_label, mode_idx); values are(V_signal, I_signal). Both signals share the same naive time axist = arange(N)·dt; the half-step Yee stagger between V and I is corrected internally by this function.port_modes (dict[str, list[Mode]]) – Per-port ordered list of
Modeobjects. Mode index in each list must align with the recorder’smode_idx. Used to evaluate the modal reference impedanceZ(ω)at everyf_axispoint.excited ((str, int)) –
(port_label, mode_idx)of the source. Defines the denominatora_excited(f)of the S-parameter ratio.reference_signal (Signal1D) – Original user excitation waveform
s(t). Currently unused in the S-parameter math itself (a_excited is extracted from V/I for self-consistency); kept on the signature as a sanity-check anchor and so the API does not need to change whenPortOperatorModaladopts the §4.3 power-normalised injection.f_axis (np.ndarray) – Target frequencies [Hz], shape
(Nf,). Must be > 0.a_threshold (float, default 1e-12) – Relative threshold below which
|a_excited(f)| / max(|a_excited|)is treated as numerical floor and S is reported asNaN.taper_signals (bool, default False) – If
True, multiply every recorderVandIarray with a symmetric Tukey window ofalpha = 0.05(i.e. the first and last 2.5 % of samples taper to zero, the inner 95 % stay untouched) before the DFT. Suppresses the rectangular-window sidelobes caused by a non-vanishing residual at the truncation edge — useful whenenergy_stop_dbterminates the run while the tail of the transient is still ringing above the FFT noise floor. Off by default; the rectangular window is the unbiased reference for closed-form mode validation.port_normal_dx (dict[str, float] or None, default None) –
Per-port boundary-cell size along the port normal (
PortOperatorModal.plane.normal_dx), keyed by port name. When given for a port, the spatial half-cell stagger of the I sampling plane is compensated exactly (concern 1b above) for every channel of that port. Ports missing from the dict (and theNonedefault) fall back to the co-located(V ∓ Z·I)/2decomposition — correct for lumped ports, where V and I live on the same cell, and the historical behaviour for modal ports.Residual accuracy after de-stagger: the historical grading floor (measured: growth-1.4 transversal grading at ≈ −23 dB) was a V/I measurement error of the pointwise H-voltage convention and is resolved by the travelling-wave port profiles — graded transversal port meshes are first-class. For TEM modes, the pair (exact DTBC termination + the
port_line_paramsdiscrete de-stagger below) removes the remaining absorber and measurement floors entirely (straight-line benchmarks −131 … −159 dB). TE/TM modes on certified uniform chains run the Klein-Gordon DTBC, which removed the former Mur-1st ≈ −19 dB near-cutoff peak structurally; only analytical-path modes remain on Mur-1st.port_line_params (dict[(str, int), tuple] or None, default None) – Per-channel discrete line parameters
(r, q)or(r, q, z0)— the modal Courant number, discrete cut-off × dt, and static impedance constant of the exact 1D chain certified by the port termination (PortOperatorModal.dtbc_line_params). Channels present here (and whose port is inport_normal_dx) are de-staggered with the exact discrete half-cell factorλ^{1/2}instead of the continuume^{−γ·d/2}(concern 1c above). Channels carrying a non-Nonez0(certified Klein-Gordon chains) additionally replace the continuumz_modal(ω)by the exact discrete wave impedancedtbc_wave_impedance()— the continuum impedance misses the discrete wave’s V/I by O((ω·dt)², (β·dz)²), a −40…−60 dB measured-|S11|cap on λ/20 meshes. Missing channels fall back to the continuum forms.
- Returns:
Mapping
(port_label, mode_idx) → S(f_axis)of shape(Nf,), complex. One entry per channel inrecorder_signals.- Return type:
dict[(str, int), np.ndarray]
- Raises:
KeyError – If
excitedis not a key inrecorder_signals, or if a recorder channel references a port not present inport_modes.ValueError – If
f_axiscontains a non-positive frequency, or if a mode index is out of range for its port.
Notes
Degenerate mode pairs. Two modes with identical cut-offs (coax TE11 polarisations, TE_mn/TM_mn in a rectangle) span a degenerate subspace: the individual per-channel
S_ijentries within the pair depend on the (mesh-dependent) basis the eigensolver picked, and the cross-coupling between the partners does not vanish with refinement —build_modal_portwarns. The basis-independent transmission observable is the total power into the degenerate subspace,Σ_k |S(out_k, in)|²summed over the pair — the convention theexamples/straight_waveguide_*.pyacceptance scripts print. ReflectionS_mmof the excited channel itself is basis-independent for a symmetric discretisation.
- magnelio.post.ntff_transform(patches, image_planes, f, theta=None, phi=None, accepted_power=None)#
Far-field pattern from tangential surface fields at one frequency.
- Parameters:
patches (sequence of SurfacePatchSet) – The Huygens surface, in as many pieces as convenient.
image_planes (sequence of ImagePlane) – Boundary planes the surface does not cross; each doubles the patch set with its mirror image.
f (float) – Frequency [Hz].
theta (array_like, optional) – Spherical evaluation angles [rad]; θ from the +z axis, φ from the +x axis in the xy-plane. Default: 2° grids over the full sphere.
phi (array_like, optional) – Spherical evaluation angles [rad]; θ from the +z axis, φ from the +x axis in the xy-plane. Default: 2° grids over the full sphere.
accepted_power (float, optional) – Accepted input power [W] for the gain normalisation; usually wired by the analysis.
- Return type:
- magnelio.post.wall_loss_Q(result, mode=0, *, sigma=None, mu=1.0, roughness=None)#
Wall-loss Q factor of an eigenmode (perturbative power-loss).
- Parameters:
result (EigenmodeResult) – A lossless eigenmode solution (fields + mesh). PEC domain walls are read from
solver_info['boundary_conditions'](omitted faces default to PEC, the solver convention).mode (int) – Mode index into
result.modes.sigma (float, optional) – Conductivity [S/m] for walls that are not lossy metals (plain-PEC solids and PEC boundary walls). Lossy-metal solids always use their own material values.
mu (float, optional) – Relative permeability accompanying
sigma(default 1).roughness (SurfaceRoughness, optional) – Surface-roughness model for the same walls
sigmaapplies to; lossy-metal solids always use their own.None(default) is a perfectly smooth conductor.
- Return type:
WallLossQ