Communicators

A communicator exchanges data across the seam between blocks for one patch type. connectivity builds and caches one per patch type on first use – reached as grid.connectivity.periodic, .mixing and .nonmatch – pairing each patch to its partner on a neighbouring block the first time it is used. Pairing is therefore automatic, triggered by the first exchange, e.g. a call to apply_bconds(). See Connectivity for how the pairing cache is invalidated on a topology change.

The pairings can be inspected directly, by calling say grid.connectivity.periodic.pair() for periodic patches. It returns a dict keyed by the (bid, pid) identifier of each patch, indexing like grid[bid].patches[pid]. The dict values are the corresponding (bid, pid) of the patch it matches and the geometric transform between the two. Both halves of a pair appear as keys, so the mapping can be followed from either side:

pairs = grid.connectivity.periodic.pair()
# block 0 patch 0 is paired with block 1 patch 0, and vice versa
pairs[(0, 0)]  # ((1, 0), transform)
pairs[(1, 0)]  # ((0, 0), transform)

Each communicator below is built lazily by GridConnectivity from the pairing above, and exposes apply() (write the exchange straight into block.conserved) and, where the patch type needs it, exchange() (compute the cross-plane targets without applying them).

Periodic boundary condition communication and data averaging utilities.

This module provides the PeriodicCommunicator class which manages data exchange and averaging between periodic patch pairs in multi-block grids. The communicator takes connectivity information from the grid and sets up efficient index mappings that account for arbitrary coordinate transformations (permutations and flips) between matching periodic patches. The class prunes bidirectional connectivity to create unidirectional pairs, precomputes transformed index arrays for fast access, and applies periodic boundary conditions by averaging conserved flow variables at corresponding spatial locations using Fortran-accelerated routines. This ensures consistency across periodic boundaries in turbomachinery simulations with blade row periodicity or other rotationally symmetric configurations.

class periodic_communicator.PeriodicCommunicator(grid, periodic_pairs)[source]

Manages data communication between periodic patches.

Takes the output of grid.connectivity.periodic.pair() and sets up matching ijk indices for efficient periodic boundary condition communication.

Parameters:
  • grid (Grid) – The grid containing blocks with periodic patches

  • periodic_pairs (dict) – Dictionary from grid.connectivity.periodic.pair() with format: {(bid, pid): ((nxbid, nxpid), (perm, flip))}

pairs

Pruned unidirectional pairs: {(bid, pid): ((nxbid, nxpid), (perm, flip))}

Type:

dict

ijk_node_flat

Flattened ijk indices: {(bid, pid): indices.reshape(-1, 3)} for both source patches (untransformed) and target patches (transformed)

Type:

dict

apply()[source]

Apply periodic boundary conditions by averaging conserved variables.

Loops over all patch pairs and calls Fortran average_by_ijk to average the conserved variables at corresponding ijk locations between patches.

exchange_faces()[source]

Fill each block’s face-buffer halo layer from its periodic partner.

The one seam exchange the viscous pass needs, run between set_tau_q_faces and set_visc_force: it fills the halo layer of each block’s tau_q_faces with the adjacent block’s owned edge-cell tau/q, so the face flux there averages two real cell values instead of the -edge ghost the producer seeded. Reads layer 0 (the partner’s own edge cells) and writes layer 1 (this side’s halo), so it never reads and writes the same storage and needs no temporary.

TWO CALLS PER PAIR, one each way, because self.pairs is pruned to a single key per pair, and a copy cannot move both directions at once. A block periodic to itself cannot catch a regression here – both ends are the same block – which is why the gate for this is a two-block case.

Mixing plane boundary condition communication.

MixingCommunicator pairs the two sides of the mixing plane of MixingPatch, holds the relaxation factor and the per-pair diagnostics, and carries out the exchange itself: it takes the cross-plane flux mismatch, splits it by direction of propagation after Saxer and Giles [2], and writes the result in the mix variables \([h_0, s, V_r, V_\theta, p]\) its patches take their pitchwise-mean residuals against.

A run with mix_reflective set bypasses all of that, exchanging the plain average of the two circumferential means instead.

class mixing_communicator.MixingCommunicator(grid, mixing_pairs)[source]

