Quinoa all test code coverage report
Current view: top level - Inciter - FV.cpp (source / functions) Hit Total Coverage
Commit: -128-NOTFOUND Lines: 372 430 86.5 %
Date: 2024-04-22 13:03:21 Functions: 27 29 93.1 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 304 618 49.2 %

           Branch data     Line data    Source code
       1                 :            : // *****************************************************************************
       2                 :            : /*!
       3                 :            :   \file      src/Inciter/FV.cpp
       4                 :            :   \copyright 2012-2015 J. Bakosi,
       5                 :            :              2016-2018 Los Alamos National Security, LLC.,
       6                 :            :              2019-2021 Triad National Security, LLC.
       7                 :            :              All rights reserved. See the LICENSE file for details.
       8                 :            :   \brief     FV advances a system of PDEs with the finite volume scheme
       9                 :            :   \details   FV advances a system of partial differential equations (PDEs) using
      10                 :            :     the finite volume (FV) (on tetrahedron elements).
      11                 :            :   \see The documentation in FV.h.
      12                 :            : */
      13                 :            : // *****************************************************************************
      14                 :            : 
      15                 :            : #include <algorithm>
      16                 :            : #include <numeric>
      17                 :            : #include <sstream>
      18                 :            : 
      19                 :            : #include "FV.hpp"
      20                 :            : #include "Discretization.hpp"
      21                 :            : #include "FVPDE.hpp"
      22                 :            : #include "DiagReducer.hpp"
      23                 :            : #include "DerivedData.hpp"
      24                 :            : #include "ElemDiagnostics.hpp"
      25                 :            : #include "Inciter/InputDeck/InputDeck.hpp"
      26                 :            : #include "Refiner.hpp"
      27                 :            : #include "Limiter.hpp"
      28                 :            : #include "PrefIndicator.hpp"
      29                 :            : #include "Reorder.hpp"
      30                 :            : #include "Vector.hpp"
      31                 :            : #include "Around.hpp"
      32                 :            : #include "Integrate/Basis.hpp"
      33                 :            : #include "FieldOutput.hpp"
      34                 :            : #include "ChareStateCollector.hpp"
      35                 :            : 
      36                 :            : namespace inciter {
      37                 :            : 
      38                 :            : extern ctr::InputDeck g_inputdeck;
      39                 :            : extern std::vector< FVPDE > g_fvpde;
      40                 :            : 
      41                 :            : } // inciter::
      42                 :            : 
      43                 :            : extern tk::CProxy_ChareStateCollector stateProxy;
      44                 :            : 
      45                 :            : using inciter::FV;
      46                 :            : 
      47                 :        297 : FV::FV( const CProxy_Discretization& disc,
      48                 :            :         const CProxy_Ghosts& ghostsproxy,
      49                 :            :         const std::map< int, std::vector< std::size_t > >& bface,
      50                 :            :         const std::map< int, std::vector< std::size_t > >& /* bnode */,
      51                 :        297 :         const std::vector< std::size_t >& triinpoel ) :
      52                 :            :   m_disc( disc ),
      53                 :            :   m_ghosts( ghostsproxy ),
      54                 :            :   m_nsol( 0 ),
      55                 :            :   m_ninitsol( 0 ),
      56                 :            :   m_nlim( 0 ),
      57                 :            :   m_nnod( 0 ),
      58         [ +  - ]:        297 :   m_u( Disc()->Inpoel().size()/4,
      59                 :        297 :        g_inputdeck.get< tag::rdof >()*
      60                 :        297 :        g_inputdeck.get< tag::ncomp >() ),
      61                 :            :   m_un( m_u.nunk(), m_u.nprop() ),
      62                 :        297 :   m_p( m_u.nunk(), g_inputdeck.get< tag::rdof >()*
      63 [ +  - ][ +  - ]:        297 :     g_fvpde[Disc()->MeshId()].nprim() ),
      64                 :            :   m_lhs( m_u.nunk(),
      65                 :            :          g_inputdeck.get< tag::ncomp >() ),
      66                 :            :   m_rhs( m_u.nunk(), m_lhs.nprop() ),
      67         [ +  - ]:        297 :   m_npoin( Disc()->Coord()[0].size() ),
      68                 :            :   m_diag(),
      69                 :            :   m_stage( 0 ),
      70                 :            :   m_uc(),
      71                 :            :   m_pc(),
      72                 :            :   m_initial( 1 ),
      73                 :            :   m_uElemfields( m_u.nunk(), m_lhs.nprop() ),
      74                 :            :   m_pElemfields(m_u.nunk(),
      75                 :        297 :     m_p.nprop()/g_inputdeck.get< tag::rdof >()),
      76                 :            :   m_uNodefields( m_npoin, m_lhs.nprop() ),
      77                 :            :   m_pNodefields(m_npoin,
      78                 :        297 :     m_p.nprop()/g_inputdeck.get< tag::rdof >()),
      79                 :            :   m_uNodefieldsc(),
      80                 :            :   m_pNodefieldsc(),
      81                 :            :   m_boxelems(),
      82         [ +  - ]:        297 :   m_srcFlag(m_u.nunk(), 0),
      83                 :            :   m_nrk(0),
      84                 :        594 :   m_dte(m_u.nunk(), 0.0),
      85 [ +  - ][ +  - ]:       1782 :   m_finished(0)
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
      86                 :            : // *****************************************************************************
      87                 :            : //  Constructor
      88                 :            : //! \param[in] disc Discretization proxy
      89                 :            : //! \param[in] bface Boundary-faces mapped to side set ids
      90                 :            : //! \param[in] triinpoel Boundary-face connectivity
      91                 :            : // *****************************************************************************
      92                 :            : {
      93                 :            :   //! Runge-Kutta coefficients
      94         [ +  - ]:        297 :   m_nrk = 2;
      95         [ +  - ]:        297 :   m_rkcoef[0].resize(m_nrk);
      96         [ +  - ]:        297 :   m_rkcoef[1].resize(m_nrk);
      97         [ +  - ]:        297 :   if (m_nrk == 2) {
      98 [ +  - ][ +  - ]:        594 :     m_rkcoef = {{ {{ 0.0, 1.0/2.0 }}, {{ 1.0, 1.0/2.0 }} }};
      99                 :            :   }
     100                 :            :   else {
     101 [ -  - ][ -  - ]:          0 :     m_rkcoef = {{ {{ 0.0, 3.0/4.0, 1.0/3.0 }}, {{ 1.0, 1.0/4.0, 2.0/3.0 }} }};
     102                 :            :   }
     103                 :            : 
     104         [ +  - ]:        297 :   if (g_inputdeck.get< tag::cmd, tag::chare >() ||
     105         [ +  + ]:        297 :       g_inputdeck.get< tag::cmd, tag::quiescence >())
     106 [ +  - ][ +  - ]:        302 :     stateProxy.ckLocalBranch()->insert( "FV", thisIndex, CkMyPe(), Disc()->It(),
         [ +  - ][ +  - ]
                 [ -  + ]
     107                 :            :                                         "FV" );
     108                 :            : 
     109                 :        297 :   usesAtSync = true;    // enable migration at AtSync
     110                 :            : 
     111                 :            :   // Enable SDAG wait for initially building the solution vector and limiting
     112         [ +  - ]:        297 :   if (m_initial) {
     113 [ +  - ][ +  - ]:        297 :     thisProxy[ thisIndex ].wait4sol();
     114 [ +  - ][ +  - ]:        297 :     thisProxy[ thisIndex ].wait4lim();
     115 [ +  - ][ +  - ]:        594 :     thisProxy[ thisIndex ].wait4nod();
     116                 :            :   }
     117                 :            : 
     118 [ +  - ][ +  - ]:        594 :   m_ghosts[thisIndex].insert(m_disc, bface, triinpoel, m_u.nunk(),
     119 [ +  - ][ +  - ]:        891 :     CkCallback(CkIndex_FV::resizeSolVectors(), thisProxy[thisIndex]));
         [ -  + ][ -  + ]
         [ -  - ][ -  - ]
     120                 :            : 
     121                 :            :   // global-sync to call doneInserting on m_ghosts
     122         [ +  - ]:        297 :   auto meshid = Disc()->MeshId();
     123         [ +  - ]:        297 :   contribute( sizeof(std::size_t), &meshid, CkReduction::nop,
     124 [ +  - ][ -  - ]:        297 :     CkCallback(CkReductionTarget(Transporter,doneInsertingGhosts),
     125         [ +  - ]:        297 :     Disc()->Tr()) );
     126                 :        297 : }
     127                 :            : 
     128                 :            : void
     129                 :        587 : FV::registerReducers()
     130                 :            : // *****************************************************************************
     131                 :            : //  Configure Charm++ reduction types
     132                 :            : //! \details Since this is a [initnode] routine, the runtime system executes the
     133                 :            : //!   routine exactly once on every logical node early on in the Charm++ init
     134                 :            : //!   sequence. Must be static as it is called without an object. See also:
     135                 :            : //!   Section "Initializations at Program Startup" at in the Charm++ manual
     136                 :            : //!   http://charm.cs.illinois.edu/manuals/html/charm++/manual.html.
     137                 :            : // *****************************************************************************
     138                 :            : {
     139                 :        587 :   ElemDiagnostics::registerReducers();
     140                 :        587 : }
     141                 :            : 
     142                 :            : void
     143                 :        512 : FV::ResumeFromSync()
     144                 :            : // *****************************************************************************
     145                 :            : //  Return from migration
     146                 :            : //! \details This is called when load balancing (LB) completes. The presence of
     147                 :            : //!   this function does not affect whether or not we block on LB.
     148                 :            : // *****************************************************************************
     149                 :            : {
     150 [ -  + ][ -  - ]:        512 :   if (Disc()->It() == 0) Throw( "it = 0 in ResumeFromSync()" );
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
                 [ -  - ]
     151                 :            : 
     152         [ +  - ]:        512 :   if (!g_inputdeck.get< tag::cmd, tag::nonblocking >()) next();
     153                 :        512 : }
     154                 :            : 
     155                 :            : void
     156                 :        297 : FV::resizeSolVectors()
     157                 :            : // *****************************************************************************
     158                 :            : // Resize solution vectors after extension due to Ghosts
     159                 :            : // *****************************************************************************
     160                 :            : {
     161                 :            :   // Resize solution vectors, lhs and rhs by the number of ghost tets
     162                 :        297 :   m_u.resize( myGhosts()->m_nunk );
     163                 :        297 :   m_un.resize( myGhosts()->m_nunk );
     164                 :        297 :   m_srcFlag.resize( myGhosts()->m_nunk );
     165                 :        297 :   m_p.resize( myGhosts()->m_nunk );
     166                 :        297 :   m_lhs.resize( myGhosts()->m_nunk );
     167                 :        297 :   m_rhs.resize( myGhosts()->m_nunk );
     168                 :        297 :   m_dte.resize( myGhosts()->m_nunk );
     169                 :            : 
     170                 :            :   // Size communication buffer for solution
     171         [ +  + ]:        891 :   for (auto& u : m_uc) u.resize( myGhosts()->m_bid.size() );
     172         [ +  + ]:        891 :   for (auto& p : m_pc) p.resize( myGhosts()->m_bid.size() );
     173                 :            : 
     174                 :            :   // Ensure that we also have all the geometry and connectivity data
     175                 :            :   // (including those of ghosts)
     176                 :            :   Assert( myGhosts()->m_geoElem.nunk() == m_u.nunk(),
     177                 :            :     "GeoElem unknowns size mismatch" );
     178                 :            : 
     179                 :            :   // Signal the runtime system that all workers have received their adjacency
     180                 :        297 :   std::vector< std::size_t > meshdata{ myGhosts()->m_initial, Disc()->MeshId() };
     181                 :        297 :   contribute( meshdata, CkReduction::sum_ulong,
     182 [ +  - ][ +  - ]:        891 :     CkCallback(CkReductionTarget(Transporter,comfinal), Disc()->Tr()) );
         [ +  - ][ -  - ]
     183                 :        297 : }
     184                 :            : 
     185                 :            : void
     186                 :        297 : FV::setup()
     187                 :            : // *****************************************************************************
     188                 :            : // Set initial conditions, generate lhs, output mesh
     189                 :            : // *****************************************************************************
     190                 :            : {
     191         [ +  - ]:        297 :   if (g_inputdeck.get< tag::cmd, tag::chare >() ||
     192         [ +  + ]:        297 :       g_inputdeck.get< tag::cmd, tag::quiescence >())
     193 [ +  - ][ +  - ]:        302 :     stateProxy.ckLocalBranch()->insert( "FV", thisIndex, CkMyPe(), Disc()->It(),
         [ +  - ][ +  - ]
                 [ -  + ]
     194                 :            :                                         "setup" );
     195                 :            : 
     196                 :        297 :   auto d = Disc();
     197                 :            : 
     198                 :            :   // Basic error checking on sizes of element geometry data and connectivity
     199                 :            :   Assert( myGhosts()->m_geoElem.nunk() == m_lhs.nunk(),
     200                 :            :     "Size mismatch in FV::setup()" );
     201                 :            : 
     202                 :            :   // Compute left-hand side of discrete PDEs
     203                 :        297 :   lhs();
     204                 :            : 
     205                 :            :   // Determine elements inside user-defined IC box
     206                 :        297 :   g_fvpde[d->MeshId()].IcBoxElems( myGhosts()->m_geoElem,
     207                 :        297 :     myGhosts()->m_fd.Esuel().size()/4, m_boxelems );
     208                 :            : 
     209                 :            :   // Compute volume of user-defined box IC
     210 [ +  - ][ -  + ]:        297 :   d->boxvol( {}, {}, 0 );      // punt for now
     211                 :            : 
     212                 :            :   // Query time history field output labels from all PDEs integrated
     213                 :            :   const auto& hist_points = g_inputdeck.get< tag::history_output, tag::point >();
     214         [ -  + ]:        297 :   if (!hist_points.empty()) {
     215                 :          0 :     std::vector< std::string > histnames;
     216         [ -  - ]:          0 :     auto n = g_fvpde[d->MeshId()].histNames();
     217         [ -  - ]:          0 :     histnames.insert( end(histnames), begin(n), end(n) );
     218         [ -  - ]:          0 :     d->histheader( std::move(histnames) );
     219                 :            :   }
     220                 :        297 : }
     221                 :            : 
     222                 :            : void
     223                 :        297 : FV::box( tk::real v, const std::vector< tk::real >& )
     224                 :            : // *****************************************************************************
     225                 :            : // Receive total box IC volume and set conditions in box
     226                 :            : //! \param[in] v Total volume within user-specified box
     227                 :            : // *****************************************************************************
     228                 :            : {
     229                 :        297 :   auto d = Disc();
     230                 :            : 
     231                 :            :   // Store user-defined box IC volume
     232                 :        297 :   d->Boxvol() = v;
     233                 :            : 
     234                 :            :   // Set initial conditions for all PDEs
     235                 :        594 :   g_fvpde[d->MeshId()].initialize( m_lhs, myGhosts()->m_inpoel,
     236                 :        297 :     myGhosts()->m_coord, m_boxelems, d->ElemBlockId(), m_u, d->T(),
     237                 :        297 :     myGhosts()->m_fd.Esuel().size()/4 );
     238                 :        297 :   g_fvpde[d->MeshId()].updatePrimitives( m_u, m_p,
     239                 :        297 :     myGhosts()->m_fd.Esuel().size()/4 );
     240                 :            : 
     241                 :            :   m_un = m_u;
     242                 :            : 
     243                 :            :   // Output initial conditions to file (regardless of whether it was requested)
     244 [ +  - ][ +  - ]:        891 :   startFieldOutput( CkCallback(CkIndex_FV::start(), thisProxy[thisIndex]) );
         [ -  + ][ -  - ]
     245                 :        297 : }
     246                 :            : 
     247                 :            : void
     248                 :        297 : FV::start()
     249                 :            : // *****************************************************************************
     250                 :            : //  Start time stepping
     251                 :            : // *****************************************************************************
     252                 :            : {
     253                 :            :   // Start timer measuring time stepping wall clock time
     254                 :        297 :   Disc()->Timer().zero();
     255                 :            :   // Zero grind-timer
     256                 :        297 :   Disc()->grindZero();
     257                 :            :   // Start time stepping by computing the size of the next time step)
     258                 :        297 :   next();
     259                 :        297 : }
     260                 :            : 
     261                 :            : void
     262                 :       1106 : FV::startFieldOutput( CkCallback c )
     263                 :            : // *****************************************************************************
     264                 :            : // Start preparing fields for output to file
     265                 :            : //! \param[in] c Function to continue with after the write
     266                 :            : // *****************************************************************************
     267                 :            : {
     268                 :            :   // No field output in benchmark mode or if field output frequency not hit
     269 [ +  + ][ +  + ]:       1106 :   if (g_inputdeck.get< tag::cmd, tag::benchmark >() || !fieldOutput()) {
     270                 :            : 
     271                 :       1080 :     c.send();
     272                 :            : 
     273                 :            :   } else {
     274                 :            : 
     275                 :            :     // Optionally refine mesh for field output
     276                 :         26 :     auto d = Disc();
     277                 :            : 
     278         [ -  + ]:         26 :     if (refinedOutput()) {
     279                 :            : 
     280                 :          0 :       const auto& tr = tk::remap( myGhosts()->m_fd.Triinpoel(), d->Gid() );
     281 [ -  - ][ -  - ]:          0 :       d->Ref()->outref( myGhosts()->m_fd.Bface(), {}, tr, c );
         [ -  - ][ -  - ]
                 [ -  - ]
     282                 :            : 
     283                 :            :     } else {
     284                 :            : 
     285                 :            :       // cut off ghosts from mesh connectivity and coordinates
     286 [ +  - ][ -  + ]:         52 :       extractFieldOutput( {}, d->Chunk(), d->Coord(), {}, {}, d->NodeCommMap(),
                 [ -  - ]
     287                 :            :         {}, {}, {}, c );
     288                 :            : 
     289                 :            :     }
     290                 :            : 
     291                 :            :   }
     292                 :       1106 : }
     293                 :            : 
     294                 :            : void
     295                 :       1618 : FV::next()
     296                 :            : // *****************************************************************************
     297                 :            : // Advance equations to next time step
     298                 :            : // *****************************************************************************
     299                 :            : {
     300                 :            :   // communicate solution ghost data (if any)
     301         [ +  + ]:       1618 :   if (myGhosts()->m_sendGhost.empty())
     302                 :         58 :     comsol_complete();
     303                 :            :   else
     304         [ +  + ]:      18192 :     for(const auto& [cid, ghostdata] : myGhosts()->m_sendGhost) {
     305         [ +  - ]:      15072 :       std::vector< std::size_t > tetid( ghostdata.size() );
     306 [ +  - ][ +  - ]:      30144 :       std::vector< std::vector< tk::real > > u( ghostdata.size() ),
                 [ -  - ]
     307         [ +  - ]:      15072 :                                              prim( ghostdata.size() );
     308                 :            :       std::size_t j = 0;
     309         [ +  + ]:     362956 :       for(const auto& i : ghostdata) {
     310                 :            :         Assert( i < myGhosts()->m_fd.Esuel().size()/4,
     311                 :            :           "Sending solution ghost data" );
     312         [ +  - ]:     347884 :         tetid[j] = i;
     313 [ +  - ][ -  + ]:     347884 :         u[j] = m_u[i];
     314 [ +  - ][ -  + ]:     347884 :         prim[j] = m_p[i];
     315                 :     347884 :         ++j;
     316                 :            :       }
     317 [ +  - ][ +  - ]:      30144 :       thisProxy[ cid ].comsol( thisIndex, tetid, u, prim );
     318                 :            :     }
     319                 :            : 
     320                 :       1618 :   ownsol_complete();
     321                 :       1618 : }
     322                 :            : 
     323                 :            : void
     324                 :      15072 : FV::comsol( int fromch,
     325                 :            :             const std::vector< std::size_t >& tetid,
     326                 :            :             const std::vector< std::vector< tk::real > >& u,
     327                 :            :             const std::vector< std::vector< tk::real > >& prim )
     328                 :            : // *****************************************************************************
     329                 :            : //  Receive chare-boundary solution ghost data from neighboring chares
     330                 :            : //! \param[in] fromch Sender chare id
     331                 :            : //! \param[in] tetid Ghost tet ids we receive solution data for
     332                 :            : //! \param[in] u Solution ghost data
     333                 :            : //! \param[in] prim Primitive variables in ghost cells
     334                 :            : //! \details This function receives contributions to the unlimited solution
     335                 :            : //!   from fellow chares.
     336                 :            : // *****************************************************************************
     337                 :            : {
     338                 :            :   Assert( u.size() == tetid.size(), "Size mismatch in FV::comsol()" );
     339                 :            :   Assert( prim.size() == tetid.size(), "Size mismatch in FV::comsol()" );
     340                 :            : 
     341                 :            :   // Find local-to-ghost tet id map for sender chare
     342                 :      15072 :   const auto& n = tk::cref_find( myGhosts()->m_ghost, fromch );
     343                 :            : 
     344         [ +  + ]:     362956 :   for (std::size_t i=0; i<tetid.size(); ++i) {
     345                 :     347884 :     auto j = tk::cref_find( n, tetid[i] );
     346                 :            :     Assert( j >= myGhosts()->m_fd.Esuel().size()/4,
     347                 :            :       "Receiving solution non-ghost data" );
     348                 :     347884 :     auto b = tk::cref_find( myGhosts()->m_bid, j );
     349                 :            :     Assert( b < m_uc[0].size(), "Indexing out of bounds" );
     350                 :     347884 :     m_uc[0][b] = u[i];
     351                 :     347884 :     m_pc[0][b] = prim[i];
     352                 :            :   }
     353                 :            : 
     354                 :            :   // if we have received all solution ghost contributions from neighboring
     355                 :            :   // chares (chares we communicate along chare-boundary faces with), and
     356                 :            :   // contributed our solution to these neighbors, proceed to reconstructions
     357         [ +  + ]:      15072 :   if (++m_nsol == myGhosts()->m_sendGhost.size()) {
     358                 :       1560 :     m_nsol = 0;
     359                 :       1560 :     comsol_complete();
     360                 :            :   }
     361                 :      15072 : }
     362                 :            : 
     363                 :            : void
     364         [ +  - ]:         26 : FV::extractFieldOutput(
     365                 :            :   const std::vector< std::size_t >& /*ginpoel*/,
     366                 :            :   const tk::UnsMesh::Chunk& chunk,
     367                 :            :   const tk::UnsMesh::Coords& coord,
     368                 :            :   const std::unordered_map< std::size_t, tk::UnsMesh::Edge >& /*addedNodes*/,
     369                 :            :   const std::unordered_map< std::size_t, std::size_t >& addedTets,
     370                 :            :   const tk::NodeCommMap& nodeCommMap,
     371                 :            :   const std::map< int, std::vector< std::size_t > >& /* bface */,
     372                 :            :   const std::map< int, std::vector< std::size_t > >& /* bnode */,
     373                 :            :   const std::vector< std::size_t >& /* triinpoel */,
     374                 :            :   CkCallback c )
     375                 :            : // *****************************************************************************
     376                 :            : // Extract field output going to file
     377                 :            : //! \param[in] chunk Field-output mesh chunk (connectivity and global<->local
     378                 :            : //!    id maps)
     379                 :            : //! \param[in] coord Field-output mesh node coordinates
     380                 :            : //! \param[in] addedTets Field-output mesh cells and their parents (local ids)
     381                 :            : //! \param[in] nodeCommMap Field-output mesh node communication map
     382                 :            : //! \param[in] c Function to continue with after the write
     383                 :            : // *****************************************************************************
     384                 :            : {
     385                 :            :   const auto& inpoel = std::get< 0 >( chunk );
     386                 :            : 
     387                 :            :   // Evaluate element solution on incoming mesh
     388 [ +  - ][ +  - ]:         52 :   evalSolution( *Disc(), inpoel, coord, addedTets, std::vector< std::size_t>{},
                 [ +  + ]
     389         [ +  - ]:         26 :     m_u, m_p, m_uElemfields, m_pElemfields, m_uNodefields, m_pNodefields );
     390                 :            : 
     391                 :            :   // Send node fields contributions to neighbor chares
     392         [ +  + ]:         26 :   if (nodeCommMap.empty())
     393                 :          2 :     comnodeout_complete();
     394                 :            :   else {
     395                 :            :     const auto& lid = std::get< 2 >( chunk );
     396                 :         48 :     auto esup = tk::genEsup( inpoel, 4 );
     397         [ +  + ]:         96 :     for(const auto& [ch,nodes] : nodeCommMap) {
     398                 :            :       // Pack node field data in chare boundary nodes
     399                 :            :       std::vector< std::vector< tk::real > >
     400 [ +  - ][ +  - ]:        216 :         lu( m_uNodefields.nprop(), std::vector< tk::real >( nodes.size() ) );
                 [ +  - ]
     401                 :            :       std::vector< std::vector< tk::real > >
     402 [ +  - ][ +  - ]:        216 :         lp( m_pNodefields.nprop(), std::vector< tk::real >( nodes.size() ) );
     403         [ +  + ]:        720 :       for (std::size_t f=0; f<m_uNodefields.nprop(); ++f) {
     404                 :            :         std::size_t j = 0;
     405         [ +  + ]:      38124 :         for (auto g : nodes)
     406                 :      37476 :           lu[f][j++] = m_uNodefields(tk::cref_find(lid,g),f);
     407                 :            :       }
     408         [ +  + ]:        432 :       for (std::size_t f=0; f<m_pNodefields.nprop(); ++f) {
     409                 :            :         std::size_t j = 0;
     410         [ +  + ]:      21180 :         for (auto g : nodes)
     411                 :      20820 :           lp[f][j++] = m_pNodefields(tk::cref_find(lid,g),f);
     412                 :            :       }
     413                 :            :       // Pack (partial) number of elements surrounding chare boundary nodes
     414         [ +  - ]:         72 :       std::vector< std::size_t > nesup( nodes.size() );
     415                 :            :       std::size_t j = 0;
     416         [ +  + ]:       4236 :       for (auto g : nodes) {
     417                 :       4164 :         auto i = tk::cref_find( lid, g );
     418                 :       4164 :         nesup[j++] = esup.second[i+1] - esup.second[i];
     419                 :            :       }
     420 [ +  - ][ +  - ]:        216 :       thisProxy[ch].comnodeout(
         [ +  - ][ -  - ]
     421 [ +  - ][ -  + ]:        144 :         std::vector<std::size_t>(begin(nodes),end(nodes)), nesup, lu, lp );
     422                 :            :     }
     423                 :            :   }
     424                 :            : 
     425         [ +  - ]:         26 :   ownnod_complete( c );
     426                 :         26 : }
     427                 :            : 
     428                 :            : void
     429                 :        297 : FV::lhs()
     430                 :            : // *****************************************************************************
     431                 :            : // Compute left-hand side of discrete transport equations
     432                 :            : // *****************************************************************************
     433                 :            : {
     434                 :        297 :   g_fvpde[Disc()->MeshId()].lhs( myGhosts()->m_geoElem, m_lhs );
     435                 :            : 
     436         [ -  + ]:        297 :   if (!m_initial) stage();
     437                 :        297 : }
     438                 :            : 
     439                 :            : void
     440                 :       1618 : FV::reco()
     441                 :            : // *****************************************************************************
     442                 :            : // Compute reconstructions
     443                 :            : // *****************************************************************************
     444                 :            : {
     445                 :       1618 :   const auto rdof = g_inputdeck.get< tag::rdof >();
     446                 :            : 
     447                 :            :   // Combine own and communicated contributions of unreconstructed solution and
     448                 :            :   // degrees of freedom in cells (if p-adaptive)
     449         [ +  + ]:     351120 :   for (const auto& b : myGhosts()->m_bid) {
     450                 :            :     Assert( m_uc[0][b.second].size() == m_u.nprop(), "ncomp size mismatch" );
     451                 :            :     Assert( m_pc[0][b.second].size() == m_p.nprop(), "ncomp size mismatch" );
     452         [ +  + ]:   14582716 :     for (std::size_t c=0; c<m_u.nprop(); ++c) {
     453                 :   14234832 :       m_u(b.first,c) = m_uc[0][b.second][c];
     454                 :            :     }
     455         [ +  + ]:    7875900 :     for (std::size_t c=0; c<m_p.nprop(); ++c) {
     456                 :    7528016 :       m_p(b.first,c) = m_pc[0][b.second][c];
     457                 :            :     }
     458                 :            :   }
     459                 :            : 
     460         [ +  - ]:       1618 :   if (rdof > 1) {
     461                 :            :     // Reconstruct second-order solution and primitive quantities
     462                 :       3236 :     g_fvpde[Disc()->MeshId()].reconstruct( myGhosts()->m_geoElem, myGhosts()->m_fd,
     463                 :       1618 :       myGhosts()->m_esup, myGhosts()->m_inpoel, myGhosts()->m_coord, m_u, m_p );
     464                 :            :   }
     465                 :            : 
     466                 :            :   // start limiting
     467                 :       1618 :   lim();
     468                 :       1618 : }
     469                 :            : 
     470                 :            : void
     471                 :       1618 : FV::lim()
     472                 :            : // *****************************************************************************
     473                 :            : // Compute limiter function
     474                 :            : // *****************************************************************************
     475                 :            : {
     476                 :       1618 :   const auto rdof = g_inputdeck.get< tag::rdof >();
     477                 :            : 
     478         [ +  - ]:       1618 :   if (rdof > 1) {
     479                 :       3236 :     g_fvpde[Disc()->MeshId()].limit( myGhosts()->m_geoFace, myGhosts()->m_fd,
     480                 :       1618 :       myGhosts()->m_esup,
     481                 :       1618 :       myGhosts()->m_inpoel, myGhosts()->m_coord, m_srcFlag, m_u, m_p );
     482                 :            :   }
     483                 :            : 
     484                 :            :   // Send limited solution to neighboring chares
     485         [ +  + ]:       1618 :   if (myGhosts()->m_sendGhost.empty())
     486                 :         58 :     comlim_complete();
     487                 :            :   else
     488         [ +  + ]:      18192 :     for(const auto& [cid, ghostdata] : myGhosts()->m_sendGhost) {
     489         [ +  - ]:      15072 :       std::vector< std::size_t > tetid( ghostdata.size() );
     490 [ +  - ][ +  - ]:      30144 :       std::vector< std::vector< tk::real > > u( ghostdata.size() ),
                 [ -  - ]
     491         [ +  - ]:      15072 :                                              prim( ghostdata.size() );
     492                 :            :       std::size_t j = 0;
     493         [ +  + ]:     362956 :       for(const auto& i : ghostdata) {
     494                 :            :         Assert( i < myGhosts()->m_fd.Esuel().size()/4,
     495                 :            :           "Sending limiter ghost data" );
     496         [ +  - ]:     347884 :         tetid[j] = i;
     497 [ +  - ][ -  + ]:     347884 :         u[j] = m_u[i];
     498 [ +  - ][ -  + ]:     347884 :         prim[j] = m_p[i];
     499                 :     347884 :         ++j;
     500                 :            :       }
     501 [ +  - ][ +  - ]:      30144 :       thisProxy[ cid ].comlim( thisIndex, tetid, u, prim );
     502                 :            :     }
     503                 :            : 
     504                 :       1618 :   ownlim_complete();
     505                 :       1618 : }
     506                 :            : 
     507                 :            : void
     508                 :      15072 : FV::comlim( int fromch,
     509                 :            :             const std::vector< std::size_t >& tetid,
     510                 :            :             const std::vector< std::vector< tk::real > >& u,
     511                 :            :             const std::vector< std::vector< tk::real > >& prim )
     512                 :            : // *****************************************************************************
     513                 :            : //  Receive chare-boundary limiter ghost data from neighboring chares
     514                 :            : //! \param[in] fromch Sender chare id
     515                 :            : //! \param[in] tetid Ghost tet ids we receive solution data for
     516                 :            : //! \param[in] u Limited high-order solution
     517                 :            : //! \param[in] prim Limited high-order primitive quantities
     518                 :            : //! \details This function receives contributions to the limited solution from
     519                 :            : //!   fellow chares.
     520                 :            : // *****************************************************************************
     521                 :            : {
     522                 :            :   Assert( u.size() == tetid.size(), "Size mismatch in FV::comlim()" );
     523                 :            :   Assert( prim.size() == tetid.size(), "Size mismatch in FV::comlim()" );
     524                 :            : 
     525                 :            :   // Find local-to-ghost tet id map for sender chare
     526                 :      15072 :   const auto& n = tk::cref_find( myGhosts()->m_ghost, fromch );
     527                 :            : 
     528         [ +  + ]:     362956 :   for (std::size_t i=0; i<tetid.size(); ++i) {
     529                 :     347884 :     auto j = tk::cref_find( n, tetid[i] );
     530                 :            :     Assert( j >= myGhosts()->m_fd.Esuel().size()/4,
     531                 :            :       "Receiving solution non-ghost data" );
     532                 :     347884 :     auto b = tk::cref_find( myGhosts()->m_bid, j );
     533                 :            :     Assert( b < m_uc[1].size(), "Indexing out of bounds" );
     534                 :            :     Assert( b < m_pc[1].size(), "Indexing out of bounds" );
     535                 :     347884 :     m_uc[1][b] = u[i];
     536                 :     347884 :     m_pc[1][b] = prim[i];
     537                 :            :   }
     538                 :            : 
     539                 :            :   // if we have received all solution ghost contributions from neighboring
     540                 :            :   // chares (chares we communicate along chare-boundary faces with), and
     541                 :            :   // contributed our solution to these neighbors, proceed to limiting
     542         [ +  + ]:      15072 :   if (++m_nlim == myGhosts()->m_sendGhost.size()) {
     543                 :       1560 :     m_nlim = 0;
     544                 :       1560 :     comlim_complete();
     545                 :            :   }
     546                 :      15072 : }
     547                 :            : 
     548                 :            : void
     549                 :       1618 : FV::dt()
     550                 :            : // *****************************************************************************
     551                 :            : // Compute time step size
     552                 :            : // *****************************************************************************
     553                 :            : {
     554                 :       1618 :   auto d = Disc();
     555                 :            : 
     556                 :            :   // Combine own and communicated contributions of limited solution and degrees
     557                 :            :   // of freedom in cells (if p-adaptive)
     558         [ +  + ]:     351120 :   for (const auto& b : myGhosts()->m_bid) {
     559                 :            :     Assert( m_uc[1][b.second].size() == m_u.nprop(), "ncomp size mismatch" );
     560                 :            :     Assert( m_pc[1][b.second].size() == m_p.nprop(), "ncomp size mismatch" );
     561         [ +  + ]:   14582716 :     for (std::size_t c=0; c<m_u.nprop(); ++c) {
     562                 :   14234832 :       m_u(b.first,c) = m_uc[1][b.second][c];
     563                 :            :     }
     564         [ +  + ]:    7875900 :     for (std::size_t c=0; c<m_p.nprop(); ++c) {
     565                 :    7528016 :       m_p(b.first,c) = m_pc[1][b.second][c];
     566                 :            :     }
     567                 :            :   }
     568                 :            : 
     569                 :       1618 :   auto mindt = std::numeric_limits< tk::real >::max();
     570                 :            : 
     571         [ +  + ]:       1618 :   if (m_stage == 0)
     572                 :            :   {
     573         [ +  + ]:        809 :     auto const_dt = g_inputdeck.get< tag::dt >();
     574                 :            :     auto eps = std::numeric_limits< tk::real >::epsilon();
     575                 :            : 
     576                 :            :     // use constant dt if configured
     577         [ +  + ]:        809 :     if (std::abs(const_dt) > eps) {
     578                 :            : 
     579                 :        584 :       mindt = const_dt;
     580                 :            : 
     581                 :            :     } else {      // compute dt based on CFL
     582                 :            : 
     583                 :            :       // find the minimum dt across all PDEs integrated
     584                 :            :       auto eqdt =
     585                 :        450 :         g_fvpde[d->MeshId()].dt( myGhosts()->m_fd, myGhosts()->m_geoFace,
     586                 :        225 :           myGhosts()->m_geoElem, m_u, m_p, myGhosts()->m_fd.Esuel().size()/4,
     587                 :        225 :           m_srcFlag, m_dte );
     588         [ +  - ]:        225 :       if (eqdt < mindt) mindt = eqdt;
     589                 :            : 
     590                 :            :       // time-step suppression for unsteady problems
     591                 :            :       tk::real coeff(1.0);
     592         [ +  - ]:        225 :       if (!g_inputdeck.get< tag::steady_state >()) {
     593         [ +  - ]:        225 :         if (d->It() < 100) coeff = 0.01 * static_cast< tk::real >(d->It());
     594                 :            :       }
     595                 :            : 
     596                 :        225 :       mindt *= coeff * g_inputdeck.get< tag::cfl >();
     597                 :            :     }
     598                 :            :   }
     599                 :            :   else
     600                 :            :   {
     601                 :        809 :     mindt = d->Dt();
     602                 :            :   }
     603                 :            : 
     604                 :            :   // Contribute to minimum dt across all chares then advance to next step
     605         [ +  - ]:       1618 :   contribute( sizeof(tk::real), &mindt, CkReduction::min_double,
     606                 :       1618 :               CkCallback(CkReductionTarget(FV,solve), thisProxy) );
     607                 :       1618 : }
     608                 :            : 
     609                 :            : void
     610                 :       1618 : FV::solve( tk::real newdt )
     611                 :            : // *****************************************************************************
     612                 :            : // Compute right-hand side of discrete transport equations
     613                 :            : //! \param[in] newdt Size of this new time step
     614                 :            : // *****************************************************************************
     615                 :            : {
     616                 :            :   // Enable SDAG wait for building the solution vector during the next stage
     617         [ +  - ]:       1618 :   thisProxy[ thisIndex ].wait4sol();
     618         [ +  - ]:       1618 :   thisProxy[ thisIndex ].wait4lim();
     619         [ +  - ]:       1618 :   thisProxy[ thisIndex ].wait4nod();
     620                 :            : 
     621                 :       1618 :   auto d = Disc();
     622                 :       1618 :   const auto rdof = g_inputdeck.get< tag::rdof >();
     623                 :       1618 :   const auto neq = m_u.nprop()/rdof;
     624                 :            : 
     625                 :            :   // Set new time step size
     626         [ +  + ]:       1618 :   if (m_stage == 0) d->setdt( newdt );
     627                 :            : 
     628                 :            :   // Update Un
     629         [ +  + ]:       1618 :   if (m_stage == 0) m_un = m_u;
     630                 :            : 
     631                 :            :   // physical time at time-stage for computing exact source terms for
     632                 :            :   // unsteady problems
     633                 :       1618 :   tk::real physT(d->T());
     634                 :            :   // 2-stage RK
     635         [ +  - ]:       1618 :   if (m_nrk == 2) {
     636         [ +  + ]:       1618 :     if (m_stage == 1) {
     637                 :        809 :       physT += d->Dt();
     638                 :            :     }
     639                 :            :   }
     640                 :            :   // 3-stage RK
     641                 :            :   else {
     642         [ -  - ]:          0 :     if (m_stage == 1) {
     643                 :          0 :       physT += d->Dt();
     644                 :            :     }
     645         [ -  - ]:          0 :     else if (m_stage == 2) {
     646                 :          0 :       physT += 0.5*d->Dt();
     647                 :            :     }
     648                 :            :   }
     649                 :            : 
     650                 :            :   // Compute rhs
     651                 :       3236 :   g_fvpde[d->MeshId()].rhs( physT, myGhosts()->m_geoFace, myGhosts()->m_geoElem,
     652                 :       1618 :     myGhosts()->m_fd, myGhosts()->m_inpoel, myGhosts()->m_coord,
     653                 :       1618 :     d->ElemBlockId(), m_u, m_p, m_rhs, m_srcFlag );
     654                 :            : 
     655                 :            :   // Explicit time-stepping using RK3 to discretize time-derivative
     656                 :       1618 :   const auto steady = g_inputdeck.get< tag::steady_state >();
     657         [ +  + ]:     570262 :   for (std::size_t e=0; e<myGhosts()->m_nunk; ++e)
     658         [ +  + ]:    6271872 :     for (std::size_t c=0; c<neq; ++c)
     659                 :            :     {
     660                 :    5703228 :       auto dte = d->Dt();
     661         [ -  + ]:    5703228 :       if (steady) dte = m_dte[e];
     662                 :    5703228 :       auto rmark = c*rdof;
     663         [ +  - ]:    5703228 :       m_u(e, rmark) =  m_rkcoef[0][m_stage] * m_un(e, rmark)
     664                 :    5703228 :         + m_rkcoef[1][m_stage] * ( m_u(e, rmark)
     665                 :    5703228 :           + dte * m_rhs(e, c)/m_lhs(e, c) );
     666                 :            :       // zero out reconstructed dofs of equations using reduced dofs
     667         [ +  - ]:    5703228 :       if (rdof > 1) {
     668         [ +  + ]:   22812912 :         for (std::size_t k=1; k<rdof; ++k)
     669                 :            :         {
     670                 :   17109684 :           rmark = c*rdof+k;
     671                 :   17109684 :           m_u(e, rmark) = 0.0;
     672                 :            :         }
     673                 :            :       }
     674                 :            :     }
     675                 :            : 
     676                 :            :   // Update primitives based on the evolved solution
     677                 :       1618 :   g_fvpde[d->MeshId()].updatePrimitives( m_u, m_p,
     678                 :       1618 :     myGhosts()->m_fd.Esuel().size()/4 );
     679         [ +  - ]:       1618 :   if (!g_inputdeck.get< tag::accuracy_test >()) {
     680                 :       1618 :     g_fvpde[d->MeshId()].cleanTraceMaterial( physT, myGhosts()->m_geoElem, m_u,
     681                 :       1618 :       m_p, myGhosts()->m_fd.Esuel().size()/4 );
     682                 :            :   }
     683                 :            : 
     684         [ +  + ]:       1618 :   if (m_stage < m_nrk-1) {
     685                 :            : 
     686                 :            :     // continue with next time step stage
     687                 :        809 :     stage();
     688                 :            : 
     689                 :            :   } else {
     690                 :            : 
     691                 :            :     // Increase number of iterations and physical time
     692                 :        809 :     d->next();
     693                 :            : 
     694                 :            :     // Compute diagnostics, e.g., residuals
     695         [ +  - ]:        809 :     auto diag_computed = m_diag.compute( *d,
     696 [ +  - ][ +  - ]:        809 :       m_u.nunk()-myGhosts()->m_fd.Esuel().size()/4, myGhosts()->m_geoElem,
                 [ +  - ]
     697         [ +  - ]:        809 :       std::vector< std::size_t>{}, m_u, m_un );
     698                 :            : 
     699                 :            :     // Continue to mesh refinement (if configured)
     700 [ +  + ][ +  - ]:        965 :     if (!diag_computed) refine( std::vector< tk::real >( m_u.nprop(), 1.0 ) );
                 [ +  - ]
     701                 :            : 
     702                 :            :   }
     703                 :       1618 : }
     704                 :            : 
     705                 :            : void
     706                 :        809 : FV::refine( const std::vector< tk::real >& l2res )
     707                 :            : // *****************************************************************************
     708                 :            : // Optionally refine/derefine mesh
     709                 :            : //! \param[in] l2res L2-norms of the residual for each scalar component
     710                 :            : //!   computed across the whole problem
     711                 :            : // *****************************************************************************
     712                 :            : {
     713                 :        809 :   auto d = Disc();
     714                 :            : 
     715                 :            :   // Assess convergence for steady state
     716                 :        809 :   const auto steady = g_inputdeck.get< tag::steady_state >();
     717                 :        809 :   const auto residual = g_inputdeck.get< tag::residual >();
     718                 :        809 :   const auto rc = g_inputdeck.get< tag::rescomp >() - 1;
     719                 :            : 
     720                 :            :   bool converged(false);
     721         [ -  + ]:        809 :   if (steady) converged = l2res[rc] < residual;
     722                 :            : 
     723                 :            :   // this is the last time step if max time of max number of time steps
     724                 :            :   // reached or the residual has reached its convergence criterion
     725 [ +  + ][ -  + ]:        809 :   if (d->finished() or converged) m_finished = 1;
     726                 :            : 
     727                 :        809 :   auto dtref = g_inputdeck.get< tag::amr, tag::dtref >();
     728                 :        809 :   auto dtfreq = g_inputdeck.get< tag::amr, tag::dtfreq >();
     729                 :            : 
     730                 :            :   // if t>0 refinement enabled and we hit the dtref frequency
     731 [ -  + ][ -  - ]:        809 :   if (dtref && !(d->It() % dtfreq)) {   // refine
     732                 :            : 
     733                 :          0 :     d->startvol();
     734 [ -  - ][ -  - ]:          0 :     d->Ref()->dtref( myGhosts()->m_fd.Bface(), {},
         [ -  - ][ -  - ]
     735                 :          0 :       tk::remap(myGhosts()->m_fd.Triinpoel(),d->Gid()) );
     736                 :          0 :     d->refined() = 1;
     737                 :            : 
     738                 :            :   } else {      // do not refine
     739                 :            : 
     740                 :        809 :     d->refined() = 0;
     741                 :        809 :     stage();
     742                 :            : 
     743                 :            :   }
     744                 :        809 : }
     745                 :            : 
     746                 :            : void
     747                 :          0 : FV::resizePostAMR(
     748                 :            :   const std::vector< std::size_t >& /*ginpoel*/,
     749                 :            :   const tk::UnsMesh::Chunk& chunk,
     750                 :            :   const tk::UnsMesh::Coords& coord,
     751                 :            :   const std::unordered_map< std::size_t, tk::UnsMesh::Edge >& /*addedNodes*/,
     752                 :            :   const std::unordered_map< std::size_t, std::size_t >& addedTets,
     753                 :            :   const std::set< std::size_t >& removedNodes,
     754                 :            :   const std::unordered_map< std::size_t, std::size_t >& amrNodeMap,
     755                 :            :   const tk::NodeCommMap& nodeCommMap,
     756                 :            :   const std::map< int, std::vector< std::size_t > >& bface,
     757                 :            :   const std::map< int, std::vector< std::size_t > >& /* bnode */,
     758                 :            :   const std::vector< std::size_t >& triinpoel,
     759                 :            :   const std::unordered_map< std::size_t, std::set< std::size_t > >& elemblockid )
     760                 :            : // *****************************************************************************
     761                 :            : //  Receive new mesh from Refiner
     762                 :            : //! \param[in] chunk New mesh chunk (connectivity and global<->local id maps)
     763                 :            : //! \param[in] coord New mesh node coordinates
     764                 :            : //! \param[in] addedTets Newly added mesh cells and their parents (local ids)
     765                 :            : //! \param[in] removedNodes Newly removed mesh node local ids
     766                 :            : //! \param[in] amrNodeMap Node id map after amr (local ids)
     767                 :            : //! \param[in] nodeCommMap New node communication map
     768                 :            : //! \param[in] bface Boundary-faces mapped to side set ids
     769                 :            : //! \param[in] triinpoel Boundary-face connectivity
     770                 :            : //! \param[in] elemblockid Local tet ids associated with mesh block ids
     771                 :            : // *****************************************************************************
     772                 :            : {
     773                 :          0 :   auto d = Disc();
     774                 :            : 
     775                 :            :   // Set flag that indicates that we are during time stepping
     776                 :          0 :   m_initial = 0;
     777                 :          0 :   myGhosts()->m_initial = 0;
     778                 :            : 
     779                 :            :   // Zero field output iteration count between two mesh refinement steps
     780                 :          0 :   d->Itf() = 0;
     781                 :            : 
     782                 :            :   // Increase number of iterations with mesh refinement
     783                 :          0 :   ++d->Itr();
     784                 :            : 
     785                 :            :   // Save old number of elements
     786                 :          0 :   [[maybe_unused]] auto old_nelem = myGhosts()->m_inpoel.size()/4;
     787                 :            : 
     788                 :            :   // Resize mesh data structures
     789                 :          0 :   d->resizePostAMR( chunk, coord, amrNodeMap, nodeCommMap, removedNodes,
     790                 :            :     elemblockid );
     791                 :            : 
     792                 :            :   // Update state
     793                 :          0 :   myGhosts()->m_inpoel = d->Inpoel();
     794                 :          0 :   myGhosts()->m_coord = d->Coord();
     795                 :          0 :   auto nelem = myGhosts()->m_inpoel.size()/4;
     796                 :            :   m_p.resize( nelem );
     797                 :            :   m_u.resize( nelem );
     798                 :          0 :   m_srcFlag.resize( nelem );
     799                 :            :   m_un.resize( nelem );
     800                 :            :   m_lhs.resize( nelem );
     801                 :            :   m_rhs.resize( nelem );
     802                 :            : 
     803 [ -  - ][ -  - ]:          0 :   myGhosts()->m_fd = FaceData( myGhosts()->m_inpoel, bface,
         [ -  - ][ -  - ]
                 [ -  - ]
     804                 :          0 :     tk::remap(triinpoel,d->Lid()) );
     805                 :            : 
     806         [ -  - ]:          0 :   myGhosts()->m_geoFace =
     807                 :          0 :     tk::Fields( tk::genGeoFaceTri( myGhosts()->m_fd.Nipfac(),
     808                 :          0 :     myGhosts()->m_fd.Inpofa(), coord ) );
     809         [ -  - ]:          0 :   myGhosts()->m_geoElem = tk::Fields( tk::genGeoElemTet( myGhosts()->m_inpoel,
     810                 :          0 :     coord ) );
     811                 :            : 
     812                 :          0 :   myGhosts()->m_nfac = myGhosts()->m_fd.Inpofa().size()/3;
     813                 :          0 :   myGhosts()->m_nunk = nelem;
     814                 :          0 :   m_npoin = coord[0].size();
     815                 :          0 :   myGhosts()->m_bndFace.clear();
     816                 :          0 :   myGhosts()->m_exptGhost.clear();
     817                 :          0 :   myGhosts()->m_sendGhost.clear();
     818                 :          0 :   myGhosts()->m_ghost.clear();
     819                 :          0 :   myGhosts()->m_esup.clear();
     820                 :            : 
     821                 :            :   // Update solution on new mesh, P0 (cell center value) only for now
     822                 :            :   m_un = m_u;
     823                 :            :   auto pn = m_p;
     824                 :          0 :   auto unprop = m_u.nprop();
     825                 :            :   auto pnprop = m_p.nprop();
     826         [ -  - ]:          0 :   for (const auto& [child,parent] : addedTets) {
     827                 :            :     Assert( child < nelem, "Indexing out of new solution vector" );
     828                 :            :     Assert( parent < old_nelem, "Indexing out of old solution vector" );
     829         [ -  - ]:          0 :     for (std::size_t i=0; i<unprop; ++i) m_u(child,i) = m_un(parent,i);
     830         [ -  - ]:          0 :     for (std::size_t i=0; i<pnprop; ++i) m_p(child,i) = pn(parent,i);
     831                 :            :   }
     832                 :            :   m_un = m_u;
     833                 :            : 
     834                 :            :   // Resize communication buffers
     835 [ -  - ][ -  - ]:          0 :   m_ghosts[thisIndex].resizeComm();
         [ -  - ][ -  - ]
     836                 :          0 : }
     837                 :            : 
     838                 :            : bool
     839                 :        230 : FV::fieldOutput() const
     840                 :            : // *****************************************************************************
     841                 :            : // Decide wether to output field data
     842                 :            : //! \return True if field data is output in this step
     843                 :            : // *****************************************************************************
     844                 :            : {
     845                 :        230 :   auto d = Disc();
     846                 :            : 
     847                 :            :   // Output field data
     848 [ +  + ][ +  - ]:        230 :   return d->fielditer() or d->fieldtime() or d->fieldrange() or m_finished;
         [ +  - ][ -  + ]
     849                 :            : }
     850                 :            : 
     851                 :            : bool
     852                 :         26 : FV::refinedOutput() const
     853                 :            : // *****************************************************************************
     854                 :            : // Decide if we write field output using a refined mesh
     855                 :            : //! \return True if field output will use a refined mesh
     856                 :            : // *****************************************************************************
     857                 :            : {
     858         [ -  + ]:         26 :   return g_inputdeck.get< tag::field_output, tag::refined >() &&
     859         [ -  - ]:          0 :          g_inputdeck.get< tag::scheme >() != ctr::SchemeType::FV;
     860                 :            : }
     861                 :            : 
     862                 :            : void
     863                 :         26 : FV::writeFields( CkCallback c )
     864                 :            : // *****************************************************************************
     865                 :            : // Output mesh field data
     866                 :            : //! \param[in] c Function to continue with after the write
     867                 :            : // *****************************************************************************
     868                 :            : {
     869                 :         26 :   auto d = Disc();
     870                 :            : 
     871                 :            :   const auto& inpoel = std::get< 0 >( d->Chunk() );
     872                 :         52 :   auto esup = tk::genEsup( inpoel, 4 );
     873                 :         26 :   auto nelem = inpoel.size() / 4;
     874                 :            : 
     875                 :            :   // Combine own and communicated contributions and finish averaging of node
     876                 :            :   // field output in chare boundary nodes
     877                 :            :   const auto& lid = std::get< 2 >( d->Chunk() );
     878         [ +  + ]:       2738 :   for (const auto& [g,f] : m_uNodefieldsc) {
     879                 :            :     Assert( m_uNodefields.nprop() == f.first.size(), "Size mismatch" );
     880                 :       2712 :     auto p = tk::cref_find( lid, g );
     881         [ +  + ]:      27120 :     for (std::size_t i=0; i<f.first.size(); ++i) {
     882                 :      24408 :       m_uNodefields(p,i) += f.first[i];
     883                 :      24408 :       m_uNodefields(p,i) /= static_cast< tk::real >(
     884                 :      24408 :                               esup.second[p+1] - esup.second[p] + f.second );
     885                 :            :     }
     886                 :            :   }
     887                 :         26 :   tk::destroy( m_uNodefieldsc );
     888         [ +  + ]:       2738 :   for (const auto& [g,f] : m_pNodefieldsc) {
     889                 :            :     Assert( m_pNodefields.nprop() == f.first.size(), "Size mismatch" );
     890                 :       2712 :     auto p = tk::cref_find( lid, g );
     891         [ +  + ]:      16272 :     for (std::size_t i=0; i<f.first.size(); ++i) {
     892                 :      13560 :       m_pNodefields(p,i) += f.first[i];
     893                 :      13560 :       m_pNodefields(p,i) /= static_cast< tk::real >(
     894                 :      13560 :                               esup.second[p+1] - esup.second[p] + f.second );
     895                 :            :     }
     896                 :            :   }
     897                 :         26 :   tk::destroy( m_pNodefieldsc );
     898                 :            : 
     899                 :            :   // Lambda to decide if a node (global id) is on a chare boundary of the field
     900                 :            :   // output mesh. p - global node id, return true if node is on the chare
     901                 :            :   // boundary.
     902                 :       4480 :   auto chbnd = [ this ]( std::size_t p ) {
     903                 :            :     return
     904                 :       4480 :       std::any_of( Disc()->NodeCommMap().cbegin(), Disc()->NodeCommMap().cend(),
     905                 :       4480 :         [&](const auto& s) { return s.second.find(p) != s.second.cend(); } );
     906                 :         26 :   };
     907                 :            : 
     908                 :            :   // Finish computing node field output averages in internal nodes
     909                 :         26 :   auto npoin = d->Coord()[0].size();
     910                 :            :   auto& gid = std::get< 1 >( d->Chunk() );
     911         [ +  + ]:       4506 :   for (std::size_t p=0; p<npoin; ++p) {
     912 [ +  - ][ +  + ]:       4480 :     if (!chbnd(gid[p])) {
     913                 :       1768 :       auto n = static_cast< tk::real >( esup.second[p+1] - esup.second[p] );
     914         [ +  + ]:      17680 :       for (std::size_t i=0; i<m_uNodefields.nprop(); ++i)
     915                 :      15912 :         m_uNodefields(p,i) /= n;
     916         [ +  + ]:      10608 :       for (std::size_t i=0; i<m_pNodefields.nprop(); ++i)
     917                 :       8840 :         m_pNodefields(p,i) /= n;
     918                 :            :     }
     919                 :            :   }
     920                 :            : 
     921                 :            :   // Collect field output from numerical solution requested by user
     922                 :         26 :   auto elemfields = numericFieldOutput( m_uElemfields, tk::Centering::ELEM,
     923 [ +  - ][ +  - ]:         52 :     g_fvpde[Disc()->MeshId()].OutVarFn(), m_pElemfields );
                 [ +  - ]
     924                 :         26 :   auto nodefields = numericFieldOutput( m_uNodefields, tk::Centering::NODE,
     925 [ +  - ][ +  - ]:         78 :     g_fvpde[Disc()->MeshId()].OutVarFn(), m_pNodefields );
         [ +  - ][ +  - ]
     926                 :            : 
     927                 :            :   // Collect field output from analytical solutions (if exist)
     928                 :            :   const auto& coord = d->Coord();
     929         [ +  - ]:         26 :   auto geoElem = tk::genGeoElemTet( inpoel, coord );
     930         [ +  - ]:         26 :   auto t = Disc()->T();
     931         [ +  - ]:         26 :   analyticFieldOutput( g_fvpde[d->MeshId()], tk::Centering::ELEM,
     932 [ +  - ][ +  - ]:        104 :     geoElem.extract_comp(1), geoElem.extract_comp(2), geoElem.extract_comp(3),
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ -  - ][ -  - ]
     933                 :            :     t, elemfields );
     934         [ +  - ]:         26 :   analyticFieldOutput( g_fvpde[d->MeshId()], tk::Centering::NODE, coord[0],
     935                 :            :     coord[1], coord[2], t, nodefields );
     936                 :            : 
     937                 :            :   // Add sound speed vector
     938 [ +  - ][ -  - ]:         26 :   std::vector< tk::real > soundspd(nelem, 0.0);
     939         [ +  - ]:         26 :   g_fvpde[d->MeshId()].soundspeed(nelem, m_u, m_p, soundspd);
     940         [ +  - ]:         26 :   elemfields.push_back(soundspd);
     941                 :            : 
     942                 :            :   // Add source flag array to element-centered field output
     943 [ +  - ][ -  - ]:         26 :   std::vector< tk::real > srcFlag( begin(m_srcFlag), end(m_srcFlag) );
     944                 :            :   // Here m_srcFlag has a size of m_u.nunk() which is the number of the
     945                 :            :   // elements within this partition (nelem) plus the ghost partition cells.
     946                 :            :   // For the purpose of output, we only need the solution data within this
     947                 :            :   // partition. Therefore, resizing it to nelem removes the extra partition
     948                 :            :   // boundary allocations in the srcFlag vector. Since the code assumes that
     949                 :            :   // the boundary elements are on the top, the resize operation keeps the lower
     950                 :            :   // portion.
     951         [ +  - ]:         26 :   srcFlag.resize( nelem );
     952         [ +  - ]:         26 :   elemfields.push_back( srcFlag );
     953                 :            : 
     954                 :            :   // Query fields names requested by user
     955         [ +  - ]:         52 :   auto elemfieldnames = numericFieldNames( tk::Centering::ELEM );
     956         [ +  - ]:         52 :   auto nodefieldnames = numericFieldNames( tk::Centering::NODE );
     957                 :            : 
     958                 :            :   // Collect field output names for analytical solutions
     959         [ +  - ]:         26 :   analyticFieldNames( g_fvpde[d->MeshId()], tk::Centering::ELEM, elemfieldnames );
     960         [ +  - ]:         26 :   analyticFieldNames( g_fvpde[d->MeshId()], tk::Centering::NODE, nodefieldnames );
     961                 :            : 
     962         [ +  - ]:         26 :   elemfieldnames.push_back( "sound speed" );
     963         [ +  - ]:         26 :   elemfieldnames.push_back( "src_flag" );
     964                 :            : 
     965                 :            :   Assert( elemfieldnames.size() == elemfields.size(), "Size mismatch" );
     966                 :            :   Assert( nodefieldnames.size() == nodefields.size(), "Size mismatch" );
     967                 :            : 
     968                 :            :   // Collect surface output names
     969         [ +  - ]:         52 :   auto surfnames = g_fvpde[d->MeshId()].surfNames();
     970                 :            : 
     971                 :            :   // Collect surface field solution
     972         [ +  - ]:         26 :   const auto& fd = myGhosts()->m_fd;
     973         [ +  - ]:         52 :   auto elemsurfs = g_fvpde[d->MeshId()].surfOutput(fd, m_u, m_p);
     974                 :            : 
     975                 :            :   // Output chare mesh and fields metadata to file
     976         [ +  - ]:         26 :   const auto& triinpoel = tk::remap( fd.Triinpoel(), d->Gid() );
     977 [ +  - ][ +  - ]:         78 :   d->write( inpoel, d->Coord(), fd.Bface(), {},
         [ +  - ][ +  - ]
         [ -  - ][ -  - ]
     978         [ +  - ]:         52 :             tk::remap( triinpoel, lid ), elemfieldnames, nodefieldnames,
     979                 :            :             surfnames, {}, elemfields, nodefields, elemsurfs, {}, c );
     980                 :         26 : }
     981                 :            : 
     982                 :            : void
     983                 :         72 : FV::comnodeout( const std::vector< std::size_t >& gid,
     984                 :            :                 const std::vector< std::size_t >& nesup,
     985                 :            :                 const std::vector< std::vector< tk::real > >& Lu,
     986                 :            :                 const std::vector< std::vector< tk::real > >& Lp )
     987                 :            : // *****************************************************************************
     988                 :            : //  Receive chare-boundary nodal solution (for field output) contributions from
     989                 :            : //  neighboring chares
     990                 :            : //! \param[in] gid Global mesh node IDs at which we receive contributions
     991                 :            : //! \param[in] nesup Number of elements surrounding points
     992                 :            : //! \param[in] Lu Partial contributions of solution nodal fields to
     993                 :            : //!   chare-boundary nodes
     994                 :            : //! \param[in] Lp Partial contributions of primitive quantity nodal fields to
     995                 :            : //!   chare-boundary nodes
     996                 :            : // *****************************************************************************
     997                 :            : {
     998                 :            :   Assert( gid.size() == nesup.size(), "Size mismatch" );
     999                 :            :   Assert(Lu.size() == m_uNodefields.nprop(), "Fields size mismatch");
    1000                 :            :   Assert(Lp.size() == m_pNodefields.nprop(), "Fields size mismatch");
    1001         [ +  + ]:        720 :   for (std::size_t f=0; f<Lu.size(); ++f)
    1002                 :            :     Assert( gid.size() == Lu[f].size(), "Size mismatch" );
    1003         [ +  + ]:        432 :   for (std::size_t f=0; f<Lp.size(); ++f)
    1004                 :            :     Assert( gid.size() == Lp[f].size(), "Size mismatch" );
    1005                 :            : 
    1006         [ +  + ]:       4236 :   for (std::size_t i=0; i<gid.size(); ++i) {
    1007                 :            :     auto& nfu = m_uNodefieldsc[ gid[i] ];
    1008                 :       4164 :     nfu.first.resize( Lu.size() );
    1009         [ +  + ]:      41640 :     for (std::size_t f=0; f<Lu.size(); ++f) nfu.first[f] += Lu[f][i];
    1010                 :       4164 :     nfu.second += nesup[i];
    1011                 :       4164 :     auto& nfp = m_pNodefieldsc[ gid[i] ];
    1012                 :       4164 :     nfp.first.resize( Lp.size() );
    1013         [ +  + ]:      24984 :     for (std::size_t f=0; f<Lp.size(); ++f) nfp.first[f] += Lp[f][i];
    1014                 :       4164 :     nfp.second += nesup[i];
    1015                 :            :   }
    1016                 :            : 
    1017                 :            :   // When we have heard from all chares we communicate with, this chare is done
    1018         [ +  + ]:         72 :   if (++m_nnod == Disc()->NodeCommMap().size()) {
    1019                 :         24 :     m_nnod = 0;
    1020                 :         24 :     comnodeout_complete();
    1021                 :            :   }
    1022                 :         72 : }
    1023                 :            : 
    1024                 :            : void
    1025                 :       1618 : FV::stage()
    1026                 :            : // *****************************************************************************
    1027                 :            : // Evaluate whether to continue with next time step stage
    1028                 :            : // *****************************************************************************
    1029                 :            : {
    1030                 :            :   // Increment Runge-Kutta stage counter
    1031                 :       1618 :   ++m_stage;
    1032                 :            : 
    1033                 :            :   // if not all Runge-Kutta stages complete, continue to next time stage,
    1034                 :            :   // otherwise prepare for nodal field output
    1035         [ +  + ]:       1618 :   if (m_stage < m_nrk)
    1036                 :        809 :     next();
    1037                 :            :   else
    1038 [ +  - ][ +  - ]:       2427 :     startFieldOutput( CkCallback(CkIndex_FV::step(), thisProxy[thisIndex]) );
         [ -  + ][ -  - ]
    1039                 :       1618 : }
    1040                 :            : 
    1041                 :            : void
    1042                 :        512 : FV::evalLB( int nrestart )
    1043                 :            : // *****************************************************************************
    1044                 :            : // Evaluate whether to do load balancing
    1045                 :            : //! \param[in] nrestart Number of times restarted
    1046                 :            : // *****************************************************************************
    1047                 :            : {
    1048                 :        512 :   auto d = Disc();
    1049                 :            : 
    1050                 :            :   // Detect if just returned from a checkpoint and if so, zero timers and flag
    1051         [ -  + ]:        512 :   if (d->restarted( nrestart )) m_finished = 0;
    1052                 :            : 
    1053                 :        512 :   const auto lbfreq = g_inputdeck.get< tag::cmd, tag::lbfreq >();
    1054                 :        512 :   const auto nonblocking = g_inputdeck.get< tag::cmd, tag::nonblocking >();
    1055                 :            : 
    1056                 :            :   // Load balancing if user frequency is reached or after the second time-step
    1057 [ -  + ][ -  - ]:        512 :   if ( (d->It()) % lbfreq == 0 || d->It() == 2 ) {
    1058                 :            : 
    1059                 :        512 :     AtSync();
    1060         [ -  + ]:        512 :     if (nonblocking) next();
    1061                 :            : 
    1062                 :            :   } else {
    1063                 :            : 
    1064                 :          0 :     next();
    1065                 :            : 
    1066                 :            :   }
    1067                 :        512 : }
    1068                 :            : 
    1069                 :            : void
    1070                 :        512 : FV::evalRestart()
    1071                 :            : // *****************************************************************************
    1072                 :            : // Evaluate whether to save checkpoint/restart
    1073                 :            : // *****************************************************************************
    1074                 :            : {
    1075                 :        512 :   auto d = Disc();
    1076                 :            : 
    1077                 :        512 :   const auto rsfreq = g_inputdeck.get< tag::cmd, tag::rsfreq >();
    1078                 :        512 :   const auto benchmark = g_inputdeck.get< tag::cmd, tag::benchmark >();
    1079                 :            : 
    1080 [ +  + ][ -  + ]:        512 :   if ( !benchmark && (d->It()) % rsfreq == 0 ) {
    1081                 :            : 
    1082                 :          0 :     std::vector< std::size_t > meshdata{ /* finished = */ 0, d->MeshId() };
    1083         [ -  - ]:          0 :     contribute( meshdata, CkReduction::nop,
    1084 [ -  - ][ -  - ]:          0 :       CkCallback(CkReductionTarget(Transporter,checkpoint), d->Tr()) );
                 [ -  - ]
    1085                 :            : 
    1086                 :            :   } else {
    1087                 :            : 
    1088                 :        512 :     evalLB( /* nrestart = */ -1 );
    1089                 :            : 
    1090                 :            :   }
    1091                 :        512 : }
    1092                 :            : 
    1093                 :            : void
    1094                 :        809 : FV::step()
    1095                 :            : // *****************************************************************************
    1096                 :            : // Evaluate wether to continue with next time step
    1097                 :            : // *****************************************************************************
    1098                 :            : {
    1099                 :        809 :   auto d = Disc();
    1100                 :            : 
    1101                 :            :   // Output time history
    1102 [ +  - ][ +  - ]:        809 :   if (d->histiter() or d->histtime() or d->histrange()) {
                 [ -  + ]
    1103                 :          0 :     std::vector< std::vector< tk::real > > hist;
    1104 [ -  - ][ -  - ]:          0 :     auto h = g_fvpde[d->MeshId()].histOutput( d->Hist(), myGhosts()->m_inpoel,
    1105 [ -  - ][ -  - ]:          0 :       myGhosts()->m_coord, m_u, m_p );
    1106         [ -  - ]:          0 :     hist.insert( end(hist), begin(h), end(h) );
    1107         [ -  - ]:          0 :     d->history( std::move(hist) );
    1108                 :            :   }
    1109                 :            : 
    1110                 :            :   // Output one-liner status report to screen
    1111                 :        809 :   d->status();
    1112                 :            :   // Reset Runge-Kutta stage counter
    1113                 :        809 :   m_stage = 0;
    1114                 :            : 
    1115                 :            :   // If neither max iterations nor max time reached, continue, otherwise finish
    1116         [ +  + ]:        809 :   if (not m_finished) {
    1117                 :            : 
    1118                 :        512 :     evalRestart();
    1119                 :            :  
    1120                 :            :   } else {
    1121                 :            : 
    1122                 :        297 :     auto meshid = d->MeshId();
    1123         [ +  - ]:        594 :     d->contribute( sizeof(std::size_t), &meshid, CkReduction::nop,
    1124                 :        594 :                    CkCallback(CkReductionTarget(Transporter,finish), d->Tr()) );
    1125                 :            : 
    1126                 :            :   }
    1127                 :        809 : }
    1128                 :            : 
    1129                 :            : #include "NoWarning/fv.def.h"

Generated by: LCOV version 1.14