Array wrappers
The PencilArrays module defines types for handling MPI-distributed data.
The most important types are:
PencilArray: array wrapper including MPI decomposition information. Takes local indices starting at 1, regardless of the location of each MPI process on the global topology.GlobalPencilArray:PencilArraywrapper that takes global indices, which generally don't start at 1. See also Global views.
Construction
An uninitialised PencilArray can be constructed from a Pencil instance as
pencil = Pencil(#= ... =#)
A = PencilArray{Float64}(undef, pencil)
parent(A) # returns the Array wrapped by `A`This allocates a new Array with the local dimensions and data type associated to the Pencil.
One can also construct a PencilArray wrapper from an existing AbstractArray, whose dimensions must be compatible with the Pencil configuration. For instance, the following works:
dims = size_local(pencil, MemoryOrder()) # dimensions must be in memory order!
data = zeros(dims)
A = PencilArray(pencil, data)Note that data does not need to be a Array, but can be any subtype of AbstractArray.
It is also possible to construct higher dimensional arrays, as in:
data = zeros(dims..., 3, 2)
A = PencilArray(pencil, data)This will construct a PencilArray where the rightmost dimensions (called extra dimensions in the PencilArrays API) will never be split among MPI processes.
Dimension permutations
Unlike the wrapped AbstractArray, the PencilArray wrapper takes indices in logical order. For instance, if the underlying permutation of the Pencil is (2, 3, 1), then A[i, j, k] points to the same value as parent(A)[j, k, i].
Global views
PencilArrays are accessed using local indices that start at 1, regardless of the location of the subdomain associated to the local process on the global grid. Sometimes it may be more convenient to use global indices describing the position of the local process in the domain. For this, the global_view function is provided that generates an OffsetArray wrapper taking global indices.
For more details, see for instance the gradient example in the PencilFFTs docs.
Regular PencilArrays have more functionality than global view wrappers. This includes broadcasting, which is currently not supported for global views. In general it should be preferred to work with PencilArrays.
Types
PencilArrays.PencilArray — TypePencilArray(pencil::Pencil, data::AbstractArray{T,N})Create array wrapper with pencil decomposition information.
The array dimensions and element type must be consistent with those of the given pencil.
If the Pencil has an associated index permutation, then data must have its dimensions permuted accordingly (in memory order).
Unlike data, the resulting PencilArray should be accessed with unpermuted indices (in logical order).
Example
Suppose pencil has local dimensions (10, 20, 30) before permutation, and has an asociated permutation (2, 3, 1). Then:
data = zeros(20, 30, 10) # parent array (with dimensions in memory order)
u = PencilArray(pencil, data) # wrapper with dimensions (10, 20, 30)
@assert size_local(u) === (10, 20, 30)
u[15, 25, 5] # BoundsError (15 > 10 and 25 > 20)
u[5, 15, 25] # correct
parent(u)[15, 25, 5] # correct
The data array can have one or more extra dimensions to the right (slow indices), which are not affected by index permutations.
Example
dims = (20, 30, 10)
PencilArray(pencil, zeros(dims...)) # works (scalar)
PencilArray(pencil, zeros(dims..., 3)) # works (3-component vector)
PencilArray(pencil, zeros(dims..., 4, 3)) # works (4×3 tensor)
PencilArray(pencil, zeros(3, dims...)) # failsPencilArray{T}(undef, pencil::Pencil, [extra_dims...])Allocate an uninitialised PencilArray that can hold data in the local pencil.
Extra dimensions, for instance representing vector components, can be specified. These dimensions are added to the rightmost (slowest) indices of the resulting array.
Example
Suppose pencil has local dimensions (20, 10, 30). Then:
PencilArray{Float64}(undef, pencil) # array dimensions are (20, 10, 30)
PencilArray{Float64}(undef, pencil, 4, 3) # array dimensions are (20, 10, 30, 4, 3)More examples:
julia> pen = Pencil((20, 10, 12), MPI.COMM_WORLD);
julia> u = PencilArray{Float64}(undef, pen);
julia> summary(u)
"20×10×12 PencilArray{Float64, 3}(::Pencil{3, 2, NoPermutation, Array})"
julia> PencilArray{Float64}(undef, pen, 4, 3) |> summary
"20×10×12×4×3 PencilArray{Float64, 5}(::Pencil{3, 2, NoPermutation, Array})"
PencilArrays.GlobalPencilArray — TypeGlobalPencilArray{T,N} <: AbstractArray{T,N}Alias for an OffsetArray wrapping a PencilArray.
Unlike PencilArrays, GlobalPencilArrays take global indices, which in general don't start at 1 for a given MPI process.
The global_view function should be used to create a GlobalPencilArray from a PencilArray.
PencilArrays.PencilArrayCollection — TypePencilArrayCollectionUnionAll type describing a collection of PencilArrays.
Such a collection can be a tuple or an array of PencilArrays.
Collections are by assumption homogeneous: each array has the same properties, and in particular, is associated to the same Pencil configuration.
For convenience, certain operations defined for PencilArray are also defined for PencilArrayCollection, and return the same value as for a single PencilArray. Some examples are pencil, range_local and get_comm.
Also note that functions from Base, such as size, ndims and eltype, are not overloaded for PencilArrayCollection, since they already have a definition for tuples and arrays (and redefining them would be type piracy...).
PencilArrays.AbstractManyPencilArray — TypeAbstractManyPencilArray{N,M}Abstract type specifying a container holding M different PencilArray views to the same underlying.
All views share the same dimensionality N. In principle, their element types can be different.
A concrete implementation, ManyPencilArray, is proposed in which all arrays have the same element type T.
PencilArrays.ManyPencilArray — TypeManyPencilArray{T,N,M} <: AbstractManyPencilArray{N,M}Container holding M different PencilArray views to the same underlying data buffer. All views share the same element type T and dimensionality N.
This can be used to perform in-place data transpositions with transpose!.
ManyPencilArray{T}(undef, pencils...; extra_dims=())Create a ManyPencilArray container that can hold data of type T associated to all the given Pencils.
The optional extra_dims argument is the same as for PencilArray.
Methods
PencilArray
PencilArrays.extra_dims — Methodextra_dims(x::PencilArray)
extra_dims(x::PencilArrayCollection)Return tuple with size of "extra" dimensions of PencilArray.
PencilArrays.Pencils.MPITopologies.get_comm — Methodget_comm(x::PencilArray)
get_comm(x::PencilArrayCollection)Get MPI communicator associated to a pencil-distributed array.
PencilArrays.Permutations.permutation — Functionpermutation(::Type{<:PencilArray})
permutation(x::PencilArray)
permutation(x::PencilArrayCollection)Get index permutation associated to the given PencilArray.
Returns NoPermutation() if there is no associated permutation.
PencilArrays.global_view — Methodglobal_view(x::PencilArray)Create an OffsetArray of a PencilArray that takes global indices in logical order.
PencilArrays.ndims_extra — Methodndims_extra(::Type{<:PencilArray})
ndims_extra(x::PencilArray)
ndims_extra(x::PencilArrayCollection)Number of "extra" dimensions associated to PencilArray.
These are the dimensions that are not associated to the domain geometry. For instance, they may correspond to vector or tensor components.
These dimensions correspond to the rightmost indices of the array.
The total number of dimensions of a PencilArray is given by:
ndims(x) == ndims_space(x) + ndims_extra(x)PencilArrays.ndims_space — Methodndims_space(x::PencilArray)
ndims_space(x::PencilArrayCollection)Number of dimensions associated to the domain geometry.
These dimensions correspond to the leftmost indices of the array.
The total number of dimensions of a PencilArray is given by:
ndims(x) == ndims_space(x) + ndims_extra(x)Base.parent — Methodparent(x::PencilArray)Return array wrapped by a PencilArray.
PencilArrays.pencil — Methodpencil(x::PencilArray)Return decomposition configuration associated to a PencilArray.
Base.pointer — Methodpointer(x::PencilArray)Return pointer to the start of the underlying data.
Use with caution: this may not make a lot of sense if the underlying data is not contiguous or strided (e.g. if the PencilArray is wrapping a non-strided SubArray).
PencilArrays.Pencils.range_local — Methodrange_local(x::PencilArray, [order = LogicalOrder()])
range_local(x::PencilArrayCollection, [order = LogicalOrder()])Local data range held by the PencilArray.
By default the dimensions are returned in logical order.
PencilArrays.Pencils.range_remote — Methodrange_remote(x::PencilArray, coords, [order = LogicalOrder()])
range_remote(x::PencilArrayCollection, coords, [order = LogicalOrder()])Get data range held by the PencilArray in a given MPI process.
The location of the MPI process in the topology is determined by the coords argument, which can be given as a linear or Cartesian index.
See range_remote(::Pencil, ...) variant for details.
Base.similar — Methodsimilar(x::PencilArray, [element_type=eltype(x)], [dims]) -> PencilArrayReturns a PencilArray similar to x.
In particular, the new array shares the same parallel decomposition (the same Pencil) than x. This means that the dimensions of the new array must be the same as those of x. Note that the optional dims argument is allowed for the sole reason of making things work nicely with other packages (such as StructArrays.jl), but things will fail if dims ≠ size(x).
Examples
julia> pen = Pencil((20, 10, 12), MPI.COMM_WORLD);
julia> u = PencilArray{Float64}(undef, pen);
julia> similar(u) |> summary
"20×10×12 PencilArray{Float64, 3}(::Pencil{3, 2, NoPermutation, Array})"
julia> similar(u, size(u)) |> summary
"20×10×12 PencilArray{Float64, 3}(::Pencil{3, 2, NoPermutation, Array})"
julia> similar(u, ComplexF32) |> summary
"20×10×12 PencilArray{ComplexF32, 3}(::Pencil{3, 2, NoPermutation, Array})"
julia> similar(u, (4, 3, 8))
ERROR: DimensionMismatch: cannot construct a similar PencilArray with different size
julia> similar(u, (4, 3)) |> summary
ERROR: DimensionMismatch: cannot construct a similar PencilArray with different size
julia> similar(u, ComplexF32) |> summary
"20×10×12 PencilArray{ComplexF32, 3}(::Pencil{3, 2, NoPermutation, Array})"
julia> similar(u, ComplexF32, (4, 3))
ERROR: DimensionMismatch: cannot construct a similar PencilArray with different sizesimilar(x::PencilArray, [element_type = eltype(x)], p::Pencil)Create a PencilArray with the decomposition described by the given Pencil.
This variant may be used to create a PencilArray that has a different decomposition than the input PencilArray.
Examples
julia> pen_u = Pencil((20, 10, 12), (2, 3), MPI.COMM_WORLD);
julia> u = PencilArray{Float64}(undef, pen_u);
julia> pen_v = Pencil(pen_u; decomp_dims = (1, 3), permute = Permutation(2, 3, 1))
Decomposition of 3D data
Data dimensions: (20, 10, 12)
Decomposed dimensions: (1, 3)
Data permutation: Permutation(2, 3, 1)
Array type: Array
julia> v = similar(u, pen_v);
julia> summary(v)
"20×10×12 PencilArray{Float64, 3}(::Pencil{3, 2, Permutation{(2, 3, 1), 3}, Array})"
julia> pencil(v) === pen_v
true
julia> vint = similar(u, Int, pen_v);
julia> summary(vint)
"20×10×12 PencilArray{Int64, 3}(::Pencil{3, 2, Permutation{(2, 3, 1), 3}, Array})"
julia> pencil(vint) === pen_v
true
similar(p::Pencil, [A = typeof_array(p)], [dims = size_global(p)])Returns a Pencil decomposition with global dimensions dims and with underlying array type A.
Typically, A should be something like Array or CuArray (see Pencil for details).
Base.length — Methodlength(A::AbstractManyPencilArray)Returns the number of PencilArrays wrapped by A.
length(x::PencilArray)Get the local number of elements stored in the PencilArray.
Equivalent to length_local(x).
PencilArrays.Pencils.length_local — Methodlength_local(x::PencilArray)Get linear length of the local data held by a PencilArray.
PencilArrays.Pencils.length_global — Methodlength_global(x::PencilArray)Get linear length of the global data held by a PencilArray.
Base.size — Methodsize(x::PencilArray)Return the local dimensions of a PencilArray in logical order.
Defined as size_local(x, LogicalOrder()).
PencilArrays.Pencils.size_local — Methodsize_local(x::PencilArray, [order = LogicalOrder()])
size_local(x::PencilArrayCollection, [order = LogicalOrder()])Local dimensions of the data held by the PencilArray.
See also size_local(::Pencil).
PencilArrays.Pencils.size_global — Methodsize_global(x::PencilArray, [order = LogicalOrder()])
size_global(x::PencilArrayCollection, [order = LogicalOrder()])Global dimensions associated to the given array.
By default, the logical dimensions of the dataset are returned.
If order = LogicalOrder(), this is the same as size(x).
See also size_global(::Pencil).
PencilArrays.sizeof_global — Methodsizeof_global(x::PencilArray)
sizeof_global(x::PencilArrayCollection)Global size of array in bytes.
PencilArrays.Pencils.topology — Methodtopology(x::PencilArray)
topology(x::PencilArrayCollection)Get MPITopology associated to a PencilArray.
PencilArrays.Pencils.typeof_array — Functiontypeof_array(x::Pencil)
typeof_array(x::PencilArray)
typeof_array(x::AbstractArray)Get the type of array (without the element type) so it can be used as a constructor.
PencilArrays.typeof_ptr — Functiontypeof_ptr(x::AbstractArray)
typeof_ptr(x::PencilArray)Get the type of pointer to the underlying array of a PencilArray or AbstractArray.
ManyPencilArray
Base.first — Methodfirst(A::AbstractManyPencilArray)Returns the first PencilArray wrapped by A.
Base.getindex — Methodgetindex(A::AbstractManyPencilArray, ::Val{i})
getindex(A::AbstractManyPencilArray, i::Integer)Returns the i-th PencilArray wrapped by A.
If possible, the Val{i} form should be preferred, as it ensures that the full type of the returned PencilArray is known by the compiler.
See also first(::AbstractManyPencilArray), last(::AbstractManyPencilArray).
Example
A = ManyPencilArray(pencil1, pencil2, pencil3)
# Get the PencilArray associated to `pencil2`.
u2 = A[2]
u2 = A[Val(2)]Base.last — Methodlast(A::AbstractManyPencilArray)Returns the last PencilArray wrapped by A.
Base.length — Methodlength(A::AbstractManyPencilArray)Returns the number of PencilArrays wrapped by A.
Core.Tuple — MethodTuple(A::AbstractManyPencilArray) -> (u1, u2, …)Returns the PencilArrays wrapped by A.
This can be useful for iterating over all the wrapped arrays.
Index
Core.TuplePencilArrays.AbstractManyPencilArrayPencilArrays.GlobalPencilArrayPencilArrays.ManyPencilArrayPencilArrays.PencilArrayPencilArrays.PencilArrayCollectionBase.firstBase.getindexBase.lastBase.lengthBase.lengthBase.parentBase.pointerBase.similarBase.sizePencilArrays.Pencils.MPITopologies.get_commPencilArrays.Pencils.length_globalPencilArrays.Pencils.length_localPencilArrays.Pencils.range_localPencilArrays.Pencils.range_remotePencilArrays.Pencils.size_globalPencilArrays.Pencils.size_localPencilArrays.Pencils.topologyPencilArrays.Pencils.typeof_arrayPencilArrays.Permutations.permutationPencilArrays.extra_dimsPencilArrays.global_viewPencilArrays.ndims_extraPencilArrays.ndims_spacePencilArrays.pencilPencilArrays.sizeof_globalPencilArrays.typeof_ptr