Palace API¶
Simulation Classes¶
DrivenSim
¶
Bases: PalaceSimMixin, BaseModel
Frequency-domain driven simulation for S-parameter extraction.
This class configures and runs driven simulations that sweep through frequencies to compute S-parameters. Uses composition (no inheritance) with shared Geometry and Stack components from gsim.common.
Example
from gsim.palace import DrivenSim
sim = DrivenSim() sim.set_geometry(component) sim.set_stack() sim.set_airbox(margin_x=120.0, margin_above=120.0, margin_below=20.0) sim.add_cpw_port("o1", layer="topmetal2", s_width=10, gap_width=6) sim.add_cpw_port("o2", layer="topmetal2", s_width=10, gap_width=6) sim.set_driven(fmin=1e9, fmax=100e9, num_points=40) sim.set_output_dir("./sim") sim.mesh(preset="default") sp = sim.run() # SParams sp.s21.db # dB magnitude of S21 vs frequency
Attributes:
-
geometry(Geometry | None) –Wrapped gdsfactory Component (from common)
-
stack(LayerStack | None) –Layer stack configuration (from common)
-
ports(list[PortConfig]) –List of single-element port configurations
-
cpw_ports(list[CPWPortConfig]) –List of CPW (two-element) port configurations
-
driven(DrivenConfig) –Driven simulation configuration (frequencies, etc.)
-
mesh(SimulationResult) –Mesh configuration
-
materials(dict[str, MaterialConfig]) –Material property overrides
-
numerical(NumericalConfig) –Numerical solver configuration
Methods:
-
set_driven–Configure driven (frequency sweep) simulation.
-
run–Run the driven sim on GDSFactory+ cloud.
set_driven
¶
set_driven(
*,
f: float | None = None,
fmin: float | None = None,
fmax: float | None = None,
num_points: int = 40,
scale: Literal["linear", "log"] = "linear",
adaptive_tol: float = 0.02,
adaptive_max_samples: int = 20,
compute_s_params: bool = True,
reference_impedance: float = 50.0,
excitation_port: str | None = None,
save_step: int = 0,
save_fields_at: list[float] | None = None,
save_freq: str | None = None,
) -> None
Configure driven (frequency sweep) simulation.
Parameters:
-
f(float | None, default:None) –Single frequency in Hz (convenience). Sets both fmin and fmax to this value with num_points=1. Ignored when both fmin and fmax are explicitly provided.
-
fmin(float | None, default:None) –Minimum frequency in Hz
-
fmax(float | None, default:None) –Maximum frequency in Hz
-
num_points(int, default:40) –Number of frequency points
-
scale(Literal['linear', 'log'], default:'linear') –"linear" or "log" frequency spacing
-
adaptive_tol(float, default:0.02) –Relative error tolerance (unitless fraction) for adaptive frequency sampling. Palace builds a reduced-order model from a few full solves and interpolates the rest. 0 = disabled (full solve at every point), 0.02 = 2% error (fast default), 1e-3 = 0.1% (accurate), 1e-4 = publication quality.
-
adaptive_max_samples(int, default:20) –Maximum number of additional frequency points that adaptive refinement may insert between the points defined by fmin, fmax, and num_points.
-
compute_s_params(bool, default:True) –Compute S-parameters
-
reference_impedance(float, default:50.0) –Reference impedance for S-params (Ohms)
-
excitation_port(str | None, default:None) –Port to excite (None = first port)
-
save_step(int, default:0) –Save fields every N frequency steps for ParaView (0 = disabled)
-
save_fields_at(list[float] | None, default:None) –Specific frequencies (Hz) at which to save fields for ParaView visualisation.
-
save_freq(str | None, default:None) –Convenience shorthand for saving fields at a named frequency.
"center"saves at(fmin + fmax) / 2. Appended to save_fields_at if both are given.
Example
sim.set_driven(f=50e9) sim.set_driven(fmin=1e9, fmax=100e9, num_points=40) sim.set_driven(fmin=1e9, fmax=100e9, save_freq="center")
run
¶
run(
parent_dir: str | Path | None = None,
*,
verbose: Literal["quiet", "status", "full"] = "status",
wait: bool = True,
check_cache: bool = False,
) -> SParams | str
Run the driven sim on GDSFactory+ cloud.
Thin wrapper over :meth:PalaceSimMixin.run that narrows the
return type: the palace result parser always turns a completed
driven run (which has port-S.csv) into an
:class:~gsim.palace.results.SParams object.
Returns:
EigenmodeSim
¶
Bases: PalaceSimMixin, BaseModel
Eigenmode simulation for finding resonant frequencies.
This class configures and runs eigenmode simulations to find resonant frequencies and mode shapes of structures.
Example
from gsim.palace import EigenmodeSim
sim = EigenmodeSim() sim.set_geometry(component) sim.set_stack() sim.set_airbox(margin_x=120.0, margin_above=120.0, margin_below=20.0) sim.add_port("o1", layer="topmetal2", length=5.0) sim.set_eigenmode(num_modes=10, target=50e9) sim.set_output_dir("./sim") sim.mesh(preset="default") results = sim.run() # dict[str, Path] print(results["eig.csv"])
Attributes:
-
geometry(Geometry | None) –Wrapped gdsfactory Component (from common)
-
stack(LayerStack | None) –Layer stack configuration (from common)
-
ports(list[PortConfig]) –List of single-element port configurations
-
cpw_ports(list[CPWPortConfig]) –List of CPW (two-element) port configurations
-
eigenmode(EigenmodeConfig) –Eigenmode simulation configuration
-
materials(dict[str, MaterialConfig]) –Material property overrides
-
numerical(NumericalConfig) –Numerical solver configuration
Methods:
-
set_eigenmode–Configure eigenmode simulation.
-
run–Run the eigenmode sim on GDSFactory+ cloud.
set_eigenmode
¶
set_eigenmode(
*,
num_modes: int = 10,
target: float | None = None,
tolerance: float = 1e-06,
save: int = 0,
floquet: bool = False,
phi_target: float = pi / 2,
n_eff_guess: float = 2.0,
) -> None
Configure eigenmode simulation.
Parameters:
-
num_modes(int, default:10) –Number of modes to find
-
target(float | None, default:None) –Positive target frequency in Hz for mode search. Required before meshing or exporting the configuration.
-
tolerance(float, default:1e-06) –Eigenvalue solver tolerance
-
save(int, default:0) –Number of eigenmodes to save as ParaView fields (0 = disabled)
-
floquet(bool, default:False) –Enable Floquet periodic boundary setup in config generation (requires mesh(periodic_axis=...)).
-
phi_target(float, default:pi / 2) –Bloch phase advance per cell in radians (Floquet only).
-
n_eff_guess(float, default:2.0) –Initial effective-index guess for Floquet k-vector setup.
Example
sim.set_eigenmode(num_modes=10, target=50e9)
run
¶
run(
parent_dir: str | Path | None = None,
*,
verbose: Literal["quiet", "status", "full"] = "status",
wait: bool = True,
check_cache: bool = False,
) -> dict[str, Path] | str
Run the eigenmode sim on GDSFactory+ cloud.
Thin wrapper over :meth:PalaceSimMixin.run that narrows the
return type: an eigenmode run returns a dict[str, Path] of
output files keyed by name (e.g. "eig.csv"), or the
job_id string when wait=False.
ElectrostaticSim
¶
Bases: PalaceSimMixin, BaseModel
Electrostatic simulation for capacitance matrix extraction.
This class configures and runs electrostatic simulations to extract the capacitance matrix between conductor terminals. Unlike driven and eigenmode simulations, this does not use ports.
Example
from gsim.palace import ElectrostaticSim
sim = ElectrostaticSim() sim.set_geometry(component) sim.set_stack() sim.set_airbox(margin_x=120.0, margin_above=120.0, margin_below=20.0) sim.add_terminal("T1", layer="topmetal2") sim.add_terminal("T2", layer="topmetal2") sim.set_electrostatic() sim.set_output_dir("./sim") sim.mesh(preset="default") results = sim.run() # dict[str, Path] print(results["terminal-C.csv"])
Attributes:
-
geometry(Geometry | None) –Wrapped gdsfactory Component (from common)
-
stack(LayerStack | None) –Layer stack configuration (from common)
-
terminals(list[TerminalConfig]) –List of terminal configurations
-
electrostatic(ElectrostaticConfig) –Electrostatic simulation configuration
-
materials(dict[str, MaterialConfig]) –Material property overrides
-
numerical(NumericalConfig) –Numerical solver configuration
Methods:
-
set_electrostatic–Configure electrostatic simulation.
-
add_terminal–Add a terminal for capacitance extraction.
-
nets–Find the electrically connected conductor shapes of the geometry.
-
load_capacitance–Load the capacitance matrices of a run, labelled with the terminals.
set_electrostatic
¶
set_electrostatic(*, save_fields: int = 0) -> None
Configure electrostatic simulation.
Parameters:
-
save_fields(int, default:0) –Number of field solutions to save
Example
sim.set_electrostatic(save_fields=1)
add_terminal
¶
nets
¶
nets() -> Nets
Find the electrically connected conductor shapes of the geometry.
Metal that touches on one layer, and metal on different layers that a via joins, is one net; disconnected electrodes stay separate even when they share a layer. Electrical ports name the nets they sit on.
A terminal still selects every shape on its layer, so two disconnected electrodes on one layer end up as a single terminal (gsim#273). This shows how many electrodes each layer holds.
Returns:
-
Nets–The nets, in the order their first shape appears in the layout.
Raises:
-
ValueError–If no geometry has been set.
Example
print(sim.nets())
load_capacitance
¶
Load the capacitance matrices of a run, labelled with the terminals.
The terminal names are the ones given to :meth:add_terminal, in the
order they were added, which is Palace's terminal order.
Parameters:
-
results(dict[str, Path] | str | Path) –The results dict that
run()returns, or the output directory of the simulation.
Returns:
-
CapacitanceMatrices–The Maxwell and mutual capacitance matrices, with checks; see
-
CapacitanceMatrices–class:
~gsim.palace.capacitance.CapacitanceMatrices.
Example
cap = sim.load_capacitance(sim.run()) cap.between("T1", "T2") # plate to plate, in F cap.to_ground("T1") # to the substrate, in F assert not cap.problems()
Capacitance¶
CapacitanceMatrices
dataclass
¶
CapacitanceMatrices(
terminals: tuple[str, ...],
maxwell: NDArray[float64],
mutual: NDArray[float64],
inverse: NDArray[float64] | None = None,
excitation_voltage: NDArray[float64] | None = None,
stored_energy: NDArray[float64] | None = None,
)
The capacitance matrices of an electrostatic run, labelled by terminal.
Attributes:
-
terminals(tuple[str, ...]) –Terminal names, in Palace's index order.
-
maxwell(NDArray[float64]) –Maxwell capacitance matrix, in F.
-
mutual(NDArray[float64]) –Mutual capacitance matrix, in F, as Palace writes it.
-
inverse(NDArray[float64] | None) –Inverse of the Maxwell matrix, in 1/F, when Palace wrote it.
-
excitation_voltage(NDArray[float64] | None) –Voltage applied in each excitation, in V.
-
stored_energy(NDArray[float64] | None) –Electric energy of each excitation, in J.
Methods:
-
between–Capacitance between two electrodes, in F (their mutual capacitance).
-
to_ground–Capacitance of an electrode to ground, in F.
-
maxwell_frame–The Maxwell matrix as a DataFrame indexed by terminal name.
-
mutual_frame–The mutual matrix as a DataFrame indexed by terminal name.
-
problems–List what is inconsistent in the matrices; empty when they hold together.
between
¶
Capacitance between two electrodes, in F (their mutual capacitance).
maxwell_frame
¶
maxwell_frame() -> DataFrame
The Maxwell matrix as a DataFrame indexed by terminal name.
mutual_frame
¶
mutual_frame() -> DataFrame
The mutual matrix as a DataFrame indexed by terminal name.
problems
¶
List what is inconsistent in the matrices; empty when they hold together.
Checks that every value is finite, that the Maxwell matrix is symmetric
and positive semi-definite, which is what a non-negative stored energy
for every set of voltages means, that each terminal has a positive self
capacitance, that its off-diagonal entries are not positive and that no
terminal has a negative capacitance to ground, that the mutual matrix
follows from it, and, when Palace wrote them, that the inverse inverts
it and that each excitation's stored energy is 1/2 C[i][i] V**2.
Only the first problem is reported when a value is not finite, since
every other check is meaningless then.
Parameters:
-
rtol(float, default:0.001) –Tolerance, relative to the largest Maxwell entry. The default lets through the small errors of a discretization, such as a weakly coupled pair whose coefficient comes out slightly positive. The inconsistencies these checks look for, a wrong sign or the wrong matrix, are as large as the matrix itself.
load_capacitance
¶
load_capacitance(
source: str | Path | dict, *, terminal_names: Sequence[str] | None = None
) -> CapacitanceMatrices
Load the capacitance matrices of an electrostatic Palace run.
Parameters:
-
source(str | Path | dict) –The results dict that
run()returns, or the simulation or Palace output directory. -
terminal_names(Sequence[str] | None, default:None) –Names for the terminals, in Palace's index order, which is the order they were added to the simulation. Defaults to
T1,T2, ...
Returns:
-
CapacitanceMatrices–The matrices, labelled by terminal.
Raises:
-
FileNotFoundError–If
terminal-C.csvorterminal-Cm.csvis missing. -
ValueError–If the terminal names do not match the matrices.
Mesh¶
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. The current cloud API does not accept a separate metadata parameter, so
the metadata travels as an input artifact pending that API integration.
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.
MeshConfig
¶
Bases: BaseModel
Configuration for mesh generation with quality presets.
Attributes:
-
refined_mesh_size(float) –Mesh size near conductors (um)
-
max_mesh_size(float) –Maximum mesh size in air/dielectric (um)
-
cells_per_wavelength(int) –Number of mesh cells per wavelength
-
margin(float) –XY margin around design (um)
-
margin_x(float | None) –X-axis margin override (um). Falls back to margin.
-
margin_y(float | None) –Y-axis margin override (um). Falls back to margin.
-
airbox_margin(float) –Extra airbox around stack (um); 0 = disabled
-
fmax(float) –Maximum frequency for mesh sizing (Hz)
-
boundary_conditions(list[str] | None) –List of boundary conditions for each face
-
planar_conductors(bool) –Treat conductors as 2D PEC surfaces instead of volumes
-
algorithm_3d(Literal['delaunay', 'hxt']) –Gmsh 3D meshing algorithm, "delaunay" or "hxt". In the 800 um CPW tests behind gsim#283 HXT gave a different mesh and was slower than Delaunay at every thread count.
-
threads(int) –Threads for 3D meshing. With Delaunay and
surface_threads=1the mesh does not depend on it, and it did not speed meshing up in those tests either. With HXT the mesh depends on the thread count. -
surface_threads(int) –Threads for 1D and 2D (surface) meshing. Values above 1 are faster but gave a different mesh on every run, so keep 1 when the mesh must be reproducible.
-
show_gui(bool) –Show gmsh GUI during meshing
-
preview_only(bool) –Generate preview only, don't save mesh
Methods:
-
coarse–Fast mesh for quick iteration (~2.5 elements per wavelength).
-
default–Balanced mesh (~5 elements per wavelength).
-
fine–High accuracy mesh (~10 elements per wavelength).
coarse
classmethod
¶
Fast mesh for quick iteration (~2.5 elements per wavelength).
This preset is suitable for initial debugging and quick checks. Not recommended for accurate results.
default
classmethod
¶
Balanced mesh (~5 elements per wavelength).
This preset provides a good balance between accuracy and computation time. Suitable for most simulations.
generate_mesh
¶
generate_mesh(
component,
stack: LayerStack,
ports: list[PalacePort],
output_dir: str | Path,
model_name: str = "palace",
refined_mesh_size: float = 5.0,
max_mesh_size: float = 300.0,
margin_x: float = 50.0,
margin_y: float = 50.0,
air_margin: float = 50.0,
airbox_margin_x: float | None = None,
airbox_margin_y: float | None = None,
airbox_z_above: float | None = None,
airbox_z_below: float | None = None,
airbox_material: str = "air",
fmax: float = 100000000000.0,
show_gui: bool = False,
simulation_type: str = "driven",
driven_config: DrivenConfig | None = None,
eigenmode_config: EigenmodeConfig | None = None,
numerical_config: NumericalConfig | None = None,
boundary_mode_config: BoundaryModeConfig | None = None,
cross_section: CrossSectionPlaneConfig | None = None,
write_config: bool = True,
planar_conductors: bool = False,
pec_blocks: list[PECBlockConfig] | None = None,
absorbing_boundary: bool = True,
periodic_axis: str | None = None,
merge_via_distance: float = 2.0,
curve_fit_mode: Literal["line", "spline", "bspline"] = "line",
curve_fit_layers: list[str] | None = None,
curve_fit_tolerance_um: float = 0.0,
curve_fit_min_points: int = 8,
curve_fit_corner_angle_deg: float = 45.0,
high_order_elements: bool = False,
high_order_order: int = 2,
high_order_optimize: bool = True,
verbosity: int = 3,
decimate_tolerance: float | None = None,
algorithm_3d: Literal["delaunay", "hxt"] = "delaunay",
threads: int = 1,
surface_threads: int = 1,
) -> MeshResult
Generate mesh for Palace EM simulation.
Parameters:
-
component–gdsfactory Component
-
stack(LayerStack) –LayerStack from palace-api
-
ports(list[PalacePort]) –List of PalacePort objects (single and multi-element)
-
output_dir(str | Path) –Directory for output files
-
model_name(str, default:'palace') –Base name for output files
-
refined_mesh_size(float, default:5.0) –Mesh size near conductors (um)
-
max_mesh_size(float, default:300.0) –Max mesh size in air/dielectric (um)
-
margin_x(float, default:50.0) –X-axis margin around design (um)
-
margin_y(float, default:50.0) –Y-axis margin around design (um)
-
air_margin(float, default:50.0) –Legacy isotropic airbox margin (um)
-
airbox_margin_x(float | None, default:None) –Extra x-margin for explicit airbox (um)
-
airbox_margin_y(float | None, default:None) –Extra y-margin for explicit airbox (um)
-
airbox_z_above(float | None, default:None) –Extra +z margin for explicit airbox (um)
-
airbox_z_below(float | None, default:None) –Extra -z margin for explicit airbox (um)
-
airbox_material(str, default:'air') –Material name for the native-2D BoundaryMode background region outside the stack/cladding (default
"air"). Pass e.g."sio2"for a uniform-dielectric cladding with no air. -
fmax(float, default:100000000000.0) –Max frequency for config (Hz)
-
show_gui(bool, default:False) –Show gmsh GUI during meshing
-
simulation_type(str, default:'driven') –Type of simulation (driven, eigenmode or electrostatics)
-
driven_config(DrivenConfig | None, default:None) –Optional DrivenConfig for frequency sweep settings
-
eigenmode_config(EigenmodeConfig | None, default:None) –Optional EigenmodeConfig for eigenmode problems
-
numerical_config(NumericalConfig | None, default:None) –Optional NumericalConfig for solver settings
-
boundary_mode_config(BoundaryModeConfig | None, default:None) –Optional BoundaryModeConfig for 2D mode problems
-
cross_section(CrossSectionPlaneConfig | None, default:None) –Explicit x/y cross-section plane for native BoundaryMode
-
write_config(bool, default:True) –Whether to write config.json (default True)
-
pec_blocks(list[PECBlockConfig] | None, default:None) –PEC configuration
-
planar_conductors(bool, default:False) –If True, treat conductors as 2D PEC surfaces
-
absorbing_boundary(bool, default:True) –If True, use absorbing boundary conditions on outer surfaces
-
periodic_axis(str | None, default:None) –("x" or "y") for meshing constraints on opposite domain sides
-
merge_via_distance(float, default:2.0) –Max gap between vias to merge (um)
-
curve_fit_mode(Literal['line', 'spline', 'bspline'], default:'line') –Patterned dielectric boundary mode: line/spline/bspline
-
curve_fit_layers(list[str] | None, default:None) –Layer names where curve fitting is applied
-
curve_fit_tolerance_um(float, default:0.0) –Point merge tolerance before curve fitting
-
curve_fit_min_points(int, default:8) –Min contour points required for curve fitting
-
curve_fit_corner_angle_deg(float, default:45.0) –Turn-angle threshold used to identify sharp corners during curve fitting segmentation
-
high_order_elements(bool, default:False) –Enable high-order geometric mesh elements
-
high_order_order(int, default:2) –Polynomial order for high-order elements
-
high_order_optimize(bool, default:True) –Run gmsh high-order optimization after meshing
-
decimate_tolerance(float | None, default:None) –Relative tolerance for polygon decimation (None = no decimation; typical 0.001-0.01)
-
verbosity(int, default:3) –Sets gmsh verbosity level
-
algorithm_3d(Literal['delaunay', 'hxt'], default:'delaunay') –Gmsh 3D meshing algorithm, "delaunay" or "hxt"
-
threads(int, default:1) –Threads for 3D meshing
-
surface_threads(int, default:1) –Threads for 1D and 2D meshing; above 1 the mesh differs from run to run
Returns:
-
MeshResult–MeshResult with paths and metadata
Nets¶
Nets
dataclass
¶
Nets(
nets: tuple[Net, ...],
unmatched_ports: tuple[str, ...] = (),
_shapes: dict[str, list[tuple[Polygon, Net]]] = dict(),
)
The nets of a layout, in the order their first shape appears.
Attributes:
-
nets(tuple[Net, ...]) –The nets.
-
unmatched_ports(tuple[str, ...]) –Names of ports whose centre is on no conductor or via shape with the GDS layer number of the port.
Methods:
-
net_at–The net under the point
(x, y)(um) on a conductor or via layer.
Net
dataclass
¶
Conductor shapes that are electrically connected.
Attributes:
-
name(str) –The names of the ports on the net joined by
+, ornet<k>when it has none, withkits position in the list of nets, counting from 1. -
ports(tuple[str, ...]) –Names of the ports whose centre lies on the net.
-
members(tuple[tuple[str, int], ...]) –(layer name, index)of each shape, where the index is the position of the shape inGeometryData.polygons.
extract_nets
¶
extract_nets(geometry: GeometryData, stack: LayerStack, ports: Iterable = ()) -> Nets
Group the conductor and via shapes of a layout into electrical nets.
Parameters:
-
geometry(GeometryData) –The polygons of the layout, as the mesher reads them (
extract_geometry). -
stack(LayerStack) –The layer stack; its conductor and via layers are the ones considered.
-
ports(Iterable, default:()) –Ports of the component. Each names the net of the shapes that cover its centre on layers with the GDS layer number of the port, whatever the datatype (pins are usually on another datatype than the metal).
Returns:
-
Nets–The nets, each shape on exactly one of them.
Stack¶
LayerStack
¶
Bases: BaseModel
Complete layer stack for Palace simulation.
Layer
¶
Bases: BaseModel
Layer information for Palace simulation.