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: PencilArray wrapper 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.

Global views

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.PencilArrayType
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.

Index permutations

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
Extra dimensions

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...))     # fails

PencilArray{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})"
source
PencilArrays.GlobalPencilArrayType
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.

source
PencilArrays.PencilArrayCollectionType
PencilArrayCollection

UnionAll 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...).

source
PencilArrays.AbstractManyPencilArrayType
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.

source
PencilArrays.ManyPencilArrayType
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.

source

Methods

PencilArray

PencilArrays.extra_dimsMethod
extra_dims(x::PencilArray)
extra_dims(x::PencilArrayCollection)

Return tuple with size of "extra" dimensions of PencilArray.

source
PencilArrays.Permutations.permutationFunction
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.

source
PencilArrays.ndims_extraMethod
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)
source
PencilArrays.ndims_spaceMethod
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)
source
Base.parentMethod
parent(x::PencilArray)

Return array wrapped by a PencilArray.

source
Base.pointerMethod
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).

source
PencilArrays.Pencils.range_localMethod
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.

source
PencilArrays.Pencils.range_remoteMethod
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.

source
Base.similarMethod
similar(x::PencilArray, [element_type=eltype(x)], [dims]) -> PencilArray

Returns 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 size

similar(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
source
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).

source
Base.lengthMethod
length(A::AbstractManyPencilArray)

Returns the number of PencilArrays wrapped by A.

source
length(x::PencilArray)

Get the local number of elements stored in the PencilArray.

Equivalent to length_local(x).

source
Base.sizeMethod
size(x::PencilArray)

Return the local dimensions of a PencilArray in logical order.

Defined as size_local(x, LogicalOrder()).

source
PencilArrays.Pencils.size_globalMethod
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).

source
PencilArrays.Pencils.typeof_arrayFunction
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.

source
PencilArrays.typeof_ptrFunction
typeof_ptr(x::AbstractArray)
typeof_ptr(x::PencilArray)

Get the type of pointer to the underlying array of a PencilArray or AbstractArray.

source

ManyPencilArray

Base.getindexMethod
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)]
source
Core.TupleMethod
Tuple(A::AbstractManyPencilArray) -> (u1, u2, …)

Returns the PencilArrays wrapped by A.

This can be useful for iterating over all the wrapped arrays.

source

Index