This document covers the core functions and classes in the pyedgefem module.
mesh = em.load_gmsh(path: str) -> MeshLoad a Gmsh v2 format mesh file.
Parameters:
path- Path to.mshfile (Gmsh v2 ASCII format)
Returns: Mesh object with nodes, elements, and edge connectivity
Example:
mesh = em.load_gmsh("waveguide.msh")
print(f"Nodes: {len(mesh.nodes)}, Elements: {len(mesh.elements)}")report = em.validate_mesh(mesh: Mesh) -> MeshQualityReportCheck mesh quality and identify problematic elements.
Parameters:
mesh- Mesh object to validate
Returns: MeshQualityReport with quality metrics
Example:
report = em.validate_mesh(mesh)
if not report.is_valid():
print(f"Warning: {report.num_degenerate_tets} degenerate elements")surface = em.extract_surface_mesh(mesh: Mesh, tag: int) -> SurfaceMeshExtract triangular surface mesh from a physical surface tag.
Parameters:
mesh- Source meshtag- Physical tag identifying the surface
Returns: SurfaceMesh containing surface triangles and edges
bc = em.build_edge_pec(mesh: Mesh, tag: int) -> BCBuild PEC (Perfect Electric Conductor) boundary condition for edges on a tagged surface.
Parameters:
mesh- Source meshtag- Physical tag identifying PEC surfaces
Returns: BC object with Dirichlet edge constraints
Example:
# Tag 1 is PEC walls
bc = em.build_edge_pec(mesh, 1)
print(f"PEC edges: {len(bc.dirichlet_edges)}")class BC:
dirichlet_edges: Set[int] # Edge indices with E_t = 0Boundary condition container. Multiple BCs can be combined by merging their edge sets.
class MaxwellParams:
omega: float # Angular frequency (rad/s)
eps_r_regions: Dict[int, complex] # Relative permittivity per volume tag
mu_r_regions: Dict[int, complex] # Relative permeability per volume tag
use_abc: bool # Enable ABC on outer boundaries
use_pml: bool # Enable PML layers
port_abc_scale: float # Port ABC operator scale (leave at default 1.0)Maxwell equation parameters for assembly.
Example:
params = em.MaxwellParams()
params.omega = 2 * np.pi * 10e9 # 10 GHz
params.eps_r_regions = {
100: complex(1.0, 0), # Air
101: complex(4.4, -0.088), # FR-4 substrate (eps_r=4.4, tan_d=0.02)
}assembly = em.assemble_maxwell(
mesh: Mesh,
params: MaxwellParams,
bc: BC,
ports: List[WavePort],
active_port: int = 0
) -> MaxwellAssemblyAssemble the Maxwell curl-curl FEM system matrix.
Parameters:
mesh- FEM meshparams- Maxwell parametersbc- Boundary conditionsports- List of wave portsactive_port- Index of port to excite (default: 0)
Returns: MaxwellAssembly with system matrix A and RHS vector b
S = em.calculate_sparams_eigenmode(
mesh: Mesh,
params: MaxwellParams,
bc: BC,
ports: List[WavePort]
) -> np.ndarrayCompute S-parameter matrix using eigenmode-based extraction. Recommended for highest accuracy.
Parameters:
mesh- FEM meshparams- Maxwell parameters (leaveport_abc_scaleat its default 1.0)bc- Boundary conditionsports- List of wave ports
Returns: Complex S-matrix as (N, N) numpy array where N is number of ports
Example:
params = em.MaxwellParams()
params.omega = 2 * np.pi * 10e9
S = em.calculate_sparams_eigenmode(mesh, params, bc, [port1, port2])
print(f"S11 = {S[0,0]:.4f}, S21 = {S[1,0]:.4f}")!!! note "Accuracy" Typical matched-waveguide accuracy at ~10 elements/wavelength: |S21| within ~0.3%, |S11| < 0.06, converging with mesh refinement (see docs/validation.md).
S = em.calculate_sparams(
mesh: Mesh,
params: MaxwellParams,
bc: BC,
ports: List[WavePort]
) -> np.ndarrayCompute S-parameters using standard port extraction. Use calculate_sparams_eigenmode for higher accuracy.
S = em.calculate_sparams_periodic(
mesh: Mesh,
params: MaxwellParams,
bc: BC,
pbc: PeriodicBC,
ports: List[WavePort]
) -> np.ndarrayCompute S-parameters for periodic structures with Floquet boundary conditions.
class RectWaveguidePort:
a: float # Waveguide width (m)
b: float # Waveguide height (m)Rectangular waveguide port dimensions.
mode = em.solve_te10_mode(port: RectWaveguidePort, freq: float) -> PortModeSolve for TE10 mode parameters at given frequency.
Parameters:
port- Waveguide dimensionsfreq- Frequency in Hz
Returns: PortMode with propagation constant, impedance, and field distribution
port = em.build_wave_port(
mesh: Mesh,
surface: SurfaceMesh,
mode: PortMode
) -> WavePortBuild a wave port from surface mesh and mode definition.
em.populate_te10_field(
surface: SurfaceMesh,
port: RectWaveguidePort,
mode: PortMode
)Populate TE10 field values on port surface edges.
class SolveOptions:
tolerance: float # Convergence tolerance (default: 1e-8)
max_iterations: int # Maximum iterations (default: 10000)
preconditioner: str # "ILUT" or "diagonal"result = em.solve_linear(
A: SparseMatrix,
b: Vector,
options: Optional[SolveOptions] = None
) -> SolveResultSolve the linear system Ax = b using BiCGSTAB with ILUT preconditioner.
Returns: SolveResult with solution vector x, convergence info
pbc = em.build_periodic_pairs(
mesh: Mesh,
tag_neg: int,
tag_pos: int,
period_vector: np.ndarray
) -> PeriodicBCBuild periodic boundary condition pairs between two surfaces.
Parameters:
mesh- Source meshtag_neg- Physical tag for -X (or -Y) surfacetag_pos- Physical tag for +X (or +Y) surfaceperiod_vector- Translation vector [dx, dy, dz]
Example:
pbc_x = em.build_periodic_pairs(mesh, 5, 6, np.array([5e-3, 0, 0]))em.set_floquet_phase(pbc: PeriodicBC, k_transverse: np.ndarray)Set Floquet phase shift for oblique incidence.
Parameters:
pbc- Periodic boundary conditionk_transverse- Transverse wave vector [kx, ky, 0]
Example:
# 30 degree incidence at 10 GHz
k0 = 2 * np.pi * 10e9 / 3e8
kx = k0 * np.sin(np.deg2rad(30))
em.set_floquet_phase(pbc_x, np.array([kx, 0, 0]))pattern = em.stratton_chu_3d(
mesh: Mesh,
E_field: Vector,
H_field: Vector,
huygens_tag: int,
freq: float,
n_theta: int = 91,
n_phi: int = 73
) -> FFPattern3DCompute 3D far-field pattern using Stratton-Chu integration over a Huygens surface.
Parameters:
mesh- FEM meshE_field- Electric field solutionH_field- Magnetic field solution (computed from E)huygens_tag- Physical tag of Huygens surfacefreq- Frequency in Hzn_theta- Number of theta samples (0 to 180 deg)n_phi- Number of phi samples (0 to 360 deg)
Returns: FFPattern3D with E_theta, E_phi components on spherical grid
D = em.compute_directivity(pattern: FFPattern3D) -> floatCompute directivity from far-field pattern (linear scale).
G = em.compute_max_gain(pattern: FFPattern3D, efficiency: float = 1.0) -> floatCompute maximum gain (linear scale).
hpbw = em.compute_hpbw(pattern: FFPattern3D, plane: str = "E") -> floatCompute half-power beamwidth in degrees.
Parameters:
pattern- Far-field patternplane- "E" for E-plane or "H" for H-plane
em.write_touchstone_nport(
path: str,
frequencies: List[float],
S_matrices: List[np.ndarray]
)Export S-parameters to Touchstone format.
Parameters:
path- Output file path (.s1p, .s2p, etc.)frequencies- List of frequencies in HzS_matrices- List of S-matrices (one per frequency)
em.export_fields_vtk(
mesh: Mesh,
E_field: Vector,
path: str,
options: Optional[VTKExportOptions] = None
)Export E-field solution to VTK format for ParaView visualization.
em.write_pattern_3d_csv(path: str, pattern: FFPattern3D)Export 3D far-field pattern to CSV format.
em.write_pattern_3d_vtk(path: str, pattern: FFPattern3D)Export 3D far-field pattern to VTK format.
em.check_passivity(S: np.ndarray, tolerance: float = 0.01)Verify that S-matrix satisfies passivity: |S11|² + |S21|² ≤ 1.
Raises ValueError if passivity is violated by more than tolerance.
reciprocal = em.is_reciprocal(S: np.ndarray, tolerance: float = 1e-3) -> boolCheck if S-matrix is reciprocal (S_ij = S_ji).
db_value = em.to_db(linear: float) -> floatConvert linear magnitude to decibels: 20*log10(|x|).