Introduction

Installation instructions and first usage examples are shown on the main page. This page collects some additional examples and translation guidelines from the C library p4est to its Julia wrapper P4est.jl.

High-level overview of the setup

The Julia bindings of p4est are generated using Clang.jl. This process is described in the dev folder of P4est.jl. Since all MPI datatypes are mapped to types from MPI.jl, the generated bindings are agnostic of the local MPI installation. Hence, there should be no need to generate new bindings as a user. If there are problems, please let us know, e.g., by creating on issue on GitHub. New bindings only need to be generated for a new version of p4est that comes with a different API. We will probably provide some new bindings when we learn about this.

Translation guidelines

Many functions of p4est work with pointers, e.g.,

int p4est_connectivity_is_valid (p4est_connectivity_t * connectivity);

or return pointers to newly created structs, e.g.,

p4est_connectivity_t *p4est_connectivity_new_periodic (void);

P4est.jl wraps these C functions accordingly and works with pointers. For a convenient alternative to using raw pointers directly, see the next section on PointerWrappers. We also follow the naming scheme of p4est. For example, we use connectivity::Ptr{p4est_connectivity}. However, it is sometimes useful/required to also load the wrappers of the C structs from their pointers. In this case, we use the naming convention to append _obj, e.g., connectivity_obj = unsafe_load(connectivity). A full example is given here:

julia> using P4est, MPI; MPI.Init()MPI.ThreadLevel(2)
julia> connectivity = p4est_connectivity_new_periodic()Ptr{p4est_connectivity} @0x00000000038a4060
julia> @doc(p4est_connectivity) p4est_connectivity This structure holds the 2D inter-tree connectivity information. Identification of arbitrary faces and corners is possible. The arrays tree_to_* are stored in z ordering. For corners the order wrt. yx is 00 01 10 11. For faces the order is -x +x -y +y. They are allocated [0][0]..[0][3]..[num_trees-1][0]..[num_trees-1][3]. The values for tree_to_face are 0..7 where ttf % 4 gives the face number and ttf / 4 the face orientation code. The orientation is 0 for edges that are aligned in z-order, and 1 for edges that are running opposite in z-order. It is valid to specify num_vertices as 0. In this case vertices and tree_to_vertex are set to NULL. Otherwise the vertex coordinates are stored in the array vertices as [0][0]..[0][2]..[num_vertices-1][0]..[num_vertices-1][2]. The corners are only stored when they connect trees. In this case tree_to_corner indexes into ctt_offset. Otherwise the tree_to_corner entry must be -1 and this corner is ignored. If num_corners == 0, tree_to_corner and corner_to_* arrays are set to NULL. The arrays corner_to_* store a variable number of entries per corner. For corner c these are at position [ctt_offset[c]]..[ctt_offset[c+1]-1]. Their number for corner c is ctt_offset[c+1] - ctt_offset[c]. The entries encode all trees adjacent to corner c. The size of the corner_to_* arrays is num_ctt = ctt_offset[num_corners]. The *_to_attr arrays may have arbitrary contents defined by the user. Field Note –––––––––––––––– ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– num_vertices the number of vertices that define the embedding of the forest (not the topology) num_trees the number of trees num_corners the number of corners that help define topology vertices an array of size (3 * num_vertices) tree_to_vertex embed each tree into c++ R^3 for e.g. visualization (see p4est_vtk.h) tree_attr_bytes bytes per tree in tree_to_attr tree_to_attr not touched by p4est tree_to_tree (4 * num_trees) neighbors across faces tree_to_face (4 * num_trees) face to face+orientation (see description) tree_to_corner (4 * num_trees) or NULL (see description) ctt_offset corner to offset in cornertotree and cornertocorner corner_to_tree list of trees that meet at a corner corner_to_corner list of tree-corners that meet at a corner
julia> connectivity_obj = unsafe_load(connectivity)p4est_connectivity(4, 1, 1, Ptr{Float64} @0x000000000392c5e0, Ptr{Int32} @0x0000000002ad9750, 0x0000000000000000, Cstring(0x0000000000000000), Ptr{Int32} @0x00000000026d1f50, Ptr{Int8} @0x0000000002a2f600, Ptr{Int32} @0x0000000001d09d50, Ptr{Int32} @0x0000000002a7c3d0, Ptr{Int32} @0x00000000025f3030, Ptr{Int8} @0x0000000002dd6ff0)
julia> connectivity_obj.num_vertices4
julia> connectivity_obj.num_trees1
julia> connectivity_obj.num_corners1
julia> p4est_connectivity_destroy(connectivity)

PointerWrappers

