1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
// *****************************************************************************
/*!
  \file      src/Inciter/DG.hpp
  \copyright 2012-2015 J. Bakosi,
             2016-2018 Los Alamos National Security, LLC.,
             2019-2021 Triad National Security, LLC.
             All rights reserved. See the LICENSE file for details.
  \brief     DG advances a system of PDEs with the discontinuous Galerkin scheme
  \details   DG advances a system of partial differential equations (PDEs) using
    discontinuous Galerkin (DG) finite element (FE) spatial discretization (on
    tetrahedron elements) combined with Runge-Kutta (RK) time stepping.

    There are a potentially large number of DG Charm++ chares created by
    Transporter. Each DG gets a chunk of the full load (part of the mesh)
    and does the same: initializes and advances a number of PDE systems in time.

    The implementation uses the Charm++ runtime system and is fully
    asynchronous, overlapping computation and communication. The algorithm
    utilizes the structured dagger (SDAG) Charm++ functionality. The high-level
    overview of the algorithm structure and how it interfaces with Charm++ is
    discussed in the Charm++ interface file src/Inciter/dg.ci.
*/
// *****************************************************************************
#ifndef DG_h
#define DG_h

#include <array>
#include <set>
#include <unordered_set>
#include <unordered_map>

#include "DerivedData.hpp"
#include "FaceData.hpp"
#include "ElemDiagnostics.hpp"

#include "NoWarning/dg.decl.h"

namespace inciter {

//! DG Charm++ chare array used to advance PDEs in time with DG+RK
class DG : public CBase_DG {

  public:
    #if defined(__clang__)
      #pragma clang diagnostic push
      #pragma clang diagnostic ignored "-Wunused-parameter"
      #pragma clang diagnostic ignored "-Wdeprecated-declarations"
    #elif defined(STRICT_GNUC)
      #pragma GCC diagnostic push
      #pragma GCC diagnostic ignored "-Wunused-parameter"
      #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
    #elif defined(__INTEL_COMPILER)
      #pragma warning( push )
      #pragma warning( disable: 1478 )
    #endif
    // Include Charm++ SDAG code. See http://charm.cs.illinois.edu/manuals/html/
    // charm++/manual.html, Sec. "Structured Control Flow: Structured Dagger".
    DG_SDAG_CODE
    #if defined(__clang__)
      #pragma clang diagnostic pop
    #elif defined(STRICT_GNUC)
      #pragma GCC diagnostic pop
    #elif defined(__INTEL_COMPILER)
      #pragma warning( pop )
    #endif

    //! Constructor
    explicit DG( const CProxy_Discretization& disc,
                 const std::map< int, std::vector< std::size_t > >& bface,
                 const std::map< int, std::vector< std::size_t > >& /* bnode */,
                 const std::vector< std::size_t >& triinpoel );

    #if defined(__clang__)
      #pragma clang diagnostic push
      #pragma clang diagnostic ignored "-Wundefined-func-template"
    #endif
    //! Migrate constructor
    // cppcheck-suppress uninitMemberVar
    explicit DG( CkMigrateMessage* msg ) : CBase_DG( msg ) {}
    #if defined(__clang__)
      #pragma clang diagnostic pop
    #endif

    //! Return from migration
    void ResumeFromSync() override;

    //! Start sizing communication buffers and setting up ghost data
    void resizeComm();

    //! Receive unique set of faces we potentially share with/from another chare
    void comfac( int fromch, const tk::UnsMesh::FaceSet& infaces );

    //! Receive ghost data on chare boundaries from fellow chare
    void comGhost( int fromch, const GhostData& ghost );

    //! Receive requests for ghost data
    void reqGhost();

    //! Send all of our ghost data to fellow chares
    void sendGhost();

    //! Setup node-neighborhood (esup)
    void nodeNeighSetup();

    //! Receive element-surr-points data on chare boundaries from fellow chare
    void comEsup( int fromch,
      const std::unordered_map< std::size_t, std::vector< std::size_t > >&
        bndEsup,
      const std::unordered_map< std::size_t, std::vector< tk::real > >&
        nodeBndCells );

    //! Configure Charm++ reduction types for concatenating BC nodelists
    static void registerReducers();

    //! Setup: query boundary conditions, output mesh, etc.
    void setup();

    //! Receive total box IC volume and set conditions in box
    void box( tk::real v );

