Palace guide¶
Use gsim.palace for driven RF and microwave simulations, eigenmodes, electrostatics, and boundary modes.
This guide covers solver configuration, meshing, and result processing. Class and function links open the API reference.
Solver settings¶
Numerical and problem-specific settings live under sim.solver. Each simulation
exposes its applicable problem group alongside the common controls:
import gsim.palace as pa
sim = pa.EigenmodeSim()
sim.solver.order = 1
sim.solver.linear.tolerance = 1e-9
sim.solver.linear.max_iterations = 400
sim.solver.eigenmode.num_modes = 2
sim.solver.eigenmode.target = 4e9 # Hz; required before meshing/exporting
sim.solver.eigenmode.tolerance = 1e-9
sim.solver.eigenmode.save = 2
The linear tolerance controls each linear solve. The eigenmode tolerance controls
eigenvalue convergence. They are independent. All Palace simulation types default
to GMRES with complex MUMPS multigrid, field order 2, 400 maximum linear
iterations, and "CPU". Linear tolerance defaults to 1e-6, except for
EigenmodeSim, whose linear and eigenvalue tolerances both default to 1e-9.
An omitted linear tolerance uses the simulation-specific default through grouped
dictionaries, settings models, and legacy numerical constructors or assignments.
An explicitly supplied tolerance, including 1e-6, is preserved.
The configurable multigrid controls live under sim.solver.linear:
| Setting | Default | Meaning |
|---|---|---|
solver_type |
"MUMPS" |
Coarse solver backend; "Default" asks Palace to choose. |
mg_max_levels |
100 |
Maximum multigrid levels; Palace uses the available hierarchy. Set 1 for a full-system solve. |
pc_mat_real |
False |
Set True to use a real approximation of the preconditioner matrix. |
complex_coarse_solve |
True |
Use a complex coarse solve for complex-valued problems. |
Real-only problems such as electrostatics still solve real equations. The complex controls do not turn materials or fields into complex quantities in those problems. MUMPS no longer forces one Krylov iteration, disables multigrid, or disables the mesh error estimator or divergence-free projection. Explicit settings are exported without backend-specific overrides.
sim.solver.linear.pc_mat_real = True
sim.solver.linear.complex_coarse_solve = False # lower-memory real approximation
sim.solver.linear.mg_max_levels = 1 # full-system solve; can need much more RAM
solver_type="SuperLU", "STRUMPACK", and "Default" remain available.
preconditioner="AMS" or "BoomerAMG" applies when solver_type="Default".
Use pc_mat_real=True and complex_coarse_solve=False to request a real
preconditioner with another backend. These settings can also be supplied to
set_solver(...) or in the constructor's solver={"linear": ...} group.
The defaults are a starting point. Check convergence and mesh resolution for each problem, especially when extracting quality factors. Driven adaptive-sweep controls are separate and retain their existing defaults.
You can also supply grouped settings at construction:
sim = pa.EigenmodeSim(
solver={
"order": 1,
"linear": {"tolerance": 1e-6, "max_iterations": 400},
"eigenmode": {"num_modes": 2, "target": 4e9, "tolerance": 1e-8, "save": 2},
}
)
Other simulation types expose their own problem group:
driven = pa.DrivenSim(solver={"driven": {"fmin": 1e9, "fmax": 10e9, "num_points": 40}})
driven.solver.driven.adaptive_tol = 0.01
electrostatic = pa.ElectrostaticSim()
electrostatic.solver.electrostatic.save_fields = 2
boundary = pa.BoundaryModeSim()
boundary.solver.boundary_mode.freq = 5e9
boundary.solver.boundary_mode.num_modes = 2
All four share nine controls: order, device, and
linear.{solver_type, preconditioner, tolerance, max_iterations, mg_max_levels,
pc_mat_real, complex_coarse_solve}.
| Simulation | Problem group | Problem settings | Total settings |
|---|---|---|---|
| DrivenSim | solver.driven |
13 | 22 |
| EigenmodeSim | solver.eigenmode |
8 | 17 |
| ElectrostaticSim | solver.electrostatic |
1 | 10 |
| BoundaryModeSim | solver.boundary_mode |
7 | 16 |
sim.set_solver(...) replaces the common controls using the same defaults as
the simulation constructor, including its linear tolerance, while preserving the problem-specific settings. Assign individual
attributes to retain other common values. The generated Palace JSON continues to
use Solver.Order, Solver.Device, Solver.Linear, and the applicable problem block.
Existing code¶
sim.numerical, NumericalConfig, set_numerical(...), the top-level problem
attributes, and legacy constructor arguments remain supported and emit
DeprecationWarning messages pointing to the grouped API. Problem-specific setters
such as set_eigenmode(...) remain available as convenience methods. Old serialized
inputs still load; simulation serialization uses the grouped solver field.
NumericalConfig retains its flat serialization format. Migrate direct access as follows:
| Previous access | Grouped access |
|---|---|
sim.numerical.order |
sim.solver.order |
sim.numerical.tolerance |
sim.solver.linear.tolerance |
sim.eigenmode.target |
sim.solver.eigenmode.target |
sim.driven.fmin |
sim.solver.driven.fmin |
sim.electrostatic.save_fields |
sim.solver.electrostatic.save_fields |
sim.boundary_mode.freq |
sim.solver.boundary_mode.freq |
MUMPS behavior change¶
Reloading a saved gsim settings model with solver_type="MUMPS" now uses the saved
iteration limit and the new multigrid defaults when multigrid controls are absent.
Previously, gsim forced MaxIts=1 and MGMaxLevels=1, regardless of the saved
iteration limit. Old settings still load, but their generated Palace configuration
and solve behavior can change. Explicit saved tolerances are retained.
For a full-system solve, set mg_max_levels=1; set max_iterations=1 to also
request the former iteration limit. This does not reproduce the entire old block:
gsim no longer writes the forced EstimatorMaxIts=0, EstimatorTol=1e-6,
DivFreeMaxIts=0, or DivFreeTol=1e-6. Those auxiliary controls are not currently
exposed by the settings API, so Palace applies its defaults. Exposing them is
deferred; there is no automatic legacy-mode fallback.
Calling set_numerical() without an explicit order now uses 2, matching the
constructor and set_solver(). Pass order=1 to retain the previous setter behavior.
Such calls emit a temporary FutureWarning explaining this change. Explicit-order
calls emit the usual API deprecation warning. The new grouped API emits neither.
Update individual controls directly to preserve other settings:
sim.set_solver(...) replaces the common controls with the supplied values and
their defaults, preserving the existing problem group, such as the eigenmode
target. Assigning sim.solver = ... replaces the entire solver configuration,
including its problem group; a new eigenmode group defaults to target=None.
sim.numerical is a deprecated alias for sim.solver, so its model_dump() uses
the grouped format. NumericalConfig(**sim.numerical.model_dump()) accepts that
format and copies the nine common numerical controls into a flat legacy model.
For new code, use sim.solver.model_copy(deep=True) to copy all solver settings.
See result validation for independent solver, adaptive sampling, mode identity, wave-port mode continuity, and provenance checks.
Numeric wave-port boundaries¶
The default waveport_boundary="pec" approximates finite conductivity, impedance
and absorbing boundaries as PEC inside the port eigenproblem. Choose
"inherit" to retain those physical conditions when solving the port mode:
from gsim.palace import DrivenSim
sim = DrivenSim()
sim.set_driven(f=50e9, waveport_boundary="inherit")
# After geometry/ports are configured and the mesh is generated:
# config = json.loads(sim.write_config().read_text())
# "WavePortPEC" not in config["Boundaries"]
Only the generated Boundaries.WavePortPEC override changes. The physical 3D
conductivity, impedance and absorbing boundaries remain identical. Lumped ports
are unaffected. This controls a modeling approximation; it does not fix Palace
mode selection or establish lossy S-parameter normalization accuracy. Check the
port fields and solver convergence for the chosen Palace runtime.
The policies differ only when absorbing, conductivity or impedance boundaries
exist; without these Robin terms, both omit WavePortPEC. A later set_driven()
call rebuilds the driven settings, so repeat waveport_boundary="inherit" when
updating other sweep options if you want to retain it.
Material override precedence is separate; see PR #277.
Result validation¶
Simulation Classes¶
Floquet eigenmodes¶
Set the signed cell phase in radians. The wave vector uses the measured donor-to-receiver mesh translation; the eigenvalue
search frequency does not determine the cell length. An optional periodic_length in mesh units (micrometers for generated
meshes) checks that the periodic faces have the expected separation:
from gsim.palace import EigenmodeSim
sim = EigenmodeSim()
sim.set_eigenmode(target=40e9, floquet=True, phi_target=-0.4, periodic_length=100.0)
sim.solver.eigenmode.compute_floquet_wave_vector(periodic_axis="x")
# [-0.004, 0.0, 0.0] rad/um
After setting geometry and stack, sim.mesh(periodic_axis="x") records the actual translation. sim.write_config() writes
it as Boundaries.Periodic.BoundaryPairs[0].Translation, and computes FloquetWaveVector from that measured length. A length
mismatch raises ValueError; omit periodic_length to use the measured value without an expected-length check. Domain
padding can change the separation of periodic faces. Generated translations use the CAD face planes rather than the
tolerance-padded OCC bounding boxes.
Zero, negative phases, and the Brillouin-zone endpoints +/-pi are supported without wrapping. With
Palace's phase convention, the receiver field is
exp(-1j * phi_target) times the donor field. The GDS mesher supports periodic axes x and y; the wave-vector helper and
config generation also support z for supplied mesh metadata.
n_eff_guess is retained as a deprecated compatibility argument and no longer affects the result. Passing it explicitly to
set_eigenmode() emits a DeprecationWarning. Direct calls to
compute_floquet_wave_vector() now require an explicit length on the config or as a periodic_length argument; the old l0
argument and frequency-based length estimate are removed. Old mesh results without translation metadata must be regenerated.
Electrostatic terminals¶
An ElectrostaticSim terminal selects conductors by connected net, by region or by
layer. A net is the metal that touches on one layer plus the metal that vias join on
other layers, so two electrodes on one layer are two nets (see sim.nets()).
sim.add_terminal("TOP", net="PLUS") # net by port name, or by its name in sim.nets()
sim.add_terminal("LEFT", region=(0, 0, 20, 20), layer="m1") # nets with an m1 shape in the box
sim.add_terminal("SHIELD", net="net3", floating=True) # solved, then eliminated
sim.add_terminal("T1", layer="metal5") # a whole layer, as before
sim.unassigned_conductors = "ground" # the default; or "floating"
print(sim.resolve_terminals()) # check the selection before meshing
cap = sim.load_capacitance(sim.run()) # floating terminals are eliminated
cap.between("TOP", "LEFT")
cap.to_ground("LEFT")
cap.pairs() # every pair and every terminal-to-ground value, as a table
- Net names are the names of the ports on the net joined by
+, ornet<k>(kcounts from 1 in layout order).net=matches the whole name or any one port name; if several nets carry the same port name they all go into that terminal.net<k>changes when the layout is edited, so prefer port names orregion=. GDS text labels are not used. region=is a box(xmin, ymin, xmax, ymax)or a list of at least three(x, y)points, in um. It selects every net with a conductor or via shape that overlaps the region, only onlayerwhen that is given. It always selects whole nets, because part of a conductor cannot be at another potential.layer=alone keeps the old behaviour while no terminal usesnet,regionorfloating: every shape on that layer, and vias by z-overlap. Once any terminal uses nets,layer=means every net with a shape on that layer, including the metal that vias join to it on other layers.net=cannot be combined withregion=orlayer=.- Conductors in no terminal are grounded, as before. With
unassigned_conductors="floating"each becomes an extra terminal named after its net. PEC blocks are always grounded. A net in two terminals is an error. - Floating conductors are solved as terminals, one extra Palace excitation each,
and
load_capacitanceremoves them with a Schur complement of the Maxwell matrix,C_tt - C_tf C_ff^-1 C_ft, which is exact for electrostatics. Passeliminate_floating=Falseto keep them. The rows are the terminals in the order they were added, then the automatic floating terminals in net order. - Vias and merging.
mesh()merges vias closer thanmerge_via_distance(2 um by default). If that would join the vias of two nets it raises aValueErrornaming the via layer and the nets; setmerge_via_distancebelow the gap between them. Merging also fills the gaps between vias, so their area grows; a warning names the layer and the area ratio when it grows by more than 5%. Passmerge_via_distance=0to keep every via as drawn. - Changing the selection style. A mesh made for layer terminals cannot serve net
terminals and the other way round;
write_config()raises and asks you to callmesh()again. In net modewrite_config()also writesterminals.json, whichload_capacitancereads when the simulation object is new. - Reading the results.
cap.between(a, b)is the capacitance between two electrodes andcap.to_ground(a)the one to ground;cap.pairs()lists both kinds apart. If no conductor is grounded,to_groundcomes out near zero.
Capacitance¶
Saved fields and S-parameters¶
load_fields accepts a simulation directory, Palace output directory, or results
dictionary. Driven, BoundaryMode, and Eigenmode volume and boundary outputs are
supported. Enable field saving with save_step >= 1 for driven simulations or
save >= 1 for eigenmode simulations.
from gsim.palace.results import load_fields
fields = load_fields("eigenmode_sim/output/palace", mode=1)
electric = fields["E_real"] + 1j * fields["E_imag"]
assert electric.shape == (fields.n_points, 3)
surface = load_fields("eigenmode_sim/output/palace", mode=1, boundary=True)
mode=1 selects Cycle000001 from Eigenmode or BoundaryMode output; a missing
mode or a mode containing only mesh metadata raises an error. Use cycle=N to
select an exact ParaView cycle instead, including metadata cycles. Omit both
selectors to load the last cycle containing solution fields, skipping final
Indicator, Rank, or attribute arrays. mode and cycle are mutually
exclusive. For driven output, excitation=N selects the excitation directory.
A missing excitation now raises FileNotFoundError instead of selecting another
excitation. Metadata-only output raises ValueError by default; explicit cycle=N
still permits inspecting that metadata. Mode/cycle selection was also checked
against saved Palace Floquet run palace-552ac5e8, in addition to synthetic tests.
load_sparams converts frequency, dB magnitude, and degree phase columns to
floating-point arrays. A whitespace-padded -inf dB value remains negative
infinity and converts to exact zero, without a magnitude floor:
from pathlib import Path
from tempfile import TemporaryDirectory
import numpy as np
from gsim.palace.results import load_sparams
with TemporaryDirectory() as directory:
output = Path(directory)
(output / "port-S.csv").write_text(
"f (GHz), |S[2][1]| (dB), arg(S[2][1]) (deg.)\n"
"1.0, -inf , 0.0\n"
)
parameters = load_sparams(output)
assert np.isneginf(parameters.s21.db[0])
assert parameters.s21.complex[0] == 0j
assert parameters.to_skrf().s[0, 1, 0] == 0j
Malformed numeric cells reject the entire CSV, including otherwise valid rows.
Errors report the path, column and one-based data row, excluding the header
and blank lines. NaN, positive infinity and empty numeric cells raise ValueError;
phase and frequency must be finite, while dB magnitudes permit negative infinity.
Mesh¶
Presets select mesh-size targets near conductors and in the bulk. Explicit
refined_mesh_size and max_mesh_size arguments override those targets.
auto_size=True only scales a preset when refined_mesh_size is omitted;
an explicit size is always preserved, even with auto_size=True.
Feature detection merges touching polygons on each conductor layer before checking trace/neck widths and gaps, including notches and holes. Distances below two layout database units are ignored as grid artifacts. Gap searches for sizing stop at the smallest width already found (and at 500 um); larger gaps cannot change the size. Cross-layer spacing and arbitrary tapered or curved widths are not measured.
Resolution warnings compare the effective size with the detected conductor
feature divided by cells_per_feature (2 by default). They suggest a smaller
explicit size when an override is present. This feature-based rule is a heuristic;
mesh validation and a lack of warnings do not establish convergence of resonance
frequencies, Q, or S-parameters. Compare results across mesh refinements.
sim.mesh() also reports Estimated Field DOFs for tetrahedral 3D driven and
eigenmode problems, using the configured field order. For order 2, the estimate
is 2 * unique_edges + 2 * unique_triangular_faces, counting interior and shared
entities once. Geometry order and field order are separate. This is the input
mesh count before Palace splits interior boundaries, applies periodic constraints
or refines the mesh; it is not a bound on the final solve size. Other problem
types and mixed/non-tetrahedral meshes have a null estimate.
Mesh results expose a JSON-compatible result.metadata dictionary, also written
to metadata.json beside the mesh and included in cloud input uploads:
result = sim.mesh()
metadata = result.metadata
metadata["schema_version"] # 1
metadata["mesh"]["topology"] # edges and triangular_faces
metadata["mesh"]["field_dofs"]["estimated_field_dofs"]
metadata["mesh"]["kappa"]["max"] # numerical distortion, not formatted text
sim.write_config() refreshes the file using the effective config's field order,
problem type, solver settings and refinement controls. The result object is a
snapshot from meshing. Consumers should accept additional metadata keys so new
measurements can be added. Allocation rules remain in DataLab; these are sizing
hints. gsim sends supported tetrahedron and field-DOF counts to DataLab before
upload, including with older SDK versions. See
cloud sizing metadata for the calibration assumptions.
The root-level metadata.json name is reserved for these diagnostics. It is
excluded from the Palace result-cache key so measurements can evolve without
rerunning identical mesh/config inputs. The full compute_dir_digest() includes
the metadata by default; the cache key is not a complete bundle-integrity hash.
sim.mesh() reports Worst element distortion, κ in its summary. The value is
also available in result.mesh_stats["kappa"]["max"] and sim.print_mesh_stats().
It uses Palace/MFEM's normalized Jacobian condition number: 1 is an ideal
equilateral tetrahedron; larger values indicate greater distortion. Curved
elements are sampled at their centers, so this is not a bound over their interiors.
Use it alongside SICN's signed validity check and solution convergence; κ alone
does not measure simulation accuracy. A numerically singular center is displayed
as infinite and stored as max=None with a nonzero singular_elements count.
Feature-based sizing¶
Automatic sizing uses a same-layer geometry heuristic. Touching and overlapping conductor polygons are merged before measuring widths and spacing. Nearly parallel opposing edges reveal narrow traces and necks; spacing checks also include notches and holes within one connected conductor. Polygon bounding boxes provide a fallback for shapes without suitable opposing edges.
Zero distances and distances below two layout database units are ignored. Gap searches are limited to 500 um; cross-layer gaps and arbitrary tapered or curved widths are not measured. These limitations make the estimate useful for choosing an initial mesh, rather than establishing simulation accuracy. Compare results across refinements before relying on resonance frequencies, Q, or S-parameters.
Nets¶
Stack¶
Circuit Synthesis¶
Fitting¶
Parameter Conversions¶
Transmission-line analysis¶
See Transmission-line analysis for using
load_sparams(...).to_skrf() with scikit-rf's multiline TRL calibration,
physical impedance normalization and independent-length checks.
Terminal S-parameters¶
See Terminal (multi-pin) Wave Ports for the conventions and a worked example.
terminal
¶
Modal to terminal S-parameters for multi-conductor (multi-pin) ports.
A field solver returns modal S-parameters: one port per propagating mode.
Circuit work (differential/common-mode analysis, bias lines, SPICE export)
needs terminal S-parameters: one port per conductor, referenced to a common
ground and a chosen reference impedance. This module converts one into the
other using, for every physical port, the matrix of terminal voltages
T_V and terminal currents T_I of its modes.
Conventions¶
Time convention exp(+j w t), as in the rest of gsim. A physical port p has
n_p terminals and n_p modes kept.
- Column
kofT_V[p]holds the terminal voltages (terminal minus the reference conductor) of modekfor a unit forward modal wave. ColumnkofT_I[p]holds the terminal currents flowing into the network for that wave. - Modal waves:
V = T_V (a + b)andI = T_I (a - b). - Terminal power waves for a real reference impedance
Zr:a_t = F (V + Zr I)andb_t = F (V - Zr I)withF = diag(1 / (2 sqrt(Zr))). For realZrthese equal pseudo waves. -
With block-diagonal
T = diag(T_V[p])andW = diag(T_I[p]), andA = F (T + Zr W),B = F (T - Zr W)::S_t = (B + A S_m) (A + B S_m)^-1 S_m = (A - S_t B)^-1 (S_t A - B)
-
Port ordering: physical port by physical port, terminals (or modes) in column order inside each port. A two-end line is ordered (A1, B1, A2, B2) and its modal S is (end 1 mode 1, end 1 mode 2, end 2 mode 1, end 2 mode 2).
Properties¶
- Joint per-mode scaling. Scaling column
kofT_VandT_Itogether by any complex constant leavesS_tunchanged whenS_mhas no cross-mode terms. The solver's modal normalisation therefore cancels; only the voltage/current shape of each mode matters. - Degenerate modes. If several modes share the same propagation constant,
any basis of their subspace gives the same
S_t. Mode identification can be unstable there; the terminal S is not. - Currents. For a reciprocal line
T_I^T T_Vis diagonal with entriesr_k = V_k^T I_k(the unconjugated reaction), soT_I = T_V^{-T} diag(r), see :func:currents_from_reaction. Replacingr_kby the real power2 P_kis only an approximation when the line is lossy. - Uniform line. The modal S of a uniform line is
[[0, E], [E, 0]]withE = diag(exp(-gamma_k * length)), see :func:uniform_line_modal_s.
Example::
import numpy as np
from gsim.palace.terminal import modal_to_terminal_s, uniform_line_modal_s
# t_v, t_i: (nf, 2, 2) for each end; gamma: (nf, 2) in 1/m
s_modal = uniform_line_modal_s(gamma, length=700e-6)
s_term = modal_to_terminal_s(s_modal, [t_v, t_v], [t_i, t_i], z_ref=50.0)
# s_term is ordered (A1, B1, A2, B2)
Limits: real positive reference impedances only; cross-mode terms in the modal S must come from the solver (a multi-mode port solve).
Functions:
-
currents_from_reaction–Terminal currents from terminal voltages and the modal reaction.
-
degenerate_mode_groups–Group modes whose propagation constants agree.
-
modal_to_terminal_s–Convert modal S-parameters to terminal (power-wave) S-parameters.
-
terminal_to_modal_s–Convert terminal S-parameters back to modal S-parameters.
-
uniform_line_modal_s–Modal S-parameters of a uniform multi-mode line, no cross-mode terms.
currents_from_reaction
¶
Terminal currents from terminal voltages and the modal reaction.
The reaction matrix is R = T_V^T T_I (unconjugated), so
T_I = T_V^{-T} R. Pass the full matrix R whenever the modes may be
mixed: within a degenerate group any basis is valid, and a mixed basis
has nonzero off-diagonal reactions.
A vector r is read as R = diag(r) with r_k = V_k^T I_k. This
holds only for a reaction-orthogonal basis (V_j^T I_k = 0 for
j != k): distinct modes of a reciprocal line, or degenerate modes
that were orthogonalised. Using the real power 2 P_k for r_k is
exact only without loss.
Parameters:
-
t_v(ArrayLike) –(n, n)or(nf, n, n)terminal voltages (column = mode). -
reaction(ArrayLike) –the full matrix
R,(n, n)for a 2-Dt_vor(nf, n, n); or the diagonal only,(n,)or(nf, n).
Returns:
-
NDArray[complex128]–T_Iwith the shape oft_v.
Raises:
-
ValueError–On shape mismatch, ill-conditioned
t_vor non-finite input.
degenerate_mode_groups
¶
Group modes whose propagation constants agree.
Modes i and j are linked when
|n_i - n_j| <= rtol * max(|n_i|, |n_j|); groups are the connected
components (single linkage). Within a group the terminal S does not depend
on the basis chosen for the modes.
Parameters:
-
n_eff(ArrayLike) –Effective indices (or propagation constants) at one frequency, shape
(n,). -
rtol(float, default:1e-06) –Relative tolerance.
Returns:
Raises:
-
ValueError–If
n_effis not 1-D or not finite.
modal_to_terminal_s
¶
modal_to_terminal_s(
s_modal: ArrayLike,
t_v: Sequence[ArrayLike],
t_i: Sequence[ArrayLike],
*,
z_ref: ArrayLike = 50.0,
) -> NDArray[complex128]
Convert modal S-parameters to terminal (power-wave) S-parameters.
Parameters:
-
s_modal(ArrayLike) –Modal S-parameters, shape
(nf, M, M). -
t_v(Sequence[ArrayLike]) –One terminal-voltage matrix per physical port, each
(n_p, n_p)or(nf, n_p, n_p). Columnkis modek. -
t_i(Sequence[ArrayLike]) –Terminal currents flowing into the network, same layout as
t_v. -
z_ref(ArrayLike, default:50.0) –Real positive reference impedance in ohm: a scalar, one value per terminal
(Nt,)or one per frequency and terminal(nf, Nt).
Returns:
-
NDArray[complex128]–Terminal S-parameters, shape
(nf, Nt, Nt)withNt = M. For real -
NDArray[complex128]–z_refpower waves and pseudo waves coincide.
Raises:
-
ValueError–On shape or port-count mismatch, non-square or ill-conditioned
t_v/t_iblocks (cond > 1e12, the modes do not span the terminals), non-positive or complexz_ref, or non-finite input.
terminal_to_modal_s
¶
terminal_to_modal_s(
s_terminal: ArrayLike,
t_v: Sequence[ArrayLike],
t_i: Sequence[ArrayLike],
*,
z_ref: ArrayLike = 50.0,
) -> NDArray[complex128]
Convert terminal S-parameters back to modal S-parameters.
Inverse of :func:modal_to_terminal_s; same arguments, conventions and
errors, with s_terminal of shape (nf, Nt, Nt).
uniform_line_modal_s
¶
uniform_line_modal_s(gamma: ArrayLike, length: float) -> NDArray[complex128]
Modal S-parameters of a uniform multi-mode line, no cross-mode terms.
S = [[0, E], [E, 0]] with E = diag(exp(-gamma_k * length)).
Parameters:
-
gamma(ArrayLike) –Propagation constants in 1/m, shape
(nf, n)(or(n,)). -
length(float) –Line length in m.
Returns:
-
NDArray[complex128]–(nf, 2n, 2n)ordered (end 1 modes, end 2 modes).
Raises:
-
ValueError–If
Re(gamma) < 0beyond round-off (a mode that grows along the line), ifgammais not finite, or iflengthis negative or not finite.