xref: /libCEED/examples/fluids/navierstokes.c (revision a3ddc43aba87db7759cfa2ade20b7fcad161b07c)
1 // Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at
2 // the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights
3 // reserved. See files LICENSE and NOTICE for details.
4 //
5 // This file is part of CEED, a collection of benchmarks, miniapps, software
6 // libraries and APIs for efficient high-order finite element and spectral
7 // element discretizations for exascale applications. For more information and
8 // source code availability see http://github.com/ceed.
9 //
10 // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC,
11 // a collaborative effort of two U.S. Department of Energy organizations (Office
12 // of Science and the National Nuclear Security Administration) responsible for
13 // the planning and preparation of a capable exascale ecosystem, including
14 // software, applications, hardware, advanced system engineering and early
15 // testbed platforms, in support of the nation's exascale computing imperative.
16 
17 //                        libCEED + PETSc Example: Navier-Stokes
18 //
19 // This example demonstrates a simple usage of libCEED with PETSc to solve a
20 // Navier-Stokes problem.
21 //
22 // The code is intentionally "raw", using only low-level communication
23 // primitives.
24 //
25 // Build with:
26 //
27 //     make [PETSC_DIR=</path/to/petsc>] [CEED_DIR=</path/to/libceed>] navierstokes
28 //
29 // Sample runs:
30 //
31 //     ./navierstokes -ceed /cpu/self -problem density_current -degree 1
32 //     ./navierstokes -ceed /gpu/cuda -problem advection -degree 1
33 //
34 //TESTARGS(name="dc_explicit") -ceed {ceed_resource} -test -degree 3 -dm_plex_box_faces 1,1,2 -units_kilogram 1e-9 -lx 125 -ly 125 -lz 250 -center 62.5,62.5,187.5 -rc 100. -thetaC -35. -ts_dt 1e-3 -compare_final_state_atol 1E-11 -compare_final_state_filename examples/fluids/tests-output/fluids-navierstokes-dc-explicit.bin
35 //TESTARGS(name="dc_implicit_stab_none") -ceed {ceed_resource} -test -degree 3 -dm_plex_box_faces 1,1,2 -units_kilogram 1e-9 -lx 125 -ly 125 -lz 250 -center 62.5,62.5,187.5 -rc 100. -thetaC -35. -ksp_atol 1e-4 -ksp_rtol 1e-3 -ksp_type bcgs -snes_atol 1e-3 -snes_lag_jacobian 100 -snes_lag_jacobian_persists -snes_mf_operator -ts_dt 1e-3 -implicit -ts_type alpha -compare_final_state_atol 5E-4 -compare_final_state_filename examples/fluids/tests-output/fluids-navierstokes-dc-implicit-stab-none.bin
36 //TESTARGS(name="adv_rotation_explicit_strong") -ceed {ceed_resource} -test -problem advection -strong_form 1 -degree 3 -dm_plex_box_faces 1,1,2 -units_kilogram 1e-9 -lx 125 -ly 125 -lz 250 -center 62.5,62.5,187.5 -rc 100. -ts_dt 1e-3 -compare_final_state_atol 1E-11 -compare_final_state_filename examples/fluids/tests-output/fluids-navierstokes-adv-rotation-explicit-strong.bin
37 //TESTARGS(name="adv_rotation_implicit_stab_supg") -ceed {ceed_resource} -test -problem advection -CtauS .3 -stab supg -degree 3 -dm_plex_box_faces 1,1,2 -units_kilogram 1e-9 -lx 125 -ly 125 -lz 250 -center 62.5,62.5,187.5 -rc 100. -ksp_atol 1e-4 -ksp_rtol 1e-3 -ksp_type bcgs -snes_atol 1e-3 -snes_lag_jacobian 100 -snes_lag_jacobian_persists -snes_mf_operator -ts_dt 1e-3 -implicit -ts_type alpha -compare_final_state_atol 5E-4 -compare_final_state_filename examples/fluids/tests-output/fluids-navierstokes-adv-rotation-implicit-stab-supg.bin
38 //TESTARGS(name="adv_translation_implicit_stab_su") -ceed {ceed_resource} -test -problem advection -CtauS .3 -stab su -degree 3 -dm_plex_box_faces 1,1,2 -units_kilogram 1e-9 -lx 125 -ly 125 -lz 250 -center 62.5,62.5,187.5 -rc 100. -ksp_atol 1e-4 -ksp_rtol 1e-3 -ksp_type bcgs -snes_atol 1e-3 -snes_lag_jacobian 100 -snes_lag_jacobian_persists -snes_mf_operator -ts_dt 1e-3 -implicit -ts_type alpha -problem_advection_wind translation -problem_advection_wind_translation .53,-1.33,-2.65 -compare_final_state_atol 5E-4 -compare_final_state_filename examples/fluids/tests-output/fluids-navierstokes-adv-translation-implicit-stab-su.bin
39 //TESTARGS(name="adv2d_rotation_explicit_strong") -ceed {ceed_resource} -test -problem advection2d -strong_form 1 -degree 3 -dm_plex_box_faces 1,1,2 -units_kilogram 1e-9 -lx 125 -ly 125 -lz 250 -center 62.5,62.5,187.5 -rc 100. -ts_dt 1e-3 -compare_final_state_atol 1E-11 -compare_final_state_filename examples/fluids/tests-output/fluids-navierstokes-adv2d-rotation-explicit-strong.bin
40 //TESTARGS(name="adv2d_rotation_implicit_stab_supg") -ceed {ceed_resource} -test -problem advection2d -CtauS .3 -stab supg -degree 3 -dm_plex_box_faces 1,1,2 -units_kilogram 1e-9 -lx 125 -ly 125 -lz 250 -center 62.5,62.5,187.5 -rc 100. -ksp_atol 1e-4 -ksp_rtol 1e-3 -ksp_type bcgs -snes_atol 1e-3 -snes_lag_jacobian 100 -snes_lag_jacobian_persists -snes_mf_operator -ts_dt 1e-3 -implicit -ts_type alpha -compare_final_state_atol 5E-4 -compare_final_state_filename examples/fluids/tests-output/fluids-navierstokes-adv2d-rotation-implicit-stab-supg.bin
41 //TESTARGS(name="adv2d_translation_implicit_stab_su") -ceed {ceed_resource} -test -problem advection2d -CtauS .3 -stab su -degree 3 -dm_plex_box_faces 1,1,2 -units_kilogram 1e-9 -lx 125 -ly 125 -lz 250 -center 62.5,62.5,187.5 -rc 100. -ksp_atol 1e-4 -ksp_rtol 1e-3 -ksp_type bcgs -snes_atol 1e-3 -snes_lag_jacobian 100 -snes_lag_jacobian_persists -snes_mf_operator -ts_dt 1e-3 -implicit -ts_type alpha -problem_advection_wind translation -problem_advection_wind_translation .53,-1.33,0 -compare_final_state_atol 5E-4 -compare_final_state_filename examples/fluids/tests-output/fluids-navierstokes-adv2d-translation-implicit-stab-su.bin
42 
43 /// @file
44 /// Navier-Stokes example using PETSc
45 
46 const char help[] = "Solve Navier-Stokes using PETSc and libCEED\n";
47 
48 #include <ceed.h>
49 #include <petscdmplex.h>
50 #include <petscsys.h>
51 #include <petscts.h>
52 #include <stdbool.h>
53 #include "advection.h"
54 #include "advection2d.h"
55 #include "common.h"
56 #include "euler-vortex.h"
57 #include "densitycurrent.h"
58 #include "setup-boundary.h"
59 
60 #if PETSC_VERSION_LT(3,14,0)
61 #  define DMPlexGetClosureIndices(a,b,c,d,e,f,g,h,i) DMPlexGetClosureIndices(a,b,c,d,f,g,i)
62 #  define DMPlexRestoreClosureIndices(a,b,c,d,e,f,g,h,i) DMPlexRestoreClosureIndices(a,b,c,d,f,g,i)
63 #endif
64 
65 #if PETSC_VERSION_LT(3,14,0)
66 #  define DMAddBoundary(a,b,c,d,e,f,g,h,i,j,k,l) DMAddBoundary(a,b,c,d,e,f,g,h,j,k,l)
67 #endif
68 
69 // MemType Options
70 static const char *const memTypes[] = {
71   "host",
72   "device",
73   "memType", "CEED_MEM_", NULL
74 };
75 
76 // Problem Options
77 typedef enum {
78   NS_DENSITY_CURRENT = 0,
79   NS_ADVECTION = 1,
80   NS_ADVECTION2D = 2,
81   NS_EULER_VORTEX = 3,
82 } problemType;
83 static const char *const problemTypes[] = {
84   "density_current",
85   "advection",
86   "advection2d",
87   "euler_vortex",
88   "problemType", "NS_", NULL
89 };
90 
91 // Wind Options for Advection
92 typedef enum {
93   ADVECTION_WIND_ROTATION = 0,
94   ADVECTION_WIND_TRANSLATION = 1,
95 } WindType;
96 static const char *const WindTypes[] = {
97   "rotation",
98   "translation",
99   "WindType", "ADVECTION_WIND_", NULL
100 };
101 
102 typedef enum {
103   STAB_NONE = 0,
104   STAB_SU = 1,   // Streamline Upwind
105   STAB_SUPG = 2, // Streamline Upwind Petrov-Galerkin
106 } StabilizationType;
107 static const char *const StabilizationTypes[] = {
108   "none",
109   "SU",
110   "SUPG",
111   "StabilizationType", "STAB_", NULL
112 };
113 
114 typedef enum {
115   EULER_TEST_NONE = 0,
116   EULER_TEST_1 = 1,
117   EULER_TEST_2 = 2,
118   EULER_TEST_3 = 3,
119   EULER_TEST_4 = 4,
120 } EulerTestType;
121 static const char *const EulerTestTypes[] = {
122   "none",
123   "t1",
124   "t2",
125   "t3",
126   "t4",
127   "EulerTestType", "EULER_TEST_", NULL
128 };
129 
130 // Problem specific data
131 typedef struct {
132   CeedInt dim, qdatasizeVol, qdatasizeSur;
133   CeedQFunctionUser setupVol, setupSur, ics, applyVol_rhs, applyVol_ifunction,
134                     applySur;
135   PetscErrorCode (*bc)(PetscInt, PetscReal, const PetscReal[], PetscInt,
136                        PetscScalar[], void *);
137   const char *setupVol_loc, *setupSur_loc, *ics_loc, *applyVol_rhs_loc,
138         *applyVol_ifunction_loc, *applySur_loc;
139   const bool non_zero_time;
140 } problemData;
141 
142 problemData problemOptions[] = {
143   [NS_DENSITY_CURRENT] = {
144     .dim                       = 3,
145     .qdatasizeVol              = 10,
146     .qdatasizeSur              = 4,
147     .setupVol                  = Setup,
148     .setupVol_loc              = Setup_loc,
149     .setupSur                  = SetupBoundary,
150     .setupSur_loc              = SetupBoundary_loc,
151     .ics                       = ICsDC,
152     .ics_loc                   = ICsDC_loc,
153     .applyVol_rhs              = DC,
154     .applyVol_rhs_loc          = DC_loc,
155     .applyVol_ifunction        = IFunction_DC,
156     .applyVol_ifunction_loc    = IFunction_DC_loc,
157     .bc                        = Exact_DC,
158     .non_zero_time             = PETSC_FALSE,
159   },
160   [NS_ADVECTION] = {
161     .dim                       = 3,
162     .qdatasizeVol              = 10,
163     .qdatasizeSur              = 4,
164     .setupVol                  = Setup,
165     .setupVol_loc              = Setup_loc,
166     .setupSur                  = SetupBoundary,
167     .setupSur_loc              = SetupBoundary_loc,
168     .ics                       = ICsAdvection,
169     .ics_loc                   = ICsAdvection_loc,
170     .applyVol_rhs              = Advection,
171     .applyVol_rhs_loc          = Advection_loc,
172     .applyVol_ifunction        = IFunction_Advection,
173     .applyVol_ifunction_loc    = IFunction_Advection_loc,
174     .applySur                  = Advection_Sur,
175     .applySur_loc              = Advection_Sur_loc,
176     .bc                        = Exact_Advection,
177     .non_zero_time             = PETSC_FALSE,
178   },
179   [NS_ADVECTION2D] = {
180     .dim                       = 2,
181     .qdatasizeVol              = 5,
182     .qdatasizeSur              = 3,
183     .setupVol                  = Setup2d,
184     .setupVol_loc              = Setup2d_loc,
185     .setupSur                  = SetupBoundary2d,
186     .setupSur_loc              = SetupBoundary2d_loc,
187     .ics                       = ICsAdvection2d,
188     .ics_loc                   = ICsAdvection2d_loc,
189     .applyVol_rhs              = Advection2d,
190     .applyVol_rhs_loc          = Advection2d_loc,
191     .applyVol_ifunction        = IFunction_Advection2d,
192     .applyVol_ifunction_loc    = IFunction_Advection2d_loc,
193     .applySur                  = Advection2d_Sur,
194     .applySur_loc              = Advection2d_Sur_loc,
195     .bc                        = Exact_Advection2d,
196     .non_zero_time             = PETSC_TRUE,
197   },
198   [NS_EULER_VORTEX] = {
199     .dim                       = 3,
200     .qdatasizeVol              = 10,
201     .qdatasizeSur              = 4,
202     .setupVol                  = Setup,
203     .setupVol_loc              = Setup_loc,
204     .setupSur                  = SetupBoundary,
205     .setupSur_loc              = SetupBoundary_loc,
206     .ics                       = ICsEuler,
207     .ics_loc                   = ICsEuler_loc,
208     .applyVol_rhs              = Euler,
209     .applyVol_rhs_loc          = Euler_loc,
210     .applySur                  = Euler_Sur,
211     .applySur_loc              = Euler_Sur_loc,
212     .bc                        = Exact_Euler,
213     .non_zero_time             = PETSC_TRUE,
214   },
215 };
216 
217 // PETSc user data
218 typedef struct User_ *User;
219 typedef struct Units_ *Units;
220 
221 struct User_ {
222   MPI_Comm comm;
223   PetscInt outputfreq;
224   DM dm;
225   DM dmviz;
226   Mat interpviz;
227   Ceed ceed;
228   Units units;
229   CeedVector qceed, qdotceed, gceed;
230   CeedOperator op_rhs_vol, op_rhs, op_ifunction_vol, op_ifunction;
231   Vec M;
232   char outputdir[PETSC_MAX_PATH_LEN];
233   PetscInt contsteps;
234   EulerContext ctxEulerData;
235 };
236 
237 struct Units_ {
238   // fundamental units
239   PetscScalar meter;
240   PetscScalar kilogram;
241   PetscScalar second;
242   PetscScalar Kelvin;
243   // derived units
244   PetscScalar Pascal;
245   PetscScalar JperkgK;
246   PetscScalar mpersquareds;
247   PetscScalar WpermK;
248   PetscScalar kgpercubicm;
249   PetscScalar kgpersquaredms;
250   PetscScalar Joulepercubicm;
251   PetscScalar Joule;
252 };
253 
254 typedef struct SimpleBC_ *SimpleBC;
255 struct SimpleBC_ {
256   PetscInt nwall, nslip[3];
257   PetscInt walls[6], slips[3][6];
258   PetscBool userbc;
259 };
260 
261 // Essential BC dofs are encoded in closure indices as -(i+1).
262 static PetscInt Involute(PetscInt i) {
263   return i >= 0 ? i : -(i+1);
264 }
265 
266 // Utility function to create local CEED restriction
267 static PetscErrorCode CreateRestrictionFromPlex(Ceed ceed, DM dm, CeedInt P,
268     CeedInt height, DMLabel domainLabel, CeedInt value,
269     CeedElemRestriction *Erestrict) {
270 
271   PetscSection section;
272   PetscInt p, Nelem, Ndof, *erestrict, eoffset, nfields, dim, depth;
273   DMLabel depthLabel;
274   IS depthIS, iterIS;
275   Vec Uloc;
276   const PetscInt *iterIndices;
277   PetscErrorCode ierr;
278 
279   PetscFunctionBeginUser;
280   ierr = DMGetDimension(dm, &dim); CHKERRQ(ierr);
281   dim -= height;
282   ierr = DMGetLocalSection(dm, &section); CHKERRQ(ierr);
283   ierr = PetscSectionGetNumFields(section, &nfields); CHKERRQ(ierr);
284   PetscInt ncomp[nfields], fieldoff[nfields+1];
285   fieldoff[0] = 0;
286   for (PetscInt f=0; f<nfields; f++) {
287     ierr = PetscSectionGetFieldComponents(section, f, &ncomp[f]); CHKERRQ(ierr);
288     fieldoff[f+1] = fieldoff[f] + ncomp[f];
289   }
290 
291   ierr = DMPlexGetDepth(dm, &depth); CHKERRQ(ierr);
292   ierr = DMPlexGetDepthLabel(dm, &depthLabel); CHKERRQ(ierr);
293   ierr = DMLabelGetStratumIS(depthLabel, depth - height, &depthIS); CHKERRQ(ierr);
294   if (domainLabel) {
295     IS domainIS;
296     ierr = DMLabelGetStratumIS(domainLabel, value, &domainIS); CHKERRQ(ierr);
297     if (domainIS) { // domainIS is non-empty
298       ierr = ISIntersect(depthIS, domainIS, &iterIS); CHKERRQ(ierr);
299       ierr = ISDestroy(&domainIS); CHKERRQ(ierr);
300     } else { // domainIS is NULL (empty)
301       iterIS = NULL;
302     }
303     ierr = ISDestroy(&depthIS); CHKERRQ(ierr);
304   } else {
305     iterIS = depthIS;
306   }
307   if (iterIS) {
308     ierr = ISGetLocalSize(iterIS, &Nelem); CHKERRQ(ierr);
309     ierr = ISGetIndices(iterIS, &iterIndices); CHKERRQ(ierr);
310   } else {
311     Nelem = 0;
312     iterIndices = NULL;
313   }
314   ierr = PetscMalloc1(Nelem*PetscPowInt(P, dim), &erestrict); CHKERRQ(ierr);
315   for (p=0,eoffset=0; p<Nelem; p++) {
316     PetscInt c = iterIndices[p];
317     PetscInt numindices, *indices, nnodes;
318     ierr = DMPlexGetClosureIndices(dm, section, section, c, PETSC_TRUE,
319                                    &numindices, &indices, NULL, NULL);
320     CHKERRQ(ierr);
321     bool flip = false;
322     if (height > 0) {
323       PetscInt numCells, numFaces, start = -1;
324       const PetscInt *orients, *faces, *cells;
325       ierr = DMPlexGetSupport(dm, c, &cells); CHKERRQ(ierr);
326       ierr = DMPlexGetSupportSize(dm, c, &numCells); CHKERRQ(ierr);
327       if (numCells != 1) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP,
328                                     "Expected one cell in support of exterior face, but got %D cells",
329                                     numCells);
330       ierr = DMPlexGetCone(dm, cells[0], &faces); CHKERRQ(ierr);
331       ierr = DMPlexGetConeSize(dm, cells[0], &numFaces); CHKERRQ(ierr);
332       for (PetscInt i=0; i<numFaces; i++) {if (faces[i] == c) start = i;}
333       if (start < 0) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_CORRUPT,
334                                 "Could not find face %D in cone of its support",
335                                 c);
336       ierr = DMPlexGetConeOrientation(dm, cells[0], &orients); CHKERRQ(ierr);
337       if (orients[start] < 0) flip = true;
338     }
339     if (numindices % fieldoff[nfields]) SETERRQ1(PETSC_COMM_SELF,
340           PETSC_ERR_ARG_INCOMP, "Number of closure indices not compatible with Cell %D",
341           c);
342     nnodes = numindices / fieldoff[nfields];
343     for (PetscInt i=0; i<nnodes; i++) {
344       PetscInt ii = i;
345       if (flip) {
346         if (P == nnodes) ii = nnodes - 1 - i;
347         else if (P*P == nnodes) {
348           PetscInt row = i / P, col = i % P;
349           ii = row + col * P;
350         } else SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_SUP,
351                           "No support for flipping point with %D nodes != P (%D) or P^2",
352                           nnodes, P);
353       }
354       // Check that indices are blocked by node and thus can be coalesced as a single field with
355       // fieldoff[nfields] = sum(ncomp) components.
356       for (PetscInt f=0; f<nfields; f++) {
357         for (PetscInt j=0; j<ncomp[f]; j++) {
358           if (Involute(indices[fieldoff[f]*nnodes + ii*ncomp[f] + j])
359               != Involute(indices[ii*ncomp[0]]) + fieldoff[f] + j)
360             SETERRQ4(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP,
361                      "Cell %D closure indices not interlaced for node %D field %D component %D",
362                      c, ii, f, j);
363         }
364       }
365       // Essential boundary conditions are encoded as -(loc+1), but we don't care so we decode.
366       PetscInt loc = Involute(indices[ii*ncomp[0]]);
367       erestrict[eoffset++] = loc;
368     }
369     ierr = DMPlexRestoreClosureIndices(dm, section, section, c, PETSC_TRUE,
370                                        &numindices, &indices, NULL, NULL);
371     CHKERRQ(ierr);
372   }
373   if (eoffset != Nelem*PetscPowInt(P, dim))
374     SETERRQ3(PETSC_COMM_SELF, PETSC_ERR_LIB,
375              "ElemRestriction of size (%D,%D) initialized %D nodes", Nelem,
376              PetscPowInt(P, dim),eoffset);
377   if (iterIS) {
378     ierr = ISRestoreIndices(iterIS, &iterIndices); CHKERRQ(ierr);
379   }
380   ierr = ISDestroy(&iterIS); CHKERRQ(ierr);
381 
382   ierr = DMGetLocalVector(dm, &Uloc); CHKERRQ(ierr);
383   ierr = VecGetLocalSize(Uloc, &Ndof); CHKERRQ(ierr);
384   ierr = DMRestoreLocalVector(dm, &Uloc); CHKERRQ(ierr);
385   CeedElemRestrictionCreate(ceed, Nelem, PetscPowInt(P, dim), fieldoff[nfields],
386                             1, Ndof, CEED_MEM_HOST, CEED_COPY_VALUES, erestrict,
387                             Erestrict);
388   ierr = PetscFree(erestrict); CHKERRQ(ierr);
389   PetscFunctionReturn(0);
390 }
391 
392 // Utility function to get Ceed Restriction for each domain
393 static PetscErrorCode GetRestrictionForDomain(Ceed ceed, DM dm, CeedInt height,
394     DMLabel domainLabel, PetscInt value, CeedInt P, CeedInt Q, CeedInt qdatasize,
395     CeedElemRestriction *restrictq, CeedElemRestriction *restrictx,
396     CeedElemRestriction *restrictqdi) {
397 
398   DM dmcoord;
399   CeedInt dim, localNelem;
400   CeedInt Qdim;
401   PetscErrorCode ierr;
402 
403   PetscFunctionBeginUser;
404   ierr = DMGetDimension(dm, &dim); CHKERRQ(ierr);
405   dim -= height;
406   Qdim = CeedIntPow(Q, dim);
407   ierr = DMGetCoordinateDM(dm, &dmcoord); CHKERRQ(ierr);
408   ierr = DMPlexSetClosurePermutationTensor(dmcoord, PETSC_DETERMINE, NULL);
409   CHKERRQ(ierr);
410   ierr = CreateRestrictionFromPlex(ceed, dm, P, height, domainLabel, value,
411                                    restrictq);
412   CHKERRQ(ierr);
413   ierr = CreateRestrictionFromPlex(ceed, dmcoord, 2, height, domainLabel, value,
414                                    restrictx);
415   CHKERRQ(ierr);
416   CeedElemRestrictionGetNumElements(*restrictq, &localNelem);
417   CeedElemRestrictionCreateStrided(ceed, localNelem, Qdim,
418                                    qdatasize, qdatasize*localNelem*Qdim,
419                                    CEED_STRIDES_BACKEND, restrictqdi);
420   PetscFunctionReturn(0);
421 }
422 
423 // Utility function to create CEED Composite Operator for the entire domain
424 static PetscErrorCode CreateOperatorForDomain(Ceed ceed, DM dm, SimpleBC bc,
425     problemType problemChoice, WindType wind_type, CeedOperator op_applyVol,
426     CeedQFunction qf_applySur, CeedQFunction qf_setupSur,CeedInt height,
427     CeedInt numP_Sur, CeedInt numQ_Sur, CeedInt qdatasizeSur, CeedInt NqptsSur,
428     CeedBasis basisxSur, CeedBasis basisqSur, CeedOperator *op_apply) {
429 
430   CeedInt dim, nFace;
431   PetscInt lsize;
432   Vec Xloc;
433   CeedVector xcorners;
434   DMLabel domainLabel;
435   PetscScalar *x;
436   PetscErrorCode ierr;
437 
438   PetscFunctionBeginUser;
439   // Composite Operaters
440   CeedCompositeOperatorCreate(ceed, op_apply);
441   // --Apply a Sub-Operator for the volume
442   CeedCompositeOperatorAddSub(*op_apply, op_applyVol);
443 
444   // Required data for in/outflow BCs
445   ierr = DMGetCoordinatesLocal(dm, &Xloc); CHKERRQ(ierr);
446   ierr = VecGetLocalSize(Xloc, &lsize); CHKERRQ(ierr);
447   ierr = CeedVectorCreate(ceed, lsize, &xcorners); CHKERRQ(ierr);
448   ierr = VecGetArray(Xloc, &x); CHKERRQ(ierr);
449   CeedVectorSetArray(xcorners, CEED_MEM_HOST, CEED_USE_POINTER, x);
450   ierr = DMGetLabel(dm, "Face Sets", &domainLabel); CHKERRQ(ierr);
451   ierr = DMGetDimension(dm, &dim); CHKERRQ(ierr);
452 
453   if (wind_type == ADVECTION_WIND_TRANSLATION
454       || problemChoice == NS_EULER_VORTEX) {
455     // Ignore wall and slip BCs
456     bc->nwall = bc->nslip[0] = bc->nslip[1] = 0;
457     if (wind_type == ADVECTION_WIND_TRANSLATION) bc->nslip[2] = 0;
458 
459     // Set number of faces
460     if (dim == 2) nFace = 4;
461     if (dim == 3) nFace = 6;
462 
463     // Create CEED Operator for each boundary face
464     PetscInt localNelemSur[6];
465     CeedVector qdataSur[6];
466     CeedOperator op_setupSur[6], op_applySur[6];
467     CeedElemRestriction restrictxSur[6], restrictqSur[6], restrictqdiSur[6];
468 
469     for (CeedInt i=0; i<nFace; i++) {
470       ierr = GetRestrictionForDomain(ceed, dm, height, domainLabel, i+1, numP_Sur,
471                                      numQ_Sur, qdatasizeSur, &restrictqSur[i],
472                                      &restrictxSur[i], &restrictqdiSur[i]);
473       CHKERRQ(ierr);
474       // Create the CEED vectors that will be needed in Boundary setup
475       CeedElemRestrictionGetNumElements(restrictqSur[i], &localNelemSur[i]);
476       CeedVectorCreate(ceed, qdatasizeSur*localNelemSur[i]*NqptsSur,
477                        &qdataSur[i]);
478       // Create the operator that builds the quadrature data for the Boundary operator
479       CeedOperatorCreate(ceed, qf_setupSur, NULL, NULL, &op_setupSur[i]);
480       CeedOperatorSetField(op_setupSur[i], "dx", restrictxSur[i], basisxSur,
481                            CEED_VECTOR_ACTIVE);
482       CeedOperatorSetField(op_setupSur[i], "weight", CEED_ELEMRESTRICTION_NONE,
483                            basisxSur, CEED_VECTOR_NONE);
484       CeedOperatorSetField(op_setupSur[i], "qdataSur", restrictqdiSur[i],
485                            CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
486       // Create Boundary operator
487       CeedOperatorCreate(ceed, qf_applySur, NULL, NULL, &op_applySur[i]);
488       CeedOperatorSetField(op_applySur[i], "q", restrictqSur[i], basisqSur,
489                            CEED_VECTOR_ACTIVE);
490       CeedOperatorSetField(op_applySur[i], "qdataSur", restrictqdiSur[i],
491                            CEED_BASIS_COLLOCATED, qdataSur[i]);
492       CeedOperatorSetField(op_applySur[i], "x", restrictxSur[i], basisxSur,
493                            xcorners);
494       CeedOperatorSetField(op_applySur[i], "v", restrictqSur[i], basisqSur,
495                            CEED_VECTOR_ACTIVE);
496       // Apply CEED operator for Boundary setup
497       CeedOperatorApply(op_setupSur[i], xcorners, qdataSur[i],
498                         CEED_REQUEST_IMMEDIATE);
499       // --Apply Sub-Operator for the Boundary
500       CeedCompositeOperatorAddSub(*op_apply, op_applySur[i]);
501     }
502     CeedVectorDestroy(&xcorners);
503   }
504   PetscFunctionReturn(0);
505 }
506 
507 static int CreateVectorFromPetscVec(Ceed ceed, Vec p, CeedVector *v) {
508   PetscErrorCode ierr;
509   PetscInt m;
510 
511   PetscFunctionBeginUser;
512   ierr = VecGetLocalSize(p, &m); CHKERRQ(ierr);
513   ierr = CeedVectorCreate(ceed, m, v); CHKERRQ(ierr);
514   PetscFunctionReturn(0);
515 }
516 
517 static int VectorPlacePetscVec(CeedVector c, Vec p) {
518   PetscErrorCode ierr;
519   PetscInt mceed, mpetsc;
520   PetscScalar *a;
521 
522   PetscFunctionBeginUser;
523   ierr = CeedVectorGetLength(c, &mceed); CHKERRQ(ierr);
524   ierr = VecGetLocalSize(p, &mpetsc); CHKERRQ(ierr);
525   if (mceed != mpetsc) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP,
526                                   "Cannot place PETSc Vec of length %D in CeedVector of length %D",
527                                   mpetsc, mceed);
528   ierr = VecGetArray(p, &a); CHKERRQ(ierr);
529   CeedVectorSetArray(c, CEED_MEM_HOST, CEED_USE_POINTER, a);
530   PetscFunctionReturn(0);
531 }
532 
533 static PetscErrorCode DMPlexInsertBoundaryValues_NS(DM dm,
534     PetscBool insertEssential, Vec Qloc, PetscReal time, Vec faceGeomFVM,
535     Vec cellGeomFVM, Vec gradFVM) {
536   PetscErrorCode ierr;
537   Vec Qbc;
538 
539   PetscFunctionBegin;
540   ierr = DMGetNamedLocalVector(dm, "Qbc", &Qbc); CHKERRQ(ierr);
541   ierr = VecAXPY(Qloc, 1., Qbc); CHKERRQ(ierr);
542   ierr = DMRestoreNamedLocalVector(dm, "Qbc", &Qbc); CHKERRQ(ierr);
543   PetscFunctionReturn(0);
544 }
545 
546 // This is the RHS of the ODE, given as u_t = G(t,u)
547 // This function takes in a state vector Q and writes into G
548 static PetscErrorCode RHS_NS(TS ts, PetscReal t, Vec Q, Vec G, void *userData) {
549   PetscErrorCode ierr;
550   User user = *(User *)userData;
551   PetscScalar *q, *g;
552   Vec Qloc, Gloc;
553 
554   // Global-to-local
555   PetscFunctionBeginUser;
556   user->ctxEulerData->currentTime = t;
557   ierr = DMGetLocalVector(user->dm, &Qloc); CHKERRQ(ierr);
558   ierr = DMGetLocalVector(user->dm, &Gloc); CHKERRQ(ierr);
559   ierr = VecZeroEntries(Qloc); CHKERRQ(ierr);
560   ierr = DMGlobalToLocal(user->dm, Q, INSERT_VALUES, Qloc); CHKERRQ(ierr);
561   ierr = DMPlexInsertBoundaryValues(user->dm, PETSC_TRUE, Qloc, 0.0,
562                                     NULL, NULL, NULL); CHKERRQ(ierr);
563   ierr = VecZeroEntries(Gloc); CHKERRQ(ierr);
564 
565   // Ceed Vectors
566   ierr = VecGetArrayRead(Qloc, (const PetscScalar **)&q); CHKERRQ(ierr);
567   ierr = VecGetArray(Gloc, &g); CHKERRQ(ierr);
568   CeedVectorSetArray(user->qceed, CEED_MEM_HOST, CEED_USE_POINTER, q);
569   CeedVectorSetArray(user->gceed, CEED_MEM_HOST, CEED_USE_POINTER, g);
570 
571   // Apply CEED operator
572   CeedOperatorApply(user->op_rhs, user->qceed, user->gceed,
573                     CEED_REQUEST_IMMEDIATE);
574 
575   // Restore vectors
576   ierr = VecRestoreArrayRead(Qloc, (const PetscScalar **)&q); CHKERRQ(ierr);
577   ierr = VecRestoreArray(Gloc, &g); CHKERRQ(ierr);
578 
579   ierr = VecZeroEntries(G); CHKERRQ(ierr);
580   ierr = DMLocalToGlobal(user->dm, Gloc, ADD_VALUES, G); CHKERRQ(ierr);
581 
582   // Inverse of the lumped mass matrix
583   ierr = VecPointwiseMult(G, G, user->M); // M is Minv
584   CHKERRQ(ierr);
585 
586   ierr = DMRestoreLocalVector(user->dm, &Qloc); CHKERRQ(ierr);
587   ierr = DMRestoreLocalVector(user->dm, &Gloc); CHKERRQ(ierr);
588   PetscFunctionReturn(0);
589 }
590 
591 static PetscErrorCode IFunction_NS(TS ts, PetscReal t, Vec Q, Vec Qdot, Vec G,
592                                    void *userData) {
593   PetscErrorCode ierr;
594   User user = *(User *)userData;
595   const PetscScalar *q, *qdot;
596   PetscScalar *g;
597   Vec Qloc, Qdotloc, Gloc;
598 
599   // Global-to-local
600   PetscFunctionBeginUser;
601   ierr = DMGetLocalVector(user->dm, &Qloc); CHKERRQ(ierr);
602   ierr = DMGetLocalVector(user->dm, &Qdotloc); CHKERRQ(ierr);
603   ierr = DMGetLocalVector(user->dm, &Gloc); CHKERRQ(ierr);
604   ierr = VecZeroEntries(Qloc); CHKERRQ(ierr);
605   ierr = DMGlobalToLocal(user->dm, Q, INSERT_VALUES, Qloc); CHKERRQ(ierr);
606   ierr = DMPlexInsertBoundaryValues(user->dm, PETSC_TRUE, Qloc, 0.0,
607                                     NULL, NULL, NULL); CHKERRQ(ierr);
608   ierr = VecZeroEntries(Qdotloc); CHKERRQ(ierr);
609   ierr = DMGlobalToLocal(user->dm, Qdot, INSERT_VALUES, Qdotloc); CHKERRQ(ierr);
610   ierr = VecZeroEntries(Gloc); CHKERRQ(ierr);
611 
612   // Ceed Vectors
613   ierr = VecGetArrayRead(Qloc, &q); CHKERRQ(ierr);
614   ierr = VecGetArrayRead(Qdotloc, &qdot); CHKERRQ(ierr);
615   ierr = VecGetArray(Gloc, &g); CHKERRQ(ierr);
616   CeedVectorSetArray(user->qceed, CEED_MEM_HOST, CEED_USE_POINTER,
617                      (PetscScalar *)q);
618   CeedVectorSetArray(user->qdotceed, CEED_MEM_HOST, CEED_USE_POINTER,
619                      (PetscScalar *)qdot);
620   CeedVectorSetArray(user->gceed, CEED_MEM_HOST, CEED_USE_POINTER, g);
621 
622   // Apply CEED operator
623   CeedOperatorApply(user->op_ifunction, user->qceed, user->gceed,
624                     CEED_REQUEST_IMMEDIATE);
625 
626   // Restore vectors
627   ierr = VecRestoreArrayRead(Qloc, &q); CHKERRQ(ierr);
628   ierr = VecRestoreArrayRead(Qdotloc, &qdot); CHKERRQ(ierr);
629   ierr = VecRestoreArray(Gloc, &g); CHKERRQ(ierr);
630 
631   ierr = VecZeroEntries(G); CHKERRQ(ierr);
632   ierr = DMLocalToGlobal(user->dm, Gloc, ADD_VALUES, G); CHKERRQ(ierr);
633 
634   ierr = DMRestoreLocalVector(user->dm, &Qloc); CHKERRQ(ierr);
635   ierr = DMRestoreLocalVector(user->dm, &Qdotloc); CHKERRQ(ierr);
636   ierr = DMRestoreLocalVector(user->dm, &Gloc); CHKERRQ(ierr);
637   PetscFunctionReturn(0);
638 }
639 
640 // User provided TS Monitor
641 static PetscErrorCode TSMonitor_NS(TS ts, PetscInt stepno, PetscReal time,
642                                    Vec Q, void *ctx) {
643   User user = ctx;
644   Vec Qloc;
645   char filepath[PETSC_MAX_PATH_LEN];
646   PetscViewer viewer;
647   PetscErrorCode ierr;
648 
649   // Set up output
650   PetscFunctionBeginUser;
651   // Print every 'outputfreq' steps
652   if (stepno % user->outputfreq != 0)
653     PetscFunctionReturn(0);
654   ierr = DMGetLocalVector(user->dm, &Qloc); CHKERRQ(ierr);
655   ierr = PetscObjectSetName((PetscObject)Qloc, "StateVec"); CHKERRQ(ierr);
656   ierr = VecZeroEntries(Qloc); CHKERRQ(ierr);
657   ierr = DMGlobalToLocal(user->dm, Q, INSERT_VALUES, Qloc); CHKERRQ(ierr);
658 
659   // Output
660   ierr = PetscSNPrintf(filepath, sizeof filepath, "%s/ns-%03D.vtu",
661                        user->outputdir, stepno + user->contsteps);
662   CHKERRQ(ierr);
663   ierr = PetscViewerVTKOpen(PetscObjectComm((PetscObject)Q), filepath,
664                             FILE_MODE_WRITE, &viewer); CHKERRQ(ierr);
665   ierr = VecView(Qloc, viewer); CHKERRQ(ierr);
666   ierr = PetscViewerDestroy(&viewer); CHKERRQ(ierr);
667   if (user->dmviz) {
668     Vec Qrefined, Qrefined_loc;
669     char filepath_refined[PETSC_MAX_PATH_LEN];
670     PetscViewer viewer_refined;
671 
672     ierr = DMGetGlobalVector(user->dmviz, &Qrefined); CHKERRQ(ierr);
673     ierr = DMGetLocalVector(user->dmviz, &Qrefined_loc); CHKERRQ(ierr);
674     ierr = PetscObjectSetName((PetscObject)Qrefined_loc, "Refined");
675     CHKERRQ(ierr);
676     ierr = MatInterpolate(user->interpviz, Q, Qrefined); CHKERRQ(ierr);
677     ierr = VecZeroEntries(Qrefined_loc); CHKERRQ(ierr);
678     ierr = DMGlobalToLocal(user->dmviz, Qrefined, INSERT_VALUES, Qrefined_loc);
679     CHKERRQ(ierr);
680     ierr = PetscSNPrintf(filepath_refined, sizeof filepath_refined,
681                          "%s/nsrefined-%03D.vtu",
682                          user->outputdir, stepno + user->contsteps);
683     CHKERRQ(ierr);
684     ierr = PetscViewerVTKOpen(PetscObjectComm((PetscObject)Qrefined),
685                               filepath_refined,
686                               FILE_MODE_WRITE, &viewer_refined); CHKERRQ(ierr);
687     ierr = VecView(Qrefined_loc, viewer_refined); CHKERRQ(ierr);
688     ierr = DMRestoreLocalVector(user->dmviz, &Qrefined_loc); CHKERRQ(ierr);
689     ierr = DMRestoreGlobalVector(user->dmviz, &Qrefined); CHKERRQ(ierr);
690     ierr = PetscViewerDestroy(&viewer_refined); CHKERRQ(ierr);
691   }
692   ierr = DMRestoreLocalVector(user->dm, &Qloc); CHKERRQ(ierr);
693 
694   // Save data in a binary file for continuation of simulations
695   ierr = PetscSNPrintf(filepath, sizeof filepath, "%s/ns-solution.bin",
696                        user->outputdir); CHKERRQ(ierr);
697   ierr = PetscViewerBinaryOpen(user->comm, filepath, FILE_MODE_WRITE, &viewer);
698   CHKERRQ(ierr);
699   ierr = VecView(Q, viewer); CHKERRQ(ierr);
700   ierr = PetscViewerDestroy(&viewer); CHKERRQ(ierr);
701 
702   // Save time stamp
703   // Dimensionalize time back
704   time /= user->units->second;
705   ierr = PetscSNPrintf(filepath, sizeof filepath, "%s/ns-time.bin",
706                        user->outputdir); CHKERRQ(ierr);
707   ierr = PetscViewerBinaryOpen(user->comm, filepath, FILE_MODE_WRITE, &viewer);
708   CHKERRQ(ierr);
709   #if PETSC_VERSION_GE(3,13,0)
710   ierr = PetscViewerBinaryWrite(viewer, &time, 1, PETSC_REAL);
711   #else
712   ierr = PetscViewerBinaryWrite(viewer, &time, 1, PETSC_REAL, true);
713   #endif
714   CHKERRQ(ierr);
715   ierr = PetscViewerDestroy(&viewer); CHKERRQ(ierr);
716 
717   PetscFunctionReturn(0);
718 }
719 
720 static PetscErrorCode ICs_FixMultiplicity(CeedOperator op_ics,
721     CeedVector xcorners, CeedVector q0ceed, DM dm, Vec Qloc, Vec Q,
722     CeedElemRestriction restrictq, CeedQFunctionContext ctxSetup, CeedScalar time) {
723   PetscErrorCode ierr;
724   CeedVector multlvec;
725   Vec Multiplicity, MultiplicityLoc;
726 
727   SetupContext ctxSetupData;
728   CeedQFunctionContextGetData(ctxSetup, CEED_MEM_HOST, (void **)&ctxSetupData);
729   ctxSetupData->time = time;
730   CeedQFunctionContextRestoreData(ctxSetup, (void **)&ctxSetupData);
731 
732   ierr = VecZeroEntries(Qloc); CHKERRQ(ierr);
733   ierr = VectorPlacePetscVec(q0ceed, Qloc); CHKERRQ(ierr);
734   CeedOperatorApply(op_ics, xcorners, q0ceed, CEED_REQUEST_IMMEDIATE);
735   ierr = VecZeroEntries(Q); CHKERRQ(ierr);
736   ierr = DMLocalToGlobal(dm, Qloc, ADD_VALUES, Q); CHKERRQ(ierr);
737 
738   // Fix multiplicity for output of ICs
739   ierr = DMGetLocalVector(dm, &MultiplicityLoc); CHKERRQ(ierr);
740   CeedElemRestrictionCreateVector(restrictq, &multlvec, NULL);
741   ierr = VectorPlacePetscVec(multlvec, MultiplicityLoc); CHKERRQ(ierr);
742   CeedElemRestrictionGetMultiplicity(restrictq, multlvec);
743   CeedVectorDestroy(&multlvec);
744   ierr = DMGetGlobalVector(dm, &Multiplicity); CHKERRQ(ierr);
745   ierr = VecZeroEntries(Multiplicity); CHKERRQ(ierr);
746   ierr = DMLocalToGlobal(dm, MultiplicityLoc, ADD_VALUES, Multiplicity);
747   CHKERRQ(ierr);
748   ierr = VecPointwiseDivide(Q, Q, Multiplicity); CHKERRQ(ierr);
749   ierr = VecPointwiseDivide(Qloc, Qloc, MultiplicityLoc); CHKERRQ(ierr);
750   ierr = DMRestoreLocalVector(dm, &MultiplicityLoc); CHKERRQ(ierr);
751   ierr = DMRestoreGlobalVector(dm, &Multiplicity); CHKERRQ(ierr);
752 
753   PetscFunctionReturn(0);
754 }
755 
756 static PetscErrorCode ComputeLumpedMassMatrix(Ceed ceed, DM dm,
757     CeedElemRestriction restrictq, CeedBasis basisq,
758     CeedElemRestriction restrictqdi, CeedVector qdata, Vec M) {
759   PetscErrorCode ierr;
760   CeedQFunction qf_mass;
761   CeedOperator op_mass;
762   CeedVector mceed;
763   Vec Mloc;
764   CeedInt ncompq, qdatasize;
765 
766   PetscFunctionBeginUser;
767   CeedElemRestrictionGetNumComponents(restrictq, &ncompq);
768   CeedElemRestrictionGetNumComponents(restrictqdi, &qdatasize);
769   // Create the Q-function that defines the action of the mass operator
770   CeedQFunctionCreateInterior(ceed, 1, Mass, Mass_loc, &qf_mass);
771   CeedQFunctionAddInput(qf_mass, "q", ncompq, CEED_EVAL_INTERP);
772   CeedQFunctionAddInput(qf_mass, "qdata", qdatasize, CEED_EVAL_NONE);
773   CeedQFunctionAddOutput(qf_mass, "v", ncompq, CEED_EVAL_INTERP);
774 
775   // Create the mass operator
776   CeedOperatorCreate(ceed, qf_mass, NULL, NULL, &op_mass);
777   CeedOperatorSetField(op_mass, "q", restrictq, basisq, CEED_VECTOR_ACTIVE);
778   CeedOperatorSetField(op_mass, "qdata", restrictqdi,
779                        CEED_BASIS_COLLOCATED, qdata);
780   CeedOperatorSetField(op_mass, "v", restrictq, basisq, CEED_VECTOR_ACTIVE);
781 
782   ierr = DMGetLocalVector(dm, &Mloc); CHKERRQ(ierr);
783   ierr = VecZeroEntries(Mloc); CHKERRQ(ierr);
784   CeedElemRestrictionCreateVector(restrictq, &mceed, NULL);
785   ierr = VectorPlacePetscVec(mceed, Mloc); CHKERRQ(ierr);
786 
787   {
788     // Compute a lumped mass matrix
789     CeedVector onesvec;
790     CeedElemRestrictionCreateVector(restrictq, &onesvec, NULL);
791     CeedVectorSetValue(onesvec, 1.0);
792     CeedOperatorApply(op_mass, onesvec, mceed, CEED_REQUEST_IMMEDIATE);
793     CeedVectorDestroy(&onesvec);
794     CeedOperatorDestroy(&op_mass);
795     CeedVectorDestroy(&mceed);
796   }
797   CeedQFunctionDestroy(&qf_mass);
798 
799   ierr = VecZeroEntries(M); CHKERRQ(ierr);
800   ierr = DMLocalToGlobal(dm, Mloc, ADD_VALUES, M); CHKERRQ(ierr);
801   ierr = DMRestoreLocalVector(dm, &Mloc); CHKERRQ(ierr);
802 
803   // Invert diagonally lumped mass vector for RHS function
804   ierr = VecReciprocal(M); CHKERRQ(ierr);
805   PetscFunctionReturn(0);
806 }
807 
808 static PetscErrorCode SetUpDM(DM dm, problemData *problem, PetscInt degree,
809                               SimpleBC bc, void *ctxSetupData, void *ctxMMS) {
810   PetscErrorCode ierr;
811 
812   PetscFunctionBeginUser;
813   {
814     // Configure the finite element space and boundary conditions
815     PetscFE fe;
816     PetscInt ncompq = 5;
817     ierr = PetscFECreateLagrange(PETSC_COMM_SELF, problem->dim, ncompq,
818                                  PETSC_FALSE, degree, PETSC_DECIDE,
819                                  &fe); CHKERRQ(ierr);
820     ierr = PetscObjectSetName((PetscObject)fe, "Q"); CHKERRQ(ierr);
821     ierr = DMAddField(dm, NULL,(PetscObject)fe); CHKERRQ(ierr);
822     ierr = DMCreateDS(dm); CHKERRQ(ierr);
823     {
824       PetscInt comps[1] = {1};
825       ierr = DMAddBoundary(dm, DM_BC_ESSENTIAL, "slipx", "Face Sets", 0,
826                            1, comps, (void(*)(void))NULL, NULL, bc->nslip[0],
827                            bc->slips[0], ctxSetupData); CHKERRQ(ierr);
828       comps[0] = 2;
829       ierr = DMAddBoundary(dm, DM_BC_ESSENTIAL, "slipy", "Face Sets", 0,
830                            1, comps, (void(*)(void))NULL, NULL, bc->nslip[1],
831                            bc->slips[1], ctxSetupData); CHKERRQ(ierr);
832       comps[0] = 3;
833       ierr = DMAddBoundary(dm, DM_BC_ESSENTIAL, "slipz", "Face Sets", 0,
834                            1, comps, (void(*)(void))NULL, NULL, bc->nslip[2],
835                            bc->slips[2], ctxSetupData); CHKERRQ(ierr);
836     }
837     if (bc->userbc == PETSC_TRUE) {
838       for (PetscInt c = 0; c < 3; c++) {
839         for (PetscInt s = 0; s < bc->nslip[c]; s++) {
840           for (PetscInt w = 0; w < bc->nwall; w++) {
841             if (bc->slips[c][s] == bc->walls[w])
842               SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
843                        "Boundary condition already set on face %D!\n",
844                        bc->walls[w]);
845           }
846         }
847       }
848     }
849     // Wall boundary conditions are zero energy density and zero flux for
850     //   velocity in advection/advection2d, and zero velocity and zero flux
851     //   for mass density and energy density in density_current
852     {
853       if (problem->bc == Exact_Advection || problem->bc == Exact_Advection2d) {
854         PetscInt comps[1] = {4};
855         ierr = DMAddBoundary(dm, DM_BC_ESSENTIAL, "wall", "Face Sets", 0,
856                              1, comps, (void(*)(void))problem->bc, NULL,
857                              bc->nwall, bc->walls, ctxSetupData); CHKERRQ(ierr);
858       } else if (problem->bc == Exact_DC) {
859         PetscInt comps[3] = {1, 2, 3};
860         ierr = DMAddBoundary(dm, DM_BC_ESSENTIAL, "wall", "Face Sets", 0,
861                              3, comps, (void(*)(void))problem->bc, NULL,
862                              bc->nwall, bc->walls, ctxSetupData); CHKERRQ(ierr);
863       } else if (problem->bc == Exact_Euler) {
864         PetscInt comps[3] = {1, 2, 3};
865         ierr = DMAddBoundary(dm, DM_BC_ESSENTIAL, "wall", "Face Sets", 0,
866                              3, comps, (void(*)(void))problem->bc, NULL,
867                              bc->nwall, bc->walls, ctxMMS); CHKERRQ(ierr);
868       } else
869         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
870                 "Undefined boundary conditions for this problem");
871     }
872     ierr = DMPlexSetClosurePermutationTensor(dm, PETSC_DETERMINE, NULL);
873     CHKERRQ(ierr);
874     ierr = PetscFEDestroy(&fe); CHKERRQ(ierr);
875   }
876   {
877     // Empty name for conserved field (because there is only one field)
878     PetscSection section;
879     ierr = DMGetLocalSection(dm, &section); CHKERRQ(ierr);
880     ierr = PetscSectionSetFieldName(section, 0, ""); CHKERRQ(ierr);
881     ierr = PetscSectionSetComponentName(section, 0, 0, "Density");
882     CHKERRQ(ierr);
883     ierr = PetscSectionSetComponentName(section, 0, 1, "MomentumX");
884     CHKERRQ(ierr);
885     ierr = PetscSectionSetComponentName(section, 0, 2, "MomentumY");
886     CHKERRQ(ierr);
887     ierr = PetscSectionSetComponentName(section, 0, 3, "MomentumZ");
888     CHKERRQ(ierr);
889     ierr = PetscSectionSetComponentName(section, 0, 4, "EnergyDensity");
890     CHKERRQ(ierr);
891   }
892   PetscFunctionReturn(0);
893 }
894 
895 int main(int argc, char **argv) {
896   PetscInt ierr;
897   MPI_Comm comm;
898   DM dm, dmcoord, dmviz;
899   Mat interpviz;
900   TS ts;
901   TSAdapt adapt;
902   User user;
903   Units units;
904   EulerContext ctxEulerData;
905   char ceedresource[4096] = "/cpu/self";
906   PetscInt localNelemVol, lnodes, gnodes, steps;
907   const PetscInt ncompq = 5;
908   PetscMPIInt rank;
909   PetscScalar ftime;
910   Vec Q, Qloc, Xloc;
911   Ceed ceed;
912   CeedInt numP, numQ;
913   CeedVector xcorners, qdata, q0ceed;
914   CeedBasis basisx, basisxc, basisq;
915   CeedElemRestriction restrictx, restrictq, restrictqdi;
916   CeedQFunction qf_setupVol, qf_ics, qf_rhsVol, qf_ifunctionVol;
917   CeedQFunctionContext ctxSetup, ctxNS, ctxAdvection2d, ctxSurface, ctxEuler;
918   CeedOperator op_setupVol, op_ics;
919   CeedScalar Rd;
920   CeedMemType memtyperequested;
921   PetscScalar WpermK, Pascal, JperkgK, mpersquareds, kgpercubicm,
922               kgpersquaredms, Joulepercubicm, Joule;
923   problemType problemChoice;
924   problemData *problem = NULL;
925   WindType wind_type;
926   StabilizationType stab;
927   EulerTestType euler_test;
928   PetscBool implicit;
929   PetscInt    viz_refine = 0;
930   struct SimpleBC_ bc = {
931     .nslip = {2, 2, 2},
932     .slips = {{5, 6}, {3, 4}, {1, 2}}
933   };
934   double start, cpu_time_used;
935   // Test variables
936   PetscBool test;
937   PetscScalar testtol = 0.;
938   char filepath[PETSC_MAX_PATH_LEN];
939   // Check PETSc CUDA support
940   PetscBool petschavecuda, setmemtyperequest = PETSC_FALSE;
941   // *INDENT-OFF*
942   #ifdef PETSC_HAVE_CUDA
943   petschavecuda = PETSC_TRUE;
944   #else
945   petschavecuda = PETSC_FALSE;
946   #endif
947   // *INDENT-ON*
948 
949   // Create the libCEED contexts
950   PetscScalar meter          = 1e-2;     // 1 meter in scaled length units
951   PetscScalar second         = 1e-2;     // 1 second in scaled time units
952   PetscScalar kilogram       = 1e-6;     // 1 kilogram in scaled mass units
953   PetscScalar Kelvin         = 1;        // 1 Kelvin in scaled temperature units
954   CeedScalar theta0          = 300.;     // K
955   CeedScalar thetaC          = -15.;     // K
956   CeedScalar P0              = 1.e5;     // Pa
957   CeedScalar E_wind          = 1.e6;     // J
958   CeedScalar N               = 0.01;     // 1/s
959   CeedScalar cv              = 717.;     // J/(kg K)
960   CeedScalar cp              = 1004.;    // J/(kg K)
961   CeedScalar vortex_strength = 5.;       // -
962   CeedScalar T_inlet         = 1.;       // K
963   CeedScalar P_inlet         = 1.e3;     // Pa
964   CeedScalar g               = 9.81;     // m/s^2
965   CeedScalar lambda          = -2./3.;   // -
966   CeedScalar mu              = 75.;      // Pa s, dynamic viscosity
967   // mu = 75 is not physical for air, but is good for numerical stability
968   CeedScalar k               = 0.02638;  // W/(m K)
969   CeedScalar CtauS           = 0.;       // dimensionless
970   CeedScalar strong_form     = 0.;       // [0,1]
971   PetscScalar lx             = 8000.;    // m
972   PetscScalar ly             = 8000.;    // m
973   PetscScalar lz             = 4000.;    // m
974   CeedScalar rc              = 1000.;    // m (Radius of bubble)
975   PetscScalar resx           = 1000.;    // m (resolution in x)
976   PetscScalar resy           = 1000.;    // m (resolution in y)
977   PetscScalar resz           = 1000.;    // m (resolution in z)
978   PetscInt outputfreq        = 10;       // -
979   PetscInt contsteps         = 0;        // -
980   PetscInt degree            = 1;        // -
981   PetscInt qextra            = 2;        // -
982   PetscInt qextraSur         = 2;        // -
983   PetscReal center[3], dc_axis[3] = {0, 0, 0}, wind[3] = {1., 0, 0},
984                                     etv_mean_velocity[3] = {1., 1., 0};
985   ierr = PetscInitialize(&argc, &argv, NULL, help);
986   if (ierr) return ierr;
987 
988   // Allocate PETSc context
989   ierr = PetscCalloc1(1, &user); CHKERRQ(ierr);
990   ierr = PetscMalloc1(1, &units); CHKERRQ(ierr);
991   ierr = PetscMalloc1(1, &ctxEulerData); CHKERRQ(ierr);
992 
993   // Parse command line options
994   comm = PETSC_COMM_WORLD;
995   ierr = PetscOptionsBegin(comm, NULL, "Navier-Stokes in PETSc with libCEED",
996                            NULL); CHKERRQ(ierr);
997   ierr = PetscOptionsString("-ceed", "CEED resource specifier",
998                             NULL, ceedresource, ceedresource,
999                             sizeof(ceedresource), NULL); CHKERRQ(ierr);
1000   ierr = PetscOptionsBool("-test", "Run in test mode",
1001                           NULL, test=PETSC_FALSE, &test, NULL); CHKERRQ(ierr);
1002   ierr = PetscOptionsScalar("-compare_final_state_atol",
1003                             "Test absolute tolerance",
1004                             NULL, testtol, &testtol, NULL); CHKERRQ(ierr);
1005   ierr = PetscOptionsString("-compare_final_state_filename", "Test filename",
1006                             NULL, filepath, filepath,
1007                             sizeof(filepath), NULL); CHKERRQ(ierr);
1008   problemChoice = NS_DENSITY_CURRENT;
1009   ierr = PetscOptionsEnum("-problem", "Problem to solve", NULL,
1010                           problemTypes, (PetscEnum)problemChoice,
1011                           (PetscEnum *)&problemChoice, NULL); CHKERRQ(ierr);
1012   problem = &problemOptions[problemChoice];
1013   ierr = PetscOptionsEnum("-problem_advection_wind", "Wind type in Advection",
1014                           NULL, WindTypes,
1015                           (PetscEnum)(wind_type = ADVECTION_WIND_ROTATION),
1016                           (PetscEnum *)&wind_type, NULL); CHKERRQ(ierr);
1017   PetscInt n = problem->dim;
1018   PetscBool userWind;
1019   ierr = PetscOptionsRealArray("-problem_advection_wind_translation",
1020                                "Constant wind vector",
1021                                NULL, wind, &n, &userWind); CHKERRQ(ierr);
1022   if (wind_type == ADVECTION_WIND_ROTATION && userWind) {
1023     ierr = PetscPrintf(comm,
1024                        "Warning! Use -problem_advection_wind_translation only with -problem_advection_wind translation\n");
1025     CHKERRQ(ierr);
1026   }
1027   if (wind_type == ADVECTION_WIND_TRANSLATION
1028       && (problemChoice == NS_DENSITY_CURRENT ||
1029           problemChoice == NS_EULER_VORTEX)) {
1030     SETERRQ(comm, PETSC_ERR_ARG_INCOMP,
1031             "-problem_advection_wind translation is not defined for -problem density_current or -problem euler_vortex");
1032   }
1033   n = problem->dim;
1034   ierr = PetscOptionsRealArray("-problem_euler_mean_velocity",
1035                                "Mean velocity vector",
1036                                NULL, etv_mean_velocity, &n, NULL);
1037   CHKERRQ(ierr);
1038   ierr = PetscOptionsEnum("-stab", "Stabilization method", NULL,
1039                           StabilizationTypes, (PetscEnum)(stab = STAB_NONE),
1040                           (PetscEnum *)&stab, NULL); CHKERRQ(ierr);
1041   ierr = PetscOptionsEnum("-euler_test", "Euler test option", NULL,
1042                           EulerTestTypes, (PetscEnum)(euler_test = EULER_TEST_NONE),
1043                           (PetscEnum *)&euler_test, NULL); CHKERRQ(ierr);
1044   ierr = PetscOptionsBool("-implicit", "Use implicit (IFunction) formulation",
1045                           NULL, implicit=PETSC_FALSE, &implicit, NULL);
1046   CHKERRQ(ierr);
1047   if (!implicit && stab != STAB_NONE) {
1048     ierr = PetscPrintf(comm, "Warning! Use -stab only with -implicit\n");
1049     CHKERRQ(ierr);
1050   }
1051   {
1052     PetscInt len;
1053     PetscBool flg;
1054     ierr = PetscOptionsIntArray("-bc_wall",
1055                                 "Use wall boundary conditions on this list of faces",
1056                                 NULL, bc.walls,
1057                                 (len = sizeof(bc.walls) / sizeof(bc.walls[0]),
1058                                  &len), &flg); CHKERRQ(ierr);
1059     if (flg) {
1060       bc.nwall = len;
1061       // Using a no-slip wall disables automatic slip walls (they must be set explicitly)
1062       bc.nslip[0] = bc.nslip[1] = bc.nslip[2] = 0;
1063     }
1064     for (PetscInt j=0; j<3; j++) {
1065       const char *flags[3] = {"-bc_slip_x", "-bc_slip_y", "-bc_slip_z"};
1066       ierr = PetscOptionsIntArray(flags[j],
1067                                   "Use slip boundary conditions on this list of faces",
1068                                   NULL, bc.slips[j],
1069                                   (len = sizeof(bc.slips[j]) / sizeof(bc.slips[j][0]),
1070                                    &len), &flg);
1071       CHKERRQ(ierr);
1072       if (flg) {
1073         bc.nslip[j] = len;
1074         bc.userbc = PETSC_TRUE;
1075       }
1076     }
1077   }
1078   ierr = PetscOptionsInt("-viz_refine",
1079                          "Regular refinement levels for visualization",
1080                          NULL, viz_refine, &viz_refine, NULL);
1081   CHKERRQ(ierr);
1082   ierr = PetscOptionsScalar("-units_meter", "1 meter in scaled length units",
1083                             NULL, meter, &meter, NULL); CHKERRQ(ierr);
1084   meter = fabs(meter);
1085   ierr = PetscOptionsScalar("-units_second","1 second in scaled time units",
1086                             NULL, second, &second, NULL); CHKERRQ(ierr);
1087   second = fabs(second);
1088   ierr = PetscOptionsScalar("-units_kilogram","1 kilogram in scaled mass units",
1089                             NULL, kilogram, &kilogram, NULL); CHKERRQ(ierr);
1090   kilogram = fabs(kilogram);
1091   ierr = PetscOptionsScalar("-units_Kelvin",
1092                             "1 Kelvin in scaled temperature units",
1093                             NULL, Kelvin, &Kelvin, NULL); CHKERRQ(ierr);
1094   Kelvin = fabs(Kelvin);
1095   ierr = PetscOptionsScalar("-theta0", "Reference potential temperature",
1096                             NULL, theta0, &theta0, NULL); CHKERRQ(ierr);
1097   ierr = PetscOptionsScalar("-thetaC", "Perturbation of potential temperature",
1098                             NULL, thetaC, &thetaC, NULL); CHKERRQ(ierr);
1099   ierr = PetscOptionsScalar("-P0", "Atmospheric pressure",
1100                             NULL, P0, &P0, NULL); CHKERRQ(ierr);
1101   ierr = PetscOptionsScalar("-E_wind", "Total energy of inflow wind",
1102                             NULL, E_wind, &E_wind, NULL); CHKERRQ(ierr);
1103   ierr = PetscOptionsScalar("-N", "Brunt-Vaisala frequency",
1104                             NULL, N, &N, NULL); CHKERRQ(ierr);
1105   ierr = PetscOptionsScalar("-cv", "Heat capacity at constant volume",
1106                             NULL, cv, &cv, NULL); CHKERRQ(ierr);
1107   ierr = PetscOptionsScalar("-cp", "Heat capacity at constant pressure",
1108                             NULL, cp, &cp, NULL); CHKERRQ(ierr);
1109   PetscBool userVortex;
1110   ierr = PetscOptionsScalar("-vortex_strength", "Strength of Vortex",
1111                             NULL, vortex_strength, &vortex_strength, &userVortex);
1112   CHKERRQ(ierr);
1113   if (problemChoice != NS_EULER_VORTEX && userVortex) {
1114     ierr = PetscPrintf(comm,
1115                        "Warning! Use -vortex_strength only with -problem euler_vortex\n");
1116     CHKERRQ(ierr);
1117   }
1118   PetscBool userTinlet;
1119   ierr = PetscOptionsScalar("-T_inlet", "Incoming Temperature",
1120                             NULL, T_inlet, &T_inlet, &userTinlet);
1121   CHKERRQ(ierr);
1122   if (problemChoice != NS_EULER_VORTEX && userTinlet) {
1123     ierr = PetscPrintf(comm,
1124                        "Warning! Use -T_inlet only with -problem euler_vortex\n");
1125     CHKERRQ(ierr);
1126   }
1127   PetscBool userPinlet;
1128   ierr = PetscOptionsScalar("-P_inlet", "Incoming Pressure",
1129                             NULL, P_inlet, &P_inlet, &userPinlet);
1130   CHKERRQ(ierr);
1131   if (problemChoice != NS_EULER_VORTEX && userPinlet) {
1132     ierr = PetscPrintf(comm,
1133                        "Warning! Use -P_inlet only with -problem euler_vortex\n");
1134     CHKERRQ(ierr);
1135   }
1136   ierr = PetscOptionsScalar("-g", "Gravitational acceleration",
1137                             NULL, g, &g, NULL); CHKERRQ(ierr);
1138   ierr = PetscOptionsScalar("-lambda",
1139                             "Stokes hypothesis second viscosity coefficient",
1140                             NULL, lambda, &lambda, NULL); CHKERRQ(ierr);
1141   ierr = PetscOptionsScalar("-mu", "Shear dynamic viscosity coefficient",
1142                             NULL, mu, &mu, NULL); CHKERRQ(ierr);
1143   ierr = PetscOptionsScalar("-k", "Thermal conductivity",
1144                             NULL, k, &k, NULL); CHKERRQ(ierr);
1145   ierr = PetscOptionsScalar("-CtauS",
1146                             "Scale coefficient for tau (nondimensional)",
1147                             NULL, CtauS, &CtauS, NULL); CHKERRQ(ierr);
1148   if (stab == STAB_NONE && CtauS != 0) {
1149     ierr = PetscPrintf(comm,
1150                        "Warning! Use -CtauS only with -stab su or -stab supg\n");
1151     CHKERRQ(ierr);
1152   }
1153   ierr = PetscOptionsScalar("-strong_form",
1154                             "Strong (1) or weak/integrated by parts (0) advection residual",
1155                             NULL, strong_form, &strong_form, NULL);
1156   CHKERRQ(ierr);
1157   if (problemChoice == NS_DENSITY_CURRENT && (CtauS != 0 || strong_form != 0)) {
1158     ierr = PetscPrintf(comm,
1159                        "Warning! Problem density_current does not support -CtauS or -strong_form\n");
1160     CHKERRQ(ierr);
1161   }
1162   ierr = PetscOptionsScalar("-lx", "Length scale in x direction",
1163                             NULL, lx, &lx, NULL); CHKERRQ(ierr);
1164   ierr = PetscOptionsScalar("-ly", "Length scale in y direction",
1165                             NULL, ly, &ly, NULL); CHKERRQ(ierr);
1166   ierr = PetscOptionsScalar("-lz", "Length scale in z direction",
1167                             NULL, lz, &lz, NULL); CHKERRQ(ierr);
1168   ierr = PetscOptionsScalar("-rc", "Characteristic radius of thermal bubble",
1169                             NULL, rc, &rc, NULL); CHKERRQ(ierr);
1170   ierr = PetscOptionsScalar("-resx","Target resolution in x",
1171                             NULL, resx, &resx, NULL); CHKERRQ(ierr);
1172   ierr = PetscOptionsScalar("-resy","Target resolution in y",
1173                             NULL, resy, &resy, NULL); CHKERRQ(ierr);
1174   ierr = PetscOptionsScalar("-resz","Target resolution in z",
1175                             NULL, resz, &resz, NULL); CHKERRQ(ierr);
1176   n = problem->dim;
1177   center[0] = 0.5 * lx;
1178   center[1] = 0.5 * ly;
1179   center[2] = 0.5 * lz;
1180   ierr = PetscOptionsRealArray("-center", "Location of bubble center",
1181                                NULL, center, &n, NULL); CHKERRQ(ierr);
1182   n = problem->dim;
1183   ierr = PetscOptionsRealArray("-dc_axis",
1184                                "Axis of density current cylindrical anomaly, or {0,0,0} for spherically symmetric",
1185                                NULL, dc_axis, &n, NULL); CHKERRQ(ierr);
1186   {
1187     PetscReal norm = PetscSqrtReal(PetscSqr(dc_axis[0]) +
1188                                    PetscSqr(dc_axis[1]) + PetscSqr(dc_axis[2]));
1189     if (norm > 0) {
1190       for (int i=0; i<3; i++) dc_axis[i] /= norm;
1191     }
1192   }
1193   ierr = PetscOptionsInt("-output_freq",
1194                          "Frequency of output, in number of steps",
1195                          NULL, outputfreq, &outputfreq, NULL); CHKERRQ(ierr);
1196   ierr = PetscOptionsInt("-continue", "Continue from previous solution",
1197                          NULL, contsteps, &contsteps, NULL); CHKERRQ(ierr);
1198   ierr = PetscOptionsInt("-degree", "Polynomial degree of finite elements",
1199                          NULL, degree, &degree, NULL); CHKERRQ(ierr);
1200   ierr = PetscOptionsInt("-qextra", "Number of extra quadrature points",
1201                          NULL, qextra, &qextra, NULL); CHKERRQ(ierr);
1202   PetscBool userQextraSur;
1203   ierr = PetscOptionsInt("-qextra_boundary",
1204                          "Number of extra quadrature points on in/outflow faces",
1205                          NULL, qextraSur, &qextraSur, &userQextraSur);
1206   CHKERRQ(ierr);
1207   if ((wind_type == ADVECTION_WIND_ROTATION
1208        || problemChoice == NS_DENSITY_CURRENT) && userQextraSur) {
1209     ierr = PetscPrintf(comm,
1210                        "Warning! Use -qextra_boundary only with -problem_advection_wind translation\n");
1211     CHKERRQ(ierr);
1212   }
1213   ierr = PetscStrncpy(user->outputdir, ".", 2); CHKERRQ(ierr);
1214   ierr = PetscOptionsString("-output_dir", "Output directory",
1215                             NULL, user->outputdir, user->outputdir,
1216                             sizeof(user->outputdir), NULL); CHKERRQ(ierr);
1217   memtyperequested = petschavecuda ? CEED_MEM_DEVICE : CEED_MEM_HOST;
1218   ierr = PetscOptionsEnum("-memtype",
1219                           "CEED MemType requested", NULL,
1220                           memTypes, (PetscEnum)memtyperequested,
1221                           (PetscEnum *)&memtyperequested, &setmemtyperequest);
1222   CHKERRQ(ierr);
1223   ierr = PetscOptionsEnd(); CHKERRQ(ierr);
1224 
1225   // Define derived units
1226   Pascal = kilogram / (meter * PetscSqr(second));
1227   JperkgK =  PetscSqr(meter) / (PetscSqr(second) * Kelvin);
1228   mpersquareds = meter / PetscSqr(second);
1229   WpermK = kilogram * meter / (pow(second,3) * Kelvin);
1230   kgpercubicm = kilogram / pow(meter,3);
1231   kgpersquaredms = kilogram / (PetscSqr(meter) * second);
1232   Joulepercubicm = kilogram / (meter * PetscSqr(second));
1233   Joule = kilogram * PetscSqr(meter) / PetscSqr(second);
1234 
1235   // Scale variables to desired units
1236   theta0 *= Kelvin;
1237   thetaC *= Kelvin;
1238   P0 *= Pascal;
1239   E_wind *= Joule;
1240   N *= (1./second);
1241   cv *= JperkgK;
1242   cp *= JperkgK;
1243   Rd = cp - cv;
1244   T_inlet *= Kelvin;
1245   P_inlet *= Pascal;
1246   g *= mpersquareds;
1247   mu *= Pascal * second;
1248   k *= WpermK;
1249   lx = fabs(lx) * meter;
1250   ly = fabs(ly) * meter;
1251   lz = fabs(lz) * meter;
1252   rc = fabs(rc) * meter;
1253   resx = fabs(resx) * meter;
1254   resy = fabs(resy) * meter;
1255   resz = fabs(resz) * meter;
1256   for (int i=0; i<3; i++) center[i] *= meter;
1257 
1258   const CeedInt dim = problem->dim, ncompx = problem->dim,
1259                 qdatasizeVol = problem->qdatasizeVol;
1260   // Set up the libCEED context
1261   struct SetupContext_ ctxSetupData = {
1262     .theta0 = theta0,
1263     .thetaC = thetaC,
1264     .P0 = P0,
1265     .N = N,
1266     .cv = cv,
1267     .cp = cp,
1268     .Rd = Rd,
1269     .g = g,
1270     .rc = rc,
1271     .lx = lx,
1272     .ly = ly,
1273     .lz = lz,
1274     .center[0] = center[0],
1275     .center[1] = center[1],
1276     .center[2] = center[2],
1277     .dc_axis[0] = dc_axis[0],
1278     .dc_axis[1] = dc_axis[1],
1279     .dc_axis[2] = dc_axis[2],
1280     .wind[0] = wind[0],
1281     .wind[1] = wind[1],
1282     .wind[2] = wind[2],
1283     .time = 0,
1284     .vortex_strength = vortex_strength,
1285     .wind_type = wind_type,
1286   };
1287 
1288   // Create the mesh
1289   {
1290     const PetscReal scale[3] = {lx, ly, lz};
1291     ierr = DMPlexCreateBoxMesh(comm, dim, PETSC_FALSE, NULL, NULL, scale,
1292                                NULL, PETSC_TRUE, &dm);
1293     CHKERRQ(ierr);
1294   }
1295 
1296   // Distribute the mesh over processes
1297   {
1298     DM               dmDist = NULL;
1299     PetscPartitioner part;
1300 
1301     ierr = DMPlexGetPartitioner(dm, &part); CHKERRQ(ierr);
1302     ierr = PetscPartitionerSetFromOptions(part); CHKERRQ(ierr);
1303     ierr = DMPlexDistribute(dm, 0, NULL, &dmDist); CHKERRQ(ierr);
1304     if (dmDist) {
1305       ierr = DMDestroy(&dm); CHKERRQ(ierr);
1306       dm  = dmDist;
1307     }
1308   }
1309   ierr = DMViewFromOptions(dm, NULL, "-dm_view"); CHKERRQ(ierr);
1310 
1311   // Setup DM
1312   ierr = DMLocalizeCoordinates(dm); CHKERRQ(ierr);
1313   ierr = DMSetFromOptions(dm); CHKERRQ(ierr);
1314   ierr = SetUpDM(dm, problem, degree, &bc, &ctxSetupData, ctxEulerData);
1315   CHKERRQ(ierr);
1316 
1317   // Refine DM for high-order viz
1318   dmviz = NULL;
1319   interpviz = NULL;
1320   if (viz_refine) {
1321     DM dmhierarchy[viz_refine+1];
1322 
1323     ierr = DMPlexSetRefinementUniform(dm, PETSC_TRUE); CHKERRQ(ierr);
1324     dmhierarchy[0] = dm;
1325     for (PetscInt i = 0, d = degree; i < viz_refine; i++) {
1326       Mat interp_next;
1327 
1328       ierr = DMRefine(dmhierarchy[i], MPI_COMM_NULL, &dmhierarchy[i+1]);
1329       CHKERRQ(ierr);
1330       ierr = DMClearDS(dmhierarchy[i+1]); CHKERRQ(ierr);
1331       ierr = DMClearFields(dmhierarchy[i+1]); CHKERRQ(ierr);
1332       ierr = DMSetCoarseDM(dmhierarchy[i+1], dmhierarchy[i]); CHKERRQ(ierr);
1333       d = (d + 1) / 2;
1334       if (i + 1 == viz_refine) d = 1;
1335       ierr = SetUpDM(dmhierarchy[i+1], problem, d, &bc, &ctxSetupData,
1336                      ctxEulerData); CHKERRQ(ierr);
1337       ierr = DMCreateInterpolation(dmhierarchy[i], dmhierarchy[i+1],
1338                                    &interp_next, NULL); CHKERRQ(ierr);
1339       if (!i) interpviz = interp_next;
1340       else {
1341         Mat C;
1342         ierr = MatMatMult(interp_next, interpviz, MAT_INITIAL_MATRIX,
1343                           PETSC_DECIDE, &C); CHKERRQ(ierr);
1344         ierr = MatDestroy(&interp_next); CHKERRQ(ierr);
1345         ierr = MatDestroy(&interpviz); CHKERRQ(ierr);
1346         interpviz = C;
1347       }
1348     }
1349     for (PetscInt i=1; i<viz_refine; i++) {
1350       ierr = DMDestroy(&dmhierarchy[i]); CHKERRQ(ierr);
1351     }
1352     dmviz = dmhierarchy[viz_refine];
1353   }
1354   ierr = DMCreateGlobalVector(dm, &Q); CHKERRQ(ierr);
1355   ierr = DMGetLocalVector(dm, &Qloc); CHKERRQ(ierr);
1356   ierr = VecGetSize(Qloc, &lnodes); CHKERRQ(ierr);
1357   lnodes /= ncompq;
1358 
1359   // Initialize CEED
1360   CeedInit(ceedresource, &ceed);
1361   // Set memtype
1362   CeedMemType memtypebackend;
1363   CeedGetPreferredMemType(ceed, &memtypebackend);
1364   // Check memtype compatibility
1365   if (!setmemtyperequest)
1366     memtyperequested = memtypebackend;
1367   else if (!petschavecuda && memtyperequested == CEED_MEM_DEVICE)
1368     SETERRQ1(PETSC_COMM_WORLD, PETSC_ERR_SUP_SYS,
1369              "PETSc was not built with CUDA. "
1370              "Requested MemType CEED_MEM_DEVICE is not supported.", NULL);
1371 
1372   // Set number of 1D nodes and quadrature points
1373   numP = degree + 1;
1374   numQ = numP + qextra;
1375 
1376   // Print summary
1377   if (!test) {
1378     CeedInt gdofs, odofs;
1379     int comm_size;
1380     char box_faces_str[PETSC_MAX_PATH_LEN] = "NONE";
1381     ierr = VecGetSize(Q, &gdofs); CHKERRQ(ierr);
1382     ierr = VecGetLocalSize(Q, &odofs); CHKERRQ(ierr);
1383     gnodes = gdofs/ncompq;
1384     ierr = MPI_Comm_size(comm, &comm_size); CHKERRQ(ierr);
1385     ierr = PetscOptionsGetString(NULL, NULL, "-dm_plex_box_faces", box_faces_str,
1386                                  sizeof(box_faces_str), NULL); CHKERRQ(ierr);
1387     const char *usedresource;
1388     CeedGetResource(ceed, &usedresource);
1389 
1390     ierr = PetscPrintf(comm,
1391                        "\n-- Navier-Stokes solver - libCEED + PETSc --\n"
1392                        "  rank(s)                              : %d\n"
1393                        "  Problem:\n"
1394                        "    Problem Name                       : %s\n"
1395                        "    Stabilization                      : %s\n"
1396                        "  PETSc:\n"
1397                        "    Box Faces                          : %s\n"
1398                        "  libCEED:\n"
1399                        "    libCEED Backend                    : %s\n"
1400                        "    libCEED Backend MemType            : %s\n"
1401                        "    libCEED User Requested MemType     : %s\n"
1402                        "  Mesh:\n"
1403                        "    Number of 1D Basis Nodes (P)       : %d\n"
1404                        "    Number of 1D Quadrature Points (Q) : %d\n"
1405                        "    Global DoFs                        : %D\n"
1406                        "    Owned DoFs                         : %D\n"
1407                        "    DoFs per node                      : %D\n"
1408                        "    Global nodes                       : %D\n"
1409                        "    Owned nodes                        : %D\n",
1410                        comm_size, problemTypes[problemChoice],
1411                        StabilizationTypes[stab], box_faces_str, usedresource,
1412                        CeedMemTypes[memtypebackend],
1413                        (setmemtyperequest) ?
1414                        CeedMemTypes[memtyperequested] : "none",
1415                        numP, numQ, gdofs, odofs, ncompq, gnodes, lnodes);
1416     CHKERRQ(ierr);
1417   }
1418 
1419   // Set up global mass vector
1420   ierr = VecDuplicate(Q, &user->M); CHKERRQ(ierr);
1421 
1422   // Set up libCEED
1423   // CEED Bases
1424   CeedInit(ceedresource, &ceed);
1425   CeedBasisCreateTensorH1Lagrange(ceed, dim, ncompq, numP, numQ, CEED_GAUSS,
1426                                   &basisq);
1427   CeedBasisCreateTensorH1Lagrange(ceed, dim, ncompx, 2, numQ, CEED_GAUSS,
1428                                   &basisx);
1429   CeedBasisCreateTensorH1Lagrange(ceed, dim, ncompx, 2, numP,
1430                                   CEED_GAUSS_LOBATTO, &basisxc);
1431   ierr = DMGetCoordinateDM(dm, &dmcoord); CHKERRQ(ierr);
1432   ierr = DMPlexSetClosurePermutationTensor(dmcoord, PETSC_DETERMINE, NULL);
1433   CHKERRQ(ierr);
1434 
1435   // CEED Restrictions
1436   ierr = GetRestrictionForDomain(ceed, dm, 0, 0, 0, numP, numQ,
1437                                  qdatasizeVol, &restrictq, &restrictx,
1438                                  &restrictqdi); CHKERRQ(ierr);
1439 
1440   ierr = DMGetCoordinatesLocal(dm, &Xloc); CHKERRQ(ierr);
1441   ierr = CreateVectorFromPetscVec(ceed, Xloc, &xcorners); CHKERRQ(ierr);
1442 
1443   // Create the CEED vectors that will be needed in setup
1444   CeedInt NqptsVol;
1445   CeedBasisGetNumQuadraturePoints(basisq, &NqptsVol);
1446   CeedElemRestrictionGetNumElements(restrictq, &localNelemVol);
1447   CeedVectorCreate(ceed, qdatasizeVol*localNelemVol*NqptsVol, &qdata);
1448   CeedElemRestrictionCreateVector(restrictq, &q0ceed, NULL);
1449 
1450   // Create the Q-function that builds the quadrature data for the NS operator
1451   CeedQFunctionCreateInterior(ceed, 1, problem->setupVol, problem->setupVol_loc,
1452                               &qf_setupVol);
1453   CeedQFunctionAddInput(qf_setupVol, "dx", ncompx*dim, CEED_EVAL_GRAD);
1454   CeedQFunctionAddInput(qf_setupVol, "weight", 1, CEED_EVAL_WEIGHT);
1455   CeedQFunctionAddOutput(qf_setupVol, "qdata", qdatasizeVol, CEED_EVAL_NONE);
1456 
1457   // Create the Q-function that sets the ICs of the operator
1458   CeedQFunctionCreateInterior(ceed, 1, problem->ics, problem->ics_loc, &qf_ics);
1459   CeedQFunctionAddInput(qf_ics, "x", ncompx, CEED_EVAL_INTERP);
1460   CeedQFunctionAddOutput(qf_ics, "q0", ncompq, CEED_EVAL_NONE);
1461 
1462   qf_rhsVol = NULL;
1463   if (problem->applyVol_rhs) { // Create the Q-function that defines the action of the RHS operator
1464     CeedQFunctionCreateInterior(ceed, 1, problem->applyVol_rhs,
1465                                 problem->applyVol_rhs_loc, &qf_rhsVol);
1466     CeedQFunctionAddInput(qf_rhsVol, "q", ncompq, CEED_EVAL_INTERP);
1467     CeedQFunctionAddInput(qf_rhsVol, "dq", ncompq*dim, CEED_EVAL_GRAD);
1468     CeedQFunctionAddInput(qf_rhsVol, "qdata", qdatasizeVol, CEED_EVAL_NONE);
1469     CeedQFunctionAddInput(qf_rhsVol, "x", ncompx, CEED_EVAL_INTERP);
1470     CeedQFunctionAddOutput(qf_rhsVol, "v", ncompq, CEED_EVAL_INTERP);
1471     CeedQFunctionAddOutput(qf_rhsVol, "dv", ncompq*dim, CEED_EVAL_GRAD);
1472   }
1473 
1474   qf_ifunctionVol = NULL;
1475   if (problem->applyVol_ifunction) { // Create the Q-function that defines the action of the IFunction
1476     CeedQFunctionCreateInterior(ceed, 1, problem->applyVol_ifunction,
1477                                 problem->applyVol_ifunction_loc, &qf_ifunctionVol);
1478     CeedQFunctionAddInput(qf_ifunctionVol, "q", ncompq, CEED_EVAL_INTERP);
1479     CeedQFunctionAddInput(qf_ifunctionVol, "dq", ncompq*dim, CEED_EVAL_GRAD);
1480     CeedQFunctionAddInput(qf_ifunctionVol, "qdot", ncompq, CEED_EVAL_INTERP);
1481     CeedQFunctionAddInput(qf_ifunctionVol, "qdata", qdatasizeVol, CEED_EVAL_NONE);
1482     CeedQFunctionAddInput(qf_ifunctionVol, "x", ncompx, CEED_EVAL_INTERP);
1483     CeedQFunctionAddOutput(qf_ifunctionVol, "v", ncompq, CEED_EVAL_INTERP);
1484     CeedQFunctionAddOutput(qf_ifunctionVol, "dv", ncompq*dim, CEED_EVAL_GRAD);
1485   }
1486 
1487   // Create the operator that builds the quadrature data for the NS operator
1488   CeedOperatorCreate(ceed, qf_setupVol, NULL, NULL, &op_setupVol);
1489   CeedOperatorSetField(op_setupVol, "dx", restrictx, basisx, CEED_VECTOR_ACTIVE);
1490   CeedOperatorSetField(op_setupVol, "weight", CEED_ELEMRESTRICTION_NONE,
1491                        basisx, CEED_VECTOR_NONE);
1492   CeedOperatorSetField(op_setupVol, "qdata", restrictqdi,
1493                        CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
1494 
1495   // Create the operator that sets the ICs
1496   CeedOperatorCreate(ceed, qf_ics, NULL, NULL, &op_ics);
1497   CeedOperatorSetField(op_ics, "x", restrictx, basisxc, CEED_VECTOR_ACTIVE);
1498   CeedOperatorSetField(op_ics, "q0", restrictq,
1499                        CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
1500 
1501   CeedElemRestrictionCreateVector(restrictq, &user->qceed, NULL);
1502   CeedElemRestrictionCreateVector(restrictq, &user->qdotceed, NULL);
1503   CeedElemRestrictionCreateVector(restrictq, &user->gceed, NULL);
1504 
1505   if (qf_rhsVol) { // Create the RHS physics operator
1506     CeedOperator op;
1507     CeedOperatorCreate(ceed, qf_rhsVol, NULL, NULL, &op);
1508     CeedOperatorSetField(op, "q", restrictq, basisq, CEED_VECTOR_ACTIVE);
1509     CeedOperatorSetField(op, "dq", restrictq, basisq, CEED_VECTOR_ACTIVE);
1510     CeedOperatorSetField(op, "qdata", restrictqdi,
1511                          CEED_BASIS_COLLOCATED, qdata);
1512     CeedOperatorSetField(op, "x", restrictx, basisx, xcorners);
1513     CeedOperatorSetField(op, "v", restrictq, basisq, CEED_VECTOR_ACTIVE);
1514     CeedOperatorSetField(op, "dv", restrictq, basisq, CEED_VECTOR_ACTIVE);
1515     user->op_rhs_vol = op;
1516   }
1517 
1518   if (qf_ifunctionVol) { // Create the IFunction operator
1519     CeedOperator op;
1520     CeedOperatorCreate(ceed, qf_ifunctionVol, NULL, NULL, &op);
1521     CeedOperatorSetField(op, "q", restrictq, basisq, CEED_VECTOR_ACTIVE);
1522     CeedOperatorSetField(op, "dq", restrictq, basisq, CEED_VECTOR_ACTIVE);
1523     CeedOperatorSetField(op, "qdot", restrictq, basisq, user->qdotceed);
1524     CeedOperatorSetField(op, "qdata", restrictqdi,
1525                          CEED_BASIS_COLLOCATED, qdata);
1526     CeedOperatorSetField(op, "x", restrictx, basisx, xcorners);
1527     CeedOperatorSetField(op, "v", restrictq, basisq, CEED_VECTOR_ACTIVE);
1528     CeedOperatorSetField(op, "dv", restrictq, basisq, CEED_VECTOR_ACTIVE);
1529     user->op_ifunction_vol = op;
1530   }
1531 
1532   // Set up CEED for the boundaries
1533   CeedInt height = 1;
1534   CeedInt dimSur = dim - height;
1535   CeedInt numP_Sur = degree + 1;
1536   CeedInt numQ_Sur = numP_Sur + qextraSur;
1537   const CeedInt qdatasizeSur = problem->qdatasizeSur;
1538   CeedBasis basisxSur, basisxcSur, basisqSur;
1539   CeedInt NqptsSur;
1540   CeedQFunction qf_setupSur, qf_applySur;
1541 
1542   // CEED bases for the boundaries
1543   CeedBasisCreateTensorH1Lagrange(ceed, dimSur, ncompq, numP_Sur, numQ_Sur,
1544                                   CEED_GAUSS,
1545                                   &basisqSur);
1546   CeedBasisCreateTensorH1Lagrange(ceed, dimSur, ncompx, 2, numQ_Sur, CEED_GAUSS,
1547                                   &basisxSur);
1548   CeedBasisCreateTensorH1Lagrange(ceed, dimSur, ncompx, 2, numP_Sur,
1549                                   CEED_GAUSS_LOBATTO, &basisxcSur);
1550   CeedBasisGetNumQuadraturePoints(basisqSur, &NqptsSur);
1551 
1552   // Create the Q-function that builds the quadrature data for the Surface operator
1553   CeedQFunctionCreateInterior(ceed, 1, problem->setupSur, problem->setupSur_loc,
1554                               &qf_setupSur);
1555   CeedQFunctionAddInput(qf_setupSur, "dx", ncompx*dimSur, CEED_EVAL_GRAD);
1556   CeedQFunctionAddInput(qf_setupSur, "weight", 1, CEED_EVAL_WEIGHT);
1557   CeedQFunctionAddOutput(qf_setupSur, "qdataSur", qdatasizeSur, CEED_EVAL_NONE);
1558 
1559   // Creat Q-Function for Boundaries
1560   // -- Defined for Advection(2d) test cases
1561   qf_applySur = NULL;
1562   if (problem->applySur) {
1563     CeedQFunctionCreateInterior(ceed, 1, problem->applySur,
1564                                 problem->applySur_loc, &qf_applySur);
1565     CeedQFunctionAddInput(qf_applySur, "q", ncompq, CEED_EVAL_INTERP);
1566     CeedQFunctionAddInput(qf_applySur, "qdataSur", qdatasizeSur, CEED_EVAL_NONE);
1567     CeedQFunctionAddInput(qf_applySur, "x", ncompx, CEED_EVAL_INTERP);
1568     CeedQFunctionAddOutput(qf_applySur, "v", ncompq, CEED_EVAL_INTERP);
1569   }
1570 
1571   // Create CEED Operator for the whole domain
1572   if (!implicit)
1573     ierr = CreateOperatorForDomain(ceed, dm, &bc, problemChoice, wind_type,
1574                                    user->op_rhs_vol, qf_applySur,
1575                                    qf_setupSur, height, numP_Sur, numQ_Sur,
1576                                    qdatasizeSur, NqptsSur, basisxSur,
1577                                    basisqSur, &user->op_rhs);
1578   CHKERRQ(ierr);
1579   if (implicit)
1580     ierr = CreateOperatorForDomain(ceed, dm, &bc, problemChoice, wind_type,
1581                                    user->op_ifunction_vol, qf_applySur,
1582                                    qf_setupSur, height, numP_Sur, numQ_Sur,
1583                                    qdatasizeSur, NqptsSur, basisxSur,
1584                                    basisqSur, &user->op_ifunction);
1585   CHKERRQ(ierr);
1586   // Set up contex for QFunctions
1587   CeedQFunctionContextCreate(ceed, &ctxSetup);
1588   CeedQFunctionContextSetData(ctxSetup, CEED_MEM_HOST, CEED_USE_POINTER,
1589                               sizeof ctxSetupData, &ctxSetupData);
1590   if (qf_ics && problemChoice != NS_EULER_VORTEX)
1591     CeedQFunctionSetContext(qf_ics, ctxSetup);
1592 
1593   CeedScalar ctxNSData[8] = {lambda, mu, k, cv, cp, g, Rd};
1594   CeedQFunctionContextCreate(ceed, &ctxNS);
1595   CeedQFunctionContextSetData(ctxNS, CEED_MEM_HOST, CEED_USE_POINTER,
1596                               sizeof ctxNSData, &ctxNSData);
1597 
1598   struct Advection2dContext_ ctxAdvection2dData = {
1599     .CtauS = CtauS,
1600     .strong_form = strong_form,
1601     .stabilization = stab,
1602   };
1603   CeedQFunctionContextCreate(ceed, &ctxAdvection2d);
1604   CeedQFunctionContextSetData(ctxAdvection2d, CEED_MEM_HOST, CEED_USE_POINTER,
1605                               sizeof ctxAdvection2dData, &ctxAdvection2dData);
1606 
1607   struct SurfaceContext_ ctxSurfaceData = {
1608     .E_wind = E_wind,
1609     .strong_form = strong_form,
1610     .T_inlet = T_inlet,
1611     .P_inlet = P_inlet,
1612     .etv_mean_velocity[0] = etv_mean_velocity[0],
1613     .etv_mean_velocity[1] = etv_mean_velocity[1],
1614     .etv_mean_velocity[2] = etv_mean_velocity[2],
1615     .implicit = implicit,
1616   };
1617   CeedQFunctionContextCreate(ceed, &ctxSurface);
1618   CeedQFunctionContextSetData(ctxSurface, CEED_MEM_HOST, CEED_USE_POINTER,
1619                               sizeof ctxSurfaceData, &ctxSurfaceData);
1620 
1621   // Set up ctxEulerData structure
1622   ctxEulerData->time = 0.;
1623   ctxEulerData->currentTime = 0.;
1624   ctxEulerData->euler_test = euler_test;
1625   ctxEulerData->center[0] = center[0];
1626   ctxEulerData->center[1] = center[1];
1627   ctxEulerData->center[2] = center[2];
1628   ctxEulerData->vortex_strength = vortex_strength;
1629   ctxEulerData->etv_mean_velocity[0] = etv_mean_velocity[0];
1630   ctxEulerData->etv_mean_velocity[1] = etv_mean_velocity[1];
1631   ctxEulerData->etv_mean_velocity[2] = etv_mean_velocity[2];
1632   user->ctxEulerData = ctxEulerData;
1633 
1634   CeedQFunctionContextCreate(ceed, &ctxEuler);
1635   CeedQFunctionContextSetData(ctxEuler, CEED_MEM_HOST, CEED_USE_POINTER,
1636                               sizeof *ctxEulerData, ctxEulerData);
1637 
1638   switch (problemChoice) {
1639   case NS_DENSITY_CURRENT:
1640     if (qf_rhsVol) CeedQFunctionSetContext(qf_rhsVol, ctxNS);
1641     if (qf_ifunctionVol) CeedQFunctionSetContext(qf_ifunctionVol, ctxNS);
1642     break;
1643   case NS_ADVECTION:
1644   case NS_ADVECTION2D:
1645     if (qf_rhsVol) CeedQFunctionSetContext(qf_rhsVol, ctxAdvection2d);
1646     if (qf_ifunctionVol) CeedQFunctionSetContext(qf_ifunctionVol, ctxAdvection2d);
1647     if (qf_applySur) CeedQFunctionSetContext(qf_applySur, ctxSurface);
1648   case NS_EULER_VORTEX:
1649     if (qf_ics) CeedQFunctionSetContext(qf_ics, ctxEuler);
1650     if (qf_rhsVol) CeedQFunctionSetContext(qf_rhsVol, ctxEuler);
1651     if (qf_applySur) CeedQFunctionSetContext(qf_applySur, ctxSurface);
1652   }
1653 
1654   // Set up PETSc context
1655   // Set up units structure
1656   units->meter = meter;
1657   units->kilogram = kilogram;
1658   units->second = second;
1659   units->Kelvin = Kelvin;
1660   units->Pascal = Pascal;
1661   units->JperkgK = JperkgK;
1662   units->mpersquareds = mpersquareds;
1663   units->WpermK = WpermK;
1664   units->kgpercubicm = kgpercubicm;
1665   units->kgpersquaredms = kgpersquaredms;
1666   units->Joulepercubicm = Joulepercubicm;
1667   units->Joule = Joule;
1668 
1669   // Set up user structure
1670   user->comm = comm;
1671   user->outputfreq = outputfreq;
1672   user->contsteps = contsteps;
1673   user->units = units;
1674   user->dm = dm;
1675   user->dmviz = dmviz;
1676   user->interpviz = interpviz;
1677   user->ceed = ceed;
1678 
1679   // Calculate qdata and ICs
1680   // Set up state global and local vectors
1681   ierr = VecZeroEntries(Q); CHKERRQ(ierr);
1682 
1683   ierr = VectorPlacePetscVec(q0ceed, Qloc); CHKERRQ(ierr);
1684 
1685   // Apply Setup Ceed Operators
1686   ierr = VectorPlacePetscVec(xcorners, Xloc); CHKERRQ(ierr);
1687   CeedOperatorApply(op_setupVol, xcorners, qdata, CEED_REQUEST_IMMEDIATE);
1688   ierr = ComputeLumpedMassMatrix(ceed, dm, restrictq, basisq, restrictqdi, qdata,
1689                                  user->M); CHKERRQ(ierr);
1690 
1691   ierr = ICs_FixMultiplicity(op_ics, xcorners, q0ceed, dm, Qloc, Q, restrictq,
1692                              ctxSetup, 0.0); CHKERRQ(ierr);
1693   if (1) { // Record boundary values from initial condition and override DMPlexInsertBoundaryValues()
1694     // We use this for the main simulation DM because the reference DMPlexInsertBoundaryValues() is very slow.  If we
1695     // disable this, we should still get the same results due to the problem->bc function, but with potentially much
1696     // slower execution.
1697     Vec Qbc;
1698     ierr = DMGetNamedLocalVector(dm, "Qbc", &Qbc); CHKERRQ(ierr);
1699     ierr = VecCopy(Qloc, Qbc); CHKERRQ(ierr);
1700     ierr = VecZeroEntries(Qloc); CHKERRQ(ierr);
1701     ierr = DMGlobalToLocal(dm, Q, INSERT_VALUES, Qloc); CHKERRQ(ierr);
1702     ierr = VecAXPY(Qbc, -1., Qloc); CHKERRQ(ierr);
1703     ierr = DMRestoreNamedLocalVector(dm, "Qbc", &Qbc); CHKERRQ(ierr);
1704     ierr = PetscObjectComposeFunction((PetscObject)dm,
1705                                       "DMPlexInsertBoundaryValues_C", DMPlexInsertBoundaryValues_NS);
1706     CHKERRQ(ierr);
1707   }
1708 
1709   MPI_Comm_rank(comm, &rank);
1710   if (!rank) {ierr = PetscMkdir(user->outputdir); CHKERRQ(ierr);}
1711   // Gather initial Q values
1712   // In case of continuation of simulation, set up initial values from binary file
1713   if (contsteps) { // continue from existent solution
1714     PetscViewer viewer;
1715     char filepath[PETSC_MAX_PATH_LEN];
1716     // Read input
1717     ierr = PetscSNPrintf(filepath, sizeof filepath, "%s/ns-solution.bin",
1718                          user->outputdir);
1719     CHKERRQ(ierr);
1720     ierr = PetscViewerBinaryOpen(comm, filepath, FILE_MODE_READ, &viewer);
1721     CHKERRQ(ierr);
1722     ierr = VecLoad(Q, viewer); CHKERRQ(ierr);
1723     ierr = PetscViewerDestroy(&viewer); CHKERRQ(ierr);
1724   }
1725   ierr = DMRestoreLocalVector(dm, &Qloc); CHKERRQ(ierr);
1726 
1727   // Create and setup TS
1728   ierr = TSCreate(comm, &ts); CHKERRQ(ierr);
1729   ierr = TSSetDM(ts, dm); CHKERRQ(ierr);
1730   if (implicit) {
1731     ierr = TSSetType(ts, TSBDF); CHKERRQ(ierr);
1732     if (user->op_ifunction) {
1733       ierr = TSSetIFunction(ts, NULL, IFunction_NS, &user); CHKERRQ(ierr);
1734     } else {                    // Implicit integrators can fall back to using an RHSFunction
1735       ierr = TSSetRHSFunction(ts, NULL, RHS_NS, &user); CHKERRQ(ierr);
1736     }
1737   } else {
1738     if (!user->op_rhs) SETERRQ(comm, PETSC_ERR_ARG_NULL,
1739                                  "Problem does not provide RHSFunction");
1740     ierr = TSSetType(ts, TSRK); CHKERRQ(ierr);
1741     ierr = TSRKSetType(ts, TSRK5F); CHKERRQ(ierr);
1742     ierr = TSSetRHSFunction(ts, NULL, RHS_NS, &user); CHKERRQ(ierr);
1743   }
1744   ierr = TSSetMaxTime(ts, 500. * units->second); CHKERRQ(ierr);
1745   ierr = TSSetExactFinalTime(ts, TS_EXACTFINALTIME_STEPOVER); CHKERRQ(ierr);
1746   ierr = TSSetTimeStep(ts, 1.e-2 * units->second); CHKERRQ(ierr);
1747   if (test) {ierr = TSSetMaxSteps(ts, 10); CHKERRQ(ierr);}
1748   ierr = TSGetAdapt(ts, &adapt); CHKERRQ(ierr);
1749   ierr = TSAdaptSetStepLimits(adapt,
1750                               1.e-12 * units->second,
1751                               1.e2 * units->second); CHKERRQ(ierr);
1752   ierr = TSSetFromOptions(ts); CHKERRQ(ierr);
1753   if (!contsteps) { // print initial condition
1754     if (!test) {
1755       ierr = TSMonitor_NS(ts, 0, 0., Q, user); CHKERRQ(ierr);
1756     }
1757   } else { // continue from time of last output
1758     PetscReal time;
1759     PetscInt count;
1760     PetscViewer viewer;
1761     char filepath[PETSC_MAX_PATH_LEN];
1762     ierr = PetscSNPrintf(filepath, sizeof filepath, "%s/ns-time.bin",
1763                          user->outputdir); CHKERRQ(ierr);
1764     ierr = PetscViewerBinaryOpen(comm, filepath, FILE_MODE_READ, &viewer);
1765     CHKERRQ(ierr);
1766     ierr = PetscViewerBinaryRead(viewer, &time, 1, &count, PETSC_REAL);
1767     CHKERRQ(ierr);
1768     ierr = PetscViewerDestroy(&viewer); CHKERRQ(ierr);
1769     ierr = TSSetTime(ts, time * user->units->second); CHKERRQ(ierr);
1770   }
1771   if (!test) {
1772     ierr = TSMonitorSet(ts, TSMonitor_NS, user, NULL); CHKERRQ(ierr);
1773   }
1774 
1775   // Solve
1776   start = MPI_Wtime();
1777   ierr = PetscBarrier((PetscObject)ts); CHKERRQ(ierr);
1778   ierr = TSSolve(ts, Q); CHKERRQ(ierr);
1779   cpu_time_used = MPI_Wtime() - start;
1780   ierr = TSGetSolveTime(ts, &ftime); CHKERRQ(ierr);
1781   ierr = MPI_Allreduce(MPI_IN_PLACE, &cpu_time_used, 1, MPI_DOUBLE, MPI_MIN,
1782                        comm); CHKERRQ(ierr);
1783   if (!test) {
1784     ierr = PetscPrintf(PETSC_COMM_WORLD,
1785                        "Time taken for solution (sec): %g\n",
1786                        (double)cpu_time_used); CHKERRQ(ierr);
1787   }
1788 
1789   // Get error
1790   if (problem->non_zero_time && !test) {
1791     Vec Qexact, Qexactloc;
1792     PetscReal norm;
1793     ierr = DMCreateGlobalVector(dm, &Qexact); CHKERRQ(ierr);
1794     ierr = DMGetLocalVector(dm, &Qexactloc); CHKERRQ(ierr);
1795     ierr = VecGetSize(Qexactloc, &lnodes); CHKERRQ(ierr);
1796 
1797     ierr = ICs_FixMultiplicity(op_ics, xcorners, q0ceed, dm, Qexactloc, Qexact,
1798                                restrictq, ctxSetup, ftime); CHKERRQ(ierr);
1799 
1800     ierr = VecAXPY(Q, -1.0, Qexact);  CHKERRQ(ierr);
1801     ierr = VecNorm(Q, NORM_MAX, &norm); CHKERRQ(ierr);
1802     CeedVectorDestroy(&q0ceed);
1803     ierr = PetscPrintf(PETSC_COMM_WORLD,
1804                        "Max Error: %g\n",
1805                        (double)norm); CHKERRQ(ierr);
1806     // Clean up vectors
1807     ierr = DMRestoreLocalVector(dm, &Qexactloc); CHKERRQ(ierr);
1808     ierr = VecDestroy(&Qexact); CHKERRQ(ierr);
1809   }
1810 
1811   // Output Statistics
1812   ierr = TSGetStepNumber(ts, &steps); CHKERRQ(ierr);
1813   if (!test) {
1814     ierr = PetscPrintf(PETSC_COMM_WORLD,
1815                        "Time integrator took %D time steps to reach final time %g\n",
1816                        steps, (double)ftime); CHKERRQ(ierr);
1817   }
1818   // Output numerical values from command line
1819   ierr = VecViewFromOptions(Q, NULL, "-vec_view"); CHKERRQ(ierr);
1820 
1821   // Compare reference solution values with current test run for CI
1822   if (test) {
1823     PetscViewer viewer;
1824     // Read reference file
1825     Vec Qref;
1826     PetscReal error, Qrefnorm;
1827     ierr = VecDuplicate(Q, &Qref); CHKERRQ(ierr);
1828     ierr = PetscViewerBinaryOpen(comm, filepath, FILE_MODE_READ, &viewer);
1829     CHKERRQ(ierr);
1830     ierr = VecLoad(Qref, viewer); CHKERRQ(ierr);
1831     ierr = PetscViewerDestroy(&viewer); CHKERRQ(ierr);
1832 
1833     // Compute error with respect to reference solution
1834     ierr = VecAXPY(Q, -1.0, Qref);  CHKERRQ(ierr);
1835     ierr = VecNorm(Qref, NORM_MAX, &Qrefnorm); CHKERRQ(ierr);
1836     ierr = VecScale(Q, 1./Qrefnorm); CHKERRQ(ierr);
1837     ierr = VecNorm(Q, NORM_MAX, &error); CHKERRQ(ierr);
1838     ierr = VecDestroy(&Qref); CHKERRQ(ierr);
1839     // Check error
1840     if (error > testtol) {
1841       ierr = PetscPrintf(PETSC_COMM_WORLD,
1842                          "Test failed with error norm %g\n",
1843                          (double)error); CHKERRQ(ierr);
1844     }
1845     ierr = PetscViewerDestroy(&viewer); CHKERRQ(ierr);
1846   }
1847 
1848   // Clean up libCEED
1849   CeedVectorDestroy(&qdata);
1850   CeedVectorDestroy(&user->qceed);
1851   CeedVectorDestroy(&user->qdotceed);
1852   CeedVectorDestroy(&user->gceed);
1853   CeedVectorDestroy(&xcorners);
1854   CeedBasisDestroy(&basisq);
1855   CeedBasisDestroy(&basisx);
1856   CeedBasisDestroy(&basisxc);
1857   CeedElemRestrictionDestroy(&restrictq);
1858   CeedElemRestrictionDestroy(&restrictx);
1859   CeedElemRestrictionDestroy(&restrictqdi);
1860   CeedQFunctionDestroy(&qf_setupVol);
1861   CeedQFunctionDestroy(&qf_ics);
1862   CeedQFunctionDestroy(&qf_rhsVol);
1863   CeedQFunctionDestroy(&qf_ifunctionVol);
1864   CeedQFunctionContextDestroy(&ctxSetup);
1865   CeedQFunctionContextDestroy(&ctxNS);
1866   CeedQFunctionContextDestroy(&ctxAdvection2d);
1867   CeedQFunctionContextDestroy(&ctxSurface);
1868   CeedQFunctionContextDestroy(&ctxEuler);
1869   CeedOperatorDestroy(&op_setupVol);
1870   CeedOperatorDestroy(&op_ics);
1871   CeedOperatorDestroy(&user->op_rhs_vol);
1872   CeedOperatorDestroy(&user->op_ifunction_vol);
1873   CeedDestroy(&ceed);
1874   CeedBasisDestroy(&basisqSur);
1875   CeedBasisDestroy(&basisxSur);
1876   CeedBasisDestroy(&basisxcSur);
1877   CeedQFunctionDestroy(&qf_setupSur);
1878   CeedQFunctionDestroy(&qf_applySur);
1879   CeedOperatorDestroy(&user->op_rhs);
1880   CeedOperatorDestroy(&user->op_ifunction);
1881 
1882   // Clean up PETSc
1883   ierr = VecDestroy(&Q); CHKERRQ(ierr);
1884   ierr = VecDestroy(&user->M); CHKERRQ(ierr);
1885   ierr = MatDestroy(&interpviz); CHKERRQ(ierr);
1886   ierr = DMDestroy(&dmviz); CHKERRQ(ierr);
1887   ierr = TSDestroy(&ts); CHKERRQ(ierr);
1888   ierr = DMDestroy(&dm); CHKERRQ(ierr);
1889   ierr = PetscFree(units); CHKERRQ(ierr);
1890   ierr = PetscFree(user); CHKERRQ(ierr);
1891   ierr = PetscFree(ctxEulerData); CHKERRQ(ierr);
1892   return PetscFinalize();
1893 }
1894