1 // Copyright (c) 2017-2021, Lawrence Livermore National Security, LLC and
2 // other Axom Project Developers. See the top-level LICENSE file for details.
3 //
4 // SPDX-License-Identifier: (BSD-3-Clause)
5 
6 /**
7  * \file
8  *
9  * \brief 1D shock tube, split flux Euler equations
10  *
11  * \author J. Keasler (original)
12  * \author K. Weiss (modified to use axom's Slam component)
13  *
14  * \details  Developing example to use and demo features of Slam on shock tube
15  *  example over structured 1D mesh.
16  *
17  *  Tests:
18  *      * Sets and subsets.
19  *      * Relations over regular grid (should be implicit; currently
20  *        implemented as explicit static constant relations)
21  *      * Fields/maps over the data -- and access to a local datastore
22  *        (using Slam::FieldRegistry, can easily convert to Sidre).
23  *
24  * \verbatim
25  *         | m  |            |    mv    |
26  *     Q = | mv |        F = | mv^2 + P |
27  *         | E  |            |  v(E+P)  |
28  *
29  *     P = (gamma - 1.0)[E - 0.5 mv^2 ]
30  *
31  *             Cp
32  *     gamma = --     m = mass/volume   v = velocity
33  *             Cv
34  *
35  *     All quantities are non-dimensionalized.
36  *
37  *     @Q   @F    @Q   @F @Q
38  *     -- + -- =  -- + -- -- = 0
39  *     @t   @x    @t   @Q @x
40  * \endverbatim
41  */
42 
43 #include "axom/config.hpp"
44 #include "axom/slic.hpp"
45 #include "axom/slam.hpp"
46 
47 #include <cmath>
48 #include <string>
49 #include <iomanip>
50 #include <sstream>
51 
52 namespace slamShocktube
53 {
54 namespace slam = axom::slam;
55 
56 using PositionType = slam::DefaultPositionType;
57 using ElementType = slam::DefaultElementType;
58 
59 using BaseSet = slam::Set<PositionType, ElementType>;
60 
61 PositionType const UPWIND = 0;
62 PositionType const DOWNWIND = 1;
63 
64 const double gammaa = M_SQRT2;
65 const double gammaaInverse = M_SQRT1_2;
66 
67 const int INIT_NUM_ELEMS = 100;
68 const int INIT_NUM_OUTPUT_DUMPS = 5;
69 const int INIT_NUM_CYCLES_PER_DUMP = 200;
70 
71 const double INIT_P_RATIO = 0.5;
72 const double INIT_D_RATIO = 0.5;
73 
74 #ifdef AXOM_DEBUG
75 const bool verboseOutput = false;
76 #endif
77 
78 /**
79  * \brief Simple representation of the mesh for this 1D example
80  *
81  * \details Mesh contains a set of elements and a set of faces between elements.
82  *  It also contains three subsets: a single inflow; a single outflow
83  *  element; all internal 'tube' elements. (added 5/2015)
84  *  The mesh contains the relations from faces to elements and from tube
85  *  elements to faces.
86  *
87  * \note (5/2015) We are currently missing an implicit constant grid relation.
88  *
89  * \note For current implementation with explicit static (constant) relations.
90  * \note We are missing a nice way to set the relation elements.
91  *  It should not have to be done explicitly in each user code --especially
92     in common use cases
93  *
94  * Idea: We could have a relationInverter function that takes a relation
95  *       from sets A to B and generates a relation from set B to set A with all
96          the arrows reversed.
97  */
98 class ShockTubeMesh
99 {
100 public:
101   /// types for Element and Face sets
102   using PositionType = slamShocktube::PositionType;
103 
104   using ElemSet = slam::PositionSet<PositionType, ElementType>;
105   using FaceSet = slam::PositionSet<PositionType, ElementType>;
106 
107   using IndexType = PositionType;
108 
109   /// types for Tube and {In,Out}Flow subsets
110   using StrideOnePolicy = slam::policies::StrideOne<PositionType>;
111   using NoIndirectionPolicy =
112     slam::policies::NoIndirection<PositionType, ElementType>;
113   using TubeSubsetPolicy = slam::policies::ConcreteParentSubset<ElemSet>;
114   using ElemSubset = slam::GenericRangeSet<PositionType,
115                                            ElementType,
116                                            StrideOnePolicy,
117                                            NoIndirectionPolicy,
118                                            TubeSubsetPolicy>;
119   using RangeSet = slam::RangeSet<PositionType, ElementType>;
120 
121   /// types for relations
122   enum
123   {
124     ELEMS_PER_FACE = 2,
125     FACES_PER_ELEM = 2
126   };
127 
128   using EFStride =
129     slam::policies::CompileTimeStride<PositionType, FACES_PER_ELEM>;
130   using FEStride =
131     slam::policies::CompileTimeStride<PositionType, ELEMS_PER_FACE>;
132 
133   using EFCard = slam::policies::ConstantCardinality<PositionType, EFStride>;
134   using FECard = slam::policies::ConstantCardinality<PositionType, FEStride>;
135   using STLIndirection =
136     slam::policies::STLVectorIndirection<PositionType, ElementType>;
137   using IndexVec = STLIndirection::VectorType;
138 
139   using TubeElemToFaceRelation =
140     slam::StaticRelation<PositionType, ElementType, EFCard, STLIndirection, ElemSubset, FaceSet>;
141   using FaceToElemRelation =
142     slam::StaticRelation<PositionType, ElementType, FECard, STLIndirection, FaceSet, ElemSet>;
143 
144 public:
145   ElemSet elems;            // The entire set of elements
146   ElemSubset tubeElems;     // Subset of internal elements
147   ElemSubset inFlowElems;   // Subset of inflow elements
148   ElemSubset outFlowElems;  // Subset of outflow elements
149   FaceSet faces;            // Faces between adjacent pairs of elements
150 
151   FaceToElemRelation relationFaceElem;      // co-boundary relation of faces
152                                             // to their elements
153   TubeElemToFaceRelation relationTubeFace;  // boundary relation of
154                                             // internal 'tube' elements
155 };
156 
157 // Define explicit instances of local (key/value) datastore for int and double
158 using IntsRegistry = slam::FieldRegistry<BaseSet, BaseSet::ElementType>;
159 using RealsRegistry = slam::FieldRegistry<BaseSet, double>;
160 using IntField = IntsRegistry::MapType;
161 using RealField = RealsRegistry::MapType;
162 
163 IntsRegistry intsRegistry;
164 RealsRegistry realsRegistry;
165 
166 /**************************************************************************
167  * Subroutine:  GetUserInput
168  * Purpose   :  Ask for control and output information
169  *************************************************************************/
170 
GetUserInput()171 void GetUserInput()
172 {
173   /**********************************/
174   /* Get mesh info, and create mesh */
175   /**********************************/
176   {
177     int numElems;
178     int numFaces;
179 
180     SLIC_INFO("How many zones for the 1D shock tube? ");
181     numElems = INIT_NUM_ELEMS;
182     SLIC_INFO("\t\t" << numElems);
183 
184     // add an inflow and outflow zone
185     numElems += 2;
186     numFaces = numElems - 1;
187 
188     intsRegistry.addScalar("numElems", numElems);
189     intsRegistry.addScalar("numFaces", numFaces);
190   }
191 
192   /********************/
193   /* Get physics info */
194   /********************/
195   {
196     double pratio = -1.0;
197     double dratio = -1.0;
198 
199     while(pratio < 0.0 || pratio > 1.0)
200     {
201       SLIC_INFO("What pressure ratio would you like (0 <= x <= 1)? ");
202       pratio = INIT_P_RATIO;
203       SLIC_INFO("\t\t" << pratio);
204     }
205 
206     while(dratio < 0.0 || dratio > 1.0)
207     {
208       SLIC_INFO("What density ratio would you like (0 <= x <= 1)? ");
209       dratio = INIT_D_RATIO;
210       SLIC_INFO("\t\t" << dratio);
211     }
212 
213     realsRegistry.addScalar("pressureRatio", pratio);
214     realsRegistry.addScalar("densityRatio", dratio);
215   }
216 
217   /********************/
218   /* Get output  info */
219   /********************/
220   {
221     int numOutputDumps;
222     int numCyclesPerDump;
223 
224     SLIC_INFO("How many dumps would you like? ");
225     numOutputDumps = INIT_NUM_OUTPUT_DUMPS;
226     SLIC_INFO("\t\t" << numOutputDumps);
227 
228     SLIC_INFO("How many cycles between dumps would you like? ");
229     numCyclesPerDump = INIT_NUM_CYCLES_PER_DUMP;
230     SLIC_INFO("\t\t" << numCyclesPerDump);
231 
232     int numTotalCycles = numOutputDumps * numCyclesPerDump;
233     SLIC_INFO("Simulation will run for " << numTotalCycles << " cycles.\n");
234 
235     intsRegistry.addScalar("numOutputDumps", numOutputDumps);
236     intsRegistry.addScalar("numCyclesPerDump", numCyclesPerDump);
237     intsRegistry.addScalar("numTotalCycles", numTotalCycles);
238   }
239 
240   return;
241 }
242 
243 /**
244  * \brief Build an empty mesh for the shock tube
245  * \details Shocktube mesh layout
246  * \verbatim
247  *      Gaps between elements are faces
248  *                     |
249  *      -------------------------------
250  *      |   |   |             |   |   |
251  *
252  *   ### ### ### ###       ### ### ### ###
253  *   ### ### ### ###  ...  ### ### ### ###  <--- 1D Shock tube model
254  *   ### ### ### ###       ### ### ### ###
255  *
256  *    |  |                           |  |
257  *    |  -----------------------------  |
258  *   Inflow           |               Outflow
259  *   Element      Tube Elements       Element
260  *
261  * \endverbatim
262  */
CreateShockTubeMesh(ShockTubeMesh * mesh)263 void CreateShockTubeMesh(ShockTubeMesh* mesh)
264 {
265   // ------------ Generate the Sets and Subsets
266 
267   // create element and face sets
268   mesh->elems = ShockTubeMesh::ElemSet(intsRegistry.getScalar("numElems"));
269   mesh->faces = ShockTubeMesh::FaceSet(intsRegistry.getScalar("numFaces"));
270 
271   // construct the element subsets
272   ShockTubeMesh::PositionType numElems = mesh->elems.size();
273   using ElemSubsetBuilder = ShockTubeMesh::ElemSubset::SetBuilder;
274   mesh->inFlowElems = ElemSubsetBuilder()  //
275                         .range(0, 1)       //
276                         .parent(&mesh->elems);
277   mesh->tubeElems = ElemSubsetBuilder()        //
278                       .range(1, numElems - 1)  //
279                       .parent(&mesh->elems);
280   mesh->outFlowElems = ElemSubsetBuilder()               //
281                          .range(numElems - 1, numElems)  //
282                          .parent(&mesh->elems);
283 
284   // ------------ Generate the Relations
285 
286   // TODO: Need to define DynamicConstantRelation
287   //       which will allow modifying the relation elements
288   // TODO: Need to define ImplicitConstantRelation
289   //       since the relations are actually implicit
290   //       no storage should be necessary for regular grid neighbors
291   //       For now, we use explicitly storage for the relation data.
292 
293   using IndexVec = ShockTubeMesh::IndexVec;
294 
295   /// Setup the FaceToElem relation
296   IndexVec& feRelVec =
297     intsRegistry.addBuffer("feRel",
298                            ShockTubeMesh::FACES_PER_ELEM * mesh->faces.size());
299   IndexVec::iterator relIt = feRelVec.begin();
300   for(ShockTubeMesh::IndexType idx = 0;
301       idx < static_cast<ShockTubeMesh::IndexType>(mesh->faces.size());
302       ++idx)
303   {
304     *relIt++ = mesh->faces[idx];
305     *relIt++ = mesh->faces[idx] + 1;
306   }
307 
308   mesh->relationFaceElem =
309     ShockTubeMesh::FaceToElemRelation(&mesh->faces, &mesh->elems);
310   mesh->relationFaceElem.bindIndices(static_cast<int>(feRelVec.size()),
311                                      &feRelVec);
312   SLIC_ASSERT(mesh->relationFaceElem.isValid(verboseOutput));
313 
314   /// Setup the TubeElementToFace relation:
315   /// A relation from the tubes subset of the elements to their incident faces
316   ShockTubeMesh::PositionType numTubeElems = mesh->tubeElems.size();
317   IndexVec& efRelVec =
318     intsRegistry.addBuffer("efRel", ShockTubeMesh::ELEMS_PER_FACE * numTubeElems);
319   relIt = efRelVec.begin();
320   for(ShockTubeMesh::IndexType idx = 0;
321       idx < static_cast<ShockTubeMesh::IndexType>(numTubeElems);
322       ++idx)
323   {
324     *relIt++ = mesh->tubeElems[idx] - 1;
325     *relIt++ = mesh->tubeElems[idx];
326   }
327 
328   mesh->relationTubeFace =
329     ShockTubeMesh::TubeElemToFaceRelation(&mesh->tubeElems, &mesh->faces);
330   mesh->relationTubeFace.bindIndices(static_cast<int>(efRelVec.size()),
331                                      &efRelVec);
332   SLIC_ASSERT(mesh->relationTubeFace.isValid(verboseOutput));
333 }
334 
335 /**************************************************************************
336  * Subroutine:  InitializeShockTube
337  * Purpose   :  Populate the mesh with values
338  *************************************************************************/
InitializeShockTube(ShockTubeMesh const & mesh)339 void InitializeShockTube(ShockTubeMesh const& mesh)
340 {
341   using IndexType = ShockTubeMesh::IndexType;
342   using RangeSet = ShockTubeMesh::RangeSet;
343 
344   // Create element centered fields
345   RealField& mass = realsRegistry.addField("mass", &mesh.elems);
346   RealField& momentum = realsRegistry.addField("momentum", &mesh.elems);
347   RealField& energy = realsRegistry.addField("energy", &mesh.elems);
348   RealField& pressure = realsRegistry.addField("pressure", &mesh.elems);
349 
350   // Create face centered fields
351   realsRegistry.addField("F0", &mesh.faces);  // mv
352   realsRegistry.addField("F1", &mesh.faces);  // mv^2+P
353   realsRegistry.addField("F2", &mesh.faces);  // v(E+P)
354 
355   // Fill left half with high pressure, right half with low pressure
356   IndexType endTube = mesh.elems.size();
357   IndexType midTube = endTube / 2;
358 
359   // Non-dimensionalized reference values
360   double massInitial = 1.0;
361   double momentumInitial = 0.0;
362   double pressureInitial = gammaaInverse;
363   double energyInitial = pressureInitial / (gammaa - 1.0);
364 
365   // Initialize zonal quantities
366   RangeSet lowerTube(0, midTube);
367   for(IndexType i = 0; i < lowerTube.size(); ++i)
368   {
369     IndexType ind = lowerTube[i];
370     mass[ind] = massInitial;
371     momentum[ind] = momentumInitial;
372     pressure[ind] = pressureInitial;
373     energy[ind] = energyInitial;
374   }
375 
376   // adjust parameters for low pressure portion of tube
377   massInitial *= realsRegistry.getScalar("densityRatio");
378   pressureInitial *= realsRegistry.getScalar("pressureRatio");
379   energyInitial = pressureInitial / (gammaa - 1.0);
380 
381   RangeSet upperTube(midTube, mesh.elems.size());
382   for(IndexType i = 0; i < upperTube.size(); ++i)
383   {
384     IndexType ind = upperTube[i];
385     mass[ind] = massInitial;
386     momentum[ind] = momentumInitial;
387     pressure[ind] = pressureInitial;
388     energy[ind] = energyInitial;
389   }
390 
391   // Create needed time info
392   realsRegistry.addScalar("time", 0.0);
393   intsRegistry.addScalar("cycle", 0);
394 
395   double dx = 1.0 / static_cast<double>(endTube);
396   realsRegistry.addScalar("dx", dx);
397   realsRegistry.addScalar("dt", 0.4 * dx);
398 }
399 
400 /**
401  * \brief Compute F quantities at faces.
402  *
403  * \details
404  *
405  * \verbatim
406  *  @F   @F0   @F1   @F2
407  *  -- = --- + --- + ---
408  *  @x   @x    @x    @x
409  *  \endverbatim
410  *
411  *  Calculate F0, F1 and F2 at the face centers.
412  *
413  */
ComputeFaceInfo(ShockTubeMesh const & mesh)414 void ComputeFaceInfo(ShockTubeMesh const& mesh)
415 {
416   using IndexType = ShockTubeMesh::IndexType;
417 
418   // Face fields
419   RealField& F0 = realsRegistry.getField("F0");
420   RealField& F1 = realsRegistry.getField("F1");
421   RealField& F2 = realsRegistry.getField("F2");
422 
423   // Element fields
424   const RealField& mass = realsRegistry.getField("mass");
425   const RealField& momentum = realsRegistry.getField("momentum");
426   const RealField& energy = realsRegistry.getField("energy");
427 
428   // Update face data using element data using the face->elem relation
429   ShockTubeMesh::PositionType numFaceElems =
430     static_cast<ShockTubeMesh::PositionType>(mesh.faces.size());
431   for(ShockTubeMesh::PositionType fIdx = 0; fIdx < numFaceElems; ++fIdx)
432   {
433     // each face has an upwind and downwind element.
434     IndexType upWind = mesh.relationFaceElem[fIdx][UPWIND];      // upwind
435                                                                  // element
436     IndexType downWind = mesh.relationFaceElem[fIdx][DOWNWIND];  // downwind
437                                                                  // element
438 
439     // calculate face centered quantities as avg of element centered ones
440     double massf = 0.5 * (mass[upWind] + mass[downWind]);
441     double momentumf = 0.5 * (momentum[upWind] + momentum[downWind]);
442     double energyf = 0.5 * (energy[upWind] + energy[downWind]);
443     double pressuref =
444       (gammaa - 1.0) * (energyf - 0.5 * momentumf * momentumf / massf);
445     double c = sqrt(gammaa * pressuref / massf);
446     double v = momentumf / massf;
447 
448     double ev;
449     double cLocal;
450 
451     // Now that we have the wave speeds, we might want to
452     // look for the max wave speed here, and update dt
453     // appropriately right before leaving this function.
454 
455     // OK, calculate face quantities
456     F0[fIdx] = F1[fIdx] = F2[fIdx] = 0.0;
457 
458     IndexType contributor = ((v >= 0.0) ? upWind : downWind);
459     massf = mass[contributor];
460     momentumf = momentum[contributor];
461     energyf = energy[contributor];
462     pressuref = energyf - 0.5 * momentumf * momentumf / massf;
463     ev = v * (gammaa - 1.0);
464 
465     F0[fIdx] += ev * massf;
466     F1[fIdx] += ev * momentumf;
467     F2[fIdx] += ev * (energyf - pressuref);
468 
469     contributor = ((v + c >= 0.0) ? upWind : downWind);
470     massf = mass[contributor];
471     momentumf = momentum[contributor];
472     energyf = energy[contributor];
473     pressuref = (gammaa - 1.0) * (energyf - 0.5 * momentumf * momentumf / massf);
474     ev = 0.5 * (v + c);
475     cLocal = sqrt(gammaa * pressuref / massf);
476 
477     F0[fIdx] += ev * massf;
478     F1[fIdx] += ev * (momentumf + massf * cLocal);
479     F2[fIdx] += ev * (energyf + pressuref + momentumf * cLocal);
480 
481     contributor = ((v - c >= 0.0) ? upWind : downWind);
482     massf = mass[contributor];
483     momentumf = momentum[contributor];
484     energyf = energy[contributor];
485     pressuref = (gammaa - 1.0) * (energyf - 0.5 * momentumf * momentumf / massf);
486     ev = 0.5 * (v - c);
487     cLocal = sqrt(gammaa * pressuref / massf);
488 
489     F0[fIdx] += ev * massf;
490     F1[fIdx] += ev * (momentumf - massf * cLocal);
491     F2[fIdx] += ev * (energyf + pressuref - momentumf * cLocal);
492   }
493 }
494 
495 /**************************************************************************
496  * Subroutine:  UpdateElemInfo
497  * Purpose   :  Q(elem) = Q(elem) + deltaQ(elem)
498  *
499  *  deltaQ(elem) = - (F(downWindFace) - F(upWindFace)) * dt / dx ;
500  *
501  *************************************************************************/
502 
UpdateElemInfo(ShockTubeMesh const & mesh)503 void UpdateElemInfo(ShockTubeMesh const& mesh)
504 {
505   // get the element quantities we want to update
506   RealField& mass = realsRegistry.getField("mass");
507   RealField& momentum = realsRegistry.getField("momentum");
508   RealField& energy = realsRegistry.getField("energy");
509   RealField& pressure = realsRegistry.getField("pressure");
510 
511   // The element update is calculated as the flux between faces
512   const RealField& F0 = realsRegistry.getField("F0");
513   const RealField& F1 = realsRegistry.getField("F1");
514   const RealField& F2 = realsRegistry.getField("F2");
515 
516   double dx = realsRegistry.getScalar("dx");
517   double dt = realsRegistry.getScalar("dt");
518   double& time = realsRegistry.getScalar("time");
519 
520   /// Update the element fields based on the face data using the elem->face
521   // relation
522   ShockTubeMesh::PositionType numTubeElems =
523     static_cast<ShockTubeMesh::PositionType>(mesh.tubeElems.size());
524 
525   for(ShockTubeMesh::PositionType tPos = 0; tPos < numTubeElems; ++tPos)
526   {
527     // Relation is over tube elements.
528     ShockTubeMesh::IndexType elemIdx = mesh.tubeElems[tPos];
529 
530     // Each element inside the tube has an upwind and downwind face
531     ShockTubeMesh::IndexType upWind =
532       mesh.relationTubeFace[tPos][UPWIND];  // upwind
533                                             // face
534     ShockTubeMesh::IndexType downWind =
535       mesh.relationTubeFace[tPos][DOWNWIND];  // downwind
536                                               // face
537 
538     mass[elemIdx] -= gammaaInverse * (F0[downWind] - F0[upWind]) * dt / dx;
539     momentum[elemIdx] -= gammaaInverse * (F1[downWind] - F1[upWind]) * dt / dx;
540     energy[elemIdx] -= gammaaInverse * (F2[downWind] - F2[upWind]) * dt / dx;
541     pressure[elemIdx] = (gammaa - 1.0) *
542       (energy[elemIdx] -
543        0.5 * momentum[elemIdx] * momentum[elemIdx] / mass[elemIdx]);
544   }
545 
546   // update the time
547   time += dt;
548 }
549 
dumpData(ShockTubeMesh const & mesh)550 void dumpData(ShockTubeMesh const& mesh)
551 {
552   RealField const& mass = realsRegistry.getField("mass");
553   RealField const& momentum = realsRegistry.getField("momentum");
554   RealField const& energy = realsRegistry.getField("energy");
555   RealField const& pressure = realsRegistry.getField("pressure");
556 
557   static const ShockTubeMesh::PositionType MAX_ELEM_DUMP = 20;
558   const int maxDumpPerSide = std::min(mesh.elems.size(), MAX_ELEM_DUMP) / 2;
559 
560   using ElemSubsetBuilder = ShockTubeMesh::ElemSubset::SetBuilder;
561   ShockTubeMesh::ElemSubset begSet, endSet;
562   begSet = ElemSubsetBuilder()     //
563              .parent(&mesh.elems)  //
564              .size(maxDumpPerSide);
565   endSet = ElemSubsetBuilder()
566              .parent(&mesh.elems)
567              .size(maxDumpPerSide)
568              .offset(mesh.elems.size() - maxDumpPerSide);
569 
570   SLIC_ASSERT(begSet.isValid(verboseOutput));
571   SLIC_ASSERT(endSet.isValid(verboseOutput));
572 
573   // Use subsets to output a few samples from the beginning and end of the tube
574   std::stringstream elemStream, mStream, pStream, eStream, rStream;
575 
576   elemStream << std::setw(5) << std::setfill(' ');
577   mStream << std::setw(5) << std::setfill(' ') << std::setprecision(3);
578   pStream << std::setw(5) << std::setfill(' ') << std::setprecision(3);
579   eStream << std::setw(5) << std::setfill(' ') << std::setprecision(3);
580   rStream << std::setw(5) << std::setfill(' ') << std::setprecision(3);
581 
582   for(int i = 0; i < begSet.size(); ++i)
583   {
584     ShockTubeMesh::IndexType ind = begSet[i];
585     if(i == 0)
586       elemStream << "IN"
587                  << "\t";
588     else
589       elemStream << ind << "\t";
590 
591     mStream << mass[ind] << "\t";
592     pStream << momentum[ind] << "\t";
593     eStream << energy[ind] << "\t";
594     rStream << pressure[ind] << "\t";
595   }
596 
597   elemStream << "...\t";
598   mStream << "...\t";
599   pStream << "...\t";
600   eStream << "...\t";
601   rStream << "...\t";
602 
603   for(int i = 0; i < endSet.size(); ++i)
604   {
605     ShockTubeMesh::IndexType ind = endSet[i];
606     if(ind == endSet.parentSet()->size() - 1)
607       elemStream << "OUT"
608                  << "\t";
609     else
610       elemStream << ind << "\t";
611 
612     mStream << mass[ind] << "\t";
613     pStream << momentum[ind] << "\t";
614     eStream << energy[ind] << "\t";
615     rStream << pressure[ind] << "\t";
616   }
617 
618   SLIC_INFO("Data dump: \n"
619             << "Elem idx: " << elemStream.str() << "\n"
620             << "mass:     " << mStream.str() << "\n"
621             << "momemtum: " << pStream.str() << "\n"
622             << "energy:   " << eStream.str() << "\n"
623             << "pressure: " << rStream.str() << "\n");
624 }
625 
626 }  // end namespace slamShocktube
627 
628 /**************************************************************************
629  * Subroutine:  main
630  * Purpose   :  Simulate a 1D Shock Tube using split flux Euler formulation
631  *************************************************************************/
632 
main(void)633 int main(void)
634 {
635   using namespace slamShocktube;
636   axom::slic::SimpleLogger logger;
637 
638   // We should be able to parallelize pretty easily by
639   // adding an MPI_Init() here, modifying the setup slightly,
640   // adding a communication subroutine in the main loop,
641   // and calling MPI_Finalize() at the end of main()
642 
643   ShockTubeMesh mesh;
644 
645   GetUserInput();
646 
647   CreateShockTubeMesh(&mesh);  // setup sets and relations
648   InitializeShockTube(mesh);   // setup fields
649 
650   int numTotalCycles = intsRegistry.getScalar("numTotalCycles");
651   int dumpInterval = intsRegistry.getScalar("numCyclesPerDump");
652 
653   // use the & operation when you want to update the param directly
654   auto& currCycle = intsRegistry.getScalar("cycle");
655   for(currCycle = 0; currCycle < numTotalCycles; ++currCycle)
656   {
657     if(currCycle % dumpInterval == 0)
658     {
659       SLIC_INFO("\tStarting cycle " << currCycle << " at time "
660                                     << realsRegistry.getScalar("time"));
661       dumpData(mesh);
662     }
663 
664     ComputeFaceInfo(mesh);
665     UpdateElemInfo(mesh);
666   }
667 
668   SLIC_INFO("\tFinished cycle " << currCycle << " at time "
669                                 << realsRegistry.getScalar("time"));
670 
671   dumpData(mesh);
672 
673   SLIC_INFO("done.");
674 
675   return 0;
676 }
677