xref: /libCEED/examples/fluids/navierstokes.c (revision 00c585afda643a90d1ea43ceca916155e9cf184d)
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 eulercontext;
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) {
438     // Ignore wall and slip BCs
439     bc->nwall = 0;
440     bc->nslip[0] = bc->nslip[1] = 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 
488   if (problemChoice == NS_EULER_VORTEX) {
489 
490     // Create CEED Operator for each boundary face
491     PetscInt localNelemSur[2];
492     CeedVector qdataSur[2];
493     CeedQFunction qf_apply;
494     CeedOperator op_setupSur[2], op_applySur[2];
495     CeedElemRestriction restrictxSur[2], restrictqSur[2], restrictqdiSur[2];
496 
497     for (CeedInt i=0; i<2; i++) {
498       ierr = GetRestrictionForDomain(ceed, dm, height, domainLabel, i+5, numP_Sur,
499                                      numQ_Sur, qdatasizeSur, &restrictqSur[i],
500                                      &restrictxSur[i], &restrictqdiSur[i]);
501       CHKERRQ(ierr);
502       // Create the CEED vectors that will be needed in Boundary setup
503       CeedElemRestrictionGetNumElements(restrictqSur[i], &localNelemSur[i]);
504       CeedVectorCreate(ceed, qdatasizeSur*localNelemSur[i]*NqptsSur,
505                        &qdataSur[i]);
506       // Create the operator that builds the quadrature data for the Boundary operator
507       CeedOperatorCreate(ceed, qf_setupSur, NULL, NULL, &op_setupSur[i]);
508       CeedOperatorSetField(op_setupSur[i], "dx", restrictxSur[i], basisxSur,
509                            CEED_VECTOR_ACTIVE);
510       CeedOperatorSetField(op_setupSur[i], "weight", CEED_ELEMRESTRICTION_NONE,
511                            basisxSur, CEED_VECTOR_NONE);
512       CeedOperatorSetField(op_setupSur[i], "qdataSur", restrictqdiSur[i],
513                            CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
514       // Create Boundary operator
515       CeedOperatorCreate(ceed, qf_applySur, NULL, NULL, &op_applySur[i]);
516       CeedOperatorSetField(op_applySur[i], "q", restrictqSur[i], basisqSur,
517                            CEED_VECTOR_ACTIVE);
518       CeedOperatorSetField(op_applySur[i], "qdataSur", restrictqdiSur[i],
519                            CEED_BASIS_COLLOCATED, qdataSur[i]);
520       CeedOperatorSetField(op_applySur[i], "x", restrictxSur[i], basisxSur,
521                            xcorners);
522       CeedOperatorSetField(op_applySur[i], "v", restrictqSur[i], basisqSur,
523                            CEED_VECTOR_ACTIVE);
524       // Apply CEED operator for Boundary setup
525       CeedOperatorApply(op_setupSur[i], xcorners, qdataSur[i],
526                         CEED_REQUEST_IMMEDIATE);
527       // --Apply Sub-Operator for the Boundary
528       CeedCompositeOperatorAddSub(*op_apply, op_applySur[i]);
529     }
530     CeedVectorDestroy(&xcorners);
531   }
532   PetscFunctionReturn(0);
533 }
534 
535 static int CreateVectorFromPetscVec(Ceed ceed, Vec p, CeedVector *v) {
536   PetscErrorCode ierr;
537   PetscInt m;
538 
539   PetscFunctionBeginUser;
540   ierr = VecGetLocalSize(p, &m); CHKERRQ(ierr);
541   ierr = CeedVectorCreate(ceed, m, v); CHKERRQ(ierr);
542   PetscFunctionReturn(0);
543 }
544 
545 static int VectorPlacePetscVec(CeedVector c, Vec p) {
546   PetscErrorCode ierr;
547   PetscInt mceed, mpetsc;
548   PetscScalar *a;
549 
550   PetscFunctionBeginUser;
551   ierr = CeedVectorGetLength(c, &mceed); CHKERRQ(ierr);
552   ierr = VecGetLocalSize(p, &mpetsc); CHKERRQ(ierr);
553   if (mceed != mpetsc) SETERRQ2(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP,
554                                   "Cannot place PETSc Vec of length %D in CeedVector of length %D",
555                                   mpetsc, mceed);
556   ierr = VecGetArray(p, &a); CHKERRQ(ierr);
557   CeedVectorSetArray(c, CEED_MEM_HOST, CEED_USE_POINTER, a);
558   PetscFunctionReturn(0);
559 }
560 
561 static PetscErrorCode DMPlexInsertBoundaryValues_NS(DM dm,
562     PetscBool insertEssential, Vec Qloc, PetscReal time, Vec faceGeomFVM,
563     Vec cellGeomFVM, Vec gradFVM) {
564   PetscErrorCode ierr;
565   Vec Qbc;
566 
567   PetscFunctionBegin;
568   ierr = DMGetNamedLocalVector(dm, "Qbc", &Qbc); CHKERRQ(ierr);
569   ierr = VecAXPY(Qloc, 1., Qbc); CHKERRQ(ierr);
570   ierr = DMRestoreNamedLocalVector(dm, "Qbc", &Qbc); CHKERRQ(ierr);
571   PetscFunctionReturn(0);
572 }
573 
574 // This is the RHS of the ODE, given as u_t = G(t,u)
575 // This function takes in a state vector Q and writes into G
576 static PetscErrorCode RHS_NS(TS ts, PetscReal t, Vec Q, Vec G, void *userData) {
577   PetscErrorCode ierr;
578   User user = *(User *)userData;
579   PetscScalar *q, *g;
580   Vec Qloc, Gloc;
581 
582   // Global-to-local
583   PetscFunctionBeginUser;
584   user->eulercontext->currentTime = t;
585   ierr = DMGetLocalVector(user->dm, &Qloc); 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(Gloc); CHKERRQ(ierr);
592 
593   // Ceed Vectors
594   ierr = VecGetArrayRead(Qloc, (const PetscScalar **)&q); CHKERRQ(ierr);
595   ierr = VecGetArray(Gloc, &g); CHKERRQ(ierr);
596   CeedVectorSetArray(user->qceed, CEED_MEM_HOST, CEED_USE_POINTER, q);
597   CeedVectorSetArray(user->gceed, CEED_MEM_HOST, CEED_USE_POINTER, g);
598 
599   // Apply CEED operator
600   CeedOperatorApply(user->op_rhs, user->qceed, user->gceed,
601                     CEED_REQUEST_IMMEDIATE);
602 
603   // Restore vectors
604   ierr = VecRestoreArrayRead(Qloc, (const PetscScalar **)&q); CHKERRQ(ierr);
605   ierr = VecRestoreArray(Gloc, &g); CHKERRQ(ierr);
606 
607   ierr = VecZeroEntries(G); CHKERRQ(ierr);
608   ierr = DMLocalToGlobal(user->dm, Gloc, ADD_VALUES, G); CHKERRQ(ierr);
609 
610   // Inverse of the lumped mass matrix
611   ierr = VecPointwiseMult(G, G, user->M); // M is Minv
612   CHKERRQ(ierr);
613 
614   ierr = DMRestoreLocalVector(user->dm, &Qloc); CHKERRQ(ierr);
615   ierr = DMRestoreLocalVector(user->dm, &Gloc); CHKERRQ(ierr);
616   PetscFunctionReturn(0);
617 }
618 
619 static PetscErrorCode IFunction_NS(TS ts, PetscReal t, Vec Q, Vec Qdot, Vec G,
620                                    void *userData) {
621   PetscErrorCode ierr;
622   User user = *(User *)userData;
623   const PetscScalar *q, *qdot;
624   PetscScalar *g;
625   Vec Qloc, Qdotloc, Gloc;
626 
627   // Global-to-local
628   PetscFunctionBeginUser;
629   ierr = DMGetLocalVector(user->dm, &Qloc); CHKERRQ(ierr);
630   ierr = DMGetLocalVector(user->dm, &Qdotloc); CHKERRQ(ierr);
631   ierr = DMGetLocalVector(user->dm, &Gloc); CHKERRQ(ierr);
632   ierr = VecZeroEntries(Qloc); CHKERRQ(ierr);
633   ierr = DMGlobalToLocal(user->dm, Q, INSERT_VALUES, Qloc); CHKERRQ(ierr);
634   ierr = DMPlexInsertBoundaryValues(user->dm, PETSC_TRUE, Qloc, 0.0,
635                                     NULL, NULL, NULL); CHKERRQ(ierr);
636   ierr = VecZeroEntries(Qdotloc); CHKERRQ(ierr);
637   ierr = DMGlobalToLocal(user->dm, Qdot, INSERT_VALUES, Qdotloc); CHKERRQ(ierr);
638   ierr = VecZeroEntries(Gloc); CHKERRQ(ierr);
639 
640   // Ceed Vectors
641   ierr = VecGetArrayRead(Qloc, &q); CHKERRQ(ierr);
642   ierr = VecGetArrayRead(Qdotloc, &qdot); CHKERRQ(ierr);
643   ierr = VecGetArray(Gloc, &g); CHKERRQ(ierr);
644   CeedVectorSetArray(user->qceed, CEED_MEM_HOST, CEED_USE_POINTER,
645                      (PetscScalar *)q);
646   CeedVectorSetArray(user->qdotceed, CEED_MEM_HOST, CEED_USE_POINTER,
647                      (PetscScalar *)qdot);
648   CeedVectorSetArray(user->gceed, CEED_MEM_HOST, CEED_USE_POINTER, g);
649 
650   // Apply CEED operator
651   CeedOperatorApply(user->op_ifunction, user->qceed, user->gceed,
652                     CEED_REQUEST_IMMEDIATE);
653 
654   // Restore vectors
655   ierr = VecRestoreArrayRead(Qloc, &q); CHKERRQ(ierr);
656   ierr = VecRestoreArrayRead(Qdotloc, &qdot); CHKERRQ(ierr);
657   ierr = VecRestoreArray(Gloc, &g); CHKERRQ(ierr);
658 
659   ierr = VecZeroEntries(G); CHKERRQ(ierr);
660   ierr = DMLocalToGlobal(user->dm, Gloc, ADD_VALUES, G); CHKERRQ(ierr);
661 
662   ierr = DMRestoreLocalVector(user->dm, &Qloc); CHKERRQ(ierr);
663   ierr = DMRestoreLocalVector(user->dm, &Qdotloc); CHKERRQ(ierr);
664   ierr = DMRestoreLocalVector(user->dm, &Gloc); CHKERRQ(ierr);
665   PetscFunctionReturn(0);
666 }
667 
668 // User provided TS Monitor
669 static PetscErrorCode TSMonitor_NS(TS ts, PetscInt stepno, PetscReal time,
670                                    Vec Q, void *ctx) {
671   User user = ctx;
672   Vec Qloc;
673   char filepath[PETSC_MAX_PATH_LEN];
674   PetscViewer viewer;
675   PetscErrorCode ierr;
676 
677   // Set up output
678   PetscFunctionBeginUser;
679   // Print every 'outputfreq' steps
680   if (stepno % user->outputfreq != 0)
681     PetscFunctionReturn(0);
682   ierr = DMGetLocalVector(user->dm, &Qloc); CHKERRQ(ierr);
683   ierr = PetscObjectSetName((PetscObject)Qloc, "StateVec"); CHKERRQ(ierr);
684   ierr = VecZeroEntries(Qloc); CHKERRQ(ierr);
685   ierr = DMGlobalToLocal(user->dm, Q, INSERT_VALUES, Qloc); CHKERRQ(ierr);
686 
687   // Output
688   ierr = PetscSNPrintf(filepath, sizeof filepath, "%s/ns-%03D.vtu",
689                        user->outputdir, stepno + user->contsteps);
690   CHKERRQ(ierr);
691   ierr = PetscViewerVTKOpen(PetscObjectComm((PetscObject)Q), filepath,
692                             FILE_MODE_WRITE, &viewer); CHKERRQ(ierr);
693   ierr = VecView(Qloc, viewer); CHKERRQ(ierr);
694   ierr = PetscViewerDestroy(&viewer); CHKERRQ(ierr);
695   if (user->dmviz) {
696     Vec Qrefined, Qrefined_loc;
697     char filepath_refined[PETSC_MAX_PATH_LEN];
698     PetscViewer viewer_refined;
699 
700     ierr = DMGetGlobalVector(user->dmviz, &Qrefined); CHKERRQ(ierr);
701     ierr = DMGetLocalVector(user->dmviz, &Qrefined_loc); CHKERRQ(ierr);
702     ierr = PetscObjectSetName((PetscObject)Qrefined_loc, "Refined");
703     CHKERRQ(ierr);
704     ierr = MatInterpolate(user->interpviz, Q, Qrefined); CHKERRQ(ierr);
705     ierr = VecZeroEntries(Qrefined_loc); CHKERRQ(ierr);
706     ierr = DMGlobalToLocal(user->dmviz, Qrefined, INSERT_VALUES, Qrefined_loc);
707     CHKERRQ(ierr);
708     ierr = PetscSNPrintf(filepath_refined, sizeof filepath_refined,
709                          "%s/nsrefined-%03D.vtu",
710                          user->outputdir, stepno + user->contsteps);
711     CHKERRQ(ierr);
712     ierr = PetscViewerVTKOpen(PetscObjectComm((PetscObject)Qrefined),
713                               filepath_refined,
714                               FILE_MODE_WRITE, &viewer_refined); CHKERRQ(ierr);
715     ierr = VecView(Qrefined_loc, viewer_refined); CHKERRQ(ierr);
716     ierr = DMRestoreLocalVector(user->dmviz, &Qrefined_loc); CHKERRQ(ierr);
717     ierr = DMRestoreGlobalVector(user->dmviz, &Qrefined); CHKERRQ(ierr);
718     ierr = PetscViewerDestroy(&viewer_refined); CHKERRQ(ierr);
719   }
720   ierr = DMRestoreLocalVector(user->dm, &Qloc); CHKERRQ(ierr);
721 
722   // Save data in a binary file for continuation of simulations
723   ierr = PetscSNPrintf(filepath, sizeof filepath, "%s/ns-solution.bin",
724                        user->outputdir); CHKERRQ(ierr);
725   ierr = PetscViewerBinaryOpen(user->comm, filepath, FILE_MODE_WRITE, &viewer);
726   CHKERRQ(ierr);
727   ierr = VecView(Q, viewer); CHKERRQ(ierr);
728   ierr = PetscViewerDestroy(&viewer); CHKERRQ(ierr);
729 
730   // Save time stamp
731   // Dimensionalize time back
732   time /= user->units->second;
733   ierr = PetscSNPrintf(filepath, sizeof filepath, "%s/ns-time.bin",
734                        user->outputdir); CHKERRQ(ierr);
735   ierr = PetscViewerBinaryOpen(user->comm, filepath, FILE_MODE_WRITE, &viewer);
736   CHKERRQ(ierr);
737   #if PETSC_VERSION_GE(3,13,0)
738   ierr = PetscViewerBinaryWrite(viewer, &time, 1, PETSC_REAL);
739   #else
740   ierr = PetscViewerBinaryWrite(viewer, &time, 1, PETSC_REAL, true);
741   #endif
742   CHKERRQ(ierr);
743   ierr = PetscViewerDestroy(&viewer); CHKERRQ(ierr);
744 
745   PetscFunctionReturn(0);
746 }
747 
748 static PetscErrorCode ICs_FixMultiplicity(CeedOperator op_ics,
749     CeedVector xcorners, CeedVector q0ceed, DM dm, Vec Qloc, Vec Q,
750     CeedElemRestriction restrictq, CeedQFunctionContext ctxSetup, CeedScalar time) {
751   PetscErrorCode ierr;
752   CeedVector multlvec;
753   Vec Multiplicity, MultiplicityLoc;
754 
755   SetupContext ctxSetupData;
756   CeedQFunctionContextGetData(ctxSetup, CEED_MEM_HOST, (void **)&ctxSetupData);
757   ctxSetupData->time = time;
758   CeedQFunctionContextRestoreData(ctxSetup, (void **)&ctxSetupData);
759 
760   ierr = VecZeroEntries(Qloc); CHKERRQ(ierr);
761   ierr = VectorPlacePetscVec(q0ceed, Qloc); CHKERRQ(ierr);
762   CeedOperatorApply(op_ics, xcorners, q0ceed, CEED_REQUEST_IMMEDIATE);
763   ierr = VecZeroEntries(Q); CHKERRQ(ierr);
764   ierr = DMLocalToGlobal(dm, Qloc, ADD_VALUES, Q); CHKERRQ(ierr);
765 
766   // Fix multiplicity for output of ICs
767   ierr = DMGetLocalVector(dm, &MultiplicityLoc); CHKERRQ(ierr);
768   CeedElemRestrictionCreateVector(restrictq, &multlvec, NULL);
769   ierr = VectorPlacePetscVec(multlvec, MultiplicityLoc); CHKERRQ(ierr);
770   CeedElemRestrictionGetMultiplicity(restrictq, multlvec);
771   CeedVectorDestroy(&multlvec);
772   ierr = DMGetGlobalVector(dm, &Multiplicity); CHKERRQ(ierr);
773   ierr = VecZeroEntries(Multiplicity); CHKERRQ(ierr);
774   ierr = DMLocalToGlobal(dm, MultiplicityLoc, ADD_VALUES, Multiplicity);
775   CHKERRQ(ierr);
776   ierr = VecPointwiseDivide(Q, Q, Multiplicity); CHKERRQ(ierr);
777   ierr = VecPointwiseDivide(Qloc, Qloc, MultiplicityLoc); CHKERRQ(ierr);
778   ierr = DMRestoreLocalVector(dm, &MultiplicityLoc); CHKERRQ(ierr);
779   ierr = DMRestoreGlobalVector(dm, &Multiplicity); CHKERRQ(ierr);
780 
781   PetscFunctionReturn(0);
782 }
783 
784 static PetscErrorCode ComputeLumpedMassMatrix(Ceed ceed, DM dm,
785     CeedElemRestriction restrictq, CeedBasis basisq,
786     CeedElemRestriction restrictqdi, CeedVector qdata, Vec M) {
787   PetscErrorCode ierr;
788   CeedQFunction qf_mass;
789   CeedOperator op_mass;
790   CeedVector mceed;
791   Vec Mloc;
792   CeedInt ncompq, qdatasize;
793 
794   PetscFunctionBeginUser;
795   CeedElemRestrictionGetNumComponents(restrictq, &ncompq);
796   CeedElemRestrictionGetNumComponents(restrictqdi, &qdatasize);
797   // Create the Q-function that defines the action of the mass operator
798   CeedQFunctionCreateInterior(ceed, 1, Mass, Mass_loc, &qf_mass);
799   CeedQFunctionAddInput(qf_mass, "q", ncompq, CEED_EVAL_INTERP);
800   CeedQFunctionAddInput(qf_mass, "qdata", qdatasize, CEED_EVAL_NONE);
801   CeedQFunctionAddOutput(qf_mass, "v", ncompq, CEED_EVAL_INTERP);
802 
803   // Create the mass operator
804   CeedOperatorCreate(ceed, qf_mass, NULL, NULL, &op_mass);
805   CeedOperatorSetField(op_mass, "q", restrictq, basisq, CEED_VECTOR_ACTIVE);
806   CeedOperatorSetField(op_mass, "qdata", restrictqdi,
807                        CEED_BASIS_COLLOCATED, qdata);
808   CeedOperatorSetField(op_mass, "v", restrictq, basisq, CEED_VECTOR_ACTIVE);
809 
810   ierr = DMGetLocalVector(dm, &Mloc); CHKERRQ(ierr);
811   ierr = VecZeroEntries(Mloc); CHKERRQ(ierr);
812   CeedElemRestrictionCreateVector(restrictq, &mceed, NULL);
813   ierr = VectorPlacePetscVec(mceed, Mloc); CHKERRQ(ierr);
814 
815   {
816     // Compute a lumped mass matrix
817     CeedVector onesvec;
818     CeedElemRestrictionCreateVector(restrictq, &onesvec, NULL);
819     CeedVectorSetValue(onesvec, 1.0);
820     CeedOperatorApply(op_mass, onesvec, mceed, CEED_REQUEST_IMMEDIATE);
821     CeedVectorDestroy(&onesvec);
822     CeedOperatorDestroy(&op_mass);
823     CeedVectorDestroy(&mceed);
824   }
825   CeedQFunctionDestroy(&qf_mass);
826 
827   ierr = VecZeroEntries(M); CHKERRQ(ierr);
828   ierr = DMLocalToGlobal(dm, Mloc, ADD_VALUES, M); CHKERRQ(ierr);
829   ierr = DMRestoreLocalVector(dm, &Mloc); CHKERRQ(ierr);
830 
831   // Invert diagonally lumped mass vector for RHS function
832   ierr = VecReciprocal(M); CHKERRQ(ierr);
833   PetscFunctionReturn(0);
834 }
835 
836 static PetscErrorCode SetUpDM(DM dm, problemData *problem, PetscInt degree,
837                               SimpleBC bc, void *ctxSetupData) {
838   PetscErrorCode ierr;
839 
840   PetscFunctionBeginUser;
841   {
842     // Configure the finite element space and boundary conditions
843     PetscFE fe;
844     PetscInt ncompq = 5;
845     ierr = PetscFECreateLagrange(PETSC_COMM_SELF, problem->dim, ncompq,
846                                  PETSC_FALSE, degree, PETSC_DECIDE,
847                                  &fe); CHKERRQ(ierr);
848     ierr = PetscObjectSetName((PetscObject)fe, "Q"); CHKERRQ(ierr);
849     ierr = DMAddField(dm, NULL,(PetscObject)fe); CHKERRQ(ierr);
850     ierr = DMCreateDS(dm); CHKERRQ(ierr);
851     if (problem->bc == Exact_Euler)
852       bc->nwall = bc->nslip[0] = bc->nslip[1] = 0;
853     {
854       PetscInt comps[1] = {1};
855       ierr = DMAddBoundary(dm, DM_BC_ESSENTIAL, "slipx", "Face Sets", 0,
856                            1, comps, (void(*)(void))NULL, NULL, bc->nslip[0],
857                            bc->slips[0], ctxSetupData); CHKERRQ(ierr);
858       comps[0] = 2;
859       ierr = DMAddBoundary(dm, DM_BC_ESSENTIAL, "slipy", "Face Sets", 0,
860                            1, comps, (void(*)(void))NULL, NULL, bc->nslip[1],
861                            bc->slips[1], ctxSetupData); CHKERRQ(ierr);
862       comps[0] = 3;
863       ierr = DMAddBoundary(dm, DM_BC_ESSENTIAL, "slipz", "Face Sets", 0,
864                            1, comps, (void(*)(void))NULL, NULL, bc->nslip[2],
865                            bc->slips[2], ctxSetupData); CHKERRQ(ierr);
866     }
867     if (bc->userbc == PETSC_TRUE) {
868       for (PetscInt c = 0; c < 3; c++) {
869         for (PetscInt s = 0; s < bc->nslip[c]; s++) {
870           for (PetscInt w = 0; w < bc->nwall; w++) {
871             if (bc->slips[c][s] == bc->walls[w])
872               SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
873                        "Boundary condition already set on face %D!\n",
874                        bc->walls[w]);
875           }
876         }
877       }
878     }
879     // Wall boundary conditions are zero energy density and zero flux for
880     //   velocity in advection/advection2d, and zero velocity and zero flux
881     //   for mass density and energy density in density_current
882     {
883       if (problem->bc == Exact_Advection || problem->bc == Exact_Advection2d) {
884         PetscInt comps[1] = {4};
885         ierr = DMAddBoundary(dm, DM_BC_ESSENTIAL, "wall", "Face Sets", 0,
886                              1, comps, (void(*)(void))problem->bc, NULL,
887                              bc->nwall, bc->walls, ctxSetup); CHKERRQ(ierr);
888       } else if (problem->bc == Exact_DC) {
889         PetscInt comps[3] = {1, 2, 3};
890         ierr = DMAddBoundary(dm, DM_BC_ESSENTIAL, "wall", "Face Sets", 0,
891                              3, comps, (void(*)(void))problem->bc, NULL,
892                              bc->nwall, bc->walls, ctxSetupData); CHKERRQ(ierr);
893       } else if (problem->bc == Exact_Euler) {
894         PetscInt bcMMS[2] = {3, 4};
895         ierr = DMAddBoundary(dm, DM_BC_ESSENTIAL, "mms", "Face Sets", 0,
896                              0, NULL, (void(*)(void))problem->bc, NULL,
897                              2, bcMMS, ctxSetup); CHKERRQ(ierr);
898       } else
899         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
900                 "Undefined boundary conditions for this problem");
901     }
902     ierr = DMPlexSetClosurePermutationTensor(dm, PETSC_DETERMINE, NULL);
903     CHKERRQ(ierr);
904     ierr = PetscFEDestroy(&fe); CHKERRQ(ierr);
905   }
906   {
907     // Empty name for conserved field (because there is only one field)
908     PetscSection section;
909     ierr = DMGetLocalSection(dm, &section); CHKERRQ(ierr);
910     ierr = PetscSectionSetFieldName(section, 0, ""); CHKERRQ(ierr);
911     ierr = PetscSectionSetComponentName(section, 0, 0, "Density");
912     CHKERRQ(ierr);
913     ierr = PetscSectionSetComponentName(section, 0, 1, "MomentumX");
914     CHKERRQ(ierr);
915     ierr = PetscSectionSetComponentName(section, 0, 2, "MomentumY");
916     CHKERRQ(ierr);
917     ierr = PetscSectionSetComponentName(section, 0, 3, "MomentumZ");
918     CHKERRQ(ierr);
919     ierr = PetscSectionSetComponentName(section, 0, 4, "EnergyDensity");
920     CHKERRQ(ierr);
921   }
922   PetscFunctionReturn(0);
923 }
924 
925 int main(int argc, char **argv) {
926   PetscInt ierr;
927   MPI_Comm comm;
928   DM dm, dmcoord, dmviz;
929   Mat interpviz;
930   TS ts;
931   TSAdapt adapt;
932   User user;
933   Units units;
934   EulerContext eulercontext;
935   char ceedresource[4096] = "/cpu/self";
936   PetscInt localNelemVol, lnodes, gnodes, steps;
937   const PetscInt ncompq = 5;
938   PetscMPIInt rank;
939   PetscScalar ftime;
940   Vec Q, Qloc, Xloc;
941   Ceed ceed;
942   CeedInt numP, numQ;
943   CeedVector xcorners, qdata, q0ceed;
944   CeedBasis basisx, basisxc, basisq;
945   CeedElemRestriction restrictx, restrictq, restrictqdi;
946   CeedQFunction qf_setupVol, qf_ics, qf_rhsVol, qf_ifunctionVol;
947   CeedQFunctionContext ctxSetup, ctxNS, ctxAdvection2d, ctxSurface;
948   CeedOperator op_setupVol, op_ics;
949   CeedScalar Rd;
950   CeedMemType memtyperequested;
951   PetscScalar WpermK, Pascal, JperkgK, mpersquareds, kgpercubicm,
952               kgpersquaredms, Joulepercubicm, Joule;
953   problemType problemChoice;
954   problemData *problem = NULL;
955   WindType wind_type;
956   StabilizationType stab;
957   PetscBool implicit;
958   PetscInt    viz_refine = 0;
959   struct SimpleBC_ bc = {
960     .nslip = {2, 2, 2},
961     .slips = {{5, 6}, {3, 4}, {1, 2}}
962   };
963   double start, cpu_time_used;
964   // Test variables
965   PetscBool test;
966   PetscScalar testtol = 0.;
967   char filepath[PETSC_MAX_PATH_LEN];
968   // Check PETSc CUDA support
969   PetscBool petschavecuda, setmemtyperequest = PETSC_FALSE;
970   // *INDENT-OFF*
971   #ifdef PETSC_HAVE_CUDA
972   petschavecuda = PETSC_TRUE;
973   #else
974   petschavecuda = PETSC_FALSE;
975   #endif
976   // *INDENT-ON*
977 
978   // Create the libCEED contexts
979   PetscScalar meter          = 1e-2;     // 1 meter in scaled length units
980   PetscScalar second         = 1e-2;     // 1 second in scaled time units
981   PetscScalar kilogram       = 1e-6;     // 1 kilogram in scaled mass units
982   PetscScalar Kelvin         = 1;        // 1 Kelvin in scaled temperature units
983   CeedScalar theta0          = 300.;     // K
984   CeedScalar thetaC          = -15.;     // K
985   CeedScalar P0              = 1.e5;     // Pa
986   CeedScalar E_wind          = 1.e6;     // J
987   CeedScalar N               = 0.01;     // 1/s
988   CeedScalar cv              = 717.;     // J/(kg K)
989   CeedScalar cp              = 1004.;    // J/(kg K)
990   CeedScalar vortex_strength = 5.;       // -
991   CeedScalar rho_enter       = 1.2;      // Kg/m^3
992   CeedScalar g               = 9.81;     // m/s^2
993   CeedScalar lambda          = -2./3.;   // -
994   CeedScalar mu              = 75.;      // Pa s, dynamic viscosity
995   // mu = 75 is not physical for air, but is good for numerical stability
996   CeedScalar k               = 0.02638;  // W/(m K)
997   CeedScalar CtauS           = 0.;       // dimensionless
998   CeedScalar strong_form     = 0.;       // [0,1]
999   PetscScalar lx             = 8000.;    // m
1000   PetscScalar ly             = 8000.;    // m
1001   PetscScalar lz             = 4000.;    // m
1002   CeedScalar rc              = 1000.;    // m (Radius of bubble)
1003   PetscScalar resx           = 1000.;    // m (resolution in x)
1004   PetscScalar resy           = 1000.;    // m (resolution in y)
1005   PetscScalar resz           = 1000.;    // m (resolution in z)
1006   PetscInt outputfreq        = 10;       // -
1007   PetscInt contsteps         = 0;        // -
1008   PetscInt degree            = 1;        // -
1009   PetscInt qextra            = 2;        // -
1010   PetscInt qextraSur         = 2;        // -
1011   PetscReal center[3], dc_axis[3] = {0, 0, 0}, wind[3] = {1., 0, 0},
1012             u_enter[3] = {-1.2, 0, 0};
1013 
1014   ierr = PetscInitialize(&argc, &argv, NULL, help);
1015   if (ierr) return ierr;
1016 
1017   // Allocate PETSc context
1018   ierr = PetscCalloc1(1, &user); CHKERRQ(ierr);
1019   ierr = PetscMalloc1(1, &units); CHKERRQ(ierr);
1020   ierr = PetscMalloc1(1, &eulercontext); CHKERRQ(ierr);
1021 
1022   // Parse command line options
1023   comm = PETSC_COMM_WORLD;
1024   ierr = PetscOptionsBegin(comm, NULL, "Navier-Stokes in PETSc with libCEED",
1025                            NULL); CHKERRQ(ierr);
1026   ierr = PetscOptionsString("-ceed", "CEED resource specifier",
1027                             NULL, ceedresource, ceedresource,
1028                             sizeof(ceedresource), NULL); CHKERRQ(ierr);
1029   ierr = PetscOptionsBool("-test", "Run in test mode",
1030                           NULL, test=PETSC_FALSE, &test, NULL); CHKERRQ(ierr);
1031   ierr = PetscOptionsScalar("-compare_final_state_atol",
1032                             "Test absolute tolerance",
1033                             NULL, testtol, &testtol, NULL); CHKERRQ(ierr);
1034   ierr = PetscOptionsString("-compare_final_state_filename", "Test filename",
1035                             NULL, filepath, filepath,
1036                             sizeof(filepath), NULL); CHKERRQ(ierr);
1037   problemChoice = NS_DENSITY_CURRENT;
1038   ierr = PetscOptionsEnum("-problem", "Problem to solve", NULL,
1039                           problemTypes, (PetscEnum)problemChoice,
1040                           (PetscEnum *)&problemChoice, NULL); CHKERRQ(ierr);
1041   problem = &problemOptions[problemChoice];
1042   ierr = PetscOptionsEnum("-problem_advection_wind", "Wind type in Advection",
1043                           NULL, WindTypes,
1044                           (PetscEnum)(wind_type = ADVECTION_WIND_ROTATION),
1045                           (PetscEnum *)&wind_type, NULL); CHKERRQ(ierr);
1046   PetscInt n = problem->dim;
1047   PetscBool userWind;
1048   ierr = PetscOptionsRealArray("-problem_advection_wind_translation",
1049                                "Constant wind vector",
1050                                NULL, wind, &n, &userWind); CHKERRQ(ierr);
1051   if (wind_type == ADVECTION_WIND_ROTATION && userWind) {
1052     ierr = PetscPrintf(comm,
1053                        "Warning! Use -problem_advection_wind_translation only with -problem_advection_wind translation\n");
1054     CHKERRQ(ierr);
1055   }
1056   if (wind_type == ADVECTION_WIND_TRANSLATION
1057       && (problemChoice == NS_DENSITY_CURRENT ||
1058           problemChoice == NS_EULER_VORTEX)) {
1059     SETERRQ(comm, PETSC_ERR_ARG_INCOMP,
1060             "-problem_advection_wind translation is not defined for -problem density_current or -problem euler_vortex");
1061   }
1062   ierr = PetscOptionsRealArray("-problem_euler_vortex_velocity",
1063                                "Incoming velocity vector",
1064                                NULL, u_enter, &n, NULL); CHKERRQ(ierr);
1065   ierr = PetscOptionsEnum("-stab", "Stabilization method", NULL,
1066                           StabilizationTypes, (PetscEnum)(stab = STAB_NONE),
1067                           (PetscEnum *)&stab, NULL); CHKERRQ(ierr);
1068   ierr = PetscOptionsBool("-implicit", "Use implicit (IFunction) formulation",
1069                           NULL, implicit=PETSC_FALSE, &implicit, NULL);
1070   CHKERRQ(ierr);
1071   if (!implicit && stab != STAB_NONE) {
1072     ierr = PetscPrintf(comm, "Warning! Use -stab only with -implicit\n");
1073     CHKERRQ(ierr);
1074   }
1075   {
1076     PetscInt len;
1077     PetscBool flg;
1078     ierr = PetscOptionsIntArray("-bc_wall",
1079                                 "Use wall boundary conditions on this list of faces",
1080                                 NULL, bc.walls,
1081                                 (len = sizeof(bc.walls) / sizeof(bc.walls[0]),
1082                                  &len), &flg); CHKERRQ(ierr);
1083     if (flg) {
1084       bc.nwall = len;
1085       // Using a no-slip wall disables automatic slip walls (they must be set explicitly)
1086       bc.nslip[0] = bc.nslip[1] = bc.nslip[2] = 0;
1087     }
1088     for (PetscInt j=0; j<3; j++) {
1089       const char *flags[3] = {"-bc_slip_x", "-bc_slip_y", "-bc_slip_z"};
1090       ierr = PetscOptionsIntArray(flags[j],
1091                                   "Use slip boundary conditions on this list of faces",
1092                                   NULL, bc.slips[j],
1093                                   (len = sizeof(bc.slips[j]) / sizeof(bc.slips[j][0]),
1094                                    &len), &flg);
1095       CHKERRQ(ierr);
1096       if (flg) {
1097         bc.nslip[j] = len;
1098         bc.userbc = PETSC_TRUE;
1099       }
1100     }
1101   }
1102   ierr = PetscOptionsInt("-viz_refine",
1103                          "Regular refinement levels for visualization",
1104                          NULL, viz_refine, &viz_refine, NULL);
1105   CHKERRQ(ierr);
1106   ierr = PetscOptionsScalar("-units_meter", "1 meter in scaled length units",
1107                             NULL, meter, &meter, NULL); CHKERRQ(ierr);
1108   meter = fabs(meter);
1109   ierr = PetscOptionsScalar("-units_second","1 second in scaled time units",
1110                             NULL, second, &second, NULL); CHKERRQ(ierr);
1111   second = fabs(second);
1112   ierr = PetscOptionsScalar("-units_kilogram","1 kilogram in scaled mass units",
1113                             NULL, kilogram, &kilogram, NULL); CHKERRQ(ierr);
1114   kilogram = fabs(kilogram);
1115   ierr = PetscOptionsScalar("-units_Kelvin",
1116                             "1 Kelvin in scaled temperature units",
1117                             NULL, Kelvin, &Kelvin, NULL); CHKERRQ(ierr);
1118   Kelvin = fabs(Kelvin);
1119   ierr = PetscOptionsScalar("-theta0", "Reference potential temperature",
1120                             NULL, theta0, &theta0, NULL); CHKERRQ(ierr);
1121   ierr = PetscOptionsScalar("-thetaC", "Perturbation of potential temperature",
1122                             NULL, thetaC, &thetaC, NULL); CHKERRQ(ierr);
1123   ierr = PetscOptionsScalar("-P0", "Atmospheric pressure",
1124                             NULL, P0, &P0, NULL); CHKERRQ(ierr);
1125   ierr = PetscOptionsScalar("-E_wind", "Total energy of inflow wind",
1126                             NULL, E_wind, &E_wind, NULL); CHKERRQ(ierr);
1127   ierr = PetscOptionsScalar("-N", "Brunt-Vaisala frequency",
1128                             NULL, N, &N, NULL); CHKERRQ(ierr);
1129   ierr = PetscOptionsScalar("-cv", "Heat capacity at constant volume",
1130                             NULL, cv, &cv, NULL); CHKERRQ(ierr);
1131   ierr = PetscOptionsScalar("-cp", "Heat capacity at constant pressure",
1132                             NULL, cp, &cp, NULL); CHKERRQ(ierr);
1133   PetscBool userVortex;
1134   ierr = PetscOptionsScalar("-vortex_strength", "Strength of Vortex",
1135                             NULL, vortex_strength, &vortex_strength, &userVortex);
1136   CHKERRQ(ierr);
1137   if (problemChoice != NS_EULER_VORTEX && userVortex) {
1138     ierr = PetscPrintf(comm,
1139                        "Warning! Use -vortex_strength only with -problem euler_vortex\n");
1140     CHKERRQ(ierr);
1141   }
1142   ierr = PetscOptionsScalar("-problem_euler_vortex_rho", "Incoming density",
1143                             NULL, rho_enter, &rho_enter, NULL);
1144   CHKERRQ(ierr);
1145   ierr = PetscOptionsScalar("-g", "Gravitational acceleration",
1146                             NULL, g, &g, NULL); CHKERRQ(ierr);
1147   ierr = PetscOptionsScalar("-lambda",
1148                             "Stokes hypothesis second viscosity coefficient",
1149                             NULL, lambda, &lambda, NULL); CHKERRQ(ierr);
1150   ierr = PetscOptionsScalar("-mu", "Shear dynamic viscosity coefficient",
1151                             NULL, mu, &mu, NULL); CHKERRQ(ierr);
1152   ierr = PetscOptionsScalar("-k", "Thermal conductivity",
1153                             NULL, k, &k, NULL); CHKERRQ(ierr);
1154   ierr = PetscOptionsScalar("-CtauS",
1155                             "Scale coefficient for tau (nondimensional)",
1156                             NULL, CtauS, &CtauS, NULL); CHKERRQ(ierr);
1157   if (stab == STAB_NONE && CtauS != 0) {
1158     ierr = PetscPrintf(comm,
1159                        "Warning! Use -CtauS only with -stab su or -stab supg\n");
1160     CHKERRQ(ierr);
1161   }
1162   ierr = PetscOptionsScalar("-strong_form",
1163                             "Strong (1) or weak/integrated by parts (0) advection residual",
1164                             NULL, strong_form, &strong_form, NULL);
1165   CHKERRQ(ierr);
1166   if (problemChoice == NS_DENSITY_CURRENT && (CtauS != 0 || strong_form != 0)) {
1167     ierr = PetscPrintf(comm,
1168                        "Warning! Problem density_current does not support -CtauS or -strong_form\n");
1169     CHKERRQ(ierr);
1170   }
1171   ierr = PetscOptionsScalar("-lx", "Length scale in x direction",
1172                             NULL, lx, &lx, NULL); CHKERRQ(ierr);
1173   ierr = PetscOptionsScalar("-ly", "Length scale in y direction",
1174                             NULL, ly, &ly, NULL); CHKERRQ(ierr);
1175   ierr = PetscOptionsScalar("-lz", "Length scale in z direction",
1176                             NULL, lz, &lz, NULL); CHKERRQ(ierr);
1177   ierr = PetscOptionsScalar("-rc", "Characteristic radius of thermal bubble",
1178                             NULL, rc, &rc, NULL); CHKERRQ(ierr);
1179   ierr = PetscOptionsScalar("-resx","Target resolution in x",
1180                             NULL, resx, &resx, NULL); CHKERRQ(ierr);
1181   ierr = PetscOptionsScalar("-resy","Target resolution in y",
1182                             NULL, resy, &resy, NULL); CHKERRQ(ierr);
1183   ierr = PetscOptionsScalar("-resz","Target resolution in z",
1184                             NULL, resz, &resz, NULL); CHKERRQ(ierr);
1185   n = problem->dim;
1186   center[0] = 0.5 * lx;
1187   center[1] = 0.5 * ly;
1188   center[2] = 0.5 * lz;
1189   ierr = PetscOptionsRealArray("-center", "Location of bubble center",
1190                                NULL, center, &n, NULL); CHKERRQ(ierr);
1191   n = problem->dim;
1192   ierr = PetscOptionsRealArray("-dc_axis",
1193                                "Axis of density current cylindrical anomaly, or {0,0,0} for spherically symmetric",
1194                                NULL, dc_axis, &n, NULL); CHKERRQ(ierr);
1195   {
1196     PetscReal norm = PetscSqrtReal(PetscSqr(dc_axis[0]) +
1197                                    PetscSqr(dc_axis[1]) + PetscSqr(dc_axis[2]));
1198     if (norm > 0) {
1199       for (int i=0; i<3; i++) dc_axis[i] /= norm;
1200     }
1201   }
1202   ierr = PetscOptionsInt("-output_freq",
1203                          "Frequency of output, in number of steps",
1204                          NULL, outputfreq, &outputfreq, NULL); CHKERRQ(ierr);
1205   ierr = PetscOptionsInt("-continue", "Continue from previous solution",
1206                          NULL, contsteps, &contsteps, NULL); CHKERRQ(ierr);
1207   ierr = PetscOptionsInt("-degree", "Polynomial degree of finite elements",
1208                          NULL, degree, &degree, NULL); CHKERRQ(ierr);
1209   ierr = PetscOptionsInt("-qextra", "Number of extra quadrature points",
1210                          NULL, qextra, &qextra, NULL); CHKERRQ(ierr);
1211   PetscBool userQextraSur;
1212   ierr = PetscOptionsInt("-qextra_boundary",
1213                          "Number of extra quadrature points on in/outflow faces",
1214                          NULL, qextraSur, &qextraSur, &userQextraSur);
1215   CHKERRQ(ierr);
1216   if ((wind_type == ADVECTION_WIND_ROTATION
1217        || problemChoice == NS_DENSITY_CURRENT) && userQextraSur) {
1218     ierr = PetscPrintf(comm,
1219                        "Warning! Use -qextra_boundary only with -problem_advection_wind translation\n");
1220     CHKERRQ(ierr);
1221   }
1222   ierr = PetscStrncpy(user->outputdir, ".", 2); CHKERRQ(ierr);
1223   ierr = PetscOptionsString("-output_dir", "Output directory",
1224                             NULL, user->outputdir, user->outputdir,
1225                             sizeof(user->outputdir), NULL); CHKERRQ(ierr);
1226   memtyperequested = petschavecuda ? CEED_MEM_DEVICE : CEED_MEM_HOST;
1227   ierr = PetscOptionsEnum("-memtype",
1228                           "CEED MemType requested", NULL,
1229                           memTypes, (PetscEnum)memtyperequested,
1230                           (PetscEnum *)&memtyperequested, &setmemtyperequest);
1231   CHKERRQ(ierr);
1232   ierr = PetscOptionsEnd(); CHKERRQ(ierr);
1233 
1234   // Define derived units
1235   Pascal = kilogram / (meter * PetscSqr(second));
1236   JperkgK =  PetscSqr(meter) / (PetscSqr(second) * Kelvin);
1237   mpersquareds = meter / PetscSqr(second);
1238   WpermK = kilogram * meter / (pow(second,3) * Kelvin);
1239   kgpercubicm = kilogram / pow(meter,3);
1240   kgpersquaredms = kilogram / (PetscSqr(meter) * second);
1241   Joulepercubicm = kilogram / (meter * PetscSqr(second));
1242   Joule = kilogram * PetscSqr(meter) / PetscSqr(second);
1243 
1244   // Scale variables to desired units
1245   theta0 *= Kelvin;
1246   thetaC *= Kelvin;
1247   P0 *= Pascal;
1248   E_wind *= Joule;
1249   rho_enter *= kgpercubicm;
1250   N *= (1./second);
1251   cv *= JperkgK;
1252   cp *= JperkgK;
1253   Rd = cp - cv;
1254   g *= mpersquareds;
1255   mu *= Pascal * second;
1256   k *= WpermK;
1257   lx = fabs(lx) * meter;
1258   ly = fabs(ly) * meter;
1259   lz = fabs(lz) * meter;
1260   rc = fabs(rc) * meter;
1261   resx = fabs(resx) * meter;
1262   resy = fabs(resy) * meter;
1263   resz = fabs(resz) * meter;
1264   for (int i=0; i<3; i++) center[i] *= meter;
1265 
1266   const CeedInt dim = problem->dim, ncompx = problem->dim,
1267                 qdatasizeVol = problem->qdatasizeVol;
1268   // Set up the libCEED context
1269   struct SetupContext_ ctxSetupData = {
1270     .theta0 = theta0,
1271     .thetaC = thetaC,
1272     .P0 = P0,
1273     .N = N,
1274     .cv = cv,
1275     .cp = cp,
1276     .Rd = Rd,
1277     .g = g,
1278     .rc = rc,
1279     .lx = lx,
1280     .ly = ly,
1281     .lz = lz,
1282     .center[0] = center[0],
1283     .center[1] = center[1],
1284     .center[2] = center[2],
1285     .dc_axis[0] = dc_axis[0],
1286     .dc_axis[1] = dc_axis[1],
1287     .dc_axis[2] = dc_axis[2],
1288     .wind[0] = wind[0],
1289     .wind[1] = wind[1],
1290     .wind[2] = wind[2],
1291     .time = 0,
1292     .vortex_strength = vortex_strength,
1293     .wind_type = wind_type,
1294   };
1295 
1296   // Create the mesh
1297   {
1298     const PetscReal scale[3] = {lx, ly, lz};
1299     ierr = DMPlexCreateBoxMesh(comm, dim, PETSC_FALSE, NULL, NULL, scale,
1300                                NULL, PETSC_TRUE, &dm);
1301     CHKERRQ(ierr);
1302   }
1303 
1304   // Distribute the mesh over processes
1305   {
1306     DM               dmDist = NULL;
1307     PetscPartitioner part;
1308 
1309     ierr = DMPlexGetPartitioner(dm, &part); CHKERRQ(ierr);
1310     ierr = PetscPartitionerSetFromOptions(part); CHKERRQ(ierr);
1311     ierr = DMPlexDistribute(dm, 0, NULL, &dmDist); CHKERRQ(ierr);
1312     if (dmDist) {
1313       ierr = DMDestroy(&dm); CHKERRQ(ierr);
1314       dm  = dmDist;
1315     }
1316   }
1317   ierr = DMViewFromOptions(dm, NULL, "-dm_view"); CHKERRQ(ierr);
1318 
1319   // Setup DM
1320   ierr = DMLocalizeCoordinates(dm); CHKERRQ(ierr);
1321   ierr = DMSetFromOptions(dm); CHKERRQ(ierr);
1322   ierr = SetUpDM(dm, problem, degree, &bc, &ctxSetupData); CHKERRQ(ierr);
1323 
1324   // Refine DM for high-order viz
1325   dmviz = NULL;
1326   interpviz = NULL;
1327   if (viz_refine) {
1328     DM dmhierarchy[viz_refine+1];
1329 
1330     ierr = DMPlexSetRefinementUniform(dm, PETSC_TRUE); CHKERRQ(ierr);
1331     dmhierarchy[0] = dm;
1332     for (PetscInt i = 0, d = degree; i < viz_refine; i++) {
1333       Mat interp_next;
1334 
1335       ierr = DMRefine(dmhierarchy[i], MPI_COMM_NULL, &dmhierarchy[i+1]);
1336       CHKERRQ(ierr);
1337       ierr = DMClearDS(dmhierarchy[i+1]); CHKERRQ(ierr);
1338       ierr = DMClearFields(dmhierarchy[i+1]); CHKERRQ(ierr);
1339       ierr = DMSetCoarseDM(dmhierarchy[i+1], dmhierarchy[i]); CHKERRQ(ierr);
1340       d = (d + 1) / 2;
1341       if (i + 1 == viz_refine) d = 1;
1342       ierr = SetUpDM(dmhierarchy[i+1], problem, d, &bc, &ctxSetupData);
1343       CHKERRQ(ierr);
1344       ierr = DMCreateInterpolation(dmhierarchy[i], dmhierarchy[i+1],
1345                                    &interp_next, NULL); CHKERRQ(ierr);
1346       if (!i) interpviz = interp_next;
1347       else {
1348         Mat C;
1349         ierr = MatMatMult(interp_next, interpviz, MAT_INITIAL_MATRIX,
1350                           PETSC_DECIDE, &C); CHKERRQ(ierr);
1351         ierr = MatDestroy(&interp_next); CHKERRQ(ierr);
1352         ierr = MatDestroy(&interpviz); CHKERRQ(ierr);
1353         interpviz = C;
1354       }
1355     }
1356     for (PetscInt i=1; i<viz_refine; i++) {
1357       ierr = DMDestroy(&dmhierarchy[i]); CHKERRQ(ierr);
1358     }
1359     dmviz = dmhierarchy[viz_refine];
1360   }
1361   ierr = DMCreateGlobalVector(dm, &Q); CHKERRQ(ierr);
1362   ierr = DMGetLocalVector(dm, &Qloc); CHKERRQ(ierr);
1363   ierr = VecGetSize(Qloc, &lnodes); CHKERRQ(ierr);
1364   lnodes /= ncompq;
1365 
1366   // Initialize CEED
1367   CeedInit(ceedresource, &ceed);
1368   // Set memtype
1369   CeedMemType memtypebackend;
1370   CeedGetPreferredMemType(ceed, &memtypebackend);
1371   // Check memtype compatibility
1372   if (!setmemtyperequest)
1373     memtyperequested = memtypebackend;
1374   else if (!petschavecuda && memtyperequested == CEED_MEM_DEVICE)
1375     SETERRQ1(PETSC_COMM_WORLD, PETSC_ERR_SUP_SYS,
1376              "PETSc was not built with CUDA. "
1377              "Requested MemType CEED_MEM_DEVICE is not supported.", NULL);
1378 
1379   // Set number of 1D nodes and quadrature points
1380   numP = degree + 1;
1381   numQ = numP + qextra;
1382 
1383   // Print summary
1384   if (!test) {
1385     CeedInt gdofs, odofs;
1386     int comm_size;
1387     char box_faces_str[PETSC_MAX_PATH_LEN] = "NONE";
1388     ierr = VecGetSize(Q, &gdofs); CHKERRQ(ierr);
1389     ierr = VecGetLocalSize(Q, &odofs); CHKERRQ(ierr);
1390     gnodes = gdofs/ncompq;
1391     ierr = MPI_Comm_size(comm, &comm_size); CHKERRQ(ierr);
1392     ierr = PetscOptionsGetString(NULL, NULL, "-dm_plex_box_faces", box_faces_str,
1393                                  sizeof(box_faces_str), NULL); CHKERRQ(ierr);
1394     const char *usedresource;
1395     CeedGetResource(ceed, &usedresource);
1396 
1397     ierr = PetscPrintf(comm,
1398                        "\n-- Navier-Stokes solver - libCEED + PETSc --\n"
1399                        "  rank(s)                              : %d\n"
1400                        "  Problem:\n"
1401                        "    Problem Name                       : %s\n"
1402                        "    Stabilization                      : %s\n"
1403                        "  PETSc:\n"
1404                        "    Box Faces                          : %s\n"
1405                        "  libCEED:\n"
1406                        "    libCEED Backend                    : %s\n"
1407                        "    libCEED Backend MemType            : %s\n"
1408                        "    libCEED User Requested MemType     : %s\n"
1409                        "  Mesh:\n"
1410                        "    Number of 1D Basis Nodes (P)       : %d\n"
1411                        "    Number of 1D Quadrature Points (Q) : %d\n"
1412                        "    Global DoFs                        : %D\n"
1413                        "    Owned DoFs                         : %D\n"
1414                        "    DoFs per node                      : %D\n"
1415                        "    Global nodes                       : %D\n"
1416                        "    Owned nodes                        : %D\n",
1417                        comm_size, problemTypes[problemChoice],
1418                        StabilizationTypes[stab], box_faces_str, usedresource,
1419                        CeedMemTypes[memtypebackend],
1420                        (setmemtyperequest) ?
1421                        CeedMemTypes[memtyperequested] : "none",
1422                        numP, numQ, gdofs, odofs, ncompq, gnodes, lnodes);
1423     CHKERRQ(ierr);
1424   }
1425 
1426   // Set up global mass vector
1427   ierr = VecDuplicate(Q, &user->M); CHKERRQ(ierr);
1428 
1429   // Set up libCEED
1430   // CEED Bases
1431   CeedInit(ceedresource, &ceed);
1432   CeedBasisCreateTensorH1Lagrange(ceed, dim, ncompq, numP, numQ, CEED_GAUSS,
1433                                   &basisq);
1434   CeedBasisCreateTensorH1Lagrange(ceed, dim, ncompx, 2, numQ, CEED_GAUSS,
1435                                   &basisx);
1436   CeedBasisCreateTensorH1Lagrange(ceed, dim, ncompx, 2, numP,
1437                                   CEED_GAUSS_LOBATTO, &basisxc);
1438   ierr = DMGetCoordinateDM(dm, &dmcoord); CHKERRQ(ierr);
1439   ierr = DMPlexSetClosurePermutationTensor(dmcoord, PETSC_DETERMINE, NULL);
1440   CHKERRQ(ierr);
1441 
1442   // CEED Restrictions
1443   ierr = GetRestrictionForDomain(ceed, dm, 0, 0, 0, numP, numQ,
1444                                  qdatasizeVol, &restrictq, &restrictx,
1445                                  &restrictqdi); CHKERRQ(ierr);
1446 
1447   ierr = DMGetCoordinatesLocal(dm, &Xloc); CHKERRQ(ierr);
1448   ierr = CreateVectorFromPetscVec(ceed, Xloc, &xcorners); CHKERRQ(ierr);
1449 
1450   // Create the CEED vectors that will be needed in setup
1451   CeedInt NqptsVol;
1452   CeedBasisGetNumQuadraturePoints(basisq, &NqptsVol);
1453   CeedElemRestrictionGetNumElements(restrictq, &localNelemVol);
1454   CeedVectorCreate(ceed, qdatasizeVol*localNelemVol*NqptsVol, &qdata);
1455   CeedElemRestrictionCreateVector(restrictq, &q0ceed, NULL);
1456 
1457   // Create the Q-function that builds the quadrature data for the NS operator
1458   CeedQFunctionCreateInterior(ceed, 1, problem->setupVol, problem->setupVol_loc,
1459                               &qf_setupVol);
1460   CeedQFunctionAddInput(qf_setupVol, "dx", ncompx*dim, CEED_EVAL_GRAD);
1461   CeedQFunctionAddInput(qf_setupVol, "weight", 1, CEED_EVAL_WEIGHT);
1462   CeedQFunctionAddOutput(qf_setupVol, "qdata", qdatasizeVol, CEED_EVAL_NONE);
1463 
1464   // Create the Q-function that sets the ICs of the operator
1465   CeedQFunctionCreateInterior(ceed, 1, problem->ics, problem->ics_loc, &qf_ics);
1466   CeedQFunctionAddInput(qf_ics, "x", ncompx, CEED_EVAL_INTERP);
1467   CeedQFunctionAddOutput(qf_ics, "q0", ncompq, CEED_EVAL_NONE);
1468 
1469   qf_rhsVol = NULL;
1470   if (problem->applyVol_rhs) { // Create the Q-function that defines the action of the RHS operator
1471     CeedQFunctionCreateInterior(ceed, 1, problem->applyVol_rhs,
1472                                 problem->applyVol_rhs_loc, &qf_rhsVol);
1473     CeedQFunctionAddInput(qf_rhsVol, "q", ncompq, CEED_EVAL_INTERP);
1474     CeedQFunctionAddInput(qf_rhsVol, "dq", ncompq*dim, CEED_EVAL_GRAD);
1475     CeedQFunctionAddInput(qf_rhsVol, "qdata", qdatasizeVol, CEED_EVAL_NONE);
1476     CeedQFunctionAddInput(qf_rhsVol, "x", ncompx, CEED_EVAL_INTERP);
1477     CeedQFunctionAddOutput(qf_rhsVol, "v", ncompq, CEED_EVAL_INTERP);
1478     CeedQFunctionAddOutput(qf_rhsVol, "dv", ncompq*dim, CEED_EVAL_GRAD);
1479   }
1480 
1481   qf_ifunctionVol = NULL;
1482   if (problem->applyVol_ifunction) { // Create the Q-function that defines the action of the IFunction
1483     CeedQFunctionCreateInterior(ceed, 1, problem->applyVol_ifunction,
1484                                 problem->applyVol_ifunction_loc, &qf_ifunctionVol);
1485     CeedQFunctionAddInput(qf_ifunctionVol, "q", ncompq, CEED_EVAL_INTERP);
1486     CeedQFunctionAddInput(qf_ifunctionVol, "dq", ncompq*dim, CEED_EVAL_GRAD);
1487     CeedQFunctionAddInput(qf_ifunctionVol, "qdot", ncompq, CEED_EVAL_INTERP);
1488     CeedQFunctionAddInput(qf_ifunctionVol, "qdata", qdatasizeVol, CEED_EVAL_NONE);
1489     CeedQFunctionAddInput(qf_ifunctionVol, "x", ncompx, CEED_EVAL_INTERP);
1490     CeedQFunctionAddOutput(qf_ifunctionVol, "v", ncompq, CEED_EVAL_INTERP);
1491     CeedQFunctionAddOutput(qf_ifunctionVol, "dv", ncompq*dim, CEED_EVAL_GRAD);
1492   }
1493 
1494   // Create the operator that builds the quadrature data for the NS operator
1495   CeedOperatorCreate(ceed, qf_setupVol, NULL, NULL, &op_setupVol);
1496   CeedOperatorSetField(op_setupVol, "dx", restrictx, basisx, CEED_VECTOR_ACTIVE);
1497   CeedOperatorSetField(op_setupVol, "weight", CEED_ELEMRESTRICTION_NONE,
1498                        basisx, CEED_VECTOR_NONE);
1499   CeedOperatorSetField(op_setupVol, "qdata", restrictqdi,
1500                        CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
1501 
1502   // Create the operator that sets the ICs
1503   CeedOperatorCreate(ceed, qf_ics, NULL, NULL, &op_ics);
1504   CeedOperatorSetField(op_ics, "x", restrictx, basisxc, CEED_VECTOR_ACTIVE);
1505   CeedOperatorSetField(op_ics, "q0", restrictq,
1506                        CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
1507 
1508   CeedElemRestrictionCreateVector(restrictq, &user->qceed, NULL);
1509   CeedElemRestrictionCreateVector(restrictq, &user->qdotceed, NULL);
1510   CeedElemRestrictionCreateVector(restrictq, &user->gceed, NULL);
1511 
1512   if (qf_rhsVol) { // Create the RHS physics operator
1513     CeedOperator op;
1514     CeedOperatorCreate(ceed, qf_rhsVol, NULL, NULL, &op);
1515     CeedOperatorSetField(op, "q", restrictq, basisq, CEED_VECTOR_ACTIVE);
1516     CeedOperatorSetField(op, "dq", restrictq, basisq, CEED_VECTOR_ACTIVE);
1517     CeedOperatorSetField(op, "qdata", restrictqdi,
1518                          CEED_BASIS_COLLOCATED, qdata);
1519     CeedOperatorSetField(op, "x", restrictx, basisx, xcorners);
1520     CeedOperatorSetField(op, "v", restrictq, basisq, CEED_VECTOR_ACTIVE);
1521     CeedOperatorSetField(op, "dv", restrictq, basisq, CEED_VECTOR_ACTIVE);
1522     user->op_rhs_vol = op;
1523   }
1524 
1525   if (qf_ifunctionVol) { // Create the IFunction operator
1526     CeedOperator op;
1527     CeedOperatorCreate(ceed, qf_ifunctionVol, NULL, NULL, &op);
1528     CeedOperatorSetField(op, "q", restrictq, basisq, CEED_VECTOR_ACTIVE);
1529     CeedOperatorSetField(op, "dq", restrictq, basisq, CEED_VECTOR_ACTIVE);
1530     CeedOperatorSetField(op, "qdot", restrictq, basisq, user->qdotceed);
1531     CeedOperatorSetField(op, "qdata", restrictqdi,
1532                          CEED_BASIS_COLLOCATED, qdata);
1533     CeedOperatorSetField(op, "x", restrictx, basisx, xcorners);
1534     CeedOperatorSetField(op, "v", restrictq, basisq, CEED_VECTOR_ACTIVE);
1535     CeedOperatorSetField(op, "dv", restrictq, basisq, CEED_VECTOR_ACTIVE);
1536     user->op_ifunction_vol = op;
1537   }
1538 
1539   // Set up CEED for the boundaries
1540   CeedInt height = 1;
1541   CeedInt dimSur = dim - height;
1542   CeedInt numP_Sur = degree + 1;
1543   CeedInt numQ_Sur = numP_Sur + qextraSur;
1544   const CeedInt qdatasizeSur = problem->qdatasizeSur;
1545   CeedBasis basisxSur, basisxcSur, basisqSur;
1546   CeedInt NqptsSur;
1547   CeedQFunction qf_setupSur, qf_applySur;
1548 
1549   // CEED bases for the boundaries
1550   CeedBasisCreateTensorH1Lagrange(ceed, dimSur, ncompq, numP_Sur, numQ_Sur,
1551                                   CEED_GAUSS,
1552                                   &basisqSur);
1553   CeedBasisCreateTensorH1Lagrange(ceed, dimSur, ncompx, 2, numQ_Sur, CEED_GAUSS,
1554                                   &basisxSur);
1555   CeedBasisCreateTensorH1Lagrange(ceed, dimSur, ncompx, 2, numP_Sur,
1556                                   CEED_GAUSS_LOBATTO, &basisxcSur);
1557   CeedBasisGetNumQuadraturePoints(basisqSur, &NqptsSur);
1558 
1559   // Create the Q-function that builds the quadrature data for the Surface operator
1560   CeedQFunctionCreateInterior(ceed, 1, problem->setupSur, problem->setupSur_loc,
1561                               &qf_setupSur);
1562   CeedQFunctionAddInput(qf_setupSur, "dx", ncompx*dimSur, CEED_EVAL_GRAD);
1563   CeedQFunctionAddInput(qf_setupSur, "weight", 1, CEED_EVAL_WEIGHT);
1564   CeedQFunctionAddOutput(qf_setupSur, "qdataSur", qdatasizeSur, CEED_EVAL_NONE);
1565 
1566   // Creat Q-Function for Boundaries
1567   // -- Defined for Advection(2d) test cases
1568   qf_applySur = NULL;
1569   if (problem->applySur) {
1570     CeedQFunctionCreateInterior(ceed, 1, problem->applySur,
1571                                 problem->applySur_loc, &qf_applySur);
1572     CeedQFunctionAddInput(qf_applySur, "q", ncompq, CEED_EVAL_INTERP);
1573     CeedQFunctionAddInput(qf_applySur, "qdataSur", qdatasizeSur, CEED_EVAL_NONE);
1574     CeedQFunctionAddInput(qf_applySur, "x", ncompx, CEED_EVAL_INTERP);
1575     CeedQFunctionAddOutput(qf_applySur, "v", ncompq, CEED_EVAL_INTERP);
1576   }
1577 
1578   // Create CEED Operator for the whole domain
1579   if (!implicit)
1580     ierr = CreateOperatorForDomain(ceed, dm, &bc, problemChoice, wind_type,
1581                                    user->op_rhs_vol, qf_applySur,
1582                                    qf_setupSur, height, numP_Sur, numQ_Sur,
1583                                    qdatasizeSur, NqptsSur, basisxSur,
1584                                    basisqSur, &user->op_rhs);
1585                                    CHKERRQ(ierr);
1586   if (implicit)
1587     ierr = CreateOperatorForDomain(ceed, dm, &bc, problemChoice, wind_type,
1588                                    user->op_ifunction_vol, qf_applySur,
1589                                    qf_setupSur, height, numP_Sur, numQ_Sur,
1590                                    qdatasizeSur, NqptsSur, basisxSur,
1591                                    basisqSur, &user->op_ifunction);
1592                                    CHKERRQ(ierr);
1593   // Set up contex for QFunctions
1594   CeedQFunctionContextCreate(ceed, &ctxSetup);
1595   CeedQFunctionContextSetData(ctxSetup, CEED_MEM_HOST, CEED_USE_POINTER,
1596                               sizeof ctxSetupData, &ctxSetupData);
1597   CeedQFunctionSetContext(qf_ics, ctxSetup);
1598 
1599   CeedScalar ctxNSData[8] = {lambda, mu, k, cv, cp, g, Rd, , user->currentTime};
1600   CeedQFunctionContextCreate(ceed, &ctxNS);
1601   CeedQFunctionContextSetData(ctxNS, CEED_MEM_HOST, CEED_USE_POINTER,
1602                               sizeof ctxNSData, &ctxNSData);
1603 
1604   struct Advection2dContext_ ctxAdvection2d = {
1605     .CtauS = CtauS,
1606     .strong_form = strong_form,
1607     .stabilization = stab,
1608   };
1609   CeedQFunctionContextCreate(ceed, &ctxAdvection2d);
1610   CeedQFunctionContextSetData(ctxAdvection2d, CEED_MEM_HOST, CEED_USE_POINTER,
1611                               sizeof ctxAdvection2dData, &ctxAdvection2dData);
1612 
1613   struct SurfaceContext_ ctxSurfaceData = {
1614     .E_wind = E_wind,
1615     .strong_form = strong_form,
1616     .implicit = implicit,
1617   };
1618   struct EulerContext_ eulerctx = {
1619     .currentTime = 0.,
1620   };
1621   CeedQFunctionContextCreate(ceed, &ctxSurface);
1622   CeedQFunctionContextSetData(ctxSurface, CEED_MEM_HOST, CEED_USE_POINTER,
1623                               sizeof ctxSurfaceData, &ctxSurfaceData);
1624 
1625   switch (problemChoice) {
1626   case NS_DENSITY_CURRENT:
1627     if (qf_rhsVol) CeedQFunctionSetContext(qf_rhsVol, &ctxNS, sizeof ctxNS);
1628     if (qf_ifunctionVol) CeedQFunctionSetContext(qf_ifunctionVol, &ctxNS,
1629           sizeof ctxNS);
1630     break;
1631   case NS_ADVECTION:
1632   case NS_ADVECTION2D:
1633     if (qf_rhsVol) CeedQFunctionSetContext(qf_rhsVol, &ctxAdvection2d,
1634           sizeof ctxAdvection2d);
1635     if (qf_ifunctionVol) CeedQFunctionSetContext(qf_ifunctionVol, &ctxAdvection2d,
1636           sizeof ctxAdvection2d);
1637     if (qf_applySur) CeedQFunctionSetContext(qf_applySur, &ctxSurface,
1638           sizeof ctxSurface);
1639   case NS_EULER_VORTEX:
1640     if (qf_rhsVol) CeedQFunctionSetContext(qf_rhsVol, &eulercontext,
1641           sizeof eulercontext);
1642     if (qf_applySur) CeedQFunctionSetContext(qf_applySur, &eulercontext,
1643           sizeof eulercontext);
1644   }
1645 
1646   // Set up PETSc context
1647   // Set up units structure
1648   units->meter = meter;
1649   units->kilogram = kilogram;
1650   units->second = second;
1651   units->Kelvin = Kelvin;
1652   units->Pascal = Pascal;
1653   units->JperkgK = JperkgK;
1654   units->mpersquareds = mpersquareds;
1655   units->WpermK = WpermK;
1656   units->kgpercubicm = kgpercubicm;
1657   units->kgpersquaredms = kgpersquaredms;
1658   units->Joulepercubicm = Joulepercubicm;
1659   units->Joule = Joule;
1660 
1661   // Set up user structure
1662   user->comm = comm;
1663   user->outputfreq = outputfreq;
1664   user->contsteps = contsteps;
1665   user->units = units;
1666   user->dm = dm;
1667   user->dmviz = dmviz;
1668   user->interpviz = interpviz;
1669   user->ceed = ceed;
1670 
1671   // Calculate qdata and ICs
1672   // Set up state global and local vectors
1673   ierr = VecZeroEntries(Q); CHKERRQ(ierr);
1674 
1675   ierr = VectorPlacePetscVec(q0ceed, Qloc); CHKERRQ(ierr);
1676 
1677   // Apply Setup Ceed Operators
1678   ierr = VectorPlacePetscVec(xcorners, Xloc); CHKERRQ(ierr);
1679   CeedOperatorApply(op_setupVol, xcorners, qdata, CEED_REQUEST_IMMEDIATE);
1680   ierr = ComputeLumpedMassMatrix(ceed, dm, restrictq, basisq, restrictqdi, qdata,
1681                                  user->M); CHKERRQ(ierr);
1682 
1683   ierr = ICs_FixMultiplicity(op_ics, xcorners, q0ceed, dm, Qloc, Q, restrictq,
1684                              ctxSetup, 0.0); CHKERRQ(ierr);
1685   if (1) { // Record boundary values from initial condition and override DMPlexInsertBoundaryValues()
1686     // We use this for the main simulation DM because the reference DMPlexInsertBoundaryValues() is very slow.  If we
1687     // disable this, we should still get the same results due to the problem->bc function, but with potentially much
1688     // slower execution.
1689     Vec Qbc;
1690     ierr = DMGetNamedLocalVector(dm, "Qbc", &Qbc); CHKERRQ(ierr);
1691     ierr = VecCopy(Qloc, Qbc); CHKERRQ(ierr);
1692     ierr = VecZeroEntries(Qloc); CHKERRQ(ierr);
1693     ierr = DMGlobalToLocal(dm, Q, INSERT_VALUES, Qloc); CHKERRQ(ierr);
1694     ierr = VecAXPY(Qbc, -1., Qloc); CHKERRQ(ierr);
1695     ierr = DMRestoreNamedLocalVector(dm, "Qbc", &Qbc); CHKERRQ(ierr);
1696     ierr = PetscObjectComposeFunction((PetscObject)dm,
1697                                       "DMPlexInsertBoundaryValues_C", DMPlexInsertBoundaryValues_NS);
1698     CHKERRQ(ierr);
1699   }
1700 
1701   MPI_Comm_rank(comm, &rank);
1702   if (!rank) {ierr = PetscMkdir(user->outputdir); CHKERRQ(ierr);}
1703   // Gather initial Q values
1704   // In case of continuation of simulation, set up initial values from binary file
1705   if (contsteps) { // continue from existent solution
1706     PetscViewer viewer;
1707     char filepath[PETSC_MAX_PATH_LEN];
1708     // Read input
1709     ierr = PetscSNPrintf(filepath, sizeof filepath, "%s/ns-solution.bin",
1710                          user->outputdir);
1711     CHKERRQ(ierr);
1712     ierr = PetscViewerBinaryOpen(comm, filepath, FILE_MODE_READ, &viewer);
1713     CHKERRQ(ierr);
1714     ierr = VecLoad(Q, viewer); CHKERRQ(ierr);
1715     ierr = PetscViewerDestroy(&viewer); CHKERRQ(ierr);
1716   }
1717   ierr = DMRestoreLocalVector(dm, &Qloc); CHKERRQ(ierr);
1718 
1719 // Create and setup TS
1720   ierr = TSCreate(comm, &ts); CHKERRQ(ierr);
1721   ierr = TSSetDM(ts, dm); CHKERRQ(ierr);
1722   if (implicit) {
1723     ierr = TSSetType(ts, TSBDF); CHKERRQ(ierr);
1724     if (user->op_ifunction) {
1725       ierr = TSSetIFunction(ts, NULL, IFunction_NS, &user); CHKERRQ(ierr);
1726     } else {                    // Implicit integrators can fall back to using an RHSFunction
1727       ierr = TSSetRHSFunction(ts, NULL, RHS_NS, &user); CHKERRQ(ierr);
1728     }
1729   } else {
1730     if (!user->op_rhs) SETERRQ(comm, PETSC_ERR_ARG_NULL,
1731                                  "Problem does not provide RHSFunction");
1732     ierr = TSSetType(ts, TSRK); CHKERRQ(ierr);
1733     ierr = TSRKSetType(ts, TSRK5F); CHKERRQ(ierr);
1734     ierr = TSSetRHSFunction(ts, NULL, RHS_NS, &user); CHKERRQ(ierr);
1735   }
1736   ierr = TSSetMaxTime(ts, 500. * units->second); CHKERRQ(ierr);
1737   ierr = TSSetExactFinalTime(ts, TS_EXACTFINALTIME_STEPOVER); CHKERRQ(ierr);
1738   ierr = TSSetTimeStep(ts, 1.e-2 * units->second); CHKERRQ(ierr);
1739   if (test) {ierr = TSSetMaxSteps(ts, 10); CHKERRQ(ierr);}
1740   ierr = TSGetAdapt(ts, &adapt); CHKERRQ(ierr);
1741   ierr = TSAdaptSetStepLimits(adapt,
1742                               1.e-12 * units->second,
1743                               1.e2 * units->second); CHKERRQ(ierr);
1744   ierr = TSSetFromOptions(ts); CHKERRQ(ierr);
1745   if (!contsteps) { // print initial condition
1746     if (!test) {
1747       ierr = TSMonitor_NS(ts, 0, 0., Q, user); CHKERRQ(ierr);
1748     }
1749   } else { // continue from time of last output
1750     PetscReal time;
1751     PetscInt count;
1752     PetscViewer viewer;
1753     char filepath[PETSC_MAX_PATH_LEN];
1754     ierr = PetscSNPrintf(filepath, sizeof filepath, "%s/ns-time.bin",
1755                          user->outputdir); CHKERRQ(ierr);
1756     ierr = PetscViewerBinaryOpen(comm, filepath, FILE_MODE_READ, &viewer);
1757     CHKERRQ(ierr);
1758     ierr = PetscViewerBinaryRead(viewer, &time, 1, &count, PETSC_REAL);
1759     CHKERRQ(ierr);
1760     ierr = PetscViewerDestroy(&viewer); CHKERRQ(ierr);
1761     ierr = TSSetTime(ts, time * user->units->second); CHKERRQ(ierr);
1762   }
1763   if (!test) {
1764     ierr = TSMonitorSet(ts, TSMonitor_NS, user, NULL); CHKERRQ(ierr);
1765   }
1766 
1767   // Solve
1768   start = MPI_Wtime();
1769   ierr = PetscBarrier((PetscObject)ts); CHKERRQ(ierr);
1770   ierr = TSSolve(ts, Q); CHKERRQ(ierr);
1771   cpu_time_used = MPI_Wtime() - start;
1772   ierr = TSGetSolveTime(ts, &ftime); CHKERRQ(ierr);
1773   ierr = MPI_Allreduce(MPI_IN_PLACE, &cpu_time_used, 1, MPI_DOUBLE, MPI_MIN,
1774                        comm); CHKERRQ(ierr);
1775   if (!test) {
1776     ierr = PetscPrintf(PETSC_COMM_WORLD,
1777                        "Time taken for solution (sec): %g\n",
1778                        (double)cpu_time_used); CHKERRQ(ierr);
1779   }
1780 
1781   // Get error
1782   if (problem->non_zero_time && !test) {
1783     Vec Qexact, Qexactloc;
1784     PetscReal norm;
1785     ierr = DMCreateGlobalVector(dm, &Qexact); CHKERRQ(ierr);
1786     ierr = DMGetLocalVector(dm, &Qexactloc); CHKERRQ(ierr);
1787     ierr = VecGetSize(Qexactloc, &lnodes); CHKERRQ(ierr);
1788 
1789     ierr = ICs_FixMultiplicity(op_ics, xcorners, q0ceed, dm, Qexactloc, Qexact,
1790                                restrictq, ctxSetup, ftime); CHKERRQ(ierr);
1791 
1792     ierr = VecAXPY(Q, -1.0, Qexact);  CHKERRQ(ierr);
1793     ierr = VecNorm(Q, NORM_MAX, &norm); CHKERRQ(ierr);
1794     CeedVectorDestroy(&q0ceed);
1795     ierr = PetscPrintf(PETSC_COMM_WORLD,
1796                        "Max Error: %g\n",
1797                        (double)norm); CHKERRQ(ierr);
1798     // Clean up vectors
1799     ierr = DMRestoreLocalVector(dm, &Qexactloc); CHKERRQ(ierr);
1800     ierr = VecDestroy(&Qexact); CHKERRQ(ierr);
1801   }
1802 
1803   // Output Statistics
1804   ierr = TSGetStepNumber(ts, &steps); CHKERRQ(ierr);
1805   if (!test) {
1806     ierr = PetscPrintf(PETSC_COMM_WORLD,
1807                        "Time integrator took %D time steps to reach final time %g\n",
1808                        steps, (double)ftime); CHKERRQ(ierr);
1809   }
1810   // Output numerical values from command line
1811   ierr = VecViewFromOptions(Q, NULL, "-vec_view"); CHKERRQ(ierr);
1812 
1813   // Compare reference solution values with current test run for CI
1814   if (test) {
1815     PetscViewer viewer;
1816     // Read reference file
1817     Vec Qref;
1818     PetscReal error, Qrefnorm;
1819     ierr = VecDuplicate(Q, &Qref); CHKERRQ(ierr);
1820     ierr = PetscViewerBinaryOpen(comm, filepath, FILE_MODE_READ, &viewer);
1821     CHKERRQ(ierr);
1822     ierr = VecLoad(Qref, viewer); CHKERRQ(ierr);
1823     ierr = PetscViewerDestroy(&viewer); CHKERRQ(ierr);
1824 
1825     // Compute error with respect to reference solution
1826     ierr = VecAXPY(Q, -1.0, Qref);  CHKERRQ(ierr);
1827     ierr = VecNorm(Qref, NORM_MAX, &Qrefnorm); CHKERRQ(ierr);
1828     ierr = VecScale(Q, 1./Qrefnorm); CHKERRQ(ierr);
1829     ierr = VecNorm(Q, NORM_MAX, &error); CHKERRQ(ierr);
1830     ierr = VecDestroy(&Qref); CHKERRQ(ierr);
1831     // Check error
1832     if (error > testtol) {
1833       ierr = PetscPrintf(PETSC_COMM_WORLD,
1834                          "Test failed with error norm %g\n",
1835                          (double)error); CHKERRQ(ierr);
1836     }
1837     ierr = PetscViewerDestroy(&viewer); CHKERRQ(ierr);
1838   }
1839 
1840   // Clean up libCEED
1841   CeedVectorDestroy(&qdata);
1842   CeedVectorDestroy(&user->qceed);
1843   CeedVectorDestroy(&user->qdotceed);
1844   CeedVectorDestroy(&user->gceed);
1845   CeedVectorDestroy(&xcorners);
1846   CeedBasisDestroy(&basisq);
1847   CeedBasisDestroy(&basisx);
1848   CeedBasisDestroy(&basisxc);
1849   CeedElemRestrictionDestroy(&restrictq);
1850   CeedElemRestrictionDestroy(&restrictx);
1851   CeedElemRestrictionDestroy(&restrictqdi);
1852   CeedQFunctionDestroy(&qf_setupVol);
1853   CeedQFunctionDestroy(&qf_ics);
1854   CeedQFunctionDestroy(&qf_rhsVol);
1855   CeedQFunctionDestroy(&qf_ifunctionVol);
1856   CeedQFunctionContextDestroy(&ctxSetup);
1857   CeedQFunctionContextDestroy(&ctxNS);
1858   CeedQFunctionContextDestroy(&ctxAdvection2d);
1859   CeedQFunctionContextDestroy(&ctxSurface);
1860   CeedOperatorDestroy(&op_setupVol);
1861   CeedOperatorDestroy(&op_ics);
1862   CeedOperatorDestroy(&user->op_rhs_vol);
1863   CeedOperatorDestroy(&user->op_ifunction_vol);
1864   CeedDestroy(&ceed);
1865   CeedBasisDestroy(&basisqSur);
1866   CeedBasisDestroy(&basisxSur);
1867   CeedBasisDestroy(&basisxcSur);
1868   CeedQFunctionDestroy(&qf_setupSur);
1869   CeedQFunctionDestroy(&qf_applySur);
1870   CeedOperatorDestroy(&user->op_rhs);
1871   CeedOperatorDestroy(&user->op_ifunction);
1872 
1873   // Clean up PETSc
1874   ierr = VecDestroy(&Q); CHKERRQ(ierr);
1875   ierr = VecDestroy(&user->M); CHKERRQ(ierr);
1876   ierr = MatDestroy(&interpviz); CHKERRQ(ierr);
1877   ierr = DMDestroy(&dmviz); CHKERRQ(ierr);
1878   ierr = TSDestroy(&ts); CHKERRQ(ierr);
1879   ierr = DMDestroy(&dm); CHKERRQ(ierr);
1880   ierr = PetscFree(units); CHKERRQ(ierr);
1881   ierr = PetscFree(user); CHKERRQ(ierr);
1882   ierr = PetscFree(eulercontext); CHKERRQ(ierr);
1883   return PetscFinalize();
1884 }
1885