Xyst test code coverage report
Current view: top level - Inciter - LaxCG.cpp (source / functions) Hit Total Coverage
Commit: 5689ba12dc66a776d3d75f1ee48cc7d78eaa18dc Lines: 552 722 76.5 %
Date: 2024-11-22 19:02:53 Functions: 36 38 94.7 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 372 914 40.7 %

           Branch data     Line data    Source code
       1                 :            : // *****************************************************************************
       2                 :            : /*!
       3                 :            :   \file      src/Inciter/LaxCG.cpp
       4                 :            :   \copyright 2012-2015 J. Bakosi,
       5                 :            :              2016-2018 Los Alamos National Security, LLC.,
       6                 :            :              2019-2021 Triad National Security, LLC.,
       7                 :            :              2022-2024 J. Bakosi
       8                 :            :              All rights reserved. See the LICENSE file for details.
       9                 :            :   \brief     LaxCG: Time-derivative preconditioning for all Ma
      10                 :            :   \see       Luo, Baum, Lohner, "Extension of Harten-Lax-van Leer Scheme for
      11                 :            :              Flows at All Speeds", AIAA Journal, Vol. 43, No. 6, 2005
      12                 :            :   \see       Weiss & Smith, "Preconditioning Applied to Variable and Constant
      13                 :            :              Density Time-Accurate Flows on Unstructured Meshes", AIAA Journal,
      14                 :            :              Vol. 33, No. 11, 1995, pp. 2050-2057.
      15                 :            : */
      16                 :            : // *****************************************************************************
      17                 :            : 
      18                 :            : #include "XystBuildConfig.hpp"
      19                 :            : #include "LaxCG.hpp"
      20                 :            : #include "Vector.hpp"
      21                 :            : #include "Reader.hpp"
      22                 :            : #include "ContainerUtil.hpp"
      23                 :            : #include "UnsMesh.hpp"
      24                 :            : #include "ExodusIIMeshWriter.hpp"
      25                 :            : #include "InciterConfig.hpp"
      26                 :            : #include "DerivedData.hpp"
      27                 :            : #include "Discretization.hpp"
      28                 :            : #include "DiagReducer.hpp"
      29                 :            : #include "IntegralReducer.hpp"
      30                 :            : #include "Integrals.hpp"
      31                 :            : #include "Refiner.hpp"
      32                 :            : #include "Reorder.hpp"
      33                 :            : #include "Around.hpp"
      34                 :            : #include "Lax.hpp"
      35                 :            : #include "Problems.hpp"
      36                 :            : #include "EOS.hpp"
      37                 :            : #include "BC.hpp"
      38                 :            : 
      39                 :            : namespace inciter {
      40                 :            : 
      41                 :            : extern ctr::Config g_cfg;
      42                 :            : 
      43                 :            : static CkReduction::reducerType IntegralsMerger;
      44                 :            : 
      45                 :            : //! Runge-Kutta coefficients
      46                 :            : static const std::array< tk::real, 3 > rkcoef{{ 1.0/3.0, 1.0/2.0, 1.0 }};
      47                 :            : 
      48                 :            : } // inciter::
      49                 :            : 
      50                 :            : using inciter::g_cfg;
      51                 :            : using inciter::LaxCG;
      52                 :            : 
      53                 :        316 : LaxCG::LaxCG( const CProxy_Discretization& disc,
      54                 :            :               const std::map< int, std::vector< std::size_t > >& bface,
      55                 :            :               const std::map< int, std::vector< std::size_t > >& bnode,
      56                 :        316 :               const std::vector< std::size_t >& triinpoel ) :
      57                 :            :   m_disc( disc ),
      58                 :        316 :   m_nrhs( 0 ),
      59                 :        316 :   m_nnorm( 0 ),
      60                 :        316 :   m_nbpint( 0 ),
      61                 :        316 :   m_nbeint( 0 ),
      62                 :        316 :   m_ndeint( 0 ),
      63         [ +  - ]:        316 :   m_ngrad( 0 ),
      64                 :            :   m_bnode( bnode ),
      65                 :            :   m_bface( bface ),
      66 [ +  - ][ +  - ]:        316 :   m_triinpoel( tk::remap( triinpoel, Disc()->Lid() ) ),
      67         [ +  - ]:        316 :   m_u( Disc()->Gid().size(), g_cfg.get< tag::problem_ncomp >() ),
      68                 :            :   m_un( m_u.nunk(), m_u.nprop() ),
      69                 :            :   m_rhs( m_u.nunk(), m_u.nprop() ),
      70         [ +  - ]:        316 :   m_grad( m_u.nunk(), m_u.nprop()*3 ),
      71                 :        316 :   m_stage( 0 ),
      72         [ +  - ]:        316 :   m_dtp( m_u.nunk(), 0.0 ),
      73 [ +  - ][ -  - ]:        316 :   m_tp( m_u.nunk(), g_cfg.get< tag::t0 >() ),
      74         [ +  - ]:        316 :   m_finished( 0 )
      75                 :            : // *****************************************************************************
      76                 :            : //  Constructor
      77                 :            : //! \param[in] disc Discretization proxy
      78                 :            : //! \param[in] bface Boundary-faces mapped to side sets used in the input file
      79                 :            : //! \param[in] bnode Boundary-node lists mapped to side sets used in input file
      80                 :            : //! \param[in] triinpoel Boundary-face connectivity where BCs set (global ids)
      81                 :            : // *****************************************************************************
      82                 :            : {
      83                 :        316 :   usesAtSync = true;    // enable migration at AtSync
      84                 :            : 
      85         [ +  - ]:        316 :   auto d = Disc();
      86                 :            : 
      87                 :            :   // Create new local ids based on mesh locality
      88                 :            :   std::unordered_map< std::size_t, std::size_t > map;
      89                 :            :   std::size_t n = 0;
      90                 :            : 
      91 [ +  - ][ +  - ]:        316 :   auto psup = tk::genPsup( d->Inpoel(), 4, tk::genEsup( d->Inpoel(), 4 ) );
      92         [ +  + ]:      47376 :   for (std::size_t p=0; p<m_u.nunk(); ++p) {  // for each point p
      93         [ +  - ]:       1338 :     if (!map.count(p)) map[p] = n++;
      94 [ +  + ][ +  + ]:     573208 :     for (auto q : tk::Around(psup,p)) {       // for each edge p-q
      95         [ +  - ]:      45722 :       if (!map.count(q)) map[q] = n++;
      96                 :            :     }
      97                 :            :   }
      98                 :            : 
      99                 :            :   Assert( map.size() == d->Gid().size(),
     100                 :            :           "Mesh-locality reorder map size mismatch" );
     101                 :            : 
     102                 :            :   // Remap data in bound Discretization object
     103         [ +  - ]:        316 :   d->remap( map );
     104                 :            :   // Remap boundary triangle face connectivity
     105         [ +  - ]:        316 :   tk::remap( m_triinpoel, map );
     106                 :            : 
     107                 :            :   // Compute total box IC volume
     108         [ +  - ]:        316 :   d->boxvol();
     109                 :            : 
     110                 :            :   // Activate SDAG wait for initially computing integrals
     111 [ +  - ][ +  - ]:        316 :   thisProxy[ thisIndex ].wait4int();
     112                 :        632 : }
     113                 :            : 
     114                 :            : void
     115                 :      10200 : LaxCG::primitive( tk::Fields& U )
     116                 :            : // *****************************************************************************
     117                 :            : //  Convert from conservative to primitive variables
     118                 :            : //! \param[in,out] U Unknown/solution vector to convert
     119                 :            : //! \details On input U is assumed to contain the conservative variables r, ru,
     120                 :            : //!    rv, rw, rE, and  on output the primitive variables p, u, v, w, T.
     121                 :            : // *****************************************************************************
     122                 :            : {
     123                 :      10200 :   auto rgas = g_cfg.get< tag::mat_spec_gas_const >();
     124                 :            : 
     125         [ +  + ]:    2602860 :   for (std::size_t i=0; i<U.nunk(); ++i) {
     126                 :    2592660 :     auto r = U(i,0);
     127                 :    2592660 :     auto u = U(i,1)/r;
     128                 :    2592660 :     auto v = U(i,2)/r;
     129                 :    2592660 :     auto w = U(i,3)/r;
     130                 :    2592660 :     auto p = eos::pressure( U(i,4) - 0.5*r*(u*u + v*v + w*w) );
     131                 :    2592660 :     U(i,0) = p;
     132                 :    2592660 :     U(i,1) = u;
     133                 :    2592660 :     U(i,2) = v;
     134                 :    2592660 :     U(i,3) = w;
     135                 :    2592660 :     U(i,4) = p/r/rgas;
     136                 :            :   }
     137                 :      10200 : }
     138                 :            : 
     139                 :            : void
     140                 :      13600 : LaxCG::conservative( tk::Fields& U )
     141                 :            : // *****************************************************************************
     142                 :            : //  Convert from primitive to conservative variables
     143                 :            : //! \param[in,out] U Unknown/solution vector to convert
     144                 :            : //! \details On input U is assumed to contain the primitive variables p, u, v,
     145                 :            : //!    w, T and on output the conservative variables r, ru, rv, rw, rE.
     146                 :            : // *****************************************************************************
     147                 :            : {
     148                 :      13600 :   auto g = g_cfg.get< tag::mat_spec_heat_ratio >();
     149                 :      13600 :   auto rgas = g_cfg.get< tag::mat_spec_gas_const >();
     150                 :            : 
     151         [ +  + ]:    3470480 :   for (std::size_t i=0; i<U.nunk(); ++i) {
     152                 :    3456880 :     auto p = U(i,0);
     153                 :    3456880 :     auto u = U(i,1);
     154                 :    3456880 :     auto v = U(i,2);
     155                 :    3456880 :     auto w = U(i,3);
     156                 :    3456880 :     auto T = U(i,4);
     157                 :    3456880 :     auto r = p/T/rgas;
     158                 :    3456880 :     U(i,0) = r;
     159                 :    3456880 :     U(i,1) = r*u;
     160                 :    3456880 :     U(i,2) = r*v;
     161                 :    3456880 :     U(i,3) = r*w;
     162                 :    3456880 :     U(i,4) = p/(g-1.0) + 0.5*r*(u*u + v*v + w*w);
     163                 :            :   }
     164                 :      13600 : }
     165                 :            : 
     166                 :            : std::array< tk::real, 5*5 >
     167                 :    2592660 : LaxCG::precond( const tk::Fields& U, std::size_t i )
     168                 :            : // *****************************************************************************
     169                 :            : //  Compute the inverse of the time-derivative preconditioning matrix
     170                 :            : //! \param[in] U Unknown/solution vector to use
     171                 :            : //! \param[in] i Mesh point index
     172                 :            : //! \return Preconditioning matrix inverse
     173                 :            : //! \see Nishikawa, Weiss-Smith Local-Preconditioning Matrix is a Diagonal
     174                 :            : //!      Matrix in the Symmetric Form of the Euler Equations, 2021.
     175                 :            : // *****************************************************************************
     176                 :            : {
     177                 :    2592660 :   auto g = g_cfg.get< tag::mat_spec_heat_ratio >();
     178                 :    2592660 :   auto rgas = g_cfg.get< tag::mat_spec_gas_const >();
     179                 :            : 
     180                 :    2592660 :   auto p = U(i,0);
     181                 :    2592660 :   auto u = U(i,1);
     182                 :    2592660 :   auto v = U(i,2);
     183                 :    2592660 :   auto w = U(i,3);
     184                 :    2592660 :   auto T = U(i,4);
     185                 :    2592660 :   auto r = p/T/rgas;
     186                 :    2592660 :   auto cp = g*rgas/(g-1.0);
     187                 :    2592660 :   auto k = u*u + v*v + w*w;
     188                 :    2592660 :   auto vr = lax::refvel( r, p, std::sqrt(k) );
     189                 :    2592660 :   auto vr2 = vr*vr;
     190                 :    2592660 :   auto rt = -r/T;
     191                 :    2592660 :   auto H = cp*T + k/2.0;
     192                 :    2592660 :   auto theta = 1.0/vr2 - rt/r/cp;
     193                 :    2592660 :   auto coef = r*cp*theta + rt;
     194                 :            : 
     195                 :            :   return {
     196                 :    2592660 :      (rt*(H - k) + r*cp)/coef,
     197                 :    2592660 :      rt*u/coef,
     198                 :    2592660 :      rt*v/coef,
     199                 :    2592660 :      rt*w/coef,
     200                 :    2592660 :     -rt/coef,
     201                 :            : 
     202                 :    2592660 :      -u/r,
     203                 :    2592660 :     1.0/r,
     204                 :            :     0.0,
     205                 :            :     0.0,
     206                 :            :     0.0,
     207                 :            : 
     208                 :    2592660 :      -v/r,
     209                 :            :     0.0,
     210                 :            :     1.0/r,
     211                 :            :     0.0,
     212                 :            :     0.0,
     213                 :            : 
     214                 :    2592660 :      -w/r,
     215                 :            :     0.0,
     216                 :            :     0.0,
     217                 :            :     1.0/r,
     218                 :            :     0.0,
     219                 :            : 
     220                 :    2592660 :     -(theta*(H - k) - 1.0)/coef,
     221                 :    2592660 :     -theta*u/coef,
     222                 :    2592660 :     -theta*v/coef,
     223                 :    2592660 :     -theta*w/coef,
     224                 :    2592660 :      theta/coef
     225                 :    2592660 :   };
     226                 :            : }
     227                 :            : 
     228                 :            : tk::real
     229                 :     787240 : LaxCG::charvel( std::size_t i )
     230                 :            : // *****************************************************************************
     231                 :            : //  Compute characteristic velocity of the preconditioned system at a point
     232                 :            : //! \param[in] i Mesh point index
     233                 :            : //! \return Maximum eigenvalue: abs(v') + c'
     234                 :            : // *****************************************************************************
     235                 :            : {
     236                 :     787240 :   auto g = g_cfg.get< tag::mat_spec_heat_ratio >();
     237                 :     787240 :   auto rgas = g_cfg.get< tag::mat_spec_gas_const >();
     238                 :     787240 :   auto cp = g*rgas/(g-1.0);
     239                 :            : 
     240                 :     787240 :   auto r = m_u(i,0);
     241                 :     787240 :   auto u = m_u(i,1) / r;
     242                 :     787240 :   auto v = m_u(i,2) / r;
     243                 :     787240 :   auto w = m_u(i,3) / r;
     244                 :     787240 :   auto k = u*u + v*v + w*w;
     245                 :     787240 :   auto e = m_u(i,4)/r - k/2.0;
     246                 :     787240 :   auto p = eos::pressure( r*e );
     247                 :     787240 :   auto T = p/r/rgas;
     248                 :     787240 :   auto rp = r/p;
     249                 :     787240 :   auto rt = -r/T;
     250                 :     787240 :   auto vel = std::sqrt( k );
     251                 :     787240 :   auto vr = lax::refvel( r, p, vel );
     252                 :     787240 :   auto vr2 = vr*vr;
     253                 :     787240 :   auto beta = rp + rt/r/cp;
     254                 :     787240 :   auto alpha = 0.5*(1.0 - beta*vr2);
     255                 :     787240 :   auto vpri = vel*(1.0 - alpha);
     256                 :     787240 :   auto cpri = std::sqrt( alpha*alpha*k + vr2 );
     257                 :            : 
     258                 :     787240 :   return std::abs(vpri) + cpri;
     259                 :            : }
     260                 :            : 
     261                 :            : void
     262                 :        316 : LaxCG::setupBC()
     263                 :            : // *****************************************************************************
     264                 :            : // Prepare boundary condition data structures
     265                 :            : // *****************************************************************************
     266                 :            : {
     267                 :            :   // Query Dirichlet BC nodes associated to side sets
     268                 :            :   std::unordered_map< int, std::unordered_set< std::size_t > > dir;
     269         [ -  + ]:        316 :   for (const auto& s : g_cfg.get< tag::bc_dir >()) {
     270                 :            :     auto k = m_bface.find(s[0]);
     271         [ -  - ]:          0 :     if (k != end(m_bface)) {
     272         [ -  - ]:          0 :       auto& n = dir[ k->first ];
     273         [ -  - ]:          0 :       for (auto f : k->second) {
     274         [ -  - ]:          0 :         n.insert( m_triinpoel[f*3+0] );
     275         [ -  - ]:          0 :         n.insert( m_triinpoel[f*3+1] );
     276         [ -  - ]:          0 :         n.insert( m_triinpoel[f*3+2] );
     277                 :            :       }
     278                 :            :     }
     279                 :            :   }
     280                 :            : 
     281                 :            :   // Augment Dirichlet BC nodes with nodes not necessarily part of faces
     282         [ +  - ]:        316 :   const auto& lid = Disc()->Lid();
     283         [ -  + ]:        316 :   for (const auto& s : g_cfg.get< tag::bc_dir >()) {
     284                 :            :     auto k = m_bnode.find(s[0]);
     285         [ -  - ]:          0 :     if (k != end(m_bnode)) {
     286         [ -  - ]:          0 :       auto& n = dir[ k->first ];
     287 [ -  - ][ -  - ]:          0 :       for (auto g : k->second) {
     288                 :            :         n.insert( tk::cref_find(lid,g) );
     289                 :            :       }
     290                 :            :     }
     291                 :            :   }
     292                 :            : 
     293                 :            :   // Collect unique set of nodes + Dirichlet BC components mask
     294                 :            :   auto ncomp = m_u.nprop();
     295                 :        316 :   auto nmask = ncomp + 1;
     296                 :            :   const auto& dbc = g_cfg.get< tag::bc_dir >();
     297                 :            :   std::unordered_map< std::size_t, std::vector< int > > dirbcset;
     298         [ -  + ]:        316 :   for (const auto& mask : dbc) {
     299 [ -  - ][ -  - ]:          0 :     ErrChk( mask.size() == nmask, "Incorrect Dirichlet BC mask ncomp" );
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
     300                 :            :     auto n = dir.find( mask[0] );
     301         [ -  - ]:          0 :     if (n != end(dir))
     302 [ -  - ][ -  - ]:          0 :       for (auto p : n->second) {
     303                 :            :         auto& m = dirbcset[p];
     304 [ -  - ][ -  - ]:          0 :         if (m.empty()) m.resize( ncomp, 0 );
     305         [ -  - ]:          0 :         for (std::size_t c=0; c<ncomp; ++c)
     306         [ -  - ]:          0 :           if (!m[c]) m[c] = mask[c+1];  // overwrite mask if 0 -> 1
     307                 :            :       }
     308                 :            :   }
     309                 :            :   // Compile streamable list of nodes + Dirichlet BC components mask
     310         [ -  + ]:        316 :   tk::destroy( m_dirbcmasks );
     311         [ -  + ]:        316 :   for (const auto& [p,mask] : dirbcset) {
     312         [ -  - ]:          0 :     m_dirbcmasks.push_back( p );
     313         [ -  - ]:          0 :     m_dirbcmasks.insert( end(m_dirbcmasks), begin(mask), end(mask) );
     314                 :            :   }
     315 [ -  + ][ -  - ]:        316 :   ErrChk( m_dirbcmasks.size() % nmask == 0, "Dirichlet BC masks incomplete" );
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
     316                 :            : 
     317                 :            :   // Query pressure BC nodes associated to side sets
     318                 :            :   std::unordered_map< int, std::unordered_set< std::size_t > > pre;
     319         [ -  + ]:        316 :   for (const auto& ss : g_cfg.get< tag::bc_pre >()) {
     320         [ -  - ]:          0 :     for (const auto& s : ss) {
     321                 :            :       auto k = m_bface.find(s);
     322         [ -  - ]:          0 :       if (k != end(m_bface)) {
     323         [ -  - ]:          0 :         auto& n = pre[ k->first ];
     324         [ -  - ]:          0 :         for (auto f : k->second) {
     325         [ -  - ]:          0 :           n.insert( m_triinpoel[f*3+0] );
     326         [ -  - ]:          0 :           n.insert( m_triinpoel[f*3+1] );
     327         [ -  - ]:          0 :           n.insert( m_triinpoel[f*3+2] );
     328                 :            :         }
     329                 :            :       }
     330                 :            :     }
     331                 :            :   }
     332                 :            : 
     333                 :            :   // Prepare density and pressure values for pressure BC nodes
     334                 :            :   const auto& pbc_set = g_cfg.get< tag::bc_pre >();
     335         [ -  + ]:        316 :   if (!pbc_set.empty()) {
     336                 :            :     const auto& pbc_r = g_cfg.get< tag::bc_pre_density >();
     337 [ -  - ][ -  - ]:          0 :     ErrChk( pbc_r.size() == pbc_set.size(), "Pressure BC density unspecified" );
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
     338                 :            :     const auto& pbc_p = g_cfg.get< tag::bc_pre_pressure >();
     339 [ -  - ][ -  - ]:          0 :     ErrChk( pbc_p.size() == pbc_set.size(), "Pressure BC pressure unspecified" );
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
     340         [ -  - ]:          0 :     tk::destroy( m_prebcnodes );
     341         [ -  - ]:          0 :     tk::destroy( m_prebcvals );
     342         [ -  - ]:          0 :     for (const auto& [s,n] : pre) {
     343         [ -  - ]:          0 :       m_prebcnodes.insert( end(m_prebcnodes), begin(n), end(n) );
     344         [ -  - ]:          0 :       for (std::size_t p=0; p<pbc_set.size(); ++p) {
     345         [ -  - ]:          0 :         for (auto u : pbc_set[p]) {
     346         [ -  - ]:          0 :           if (s == u) {
     347         [ -  - ]:          0 :             for (std::size_t i=0; i<n.size(); ++i) {
     348         [ -  - ]:          0 :               m_prebcvals.push_back( pbc_r[p] );
     349         [ -  - ]:          0 :               m_prebcvals.push_back( pbc_p[p] );
     350                 :            :             }
     351                 :            :           }
     352                 :            :         }
     353                 :            :       }
     354                 :            :     }
     355 [ -  - ][ -  - ]:          0 :     ErrChk( m_prebcnodes.size()*2 == m_prebcvals.size(),
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
     356                 :            :             "Pressure BC data incomplete" );
     357                 :            :   }
     358                 :            : 
     359                 :            :   // Query symmetry BC nodes associated to side sets
     360                 :            :   std::unordered_map< int, std::unordered_set< std::size_t > > sym;
     361         [ +  + ]:        632 :   for (auto s : g_cfg.get< tag::bc_sym >()) {
     362                 :            :     auto k = m_bface.find(s);
     363         [ +  + ]:        316 :     if (k != end(m_bface)) {
     364         [ +  - ]:        134 :       auto& n = sym[ k->first ];
     365         [ +  + ]:      19904 :       for (auto f : k->second) {
     366         [ +  - ]:      19770 :         n.insert( m_triinpoel[f*3+0] );
     367         [ +  - ]:      19770 :         n.insert( m_triinpoel[f*3+1] );
     368         [ +  - ]:      19770 :         n.insert( m_triinpoel[f*3+2] );
     369                 :            :       }
     370                 :            :     }
     371                 :            :   }
     372                 :            : 
     373                 :            :   // Query farfield BC nodes associated to side sets
     374                 :            :   std::unordered_map< int, std::unordered_set< std::size_t > > far;
     375         [ +  + ]:        340 :   for (auto s : g_cfg.get< tag::bc_far >()) {
     376                 :            :     auto k = m_bface.find(s);
     377         [ +  + ]:         24 :     if (k != end(m_bface)) {
     378         [ +  - ]:          7 :       auto& n = far[ k->first ];
     379         [ +  + ]:        715 :       for (auto f : k->second) {
     380         [ +  - ]:        708 :         n.insert( m_triinpoel[f*3+0] );
     381         [ +  - ]:        708 :         n.insert( m_triinpoel[f*3+1] );
     382         [ +  - ]:        708 :         n.insert( m_triinpoel[f*3+2] );
     383                 :            :       }
     384                 :            :     }
     385                 :            :   }
     386                 :            : 
     387                 :            :   // Generate unique set of symmetry BC nodes
     388                 :        316 :   tk::destroy( m_symbcnodeset );
     389         [ +  + ]:        450 :   for (const auto& [s,n] : sym) m_symbcnodeset.insert( begin(n), end(n) );
     390                 :            :   // Generate unique set of farfield BC nodes
     391                 :        316 :   tk::destroy( m_farbcnodeset );
     392         [ +  + ]:        323 :   for (const auto& [s,n] : far) m_farbcnodeset.insert( begin(n), end(n) );
     393                 :            : 
     394                 :            :   // If farfield BC is set on a node, will not also set symmetry BC
     395         [ +  + ]:        804 :   for (auto i : m_farbcnodeset) m_symbcnodeset.erase(i);
     396                 :        316 : }
     397                 :            : 
     398                 :            : void
     399                 :        316 : LaxCG::feop()
     400                 :            : // *****************************************************************************
     401                 :            : // Start (re-)computing finite element domain and boundary operators
     402                 :            : // *****************************************************************************
     403                 :            : {
     404                 :        316 :   auto d = Disc();
     405                 :            : 
     406                 :            :   // Prepare boundary conditions data structures
     407                 :        316 :   setupBC();
     408                 :            : 
     409                 :            :   // Compute local contributions to boundary normals and integrals
     410                 :        316 :   bndint();
     411                 :            :   // Compute local contributions to domain edge integrals
     412                 :        316 :   domint();
     413                 :            : 
     414                 :            :   // Send boundary point normal contributions to neighbor chares
     415         [ +  + ]:        316 :   if (d->NodeCommMap().empty()) {
     416                 :          3 :     comnorm_complete();
     417                 :            :   } else {
     418         [ +  + ]:       3897 :     for (const auto& [c,nodes] : d->NodeCommMap()) {
     419                 :            :       decltype(m_bnorm) exp;
     420         [ +  + ]:      25998 :       for (auto i : nodes) {
     421         [ +  + ]:      58699 :         for (const auto& [s,b] : m_bnorm) {
     422                 :            :           auto k = b.find(i);
     423         [ +  + ]:      44265 :           if (k != end(b)) exp[s][i] = k->second;
     424                 :            :         }
     425                 :            :       }
     426 [ +  - ][ +  - ]:       7168 :       thisProxy[c].comnorm( exp );
     427                 :            :     }
     428                 :            :   }
     429                 :        316 :   ownnorm_complete();
     430                 :        316 : }
     431                 :            : 
     432                 :            : void
     433                 :        316 : LaxCG::bndint()
     434                 :            : // *****************************************************************************
     435                 :            : //! Compute local contributions to boundary normals and integrals
     436                 :            : // *****************************************************************************
     437                 :            : {
     438                 :        316 :   auto d = Disc();
     439                 :            :   const auto& coord = d->Coord();
     440                 :            :   const auto& gid = d->Gid();
     441                 :            :   const auto& x = coord[0];
     442                 :            :   const auto& y = coord[1];
     443                 :            :   const auto& z = coord[2];
     444                 :            : 
     445                 :            :   // Lambda to compute the inverse distance squared between boundary face
     446                 :            :   // centroid and boundary point. Here p is the global node id and c is the
     447                 :            :   // the boundary face centroid.
     448                 :      79794 :   auto invdistsq = [&]( const tk::real c[], std::size_t p ){
     449                 :      79794 :     return 1.0 / ( (c[0] - x[p]) * (c[0] - x[p]) +
     450                 :      79794 :                    (c[1] - y[p]) * (c[1] - y[p]) +
     451                 :      79794 :                    (c[2] - z[p]) * (c[2] - z[p]) );
     452                 :        316 :   };
     453                 :            : 
     454                 :        316 :   tk::destroy( m_bnorm );
     455                 :        316 :   tk::destroy( m_bndpoinint );
     456                 :            : 
     457         [ +  + ]:        957 :   for (const auto& [ setid, faceids ] : m_bface) { // for all side sets
     458         [ +  + ]:      27239 :     for (auto f : faceids) { // for all side set triangles
     459                 :      26598 :       const auto N = m_triinpoel.data() + f*3;
     460                 :            :       const std::array< tk::real, 3 >
     461                 :      26598 :         ba{ x[N[1]]-x[N[0]], y[N[1]]-y[N[0]], z[N[1]]-z[N[0]] },
     462                 :      26598 :         ca{ x[N[2]]-x[N[0]], y[N[2]]-y[N[0]], z[N[2]]-z[N[0]] };
     463                 :            :       auto n = tk::cross( ba, ca );
     464                 :            :       auto A2 = tk::length( n );
     465                 :      26598 :       n[0] /= A2;
     466                 :      26598 :       n[1] /= A2;
     467                 :      26598 :       n[2] /= A2;
     468                 :            :       const tk::real centroid[3] = {
     469                 :      26598 :         (x[N[0]] + x[N[1]] + x[N[2]]) / 3.0,
     470                 :      26598 :         (y[N[0]] + y[N[1]] + y[N[2]]) / 3.0,
     471                 :      26598 :         (z[N[0]] + z[N[1]] + z[N[2]]) / 3.0 };
     472         [ +  + ]:     106392 :       for (const auto& [i,j] : tk::lpoet) {
     473                 :      79794 :         auto p = N[i];
     474                 :      79794 :         tk::real r = invdistsq( centroid, p );
     475                 :            :         auto& v = m_bnorm[setid];      // associate side set id
     476                 :            :         auto& bpn = v[gid[p]];         // associate global node id of bnd pnt
     477                 :      79794 :         bpn[0] += r * n[0];            // inv.dist.sq-weighted normal
     478                 :      79794 :         bpn[1] += r * n[1];
     479                 :      79794 :         bpn[2] += r * n[2];
     480                 :      79794 :         bpn[3] += r;                   // inv.dist.sq of node from centroid
     481                 :            :         auto& b = m_bndpoinint[gid[p]];// assoc global id of bnd point
     482                 :      79794 :         b[0] += n[0] * A2 / 6.0;        // bnd-point integral
     483                 :      79794 :         b[1] += n[1] * A2 / 6.0;
     484                 :      79794 :         b[2] += n[2] * A2 / 6.0;
     485                 :            :       }
     486                 :            :     }
     487                 :            :   }
     488                 :        316 : }
     489                 :            : 
     490                 :            : void
     491                 :        316 : LaxCG::domint()
     492                 :            : // *****************************************************************************
     493                 :            : //! Compute local contributions to domain edge integrals
     494                 :            : // *****************************************************************************
     495                 :            : {
     496                 :        316 :   auto d = Disc();
     497                 :            : 
     498                 :            :   const auto& gid = d->Gid();
     499                 :            :   const auto& inpoel = d->Inpoel();
     500                 :            : 
     501                 :            :   const auto& coord = d->Coord();
     502                 :            :   const auto& x = coord[0];
     503                 :            :   const auto& y = coord[1];
     504                 :            :   const auto& z = coord[2];
     505                 :            : 
     506                 :        316 :   tk::destroy( m_domedgeint );
     507                 :            : 
     508         [ +  + ]:     192514 :   for (std::size_t e=0; e<inpoel.size()/4; ++e) {
     509                 :     192198 :     const auto N = inpoel.data() + e*4;
     510                 :            :     const std::array< tk::real, 3 >
     511                 :     192198 :       ba{{ x[N[1]]-x[N[0]], y[N[1]]-y[N[0]], z[N[1]]-z[N[0]] }},
     512                 :     192198 :       ca{{ x[N[2]]-x[N[0]], y[N[2]]-y[N[0]], z[N[2]]-z[N[0]] }},
     513                 :     192198 :       da{{ x[N[3]]-x[N[0]], y[N[3]]-y[N[0]], z[N[3]]-z[N[0]] }};
     514                 :            :     std::array< std::array< tk::real, 3 >, 4 > grad;
     515                 :     192198 :     grad[1] = tk::cross( ca, da );
     516                 :     192198 :     grad[2] = tk::cross( da, ba );
     517                 :     192198 :     grad[3] = tk::cross( ba, ca );
     518         [ +  + ]:     768792 :     for (std::size_t i=0; i<3; ++i)
     519                 :     576594 :       grad[0][i] = -grad[1][i]-grad[2][i]-grad[3][i];
     520         [ +  + ]:    1345386 :     for (const auto& [p,q] : tk::lpoed) {
     521         [ +  + ]:    1153188 :       tk::UnsMesh::Edge ed{ gid[N[p]], gid[N[q]] };
     522                 :            :       tk::real sig = 1.0;
     523         [ +  + ]:    1153188 :       if (ed[0] > ed[1]) {
     524                 :            :         std::swap( ed[0], ed[1] );
     525                 :            :         sig = -1.0;
     526                 :            :       }
     527                 :            :       auto& n = m_domedgeint[ ed ];
     528                 :    1153188 :       n[0] += sig * (grad[p][0] - grad[q][0]) / 48.0;
     529                 :    1153188 :       n[1] += sig * (grad[p][1] - grad[q][1]) / 48.0;
     530                 :    1153188 :       n[2] += sig * (grad[p][2] - grad[q][2]) / 48.0;
     531                 :            :     }
     532                 :            :   }
     533                 :        316 : }
     534                 :            : 
     535                 :            : void
     536                 :       3584 : LaxCG::comnorm( const decltype(m_bnorm)& inbnd )
     537                 :            : // *****************************************************************************
     538                 :            : // Receive contributions to boundary point normals on chare-boundaries
     539                 :            : //! \param[in] inbnd Incoming partial sums of boundary point normals
     540                 :            : // *****************************************************************************
     541                 :            : {
     542                 :            :   // Buffer up incoming boundary point normal vector contributions
     543         [ +  + ]:       6138 :   for (const auto& [s,b] : inbnd) {
     544                 :            :     auto& bndnorm = m_bnormc[s];
     545         [ +  + ]:      10534 :     for (const auto& [p,n] : b) {
     546                 :            :       auto& norm = bndnorm[p];
     547                 :       7980 :       norm[0] += n[0];
     548                 :       7980 :       norm[1] += n[1];
     549                 :       7980 :       norm[2] += n[2];
     550                 :       7980 :       norm[3] += n[3];
     551                 :            :     }
     552                 :            :   }
     553                 :            : 
     554         [ +  + ]:       3584 :   if (++m_nnorm == Disc()->NodeCommMap().size()) {
     555                 :        313 :     m_nnorm = 0;
     556                 :        313 :     comnorm_complete();
     557                 :            :   }
     558                 :       3584 : }
     559                 :            : 
     560                 :            : void
     561                 :        768 : LaxCG::registerReducers()
     562                 :            : // *****************************************************************************
     563                 :            : //  Configure Charm++ reduction types initiated from this chare array
     564                 :            : //! \details Since this is a [initnode] routine, the runtime system executes the
     565                 :            : //!   routine exactly once on every logical node early on in the Charm++ init
     566                 :            : //!   sequence. Must be static as it is called without an object. See also:
     567                 :            : //!   Section "Initializations at Program Startup" at in the Charm++ manual
     568                 :            : //!   http://charm.cs.illinois.edu/manuals/html/charm++/manual.html.
     569                 :            : // *****************************************************************************
     570                 :            : {
     571                 :        768 :   NodeDiagnostics::registerReducers();
     572                 :        768 :   IntegralsMerger = CkReduction::addReducer( integrals::mergeIntegrals );
     573                 :        768 : }
     574                 :            : 
     575                 :            : void
     576                 :            : // cppcheck-suppress unusedFunction
     577                 :       2799 : LaxCG::ResumeFromSync()
     578                 :            : // *****************************************************************************
     579                 :            : //  Return from migration
     580                 :            : //! \details This is called when load balancing (LB) completes. The presence of
     581                 :            : //!   this function does not affect whether or not we block on LB.
     582                 :            : // *****************************************************************************
     583                 :            : {
     584 [ -  + ][ -  - ]:       2799 :   if (Disc()->It() == 0) Throw( "it = 0 in ResumeFromSync()" );
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
     585                 :            : 
     586         [ +  - ]:       2799 :   if (!g_cfg.get< tag::nonblocking >()) dt();
     587                 :       2799 : }
     588                 :            : 
     589                 :            : void
     590                 :        316 : LaxCG::setup( tk::real v )
     591                 :            : // *****************************************************************************
     592                 :            : // Start setup for solution
     593                 :            : //! \param[in] v Total volume within user-specified box
     594                 :            : // *****************************************************************************
     595                 :            : {
     596                 :        316 :   auto d = Disc();
     597                 :            : 
     598                 :            :   // Store user-defined box IC volume
     599                 :        316 :   Disc()->Boxvol() = v;
     600                 :            : 
     601                 :            :   // Set initial conditions
     602                 :        316 :   problems::initialize( d->Coord(), m_u, d->T(), d->BoxNodes() );
     603                 :            : 
     604                 :            :   // Query time history field output labels from all PDEs integrated
     605         [ -  + ]:        316 :   if (!g_cfg.get< tag::histout >().empty()) {
     606                 :            :     std::vector< std::string > var
     607 [ -  - ][ -  - ]:          0 :       {"density", "xvelocity", "yvelocity", "zvelocity", "energy", "pressure"};
         [ -  - ][ -  - ]
                 [ -  - ]
     608                 :            :     auto ncomp = m_u.nprop();
     609         [ -  - ]:          0 :     for (std::size_t c=5; c<ncomp; ++c)
     610         [ -  - ]:          0 :       var.push_back( "c" + std::to_string(c-5) );
     611         [ -  - ]:          0 :     d->histheader( std::move(var) );
     612                 :          0 :   }
     613                 :            : 
     614                 :            :   // Compute finite element operators
     615                 :        316 :   feop();
     616 [ -  - ][ -  - ]:        316 : }
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
     617                 :            : 
     618                 :            : void
     619                 :        316 : LaxCG::start()
     620                 :            : // *****************************************************************************
     621                 :            : // Start time stepping
     622                 :            : // *****************************************************************************
     623                 :            : {
     624                 :            :   // Set flag that indicates that we are now during time stepping
     625                 :        316 :   Disc()->Initial( 0 );
     626                 :            :   // Start timer measuring time stepping wall clock time
     627                 :        316 :   Disc()->Timer().zero();
     628                 :            :   // Zero grind-timer
     629                 :        316 :   Disc()->grindZero();
     630                 :            :   // Continue to first time step
     631                 :        316 :   dt();
     632                 :        316 : }
     633                 :            : 
     634                 :            : void
     635                 :        316 : LaxCG::bnorm()
     636                 :            : // *****************************************************************************
     637                 :            : // Combine own and communicated portions of the boundary point normals
     638                 :            : // *****************************************************************************
     639                 :            : {
     640                 :        316 :   const auto& lid = Disc()->Lid();
     641                 :            : 
     642                 :            :   // Combine own and communicated contributions to boundary point normals
     643         [ +  + ]:        989 :   for (const auto& [s,b] : m_bnormc) {
     644                 :            :     auto& bndnorm = m_bnorm[s];
     645         [ +  + ]:       6521 :     for (const auto& [g,n] : b) {
     646                 :            :       auto& norm = bndnorm[g];
     647                 :       5848 :       norm[0] += n[0];
     648                 :       5848 :       norm[1] += n[1];
     649                 :       5848 :       norm[2] += n[2];
     650                 :       5848 :       norm[3] += n[3];
     651                 :            :     }
     652                 :            :   }
     653                 :        316 :   tk::destroy( m_bnormc );
     654                 :            : 
     655                 :            :   // Divide summed point normals by the sum of the inverse distance squared
     656         [ +  + ]:       1011 :   for (auto& [s,b] : m_bnorm) {
     657         [ +  + ]:      18715 :     for (auto& [g,n] : b) {
     658                 :      18020 :       n[0] /= n[3];
     659                 :      18020 :       n[1] /= n[3];
     660                 :      18020 :       n[2] /= n[3];
     661                 :            :       Assert( (n[0]*n[0] + n[1]*n[1] + n[2]*n[2] - 1.0) <
     662                 :            :               1.0e+3*std::numeric_limits< tk::real >::epsilon(),
     663                 :            :               "Non-unit normal" );
     664                 :            :     }
     665                 :            :   }
     666                 :            : 
     667                 :            :   // Replace global->local ids associated to boundary point normals
     668                 :            :   decltype(m_bnorm) loc;
     669         [ +  + ]:       1011 :   for (auto& [s,b] : m_bnorm) {
     670                 :            :     auto& bnd = loc[s];
     671         [ +  + ]:      18715 :     for (auto&& [g,n] : b) {
     672                 :      18020 :       bnd[ tk::cref_find(lid,g) ] = std::move(n);
     673                 :            :     }
     674                 :            :   }
     675                 :            :   m_bnorm = std::move(loc);
     676                 :        316 : }
     677                 :            : 
     678                 :            : void
     679                 :        316 : LaxCG::streamable()
     680                 :            : // *****************************************************************************
     681                 :            : // Convert integrals into streamable data structures
     682                 :            : // *****************************************************************************
     683                 :            : {
     684                 :            :   // Generate boundary element symmetry BC flags
     685                 :        316 :   m_besym.resize( m_triinpoel.size() );
     686                 :            :   std::size_t i = 0;
     687         [ +  + ]:      80110 :   for (auto p : m_triinpoel) {
     688                 :      79794 :     m_besym[i++] = static_cast< std::uint8_t >(m_symbcnodeset.count(p));
     689                 :            :   }
     690                 :            : 
     691                 :            :   // Query surface integral output nodes
     692                 :            :   std::unordered_map< int, std::vector< std::size_t > > surfintnodes;
     693                 :            :   const auto& is = g_cfg.get< tag::integout >();
     694         [ +  - ]:        316 :   std::set< int > outsets( begin(is), end(is) );
     695         [ -  + ]:        316 :   for (auto s : outsets) {
     696                 :            :     auto m = m_bface.find(s);
     697         [ -  - ]:          0 :     if (m != end(m_bface)) {
     698         [ -  - ]:          0 :       auto& n = surfintnodes[ m->first ];       // associate set id
     699         [ -  - ]:          0 :       for (auto f : m->second) {                // face ids on side set
     700         [ -  - ]:          0 :         n.push_back( m_triinpoel[f*3+0] );      // nodes on side set
     701         [ -  - ]:          0 :         n.push_back( m_triinpoel[f*3+1] );
     702         [ -  - ]:          0 :         n.push_back( m_triinpoel[f*3+2] );
     703                 :            :       }
     704                 :            :     }
     705                 :            :   }
     706 [ -  - ][ -  + ]:        316 :   for (auto& [s,n] : surfintnodes) tk::unique( n );
     707                 :            :   // Prepare surface integral data
     708                 :        316 :   tk::destroy( m_surfint );
     709         [ +  - ]:        316 :   const auto& gid = Disc()->Gid();
     710         [ -  + ]:        316 :   for (auto&& [s,n] : surfintnodes) {
     711         [ -  - ]:          0 :     auto& sint = m_surfint[s];  // associate set id
     712                 :          0 :     auto& nodes = sint.first;
     713                 :          0 :     auto& ndA = sint.second;
     714                 :            :     nodes = std::move(n);
     715         [ -  - ]:          0 :     ndA.resize( nodes.size()*3 );
     716                 :            :     std::size_t a = 0;
     717         [ -  - ]:          0 :     for (auto p : nodes) {
     718                 :            :       const auto& b = tk::cref_find( m_bndpoinint, gid[p] );
     719                 :          0 :       ndA[a*3+0] = b[0];        // store ni * dA
     720                 :          0 :       ndA[a*3+1] = b[1];
     721                 :          0 :       ndA[a*3+2] = b[2];
     722                 :          0 :       ++a;
     723                 :            :     }
     724                 :            :   }
     725                 :        316 :   tk::destroy( m_bndpoinint );
     726                 :            : 
     727                 :            :   // Generate domain superedges
     728         [ +  - ]:        316 :   domsuped();
     729                 :        316 :   tk::destroy( m_domedgeint );
     730                 :            : 
     731                 :            :   // Convert symmetry BC data to streamable data structures
     732         [ -  + ]:        316 :   tk::destroy( m_symbcnodes );
     733         [ -  + ]:        316 :   tk::destroy( m_symbcnorms );
     734         [ +  + ]:      11538 :   for (auto p : m_symbcnodeset) {
     735         [ +  + ]:      22444 :     for (const auto& s : g_cfg.get< tag::bc_sym >()) {
     736                 :            :       auto m = m_bnorm.find(s);
     737         [ +  - ]:      11222 :       if (m != end(m_bnorm)) {
     738                 :            :         auto r = m->second.find(p);
     739         [ +  - ]:      11222 :         if (r != end(m->second)) {
     740         [ +  - ]:      11222 :           m_symbcnodes.push_back( p );
     741         [ +  - ]:      11222 :           m_symbcnorms.push_back( r->second[0] );
     742         [ +  - ]:      11222 :           m_symbcnorms.push_back( r->second[1] );
     743         [ +  - ]:      11222 :           m_symbcnorms.push_back( r->second[2] );
     744                 :            :         }
     745                 :            :       }
     746                 :            :     }
     747                 :            :   }
     748                 :        316 :   tk::destroy( m_symbcnodeset );
     749                 :            : 
     750                 :            :   // Convert farfield BC data to streamable data structures
     751         [ -  + ]:        316 :   tk::destroy( m_farbcnodes );
     752         [ -  + ]:        316 :   tk::destroy( m_farbcnorms );
     753         [ +  + ]:        804 :   for (auto p : m_farbcnodeset) {
     754         [ +  + ]:        976 :     for (const auto& s : g_cfg.get< tag::bc_far >()) {
     755                 :            :       auto n = m_bnorm.find(s);
     756         [ +  - ]:        488 :       if (n != end(m_bnorm)) {
     757                 :            :         auto a = n->second.find(p);
     758         [ +  - ]:        488 :         if (a != end(n->second)) {
     759         [ +  - ]:        488 :           m_farbcnodes.push_back( p );
     760         [ +  - ]:        488 :           m_farbcnorms.push_back( a->second[0] );
     761         [ +  - ]:        488 :           m_farbcnorms.push_back( a->second[1] );
     762         [ +  - ]:        488 :           m_farbcnorms.push_back( a->second[2] );
     763                 :            :         }
     764                 :            :       }
     765                 :            :     }
     766                 :            :   }
     767                 :        316 :   tk::destroy( m_farbcnodeset );
     768                 :        316 :   tk::destroy( m_bnorm );
     769                 :        316 : }
     770                 :            : 
     771                 :            : void
     772                 :        316 : LaxCG::domsuped()
     773                 :            : // *****************************************************************************
     774                 :            : // Generate superedge-groups for domain-edge loops
     775                 :            : //! \see See Lohner, Sec. 15.1.6.2, An Introduction to Applied CFD Techniques,
     776                 :            : //!      Wiley, 2008.
     777                 :            : // *****************************************************************************
     778                 :            : {
     779                 :            :   Assert( !m_domedgeint.empty(), "No domain edges to group" );
     780                 :            : 
     781                 :            :   #ifndef NDEBUG
     782                 :            :   auto nedge = m_domedgeint.size();
     783                 :            :   #endif
     784                 :            : 
     785                 :        316 :   const auto& inpoel = Disc()->Inpoel();
     786                 :        316 :   const auto& lid = Disc()->Lid();
     787                 :        316 :   const auto& gid = Disc()->Gid();
     788                 :            : 
     789                 :            :   tk::destroy( m_dsupedge[0] );
     790                 :            :   tk::destroy( m_dsupedge[1] );
     791                 :            :   tk::destroy( m_dsupedge[2] );
     792                 :            : 
     793                 :            :   tk::destroy( m_dsupint[0] );
     794                 :            :   tk::destroy( m_dsupint[1] );
     795                 :            :   tk::destroy( m_dsupint[2] );
     796                 :            : 
     797                 :            :   tk::UnsMesh::FaceSet untri;
     798         [ +  + ]:     192514 :   for (std::size_t e=0; e<inpoel.size()/4; e++) {
     799                 :            :     std::size_t N[4] = {
     800                 :     192198 :       inpoel[e*4+0], inpoel[e*4+1], inpoel[e*4+2], inpoel[e*4+3] };
     801 [ +  - ][ +  + ]:     960990 :     for (const auto& [a,b,c] : tk::lpofa) untri.insert( { N[a], N[b], N[c] } );
     802                 :            :   }
     803                 :            : 
     804         [ +  + ]:     192514 :   for (std::size_t e=0; e<inpoel.size()/4; ++e) {
     805                 :            :     std::size_t N[4] = {
     806                 :     192198 :       inpoel[e*4+0], inpoel[e*4+1], inpoel[e*4+2], inpoel[e*4+3] };
     807                 :            :     int f = 0;
     808                 :            :     tk::real sig[6];
     809         [ +  + ]:    1345386 :     decltype(m_domedgeint)::const_iterator d[6];
     810         [ +  + ]:     600150 :     for (const auto& [p,q] : tk::lpoed) {
     811         [ +  + ]:     572444 :       tk::UnsMesh::Edge ed{ gid[N[p]], gid[N[q]] };
     812         [ +  + ]:     872915 :       sig[f] = ed[0] < ed[1] ? 1.0 : -1.0;
     813                 :     572444 :       d[f] = m_domedgeint.find( ed );
     814         [ +  + ]:     572444 :       if (d[f] == end(m_domedgeint)) break; else ++f;
     815                 :            :     }
     816         [ +  + ]:     192198 :     if (f == 6) {
     817         [ +  - ]:      27706 :       m_dsupedge[0].push_back( N[0] );
     818         [ +  - ]:      27706 :       m_dsupedge[0].push_back( N[1] );
     819         [ +  - ]:      27706 :       m_dsupedge[0].push_back( N[2] );
     820         [ +  - ]:      27706 :       m_dsupedge[0].push_back( N[3] );
     821         [ +  + ]:     138530 :       for (const auto& [a,b,c] : tk::lpofa) untri.erase( { N[a], N[b], N[c] } );
     822         [ +  + ]:     193942 :       for (int ed=0; ed<6; ++ed) {
     823         [ +  - ]:     166236 :         m_dsupint[0].push_back( sig[ed] * d[ed]->second[0] );
     824         [ +  - ]:     166236 :         m_dsupint[0].push_back( sig[ed] * d[ed]->second[1] );
     825         [ +  - ]:     166236 :         m_dsupint[0].push_back( sig[ed] * d[ed]->second[2] );
     826                 :     166236 :         m_domedgeint.erase( d[ed] );
     827                 :            :       }
     828                 :            :     }
     829                 :            :   }
     830                 :            : 
     831         [ +  + ]:     298020 :   for (const auto& N : untri) {
     832                 :            :     int f = 0;
     833                 :            :     tk::real sig[3];
     834         [ +  + ]:    1190816 :     decltype(m_domedgeint)::const_iterator d[3];
     835         [ +  + ]:     459330 :     for (const auto& [p,q] : tk::lpoet) {
     836         [ +  + ]:     446819 :       tk::UnsMesh::Edge ed{ gid[N[p]], gid[N[q]] };
     837         [ +  + ]:     673723 :       sig[f] = ed[0] < ed[1] ? 1.0 : -1.0;
     838                 :     446819 :       d[f] = m_domedgeint.find( ed );
     839         [ +  + ]:     446819 :       if (d[f] == end(m_domedgeint)) break; else ++f;
     840                 :            :     }
     841         [ +  + ]:     297704 :     if (f == 3) {
     842         [ +  - ]:      12511 :       m_dsupedge[1].push_back( N[0] );
     843         [ +  - ]:      12511 :       m_dsupedge[1].push_back( N[1] );
     844         [ +  - ]:      12511 :       m_dsupedge[1].push_back( N[2] );
     845         [ +  + ]:      50044 :       for (int ed=0; ed<3; ++ed) {
     846         [ +  - ]:      37533 :         m_dsupint[1].push_back( sig[ed] * d[ed]->second[0] );
     847         [ +  - ]:      37533 :         m_dsupint[1].push_back( sig[ed] * d[ed]->second[1] );
     848         [ +  - ]:      37533 :         m_dsupint[1].push_back( sig[ed] * d[ed]->second[2] );
     849                 :      37533 :         m_domedgeint.erase( d[ed] );
     850                 :            :       }
     851                 :            :     }
     852                 :            :   }
     853                 :            : 
     854         [ +  - ]:        316 :   m_dsupedge[2].resize( m_domedgeint.size()*2 );
     855         [ +  - ]:        316 :   m_dsupint[2].resize( m_domedgeint.size()*3 );
     856                 :            :   std::size_t k = 0;
     857         [ +  + ]:      59621 :   for (const auto& [ed,d] : m_domedgeint) {
     858                 :      59305 :     auto e = m_dsupedge[2].data() + k*2;
     859                 :      59305 :     e[0] = tk::cref_find( lid, ed[0] );
     860                 :      59305 :     e[1] = tk::cref_find( lid, ed[1] );
     861                 :      59305 :     auto i = m_dsupint[2].data() + k*3;
     862                 :      59305 :     i[0] = d[0];
     863                 :      59305 :     i[1] = d[1];
     864                 :      59305 :     i[2] = d[2];
     865                 :      59305 :     ++k;
     866                 :            :   }
     867                 :            : 
     868                 :            :   //std::cout << std::setprecision(2)
     869                 :            :   //          << "superedges: ntet:" << m_dsupedge[0].size()/4 << "(nedge:"
     870                 :            :   //          << m_dsupedge[0].size()/4*6 << ","
     871                 :            :   //          << 100.0 * static_cast< tk::real >( m_dsupedge[0].size()/4*6 ) /
     872                 :            :   //                     static_cast< tk::real >( nedge )
     873                 :            :   //          << "%) + ntri:" << m_dsupedge[1].size()/3
     874                 :            :   //          << "(nedge:" << m_dsupedge[1].size() << ","
     875                 :            :   //          << 100.0 * static_cast< tk::real >( m_dsupedge[1].size() ) /
     876                 :            :   //                     static_cast< tk::real >( nedge )
     877                 :            :   //          << "%) + nedge:"
     878                 :            :   //          << m_dsupedge[2].size()/2 << "("
     879                 :            :   //          << 100.0 * static_cast< tk::real >( m_dsupedge[2].size()/2 ) /
     880                 :            :   //                     static_cast< tk::real >( nedge )
     881                 :            :   //          << "%) = " << m_dsupedge[0].size()/4*6 + m_dsupedge[1].size() +
     882                 :            :   //             m_dsupedge[2].size()/2 << " of "<< nedge << " total edges\n";
     883                 :            : 
     884                 :            :   Assert( m_dsupedge[0].size()/4*6 + m_dsupedge[1].size() +
     885                 :            :           m_dsupedge[2].size()/2 == nedge,
     886                 :            :           "Not all edges accounted for in superedge groups" );
     887                 :        316 : }
     888                 :            : 
     889                 :            : void
     890                 :            : // cppcheck-suppress unusedFunction
     891                 :        316 : LaxCG::merge()
     892                 :            : // *****************************************************************************
     893                 :            : // Combine own and communicated portions of the integrals
     894                 :            : // *****************************************************************************
     895                 :            : {
     896                 :        316 :   auto d = Disc();
     897                 :            : 
     898                 :            :   // Combine own and communicated contributions to boundary point normals
     899                 :        316 :   bnorm();
     900                 :            : 
     901                 :            :   // Convert integrals into streamable data structures
     902                 :        316 :   streamable();
     903                 :            : 
     904                 :            :   // Enforce boundary conditions using (re-)computed boundary data
     905                 :        316 :   BC( d->T() );
     906                 :            : 
     907         [ +  - ]:        316 :   if (d->Initial()) {
     908                 :            :     // Output initial conditions to file
     909 [ +  - ][ +  - ]:        948 :     writeFields( CkCallback(CkIndex_LaxCG::start(), thisProxy[thisIndex]) );
     910                 :            :   } else {
     911                 :          0 :     feop_complete();
     912                 :            :   }
     913                 :        316 : }
     914                 :            : 
     915                 :            : void
     916                 :      10516 : LaxCG::BC( tk::real t )
     917                 :            : // *****************************************************************************
     918                 :            : // Apply boundary conditions
     919                 :            : //! \param[in] t Physical time
     920                 :            : // *****************************************************************************
     921                 :            : {
     922                 :      10516 :   auto d = Disc();
     923                 :            : 
     924                 :            :   // Apply Dirichlet BCs
     925         [ +  - ]:      10516 :   physics::dirbc( m_u, t, d->Coord(), d->BoxNodes(), m_dirbcmasks );
     926                 :            : 
     927                 :            :   // Apply symmetry BCs
     928                 :      10516 :   physics::symbc( m_u, m_symbcnodes, m_symbcnorms, /*pos=*/1 );
     929                 :            : 
     930                 :            :   // Apply farfield BCs
     931                 :      10516 :   physics::farbc( m_u, m_farbcnodes, m_farbcnorms );
     932                 :            : 
     933                 :            :   // Apply pressure BCs
     934                 :      10516 :   physics::prebc( m_u, m_prebcnodes, m_prebcvals );
     935                 :      10516 : }
     936                 :            : 
     937                 :            : void
     938                 :       3400 : LaxCG::dt()
     939                 :            : // *****************************************************************************
     940                 :            : // Compute time step size
     941                 :            : // *****************************************************************************
     942                 :            : {
     943                 :       3400 :   tk::real mindt = std::numeric_limits< tk::real >::max();
     944                 :            : 
     945                 :       3400 :   auto const_dt = g_cfg.get< tag::dt >();
     946                 :            :   auto eps = std::numeric_limits< tk::real >::epsilon();
     947                 :       3400 :   auto d = Disc();
     948                 :            : 
     949                 :            :   // use constant dt if configured
     950         [ +  + ]:       3400 :   if (std::abs(const_dt) > eps) {
     951                 :            : 
     952                 :            :     // cppcheck-suppress redundantInitialization
     953                 :       2920 :     mindt = const_dt;
     954                 :            : 
     955                 :            :   } else {
     956                 :            : 
     957                 :            :     const auto& vol = d->Vol();
     958                 :        480 :     auto cfl = g_cfg.get< tag::cfl >();
     959                 :            : 
     960         [ +  - ]:        480 :     if (g_cfg.get< tag::steady >()) {
     961                 :            : 
     962         [ +  + ]:     787720 :       for (std::size_t i=0; i<m_u.nunk(); ++i) {
     963         [ -  + ]:     787240 :         auto v = charvel( i );
     964                 :     787240 :         auto L = std::cbrt( vol[i] );
     965         [ -  + ]:     787240 :         m_dtp[i] = L / std::max( v, 1.0e-8 ) * cfl;
     966                 :            :       }
     967                 :        480 :       mindt = *std::min_element( begin(m_dtp), end(m_dtp) );
     968                 :            : 
     969                 :            :     } else {
     970                 :            : 
     971         [ -  - ]:          0 :       for (std::size_t i=0; i<m_u.nunk(); ++i) {
     972         [ -  - ]:          0 :         auto v = charvel( i );
     973                 :          0 :         auto L = std::cbrt( vol[i] );
     974 [ -  - ][ -  - ]:          0 :         auto euler_dt = L / std::max( v, 1.0e-8 );
     975                 :          0 :         mindt = std::min( mindt, euler_dt );
     976                 :            :       }
     977                 :          0 :       mindt *= cfl;
     978                 :            : 
     979                 :            :     }
     980                 :            : 
     981                 :            :   }
     982                 :            : 
     983                 :            :   // Actiavate SDAG waits for next time step stage
     984         [ +  - ]:       3400 :   thisProxy[ thisIndex ].wait4grad();
     985         [ +  - ]:       3400 :   thisProxy[ thisIndex ].wait4rhs();
     986                 :            : 
     987                 :            :   // Contribute to minimum dt across all chares and advance to next step
     988         [ +  - ]:       3400 :   contribute( sizeof(tk::real), &mindt, CkReduction::min_double,
     989                 :       3400 :               CkCallback(CkReductionTarget(LaxCG,advance), thisProxy) );
     990                 :       3400 : }
     991                 :            : 
     992                 :            : void
     993                 :       3400 : LaxCG::advance( tk::real newdt )
     994                 :            : // *****************************************************************************
     995                 :            : // Advance equations to next time step
     996                 :            : //! \param[in] newdt The smallest dt across the whole problem
     997                 :            : // *****************************************************************************
     998                 :            : {
     999                 :            :   // Set new time step size
    1000         [ +  - ]:       3400 :   if (m_stage == 0) Disc()->setdt( newdt );
    1001                 :            : 
    1002                 :       3400 :   grad();
    1003                 :       3400 : }
    1004                 :            : 
    1005                 :            : void
    1006                 :      10200 : LaxCG::grad()
    1007                 :            : // *****************************************************************************
    1008                 :            : // Compute gradients for next time step
    1009                 :            : // *****************************************************************************
    1010                 :            : {
    1011                 :      10200 :   auto d = Disc();
    1012                 :            : 
    1013                 :            :   // Convert unknowns: r,ru,rv,rw,rE -> p,u,v,w,T
    1014                 :      10200 :   primitive( m_u );
    1015                 :            : 
    1016                 :      10200 :   lax::grad( m_dsupedge, m_dsupint, d->Coord(), m_triinpoel, m_u, m_grad );
    1017                 :            : 
    1018                 :            :   // Send gradient contributions to neighbor chares
    1019         [ +  + ]:      10200 :   if (d->NodeCommMap().empty()) {
    1020                 :        120 :     comgrad_complete();
    1021                 :            :   } else {
    1022                 :            :     const auto& lid = d->Lid();
    1023         [ +  + ]:     120480 :     for (const auto& [c,n] : d->NodeCommMap()) {
    1024                 :            :       std::unordered_map< std::size_t, std::vector< tk::real > > exp;
    1025 [ +  - ][ +  + ]:    1062240 :       for (auto g : n) exp[g] = m_grad[ tk::cref_find(lid,g) ];
    1026 [ +  - ][ +  - ]:     220800 :       thisProxy[c].comgrad( exp );
    1027                 :            :     }
    1028                 :            :   }
    1029                 :      10200 :   owngrad_complete();
    1030                 :      10200 : }
    1031                 :            : 
    1032                 :            : void
    1033                 :     110400 : LaxCG::comgrad(
    1034                 :            :   const std::unordered_map< std::size_t, std::vector< tk::real > >& ingrad )
    1035                 :            : // *****************************************************************************
    1036                 :            : //  Receive contributions to node gradients on chare-boundaries
    1037                 :            : //! \param[in] ingrad Partial contributions to chare-boundary nodes. Key: 
    1038                 :            : //!   global mesh node IDs, value: contributions for all scalar components.
    1039                 :            : //! \details This function receives contributions to m_grad, which stores the
    1040                 :            : //!   gradients at mesh nodes. While m_grad stores own contributions, m_gradc
    1041                 :            : //!   collects the neighbor chare contributions during communication. This way
    1042                 :            : //!   work on m_grad and m_gradc is overlapped. The two are combined in rhs().
    1043                 :            : // *****************************************************************************
    1044                 :            : {
    1045                 :            :   using tk::operator+=;
    1046         [ +  + ]:    2014080 :   for (const auto& [g,r] : ingrad) m_gradc[g] += r;
    1047                 :            : 
    1048                 :            :   // When we have heard from all chares we communicate with, this chare is done
    1049         [ +  + ]:     110400 :   if (++m_ngrad == Disc()->NodeCommMap().size()) {
    1050                 :      10080 :     m_ngrad = 0;
    1051                 :      10080 :     comgrad_complete();
    1052                 :            :   }
    1053                 :     110400 : }
    1054                 :            : 
    1055                 :            : void
    1056                 :      10200 : LaxCG::rhs()
    1057                 :            : // *****************************************************************************
    1058                 :            : // Compute right-hand side of transport equations
    1059                 :            : // *****************************************************************************
    1060                 :            : {
    1061                 :      10200 :   auto d = Disc();
    1062                 :            :   const auto& lid = d->Lid();
    1063                 :      10200 :   const auto steady = g_cfg.get< tag::steady >();
    1064                 :            : 
    1065                 :            :   // Combine own and communicated contributions to gradients
    1066         [ +  + ]:     666840 :   for (const auto& [g,r] : m_gradc) {
    1067                 :     656640 :     auto i = tk::cref_find( lid, g );
    1068         [ +  + ]:   10506240 :     for (std::size_t c=0; c<r.size(); ++c) m_grad(i,c) += r[c];
    1069                 :            :   }
    1070                 :      10200 :   tk::destroy(m_gradc);
    1071                 :            : 
    1072                 :            :   // divide weak result in gradients by nodal volume
    1073                 :            :   const auto& vol = d->Vol();
    1074         [ +  + ]:    2602860 :   for (std::size_t p=0; p<m_grad.nunk(); ++p)
    1075         [ +  + ]:   41482560 :     for (std::size_t c=0; c<m_grad.nprop(); ++c)
    1076                 :   38889900 :       m_grad(p,c) /= vol[p];
    1077                 :            : 
    1078                 :            :   // Compute own portion of right-hand side for all equations
    1079         [ +  + ]:      10200 :   auto prev_rkcoef = m_stage == 0 ? 0.0 : rkcoef[m_stage-1];
    1080                 :            : 
    1081         [ +  + ]:      10200 :   if (steady) {
    1082         [ +  + ]:    2363160 :     for (std::size_t p=0; p<m_tp.size(); ++p) m_tp[p] += prev_rkcoef * m_dtp[p];
    1083                 :            :   }
    1084                 :            : 
    1085                 :      10200 :   lax::rhs( m_dsupedge, m_dsupint, d->Coord(), m_triinpoel, m_besym, m_grad,
    1086                 :      10200 :             m_u, d->V(), d->T(), m_tp, m_rhs );
    1087                 :            : 
    1088         [ +  + ]:      10200 :   if (steady) {
    1089         [ +  + ]:    2363160 :     for (std::size_t p=0; p<m_tp.size(); ++p) m_tp[p] -= prev_rkcoef * m_dtp[p];
    1090                 :            :   }
    1091                 :            : 
    1092                 :            :   // Communicate rhs to other chares on chare-boundary
    1093         [ +  + ]:      10200 :   if (d->NodeCommMap().empty()) {
    1094                 :        120 :     comrhs_complete();
    1095                 :            :   } else {
    1096         [ +  + ]:     120480 :     for (const auto& [c,n] : d->NodeCommMap()) {
    1097                 :            :       std::unordered_map< std::size_t, std::vector< tk::real > > exp;
    1098 [ +  - ][ +  + ]:    1062240 :       for (auto g : n) exp[g] = m_rhs[ tk::cref_find(lid,g) ];
    1099 [ +  - ][ +  - ]:     220800 :       thisProxy[c].comrhs( exp );
    1100                 :            :     }
    1101                 :            :   }
    1102                 :      10200 :   ownrhs_complete();
    1103                 :      10200 : }
    1104                 :            : 
    1105                 :            : void
    1106                 :     110400 : LaxCG::comrhs(
    1107                 :            :   const std::unordered_map< std::size_t, std::vector< tk::real > >& inrhs )
    1108                 :            : // *****************************************************************************
    1109                 :            : //  Receive contributions to right-hand side vector on chare-boundaries
    1110                 :            : //! \param[in] inrhs Partial contributions of RHS to chare-boundary nodes. Key:
    1111                 :            : //!   global mesh node IDs, value: contributions for all scalar components.
    1112                 :            : //! \details This function receives contributions to m_rhs, which stores the
    1113                 :            : //!   right hand side vector at mesh nodes. While m_rhs stores own
    1114                 :            : //!   contributions, m_rhsc collects the neighbor chare contributions during
    1115                 :            : //!   communication. This way work on m_rhs and m_rhsc is overlapped. The two
    1116                 :            : //!   are combined in solve().
    1117                 :            : // *****************************************************************************
    1118                 :            : {
    1119                 :            :   using tk::operator+=;
    1120         [ +  + ]:    2014080 :   for (const auto& [g,r] : inrhs) m_rhsc[g] += r;
    1121                 :            : 
    1122                 :            :   // When we have heard from all chares we communicate with, this chare is done
    1123         [ +  + ]:     110400 :   if (++m_nrhs == Disc()->NodeCommMap().size()) {
    1124                 :      10080 :     m_nrhs = 0;
    1125                 :      10080 :     comrhs_complete();
    1126                 :            :   }
    1127                 :     110400 : }
    1128                 :            : 
    1129                 :            : void
    1130                 :            : // cppcheck-suppress unusedFunction
    1131                 :      10200 : LaxCG::solve()
    1132                 :            : // *****************************************************************************
    1133                 :            : //  Advance systems of equations
    1134                 :            : // *****************************************************************************
    1135                 :            : {
    1136                 :      10200 :   auto d = Disc();
    1137                 :            :   const auto lid = d->Lid();
    1138                 :      10200 :   const auto steady = g_cfg.get< tag::steady >();
    1139                 :            : 
    1140                 :            :   // Combine own and communicated contributions to rhs
    1141         [ +  + ]:     666840 :   for (const auto& [g,r] : m_rhsc) {
    1142                 :     656640 :     auto i = tk::cref_find( lid, g );
    1143         [ +  + ]:    3939840 :     for (std::size_t c=0; c<r.size(); ++c) m_rhs(i,c) += r[c];
    1144                 :            :   }
    1145                 :      10200 :   tk::destroy(m_rhsc);
    1146                 :            : 
    1147                 :            :   // Update state at time n
    1148         [ +  + ]:      10200 :   if (m_stage == 0) m_un = m_u;
    1149                 :            : 
    1150                 :            :   // Advance solution
    1151                 :            :   auto dt = d->Dt();
    1152                 :            :   const auto& vol = d->Vol();
    1153                 :            :   auto ncomp = m_u.nprop();
    1154         [ +  + ]:    2602860 :   for (std::size_t i=0; i<m_u.nunk(); ++i) {
    1155         [ +  + ]:    2592660 :     if (steady) dt = m_dtp[i];
    1156         [ +  - ]:    2592660 :     auto R = -rkcoef[m_stage] * dt / vol[i];
    1157                 :            :     // flow
    1158         [ +  - ]:    2592660 :     auto P = precond( m_u, i );
    1159                 :    2592660 :     tk::real r[] = { R*m_rhs(i,0), R*m_rhs(i,1),  R*m_rhs(i,2),
    1160                 :    2592660 :                      R*m_rhs(i,3), R*m_rhs(i,4) };
    1161                 :            :     auto p = P.data();
    1162         [ +  + ]:   15555960 :     for (std::size_t c=0; c<5; ++c, p+=5) {
    1163                 :   12963300 :       m_u(i,c) = m_un(i,c)
    1164                 :   12963300 :                + p[0]*r[0] + p[1]*r[1] + p[2]*r[2] + p[3]*r[3] + p[4]*r[4];
    1165                 :            :     }
    1166                 :            :     // scalar
    1167         [ -  + ]:    2592660 :     for (std::size_t c=5; c<ncomp; ++c) m_u(i,c) = m_un(i,c) + R*m_rhs(i,c);
    1168                 :            :   }
    1169                 :            : 
    1170                 :            :   // Convert unknowns: p,u,v,w,T -> r,ru,rv,rw,rE
    1171         [ +  - ]:      10200 :   conservative( m_u );
    1172                 :            : 
    1173                 :            :   // Configure and apply scalar source to solution (if defined)
    1174         [ +  - ]:      10200 :   auto src = problems::PHYS_SRC();
    1175         [ -  + ]:      10200 :   if (src) src( d->Coord(), d->T(), m_u );
    1176                 :            : 
    1177                 :            :   // Enforce boundary conditions
    1178         [ +  - ]:      10200 :   BC( d->T() + rkcoef[m_stage] * d->Dt() );
    1179                 :            : 
    1180         [ +  + ]:      10200 :   if (m_stage < 2) {
    1181                 :            : 
    1182                 :            :     // Activate SDAG wait for next time step stage
    1183 [ +  - ][ +  - ]:       6800 :     thisProxy[ thisIndex ].wait4grad();
    1184 [ +  - ][ +  - ]:       6800 :     thisProxy[ thisIndex ].wait4rhs();
    1185                 :            : 
    1186                 :            :     // start next time step stage
    1187         [ +  - ]:       6800 :     stage();
    1188                 :            : 
    1189                 :            :   } else {
    1190                 :            : 
    1191                 :            :     // Activate SDAG waits for finishing this time step stage
    1192 [ +  - ][ +  - ]:       3400 :     thisProxy[ thisIndex ].wait4stage();
    1193                 :            :     // Compute diagnostics, e.g., residuals
    1194         [ +  - ]:       3400 :     conservative( m_un );
    1195                 :       3400 :     auto diag_iter = g_cfg.get< tag::diag_iter >();
    1196         [ +  - ]:       3400 :     auto diag = m_diag.rhocompute( *d, m_u, m_un, diag_iter );
    1197                 :            :     // Increase number of iterations and physical time
    1198         [ +  - ]:       3400 :     d->next();
    1199                 :            :     // Advance physical time for local time stepping
    1200         [ +  + ]:       3400 :     if (steady) {
    1201                 :            :       using tk::operator+=;
    1202         [ +  - ]:        480 :       m_tp += m_dtp;
    1203                 :            :     }
    1204                 :            :     // Evaluate residuals
    1205 [ -  + ][ -  - ]:       3400 :     if (!diag) evalres( std::vector< tk::real >( m_u.nprop(), 1.0 ) );
         [ -  - ][ -  - ]
    1206                 :            : 
    1207                 :            :   }
    1208                 :      10200 : }
    1209                 :            : 
    1210                 :            : void
    1211                 :       3400 : LaxCG::evalres( const std::vector< tk::real >& l2res )
    1212                 :            : // *****************************************************************************
    1213                 :            : //  Evaluate residuals
    1214                 :            : //! \param[in] l2res L2-norms of the residual for each scalar component
    1215                 :            : //!   computed across the whole problem
    1216                 :            : // *****************************************************************************
    1217                 :            : {
    1218         [ +  + ]:       3400 :   if (g_cfg.get< tag::steady >()) {
    1219                 :        480 :     const auto rc = g_cfg.get< tag::rescomp >() - 1;
    1220                 :        480 :     Disc()->residual( l2res[rc] );
    1221                 :            :   }
    1222                 :            : 
    1223                 :       3400 :   refine();
    1224                 :       3400 : }
    1225                 :            : 
    1226                 :            : void
    1227                 :       3400 : LaxCG::refine()
    1228                 :            : // *****************************************************************************
    1229                 :            : // Optionally refine/derefine mesh
    1230                 :            : // *****************************************************************************
    1231                 :            : {
    1232                 :       3400 :   auto d = Disc();
    1233                 :            : 
    1234                 :            :   // See if this is the last time step
    1235         [ +  + ]:       3400 :   if (d->finished()) m_finished = 1;
    1236                 :            : 
    1237                 :       3400 :   auto dtref = g_cfg.get< tag::href_dt >();
    1238                 :       3400 :   auto dtfreq = g_cfg.get< tag::href_dtfreq >();
    1239                 :            : 
    1240                 :            :   // if t>0 refinement enabled and we hit the frequency
    1241 [ -  + ][ -  - ]:       3400 :   if (dtref && !(d->It() % dtfreq)) {   // refine
    1242                 :            : 
    1243                 :          0 :     d->refined() = 1;
    1244                 :          0 :     d->startvol();
    1245                 :          0 :     d->Ref()->dtref( m_bface, m_bnode, m_triinpoel );
    1246                 :            : 
    1247                 :            :     // Activate SDAG waits for re-computing the integrals
    1248         [ -  - ]:          0 :     thisProxy[ thisIndex ].wait4int();
    1249                 :            : 
    1250                 :            :   } else {      // do not refine
    1251                 :            : 
    1252                 :       3400 :     d->refined() = 0;
    1253                 :       3400 :     feop_complete();
    1254                 :       3400 :     resize_complete();
    1255                 :            : 
    1256                 :            :   }
    1257                 :       3400 : }
    1258                 :            : 
    1259                 :            : void
    1260                 :          0 : LaxCG::resizePostAMR(
    1261                 :            :   const std::vector< std::size_t >& /*ginpoel*/,
    1262                 :            :   const tk::UnsMesh::Chunk& chunk,
    1263                 :            :   const tk::UnsMesh::Coords& coord,
    1264                 :            :   const std::unordered_map< std::size_t, tk::UnsMesh::Edge >& addedNodes,
    1265                 :            :   const std::unordered_map< std::size_t, std::size_t >& /*addedTets*/,
    1266                 :            :   const std::set< std::size_t >& removedNodes,
    1267                 :            :   const std::unordered_map< int, std::unordered_set< std::size_t > >&
    1268                 :            :     nodeCommMap,
    1269                 :            :   const std::map< int, std::vector< std::size_t > >& bface,
    1270                 :            :   const std::map< int, std::vector< std::size_t > >& bnode,
    1271                 :            :   const std::vector< std::size_t >& triinpoel )
    1272                 :            : // *****************************************************************************
    1273                 :            : //  Receive new mesh from Refiner
    1274                 :            : //! \param[in] ginpoel Mesh connectivity with global node ids
    1275                 :            : //! \param[in] chunk New mesh chunk (connectivity and global<->local id maps)
    1276                 :            : //! \param[in] coord New mesh node coordinates
    1277                 :            : //! \param[in] addedNodes Newly added mesh nodes and their parents (local ids)
    1278                 :            : //! \param[in] addedTets Newly added mesh cells and their parents (local ids)
    1279                 :            : //! \param[in] removedNodes Newly removed mesh node local ids
    1280                 :            : //! \param[in] nodeCommMap New node communication map
    1281                 :            : //! \param[in] bface Boundary-faces mapped to side set ids
    1282                 :            : //! \param[in] bnode Boundary-node lists mapped to side set ids
    1283                 :            : //! \param[in] triinpoel Boundary-face connectivity
    1284                 :            : // *****************************************************************************
    1285                 :            : {
    1286                 :          0 :   auto d = Disc();
    1287                 :            : 
    1288                 :          0 :   d->Itf() = 0;  // Zero field output iteration count if AMR
    1289                 :          0 :   ++d->Itr();    // Increase number of iterations with a change in the mesh
    1290                 :            : 
    1291                 :            :   // Resize mesh data structures after mesh refinement
    1292                 :          0 :   d->resizePostAMR( chunk, coord, nodeCommMap, removedNodes );
    1293                 :            : 
    1294                 :            :   Assert(coord[0].size() == m_u.nunk()-removedNodes.size()+addedNodes.size(),
    1295                 :            :     "Incorrect vector length post-AMR: expected length after resizing = " +
    1296                 :            :     std::to_string(coord[0].size()) + ", actual unknown vector length = " +
    1297                 :            :     std::to_string(m_u.nunk()-removedNodes.size()+addedNodes.size()));
    1298                 :            : 
    1299                 :            :   // Remove newly removed nodes from solution vectors
    1300                 :          0 :   m_u.rm( removedNodes );
    1301                 :          0 :   m_un.rm( removedNodes );
    1302                 :          0 :   m_rhs.rm( removedNodes );
    1303                 :          0 :   m_grad.rm( removedNodes );
    1304                 :            : 
    1305                 :            :   // Resize auxiliary solution vectors
    1306                 :            :   auto npoin = coord[0].size();
    1307                 :            :   m_u.resize( npoin );
    1308                 :            :   m_un.resize( npoin );
    1309                 :            :   m_rhs.resize( npoin );
    1310                 :            :   m_grad.resize( npoin );
    1311                 :            : 
    1312                 :            :   // Update solution on new mesh
    1313         [ -  - ]:          0 :   for (const auto& n : addedNodes)
    1314         [ -  - ]:          0 :     for (std::size_t c=0; c<m_u.nprop(); ++c) {
    1315                 :            :       Assert(n.first < m_u.nunk(), "Added node index out of bounds post-AMR");
    1316                 :            :       Assert(n.second[0] < m_u.nunk() && n.second[1] < m_u.nunk(),
    1317                 :            :         "Indices of parent-edge nodes out of bounds post-AMR");
    1318                 :          0 :       m_u(n.first,c) = (m_u(n.second[0],c) + m_u(n.second[1],c))/2.0;
    1319                 :            :     }
    1320                 :            : 
    1321                 :            :   // Update physical-boundary node-, face-, and element lists
    1322                 :            :   m_bnode = bnode;
    1323                 :            :   m_bface = bface;
    1324                 :          0 :   m_triinpoel = tk::remap( triinpoel, d->Lid() );
    1325                 :            : 
    1326                 :          0 :   auto meshid = d->MeshId();
    1327         [ -  - ]:          0 :   contribute( sizeof(std::size_t), &meshid, CkReduction::nop,
    1328                 :          0 :               CkCallback(CkReductionTarget(Transporter,resized), d->Tr()) );
    1329                 :          0 : }
    1330                 :            : 
    1331                 :            : void
    1332                 :        656 : LaxCG::writeFields( CkCallback cb )
    1333                 :            : // *****************************************************************************
    1334                 :            : // Output mesh-based fields to file
    1335                 :            : //! \param[in] cb Function to continue with after the write
    1336                 :            : // *****************************************************************************
    1337                 :            : {
    1338         [ +  + ]:        656 :   if (g_cfg.get< tag::benchmark >()) { cb.send(); return; }
    1339                 :            : 
    1340                 :         72 :   auto d = Disc();
    1341                 :            :   auto ncomp = m_u.nprop();
    1342                 :         72 :   auto steady = g_cfg.get< tag::steady >();
    1343                 :            : 
    1344                 :            :   // Field output
    1345                 :            : 
    1346                 :            :   std::vector< std::string > nodefieldnames
    1347 [ -  + ][ +  + ]:        504 :     {"density", "velocityx", "velocityy", "velocityz", "energy", "pressure"};
         [ -  + ][ -  - ]
                 [ -  - ]
    1348         [ +  - ]:        144 :   if (steady) nodefieldnames.push_back( "mach" );
    1349                 :            : 
    1350                 :            :   using tk::operator/=;
    1351         [ +  - ]:         72 :   auto r = m_u.extract(0);
    1352 [ +  - ][ +  - ]:         72 :   auto u = m_u.extract(1);  u /= r;
    1353 [ +  - ][ +  - ]:         72 :   auto v = m_u.extract(2);  v /= r;
    1354 [ +  - ][ +  - ]:         72 :   auto w = m_u.extract(3);  w /= r;
    1355 [ +  - ][ +  - ]:         72 :   auto e = m_u.extract(4);  e /= r;
    1356 [ +  - ][ +  - ]:         72 :   std::vector< tk::real > pr( m_u.nunk() ), ma;
                 [ -  - ]
    1357 [ +  - ][ +  - ]:         72 :   if (steady) ma.resize( m_u.nunk() );
    1358         [ +  + ]:     118158 :   for (std::size_t i=0; i<pr.size(); ++i) {
    1359                 :     118086 :     auto vv = u[i]*u[i] + v[i]*v[i] + w[i]*w[i];
    1360         [ +  - ]:     118086 :     pr[i] = eos::pressure( r[i]*(e[i] - 0.5*vv) );
    1361         [ +  - ]:     118086 :     if (steady) ma[i] = std::sqrt(vv) / eos::soundspeed( r[i], pr[i] );
    1362                 :            :   }
    1363                 :            : 
    1364                 :            :   std::vector< std::vector< tk::real > > nodefields{
    1365                 :            :     std::move(r), std::move(u), std::move(v), std::move(w), std::move(e),
    1366 [ -  + ][ +  + ]:        504 :     std::move(pr) };
         [ +  - ][ -  - ]
         [ -  - ][ -  - ]
    1367         [ +  - ]:         72 :   if (steady) nodefields.push_back( std::move(ma) );
    1368                 :            : 
    1369         [ -  + ]:         72 :   for (std::size_t c=0; c<ncomp-5; ++c) {
    1370         [ -  - ]:          0 :     nodefieldnames.push_back( "c" + std::to_string(c) );
    1371         [ -  - ]:          0 :     nodefields.push_back( m_u.extract(5+c) );
    1372                 :            :   }
    1373                 :            : 
    1374                 :            :   // query function to evaluate analytic solution (if defined)
    1375         [ +  - ]:         72 :   auto sol = problems::SOL();
    1376                 :            : 
    1377         [ -  + ]:         72 :   if (sol) {
    1378                 :            :     const auto& coord = d->Coord();
    1379                 :            :     const auto& x = coord[0];
    1380                 :            :     const auto& y = coord[1];
    1381                 :            :     const auto& z = coord[2];
    1382                 :            :     auto an = m_u;
    1383 [ -  - ][ -  - ]:          0 :     std::vector< tk::real > ap( m_u.nunk() );
    1384         [ -  - ]:          0 :     for (std::size_t i=0; i<an.nunk(); ++i) {
    1385         [ -  - ]:          0 :       auto s = sol( x[i], y[i], z[i], d->T() );
    1386                 :          0 :       s[1] /= s[0];
    1387                 :          0 :       s[2] /= s[0];
    1388                 :          0 :       s[3] /= s[0];
    1389                 :          0 :       s[4] /= s[0];
    1390         [ -  - ]:          0 :       for (std::size_t c=0; c<s.size(); ++c) an(i,c) = s[c];
    1391                 :          0 :       s[4] -= 0.5*(s[1]*s[1] + s[2]*s[2] + s[3]*s[3]);
    1392                 :          0 :       ap[i] = eos::pressure( s[0]*s[4] );
    1393                 :            :     }
    1394         [ -  - ]:          0 :     for (std::size_t c=0; c<5; ++c) {
    1395         [ -  - ]:          0 :       nodefieldnames.push_back( nodefieldnames[c] + "_analytic" );
    1396         [ -  - ]:          0 :       nodefields.push_back( an.extract(c) );
    1397                 :            :     }
    1398 [ -  - ][ -  - ]:          0 :     nodefieldnames.push_back( nodefieldnames[5] + "_analytic" );
    1399                 :            :     nodefields.push_back( std::move(ap) );
    1400         [ -  - ]:          0 :     for (std::size_t c=0; c<ncomp-5; ++c) {
    1401         [ -  - ]:          0 :       nodefieldnames.push_back( nodefieldnames[6+c] + "_analytic" );
    1402 [ -  - ][ -  - ]:          0 :       nodefields.push_back( an.extract(5+c) );
    1403                 :            :     }
    1404                 :            :   }
    1405                 :            : 
    1406                 :            :   Assert( nodefieldnames.size() == nodefields.size(), "Size mismatch" );
    1407                 :            : 
    1408                 :            :   // Surface output
    1409                 :            : 
    1410                 :            :   std::vector< std::string > nodesurfnames;
    1411                 :            :   std::vector< std::vector< tk::real > > nodesurfs;
    1412                 :            : 
    1413                 :            :   const auto& f = g_cfg.get< tag::fieldout >();
    1414                 :            : 
    1415         [ -  + ]:         72 :   if (!f.empty()) {
    1416         [ -  - ]:          0 :     nodesurfnames.push_back( "density" );
    1417         [ -  - ]:          0 :     nodesurfnames.push_back( "velocityx" );
    1418         [ -  - ]:          0 :     nodesurfnames.push_back( "velocityy" );
    1419         [ -  - ]:          0 :     nodesurfnames.push_back( "velocityz" );
    1420         [ -  - ]:          0 :     nodesurfnames.push_back( "energy" );
    1421         [ -  - ]:          0 :     nodesurfnames.push_back( "pressure" );
    1422                 :            : 
    1423         [ -  - ]:          0 :     for (std::size_t c=0; c<ncomp-5; ++c) {
    1424         [ -  - ]:          0 :       nodesurfnames.push_back( "c" + std::to_string(c) );
    1425                 :            :     }
    1426                 :            : 
    1427 [ -  - ][ -  - ]:          0 :     if (steady) nodesurfnames.push_back( "mach" );
    1428                 :            : 
    1429         [ -  - ]:          0 :     auto bnode = tk::bfacenodes( m_bface, m_triinpoel );
    1430         [ -  - ]:          0 :     std::set< int > outsets( begin(f), end(f) );
    1431         [ -  - ]:          0 :     for (auto sideset : outsets) {
    1432                 :            :       auto b = bnode.find(sideset);
    1433         [ -  - ]:          0 :       if (b == end(bnode)) continue;
    1434                 :            :       const auto& nodes = b->second;
    1435                 :            :       auto i = nodesurfs.size();
    1436                 :          0 :       auto ns = ncomp + 1;
    1437         [ -  - ]:          0 :       if (steady) ++ns;
    1438                 :            :       nodesurfs.insert( end(nodesurfs), ns,
    1439         [ -  - ]:          0 :                         std::vector< tk::real >( nodes.size() ) );
    1440                 :            :       std::size_t j = 0;
    1441         [ -  - ]:          0 :       for (auto n : nodes) {
    1442         [ -  - ]:          0 :         const auto s = m_u[n];
    1443                 :            :         std::size_t p = 0;
    1444                 :          0 :         nodesurfs[i+(p++)][j] = s[0];
    1445                 :          0 :         nodesurfs[i+(p++)][j] = s[1]/s[0];
    1446                 :          0 :         nodesurfs[i+(p++)][j] = s[2]/s[0];
    1447                 :          0 :         nodesurfs[i+(p++)][j] = s[3]/s[0];
    1448                 :          0 :         nodesurfs[i+(p++)][j] = s[4]/s[0];
    1449                 :          0 :         auto vv = (s[1]*s[1] + s[2]*s[2] + s[3]*s[3])/s[0]/s[0];
    1450                 :          0 :         auto ei = s[4]/s[0] - 0.5*vv;
    1451                 :          0 :         auto sp = eos::pressure( s[0]*ei );
    1452                 :          0 :         nodesurfs[i+(p++)][j] = sp;
    1453         [ -  - ]:          0 :         for (std::size_t c=0; c<ncomp-5; ++c) nodesurfs[i+(p++)+c][j] = s[5+c];
    1454         [ -  - ]:          0 :         if (steady) {
    1455                 :          0 :           nodesurfs[i+(p++)][j] = std::sqrt(vv) / eos::soundspeed( s[0], sp );
    1456                 :            :         }
    1457                 :          0 :         ++j;
    1458                 :            :       }
    1459                 :            :     }
    1460                 :            :   }
    1461                 :            : 
    1462                 :            :   // Send mesh and fields data (solution dump) for output to file
    1463 [ +  - ][ +  - ]:        144 :   d->write( d->Inpoel(), d->Coord(), m_bface, tk::remap(m_bnode,d->Lid()),
    1464         [ +  - ]:         72 :             m_triinpoel, {}, nodefieldnames, {}, nodesurfnames,
    1465                 :            :             {}, nodefields, {}, nodesurfs, cb );
    1466 [ +  - ][ +  - ]:        360 : }
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ -  - ]
                 [ -  - ]
    1467                 :            : 
    1468                 :            : void
    1469                 :       3400 : LaxCG::out()
    1470                 :            : // *****************************************************************************
    1471                 :            : // Output mesh field data
    1472                 :            : // *****************************************************************************
    1473                 :            : {
    1474                 :       3400 :   auto d = Disc();
    1475                 :            : 
    1476                 :            :   // Time history
    1477 [ +  - ][ +  - ]:       3400 :   if (d->histiter() or d->histtime() or d->histrange()) {
                 [ -  + ]
    1478                 :            :     auto ncomp = m_u.nprop();
    1479                 :            :     const auto& inpoel = d->Inpoel();
    1480                 :          0 :     std::vector< std::vector< tk::real > > hist( d->Hist().size() );
    1481                 :            :     std::size_t j = 0;
    1482         [ -  - ]:          0 :     for (const auto& p : d->Hist()) {
    1483         [ -  - ]:          0 :       auto e = p.get< tag::elem >();        // host element id
    1484                 :            :       const auto& n = p.get< tag::fn >();   // shapefunctions evaluated at point
    1485         [ -  - ]:          0 :       hist[j].resize( ncomp+1, 0.0 );
    1486         [ -  - ]:          0 :       for (std::size_t i=0; i<4; ++i) {
    1487         [ -  - ]:          0 :         const auto u = m_u[ inpoel[e*4+i] ];
    1488                 :          0 :         hist[j][0] += n[i] * u[0];
    1489                 :          0 :         hist[j][1] += n[i] * u[1]/u[0];
    1490                 :          0 :         hist[j][2] += n[i] * u[2]/u[0];
    1491                 :          0 :         hist[j][3] += n[i] * u[3]/u[0];
    1492                 :          0 :         hist[j][4] += n[i] * u[4]/u[0];
    1493                 :          0 :         auto ei = u[4]/u[0] - 0.5*(u[1]*u[1] + u[2]*u[2] + u[3]*u[3])/u[0]/u[0];
    1494                 :          0 :         hist[j][5] += n[i] * eos::pressure( u[0]*ei );
    1495         [ -  - ]:          0 :         for (std::size_t c=5; c<ncomp; ++c) hist[j][c+1] += n[i] * u[c];
    1496                 :            :       }
    1497                 :          0 :       ++j;
    1498                 :            :     }
    1499         [ -  - ]:          0 :     d->history( std::move(hist) );
    1500                 :          0 :   }
    1501                 :            : 
    1502                 :            :   // Field data
    1503 [ +  + ][ +  - ]:       3400 :   if (d->fielditer() or d->fieldtime() or d->fieldrange() or m_finished) {
         [ +  - ][ +  + ]
    1504 [ +  - ][ +  - ]:       1020 :     writeFields( CkCallback(CkIndex_LaxCG::integrals(), thisProxy[thisIndex]) );
    1505                 :            :   } else {
    1506                 :       3060 :     integrals();
    1507                 :            :   }
    1508                 :       3400 : }
    1509                 :            : 
    1510                 :            : void
    1511                 :       3400 : LaxCG::integrals()
    1512                 :            : // *****************************************************************************
    1513                 :            : // Compute integral quantities for output
    1514                 :            : // *****************************************************************************
    1515                 :            : {
    1516                 :       3400 :   auto d = Disc();
    1517                 :            : 
    1518 [ +  - ][ +  - ]:       3400 :   if (d->integiter() or d->integtime() or d->integrange()) {
                 [ -  + ]
    1519                 :            : 
    1520                 :            :     using namespace integrals;
    1521         [ -  - ]:          0 :     std::vector< std::map< int, tk::real > > ints( NUMINT );
    1522                 :            :     // Prepend integral vector with metadata on the current time step:
    1523                 :            :     // current iteration count, current physical time, time step size
    1524 [ -  - ][ -  - ]:          0 :     ints[ ITER ][ 0 ] = static_cast< tk::real >( d->It() );
    1525 [ -  - ][ -  - ]:          0 :     ints[ TIME ][ 0 ] = d->T();
    1526         [ -  - ]:          0 :     ints[ DT ][ 0 ] = d->Dt();
    1527                 :            :     // Compute mass flow rate for surfaces requested
    1528         [ -  - ]:          0 :     for (const auto& [s,sint] : m_surfint) {
    1529                 :            :       // cppcheck-suppress unreadVariable
    1530         [ -  - ]:          0 :       auto& mfr = ints[ MASS_FLOW_RATE ][ s ];
    1531                 :            :       const auto& nodes = sint.first;
    1532                 :            :       const auto& ndA = sint.second;
    1533         [ -  - ]:          0 :       for (std::size_t i=0; i<nodes.size(); ++i) {
    1534                 :          0 :         auto p = nodes[i];
    1535                 :          0 :         mfr += ndA[i*3+0] * m_u(p,1)
    1536                 :          0 :              + ndA[i*3+1] * m_u(p,2)
    1537                 :          0 :              + ndA[i*3+2] * m_u(p,3);
    1538                 :            :       }
    1539                 :            :     }
    1540         [ -  - ]:          0 :     auto stream = serialize( d->MeshId(), ints );
    1541 [ -  - ][ -  - ]:          0 :     d->contribute( stream.first, stream.second.get(), IntegralsMerger,
    1542 [ -  - ][ -  - ]:          0 :       CkCallback(CkIndex_Transporter::integrals(nullptr), d->Tr()) );
    1543                 :            : 
    1544                 :          0 :   } else {
    1545                 :            : 
    1546                 :       3400 :     step();
    1547                 :            : 
    1548                 :            :   }
    1549                 :       3400 : }
    1550                 :            : 
    1551                 :            : void
    1552                 :      10200 : LaxCG::stage()
    1553                 :            : // *****************************************************************************
    1554                 :            : // Evaluate whether to continue with next time step stage
    1555                 :            : // *****************************************************************************
    1556                 :            : {
    1557                 :            :   // Increment Runge-Kutta stage counter
    1558                 :      10200 :   ++m_stage;
    1559                 :            : 
    1560                 :            :   // If not all Runge-Kutta stages complete, continue to next time stage,
    1561                 :            :   // otherwise output field data to file(s)
    1562         [ +  + ]:      10200 :   if (m_stage < 3) grad(); else out();
    1563                 :      10200 : }
    1564                 :            : 
    1565                 :            : void
    1566                 :       3084 : LaxCG::evalLB( int nrestart )
    1567                 :            : // *****************************************************************************
    1568                 :            : // Evaluate whether to do load balancing
    1569                 :            : //! \param[in] nrestart Number of times restarted
    1570                 :            : // *****************************************************************************
    1571                 :            : {
    1572                 :       3084 :   auto d = Disc();
    1573                 :            : 
    1574                 :            :   // Detect if just returned from a checkpoint and if so, zero timers and
    1575                 :            :   // finished flag
    1576         [ -  + ]:       3084 :   if (d->restarted( nrestart )) m_finished = 0;
    1577                 :            : 
    1578                 :       3084 :   const auto lbfreq = g_cfg.get< tag::lbfreq >();
    1579         [ +  + ]:       3084 :   const auto nonblocking = g_cfg.get< tag::nonblocking >();
    1580                 :            : 
    1581                 :            :   // Load balancing if user frequency is reached or after the second time-step
    1582 [ +  + ][ +  + ]:       3084 :   if ( (d->It()) % lbfreq == 0 || d->It() == 2 ) {
    1583                 :            : 
    1584                 :       2799 :     AtSync();
    1585         [ -  + ]:       2799 :     if (nonblocking) dt();
    1586                 :            : 
    1587                 :            :   } else {
    1588                 :            : 
    1589                 :        285 :     dt();
    1590                 :            : 
    1591                 :            :   }
    1592                 :       3084 : }
    1593                 :            : 
    1594                 :            : void
    1595                 :       3084 : LaxCG::evalRestart()
    1596                 :            : // *****************************************************************************
    1597                 :            : // Evaluate whether to save checkpoint/restart
    1598                 :            : // *****************************************************************************
    1599                 :            : {
    1600                 :       3084 :   auto d = Disc();
    1601                 :            : 
    1602                 :       3084 :   const auto rsfreq = g_cfg.get< tag::rsfreq >();
    1603                 :       3084 :   const auto benchmark = g_cfg.get< tag::benchmark >();
    1604                 :            : 
    1605 [ +  + ][ -  + ]:       3084 :   if ( !benchmark && (d->It()) % rsfreq == 0 ) {
    1606                 :            : 
    1607                 :          0 :     std::vector< std::size_t > meshdata{ /* finished = */ 0, d->MeshId() };
    1608         [ -  - ]:          0 :     contribute( meshdata, CkReduction::nop,
    1609 [ -  - ][ -  - ]:          0 :       CkCallback(CkReductionTarget(Transporter,checkpoint), d->Tr()) );
    1610                 :            : 
    1611                 :            :   } else {
    1612                 :            : 
    1613                 :       3084 :     evalLB( /* nrestart = */ -1 );
    1614                 :            : 
    1615                 :            :   }
    1616                 :       3084 : }
    1617                 :            : 
    1618                 :            : void
    1619                 :       3400 : LaxCG::step()
    1620                 :            : // *****************************************************************************
    1621                 :            : // Evaluate whether to continue with next time step
    1622                 :            : // *****************************************************************************
    1623                 :            : {
    1624                 :       3400 :   auto d = Disc();
    1625                 :            : 
    1626                 :            :   // Output one-liner status report to screen
    1627                 :       3400 :   d->status();
    1628                 :            :   // Reset Runge-Kutta stage counter
    1629                 :       3400 :   m_stage = 0;
    1630                 :            : 
    1631         [ +  + ]:       3400 :   if (not m_finished) {
    1632                 :            : 
    1633                 :       3084 :     evalRestart();
    1634                 :            : 
    1635                 :            :   } else {
    1636                 :            : 
    1637                 :        316 :     auto meshid = d->MeshId();
    1638         [ +  - ]:        632 :     d->contribute( sizeof(std::size_t), &meshid, CkReduction::nop,
    1639                 :        316 :                    CkCallback(CkReductionTarget(Transporter,finish), d->Tr()) );
    1640                 :            : 
    1641                 :            :   }
    1642                 :       3400 : }
    1643                 :            : 
    1644                 :            : #include "NoWarning/laxcg.def.h"

Generated by: LCOV version 1.16