Quinoa regression test code coverage report
Current view: top level - Inciter - FluxCorrector.cpp (source / functions) Hit Total Coverage
Commit: Quinoa_v0.3-957-gb4f0efae0 Lines: 181 190 95.3 %
Date: 2021-11-11 13:17:06 Functions: 6 6 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 229 452 50.7 %

           Branch data     Line data    Source code
       1                 :            : // *****************************************************************************
       2                 :            : /*!
       3                 :            :   \file      src/Inciter/FluxCorrector.cpp
       4                 :            :   \copyright 2012-2015 J. Bakosi,
       5                 :            :              2016-2018 Los Alamos National Security, LLC.,
       6                 :            :              2019-2021 Triad National Security, LLC.
       7                 :            :              All rights reserved. See the LICENSE file for details.
       8                 :            :   \brief     FluxCorrector performs limiting for transport equations
       9                 :            :   \details   FluxCorrector performs limiting for transport equations. Each
      10                 :            :     FluxCorrector object performs the limiting procedure, according to a
      11                 :            :     flux-corrected transport algorithm, on a chunk of the full load (part of the
      12                 :            :     mesh).
      13                 :            : */
      14                 :            : // *****************************************************************************
      15                 :            : 
      16                 :            : #include <limits>
      17                 :            : #include <sstream>
      18                 :            : #include <algorithm>
      19                 :            : #include <unordered_map>
      20                 :            : 
      21                 :            : #include "Macro.hpp"
      22                 :            : #include "Vector.hpp"
      23                 :            : #include "Around.hpp"
      24                 :            : #include "DerivedData.hpp"
      25                 :            : #include "FluxCorrector.hpp"
      26                 :            : #include "Inciter/InputDeck/InputDeck.hpp"
      27                 :            : 
      28                 :            : using inciter::FluxCorrector;
      29                 :            : 
      30                 :            : void
      31                 :      18283 : FluxCorrector::aec(
      32                 :            :   const std::array< std::vector< tk::real >, 3 >& coord,
      33                 :            :   const std::vector< std::size_t >& inpoel,
      34                 :            :   const std::vector< tk::real >& vol,
      35                 :            :   const std::unordered_map< std::size_t,
      36                 :            :     std::vector< std::pair< bool, tk::real > > >& bcdir,
      37                 :            :   const std::unordered_map< int,
      38                 :            :     std::unordered_set< std::size_t > >& symbcnodemap,
      39                 :            :   const std::unordered_map< int,
      40                 :            :     std::unordered_map< std::size_t, std::array< tk::real, 4 > > >& bnorm,
      41                 :            :   const tk::Fields& Un,
      42                 :            :   tk::Fields& P )
      43                 :            : // *****************************************************************************
      44                 :            : //  Compute antidiffusive element contributions (AEC)
      45                 :            : //! \param[in] coord Mesh node coordinates
      46                 :            : //! \param[in] inpoel Mesh element connectivity
      47                 :            : //! \param[in] vol Volume associated to mesh nodes
      48                 :            : //! \param[in] bcdir Vector of pairs of bool and boundary condition value
      49                 :            : //!   associated to local mesh node IDs at which to set Dirichlet boundary
      50                 :            : //!   conditions.
      51                 :            : //! \param[in] symbcnodemap Unique set of node ids at which to set symmetry BCs
      52                 :            : //!   associated to side set ids
      53                 :            : //! \param[in] bnorm Face normals in boundary points: key global node id,
      54                 :            : //!   value: unit normal, outer key: side set id
      55                 :            : //! \param[in] Un Solution at the previous time step
      56                 :            : //! \param[in,out] P The sums of positive (negative) AECs to nodes
      57                 :            : //! \details The antidiffusive element contributions (AEC) are defined as the
      58                 :            : //!   difference between the high and low order solution, where the high order
      59                 :            : //!   solution is obtained from consistent mass Taylor-Galerkin discretization
      60                 :            : //!   and the low order solution is lumped mass Taylor-Galerkin + diffusion.
      61                 :            : //!   Note that AEC is not directly computed as dUh - dUl (although that could
      62                 :            : //!   also be done), but as AEC = M_L^{-1} (M_Le - M_ce) (ctau * Un + dUh),
      63                 :            : //!   where
      64                 :            : //!    * M_ce is the element's consistent mass matrix,
      65                 :            : //!    * M_Le is the element's lumped mass matrix,
      66                 :            : //!    * ctau is the mass diffusion coefficient on the rhs of the low order
      67                 :            : //!      solution, see also FluxCorrector::diff(),
      68                 :            : //!    * Un is the solution at the previous time step
      69                 :            : //!    * dUh is the increment of the high order solution, and
      70                 :            : //!    * M_L^{-1} is the inverse of the assembled lumped mass matrix, i.e., the
      71                 :            : //!      volume associated to a mesh node by summing the quarter of the element
      72                 :            : //!      volumes surrounding the node. Note that this is the correct node volume
      73                 :            : //!      taking into account that some nodes are on chare boundaries.
      74                 :            : //! \note Since we use the lumped-mass for the high-order solution, dUh
      75                 :            : //!   does not contribute to AEC, as computed above.
      76                 :            : //! \see Löhner, R., Morgan, K., Peraire, J. and Vahdati, M. (1987), Finite
      77                 :            : //!   element flux-corrected transport (FEM–FCT) for the Euler and Navier–Stokes
      78                 :            : //!   equations. Int. J. Numer. Meth. Fluids, 7: 1093–1109.
      79                 :            : //!   doi:10.1002/fld.1650071007
      80                 :            : // *****************************************************************************
      81                 :            : {
      82                 :      18283 :   auto ncomp = g_inputdeck.get< tag::component >().nprop();
      83                 :      18283 :   auto ctau = g_inputdeck.get< tag::discr, tag::ctau >();
      84                 :            : 
      85 [ -  + ][ -  - ]:      18283 :   Assert( vol.size() == coord[0].size(), "Nodal volume vector size mismatch" );
         [ -  - ][ -  - ]
      86 [ +  - ][ +  - ]:      18283 :   Assert( m_aec.nunk() == inpoel.size() && m_aec.nprop() == ncomp,
         [ -  - ][ -  - ]
                 [ -  - ]
      87                 :            :           "AEC and mesh connectivity size mismatch" );
      88 [ +  - ][ +  - ]:      18283 :   Assert( Un.nunk() == P.nunk() && Un.nprop() == P.nprop()/2, "Size mismatch" );
         [ -  - ][ -  - ]
                 [ -  - ]
      89                 :            : 
      90                 :      18283 :   const auto& x = coord[0];
      91                 :      18283 :   const auto& y = coord[1];
      92                 :      18283 :   const auto& z = coord[2];
      93                 :            : 
      94                 :      18283 :   m_aec.fill( 0.0 );
      95                 :            : 
      96         [ +  + ]:    9273135 :   for (std::size_t e=0; e<inpoel.size()/4; ++e) {
      97                 :    9254852 :     const std::array< std::size_t, 4 > N{{ inpoel[e*4+0], inpoel[e*4+1],
      98                 :    9254852 :                                            inpoel[e*4+2], inpoel[e*4+3] }};
      99                 :            : 
     100                 :            :     // compute element Jacobi determinant
     101                 :            :     const std::array< tk::real, 3 >
     102                 :    9254852 :       ba{{ x[N[1]]-x[N[0]], y[N[1]]-y[N[0]], z[N[1]]-z[N[0]] }},
     103                 :    9254852 :       ca{{ x[N[2]]-x[N[0]], y[N[2]]-y[N[0]], z[N[2]]-z[N[0]] }},
     104                 :    9254852 :       da{{ x[N[3]]-x[N[0]], y[N[3]]-y[N[0]], z[N[3]]-z[N[0]] }};
     105                 :    9254852 :     const auto J = tk::triple( ba, ca, da );
     106 [ -  + ][ -  - ]:    9254852 :     Assert( J > 0, "Element Jacobian non-positive" );
         [ -  - ][ -  - ]
     107                 :            : 
     108                 :            :     // lumped - consistent mass
     109                 :            :     std::array< std::array< tk::real, 4 >, 4 > m;       // nnode*nnode [4][4]
     110                 :    9254852 :     m[0][0] = m[1][1] = m[2][2] = m[3][3] = 3.0*J/120.0;// diagonal
     111                 :    9254852 :     m[0][1] = m[0][2] = m[0][3] =                       // off-diagonal
     112                 :    9254852 :     m[1][0] = m[1][2] = m[1][3] =
     113                 :    9254852 :     m[2][0] = m[2][1] = m[2][3] =
     114                 :    9254852 :     m[3][0] = m[3][1] = m[3][2] = -J/120.0;
     115                 :            : 
     116                 :            :     // access solution at element nodes at time n
     117         [ +  - ]:   18509704 :     std::vector< std::array< tk::real, 4 > > un( ncomp );
     118 [ +  + ][ +  - ]:   23143444 :     for (ncomp_t c=0; c<ncomp; ++c) un[c] = Un.extract( c, 0, N );
     119                 :            : 
     120                 :            :     // Compute antidiffusive element contributions (AEC). The high order system
     121                 :            :     // is M_c * dUh = r, where M_c is the consistent mass matrix and r is the
     122                 :            :     // high order right hand side. The low order system is constructed from the
     123                 :            :     // high order one by lumping the consistent mass matrix and adding mass
     124                 :            :     // diffusion: M_L * dUl = r + c_tau * (M_c - M_L) Un, where M_L is the
     125                 :            :     // lumped mass matrix, c_tau is the mass diffusion coefficient (c_tau = 1.0
     126                 :            :     // guarantees a monotonic solution). See also the details in the function
     127                 :            :     // header for the notation. Based on the above, the AEC, in general, is
     128                 :            :     // computed as AEC = M_L^{-1} (M_Le - M_ce) (ctau * Un + dUh), which can be
     129                 :            :     // obtained by subtracting the low order system from the high order system.
     130                 :            :     // Note that the solution update is U^{n+1} = Un + dUl + lim(dUh - dUl),
     131                 :            :     // where the last term is the limited AEC. (Think of 'lim' as the limit
     132                 :            :     // coefficient between 0 and 1.)
     133         [ +  + ]:   46274260 :     for (std::size_t j=0; j<4; ++j)
     134         [ +  + ]:   92573776 :       for (ncomp_t c=0; c<ncomp; ++c)
     135         [ +  + ]:  277771840 :         for (std::size_t k=0; k<4; ++k)
     136         [ +  - ]:  222217472 :           m_aec(e*4+j,c,0) += m[j][k] * ctau*un[c][k] / vol[N[j]];
     137                 :            :   }
     138                 :            : 
     139         [ +  + ]:    9273135 :   for (std::size_t e=0; e<inpoel.size()/4; ++e) {
     140                 :            :     const std::array< std::size_t, 4 >
     141                 :    9254852 :       N{{ inpoel[e*4+0], inpoel[e*4+1], inpoel[e*4+2], inpoel[e*4+3] }};
     142         [ +  + ]:   46274260 :     for (std::size_t j=0; j<4; ++j) {
     143                 :            :       // Dirichlet BCs: At nodes where Dirichlet boundary conditions (BC) are
     144                 :            :       // set, we set the AEC to zero. This is because if the (same) BCs are
     145                 :            :       // correctly set for both the low and the high order solution, there
     146                 :            :       // should be no difference between the low and high order increments,
     147                 :            :       // thus AEC = dUh - dUl = 0.
     148         [ +  - ]:   37019408 :       auto b = bcdir.find(N[j]);
     149         [ +  + ]:   37019408 :       if (b != end(bcdir)) {
     150         [ +  + ]:   18417696 :         for (ncomp_t c=0; c<ncomp; ++c) {
     151         [ +  - ]:   14546180 :           if (b->second[c].first) {
     152         [ +  - ]:   14546180 :             m_aec(e*4+j,c,0) = 0.0;
     153                 :            :           }
     154                 :            :         }
     155                 :            :       }
     156                 :            :       // Symmetry BCs
     157         [ +  + ]:   37787488 :       for (const auto& [s,nodes] : symbcnodemap) {
     158         [ +  - ]:     768080 :         auto i = nodes.find(N[j]);
     159         [ +  + ]:     768080 :         if (i != end(nodes)) {
     160         [ +  - ]:     184280 :           auto l = bnorm.find(s);
     161         [ +  - ]:     184280 :           if (l != end(bnorm)) {
     162         [ +  - ]:     184280 :             auto k = l->second.find(N[j]);
     163         [ +  + ]:     368560 :             for (const auto& vel : m_vel) {
     164                 :            :               std::array< tk::real, 3 >
     165         [ +  - ]:     184280 :                 v{ m_aec(e*4+j,vel[0],0),
     166         [ +  - ]:     184280 :                    m_aec(e*4+j,vel[1],0),
     167         [ +  - ]:     368560 :                    m_aec(e*4+j,vel[2],0) },
     168                 :     184280 :                 n{ k->second[0], k->second[1], k->second[2] };
     169                 :     184280 :               auto vn = tk::dot( v, n );
     170         [ +  - ]:     184280 :               m_aec(e*4+j,vel[0],0) -= vn * n[0];
     171         [ +  - ]:     184280 :               m_aec(e*4+j,vel[1],0) -= vn * n[1];
     172         [ +  - ]:     184280 :               m_aec(e*4+j,vel[2],0) -= vn * n[2];
     173                 :            :             }
     174                 :            :           }
     175                 :            :         }
     176                 :            :       }
     177                 :            :     }
     178                 :            :   }
     179                 :            : 
     180                 :            :   // sum all positive (negative) antidiffusive element contributions to nodes
     181                 :            :   // (Lohner: P^{+,-}_i)
     182         [ +  + ]:    9273135 :   for (std::size_t e=0; e<inpoel.size()/4; ++e) {
     183                 :    9254852 :     const std::array< std::size_t, 4 > N{{ inpoel[e*4+0], inpoel[e*4+1],
     184                 :    9254852 :                                            inpoel[e*4+2], inpoel[e*4+3] }};
     185         [ +  + ]:   46274260 :     for (std::size_t j=0; j<4; ++j) {
     186         [ +  + ]:   92573776 :       for (ncomp_t c=0; c<ncomp; ++c) {
     187 [ +  - ][ +  - ]:   55554368 :         P(N[j],c*2+0,0) += std::max( 0.0, m_aec(e*4+j,c,0) );
     188 [ +  - ][ +  - ]:   55554368 :         P(N[j],c*2+1,0) += std::min( 0.0, m_aec(e*4+j,c,0) );
     189                 :            :       }
     190                 :            :     }
     191                 :            :   }
     192                 :      18283 : }
     193                 :            : 
     194                 :            : bool
     195                 :      18283 : FluxCorrector::verify( std::size_t nchare,
     196                 :            :                        const std::vector< std::size_t >& inpoel,
     197                 :            :                        const tk::Fields& dUh,
     198                 :            :                        const tk::Fields& dUl ) const
     199                 :            : // *****************************************************************************
     200                 :            : //  Verify the assembled antidiffusive element contributions (AEC)
     201                 :            : //! \param[in] nchare Total number of host chares
     202                 :            : //! \param[in] inpoel Mesh element connectivity
     203                 :            : //! \param[in] dUh Increment of the high order solution
     204                 :            : //! \param[in] dUl Increment of the low order solution
     205                 :            : //! \return True if verification has been done
     206                 :            : //! \details This verification only makes sense if no communication is to be
     207                 :            : //!   done, i.e., if there is a single host chare, because the AEC assembled to
     208                 :            : //!   mesh nodes only contains partial contributions on chare boundaries at this
     209                 :            : //!   point. Verification in parallel would incure communication of the
     210                 :            : //!   unlimited AEC, which in general is not necessary, so we will not do that
     211                 :            : //!   for the sake of verification.
     212                 :            : //! \note Client code should ensure that this function is optimized away in
     213                 :            : //!   RELEASE mode.
     214                 :            : // *****************************************************************************
     215                 :            : {
     216 [ +  - ][ +  - ]:      18283 :   Assert( dUl.nunk() == dUh.nunk() && dUl.nprop() == dUh.nprop(),
         [ -  - ][ -  - ]
                 [ -  - ]
     217                 :            :           "Unknown array size mismatch" );
     218                 :            : 
     219         [ +  + ]:      18283 :   if (nchare == 1) {
     220                 :        499 :     auto ncomp = g_inputdeck.get< tag::component >().nprop();
     221         [ +  - ]:        499 :     tk::Fields U( dUh.nunk(), dUh.nprop() );
     222         [ +  - ]:        499 :     U.fill( 0.0 );
     223                 :            : 
     224         [ +  + ]:    1989253 :     for (std::size_t e=0; e<inpoel.size()/4; ++e) {
     225                 :    1988754 :       const std::array< std::size_t, 4 > N{{ inpoel[e*4+0], inpoel[e*4+1],
     226                 :    1988754 :                                              inpoel[e*4+2], inpoel[e*4+3] }};
     227                 :            :       // access pointer to solution at element nodes
     228         [ +  - ]:    3977508 :       std::vector< const tk::real* > u( ncomp );
     229 [ +  + ][ +  - ]:    5251688 :       for (ncomp_t c=0; c<ncomp; ++c) u[c] = U.cptr( c, 0 );
     230                 :            :       // scatter-add antidiffusive element contributions to nodes
     231         [ +  + ]:    5251688 :       for (ncomp_t c=0; c<ncomp; ++c)
     232         [ +  + ]:   16314670 :         for (std::size_t j=0; j<4; ++j)
     233 [ +  - ][ +  - ]:   13051736 :           U.var(u[c],N[j]) += m_aec(e*4+j,c,0);
     234                 :            :     }
     235                 :            : 
     236                 :            :     // Compute maximum difference between the assembled AEC and dUh-dUl
     237 [ +  - ][ +  - ]:        499 :     auto d = tk::maxdiff( U, dUh-dUl );
     238                 :            : 
     239                 :            :     // Tolerance: 10 x the linear solver tolerance for the high order solution.
     240         [ -  + ]:        499 :     if (d.second > 1.0e-7) {
     241                 :          0 :       const auto& duh = dUh.vec();
     242                 :          0 :       const auto& dul = dUl.vec();
     243                 :          0 :       const auto& u = U.vec();
     244         [ -  - ]:          0 :       std::stringstream ss;
     245 [ -  - ][ -  - ]:          0 :       ss << "maximum difference at mesh node " << d.first << ": " << d.second
         [ -  - ][ -  - ]
     246 [ -  - ][ -  - ]:          0 :          << ", dUh:" << duh[d.first] << ", dUl:" << dul[d.first]
         [ -  - ][ -  - ]
     247 [ -  - ][ -  - ]:          0 :          << ", dUh-dUl:" << duh[d.first] - dul[d.first]
     248 [ -  - ][ -  - ]:          0 :          << ", AEC:" << u[d.first];
     249 [ -  - ][ -  - ]:          0 :       Throw( "Assembled AEC does not equal dUh-dUl: " + ss.str() );
         [ -  - ][ -  - ]
     250                 :            :     }
     251                 :            : 
     252                 :        499 :     return true;
     253                 :            :   }
     254                 :            : 
     255                 :      17784 :   return false;
     256                 :            : }
     257                 :            : 
     258                 :            : tk::Fields
     259                 :      18283 : FluxCorrector::diff( const std::array< std::vector< tk::real >, 3 >& coord,
     260                 :            :                      const std::vector< std::size_t >& inpoel,
     261                 :            :                      const tk::Fields& Un ) const
     262                 :            : // *****************************************************************************
     263                 :            : //  Compute mass diffusion contribution to the RHS of the low order system
     264                 :            : //! \param[in] coord Mesh node coordinates
     265                 :            : //! \param[in] inpoel Mesh element connectivity
     266                 :            : //! \param[in] Un Solution at the previous time step
     267                 :            : //! \return Mass diffusion contribution to the RHS of the low order system
     268                 :            : // *****************************************************************************
     269                 :            : {
     270                 :      18283 :   auto ncomp = g_inputdeck.get< tag::component >().nprop();
     271                 :      18283 :   auto ctau = g_inputdeck.get< tag::discr, tag::ctau >();
     272                 :            : 
     273                 :            :   // access node coordinates
     274                 :      18283 :   const auto& x = coord[0];
     275                 :      18283 :   const auto& y = coord[1];
     276                 :      18283 :   const auto& z = coord[2];
     277                 :            : 
     278                 :      18283 :   tk::Fields D( Un.nunk(), Un.nprop() );
     279         [ +  - ]:      18283 :   D.fill( 0.0 );
     280                 :            : 
     281         [ +  + ]:    9273135 :   for (std::size_t e=0; e<inpoel.size()/4; ++e) {
     282                 :            :     // access node IDs
     283                 :            :     const std::array< std::size_t, 4 >
     284                 :    9254852 :        N{{ inpoel[e*4+0], inpoel[e*4+1], inpoel[e*4+2], inpoel[e*4+3] }};
     285                 :            :      // compute element Jacobi determinant
     286                 :            :      const std::array< tk::real, 3 >
     287                 :    9254852 :        ba{{ x[N[1]]-x[N[0]], y[N[1]]-y[N[0]], z[N[1]]-z[N[0]] }},
     288                 :    9254852 :        ca{{ x[N[2]]-x[N[0]], y[N[2]]-y[N[0]], z[N[2]]-z[N[0]] }},
     289                 :    9254852 :        da{{ x[N[3]]-x[N[0]], y[N[3]]-y[N[0]], z[N[3]]-z[N[0]] }};
     290                 :    9254852 :      const auto J = tk::triple( ba, ca, da );   // J = 6V
     291 [ -  + ][ -  - ]:    9254852 :      Assert( J > 0, "Element Jacobian non-positive" );
         [ -  - ][ -  - ]
     292                 :            : 
     293                 :            :      // lumped - consistent mass
     294                 :            :      std::array< std::array< tk::real, 4 >, 4 > m;       // nnode*nnode [4][4]
     295                 :    9254852 :      m[0][0] = m[1][1] = m[2][2] = m[3][3] = 3.0*J/120.0;// diagonal
     296                 :    9254852 :      m[0][1] = m[0][2] = m[0][3] =                       // off-diagonal
     297                 :    9254852 :      m[1][0] = m[1][2] = m[1][3] =
     298                 :    9254852 :      m[2][0] = m[2][1] = m[2][3] =
     299                 :    9254852 :      m[3][0] = m[3][1] = m[3][2] = -J/120.0;
     300                 :            : 
     301                 :            :      // access solution at element nodes at time n
     302         [ +  - ]:   18509704 :      std::vector< std::array< tk::real, 4 > > un( ncomp );
     303 [ +  + ][ +  - ]:   23143444 :      for (ncomp_t c=0; c<ncomp; ++c) un[c] = Un.extract( c, 0, N );
     304                 :            :      // access pointer to mass diffusion right hand side at element nodes
     305         [ +  - ]:   18509704 :      std::vector< const tk::real* > d( ncomp );
     306 [ +  + ][ +  - ]:   23143444 :      for (ncomp_t c=0; c<ncomp; ++c) d[c] = D.cptr( c, 0 );
     307                 :            : 
     308                 :            :      // scatter-add mass diffusion element contributions to rhs nodes
     309         [ +  + ]:   46274260 :      for (std::size_t a=0; a<4; ++a) {
     310         [ +  + ]:   92573776 :        for (ncomp_t c=0; c<ncomp; ++c)
     311         [ +  + ]:  277771840 :          for (std::size_t b=0; b<4; ++b)
     312         [ +  - ]:  222217472 :            D.var(d[c],N[a]) -= ctau * m[a][b] * un[c][b];
     313                 :            :      }
     314                 :            :   }
     315                 :            : 
     316                 :      18283 :   return D;
     317                 :            : }
     318                 :            : 
     319                 :            : void
     320                 :      18283 : FluxCorrector::alw( const std::vector< std::size_t >& inpoel,
     321                 :            :                     const tk::Fields& Un,
     322                 :            :                     const tk::Fields& Ul,
     323                 :            :                     tk::Fields& Q ) const
     324                 :            : // *****************************************************************************
     325                 :            : //  Compute the maximum and minimum unknowns of elements surrounding nodes
     326                 :            : //! \param[in] inpoel Mesh element connectivity
     327                 :            : //! \param[in] Un Solution at the previous time step
     328                 :            : //! \param[in] Ul Low order solution
     329                 :            : //! \param[in,out] Q Maximum and mimimum unknowns of elements surrounding nodes
     330                 :            : // *****************************************************************************
     331                 :            : {
     332 [ +  - ][ +  - ]:      18283 :   Assert( Q.nunk() == Un.nunk() && Q.nprop() == Un.nprop()*2, "Max and min "
         [ -  - ][ -  - ]
                 [ -  - ]
     333                 :            :           "unknowns of elements surrounding nodes array size mismatch" );
     334                 :            : 
     335                 :      18283 :   auto ncomp = g_inputdeck.get< tag::component >().nprop();
     336                 :      18283 :   auto clip = g_inputdeck.get< tag::discr, tag::fctclip >();
     337                 :            : 
     338                 :            :   // compute maximum and minimum nodal values of all elements (Lohner: u^*_el)
     339         [ +  - ]:      36566 :   tk::Fields S( inpoel.size()/4, ncomp*2 );
     340         [ +  + ]:    9273135 :   for (std::size_t e=0; e<inpoel.size()/4; ++e) {
     341                 :    9254852 :     const std::array< std::size_t, 4 > N{{ inpoel[e*4+0], inpoel[e*4+1],
     342                 :    9254852 :                                            inpoel[e*4+2], inpoel[e*4+3] }};
     343         [ +  + ]:   23143444 :     for (ncomp_t c=0; c<ncomp; ++c) {
     344         [ +  - ]:   13888592 :       S(e,c*2+0,0) = -std::numeric_limits< tk::real >::max();
     345         [ +  - ]:   13888592 :       S(e,c*2+1,0) = std::numeric_limits< tk::real >::max();
     346         [ +  + ]:   69442960 :       for (std::size_t j=0; j<4; ++j) {
     347                 :            :         // compute maximum and minimum nodal values of Ul and Un (Lohner: u^*_i)
     348 [ -  + ][ -  - ]:   55554368 :         auto jmax = clip ? Ul(N[j],c,0) : std::max(Ul(N[j],c,0), Un(N[j],c,0));
         [ +  - ][ +  - ]
     349 [ -  + ][ -  - ]:   55554368 :         auto jmin = clip ? Ul(N[j],c,0) : std::min(Ul(N[j],c,0), Un(N[j],c,0));
         [ +  - ][ +  - ]
     350 [ +  - ][ +  + ]:   55554368 :         if (jmax > S(e,c*2+0,0)) S(e,c*2+0,0) = jmax;
                 [ +  - ]
     351 [ +  - ][ +  + ]:   55554368 :         if (jmin < S(e,c*2+1,0)) S(e,c*2+1,0) = jmin;
                 [ +  - ]
     352                 :            :       }
     353                 :            :     }
     354                 :            :   }
     355                 :            : 
     356                 :            :   // compute maximum and mimimum unknowns of all elements surrounding each node
     357                 :            :   // (Lohner: u^{max,min}_i)
     358         [ +  - ]:      36566 :   const auto esup = tk::genEsup( inpoel, 4 );
     359         [ +  + ]:    2755420 :   for (std::size_t p=0; p<Un.nunk(); ++p) {
     360         [ +  + ]:   39756545 :     for (auto e : tk::Around(esup,p)) {
     361         [ +  + ]:   92573776 :       for (ncomp_t c=0; c<ncomp; ++c) {
     362 [ +  - ][ +  - ]:   55554368 :         if (S(e,c*2+0,0) > Q(p,c*2+0,0)) Q(p,c*2+0,0) = S(e,c*2+0,0);
         [ +  + ][ +  - ]
                 [ +  - ]
     363 [ +  - ][ +  - ]:   55554368 :         if (S(e,c*2+1,0) < Q(p,c*2+1,0)) Q(p,c*2+1,0) = S(e,c*2+1,0);
         [ +  + ][ +  - ]
                 [ +  - ]
     364                 :            :       }
     365                 :            :     }
     366                 :            :   }
     367                 :      18283 : }
     368                 :            : 
     369                 :            : void
     370                 :      18283 : FluxCorrector::lim( const std::vector< std::size_t >& inpoel,
     371                 :            :                     const std::unordered_map< std::size_t,
     372                 :            :                             std::vector< std::pair< bool, tk::real > > >& bcdir,
     373                 :            :                     const tk::Fields& P,
     374                 :            :                     const tk::Fields& Ul,
     375                 :            :                     tk::Fields& Q,
     376                 :            :                     tk::Fields& A ) const
     377                 :            : // *****************************************************************************
     378                 :            : // Compute limited antiffusive element contributions and apply to mesh nodes
     379                 :            : //! \param[in] inpoel Mesh element connectivity
     380                 :            : //! \param[in] bcdir Vector of pairs of bool and boundary condition value
     381                 :            : //!   associated to mesh node IDs at which to set Dirichlet boundary conditions.
     382                 :            : //! \param[in] P The sums of all positive (negative) AECs to nodes
     383                 :            : //! \param[in] Ul Low order solution
     384                 :            : //! \param[in,out] Q The maximum and mimimum unknowns of elements surrounding
     385                 :            : //!   each node
     386                 :            : //! \param[in,out] A Limited antidiffusive element contributions scatter-added
     387                 :            : //!   to nodes
     388                 :            : //! \note Q is also overwritten to avoid using temporary memory
     389                 :            : // *****************************************************************************
     390                 :            : {
     391 [ +  - ][ +  - ]:      18283 :   Assert( P.nunk() == Q.nunk() && P.nprop() == Q.nprop(), "Size mismatch" );
         [ -  - ][ -  - ]
                 [ -  - ]
     392 [ +  - ][ +  - ]:      18283 :   Assert( P.nunk() == Ul.nunk() && P.nprop() == Ul.nprop()*2, "Size mismatch" );
         [ -  - ][ -  - ]
                 [ -  - ]
     393 [ +  - ][ +  - ]:      18283 :   Assert( A.nunk() == Ul.nunk() && A.nprop() == Ul.nprop(), "Size mismatch" );
         [ -  - ][ -  - ]
                 [ -  - ]
     394                 :            : 
     395                 :      18283 :   auto ncomp = g_inputdeck.get< tag::component >().nprop();
     396                 :            : 
     397                 :            :   // compute the maximum and minimum increments and decrements nodal solution
     398                 :            :   // values are allowed to achieve (Lohner: Q^{+,-}_i)
     399         [ +  + ]:    2755420 :   for (std::size_t p=0; p<Ul.nunk(); ++p)
     400         [ +  + ]:    7596754 :     for (ncomp_t c=0; c<ncomp; ++c) {
     401 [ +  - ][ +  - ]:    4859617 :       Q(p,c*2+0,0) -= Ul(p,c,0);
     402 [ +  - ][ +  - ]:    4859617 :       Q(p,c*2+1,0) -= Ul(p,c,0);
     403                 :            :     }
     404                 :            : 
     405                 :      18283 :   auto eps = g_inputdeck.get< tag::discr, tag::fcteps >();
     406                 :            : 
     407                 :            :   // compute the ratios of positive and negative element contributions that
     408                 :            :   // ensure monotonicity (Lohner: R^{+,-})
     409         [ +  + ]:    2755420 :   for (std::size_t p=0; p<P.nunk(); ++p) {
     410         [ +  + ]:    7596754 :     for (ncomp_t c=0; c<ncomp; ++c) {
     411                 :            : 
     412 [ +  - ][ +  + ]:    4859617 :       if (P(p,c*2+0,0) < eps)
     413         [ +  - ]:    3077098 :         Q(p,c*2+0,0) = 1.0;
     414                 :            :       else
     415 [ +  - ][ +  - ]:    1782519 :         Q(p,c*2+0,0) = std::min(1.0,Q(p,c*2+0,0)/P(p,c*2+0,0));
                 [ +  - ]
     416                 :            : 
     417 [ +  - ][ +  + ]:    4859617 :       if (P(p,c*2+1,0) > -eps)
     418         [ +  - ]:    2665865 :         Q(p,c*2+1,0) = 1.0;
     419                 :            :       else
     420 [ +  - ][ +  - ]:    2193752 :         Q(p,c*2+1,0) = std::min(1.0,Q(p,c*2+1,0)/P(p,c*2+1,0));
                 [ +  - ]
     421                 :            : 
     422                 :            :     }
     423                 :            :   }
     424                 :            : 
     425                 :            :   // calculate limit coefficient for all elements (Lohner: C_el)
     426         [ +  - ]:      36566 :   tk::Fields C( inpoel.size()/4, ncomp );
     427         [ +  + ]:    9273135 :   for (std::size_t e=0; e<inpoel.size()/4; ++e) {
     428                 :    9254852 :     const std::array< std::size_t, 4 > N{{ inpoel[e*4+0], inpoel[e*4+1],
     429                 :    9254852 :                                            inpoel[e*4+2], inpoel[e*4+3] }};
     430         [ +  + ]:   23143444 :     for (ncomp_t c=0; c<ncomp; ++c) {
     431                 :            :       std::array< tk::real, 4 > R;
     432         [ +  + ]:   69442960 :       for (std::size_t j=0; j<4; ++j) {
     433                 :            : 
     434 [ +  - ][ +  + ]:   55554368 :         if (std::abs(m_aec(e*4+j,c,0)) < eps)
     435                 :   23434103 :           R[j] = 1.0;
     436 [ +  - ][ +  + ]:   32120265 :         else if (m_aec(e*4+j,c,0) > 0.0)
     437         [ +  - ]:   13060745 :           R[j] = Q(N[j],c*2+0,0);
     438                 :            :         else
     439         [ +  - ]:   19059520 :           R[j] = Q(N[j],c*2+1,0);
     440                 :            : 
     441                 :            :       }
     442 [ +  - ][ +  - ]:   13888592 :       C(e,c,0) = *std::min_element( begin(R), end(R) );
     443                 :            :       // if all vertices happened to be on a Dirichlet boundary, ignore limiting
     444 [ +  - ][ -  + ]:   13888592 :       if (C(e,c,0) > 1.0) C(e,c,0) = 1.0;
                 [ -  - ]
     445 [ +  - ][ +  - ]:   13888592 :       Assert( C(e,c,0) > -eps && C(e,c,0) < 1.0+eps,
         [ +  - ][ +  - ]
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
                 [ -  - ]
     446                 :            :               "0 <= AEC <= 1.0 failed: C = " + std::to_string(C(e,c,0)) );
     447                 :            :     }
     448                 :            :   }
     449                 :            : 
     450                 :            :   // System limiting
     451         [ +  + ]:    9273135 :   for (std::size_t e=0; e<inpoel.size()/4; ++e) {
     452         [ +  + ]:    9315492 :     for (const auto& sys : m_sys) {
     453                 :      60640 :       tk::real cs = 1.0;
     454 [ +  + ][ +  - ]:     363840 :       for (auto i : sys) if (C(e,i,0) < cs) cs = C(e,i,0);
         [ +  + ][ +  - ]
     455 [ +  + ][ +  - ]:     363840 :       for (auto i : sys) C(e,i,0) = cs;
     456                 :            :     }
     457                 :            :   }
     458                 :            : 
     459                 :            :   // save the limited antidiffusive element contributions (Lohner: AEC^c)
     460         [ +  + ]:    9273135 :   for (std::size_t e=0; e<inpoel.size()/4; ++e) {
     461                 :    9254852 :     const std::array< std::size_t, 4 > N{{ inpoel[e*4+0], inpoel[e*4+1],
     462                 :    9254852 :                                            inpoel[e*4+2], inpoel[e*4+3] }};
     463                 :            : 
     464                 :            :     // access pointer to solution at element nodes
     465         [ +  - ]:   18509704 :     std::vector< const tk::real* > a( ncomp );
     466 [ +  + ][ +  - ]:   23143444 :     for (ncomp_t c=0; c<ncomp; ++c) a[c] = A.cptr( c, 0 );
     467                 :            : 
     468                 :            :     // Scatter-add limited antidiffusive element contributions to nodes. At
     469                 :            :     // nodes where Dirichlet boundary conditions are set, the AECs are set to
     470                 :            :     // zero so the limit coefficient has no effect. This yields no increment for
     471                 :            :     // those nodes. See the detailed discussion when computing the AECs.
     472         [ +  + ]:   46274260 :     for (std::size_t j=0; j<4; ++j) {
     473         [ +  - ]:   37019408 :       auto b = bcdir.find( N[j] );    // Dirichlet BC
     474         [ +  + ]:   92573776 :       for (ncomp_t c=0; c<ncomp; ++c) {
     475 [ +  + ][ +  - ]:   55554368 :         if (b != end(bcdir) && b->second[c].first) {
                 [ +  + ]
     476 [ +  - ][ +  - ]:   14546180 :           A.var(a[c],N[j]) += m_aec(e*4+j,c,0);
     477                 :            :         } else {
     478 [ +  - ][ +  - ]:   41008188 :           A.var(a[c],N[j]) += C(e,c,0) * m_aec(e*4+j,c,0);
                 [ +  - ]
     479                 :            :         }
     480                 :            :       }
     481                 :            :     }
     482                 :            :   }
     483                 :      18283 : }
     484                 :            : 
     485                 :            : std::tuple< std::vector< std::string >,
     486                 :            :             std::vector< std::vector< tk::real > > >
     487                 :       2233 : FluxCorrector::fields( const std::vector< std::size_t >& /*inpoel*/ ) const
     488                 :            : // *****************************************************************************
     489                 :            : //  Collect mesh output fields from FCT
     490                 :            : //! \return Names and fields in mesh cells
     491                 :            : // *****************************************************************************
     492                 :            : {
     493                 :            :   using tuple_t = std::tuple< std::vector< std::string >,
     494                 :            :                               std::vector< std::vector< tk::real > > >;
     495                 :       2233 :   return tuple_t{};
     496                 :            : }

Generated by: LCOV version 1.14