patchwork.mesh
Block-structured adaptive mesh hierarchy and mpiarray-backed patch fields.
Attributes
| Name | Description |
|---|---|
IndexBox | No description. |
PhysicalBounds | No description. |
Classes
| Name | Description |
|---|---|
AMRHierarchy | A 2D nested patch hierarchy with mpiarray-backed fields. |
Patch | One rectangular patch; its box uses level-local, half-open cell indices. |
IndexBoxattributemodule attribute#
IndexBox = tuple[tuple[int, int], tuple[int, int]]PhysicalBoundsattributemodule attribute#
PhysicalBounds = tuple[tuple[float, float], tuple[float, float]]AMRHierarchyclass#
class AMRHierarchy(base_shape: tuple[int, int], bounds: PhysicalBounds = ((0.0, 1.0), (0.0, 1.0)), *, refinement_ratio: int = 2, periodic: tuple[bool, bool] = (False, False), base_boxes: Sequence[Sequence[Sequence[int]]] | None = None, placement: str = 'all', base_ranks: Sequence[int | Sequence[int] | None] | None = None, comm: Any = None)A 2D nested patch hierarchy with mpiarray-backed fields.
Patch coordinates are integer cell bounds on their own refinement level.
A child patch’s bounds must align with the parent level and lie within the
patches of that level. The refinement ratio is two by default. Fields
are cell-centered, or node-centered (one value per cell corner, see
:meth:allocate_field). periodic flags the axes on which the last
cell neighbours the first; the node-centered solver
(:class:~patchwork.nodal.NodalPoisson) uses it. Without an MPI launcher,
mpiarray supplies a serial communicator and the same interface works with a
single process.
Under MPI every rank builds the same hierarchy, with the same calls in the
same order. Each patch then lives on some of the ranks of comm and has
a communicator of its own for them: a small patch on one rank, a large one
on several, over which mpiarray splits its fields. placement says
where a patch goes that is added without ranks: "all" puts it on
every rank, "balanced" on the one rank that holds the fewest cells so
far.
The domain is the rectangle of base_shape cells, or with base_boxes
the union of those boxes of base cells: an L, a T, a rectangle with a notch.
The boxes are the patches of level zero. Where the domain does not fill
the rectangle, its edge is a boundary like the rectangle’s own faces.
base_shapeattributeinstance attribute#
base_shape = tuple((int(n) for n in base_shape))boundsattributeinstance attribute#
bounds = tuple((tuple((float(x) for x in axis)) for axis in bounds))commattributeinstance attribute#
comm = mpa.default_comm() if comm is None else commperiodicattributeinstance attribute#
periodic = (bool(periodic[0]), bool(periodic[1]))placementattributeinstance attribute#
placement = placementrefinement_ratioattributeinstance attribute#
refinement_ratio = int(refinement_ratio)cells_on_rankproperty#
cells_on_rank: tuple[float, ...]Cells of all patches held by every rank (a patch on several ranks is shared).
field_namesproperty#
field_names: tuple[str, ...]Names allocated throughout the hierarchy.
local_patchesproperty#
local_patches: tuple[Patch, ...]The patches this rank holds a part of, ordered as :attr:patches.
lower_boundproperty#
lower_bound: tuple[float, float]Physical lower-left corner of the domain.
max_levelproperty#
max_level: intFinest level currently present.
patchesproperty#
patches: tuple[Patch, ...]All patches, ordered by level and then lower corner.
upper_boundproperty#
upper_bound: tuple[float, float]Physical upper-right corner of the domain.
add_patchmethod#
def add_patch(level: int, box: Sequence[Sequence[int]], ranks: int | Sequence[int] | None = None) -> PatchAdd a non-overlapping, properly nested fine patch.
box is half-open and measured in cells at level. Bounds must
align to the cells of the level below, and the patch must lie within
the patches of that level (it may span several that touch).
ranks are the ranks of the hierarchy’s communicator that hold the
patch: one rank or several, over which its fields are split. Without
it, placement decides. Under MPI every rank adds the same patches
in the same order.
allocate_fieldmethod#
def allocate_field(name: str, *, dtype: Any = float, halo: int = 1, split: Any = 0, centering: str = 'cell', conservative: bool = False) -> NoneAllocate a named zero-filled field on every existing patch.
A patch’s arrays exist on the ranks that hold it. The default splits
them over those ranks along the first axis (which then needs at least
one cell per rank). Use split=None to replicate them instead. With
centering="node" the values sit on the cell corners: a patch of
(nx, ny) cells holds (nx + 1, ny + 1) of them, and patches that
touch or nest share the nodes they have in common.
A conservative field is an amount per area, such as a density, whose
integral (:meth:integral) :meth:regrid keeps: it is restricted
before a regrid, and the cells of new patches are filled from the level
below so that the fine cells of a coarse cell keep its mean. It must be
cell-centered.
centeringmethod#
def centering(name: str) -> str"cell" or "node": where the values of field name sit.
exchange_halosmethod#
def exchange_halos(name: str) -> NoneFill the halo of every patch of field name from the rest of the hierarchy.
From the patches of the same level the halo overlaps (across periodic
faces too), else by bilinear interpolation of the level below, and
between the ranks of each patch. Halo points outside the domain are
left for the boundary conditions. Collective over comm; the
schedule is built at the first call and kept until a patch is added.
See :class:~patchwork.halos.HaloExchange.
gathermethod#
def gather(patch: Patch, name: str, root: int | None = None) -> AnyThe whole of field name of patch as a NumPy array.
Collective over the hierarchy’s communicator: every rank calls it,
whether it holds the patch or not. The array is returned on every rank,
or with root on that rank only (None on the others).
integralmethod#
def integral(name: str) -> floatThe integral of field name over the domain, from the finest values.
Cell-centered: the sum of value times area over the cells that no finer
patch covers. Node-centered: the trapezoidal rule over those cells.
Collective over comm; the same value on every rank.
level_shapemethod#
def level_shape(level: int) -> tuple[int, int]Number of cells in the full domain at a refinement level.
patches_at_levelmethod#
def patches_at_level(level: int) -> tuple[Patch, ...]Patches on one level.
regridmethod#
def regrid(tags: Mapping[int, Any], *, efficiency: float = 0.7, buffer: int = 1, nesting: int = 1, max_patch_size: int | None = None, placement: str | None = None) -> AMRHierarchyA new hierarchy with patches over the tagged cells, and the fields moved to it.
tags maps a level to booleans of that level’s shape
(:meth:level_shape): the cells that need the next finer level. Make
them with :func:~patchwork.regrid.gradient_tags,
:func:~patchwork.regrid.function_tags or by hand; each rank may tag
from the data it holds, and the tags are combined over the ranks.
Level by level from the coarsest, the tagged cells, grown by
buffer cells, are covered with boxes by
:func:~patchwork.regrid.cluster (at least efficiency of their
cells tagged, at most max_patch_size cells of the new level long).
A level also covers, with a margin, what the finer levels need, and
each patch keeps nesting cells of the level below between itself
and that level’s edge (the edge of the domain excepted). The base level
stays as it is. The levels above the last tagged one are dropped.
Every field is moved: a point takes the value of an old patch of the
same level that has it, else the bilinear interpolation of the new
level below. A conservative field (see :meth:allocate_field) is first
restricted on this hierarchy, and its new cells are filled so that the
fine cells of a coarse cell keep its mean: its :meth:integral is the
same before and after. Halos are not filled; call :meth:exchange_halos.
New patches are placed by placement (default: this hierarchy’s),
the largest first. Collective over comm; this hierarchy is left as
it is, except for the restriction of conservative fields.
restrictmethod#
def restrict(name: str) -> NoneGive the points of field name under finer patches the fine values.
The mean of the fine cells (cell-centered) or the coinciding fine node
(node-centered), finest level first. Collective over comm; call
:meth:exchange_halos after it for the halos. See
:class:~patchwork.halos.Restriction.
spacingmethod#
def spacing(level: int = 0) -> tuple[float, float]Cell spacing at a refinement level.
Patchclassdataclass#
class Patch(level: int, box: IndexBox, ranks: tuple[int, ...] = (0), comm: Any = field(default=None, repr=False, compare=False), _fields: dict[str, DistributedArray] = field(default_factory=dict, repr=False))One rectangular patch; its box uses level-local, half-open cell indices.
A patch lives on some of the ranks of its hierarchy: ranks, the same on
every rank. On those ranks comm is the patch’s own communicator, over
which mpiarray splits its fields; on the others it is None and the
patch holds no data (is_local is false).
boxattributeinstance attribute#
box: IndexBoxcommattributeclass attributeinstance attribute#
comm: Any = field(default=None, repr=False, compare=False)levelattributeinstance attribute#
level: intranksattributeclass attributeinstance attribute#
ranks: tuple[int, ...] = (0)field_namesproperty#
field_names: tuple[str, ...]Names of allocated fields on this patch.
is_localproperty#
is_local: boolWhether this rank holds a part of the patch.
node_shapeproperty#
node_shape: tuple[int, int]Number of nodes (cell corners) along each axis: one more than cells.
shapeproperty#
shape: tuple[int, int]Number of cells along each axis.
fieldmethod#
def field(name: str) -> DistributedArrayGet an allocated field by name, on a rank that holds the patch.
local_cell_centersmethod#
def local_cell_centers(hierarchy: AMRHierarchy, name: str | None = None) -> tuple[Any, Any]Coordinates of this rank’s cells of the cell-centered field name.
Two arrays of the shape of field(name).local, for filling it.
Without name, of the first cell-centered field allocated.
local_node_coordinatesmethod#
def local_node_coordinates(hierarchy: AMRHierarchy, name: str) -> tuple[Any, Any]Coordinates of this rank’s nodes of the node-centered field name.
Two arrays of the shape of field(name).local, for filling it.
node_coordinatesmethod#
def node_coordinates(hierarchy: AMRHierarchy) -> tuple[Any, Any]Coordinates of the nodes of the whole patch along each axis (1D arrays).
physical_boundsmethod#
def physical_bounds(hierarchy: AMRHierarchy) -> PhysicalBoundsPhysical rectangle occupied by this patch.