    // Evaluate whether to do load balancing
    void evalLB( int nrestart );

    //! Start time stepping
    void start();

    //! Continue to next time step
    void next();

    //! Receive chare-boundary limiter function data from neighboring chares
    void comlim( int fromch,
                 const std::vector< std::size_t >& tetid,
                 const std::vector< std::vector< tk::real > >& u,
                 const std::vector< std::vector< tk::real > >& prim,
                 const std::vector< std::size_t >& ndof );

    //! Receive chare-boundary reconstructed data from neighboring chares
    void comreco( int fromch,
                  const std::vector< std::size_t >& tetid,
                  const std::vector< std::vector< tk::real > >& u,
                  const std::vector< std::vector< tk::real > >& prim,
                  const std::vector< std::size_t >& ndof );

    //! Receive chare-boundary ghost data from neighboring chares
    void comsol( int fromch,
                 std::size_t fromstage,
                 const std::vector< std::size_t >& tetid,
                 const std::vector< std::vector< tk::real > >& u,
                 const std::vector< std::vector< tk::real > >& prim,
                 const std::vector< std::size_t >& ndof );

    //! Receive contributions to nodal gradients on chare-boundaries
    void
    comnodalExtrema( const std::vector< std::size_t >& gid,
                     const std::vector< std::vector< tk::real > >& G1,
                     const std::vector< std::vector< tk::real > >& G2 );

    //! Initialize the vector of nodal extrema
    void resizeNodalExtremac();

    //! Compute the nodal extrema for chare-boundary nodes
    void evalNodalExtrm( const std::size_t ncomp,
                         const std::size_t nprim,
                         const std::size_t ndof_NodalExtrm,
                         const std::vector< std::size_t >& bndel,
                         const std::vector< std::size_t >& inpoel,
                         const tk::UnsMesh::Coords& coord,
                         const std::vector< std::size_t >& gid,
                         const std::unordered_map< std::size_t, std::size_t >&
                           bid,
                         const tk::Fields& U,
                         const tk::Fields& P,
                         std::vector< std::vector<tk::real> >& uNodalExtrm,
                         std::vector< std::vector<tk::real> >& pNodalExtrm );

    //! \brief Receive nodal solution (ofor field output) contributions from
    //!   neighboring chares
    void comnodeout( const std::vector< std::size_t >& gid,
                     const std::vector< std::size_t >& nesup,
                     const std::vector< std::vector< tk::real > >& L );

    //! Optionally refine/derefine mesh
    void refine( const std::vector< tk::real >& l2res );

    //! Receive new mesh from Refiner
    void resizePostAMR(
      const std::vector< std::size_t >& /* ginpoel */,
      const tk::UnsMesh::Chunk& chunk,
      const tk::UnsMesh::Coords& coord,
      const std::unordered_map< std::size_t, tk::UnsMesh::Edge >& /* addedNodes */,
      const std::unordered_map< std::size_t, std::size_t >& addedTets,
      const std::set< std::size_t >& /*removedNodes*/,
      const tk::NodeCommMap& nodeCommMap,
      const std::map< int, std::vector< std::size_t > >& bface,
      const std::map< int, std::vector< std::size_t > >& /* bnode */,
      const std::vector< std::size_t >& triinpoel );

    //! Extract field output going to file
    void extractFieldOutput(
      const std::vector< std::size_t >& /* ginpoel */,
      const tk::UnsMesh::Chunk& chunk,
      const tk::UnsMesh::Coords& coord,
      const std::unordered_map< std::size_t, tk::UnsMesh::Edge >& /* addedNodes */,
      const std::unordered_map< std::size_t, std::size_t >& addedTets,
      const tk::NodeCommMap& nodeCommMap,
      const std::map< int, std::vector< std::size_t > >& bface,
      const std::map< int, std::vector< std::size_t > >& /* bnode */,
      const std::vector< std::size_t >& triinpoel,
      CkCallback c );

    //! Const-ref access to current solution
    //! \return Const-ref to current solution
    const tk::Fields& solution() const { return m_u; }

    //! Compute left hand side
    void lhs();

    //! Unused in DG
    void resized() {}

    //! Compute right hand side and solve system
    void solve( tk::real newdt );

    //! Evaluate whether to continue with next time step
    void step();

