padne.solver

Attributes

Exceptions

SolverWarning

A warning that is raised by the solver when it encounters a problem

Classes

SolverBackend

Sparse direct solver used for the final linear system.

SolverInfo

Diagnostic information from the solver.

LayerSolution

Solution

ConnectivityGraph

VertexIndexer

Maps between (mesh index, vertex index) pairs and global system indices.

NodeIndexer

TripletList

COO triplet accumulator for sparse system assembly.

Functions

solver_backends(→ list[SolverBackend])

Backends usable in this build, the default first.

resolve_backend(→ SolverBackend)

Validate an explicit backend choice, or pick the default for None.

locate_connection_points(...)

Resolve which layer geometries each network connection point lies on.

laplace_operator(→ scipy.sparse.coo_matrix)

Compute the Laplace operator for a given mesh. This is in "mesh-local"

find_connected_layer_geom_indices(→ set[tuple[int, int]])

compute_connectivity(→ tuple[ConnectivityGraph, ...)

Run the geometric-connectivity pre-pass.

generate_meshes_for_problem(...)

generate_disconnected_meshes(→ list[list[padne.mesh.Mesh]])

Generate simple triangulations for disconnected copper regions.

stamp_network_into_system(→ None)

setup_ground_node(→ None)

process_mesh_laplace_operators(→ scipy.sparse.coo_matrix)

Assemble the mesh Laplacians into one global NxN COO matrix.

produce_layer_solutions(→ list[LayerSolution])

network_has_a_dead_terminal(→ bool)

Check if a network has any connection on a dead (disconnected) copper region.

filter_dead_networks(→ list[padne.problem.Network])

Drop networks with any connection on disconnected copper. See

find_best_ground_node_index(→ int)

compute_power_density(→ padne.mesh.TwoForm)

Compute the power density at the mesh faces.

system_size(→ int)

Size of the global system: mesh vertices, internal network nodes, extra

solve_system(→ tuple[numpy.ndarray, SolverInfo])

Solve L * v = r and return the solution vector together with diagnostics.

assemble_system(→ tuple[scipy.sparse.csc_matrix, ...)

Allocate (L, r) and stamp the mesh Laplacians, all networks, and the

solve(→ Solution)

Solve the given PCB problem to find voltage and current distribution.

Module Contents

padne.solver.log[source]
padne.solver.DTYPE[source]
padne.solver.RESIDUAL_WARNING_THRESHOLD = 1e-06[source]
class padne.solver.SolverBackend(*args, **kwds)[source]

Bases: enum.Enum

Sparse direct solver used for the final linear system.

SCIPY = 'scipy'[source]
PARDISO = 'pardiso'[source]
padne.solver.solver_backends() → list[SolverBackend][source]

Backends usable in this build, the default first.

padne.solver.resolve_backend(backend: SolverBackend | None) → SolverBackend[source]

Validate an explicit backend choice, or pick the default for None.

exception padne.solver.SolverWarning[source]

Bases: Warning

A warning that is raised by the solver when it encounters a problem that does not prevent it from solving the problem, but may indicate a potential issue with the problem definition.

class padne.solver.SolverInfo[source]

Diagnostic information from the solver.

ground_node_current: float[source]
residual_norm: float[source]
relative_residual: float[source]
class padne.solver.LayerSolution[source]
meshes: list[padne.mesh.Mesh][source]
potentials: list[padne.mesh.ZeroForm][source]
power_densities: list[padne.mesh.TwoForm] = [][source]
disconnected_meshes: list[padne.mesh.Mesh] = [][source]
class padne.solver.Solution[source]
problem: Solution.problem[source]
layer_solutions: list[LayerSolution][source]
solver_info: SolverInfo[source]
padne.solver.locate_connection_points(prob: padne.problem.Problem, interior_only: bool = False) → dict[padne.problem.Connection, list[tuple[int, int]]][source]

Resolve which layer geometries each network connection point lies on.

Groups the connection points of all networks by layer and runs a single vectorized STRtree query per layer, which is far cheaper than one query plus one predicate evaluation per connection. The tree is built over the points and queried with the layer geometries: shapely prepares the query geometries during predicate evaluation, which makes the point-in-polygon tests logarithmic in polygon size instead of linear.

With interior_only, points exactly on a geometry boundary do not match (“contains”); otherwise they do (“intersects”).

Returns a map from each Connection to the (layer_i, geom_i) pairs it matched; connections that matched nothing are absent.

class padne.solver.ConnectivityGraph[source]
nodes: list[ConnectivityGraph.Node] = [][source]
class Node[source]
layer_i: int[source]
geom_i: int[source]
is_root: bool = False[source]
neighbors: set[ConnectivityGraph][source]
classmethod create_from_problem(problem: ConnectivityGraph.create_from_problem.problem) → ConnectivityGraph[source]
compute_connected_nodes() → list[ConnectivityGraph][source]

Return a list of all nodes that are either root nodes themselves or are connected to a root node via any connection.

padne.solver.laplace_operator(mesh: laplace_operator.mesh) → scipy.sparse.coo_matrix[source]

Compute the Laplace operator for a given mesh. This is in “mesh-local” indices, so the variable indices are given by the mesh.vertices indices.

class padne.solver.VertexIndexer[source]

Maps between (mesh index, vertex index) pairs and global system indices.

API contract: global indices are assigned contiguously per mesh, in mesh order and in local vertex order. That is, mesh i occupies the half-open global index range [offset_i, offset_i + len(meshes[i].vertices)) with (i, v) -> offset_i + v. process_mesh_laplace_operators relies on this to translate per-mesh COO blocks by a single offset.

global_index_to_vertex_index: list[tuple[int, int]] = [][source]
mesh_vertex_index_to_global_index: dict[tuple[int, int], int][source]
mesh_offsets: list[int] = [][source]
classmethod create(meshes: list[padne.mesh.Mesh]) → VertexIndexer[source]
mesh_offset(mesh_idx: int) → int[source]

Global index of local vertex 0 of the given mesh. Local vertex v of that mesh maps to mesh_offset(mesh_idx) + v.

padne.solver.find_connected_layer_geom_indices(connectivity_graph: ConnectivityGraph) → set[tuple[int, int]][source]
padne.solver.compute_connectivity(prob: padne.problem.Problem) → tuple[ConnectivityGraph, set[tuple[int, int]]][source]

Run the geometric-connectivity pre-pass.

Derives the connectivity graph that links layer geometries via lumped-element networks, and flattens the connected set into the (layer_i, geom_i) pairs reachable from a driven network.

Returns (connectivity_graph, connected_layer_mesh_pairs).

padne.solver.generate_meshes_for_problem(prob: padne.problem.Problem, mesher: padne.mesh.Mesher, connected_layer_mesh_pairs: set[tuple[int, int]]) → tuple[list[padne.mesh.Mesh], list[int]][source]
padne.solver.generate_disconnected_meshes(prob: padne.problem.Problem, connected_layer_mesh_pairs: set[tuple[int, int]]) → list[list[padne.mesh.Mesh]][source]

Generate simple triangulations for disconnected copper regions.

Args:

prob: The Problem containing layers and geometry connected_layer_mesh_pairs: Set of (layer_i, geom_i) pairs that are electrically connected

Returns:

List of disconnected meshes per layer: disconnected_meshes_by_layer[layer_i] = [mesh1, mesh2, …]

class padne.solver.NodeIndexer[source]
node_to_global_index: dict[padne.problem.NodeID, int][source]
extra_source_to_global_index: dict[padne.problem.BaseLumped, int][source]
internal_node_count: int = 0[source]
classmethod create(prob: padne.problem.Problem, meshes: list[padne.mesh.Mesh], mesh_index_to_layer_index: list[int], vindex: VertexIndexer, filtered_networks: list[padne.problem.Network]) → NodeIndexer[source]
class padne.solver.TripletList[source]

COO triplet accumulator for sparse system assembly.

Duplicate (row, col) entries sum on conversion, which matches the element-stamping semantics: every stamp is an addition into the matrix.

rows: list[int] = [][source]
cols: list[int] = [][source]
values: list[float] = [][source]
add(row: int, col: int, value: float) → None[source]
to_coo(N: int) → scipy.sparse.coo_matrix[source]
padne.solver.stamp_network_into_system(network: padne.problem.Network, node_indexer: NodeIndexer, triplets: TripletList, r: numpy.ndarray) → None[source]
padne.solver.setup_ground_node(i_gnd: int, N: int, triplets: TripletList, r: numpy.ndarray) → None[source]
padne.solver.process_mesh_laplace_operators(meshes: list[padne.mesh.Mesh], conductances: list[float], vindex: VertexIndexer, N: int) → scipy.sparse.coo_matrix[source]

Assemble the mesh Laplacians into one global NxN COO matrix.

VertexIndexer assigns global indices contiguously in mesh order, so the local COO indices of each mesh translate to global ones by adding the mesh’s offset. Everything stays in numpy; no per-entry Python work.

padne.solver.produce_layer_solutions(layers: list[padne.problem.Layer], vindex: VertexIndexer, meshes: list[padne.mesh.Mesh], mesh_index_to_layer_index: list[int], v: numpy.ndarray, disconnected_meshes_by_layer: list[list[padne.mesh.Mesh]]) → list[LayerSolution][source]
padne.solver.network_has_a_dead_terminal(network: padne.problem.Network, conn_locations: dict[padne.problem.Connection, list[tuple[int, int]]], connected_layer_mesh_pairs: set[tuple[int, int]]) → bool[source]

Check if a network has any connection on a dead (disconnected) copper region.

In practice, it should not happen that a network has some dead terminals and some live terminals (that would mean ConnectivityGraph is broken), so finding the first dead terminal is enough to eliminate the whole network.

padne.solver.filter_dead_networks(prob: padne.problem.Problem, connected_layer_mesh_pairs: set[tuple[int, int]]) → list[padne.problem.Network][source]

Drop networks with any connection on disconnected copper. See network_has_a_dead_terminal for the per-network check.

padne.solver.find_best_ground_node_index(prob: padne.problem.Problem, node_indexer: NodeIndexer) → int[source]
padne.solver.compute_power_density(voltage: padne.mesh.ZeroForm, conductance: float) → padne.mesh.TwoForm[source]

Compute the power density at the mesh faces.

padne.solver.system_size(vindex: VertexIndexer, node_indexer: NodeIndexer) → int[source]

Size of the global system: mesh vertices, internal network nodes, extra variables for voltage sources/regulators, plus one ground-node row.

padne.solver.solve_system(L: scipy.sparse.spmatrix, r: numpy.ndarray, backend: SolverBackend | None = None) → tuple[numpy.ndarray, SolverInfo][source]

Solve L * v = r and return the solution vector together with diagnostics.

padne.solver.assemble_system(prob: padne.problem.Problem, meshes: list[padne.mesh.Mesh], mesh_index_to_layer_index: list[int], vindex: VertexIndexer, filtered_networks: list[padne.problem.Network], node_indexer: NodeIndexer) → tuple[scipy.sparse.csc_matrix, numpy.ndarray][source]

Allocate (L, r) and stamp the mesh Laplacians, all networks, and the ground node. Returns the system ready to be passed to solve_system.

padne.solver.solve(prob: padne.problem.Problem, mesher_config: padne.mesh.Mesher.Config | None = None, backend: SolverBackend | None = None) → Solution[source]

Solve the given PCB problem to find voltage and current distribution.

Args:

prob: The Problem object containing layers and lumped elements mesher_config: Configuration for mesh generation, uses defaults if None backend: Sparse direct solver to use, None picks the default (SciPy)

Returns:

A Solution object with the computed results