As we have seen in the previous section on translation guidelines, many functions provided by p4est return pointers to structs, which requires one to unsafe_load the pointer in order to access the underlying data. P4est.jl offers a convenient alternative to directly using unsafe_load by providing the PointerWrapper data type. If you, e.g., have a pointer to a p4est_connectivity (i.e., an object of type Ptr{p4est_connectivity}) called connectivity, you can use connectivity_pw = PointerWrapper(connectivity) to obtain a wrapped version of the pointer, where the underlying data can be accessed simply by connectivity_pw.num_trees[] without the need to call unsafe_load manually. This works even for nested structures, e.g, where the named field of a struct is a pointer to a struct. A full example on how to use the PointerWrapper is given here (note the nested access at p4est_pw.connectivity.num_trees[] compared to unsafe_load(unsafe_load(p4est).connectivity).num_trees without a PointerWrapper):

julia> using P4est, MPI; MPI.Init()MPI.ThreadLevel(2)
julia> connectivity = p4est_connectivity_new_periodic()Ptr{p4est_connectivity} @0x0000000002cb68e0
julia> connectivity_pw = PointerWrapper(connectivity)PointerWrapper{p4est_connectivity}(Ptr{p4est_connectivity} @0x0000000002cb68e0)
julia> connectivity_pw.num_trees[]1
julia> p4est = p4est_new_ext(MPI.COMM_WORLD, connectivity, 0, 0, true, 0, C_NULL, C_NULL)Into p4est_new with min quadrants 0 level 0 uniform 1 New p4est with 1 trees on 1 processors Initial level 0 potential global quadrants 1 per tree 1 Done p4est_new with 1 total quadrants Ptr{P4est.LibP4est.p4est} @0x000000000281bb70
julia> p4est_pw = PointerWrapper(p4est)PointerWrapper{P4est.LibP4est.p4est}(Ptr{P4est.LibP4est.p4est} @0x000000000281bb70)
julia> p4est_pw.connectivity.num_trees[]1
julia> p4est_destroy(p4est)
julia> p4est_connectivity_destroy(connectivity)

You may also use the PointerWrapper to set variables in structs. Here we set the user data pointer in the p4est_t struct to point to some data:

julia> using P4est, MPI; MPI.Init()MPI.ThreadLevel(2)
julia> connectivity = p4est_connectivity_new_periodic()Ptr{p4est_connectivity} @0x0000000003599470
julia> p4est = p4est_new_ext(MPI.COMM_WORLD, connectivity, 0, 0, true, 0, C_NULL, C_NULL)Into p4est_new with min quadrants 0 level 0 uniform 1 New p4est with 1 trees on 1 processors Initial level 0 potential global quadrants 1 per tree 1 Done p4est_new with 1 total quadrants Ptr{P4est.LibP4est.p4est} @0x0000000002bf28b0
julia> p4est_pw = PointerWrapper(p4est)PointerWrapper{P4est.LibP4est.p4est}(Ptr{P4est.LibP4est.p4est} @0x0000000002bf28b0)
julia> data = Ref((rand(4), rand(5)))Base.RefValue{Tuple{Vector{Float64}, Vector{Float64}}}(([0.9563143700262997, 0.16619213386036424, 0.7944128972383298, 0.9264181925787428], [0.750243785467092, 0.7525164883579533, 0.5929672400982664, 0.9314917147561163, 0.6349109946176067]))
julia> GC.@preserve data begin p4est_pw.user_pointer = pointer_from_objref(data) # Call p4est function with callback that uses p4est_pw.user_pointer here. # You may retrieve the data `Ref` in the callback with # data = unsafe_pointer_to_objref(pointer(p4est_pw.user_pointer)) endPtr{Nothing} @0x00007f8c8c044390
julia> p4est_destroy(p4est)
julia> p4est_connectivity_destroy(connectivity)

In addition, you can use a PointerWrapper as an array if the underlying datastructure is an array, i.e. you can access the i-th element of the underlying array by pw[i] for a PointerWrapper pw. See the following code for a full example:

julia> using P4est, MPI; MPI.Init()MPI.ThreadLevel(2)
julia> connectivity = p4est_connectivity_new_periodic()Ptr{p4est_connectivity} @0x000000000226bfe0
julia> p4est = p4est_new_ext(MPI.COMM_WORLD, connectivity, 0, 0, true, 0, C_NULL, C_NULL)Into p4est_new with min quadrants 0 level 0 uniform 1 New p4est with 1 trees on 1 processors Initial level 0 potential global quadrants 1 per tree 1 Done p4est_new with 1 total quadrants Ptr{P4est.LibP4est.p4est} @0x00000000026cf1d0
julia> p4est_pw = PointerWrapper(p4est)PointerWrapper{P4est.LibP4est.p4est}(Ptr{P4est.LibP4est.p4est} @0x00000000026cf1d0)
julia> p4est_pw.global_first_quadrant[2]1
julia> p4est_destroy(p4est)
julia> p4est_connectivity_destroy(connectivity)

Note on MPI datatypes

MPI types used by p4est are mapped to the types provided by MPI.jl.

p4estP4est.jl
sc_MPI_CommMPI.MPI_Comm
MPI_DatatypeMPI.MPI_Datatype
MPI_FileMPI.MPI_File

In particular, it is currently not possible to use p4est without MPI support.