    /** @name Charm++ pack/unpack serializer member functions */
    ///@{
    //! \brief Pack/Unpack serialize member function
    //! \param[in,out] p Charm++'s PUP::er serializer object reference
    void pup( PUP::er &p ) override {
      p | m_disc;
      p | m_ndof_NodalExtrm;
      p | m_ncomfac;
      p | m_nadj;
      p | m_ncomEsup;
      p | m_nsol;
      p | m_ninitsol;
      p | m_nlim;
      p | m_nnod;
      p | m_nreco;
      p | m_nnodalExtrema;
      p | m_inpoel;
      p | m_coord;
      p | m_fd;
      p | m_u;
      p | m_un;
      p | m_p;
      p | m_geoFace;
      p | m_geoElem;
      p | m_lhs;
      p | m_uNodalExtrm;
      p | m_pNodalExtrm;
      p | m_uNodalExtrmc;
      p | m_pNodalExtrmc;
      p | m_rhs;
      p | m_nfac;
      p | m_nunk;
      p | m_npoin;
      p | m_ipface;
      p | m_bndFace;
      p | m_ghostData;
      p | m_sendGhost;
      p | m_ghostReq;
      p | m_ghost;
      p | m_exptGhost;
      p | m_recvGhost;
      p | m_diag;
      p | m_stage;
      p | m_ndof;
      p | m_numEqDof;
      p | m_bid;
      p | m_uc;
      p | m_pc;
      p | m_ndofc;
      p | m_initial;
      p | m_expChBndFace;
      p | m_infaces;
      p | m_esup;
      p | m_esupc;
      p | m_elemfields;
      p | m_nodefields;
      p | m_nodefieldsc;
      p | m_outmesh;
      p | m_boxelems;
      p | m_shockmarker;
    }
    //! \brief Pack/Unpack serialize operator|
    //! \param[in,out] p Charm++'s PUP::er serializer object reference
    //! \param[in,out] i DG object reference
    friend void operator|( PUP::er& p, DG& i ) { i.pup(p); }
    //@}

  private:
    //! Local face & tet IDs associated to 3 global node IDs
    //! \details This map stores tetrahedron cell faces (map key) and their
    //!   associated local face ID and inner local tet id adjacent to the face
    //!   (map value). A face is given by 3 global node IDs.
    using FaceMap =
      std::unordered_map< tk::UnsMesh::Face,  // 3 global node IDs
                          std::array< std::size_t, 2 >, // local face & tet ID
                          tk::UnsMesh::Hash<3>,
                          tk::UnsMesh::Eq<3> >;

    //! Storage type for refined mesh used for field output
    struct OutMesh {
      //! Element connectivity, local->global node ids, global->local nodes ids
      tk::UnsMesh::Chunk chunk;
      //! Node coordinates
      tk::UnsMesh::Coords coord;
      //! Triangle element connectivity
      std::vector< std::size_t > triinpoel;
      //! Boundary-face connectivity
      std::map< int, std::vector< std::size_t > > bface;
      //! Node communinaction map
      tk::NodeCommMap nodeCommMap;
      //! \brief Pack/Unpack serialize member function
      //! \param[in,out] p Charm++'s PUP::er serializer object reference
      void pup( PUP::er& p ) {<--- Parameter 'p' can be declared with const
        p|chunk; p|coord; p|triinpoel; p|bface; p|nodeCommMap;
      }
      //! Destroyer
      void destroy() {
        tk::destroy( std::get<0>(chunk) );
        tk::destroy( std::get<1>(chunk) );
        tk::destroy( std::get<2>(chunk) );
        tk::destroy( coord[0] );
        tk::destroy( coord[1] );
        tk::destroy( coord[2] );
        tk::destroy( triinpoel );
        tk::destroy( bface );
        tk::destroy( nodeCommMap );
      }
    };

