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