xref: /libCEED/examples/petsc/dmswarm.c (revision 93f4dbf17d100448608783967d62dd34e2d893bb)
1 // Copyright (c) 2017-2022, Lawrence Livermore National Security, LLC and other CEED contributors.
2 // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
3 //
4 // SPDX-License-Identifier: BSD-2-Clause
5 //
6 // This file is part of CEED:  http://github.com/ceed
7 
8 //                        libCEED + PETSc DMSwarm Example
9 //
10 // This example demonstrates a simple usage of libCEED with DMSwarm.
11 // This example combines elements of PETSc src/impls/dm/swam/tutorials/ex1.c and src/impls/dm/swarm/tests/ex6.c
12 //
13 // Build with:
14 //
15 //     make dmswarm [PETSC_DIR=</path/to/petsc>] [CEED_DIR=</path/to/libceed>]
16 //
17 // Sample runs:
18 //
19 //  ./dmswarm -dm_plex_dim 3 -dm_plex_box_faces 3,3,3 -dm_plex_box_lower -1.0,-1.0,-1.0 -dm_plex_simplex 0 -num_comp 2 -swarm gauss
20 //
21 //TESTARGS(name="Uniform swarm, CG projection") -ceed {ceed_resource} -test -dm_plex_dim 3 -dm_plex_box_faces 3,3,3 -dm_plex_box_lower -1.0,-1.0,-1.0 -dm_plex_simplex 0 -dm_plex_hash_location true -num_comp 2 -swarm uniform -solution_order 3 -points_per_cell 125
22 //TESTARGS(name="Gauss swarm, lumped projection") -ceed {ceed_resource} -test -dm_plex_dim 3 -dm_plex_box_faces 3,3,3 -dm_plex_box_lower -1.0,-1.0,-1.0 -dm_plex_simplex 0 -dm_plex_hash_location true -num_comp 2 -swarm gauss -ksp_type preonly -pc_type jacobi -pc_jacobi_type rowsum -tolerance 9e-2
23 
24 /// @file
25 /// libCEED example using PETSc with DMSwarm
26 const char help[] = "libCEED example using PETSc with DMSwarm\n";
27 
28 #include <ceed.h>
29 #include <math.h>
30 #include <petscdmplex.h>
31 #include <petscdmswarm.h>
32 #include <petscds.h>
33 #include <petscfe.h>
34 #include <petscksp.h>
35 #include <petsc/private/petscfeimpl.h> /* For interpolation */
36 
37 #include "include/petscutils.h"
38 #include "include/petscversion.h"
39 
40 const char DMSwarmPICField_u[] = "u";
41 
42 // libCEED context data
43 typedef struct DMSwarmCeedContext_ *DMSwarmCeedContext;
44 struct DMSwarmCeedContext_ {
45   Ceed                ceed;
46   CeedVector          u_mesh_loc, v_mesh_loc, u_mesh_elem, u_points_loc, u_points_elem, x_ref_points_loc, x_ref_points_elem;
47   CeedElemRestriction restriction_u_mesh, restriction_x_points, restriction_u_points;
48   CeedBasis           basis_u;
49 };
50 
51 PetscErrorCode DMSwarmCeedContextCreate(DM dm_swarm, const char *ceed_resource, DMSwarmCeedContext *ctx);
52 PetscErrorCode DMSwarmCeedContextDestroy(DMSwarmCeedContext *ctx);
53 
54 // Target functions
55 typedef enum { TARGET_TANH = 0, TARGET_POLYNOMIAL = 1, TARGET_SPHERE = 2 } TargetType;
56 static const char *const target_types[] = {"tanh", "polynomial", "sphere", "TargetType", "TARGET", 0};
57 
58 typedef PetscErrorCode (*TargetFunc)(PetscInt dim, const PetscScalar x[]);
59 typedef PetscErrorCode (*TargetFuncProj)(PetscInt dim, PetscReal time, const PetscReal x[], PetscInt Nc, PetscScalar *u, void *ctx);
60 
61 PetscScalar    EvalU_Tanh(PetscInt dim, const PetscScalar x[]);
62 PetscScalar    EvalU_Poly(PetscInt dim, const PetscScalar x[]);
63 PetscScalar    EvalU_Sphere(PetscInt dim, const PetscScalar x[]);
64 PetscErrorCode EvalU_Tanh_proj(PetscInt dim, PetscReal t, const PetscReal x[], PetscInt num_comp, PetscScalar *u, void *ctx);
65 PetscErrorCode EvalU_Poly_proj(PetscInt dim, PetscReal t, const PetscReal x[], PetscInt num_comp, PetscScalar *u, void *ctx);
66 PetscErrorCode EvalU_Sphere_proj(PetscInt dim, PetscReal t, const PetscReal x[], PetscInt num_comp, PetscScalar *u, void *ctx);
67 
68 // Swarm point distribution
69 typedef enum { SWARM_GAUSS = 0, SWARM_UNIFORM = 1, SWARM_CELL_RANDOM = 2, SWARM_SINUSOIDAL = 3 } PointSwarmType;
70 static const char *const point_swarm_types[] = {"gauss", "uniform", "cell_random", "sinusoidal", "PointSwarmType", "SWARM", 0};
71 
72 // Swarm helper function
73 PetscErrorCode DMSwarmInitalizePointLocations(DM dm_swarm, PointSwarmType point_swarm_type, PetscInt num_points, PetscInt num_points_per_cell);
74 
75 // Memory utilities
76 PetscErrorCode VecP2C(Vec X_petsc, PetscMemType *mem_type, CeedVector x_ceed);
77 PetscErrorCode VecC2P(CeedVector x_ceed, PetscMemType mem_type, Vec X_petsc);
78 PetscErrorCode VecReadP2C(Vec X_petsc, PetscMemType *mem_type, CeedVector x_ceed);
79 PetscErrorCode VecReadC2P(CeedVector x_ceed, PetscMemType mem_type, Vec X_petsc);
80 PetscErrorCode DMSwarmPICFieldP2C(DM dm_swarm, const char *field, CeedVector x_ceed);
81 PetscErrorCode DMSwarmPICFieldC2P(DM dm_swarm, const char *field, CeedVector x_ceed);
82 
83 // Swarm to mesh and mesh to swarm
84 PetscErrorCode DMSwarmCreateReferenceCoordinates(DM dm_swarm, IS *is_points, Vec *ref_coords);
85 PetscErrorCode DMSwarmInterpolateFromCellToSwarm_Petsc(DM dm_swarm, const char *field, Vec U_mesh);
86 PetscErrorCode DMSwarmInterpolateFromCellToSwarm_Ceed(DM dm_swarm, const char *field, Vec U_mesh);
87 PetscErrorCode DMSwarmCheckSwarmValues(DM dm_swarm, const char *field, PetscScalar tolerance, TargetFuncProj TrueSolution);
88 
89 PetscErrorCode DMSwarmCreateProjectionRHS(DM dm_swarm, const char *field, Vec B_mesh);
90 PetscErrorCode MatMult_SwarmMass(Mat A, Vec U_mesh, Vec V_mesh);
91 
92 // ------------------------------------------------------------------------------------------------
93 // main driver
94 // ------------------------------------------------------------------------------------------------
95 int main(int argc, char **argv) {
96   MPI_Comm           comm;
97   char               ceed_resource[PETSC_MAX_PATH_LEN] = "/cpu/self";
98   PetscBool          test_mode = PETSC_FALSE, view_petsc_swarm = PETSC_FALSE, view_ceed_swarm = PETSC_FALSE;
99   PetscInt           dim = 3, num_comp = 1, num_points = 1728, num_points_per_cell = 64, mesh_order = 1, solution_order = 2, q_extra = 3;
100   PetscScalar        tolerance = 1E-3;
101   DM                 dm_mesh, dm_swarm;
102   Vec                U_mesh;
103   DMSwarmCeedContext swarm_ceed_context;
104   PointSwarmType     point_swarm_type     = SWARM_UNIFORM;
105   TargetType         target_type          = TARGET_TANH;
106   TargetFuncProj     target_function_proj = EvalU_Tanh_proj;
107 
108   PetscCall(PetscInitialize(&argc, &argv, NULL, help));
109   comm = PETSC_COMM_WORLD;
110 
111   // Read command line options
112   PetscOptionsBegin(comm, NULL, "libCEED example using PETSc with DMSwarm", NULL);
113 
114   PetscCall(PetscOptionsBool("-test", "Testing mode (do not print unless error is large)", NULL, test_mode, &test_mode, NULL));
115   PetscCall(
116       PetscOptionsBool("-u_petsc_swarm_view", "View XDMF of swarm values interpolated by PETSc", NULL, view_petsc_swarm, &view_petsc_swarm, NULL));
117   PetscCall(
118       PetscOptionsBool("-u_ceed_swarm_view", "View XDMF of swarm values interpolated by libCEED", NULL, view_ceed_swarm, &view_ceed_swarm, NULL));
119   PetscCall(PetscOptionsEnum("-target", "Target field function", NULL, target_types, (PetscEnum)target_type, (PetscEnum *)&target_type, NULL));
120   PetscCall(PetscOptionsInt("-solution_order", "Order of mesh solution space", NULL, solution_order, &solution_order, NULL));
121   PetscCall(PetscOptionsInt("-mesh_order", "Order of mesh coordinate space", NULL, mesh_order, &mesh_order, NULL));
122   PetscCall(PetscOptionsInt("-q_extra", "Number of extra quadrature points", NULL, q_extra, &q_extra, NULL));
123   PetscCall(PetscOptionsInt("-num_comp", "Number of components in solution", NULL, num_comp, &num_comp, NULL));
124   PetscCall(PetscOptionsEnum("-swarm", "Swarm points distribution", NULL, point_swarm_types, (PetscEnum)point_swarm_type,
125                              (PetscEnum *)&point_swarm_type, NULL));
126   {
127     PetscBool user_set_num_points_per_cell = PETSC_FALSE;
128     PetscInt  dim = 3, num_cells_total = 1;
129     PetscInt  num_cells[] = {1, 1, 1};
130 
131     PetscCall(PetscOptionsInt("-points_per_cell", "Total number of swarm points in each cell", NULL, num_points_per_cell, &num_points_per_cell,
132                               &user_set_num_points_per_cell));
133     PetscCall(PetscOptionsInt("-dm_plex_dim", "Background mesh dimension", NULL, dim, &dim, NULL));
134     PetscCall(PetscOptionsIntArray("-dm_plex_box_faces", "Number of cells", NULL, num_cells, &dim, NULL));
135 
136     num_cells_total = num_cells[0] * num_cells[1] * num_cells[2];
137     PetscCheck(!user_set_num_points_per_cell || point_swarm_type != SWARM_SINUSOIDAL, comm, PETSC_ERR_USER,
138                "Cannot specify points per cell with sinusoidal points locations");
139     if (!user_set_num_points_per_cell) {
140       PetscCall(PetscOptionsInt("-points", "Total number of swarm points", NULL, num_points, &num_points, NULL));
141       num_points_per_cell = PetscCeilInt(num_points, num_cells_total);
142     }
143     if (point_swarm_type != SWARM_SINUSOIDAL) {
144       PetscInt num_points_per_cell_1d = round(cbrt(num_points_per_cell * 1.0));
145 
146       num_points_per_cell = 1;
147       for (PetscInt i = 0; i < dim; i++) num_points_per_cell *= num_points_per_cell_1d;
148     }
149     num_points = num_points_per_cell * num_cells_total;
150   }
151   PetscCall(PetscOptionsScalar("-tolerance", "Tolerance for swarm point values and projection relative L2 error", NULL, tolerance, &tolerance, NULL));
152   PetscCall(PetscOptionsString("-ceed", "CEED resource specifier", NULL, ceed_resource, ceed_resource, sizeof(ceed_resource), NULL));
153 
154   PetscOptionsEnd();
155 
156   // Create background mesh
157   {
158     PetscCall(DMCreate(comm, &dm_mesh));
159     PetscCall(DMSetType(dm_mesh, DMPLEX));
160     PetscCall(DMSetFromOptions(dm_mesh));
161 
162     // -- Check for tensor product mesh
163     {
164       PetscBool is_simplex;
165 
166       PetscCall(DMPlexIsSimplex(dm_mesh, &is_simplex));
167       PetscCheck(!is_simplex, comm, PETSC_ERR_USER, "Only tensor-product background meshes supported");
168     }
169 
170     // -- Mesh FE space
171     PetscCall(DMGetDimension(dm_mesh, &dim));
172     {
173       PetscFE fe;
174 
175       PetscCall(DMGetDimension(dm_mesh, &dim));
176       PetscCall(PetscFECreateLagrange(comm, dim, num_comp, PETSC_FALSE, solution_order, solution_order + q_extra, &fe));
177       PetscCall(DMAddField(dm_mesh, NULL, (PetscObject)fe));
178       PetscCall(PetscFEDestroy(&fe));
179     }
180     PetscCall(DMCreateDS(dm_mesh));
181 
182     // -- Coordinate FE space
183     {
184       PetscFE fe_coord;
185 
186       PetscCall(PetscFECreateLagrange(comm, dim, dim, PETSC_FALSE, mesh_order, solution_order + q_extra, &fe_coord));
187       PetscCall(DMSetCoordinateDisc(dm_mesh, fe_coord, PETSC_TRUE));
188       PetscCall(PetscFEDestroy(&fe_coord));
189     }
190 
191     // -- Set tensor permutation
192     {
193       DM dm_coord;
194 
195       PetscCall(DMGetCoordinateDM(dm_mesh, &dm_coord));
196       PetscCall(DMPlexSetClosurePermutationTensor(dm_mesh, PETSC_DETERMINE, NULL));
197       PetscCall(DMPlexSetClosurePermutationTensor(dm_coord, PETSC_DETERMINE, NULL));
198     }
199 
200     // -- Final background mesh
201     PetscCall(PetscObjectSetName((PetscObject)dm_mesh, "Background Mesh"));
202     PetscCall(DMViewFromOptions(dm_mesh, NULL, "-dm_mesh_view"));
203   }
204 
205   // Create particle swarm
206   {
207     PetscCall(DMCreate(comm, &dm_swarm));
208     PetscCall(DMSetType(dm_swarm, DMSWARM));
209     PetscCall(DMSetDimension(dm_swarm, dim));
210     PetscCall(DMSwarmSetType(dm_swarm, DMSWARM_PIC));
211     PetscCall(DMSwarmSetCellDM(dm_swarm, dm_mesh));
212 
213     // -- Swarm field
214     PetscCall(DMSwarmRegisterPetscDatatypeField(dm_swarm, DMSwarmPICField_u, num_comp, PETSC_SCALAR));
215     PetscCall(DMSwarmFinalizeFieldRegister(dm_swarm));
216     PetscCall(DMSwarmSetLocalSizes(dm_swarm, num_points, 0));
217     PetscCall(DMSetFromOptions(dm_swarm));
218 
219     // -- Set swarm point locations
220     PetscCall(DMSwarmInitalizePointLocations(dm_swarm, point_swarm_type, num_points, num_points_per_cell));
221 
222     // -- Final particle swarm
223     PetscCall(PetscObjectSetName((PetscObject)dm_swarm, "Particle Swarm"));
224     PetscCall(DMViewFromOptions(dm_swarm, NULL, "-dm_swarm_view"));
225   }
226 
227   // Set field values on background mesh
228   PetscCall(DMCreateGlobalVector(dm_mesh, &U_mesh));
229   switch (target_type) {
230     case TARGET_TANH:
231       target_function_proj = EvalU_Tanh_proj;
232       break;
233     case TARGET_POLYNOMIAL:
234       target_function_proj = EvalU_Poly_proj;
235       break;
236     case TARGET_SPHERE:
237       target_function_proj = EvalU_Sphere_proj;
238       break;
239   }
240   {
241     TargetFuncProj mesh_solution[1] = {target_function_proj};
242 
243     PetscCall(DMProjectFunction(dm_mesh, 0.0, mesh_solution, NULL, INSERT_VALUES, U_mesh));
244   }
245 
246   // Visualize background mesh
247   PetscCall(PetscObjectSetName((PetscObject)U_mesh, "U on Background Mesh"));
248   PetscCall(VecViewFromOptions(U_mesh, NULL, "-u_mesh_view"));
249 
250   // libCEED objects for swarm and background mesh
251   PetscCall(DMSwarmCeedContextCreate(dm_swarm, ceed_resource, &swarm_ceed_context));
252 
253   // Interpolate from mesh to points via PETSc
254   {
255     PetscCall(DMSwarmInterpolateFromCellToSwarm_Petsc(dm_swarm, DMSwarmPICField_u, U_mesh));
256     if (view_petsc_swarm) PetscCall(DMSwarmViewXDMF(dm_swarm, "swarm_petsc.xmf"));
257     PetscCall(DMSwarmCheckSwarmValues(dm_swarm, DMSwarmPICField_u, tolerance, target_function_proj));
258   }
259 
260   // Interpolate from mesh to points via libCEED
261   {
262     PetscCall(DMSwarmInterpolateFromCellToSwarm_Ceed(dm_swarm, DMSwarmPICField_u, U_mesh));
263     if (view_ceed_swarm) PetscCall(DMSwarmViewXDMF(dm_swarm, "swarm_ceed.xmf"));
264     PetscCall(DMSwarmCheckSwarmValues(dm_swarm, DMSwarmPICField_u, tolerance, target_function_proj));
265   }
266 
267   // Project from points to mesh via libCEED
268   {
269     Vec B_mesh, U_projected;
270     Mat M;
271     KSP ksp;
272 
273     PetscCall(VecDuplicate(U_mesh, &B_mesh));
274     PetscCall(VecDuplicate(U_mesh, &U_projected));
275 
276     // -- Setup "mass matrix"
277     {
278       PetscInt l_size, g_size;
279 
280       PetscCall(VecGetLocalSize(U_mesh, &l_size));
281       PetscCall(VecGetSize(U_mesh, &g_size));
282       PetscCall(MatCreateShell(comm, l_size, l_size, g_size, g_size, swarm_ceed_context, &M));
283       PetscCall(MatSetDM(M, dm_mesh));
284       PetscCall(MatShellSetOperation(M, MATOP_MULT, (void (*)(void))MatMult_SwarmMass));
285     }
286 
287     // -- Setup KSP
288     {
289       PC pc;
290 
291       PetscCall(KSPCreate(comm, &ksp));
292       PetscCall(KSPGetPC(ksp, &pc));
293       PetscCall(PCSetType(pc, PCJACOBI));
294       PetscCall(PCJacobiSetType(pc, PC_JACOBI_ROWSUM));
295       PetscCall(KSPSetType(ksp, KSPCG));
296       PetscCall(KSPSetNormType(ksp, KSP_NORM_NATURAL));
297       PetscCall(KSPSetTolerances(ksp, 1e-10, PETSC_DEFAULT, PETSC_DEFAULT, PETSC_DEFAULT));
298       PetscCall(KSPSetOperators(ksp, M, M));
299       PetscCall(KSPSetFromOptions(ksp));
300       PetscCall(PetscObjectSetName((PetscObject)ksp, "Swarm-to-Mesh Projection"));
301       PetscCall(KSPViewFromOptions(ksp, NULL, "-ksp_projection_view"));
302     }
303 
304     // -- Setup RHS
305     PetscCall(DMSwarmCreateProjectionRHS(dm_swarm, DMSwarmPICField_u, B_mesh));
306 
307     // -- Solve
308     PetscCall(VecZeroEntries(U_projected));
309     PetscCall(KSPSolve(ksp, B_mesh, U_projected));
310 
311     // -- KSP summary
312     {
313       KSPType            ksp_type;
314       KSPConvergedReason reason;
315       PetscReal          rnorm;
316       PetscInt           its;
317       PetscCall(KSPGetType(ksp, &ksp_type));
318       PetscCall(KSPGetConvergedReason(ksp, &reason));
319       PetscCall(KSPGetIterationNumber(ksp, &its));
320       PetscCall(KSPGetResidualNorm(ksp, &rnorm));
321 
322       if (!test_mode || reason < 0 || rnorm > 1e-8) {
323         PetscCall(PetscPrintf(comm,
324                               "Swarm-to-Mesh Projection KSP Solve:\n"
325                               "  KSP type: %s\n"
326                               "  KSP convergence: %s\n"
327                               "  Total KSP iterations: %" PetscInt_FMT "\n"
328                               "  Final rnorm: %e\n",
329                               ksp_type, KSPConvergedReasons[reason], its, (double)rnorm));
330       }
331     }
332 
333     // -- Check error
334     PetscCall(KSPViewFromOptions(ksp, NULL, "-ksp_view"));
335     PetscCall(PetscObjectSetName((PetscObject)U_mesh, "U projected to Background Mesh"));
336     PetscCall(VecViewFromOptions(U_projected, NULL, "-u_projected_view"));
337     PetscCall(VecAXPY(U_projected, -1.0, U_mesh));
338     PetscCall(PetscObjectSetName((PetscObject)U_projected, "U Projection Error"));
339     PetscCall(VecViewFromOptions(U_projected, NULL, "-u_error_view"));
340     {
341       PetscScalar error, norm_u_mesh;
342 
343       PetscCall(VecNorm(U_projected, NORM_2, &error));
344       PetscCall(VecNorm(U_mesh, NORM_2, &norm_u_mesh));
345       PetscCheck(error / norm_u_mesh < tolerance, comm, PETSC_ERR_USER, "Projection error too high: %e\n", error / norm_u_mesh);
346       if (!test_mode) PetscCall(PetscPrintf(comm, "  Projection error: %e\n", error / norm_u_mesh));
347     }
348 
349     // -- Cleanup
350     PetscCall(VecDestroy(&B_mesh));
351     PetscCall(VecDestroy(&U_projected));
352     PetscCall(MatDestroy(&M));
353     PetscCall(KSPDestroy(&ksp));
354   }
355 
356   // Cleanup
357   PetscCall(DMSwarmCeedContextDestroy(&swarm_ceed_context));
358   PetscCall(DMDestroy(&dm_swarm));
359   PetscCall(DMDestroy(&dm_mesh));
360   PetscCall(VecDestroy(&U_mesh));
361   return PetscFinalize();
362 }
363 
364 // ------------------------------------------------------------------------------------------------
365 // Context utilities
366 // ------------------------------------------------------------------------------------------------
367 PetscErrorCode DMSwarmCeedContextCreate(DM dm_swarm, const char *ceed_resource, DMSwarmCeedContext *ctx) {
368   DM dm_mesh;
369 
370   PetscFunctionBeginUser;
371   PetscCall(PetscNew(ctx));
372   PetscCall(DMSwarmGetCellDM(dm_swarm, &dm_mesh));
373 
374   CeedInit(ceed_resource, &(*ctx)->ceed);
375   // Background mesh objects
376   {
377     CeedInt elem_size, num_comp;
378     BPData  bp_data = {.q_mode = CEED_GAUSS};
379 
380     PetscCall(CreateBasisFromPlex((*ctx)->ceed, dm_mesh, NULL, 0, 0, 0, bp_data, &(*ctx)->basis_u));
381     PetscCall(CreateRestrictionFromPlex((*ctx)->ceed, dm_mesh, 0, NULL, 0, &(*ctx)->restriction_u_mesh));
382 
383     // -- U vector
384     CeedElemRestrictionCreateVector((*ctx)->restriction_u_mesh, &(*ctx)->u_mesh_loc, NULL);
385     CeedElemRestrictionCreateVector((*ctx)->restriction_u_mesh, &(*ctx)->v_mesh_loc, NULL);
386     CeedElemRestrictionGetElementSize((*ctx)->restriction_u_mesh, &elem_size);
387     CeedElemRestrictionGetNumComponents((*ctx)->restriction_u_mesh, &num_comp);
388     CeedVectorCreate((*ctx)->ceed, elem_size * num_comp, &(*ctx)->u_mesh_elem);
389   }
390   // Swarm objects
391   {
392     PetscInt        dim;
393     const PetscInt *cell_points;
394     IS              is_points;
395     Vec             X_ref;
396     CeedInt         num_elem, num_comp, max_points_in_cell;
397 
398     PetscCall(DMSwarmCreateReferenceCoordinates(dm_swarm, &is_points, &X_ref));
399     PetscCall(DMGetDimension(dm_mesh, &dim));
400     CeedElemRestrictionGetNumElements((*ctx)->restriction_u_mesh, &num_elem);
401     CeedElemRestrictionGetNumComponents((*ctx)->restriction_u_mesh, &num_comp);
402 
403     PetscCall(ISGetIndices(is_points, &cell_points));
404     PetscInt num_points = cell_points[num_elem + 1] - num_elem - 2;
405     CeedInt  offsets[num_elem + 1 + num_points];
406 
407     for (PetscInt i = 0; i < num_elem + 1; i++) offsets[i] = cell_points[i + 1] - 1;
408     for (PetscInt i = num_elem + 1; i < num_points + num_elem + 1; i++) offsets[i] = cell_points[i + 1];
409     PetscCall(ISRestoreIndices(is_points, &cell_points));
410 
411     // -- Points restrictions
412     CeedElemRestrictionCreateAtPoints((*ctx)->ceed, num_elem, num_points, num_comp, num_points * num_comp, CEED_MEM_HOST, CEED_COPY_VALUES, offsets,
413                                       &(*ctx)->restriction_u_points);
414     CeedElemRestrictionCreateAtPoints((*ctx)->ceed, num_elem, num_points, dim, num_points * dim, CEED_MEM_HOST, CEED_COPY_VALUES, offsets,
415                                       &(*ctx)->restriction_x_points);
416 
417     // -- U vector
418     CeedElemRestrictionGetMaxPointsInElement((*ctx)->restriction_u_points, &max_points_in_cell);
419     CeedElemRestrictionCreateVector((*ctx)->restriction_u_points, &(*ctx)->u_points_loc, NULL);
420     CeedVectorCreate((*ctx)->ceed, max_points_in_cell * num_comp, &(*ctx)->u_points_elem);
421 
422     // -- Ref coordinates
423     {
424       PetscMemType       X_mem_type;
425       const PetscScalar *x;
426 
427       CeedVectorCreate((*ctx)->ceed, num_points * dim, &(*ctx)->x_ref_points_loc);
428       CeedVectorCreate((*ctx)->ceed, max_points_in_cell * dim, &(*ctx)->x_ref_points_elem);
429 
430       PetscCall(VecGetArrayReadAndMemType(X_ref, (const PetscScalar **)&x, &X_mem_type));
431       CeedVectorSetArray((*ctx)->x_ref_points_loc, MemTypeP2C(X_mem_type), CEED_COPY_VALUES, (CeedScalar *)x);
432       PetscCall(VecRestoreArrayReadAndMemType(X_ref, (const PetscScalar **)&x));
433     }
434 
435     // -- Cleanup
436     PetscCall(ISDestroy(&is_points));
437     PetscCall(VecDestroy(&X_ref));
438   }
439 
440   PetscCall(DMSetApplicationContext(dm_mesh, (void *)(*ctx)));
441   PetscFunctionReturn(PETSC_SUCCESS);
442 }
443 
444 PetscErrorCode DMSwarmCeedContextDestroy(DMSwarmCeedContext *ctx) {
445   PetscFunctionBeginUser;
446   CeedDestroy(&(*ctx)->ceed);
447   CeedVectorDestroy(&(*ctx)->u_mesh_loc);
448   CeedVectorDestroy(&(*ctx)->v_mesh_loc);
449   CeedVectorDestroy(&(*ctx)->u_mesh_elem);
450   CeedVectorDestroy(&(*ctx)->u_points_loc);
451   CeedVectorDestroy(&(*ctx)->u_points_elem);
452   CeedVectorDestroy(&(*ctx)->x_ref_points_loc);
453   CeedVectorDestroy(&(*ctx)->x_ref_points_elem);
454   CeedElemRestrictionDestroy(&(*ctx)->restriction_u_mesh);
455   CeedElemRestrictionDestroy(&(*ctx)->restriction_x_points);
456   CeedElemRestrictionDestroy(&(*ctx)->restriction_u_points);
457   CeedBasisDestroy(&(*ctx)->basis_u);
458   PetscCall(PetscFree(*ctx));
459   PetscFunctionReturn(PETSC_SUCCESS);
460 }
461 
462 // ------------------------------------------------------------------------------------------------
463 // PETSc-libCEED memory space utilities
464 // ------------------------------------------------------------------------------------------------
465 PetscErrorCode VecP2C(Vec X_petsc, PetscMemType *mem_type, CeedVector x_ceed) {
466   PetscScalar *x;
467 
468   PetscFunctionBeginUser;
469   PetscCall(VecGetArrayAndMemType(X_petsc, &x, mem_type));
470   CeedVectorSetArray(x_ceed, MemTypeP2C(*mem_type), CEED_USE_POINTER, x);
471   PetscFunctionReturn(PETSC_SUCCESS);
472 }
473 
474 PetscErrorCode VecC2P(CeedVector x_ceed, PetscMemType mem_type, Vec X_petsc) {
475   PetscScalar *x;
476 
477   PetscFunctionBeginUser;
478   CeedVectorTakeArray(x_ceed, MemTypeP2C(mem_type), &x);
479   PetscCall(VecRestoreArrayAndMemType(X_petsc, &x));
480   PetscFunctionReturn(PETSC_SUCCESS);
481 }
482 
483 PetscErrorCode VecReadP2C(Vec X_petsc, PetscMemType *mem_type, CeedVector x_ceed) {
484   PetscScalar *x;
485 
486   PetscFunctionBeginUser;
487   PetscCall(VecGetArrayReadAndMemType(X_petsc, (const PetscScalar **)&x, mem_type));
488   CeedVectorSetArray(x_ceed, MemTypeP2C(*mem_type), CEED_USE_POINTER, x);
489   PetscFunctionReturn(PETSC_SUCCESS);
490 }
491 
492 PetscErrorCode VecReadC2P(CeedVector x_ceed, PetscMemType mem_type, Vec X_petsc) {
493   PetscScalar *x;
494 
495   PetscFunctionBeginUser;
496   CeedVectorTakeArray(x_ceed, MemTypeP2C(mem_type), &x);
497   PetscCall(VecRestoreArrayReadAndMemType(X_petsc, (const PetscScalar **)&x));
498   PetscFunctionReturn(PETSC_SUCCESS);
499 }
500 
501 PetscErrorCode DMSwarmPICFieldP2C(DM dm_swarm, const char *field, CeedVector x_ceed) {
502   PetscScalar *x;
503 
504   PetscFunctionBeginUser;
505   PetscCall(DMSwarmGetField(dm_swarm, field, NULL, NULL, (void **)&x));
506   CeedVectorSetArray(x_ceed, CEED_MEM_HOST, CEED_USE_POINTER, (CeedScalar *)x);
507   PetscFunctionReturn(PETSC_SUCCESS);
508 }
509 
510 PetscErrorCode DMSwarmPICFieldC2P(DM dm_swarm, const char *field, CeedVector x_ceed) {
511   PetscScalar *x;
512 
513   PetscFunctionBeginUser;
514   CeedVectorTakeArray(x_ceed, CEED_MEM_HOST, (CeedScalar **)&x);
515   PetscCall(DMSwarmRestoreField(dm_swarm, field, NULL, NULL, (void **)&x));
516   PetscFunctionReturn(PETSC_SUCCESS);
517 }
518 
519 // ------------------------------------------------------------------------------------------------
520 // Solution functions
521 // ------------------------------------------------------------------------------------------------
522 PetscScalar EvalU_Poly(PetscInt dim, const PetscScalar x[]) {
523   PetscScalar       result = 0.0;
524   const PetscScalar p[5]   = {3, 1, 4, 1, 5};
525 
526   for (PetscInt d = 0; d < dim; d++) {
527     PetscScalar result_1d = 1.0;
528 
529     for (PetscInt i = 4; i >= 0; i--) result_1d = result_1d * x[d] + p[i];
530     result += result_1d;
531   }
532   return result * 1E-3;
533 }
534 
535 PetscScalar EvalU_Tanh(PetscInt dim, const PetscScalar x[]) {
536   PetscScalar result = 1.0, center = 0.1;
537 
538   for (PetscInt d = 0; d < dim; d++) {
539     result *= tanh(x[d] - center);
540     center += 0.1;
541   }
542   return result;
543 }
544 
545 PetscScalar EvalU_Sphere(PetscInt dim, const PetscScalar x[]) {
546   PetscScalar distance = sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);
547 
548   return distance < 1.0 ? 1.0 : 0.1;
549 }
550 
551 PetscErrorCode EvalU_Poly_proj(PetscInt dim, PetscReal t, const PetscReal x[], PetscInt num_comp, PetscScalar *u, void *ctx) {
552   PetscFunctionBeginUser;
553 
554   const PetscScalar f_x = EvalU_Poly(dim, x);
555 
556   for (PetscInt c = 0; c < num_comp; c++) u[c] = (c + 1.0) * f_x;
557   PetscFunctionReturn(PETSC_SUCCESS);
558 }
559 
560 PetscErrorCode EvalU_Tanh_proj(PetscInt dim, PetscReal t, const PetscReal x[], PetscInt num_comp, PetscScalar *u, void *ctx) {
561   PetscFunctionBeginUser;
562 
563   const PetscScalar f_x = EvalU_Tanh(dim, x);
564 
565   for (PetscInt c = 0; c < num_comp; c++) u[c] = (c + 1.0) * f_x;
566   PetscFunctionReturn(PETSC_SUCCESS);
567 }
568 
569 PetscErrorCode EvalU_Sphere_proj(PetscInt dim, PetscReal t, const PetscReal x[], PetscInt num_comp, PetscScalar *u, void *ctx) {
570   PetscFunctionBeginUser;
571 
572   const PetscScalar f_x = EvalU_Sphere(dim, x);
573 
574   for (PetscInt c = 0; c < num_comp; c++) u[c] = (c + 1.0) * f_x;
575   PetscFunctionReturn(PETSC_SUCCESS);
576 }
577 
578 // ------------------------------------------------------------------------------------------------
579 // Swarm point location utility
580 // ------------------------------------------------------------------------------------------------
581 PetscErrorCode DMSwarmInitalizePointLocations(DM dm_swarm, PointSwarmType point_swarm_type, PetscInt num_points, PetscInt num_points_per_cell) {
582   PetscFunctionBeginUser;
583 
584   switch (point_swarm_type) {
585     case SWARM_GAUSS:
586     case SWARM_UNIFORM: {
587       // -- Set gauss or uniform point locations in each cell
588       PetscInt    num_points_per_cell_1d = round(cbrt(num_points_per_cell * 1.0)), dim = 3;
589       PetscScalar point_coords[num_points_per_cell * 3];
590       CeedScalar  points_1d[num_points_per_cell_1d], weights_1d[num_points_per_cell_1d];
591 
592       if (point_swarm_type == SWARM_GAUSS) {
593         PetscCall(CeedGaussQuadrature(num_points_per_cell_1d, points_1d, weights_1d));
594       } else {
595         for (PetscInt i = 0; i < num_points_per_cell_1d; i++) points_1d[i] = 2.0 * (PetscReal)(i + 1) / (PetscReal)(num_points_per_cell_1d + 1) - 1;
596       }
597       for (PetscInt i = 0; i < num_points_per_cell_1d; i++) {
598         for (PetscInt j = 0; j < num_points_per_cell_1d; j++) {
599           for (PetscInt k = 0; k < num_points_per_cell_1d; k++) {
600             PetscInt p = (i * num_points_per_cell_1d + j) * num_points_per_cell_1d + k;
601 
602             point_coords[p * dim + 0] = points_1d[i];
603             point_coords[p * dim + 1] = points_1d[j];
604             point_coords[p * dim + 2] = points_1d[k];
605           }
606         }
607       }
608       PetscCall(DMSwarmSetPointCoordinatesCellwise(dm_swarm, num_points_per_cell_1d * num_points_per_cell_1d * num_points_per_cell_1d, point_coords));
609     } break;
610     case SWARM_CELL_RANDOM: {
611       // -- Set points randomly in each cell
612       PetscInt     dim = 3, num_cells_total = 1, num_cells[] = {1, 1, 1};
613       PetscScalar *point_coords;
614       PetscRandom  rng;
615 
616       PetscOptionsBegin(PetscObjectComm((PetscObject)dm_swarm), NULL, "libCEED example using PETSc with DMSwarm", NULL);
617 
618       PetscCall(PetscOptionsInt("-dm_plex_dim", "Background mesh dimension", NULL, dim, &dim, NULL));
619       PetscCall(PetscOptionsIntArray("-dm_plex_box_faces", "Number of cells", NULL, num_cells, &dim, NULL));
620 
621       PetscOptionsEnd();
622 
623       PetscCall(PetscRandomCreate(PetscObjectComm((PetscObject)dm_swarm), &rng));
624 
625       num_cells_total = num_cells[0] * num_cells[1] * num_cells[2];
626       PetscCall(DMSwarmGetField(dm_swarm, DMSwarmPICField_coor, NULL, NULL, (void **)&point_coords));
627       for (PetscInt c = 0; c < num_cells_total; c++) {
628         PetscInt cell_index[3] = {c % num_cells[0], (c / num_cells[0]) % num_cells[1], (c / num_cells[0] / num_cells[1]) % num_cells[2]};
629 
630         for (PetscInt p = 0; p < num_points_per_cell; p++) {
631           PetscInt    point_index = c * num_points_per_cell + p;
632           PetscScalar random_value;
633 
634           for (PetscInt i = 0; i < dim; i++) {
635             PetscCall(PetscRandomGetValue(rng, &random_value));
636             point_coords[point_index * dim + i] = -1.0 + cell_index[i] * 2.0 / (num_cells[i] + 1.0) + random_value;
637           }
638         }
639       }
640       PetscCall(DMSwarmRestoreField(dm_swarm, DMSwarmPICField_coor, NULL, NULL, (void **)&point_coords));
641       PetscCall(PetscRandomDestroy(&rng));
642     } break;
643     case SWARM_SINUSOIDAL: {
644       // -- Set points distributed per sinusoidal functions
645       PetscInt     dim = 3;
646       PetscScalar *point_coords;
647       DM           dm_mesh;
648 
649       PetscCall(DMSwarmGetCellDM(dm_swarm, &dm_mesh));
650       PetscCall(DMGetDimension(dm_mesh, &dim));
651 
652       PetscCall(DMSwarmGetField(dm_swarm, DMSwarmPICField_coor, NULL, NULL, (void **)&point_coords));
653       for (PetscInt p = 0; p < num_points; p++) {
654         point_coords[p * dim + 0] = -PetscCosReal((PetscReal)(p + 1) / (PetscReal)(num_points + 1) * PETSC_PI);
655         if (dim > 1) point_coords[p * dim + 1] = -PetscSinReal((PetscReal)(p + 1) / (PetscReal)(num_points + 1) * PETSC_PI);
656         if (dim > 2) point_coords[p * dim + 2] = PetscSinReal((PetscReal)(p + 1) / (PetscReal)(num_points + 1) * PETSC_PI);
657       }
658       PetscCall(DMSwarmRestoreField(dm_swarm, DMSwarmPICField_coor, NULL, NULL, (void **)&point_coords));
659     } break;
660   }
661   PetscCall(DMSwarmMigrate(dm_swarm, PETSC_TRUE));
662   PetscFunctionReturn(PETSC_SUCCESS);
663 }
664 
665 /*@C
666   DMSwarmCreateReferenceCoordinates - Compute the cell reference coordinates for local DMSwarm points.
667 
668   Collective
669 
670   Input Parameter:
671 . dm_swarm  - the `DMSwarm`
672 
673   Output Parameters:
674 + is_points    - The IS object for indexing into points per cell
675 - X_points_ref - Vec holding the cell reference coordinates for local DMSwarm points
676 
677 The index set contains ranges of indices for each local cell. This range contains the indices of every point in the cell.
678 
679 ```
680 total_num_cells
681 cell_0_start_index
682 cell_1_start_index
683 cell_2_start_index
684 ...
685 cell_n_start_index
686 cell_n_stop_index
687 cell_0_point_0
688 cell_0_point_0
689 ...
690 cell_n_point_m
691 ```
692 
693   Level: beginner
694 
695 .seealso: `DMSwarm`
696 @*/
697 PetscErrorCode DMSwarmCreateReferenceCoordinates(DM dm_swarm, IS *is_points, Vec *X_points_ref) {
698   PetscInt           cell_start, cell_end, num_cells_local, dim, num_points_local, *cell_points, points_offset;
699   PetscScalar       *coords_points_ref;
700   const PetscScalar *coords_points_true;
701   DM                 dm_mesh;
702 
703   PetscFunctionBeginUser;
704   PetscCall(DMSwarmGetCellDM(dm_swarm, &dm_mesh));
705 
706   // Create vector to hold reference coordinates
707   {
708     Vec X_points_true;
709 
710     PetscCall(DMSwarmCreateLocalVectorFromField(dm_swarm, DMSwarmPICField_coor, &X_points_true));
711     PetscCall(VecDuplicate(X_points_true, X_points_ref));
712     PetscCall(DMSwarmDestroyLocalVectorFromField(dm_swarm, DMSwarmPICField_coor, &X_points_true));
713   }
714 
715   // Allocate index set array
716   PetscCall(DMPlexGetHeightStratum(dm_mesh, 0, &cell_start, &cell_end));
717   num_cells_local = cell_end - cell_start;
718   points_offset   = num_cells_local + 2;
719   PetscCall(VecGetLocalSize(*X_points_ref, &num_points_local));
720   PetscCall(DMGetDimension(dm_mesh, &dim));
721   num_points_local /= dim;
722   PetscCall(PetscMalloc1(num_points_local + num_cells_local + 2, &cell_points));
723   cell_points[0] = num_cells_local;
724 
725   // Get reference coordinates for each swarm point wrt the elements in the background mesh
726   PetscCall(DMSwarmSortGetAccess(dm_swarm));
727   PetscCall(DMSwarmGetField(dm_swarm, DMSwarmPICField_coor, NULL, NULL, (void **)&coords_points_true));
728   PetscCall(VecGetArray(*X_points_ref, &coords_points_ref));
729   for (PetscInt cell = cell_start, num_points_processed = 0; cell < cell_end; cell++) {
730     PetscInt *points_in_cell, num_points_in_cell, local_cell = cell - cell_start;
731     PetscReal v[3], J[9], invJ[9], detJ, v0_ref[3] = {-1.0, -1.0, -1.0};
732 
733     PetscCall(DMSwarmSortGetPointsPerCell(dm_swarm, cell, &num_points_in_cell, &points_in_cell));
734     // -- Reference coordinates for swarm points in background mesh element
735     PetscCall(DMPlexComputeCellGeometryFEM(dm_mesh, cell, NULL, v, J, invJ, &detJ));
736     cell_points[local_cell + 1] = num_points_processed + points_offset;
737     for (PetscInt p = 0; p < num_points_in_cell; p++) {
738       const CeedInt point = points_in_cell[p];
739 
740       cell_points[points_offset + (num_points_processed++)] = point;
741       CoordinatesRealToRef(dim, dim, v0_ref, v, invJ, &coords_points_true[point * dim], &coords_points_ref[point * dim]);
742     }
743 
744     // -- Cleanup
745     PetscCall(PetscFree(points_in_cell));
746   }
747   cell_points[points_offset - 1] = num_points_local + points_offset;
748 
749   // Cleanup
750   PetscCall(DMSwarmRestoreField(dm_swarm, DMSwarmPICField_coor, NULL, NULL, (void **)&coords_points_true));
751   PetscCall(VecRestoreArray(*X_points_ref, &coords_points_ref));
752   PetscCall(DMSwarmSortRestoreAccess(dm_swarm));
753 
754   // Create index set
755   PetscCall(ISCreateGeneral(PETSC_COMM_SELF, num_points_local + points_offset, cell_points, PETSC_OWN_POINTER, is_points));
756   PetscFunctionReturn(PETSC_SUCCESS);
757 }
758 
759 // ------------------------------------------------------------------------------------------------
760 // Projection via PETSc
761 // ------------------------------------------------------------------------------------------------
762 PetscErrorCode DMSwarmInterpolateFromCellToSwarm_Petsc(DM dm_swarm, const char *field, Vec U_mesh) {
763   PetscInt           dim, num_comp, cell_start, cell_end;
764   PetscScalar       *u_points;
765   const PetscScalar *coords_points;
766   const PetscReal    v0_ref[3] = {-1.0, -1.0, -1.0};
767   DM                 dm_mesh;
768   PetscSection       section_u_mesh_loc;
769   PetscDS            ds;
770   PetscFE            fe;
771   PetscFEGeom        fe_geometry;
772   PetscQuadrature    quadrature;
773   Vec                U_loc;
774 
775   PetscFunctionBeginUser;
776   // Get mesh DM
777   PetscCall(DMSwarmGetCellDM(dm_swarm, &dm_mesh));
778   PetscCall(DMGetDimension(dm_mesh, &dim));
779   {
780     PetscSection section_u_mesh_loc_closure_permutation;
781 
782     PetscCall(DMGetLocalSection(dm_mesh, &section_u_mesh_loc_closure_permutation));
783     PetscCall(PetscSectionClone(section_u_mesh_loc_closure_permutation, &section_u_mesh_loc));
784     PetscCall(PetscSectionResetClosurePermutation(section_u_mesh_loc));
785   }
786 
787   // Get local mesh values
788   PetscCall(DMGetLocalVector(dm_mesh, &U_loc));
789   PetscCall(VecZeroEntries(U_loc));
790   PetscCall(DMGlobalToLocal(dm_mesh, U_mesh, INSERT_VALUES, U_loc));
791 
792   // Get local swarm data
793   PetscCall(DMSwarmSortGetAccess(dm_swarm));
794   PetscCall(DMPlexGetHeightStratum(dm_mesh, 0, &cell_start, &cell_end));
795   PetscCall(DMSwarmGetField(dm_swarm, DMSwarmPICField_coor, NULL, NULL, (void **)&coords_points));
796   PetscCall(DMSwarmGetField(dm_swarm, field, &num_comp, NULL, (void **)&u_points));
797 
798   // Interpolate values to each swarm point, one element in the background mesh at a time
799   PetscCall(DMGetDS(dm_mesh, &ds));
800   PetscCall(PetscDSGetDiscretization(ds, 0, (PetscObject *)&fe));
801   for (PetscInt cell = cell_start; cell < cell_end; cell++) {
802     PetscTabulation tabulation;
803     PetscScalar    *u_cell = NULL, *coords_points_cell_true, *coords_points_cell_ref;
804     PetscReal       v[dim], J[dim * dim], invJ[dim * dim], detJ;
805     PetscInt       *points_cell;
806     PetscInt        num_points_in_cell;
807 
808     PetscCall(DMSwarmSortGetPointsPerCell(dm_swarm, cell, &num_points_in_cell, &points_cell));
809     PetscCall(DMGetWorkArray(dm_mesh, num_points_in_cell * dim, MPIU_REAL, &coords_points_cell_true));
810     PetscCall(DMGetWorkArray(dm_mesh, num_points_in_cell * dim, MPIU_REAL, &coords_points_cell_ref));
811     // -- Reference coordinates for swarm points in background mesh element
812     for (PetscInt p = 0; p < num_points_in_cell; p++) {
813       for (PetscInt d = 0; d < dim; d++) coords_points_cell_true[p * dim + d] = coords_points[points_cell[p] * dim + d];
814     }
815     PetscCall(DMPlexComputeCellGeometryFEM(dm_mesh, cell, NULL, v, J, invJ, &detJ));
816     for (PetscInt p = 0; p < num_points_in_cell; p++) {
817       CoordinatesRealToRef(dim, dim, v0_ref, v, invJ, &coords_points_cell_true[p * dim], &coords_points_cell_ref[p * dim]);
818     }
819     // -- Interpolate values from current element in background mesh to swarm points
820     PetscCall(PetscFECreateTabulation(fe, 1, num_points_in_cell, coords_points_cell_ref, 1, &tabulation));
821     PetscCall(DMPlexVecGetClosure(dm_mesh, section_u_mesh_loc, U_loc, cell, NULL, &u_cell));
822     PetscCall(PetscFEGetQuadrature(fe, &quadrature));
823     PetscCall(PetscFECreateCellGeometry(fe, quadrature, &fe_geometry));
824     for (PetscInt p = 0; p < num_points_in_cell; p++) {
825       PetscCall(PetscFEInterpolateAtPoints_Static(fe, tabulation, u_cell, &fe_geometry, p, &u_points[points_cell[p] * num_comp]));
826     }
827 
828     // -- Cleanup
829     PetscCall(PetscFEDestroyCellGeometry(fe, &fe_geometry));
830     PetscCall(DMPlexVecRestoreClosure(dm_mesh, section_u_mesh_loc, U_loc, cell, NULL, &u_cell));
831     PetscCall(DMRestoreWorkArray(dm_mesh, num_points_in_cell * dim, MPIU_REAL, &coords_points_cell_true));
832     PetscCall(DMRestoreWorkArray(dm_mesh, num_points_in_cell * dim, MPIU_REAL, &coords_points_cell_ref));
833     PetscCall(PetscTabulationDestroy(&tabulation));
834     PetscCall(PetscFree(points_cell));
835   }
836 
837   // Cleanup
838   PetscCall(DMSwarmRestoreField(dm_swarm, DMSwarmPICField_coor, NULL, NULL, (void **)&coords_points));
839   PetscCall(DMSwarmRestoreField(dm_swarm, field, NULL, NULL, (void **)&u_points));
840   PetscCall(DMSwarmSortRestoreAccess(dm_swarm));
841   PetscCall(DMRestoreLocalVector(dm_mesh, &U_loc));
842   PetscCall(PetscSectionDestroy(&section_u_mesh_loc));
843   PetscFunctionReturn(PETSC_SUCCESS);
844 }
845 
846 // ------------------------------------------------------------------------------------------------
847 // Projection via libCEED
848 // ------------------------------------------------------------------------------------------------
849 PetscErrorCode DMSwarmInterpolateFromCellToSwarm_Ceed(DM dm_swarm, const char *field, Vec U_mesh) {
850   PetscInt           num_elem;
851   PetscMemType       U_mem_type;
852   DM                 dm_mesh;
853   Vec                U_mesh_loc;
854   DMSwarmCeedContext swarm_ceed_context;
855 
856   PetscFunctionBeginUser;
857   // Get mesh DM
858   PetscCall(DMSwarmGetCellDM(dm_swarm, &dm_mesh));
859   PetscCall(DMGetApplicationContext(dm_mesh, (void *)&swarm_ceed_context));
860 
861   // Get mesh values
862   PetscCall(DMGetLocalVector(dm_mesh, &U_mesh_loc));
863   PetscCall(VecZeroEntries(U_mesh_loc));
864   PetscCall(DMGlobalToLocal(dm_mesh, U_mesh, INSERT_VALUES, U_mesh_loc));
865   PetscCall(VecReadP2C(U_mesh_loc, &U_mem_type, swarm_ceed_context->u_mesh_loc));
866 
867   // Get swarm access
868   PetscCall(DMSwarmSortGetAccess(dm_swarm));
869   PetscCall(DMSwarmPICFieldP2C(dm_swarm, field, swarm_ceed_context->u_points_loc));
870 
871   // Interpolate values to each swarm point, one element in the background mesh at a time
872   CeedElemRestrictionGetNumElements(swarm_ceed_context->restriction_u_mesh, &num_elem);
873   for (PetscInt e = 0; e < num_elem; e++) {
874     PetscInt num_points_in_elem;
875 
876     CeedElemRestrictionGetNumPointsInElement(swarm_ceed_context->restriction_u_points, e, &num_points_in_elem);
877 
878     // -- Reference coordinates for swarm points in background mesh element
879     CeedElemRestrictionApplyAtPointsInElement(swarm_ceed_context->restriction_x_points, e, CEED_NOTRANSPOSE, swarm_ceed_context->x_ref_points_loc,
880                                               swarm_ceed_context->x_ref_points_elem, CEED_REQUEST_IMMEDIATE);
881 
882     // -- Interpolate values from current element in background mesh to swarm points
883     // Note: This will only work for CPU backends at this time, as only CPU backends support ApplyBlock and ApplyAtPoints
884     CeedElemRestrictionApplyBlock(swarm_ceed_context->restriction_u_mesh, e, CEED_NOTRANSPOSE, swarm_ceed_context->u_mesh_loc,
885                                   swarm_ceed_context->u_mesh_elem, CEED_REQUEST_IMMEDIATE);
886     CeedBasisApplyAtPoints(swarm_ceed_context->basis_u, num_points_in_elem, CEED_NOTRANSPOSE, CEED_EVAL_INTERP, swarm_ceed_context->x_ref_points_elem,
887                            swarm_ceed_context->u_mesh_elem, swarm_ceed_context->u_points_elem);
888 
889     // -- Insert result back into local vector
890     CeedElemRestrictionApplyAtPointsInElement(swarm_ceed_context->restriction_u_points, e, CEED_TRANSPOSE, swarm_ceed_context->u_points_elem,
891                                               swarm_ceed_context->u_points_loc, CEED_REQUEST_IMMEDIATE);
892   }
893 
894   // Cleanup
895   PetscCall(DMSwarmPICFieldC2P(dm_swarm, field, swarm_ceed_context->u_points_loc));
896   PetscCall(DMSwarmSortRestoreAccess(dm_swarm));
897   PetscCall(VecReadC2P(swarm_ceed_context->u_mesh_loc, U_mem_type, U_mesh_loc));
898   PetscCall(DMRestoreLocalVector(dm_mesh, &U_mesh_loc));
899   PetscFunctionReturn(PETSC_SUCCESS);
900 }
901 
902 // ------------------------------------------------------------------------------------------------
903 // Error checking utility
904 // ------------------------------------------------------------------------------------------------
905 PetscErrorCode DMSwarmCheckSwarmValues(DM dm_swarm, const char *field, PetscScalar tolerance, TargetFuncProj TrueSolution) {
906   PetscBool          within_tolerance = PETSC_TRUE;
907   PetscInt           dim, num_comp, cell_start, cell_end;
908   const PetscScalar *u_points, *coords_points;
909   DM                 dm_mesh;
910 
911   PetscFunctionBeginUser;
912   PetscCall(DMSwarmSortGetAccess(dm_swarm));
913   PetscCall(DMSwarmGetCellDM(dm_swarm, &dm_mesh));
914   PetscCall(DMGetDimension(dm_mesh, &dim));
915   PetscCall(DMPlexGetHeightStratum(dm_mesh, 0, &cell_start, &cell_end));
916   PetscCall(DMSwarmGetField(dm_swarm, DMSwarmPICField_coor, NULL, NULL, (void **)&coords_points));
917   PetscCall(DMSwarmGetField(dm_swarm, field, &num_comp, NULL, (void **)&u_points));
918 
919   // Interpolate values to each swarm point, one element in the background mesh at a time
920   for (PetscInt cell = cell_start; cell < cell_end; cell++) {
921     PetscInt *points;
922     PetscInt  num_points_in_cell;
923 
924     PetscCall(DMSwarmSortGetPointsPerCell(dm_swarm, cell, &num_points_in_cell, &points));
925     // -- Reference coordinates for swarm points in background mesh element
926     for (PetscInt p = 0; p < num_points_in_cell; p++) {
927       PetscScalar x[dim], u_true[num_comp];
928 
929       for (PetscInt d = 0; d < dim; d++) x[d] = coords_points[points[p] * dim + d];
930       PetscCall(TrueSolution(dim, 0.0, x, num_comp, u_true, NULL));
931       for (PetscInt i = 0; i < num_comp; i++) {
932         if (PetscAbs(u_points[points[p] * num_comp + i] - u_true[i]) > tolerance) {
933           within_tolerance = PETSC_FALSE;
934           PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm_swarm),
935                                 "Incorrect interpolated value, cell %" PetscInt_FMT " point %" PetscInt_FMT " component %" PetscInt_FMT
936                                 ", found %f expected %f\n",
937                                 cell, p, i, u_points[points[p] * num_comp + i], u_true[i]));
938         }
939       }
940     }
941 
942     // -- Cleanup
943     PetscCall(PetscFree(points));
944   }
945 
946   // Cleanup
947   PetscCall(DMSwarmRestoreField(dm_swarm, DMSwarmPICField_coor, NULL, NULL, (void **)&coords_points));
948   PetscCall(DMSwarmRestoreField(dm_swarm, field, NULL, NULL, (void **)&u_points));
949   PetscCall(DMSwarmSortRestoreAccess(dm_swarm));
950   PetscCheck(within_tolerance, PetscObjectComm((PetscObject)dm_swarm), PETSC_ERR_USER, "Interpolation to swarm points not within tolerance");
951   PetscFunctionReturn(PETSC_SUCCESS);
952 }
953 
954 // ------------------------------------------------------------------------------------------------
955 // RHS for Swarm to Mesh projection
956 // ------------------------------------------------------------------------------------------------
957 PetscErrorCode DMSwarmCreateProjectionRHS(DM dm_swarm, const char *field, Vec B_mesh) {
958   PetscInt           num_elem;
959   PetscMemType       B_mem_type;
960   DM                 dm_mesh;
961   Vec                B_mesh_loc;
962   DMSwarmCeedContext swarm_ceed_context;
963 
964   PetscFunctionBeginUser;
965   // Get mesh DM
966   PetscCall(DMSwarmGetCellDM(dm_swarm, &dm_mesh));
967   PetscCall(DMGetApplicationContext(dm_mesh, (void *)&swarm_ceed_context));
968 
969   // Get mesh values
970   PetscCall(DMGetLocalVector(dm_mesh, &B_mesh_loc));
971   PetscCall(VecZeroEntries(B_mesh_loc));
972   PetscCall(VecP2C(B_mesh_loc, &B_mem_type, swarm_ceed_context->v_mesh_loc));
973 
974   // Get swarm access
975   PetscCall(DMSwarmSortGetAccess(dm_swarm));
976   PetscCall(DMSwarmPICFieldP2C(dm_swarm, field, swarm_ceed_context->u_points_loc));
977 
978   // Interpolate values to each swarm point, one element in the background mesh at a time
979   CeedElemRestrictionGetNumElements(swarm_ceed_context->restriction_u_mesh, &num_elem);
980   for (PetscInt e = 0; e < num_elem; e++) {
981     PetscInt num_points_in_elem;
982 
983     CeedElemRestrictionGetNumPointsInElement(swarm_ceed_context->restriction_u_points, e, &num_points_in_elem);
984 
985     // -- Reference coordinates for swarm points in background mesh element
986     CeedElemRestrictionApplyAtPointsInElement(swarm_ceed_context->restriction_x_points, e, CEED_NOTRANSPOSE, swarm_ceed_context->x_ref_points_loc,
987                                               swarm_ceed_context->x_ref_points_elem, CEED_REQUEST_IMMEDIATE);
988 
989     // -- Interpolate values from current element in background mesh to swarm points
990     // Note: This will only work for CPU backends at this time, as only CPU backends support ApplyBlock and ApplyAtPoints
991     CeedElemRestrictionApplyAtPointsInElement(swarm_ceed_context->restriction_u_points, e, CEED_NOTRANSPOSE, swarm_ceed_context->u_points_loc,
992                                               swarm_ceed_context->u_points_elem, CEED_REQUEST_IMMEDIATE);
993     CeedBasisApplyAtPoints(swarm_ceed_context->basis_u, num_points_in_elem, CEED_TRANSPOSE, CEED_EVAL_INTERP, swarm_ceed_context->x_ref_points_elem,
994                            swarm_ceed_context->u_points_elem, swarm_ceed_context->u_mesh_elem);
995 
996     // -- Insert result back into local vector
997     CeedElemRestrictionApplyBlock(swarm_ceed_context->restriction_u_mesh, e, CEED_TRANSPOSE, swarm_ceed_context->u_mesh_elem,
998                                   swarm_ceed_context->v_mesh_loc, CEED_REQUEST_IMMEDIATE);
999   }
1000 
1001   // Restore PETSc Vecs and Local to Global
1002   PetscCall(VecC2P(swarm_ceed_context->v_mesh_loc, B_mem_type, B_mesh_loc));
1003   PetscCall(VecZeroEntries(B_mesh));
1004   PetscCall(DMLocalToGlobal(dm_mesh, B_mesh_loc, ADD_VALUES, B_mesh));
1005 
1006   // Cleanup
1007   PetscCall(DMSwarmPICFieldC2P(dm_swarm, field, swarm_ceed_context->u_points_loc));
1008   PetscCall(DMSwarmSortRestoreAccess(dm_swarm));
1009   PetscCall(DMRestoreLocalVector(dm_mesh, &B_mesh_loc));
1010   PetscFunctionReturn(PETSC_SUCCESS);
1011 }
1012 
1013 // ------------------------------------------------------------------------------------------------
1014 // Swarm "mass matrix"
1015 // ------------------------------------------------------------------------------------------------
1016 PetscErrorCode MatMult_SwarmMass(Mat A, Vec U_mesh, Vec V_mesh) {
1017   PetscInt           num_elem;
1018   PetscMemType       U_mem_type, V_mem_type;
1019   DM                 dm_mesh;
1020   Vec                U_mesh_loc, V_mesh_loc;
1021   DMSwarmCeedContext swarm_ceed_context;
1022 
1023   PetscFunctionBeginUser;
1024   // Get mesh DM
1025   PetscCall(MatGetDM(A, &dm_mesh));
1026   PetscCall(DMGetApplicationContext(dm_mesh, (void *)&swarm_ceed_context));
1027 
1028   // Global to Local and get PETSc Vec access
1029   PetscCall(DMGetLocalVector(dm_mesh, &U_mesh_loc));
1030   PetscCall(VecZeroEntries(U_mesh_loc));
1031   PetscCall(DMGlobalToLocal(dm_mesh, U_mesh, INSERT_VALUES, U_mesh_loc));
1032   PetscCall(VecReadP2C(U_mesh_loc, &U_mem_type, swarm_ceed_context->u_mesh_loc));
1033   PetscCall(DMGetLocalVector(dm_mesh, &V_mesh_loc));
1034   PetscCall(VecZeroEntries(V_mesh_loc));
1035   PetscCall(VecP2C(V_mesh_loc, &V_mem_type, swarm_ceed_context->v_mesh_loc));
1036 
1037   // Interpolate values to each swarm point, one element in the background mesh at a time
1038   CeedElemRestrictionGetNumElements(swarm_ceed_context->restriction_u_mesh, &num_elem);
1039   for (PetscInt e = 0; e < num_elem; e++) {
1040     PetscInt num_points_in_elem;
1041 
1042     CeedElemRestrictionGetNumPointsInElement(swarm_ceed_context->restriction_u_points, e, &num_points_in_elem);
1043 
1044     // -- Reference coordinates for swarm points in background mesh element
1045     CeedElemRestrictionApplyAtPointsInElement(swarm_ceed_context->restriction_x_points, e, CEED_NOTRANSPOSE, swarm_ceed_context->x_ref_points_loc,
1046                                               swarm_ceed_context->x_ref_points_elem, CEED_REQUEST_IMMEDIATE);
1047 
1048     // -- Interpolate values from current element in background mesh to swarm points
1049     // Note: This will only work for CPU backends at this time, as only CPU backends support ApplyBlock and ApplyAtPoints
1050     CeedElemRestrictionApplyBlock(swarm_ceed_context->restriction_u_mesh, e, CEED_NOTRANSPOSE, swarm_ceed_context->u_mesh_loc,
1051                                   swarm_ceed_context->u_mesh_elem, CEED_REQUEST_IMMEDIATE);
1052     CeedBasisApplyAtPoints(swarm_ceed_context->basis_u, num_points_in_elem, CEED_NOTRANSPOSE, CEED_EVAL_INTERP, swarm_ceed_context->x_ref_points_elem,
1053                            swarm_ceed_context->u_mesh_elem, swarm_ceed_context->u_points_elem);
1054 
1055     // -- Interpolate transpose back from swarm points to mesh
1056     CeedBasisApplyAtPoints(swarm_ceed_context->basis_u, num_points_in_elem, CEED_TRANSPOSE, CEED_EVAL_INTERP, swarm_ceed_context->x_ref_points_elem,
1057                            swarm_ceed_context->u_points_elem, swarm_ceed_context->u_mesh_elem);
1058     CeedElemRestrictionApplyBlock(swarm_ceed_context->restriction_u_mesh, e, CEED_TRANSPOSE, swarm_ceed_context->u_mesh_elem,
1059                                   swarm_ceed_context->v_mesh_loc, CEED_REQUEST_IMMEDIATE);
1060   }
1061 
1062   // Restore PETSc Vecs and Local to Global
1063   PetscCall(VecReadC2P(swarm_ceed_context->u_mesh_loc, U_mem_type, U_mesh_loc));
1064   PetscCall(VecC2P(swarm_ceed_context->v_mesh_loc, V_mem_type, V_mesh_loc));
1065   PetscCall(VecZeroEntries(V_mesh));
1066   PetscCall(DMLocalToGlobal(dm_mesh, V_mesh_loc, ADD_VALUES, V_mesh));
1067 
1068   // Cleanup
1069   PetscCall(DMRestoreLocalVector(dm_mesh, &U_mesh_loc));
1070   PetscCall(DMRestoreLocalVector(dm_mesh, &V_mesh_loc));
1071   PetscFunctionReturn(PETSC_SUCCESS);
1072 }
1073