    //! Discretization proxy
    CProxy_Discretization m_disc;
    //! \brief Degree of freedom for nodal extrema vector. When DGP1 is applied,
    //!   there is one degree of freedom for cell average variable. When DGP2 is
    //!   applied, the degree of freedom is 4 which refers to cell average and
    //!   gradients in three directions
    std::size_t m_ndof_NodalExtrm;
    //! Counter for face adjacency communication map
    std::size_t m_ncomfac;
    //! Counter signaling that all ghost data have been received
    std::size_t m_nadj;
    //! Counter for element-surr-node adjacency communication map
    std::size_t m_ncomEsup;
    //! Counter signaling that we have received all our solution ghost data
    std::size_t m_nsol;
    //! \brief Counter signaling that we have received all our solution ghost
    //!    data during setup
    std::size_t m_ninitsol;
    //! \brief Counter signaling that we have received all our limiter function
    //!   ghost data
    std::size_t m_nlim;
    //! \brief Counter signaling that we have received all our node solution
    //!   contributions
    std::size_t m_nnod;
    //! Counter signaling that we have received all our reconstructed ghost data
    std::size_t m_nreco;
    //! \brief Counter signaling that we have received all our nodal extrema from
    //!   ghost chare partitions
    std::size_t m_nnodalExtrema;
    //! Mesh connectivity extended
    std::vector< std::size_t > m_inpoel;
    //! Node coordinates extended
    tk::UnsMesh::Coords m_coord;
    //! Face data
    FaceData m_fd;
    //! Vector of unknown/solution average over each mesh element
    tk::Fields m_u;
    //! Vector of unknown at previous time-step
    tk::Fields m_un;
    //! Vector of primitive quantities over each mesh element
    tk::Fields m_p;
    //! Face geometry
    tk::Fields m_geoFace;
    //! Element geometry
    tk::Fields m_geoElem;
    //! Left-hand side mass-matrix which is a diagonal matrix
    tk::Fields m_lhs;
    //! Vector of right-hand side
    tk::Fields m_rhs;
    //! Vector of nodal extrema for conservative variables
    std::vector< std::vector<tk::real> > m_uNodalExtrm;
    //! Vector of nodal extrema for primitive variables
    std::vector< std::vector<tk::real> > m_pNodalExtrm;
    //! Buffer for vector of nodal extrema for conservative variables
    std::unordered_map< std::size_t, std::vector< tk::real > > m_uNodalExtrmc;
    //! Buffer for vector of nodal extrema for primitive variables
    std::unordered_map< std::size_t, std::vector< tk::real > > m_pNodalExtrmc;
    //! Counter for number of faces on this chare (including chare boundaries)
    std::size_t m_nfac;
    //! Counter for number of unknowns on this chare (including ghosts)
    std::size_t m_nunk;
    //! Counter for number of nodes on this chare excluding ghosts
    std::size_t m_npoin;
    //! Internal + physical boundary faces (inverse of inpofa)
    tk::UnsMesh::FaceSet m_ipface;
    //! Face & tet IDs associated to global node IDs of the face for each chare
    //! \details This map stores not only the unique faces associated to
    //!   fellow chares, but also a newly assigned local face ID and adjacent
    //!   local tet ID.
    std::unordered_map< int, FaceMap > m_bndFace;
    //! Ghost data associated to chare IDs we communicate with
    std::unordered_map< int, GhostData > m_ghostData;
    //! Elements which are ghosts for other chares associated to those chare IDs
    std::unordered_map< int, std::unordered_set< std::size_t > > m_sendGhost;
    //! Number of chares requesting ghost data
    std::size_t m_ghostReq;
    //! Local element id associated to ghost remote id charewise
    //! \details This map associates the local element id (inner map value) to
    //!    the (remote) element id of the ghost (inner map key) based on the
    //!    chare id (outer map key) this remote element lies in.
    std::unordered_map< int,
      std::unordered_map< std::size_t, std::size_t > > m_ghost;
    //! Expected ghost tet ids (used only in DEBUG)
    std::set< std::size_t > m_exptGhost;
    //! Received ghost tet ids (used only in DEBUG)
    std::set< std::size_t > m_recvGhost;
    //! Diagnostics object
    ElemDiagnostics m_diag;
    //! Runge-Kutta stage counter
    std::size_t m_stage;
    //! Vector of local number of degrees of freedom for each element
    std::vector< std::size_t > m_ndof;
    //! Vector of number of degrees of freedom for each PDE equation/component
    std::vector< std::size_t > m_numEqDof;
    //! Map local ghost tet ids (value) and zero-based boundary ids (key)
    std::unordered_map< std::size_t, std::size_t > m_bid;
    //! Solution receive buffers for ghosts only
    std::array< std::vector< std::vector< tk::real > >, 3 > m_uc;
    //! Primitive-variable receive buffers for ghosts only
    std::array< std::vector< std::vector< tk::real > >, 3 > m_pc;
    //! \brief Number of degrees of freedom (for p-adaptive) receive buffers
    //!   for ghosts only
    std::array< std::vector< std::size_t >, 3 > m_ndofc;
    //! 1 if starting time stepping, 0 if during time stepping
    std::size_t m_initial;
    //! Unique set of chare-boundary faces this chare is expected to receive
    tk::UnsMesh::FaceSet m_expChBndFace;
    //! Incoming communication buffer during chare-boundary face communication
    std::unordered_map< int, tk::UnsMesh::FaceSet > m_infaces;
    //! Elements (value) surrounding point (key) data-structure
    std::map< std::size_t, std::vector< std::size_t > > m_esup;
    //! Communication buffer for esup data-structure
    std::map< std::size_t, std::vector< std::size_t > > m_esupc;
    //! Elem output fields
    std::vector< std::vector< tk::real > > m_elemfields;
    //! Node output fields
    std::vector< std::vector< tk::real > > m_nodefields;
    //! Receive buffer for communication of node output fields
    //! \details Key: global node id, value: output fields and number of
    //!   elements surrounding the node
    std::unordered_map< std::size_t, std::pair< std::vector< tk::real >,
                                                std::size_t > > m_nodefieldsc;
    //! Storage for refined mesh used for field output
    OutMesh m_outmesh;
    //! Element ids at which box ICs are defined by user (multiple boxes)
    std::vector< std::unordered_set< std::size_t > > m_boxelems;
    //! Shock detection marker for field output
    std::vector< std::size_t > m_shockmarker;

