xref: /honee/src/dm-utils.c (revision 26d401f341abd2ffa9d6447a9dd4eef440436380)
17b8c5038SJames Wright // SPDX-FileCopyrightText: Copyright (c) 2017-2024, HONEE contributors.
27b8c5038SJames Wright // SPDX-License-Identifier: Apache-2.0 OR BSD-2-Clause
37b8c5038SJames Wright 
47b8c5038SJames Wright /// @file
57b8c5038SJames Wright /// Utilities for setting up DM and PetscFE
67b8c5038SJames Wright 
77b8c5038SJames Wright #include <ceed.h>
87b8c5038SJames Wright #include <dm-utils.h>
97b8c5038SJames Wright #include <petsc-ceed-utils.h>
107b8c5038SJames Wright #include <petscdmplex.h>
117b8c5038SJames Wright #include <petscds.h>
127b8c5038SJames Wright 
137b8c5038SJames Wright /**
147b8c5038SJames Wright   @brief Convert `DM` field to `DS` field.
157b8c5038SJames Wright 
167b8c5038SJames Wright   @param[in]   dm            `DM` holding mesh
177b8c5038SJames Wright   @param[in]   domain_label  Label for `DM` domain
187b8c5038SJames Wright   @param[in]   dm_field      Index of `DM` field
197b8c5038SJames Wright   @param[out]  ds_field      Index of `DS` field
207b8c5038SJames Wright 
217b8c5038SJames Wright   @return An error code: 0 - success, otherwise - failure
227b8c5038SJames Wright **/
237b8c5038SJames Wright PetscErrorCode DMFieldToDSField(DM dm, DMLabel domain_label, PetscInt dm_field, PetscInt *ds_field) {
247b8c5038SJames Wright   PetscDS         ds;
257b8c5038SJames Wright   IS              field_is;
267b8c5038SJames Wright   const PetscInt *fields;
277b8c5038SJames Wright   PetscInt        num_fields;
287b8c5038SJames Wright 
297b8c5038SJames Wright   PetscFunctionBeginUser;
307b8c5038SJames Wright   PetscCall(DMGetRegionDS(dm, domain_label, &field_is, &ds, NULL));
317b8c5038SJames Wright   PetscCall(ISGetIndices(field_is, &fields));
327b8c5038SJames Wright   PetscCall(ISGetSize(field_is, &num_fields));
337b8c5038SJames Wright   for (PetscInt i = 0; i < num_fields; i++) {
347b8c5038SJames Wright     if (dm_field == fields[i]) {
357b8c5038SJames Wright       *ds_field = i;
367b8c5038SJames Wright       break;
377b8c5038SJames Wright     }
387b8c5038SJames Wright   }
397b8c5038SJames Wright   PetscCall(ISRestoreIndices(field_is, &fields));
407b8c5038SJames Wright 
417b8c5038SJames Wright   PetscCheck(*ds_field != -1, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Could not find dm_field %" PetscInt_FMT " in DS", dm_field);
427b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
437b8c5038SJames Wright }
447b8c5038SJames Wright 
457b8c5038SJames Wright /**
467b8c5038SJames Wright   @brief Destroy `ElemRestriction` in a `PetscContainer`.
477b8c5038SJames Wright 
487b8c5038SJames Wright   Not collective across MPI processes.
497b8c5038SJames Wright 
507b8c5038SJames Wright   @param[in,out]  ctx  `CeedElemRestriction`
517b8c5038SJames Wright 
527b8c5038SJames Wright   @return An error code: 0 - success, otherwise - failure
537b8c5038SJames Wright **/
547b8c5038SJames Wright static PetscErrorCode DMPlexCeedElemRestrictionDestroy(void **ctx) {
557b8c5038SJames Wright   CeedElemRestriction rstr = *(CeedElemRestriction *)ctx;
567b8c5038SJames Wright 
577b8c5038SJames Wright   PetscFunctionBegin;
587b8c5038SJames Wright   PetscCallCeed(CeedElemRestrictionReturnCeed(rstr), CeedElemRestrictionDestroy(&rstr));
597b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
607b8c5038SJames Wright }
617b8c5038SJames Wright 
627b8c5038SJames Wright /**
637b8c5038SJames Wright   @brief Create `CeedElemRestriction` from `DMPlex`.
647b8c5038SJames Wright 
657b8c5038SJames Wright  The `CeedElemRestriction` is stored in the `DMPlex` object for reuse;
667b8c5038SJames Wright  if the routine is called again with the same arguments, the same `CeedElemRestriction` is returned.
677b8c5038SJames Wright 
687b8c5038SJames Wright   Not collective across MPI processes.
697b8c5038SJames Wright 
707b8c5038SJames Wright   @param[in]   ceed          `Ceed` context
717b8c5038SJames Wright   @param[in]   dm            `DMPlex` holding mesh
727b8c5038SJames Wright   @param[in]   domain_label  `DMLabel` for `DMPlex` domain
737b8c5038SJames Wright   @param[in]   label_value   Stratum value
747b8c5038SJames Wright   @param[in]   height        Height of `DMPlex` topology
757b8c5038SJames Wright   @param[in]   dm_field      Index of `DMPlex` field
767b8c5038SJames Wright   @param[out]  restriction   `CeedElemRestriction` for `DMPlex`
777b8c5038SJames Wright 
787b8c5038SJames Wright   @return An error code: 0 - success, otherwise - failure
797b8c5038SJames Wright **/
807b8c5038SJames Wright PetscErrorCode DMPlexCeedElemRestrictionCreate(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt height, PetscInt dm_field,
817b8c5038SJames Wright                                                CeedElemRestriction *restriction) {
827b8c5038SJames Wright   char                container_name[1024];
837b8c5038SJames Wright   CeedElemRestriction container_restriction;
847b8c5038SJames Wright 
857b8c5038SJames Wright   PetscFunctionBeginUser;
867b8c5038SJames Wright   {
877b8c5038SJames Wright     const char *label_name = NULL;
887b8c5038SJames Wright 
897b8c5038SJames Wright     if (domain_label) PetscCall(PetscObjectGetName((PetscObject)domain_label, &label_name));
907b8c5038SJames Wright     PetscCall(PetscSNPrintf(container_name, sizeof(container_name),
917b8c5038SJames Wright                             "DM CeedElemRestriction - label: %s label value: %" PetscInt_FMT " height: %" PetscInt_FMT " DM field: %" PetscInt_FMT,
927b8c5038SJames Wright                             label_name ? label_name : "NONE", label_value, height, dm_field));
937b8c5038SJames Wright   }
947b8c5038SJames Wright   PetscCall(PetscObjectContainerQuery((PetscObject)dm, container_name, (void **)&container_restriction));
957b8c5038SJames Wright 
967b8c5038SJames Wright   if (container_restriction) {
977b8c5038SJames Wright     // Copy existing restriction
987b8c5038SJames Wright     *restriction = NULL;
997b8c5038SJames Wright     PetscCallCeed(ceed, CeedElemRestrictionReferenceCopy(container_restriction, restriction));
1007b8c5038SJames Wright   } else {
1017b8c5038SJames Wright     PetscInt num_elem, elem_size, num_dof, num_comp, *restriction_offsets_petsc;
1027b8c5038SJames Wright     CeedInt *restriction_offsets_ceed = NULL;
1037b8c5038SJames Wright 
1047b8c5038SJames Wright     // Build restriction
1057b8c5038SJames Wright     PetscCall(DMPlexGetLocalOffsets(dm, domain_label, label_value, height, dm_field, &num_elem, &elem_size, &num_comp, &num_dof,
1067b8c5038SJames Wright                                     &restriction_offsets_petsc));
1077b8c5038SJames Wright     PetscCall(IntArrayPetscToCeed(num_elem * elem_size, &restriction_offsets_petsc, &restriction_offsets_ceed));
1087b8c5038SJames Wright     PetscCallCeed(ceed, CeedElemRestrictionCreate(ceed, num_elem, elem_size, num_comp, 1, num_dof, CEED_MEM_HOST, CEED_COPY_VALUES,
1097b8c5038SJames Wright                                                   restriction_offsets_ceed, restriction));
1107b8c5038SJames Wright     PetscCall(PetscFree(restriction_offsets_ceed));
1117b8c5038SJames Wright 
1127b8c5038SJames Wright     // Set in container
1137b8c5038SJames Wright     PetscCallCeed(ceed, CeedElemRestrictionReferenceCopy(*restriction, &container_restriction));
114*26d401f3SJames Wright     PetscCall(PetscObjectContainerCompose((PetscObject)dm, container_name, container_restriction,
115*26d401f3SJames Wright                                           (PetscCtxDestroyFn *)DMPlexCeedElemRestrictionDestroy));
1167b8c5038SJames Wright   }
1177b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
1187b8c5038SJames Wright }
1197b8c5038SJames Wright 
1207b8c5038SJames Wright /**
1217b8c5038SJames Wright   @brief Create `CeedElemRestriction` from `DMPlex` domain for mesh coordinates.
1227b8c5038SJames Wright 
1237b8c5038SJames Wright  The `CeedElemRestriction` is stored in the coordinate `DMPlex` object for reuse;
1247b8c5038SJames Wright  if the routine is called again with the same arguments, the same `CeedElemRestriction` is returned.
1257b8c5038SJames Wright 
1267b8c5038SJames Wright   Not collective across MPI processes.
1277b8c5038SJames Wright 
1287b8c5038SJames Wright   @param[in]   ceed          `Ceed` context
1297b8c5038SJames Wright   @param[in]   dm            `DMPlex` holding mesh
1307b8c5038SJames Wright   @param[in]   domain_label  Label for `DMPlex` domain
1317b8c5038SJames Wright   @param[in]   label_value   Stratum value
1327b8c5038SJames Wright   @param[in]   height        Height of `DMPlex` topology
1337b8c5038SJames Wright   @param[out]  restriction   `CeedElemRestriction` for mesh
1347b8c5038SJames Wright 
1357b8c5038SJames Wright   @return An error code: 0 - success, otherwise - failure
1367b8c5038SJames Wright **/
1377b8c5038SJames Wright PetscErrorCode DMPlexCeedElemRestrictionCoordinateCreate(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt height,
1387b8c5038SJames Wright                                                          CeedElemRestriction *restriction) {
1397b8c5038SJames Wright   DM dm_coord;
1407b8c5038SJames Wright 
1417b8c5038SJames Wright   PetscFunctionBeginUser;
1427b8c5038SJames Wright   PetscCall(DMGetCellCoordinateDM(dm, &dm_coord));
1437b8c5038SJames Wright   if (!dm_coord) {
1447b8c5038SJames Wright     PetscCall(DMGetCoordinateDM(dm, &dm_coord));
1457b8c5038SJames Wright   }
1467b8c5038SJames Wright   PetscCall(DMPlexCeedElemRestrictionCreate(ceed, dm_coord, domain_label, label_value, height, 0, restriction));
1477b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
1487b8c5038SJames Wright }
1497b8c5038SJames Wright 
1507b8c5038SJames Wright /**
1517b8c5038SJames Wright   @brief Create `CeedElemRestriction` from `DMPlex` domain for auxilury `QFunction` data.
1527b8c5038SJames Wright 
1537b8c5038SJames Wright   Not collective across MPI processes.
1547b8c5038SJames Wright 
1557b8c5038SJames Wright   @param[in]   ceed           `Ceed` context
1567b8c5038SJames Wright   @param[in]   dm             `DMPlex` holding mesh
1577b8c5038SJames Wright   @param[in]   domain_label   Label for `DMPlex` domain
1587b8c5038SJames Wright   @param[in]   label_value    Stratum value
1597b8c5038SJames Wright   @param[in]   height         Height of `DMPlex` topology
1607b8c5038SJames Wright   @param[in]   q_data_size    Number of components for `QFunction` data
1617b8c5038SJames Wright   @param[in]   is_collocated  Boolean flag indicating if the data is collocated on the nodes (`PETSC_TRUE`) or on quadrature points (`PETSC_FALSE`)
1627b8c5038SJames Wright   @param[out]  restriction    Strided `CeedElemRestriction` for `QFunction` data
1637b8c5038SJames Wright 
1647b8c5038SJames Wright   @return An error code: 0 - success, otherwise - failure
1657b8c5038SJames Wright **/
1667b8c5038SJames Wright static PetscErrorCode DMPlexCeedElemRestrictionStridedCreate(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt height,
1677b8c5038SJames Wright                                                              PetscInt q_data_size, PetscBool is_collocated, CeedElemRestriction *restriction) {
1687b8c5038SJames Wright   PetscInt num_elem, num_qpts, dm_field = 0;
1697b8c5038SJames Wright 
1707b8c5038SJames Wright   PetscFunctionBeginUser;
1717b8c5038SJames Wright   {  // Get number of elements
1727b8c5038SJames Wright     PetscInt depth;
1737b8c5038SJames Wright     DMLabel  depth_label;
1747b8c5038SJames Wright     IS       point_is, depth_is;
1757b8c5038SJames Wright 
1767b8c5038SJames Wright     PetscCall(DMPlexGetDepth(dm, &depth));
1777b8c5038SJames Wright     PetscCall(DMPlexGetDepthLabel(dm, &depth_label));
1787b8c5038SJames Wright     PetscCall(DMLabelGetStratumIS(depth_label, depth - height, &depth_is));
1797b8c5038SJames Wright     if (domain_label) {
1807b8c5038SJames Wright       IS domain_is;
1817b8c5038SJames Wright 
1827b8c5038SJames Wright       PetscCall(DMLabelGetStratumIS(domain_label, label_value, &domain_is));
1837b8c5038SJames Wright       if (domain_is) {
1847b8c5038SJames Wright         PetscCall(ISIntersect(depth_is, domain_is, &point_is));
1857b8c5038SJames Wright         PetscCall(ISDestroy(&domain_is));
1867b8c5038SJames Wright       } else {
1877b8c5038SJames Wright         point_is = NULL;
1887b8c5038SJames Wright       }
1897b8c5038SJames Wright       PetscCall(ISDestroy(&depth_is));
1907b8c5038SJames Wright     } else {
1917b8c5038SJames Wright       point_is = depth_is;
1927b8c5038SJames Wright     }
1937b8c5038SJames Wright     if (point_is) {
1947b8c5038SJames Wright       PetscCall(ISGetLocalSize(point_is, &num_elem));
1957b8c5038SJames Wright     } else {
1967b8c5038SJames Wright       num_elem = 0;
1977b8c5038SJames Wright     }
1987b8c5038SJames Wright     PetscCall(ISDestroy(&point_is));
1997b8c5038SJames Wright   }
2007b8c5038SJames Wright 
2017b8c5038SJames Wright   {  // Get number of quadrature points
2027b8c5038SJames Wright     PetscDS  ds;
2037b8c5038SJames Wright     PetscFE  fe;
2047b8c5038SJames Wright     PetscInt ds_field = -1;
2057b8c5038SJames Wright 
2067b8c5038SJames Wright     PetscCall(DMGetRegionDS(dm, domain_label, NULL, &ds, NULL));
2077b8c5038SJames Wright     PetscCall(DMFieldToDSField(dm, domain_label, dm_field, &ds_field));
2087b8c5038SJames Wright     PetscCall(PetscDSGetDiscretization(ds, ds_field, (PetscObject *)&fe));
2097b8c5038SJames Wright     PetscCall(PetscFEGetHeightSubspace(fe, height, &fe));
2107b8c5038SJames Wright     if (is_collocated) {
2117b8c5038SJames Wright       PetscDualSpace dual_space;
2127b8c5038SJames Wright       PetscInt       num_dual_basis_vectors, dim, num_comp;
2137b8c5038SJames Wright 
2147b8c5038SJames Wright       PetscCall(PetscFEGetSpatialDimension(fe, &dim));
2157b8c5038SJames Wright       PetscCall(PetscFEGetNumComponents(fe, &num_comp));
2167b8c5038SJames Wright       PetscCall(PetscFEGetDualSpace(fe, &dual_space));
2177b8c5038SJames Wright       PetscCall(PetscDualSpaceGetDimension(dual_space, &num_dual_basis_vectors));
2187b8c5038SJames Wright       num_qpts = num_dual_basis_vectors / num_comp;
2197b8c5038SJames Wright     } else {
2207b8c5038SJames Wright       PetscQuadrature quadrature;
2217b8c5038SJames Wright 
2227b8c5038SJames Wright       PetscCall(DMGetRegionDS(dm, domain_label, NULL, &ds, NULL));
2237b8c5038SJames Wright       PetscCall(DMFieldToDSField(dm, domain_label, dm_field, &ds_field));
2247b8c5038SJames Wright       PetscCall(PetscDSGetDiscretization(ds, ds_field, (PetscObject *)&fe));
2257b8c5038SJames Wright       PetscCall(PetscFEGetHeightSubspace(fe, height, &fe));
2267b8c5038SJames Wright       PetscCall(PetscFEGetQuadrature(fe, &quadrature));
2277b8c5038SJames Wright       PetscCall(PetscQuadratureGetData(quadrature, NULL, NULL, &num_qpts, NULL, NULL));
2287b8c5038SJames Wright     }
2297b8c5038SJames Wright   }
2307b8c5038SJames Wright 
2317b8c5038SJames Wright   // Create the restriction
2327b8c5038SJames Wright   PetscCallCeed(ceed, CeedElemRestrictionCreateStrided(ceed, num_elem, num_qpts, q_data_size, num_elem * num_qpts * q_data_size, CEED_STRIDES_BACKEND,
2337b8c5038SJames Wright                                                        restriction));
2347b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
2357b8c5038SJames Wright }
2367b8c5038SJames Wright 
2377b8c5038SJames Wright /**
2387b8c5038SJames Wright   @brief Create `CeedElemRestriction` from `DMPlex` domain for auxilury `QFunction` data.
2397b8c5038SJames Wright 
2407b8c5038SJames Wright   Not collective across MPI processes.
2417b8c5038SJames Wright 
2427b8c5038SJames Wright   @param[in]   ceed           `Ceed` context
2437b8c5038SJames Wright   @param[in]   dm             `DMPlex` holding mesh
2447b8c5038SJames Wright   @param[in]   domain_label   Label for `DMPlex` domain
2457b8c5038SJames Wright   @param[in]   label_value    Stratum value
2467b8c5038SJames Wright   @param[in]   height         Height of `DMPlex` topology
2477b8c5038SJames Wright   @param[in]   q_data_size    Number of components for `QFunction` data
2487b8c5038SJames Wright   @param[out]  restriction    Strided `CeedElemRestriction` for `QFunction` data
2497b8c5038SJames Wright 
2507b8c5038SJames Wright   @return An error code: 0 - success, otherwise - failure
2517b8c5038SJames Wright **/
2527b8c5038SJames Wright PetscErrorCode DMPlexCeedElemRestrictionQDataCreate(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt height,
2537b8c5038SJames Wright                                                     PetscInt q_data_size, CeedElemRestriction *restriction) {
2547b8c5038SJames Wright   PetscFunctionBeginUser;
2557b8c5038SJames Wright   PetscCall(DMPlexCeedElemRestrictionStridedCreate(ceed, dm, domain_label, label_value, height, q_data_size, PETSC_FALSE, restriction));
2567b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
2577b8c5038SJames Wright }
2587b8c5038SJames Wright 
2597b8c5038SJames Wright /**
2607b8c5038SJames Wright   @brief Create `CeedElemRestriction` from `DMPlex` domain for nodally collocated auxilury `QFunction` data.
2617b8c5038SJames Wright 
2627b8c5038SJames Wright   Not collective across MPI processes.
2637b8c5038SJames Wright 
2647b8c5038SJames Wright   @param[in]   ceed           `Ceed` context
2657b8c5038SJames Wright   @param[in]   dm             `DMPlex` holding mesh
2667b8c5038SJames Wright   @param[in]   domain_label   Label for `DMPlex` domain
2677b8c5038SJames Wright   @param[in]   label_value    Stratum value
2687b8c5038SJames Wright   @param[in]   height         Height of `DMPlex` topology
2697b8c5038SJames Wright   @param[in]   q_data_size    Number of components for `QFunction` data
2707b8c5038SJames Wright   @param[out]  restriction    Strided `CeedElemRestriction` for `QFunction` data
2717b8c5038SJames Wright 
2727b8c5038SJames Wright   @return An error code: 0 - success, otherwise - failure
2737b8c5038SJames Wright **/
2747b8c5038SJames Wright PetscErrorCode DMPlexCeedElemRestrictionCollocatedCreate(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt height,
2757b8c5038SJames Wright                                                          PetscInt q_data_size, CeedElemRestriction *restriction) {
2767b8c5038SJames Wright   PetscFunctionBeginUser;
2777b8c5038SJames Wright   PetscCall(DMPlexCeedElemRestrictionStridedCreate(ceed, dm, domain_label, label_value, height, q_data_size, PETSC_TRUE, restriction));
2787b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
2797b8c5038SJames Wright }
2807b8c5038SJames Wright 
2817b8c5038SJames Wright /**
2827b8c5038SJames Wright   @brief Creates a tensor-product uniform quadrature.
2837b8c5038SJames Wright          This is only for comparison studies and generally should not be used.
2847b8c5038SJames Wright 
2857b8c5038SJames Wright   @param[in]   dim         Spatial dimension
2867b8c5038SJames Wright   @param[in]   num_comp    Number of components
2877b8c5038SJames Wright   @param[in]   num_points  Number of points in one dimension
2887b8c5038SJames Wright   @param[in]   a           Left end of interval (often -1)
2897b8c5038SJames Wright   @param[in]   b           Right end of interval (often +1)
2907b8c5038SJames Wright   @param[out]  q           `PetscQuadrature` object
2917b8c5038SJames Wright 
2927b8c5038SJames Wright   @return An error code: 0 - success, otherwise - failure
2937b8c5038SJames Wright **/
2947b8c5038SJames Wright static PetscErrorCode PetscDTUniformTensorQuadrature(PetscInt dim, PetscInt num_comp, PetscInt num_points, PetscReal a, PetscReal b,
2957b8c5038SJames Wright                                                      PetscQuadrature *q) {
2967b8c5038SJames Wright   DMPolytopeType ct;
2977b8c5038SJames Wright   PetscInt       num_total_points = dim > 1 ? (dim > 2 ? (num_points * num_points * num_points) : (num_points * num_points)) : num_points;
2987b8c5038SJames Wright   PetscReal     *coords, *weights, *coords_1d;
2997b8c5038SJames Wright 
3007b8c5038SJames Wright   PetscFunctionBeginUser;
3017b8c5038SJames Wright   PetscCall(PetscMalloc1(num_total_points * dim, &coords));
3027b8c5038SJames Wright   PetscCall(PetscMalloc1(num_total_points * num_comp, &weights));
3037b8c5038SJames Wright   // Compute weights, points
3047b8c5038SJames Wright   switch (dim) {
3057b8c5038SJames Wright     case 0:
3067b8c5038SJames Wright       ct = DM_POLYTOPE_POINT;
3077b8c5038SJames Wright       PetscCall(PetscFree(coords));
3087b8c5038SJames Wright       PetscCall(PetscFree(weights));
3097b8c5038SJames Wright       PetscCall(PetscMalloc1(1, &coords));
3107b8c5038SJames Wright       PetscCall(PetscMalloc1(num_comp, &weights));
3117b8c5038SJames Wright       num_total_points = 1;
3127b8c5038SJames Wright       coords[0]        = 0.0;
3137b8c5038SJames Wright       for (PetscInt c = 0; c < num_comp; c++) weights[c] = 1.0;
3147b8c5038SJames Wright       break;
3157b8c5038SJames Wright     case 1: {
3167b8c5038SJames Wright       ct             = DM_POLYTOPE_SEGMENT;
3177b8c5038SJames Wright       PetscReal step = (b - a) / num_points;
3187b8c5038SJames Wright 
3197b8c5038SJames Wright       for (PetscInt i = 0; i < num_points; i++) {
3207b8c5038SJames Wright         coords[i] = step * (i + 0.5) + a;
3217b8c5038SJames Wright         for (PetscInt c = 0; c < num_comp; c++) weights[i * num_comp + c] = 1.0;
3227b8c5038SJames Wright       }
3237b8c5038SJames Wright     } break;
3247b8c5038SJames Wright     case 2: {
3257b8c5038SJames Wright       ct = DM_POLYTOPE_QUADRILATERAL;
3267b8c5038SJames Wright       PetscCall(PetscMalloc1(num_points, &coords_1d));
3277b8c5038SJames Wright       PetscReal step = (b - a) / num_points;
3287b8c5038SJames Wright 
3297b8c5038SJames Wright       for (PetscInt i = 0; i < num_points; i++) coords_1d[i] = step * (i + 0.5) + a;
3307b8c5038SJames Wright       for (PetscInt i = 0; i < num_points; i++) {
3317b8c5038SJames Wright         for (PetscInt j = 0; j < num_points; j++) {
3327b8c5038SJames Wright           coords[(i * num_points + j) * dim + 0] = coords_1d[i];
3337b8c5038SJames Wright           coords[(i * num_points + j) * dim + 1] = coords_1d[j];
3347b8c5038SJames Wright           for (PetscInt c = 0; c < num_comp; c++) weights[(i * num_points + j) * num_comp + c] = 1.0;
3357b8c5038SJames Wright         }
3367b8c5038SJames Wright       }
3377b8c5038SJames Wright       PetscCall(PetscFree(coords_1d));
3387b8c5038SJames Wright     } break;
3397b8c5038SJames Wright     case 3: {
3407b8c5038SJames Wright       ct = DM_POLYTOPE_HEXAHEDRON;
3417b8c5038SJames Wright       PetscCall(PetscMalloc1(num_points, &coords_1d));
3427b8c5038SJames Wright       PetscReal step = (b - a) / num_points;
3437b8c5038SJames Wright 
3447b8c5038SJames Wright       for (PetscInt i = 0; i < num_points; i++) coords_1d[i] = step * (i + 0.5) + a;
3457b8c5038SJames Wright       for (PetscInt i = 0; i < num_points; i++) {
3467b8c5038SJames Wright         for (PetscInt j = 0; j < num_points; j++) {
3477b8c5038SJames Wright           for (PetscInt k = 0; k < num_points; k++) {
3487b8c5038SJames Wright             coords[((i * num_points + j) * num_points + k) * dim + 0] = coords_1d[i];
3497b8c5038SJames Wright             coords[((i * num_points + j) * num_points + k) * dim + 1] = coords_1d[j];
3507b8c5038SJames Wright             coords[((i * num_points + j) * num_points + k) * dim + 2] = coords_1d[k];
3517b8c5038SJames Wright             for (PetscInt c = 0; c < num_comp; c++) weights[((i * num_points + j) * num_points + k) * num_comp + c] = 1.0;
3527b8c5038SJames Wright           }
3537b8c5038SJames Wright         }
3547b8c5038SJames Wright       }
3557b8c5038SJames Wright       PetscCall(PetscFree(coords_1d));
3567b8c5038SJames Wright     } break;
3577b8c5038SJames Wright     // LCOV_EXCL_START
3587b8c5038SJames Wright     default:
3597b8c5038SJames Wright       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cannot construct quadrature rule for dimension %" PetscInt_FMT, dim);
3607b8c5038SJames Wright       // LCOV_EXCL_STOP
3617b8c5038SJames Wright   }
3627b8c5038SJames Wright   PetscCall(PetscQuadratureCreate(PETSC_COMM_SELF, q));
3637b8c5038SJames Wright   PetscCall(PetscQuadratureSetCellType(*q, ct));
3647b8c5038SJames Wright   PetscCall(PetscQuadratureSetOrder(*q, num_points));
3657b8c5038SJames Wright   PetscCall(PetscQuadratureSetData(*q, dim, num_comp, num_total_points, coords, weights));
3667b8c5038SJames Wright   PetscCall(PetscObjectChangeTypeName((PetscObject)*q, "UniformTensor"));
3677b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
3687b8c5038SJames Wright }
3697b8c5038SJames Wright 
3707b8c5038SJames Wright /**
3717b8c5038SJames Wright   @brief Setup `DM` with FE space of appropriate degree
3727b8c5038SJames Wright 
3737b8c5038SJames Wright   @param[in]   comm        MPI communicator
3747b8c5038SJames Wright   @param[in]   dim         Spatial dimension
3757b8c5038SJames Wright   @param[in]   num_comp    Number of components
3767b8c5038SJames Wright   @param[in]   is_simplex  Flag for simplex or tensor product element
3777b8c5038SJames Wright   @param[in]   order       Polynomial order of space
3787b8c5038SJames Wright   @param[in]   q_order     Quadrature order
3797b8c5038SJames Wright   @param[in]   prefix      The options prefix, or `NULL`
3807b8c5038SJames Wright   @param[out]  fem         `PetscFE` object
3817b8c5038SJames Wright 
3827b8c5038SJames Wright   @return An error code: 0 - success, otherwise - failure
3837b8c5038SJames Wright **/
3847b8c5038SJames Wright PetscErrorCode PetscFECreateLagrangeFromOptions(MPI_Comm comm, PetscInt dim, PetscInt num_comp, PetscBool is_simplex, PetscInt order,
3857b8c5038SJames Wright                                                 PetscInt q_order, const char prefix[], PetscFE *fem) {
3867b8c5038SJames Wright   PetscBool       is_tensor     = !is_simplex;
3877b8c5038SJames Wright   DMPolytopeType  polytope_type = DMPolytopeTypeSimpleShape(dim, is_simplex);
3887b8c5038SJames Wright   PetscSpace      fe_space;
3897b8c5038SJames Wright   PetscDualSpace  fe_dual_space;
3907b8c5038SJames Wright   PetscQuadrature quadrature, face_quadrature;
3917b8c5038SJames Wright 
3927b8c5038SJames Wright   PetscFunctionBeginUser;
3937b8c5038SJames Wright   // Create space
3947b8c5038SJames Wright   PetscCall(PetscSpaceCreate(comm, &fe_space));
3957b8c5038SJames Wright   PetscCall(PetscSpaceSetType(fe_space, PETSCSPACEPOLYNOMIAL));
3967b8c5038SJames Wright   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)fe_space, prefix));
3977b8c5038SJames Wright   PetscCall(PetscSpacePolynomialSetTensor(fe_space, is_tensor));
3987b8c5038SJames Wright   PetscCall(PetscSpaceSetNumComponents(fe_space, num_comp));
3997b8c5038SJames Wright   PetscCall(PetscSpaceSetNumVariables(fe_space, dim));
4007b8c5038SJames Wright   if (order >= 0) {
4017b8c5038SJames Wright     PetscCall(PetscSpaceSetDegree(fe_space, order, PETSC_DETERMINE));
4027b8c5038SJames Wright     if (polytope_type == DM_POLYTOPE_TRI_PRISM || polytope_type == DM_POLYTOPE_TRI_PRISM_TENSOR) {
4037b8c5038SJames Wright       PetscSpace fe_space_end, fe_space_side;
4047b8c5038SJames Wright 
4057b8c5038SJames Wright       PetscCall(PetscSpaceSetNumComponents(fe_space, 1));
4067b8c5038SJames Wright       PetscCall(PetscSpaceCreate(comm, &fe_space_end));
4077b8c5038SJames Wright       PetscCall(PetscSpaceSetType(fe_space_end, PETSCSPACEPOLYNOMIAL));
4087b8c5038SJames Wright       PetscCall(PetscSpacePolynomialSetTensor(fe_space_end, PETSC_FALSE));
4097b8c5038SJames Wright       PetscCall(PetscSpaceSetNumComponents(fe_space_end, 1));
4107b8c5038SJames Wright       PetscCall(PetscSpaceSetNumVariables(fe_space_end, dim - 1));
4117b8c5038SJames Wright       PetscCall(PetscSpaceSetDegree(fe_space_end, order, PETSC_DETERMINE));
4127b8c5038SJames Wright       PetscCall(PetscSpaceCreate(comm, &fe_space_side));
4137b8c5038SJames Wright       PetscCall(PetscSpaceSetType(fe_space_side, PETSCSPACEPOLYNOMIAL));
4147b8c5038SJames Wright       PetscCall(PetscSpacePolynomialSetTensor(fe_space_side, PETSC_FALSE));
4157b8c5038SJames Wright       PetscCall(PetscSpaceSetNumComponents(fe_space_side, 1));
4167b8c5038SJames Wright       PetscCall(PetscSpaceSetNumVariables(fe_space_side, 1));
4177b8c5038SJames Wright       PetscCall(PetscSpaceSetDegree(fe_space_side, order, PETSC_DETERMINE));
4187b8c5038SJames Wright       PetscCall(PetscSpaceSetType(fe_space, PETSCSPACETENSOR));
4197b8c5038SJames Wright       PetscCall(PetscSpaceTensorSetNumSubspaces(fe_space, 2));
4207b8c5038SJames Wright       PetscCall(PetscSpaceTensorSetSubspace(fe_space, 0, fe_space_end));
4217b8c5038SJames Wright       PetscCall(PetscSpaceTensorSetSubspace(fe_space, 1, fe_space_side));
4227b8c5038SJames Wright       PetscCall(PetscSpaceDestroy(&fe_space_end));
4237b8c5038SJames Wright       PetscCall(PetscSpaceDestroy(&fe_space_side));
4247b8c5038SJames Wright 
4257b8c5038SJames Wright       if (num_comp > 1) {
4267b8c5038SJames Wright         PetscSpace scalar_fe_space = fe_space;
4277b8c5038SJames Wright 
4287b8c5038SJames Wright         PetscCall(PetscSpaceCreate(comm, &fe_space));
4297b8c5038SJames Wright         PetscCall(PetscSpaceSetNumVariables(fe_space, dim));
4307b8c5038SJames Wright         PetscCall(PetscSpaceSetNumComponents(fe_space, num_comp));
4317b8c5038SJames Wright         PetscCall(PetscSpaceSetType(fe_space, PETSCSPACESUM));
4327b8c5038SJames Wright         PetscCall(PetscSpaceSumSetNumSubspaces(fe_space, num_comp));
4337b8c5038SJames Wright         PetscCall(PetscSpaceSumSetConcatenate(fe_space, PETSC_TRUE));
4347b8c5038SJames Wright         PetscCall(PetscSpaceSumSetInterleave(fe_space, PETSC_TRUE, PETSC_FALSE));
4357b8c5038SJames Wright         for (PetscInt i = 0; i < num_comp; i++) PetscCall(PetscSpaceSumSetSubspace(fe_space, i, scalar_fe_space));
4367b8c5038SJames Wright         PetscCall(PetscSpaceDestroy(&scalar_fe_space));
4377b8c5038SJames Wright       }
4387b8c5038SJames Wright     }
4397b8c5038SJames Wright   }
4407b8c5038SJames Wright   PetscCall(PetscSpaceSetFromOptions(fe_space));
4417b8c5038SJames Wright   PetscCall(PetscSpaceSetUp(fe_space));
4427b8c5038SJames Wright   PetscCall(PetscSpaceGetDegree(fe_space, &order, NULL));
4437b8c5038SJames Wright   PetscCall(PetscSpacePolynomialGetTensor(fe_space, &is_tensor));
4447b8c5038SJames Wright   PetscCall(PetscSpaceGetNumComponents(fe_space, &num_comp));
4457b8c5038SJames Wright 
4467b8c5038SJames Wright   // Create dual space
4477b8c5038SJames Wright   PetscCall(PetscDualSpaceCreate(comm, &fe_dual_space));
4487b8c5038SJames Wright   PetscCall(PetscDualSpaceSetType(fe_dual_space, PETSCDUALSPACELAGRANGE));
4497b8c5038SJames Wright   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)fe_dual_space, prefix));
4507b8c5038SJames Wright   {
4517b8c5038SJames Wright     DM dual_space_dm;
4527b8c5038SJames Wright 
4537b8c5038SJames Wright     PetscCall(DMPlexCreateReferenceCell(PETSC_COMM_SELF, polytope_type, &dual_space_dm));
4547b8c5038SJames Wright     PetscCall(PetscDualSpaceSetDM(fe_dual_space, dual_space_dm));
4557b8c5038SJames Wright     PetscCall(DMDestroy(&dual_space_dm));
4567b8c5038SJames Wright   }
4577b8c5038SJames Wright   PetscCall(PetscDualSpaceSetNumComponents(fe_dual_space, num_comp));
4587b8c5038SJames Wright   PetscCall(PetscDualSpaceSetOrder(fe_dual_space, order));
4597b8c5038SJames Wright   PetscCall(PetscDualSpaceLagrangeSetTensor(fe_dual_space, (is_tensor || (polytope_type == DM_POLYTOPE_TRI_PRISM)) ? PETSC_TRUE : PETSC_FALSE));
4607b8c5038SJames Wright   PetscCall(PetscDualSpaceSetFromOptions(fe_dual_space));
4617b8c5038SJames Wright   PetscCall(PetscDualSpaceSetUp(fe_dual_space));
4627b8c5038SJames Wright 
4637b8c5038SJames Wright   // Create quadrature
4647b8c5038SJames Wright   q_order               = q_order >= 0 ? q_order : order;
4657b8c5038SJames Wright   PetscBool use_uniform = PETSC_FALSE;
4667b8c5038SJames Wright 
4677b8c5038SJames Wright   PetscOptionsBegin(comm, NULL, "Uniform quadrature check", NULL);
4687b8c5038SJames Wright   PetscCall(PetscOptionsBool("-petscdt_uniform_tensor_quadrature", "", NULL, use_uniform, &use_uniform, NULL));
4697b8c5038SJames Wright   PetscOptionsEnd();
4707b8c5038SJames Wright   PetscCheck(!use_uniform || (is_tensor || dim == 1), comm, PETSC_ERR_SUP, "Can only use uniform quadrature on tensor product elements");
4717b8c5038SJames Wright   if (use_uniform) {
4727b8c5038SJames Wright     PetscCall(PetscDTUniformTensorQuadrature(dim, 1, q_order + 1, -1.0, 1.0, &quadrature));
4737b8c5038SJames Wright     PetscCall(PetscDTUniformTensorQuadrature(dim - 1, 1, q_order + 1, -1.0, 1.0, &face_quadrature));
4747b8c5038SJames Wright   } else {
4757b8c5038SJames Wright     PetscCall(PetscDTCreateDefaultQuadrature(polytope_type, q_order, &quadrature, &face_quadrature));
4767b8c5038SJames Wright   }
4777b8c5038SJames Wright 
4787b8c5038SJames Wright   // Create finite element
4797b8c5038SJames Wright   PetscCall(PetscFECreateFromSpaces(fe_space, fe_dual_space, quadrature, face_quadrature, fem));
4807b8c5038SJames Wright   PetscCall(PetscFESetFromOptions(*fem));
4817b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
4827b8c5038SJames Wright }
4837b8c5038SJames Wright 
4847b8c5038SJames Wright /**
4857b8c5038SJames Wright   @brief Get global `DMPlex` topology type.
4867b8c5038SJames Wright 
4877b8c5038SJames Wright   Collective across MPI processes.
4887b8c5038SJames Wright 
4897b8c5038SJames Wright   @param[in]   dm            `DMPlex` holding mesh
4907b8c5038SJames Wright   @param[in]   domain_label  `DMLabel` for `DMPlex` domain
4917b8c5038SJames Wright   @param[in]   label_value   Stratum value
4927b8c5038SJames Wright   @param[in]   height        Height of `DMPlex` topology
4937b8c5038SJames Wright   @param[out]  cell_type     `DMPlex` topology type
4947b8c5038SJames Wright **/
4957b8c5038SJames Wright static inline PetscErrorCode GetGlobalDMPlexPolytopeType(DM dm, DMLabel domain_label, PetscInt label_value, PetscInt height,
4967b8c5038SJames Wright                                                          DMPolytopeType *cell_type) {
4977b8c5038SJames Wright   PetscInt first_point;
4987b8c5038SJames Wright   PetscInt ids[1] = {label_value};
4997b8c5038SJames Wright   DMLabel  depth_label;
5007b8c5038SJames Wright 
5017b8c5038SJames Wright   PetscFunctionBeginUser;
5027b8c5038SJames Wright   // Use depth label if no domain label present
5037b8c5038SJames Wright   if (!domain_label) {
5047b8c5038SJames Wright     PetscInt depth;
5057b8c5038SJames Wright 
5067b8c5038SJames Wright     PetscCall(DMPlexGetDepth(dm, &depth));
5077b8c5038SJames Wright     PetscCall(DMPlexGetDepthLabel(dm, &depth_label));
5087b8c5038SJames Wright     ids[0] = depth - height;
5097b8c5038SJames Wright   }
5107b8c5038SJames Wright 
5117b8c5038SJames Wright   // Get cell interp, grad, and quadrature data
5127b8c5038SJames Wright   PetscCall(DMGetFirstLabeledPoint(dm, dm, domain_label ? domain_label : depth_label, 1, ids, height, &first_point, NULL));
5137b8c5038SJames Wright   if (first_point != -1) PetscCall(DMPlexGetCellType(dm, first_point, cell_type));
5147b8c5038SJames Wright 
5157b8c5038SJames Wright   {  // Form agreement about CellType
5167b8c5038SJames Wright     PetscInt cell_type_local = -1, cell_type_global = -1;
5177b8c5038SJames Wright 
5187b8c5038SJames Wright     if (first_point != -1) cell_type_local = (PetscInt)*cell_type;
5197b8c5038SJames Wright     PetscCallMPI(MPIU_Allreduce(&cell_type_local, &cell_type_global, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
5207b8c5038SJames Wright     *cell_type = (DMPolytopeType)cell_type_global;
5217b8c5038SJames Wright   }
5227b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
5237b8c5038SJames Wright }
5247b8c5038SJames Wright 
5257b8c5038SJames Wright /**
5267b8c5038SJames Wright   @brief Get the permutation and field offset for a given depth.
5277b8c5038SJames Wright 
5287b8c5038SJames Wright   Not collective across MPI processes.
5297b8c5038SJames Wright 
5307b8c5038SJames Wright   @param[in]   dm            `DMPlex` holding mesh
5317b8c5038SJames Wright   @param[in]   depth         Depth of `DMPlex` topology
5327b8c5038SJames Wright   @param[in]   field         Index of `DMPlex` field
5337b8c5038SJames Wright   @param[out]  permutation   Permutation for `DMPlex` field
5347b8c5038SJames Wright   @param[out]  field_offset  Offset to `DMPlex` field
5357b8c5038SJames Wright **/
5367b8c5038SJames Wright static inline PetscErrorCode GetClosurePermutationAndFieldOffsetAtDepth(DM dm, PetscInt depth, PetscInt field, IS *permutation,
5377b8c5038SJames Wright                                                                         PetscInt *field_offset) {
5387b8c5038SJames Wright   PetscBool    is_field_continuous = PETSC_TRUE;
5397b8c5038SJames Wright   PetscInt     dim, num_fields, offset = 0, size = 0;
5407b8c5038SJames Wright   PetscSection section;
5417b8c5038SJames Wright 
5427b8c5038SJames Wright   PetscFunctionBeginUser;
5437b8c5038SJames Wright   *field_offset = 0;
5447b8c5038SJames Wright 
5457b8c5038SJames Wright   PetscCall(DMGetDimension(dm, &dim));
5467b8c5038SJames Wright   PetscCall(DMGetLocalSection(dm, &section));
5477b8c5038SJames Wright   PetscCall(PetscSectionGetNumFields(section, &num_fields));
5487b8c5038SJames Wright   // ---- Permutation size and offset to current field
5497b8c5038SJames Wright   for (PetscInt f = 0; f < num_fields; f++) {
5507b8c5038SJames Wright     PetscBool      is_continuous;
5517b8c5038SJames Wright     PetscInt       num_components, num_dof_1d, dual_space_size, field_size;
5527b8c5038SJames Wright     PetscObject    obj;
5537b8c5038SJames Wright     PetscFE        fe = NULL;
5547b8c5038SJames Wright     PetscDualSpace dual_space;
5557b8c5038SJames Wright 
5567b8c5038SJames Wright     PetscCall(PetscSectionGetFieldComponents(section, f, &num_components));
5577b8c5038SJames Wright     PetscCall(DMGetField(dm, f, NULL, &obj));
5587b8c5038SJames Wright     fe = (PetscFE)obj;
5597b8c5038SJames Wright     PetscCall(PetscFEGetDualSpace(fe, &dual_space));
5607b8c5038SJames Wright     PetscCall(PetscDualSpaceLagrangeGetContinuity(dual_space, &is_continuous));
5617b8c5038SJames Wright     if (f == field) is_field_continuous = is_continuous;
5627b8c5038SJames Wright     if (!is_continuous) continue;
5637b8c5038SJames Wright     PetscCall(PetscDualSpaceGetDimension(dual_space, &dual_space_size));
5647b8c5038SJames Wright     num_dof_1d = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / num_components, 1.0 / dim));
5657b8c5038SJames Wright     field_size = PetscPowInt(num_dof_1d, depth) * num_components;
5667b8c5038SJames Wright     if (f < field) offset += field_size;
5677b8c5038SJames Wright     size += field_size;
5687b8c5038SJames Wright   }
5697b8c5038SJames Wright 
5707b8c5038SJames Wright   if (is_field_continuous) {
5717b8c5038SJames Wright     *field_offset = offset;
5727b8c5038SJames Wright     PetscCall(PetscSectionGetClosurePermutation(section, (PetscObject)dm, depth, size, permutation));
5737b8c5038SJames Wright   }
5747b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
5757b8c5038SJames Wright }
5767b8c5038SJames Wright 
5777b8c5038SJames Wright /**
5787b8c5038SJames Wright   @brief Compute field tabulation from `PetscTabulation`.
5797b8c5038SJames Wright 
5807b8c5038SJames Wright   Not collective across MPI processes.
5817b8c5038SJames Wright 
5827b8c5038SJames Wright   @param[in]   dm          `DMPlex` holding mesh
5837b8c5038SJames Wright   @param[in]   field       Index of `DMPlex` field
5847b8c5038SJames Wright   @param[in]   face        Index of basis face, or 0
5857b8c5038SJames Wright   @param[in]   tabulation  PETSc basis tabulation
5867b8c5038SJames Wright   @param[out]  interp      Interpolation matrix in libCEED orientation
5877b8c5038SJames Wright   @param[out]  grad        Gradient matrix in libCEED orientation
5887b8c5038SJames Wright 
5897b8c5038SJames Wright   @note `interp` and `grad` are allocated using `PetscCalloc1` and must be freed by the user.
5907b8c5038SJames Wright **/
5917b8c5038SJames Wright static inline PetscErrorCode ComputeFieldTabulationP2C(DM dm, PetscInt field, PetscInt face, PetscTabulation tabulation, CeedScalar **interp,
5927b8c5038SJames Wright                                                        CeedScalar **grad) {
5937b8c5038SJames Wright   PetscBool       is_simplex, has_permutation = PETSC_FALSE;
5947b8c5038SJames Wright   PetscInt        field_offset = 0, num_comp, P, Q, dim;
5957b8c5038SJames Wright   const PetscInt *permutation_indices;
5967b8c5038SJames Wright   IS              permutation = NULL;
5977b8c5038SJames Wright 
5987b8c5038SJames Wright   PetscFunctionBeginUser;
5997b8c5038SJames Wright   num_comp = tabulation->Nc;
6007b8c5038SJames Wright   P        = tabulation->Nb / num_comp;
6017b8c5038SJames Wright   Q        = tabulation->Np;
6027b8c5038SJames Wright   dim      = tabulation->cdim;
6037b8c5038SJames Wright 
6047b8c5038SJames Wright   PetscCall(PetscCalloc1(P * Q, interp));
6057b8c5038SJames Wright   PetscCall(PetscCalloc1(P * Q * dim, grad));
6067b8c5038SJames Wright 
6077b8c5038SJames Wright   PetscCall(DMPlexIsSimplex(dm, &is_simplex));
6087b8c5038SJames Wright   if (!is_simplex) {
6097b8c5038SJames Wright     PetscCall(GetClosurePermutationAndFieldOffsetAtDepth(dm, dim, field, &permutation, &field_offset));
6107b8c5038SJames Wright     has_permutation = !!permutation;
6117b8c5038SJames Wright     if (has_permutation) PetscCall(ISGetIndices(permutation, &permutation_indices));
6127b8c5038SJames Wright   }
6137b8c5038SJames Wright 
6147b8c5038SJames Wright   const CeedInt c = 0;
6157b8c5038SJames Wright   for (CeedInt q = 0; q < Q; q++) {
6167b8c5038SJames Wright     for (CeedInt p_ceed = 0; p_ceed < P; p_ceed++) {
6177b8c5038SJames Wright       CeedInt p_petsc           = has_permutation ? (permutation_indices[field_offset + p_ceed * num_comp] - field_offset) : (p_ceed * num_comp);
6187b8c5038SJames Wright       (*interp)[q * P + p_ceed] = tabulation->T[0][((face * Q + q) * P * num_comp + p_petsc) * num_comp + c];
6197b8c5038SJames Wright       for (CeedInt d = 0; d < dim; d++) {
6207b8c5038SJames Wright         (*grad)[(d * Q + q) * P + p_ceed] = tabulation->T[1][(((face * Q + q) * P * num_comp + p_petsc) * num_comp + c) * dim + d];
6217b8c5038SJames Wright       }
6227b8c5038SJames Wright     }
6237b8c5038SJames Wright   }
6247b8c5038SJames Wright 
6257b8c5038SJames Wright   if (has_permutation) PetscCall(ISRestoreIndices(permutation, &permutation_indices));
6267b8c5038SJames Wright   PetscCall(ISDestroy(&permutation));
6277b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
6287b8c5038SJames Wright }
6297b8c5038SJames Wright 
6307b8c5038SJames Wright /**
6317b8c5038SJames Wright   @brief Get quadrature data from `PetscQuadrature` for use with libCEED.
6327b8c5038SJames Wright 
6337b8c5038SJames Wright   Not collective across MPI processes.
6347b8c5038SJames Wright 
6357b8c5038SJames Wright   @param[in]   fe          `PetscFE` object
6367b8c5038SJames Wright   @param[in]   quadrature  PETSc basis quadrature
6377b8c5038SJames Wright   @param[out]  q_dim       Quadrature dimension, or NULL if not needed
6387b8c5038SJames Wright   @param[out]  num_comp    Number of components, or NULL if not needed
6397b8c5038SJames Wright   @param[out]  Q           Number of quadrature points, or NULL if not needed
6407b8c5038SJames Wright   @param[out]  q_points    Quadrature points in libCEED orientation, or NULL if not needed
6417b8c5038SJames Wright   @param[out]  q_weights   Quadrature weights in libCEED orientation, or NULL if not needed
6427b8c5038SJames Wright 
6437b8c5038SJames Wright   @note `q_weights` and `q_points` are allocated using `PetscCalloc1` and must be freed by the user.
6447b8c5038SJames Wright **/
6457b8c5038SJames Wright static inline PetscErrorCode GetQuadratureDataP2C(PetscFE fe, PetscQuadrature quadrature, PetscInt *q_dim, PetscInt *num_comp, PetscInt *Q,
6467b8c5038SJames Wright                                                   CeedScalar **q_points, CeedScalar **q_weights) {
6477b8c5038SJames Wright   PetscInt         spatial_dim, dim, num_comp_quadrature, num_q_points;
6487b8c5038SJames Wright   const PetscReal *q_points_petsc, *q_weights_petsc;
6497b8c5038SJames Wright 
6507b8c5038SJames Wright   PetscFunctionBeginUser;
6517b8c5038SJames Wright   PetscCall(PetscFEGetSpatialDimension(fe, &spatial_dim));
6527b8c5038SJames Wright   PetscCall(PetscQuadratureGetData(quadrature, &dim, &num_comp_quadrature, &num_q_points, &q_points_petsc, &q_weights_petsc));
6537b8c5038SJames Wright   if (q_dim) *q_dim = dim;
6547b8c5038SJames Wright   if (num_comp) *num_comp = num_comp_quadrature;
6557b8c5038SJames Wright   if (Q) *Q = num_q_points;
6567b8c5038SJames Wright   if (q_weights) {
6577b8c5038SJames Wright     PetscSizeT q_weights_size = num_q_points;
6587b8c5038SJames Wright 
6597b8c5038SJames Wright     PetscCall(PetscCalloc1(q_weights_size, q_weights));
6607b8c5038SJames Wright     for (CeedInt i = 0; i < num_q_points; i++) (*q_weights)[i] = q_weights_petsc[i];
6617b8c5038SJames Wright   }
6627b8c5038SJames Wright   if (q_points) {
6637b8c5038SJames Wright     PetscSizeT q_points_size = num_q_points * spatial_dim;
6647b8c5038SJames Wright 
6657b8c5038SJames Wright     PetscCall(PetscCalloc1(q_points_size, q_points));
6667b8c5038SJames Wright     for (CeedInt i = 0; i < num_q_points; i++) {
6677b8c5038SJames Wright       for (CeedInt d = 0; d < dim; d++) (*q_points)[i * spatial_dim + d] = q_points_petsc[i * dim + d];
6687b8c5038SJames Wright     }
6697b8c5038SJames Wright   }
6707b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
6717b8c5038SJames Wright }
6727b8c5038SJames Wright 
6737b8c5038SJames Wright /**
6747b8c5038SJames Wright   @brief Create 1D tabulation from `PetscFE`.
6757b8c5038SJames Wright 
6767b8c5038SJames Wright   Collective across MPI processes.
6777b8c5038SJames Wright 
6787b8c5038SJames Wright   @note `q_weights` and `q_points` are allocated using `PetscCalloc1` and must be freed by the user.
6797b8c5038SJames Wright 
6807b8c5038SJames Wright   @param[in]   comm          `MPI_Comm` for creating `FE` object from options
6817b8c5038SJames Wright   @param[in]   fe            PETSc basis `FE` object
6827b8c5038SJames Wright   @param[out]  tabulation    PETSc basis tabulation
6837b8c5038SJames Wright   @param[out]  q_points_1d   Quadrature points in libCEED orientation
6847b8c5038SJames Wright   @param[out]  q_weights_1d  Quadrature weights in libCEED orientation
6857b8c5038SJames Wright 
6867b8c5038SJames Wright   @return An error code: 0 - success, otherwise - failure
6877b8c5038SJames Wright **/
6887b8c5038SJames Wright static inline PetscErrorCode Create1DTabulation_Tensor(MPI_Comm comm, PetscFE fe, PetscTabulation *tabulation, PetscReal **q_points_1d,
6897b8c5038SJames Wright                                                        CeedScalar **q_weights_1d) {
6907b8c5038SJames Wright   PetscFE         fe_1d;
6917b8c5038SJames Wright   PetscInt        dim, num_comp, Q = -1, q_dim = -1, num_derivatives = 1;
6927b8c5038SJames Wright   PetscQuadrature quadrature;
6937b8c5038SJames Wright 
6947b8c5038SJames Wright   PetscFunctionBeginUser;
6957b8c5038SJames Wright   // Create 1D FE
6967b8c5038SJames Wright   PetscCall(PetscFEGetNumComponents(fe, &num_comp));
6977b8c5038SJames Wright   PetscCall(PetscFEGetSpatialDimension(fe, &dim));
6987b8c5038SJames Wright   {
6997b8c5038SJames Wright     const char     *prefix;
7007b8c5038SJames Wright     PetscBool       is_tensor;
7017b8c5038SJames Wright     PetscInt        num_comp, order, q_order;
7027b8c5038SJames Wright     PetscDualSpace  dual_space;
7037b8c5038SJames Wright     PetscQuadrature quadrature;
7047b8c5038SJames Wright 
7057b8c5038SJames Wright     PetscCall(PetscObjectGetOptionsPrefix((PetscObject)fe, &prefix));
7067b8c5038SJames Wright     PetscCall(PetscFEGetDualSpace(fe, &dual_space));
7077b8c5038SJames Wright     PetscCall(PetscFEGetNumComponents(fe, &num_comp));
7087b8c5038SJames Wright     PetscCall(PetscDualSpaceLagrangeGetTensor(dual_space, &is_tensor));
7097b8c5038SJames Wright     PetscCall(PetscDualSpaceGetOrder(dual_space, &order));
7107b8c5038SJames Wright     PetscCall(PetscFEGetQuadrature(fe, &quadrature));
7117b8c5038SJames Wright     PetscCall(PetscQuadratureGetData(quadrature, NULL, NULL, &q_order, NULL, NULL));
7127b8c5038SJames Wright     PetscCall(PetscFECreateLagrangeFromOptions(comm, 1, num_comp, !is_tensor, order,
7137b8c5038SJames Wright                                                (PetscInt)PetscCeilReal(PetscPowReal(1.0 * q_order, 1.0 / dim)) - 1, prefix, &fe_1d));
7147b8c5038SJames Wright   }
7157b8c5038SJames Wright 
7167b8c5038SJames Wright   // Get quadrature data
7177b8c5038SJames Wright   PetscCall(PetscFEGetQuadrature(fe_1d, &quadrature));
7187b8c5038SJames Wright   PetscCall(GetQuadratureDataP2C(fe_1d, quadrature, &q_dim, NULL, &Q, q_points_1d, q_weights_1d));
7197b8c5038SJames Wright 
7207b8c5038SJames Wright   // Compute 1D tabulation
7217b8c5038SJames Wright   PetscCall(PetscFECreateTabulation(fe_1d, 1, Q, *q_points_1d, num_derivatives, tabulation));
7227b8c5038SJames Wright 
7237b8c5038SJames Wright   // Cleanup
7247b8c5038SJames Wright   PetscCall(PetscFEDestroy(&fe_1d));
7257b8c5038SJames Wright 
7267b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
7277b8c5038SJames Wright }
7287b8c5038SJames Wright 
7297b8c5038SJames Wright /**
7307b8c5038SJames Wright   @brief Destroy `CeedBasis` in a `PetscContainer`.
7317b8c5038SJames Wright 
7327b8c5038SJames Wright   Not collective across MPI processes.
7337b8c5038SJames Wright 
7347b8c5038SJames Wright   @param[in,out]  ctx  `CeedBasis`
7357b8c5038SJames Wright 
7367b8c5038SJames Wright   @return An error code: 0 - success, otherwise - failure
7377b8c5038SJames Wright **/
7387b8c5038SJames Wright static PetscErrorCode DMPlexCeedBasisDestroy(void **ctx) {
7397b8c5038SJames Wright   CeedBasis basis = *(CeedBasis *)ctx;
7407b8c5038SJames Wright 
7417b8c5038SJames Wright   PetscFunctionBegin;
7427b8c5038SJames Wright   PetscCallCeed(CeedBasisReturnCeed(basis), CeedBasisDestroy(&basis));
7437b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
7447b8c5038SJames Wright }
7457b8c5038SJames Wright 
7467b8c5038SJames Wright /**
7477b8c5038SJames Wright   @brief Create `CeedBasis` from `DMPlex`.
7487b8c5038SJames Wright 
7497b8c5038SJames Wright  The `CeedBasis` is stored in the `DMPlex` object for reuse;
7507b8c5038SJames Wright  if the routine is called again with the same arguments, the same `CeedBasis` is returned.
7517b8c5038SJames Wright 
7527b8c5038SJames Wright   Collective across MPI processes.
7537b8c5038SJames Wright 
7547b8c5038SJames Wright   @param[in]   ceed          `Ceed` context
7557b8c5038SJames Wright   @param[in]   dm            `DMPlex` holding mesh
7567b8c5038SJames Wright   @param[in]   domain_label  `DMLabel` for `DMPlex` domain
7577b8c5038SJames Wright   @param[in]   label_value   Stratum value
7587b8c5038SJames Wright   @param[in]   height        Height of `DMPlex` topology
7597b8c5038SJames Wright   @param[in]   dm_field      Index of `DMPlex` field
7607b8c5038SJames Wright   @param[out]  basis         `CeedBasis` for `DMPlex`
7617b8c5038SJames Wright 
7627b8c5038SJames Wright   @return An error code: 0 - success, otherwise - failure
7637b8c5038SJames Wright   **/
7647b8c5038SJames Wright PetscErrorCode DMPlexCeedBasisCreate(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt height, PetscInt dm_field,
7657b8c5038SJames Wright                                      CeedBasis *basis) {
7667b8c5038SJames Wright   MPI_Comm  comm = PetscObjectComm((PetscObject)dm);
7677b8c5038SJames Wright   char      container_name[1024];
7687b8c5038SJames Wright   CeedBasis container_basis;
7697b8c5038SJames Wright 
7707b8c5038SJames Wright   PetscFunctionBeginUser;
7717b8c5038SJames Wright   {
7727b8c5038SJames Wright     const char *label_name = NULL;
7737b8c5038SJames Wright 
7747b8c5038SJames Wright     if (domain_label) PetscCall(PetscObjectGetName((PetscObject)domain_label, &label_name));
7757b8c5038SJames Wright     PetscCall(PetscSNPrintf(container_name, sizeof(container_name),
7767b8c5038SJames Wright                             "DM CeedBasis - label: %s label value: %" PetscInt_FMT " height: %" PetscInt_FMT " DM field: %" PetscInt_FMT,
7777b8c5038SJames Wright                             label_name ? label_name : "NONE", label_value, height, dm_field));
7787b8c5038SJames Wright   }
7797b8c5038SJames Wright   PetscCall(PetscObjectContainerQuery((PetscObject)dm, container_name, (void **)&container_basis));
7807b8c5038SJames Wright 
7817b8c5038SJames Wright   if (container_basis) {
7827b8c5038SJames Wright     // Copy existing basis
7837b8c5038SJames Wright     *basis = NULL;
7847b8c5038SJames Wright     PetscCallCeed(ceed, CeedBasisReferenceCopy(container_basis, basis));
7857b8c5038SJames Wright   } else {
7867b8c5038SJames Wright     PetscDS         ds;
7877b8c5038SJames Wright     PetscFE         fe;
7887b8c5038SJames Wright     PetscDualSpace  dual_space;
7897b8c5038SJames Wright     PetscQuadrature quadrature;
7907b8c5038SJames Wright     PetscBool       is_tensor = PETSC_TRUE;
7917b8c5038SJames Wright     PetscInt        ds_field  = -1;
7927b8c5038SJames Wright 
7937b8c5038SJames Wright     // Get element information
7947b8c5038SJames Wright     PetscCall(DMGetRegionDS(dm, domain_label, NULL, &ds, NULL));
7957b8c5038SJames Wright     PetscCall(DMFieldToDSField(dm, domain_label, dm_field, &ds_field));
7967b8c5038SJames Wright     PetscCall(PetscDSGetDiscretization(ds, ds_field, (PetscObject *)&fe));
7977b8c5038SJames Wright     PetscCall(PetscFEGetHeightSubspace(fe, height, &fe));
7987b8c5038SJames Wright     PetscCall(PetscFEGetQuadrature(fe, &quadrature));
7997b8c5038SJames Wright     PetscCall(PetscFEGetDualSpace(fe, &dual_space));
8007b8c5038SJames Wright     PetscCall(PetscDualSpaceLagrangeGetTensor(dual_space, &is_tensor));
8017b8c5038SJames Wright 
8027b8c5038SJames Wright     // Build libCEED basis
8037b8c5038SJames Wright     PetscBool use_nontensor = PETSC_FALSE;
8047b8c5038SJames Wright 
8057b8c5038SJames Wright     PetscOptionsBegin(comm, NULL, "Tensor Basis as Nontensor Check", NULL);
8067b8c5038SJames Wright     PetscCall(PetscOptionsBool("-ceed_basis_as_nontensor", "", NULL, use_nontensor, &use_nontensor, NULL));
8077b8c5038SJames Wright     PetscOptionsEnd();
8087b8c5038SJames Wright 
8097b8c5038SJames Wright     if (!is_tensor || use_nontensor) {
8107b8c5038SJames Wright       PetscTabulation  basis_tabulation;
8117b8c5038SJames Wright       PetscInt         num_derivatives = 1, face = 0;
8127b8c5038SJames Wright       CeedScalar      *q_points, *q_weights, *interp, *grad;
8137b8c5038SJames Wright       CeedElemTopology elem_topo;
8147b8c5038SJames Wright 
8157b8c5038SJames Wright       {
8167b8c5038SJames Wright         DMPolytopeType cell_type;
8177b8c5038SJames Wright 
8187b8c5038SJames Wright         PetscCall(GetGlobalDMPlexPolytopeType(dm, domain_label, label_value, height, &cell_type));
8197b8c5038SJames Wright         elem_topo = PolytopeTypePetscToCeed(cell_type);
8207b8c5038SJames Wright         PetscCheck(elem_topo, comm, PETSC_ERR_SUP, "DMPlex topology not supported: %s", DMPolytopeTypes[cell_type]);
8217b8c5038SJames Wright       }
8227b8c5038SJames Wright 
8237b8c5038SJames Wright       // Compute basis tabulation
8247b8c5038SJames Wright       PetscCall(GetQuadratureDataP2C(fe, quadrature, NULL, NULL, NULL, &q_points, &q_weights));
8257b8c5038SJames Wright       PetscCall(PetscFEGetCellTabulation(fe, num_derivatives, &basis_tabulation));
8267b8c5038SJames Wright       PetscCall(ComputeFieldTabulationP2C(dm, dm_field, face, basis_tabulation, &interp, &grad));
8277b8c5038SJames Wright       {
8287b8c5038SJames Wright         PetscInt num_comp = basis_tabulation->Nc, P = basis_tabulation->Nb / num_comp, Q = basis_tabulation->Np;
8297b8c5038SJames Wright 
8307b8c5038SJames Wright         PetscCallCeed(ceed, CeedBasisCreateH1(ceed, elem_topo, num_comp, P, Q, interp, grad, q_points, q_weights, basis));
8317b8c5038SJames Wright       }
8327b8c5038SJames Wright 
8337b8c5038SJames Wright       PetscCall(PetscFree(q_points));
8347b8c5038SJames Wright       PetscCall(PetscFree(q_weights));
8357b8c5038SJames Wright       PetscCall(PetscFree(interp));
8367b8c5038SJames Wright       PetscCall(PetscFree(grad));
8377b8c5038SJames Wright     } else {
8387b8c5038SJames Wright       PetscInt        P_1d, Q_1d, num_comp, dim;
8397b8c5038SJames Wright       PetscTabulation basis_tabulation;
8407b8c5038SJames Wright       CeedScalar     *q_points_1d, *q_weights_1d, *interp_1d, *grad_1d;
8417b8c5038SJames Wright 
8427b8c5038SJames Wright       PetscCall(PetscFEGetSpatialDimension(fe, &dim));
8437b8c5038SJames Wright       PetscCall(Create1DTabulation_Tensor(comm, fe, &basis_tabulation, &q_points_1d, &q_weights_1d));
8447b8c5038SJames Wright       num_comp = basis_tabulation->Nc;
8457b8c5038SJames Wright       P_1d     = basis_tabulation->Nb / num_comp;
8467b8c5038SJames Wright       Q_1d     = basis_tabulation->Np;
8477b8c5038SJames Wright       PetscCall(ComputeFieldTabulationP2C(dm, dm_field, 0, basis_tabulation, &interp_1d, &grad_1d));
8487b8c5038SJames Wright       PetscCallCeed(ceed, CeedBasisCreateTensorH1(ceed, dim, num_comp, P_1d, Q_1d, interp_1d, grad_1d, q_points_1d, q_weights_1d, basis));
8497b8c5038SJames Wright 
8507b8c5038SJames Wright       // Cleanup
8517b8c5038SJames Wright       PetscCall(PetscFree(q_points_1d));
8527b8c5038SJames Wright       PetscCall(PetscFree(q_weights_1d));
8537b8c5038SJames Wright       PetscCall(PetscFree(interp_1d));
8547b8c5038SJames Wright       PetscCall(PetscFree(grad_1d));
8557b8c5038SJames Wright       PetscCall(PetscTabulationDestroy(&basis_tabulation));
8567b8c5038SJames Wright     }
8577b8c5038SJames Wright     // Set in container
8587b8c5038SJames Wright     PetscCallCeed(ceed, CeedBasisReferenceCopy(*basis, &container_basis));
859*26d401f3SJames Wright     PetscCall(PetscObjectContainerCompose((PetscObject)dm, container_name, container_basis, (PetscCtxDestroyFn *)DMPlexCeedBasisDestroy));
8607b8c5038SJames Wright   }
8617b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
8627b8c5038SJames Wright }
8637b8c5038SJames Wright 
8647b8c5038SJames Wright /**
8657b8c5038SJames Wright   @brief Create `CeedBasis` for coordinate space of `DMPlex`.
8667b8c5038SJames Wright 
8677b8c5038SJames Wright  The `CeedBasis` is stored in the coordinate `DMPlex` object for reuse;
8687b8c5038SJames Wright  if the routine is called again with the same arguments, the same `CeedBasis` is returned.
8697b8c5038SJames Wright 
8707b8c5038SJames Wright   Collective across MPI processes.
8717b8c5038SJames Wright 
8727b8c5038SJames Wright   @param[in]   ceed          `Ceed` context
8737b8c5038SJames Wright   @param[in]   dm            `DMPlex` holding mesh
8747b8c5038SJames Wright   @param[in]   domain_label  `DMLabel` for `DMPlex` domain
8757b8c5038SJames Wright   @param[in]   label_value   Stratum value
8767b8c5038SJames Wright   @param[in]   height        Height of `DMPlex` topology
8777b8c5038SJames Wright   @param[out]  basis         `CeedBasis` for coordinate space of `DMPlex`
8787b8c5038SJames Wright **/
8797b8c5038SJames Wright PetscErrorCode DMPlexCeedBasisCoordinateCreate(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt height, CeedBasis *basis) {
8807b8c5038SJames Wright   DM dm_coord = NULL;
8817b8c5038SJames Wright 
8827b8c5038SJames Wright   PetscFunctionBeginUser;
88377efe802SJames Wright   PetscCall(DMGetCoordinateDM(dm, &dm_coord));
8847b8c5038SJames Wright   PetscCall(DMPlexCeedBasisCreate(ceed, dm_coord, domain_label, label_value, height, 0, basis));
8857b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
8867b8c5038SJames Wright }
8877b8c5038SJames Wright 
88877efe802SJames Wright typedef struct CeedVectorAndState_ *CeedVectorAndState;
88977efe802SJames Wright struct CeedVectorAndState_ {
89077efe802SJames Wright   CeedVector       vector;  // !< `CeedVector` containing copy of data from a `Vec`
89177efe802SJames Wright   PetscObjectState state;   // !< Object state of the `Vec` when the data was copied
89277efe802SJames Wright };
89377efe802SJames Wright 
89477efe802SJames Wright /**
89577efe802SJames Wright   @brief Destroy `CeedVectorAndState` in a `PetscContainer`.
89677efe802SJames Wright 
89777efe802SJames Wright   Not collective across MPI processes.
89877efe802SJames Wright 
89977efe802SJames Wright   @param[in,out]  ctx  `CeedVectorAndState`
90077efe802SJames Wright 
90177efe802SJames Wright   @return An error code: 0 - success, otherwise - failure
90277efe802SJames Wright **/
90377efe802SJames Wright static PetscErrorCode CeedVectorAndStateDestroy(void **ctx) {
90477efe802SJames Wright   CeedVectorAndState vec_and_state = *(CeedVectorAndState *)ctx;
90577efe802SJames Wright 
90677efe802SJames Wright   PetscFunctionBegin;
90777efe802SJames Wright   PetscCallCeed(CeedVectorReturnCeed(vec_and_state->vector), CeedVectorDestroy(&vec_and_state->vector));
90877efe802SJames Wright   PetscCall(PetscFree(vec_and_state));
90977efe802SJames Wright   *ctx = NULL;
91077efe802SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
91177efe802SJames Wright }
91277efe802SJames Wright 
91377efe802SJames Wright /**
91477efe802SJames Wright   @brief Get Ceed objects for coordinate field of DM
91577efe802SJames Wright 
91677efe802SJames Wright   @param[in]  ceed         `Ceed` context
91777efe802SJames Wright   @param[in]  dm           `DMPlex` holding mesh
91877efe802SJames Wright   @param[in]  domain_label `DMLabel` for `DMPlex` domain
91977efe802SJames Wright   @param[in]  label_value  Stratum value
92077efe802SJames Wright   @param[in]  height       Height of `DMPlex` topology
92177efe802SJames Wright   @param[out] elem_restr   `CeedElemRestriction` for coodinates of `DMPlex`, or `NULL`
92277efe802SJames Wright   @param[out] basis        `CeedBasis` for coordinate space of `DMPlex`, or `NULL`
92377efe802SJames Wright   @param[out] vector       `CeedVector` for coordinates of `DMPlex`, or `NULL`
92477efe802SJames Wright **/
92577efe802SJames Wright PetscErrorCode DMPlexCeedCoordinateCreateField(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt height,
92677efe802SJames Wright                                                CeedElemRestriction *elem_restr, CeedBasis *basis, CeedVector *vector) {
92777efe802SJames Wright   CeedElemRestriction elem_restr_ = NULL;
92877efe802SJames Wright 
92977efe802SJames Wright   PetscFunctionBeginUser;
93077efe802SJames Wright   PetscCall(DMPlexCeedElemRestrictionCoordinateCreate(ceed, dm, domain_label, label_value, height, &elem_restr_));
93177efe802SJames Wright   if (elem_restr) {
93277efe802SJames Wright     *elem_restr = NULL;
93377efe802SJames Wright     PetscCallCeed(ceed, CeedElemRestrictionReferenceCopy(elem_restr_, elem_restr));
93477efe802SJames Wright   }
93577efe802SJames Wright   if (basis) PetscCall(DMPlexCeedBasisCoordinateCreate(ceed, dm, domain_label, label_value, height, basis));
93677efe802SJames Wright   if (vector) {
93777efe802SJames Wright     DM                 cdm;
93877efe802SJames Wright     char               container_name[] = "DM Coordinate CeedVectorAndState";
93977efe802SJames Wright     CeedVectorAndState vec_and_state;
94077efe802SJames Wright     Vec                X_loc;
94177efe802SJames Wright     PetscObjectState   X_loc_state;
94277efe802SJames Wright 
94377efe802SJames Wright     PetscCall(DMGetCellCoordinateDM(dm, &cdm));
94477efe802SJames Wright     if (cdm) {
94577efe802SJames Wright       PetscCall(DMGetCellCoordinatesLocal(dm, &X_loc));
94677efe802SJames Wright     } else {
94777efe802SJames Wright       PetscCall(DMGetCoordinatesLocal(dm, &X_loc));
94877efe802SJames Wright     }
94977efe802SJames Wright     PetscCall(VecGetState(X_loc, &X_loc_state));
95077efe802SJames Wright     PetscCall(PetscObjectContainerQuery((PetscObject)X_loc, container_name, (void **)&vec_and_state));
95177efe802SJames Wright 
95277efe802SJames Wright     if (vec_and_state && vec_and_state->state == X_loc_state) {
95377efe802SJames Wright       // Copy existing vector
95477efe802SJames Wright       *vector = NULL;
95577efe802SJames Wright       PetscCallCeed(ceed, CeedVectorReferenceCopy(vec_and_state->vector, vector));
95677efe802SJames Wright     } else {
95777efe802SJames Wright       PetscCall(CeedElemRestrictionCreateVector(elem_restr_, vector, NULL));
95877efe802SJames Wright       PetscCall(VecCopyPetscToCeed(X_loc, *vector));
95977efe802SJames Wright 
96077efe802SJames Wright       // Set in container
96177efe802SJames Wright       PetscCall(PetscNew(&vec_and_state));
96277efe802SJames Wright       PetscCallCeed(ceed, CeedVectorReferenceCopy(*vector, &vec_and_state->vector));
96377efe802SJames Wright       vec_and_state->state = X_loc_state;
964*26d401f3SJames Wright       PetscCall(PetscObjectContainerCompose((PetscObject)X_loc, container_name, vec_and_state, (PetscCtxDestroyFn *)CeedVectorAndStateDestroy));
96577efe802SJames Wright     }
96677efe802SJames Wright   }
96777efe802SJames Wright   PetscCallCeed(ceed, CeedElemRestrictionDestroy(&elem_restr_));
96877efe802SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
96977efe802SJames Wright }
97077efe802SJames Wright 
9717b8c5038SJames Wright /**
9727b8c5038SJames Wright   @brief Create `CeedBasis` for cell to face quadrature space evaluation from `DMPlex`.
9737b8c5038SJames Wright 
9747b8c5038SJames Wright   Collective across MPI processes.
9757b8c5038SJames Wright 
9767b8c5038SJames Wright   @param[in]   ceed          `Ceed` context
9777b8c5038SJames Wright   @param[in]   dm            `DMPlex` holding mesh
9787b8c5038SJames Wright   @param[in]   domain_label  `DMLabel` for `DMPlex` domain
9797b8c5038SJames Wright   @param[in]   label_value   Stratum value
9807b8c5038SJames Wright   @param[in]   face          Index of face
9817b8c5038SJames Wright   @param[in]   dm_field      Index of `DMPlex` field
9827b8c5038SJames Wright   @param[out]  basis         `CeedBasis` for cell to face evaluation
9837b8c5038SJames Wright **/
9847b8c5038SJames Wright PetscErrorCode DMPlexCeedBasisCellToFaceCreate(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt face, PetscInt dm_field,
9857b8c5038SJames Wright                                                CeedBasis *basis) {
9867b8c5038SJames Wright   PetscInt        ds_field = -1, height = 0;
9877b8c5038SJames Wright   PetscDS         ds;
9887b8c5038SJames Wright   PetscFE         fe;
9897b8c5038SJames Wright   PetscQuadrature face_quadrature;
9907b8c5038SJames Wright 
9917b8c5038SJames Wright   PetscFunctionBeginUser;
9927b8c5038SJames Wright   // Get element information
9937b8c5038SJames Wright   PetscCall(DMGetRegionDS(dm, domain_label, NULL, &ds, NULL));
9947b8c5038SJames Wright   PetscCall(DMFieldToDSField(dm, domain_label, dm_field, &ds_field));
9957b8c5038SJames Wright   PetscCall(PetscDSGetDiscretization(ds, ds_field, (PetscObject *)&fe));
9967b8c5038SJames Wright   PetscCall(PetscFEGetFaceQuadrature(fe, &face_quadrature));
9977b8c5038SJames Wright 
9987b8c5038SJames Wright   {  // Build libCEED basis
9997b8c5038SJames Wright     PetscInt         num_derivatives = 1, num_comp, P, Q = -1;
10007b8c5038SJames Wright     PetscTabulation  basis_tabulation;
10017b8c5038SJames Wright     CeedScalar      *q_points, *q_weights, *interp, *grad;
10027b8c5038SJames Wright     CeedElemTopology elem_topo;
10037b8c5038SJames Wright 
10047b8c5038SJames Wright     {  // Verify compatible element topology
10057b8c5038SJames Wright       DMPolytopeType cell_type;
10067b8c5038SJames Wright 
10077b8c5038SJames Wright       PetscCall(GetGlobalDMPlexPolytopeType(dm, domain_label, label_value, height, &cell_type));
10087b8c5038SJames Wright       elem_topo = PolytopeTypePetscToCeed(cell_type);
10097b8c5038SJames Wright       PetscCheck(elem_topo, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "DMPlex topology not supported: %s", DMPolytopeTypes[cell_type]);
10107b8c5038SJames Wright     }
10117b8c5038SJames Wright 
10127b8c5038SJames Wright     // Compute basis tabulation
10137b8c5038SJames Wright     PetscCall(GetQuadratureDataP2C(fe, face_quadrature, NULL, NULL, &Q, &q_points, &q_weights));
10147b8c5038SJames Wright     PetscCall(PetscFEGetFaceTabulation(fe, num_derivatives, &basis_tabulation));
10157b8c5038SJames Wright     num_comp = basis_tabulation->Nc;
10167b8c5038SJames Wright     P        = basis_tabulation->Nb / num_comp;
10177b8c5038SJames Wright     PetscCall(ComputeFieldTabulationP2C(dm, dm_field, face, basis_tabulation, &interp, &grad));
10187b8c5038SJames Wright     PetscCallCeed(ceed, CeedBasisCreateH1(ceed, elem_topo, num_comp, P, Q, interp, grad, q_points, q_weights, basis));
10197b8c5038SJames Wright 
10207b8c5038SJames Wright     PetscCall(PetscFree(q_points));
10217b8c5038SJames Wright     PetscCall(PetscFree(q_weights));
10227b8c5038SJames Wright     PetscCall(PetscFree(interp));
10237b8c5038SJames Wright     PetscCall(PetscFree(grad));
10247b8c5038SJames Wright   }
10257b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
10267b8c5038SJames Wright }
10277b8c5038SJames Wright 
10287b8c5038SJames Wright /**
10297b8c5038SJames Wright   @brief Create `CeedBasis` for cell to face quadrature space evaluation on coordinate space of `DMPlex`.
10307b8c5038SJames Wright 
10317b8c5038SJames Wright   Collective across MPI processes.
10327b8c5038SJames Wright 
10337b8c5038SJames Wright   @param[in]   ceed          `Ceed` context
10347b8c5038SJames Wright   @param[in]   dm            `DMPlex` holding mesh
10357b8c5038SJames Wright   @param[in]   domain_label  `DMLabel` for `DMPlex` domain
10367b8c5038SJames Wright   @param[in]   label_value   Stratum value
10377b8c5038SJames Wright   @param[in]   face          Index of face
10387b8c5038SJames Wright   @param[out]  basis         `CeedBasis` for cell to face evaluation on coordinate space of `DMPlex`
10397b8c5038SJames Wright **/
10407b8c5038SJames Wright PetscErrorCode DMPlexCeedBasisCellToFaceCoordinateCreate(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt face,
10417b8c5038SJames Wright                                                          CeedBasis *basis) {
10427b8c5038SJames Wright   DM dm_coord;
10437b8c5038SJames Wright 
10447b8c5038SJames Wright   PetscFunctionBeginUser;
10457b8c5038SJames Wright   PetscCall(DMGetCellCoordinateDM(dm, &dm_coord));
10467b8c5038SJames Wright   if (!dm_coord) {
10477b8c5038SJames Wright     PetscCall(DMGetCoordinateDM(dm, &dm_coord));
10487b8c5038SJames Wright   }
10497b8c5038SJames Wright   PetscCall(DMPlexCeedBasisCellToFaceCreate(ceed, dm_coord, domain_label, label_value, face, 0, basis));
10507b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
10517b8c5038SJames Wright }
10527b8c5038SJames Wright 
10537b8c5038SJames Wright /**
10547b8c5038SJames Wright   @brief Setup `DM` with FE space of appropriate degree
10557b8c5038SJames Wright 
10567b8c5038SJames Wright   Must be followed by `DMSetupByOrderEnd_FEM`
10577b8c5038SJames Wright 
10587b8c5038SJames Wright   @param[in]   setup_faces     Flag to setup face geometry
10597b8c5038SJames Wright   @param[in]   setup_coords    Flag to setup coordinate spaces
10607b8c5038SJames Wright   @param[in]   degree          Polynomial orders of field
10617b8c5038SJames Wright   @param[in]   coord_order     Polynomial order of coordinate basis, or `PETSC_DECIDE` for default
10627b8c5038SJames Wright   @param[in]   q_extra         Additional quadrature order
10637b8c5038SJames Wright   @param[in]   num_fields      Number of fields in solution vector
10647b8c5038SJames Wright   @param[in]   field_sizes     Array of number of components for each field
10657b8c5038SJames Wright   @param[out]  dm              `DM` to setup
10667b8c5038SJames Wright 
10677b8c5038SJames Wright   @return An error code: 0 - success, otherwise - failure
10687b8c5038SJames Wright **/
10697b8c5038SJames Wright PetscErrorCode DMSetupByOrderBegin_FEM(PetscBool setup_faces, PetscBool setup_coords, PetscInt degree, PetscInt coord_order, PetscInt q_extra,
10707b8c5038SJames Wright                                        PetscInt num_fields, const PetscInt *field_sizes, DM dm) {
10717b8c5038SJames Wright   PetscInt  dim, q_order = degree + q_extra;
10727b8c5038SJames Wright   PetscBool is_simplex = PETSC_TRUE;
10737b8c5038SJames Wright   PetscFE   fe;
10747b8c5038SJames Wright   MPI_Comm  comm = PetscObjectComm((PetscObject)dm);
10757b8c5038SJames Wright 
10767b8c5038SJames Wright   PetscFunctionBeginUser;
10777b8c5038SJames Wright   PetscCall(DMPlexIsSimplex(dm, &is_simplex));
10787b8c5038SJames Wright 
10797b8c5038SJames Wright   // Setup DM
10807b8c5038SJames Wright   PetscCall(DMGetDimension(dm, &dim));
10817b8c5038SJames Wright   for (PetscInt i = 0; i < num_fields; i++) {
10827b8c5038SJames Wright     PetscFE  fe_face;
10837b8c5038SJames Wright     PetscInt q_order = degree + q_extra;
10847b8c5038SJames Wright 
10857b8c5038SJames Wright     PetscCall(PetscFECreateLagrange(comm, dim, field_sizes[i], is_simplex, degree, q_order, &fe));
10867b8c5038SJames Wright     if (setup_faces) PetscCall(PetscFEGetHeightSubspace(fe, 1, &fe_face));
10877b8c5038SJames Wright     PetscCall(DMAddField(dm, NULL, (PetscObject)fe));
10887b8c5038SJames Wright     PetscCall(PetscFEDestroy(&fe));
10897b8c5038SJames Wright   }
10907b8c5038SJames Wright   PetscCall(DMCreateDS(dm));
10917b8c5038SJames Wright 
10927b8c5038SJames Wright   // Project coordinates to enrich quadrature space
10937b8c5038SJames Wright   if (setup_coords) {
10947b8c5038SJames Wright     DM             dm_coord;
10957b8c5038SJames Wright     PetscDS        ds_coord;
10967b8c5038SJames Wright     PetscFE        fe_coord_current, fe_coord_new, fe_coord_face_new;
10977b8c5038SJames Wright     PetscDualSpace fe_coord_dual_space;
10987b8c5038SJames Wright     PetscInt       fe_coord_order, num_comp_coord;
10997b8c5038SJames Wright 
11007b8c5038SJames Wright     PetscCall(DMGetCoordinateDM(dm, &dm_coord));
11017b8c5038SJames Wright     PetscCall(DMGetCoordinateDim(dm, &num_comp_coord));
11027b8c5038SJames Wright     PetscCall(DMGetRegionDS(dm_coord, NULL, NULL, &ds_coord, NULL));
11037b8c5038SJames Wright     PetscCall(PetscDSGetDiscretization(ds_coord, 0, (PetscObject *)&fe_coord_current));
11047b8c5038SJames Wright     PetscCall(PetscFEGetDualSpace(fe_coord_current, &fe_coord_dual_space));
11057b8c5038SJames Wright     PetscCall(PetscDualSpaceGetOrder(fe_coord_dual_space, &fe_coord_order));
11067b8c5038SJames Wright 
11077b8c5038SJames Wright     // Create FE for coordinates
11087b8c5038SJames Wright     if (coord_order != PETSC_DECIDE) fe_coord_order = coord_order;
11097b8c5038SJames Wright     PetscCall(PetscFECreateLagrange(comm, dim, num_comp_coord, is_simplex, fe_coord_order, q_order, &fe_coord_new));
11107b8c5038SJames Wright     if (setup_faces) PetscCall(PetscFEGetHeightSubspace(fe_coord_new, 1, &fe_coord_face_new));
11117b8c5038SJames Wright     PetscCall(DMSetCoordinateDisc(dm, fe_coord_new, PETSC_FALSE, PETSC_TRUE));
11127b8c5038SJames Wright     PetscCall(PetscFEDestroy(&fe_coord_new));
11137b8c5038SJames Wright   }
11147b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
11157b8c5038SJames Wright }
11167b8c5038SJames Wright 
11177b8c5038SJames Wright /**
11187b8c5038SJames Wright   @brief Finish setting up `DM`
11197b8c5038SJames Wright 
11207b8c5038SJames Wright   Must be called after `DMSetupByOrderBegin_FEM` and all strong BCs have be declared via `DMAddBoundaries`.
11217b8c5038SJames Wright 
11227b8c5038SJames Wright   @param[in]   setup_coords    Flag to setup coordinate spaces
11237b8c5038SJames Wright   @param[out]  dm              `DM` to setup
11247b8c5038SJames Wright 
11257b8c5038SJames Wright   @return An error code: 0 - success, otherwise - failure
11267b8c5038SJames Wright **/
11277b8c5038SJames Wright PetscErrorCode DMSetupByOrderEnd_FEM(PetscBool setup_coords, DM dm) {
11287b8c5038SJames Wright   PetscBool is_simplex;
11297b8c5038SJames Wright 
11307b8c5038SJames Wright   PetscFunctionBeginUser;
11317b8c5038SJames Wright   PetscCall(DMPlexIsSimplex(dm, &is_simplex));
11327b8c5038SJames Wright   // Set tensor permutation if needed
11337b8c5038SJames Wright   if (!is_simplex) {
11347b8c5038SJames Wright     PetscCall(DMPlexSetClosurePermutationTensor(dm, PETSC_DETERMINE, NULL));
11357b8c5038SJames Wright     if (setup_coords) {
11367b8c5038SJames Wright       DM dm_coord;
11377b8c5038SJames Wright 
11387b8c5038SJames Wright       PetscCall(DMGetCoordinateDM(dm, &dm_coord));
11397b8c5038SJames Wright       PetscCall(DMPlexSetClosurePermutationTensor(dm_coord, PETSC_DETERMINE, NULL));
11407b8c5038SJames Wright     }
11417b8c5038SJames Wright   }
11427b8c5038SJames Wright   PetscCall(DMLocalizeCoordinates(dm));  // Must localize after tensor closure setting
11437b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
11447b8c5038SJames Wright }
11457b8c5038SJames Wright 
11467b8c5038SJames Wright /**
11477b8c5038SJames Wright   @brief Setup `DM` with FE space of appropriate degree with no boundary conditions
11487b8c5038SJames Wright 
11497b8c5038SJames Wright   Calls `DMSetupByOrderBegin_FEM` and `DMSetupByOrderEnd_FEM` successively
11507b8c5038SJames Wright 
11517b8c5038SJames Wright   @param[in]   setup_faces     Flag to setup face geometry
11527b8c5038SJames Wright   @param[in]   setup_coords    Flag to setup coordinate spaces
11537b8c5038SJames Wright   @param[in]   degree          Polynomial orders of field
11547b8c5038SJames Wright   @param[in]   coord_order     Polynomial order of coordinate basis, or `PETSC_DECIDE` for default
11557b8c5038SJames Wright   @param[in]   q_extra         Additional quadrature order
11567b8c5038SJames Wright   @param[in]   num_fields      Number of fields in solution vector
11577b8c5038SJames Wright   @param[in]   field_sizes     Array of number of components for each field
11587b8c5038SJames Wright   @param[out]  dm              `DM` to setup
11597b8c5038SJames Wright 
11607b8c5038SJames Wright   @return An error code: 0 - success, otherwise - failure
11617b8c5038SJames Wright **/
11627b8c5038SJames Wright PetscErrorCode DMSetupByOrder_FEM(PetscBool setup_faces, PetscBool setup_coords, PetscInt degree, PetscInt coord_order, PetscInt q_extra,
11637b8c5038SJames Wright                                   PetscInt num_fields, const PetscInt *field_sizes, DM dm) {
11647b8c5038SJames Wright   PetscFunctionBeginUser;
11657b8c5038SJames Wright   PetscCall(DMSetupByOrderBegin_FEM(setup_faces, setup_coords, degree, coord_order, q_extra, num_fields, field_sizes, dm));
11667b8c5038SJames Wright   PetscCall(DMSetupByOrderEnd_FEM(setup_coords, dm));
11677b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
11687b8c5038SJames Wright }
11697b8c5038SJames Wright 
11707b8c5038SJames Wright /**
11717b8c5038SJames Wright   @brief Get the points in a label stratum which have requested height
11727b8c5038SJames Wright 
11737b8c5038SJames Wright   Example, from the "Face Sets" label (which contains face, edge, and vertex points), return points that correspond to face points (height = 1) and have a label value of 3:
11747b8c5038SJames Wright   ```
11757b8c5038SJames Wright   PetscCall(DMGetStratumISAtHeight(dm, "Face Sets", 3, 1, &is_face_points));
11767b8c5038SJames Wright   ```
11777b8c5038SJames Wright 
11787b8c5038SJames Wright   @param[in]  dm     DM which has the label
11797b8c5038SJames Wright   @param[in]  name   Name fo the label
11807b8c5038SJames Wright   @param[in]  value  Label value of points to return
11817b8c5038SJames Wright   @param[in]  height Height of points to return
11827b8c5038SJames Wright   @param[out] points `IS` of points, or `NULL` is there are no points in that stratum at height
11837b8c5038SJames Wright **/
11847b8c5038SJames Wright static PetscErrorCode DMGetStratumISAtHeight(DM dm, const char name[], PetscInt value, PetscInt height, IS *points) {
11857b8c5038SJames Wright   PetscInt depth;
11867b8c5038SJames Wright   DMLabel  depth_label;
11877b8c5038SJames Wright   IS       label_is, depth_is;
11887b8c5038SJames Wright 
11897b8c5038SJames Wright   PetscFunctionBeginUser;
11907b8c5038SJames Wright   PetscCall(DMPlexGetDepth(dm, &depth));
11917b8c5038SJames Wright   PetscCall(DMPlexGetDepthLabel(dm, &depth_label));
11927b8c5038SJames Wright   PetscCall(DMLabelGetStratumIS(depth_label, depth - height, &depth_is));
11937b8c5038SJames Wright   PetscCall(DMGetStratumIS(dm, name, value, &label_is));
11947b8c5038SJames Wright   if (label_is == NULL || depth_is == NULL) *points = NULL;
11957b8c5038SJames Wright   else PetscCall(ISIntersect(depth_is, label_is, points));
11967b8c5038SJames Wright   PetscCall(ISDestroy(&depth_is));
11977b8c5038SJames Wright   PetscCall(ISDestroy(&label_is));
11987b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
11997b8c5038SJames Wright }
12007b8c5038SJames Wright 
12017b8c5038SJames Wright /**
12027b8c5038SJames Wright   @brief Create a label with separate values for the `FE` orientations for all cells with this material for the `DMPlex` face.
12037b8c5038SJames Wright 
12047b8c5038SJames Wright   Collective across MPI processes.
12057b8c5038SJames Wright 
12067b8c5038SJames Wright   @param[in,out]  dm                `DMPlex` holding mesh
12077b8c5038SJames Wright   @param[in]      dm_face           Face number on `DMPlex`
12087b8c5038SJames Wright   @param[out]     face_label_name   Label name for newly created label, or `NULL` if no elements match the `material` and `dm_face`
12097b8c5038SJames Wright **/
12107b8c5038SJames Wright PetscErrorCode DMPlexCreateFaceLabel(DM dm, PetscInt dm_face, char **face_label_name) {
12117b8c5038SJames Wright   PetscInt        num_face_points = 0, face_height = 1;
12127b8c5038SJames Wright   const PetscInt *face_points;
12137b8c5038SJames Wright   IS              is_face_points;
12147b8c5038SJames Wright   DMLabel         face_label = NULL;
12157b8c5038SJames Wright   MPI_Comm        comm       = PetscObjectComm((PetscObject)dm);
12167b8c5038SJames Wright 
12177b8c5038SJames Wright   PetscFunctionBeginUser;
12187b8c5038SJames Wright   {  // Create label name and label
12197b8c5038SJames Wright     PetscSizeT label_name_len = 64;
12207b8c5038SJames Wright     PetscBool  has_label_already;
12217b8c5038SJames Wright 
12227b8c5038SJames Wright     // Face label
12237b8c5038SJames Wright     PetscCall(PetscCalloc1(label_name_len, face_label_name));
12247b8c5038SJames Wright     PetscCall(PetscSNPrintf(*face_label_name, label_name_len, "Orientation for DM face %" PetscInt_FMT, dm_face));
12257b8c5038SJames Wright     PetscCall(DMHasLabel(dm, *face_label_name, &has_label_already));
12267b8c5038SJames Wright     if (has_label_already) PetscFunctionReturn(PETSC_SUCCESS);
12277b8c5038SJames Wright 
12287b8c5038SJames Wright     PetscCall(DMCreateLabel(dm, *face_label_name));
12297b8c5038SJames Wright     PetscCall(DMGetLabel(dm, *face_label_name, &face_label));
12307b8c5038SJames Wright   }
12317b8c5038SJames Wright 
12327b8c5038SJames Wright   PetscCall(DMGetStratumISAtHeight(dm, "Face Sets", dm_face, face_height, &is_face_points));
12337b8c5038SJames Wright   if (is_face_points) {
12347b8c5038SJames Wright     PetscCall(ISGetLocalSize(is_face_points, &num_face_points));
12357b8c5038SJames Wright     PetscCall(ISGetIndices(is_face_points, &face_points));
12367b8c5038SJames Wright   }
12377b8c5038SJames Wright 
12387b8c5038SJames Wright   for (PetscInt face_index = 0; face_index < num_face_points; face_index++) {
12397b8c5038SJames Wright     const PetscInt *face_support, *cell_cone;
12407b8c5038SJames Wright     PetscInt        face_support_size = 0, cell_cone_size = 0, fe_face_on_cell = -1;
12417b8c5038SJames Wright     PetscInt        face_point = face_points[face_index];
12427b8c5038SJames Wright 
12437b8c5038SJames Wright     // Get cell supporting face
12447b8c5038SJames Wright     PetscCall(DMPlexGetSupport(dm, face_point, &face_support));
12457b8c5038SJames Wright     PetscCall(DMPlexGetSupportSize(dm, face_point, &face_support_size));
12467b8c5038SJames Wright     PetscCheck(face_support_size == 1, comm, PETSC_ERR_SUP, "Expected one cell in support of exterior face, but got %" PetscInt_FMT " cells",
12477b8c5038SJames Wright                face_support_size);
12487b8c5038SJames Wright     PetscInt cell_point = face_support[0];
12497b8c5038SJames Wright 
12507b8c5038SJames Wright     // Find face number
12517b8c5038SJames Wright     PetscCall(DMPlexGetCone(dm, cell_point, &cell_cone));
12527b8c5038SJames Wright     PetscCall(DMPlexGetConeSize(dm, cell_point, &cell_cone_size));
12537b8c5038SJames Wright     for (PetscInt i = 0; i < cell_cone_size; i++) {
12547b8c5038SJames Wright       if (cell_cone[i] == face_point) fe_face_on_cell = i;
12557b8c5038SJames Wright     }
12567b8c5038SJames Wright     PetscCheck(fe_face_on_cell != -1, comm, PETSC_ERR_SUP, "Could not find face %" PetscInt_FMT " in cone of its support", face_point);
12577b8c5038SJames Wright 
12587b8c5038SJames Wright     // Add to label
12597b8c5038SJames Wright     PetscCall(DMLabelSetValue(face_label, face_point, fe_face_on_cell));
12607b8c5038SJames Wright   }
12617b8c5038SJames Wright   PetscCall(DMPlexLabelAddFaceCells(dm, face_label));
12627b8c5038SJames Wright   PetscCall(ISDestroy(&is_face_points));
12637b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
12647b8c5038SJames Wright }
12657b8c5038SJames Wright 
12667b8c5038SJames Wright /**
12677b8c5038SJames Wright    @brief Creates an array of all the values set for a `DMLabel`
12687b8c5038SJames Wright 
12697b8c5038SJames Wright    Because `DMLabelGetValueIS` returns label values locally, not globally.
12707b8c5038SJames Wright 
12717b8c5038SJames Wright    Collective across MPI processes.
12727b8c5038SJames Wright 
12737b8c5038SJames Wright    @param[in]  dm          `DM` with the label
12747b8c5038SJames Wright    @param[in]  label       `DMLabel` to get values of
12757b8c5038SJames Wright    @param[out] num_values  Total number of values
12767b8c5038SJames Wright    @param[out] value_array Array of label values, must be freed by user
12777b8c5038SJames Wright **/
12787b8c5038SJames Wright PetscErrorCode DMLabelCreateGlobalValueArray(DM dm, DMLabel label, PetscInt *num_values, PetscInt **value_array) {
12797b8c5038SJames Wright   PetscInt       num_values_local, minmax_values[2], minmax_values_loc[2];
12807b8c5038SJames Wright   PetscInt      *values_local = NULL;
12817b8c5038SJames Wright   MPI_Comm       comm         = PetscObjectComm((PetscObject)dm);
12827b8c5038SJames Wright   PetscSegBuffer seg;
12837b8c5038SJames Wright   PetscCount     seg_size;
12847b8c5038SJames Wright 
12857b8c5038SJames Wright   PetscFunctionBeginUser;
12867b8c5038SJames Wright   {  // Extract array of local DMLabel values so it can be sorted
12877b8c5038SJames Wright     IS              is_values;
12887b8c5038SJames Wright     const PetscInt *is_values_local = NULL;
12897b8c5038SJames Wright 
12907b8c5038SJames Wright     PetscCall(DMLabelGetValueIS(label, &is_values));
12917b8c5038SJames Wright     PetscCall(ISGetLocalSize(is_values, &num_values_local));
12927b8c5038SJames Wright 
12937b8c5038SJames Wright     PetscCall(ISGetIndices(is_values, &is_values_local));
12947b8c5038SJames Wright     PetscCall(PetscMalloc1(num_values_local, &values_local));
12957b8c5038SJames Wright     PetscCall(PetscArraycpy(values_local, is_values_local, num_values_local));
12967b8c5038SJames Wright     PetscCall(ISRestoreIndices(is_values, &is_values_local));
12977b8c5038SJames Wright     PetscCall(ISDestroy(&is_values));
12987b8c5038SJames Wright   }
12997b8c5038SJames Wright 
13007b8c5038SJames Wright   if (num_values_local) {
13017b8c5038SJames Wright     PetscCall(PetscSortInt(num_values_local, values_local));
13027b8c5038SJames Wright     minmax_values_loc[0] = values_local[0];
13037b8c5038SJames Wright     minmax_values_loc[1] = values_local[num_values_local - 1];
13047b8c5038SJames Wright   } else {
13057b8c5038SJames Wright     // Rank has no set label values, therefore set local min/max to have no effect on global min/max
13067b8c5038SJames Wright     minmax_values_loc[0] = PETSC_INT_MAX;
13077b8c5038SJames Wright     minmax_values_loc[1] = PETSC_INT_MIN;
13087b8c5038SJames Wright   }
13097b8c5038SJames Wright   PetscCall(PetscGlobalMinMaxInt(comm, minmax_values_loc, minmax_values));
13107b8c5038SJames Wright 
13117b8c5038SJames Wright   PetscCall(PetscSegBufferCreate(sizeof(PetscInt), 16, &seg));
13127b8c5038SJames Wright   for (PetscInt value = minmax_values[0]; value <= minmax_values[1]; value++) {
13137b8c5038SJames Wright     PetscInt location_local = -1, location_global = -1;
13147b8c5038SJames Wright 
13157b8c5038SJames Wright     PetscCall(PetscFindInt(value, num_values_local, values_local, &location_local));
13167b8c5038SJames Wright     PetscCallMPI(MPIU_Allreduce(&location_local, &location_global, 1, MPIU_INT, MPI_MAX, comm));
13177b8c5038SJames Wright     if (location_global < 0) continue;
13187b8c5038SJames Wright     else {
13197b8c5038SJames Wright       PetscInt *seg_slot;
13207b8c5038SJames Wright 
13217b8c5038SJames Wright       PetscCall(PetscSegBufferGetInts(seg, 1, &seg_slot));
13227b8c5038SJames Wright       *seg_slot = value;
13237b8c5038SJames Wright     }
13247b8c5038SJames Wright   }
13257b8c5038SJames Wright   PetscCall(PetscSegBufferGetSize(seg, &seg_size));
13267b8c5038SJames Wright   *num_values = (PetscInt)seg_size;
13277b8c5038SJames Wright   PetscCall(PetscSegBufferExtractAlloc(seg, value_array));
13287b8c5038SJames Wright   PetscCall(PetscSegBufferDestroy(&seg));
13297b8c5038SJames Wright   if (values_local) PetscCall(PetscFree(values_local));
13307b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
13317b8c5038SJames Wright }
13327b8c5038SJames Wright 
13337b8c5038SJames Wright /**
13347b8c5038SJames Wright   @brief Get the number of components for `dm`'s coordinate field
13357b8c5038SJames Wright 
13367b8c5038SJames Wright   @param[in]  dm       DM
13377b8c5038SJames Wright   @param[out] num_comp Number of components for the coordinate field
13387b8c5038SJames Wright **/
13397b8c5038SJames Wright PetscErrorCode DMGetCoordinateNumComps(DM dm, PetscInt *num_comp) {
13407b8c5038SJames Wright   DM           dm_coord;
13417b8c5038SJames Wright   PetscSection section_coord;
13427b8c5038SJames Wright   PetscInt     field = 0;  // Default field has the coordinates
13437b8c5038SJames Wright 
13447b8c5038SJames Wright   PetscFunctionBeginUser;
13457b8c5038SJames Wright   PetscCall(DMGetCoordinateDM(dm, &dm_coord));
13467b8c5038SJames Wright   PetscCall(DMGetLocalSection(dm_coord, &section_coord));
13477b8c5038SJames Wright   PetscCall(PetscSectionGetFieldComponents(section_coord, field, num_comp));
13487b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
13497b8c5038SJames Wright }
13507b8c5038SJames Wright 
13517b8c5038SJames Wright /**
13527b8c5038SJames Wright   @brief Get the number of components for `dm`'s field
13537b8c5038SJames Wright 
13547b8c5038SJames Wright   @param[in]  dm       DM
13557b8c5038SJames Wright   @param[in]  field    The field ID to get the number of components for
13567b8c5038SJames Wright   @param[out] num_comp Number of components for the coordinate field
13577b8c5038SJames Wright **/
13587b8c5038SJames Wright PetscErrorCode DMGetFieldNumComps(DM dm, PetscInt field, PetscInt *num_comp) {
13597b8c5038SJames Wright   PetscSection section;
13607b8c5038SJames Wright 
13617b8c5038SJames Wright   PetscFunctionBeginUser;
13627b8c5038SJames Wright   PetscCall(DMGetLocalSection(dm, &section));
13637b8c5038SJames Wright   PetscCall(PetscSectionGetFieldComponents(section, field, num_comp));
13647b8c5038SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
13657b8c5038SJames Wright }
1366