tensormesh.distributed¶
Mesh partitioning and parallel assembly across multiple devices,
with integration into torch-sla’s distributed sparse solver. See
the user guide chapter for a worked
walkthrough.
DistributedMesh¶
- class DistributedMesh(mesh: Mesh, num_partitions: int | None = None, method: str = 'coordinate', devices: List[device] | None = None)[source]¶
Bases:
objectPartitioned mesh for multi-GPU parallel assembly.
Wraps
partition_meshto split a global mesh into submeshes, each assigned to a separate device. Each submesh stores anorig_nidmapping (local node index → global node index) inpoint_data.- Parameters:
mesh (Mesh) – Global mesh (typically on CPU).
num_partitions (int, optional) – Number of partitions. Defaults to
torch.cuda.device_count(), or 2 if no CUDA devices are available.method (str, optional) – Partitioning method:
'coordinate'(default, fast RCB),'spectral', or'metis'.devices (list of device, optional) – Devices to assign partitions to. Defaults to
cuda:0, cuda:1, ...orcpuif CUDA is unavailable.
Examples
>>> mesh = tm.Mesh.gen_rectangle(chara_length=0.05) >>> dmesh = DistributedMesh(mesh, num_partitions=4) >>> print(dmesh.num_partitions, dmesh.n_global_points)
DSparseMatrix¶
- class DSparseMatrix(dsparse_tensor: DSparseTensor, partition_uuid: int | None = None)[source]¶
Bases:
_FEMSparsityMixin,DSparseTensorDistributed FEM sparse matrix.
Subclass of
torch_sla.distributed.DSparseTensorsoisinstance(D, DSparseTensor)holds and torch-sla’s free functions (solve / io / arithmetic dispatch) accept it.Constructed by wrapping an existing
DSparseTensor(typically returned byDSparseTensor.partition()ordistributed_element_assemble()).- Parameters:
dsparse_tensor – The underlying distributed sparse tensor.
DSparseMatrixcopies its state and adds the FEM identity layer on top.partition_uuid – 63-bit UUID identifying the partition build. Pass when deriving a new
DSparseMatrixfrom an existing one (so caches are shared); leave asNoneto freshly generate + broadcast.
- property partition¶
The
Partitionfrom the spec.
- property layout_signature: Tuple¶
Sequence-identity signature scoped to the partition build.
Combines the mixin’s local sequence identity with the partition UUID so two matrices on the same partition share signatures (and therefore caches) while two matrices on independently-built partitions do not, even if local layouts coincidentally match.
- to(*args, **kw) DSparseMatrix[source]¶
- cuda(device=None) DSparseMatrix[source]¶
- cpu() DSparseMatrix[source]¶
- double() DSparseMatrix[source]¶
- float() DSparseMatrix[source]¶
- to_single() SparseMatrix[source]¶
Allgather the distributed matrix into a global single-device
SparseMatrixmaterialised on every rank.Used as a temporary bridge to FEM operators that have not yet learned to consume
DSparseMatrixdirectly (notablyCondenser). Costs an all-gather of the COO triples; avoid in hot paths.
The @distributed decorator¶
- distributed(asm_cls: Type[T]) Type[T][source]¶
Class decorator: turn an Assembler class into a distributed one.
The wrapped class shares the original’s weak form, quadrature setup and element kernel – only the entry points (
from_mesh+__call__) are swapped to run a distributed assembly path.- Parameters:
asm_cls – An
ElementAssemblerorNodeAssemblersubclass. The decision between matrix- and vector-flavoured assembly is taken at the wrapped__call__based on the original class type.- Returns:
A new subclass of
asm_clswith overridden entry points; thename is prefixed with
Distributedfor repr / debugging clarity.
Distributed assembly¶
- distributed_element_assemble(assembler_cls: Type[ElementAssembler], dmesh: DistributedMesh, quadrature_order: int = 2, project: str = 'reduce', call_kwargs: dict | None = None, **assembler_kwargs) DSparseTensor[source]¶
Assemble element matrix in parallel across multiple devices.
Assemblers are created sequentially (for CUDA thread-safety), then assembly computation runs in parallel threads on separate GPUs.
- Parameters:
assembler_cls (Type[ElementAssembler]) – The assembler class (e.g.,
LaplaceElementAssembler).dmesh (DistributedMesh) – Partitioned mesh with device assignments.
quadrature_order (int, optional) – Quadrature order for integration. Default: 2.
project (str, optional) – Projection method:
'reduce'or'sparse'. Default:'reduce'.call_kwargs (dict, optional) – Extra keyword arguments passed to
assembler.__call__()(e.g.,point_data,scalar_data).**assembler_kwargs – Extra keyword arguments passed to
assembler_cls.from_mesh().
- Returns:
Distributed sparse matrix ready for distributed solve.
- Return type:
DSparseTensor
- distributed_element_assemble_per_rank(assembler_cls: Type[ElementAssembler], dmesh: DistributedMesh, rank: int, quadrature_order: int = 2, project: str = 'reduce', call_kwargs: dict | None = None, **assembler_kwargs) DSparseTensor[source]¶
True distributed assembly: each rank does ONLY its submesh.
Pipeline (every rank in lockstep, torch.distributed must be init’d):
Compute partition_ids deterministically from dmesh (lowest rank wins for shared boundary nodes). No communication.
This rank assembles submesh[rank] -> local triples in global coords. No threading, no work for other ranks’ submeshes.
For each local triple, route it to the rank that owns its row. all_to_all_single exchange (NCCL on CUDA, gloo on CPU).
Coalesce incoming triples (sum duplicate (row,col) pairs from elements assembled by different ranks that touch the same row).
Discover halo columns: distinct columns in received triples that aren’t owned by this rank.
Build Partition + remap to local coords + DSparseTensor.from_sparse_local.
Total compute is roughly the same as single-process assembly divided across ranks, instead of duplicated.
Caller is responsible for initialising torch.distributed before calling. The (rank, world) is taken from dist.
Returns a row-shard DSparseTensor with each rank holding only its owned-row contributions.
- distributed_element_assemble_to_sparse(assembler_cls: Type[ElementAssembler], dmesh: DistributedMesh, quadrature_order: int = 2, project: str = 'reduce', call_kwargs: dict | None = None, **assembler_kwargs) SparseMatrix[source]¶
Assemble element matrix in parallel, returning a global SparseMatrix.
Same as
distributed_element_assemble()but returns a standardSparseMatrixinstead of torch-sla’sDSparseTensor.
- distributed_node_assemble(assembler_cls: Type[NodeAssembler], dmesh: DistributedMesh, quadrature_order: int = 2, project: str = 'reduce', point_data: Dict[str, Tensor] | None = None, call_kwargs: dict | None = None, **assembler_kwargs) Tensor[source]¶
Assemble node vector (RHS) in parallel across multiple devices.
Utilities¶
- broadcast_from_rank0(factory: Callable[[], Tensor], *, dst_device: device | None = None) Tensor[source]¶
Sample on rank 0, broadcast to every rank.
- Parameters:
factory – Zero-arg callable returning a fresh
torch.Tensor. Called only on rank 0 when a process group is active; called on every rank in single-process / no-process-group mode.dst_device – Optional override for the device the broadcast lands on. Defaults to CUDA when NCCL is the active backend, CPU otherwise; the returned tensor is moved back to CPU so it composes with caller code that does its own
.to(device)later. Pass an explicit device if you want a different placement.
- Returns:
A tensor with the same content on every rank. Single-process mode returns
factory()directly.- Return type:
Notes
Uses
torch.distributed.broadcast_object_list()under the hood, which pickles the tensor. This is fine for the typical use case (small coefficient matrices, scalar parameters); for large buffers prefer manualdist.broadcastwith pre-allocated receive buffers on every rank.