Quinoa all test code coverage report
Current view: top level - Inciter - FV.cpp (source / functions) Hit Total Coverage
Commit: -128-NOTFOUND Lines: 416 487 85.4 %
Date: 2024-11-22 08:51:48 Functions: 28 30 93.3 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 384 968 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                 :        298 : 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                 :        298 :         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                 :        298 :   m_u( Disc()->Inpoel().size()/4,
      59                 :        596 :        g_inputdeck.get< tag::rdof >()*
      60                 :        298 :        g_inputdeck.get< tag::ncomp >() ),
      61                 :            :   m_un( m_u.nunk(), m_u.nprop() ),
      62                 :        596 :   m_p( m_u.nunk(), g_inputdeck.get< tag::rdof >()*
      63 [ +  - ][ +  - ]:        298 :     g_fvpde[Disc()->MeshId()].nprim() ),
      64                 :            :   m_lhs( m_u.nunk(),
      65                 :        298 :          g_inputdeck.get< tag::ncomp >() ),
      66                 :            :   m_rhs( m_u.nunk(), m_lhs.nprop() ),
      67         [ +  - ]:        298 :   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                 :        298 :     m_p.nprop()/g_inputdeck.get< tag::rdof >()),
      76                 :            :   m_uNodefields( m_npoin, m_lhs.nprop() ),
      77                 :            :   m_pNodefields(m_npoin,
      78                 :        298 :     m_p.nprop()/g_inputdeck.get< tag::rdof >()),
      79                 :            :   m_uNodefieldsc(),
      80                 :            :   m_pNodefieldsc(),
      81                 :            :   m_boxelems(),
      82                 :          0 :   m_srcFlag(m_u.nunk(), 0),
      83                 :            :   m_nrk(0),
      84                 :          0 :   m_dte(m_u.nunk(), 0.0),
      85 [ +  - ][ +  - ]:       2086 :   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                 :        298 :   m_nrk = 2;
      95         [ +  - ]:        298 :   m_rkcoef[0].resize(m_nrk);
      96         [ +  - ]:        298 :   m_rkcoef[1].resize(m_nrk);
      97         [ +  - ]:        298 :   if (m_nrk == 2) {
      98 [ +  - ][ +  - ]:        298 :     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 [ +  - ][ +  + ]:        596 :   if (g_inputdeck.get< tag::cmd, tag::chare >() ||
     105         [ +  + ]:        298 :       g_inputdeck.get< tag::cmd, tag::quiescence >())
     106 [ +  - ][ +  - ]:        152 :     stateProxy.ckLocalBranch()->insert( "FV", thisIndex, CkMyPe(), Disc()->It(),
         [ +  - ][ +  - ]
                 [ +  - ]
     107                 :            :                                         "FV" );
     108                 :            : 
     109                 :        298 :   usesAtSync = true;    // enable migration at AtSync
     110                 :            : 
     111                 :            :   // Enable SDAG wait for initially building the solution vector and limiting
     112         [ +  - ]:        298 :   if (m_initial) {
     113 [ +  - ][ +  - ]:        298 :     thisProxy[ thisIndex ].wait4sol();
     114 [ +  - ][ +  - ]:        298 :     thisProxy[ thisIndex ].wait4lim();
     115 [ +  - ][ +  - ]:        298 :     thisProxy[ thisIndex ].wait4nod();
     116                 :            :   }
     117                 :            : 
     118 [ +  - ][ +  - ]:        596 :   m_ghosts[thisIndex].insert(m_disc, bface, triinpoel, m_u.nunk(),
     119 [ +  - ][ +  - ]:        596 :     CkCallback(CkIndex_FV::resizeSolVectors(), thisProxy[thisIndex]));
                 [ +  - ]
     120                 :            : 
     121                 :            :   // global-sync to call doneInserting on m_ghosts
     122         [ +  - ]:        298 :   auto meshid = Disc()->MeshId();
     123         [ +  - ]:        298 :   contribute( sizeof(std::size_t), &meshid, CkReduction::nop,
     124 [ +  - ][ +  - ]:        596 :     CkCallback(CkReductionTarget(Transporter,doneInsertingGhosts),
     125         [ +  - ]:        298 :     Disc()->Tr()) );
     126                 :        298 : }
     127                 :            : 
     128                 :            : void
     129                 :        572 : 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                 :        572 :   ElemDiagnostics::registerReducers();
     140                 :        572 : }
     141                 :            : 
     142                 :            : void
     143                 :        536 : 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 [ -  + ][ -  - ]:        536 :   if (Disc()->It() == 0) Throw( "it = 0 in ResumeFromSync()" );
         [ -  - ][ -  - ]
     151                 :            : 
     152         [ +  - ]:        536 :   if (!g_inputdeck.get< tag::cmd, tag::nonblocking >()) next();
     153                 :        536 : }
     154                 :            : 
     155                 :            : void
     156                 :        298 : 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 [ +  - ][ +  - ]:        298 :   m_u.resize( myGhosts()->m_nunk );
     163 [ +  - ][ +  - ]:        298 :   m_un.resize( myGhosts()->m_nunk );
     164 [ +  - ][ +  - ]:        298 :   m_srcFlag.resize( myGhosts()->m_nunk );
     165 [ +  - ][ +  - ]:        298 :   m_p.resize( myGhosts()->m_nunk );
     166 [ +  - ][ +  - ]:        298 :   m_lhs.resize( myGhosts()->m_nunk );
     167 [ +  - ][ +  - ]:        298 :   m_rhs.resize( myGhosts()->m_nunk );
     168 [ +  - ][ +  - ]:        298 :   m_dte.resize( myGhosts()->m_nunk );
     169                 :            : 
     170                 :            :   // Size communication buffer for solution
     171 [ +  + ][ +  - ]:        894 :   for (auto& u : m_uc) u.resize( myGhosts()->m_bid.size() );
                 [ +  - ]
     172 [ +  + ][ +  - ]:        894 :   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 [ +  - ][ -  + ]:        298 :   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 [ +  - ][ +  - ]:        298 :   std::vector< std::size_t > meshdata{ myGhosts()->m_initial, Disc()->MeshId() };
                 [ +  - ]
     181         [ +  - ]:        298 :   contribute( meshdata, CkReduction::sum_ulong,
     182 [ +  - ][ +  - ]:        596 :     CkCallback(CkReductionTarget(Transporter,comfinal), Disc()->Tr()) );
                 [ +  - ]
     183                 :        298 : }
     184                 :            : 
     185                 :            : void
     186                 :        298 : FV::setup()
     187                 :            : // *****************************************************************************
     188                 :            : // Set initial conditions, generate lhs, output mesh
     189                 :            : // *****************************************************************************
     190                 :            : {
     191 [ +  - ][ +  + ]:        596 :   if (g_inputdeck.get< tag::cmd, tag::chare >() ||
     192         [ +  + ]:        298 :       g_inputdeck.get< tag::cmd, tag::quiescence >())
     193 [ +  - ][ +  - ]:        152 :     stateProxy.ckLocalBranch()->insert( "FV", thisIndex, CkMyPe(), Disc()->It(),
         [ +  - ][ +  - ]
     194                 :            :                                         "setup" );
     195                 :            : 
     196                 :        298 :   auto d = Disc();
     197                 :            : 
     198                 :            :   // Basic error checking on sizes of element geometry data and connectivity
     199 [ -  + ][ -  - ]:        298 :   Assert( myGhosts()->m_geoElem.nunk() == m_lhs.nunk(),
         [ -  - ][ -  - ]
     200                 :            :     "Size mismatch in FV::setup()" );
     201                 :            : 
     202                 :            :   // Compute left-hand side of discrete PDEs
     203                 :        298 :   lhs();
     204                 :            : 
     205                 :            :   // Determine elements inside user-defined IC box
     206                 :        596 :   g_fvpde[d->MeshId()].IcBoxElems( myGhosts()->m_geoElem,
     207                 :        298 :     myGhosts()->m_fd.Esuel().size()/4, m_boxelems );
     208                 :            : 
     209                 :            :   // Compute volume of user-defined box IC
     210         [ +  - ]:        298 :   d->boxvol( {}, {}, 0 );      // punt for now
     211                 :            : 
     212                 :            :   // Query time history field output labels from all PDEs integrated
     213                 :        298 :   const auto& hist_points = g_inputdeck.get< tag::history_output, tag::point >();
     214         [ -  + ]:        298 :   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                 :        298 : }
     221                 :            : 
     222                 :            : void
     223                 :        298 : 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                 :        298 :   auto d = Disc();
     230                 :            : 
     231                 :            :   // Store user-defined box IC volume
     232                 :        298 :   d->Boxvol() = v;
     233                 :            : 
     234                 :            :   // Set initial conditions for all PDEs
     235                 :        894 :   g_fvpde[d->MeshId()].initialize( m_lhs, myGhosts()->m_inpoel,
     236                 :        298 :     myGhosts()->m_coord, m_boxelems, d->ElemBlockId(), m_u, d->T(),
     237                 :        298 :     myGhosts()->m_fd.Esuel().size()/4 );
     238                 :        596 :   g_fvpde[d->MeshId()].updatePrimitives( m_u, m_p,
     239                 :        298 :     myGhosts()->m_fd.Esuel().size()/4 );
     240                 :            : 
     241                 :        298 :   m_un = m_u;
     242                 :            : 
     243                 :            :   // Output initial conditions to file (regardless of whether it was requested)
     244 [ +  - ][ +  - ]:        298 :   startFieldOutput( CkCallback(CkIndex_FV::start(), thisProxy[thisIndex]) );
                 [ +  - ]
     245                 :        298 : }
     246                 :            : 
     247                 :            : void
     248                 :        298 : FV::start()
     249                 :            : // *****************************************************************************
     250                 :            : //  Start time stepping
     251                 :            : // *****************************************************************************
     252                 :            : {
     253                 :            :   // Start timer measuring time stepping wall clock time
     254                 :        298 :   Disc()->Timer().zero();
     255                 :            :   // Zero grind-timer
     256                 :        298 :   Disc()->grindZero();
     257                 :            :   // Start time stepping by computing the size of the next time step)
     258                 :        298 :   next();
     259                 :        298 : }
     260                 :            : 
     261                 :            : void
     262                 :       1132 : 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 [ +  + ][ +  + ]:       1132 :   if (g_inputdeck.get< tag::cmd, tag::benchmark >() || !fieldOutput()) {
                 [ +  + ]
     270                 :            : 
     271                 :       1104 :     c.send();
     272                 :            : 
     273                 :            :   } else {
     274                 :            : 
     275                 :            :     // Optionally refine mesh for field output
     276                 :         28 :     auto d = Disc();
     277                 :            : 
     278         [ -  + ]:         28 :     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         [ +  - ]:         28 :       extractFieldOutput( {}, d->Chunk(), d->Coord(), {}, {}, d->NodeCommMap(),
     287                 :            :         {}, {}, {}, c );
     288                 :            : 
     289                 :            :     }
     290                 :            : 
     291                 :            :   }
     292                 :       1132 : }
     293                 :            : 
     294                 :            : void
     295                 :       1668 : FV::next()
     296                 :            : // *****************************************************************************
     297                 :            : // Advance equations to next time step
     298                 :            : // *****************************************************************************
     299                 :            : {
     300                 :            :   // communicate solution ghost data (if any)
     301         [ +  + ]:       1668 :   if (myGhosts()->m_sendGhost.empty())
     302                 :        108 :     comsol_complete();
     303                 :            :   else
     304 [ +  - ][ +  + ]:      16632 :     for(const auto& [cid, ghostdata] : myGhosts()->m_sendGhost) {
     305         [ +  - ]:      30144 :       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                 :      15072 :       std::size_t j = 0;
     309         [ +  + ]:     362956 :       for(const auto& i : ghostdata) {
     310 [ +  - ][ -  + ]:     347884 :         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 [ +  - ][ +  - ]:      15072 :       thisProxy[ cid ].comsol( thisIndex, tetid, u, prim );
     318                 :            :     }
     319                 :            : 
     320                 :       1668 :   ownsol_complete();
     321                 :       1668 : }
     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 [ -  + ][ -  - ]:      15072 :   Assert( u.size() == tetid.size(), "Size mismatch in FV::comsol()" );
         [ -  - ][ -  - ]
     339 [ -  + ][ -  - ]:      15072 :   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 [ +  - ][ -  + ]:     347884 :     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 [ -  + ][ -  - ]:     347884 :     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                 :         28 : 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                 :         28 :   const auto& inpoel = std::get< 0 >( chunk );
     386                 :            : 
     387                 :            :   // Evaluate element solution on incoming mesh
     388 [ +  - ][ +  - ]:         28 :   evalSolution( *Disc(), inpoel, coord, addedTets, std::vector< std::size_t>{},
     389                 :         28 :     m_u, m_p, m_uElemfields, m_pElemfields, m_uNodefields, m_pNodefields );
     390                 :            : 
     391                 :            :   // Send node fields contributions to neighbor chares
     392         [ +  + ]:         28 :   if (nodeCommMap.empty())
     393                 :          4 :     comnodeout_complete();
     394                 :            :   else {
     395                 :         24 :     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                 :        648 :         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                 :        360 :         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                 :         72 :       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         [ +  - ]:         28 :   ownnod_complete( c );
     426                 :         28 : }
     427                 :            : 
     428                 :            : void
     429                 :        298 : FV::lhs()
     430                 :            : // *****************************************************************************
     431                 :            : // Compute left-hand side of discrete transport equations
     432                 :            : // *****************************************************************************
     433                 :            : {
     434                 :        298 :   g_fvpde[Disc()->MeshId()].lhs( myGhosts()->m_geoElem, m_lhs );
     435                 :            : 
     436         [ -  + ]:        298 :   if (!m_initial) stage();
     437                 :        298 : }
     438                 :            : 
     439                 :            : void
     440                 :       1668 : FV::reco()
     441                 :            : // *****************************************************************************
     442                 :            : // Compute reconstructions
     443                 :            : // *****************************************************************************
     444                 :            : {
     445                 :       1668 :   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 [ +  - ][ +  + ]:     349552 :   for (const auto& b : myGhosts()->m_bid) {
     450 [ -  + ][ -  - ]:     347884 :     Assert( m_uc[0][b.second].size() == m_u.nprop(), "ncomp size mismatch" );
         [ -  - ][ -  - ]
     451 [ -  + ][ -  - ]:     347884 :     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         [ +  - ]:       1668 :   if (rdof > 1) {
     461                 :            :     // Reconstruct second-order solution and primitive quantities
     462                 :       3336 :     g_fvpde[Disc()->MeshId()].reconstruct( myGhosts()->m_geoElem, myGhosts()->m_fd,
     463                 :       1668 :       myGhosts()->m_esup, myGhosts()->m_inpoel, myGhosts()->m_coord, m_u, m_p );
     464                 :            :   }
     465                 :            : 
     466                 :            :   // start limiting
     467                 :       1668 :   lim();
     468                 :       1668 : }
     469                 :            : 
     470                 :            : void
     471                 :       1668 : FV::lim()
     472                 :            : // *****************************************************************************
     473                 :            : // Compute limiter function
     474                 :            : // *****************************************************************************
     475                 :            : {
     476                 :       1668 :   const auto rdof = g_inputdeck.get< tag::rdof >();
     477                 :            : 
     478         [ +  - ]:       1668 :   if (rdof > 1) {
     479                 :       3336 :     g_fvpde[Disc()->MeshId()].limit( myGhosts()->m_geoFace, myGhosts()->m_fd,
     480                 :       1668 :       myGhosts()->m_esup,
     481                 :       1668 :       myGhosts()->m_inpoel, myGhosts()->m_coord, m_srcFlag, m_u, m_p );
     482                 :            :   }
     483                 :            : 
     484                 :            :   // Send limited solution to neighboring chares
     485         [ +  + ]:       1668 :   if (myGhosts()->m_sendGhost.empty())
     486                 :        108 :     comlim_complete();
     487                 :            :   else
     488 [ +  - ][ +  + ]:      16632 :     for(const auto& [cid, ghostdata] : myGhosts()->m_sendGhost) {
     489         [ +  - ]:      30144 :       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                 :      15072 :       std::size_t j = 0;
     493         [ +  + ]:     362956 :       for(const auto& i : ghostdata) {
     494 [ +  - ][ -  + ]:     347884 :         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 [ +  - ][ +  - ]:      15072 :       thisProxy[ cid ].comlim( thisIndex, tetid, u, prim );
     502                 :            :     }
     503                 :            : 
     504                 :       1668 :   ownlim_complete();
     505                 :       1668 : }
     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 [ -  + ][ -  - ]:      15072 :   Assert( u.size() == tetid.size(), "Size mismatch in FV::comlim()" );
         [ -  - ][ -  - ]
     523 [ -  + ][ -  - ]:      15072 :   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 [ +  - ][ -  + ]:     347884 :     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 [ -  + ][ -  - ]:     347884 :     Assert( b < m_uc[1].size(), "Indexing out of bounds" );
         [ -  - ][ -  - ]
     534 [ -  + ][ -  - ]:     347884 :     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                 :       1668 : FV::dt()
     550                 :            : // *****************************************************************************
     551                 :            : // Compute time step size
     552                 :            : // *****************************************************************************
     553                 :            : {
     554         [ +  - ]:       1668 :   auto d = Disc();
     555                 :            : 
     556                 :            :   // Combine own and communicated contributions of limited solution and degrees
     557                 :            :   // of freedom in cells (if p-adaptive)
     558 [ +  - ][ +  + ]:     349552 :   for (const auto& b : myGhosts()->m_bid) {
     559 [ -  + ][ -  - ]:     347884 :     Assert( m_uc[1][b.second].size() == m_u.nprop(), "ncomp size mismatch" );
         [ -  - ][ -  - ]
     560 [ -  + ][ -  - ]:     347884 :     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                 :       1668 :   auto mindt = std::numeric_limits< tk::real >::max();
     570                 :            : 
     571         [ +  + ]:       1668 :   if (m_stage == 0)
     572                 :            :   {
     573                 :        834 :     auto const_dt = g_inputdeck.get< tag::dt >();
     574                 :        834 :     auto eps = std::numeric_limits< tk::real >::epsilon();
     575                 :            : 
     576                 :            :     // use constant dt if configured
     577         [ +  + ]:        834 :     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 [ +  - ][ +  - ]:        500 :         g_fvpde[d->MeshId()].dt( myGhosts()->m_fd, myGhosts()->m_geoFace,
     586 [ +  - ][ +  - ]:        250 :           myGhosts()->m_geoElem, m_u, m_p, myGhosts()->m_fd.Esuel().size()/4,
     587         [ +  - ]:        250 :           m_srcFlag, m_dte );
     588         [ +  - ]:        250 :       if (eqdt < mindt) mindt = eqdt;
     589                 :            : 
     590                 :            :       // time-step suppression for unsteady problems
     591                 :        250 :       tk::real coeff(1.0);
     592         [ +  - ]:        250 :       if (!g_inputdeck.get< tag::steady_state >()) {
     593         [ +  - ]:        250 :         if (d->It() < 100) coeff = 0.01 * static_cast< tk::real >(d->It());
     594                 :            :       }
     595                 :            :       else {
     596         [ -  - ]:          0 :         for (auto& edt : m_dte) edt *= g_inputdeck.get< tag::cfl >();
     597                 :            :       }
     598                 :            : 
     599                 :        250 :       mindt *= coeff * g_inputdeck.get< tag::cfl >();
     600                 :            :     }
     601                 :            :   }
     602                 :            :   else
     603                 :            :   {
     604                 :        834 :     mindt = d->Dt();
     605                 :            :   }
     606                 :            : 
     607                 :            :   // Contribute to minimum dt across all chares then advance to next step
     608         [ +  - ]:       1668 :   contribute( sizeof(tk::real), &mindt, CkReduction::min_double,
     609 [ +  - ][ +  - ]:       3336 :               CkCallback(CkReductionTarget(FV,solve), thisProxy) );
     610                 :       1668 : }
     611                 :            : 
     612                 :            : void
     613                 :       1668 : FV::solve( tk::real newdt )
     614                 :            : // *****************************************************************************
     615                 :            : // Compute right-hand side of discrete transport equations
     616                 :            : //! \param[in] newdt Size of this new time step
     617                 :            : // *****************************************************************************
     618                 :            : {
     619                 :            :   // Enable SDAG wait for building the solution vector during the next stage
     620         [ +  - ]:       1668 :   thisProxy[ thisIndex ].wait4sol();
     621         [ +  - ]:       1668 :   thisProxy[ thisIndex ].wait4lim();
     622         [ +  - ]:       1668 :   thisProxy[ thisIndex ].wait4nod();
     623                 :            : 
     624                 :       1668 :   auto d = Disc();
     625                 :       1668 :   const auto rdof = g_inputdeck.get< tag::rdof >();
     626                 :       1668 :   const auto neq = m_u.nprop()/rdof;
     627                 :            : 
     628                 :            :   // Set new time step size
     629         [ +  + ]:       1668 :   if (m_stage == 0) d->setdt( newdt );
     630                 :            : 
     631                 :            :   // Update Un
     632         [ +  + ]:       1668 :   if (m_stage == 0) m_un = m_u;
     633                 :            : 
     634                 :            :   // physical time at time-stage for computing exact source terms for
     635                 :            :   // unsteady problems
     636                 :       1668 :   tk::real physT(d->T());
     637                 :            :   // 2-stage RK
     638         [ +  - ]:       1668 :   if (m_nrk == 2) {
     639         [ +  + ]:       1668 :     if (m_stage == 1) {
     640                 :        834 :       physT += d->Dt();
     641                 :            :     }
     642                 :            :   }
     643                 :            :   // 3-stage RK
     644                 :            :   else {
     645         [ -  - ]:          0 :     if (m_stage == 1) {
     646                 :          0 :       physT += d->Dt();
     647                 :            :     }
     648         [ -  - ]:          0 :     else if (m_stage == 2) {
     649                 :          0 :       physT += 0.5*d->Dt();
     650                 :            :     }
     651                 :            :   }
     652                 :            : 
     653                 :            :   // Compute rhs
     654                 :       3336 :   g_fvpde[d->MeshId()].rhs( physT, myGhosts()->m_geoFace, myGhosts()->m_geoElem,
     655                 :       1668 :     myGhosts()->m_fd, myGhosts()->m_inpoel, myGhosts()->m_coord,
     656                 :       1668 :     d->ElemBlockId(), m_u, m_p, m_rhs, m_srcFlag );
     657                 :            : 
     658                 :            :   // Explicit time-stepping using RK3 to discretize time-derivative
     659                 :       1668 :   const auto steady = g_inputdeck.get< tag::steady_state >();
     660         [ +  + ]:     646112 :   for (std::size_t e=0; e<myGhosts()->m_nunk; ++e)
     661         [ +  + ]:    7029872 :     for (std::size_t c=0; c<neq; ++c)
     662                 :            :     {
     663                 :    6385428 :       auto dte = d->Dt();
     664         [ -  + ]:    6385428 :       if (steady) dte = m_dte[e];
     665                 :    6385428 :       auto rmark = c*rdof;
     666                 :    6385428 :       m_u(e, rmark) =  m_rkcoef[0][m_stage] * m_un(e, rmark)
     667                 :    6385428 :         + m_rkcoef[1][m_stage] * ( m_u(e, rmark)
     668                 :    6385428 :           + dte * m_rhs(e, c)/m_lhs(e, c) );
     669                 :            :       // zero out reconstructed dofs of equations using reduced dofs
     670         [ +  - ]:    6385428 :       if (rdof > 1) {
     671         [ +  + ]:   25541712 :         for (std::size_t k=1; k<rdof; ++k)
     672                 :            :         {
     673                 :   19156284 :           rmark = c*rdof+k;
     674                 :   19156284 :           m_u(e, rmark) = 0.0;
     675                 :            :         }
     676                 :            :       }
     677                 :            :     }
     678                 :            : 
     679                 :            :   // Update primitives based on the evolved solution
     680                 :       3336 :   g_fvpde[d->MeshId()].updatePrimitives( m_u, m_p,
     681                 :       1668 :     myGhosts()->m_fd.Esuel().size()/4 );
     682         [ +  - ]:       1668 :   if (!g_inputdeck.get< tag::accuracy_test >()) {
     683                 :       3336 :     g_fvpde[d->MeshId()].cleanTraceMaterial( physT, myGhosts()->m_geoElem, m_u,
     684                 :       1668 :       m_p, myGhosts()->m_fd.Esuel().size()/4 );
     685                 :            :   }
     686                 :            : 
     687         [ +  + ]:       1668 :   if (m_stage < m_nrk-1) {
     688                 :            : 
     689                 :            :     // continue with next time step stage
     690                 :        834 :     stage();
     691                 :            : 
     692                 :            :   } else {
     693                 :            : 
     694                 :            :     // Increase number of iterations and physical time
     695                 :        834 :     d->next();
     696                 :            : 
     697                 :            :     // Compute diagnostics, e.g., residuals
     698                 :       2502 :     auto diag_computed = m_diag.compute( *d,
     699 [ +  - ][ +  - ]:        834 :       m_u.nunk()-myGhosts()->m_fd.Esuel().size()/4, myGhosts()->m_geoElem,
                 [ +  - ]
     700                 :       1668 :       std::vector< std::size_t>{}, m_u, m_un );
     701                 :            : 
     702                 :            :     // Continue to mesh refinement (if configured)
     703 [ +  + ][ +  - ]:        834 :     if (!diag_computed) refine( std::vector< tk::real >( m_u.nprop(), 1.0 ) );
                 [ +  - ]
     704                 :            : 
     705                 :            :   }
     706                 :       1668 : }
     707                 :            : 
     708                 :            : void
     709                 :        834 : FV::refine( const std::vector< tk::real >& l2res )
     710                 :            : // *****************************************************************************
     711                 :            : // Optionally refine/derefine mesh
     712                 :            : //! \param[in] l2res L2-norms of the residual for each scalar component
     713                 :            : //!   computed across the whole problem
     714                 :            : // *****************************************************************************
     715                 :            : {
     716                 :        834 :   auto d = Disc();
     717                 :            : 
     718                 :            :   // Assess convergence for steady state
     719                 :        834 :   const auto steady = g_inputdeck.get< tag::steady_state >();
     720                 :        834 :   const auto residual = g_inputdeck.get< tag::residual >();
     721                 :        834 :   const auto rc = g_inputdeck.get< tag::rescomp >() - 1;
     722                 :            : 
     723                 :        834 :   bool converged(false);
     724         [ -  + ]:        834 :   if (steady) converged = l2res[rc] < residual;
     725                 :            : 
     726                 :            :   // this is the last time step if max time of max number of time steps
     727                 :            :   // reached or the residual has reached its convergence criterion
     728 [ +  + ][ -  + ]:        834 :   if (d->finished() or converged) m_finished = 1;
                 [ +  + ]
     729                 :            : 
     730                 :        834 :   auto dtref = g_inputdeck.get< tag::amr, tag::dtref >();
     731                 :        834 :   auto dtfreq = g_inputdeck.get< tag::amr, tag::dtfreq >();
     732                 :            : 
     733                 :            :   // if t>0 refinement enabled and we hit the dtref frequency
     734 [ -  + ][ -  - ]:        834 :   if (dtref && !(d->It() % dtfreq)) {   // refine
                 [ -  + ]
     735                 :            : 
     736                 :          0 :     d->startvol();
     737 [ -  - ][ -  - ]:          0 :     d->Ref()->dtref( myGhosts()->m_fd.Bface(), {},
     738                 :          0 :       tk::remap(myGhosts()->m_fd.Triinpoel(),d->Gid()) );
     739                 :          0 :     d->refined() = 1;
     740                 :            : 
     741                 :            :   } else {      // do not refine
     742                 :            : 
     743                 :        834 :     d->refined() = 0;
     744                 :        834 :     stage();
     745                 :            : 
     746                 :            :   }
     747                 :        834 : }
     748                 :            : 
     749                 :            : void
     750                 :          0 : FV::resizePostAMR(
     751                 :            :   const std::vector< std::size_t >& /*ginpoel*/,
     752                 :            :   const tk::UnsMesh::Chunk& chunk,
     753                 :            :   const tk::UnsMesh::Coords& coord,
     754                 :            :   const std::unordered_map< std::size_t, tk::UnsMesh::Edge >& /*addedNodes*/,
     755                 :            :   const std::unordered_map< std::size_t, std::size_t >& addedTets,
     756                 :            :   const std::set< std::size_t >& removedNodes,
     757                 :            :   const std::unordered_map< std::size_t, std::size_t >& amrNodeMap,
     758                 :            :   const tk::NodeCommMap& nodeCommMap,
     759                 :            :   const std::map< int, std::vector< std::size_t > >& bface,
     760                 :            :   const std::map< int, std::vector< std::size_t > >& /* bnode */,
     761                 :            :   const std::vector< std::size_t >& triinpoel,
     762                 :            :   const std::unordered_map< std::size_t, std::set< std::size_t > >& elemblockid )
     763                 :            : // *****************************************************************************
     764                 :            : //  Receive new mesh from Refiner
     765                 :            : //! \param[in] chunk New mesh chunk (connectivity and global<->local id maps)
     766                 :            : //! \param[in] coord New mesh node coordinates
     767                 :            : //! \param[in] addedTets Newly added mesh cells and their parents (local ids)
     768                 :            : //! \param[in] removedNodes Newly removed mesh node local ids
     769                 :            : //! \param[in] amrNodeMap Node id map after amr (local ids)
     770                 :            : //! \param[in] nodeCommMap New node communication map
     771                 :            : //! \param[in] bface Boundary-faces mapped to side set ids
     772                 :            : //! \param[in] triinpoel Boundary-face connectivity
     773                 :            : //! \param[in] elemblockid Local tet ids associated with mesh block ids
     774                 :            : // *****************************************************************************
     775                 :            : {
     776         [ -  - ]:          0 :   auto d = Disc();
     777                 :            : 
     778                 :            :   // Set flag that indicates that we are during time stepping
     779                 :          0 :   m_initial = 0;
     780         [ -  - ]:          0 :   myGhosts()->m_initial = 0;
     781                 :            : 
     782                 :            :   // Zero field output iteration count between two mesh refinement steps
     783                 :          0 :   d->Itf() = 0;
     784                 :            : 
     785                 :            :   // Increase number of iterations with mesh refinement
     786                 :          0 :   ++d->Itr();
     787                 :            : 
     788                 :            :   // Save old number of elements
     789         [ -  - ]:          0 :   [[maybe_unused]] auto old_nelem = myGhosts()->m_inpoel.size()/4;
     790                 :            : 
     791                 :            :   // Resize mesh data structures
     792         [ -  - ]:          0 :   d->resizePostAMR( chunk, coord, amrNodeMap, nodeCommMap, removedNodes,
     793                 :            :     elemblockid );
     794                 :            : 
     795                 :            :   // Update state
     796 [ -  - ][ -  - ]:          0 :   myGhosts()->m_inpoel = d->Inpoel();
     797 [ -  - ][ -  - ]:          0 :   myGhosts()->m_coord = d->Coord();
     798         [ -  - ]:          0 :   auto nelem = myGhosts()->m_inpoel.size()/4;
     799         [ -  - ]:          0 :   m_p.resize( nelem );
     800         [ -  - ]:          0 :   m_u.resize( nelem );
     801         [ -  - ]:          0 :   m_srcFlag.resize( nelem );
     802         [ -  - ]:          0 :   m_un.resize( nelem );
     803         [ -  - ]:          0 :   m_lhs.resize( nelem );
     804         [ -  - ]:          0 :   m_rhs.resize( nelem );
     805                 :            : 
     806 [ -  - ][ -  - ]:          0 :   myGhosts()->m_fd = FaceData( myGhosts()->m_inpoel, bface,
                 [ -  - ]
     807         [ -  - ]:          0 :     tk::remap(triinpoel,d->Lid()) );
     808                 :            : 
     809         [ -  - ]:          0 :   myGhosts()->m_geoFace =
     810 [ -  - ][ -  - ]:          0 :     tk::Fields( tk::genGeoFaceTri( myGhosts()->m_fd.Nipfac(),
     811         [ -  - ]:          0 :     myGhosts()->m_fd.Inpofa(), coord ) );
     812 [ -  - ][ -  - ]:          0 :   myGhosts()->m_geoElem = tk::Fields( tk::genGeoElemTet( myGhosts()->m_inpoel,
                 [ -  - ]
     813                 :          0 :     coord ) );
     814                 :            : 
     815 [ -  - ][ -  - ]:          0 :   myGhosts()->m_nfac = myGhosts()->m_fd.Inpofa().size()/3;
     816         [ -  - ]:          0 :   myGhosts()->m_nunk = nelem;
     817                 :          0 :   m_npoin = coord[0].size();
     818         [ -  - ]:          0 :   myGhosts()->m_bndFace.clear();
     819         [ -  - ]:          0 :   myGhosts()->m_exptGhost.clear();
     820         [ -  - ]:          0 :   myGhosts()->m_sendGhost.clear();
     821         [ -  - ]:          0 :   myGhosts()->m_ghost.clear();
     822         [ -  - ]:          0 :   myGhosts()->m_esup.clear();
     823                 :            : 
     824                 :            :   // Update solution on new mesh, P0 (cell center value) only for now
     825         [ -  - ]:          0 :   m_un = m_u;
     826         [ -  - ]:          0 :   auto pn = m_p;
     827                 :          0 :   auto unprop = m_u.nprop();
     828                 :          0 :   auto pnprop = m_p.nprop();
     829         [ -  - ]:          0 :   for (const auto& [child,parent] : addedTets) {
     830 [ -  - ][ -  - ]:          0 :     Assert( child < nelem, "Indexing out of new solution vector" );
         [ -  - ][ -  - ]
     831 [ -  - ][ -  - ]:          0 :     Assert( parent < old_nelem, "Indexing out of old solution vector" );
         [ -  - ][ -  - ]
     832 [ -  - ][ -  - ]:          0 :     for (std::size_t i=0; i<unprop; ++i) m_u(child,i) = m_un(parent,i);
                 [ -  - ]
     833 [ -  - ][ -  - ]:          0 :     for (std::size_t i=0; i<pnprop; ++i) m_p(child,i) = pn(parent,i);
                 [ -  - ]
     834                 :            :   }
     835         [ -  - ]:          0 :   m_un = m_u;
     836                 :            : 
     837                 :            :   // Resize communication buffers
     838 [ -  - ][ -  - ]:          0 :   m_ghosts[thisIndex].resizeComm();
     839                 :          0 : }
     840                 :            : 
     841                 :            : bool
     842                 :        256 : FV::fieldOutput() const
     843                 :            : // *****************************************************************************
     844                 :            : // Decide wether to output field data
     845                 :            : //! \return True if field data is output in this step
     846                 :            : // *****************************************************************************
     847                 :            : {
     848                 :        256 :   auto d = Disc();
     849                 :            : 
     850                 :            :   // Output field data
     851 [ +  + ][ +  - ]:        256 :   return d->fielditer() or d->fieldtime() or d->fieldrange() or m_finished;
         [ +  - ][ -  + ]
     852                 :            : }
     853                 :            : 
     854                 :            : bool
     855                 :         28 : FV::refinedOutput() const
     856                 :            : // *****************************************************************************
     857                 :            : // Decide if we write field output using a refined mesh
     858                 :            : //! \return True if field output will use a refined mesh
     859                 :            : // *****************************************************************************
     860                 :            : {
     861         [ -  + ]:         28 :   return g_inputdeck.get< tag::field_output, tag::refined >() &&
     862         [ -  - ]:         28 :          g_inputdeck.get< tag::scheme >() != ctr::SchemeType::FV;
     863                 :            : }
     864                 :            : 
     865                 :            : void
     866                 :         28 : FV::writeFields( CkCallback c )
     867                 :            : // *****************************************************************************
     868                 :            : // Output mesh field data
     869                 :            : //! \param[in] c Function to continue with after the write
     870                 :            : // *****************************************************************************
     871                 :            : {
     872         [ +  - ]:         28 :   auto d = Disc();
     873                 :            : 
     874                 :         28 :   const auto& inpoel = std::get< 0 >( d->Chunk() );
     875         [ +  - ]:         56 :   auto esup = tk::genEsup( inpoel, 4 );
     876                 :         28 :   auto nelem = inpoel.size() / 4;
     877                 :            : 
     878                 :            :   // Combine own and communicated contributions and finish averaging of node
     879                 :            :   // field output in chare boundary nodes
     880                 :         28 :   const auto& lid = std::get< 2 >( d->Chunk() );
     881         [ +  + ]:       2740 :   for (const auto& [g,f] : m_uNodefieldsc) {
     882 [ -  + ][ -  - ]:       2712 :     Assert( m_uNodefields.nprop() == f.first.size(), "Size mismatch" );
         [ -  - ][ -  - ]
     883         [ +  - ]:       2712 :     auto p = tk::cref_find( lid, g );
     884         [ +  + ]:      27120 :     for (std::size_t i=0; i<f.first.size(); ++i) {
     885         [ +  - ]:      24408 :       m_uNodefields(p,i) += f.first[i];
     886                 :      24408 :       m_uNodefields(p,i) /= static_cast< tk::real >(
     887         [ +  - ]:      24408 :                               esup.second[p+1] - esup.second[p] + f.second );
     888                 :            :     }
     889                 :            :   }
     890                 :         28 :   tk::destroy( m_uNodefieldsc );
     891         [ +  + ]:       2740 :   for (const auto& [g,f] : m_pNodefieldsc) {
     892 [ -  + ][ -  - ]:       2712 :     Assert( m_pNodefields.nprop() == f.first.size(), "Size mismatch" );
         [ -  - ][ -  - ]
     893         [ +  - ]:       2712 :     auto p = tk::cref_find( lid, g );
     894         [ +  + ]:      16272 :     for (std::size_t i=0; i<f.first.size(); ++i) {
     895         [ +  - ]:      13560 :       m_pNodefields(p,i) += f.first[i];
     896                 :      13560 :       m_pNodefields(p,i) /= static_cast< tk::real >(
     897         [ +  - ]:      13560 :                               esup.second[p+1] - esup.second[p] + f.second );
     898                 :            :     }
     899                 :            :   }
     900                 :         28 :   tk::destroy( m_pNodefieldsc );
     901                 :            : 
     902                 :            :   // Lambda to decide if a node (global id) is on a chare boundary of the field
     903                 :            :   // output mesh. p - global node id, return true if node is on the chare
     904                 :            :   // boundary.
     905                 :      10924 :   auto chbnd = [ this ]( std::size_t p ) {
     906                 :            :     return
     907                 :       5462 :       std::any_of( Disc()->NodeCommMap().cbegin(), Disc()->NodeCommMap().cend(),
     908         [ +  - ]:      12476 :         [&](const auto& s) { return s.second.find(p) != s.second.cend(); } );
     909                 :         28 :   };
     910                 :            : 
     911                 :            :   // Finish computing node field output averages in internal nodes
     912                 :         28 :   auto npoin = d->Coord()[0].size();
     913                 :         28 :   auto& gid = std::get< 1 >( d->Chunk() );
     914         [ +  + ]:       5490 :   for (std::size_t p=0; p<npoin; ++p) {
     915 [ +  - ][ +  + ]:       5462 :     if (!chbnd(gid[p])) {
     916                 :       2750 :       auto n = static_cast< tk::real >( esup.second[p+1] - esup.second[p] );
     917         [ +  + ]:      27500 :       for (std::size_t i=0; i<m_uNodefields.nprop(); ++i)
     918         [ +  - ]:      24750 :         m_uNodefields(p,i) /= n;
     919         [ +  + ]:      16500 :       for (std::size_t i=0; i<m_pNodefields.nprop(); ++i)
     920         [ +  - ]:      13750 :         m_pNodefields(p,i) /= n;
     921                 :            :     }
     922                 :            :   }
     923                 :            : 
     924                 :            :   // Collect field output from numerical solution requested by user
     925                 :         28 :   auto elemfields = numericFieldOutput( m_uElemfields, tk::Centering::ELEM,
     926 [ +  - ][ +  - ]:         56 :     g_fvpde[Disc()->MeshId()].OutVarFn(), m_pElemfields );
                 [ +  - ]
     927                 :         28 :   auto nodefields = numericFieldOutput( m_uNodefields, tk::Centering::NODE,
     928 [ +  - ][ +  - ]:         56 :     g_fvpde[Disc()->MeshId()].OutVarFn(), m_pNodefields );
                 [ +  - ]
     929                 :            : 
     930                 :            :   // Collect field output from analytical solutions (if exist)
     931                 :         28 :   const auto& coord = d->Coord();
     932         [ +  - ]:         56 :   auto geoElem = tk::genGeoElemTet( inpoel, coord );
     933         [ +  - ]:         28 :   auto t = Disc()->T();
     934         [ +  - ]:         28 :   analyticFieldOutput( g_fvpde[d->MeshId()], tk::Centering::ELEM,
     935 [ +  - ][ +  - ]:         56 :     geoElem.extract_comp(1), geoElem.extract_comp(2), geoElem.extract_comp(3),
                 [ +  - ]
     936                 :            :     t, elemfields );
     937         [ +  - ]:         28 :   analyticFieldOutput( g_fvpde[d->MeshId()], tk::Centering::NODE, coord[0],
     938                 :         28 :     coord[1], coord[2], t, nodefields );
     939                 :            : 
     940                 :            :   // Add sound speed vector
     941         [ +  - ]:         56 :   std::vector< tk::real > soundspd(nelem, 0.0);
     942         [ +  - ]:         28 :   g_fvpde[d->MeshId()].soundspeed(nelem, m_u, m_p, soundspd);
     943         [ +  - ]:         28 :   elemfields.push_back(soundspd);
     944                 :            : 
     945                 :            :   // Add source flag array to element-centered field output
     946         [ +  - ]:         56 :   std::vector< tk::real > srcFlag( begin(m_srcFlag), end(m_srcFlag) );
     947                 :            :   // Here m_srcFlag has a size of m_u.nunk() which is the number of the
     948                 :            :   // elements within this partition (nelem) plus the ghost partition cells.
     949                 :            :   // For the purpose of output, we only need the solution data within this
     950                 :            :   // partition. Therefore, resizing it to nelem removes the extra partition
     951                 :            :   // boundary allocations in the srcFlag vector. Since the code assumes that
     952                 :            :   // the boundary elements are on the top, the resize operation keeps the lower
     953                 :            :   // portion.
     954         [ +  - ]:         28 :   srcFlag.resize( nelem );
     955         [ +  - ]:         28 :   elemfields.push_back( srcFlag );
     956                 :            : 
     957                 :            :   // Query fields names requested by user
     958         [ +  - ]:         56 :   auto elemfieldnames = numericFieldNames( tk::Centering::ELEM );
     959         [ +  - ]:         56 :   auto nodefieldnames = numericFieldNames( tk::Centering::NODE );
     960                 :            : 
     961                 :            :   // Collect field output names for analytical solutions
     962         [ +  - ]:         28 :   analyticFieldNames( g_fvpde[d->MeshId()], tk::Centering::ELEM, elemfieldnames );
     963         [ +  - ]:         28 :   analyticFieldNames( g_fvpde[d->MeshId()], tk::Centering::NODE, nodefieldnames );
     964                 :            : 
     965 [ +  - ][ +  - ]:         28 :   elemfieldnames.push_back( "sound speed" );
     966 [ +  - ][ +  - ]:         28 :   elemfieldnames.push_back( "src_flag" );
     967                 :            : 
     968 [ -  + ][ -  - ]:         28 :   Assert( elemfieldnames.size() == elemfields.size(), "Size mismatch" );
         [ -  - ][ -  - ]
     969 [ -  + ][ -  - ]:         28 :   Assert( nodefieldnames.size() == nodefields.size(), "Size mismatch" );
         [ -  - ][ -  - ]
     970                 :            : 
     971                 :            :   // Collect surface output names
     972         [ +  - ]:         56 :   auto surfnames = g_fvpde[d->MeshId()].surfNames();
     973                 :            : 
     974                 :            :   // Collect surface field solution
     975         [ +  - ]:         28 :   const auto& fd = myGhosts()->m_fd;
     976         [ +  - ]:         56 :   auto elemsurfs = g_fvpde[d->MeshId()].surfOutput(fd, m_u, m_p);
     977                 :            : 
     978                 :            :   // Output chare mesh and fields metadata to file
     979         [ +  - ]:         28 :   const auto& triinpoel = tk::remap( fd.Triinpoel(), d->Gid() );
     980         [ +  - ]:         84 :   d->write( inpoel, d->Coord(), fd.Bface(), {},
     981         [ +  - ]:         56 :             tk::remap( triinpoel, lid ), elemfieldnames, nodefieldnames,
     982                 :            :             surfnames, {}, elemfields, nodefields, elemsurfs, {}, c );
     983                 :         28 : }
     984                 :            : 
     985                 :            : void
     986                 :         72 : FV::comnodeout( const std::vector< std::size_t >& gid,
     987                 :            :                 const std::vector< std::size_t >& nesup,
     988                 :            :                 const std::vector< std::vector< tk::real > >& Lu,
     989                 :            :                 const std::vector< std::vector< tk::real > >& Lp )
     990                 :            : // *****************************************************************************
     991                 :            : //  Receive chare-boundary nodal solution (for field output) contributions from
     992                 :            : //  neighboring chares
     993                 :            : //! \param[in] gid Global mesh node IDs at which we receive contributions
     994                 :            : //! \param[in] nesup Number of elements surrounding points
     995                 :            : //! \param[in] Lu Partial contributions of solution nodal fields to
     996                 :            : //!   chare-boundary nodes
     997                 :            : //! \param[in] Lp Partial contributions of primitive quantity nodal fields to
     998                 :            : //!   chare-boundary nodes
     999                 :            : // *****************************************************************************
    1000                 :            : {
    1001 [ -  + ][ -  - ]:         72 :   Assert( gid.size() == nesup.size(), "Size mismatch" );
         [ -  - ][ -  - ]
    1002 [ -  + ][ -  - ]:         72 :   Assert(Lu.size() == m_uNodefields.nprop(), "Fields size mismatch");
         [ -  - ][ -  - ]
    1003 [ -  + ][ -  - ]:         72 :   Assert(Lp.size() == m_pNodefields.nprop(), "Fields size mismatch");
         [ -  - ][ -  - ]
    1004         [ +  + ]:        720 :   for (std::size_t f=0; f<Lu.size(); ++f)
    1005 [ -  + ][ -  - ]:        648 :     Assert( gid.size() == Lu[f].size(), "Size mismatch" );
         [ -  - ][ -  - ]
    1006         [ +  + ]:        432 :   for (std::size_t f=0; f<Lp.size(); ++f)
    1007 [ -  + ][ -  - ]:        360 :     Assert( gid.size() == Lp[f].size(), "Size mismatch" );
         [ -  - ][ -  - ]
    1008                 :            : 
    1009         [ +  + ]:       4236 :   for (std::size_t i=0; i<gid.size(); ++i) {
    1010                 :       4164 :     auto& nfu = m_uNodefieldsc[ gid[i] ];
    1011                 :       4164 :     nfu.first.resize( Lu.size() );
    1012         [ +  + ]:      41640 :     for (std::size_t f=0; f<Lu.size(); ++f) nfu.first[f] += Lu[f][i];
    1013                 :       4164 :     nfu.second += nesup[i];
    1014                 :       4164 :     auto& nfp = m_pNodefieldsc[ gid[i] ];
    1015                 :       4164 :     nfp.first.resize( Lp.size() );
    1016         [ +  + ]:      24984 :     for (std::size_t f=0; f<Lp.size(); ++f) nfp.first[f] += Lp[f][i];
    1017                 :       4164 :     nfp.second += nesup[i];
    1018                 :            :   }
    1019                 :            : 
    1020                 :            :   // When we have heard from all chares we communicate with, this chare is done
    1021         [ +  + ]:         72 :   if (++m_nnod == Disc()->NodeCommMap().size()) {
    1022                 :         24 :     m_nnod = 0;
    1023                 :         24 :     comnodeout_complete();
    1024                 :            :   }
    1025                 :         72 : }
    1026                 :            : 
    1027                 :            : void
    1028                 :       1668 : FV::stage()
    1029                 :            : // *****************************************************************************
    1030                 :            : // Evaluate whether to continue with next time step stage
    1031                 :            : // *****************************************************************************
    1032                 :            : {
    1033                 :            :   // Increment Runge-Kutta stage counter
    1034                 :       1668 :   ++m_stage;
    1035                 :            : 
    1036                 :            :   // if not all Runge-Kutta stages complete, continue to next time stage,
    1037                 :            :   // otherwise prepare for nodal field output
    1038         [ +  + ]:       1668 :   if (m_stage < m_nrk)
    1039                 :        834 :     next();
    1040                 :            :   else
    1041 [ +  - ][ +  - ]:        834 :     startFieldOutput( CkCallback(CkIndex_FV::step(), thisProxy[thisIndex]) );
                 [ +  - ]
    1042                 :       1668 : }
    1043                 :            : 
    1044                 :            : void
    1045                 :        536 : FV::evalLB( int nrestart )
    1046                 :            : // *****************************************************************************
    1047                 :            : // Evaluate whether to do load balancing
    1048                 :            : //! \param[in] nrestart Number of times restarted
    1049                 :            : // *****************************************************************************
    1050                 :            : {
    1051                 :        536 :   auto d = Disc();
    1052                 :            : 
    1053                 :            :   // Detect if just returned from a checkpoint and if so, zero timers and flag
    1054         [ -  + ]:        536 :   if (d->restarted( nrestart )) m_finished = 0;
    1055                 :            : 
    1056                 :        536 :   const auto lbfreq = g_inputdeck.get< tag::cmd, tag::lbfreq >();
    1057                 :        536 :   const auto nonblocking = g_inputdeck.get< tag::cmd, tag::nonblocking >();
    1058                 :            : 
    1059                 :            :   // Load balancing if user frequency is reached or after the second time-step
    1060 [ -  + ][ -  - ]:        536 :   if ( (d->It()) % lbfreq == 0 || d->It() == 2 ) {
                 [ +  - ]
    1061                 :            : 
    1062                 :        536 :     AtSync();
    1063         [ -  + ]:        536 :     if (nonblocking) next();
    1064                 :            : 
    1065                 :            :   } else {
    1066                 :            : 
    1067                 :          0 :     next();
    1068                 :            : 
    1069                 :            :   }
    1070                 :        536 : }
    1071                 :            : 
    1072                 :            : void
    1073                 :        536 : FV::evalRestart()
    1074                 :            : // *****************************************************************************
    1075                 :            : // Evaluate whether to save checkpoint/restart
    1076                 :            : // *****************************************************************************
    1077                 :            : {
    1078                 :        536 :   auto d = Disc();
    1079                 :            : 
    1080                 :        536 :   const auto rsfreq = g_inputdeck.get< tag::cmd, tag::rsfreq >();
    1081                 :        536 :   const auto benchmark = g_inputdeck.get< tag::cmd, tag::benchmark >();
    1082                 :            : 
    1083 [ +  + ][ -  + ]:        536 :   if ( !benchmark && (d->It()) % rsfreq == 0 ) {
                 [ -  + ]
    1084                 :            : 
    1085         [ -  - ]:          0 :     std::vector< std::size_t > meshdata{ /* finished = */ 0, d->MeshId() };
    1086         [ -  - ]:          0 :     contribute( meshdata, CkReduction::nop,
    1087 [ -  - ][ -  - ]:          0 :       CkCallback(CkReductionTarget(Transporter,checkpoint), d->Tr()) );
    1088                 :            : 
    1089                 :            :   } else {
    1090                 :            : 
    1091                 :        536 :     evalLB( /* nrestart = */ -1 );
    1092                 :            : 
    1093                 :            :   }
    1094                 :        536 : }
    1095                 :            : 
    1096                 :            : void
    1097                 :        834 : FV::step()
    1098                 :            : // *****************************************************************************
    1099                 :            : // Evaluate wether to continue with next time step
    1100                 :            : // *****************************************************************************
    1101                 :            : {
    1102                 :        834 :   auto d = Disc();
    1103                 :            : 
    1104                 :            :   // Output time history
    1105 [ +  - ][ +  - ]:        834 :   if (d->histiter() or d->histtime() or d->histrange()) {
         [ -  + ][ -  + ]
    1106                 :          0 :     std::vector< std::vector< tk::real > > hist;
    1107                 :          0 :     auto h = g_fvpde[d->MeshId()].histOutput( d->Hist(), myGhosts()->m_inpoel,
    1108 [ -  - ][ -  - ]:          0 :       myGhosts()->m_coord, m_u, m_p );
                 [ -  - ]
    1109         [ -  - ]:          0 :     hist.insert( end(hist), begin(h), end(h) );
    1110         [ -  - ]:          0 :     d->history( std::move(hist) );
    1111                 :            :   }
    1112                 :            : 
    1113                 :            :   // Output one-liner status report to screen
    1114                 :        834 :   d->status();
    1115                 :            :   // Reset Runge-Kutta stage counter
    1116                 :        834 :   m_stage = 0;
    1117                 :            : 
    1118                 :            :   // If neither max iterations nor max time reached, continue, otherwise finish
    1119         [ +  + ]:        834 :   if (not m_finished) {
    1120                 :            : 
    1121                 :        536 :     evalRestart();
    1122                 :            :  
    1123                 :            :   } else {
    1124                 :            : 
    1125                 :        298 :     auto meshid = d->MeshId();
    1126         [ +  - ]:        298 :     d->contribute( sizeof(std::size_t), &meshid, CkReduction::nop,
    1127 [ +  - ][ +  - ]:        596 :                    CkCallback(CkReductionTarget(Transporter,finish), d->Tr()) );
    1128                 :            : 
    1129                 :            :   }
    1130                 :        834 : }
    1131                 :            : 
    1132                 :            : #include "NoWarning/fv.def.h"

Generated by: LCOV version 1.14