Initialize with grid and mixing patch pairs.

Parameters:
  • grid (Grid) – The grid instance.

  • mixing_pairs (dict) – Mapping of mixing patch pair information.

Raises:

ValueError – If the two sides of a plane disagree on rf_exchange.

leak = 0.0

Anti-windup leak on the integrating relaxation, as a fraction of rf_exchange per exchange.

The exchange accumulates its correction onto the previous target rather than re-anchoring to the live interface baseline every step, so that the fixed point is exact flux balance rather than the proportional form’s standing offset (Holmes [3] Eq. 15, applied to the auxiliary cells rather than re-derived each step). A pure integrator can wind up while the mismatch has not yet resolved – most a reversed station whose own boundary condition has not caught up with a target that has already moved past what the flow was ever in. A positive leak bleeds the target back toward the live baseline each step, trading a leak/rf_exchange-scaled residual flux mismatch for a bound on how far the target can wander. Zero is exact Holmes; engage only if a station is seen to wind up.

Ma_clip = 0.05

Floor on \(\lvert \mathit{Ma}_x \rvert\) of the symmetrised mean state the Jacobians are evaluated on.

Holmes [3] Eq. 16: as the mean normal velocity tends to zero the eigenvalues of the transformation matrices grow, so the interface over-controls to make up for the slow advection the small velocity implies. Bounding the magnitude – not the direction, which a reversed station keeps – is his remedy and this is ember’s form of it. It also keeps flux_to_primitive(), which divides by the axial velocity, away from zero.

See Ma_clip_max for the other end of the same band.

Ma_clip_max = 0.9

Ceiling on \(\lvert \mathit{Ma}_x \rvert\) of that same state, the other end of Ma_clip’s band.

Holmes bounds the normal velocity only from below, because that is where his eigenvalues blow up. The upper end matters here for a different reason: the plane’s whole characteristic treatment is derived for a mean state subsonic normal to it (see MixingPatch), and a station that briefly runs past sonic during a transient would otherwise be linearised with the wrong number of incoming characteristics rather than merely inaccurately. Clipping keeps such a station inside the theory until the flow brings it back.

get_stats(bid, pid)[source]

Return last-step relaxation increment for one pair.

Returns:

Key du (last relaxation increment in the exchanged target’s own variables, shape (nspan, 5)). Returns None if the pair has not been exchanged yet.

Return type:

dict or None

exchange()[source]

Compute and write targets for all pairs (no apply step).

Under mix_reflective a pair is instead given the plain average of the two circumferential means.

Non-matching patch boundary condition communication.

This module provides the NonMatchCommunicator class which manages data exchange between non-matching patch pairs in multi-block grids. Unlike periodic patches that require identical node distributions, non-matching patches allow arbitrary mesh refinement changes across block boundaries.

The communicator uses parametric interpolation to transfer conserved variables between patches with different nodal distributions but identical physical locations. It precomputes parametric (u,v) coordinates during initialization and uses bilinear interpolation to transfer data at each timestep.

class nonmatch_communicator.NonMatchCommunicator(grid, nonmatch_pairs)[source]

Manages data communication between non-matching patches.

Takes the output of grid.connectivity.nonmatch.pair() and sets up parametric interpolation for efficient non-matching boundary condition communication.

Unlike PeriodicCommunicator which averages at matching ijk indices, this communicator interpolates between patches with different node distributions.

Parameters:
  • grid (Grid) – The grid containing blocks with non-matching patches

  • nonmatch_pairs (dict) – Dictionary from grid.connectivity.nonmatch.pair() with format: {(bid, pid): ((nxbid, nxpid), (perm, flip))}

pairs

Pruned unidirectional pairs: {(bid, pid): ((nxbid, nxpid), (perm, flip))}

Type:

dict

uv_coords

Cached parametric coordinates: {(bid, pid): uv_array} where uv_array has shape (…, …, 2) with [u, v] ∈ [0,1]^2

Type:

dict

apply()[source]

Apply non-matching boundary conditions via interpolation.

Loops over all patch pairs and interpolates conserved variables between patches with different node distributions. Performs bidirectional interpolation and averaging for consistency at the interface.