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 — Type
PencilArray(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 — Type
GlobalPencilArray{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 — Type
PencilArrayCollectionUnionAll 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 — Type
AbstractManyPencilArray{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 — Type
ManyPencilArray{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 — Method
extra_dims(x::PencilArray)
extra_dims(x::PencilArrayCollection)Return tuple with size of "extra" dimensions of PencilArray.
PencilArrays.Pencils.MPITopologies.get_comm — Method
get_comm(x::PencilArray)
get_comm(x::PencilArrayCollection)Get MPI communicator associated to a pencil-distributed array.
PencilArrays.Permutations.permutation — Function
permutation(::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 — Method
global_view(x::PencilArray)Create an OffsetArray of a PencilArray that takes global indices in logical order.
PencilArrays.ndims_extra — Method
ndims_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 — Method
ndims_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 — Method
parent(x::PencilArray)Return array wrapped by a PencilArray.
PencilArrays.pencil — Method
pencil(x::PencilArray)Return decomposition configuration associated to a PencilArray.
Base.pointer — Method
pointer(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 — Method
range_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 — Method
range_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 — Method
similar(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 — Method
length(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 — Method
length_local(x::PencilArray)Get linear length of the local data held by a PencilArray.
PencilArrays.Pencils.length_global — Method
length_global(x::PencilArray)Get linear length of the global data held by a PencilArray.
PencilArrays.Pencils.size_local — Method
size_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 — Method
size_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 — Method
sizeof_global(x::PencilArray)
sizeof_global(x::PencilArrayCollection)Global size of array in bytes.
PencilArrays.Pencils.topology — Method
topology(x::PencilArray)
topology(x::PencilArrayCollection)Get MPITopology associated to a PencilArray.
PencilArrays.Pencils.typeof_array — Function
typeof_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 — Function
typeof_ptr(x::AbstractArray)
typeof_ptr(x::PencilArray)Get the type of pointer to the underlying array of a PencilArray or AbstractArray.
ManyPencilArray
Base.first — Method
first(A::AbstractManyPencilArray)Returns the first PencilArray wrapped by A.
Base.getindex — Method
getindex(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 — Method
last(A::AbstractManyPencilArray)Returns the last PencilArray wrapped by A.
Base.length — Method
length(A::AbstractManyPencilArray)Returns the number of PencilArrays wrapped by A.
Core.Tuple — Method
Tuple(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