    //! Access bound Discretization class pointer
    Discretization* Disc() const {
      Assert( m_disc[ thisIndex ].ckLocal() != nullptr, "ckLocal() null" );
      return m_disc[ thisIndex ].ckLocal();
    }

    //! Compute partial boundary surface integral and sum across all chares
    void bndIntegral();

    //! Compute chare-boundary faces
    void bndFaces();

    //! Perform leak test on chare-boundary faces
    bool leakyAdjacency();

    //! Check if esuf of chare-boundary faces matches
    bool faceMatch();

    //! Verify that all chare-boundary faces have been received
    bool receivedChBndFaces();

    //! Check if entries in inpoel, inpofa and node-triplet are consistent
    std::size_t
    nodetripletMatch( const std::array< std::size_t, 2 >& id,
                      const tk::UnsMesh::Face& t );

    //! Find any chare for face (given by 3 global node IDs)
    int findchare( const tk::UnsMesh::Face& t );

    //! Setup own ghost data on this chare
    void setupGhost();

    //! Continue after face adjacency communication map completed on this chare
    void faceAdj();

    //! Continue after node adjacency communication map completed on this chare
    void adj();

    //! Fill elements surrounding a face along chare boundary
    void addEsuf( const std::array< std::size_t, 2 >& id, std::size_t ghostid );

    //! Fill elements surrounding a element along chare boundary
    void addEsuel( const std::array< std::size_t, 2 >& id,
                   std::size_t ghostid,
                   const tk::UnsMesh::Face& t );

    void addEsup();

    //! Fill face geometry data along chare boundary
    void addGeoFace( const tk::UnsMesh::Face& t,
                     const std::array< std::size_t, 2 >& id );

    //! Output mesh field data
    void writeFields( CkCallback c );

    //! Compute solution reconstructions
    void reco();

    //! Compute nodal extrema at chare-boundary nodes
    void nodalExtrema();

    //! Compute limiter function
    void lim();

    //! Compute time step size
    void dt();

    //! Evaluate whether to continue with next time step stage
    void stage();

    //! Evaluate whether to save checkpoint/restart
    void evalRestart();

    //! p-refine all elements that are adjacent to p-refined elements
    void propagate_ndof();

    //! Evaluate solution on incomping (a potentially refined) mesh
    std::tuple< tk::Fields, tk::Fields, tk::Fields, tk::Fields >
    evalSolution(
      const std::vector< std::size_t >& inpoel,
      const tk::UnsMesh::Coords& coord,
      const std::unordered_map< std::size_t, std::size_t >& addedTets );

    //! Decide wether to output field data
    bool fieldOutput() const;

    //! Decide if we write field output using a refined mesh
    bool refinedOutput() const;

    //! Start preparing fields for output to file
    void startFieldOutput( CkCallback c );
};

} // inciter::

#endif // DG_h