xref: /libCEED/examples/fluids/src/dm_utils.c (revision d68a66c4667a4dc19397db12012e87e45bb921c9)
1*d68a66c4SJames Wright // Copyright (c) 2017-2022, Lawrence Livermore National Security, LLC and other CEED contributors.
2*d68a66c4SJames Wright // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
3*d68a66c4SJames Wright //
4*d68a66c4SJames Wright // SPDX-License-Identifier: BSD-2-Clause
5*d68a66c4SJames Wright //
6*d68a66c4SJames Wright // This file is part of CEED:  http://github.com/ceed
7*d68a66c4SJames Wright 
8*d68a66c4SJames Wright /// @file
9*d68a66c4SJames Wright /// Utilities for setting up DM and PetscFE
10*d68a66c4SJames Wright 
11*d68a66c4SJames Wright #include <ceed.h>
12*d68a66c4SJames Wright #include <petscdmplex.h>
13*d68a66c4SJames Wright #include <petscds.h>
14*d68a66c4SJames Wright 
15*d68a66c4SJames Wright #include "../navierstokes.h"
16*d68a66c4SJames Wright 
17*d68a66c4SJames Wright // Utility function to create local CEED restriction
18*d68a66c4SJames Wright PetscErrorCode CreateRestrictionFromPlex(Ceed ceed, DM dm, CeedInt height, DMLabel domain_label, CeedInt label_value, PetscInt dm_field,
19*d68a66c4SJames Wright                                          CeedElemRestriction *elem_restr) {
20*d68a66c4SJames Wright   PetscInt num_elem, elem_size, num_dof, num_comp, *elem_restr_offsets_petsc;
21*d68a66c4SJames Wright   CeedInt *elem_restr_offsets_ceed;
22*d68a66c4SJames Wright 
23*d68a66c4SJames Wright   PetscFunctionBeginUser;
24*d68a66c4SJames Wright   PetscCall(
25*d68a66c4SJames Wright       DMPlexGetLocalOffsets(dm, domain_label, label_value, height, dm_field, &num_elem, &elem_size, &num_comp, &num_dof, &elem_restr_offsets_petsc));
26*d68a66c4SJames Wright 
27*d68a66c4SJames Wright   PetscCall(IntArrayP2C(num_elem * elem_size, &elem_restr_offsets_petsc, &elem_restr_offsets_ceed));
28*d68a66c4SJames Wright   PetscCallCeed(ceed, CeedElemRestrictionCreate(ceed, num_elem, elem_size, num_comp, 1, num_dof, CEED_MEM_HOST, CEED_COPY_VALUES,
29*d68a66c4SJames Wright                                                 elem_restr_offsets_ceed, elem_restr));
30*d68a66c4SJames Wright   PetscCall(PetscFree(elem_restr_offsets_ceed));
31*d68a66c4SJames Wright 
32*d68a66c4SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
33*d68a66c4SJames Wright }
34*d68a66c4SJames Wright 
35*d68a66c4SJames Wright // Utility function to get Ceed Restriction for each domain
36*d68a66c4SJames Wright PetscErrorCode GetRestrictionForDomain(Ceed ceed, DM dm, CeedInt height, DMLabel domain_label, PetscInt label_value, PetscInt dm_field, CeedInt Q,
37*d68a66c4SJames Wright                                        CeedInt q_data_size, CeedElemRestriction *elem_restr_q, CeedElemRestriction *elem_restr_x,
38*d68a66c4SJames Wright                                        CeedElemRestriction *elem_restr_qd_i) {
39*d68a66c4SJames Wright   DM                  dm_coord;
40*d68a66c4SJames Wright   CeedInt             loc_num_elem;
41*d68a66c4SJames Wright   PetscInt            dim;
42*d68a66c4SJames Wright   CeedElemRestriction elem_restr_tmp;
43*d68a66c4SJames Wright   PetscFunctionBeginUser;
44*d68a66c4SJames Wright 
45*d68a66c4SJames Wright   PetscCall(DMGetDimension(dm, &dim));
46*d68a66c4SJames Wright   dim -= height;
47*d68a66c4SJames Wright   PetscCall(CreateRestrictionFromPlex(ceed, dm, height, domain_label, label_value, dm_field, &elem_restr_tmp));
48*d68a66c4SJames Wright   if (elem_restr_q) *elem_restr_q = elem_restr_tmp;
49*d68a66c4SJames Wright   if (elem_restr_x) {
50*d68a66c4SJames Wright     PetscCall(DMGetCellCoordinateDM(dm, &dm_coord));
51*d68a66c4SJames Wright     if (!dm_coord) {
52*d68a66c4SJames Wright       PetscCall(DMGetCoordinateDM(dm, &dm_coord));
53*d68a66c4SJames Wright     }
54*d68a66c4SJames Wright     PetscCall(DMPlexSetClosurePermutationTensor(dm_coord, PETSC_DETERMINE, NULL));
55*d68a66c4SJames Wright     PetscCall(CreateRestrictionFromPlex(ceed, dm_coord, height, domain_label, label_value, dm_field, elem_restr_x));
56*d68a66c4SJames Wright   }
57*d68a66c4SJames Wright   if (elem_restr_qd_i) {
58*d68a66c4SJames Wright     PetscCallCeed(ceed, CeedElemRestrictionGetNumElements(elem_restr_tmp, &loc_num_elem));
59*d68a66c4SJames Wright     PetscCallCeed(ceed, CeedElemRestrictionCreateStrided(ceed, loc_num_elem, Q, q_data_size, q_data_size * loc_num_elem * Q, CEED_STRIDES_BACKEND,
60*d68a66c4SJames Wright                                                          elem_restr_qd_i));
61*d68a66c4SJames Wright   }
62*d68a66c4SJames Wright   if (!elem_restr_q) PetscCallCeed(ceed, CeedElemRestrictionDestroy(&elem_restr_tmp));
63*d68a66c4SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
64*d68a66c4SJames Wright }
65*d68a66c4SJames Wright 
66*d68a66c4SJames Wright /**
67*d68a66c4SJames Wright   @brief Convert `DM` field to `DS` field.
68*d68a66c4SJames Wright 
69*d68a66c4SJames Wright   @param[in]   dm            `DM` holding mesh
70*d68a66c4SJames Wright   @param[in]   domain_label  Label for `DM` domain
71*d68a66c4SJames Wright   @param[in]   dm_field      Index of `DM` field
72*d68a66c4SJames Wright   @param[out]  ds_field      Index of `DS` field
73*d68a66c4SJames Wright 
74*d68a66c4SJames Wright   @return An error code: 0 - success, otherwise - failure
75*d68a66c4SJames Wright **/
76*d68a66c4SJames Wright PetscErrorCode DMFieldToDSField(DM dm, DMLabel domain_label, PetscInt dm_field, PetscInt *ds_field) {
77*d68a66c4SJames Wright   PetscDS         ds;
78*d68a66c4SJames Wright   IS              field_is;
79*d68a66c4SJames Wright   const PetscInt *fields;
80*d68a66c4SJames Wright   PetscInt        num_fields;
81*d68a66c4SJames Wright 
82*d68a66c4SJames Wright   PetscFunctionBeginUser;
83*d68a66c4SJames Wright   PetscCall(DMGetRegionDS(dm, domain_label, &field_is, &ds, NULL));
84*d68a66c4SJames Wright   PetscCall(ISGetIndices(field_is, &fields));
85*d68a66c4SJames Wright   PetscCall(ISGetSize(field_is, &num_fields));
86*d68a66c4SJames Wright   for (PetscInt i = 0; i < num_fields; i++) {
87*d68a66c4SJames Wright     if (dm_field == fields[i]) {
88*d68a66c4SJames Wright       *ds_field = i;
89*d68a66c4SJames Wright       break;
90*d68a66c4SJames Wright     }
91*d68a66c4SJames Wright   }
92*d68a66c4SJames Wright   PetscCall(ISRestoreIndices(field_is, &fields));
93*d68a66c4SJames Wright 
94*d68a66c4SJames Wright   if (*ds_field == -1) SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Could not find dm_field %" PetscInt_FMT " in DS", dm_field);
95*d68a66c4SJames Wright 
96*d68a66c4SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
97*d68a66c4SJames Wright }
98*d68a66c4SJames Wright 
99*d68a66c4SJames Wright // -----------------------------------------------------------------------------
100*d68a66c4SJames Wright // Utility function - convert from DMPolytopeType to CeedElemTopology
101*d68a66c4SJames Wright // -----------------------------------------------------------------------------
102*d68a66c4SJames Wright static CeedElemTopology ElemTopologyP2C(DMPolytopeType cell_type) {
103*d68a66c4SJames Wright   switch (cell_type) {
104*d68a66c4SJames Wright     case DM_POLYTOPE_TRIANGLE:
105*d68a66c4SJames Wright       return CEED_TOPOLOGY_TRIANGLE;
106*d68a66c4SJames Wright     case DM_POLYTOPE_QUADRILATERAL:
107*d68a66c4SJames Wright       return CEED_TOPOLOGY_QUAD;
108*d68a66c4SJames Wright     case DM_POLYTOPE_TETRAHEDRON:
109*d68a66c4SJames Wright       return CEED_TOPOLOGY_TET;
110*d68a66c4SJames Wright     case DM_POLYTOPE_HEXAHEDRON:
111*d68a66c4SJames Wright       return CEED_TOPOLOGY_HEX;
112*d68a66c4SJames Wright     default:
113*d68a66c4SJames Wright       return 0;
114*d68a66c4SJames Wright   }
115*d68a66c4SJames Wright }
116*d68a66c4SJames Wright 
117*d68a66c4SJames Wright // -----------------------------------------------------------------------------
118*d68a66c4SJames Wright // Create libCEED Basis from PetscTabulation
119*d68a66c4SJames Wright // -----------------------------------------------------------------------------
120*d68a66c4SJames Wright PetscErrorCode BasisCreateFromTabulation(Ceed ceed, DM dm, DMLabel domain_label, PetscInt label_value, PetscInt height, PetscInt face, PetscFE fe,
121*d68a66c4SJames Wright                                          PetscTabulation basis_tabulation, PetscQuadrature quadrature, CeedBasis *basis) {
122*d68a66c4SJames Wright   PetscInt           first_point;
123*d68a66c4SJames Wright   PetscInt           ids[1] = {label_value};
124*d68a66c4SJames Wright   DMLabel            depth_label;
125*d68a66c4SJames Wright   DMPolytopeType     cell_type;
126*d68a66c4SJames Wright   CeedElemTopology   elem_topo;
127*d68a66c4SJames Wright   PetscScalar       *q_points, *interp, *grad;
128*d68a66c4SJames Wright   const PetscScalar *q_weights;
129*d68a66c4SJames Wright   PetscDualSpace     dual_space;
130*d68a66c4SJames Wright   PetscInt           num_dual_basis_vectors;
131*d68a66c4SJames Wright   PetscInt           dim, num_comp, P, Q;
132*d68a66c4SJames Wright 
133*d68a66c4SJames Wright   PetscFunctionBeginUser;
134*d68a66c4SJames Wright   PetscCall(PetscFEGetSpatialDimension(fe, &dim));
135*d68a66c4SJames Wright   PetscCall(PetscFEGetNumComponents(fe, &num_comp));
136*d68a66c4SJames Wright   PetscCall(PetscFEGetDualSpace(fe, &dual_space));
137*d68a66c4SJames Wright   PetscCall(PetscDualSpaceGetDimension(dual_space, &num_dual_basis_vectors));
138*d68a66c4SJames Wright   P = num_dual_basis_vectors / num_comp;
139*d68a66c4SJames Wright 
140*d68a66c4SJames Wright   // Use depth label if no domain label present
141*d68a66c4SJames Wright   if (!domain_label) {
142*d68a66c4SJames Wright     PetscInt depth;
143*d68a66c4SJames Wright 
144*d68a66c4SJames Wright     PetscCall(DMPlexGetDepth(dm, &depth));
145*d68a66c4SJames Wright     PetscCall(DMPlexGetDepthLabel(dm, &depth_label));
146*d68a66c4SJames Wright     ids[0] = depth - height;
147*d68a66c4SJames Wright   }
148*d68a66c4SJames Wright 
149*d68a66c4SJames Wright   // Get cell interp, grad, and quadrature data
150*d68a66c4SJames Wright   PetscCall(DMGetFirstLabeledPoint(dm, dm, domain_label ? domain_label : depth_label, 1, ids, height, &first_point, NULL));
151*d68a66c4SJames Wright   PetscCall(DMPlexGetCellType(dm, first_point, &cell_type));
152*d68a66c4SJames Wright   elem_topo = ElemTopologyP2C(cell_type);
153*d68a66c4SJames Wright   if (!elem_topo) SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "DMPlex topology not supported");
154*d68a66c4SJames Wright   {
155*d68a66c4SJames Wright     size_t             q_points_size;
156*d68a66c4SJames Wright     const PetscScalar *q_points_petsc;
157*d68a66c4SJames Wright     PetscInt           q_dim;
158*d68a66c4SJames Wright 
159*d68a66c4SJames Wright     PetscCall(PetscQuadratureGetData(quadrature, &q_dim, NULL, &Q, &q_points_petsc, &q_weights));
160*d68a66c4SJames Wright     q_points_size = Q * dim * sizeof(CeedScalar);
161*d68a66c4SJames Wright     PetscCall(PetscCalloc(q_points_size, &q_points));
162*d68a66c4SJames Wright     for (PetscInt q = 0; q < Q; q++) {
163*d68a66c4SJames Wright       for (PetscInt d = 0; d < q_dim; d++) q_points[q * dim + d] = q_points_petsc[q * q_dim + d];
164*d68a66c4SJames Wright     }
165*d68a66c4SJames Wright   }
166*d68a66c4SJames Wright 
167*d68a66c4SJames Wright   {  // Convert to libCEED orientation
168*d68a66c4SJames Wright     PetscBool       is_simplex  = PETSC_FALSE;
169*d68a66c4SJames Wright     IS              permutation = NULL;
170*d68a66c4SJames Wright     const PetscInt *permutation_indices;
171*d68a66c4SJames Wright 
172*d68a66c4SJames Wright     PetscCall(DMPlexIsSimplex(dm, &is_simplex));
173*d68a66c4SJames Wright     if (!is_simplex) {
174*d68a66c4SJames Wright       PetscSection section;
175*d68a66c4SJames Wright 
176*d68a66c4SJames Wright       // -- Get permutation
177*d68a66c4SJames Wright       PetscCall(DMGetLocalSection(dm, &section));
178*d68a66c4SJames Wright       PetscCall(PetscSectionGetClosurePermutation(section, (PetscObject)dm, dim, num_comp * P, &permutation));
179*d68a66c4SJames Wright       PetscCall(ISGetIndices(permutation, &permutation_indices));
180*d68a66c4SJames Wright     }
181*d68a66c4SJames Wright 
182*d68a66c4SJames Wright     // -- Copy interp, grad matrices
183*d68a66c4SJames Wright     PetscCall(PetscCalloc(P * Q * sizeof(CeedScalar), &interp));
184*d68a66c4SJames Wright     PetscCall(PetscCalloc(P * Q * dim * sizeof(CeedScalar), &grad));
185*d68a66c4SJames Wright     const CeedInt c = 0;
186*d68a66c4SJames Wright     for (CeedInt q = 0; q < Q; q++) {
187*d68a66c4SJames Wright       for (CeedInt p_ceed = 0; p_ceed < P; p_ceed++) {
188*d68a66c4SJames Wright         CeedInt p_petsc = is_simplex ? (p_ceed * num_comp) : permutation_indices[p_ceed * num_comp];
189*d68a66c4SJames Wright 
190*d68a66c4SJames Wright         interp[q * P + p_ceed] = basis_tabulation->T[0][((face * Q + q) * P * num_comp + p_petsc) * num_comp + c];
191*d68a66c4SJames Wright         for (CeedInt d = 0; d < dim; d++) {
192*d68a66c4SJames Wright           grad[(d * Q + q) * P + p_ceed] = basis_tabulation->T[1][(((face * Q + q) * P * num_comp + p_petsc) * num_comp + c) * dim + d];
193*d68a66c4SJames Wright         }
194*d68a66c4SJames Wright       }
195*d68a66c4SJames Wright     }
196*d68a66c4SJames Wright 
197*d68a66c4SJames Wright     // -- Cleanup
198*d68a66c4SJames Wright     if (permutation) PetscCall(ISRestoreIndices(permutation, &permutation_indices));
199*d68a66c4SJames Wright     PetscCall(ISDestroy(&permutation));
200*d68a66c4SJames Wright   }
201*d68a66c4SJames Wright 
202*d68a66c4SJames Wright   // Finally, create libCEED basis
203*d68a66c4SJames Wright   PetscCallCeed(ceed, CeedBasisCreateH1(ceed, elem_topo, num_comp, P, Q, interp, grad, q_points, q_weights, basis));
204*d68a66c4SJames Wright   PetscCall(PetscFree(q_points));
205*d68a66c4SJames Wright   PetscCall(PetscFree(interp));
206*d68a66c4SJames Wright   PetscCall(PetscFree(grad));
207*d68a66c4SJames Wright 
208*d68a66c4SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
209*d68a66c4SJames Wright }
210*d68a66c4SJames Wright 
211*d68a66c4SJames Wright // -----------------------------------------------------------------------------
212*d68a66c4SJames Wright // Get CEED Basis from DMPlex
213*d68a66c4SJames Wright // -----------------------------------------------------------------------------
214*d68a66c4SJames Wright PetscErrorCode CreateBasisFromPlex(Ceed ceed, DM dm, DMLabel domain_label, CeedInt label_value, CeedInt height, CeedInt dm_field, CeedBasis *basis) {
215*d68a66c4SJames Wright   PetscDS         ds;
216*d68a66c4SJames Wright   PetscFE         fe;
217*d68a66c4SJames Wright   PetscQuadrature quadrature;
218*d68a66c4SJames Wright   PetscBool       is_simplex = PETSC_TRUE;
219*d68a66c4SJames Wright   PetscInt        ds_field   = -1;
220*d68a66c4SJames Wright 
221*d68a66c4SJames Wright   PetscFunctionBeginUser;
222*d68a66c4SJames Wright 
223*d68a66c4SJames Wright   // Get element information
224*d68a66c4SJames Wright   PetscCall(DMGetRegionDS(dm, domain_label, NULL, &ds, NULL));
225*d68a66c4SJames Wright   PetscCall(DMFieldToDSField(dm, domain_label, dm_field, &ds_field));
226*d68a66c4SJames Wright   PetscCall(PetscDSGetDiscretization(ds, ds_field, (PetscObject *)&fe));
227*d68a66c4SJames Wright   PetscCall(PetscFEGetHeightSubspace(fe, height, &fe));
228*d68a66c4SJames Wright   PetscCall(PetscFEGetQuadrature(fe, &quadrature));
229*d68a66c4SJames Wright 
230*d68a66c4SJames Wright   // Check if simplex or tensor-product mesh
231*d68a66c4SJames Wright   PetscCall(DMPlexIsSimplex(dm, &is_simplex));
232*d68a66c4SJames Wright 
233*d68a66c4SJames Wright   // Build libCEED basis
234*d68a66c4SJames Wright   if (is_simplex) {
235*d68a66c4SJames Wright     PetscTabulation basis_tabulation;
236*d68a66c4SJames Wright     PetscInt        num_derivatives = 1, face = 0;
237*d68a66c4SJames Wright 
238*d68a66c4SJames Wright     PetscCall(PetscFEGetCellTabulation(fe, num_derivatives, &basis_tabulation));
239*d68a66c4SJames Wright     PetscCall(BasisCreateFromTabulation(ceed, dm, domain_label, label_value, height, face, fe, basis_tabulation, quadrature, basis));
240*d68a66c4SJames Wright   } else {
241*d68a66c4SJames Wright     PetscDualSpace dual_space;
242*d68a66c4SJames Wright     PetscInt       num_dual_basis_vectors;
243*d68a66c4SJames Wright     PetscInt       dim, num_comp, P, Q;
244*d68a66c4SJames Wright 
245*d68a66c4SJames Wright     PetscCall(PetscFEGetSpatialDimension(fe, &dim));
246*d68a66c4SJames Wright     PetscCall(PetscFEGetNumComponents(fe, &num_comp));
247*d68a66c4SJames Wright     PetscCall(PetscFEGetDualSpace(fe, &dual_space));
248*d68a66c4SJames Wright     PetscCall(PetscDualSpaceGetDimension(dual_space, &num_dual_basis_vectors));
249*d68a66c4SJames Wright     P = num_dual_basis_vectors / num_comp;
250*d68a66c4SJames Wright     PetscCall(PetscQuadratureGetData(quadrature, NULL, NULL, &Q, NULL, NULL));
251*d68a66c4SJames Wright 
252*d68a66c4SJames Wright     CeedInt P_1d = (CeedInt)round(pow(P, 1.0 / dim));
253*d68a66c4SJames Wright     CeedInt Q_1d = (CeedInt)round(pow(Q, 1.0 / dim));
254*d68a66c4SJames Wright 
255*d68a66c4SJames Wright     PetscCallCeed(ceed, CeedBasisCreateTensorH1Lagrange(ceed, dim, num_comp, P_1d, Q_1d, CEED_GAUSS, basis));
256*d68a66c4SJames Wright   }
257*d68a66c4SJames Wright 
258*d68a66c4SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
259*d68a66c4SJames Wright }
260*d68a66c4SJames Wright 
261*d68a66c4SJames Wright /**
262*d68a66c4SJames Wright   @brief Setup `DM` with FE space of appropriate degree
263*d68a66c4SJames Wright 
264*d68a66c4SJames Wright   Must be followed by `DMSetupByOrderEnd_FEM`
265*d68a66c4SJames Wright 
266*d68a66c4SJames Wright   @param[in]   setup_faces     Flag to setup face geometry
267*d68a66c4SJames Wright   @param[in]   setup_coords    Flag to setup coordinate spaces
268*d68a66c4SJames Wright   @param[in]   degree          Polynomial orders of field
269*d68a66c4SJames Wright   @param[in]   coord_order     Polynomial order of coordinate basis, or `RATEL_DECIDE` for default
270*d68a66c4SJames Wright   @param[in]   q_extra         Additional quadrature order, or `RATEL_DECIDE` for default
271*d68a66c4SJames Wright   @param[in]   num_fields      Number of fields in solution vector
272*d68a66c4SJames Wright   @param[in]   field_sizes     Array of number of components for each field
273*d68a66c4SJames Wright   @param[out]  dm              `DM` to setup
274*d68a66c4SJames Wright 
275*d68a66c4SJames Wright   @return An error code: 0 - success, otherwise - failure
276*d68a66c4SJames Wright **/
277*d68a66c4SJames Wright PetscErrorCode DMSetupByOrderBegin_FEM(PetscBool setup_faces, PetscBool setup_coords, PetscInt degree, PetscInt coord_order, PetscInt q_extra,
278*d68a66c4SJames Wright                                        CeedInt num_fields, const CeedInt *field_sizes, DM dm) {
279*d68a66c4SJames Wright   PetscInt  dim, q_order = degree + q_extra;
280*d68a66c4SJames Wright   PetscBool is_simplex = PETSC_TRUE;
281*d68a66c4SJames Wright   PetscFE   fe;
282*d68a66c4SJames Wright   MPI_Comm  comm = PetscObjectComm((PetscObject)dm);
283*d68a66c4SJames Wright 
284*d68a66c4SJames Wright   PetscFunctionBeginUser;
285*d68a66c4SJames Wright   PetscCall(DMPlexIsSimplex(dm, &is_simplex));
286*d68a66c4SJames Wright 
287*d68a66c4SJames Wright   // Setup DM
288*d68a66c4SJames Wright   PetscCall(DMGetDimension(dm, &dim));
289*d68a66c4SJames Wright   for (PetscInt i = 0; i < num_fields; i++) {
290*d68a66c4SJames Wright     PetscFE  fe_face;
291*d68a66c4SJames Wright     PetscInt q_order = degree + q_extra;
292*d68a66c4SJames Wright 
293*d68a66c4SJames Wright     PetscCall(PetscFECreateLagrange(comm, dim, field_sizes[i], is_simplex, degree, q_order, &fe));
294*d68a66c4SJames Wright     if (setup_faces) PetscCall(PetscFEGetHeightSubspace(fe, 1, &fe_face));
295*d68a66c4SJames Wright     PetscCall(DMAddField(dm, NULL, (PetscObject)fe));
296*d68a66c4SJames Wright     PetscCall(PetscFEDestroy(&fe));
297*d68a66c4SJames Wright   }
298*d68a66c4SJames Wright   PetscCall(DMCreateDS(dm));
299*d68a66c4SJames Wright 
300*d68a66c4SJames Wright   // Project coordinates to enrich quadrature space
301*d68a66c4SJames Wright   if (setup_coords) {
302*d68a66c4SJames Wright     DM             dm_coord;
303*d68a66c4SJames Wright     PetscDS        ds_coord;
304*d68a66c4SJames Wright     PetscFE        fe_coord_current, fe_coord_new, fe_coord_face_new;
305*d68a66c4SJames Wright     PetscDualSpace fe_coord_dual_space;
306*d68a66c4SJames Wright     PetscInt       fe_coord_order, num_comp_coord;
307*d68a66c4SJames Wright 
308*d68a66c4SJames Wright     PetscCall(DMGetCoordinateDM(dm, &dm_coord));
309*d68a66c4SJames Wright     PetscCall(DMGetCoordinateDim(dm, &num_comp_coord));
310*d68a66c4SJames Wright     PetscCall(DMGetRegionDS(dm_coord, NULL, NULL, &ds_coord, NULL));
311*d68a66c4SJames Wright     PetscCall(PetscDSGetDiscretization(ds_coord, 0, (PetscObject *)&fe_coord_current));
312*d68a66c4SJames Wright     PetscCall(PetscFEGetDualSpace(fe_coord_current, &fe_coord_dual_space));
313*d68a66c4SJames Wright     PetscCall(PetscDualSpaceGetOrder(fe_coord_dual_space, &fe_coord_order));
314*d68a66c4SJames Wright 
315*d68a66c4SJames Wright     // Create FE for coordinates
316*d68a66c4SJames Wright     PetscCheck(fe_coord_order == 1 || coord_order == 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_USER_INPUT,
317*d68a66c4SJames Wright                "Only linear mesh geometry supported. Recieved %d order", fe_coord_order);
318*d68a66c4SJames Wright     PetscCall(PetscFECreateLagrange(comm, dim, num_comp_coord, is_simplex, fe_coord_order, q_order, &fe_coord_new));
319*d68a66c4SJames Wright     if (setup_faces) PetscCall(PetscFEGetHeightSubspace(fe_coord_new, 1, &fe_coord_face_new));
320*d68a66c4SJames Wright     PetscCall(DMProjectCoordinates(dm, fe_coord_new));
321*d68a66c4SJames Wright     PetscCall(PetscFEDestroy(&fe_coord_new));
322*d68a66c4SJames Wright   }
323*d68a66c4SJames Wright 
324*d68a66c4SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
325*d68a66c4SJames Wright }
326*d68a66c4SJames Wright 
327*d68a66c4SJames Wright /**
328*d68a66c4SJames Wright   @brief Finish setting up `DM`
329*d68a66c4SJames Wright 
330*d68a66c4SJames Wright   Must be called after `DMSetupByOrderBegin_FEM` and all strong BCs have be declared via `DMAddBoundaries`.
331*d68a66c4SJames Wright 
332*d68a66c4SJames Wright   @param[in]   setup_coords    Flag to setup coordinate spaces
333*d68a66c4SJames Wright   @param[out]  dm              `DM` to setup
334*d68a66c4SJames Wright 
335*d68a66c4SJames Wright   @return An error code: 0 - success, otherwise - failure
336*d68a66c4SJames Wright **/
337*d68a66c4SJames Wright PetscErrorCode DMSetupByOrderEnd_FEM(PetscBool setup_coords, DM dm) {
338*d68a66c4SJames Wright   PetscBool is_simplex;
339*d68a66c4SJames Wright   PetscFunctionBeginUser;
340*d68a66c4SJames Wright 
341*d68a66c4SJames Wright   PetscCall(DMPlexIsSimplex(dm, &is_simplex));
342*d68a66c4SJames Wright   // Set tensor permutation if needed
343*d68a66c4SJames Wright   if (!is_simplex) {
344*d68a66c4SJames Wright     PetscCall(DMPlexSetClosurePermutationTensor(dm, PETSC_DETERMINE, NULL));
345*d68a66c4SJames Wright     if (setup_coords) {
346*d68a66c4SJames Wright       DM dm_coord;
347*d68a66c4SJames Wright 
348*d68a66c4SJames Wright       PetscCall(DMGetCoordinateDM(dm, &dm_coord));
349*d68a66c4SJames Wright       PetscCall(DMPlexSetClosurePermutationTensor(dm_coord, PETSC_DETERMINE, NULL));
350*d68a66c4SJames Wright     }
351*d68a66c4SJames Wright   }
352*d68a66c4SJames Wright 
353*d68a66c4SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
354*d68a66c4SJames Wright }
355*d68a66c4SJames Wright 
356*d68a66c4SJames Wright /**
357*d68a66c4SJames Wright   @brief Setup `DM` with FE space of appropriate degree with no boundary conditions
358*d68a66c4SJames Wright 
359*d68a66c4SJames Wright   Calls `DMSetupByOrderBegin_FEM` and `DMSetupByOrderEnd_FEM` successively
360*d68a66c4SJames Wright 
361*d68a66c4SJames Wright   @param[in]   setup_faces     Flag to setup face geometry
362*d68a66c4SJames Wright   @param[in]   setup_coords    Flag to setup coordinate spaces
363*d68a66c4SJames Wright   @param[in]   degree          Polynomial orders of field
364*d68a66c4SJames Wright   @param[in]   coord_order     Polynomial order of coordinate basis, or `RATEL_DECIDE` for default
365*d68a66c4SJames Wright   @param[in]   q_extra         Additional quadrature order, or `RATEL_DECIDE` for default
366*d68a66c4SJames Wright   @param[in]   num_fields      Number of fields in solution vector
367*d68a66c4SJames Wright   @param[in]   field_sizes     Array of number of components for each field
368*d68a66c4SJames Wright   @param[out]  dm              `DM` to setup
369*d68a66c4SJames Wright 
370*d68a66c4SJames Wright   @return An error code: 0 - success, otherwise - failure
371*d68a66c4SJames Wright **/
372*d68a66c4SJames Wright PetscErrorCode DMSetupByOrder_FEM(PetscBool setup_faces, PetscBool setup_coords, PetscInt degree, PetscInt coord_order, PetscInt q_extra,
373*d68a66c4SJames Wright                                   CeedInt num_fields, const CeedInt *field_sizes, DM dm) {
374*d68a66c4SJames Wright   PetscFunctionBeginUser;
375*d68a66c4SJames Wright   PetscCall(DMSetupByOrderBegin_FEM(setup_faces, setup_coords, degree, coord_order, q_extra, num_fields, field_sizes, dm));
376*d68a66c4SJames Wright   PetscCall(DMSetupByOrderEnd_FEM(setup_coords, dm));
377*d68a66c4SJames Wright   PetscFunctionReturn(PETSC_SUCCESS);
378*d68a66c4SJames Wright }
379