Quinoa all test code coverage report
Current view: top level - Inciter - FV.cpp (source / functions) Hit Total Coverage
Commit: -128-NOTFOUND Lines: 410 480 85.4 %
Date: 2025-10-21 17:21:13 Functions: 27 29 93.1 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 377 950 39.7 %

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

Generated by: LCOV version 1.14