xref: /libCEED/examples/petsc/src/petscutils.c (revision 9ba83ac0e4b1fca39d6fa6737a318a9f0cbc172d)
1*9ba83ac0SJeremy L Thompson // Copyright (c) 2017-2026, Lawrence Livermore National Security, LLC and other CEED contributors.
298285ab4SZach Atkins // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
398285ab4SZach Atkins //
498285ab4SZach Atkins // SPDX-License-Identifier: BSD-2-Clause
598285ab4SZach Atkins //
698285ab4SZach Atkins // This file is part of CEED:  http://github.com/ceed
798285ab4SZach Atkins 
8e83e87a5Sjeremylt #include "../include/petscutils.h"
9e83e87a5Sjeremylt 
10e83e87a5Sjeremylt // -----------------------------------------------------------------------------
11e83e87a5Sjeremylt // Convert PETSc MemType to libCEED MemType
12e83e87a5Sjeremylt // -----------------------------------------------------------------------------
132b730f8bSJeremy L Thompson CeedMemType MemTypeP2C(PetscMemType mem_type) { return PetscMemTypeDevice(mem_type) ? CEED_MEM_DEVICE : CEED_MEM_HOST; }
14e83e87a5Sjeremylt 
15b6972d74SZach Atkins // ------------------------------------------------------------------------------------------------
16b6972d74SZach Atkins // PETSc-libCEED memory space utilities
17b6972d74SZach Atkins // ------------------------------------------------------------------------------------------------
18b6972d74SZach Atkins PetscErrorCode VecP2C(Vec X_petsc, PetscMemType *mem_type, CeedVector x_ceed) {
19b6972d74SZach Atkins   PetscScalar *x;
20b6972d74SZach Atkins 
21b6972d74SZach Atkins   PetscFunctionBeginUser;
22b6972d74SZach Atkins   PetscCall(VecGetArrayAndMemType(X_petsc, &x, mem_type));
23b6972d74SZach Atkins   CeedVectorSetArray(x_ceed, MemTypeP2C(*mem_type), CEED_USE_POINTER, x);
24b6972d74SZach Atkins   PetscFunctionReturn(PETSC_SUCCESS);
25b6972d74SZach Atkins }
26b6972d74SZach Atkins 
27b6972d74SZach Atkins PetscErrorCode VecC2P(CeedVector x_ceed, PetscMemType mem_type, Vec X_petsc) {
28b6972d74SZach Atkins   PetscScalar *x;
29b6972d74SZach Atkins 
30b6972d74SZach Atkins   PetscFunctionBeginUser;
31b6972d74SZach Atkins   CeedVectorTakeArray(x_ceed, MemTypeP2C(mem_type), &x);
32b6972d74SZach Atkins   PetscCall(VecRestoreArrayAndMemType(X_petsc, &x));
33b6972d74SZach Atkins   PetscFunctionReturn(PETSC_SUCCESS);
34b6972d74SZach Atkins }
35b6972d74SZach Atkins 
36b6972d74SZach Atkins PetscErrorCode VecReadP2C(Vec X_petsc, PetscMemType *mem_type, CeedVector x_ceed) {
37b6972d74SZach Atkins   PetscScalar *x;
38b6972d74SZach Atkins 
39b6972d74SZach Atkins   PetscFunctionBeginUser;
40b6972d74SZach Atkins   PetscCall(VecGetArrayReadAndMemType(X_petsc, (const PetscScalar **)&x, mem_type));
41b6972d74SZach Atkins   CeedVectorSetArray(x_ceed, MemTypeP2C(*mem_type), CEED_USE_POINTER, x);
42b6972d74SZach Atkins   PetscFunctionReturn(PETSC_SUCCESS);
43b6972d74SZach Atkins }
44b6972d74SZach Atkins 
45b6972d74SZach Atkins PetscErrorCode VecReadC2P(CeedVector x_ceed, PetscMemType mem_type, Vec X_petsc) {
46b6972d74SZach Atkins   PetscScalar *x;
47b6972d74SZach Atkins 
48b6972d74SZach Atkins   PetscFunctionBeginUser;
49b6972d74SZach Atkins   CeedVectorTakeArray(x_ceed, MemTypeP2C(mem_type), &x);
50b6972d74SZach Atkins   PetscCall(VecRestoreArrayReadAndMemType(X_petsc, (const PetscScalar **)&x));
51b6972d74SZach Atkins   PetscFunctionReturn(PETSC_SUCCESS);
52b6972d74SZach Atkins }
53b6972d74SZach Atkins 
54e83e87a5Sjeremylt // -----------------------------------------------------------------------------
552c58efb6Sjeremylt // Apply 3D Kershaw mesh transformation
562c58efb6Sjeremylt // -----------------------------------------------------------------------------
572c58efb6Sjeremylt // Transition from a value of "a" for x=0, to a value of "b" for x=1.  Optionally
582c58efb6Sjeremylt // smooth -- see the commented versions at the end.
592c58efb6Sjeremylt static double step(const double a, const double b, double x) {
602c58efb6Sjeremylt   if (x <= 0) return a;
612c58efb6Sjeremylt   if (x >= 1) return b;
622c58efb6Sjeremylt   return a + (b - a) * (x);
632c58efb6Sjeremylt }
642c58efb6Sjeremylt 
652c58efb6Sjeremylt // 1D transformation at the right boundary
662b730f8bSJeremy L Thompson static double right(const double eps, const double x) { return (x <= 0.5) ? (2 - eps) * x : 1 + eps * (x - 1); }
672c58efb6Sjeremylt 
682c58efb6Sjeremylt // 1D transformation at the left boundary
692b730f8bSJeremy L Thompson static double left(const double eps, const double x) { return 1 - right(eps, 1 - x); }
702c58efb6Sjeremylt 
712c58efb6Sjeremylt // Apply 3D Kershaw mesh transformation
722c58efb6Sjeremylt // The eps parameters are in (0, 1]
732c58efb6Sjeremylt // Uniform mesh is recovered for eps=1
749b072555Sjeremylt PetscErrorCode Kershaw(DM dm_orig, PetscScalar eps) {
752c58efb6Sjeremylt   Vec          coord;
762c58efb6Sjeremylt   PetscInt     ncoord;
772c58efb6Sjeremylt   PetscScalar *c;
782c58efb6Sjeremylt 
792c58efb6Sjeremylt   PetscFunctionBeginUser;
802b730f8bSJeremy L Thompson   PetscCall(DMGetCoordinatesLocal(dm_orig, &coord));
812b730f8bSJeremy L Thompson   PetscCall(VecGetLocalSize(coord, &ncoord));
822b730f8bSJeremy L Thompson   PetscCall(VecGetArray(coord, &c));
832c58efb6Sjeremylt 
842c58efb6Sjeremylt   for (PetscInt i = 0; i < ncoord; i += 3) {
852c58efb6Sjeremylt     PetscScalar x = c[i], y = c[i + 1], z = c[i + 2];
862c58efb6Sjeremylt     PetscInt    layer  = x * 6;
872c58efb6Sjeremylt     PetscScalar lambda = (x - layer / 6.0) * 6;
882c58efb6Sjeremylt     c[i]               = x;
892c58efb6Sjeremylt 
902c58efb6Sjeremylt     switch (layer) {
912c58efb6Sjeremylt       case 0:
922c58efb6Sjeremylt         c[i + 1] = left(eps, y);
932c58efb6Sjeremylt         c[i + 2] = left(eps, z);
942c58efb6Sjeremylt         break;
952c58efb6Sjeremylt       case 1:
962c58efb6Sjeremylt       case 4:
972c58efb6Sjeremylt         c[i + 1] = step(left(eps, y), right(eps, y), lambda);
982c58efb6Sjeremylt         c[i + 2] = step(left(eps, z), right(eps, z), lambda);
992c58efb6Sjeremylt         break;
1002c58efb6Sjeremylt       case 2:
1012c58efb6Sjeremylt         c[i + 1] = step(right(eps, y), left(eps, y), lambda / 2);
1022c58efb6Sjeremylt         c[i + 2] = step(right(eps, z), left(eps, z), lambda / 2);
1032c58efb6Sjeremylt         break;
1042c58efb6Sjeremylt       case 3:
1052c58efb6Sjeremylt         c[i + 1] = step(right(eps, y), left(eps, y), (1 + lambda) / 2);
1062c58efb6Sjeremylt         c[i + 2] = step(right(eps, z), left(eps, z), (1 + lambda) / 2);
1072c58efb6Sjeremylt         break;
1082c58efb6Sjeremylt       default:
1092c58efb6Sjeremylt         c[i + 1] = right(eps, y);
1102c58efb6Sjeremylt         c[i + 2] = right(eps, z);
1112c58efb6Sjeremylt     }
1122c58efb6Sjeremylt   }
1132b730f8bSJeremy L Thompson   PetscCall(VecRestoreArray(coord, &c));
114ee4ca9cbSJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
1152c58efb6Sjeremylt }
1162c58efb6Sjeremylt 
1172c58efb6Sjeremylt // -----------------------------------------------------------------------------
118e83e87a5Sjeremylt // Create BC label
119e83e87a5Sjeremylt // -----------------------------------------------------------------------------
120e83e87a5Sjeremylt static PetscErrorCode CreateBCLabel(DM dm, const char name[]) {
121e83e87a5Sjeremylt   DMLabel label;
122e83e87a5Sjeremylt 
123e83e87a5Sjeremylt   PetscFunctionBeginUser;
124751eb813Srezgarshakeri   PetscCall(DMCreateLabel(dm, name));
125751eb813Srezgarshakeri   PetscCall(DMGetLabel(dm, name, &label));
126751eb813Srezgarshakeri   PetscCall(DMPlexMarkBoundaryFaces(dm, PETSC_DETERMINE, label));
127751eb813Srezgarshakeri   PetscCall(DMPlexLabelComplete(dm, label));
128ee4ca9cbSJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
129fd8f24faSSebastian Grimberg }
130e83e87a5Sjeremylt 
131e83e87a5Sjeremylt // -----------------------------------------------------------------------------
132e83e87a5Sjeremylt // This function sets up a DM for a given degree
133e83e87a5Sjeremylt // -----------------------------------------------------------------------------
1342b730f8bSJeremy L Thompson PetscErrorCode SetupDMByDegree(DM dm, PetscInt p_degree, PetscInt q_extra, PetscInt num_comp_u, PetscInt dim, bool enforce_bc) {
1352b730f8bSJeremy L Thompson   PetscInt  marker_ids[1] = {1};
136de1229c5Srezgarshakeri   PetscInt  q_degree      = p_degree + q_extra;
137e83e87a5Sjeremylt   PetscFE   fe;
13865233696SJeremy L Thompson   MPI_Comm  comm;
139129d8736Srezgarshakeri   PetscBool is_simplex = PETSC_TRUE;
140e83e87a5Sjeremylt 
141e83e87a5Sjeremylt   PetscFunctionBeginUser;
142129d8736Srezgarshakeri   // Check if simplex or tensor-product mesh
1432b730f8bSJeremy L Thompson   PetscCall(DMPlexIsSimplex(dm, &is_simplex));
144e83e87a5Sjeremylt   // Setup FE
1452b730f8bSJeremy L Thompson   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1462b730f8bSJeremy L Thompson   PetscCall(PetscFECreateLagrange(comm, dim, num_comp_u, is_simplex, p_degree, q_degree, &fe));
1472b730f8bSJeremy L Thompson   PetscCall(DMAddField(dm, NULL, (PetscObject)fe));
1482b730f8bSJeremy L Thompson   PetscCall(DMCreateDS(dm));
149129d8736Srezgarshakeri 
150129d8736Srezgarshakeri   {
151129d8736Srezgarshakeri     // create FE field for coordinates
1527ed3e4cdSJeremy L Thompson     PetscFE  fe_coords;
1537ed3e4cdSJeremy L Thompson     PetscInt num_comp_coord;
1542b730f8bSJeremy L Thompson     PetscCall(DMGetCoordinateDim(dm, &num_comp_coord));
1552b730f8bSJeremy L Thompson     PetscCall(PetscFECreateLagrange(comm, dim, num_comp_coord, is_simplex, 1, q_degree, &fe_coords));
15649a40c8aSKenneth E. Jansen     PetscCall(DMSetCoordinateDisc(dm, fe_coords, PETSC_TRUE));
1572b730f8bSJeremy L Thompson     PetscCall(PetscFEDestroy(&fe_coords));
1587ed3e4cdSJeremy L Thompson   }
159de1229c5Srezgarshakeri 
160ab1ff315Srezgarshakeri   // Setup Dirichlet BC
161ab1ff315Srezgarshakeri   // Note bp1, bp2 are projection and we don't need to apply BC
162ab1ff315Srezgarshakeri   // For bp3,bp4, the target function is zero on the boundaries
163ab1ff315Srezgarshakeri   // So we pass bcFunc = NULL in DMAddBoundary function
1649b072555Sjeremylt   if (enforce_bc) {
1659b072555Sjeremylt     PetscBool has_label;
166b6972d74SZach Atkins     PetscCall(DMHasLabel(dm, "marker", &has_label));
1672b730f8bSJeremy L Thompson     if (!has_label) {
168b6972d74SZach Atkins       PetscCall(CreateBCLabel(dm, "marker"));
1692b730f8bSJeremy L Thompson     }
17069f5adf1Sjeremylt     DMLabel label;
1712b730f8bSJeremy L Thompson     PetscCall(DMGetLabel(dm, "marker", &label));
1722b730f8bSJeremy L Thompson     PetscCall(DMAddBoundary(dm, DM_BC_ESSENTIAL, "wall", label, 1, marker_ids, 0, 0, NULL, NULL, NULL, NULL, NULL));
173751eb813Srezgarshakeri     PetscCall(DMSetOptionsPrefix(dm, "final_"));
174751eb813Srezgarshakeri     PetscCall(DMViewFromOptions(dm, NULL, "-dm_view"));
175e83e87a5Sjeremylt   }
176129d8736Srezgarshakeri 
177129d8736Srezgarshakeri   if (!is_simplex) {
178129d8736Srezgarshakeri     DM dm_coord;
1792b730f8bSJeremy L Thompson     PetscCall(DMGetCoordinateDM(dm, &dm_coord));
1802b730f8bSJeremy L Thompson     PetscCall(DMPlexSetClosurePermutationTensor(dm, PETSC_DETERMINE, NULL));
1812b730f8bSJeremy L Thompson     PetscCall(DMPlexSetClosurePermutationTensor(dm_coord, PETSC_DETERMINE, NULL));
182129d8736Srezgarshakeri   }
1832b730f8bSJeremy L Thompson   PetscCall(PetscFEDestroy(&fe));
184ee4ca9cbSJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
185fd8f24faSSebastian Grimberg }
186e83e87a5Sjeremylt 
187e83e87a5Sjeremylt // -----------------------------------------------------------------------------
188e83e87a5Sjeremylt // Get CEED restriction data from DMPlex
189e83e87a5Sjeremylt // -----------------------------------------------------------------------------
1902b730f8bSJeremy L Thompson PetscErrorCode CreateRestrictionFromPlex(Ceed ceed, DM dm, CeedInt height, DMLabel domain_label, CeedInt value, CeedElemRestriction *elem_restr) {
1914d00b080SJeremy L Thompson   PetscInt num_elem, elem_size, num_dof, num_comp, *elem_restr_offsets_petsc;
1924d00b080SJeremy L Thompson   CeedInt *elem_restr_offsets_ceed;
193e83e87a5Sjeremylt 
194e83e87a5Sjeremylt   PetscFunctionBeginUser;
1954d00b080SJeremy L Thompson   PetscCall(DMPlexGetLocalOffsets(dm, domain_label, value, height, 0, &num_elem, &elem_size, &num_comp, &num_dof, &elem_restr_offsets_petsc));
196e83e87a5Sjeremylt 
1974d00b080SJeremy L Thompson   PetscCall(IntArrayPetscToCeed(num_elem * elem_size, &elem_restr_offsets_petsc, &elem_restr_offsets_ceed));
1984d00b080SJeremy L Thompson   CeedElemRestrictionCreate(ceed, num_elem, elem_size, num_comp, 1, num_dof, CEED_MEM_HOST, CEED_COPY_VALUES, elem_restr_offsets_ceed, elem_restr);
1994d00b080SJeremy L Thompson   PetscCall(PetscFree(elem_restr_offsets_ceed));
200ee4ca9cbSJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
201fd8f24faSSebastian Grimberg }
202e83e87a5Sjeremylt 
203e83e87a5Sjeremylt // -----------------------------------------------------------------------------
204129d8736Srezgarshakeri // Utility function - convert from DMPolytopeType to CeedElemTopology
205129d8736Srezgarshakeri // -----------------------------------------------------------------------------
206de1229c5Srezgarshakeri CeedElemTopology ElemTopologyP2C(DMPolytopeType cell_type) {
207129d8736Srezgarshakeri   switch (cell_type) {
2082b730f8bSJeremy L Thompson     case DM_POLYTOPE_TRIANGLE:
2092b730f8bSJeremy L Thompson       return CEED_TOPOLOGY_TRIANGLE;
2102b730f8bSJeremy L Thompson     case DM_POLYTOPE_QUADRILATERAL:
2112b730f8bSJeremy L Thompson       return CEED_TOPOLOGY_QUAD;
2122b730f8bSJeremy L Thompson     case DM_POLYTOPE_TETRAHEDRON:
2132b730f8bSJeremy L Thompson       return CEED_TOPOLOGY_TET;
2142b730f8bSJeremy L Thompson     case DM_POLYTOPE_HEXAHEDRON:
2152b730f8bSJeremy L Thompson       return CEED_TOPOLOGY_HEX;
2162b730f8bSJeremy L Thompson     default:
2172b730f8bSJeremy L Thompson       return 0;
218129d8736Srezgarshakeri   }
219129d8736Srezgarshakeri }
220129d8736Srezgarshakeri 
221129d8736Srezgarshakeri // -----------------------------------------------------------------------------
222f755c37aSrezgarshakeri // Convert DM field to DS field
223129d8736Srezgarshakeri // -----------------------------------------------------------------------------
2242b730f8bSJeremy L Thompson PetscErrorCode DMFieldToDSField(DM dm, DMLabel domain_label, PetscInt dm_field, PetscInt *ds_field) {
225129d8736Srezgarshakeri   PetscDS         ds;
226129d8736Srezgarshakeri   IS              field_is;
227129d8736Srezgarshakeri   const PetscInt *fields;
228129d8736Srezgarshakeri   PetscInt        num_fields;
229129d8736Srezgarshakeri 
230f755c37aSrezgarshakeri   PetscFunctionBeginUser;
231129d8736Srezgarshakeri   // Translate dm_field to ds_field
232baf96a30SJames Wright   PetscCall(DMGetRegionDS(dm, domain_label, &field_is, &ds, NULL));
233f755c37aSrezgarshakeri   PetscCall(ISGetIndices(field_is, &fields));
234f755c37aSrezgarshakeri   PetscCall(ISGetSize(field_is, &num_fields));
235129d8736Srezgarshakeri   for (PetscInt i = 0; i < num_fields; i++) {
236129d8736Srezgarshakeri     if (dm_field == fields[i]) {
237f755c37aSrezgarshakeri       *ds_field = i;
238129d8736Srezgarshakeri       break;
239129d8736Srezgarshakeri     }
240129d8736Srezgarshakeri   }
241f755c37aSrezgarshakeri   PetscCall(ISRestoreIndices(field_is, &fields));
242f755c37aSrezgarshakeri 
2432b730f8bSJeremy L Thompson   if (*ds_field == -1) SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Could not find dm_field %" PetscInt_FMT " in DS", dm_field);
244ee4ca9cbSJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
245129d8736Srezgarshakeri }
246129d8736Srezgarshakeri 
247f755c37aSrezgarshakeri // -----------------------------------------------------------------------------
248f755c37aSrezgarshakeri // Create libCEED Basis from PetscTabulation
249f755c37aSrezgarshakeri // -----------------------------------------------------------------------------
2502b730f8bSJeremy L Thompson PetscErrorCode BasisCreateFromTabulation(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt height, PetscInt face, PetscFE fe,
2512b730f8bSJeremy L Thompson                                          PetscTabulation basis_tabulation, PetscQuadrature quadrature, CeedBasis *basis) {
252f755c37aSrezgarshakeri   PetscInt           first_point;
253129d8736Srezgarshakeri   PetscInt           ids[1] = {label_value};
254129d8736Srezgarshakeri   DMLabel            depth_label;
255129d8736Srezgarshakeri   DMPolytopeType     cell_type;
256129d8736Srezgarshakeri   CeedElemTopology   elem_topo;
257f755c37aSrezgarshakeri   PetscScalar       *q_points, *interp, *grad;
258f755c37aSrezgarshakeri   const PetscScalar *q_weights;
259f755c37aSrezgarshakeri   PetscDualSpace     dual_space;
260f755c37aSrezgarshakeri   PetscInt           num_dual_basis_vectors;
261f755c37aSrezgarshakeri   PetscInt           dim, num_comp, P, Q;
262f755c37aSrezgarshakeri 
263f755c37aSrezgarshakeri   PetscFunctionBeginUser;
264f755c37aSrezgarshakeri   // General basis information
265f755c37aSrezgarshakeri   PetscCall(PetscFEGetSpatialDimension(fe, &dim));
266f755c37aSrezgarshakeri   PetscCall(PetscFEGetNumComponents(fe, &num_comp));
267f755c37aSrezgarshakeri   PetscCall(PetscFEGetDualSpace(fe, &dual_space));
268f755c37aSrezgarshakeri   PetscCall(PetscDualSpaceGetDimension(dual_space, &num_dual_basis_vectors));
269f755c37aSrezgarshakeri   P = num_dual_basis_vectors / num_comp;
270129d8736Srezgarshakeri 
271129d8736Srezgarshakeri   // Use depth label if no domain label present
272129d8736Srezgarshakeri   if (!domain_label) {
273129d8736Srezgarshakeri     PetscInt depth;
274129d8736Srezgarshakeri 
275f755c37aSrezgarshakeri     PetscCall(DMPlexGetDepth(dm, &depth));
276f755c37aSrezgarshakeri     PetscCall(DMPlexGetDepthLabel(dm, &depth_label));
277129d8736Srezgarshakeri     ids[0] = depth - height;
278129d8736Srezgarshakeri   }
279f755c37aSrezgarshakeri 
280129d8736Srezgarshakeri   // Get cell interp, grad, and quadrature data
2812b730f8bSJeremy L Thompson   PetscCall(DMGetFirstLabeledPoint(dm, dm, domain_label ? domain_label : depth_label, 1, ids, height, &first_point, NULL));
282f755c37aSrezgarshakeri   PetscCall(DMPlexGetCellType(dm, first_point, &cell_type));
283129d8736Srezgarshakeri   elem_topo = ElemTopologyP2C(cell_type);
2842b730f8bSJeremy L Thompson   if (!elem_topo) SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "DMPlex topology not supported");
285f755c37aSrezgarshakeri   {
286f755c37aSrezgarshakeri     size_t             q_points_size;
287f755c37aSrezgarshakeri     const PetscScalar *q_points_petsc;
288f755c37aSrezgarshakeri     PetscInt           q_dim;
289f755c37aSrezgarshakeri 
2902b730f8bSJeremy L Thompson     PetscCall(PetscQuadratureGetData(quadrature, &q_dim, NULL, &Q, &q_points_petsc, &q_weights));
291f755c37aSrezgarshakeri     q_points_size = Q * dim * sizeof(CeedScalar);
292f755c37aSrezgarshakeri     PetscCall(PetscCalloc(q_points_size, &q_points));
293f755c37aSrezgarshakeri     for (PetscInt q = 0; q < Q; q++) {
2942b730f8bSJeremy L Thompson       for (PetscInt d = 0; d < q_dim; d++) q_points[q * dim + d] = q_points_petsc[q * q_dim + d];
295f755c37aSrezgarshakeri     }
296f755c37aSrezgarshakeri   }
297f755c37aSrezgarshakeri 
298129d8736Srezgarshakeri   // Convert to libCEED orientation
299f755c37aSrezgarshakeri   {
300f755c37aSrezgarshakeri     PetscBool       is_simplex  = PETSC_FALSE;
301f755c37aSrezgarshakeri     IS              permutation = NULL;
302f755c37aSrezgarshakeri     const PetscInt *permutation_indices;
303f755c37aSrezgarshakeri 
304f755c37aSrezgarshakeri     PetscCall(DMPlexIsSimplex(dm, &is_simplex));
305f755c37aSrezgarshakeri     if (!is_simplex) {
306f755c37aSrezgarshakeri       PetscSection section;
307f755c37aSrezgarshakeri 
308f755c37aSrezgarshakeri       // -- Get permutation
309f755c37aSrezgarshakeri       PetscCall(DMGetLocalSection(dm, &section));
3102b730f8bSJeremy L Thompson       PetscCall(PetscSectionGetClosurePermutation(section, (PetscObject)dm, dim, num_comp * P, &permutation));
311f755c37aSrezgarshakeri       PetscCall(ISGetIndices(permutation, &permutation_indices));
312f755c37aSrezgarshakeri     }
313f755c37aSrezgarshakeri 
314f755c37aSrezgarshakeri     // -- Copy interp, grad matrices
315f755c37aSrezgarshakeri     PetscCall(PetscCalloc(P * Q * sizeof(CeedScalar), &interp));
316f755c37aSrezgarshakeri     PetscCall(PetscCalloc(P * Q * dim * sizeof(CeedScalar), &grad));
317129d8736Srezgarshakeri     const CeedInt c = 0;
318129d8736Srezgarshakeri     for (CeedInt q = 0; q < Q; q++) {
319f755c37aSrezgarshakeri       for (CeedInt p_ceed = 0; p_ceed < P; p_ceed++) {
3202b730f8bSJeremy L Thompson         CeedInt p_petsc = is_simplex ? (p_ceed * num_comp) : permutation_indices[p_ceed * num_comp];
321f755c37aSrezgarshakeri 
3222b730f8bSJeremy L Thompson         interp[q * P + p_ceed] = basis_tabulation->T[0][((face * Q + q) * P * num_comp + p_petsc) * num_comp + c];
323129d8736Srezgarshakeri         for (CeedInt d = 0; d < dim; d++) {
3242b730f8bSJeremy L Thompson           grad[(d * Q + q) * P + p_ceed] = basis_tabulation->T[1][(((face * Q + q) * P * num_comp + p_petsc) * num_comp + c) * dim + d];
325129d8736Srezgarshakeri         }
326129d8736Srezgarshakeri       }
327129d8736Srezgarshakeri     }
328f755c37aSrezgarshakeri 
329f755c37aSrezgarshakeri     // -- Cleanup
330f755c37aSrezgarshakeri     if (permutation) PetscCall(ISRestoreIndices(permutation, &permutation_indices));
331f755c37aSrezgarshakeri     PetscCall(ISDestroy(&permutation));
332f755c37aSrezgarshakeri   }
333f755c37aSrezgarshakeri 
334f755c37aSrezgarshakeri   // Finally, create libCEED basis
3352b730f8bSJeremy L Thompson   CeedBasisCreateH1(ceed, elem_topo, num_comp, P, Q, interp, grad, q_points, q_weights, basis);
336f755c37aSrezgarshakeri   PetscCall(PetscFree(q_points));
337f755c37aSrezgarshakeri   PetscCall(PetscFree(interp));
338f755c37aSrezgarshakeri   PetscCall(PetscFree(grad));
339ee4ca9cbSJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
340f755c37aSrezgarshakeri }
341f755c37aSrezgarshakeri 
342f755c37aSrezgarshakeri // -----------------------------------------------------------------------------
343f755c37aSrezgarshakeri // Get CEED Basis from DMPlex
344f755c37aSrezgarshakeri // -----------------------------------------------------------------------------
3452b730f8bSJeremy L Thompson PetscErrorCode CreateBasisFromPlex(Ceed ceed, DM dm, DMLabel domain_label, CeedInt label_value, CeedInt height, CeedInt dm_field, BPData bp_data,
3462b730f8bSJeremy L Thompson                                    CeedBasis *basis) {
347f755c37aSrezgarshakeri   PetscDS         ds;
348f755c37aSrezgarshakeri   PetscFE         fe;
349f755c37aSrezgarshakeri   PetscQuadrature quadrature;
350f755c37aSrezgarshakeri   PetscBool       is_simplex = PETSC_TRUE;
351f755c37aSrezgarshakeri   PetscInt        ds_field   = -1;
352f755c37aSrezgarshakeri 
353f755c37aSrezgarshakeri   PetscFunctionBeginUser;
354f755c37aSrezgarshakeri   // Get element information
355baf96a30SJames Wright   PetscCall(DMGetRegionDS(dm, domain_label, NULL, &ds, NULL));
356f755c37aSrezgarshakeri   PetscCall(DMFieldToDSField(dm, domain_label, dm_field, &ds_field));
357f755c37aSrezgarshakeri   PetscCall(PetscDSGetDiscretization(ds, ds_field, (PetscObject *)&fe));
358f755c37aSrezgarshakeri   PetscCall(PetscFEGetHeightSubspace(fe, height, &fe));
359f755c37aSrezgarshakeri   PetscCall(PetscFEGetQuadrature(fe, &quadrature));
360f755c37aSrezgarshakeri 
361f755c37aSrezgarshakeri   // Check if simplex or tensor-product mesh
362f755c37aSrezgarshakeri   PetscCall(DMPlexIsSimplex(dm, &is_simplex));
363f755c37aSrezgarshakeri 
364f755c37aSrezgarshakeri   // Build libCEED basis
365f755c37aSrezgarshakeri   if (is_simplex) {
366f755c37aSrezgarshakeri     PetscTabulation basis_tabulation;
367f755c37aSrezgarshakeri     PetscInt        num_derivatives = 1, face = 0;
368f755c37aSrezgarshakeri 
369f755c37aSrezgarshakeri     PetscCall(PetscFEGetCellTabulation(fe, num_derivatives, &basis_tabulation));
3702b730f8bSJeremy L Thompson     PetscCall(BasisCreateFromTabulation(ceed, dm, domain_label, label_value, height, face, fe, basis_tabulation, quadrature, basis));
371129d8736Srezgarshakeri   } else {
372f755c37aSrezgarshakeri     PetscDualSpace dual_space;
373f755c37aSrezgarshakeri     PetscInt       num_dual_basis_vectors;
374f755c37aSrezgarshakeri     PetscInt       dim, num_comp, P, Q;
375f755c37aSrezgarshakeri 
376f755c37aSrezgarshakeri     PetscCall(PetscFEGetSpatialDimension(fe, &dim));
377f755c37aSrezgarshakeri     PetscCall(PetscFEGetNumComponents(fe, &num_comp));
378f755c37aSrezgarshakeri     PetscCall(PetscFEGetDualSpace(fe, &dual_space));
379f755c37aSrezgarshakeri     PetscCall(PetscDualSpaceGetDimension(dual_space, &num_dual_basis_vectors));
380f755c37aSrezgarshakeri     P = num_dual_basis_vectors / num_comp;
381f755c37aSrezgarshakeri     PetscCall(PetscQuadratureGetData(quadrature, NULL, NULL, &Q, NULL, NULL));
382f755c37aSrezgarshakeri 
383129d8736Srezgarshakeri     CeedInt P_1d = (CeedInt)round(pow(P, 1.0 / dim));
384129d8736Srezgarshakeri     CeedInt Q_1d = (CeedInt)round(pow(Q, 1.0 / dim));
385129d8736Srezgarshakeri 
3862b730f8bSJeremy L Thompson     CeedBasisCreateTensorH1Lagrange(ceed, dim, num_comp, P_1d, Q_1d, bp_data.q_mode, basis);
387129d8736Srezgarshakeri   }
388ee4ca9cbSJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
389f755c37aSrezgarshakeri }
390129d8736Srezgarshakeri 
391129d8736Srezgarshakeri // -----------------------------------------------------------------------------
392de1229c5Srezgarshakeri // Utilities
393de1229c5Srezgarshakeri // -----------------------------------------------------------------------------
394de1229c5Srezgarshakeri 
395de1229c5Srezgarshakeri // Utility function, compute three factors of an integer
396de1229c5Srezgarshakeri static void Split3(PetscInt size, PetscInt m[3], bool reverse) {
397de1229c5Srezgarshakeri   for (PetscInt d = 0, size_left = size; d < 3; d++) {
398de1229c5Srezgarshakeri     PetscInt try = (PetscInt)PetscCeilReal(PetscPowReal(size_left, 1. / (3 - d)));
399de1229c5Srezgarshakeri     while (try * (size_left / try) != size_left) try++;
400de1229c5Srezgarshakeri     m[reverse ? 2 - d : d] = try;
401de1229c5Srezgarshakeri     size_left /= try;
402de1229c5Srezgarshakeri   }
403de1229c5Srezgarshakeri }
404de1229c5Srezgarshakeri 
4052b730f8bSJeremy L Thompson static int Max3(const PetscInt a[3]) { return PetscMax(a[0], PetscMax(a[1], a[2])); }
406de1229c5Srezgarshakeri 
4072b730f8bSJeremy L Thompson static int Min3(const PetscInt a[3]) { return PetscMin(a[0], PetscMin(a[1], a[2])); }
408de1229c5Srezgarshakeri 
409de1229c5Srezgarshakeri // -----------------------------------------------------------------------------
410de1229c5Srezgarshakeri // Create distribute dm
411de1229c5Srezgarshakeri // -----------------------------------------------------------------------------
412de1229c5Srezgarshakeri PetscErrorCode CreateDistributedDM(RunParams rp, DM *dm) {
413de1229c5Srezgarshakeri   PetscFunctionBeginUser;
414de1229c5Srezgarshakeri   // Setup DM
415de1229c5Srezgarshakeri   if (rp->read_mesh) {
4162b730f8bSJeremy L Thompson     PetscCall(DMPlexCreateFromFile(PETSC_COMM_WORLD, rp->filename, NULL, PETSC_TRUE, dm));
417de1229c5Srezgarshakeri   } else {
418de1229c5Srezgarshakeri     if (rp->user_l_nodes) {
419de1229c5Srezgarshakeri       // Find a nicely composite number of elements no less than global nodes
420de1229c5Srezgarshakeri       PetscMPIInt size;
4212b730f8bSJeremy L Thompson       PetscCall(MPI_Comm_size(rp->comm, &size));
4222b730f8bSJeremy L Thompson       for (PetscInt g_elem = PetscMax(1, size * rp->local_nodes / PetscPowInt(rp->degree, rp->dim));; g_elem++) {
423de1229c5Srezgarshakeri         Split3(g_elem, rp->mesh_elem, true);
424de1229c5Srezgarshakeri         if (Max3(rp->mesh_elem) / Min3(rp->mesh_elem) <= 2) break;
425de1229c5Srezgarshakeri       }
426de1229c5Srezgarshakeri     }
427ab1ff315Srezgarshakeri 
4287cf95199SJeremy L Thompson     PetscCall(DMPlexCreateBoxMesh(PETSC_COMM_WORLD, rp->dim, rp->simplex, rp->mesh_elem, NULL, NULL, NULL, PETSC_TRUE, 0, PETSC_FALSE, dm));
429de1229c5Srezgarshakeri   }
430de1229c5Srezgarshakeri 
4312b730f8bSJeremy L Thompson   PetscCall(DMSetFromOptions(*dm));
4322b730f8bSJeremy L Thompson   PetscCall(DMViewFromOptions(*dm, NULL, "-dm_view"));
433ee4ca9cbSJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
434de1229c5Srezgarshakeri }
435de1229c5Srezgarshakeri 
436de1229c5Srezgarshakeri // -----------------------------------------------------------------------------
437