Quinoa all test code coverage report
Current view: top level - PDE - Limiter.cpp (source / functions) Hit Total Coverage
Commit: -128-NOTFOUND Lines: 775 1134 68.3 %
Date: 2025-05-30 09:21:53 Functions: 22 28 78.6 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 570 1424 40.0 %

           Branch data     Line data    Source code
       1                 :            : // *****************************************************************************
       2                 :            : /*!
       3                 :            :   \file      src/PDE/Limiter.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     Limiters for discontiunous Galerkin methods
       9                 :            :   \details   This file contains functions that provide limiter function
      10                 :            :     calculations for maintaining monotonicity near solution discontinuities
      11                 :            :     for the DG discretization.
      12                 :            : */
      13                 :            : // *****************************************************************************
      14                 :            : 
      15                 :            : #include <array>
      16                 :            : #include <vector>
      17                 :            : 
      18                 :            : #include "FaceData.hpp"
      19                 :            : #include "Vector.hpp"
      20                 :            : #include "Limiter.hpp"
      21                 :            : #include "DerivedData.hpp"
      22                 :            : #include "Integrate/Quadrature.hpp"
      23                 :            : #include "Integrate/Basis.hpp"
      24                 :            : #include "Inciter/InputDeck/InputDeck.hpp"
      25                 :            : #include "PrefIndicator.hpp"
      26                 :            : #include "Reconstruction.hpp"
      27                 :            : #include "Integrate/Mass.hpp"
      28                 :            : #include "MultiMat/MiscMultiMatFns.hpp"
      29                 :            : #include "MultiSpecies/MultiSpeciesIndexing.hpp"
      30                 :            : #include "MultiSpecies/Mixture/Mixture.hpp"
      31                 :            : 
      32                 :            : namespace inciter {
      33                 :            : 
      34                 :            : extern ctr::InputDeck g_inputdeck;
      35                 :            : 
      36                 :            : void
      37                 :       6000 : WENO_P1( const std::vector< int >& esuel,
      38                 :            :          tk::Fields& U )
      39                 :            : // *****************************************************************************
      40                 :            : //  Weighted Essentially Non-Oscillatory (WENO) limiter for DGP1
      41                 :            : //! \param[in] esuel Elements surrounding elements
      42                 :            : //! \param[in,out] U High-order solution vector which gets limited
      43                 :            : //! \details This WENO function should be called for transport and compflow
      44                 :            : //! \note This limiter function is experimental and untested. Use with caution.
      45                 :            : // *****************************************************************************
      46                 :            : {
      47                 :       6000 :   const auto rdof = inciter::g_inputdeck.get< tag::rdof >();
      48                 :       6000 :   const auto cweight = inciter::g_inputdeck.get< tag::cweight >();
      49                 :       6000 :   auto nelem = esuel.size()/4;
      50                 :            :   std::array< std::vector< tk::real >, 3 >
      51                 :            :     limU {{ std::vector< tk::real >(nelem),
      52                 :            :             std::vector< tk::real >(nelem),
      53 [ +  - ][ +  - ]:      12000 :             std::vector< tk::real >(nelem) }};
                 [ +  - ]
      54                 :            : 
      55                 :       6000 :   std::size_t ncomp = U.nprop()/rdof;
      56                 :            : 
      57         [ +  + ]:      12000 :   for (inciter::ncomp_t c=0; c<ncomp; ++c)
      58                 :            :   {
      59         [ +  + ]:     552450 :     for (std::size_t e=0; e<nelem; ++e)
      60                 :            :     {
      61         [ +  - ]:     546450 :       WENOLimiting(U, esuel, e, c, rdof, cweight, limU);
      62                 :            :     }
      63                 :            : 
      64                 :       6000 :     auto mark = c*rdof;
      65                 :            : 
      66         [ +  + ]:     552450 :     for (std::size_t e=0; e<nelem; ++e)
      67                 :            :     {
      68         [ +  - ]:     546450 :       U(e, mark+1) = limU[0][e];
      69         [ +  - ]:     546450 :       U(e, mark+2) = limU[1][e];
      70         [ +  - ]:     546450 :       U(e, mark+3) = limU[2][e];
      71                 :            :     }
      72                 :            :   }
      73                 :       6000 : }
      74                 :            : 
      75                 :            : void
      76                 :      14400 : Superbee_P1( const std::vector< int >& esuel,
      77                 :            :              const std::vector< std::size_t >& inpoel,
      78                 :            :              const std::vector< std::size_t >& ndofel,
      79                 :            :              const tk::UnsMesh::Coords& coord,
      80                 :            :              tk::Fields& U )
      81                 :            : // *****************************************************************************
      82                 :            : //  Superbee limiter for DGP1
      83                 :            : //! \param[in] esuel Elements surrounding elements
      84                 :            : //! \param[in] inpoel Element connectivity
      85                 :            : //! \param[in] ndofel Vector of local number of degrees of freedom
      86                 :            : //! \param[in] coord Array of nodal coordinates
      87                 :            : //! \param[in,out] U High-order solution vector which gets limited
      88                 :            : //! \details This Superbee function should be called for transport and compflow
      89                 :            : // *****************************************************************************
      90                 :            : {
      91                 :      14400 :   const auto rdof = inciter::g_inputdeck.get< tag::rdof >();
      92                 :      14400 :   const auto ndof = inciter::g_inputdeck.get< tag::ndof >();
      93                 :      14400 :   std::size_t ncomp = U.nprop()/rdof;
      94                 :            : 
      95                 :      14400 :   auto beta_lim = 2.0;
      96                 :            : 
      97         [ +  + ]:    2965230 :   for (std::size_t e=0; e<esuel.size()/4; ++e)
      98                 :            :   {
      99                 :            :     // If an rDG method is set up (P0P1), then, currently we compute the P1
     100                 :            :     // basis functions and solutions by default. This implies that P0P1 is
     101                 :            :     // unsupported in the p-adaptive DG (PDG). This is a workaround until we
     102                 :            :     // have rdofel, which is needed to distinguish between ndofs and rdofs per
     103                 :            :     // element for pDG.
     104                 :            :     std::size_t dof_el;
     105         [ -  + ]:    2950830 :     if (rdof > ndof)
     106                 :            :     {
     107                 :          0 :       dof_el = rdof;
     108                 :            :     }
     109                 :            :     else
     110                 :            :     {
     111                 :    2950830 :       dof_el = ndofel[e];
     112                 :            :     }
     113                 :            : 
     114         [ +  + ]:    2950830 :     if (dof_el > 1)
     115                 :            :     {
     116                 :            :       auto phi = SuperbeeLimiting(U, esuel, inpoel, coord, e, ndof, rdof,
     117         [ +  - ]:    5324238 :                    dof_el, ncomp, beta_lim);
     118                 :            : 
     119                 :            :       // apply limiter function
     120         [ +  + ]:    7229514 :       for (inciter::ncomp_t c=0; c<ncomp; ++c)
     121                 :            :       {
     122                 :    4567395 :         auto mark = c*rdof;
     123 [ +  - ][ +  - ]:    4567395 :         U(e, mark+1) = phi[c] * U(e, mark+1);
     124 [ +  - ][ +  - ]:    4567395 :         U(e, mark+2) = phi[c] * U(e, mark+2);
     125 [ +  - ][ +  - ]:    4567395 :         U(e, mark+3) = phi[c] * U(e, mark+3);
     126                 :            :       }
     127                 :            :     }
     128                 :            :   }
     129                 :      14400 : }
     130                 :            : 
     131                 :            : void
     132                 :          0 : SuperbeeMultiMat_P1(
     133                 :            :   const std::vector< int >& esuel,
     134                 :            :   const std::vector< std::size_t >& inpoel,
     135                 :            :   const std::vector< std::size_t >& ndofel,
     136                 :            :   const tk::UnsMesh::Coords& coord,
     137                 :            :   const std::vector< std::size_t >& solidx,
     138                 :            :   tk::Fields& U,
     139                 :            :   tk::Fields& P,
     140                 :            :   std::size_t nmat )
     141                 :            : // *****************************************************************************
     142                 :            : //  Superbee limiter for multi-material DGP1
     143                 :            : //! \param[in] esuel Elements surrounding elements
     144                 :            : //! \param[in] inpoel Element connectivity
     145                 :            : //! \param[in] ndofel Vector of local number of degrees of freedom
     146                 :            : //! \param[in] coord Array of nodal coordinates
     147                 :            : //! \param[in] solidx Solid material index indicator
     148                 :            : //! \param[in,out] U High-order solution vector which gets limited
     149                 :            : //! \param[in,out] P High-order vector of primitives which gets limited
     150                 :            : //! \param[in] nmat Number of materials in this PDE system
     151                 :            : //! \details This Superbee function should be called for multimat
     152                 :            : // *****************************************************************************
     153                 :            : {
     154                 :          0 :   const auto rdof = inciter::g_inputdeck.get< tag::rdof >();
     155                 :          0 :   const auto ndof = inciter::g_inputdeck.get< tag::ndof >();
     156                 :            :   const auto intsharp = inciter::g_inputdeck.get< tag::multimat,
     157                 :          0 :     tag::intsharp >();
     158                 :          0 :   std::size_t ncomp = U.nprop()/rdof;
     159                 :          0 :   std::size_t nprim = P.nprop()/rdof;
     160                 :            : 
     161                 :          0 :   auto beta_lim = 2.0;
     162                 :            : 
     163         [ -  - ]:          0 :   for (std::size_t e=0; e<esuel.size()/4; ++e)
     164                 :            :   {
     165                 :            :     // If an rDG method is set up (P0P1), then, currently we compute the P1
     166                 :            :     // basis functions and solutions by default. This implies that P0P1 is
     167                 :            :     // unsupported in the p-adaptive DG (PDG). This is a workaround until we
     168                 :            :     // have rdofel, which is needed to distinguish between ndofs and rdofs per
     169                 :            :     // element for pDG.
     170                 :            :     std::size_t dof_el;
     171         [ -  - ]:          0 :     if (rdof > ndof)
     172                 :            :     {
     173                 :          0 :       dof_el = rdof;
     174                 :            :     }
     175                 :            :     else
     176                 :            :     {
     177                 :          0 :       dof_el = ndofel[e];
     178                 :            :     }
     179                 :            : 
     180         [ -  - ]:          0 :     if (dof_el > 1)
     181                 :            :     {
     182                 :            :       // limit conserved quantities
     183                 :            :       auto phic = SuperbeeLimiting(U, esuel, inpoel, coord, e, ndof, rdof,
     184         [ -  - ]:          0 :                     dof_el, ncomp, beta_lim);
     185                 :            :       // limit primitive quantities
     186                 :            :       auto phip = SuperbeeLimiting(P, esuel, inpoel, coord, e, ndof, rdof,
     187         [ -  - ]:          0 :                     dof_el, nprim, beta_lim);
     188                 :            : 
     189                 :          0 :       std::vector< tk::real > phic_p2;
     190                 :          0 :       std::vector< std::vector< tk::real > > unk, prim;
     191         [ -  - ]:          0 :       if(ndof > 1)
     192         [ -  - ]:          0 :         BoundPreservingLimiting(nmat, ndof, e, inpoel, coord, U, phic,
     193                 :            :           phic_p2);
     194                 :            : 
     195                 :            :       // limits under which compression is to be performed
     196         [ -  - ]:          0 :       std::vector< std::size_t > matInt(nmat, 0);
     197         [ -  - ]:          0 :       std::vector< tk::real > alAvg(nmat, 0.0);
     198         [ -  - ]:          0 :       for (std::size_t k=0; k<nmat; ++k)
     199         [ -  - ]:          0 :         alAvg[k] = U(e, volfracDofIdx(nmat,k,rdof,0));
     200         [ -  - ]:          0 :       auto intInd = interfaceIndicator(nmat, alAvg, matInt);
     201 [ -  - ][ -  - ]:          0 :       if ((intsharp > 0) && intInd)
     202                 :            :       {
     203         [ -  - ]:          0 :         for (std::size_t k=0; k<nmat; ++k)
     204                 :            :         {
     205         [ -  - ]:          0 :           if (matInt[k])
     206                 :          0 :             phic[volfracIdx(nmat,k)] = 1.0;
     207                 :          0 :         }
     208                 :            :       }
     209                 :            :       else
     210                 :            :       {
     211         [ -  - ]:          0 :         if (!g_inputdeck.get< tag::accuracy_test >())
     212         [ -  - ]:          0 :           consistentMultiMatLimiting_P1(nmat, rdof, e, solidx, U, P, phic,
     213                 :            :             phic_p2);
     214                 :            :       }
     215                 :            : 
     216                 :            :       // apply limiter function
     217         [ -  - ]:          0 :       for (inciter::ncomp_t c=0; c<ncomp; ++c)
     218                 :            :       {
     219                 :          0 :         auto mark = c*rdof;
     220 [ -  - ][ -  - ]:          0 :         U(e, mark+1) = phic[c] * U(e, mark+1);
     221 [ -  - ][ -  - ]:          0 :         U(e, mark+2) = phic[c] * U(e, mark+2);
     222 [ -  - ][ -  - ]:          0 :         U(e, mark+3) = phic[c] * U(e, mark+3);
     223                 :            :       }
     224         [ -  - ]:          0 :       for (inciter::ncomp_t c=0; c<nprim; ++c)
     225                 :            :       {
     226                 :          0 :         auto mark = c*rdof;
     227 [ -  - ][ -  - ]:          0 :         P(e, mark+1) = phip[c] * P(e, mark+1);
     228 [ -  - ][ -  - ]:          0 :         P(e, mark+2) = phip[c] * P(e, mark+2);
     229 [ -  - ][ -  - ]:          0 :         P(e, mark+3) = phip[c] * P(e, mark+3);
     230                 :            :       }
     231                 :            :     }
     232                 :            :   }
     233                 :          0 : }
     234                 :            : 
     235                 :            : void
     236                 :          0 : VertexBasedTransport_P1(
     237                 :            :   const std::map< std::size_t, std::vector< std::size_t > >& esup,
     238                 :            :   const std::vector< std::size_t >& inpoel,
     239                 :            :   const std::vector< std::size_t >& ndofel,
     240                 :            :   std::size_t nelem,
     241                 :            :   const tk::UnsMesh::Coords& coord,
     242                 :            :   tk::Fields& U )
     243                 :            : // *****************************************************************************
     244                 :            : //  Kuzmin's vertex-based limiter for transport DGP1
     245                 :            : //! \param[in] esup Elements surrounding points
     246                 :            : //! \param[in] inpoel Element connectivity
     247                 :            : //! \param[in] ndofel Vector of local number of degrees of freedom
     248                 :            : //! \param[in] nelem Number of elements
     249                 :            : //! \param[in] coord Array of nodal coordinates
     250                 :            : //! \param[in,out] U High-order solution vector which gets limited
     251                 :            : //! \details This vertex-based limiter function should be called for transport.
     252                 :            : //!   For details see: Kuzmin, D. (2010). A vertex-based hierarchical slope
     253                 :            : //!   limiter for p-adaptive discontinuous Galerkin methods. Journal of
     254                 :            : //!   computational and applied mathematics, 233(12), 3077-3085.
     255                 :            : // *****************************************************************************
     256                 :            : {
     257                 :          0 :   const auto rdof = inciter::g_inputdeck.get< tag::rdof >();
     258                 :          0 :   const auto ndof = inciter::g_inputdeck.get< tag::ndof >();
     259                 :            :   const auto intsharp = inciter::g_inputdeck.get< tag::transport,
     260                 :          0 :     tag::intsharp >();
     261                 :          0 :   std::size_t ncomp = U.nprop()/rdof;
     262                 :            : 
     263         [ -  - ]:          0 :   for (std::size_t e=0; e<nelem; ++e)
     264                 :            :   {
     265                 :            :     // If an rDG method is set up (P0P1), then, currently we compute the P1
     266                 :            :     // basis functions and solutions by default. This implies that P0P1 is
     267                 :            :     // unsupported in the p-adaptive DG (PDG). This is a workaround until we
     268                 :            :     // have rdofel, which is needed to distinguish between ndofs and rdofs per
     269                 :            :     // element for pDG.
     270                 :            :     std::size_t dof_el;
     271         [ -  - ]:          0 :     if (rdof > ndof)
     272                 :            :     {
     273                 :          0 :       dof_el = rdof;
     274                 :            :     }
     275                 :            :     else
     276                 :            :     {
     277                 :          0 :       dof_el = ndofel[e];
     278                 :            :     }
     279                 :            : 
     280         [ -  - ]:          0 :     if (dof_el > 1)
     281                 :            :     {
     282         [ -  - ]:          0 :       std::vector< tk::real > phi(ncomp, 1.0);
     283                 :          0 :       std::vector< std::size_t > var;
     284 [ -  - ][ -  - ]:          0 :       for (std::size_t c=0; c<ncomp; ++c) var.push_back(c);
     285                 :            :       // limit conserved quantities
     286         [ -  - ]:          0 :       VertexBasedLimiting(U, esup, inpoel, coord, e, rdof, dof_el,
     287                 :            :         ncomp, phi, var);
     288                 :            : 
     289                 :            :       // limits under which compression is to be performed
     290         [ -  - ]:          0 :       std::vector< std::size_t > matInt(ncomp, 0);
     291         [ -  - ]:          0 :       std::vector< tk::real > alAvg(ncomp, 0.0);
     292         [ -  - ]:          0 :       for (std::size_t k=0; k<ncomp; ++k)
     293         [ -  - ]:          0 :         alAvg[k] = U(e,k*rdof);
     294         [ -  - ]:          0 :       auto intInd = interfaceIndicator(ncomp, alAvg, matInt);
     295 [ -  - ][ -  - ]:          0 :       if ((intsharp > 0) && intInd)
     296                 :            :       {
     297         [ -  - ]:          0 :         for (std::size_t k=0; k<ncomp; ++k)
     298                 :            :         {
     299         [ -  - ]:          0 :           if (matInt[k]) phi[k] = 1.0;
     300                 :            :         }
     301                 :            :       }
     302                 :            : 
     303                 :            :       // apply limiter function
     304         [ -  - ]:          0 :       for (std::size_t c=0; c<ncomp; ++c)
     305                 :            :       {
     306                 :          0 :         auto mark = c*rdof;
     307 [ -  - ][ -  - ]:          0 :         U(e, mark+1) = phi[c] * U(e, mark+1);
     308 [ -  - ][ -  - ]:          0 :         U(e, mark+2) = phi[c] * U(e, mark+2);
     309 [ -  - ][ -  - ]:          0 :         U(e, mark+3) = phi[c] * U(e, mark+3);
     310                 :            :       }
     311                 :            :     }
     312                 :            :   }
     313                 :          0 : }
     314                 :            : 
     315                 :            : void
     316                 :          0 : VertexBasedCompflow_P1(
     317                 :            :   const std::map< std::size_t, std::vector< std::size_t > >& esup,
     318                 :            :   const std::vector< std::size_t >& inpoel,
     319                 :            :   const std::vector< std::size_t >& ndofel,
     320                 :            :   std::size_t nelem,
     321                 :            :   const std::vector< inciter::EOS >& mat_blk,
     322                 :            :   const inciter::FaceData& fd,
     323                 :            :   const tk::Fields& geoFace,
     324                 :            :   const tk::Fields& geoElem,
     325                 :            :   const tk::UnsMesh::Coords& coord,
     326                 :            :   const tk::FluxFn& flux,
     327                 :            :   const std::vector< std::size_t >& solidx,
     328                 :            :   tk::Fields& U,
     329                 :            :   std::vector< std::size_t >& shockmarker )
     330                 :            : // *****************************************************************************
     331                 :            : //  Kuzmin's vertex-based limiter for single-material DGP1
     332                 :            : //! \param[in] esup Elements surrounding points
     333                 :            : //! \param[in] inpoel Element connectivity
     334                 :            : //! \param[in] ndofel Vector of local number of degrees of freedom
     335                 :            : //! \param[in] nelem Number of elements
     336                 :            : //! \param[in] mat_blk EOS material block
     337                 :            : //! \param[in] fd Face connectivity and boundary conditions object
     338                 :            : //! \param[in] geoFace Face geometry array
     339                 :            : // //! \param[in] geoElem Element geometry array
     340                 :            : //! \param[in] coord Array of nodal coordinates
     341                 :            : //! \param[in] flux Riemann flux function to use
     342                 :            : //! \param[in] solidx Solid material index indicator
     343                 :            : //! \param[in,out] U High-order solution vector which gets limited
     344                 :            : //! \param[in,out] shockmarker Shock detection marker array
     345                 :            : //! \details This vertex-based limiter function should be called for compflow.
     346                 :            : //!   For details see: Kuzmin, D. (2010). A vertex-based hierarchical slope
     347                 :            : //!   limiter for p-adaptive discontinuous Galerkin methods. Journal of
     348                 :            : //!   computational and applied mathematics, 233(12), 3077-3085.
     349                 :            : // *****************************************************************************
     350                 :            : {
     351                 :          0 :   const auto rdof = inciter::g_inputdeck.get< tag::rdof >();
     352                 :          0 :   const auto ndof = inciter::g_inputdeck.get< tag::ndof >();
     353                 :          0 :   std::size_t ncomp = U.nprop()/rdof;
     354                 :            : 
     355                 :            :   // Null field for MarkShockCells argument
     356                 :          0 :   tk::Fields P;
     357                 :            : 
     358         [ -  - ]:          0 :   if (inciter::g_inputdeck.get< tag::shock_detector_coeff >() > 1e-6) {
     359                 :            :     // Indicator based on momentum flux jump
     360                 :          0 :     std::set< std::size_t > vars;
     361 [ -  - ][ -  - ]:          0 :     for (std::size_t i=1; i<=3; ++i) vars.insert(i);
     362         [ -  - ]:          0 :     MarkShockCells(false, nelem, 1, ndof, rdof, mat_blk, ndofel,
     363                 :            :       inpoel, coord, fd, geoFace, geoElem, flux, solidx, U, P,
     364                 :            :       vars, shockmarker);
     365                 :            :   }
     366                 :            : 
     367         [ -  - ]:          0 :   for (std::size_t e=0; e<nelem; ++e)
     368                 :            :   {
     369                 :            :     // If an rDG method is set up (P0P1), then, currently we compute the P1
     370                 :            :     // basis functions and solutions by default. This implies that P0P1 is
     371                 :            :     // unsupported in the p-adaptive DG (PDG). This is a workaround until we
     372                 :            :     // have rdofel, which is needed to distinguish between ndofs and rdofs per
     373                 :            :     // element for pDG.
     374                 :            :     std::size_t dof_el;
     375         [ -  - ]:          0 :     if (rdof > ndof)
     376                 :            :     {
     377                 :          0 :       dof_el = rdof;
     378                 :            :     }
     379                 :            :     else
     380                 :            :     {
     381                 :          0 :       dof_el = ndofel[e];
     382                 :            :     }
     383                 :            : 
     384 [ -  - ][ -  - ]:          0 :     if (dof_el > 1 && shockmarker[e])
                 [ -  - ]
     385                 :            :     {
     386         [ -  - ]:          0 :       std::vector< tk::real > phi(ncomp, 1.0);
     387                 :          0 :       std::vector< std::size_t > var;
     388 [ -  - ][ -  - ]:          0 :       for (std::size_t c=0; c<ncomp; ++c) var.push_back(c);
     389                 :            :       // limit conserved quantities
     390         [ -  - ]:          0 :       VertexBasedLimiting(U, esup, inpoel, coord, e, rdof, dof_el,
     391                 :            :         ncomp, phi, var);
     392                 :            : 
     393                 :            :       // apply limiter function
     394         [ -  - ]:          0 :       for (std::size_t c=0; c<ncomp; ++c)
     395                 :            :       {
     396                 :          0 :         auto mark = c*rdof;
     397 [ -  - ][ -  - ]:          0 :         U(e, mark+1) = phi[c] * U(e, mark+1);
     398 [ -  - ][ -  - ]:          0 :         U(e, mark+2) = phi[c] * U(e, mark+2);
     399 [ -  - ][ -  - ]:          0 :         U(e, mark+3) = phi[c] * U(e, mark+3);
     400                 :            :       }
     401                 :            :     }
     402                 :            :   }
     403                 :          0 : }
     404                 :            : 
     405                 :            : void
     406                 :       2580 : VertexBasedCompflow_P2(
     407                 :            :   const std::map< std::size_t, std::vector< std::size_t > >& esup,
     408                 :            :   const std::vector< std::size_t >& inpoel,
     409                 :            :   const std::vector< std::size_t >& ndofel,
     410                 :            :   std::size_t nelem,
     411                 :            :   const std::vector< inciter::EOS >& mat_blk,
     412                 :            :   const inciter::FaceData& fd,
     413                 :            :   const tk::Fields& geoFace,
     414                 :            :   const tk::Fields& geoElem,
     415                 :            :   const tk::UnsMesh::Coords& coord,
     416                 :            :   [[maybe_unused]] const std::vector< std::size_t >& gid,
     417                 :            :   [[maybe_unused]] const std::unordered_map< std::size_t, std::size_t >& bid,
     418                 :            :   [[maybe_unused]] const std::vector< std::vector<tk::real> >& uNodalExtrm,
     419                 :            :   [[maybe_unused]] const std::vector< std::vector<tk::real> >& mtInv,
     420                 :            :   const tk::FluxFn& flux,
     421                 :            :   const std::vector< std::size_t >& solidx,
     422                 :            :   tk::Fields& U,
     423                 :            :   std::vector< std::size_t >& shockmarker )
     424                 :            : // *****************************************************************************
     425                 :            : //  Kuzmin's vertex-based limiter on reference element for single-material DGP2
     426                 :            : //! \param[in] esup Elements surrounding points
     427                 :            : //! \param[in] inpoel Element connectivity
     428                 :            : //! \param[in] ndofel Vector of local number of degrees of freedom
     429                 :            : //! \param[in] nelem Number of elements
     430                 :            : //! \param[in] mat_blk EOS material block
     431                 :            : //! \param[in] fd Face connectivity and boundary conditions object
     432                 :            : //! \param[in] geoFace Face geometry array
     433                 :            : // //! \param[in] geoElem Element geometry array
     434                 :            : //! \param[in] coord Array of nodal coordinates
     435                 :            : //! \param[in] gid Local->global node id map
     436                 :            : //! \param[in] bid Local chare-boundary node ids (value) associated to
     437                 :            : //!   global node ids (key)
     438                 :            : //! \param[in] uNodalExtrm Chare-boundary nodal extrema for conservative
     439                 :            : //!   variables
     440                 :            : //! \param[in] mtInv Inverse of Taylor mass matrix
     441                 :            : //! \param[in] flux Riemann flux function to use
     442                 :            : //! \param[in] solidx Solid material index indicator
     443                 :            : //! \param[in,out] U High-order solution vector which gets limited
     444                 :            : //! \param[in,out] shockmarker Shock detection marker array
     445                 :            : //! \details This vertex-based limiter function should be called for compflow.
     446                 :            : //!   For details see: Kuzmin, D. (2010). A vertex-based hierarchical slope
     447                 :            : //!   limiter for p-adaptive discontinuous Galerkin methods. Journal of
     448                 :            : //!   computational and applied mathematics, 233(12), 3077-3085.
     449                 :            : // *****************************************************************************
     450                 :            : {
     451                 :       2580 :   const auto rdof = inciter::g_inputdeck.get< tag::rdof >();
     452                 :       2580 :   const auto ndof = inciter::g_inputdeck.get< tag::ndof >();
     453                 :       2580 :   std::size_t ncomp = U.nprop()/rdof;
     454                 :            : 
     455                 :            :   // Null field for MarkShockCells argument
     456                 :       5160 :   tk::Fields P;
     457                 :            : 
     458         [ +  - ]:       2580 :   if (inciter::g_inputdeck.get< tag::shock_detector_coeff >() > 1e-6) {
     459                 :            :     // Indicator based on momentum flux jump
     460                 :       5160 :     std::set< std::size_t > vars;
     461 [ +  + ][ +  - ]:      10320 :     for (std::size_t i=1; i<=3; ++i) vars.insert(i);
     462         [ +  - ]:       2580 :     MarkShockCells(false, nelem, 1, ndof, rdof, mat_blk, ndofel,
     463                 :            :       inpoel, coord, fd, geoFace, geoElem, flux, solidx, U, P,
     464                 :            :       vars, shockmarker);
     465                 :            :   }
     466                 :            : 
     467         [ +  + ]:     366420 :   for (std::size_t e=0; e<nelem; ++e)
     468                 :            :   {
     469                 :            :     // If an rDG method is set up (P0P1), then, currently we compute the P1
     470                 :            :     // basis functions and solutions by default. This implies that P0P1 is
     471                 :            :     // unsupported in the p-adaptive DG (PDG). This is a workaround until we
     472                 :            :     // have rdofel, which is needed to distinguish between ndofs and rdofs per
     473                 :            :     // element for pDG.
     474                 :            :     std::size_t dof_el;
     475         [ -  + ]:     363840 :     if (rdof > ndof)
     476                 :            :     {
     477                 :          0 :       dof_el = rdof;
     478                 :            :     }
     479                 :            :     else
     480                 :            :     {
     481                 :     363840 :       dof_el = ndofel[e];
     482                 :            :     }
     483                 :            : 
     484 [ +  + ][ +  + ]:     363840 :     if (dof_el > 1 && shockmarker[e])
                 [ +  + ]
     485                 :            :     {
     486                 :            :       // The vector of limiting coefficients for P1 and P2 coefficients
     487         [ +  - ]:      33128 :       std::vector< tk::real > phic_p1(ncomp, 1.0);
     488                 :            : 
     489                 :            :       // Removing 3rd order DOFs if discontinuity is detected, and applying
     490                 :            :       // limiting to the 2nd order/P1 solution
     491         [ +  + ]:      99384 :       for (std::size_t c=0; c<ncomp; ++c) {
     492         [ +  + ]:     579740 :         for(std::size_t idof = 4; idof < rdof; idof++) {
     493                 :     496920 :           auto mark = c * rdof + idof;
     494         [ +  - ]:     496920 :           U(e, mark) = 0.0;
     495                 :            :         }
     496                 :            :       }
     497                 :            : 
     498                 :            :       // Obtain limiting coefficient for P1 coefficients
     499                 :      33128 :       std::vector< std::size_t > var;
     500 [ +  + ][ +  - ]:      99384 :       for (std::size_t c=0; c<ncomp; ++c) var.push_back(c);
     501         [ +  - ]:      16564 :       VertexBasedLimiting(U, esup, inpoel, coord, e, rdof, dof_el,
     502                 :            :         ncomp, phic_p1, var);
     503                 :            : 
     504                 :            :       // apply limiter function to the solution with Taylor basis
     505         [ +  + ]:      99384 :       for (std::size_t c=0; c<ncomp; ++c) {
     506                 :      82820 :         auto mark = c * rdof;
     507         [ +  + ]:     331280 :         for(std::size_t idof=1; idof<4; idof++)
     508 [ +  - ][ +  - ]:     248460 :           U(e, mark+idof) = phic_p1[c] * U(e, mark+idof);
     509                 :            :       }
     510                 :            :     }
     511                 :            :   }
     512                 :       2580 : }
     513                 :            : 
     514                 :            : void
     515                 :       4080 : VertexBasedMultiMat_P1(
     516                 :            :   const std::map< std::size_t, std::vector< std::size_t > >& esup,
     517                 :            :   const std::vector< std::size_t >& inpoel,
     518                 :            :   const std::vector< std::size_t >& ndofel,
     519                 :            :   std::size_t nelem,
     520                 :            :   const std::vector< inciter::EOS >& mat_blk,
     521                 :            :   const inciter::FaceData& fd,
     522                 :            :   const tk::Fields& geoFace,
     523                 :            :   const tk::Fields& geoElem,
     524                 :            :   const tk::UnsMesh::Coords& coord,
     525                 :            :   const tk::FluxFn& flux,
     526                 :            :   const std::vector< std::size_t >& solidx,
     527                 :            :   tk::Fields& U,
     528                 :            :   tk::Fields& P,
     529                 :            :   std::size_t nmat,
     530                 :            :   std::vector< std::size_t >& shockmarker )
     531                 :            : // *****************************************************************************
     532                 :            : //  Kuzmin's vertex-based limiter for multi-material DGP1
     533                 :            : //! \param[in] esup Elements surrounding points
     534                 :            : //! \param[in] inpoel Element connectivity
     535                 :            : //! \param[in] ndofel Vector of local number of degrees of freedom
     536                 :            : //! \param[in] nelem Number of elements
     537                 :            : //! \param[in] mat_blk EOS material block
     538                 :            : //! \param[in] fd Face connectivity and boundary conditions object
     539                 :            : //! \param[in] geoFace Face geometry array
     540                 :            : // //! \param[in] geoElem Element geometry array
     541                 :            : //! \param[in] coord Array of nodal coordinates
     542                 :            : //! \param[in] flux Riemann flux function to use
     543                 :            : //! \param[in] solidx Solid material index indicator
     544                 :            : //! \param[in,out] U High-order solution vector which gets limited
     545                 :            : //! \param[in,out] P High-order vector of primitives which gets limited
     546                 :            : //! \param[in] nmat Number of materials in this PDE system
     547                 :            : //! \param[in,out] shockmarker Shock detection marker array
     548                 :            : //! \details This vertex-based limiter function should be called for multimat.
     549                 :            : //!   For details see: Kuzmin, D. (2010). A vertex-based hierarchical slope
     550                 :            : //!   limiter for p-adaptive discontinuous Galerkin methods. Journal of
     551                 :            : //!   computational and applied mathematics, 233(12), 3077-3085.
     552                 :            : // *****************************************************************************
     553                 :            : {
     554                 :       4080 :   const auto rdof = inciter::g_inputdeck.get< tag::rdof >();
     555                 :       4080 :   const auto ndof = inciter::g_inputdeck.get< tag::ndof >();
     556                 :            :   const auto intsharp = inciter::g_inputdeck.get< tag::multimat,
     557                 :       4080 :     tag::intsharp >();
     558                 :       4080 :   std::size_t ncomp = U.nprop()/rdof;
     559                 :       4080 :   std::size_t nprim = P.nprop()/rdof;
     560                 :            : 
     561                 :            :   // Evaluate the interface condition and mark the shock cells
     562                 :       4080 :   if (inciter::g_inputdeck.get< tag::shock_detector_coeff >()
     563 [ +  + ][ +  - ]:       4080 :     > 1e-6 && ndof > 1) {
                 [ +  + ]
     564                 :            :     // Indicator based on momentum flux jump
     565                 :       1500 :     std::set< std::size_t > vars;
     566 [ +  + ][ +  - ]:       3000 :     for (std::size_t i=0; i<3; ++i) vars.insert(momentumIdx(nmat, i));
     567         [ +  - ]:        750 :     MarkShockCells(false, nelem, nmat, ndof, rdof, mat_blk, ndofel,
     568                 :            :       inpoel, coord, fd, geoFace, geoElem, flux, solidx, U, P,
     569                 :            :       vars, shockmarker);
     570                 :            :   }
     571                 :            : 
     572         [ +  + ]:     845460 :   for (std::size_t e=0; e<nelem; ++e)
     573                 :            :   {
     574                 :            :     // If an rDG method is set up (P0P1), then, currently we compute the P1
     575                 :            :     // basis functions and solutions by default. This implies that P0P1 is
     576                 :            :     // unsupported in the p-adaptive DG (PDG). This is a workaround until we
     577                 :            :     // have rdofel, which is needed to distinguish between ndofs and rdofs per
     578                 :            :     // element for pDG.
     579                 :            :     std::size_t dof_el;
     580         [ +  + ]:     841380 :     if (rdof > ndof)
     581                 :            :     {
     582                 :     341100 :       dof_el = rdof;
     583                 :            :     }
     584                 :            :     else
     585                 :            :     {
     586                 :     500280 :       dof_el = ndofel[e];
     587                 :            :     }
     588                 :            : 
     589         [ +  - ]:     841380 :     if (dof_el > 1)
     590                 :            :     {
     591         [ +  - ]:    1682760 :       std::vector< tk::real > phic(ncomp, 1.0);
     592         [ +  - ]:    1682760 :       std::vector< tk::real > phip(nprim, 1.0);
     593         [ +  + ]:     841380 :       if(shockmarker[e]) {
     594                 :            :         // When shockmarker is 1, there is discontinuity within the element.
     595                 :            :         // Hence, the vertex-based limiter will be applied.
     596                 :            : 
     597                 :            :         // limit conserved quantities
     598                 :     840640 :         std::vector< std::size_t > varc;
     599 [ +  + ][ +  - ]:    4203200 :         for (std::size_t c=0; c<ncomp; ++c) varc.push_back(c);
     600         [ +  - ]:     420320 :         VertexBasedLimiting(U, esup, inpoel, coord, e, rdof, dof_el,
     601                 :            :           ncomp, phic, varc);
     602                 :            :         // limit primitive quantities
     603                 :     840640 :         std::vector< std::size_t > varp;
     604 [ +  + ][ +  - ]:    2521920 :         for (std::size_t c=0; c<nprim; ++c) varp.push_back(c);
     605         [ +  - ]:     420320 :         VertexBasedLimiting(P, esup, inpoel, coord, e, rdof, dof_el,
     606                 :            :           nprim, phip, varp);
     607                 :            :       } else {
     608                 :            :         // When shockmarker is 0, the volume fraction, density and energy
     609                 :            :         // of minor material will still be limited to ensure a stable solution.
     610                 :     842120 :         std::vector< std::size_t > vars;
     611 [ +  + ][ +  - ]:    1263180 :         for (std::size_t k=0; k<nmat; ++k) vars.push_back(volfracIdx(nmat,k));
     612         [ +  - ]:     421060 :         VertexBasedLimiting(U, esup, inpoel, coord, e, rdof, dof_el,
     613                 :            :           ncomp, phic, vars);
     614                 :            : 
     615         [ +  + ]:    1263180 :         for(std::size_t k=0; k<nmat; ++k) {
     616 [ +  - ][ +  + ]:     842120 :           if(U(e, volfracDofIdx(nmat,k,rdof,0)) < 1e-4) {
     617                 :            :             // limit the density and energy of minor materials
     618                 :     421060 :             vars.clear();
     619         [ +  - ]:     421060 :             vars.push_back(densityIdx(nmat, k));
     620         [ +  - ]:     421060 :             vars.push_back(energyIdx(nmat, k));
     621         [ -  + ]:     421060 :             if (solidx[k] > 0) {
     622         [ -  - ]:          0 :               for (std::size_t i=0; i<3; ++i)
     623         [ -  - ]:          0 :                 for (std::size_t j=0; j<3; ++j)
     624         [ -  - ]:          0 :                   vars.push_back(deformIdx(nmat, solidx[k], i, j));
     625                 :            :             }
     626         [ +  - ]:     421060 :             VertexBasedLimiting(U, esup, inpoel, coord, e, rdof, dof_el,
     627                 :            :               ncomp, phic, vars);
     628                 :            : 
     629                 :            :             // limit the pressure of minor materials
     630         [ +  - ]:     421060 :             VertexBasedLimiting(P, esup, inpoel, coord, e, rdof, dof_el,
     631         [ +  - ]:     842120 :               nprim, phip, std::vector< std::size_t >{pressureIdx(nmat, k)});
     632                 :            :           }
     633                 :            :         }
     634                 :            :       }
     635                 :            : 
     636                 :    1682760 :       std::vector< tk::real > phic_p2, phip_p2;
     637                 :            : 
     638         [ +  - ]:     841380 :       PositivityLimiting(nmat, 1, mat_blk, rdof, dof_el, ndofel, e,
     639                 :            :         inpoel, coord, fd.Esuel(), U, P, phic, phic_p2, phip, phip_p2);
     640                 :            : 
     641                 :            :       // limits under which compression is to be performed
     642         [ +  - ]:    1682760 :       std::vector< std::size_t > matInt(nmat, 0);
     643         [ +  - ]:    1682760 :       std::vector< tk::real > alAvg(nmat, 0.0);
     644         [ +  + ]:    2524140 :       for (std::size_t k=0; k<nmat; ++k)
     645         [ +  - ]:    1682760 :         alAvg[k] = U(e, volfracDofIdx(nmat,k,rdof,0));
     646         [ +  - ]:     841380 :       auto intInd = interfaceIndicator(nmat, alAvg, matInt);
     647 [ +  + ][ +  + ]:     841380 :       if ((intsharp > 0) && intInd) {
     648         [ +  + ]:      32214 :         for (std::size_t k=0; k<nmat; ++k) {
     649         [ +  - ]:      21476 :           if (matInt[k]) {
     650                 :      21476 :             phic[volfracIdx(nmat,k)] = 1.0;
     651                 :            :           }
     652                 :      10738 :         }
     653                 :            :       }
     654                 :            :       else {
     655         [ +  - ]:     830642 :         if(nmat > 1)
     656         [ +  - ]:     830642 :           BoundPreservingLimiting(nmat, ndof, e, inpoel, coord, U, phic,
     657                 :            :             phic_p2);
     658                 :            : 
     659         [ +  - ]:     830642 :         if (!g_inputdeck.get< tag::accuracy_test >())
     660         [ +  - ]:     830642 :           consistentMultiMatLimiting_P1(nmat, rdof, e, solidx, U, P, phic,
     661                 :            :             phic_p2);
     662                 :            :       }
     663                 :            : 
     664                 :            :       // apply limiter function
     665         [ +  + ]:    8413800 :       for (std::size_t c=0; c<ncomp; ++c)
     666                 :            :       {
     667                 :    7572420 :         auto mark = c*rdof;
     668 [ +  - ][ +  - ]:    7572420 :         U(e, mark+1) = phic[c] * U(e, mark+1);
     669 [ +  - ][ +  - ]:    7572420 :         U(e, mark+2) = phic[c] * U(e, mark+2);
     670 [ +  - ][ +  - ]:    7572420 :         U(e, mark+3) = phic[c] * U(e, mark+3);
     671                 :            :       }
     672         [ +  + ]:    5048280 :       for (std::size_t c=0; c<nprim; ++c)
     673                 :            :       {
     674                 :    4206900 :         auto mark = c*rdof;
     675 [ +  - ][ +  - ]:    4206900 :         P(e, mark+1) = phip[c] * P(e, mark+1);
     676 [ +  - ][ +  - ]:    4206900 :         P(e, mark+2) = phip[c] * P(e, mark+2);
     677 [ +  - ][ +  - ]:    4206900 :         P(e, mark+3) = phip[c] * P(e, mark+3);
     678                 :            :       }
     679                 :            :     }
     680                 :            :   }
     681                 :       4080 : }
     682                 :            : 
     683                 :            : void
     684                 :          0 : VertexBasedMultiMat_P2(
     685                 :            :   const bool pref,
     686                 :            :   const std::map< std::size_t, std::vector< std::size_t > >& esup,
     687                 :            :   const std::vector< std::size_t >& inpoel,
     688                 :            :   const std::vector< std::size_t >& ndofel,
     689                 :            :   std::size_t nelem,
     690                 :            :   const std::vector< inciter::EOS >& mat_blk,
     691                 :            :   const inciter::FaceData& fd,
     692                 :            :   const tk::Fields& geoFace,
     693                 :            :   const tk::Fields& geoElem,
     694                 :            :   const tk::UnsMesh::Coords& coord,
     695                 :            :   [[maybe_unused]] const std::vector< std::size_t >& gid,
     696                 :            :   [[maybe_unused]] const std::unordered_map< std::size_t, std::size_t >& bid,
     697                 :            :   [[maybe_unused]] const std::vector< std::vector<tk::real> >& uNodalExtrm,
     698                 :            :   [[maybe_unused]] const std::vector< std::vector<tk::real> >& pNodalExtrm,
     699                 :            :   [[maybe_unused]] const std::vector< std::vector<tk::real> >& mtInv,
     700                 :            :   const tk::FluxFn& flux,
     701                 :            :   const std::vector< std::size_t >& solidx,
     702                 :            :   tk::Fields& U,
     703                 :            :   tk::Fields& P,
     704                 :            :   std::size_t nmat,
     705                 :            :   std::vector< std::size_t >& shockmarker )
     706                 :            : // *****************************************************************************
     707                 :            : //  Kuzmin's vertex-based limiter for multi-material DGP2
     708                 :            : //! \param[in] pref Indicator for p-adaptive algorithm
     709                 :            : //! \param[in] esup Elements surrounding points
     710                 :            : //! \param[in] inpoel Element connectivity
     711                 :            : //! \param[in] ndofel Vector of local number of degrees of freedom
     712                 :            : //! \param[in] nelem Number of elements
     713                 :            : //! \param[in] mat_blk EOS material block
     714                 :            : //! \param[in] fd Face connectivity and boundary conditions object
     715                 :            : //! \param[in] geoFace Face geometry array
     716                 :            : // //! \param[in] geoElem Element geometry array
     717                 :            : //! \param[in] coord Array of nodal coordinates
     718                 :            : //! \param[in] gid Local->global node id map
     719                 :            : //! \param[in] bid Local chare-boundary node ids (value) associated to
     720                 :            : //!   global node ids (key)
     721                 :            : //! \param[in] uNodalExtrm Chare-boundary nodal extrema for conservative
     722                 :            : //!   variables
     723                 :            : //! \param[in] pNodalExtrm Chare-boundary nodal extrema for primitive
     724                 :            : //!   variables
     725                 :            : //! \param[in] mtInv Inverse of Taylor mass matrix
     726                 :            : //! \param[in] flux Riemann flux function to use
     727                 :            : //! \param[in] solidx Solid material index indicator
     728                 :            : //! \param[in,out] U High-order solution vector which gets limited
     729                 :            : //! \param[in,out] P High-order vector of primitives which gets limited
     730                 :            : //! \param[in] nmat Number of materials in this PDE system
     731                 :            : //! \param[in,out] shockmarker Shock detection marker array
     732                 :            : //! \details This vertex-based limiter function should be called for multimat.
     733                 :            : //!   For details see: Kuzmin, D. (2010). A vertex-based hierarchical slope
     734                 :            : //!   limiter for p-adaptive discontinuous Galerkin methods. Journal of
     735                 :            : //!   computational and applied mathematics, 233(12), 3077-3085.
     736                 :            : // *****************************************************************************
     737                 :            : {
     738                 :          0 :   const auto rdof = inciter::g_inputdeck.get< tag::rdof >();
     739                 :          0 :   const auto ndof = inciter::g_inputdeck.get< tag::ndof >();
     740                 :            :   const auto intsharp = inciter::g_inputdeck.get< tag::multimat,
     741                 :          0 :     tag::intsharp >();
     742                 :          0 :   std::size_t ncomp = U.nprop()/rdof;
     743                 :          0 :   std::size_t nprim = P.nprop()/rdof;
     744                 :            : 
     745                 :            :   // Evaluate the interface condition and mark the shock cells
     746                 :          0 :   if (inciter::g_inputdeck.get< tag::shock_detector_coeff >()
     747         [ -  - ]:          0 :     > 1e-6) {
     748                 :            :     // Indicator based on momentum flux jump
     749                 :          0 :     std::set< std::size_t > vars;
     750 [ -  - ][ -  - ]:          0 :     for (std::size_t i=0; i<3; ++i) vars.insert(momentumIdx(nmat, i));
     751         [ -  - ]:          0 :     MarkShockCells(pref, nelem, nmat, ndof, rdof, mat_blk, ndofel,
     752                 :            :       inpoel, coord, fd, geoFace, geoElem, flux, solidx, U, P,
     753                 :            :       vars, shockmarker);
     754                 :            :   }
     755                 :            : 
     756         [ -  - ]:          0 :   for (std::size_t e=0; e<nelem; ++e)
     757                 :            :   {
     758                 :            :     // If an rDG method is set up (P0P1), then, currently we compute the P1
     759                 :            :     // basis functions and solutions by default. This implies that P0P1 is
     760                 :            :     // unsupported in the p-adaptive DG (PDG). This is a workaround until we
     761                 :            :     // have rdofel, which is needed to distinguish between ndofs and rdofs per
     762                 :            :     // element for pDG.
     763                 :            :     std::size_t dof_el;
     764         [ -  - ]:          0 :     if (rdof > ndof)
     765                 :            :     {
     766                 :          0 :       dof_el = rdof;
     767                 :            :     }
     768                 :            :     else
     769                 :            :     {
     770                 :          0 :       dof_el = ndofel[e];
     771                 :            :     }
     772                 :            : 
     773                 :            :     // For multi-material simulation, when dofel = 1, p0p1 is applied and ndof
     774                 :            :     // for solution evaluation should be 4
     775 [ -  - ][ -  - ]:          0 :     if(ncomp > 5 && dof_el == 1)
     776                 :          0 :       dof_el = 4;
     777                 :            : 
     778         [ -  - ]:          0 :     if (dof_el > 1)
     779                 :            :     {
     780                 :            :       // The vector of limiting coefficients for P1
     781 [ -  - ][ -  - ]:          0 :       std::vector< tk::real > phic_p1(ncomp, 1.0), phic_p2(ncomp, 1.0);
     782 [ -  - ][ -  - ]:          0 :       std::vector< tk::real > phip_p1(nprim, 1.0), phip_p2(nprim, 1.0);
     783                 :            : 
     784                 :            :       // Only when the cell is marked with discontinuous solution or P0P1 scheme
     785                 :            :       // is used, the vertex-based slope limiter will be applied.
     786 [ -  - ][ -  - ]:          0 :       if(shockmarker[e] || dof_el == 4) {
                 [ -  - ]
     787                 :            :         // Removing 3rd order DOFs if discontinuity is detected, and applying
     788                 :            :         // limiting to the 2nd order/P1 solution
     789         [ -  - ]:          0 :         for (std::size_t c=0; c<ncomp; ++c) {
     790                 :          0 :           auto mark = c * rdof;
     791         [ -  - ]:          0 :           for(std::size_t idof = 4; idof < rdof; idof++)
     792         [ -  - ]:          0 :             U(e, mark+idof) = 0.0;
     793                 :            :         }
     794         [ -  - ]:          0 :         for (std::size_t c=0; c<nprim; ++c) {
     795                 :          0 :           auto mark = c * rdof;
     796         [ -  - ]:          0 :           for(std::size_t idof = 4; idof < rdof; idof++)
     797         [ -  - ]:          0 :             P(e, mark+idof) = 0.0;
     798                 :            :         }
     799                 :            : 
     800                 :            :         // Obtain limiter coefficient for P1 conserved quantities
     801                 :          0 :         std::vector< std::size_t > varc;
     802 [ -  - ][ -  - ]:          0 :         for (std::size_t c=0; c<ncomp; ++c) varc.push_back(c);
     803         [ -  - ]:          0 :         VertexBasedLimiting(U, esup, inpoel, coord, e, rdof, dof_el,
     804                 :            :           ncomp, phic_p1, varc);
     805                 :            :         // Obtain limiter coefficient for P1 primitive quantities
     806                 :          0 :         std::vector< std::size_t > varp;
     807 [ -  - ][ -  - ]:          0 :         for (std::size_t c=0; c<nprim; ++c) varp.push_back(c);
     808         [ -  - ]:          0 :         VertexBasedLimiting(P, esup, inpoel, coord, e, rdof, dof_el,
     809                 :            :           nprim, phip_p1, varp);
     810                 :            :       } else {
     811                 :            :         // When shockmarker is 0, the volume fraction will still be limited to
     812                 :            :         // ensure a stable solution. Since the limiting strategy for third order
     813                 :            :         // solution will downgrade the accuracy to second order, the density,
     814                 :            :         // energy and pressure of minor material will not be limited.
     815                 :          0 :         std::vector< std::size_t > vars;
     816 [ -  - ][ -  - ]:          0 :         for (std::size_t k=0; k<nmat; ++k) vars.push_back(volfracIdx(nmat,k));
     817         [ -  - ]:          0 :         VertexBasedLimiting(U, esup, inpoel, coord, e, rdof, dof_el,
     818                 :            :           ncomp, phic_p1, vars);
     819                 :            : 
     820                 :            :         //for(std::size_t k=0; k<nmat; ++k) {
     821                 :            :         //  if(U(e, volfracDofIdx(nmat,k,rdof,0)) < 1e-4) {
     822                 :            :         //    // limit the density of minor materials
     823                 :            :         //    VertexBasedLimiting(unk, U, esup, inpoel, coord, e, rdof, dof_el,
     824                 :            :         //      ncomp, phic_p1, std::vector< std::size_t >{densityIdx(nmat,k)});
     825                 :            : 
     826                 :            :         //    // limit the pressure of minor materials
     827                 :            :         //    VertexBasedLimiting(prim, P, esup, inpoel, coord, e, rdof, dof_el,
     828                 :            :         //      nprim, phip_p1, std::vector< std::size_t >{pressureIdx(nmat,k)});
     829                 :            :         //  }
     830                 :            :         //}
     831                 :            :       }
     832                 :            : 
     833         [ -  - ]:          0 :       PositivityLimiting(nmat, 1, mat_blk, ndof, dof_el, ndofel, e,
     834                 :            :         inpoel, coord, fd.Esuel(), U, P, phic_p1, phic_p2, phip_p1, phic_p2);
     835                 :            : 
     836                 :            :       // limits under which compression is to be performed
     837         [ -  - ]:          0 :       std::vector< std::size_t > matInt(nmat, 0);
     838         [ -  - ]:          0 :       std::vector< tk::real > alAvg(nmat, 0.0);
     839         [ -  - ]:          0 :       for (std::size_t k=0; k<nmat; ++k)
     840         [ -  - ]:          0 :         alAvg[k] = U(e, volfracDofIdx(nmat,k,rdof,0));
     841         [ -  - ]:          0 :       auto intInd = interfaceIndicator(nmat, alAvg, matInt);
     842 [ -  - ][ -  - ]:          0 :       if ((intsharp > 0) && intInd) {
     843         [ -  - ]:          0 :         for (std::size_t k=0; k<nmat; ++k) {
     844         [ -  - ]:          0 :           if (matInt[k]) {
     845                 :          0 :             phic_p1[volfracIdx(nmat,k)] = 1.0;
     846                 :            :           }
     847                 :          0 :         }
     848                 :            :       }
     849                 :            :       else {
     850         [ -  - ]:          0 :         if(nmat > 1)
     851         [ -  - ]:          0 :           BoundPreservingLimiting(nmat, ndof, e, inpoel, coord, U,
     852                 :            :             phic_p1, phic_p2);
     853                 :            : 
     854         [ -  - ]:          0 :         if (!g_inputdeck.get< tag::accuracy_test >())
     855         [ -  - ]:          0 :           consistentMultiMatLimiting_P1(nmat, rdof, e, solidx, U, P, phic_p1,
     856                 :            :             phic_p2);
     857                 :            :       }
     858                 :            : 
     859                 :            :       // apply limiing coefficient
     860         [ -  - ]:          0 :       for (std::size_t c=0; c<ncomp; ++c)
     861                 :            :       {
     862                 :          0 :         auto mark = c * rdof;
     863         [ -  - ]:          0 :         for(std::size_t idof=1; idof<4; idof++)
     864 [ -  - ][ -  - ]:          0 :           U(e, mark+idof) = phic_p1[c] * U(e, mark+idof);
     865         [ -  - ]:          0 :         for(std::size_t idof=4; idof<rdof; idof++)
     866 [ -  - ][ -  - ]:          0 :           U(e, mark+idof) = phic_p2[c] * U(e, mark+idof);
     867                 :            :       }
     868         [ -  - ]:          0 :       for (std::size_t c=0; c<nprim; ++c)
     869                 :            :       {
     870                 :          0 :         auto mark = c * rdof;
     871         [ -  - ]:          0 :         for(std::size_t idof=1; idof<4; idof++)
     872 [ -  - ][ -  - ]:          0 :           P(e, mark+idof) = phip_p1[c] * P(e, mark+idof);
     873         [ -  - ]:          0 :         for(std::size_t idof=4; idof<rdof; idof++)
     874 [ -  - ][ -  - ]:          0 :           P(e, mark+idof) = phip_p2[c] * P(e, mark+idof);
     875                 :            :       }
     876                 :            :     }
     877                 :            :   }
     878                 :          0 : }
     879                 :            : 
     880                 :            : void
     881                 :       1268 : VertexBasedMultiMat_FV(
     882                 :            :   const std::map< std::size_t, std::vector< std::size_t > >& esup,
     883                 :            :   const std::vector< std::size_t >& inpoel,
     884                 :            :   std::size_t nelem,
     885                 :            :   const tk::UnsMesh::Coords& coord,
     886                 :            :   const std::vector< int >& srcFlag,
     887                 :            :   const std::vector< std::size_t >& solidx,
     888                 :            :   tk::Fields& U,
     889                 :            :   tk::Fields& P,
     890                 :            :   std::size_t nmat )
     891                 :            : // *****************************************************************************
     892                 :            : //  Kuzmin's vertex-based limiter for multi-material FV
     893                 :            : //! \param[in] esup Elements surrounding points
     894                 :            : //! \param[in] inpoel Element connectivity
     895                 :            : //! \param[in] nelem Number of elements
     896                 :            : //! \param[in] coord Array of nodal coordinates
     897                 :            : //! \param[in] srcFlag Whether the energy source was added
     898                 :            : //! \param[in] solidx Solid material index indicator
     899                 :            : //! \param[in,out] U High-order solution vector which gets limited
     900                 :            : //! \param[in,out] P High-order vector of primitives which gets limited
     901                 :            : //! \param[in] nmat Number of materials in this PDE system
     902                 :            : //! \details This vertex-based limiter function should be called for multimat.
     903                 :            : //!   For details see: Kuzmin, D. (2010). A vertex-based hierarchical slope
     904                 :            : //!   limiter for p-adaptive discontinuous Galerkin methods. Journal of
     905                 :            : //!   computational and applied mathematics, 233(12), 3077-3085.
     906                 :            : // *****************************************************************************
     907                 :            : {
     908                 :       1268 :   const auto rdof = inciter::g_inputdeck.get< tag::rdof >();
     909                 :            :   const auto intsharp = inciter::g_inputdeck.get< tag::multimat,
     910                 :       1268 :     tag::intsharp >();
     911                 :       1268 :   std::size_t ncomp = U.nprop()/rdof;
     912                 :       1268 :   std::size_t nprim = P.nprop()/rdof;
     913                 :            : 
     914         [ +  + ]:     205428 :   for (std::size_t e=0; e<nelem; ++e)
     915                 :            :   {
     916         [ +  - ]:     408320 :     std::vector< tk::real > phic(ncomp, 1.0);
     917         [ +  - ]:     408320 :     std::vector< tk::real > phip(nprim, 1.0);
     918                 :            :     // limit conserved quantities
     919                 :     408320 :     std::vector< std::size_t > var;
     920         [ +  + ]:     665040 :     for (std::size_t k=0; k<nmat; ++k) {
     921         [ +  - ]:     460880 :       var.push_back(volfracIdx(nmat,k));
     922         [ +  - ]:     460880 :       var.push_back(densityIdx(nmat,k));
     923                 :            :     }
     924         [ +  - ]:     204160 :     VertexBasedLimiting(U, esup, inpoel, coord, e, rdof, rdof, ncomp,
     925                 :            :       phic, var);
     926                 :            :     // limit primitive quantities
     927                 :     204160 :     var.clear();
     928 [ +  + ][ +  - ]:    1277520 :     for (std::size_t c=0; c<nprim; ++c) var.push_back(c);
     929         [ +  - ]:     204160 :     VertexBasedLimiting(P, esup, inpoel, coord, e, rdof, rdof, nprim,
     930                 :            :       phip, var);
     931                 :            : 
     932                 :            :     // limits under which compression is to be performed
     933         [ +  - ]:     408320 :     std::vector< std::size_t > matInt(nmat, 0);
     934         [ +  - ]:     408320 :     std::vector< tk::real > alAvg(nmat, 0.0);
     935         [ +  + ]:     665040 :     for (std::size_t k=0; k<nmat; ++k)
     936         [ +  - ]:     460880 :       alAvg[k] = U(e, volfracDofIdx(nmat,k,rdof,0));
     937         [ +  - ]:     204160 :     auto intInd = interfaceIndicator(nmat, alAvg, matInt);
     938 [ +  + ][ +  + ]:     204160 :     if ((intsharp > 0) && intInd && srcFlag[e] == 0)
         [ +  - ][ +  + ]
     939                 :            :     {
     940         [ +  + ]:       5205 :       for (std::size_t k=0; k<nmat; ++k)
     941                 :            :       {
     942         [ +  - ]:       3470 :         if (matInt[k])
     943                 :       3470 :           phic[volfracIdx(nmat,k)] = 1.0;
     944                 :            :       }
     945                 :            :     }
     946                 :            :     else
     947                 :            :     {
     948         [ +  - ]:     202425 :       if (!g_inputdeck.get< tag::accuracy_test >()) {
     949         [ +  - ]:     404850 :         std::vector< tk::real > phic_p2(ncomp, 1.0);
     950         [ +  - ]:     202425 :         consistentMultiMatLimiting_P1(nmat, rdof, e, solidx, U, P, phic,
     951                 :            :           phic_p2);
     952                 :            :       }
     953                 :            :     }
     954                 :            : 
     955                 :            :     // apply limiter function
     956         [ +  + ]:    2199280 :     for (std::size_t c=0; c<ncomp; ++c)
     957                 :            :     {
     958                 :    1995120 :       auto mark = c*rdof;
     959 [ +  - ][ +  - ]:    1995120 :       U(e, mark+1) = phic[c] * U(e, mark+1);
     960 [ +  - ][ +  - ]:    1995120 :       U(e, mark+2) = phic[c] * U(e, mark+2);
     961 [ +  - ][ +  - ]:    1995120 :       U(e, mark+3) = phic[c] * U(e, mark+3);
     962                 :            :     }
     963         [ +  + ]:    1277520 :     for (std::size_t c=0; c<nprim; ++c)
     964                 :            :     {
     965                 :    1073360 :       auto mark = c*rdof;
     966 [ +  - ][ +  - ]:    1073360 :       P(e, mark+1) = phip[c] * P(e, mark+1);
     967 [ +  - ][ +  - ]:    1073360 :       P(e, mark+2) = phip[c] * P(e, mark+2);
     968 [ +  - ][ +  - ]:    1073360 :       P(e, mark+3) = phip[c] * P(e, mark+3);
     969                 :            :     }
     970                 :            :   }
     971                 :       1268 : }
     972                 :            : 
     973                 :            : void
     974                 :       6600 : VertexBasedMultiSpecies_P1(
     975                 :            :   const std::map< std::size_t, std::vector< std::size_t > >& esup,
     976                 :            :   const std::vector< std::size_t >& inpoel,
     977                 :            :   const std::vector< std::size_t >& ndofel,
     978                 :            :   std::size_t nelem,
     979                 :            :   const std::vector< inciter::EOS >& mat_blk,
     980                 :            :   const inciter::FaceData& fd,
     981                 :            :   const tk::Fields& geoFace,
     982                 :            :   const tk::Fields& geoElem,
     983                 :            :   const tk::UnsMesh::Coords& coord,
     984                 :            :   const tk::FluxFn& flux,
     985                 :            :   const std::vector< std::size_t >& solidx,
     986                 :            :   tk::Fields& U,
     987                 :            :   tk::Fields& P,
     988                 :            :   std::size_t nspec,
     989                 :            :   std::vector< std::size_t >& shockmarker )
     990                 :            : // *****************************************************************************
     991                 :            : //  Kuzmin's vertex-based limiter for multi-species DGP1
     992                 :            : //! \param[in] esup Elements surrounding points
     993                 :            : //! \param[in] inpoel Element connectivity
     994                 :            : //! \param[in] ndofel Vector of local number of degrees of freedom
     995                 :            : //! \param[in] nelem Number of elements
     996                 :            : //! \param[in] mat_blk EOS material block
     997                 :            : //! \param[in] fd Face connectivity and boundary conditions object
     998                 :            : //! \param[in] geoFace Face geometry array
     999                 :            : //! \param[in] geoElem Element geometry array
    1000                 :            : //! \param[in] coord Array of nodal coordinates
    1001                 :            : //! \param[in] flux Riemann flux function to use
    1002                 :            : //! \param[in] solidx Solid material index indicator
    1003                 :            : //! \param[in,out] U High-order solution vector which gets limited
    1004                 :            : //! \param[in,out] P High-order primitive vector which gets limited
    1005                 :            : //! \param[in] nspec Number of species in this PDE system
    1006                 :            : //! \param[in,out] shockmarker Shock detection marker array
    1007                 :            : //! \details This vertex-based limiter function should be called for
    1008                 :            : //!   multispecies. For details see: Kuzmin, D. (2010). A vertex-based
    1009                 :            : //!   hierarchical slope limiter for p-adaptive discontinuous Galerkin methods.
    1010                 :            : //!   Journal of computational and applied mathematics, 233(12), 3077-3085.
    1011                 :            : // *****************************************************************************
    1012                 :            : {
    1013                 :       6600 :   const auto rdof = inciter::g_inputdeck.get< tag::rdof >();
    1014                 :       6600 :   const auto ndof = inciter::g_inputdeck.get< tag::ndof >();
    1015                 :       6600 :   std::size_t ncomp = U.nprop()/rdof;
    1016                 :       6600 :   std::size_t nprim = P.nprop()/rdof;
    1017                 :            : 
    1018                 :            :   // Evaluate the interface condition and mark the shock cells
    1019                 :       6600 :   if (inciter::g_inputdeck.get< tag::shock_detector_coeff >()
    1020 [ +  - ][ -  + ]:       6600 :     > 1e-6 && ndof > 1) {
                 [ -  + ]
    1021                 :            :     // Indicator based on momentum flux jump
    1022                 :          0 :     std::set< std::size_t > vars;
    1023         [ -  - ]:          0 :     for (std::size_t i=0; i<3; ++i)
    1024         [ -  - ]:          0 :       vars.insert(multispecies::momentumIdx(nspec, i));
    1025         [ -  - ]:          0 :     MarkShockCells(false, nelem, 1, ndof, rdof, mat_blk,
    1026                 :            :       ndofel, inpoel, coord, fd, geoFace, geoElem, flux, solidx, U, P,
    1027                 :            :       vars, shockmarker);
    1028                 :            :   }
    1029                 :            : 
    1030         [ +  + ]:     688800 :   for (std::size_t e=0; e<nelem; ++e)
    1031                 :            :   {
    1032                 :            :     // If an rDG method is set up (P0P1), then, currently we compute the P1
    1033                 :            :     // basis functions and solutions by default. This implies that P0P1 is
    1034                 :            :     // unsupported in the p-adaptive DG (PDG). This is a workaround until we
    1035                 :            :     // have rdofel, which is needed to distinguish between ndofs and rdofs per
    1036                 :            :     // element for pDG.
    1037                 :            :     std::size_t dof_el;
    1038         [ +  - ]:     682200 :     if (rdof > ndof)
    1039                 :            :     {
    1040                 :     682200 :       dof_el = rdof;
    1041                 :            :     }
    1042                 :            :     else
    1043                 :            :     {
    1044                 :          0 :       dof_el = ndofel[e];
    1045                 :            :     }
    1046                 :            : 
    1047         [ +  - ]:     682200 :     if (dof_el > 1)
    1048                 :            :     {
    1049                 :    1364400 :       std::vector< std::size_t > varc, varp;
    1050         [ +  - ]:     682200 :       if(shockmarker[e]) {
    1051                 :            :         // When shockmarker is 1, there is discontinuity within the element.
    1052                 :            :         // Hence, the vertex-based limiter will be applied.
    1053                 :            : 
    1054 [ +  + ][ +  - ]:    4434300 :         for (std::size_t c=0; c<ncomp; ++c) varc.push_back(c);
    1055 [ +  + ][ +  - ]:    1364400 :         for (std::size_t c=0; c<nprim; ++c) varp.push_back(c);
    1056                 :            :       } else {
    1057                 :            :         // When shockmarker is 0, the density of minor species will still be
    1058                 :            :         // limited to ensure a stable solution.
    1059                 :            : 
    1060                 :          0 :         tk::real rhob(0.0);
    1061         [ -  - ]:          0 :         for(std::size_t k=0; k<nspec; ++k)
    1062         [ -  - ]:          0 :           rhob += U(e, multispecies::densityDofIdx(nspec,k,rdof,0));
    1063         [ -  - ]:          0 :         for(std::size_t k=0; k<nspec; ++k) {
    1064 [ -  - ][ -  - ]:          0 :           if (U(e, multispecies::densityDofIdx(nspec,k,rdof,0))/rhob < 1e-4) {
    1065                 :            :             // limit the density of minor species
    1066         [ -  - ]:          0 :             varc.push_back(multispecies::densityIdx(nspec, k));
    1067                 :            :           }
    1068                 :            :         }
    1069                 :            :       }
    1070                 :            : 
    1071 [ +  - ][ +  - ]:    1364400 :       std::vector< tk::real > phic(ncomp, 1.0), phip(nprim, 1.0);
    1072         [ +  - ]:     682200 :       VertexBasedLimiting(U, esup, inpoel, coord, e, rdof, dof_el,
    1073                 :            :         ncomp, phic, varc);
    1074         [ +  - ]:     682200 :       if (!varp.empty())
    1075         [ +  - ]:     682200 :         VertexBasedLimiting(P, esup, inpoel, coord, e, rdof, dof_el,
    1076                 :            :           nprim, phip, varp);
    1077                 :            : 
    1078                 :    1364400 :       std::vector< tk::real > phic_p2, phip_p2;
    1079         [ +  - ]:     682200 :       PositivityLimiting(1, nspec, mat_blk, rdof, dof_el, ndofel, e,
    1080                 :            :         inpoel, coord, fd.Esuel(), U, P, phic, phic_p2, phip, phip_p2);
    1081                 :            : 
    1082                 :            :       // TODO: Unit sum of mass fractions is maintained by using common limiter
    1083                 :            :       // for all species densities. Investigate better approaches.
    1084         [ +  - ]:     682200 :       if (!g_inputdeck.get< tag::accuracy_test >()) {
    1085                 :     682200 :         tk::real phi_rhos_p1(1.0);
    1086         [ +  + ]:    1705500 :         for (std::size_t k=0; k<nspec; ++k)
    1087                 :    1023300 :           phi_rhos_p1 = std::min( phi_rhos_p1,
    1088                 :    1023300 :             phic[multispecies::densityIdx(nspec, k)] );
    1089                 :            :         // same limiter for all densities
    1090         [ +  + ]:    1705500 :         for (std::size_t k=0; k<nspec; ++k)
    1091                 :    1023300 :           phic[multispecies::densityIdx(nspec, k)] = phi_rhos_p1;
    1092                 :            :       }
    1093                 :            : 
    1094                 :            :       // apply limiter function
    1095         [ +  + ]:    4434300 :       for (std::size_t c=0; c<ncomp; ++c)
    1096                 :            :       {
    1097                 :    3752100 :         auto mark = c*rdof;
    1098 [ +  - ][ +  - ]:    3752100 :         U(e, mark+1) = phic[c] * U(e, mark+1);
    1099 [ +  - ][ +  - ]:    3752100 :         U(e, mark+2) = phic[c] * U(e, mark+2);
    1100 [ +  - ][ +  - ]:    3752100 :         U(e, mark+3) = phic[c] * U(e, mark+3);
    1101                 :            :       }
    1102         [ +  + ]:    1364400 :       for (std::size_t c=0; c<nprim; ++c)
    1103                 :            :       {
    1104                 :     682200 :         auto mark = c*rdof;
    1105 [ +  - ][ +  - ]:     682200 :         P(e, mark+1) = phip[c] * P(e, mark+1);
    1106 [ +  - ][ +  - ]:     682200 :         P(e, mark+2) = phip[c] * P(e, mark+2);
    1107 [ +  - ][ +  - ]:     682200 :         P(e, mark+3) = phip[c] * P(e, mark+3);
    1108                 :            :       }
    1109                 :            :     }
    1110                 :            :   }
    1111                 :       6600 : }
    1112                 :            : 
    1113                 :            : void
    1114                 :          0 : VertexBasedMultiSpecies_P2(
    1115                 :            :   const std::map< std::size_t, std::vector< std::size_t > >& esup,
    1116                 :            :   const std::vector< std::size_t >& inpoel,
    1117                 :            :   const std::vector< std::size_t >& ndofel,
    1118                 :            :   std::size_t nelem,
    1119                 :            :   const std::vector< inciter::EOS >& mat_blk,
    1120                 :            :   const inciter::FaceData& fd,
    1121                 :            :   const tk::Fields& geoFace,
    1122                 :            :   const tk::Fields& geoElem,
    1123                 :            :   const tk::UnsMesh::Coords& coord,
    1124                 :            :   const tk::FluxFn& flux,
    1125                 :            :   const std::vector< std::size_t >& solidx,
    1126                 :            :   tk::Fields& U,
    1127                 :            :   tk::Fields& P,
    1128                 :            :   std::size_t nspec,
    1129                 :            :   std::vector< std::size_t >& shockmarker )
    1130                 :            : // *****************************************************************************
    1131                 :            : //  Kuzmin's vertex-based limiter for multi-species DGP2
    1132                 :            : //! \param[in] esup Elements surrounding points
    1133                 :            : //! \param[in] inpoel Element connectivity
    1134                 :            : //! \param[in] ndofel Vector of local number of degrees of freedom
    1135                 :            : //! \param[in] nelem Number of elements
    1136                 :            : //! \param[in] mat_blk EOS material block
    1137                 :            : //! \param[in] fd Face connectivity and boundary conditions object
    1138                 :            : //! \param[in] geoFace Face geometry array
    1139                 :            : //! \param[in] geoElem Element geometry array
    1140                 :            : //! \param[in] coord Array of nodal coordinates
    1141                 :            : //! \param[in] flux Riemann flux function to use
    1142                 :            : //! \param[in] solidx Solid material index indicator
    1143                 :            : //! \param[in,out] U High-order solution vector which gets limited
    1144                 :            : //! \param[in,out] P High-order primitive vector which gets limited
    1145                 :            : //! \param[in] nspec Number of species in this PDE system
    1146                 :            : //! \param[in,out] shockmarker Shock detection marker array
    1147                 :            : //! \details This vertex-based limiter function should be called for
    1148                 :            : //!   multispecies. For details see: Kuzmin, D. (2010). A vertex-based
    1149                 :            : //!   hierarchical slope limiter for p-adaptive discontinuous Galerkin methods.
    1150                 :            : //!   Journal of computational and applied mathematics, 233(12), 3077-3085.
    1151                 :            : // *****************************************************************************
    1152                 :            : {
    1153                 :          0 :   const auto rdof = inciter::g_inputdeck.get< tag::rdof >();
    1154                 :          0 :   const auto ndof = inciter::g_inputdeck.get< tag::ndof >();
    1155                 :          0 :   std::size_t ncomp = U.nprop()/rdof;
    1156                 :          0 :   std::size_t nprim = P.nprop()/rdof;
    1157                 :            : 
    1158                 :            :   // Evaluate the interface condition and mark the shock cells
    1159         [ -  - ]:          0 :   if (inciter::g_inputdeck.get< tag::shock_detector_coeff >() > 1e-6) {
    1160                 :          0 :     std::set< std::size_t > vars;
    1161         [ -  - ]:          0 :     for (std::size_t i=0; i<3; ++i)
    1162         [ -  - ]:          0 :       vars.insert(multispecies::momentumIdx(nspec, i));
    1163         [ -  - ]:          0 :     MarkShockCells(false, nelem, 1, ndof, rdof, mat_blk,
    1164                 :            :       ndofel, inpoel, coord, fd, geoFace, geoElem, flux, solidx, U, P,
    1165                 :            :       vars, shockmarker);
    1166                 :            :   }
    1167                 :            : 
    1168         [ -  - ]:          0 :   for (std::size_t e=0; e<nelem; ++e)
    1169                 :            :   {
    1170                 :            :     // If an rDG method is set up (P0P1), then, currently we compute the P1
    1171                 :            :     // basis functions and solutions by default. This implies that P0P1 is
    1172                 :            :     // unsupported in the p-adaptive DG (PDG). This is a workaround until we
    1173                 :            :     // have rdofel, which is needed to distinguish between ndofs and rdofs per
    1174                 :            :     // element for pDG.
    1175                 :            :     std::size_t dof_el;
    1176         [ -  - ]:          0 :     if (rdof > ndof)
    1177                 :            :     {
    1178                 :          0 :       dof_el = rdof;
    1179                 :            :     }
    1180                 :            :     else
    1181                 :            :     {
    1182                 :          0 :       dof_el = ndofel[e];
    1183                 :            :     }
    1184                 :            : 
    1185         [ -  - ]:          0 :     if (dof_el > 1)
    1186                 :            :     {
    1187                 :          0 :       std::vector< std::size_t > varc, varp;
    1188         [ -  - ]:          0 :       if(shockmarker[e]) {
    1189                 :            :         // When shockmarker is 1, there is discontinuity within the element.
    1190                 :            :         // Hence, the vertex-based limiter will be applied.
    1191                 :            : 
    1192 [ -  - ][ -  - ]:          0 :         for (std::size_t c=0; c<ncomp; ++c) varc.push_back(c);
    1193 [ -  - ][ -  - ]:          0 :         for (std::size_t c=0; c<nprim; ++c) varp.push_back(c);
    1194                 :            :       }
    1195                 :            :         // When shockmarker is 0, the density of minor species is not limited
    1196                 :            :         // since it will degrade the accuracy to second order.
    1197                 :            : 
    1198                 :            :       // Removing 3rd order DOFs if discontinuity is detected, and applying
    1199                 :            :       // limiting to the 2nd order/P1 solution
    1200         [ -  - ]:          0 :       for (std::size_t c=0; c<varc.size(); c++) {
    1201         [ -  - ]:          0 :         for(std::size_t idof = 4; idof < rdof; idof++) {
    1202                 :          0 :           auto mark = varc[c] * rdof + idof;
    1203         [ -  - ]:          0 :           U(e, mark) = 0.0;
    1204                 :            :         }
    1205                 :            :       }
    1206         [ -  - ]:          0 :       for (std::size_t c=0; c<varp.size(); c++) {
    1207         [ -  - ]:          0 :         for(std::size_t idof = 4; idof < rdof; idof++) {
    1208                 :          0 :           auto mark = varp[c] * rdof + idof;
    1209         [ -  - ]:          0 :           P(e, mark) = 0.0;
    1210                 :            :         }
    1211                 :            :       }
    1212                 :            : 
    1213 [ -  - ][ -  - ]:          0 :       std::vector< tk::real > phic_p1(ncomp, 1.0), phip_p1(nprim, 1.0);
    1214         [ -  - ]:          0 :       if (!varc.empty())
    1215         [ -  - ]:          0 :         VertexBasedLimiting(U, esup, inpoel, coord, e, rdof, dof_el,
    1216                 :            :           ncomp, phic_p1, varc);
    1217         [ -  - ]:          0 :       if (!varp.empty())
    1218         [ -  - ]:          0 :         VertexBasedLimiting(P, esup, inpoel, coord, e, rdof, dof_el,
    1219                 :            :           nprim, phip_p1, varp);
    1220                 :            : 
    1221 [ -  - ][ -  - ]:          0 :       std::vector< tk::real > phic_p2(ncomp, 1.0), phip_p2(nprim, 1.0);
    1222         [ -  - ]:          0 :       PositivityLimiting(1, nspec, mat_blk, ndof, dof_el, ndofel, e,
    1223                 :            :         inpoel, coord, fd.Esuel(), U, P, phic_p1, phic_p2, phip_p1, phip_p2);
    1224                 :            : 
    1225                 :            :       // TODO: Unit sum of mass fractions is maintained by using common limiter
    1226                 :            :       // for all species densities. Investigate better approaches.
    1227         [ -  - ]:          0 :       if (!g_inputdeck.get< tag::accuracy_test >()) {
    1228                 :          0 :         tk::real phi_rhos_p1(1.0), phi_rhos_p2(1.0);
    1229         [ -  - ]:          0 :         for (std::size_t k=0; k<nspec; ++k) {
    1230                 :          0 :           phi_rhos_p1 = std::min( phi_rhos_p1,
    1231                 :          0 :             phic_p1[multispecies::densityIdx(nspec, k)] );
    1232                 :          0 :           phi_rhos_p2 = std::min( phi_rhos_p2,
    1233                 :          0 :             phic_p2[multispecies::densityIdx(nspec, k)] );
    1234                 :            :         }
    1235                 :            :         // same limiter for all densities
    1236         [ -  - ]:          0 :         for (std::size_t k=0; k<nspec; ++k) {
    1237                 :          0 :           phic_p1[multispecies::densityIdx(nspec, k)] = phi_rhos_p1;
    1238                 :          0 :           phic_p2[multispecies::densityIdx(nspec, k)] = phi_rhos_p2;
    1239                 :            :         }
    1240                 :            :       }
    1241                 :            : 
    1242                 :            :       // apply limiter function to the solution
    1243         [ -  - ]:          0 :       for (std::size_t c=0; c<ncomp; ++c)
    1244                 :            :       {
    1245                 :          0 :         auto mark = c * rdof;
    1246         [ -  - ]:          0 :         for(std::size_t idof=1; idof<4; idof++)
    1247 [ -  - ][ -  - ]:          0 :           U(e, mark+idof) = phic_p1[c] * U(e, mark+idof);
    1248         [ -  - ]:          0 :         for(std::size_t idof=4; idof<rdof; idof++)
    1249 [ -  - ][ -  - ]:          0 :           U(e, mark+idof) = phic_p2[c] * U(e, mark+idof);
    1250                 :            :       }
    1251         [ -  - ]:          0 :       for (std::size_t c=0; c<nprim; ++c)
    1252                 :            :       {
    1253                 :          0 :         auto mark = c * rdof;
    1254         [ -  - ]:          0 :         for(std::size_t idof=1; idof<4; idof++)
    1255 [ -  - ][ -  - ]:          0 :           P(e, mark+idof) = phip_p1[c] * P(e, mark+idof);
    1256         [ -  - ]:          0 :         for(std::size_t idof=4; idof<rdof; idof++)
    1257 [ -  - ][ -  - ]:          0 :           P(e, mark+idof) = phip_p2[c] * P(e, mark+idof);
    1258                 :            :       }
    1259                 :            :     }
    1260                 :            :   }
    1261                 :          0 : }
    1262                 :            : 
    1263                 :            : void
    1264                 :     546450 : WENOLimiting( const tk::Fields& U,
    1265                 :            :               const std::vector< int >& esuel,
    1266                 :            :               std::size_t e,
    1267                 :            :               inciter::ncomp_t c,
    1268                 :            :               std::size_t rdof,
    1269                 :            :               tk::real cweight,
    1270                 :            :               std::array< std::vector< tk::real >, 3 >& limU )
    1271                 :            : // *****************************************************************************
    1272                 :            : //  WENO limiter function calculation for P1 dofs
    1273                 :            : //! \param[in] U High-order solution vector which is to be limited
    1274                 :            : //! \param[in] esuel Elements surrounding elements
    1275                 :            : //! \param[in] e Id of element whose solution is to be limited
    1276                 :            : //! \param[in] c Index of component which is to be limited
    1277                 :            : //! \param[in] rdof Maximum number of reconstructed degrees of freedom
    1278                 :            : //! \param[in] cweight Weight of the central stencil
    1279                 :            : //! \param[in,out] limU Limited gradients of component c
    1280                 :            : // *****************************************************************************
    1281                 :            : {
    1282                 :            :   std::array< std::array< tk::real, 3 >, 5 > gradu;
    1283                 :            :   std::array< tk::real, 5 > wtStencil, osc, wtDof;
    1284                 :            : 
    1285                 :     546450 :   auto mark = c*rdof;
    1286                 :            : 
    1287                 :            :   // reset all stencil values to zero
    1288 [ +  + ][ +  - ]:    3278700 :   for (auto& g : gradu) g.fill(0.0);
    1289         [ +  - ]:     546450 :   osc.fill(0);
    1290         [ +  - ]:     546450 :   wtDof.fill(0);
    1291         [ +  - ]:     546450 :   wtStencil.fill(0);
    1292                 :            : 
    1293                 :            :   // The WENO limiter uses solution data from the neighborhood in the form
    1294                 :            :   // of stencils to enforce non-oscillatory conditions. The immediate
    1295                 :            :   // (Von Neumann) neighborhood of a tetrahedral cell consists of the 4
    1296                 :            :   // cells that share faces with it. These are the 4 neighborhood-stencils
    1297                 :            :   // for the tetrahedron. The primary stencil is the tet itself. Weights are
    1298                 :            :   // assigned to these stencils, with the primary stencil usually assigned
    1299                 :            :   // the highest weight. The lower the primary/central weight, the more
    1300                 :            :   // dissipative the limiting effect. This central weight is usually problem
    1301                 :            :   // dependent. It is set higher for relatively weaker discontinuities, and
    1302                 :            :   // lower for stronger discontinuities.
    1303                 :            : 
    1304                 :            :   // primary stencil
    1305         [ +  - ]:     546450 :   gradu[0][0] = U(e, mark+1);
    1306         [ +  - ]:     546450 :   gradu[0][1] = U(e, mark+2);
    1307         [ +  - ]:     546450 :   gradu[0][2] = U(e, mark+3);
    1308                 :     546450 :   wtStencil[0] = cweight;
    1309                 :            : 
    1310                 :            :   // stencils from the neighborhood
    1311         [ +  + ]:    2732250 :   for (std::size_t is=1; is<5; ++is)
    1312                 :            :   {
    1313                 :    2185800 :     auto nel = esuel[ 4*e+(is-1) ];
    1314                 :            : 
    1315                 :            :     // ignore physical domain ghosts
    1316         [ +  + ]:    2185800 :     if (nel == -1)
    1317                 :            :     {
    1318         [ +  - ]:     359700 :       gradu[is].fill(0.0);
    1319                 :     359700 :       wtStencil[is] = 0.0;
    1320                 :     359700 :       continue;
    1321                 :            :     }
    1322                 :            : 
    1323                 :    1826100 :     std::size_t n = static_cast< std::size_t >( nel );
    1324         [ +  - ]:    1826100 :     gradu[is][0] = U(n, mark+1);
    1325         [ +  - ]:    1826100 :     gradu[is][1] = U(n, mark+2);
    1326         [ +  - ]:    1826100 :     gradu[is][2] = U(n, mark+3);
    1327                 :    1826100 :     wtStencil[is] = 1.0;
    1328                 :            :   }
    1329                 :            : 
    1330                 :            :   // From these stencils, an oscillation indicator is calculated, which
    1331                 :            :   // determines the effective weights for the high-order solution DOFs.
    1332                 :            :   // These effective weights determine the contribution of each of the
    1333                 :            :   // stencils to the high-order solution DOFs of the current cell which are
    1334                 :            :   // being limited. If this indicator detects a large oscillation in the
    1335                 :            :   // solution of the current cell, it reduces the effective weight for the
    1336                 :            :   // central stencil contribution to its high-order DOFs. This results in
    1337                 :            :   // a more dissipative and well-behaved solution in the troubled cell.
    1338                 :            : 
    1339                 :            :   // oscillation indicators
    1340         [ +  + ]:    3278700 :   for (std::size_t is=0; is<5; ++is)
    1341                 :    2732250 :     osc[is] = std::sqrt( tk::dot(gradu[is], gradu[is]) );
    1342                 :            : 
    1343                 :     546450 :   tk::real wtotal = 0;
    1344                 :            : 
    1345                 :            :   // effective weights for dofs
    1346         [ +  + ]:    3278700 :   for (std::size_t is=0; is<5; ++is)
    1347                 :            :   {
    1348                 :            :     // A small number (1.0e-8) is needed here to avoid dividing by a zero in
    1349                 :            :     // the case of a constant solution, where osc would be zero. The number
    1350                 :            :     // is not set to machine zero because it is squared, and a number
    1351                 :            :     // between 1.0e-8 to 1.0e-6 is needed.
    1352                 :    2732250 :     wtDof[is] = wtStencil[is] * pow( (1.0e-8 + osc[is]), -2 );
    1353                 :    2732250 :     wtotal += wtDof[is];
    1354                 :            :   }
    1355                 :            : 
    1356         [ +  + ]:    3278700 :   for (std::size_t is=0; is<5; ++is)
    1357                 :            :   {
    1358                 :    2732250 :     wtDof[is] = wtDof[is]/wtotal;
    1359                 :            :   }
    1360                 :            : 
    1361                 :     546450 :   limU[0][e] = 0.0;
    1362                 :     546450 :   limU[1][e] = 0.0;
    1363                 :     546450 :   limU[2][e] = 0.0;
    1364                 :            : 
    1365                 :            :   // limiter function
    1366         [ +  + ]:    3278700 :   for (std::size_t is=0; is<5; ++is)
    1367                 :            :   {
    1368                 :    2732250 :     limU[0][e] += wtDof[is]*gradu[is][0];
    1369                 :    2732250 :     limU[1][e] += wtDof[is]*gradu[is][1];
    1370                 :    2732250 :     limU[2][e] += wtDof[is]*gradu[is][2];
    1371                 :            :   }
    1372                 :     546450 : }
    1373                 :            : 
    1374                 :            : std::vector< tk::real >
    1375                 :    2662119 : SuperbeeLimiting( const tk::Fields& U,
    1376                 :            :                   const std::vector< int >& esuel,
    1377                 :            :                   const std::vector< std::size_t >& inpoel,
    1378                 :            :                   const tk::UnsMesh::Coords& coord,
    1379                 :            :                   std::size_t e,
    1380                 :            :                   std::size_t ndof,
    1381                 :            :                   std::size_t rdof,
    1382                 :            :                   std::size_t dof_el,
    1383                 :            :                   inciter:: ncomp_t ncomp,
    1384                 :            :                   tk::real beta_lim )
    1385                 :            : // *****************************************************************************
    1386                 :            : //  Superbee limiter function calculation for P1 dofs
    1387                 :            : //! \param[in] U High-order solution vector which is to be limited
    1388                 :            : //! \param[in] esuel Elements surrounding elements
    1389                 :            : //! \param[in] inpoel Element connectivity
    1390                 :            : //! \param[in] coord Array of nodal coordinates
    1391                 :            : //! \param[in] e Id of element whose solution is to be limited
    1392                 :            : //! \param[in] ndof Maximum number of degrees of freedom
    1393                 :            : //! \param[in] rdof Maximum number of reconstructed degrees of freedom
    1394                 :            : //! \param[in] dof_el Local number of degrees of freedom
    1395                 :            : //! \param[in] ncomp Number of scalar components in this PDE system
    1396                 :            : //! \param[in] beta_lim Parameter which is equal to 2 for Superbee and 1 for
    1397                 :            : //!   minmod limiter
    1398                 :            : //! \return phi Limiter function for solution in element e
    1399                 :            : // *****************************************************************************
    1400                 :            : {
    1401                 :            :   // Superbee is a TVD limiter, which uses min-max bounds that the
    1402                 :            :   // high-order solution should satisfy, to ensure TVD properties. For a
    1403                 :            :   // high-order method like DG, this involves the following steps:
    1404                 :            :   // 1. Find min-max bounds in the immediate neighborhood of cell.
    1405                 :            :   // 2. Calculate the Superbee function for all the points where solution
    1406                 :            :   //    needs to be reconstructed to (all quadrature points). From these,
    1407                 :            :   //    use the minimum value of the limiter function.
    1408                 :            : 
    1409 [ +  - ][ +  - ]:    5324238 :   std::vector< tk::real > uMin(ncomp, 0.0), uMax(ncomp, 0.0);
    1410                 :            : 
    1411         [ +  + ]:    7229514 :   for (inciter::ncomp_t c=0; c<ncomp; ++c)
    1412                 :            :   {
    1413                 :    4567395 :     auto mark = c*rdof;
    1414         [ +  - ]:    4567395 :     uMin[c] = U(e, mark);
    1415         [ +  - ]:    4567395 :     uMax[c] = U(e, mark);
    1416                 :            :   }
    1417                 :            : 
    1418                 :            :   // ----- Step-1: find min/max in the neighborhood
    1419         [ +  + ]:   13310595 :   for (std::size_t is=0; is<4; ++is)
    1420                 :            :   {
    1421                 :   10648476 :     auto nel = esuel[ 4*e+is ];
    1422                 :            : 
    1423                 :            :     // ignore physical domain ghosts
    1424         [ +  + ]:   10648476 :     if (nel == -1) continue;
    1425                 :            : 
    1426                 :    8895501 :     auto n = static_cast< std::size_t >( nel );
    1427         [ +  + ]:   24155406 :     for (inciter::ncomp_t c=0; c<ncomp; ++c)
    1428                 :            :     {
    1429                 :   15259905 :       auto mark = c*rdof;
    1430         [ +  - ]:   15259905 :       uMin[c] = std::min(uMin[c], U(n, mark));
    1431         [ +  - ]:   15259905 :       uMax[c] = std::max(uMax[c], U(n, mark));
    1432                 :            :     }
    1433                 :            :   }
    1434                 :            : 
    1435                 :            :   // ----- Step-2: loop over all quadrature points to get limiter function
    1436                 :            : 
    1437                 :            :   // to loop over all the quadrature points of all faces of element e,
    1438                 :            :   // coordinates of the quadrature points are needed.
    1439                 :            :   // Number of quadrature points for face integration
    1440         [ +  - ]:    2662119 :   auto ng = tk::NGfa(ndof);
    1441                 :            : 
    1442                 :            :   // arrays for quadrature points
    1443                 :    5324238 :   std::array< std::vector< tk::real >, 2 > coordgp;
    1444                 :    5324238 :   std::vector< tk::real > wgp;
    1445                 :            : 
    1446         [ +  - ]:    2662119 :   coordgp[0].resize( ng );
    1447         [ +  - ]:    2662119 :   coordgp[1].resize( ng );
    1448         [ +  - ]:    2662119 :   wgp.resize( ng );
    1449                 :            : 
    1450                 :            :   // get quadrature point weights and coordinates for triangle
    1451         [ +  - ]:    2662119 :   tk::GaussQuadratureTri( ng, coordgp, wgp );
    1452                 :            : 
    1453                 :    2662119 :   const auto& cx = coord[0];
    1454                 :    2662119 :   const auto& cy = coord[1];
    1455                 :    2662119 :   const auto& cz = coord[2];
    1456                 :            : 
    1457                 :            :   // Extract the element coordinates
    1458                 :            :   std::array< std::array< tk::real, 3>, 4 > coordel {{
    1459                 :    7986357 :     {{ cx[ inpoel[4*e  ] ], cy[ inpoel[4*e  ] ], cz[ inpoel[4*e  ] ] }},
    1460                 :    7986357 :     {{ cx[ inpoel[4*e+1] ], cy[ inpoel[4*e+1] ], cz[ inpoel[4*e+1] ] }},
    1461                 :    7986357 :     {{ cx[ inpoel[4*e+2] ], cy[ inpoel[4*e+2] ], cz[ inpoel[4*e+2] ] }},
    1462                 :   23959071 :     {{ cx[ inpoel[4*e+3] ], cy[ inpoel[4*e+3] ], cz[ inpoel[4*e+3] ] }} }};
    1463                 :            : 
    1464                 :            :   // Compute the determinant of Jacobian matrix
    1465                 :            :   auto detT =
    1466                 :    2662119 :     tk::Jacobian( coordel[0], coordel[1], coordel[2], coordel[3] );
    1467                 :            : 
    1468                 :            :   // initialize limiter function
    1469         [ +  - ]:    2662119 :   std::vector< tk::real > phi(ncomp, 1.0);
    1470         [ +  + ]:   13310595 :   for (std::size_t lf=0; lf<4; ++lf)
    1471                 :            :   {
    1472                 :            :     // Extract the face coordinates
    1473                 :   10648476 :     std::array< std::size_t, 3 > inpofa_l {{ inpoel[4*e+tk::lpofa[lf][0]],
    1474                 :   10648476 :                                              inpoel[4*e+tk::lpofa[lf][1]],
    1475                 :   21296952 :                                              inpoel[4*e+tk::lpofa[lf][2]] }};
    1476                 :            : 
    1477                 :            :     std::array< std::array< tk::real, 3>, 3 > coordfa {{
    1478                 :   31945428 :       {{ cx[ inpofa_l[0] ], cy[ inpofa_l[0] ], cz[ inpofa_l[0] ] }},
    1479                 :   31945428 :       {{ cx[ inpofa_l[1] ], cy[ inpofa_l[1] ], cz[ inpofa_l[1] ] }},
    1480                 :   63890856 :       {{ cx[ inpofa_l[2] ], cy[ inpofa_l[2] ], cz[ inpofa_l[2] ] }} }};
    1481                 :            : 
    1482                 :            :     // Gaussian quadrature
    1483         [ +  + ]:   43063812 :     for (std::size_t igp=0; igp<ng; ++igp)
    1484                 :            :     {
    1485                 :            :       // Compute the coordinates of quadrature point at physical domain
    1486         [ +  - ]:   32415336 :       auto gp = tk::eval_gp( igp, coordfa, coordgp );
    1487                 :            : 
    1488                 :            :       //Compute the basis functions
    1489                 :            :       auto B_l = tk::eval_basis( rdof,
    1490                 :   32415336 :             tk::Jacobian( coordel[0], gp, coordel[2], coordel[3] ) / detT,
    1491                 :   32415336 :             tk::Jacobian( coordel[0], coordel[1], gp, coordel[3] ) / detT,
    1492         [ +  - ]:  129661344 :             tk::Jacobian( coordel[0], coordel[1], coordel[2], gp ) / detT );
    1493                 :            : 
    1494                 :            :       auto state =
    1495         [ +  - ]:   64830672 :         tk::eval_state(ncomp, rdof, dof_el, e, U, B_l);
    1496                 :            : 
    1497 [ -  + ][ -  - ]:   32415336 :       Assert( state.size() == ncomp, "Size mismatch" );
         [ -  - ][ -  - ]
    1498                 :            : 
    1499                 :            :       // compute the limiter function
    1500         [ +  + ]:   89573616 :       for (inciter::ncomp_t c=0; c<ncomp; ++c)
    1501                 :            :       {
    1502                 :   57158280 :         auto phi_gp = 1.0;
    1503                 :   57158280 :         auto mark = c*rdof;
    1504         [ +  - ]:   57158280 :         auto uNeg = state[c] - U(e, mark);
    1505         [ +  + ]:   57158280 :         if (uNeg > 1.0e-14)
    1506                 :            :         {
    1507                 :    2780046 :           uNeg = std::max(uNeg, 1.0e-08);
    1508         [ +  - ]:    2780046 :           phi_gp = std::min( 1.0, (uMax[c]-U(e, mark))/(2.0*uNeg) );
    1509                 :            :         }
    1510         [ +  + ]:   54378234 :         else if (uNeg < -1.0e-14)
    1511                 :            :         {
    1512                 :    2653432 :           uNeg = std::min(uNeg, -1.0e-08);
    1513         [ +  - ]:    2653432 :           phi_gp = std::min( 1.0, (uMin[c]-U(e, mark))/(2.0*uNeg) );
    1514                 :            :         }
    1515                 :            :         else
    1516                 :            :         {
    1517                 :   51724802 :           phi_gp = 1.0;
    1518                 :            :         }
    1519                 :  114316560 :         phi_gp = std::max( 0.0,
    1520                 :  114316560 :                            std::max( std::min(beta_lim*phi_gp, 1.0),
    1521                 :   57158280 :                                      std::min(phi_gp, beta_lim) ) );
    1522                 :   57158280 :         phi[c] = std::min( phi[c], phi_gp );
    1523                 :            :       }
    1524                 :            :     }
    1525                 :            :   }
    1526                 :            : 
    1527                 :    5324238 :   return phi;
    1528                 :            : }
    1529                 :            : 
    1530                 :            : void
    1531                 :    3893104 : VertexBasedLimiting(
    1532                 :            :   const tk::Fields& U,
    1533                 :            :   const std::map< std::size_t, std::vector< std::size_t > >& esup,
    1534                 :            :   const std::vector< std::size_t >& inpoel,
    1535                 :            :   const tk::UnsMesh::Coords& coord,
    1536                 :            :   std::size_t e,
    1537                 :            :   std::size_t rdof,
    1538                 :            :   std::size_t dof_el,
    1539                 :            :   std::size_t ncomp,
    1540                 :            :   std::vector< tk::real >& phi,
    1541                 :            :   const std::vector< std::size_t >& VarList )
    1542                 :            : // *****************************************************************************
    1543                 :            : //  Kuzmin's vertex-based limiter function calculation for P1 dofs
    1544                 :            : //! \param[in] U High-order solution vector which is to be limited
    1545                 :            : //! \param[in] esup Elements surrounding points
    1546                 :            : //! \param[in] inpoel Element connectivity
    1547                 :            : //! \param[in] coord Array of nodal coordinates
    1548                 :            : //! \param[in] e Id of element whose solution is to be limited
    1549                 :            : //! \param[in] rdof Maximum number of reconstructed degrees of freedom
    1550                 :            : //! \param[in] dof_el Local number of degrees of freedom
    1551                 :            : //! \param[in] ncomp Number of scalar components in this PDE system
    1552                 :            : //! \param[in,out] phi Limiter function for solution in element e
    1553                 :            : //! \param[in] VarList List of variable indices to be limited
    1554                 :            : // *****************************************************************************
    1555                 :            : {
    1556                 :            :   // Kuzmin's vertex-based TVD limiter uses min-max bounds that the
    1557                 :            :   // high-order solution should satisfy, to ensure TVD properties. For a
    1558                 :            :   // high-order method like DG, this involves the following steps:
    1559                 :            :   // 1. Find min-max bounds in the nodal-neighborhood of cell.
    1560                 :            :   // 2. Calculate the limiter function (Superbee) for all the vertices of cell.
    1561                 :            :   //    From these, use the minimum value of the limiter function.
    1562                 :            : 
    1563                 :            :   // Prepare for calculating Basis functions
    1564                 :    3893104 :   const auto& cx = coord[0];
    1565                 :    3893104 :   const auto& cy = coord[1];
    1566                 :    3893104 :   const auto& cz = coord[2];
    1567                 :            : 
    1568                 :            :   // Extract the element coordinates
    1569                 :            :   std::array< std::array< tk::real, 3>, 4 > coordel {{
    1570                 :   11679312 :     {{ cx[ inpoel[4*e  ] ], cy[ inpoel[4*e  ] ], cz[ inpoel[4*e  ] ] }},
    1571                 :   11679312 :     {{ cx[ inpoel[4*e+1] ], cy[ inpoel[4*e+1] ], cz[ inpoel[4*e+1] ] }},
    1572                 :   11679312 :     {{ cx[ inpoel[4*e+2] ], cy[ inpoel[4*e+2] ], cz[ inpoel[4*e+2] ] }},
    1573                 :   35037936 :     {{ cx[ inpoel[4*e+3] ], cy[ inpoel[4*e+3] ], cz[ inpoel[4*e+3] ] }} }};
    1574                 :            : 
    1575                 :            :   // Compute the determinant of Jacobian matrix
    1576                 :            :   auto detT =
    1577                 :    3893104 :     tk::Jacobian( coordel[0], coordel[1], coordel[2], coordel[3] );
    1578                 :            : 
    1579         [ +  - ]:    7786208 :   std::vector< tk::real > uMin(VarList.size(), 0.0),
    1580         [ +  - ]:    7786208 :                           uMax(VarList.size(), 0.0);
    1581                 :            : 
    1582                 :            :   // loop over all nodes of the element e
    1583         [ +  + ]:   19465520 :   for (std::size_t lp=0; lp<4; ++lp)
    1584                 :            :   {
    1585                 :            :     // reset min/max
    1586         [ +  + ]:   73580496 :     for (std::size_t i=0; i<VarList.size(); ++i)
    1587                 :            :     {
    1588                 :   58008080 :       auto mark = VarList[i]*rdof;
    1589         [ +  - ]:   58008080 :       uMin[i] = U(e, mark);
    1590         [ +  - ]:   58008080 :       uMax[i] = U(e, mark);
    1591                 :            :     }
    1592                 :   15572416 :     auto p = inpoel[4*e+lp];
    1593         [ +  - ]:   15572416 :     const auto& pesup = tk::cref_find(esup, p);
    1594                 :            : 
    1595                 :            :     // ----- Step-1: find min/max in the neighborhood of node p
    1596                 :            :     // loop over all the internal elements surrounding this node p
    1597         [ +  + ]:  258557560 :     for (auto er : pesup)
    1598                 :            :     {
    1599         [ +  + ]: 1151368716 :       for (std::size_t i=0; i<VarList.size(); ++i)
    1600                 :            :       {
    1601                 :  908383572 :         auto mark = VarList[i]*rdof;
    1602         [ +  - ]:  908383572 :         uMin[i] = std::min(uMin[i], U(er, mark));
    1603         [ +  - ]:  908383572 :         uMax[i] = std::max(uMax[i], U(er, mark));
    1604                 :            :       }
    1605                 :            :     }
    1606                 :            : 
    1607                 :            :     // ----- Step-2: compute the limiter function at this node
    1608                 :            :     // find high-order solution
    1609                 :   31144832 :     std::vector< tk::real > state;
    1610                 :   15572416 :     std::array< tk::real, 3 > gp{cx[p], cy[p], cz[p]};
    1611                 :            :     auto B_p = tk::eval_basis( rdof,
    1612                 :   15572416 :           tk::Jacobian( coordel[0], gp, coordel[2], coordel[3] ) / detT,
    1613                 :   15572416 :           tk::Jacobian( coordel[0], coordel[1], gp, coordel[3] ) / detT,
    1614         [ +  - ]:   62289664 :           tk::Jacobian( coordel[0], coordel[1], coordel[2], gp ) / detT );
    1615         [ +  - ]:   15572416 :     state = tk::eval_state(ncomp, rdof, dof_el, e, U, B_p);
    1616                 :            : 
    1617 [ -  + ][ -  - ]:   15572416 :     Assert( state.size() == ncomp, "Size mismatch" );
         [ -  - ][ -  - ]
    1618                 :            : 
    1619                 :            :     // compute the limiter function
    1620         [ +  + ]:   73580496 :     for (std::size_t i=0; i<VarList.size(); ++i)
    1621                 :            :     {
    1622                 :   58008080 :       auto c = VarList[i];
    1623                 :   58008080 :       auto phi_gp = 1.0;
    1624                 :   58008080 :       auto mark = c*rdof;
    1625         [ +  - ]:   58008080 :       auto uNeg = state[c] - U(e, mark);
    1626         [ +  - ]:   58008080 :       auto uref = std::max(std::fabs(U(e,mark)), 1e-14);
    1627         [ +  + ]:   58008080 :       if (uNeg > 1.0e-06*uref)
    1628                 :            :       {
    1629         [ +  - ]:    8666955 :         phi_gp = std::min( 1.0, (uMax[i]-U(e, mark))/uNeg );
    1630                 :            :       }
    1631         [ +  + ]:   49341125 :       else if (uNeg < -1.0e-06*uref)
    1632                 :            :       {
    1633         [ +  - ]:    8711394 :         phi_gp = std::min( 1.0, (uMin[i]-U(e, mark))/uNeg );
    1634                 :            :       }
    1635                 :            :       else
    1636                 :            :       {
    1637                 :   40629731 :         phi_gp = 1.0;
    1638                 :            :       }
    1639                 :            : 
    1640                 :            :     // ----- Step-3: take the minimum of the nodal-limiter functions
    1641                 :   58008080 :       phi[c] = std::min( phi[c], phi_gp );
    1642                 :            :     }
    1643                 :            :   }
    1644                 :    3893104 : }
    1645                 :            : 
    1646                 :            : void
    1647                 :          0 : VertexBasedLimiting_P2( const std::vector< std::vector< tk::real > >& unk,
    1648                 :            :   const tk::Fields& U,
    1649                 :            :   const std::map< std::size_t, std::vector< std::size_t > >& esup,
    1650                 :            :   const std::vector< std::size_t >& inpoel,
    1651                 :            :   std::size_t e,
    1652                 :            :   std::size_t rdof,
    1653                 :            :   [[maybe_unused]] std::size_t dof_el,
    1654                 :            :   std::size_t ncomp,
    1655                 :            :   const std::vector< std::size_t >& gid,
    1656                 :            :   const std::unordered_map< std::size_t, std::size_t >& bid,
    1657                 :            :   const std::vector< std::vector<tk::real> >& NodalExtrm,
    1658                 :            :   const std::vector< std::size_t >& VarList,
    1659                 :            :   std::vector< tk::real >& phi )
    1660                 :            : // *****************************************************************************
    1661                 :            : //  Kuzmin's vertex-based limiter function calculation for P2 dofs
    1662                 :            : //! \param[in] U High-order solution vector which is to be limited
    1663                 :            : //! \param[in] esup Elements surrounding points
    1664                 :            : //! \param[in] inpoel Element connectivity
    1665                 :            : //! \param[in] e Id of element whose solution is to be limited
    1666                 :            : //! \param[in] rdof Maximum number of reconstructed degrees of freedom
    1667                 :            : //! \param[in] dof_el Local number of degrees of freedom
    1668                 :            : //! \param[in] ncomp Number of scalar components in this PDE system
    1669                 :            : //! \param[in] gid Local->global node id map
    1670                 :            : //! \param[in] bid Local chare-boundary node ids (value) associated to
    1671                 :            : //!   global node ids (key)
    1672                 :            : //! \param[in] NodalExtrm Chare-boundary nodal extrema
    1673                 :            : //! \param[in] VarList List of variable indices that need to be limited
    1674                 :            : //! \param[out] phi Limiter function for solution in element e
    1675                 :            : //! \details This function limits the P2 dofs of P2 solution in a hierachical
    1676                 :            : //!   way to P1 dof limiting. Here we treat the first order derivatives the same
    1677                 :            : //!   way as cell average while second order derivatives represent the gradients
    1678                 :            : //!   to be limited in the P1 limiting procedure.
    1679                 :            : // *****************************************************************************
    1680                 :            : {
    1681                 :          0 :   const auto nelem = inpoel.size() / 4;
    1682                 :            : 
    1683                 :          0 :   std::vector< std::vector< tk::real > > uMin, uMax;
    1684 [ -  - ][ -  - ]:          0 :   uMin.resize( VarList.size(), std::vector<tk::real>(3, 0.0) );
    1685 [ -  - ][ -  - ]:          0 :   uMax.resize( VarList.size(), std::vector<tk::real>(3, 0.0) );
    1686                 :            : 
    1687                 :            :   // The coordinates of centroid in the reference domain
    1688                 :          0 :   std::array< std::vector< tk::real >, 3 > center;
    1689         [ -  - ]:          0 :   center[0].resize(1, 0.25);
    1690         [ -  - ]:          0 :   center[1].resize(1, 0.25);
    1691         [ -  - ]:          0 :   center[2].resize(1, 0.25);
    1692                 :            : 
    1693                 :          0 :   std::array< std::array< tk::real, 4 >, 3 > cnodes{{
    1694                 :            :     {{0, 1, 0, 0}},
    1695                 :            :     {{0, 0, 1, 0}},
    1696                 :            :     {{0, 0, 0, 1}} }};
    1697                 :            : 
    1698                 :            :   // loop over all nodes of the element e
    1699         [ -  - ]:          0 :   for (std::size_t lp=0; lp<4; ++lp)
    1700                 :            :   {
    1701                 :            :     // Find the max/min first-order derivatives for internal element
    1702         [ -  - ]:          0 :     for (std::size_t i=0; i<VarList.size(); ++i)
    1703                 :            :     {
    1704         [ -  - ]:          0 :       for (std::size_t idir=1; idir < 4; ++idir)
    1705                 :            :       {
    1706                 :          0 :         uMin[i][idir-1] = unk[VarList[i]][idir];
    1707                 :          0 :         uMax[i][idir-1] = unk[VarList[i]][idir];
    1708                 :            :       }
    1709                 :            :     }
    1710                 :            : 
    1711                 :          0 :     auto p = inpoel[4*e+lp];
    1712         [ -  - ]:          0 :     const auto& pesup = tk::cref_find(esup, p);
    1713                 :            : 
    1714                 :            :     // Step-1: find min/max first order derivative at the centroid in the
    1715                 :            :     // neighborhood of node p
    1716         [ -  - ]:          0 :     for (auto er : pesup)
    1717                 :            :     {
    1718         [ -  - ]:          0 :       if(er < nelem)      // If this is internal element
    1719                 :            :       {
    1720                 :            :         // Compute the derivatives of basis function in the reference domain
    1721                 :            :         auto dBdxi_er = tk::eval_dBdxi(rdof,
    1722         [ -  - ]:          0 :           {{center[0][0], center[1][0], center[2][0]}});
    1723                 :            : 
    1724         [ -  - ]:          0 :         for (std::size_t i=0; i<VarList.size(); ++i)
    1725                 :            :         {
    1726                 :          0 :           auto mark = VarList[i]*rdof;
    1727         [ -  - ]:          0 :           for (std::size_t idir = 0; idir < 3; ++idir)
    1728                 :            :           {
    1729                 :            :             // The first order derivative at the centroid of element er
    1730                 :          0 :             tk::real slope_er(0.0);
    1731         [ -  - ]:          0 :             for(std::size_t idof = 1; idof < rdof; idof++)
    1732         [ -  - ]:          0 :               slope_er += U(er, mark+idof) * dBdxi_er[idir][idof];
    1733                 :            : 
    1734                 :          0 :             uMin[i][idir] = std::min(uMin[i][idir], slope_er);
    1735                 :          0 :             uMax[i][idir] = std::max(uMax[i][idir], slope_er);
    1736                 :            : 
    1737                 :            :           }
    1738                 :            :         }
    1739                 :            :       }
    1740                 :            :     }
    1741                 :            :     // If node p is the chare-boundary node, find min/max by comparing with
    1742                 :            :     // the chare-boundary nodal extrema from vector NodalExtrm
    1743         [ -  - ]:          0 :     auto gip = bid.find( gid[p] );
    1744         [ -  - ]:          0 :     if(gip != end(bid))
    1745                 :            :     {
    1746                 :          0 :       auto ndof_NodalExtrm = NodalExtrm[0].size() / (ncomp * 2);
    1747         [ -  - ]:          0 :       for (std::size_t i=0; i<VarList.size(); ++i)
    1748                 :            :       {
    1749         [ -  - ]:          0 :         for (std::size_t idir = 0; idir < 3; idir++)
    1750                 :            :         {
    1751                 :          0 :           auto max_mark = 2*VarList[i]*ndof_NodalExtrm + 2*idir;
    1752                 :          0 :           auto min_mark = max_mark + 1;
    1753                 :          0 :           const auto& ex = NodalExtrm[gip->second];
    1754                 :          0 :           uMax[i][idir] = std::max(ex[max_mark], uMax[i][idir]);
    1755                 :          0 :           uMin[i][idir] = std::min(ex[min_mark], uMin[i][idir]);
    1756                 :            :         }
    1757                 :            :       }
    1758                 :            :     }
    1759                 :            : 
    1760                 :            :     //Step-2: compute the limiter function at this node
    1761                 :          0 :     std::array< tk::real, 3 > node{cnodes[0][lp], cnodes[1][lp], cnodes[2][lp]};
    1762                 :            : 
    1763                 :            :     // find high-order solution
    1764                 :          0 :     std::vector< std::array< tk::real, 3 > > state;
    1765         [ -  - ]:          0 :     state.resize(VarList.size());
    1766                 :            : 
    1767         [ -  - ]:          0 :     for (std::size_t i=0; i<VarList.size(); ++i)
    1768                 :            :     {
    1769                 :          0 :       auto dx = node[0] - center[0][0];
    1770                 :          0 :       auto dy = node[1] - center[1][0];
    1771                 :          0 :       auto dz = node[2] - center[2][0];
    1772                 :            : 
    1773                 :          0 :       auto c = VarList[i];
    1774                 :            : 
    1775                 :          0 :       state[i][0] = unk[c][1] + unk[c][4]*dx + unk[c][7]*dy + unk[c][8]*dz;
    1776                 :          0 :       state[i][1] = unk[c][2] + unk[c][5]*dy + unk[c][7]*dx + unk[c][9]*dz;
    1777                 :          0 :       state[i][2] = unk[c][3] + unk[c][6]*dz + unk[c][8]*dx + unk[c][9]*dy;
    1778                 :            :     }
    1779                 :            : 
    1780                 :            :     // compute the limiter function
    1781         [ -  - ]:          0 :     for (std::size_t i=0; i<VarList.size(); ++i)
    1782                 :            :     {
    1783                 :          0 :       auto c = VarList[i];
    1784                 :          0 :       tk::real phi_dir(1.0);
    1785         [ -  - ]:          0 :       for (std::size_t idir = 1; idir <= 3; ++idir)
    1786                 :            :       {
    1787                 :          0 :         phi_dir = 1.0;
    1788                 :          0 :         auto uNeg = state[i][idir-1] - unk[c][idir];
    1789                 :          0 :         auto uref = std::max(std::fabs(unk[c][idir]), 1e-14);
    1790         [ -  - ]:          0 :         if (uNeg > 1.0e-6*uref)
    1791                 :            :         {
    1792                 :          0 :           phi_dir =
    1793                 :          0 :             std::min( 1.0, ( uMax[i][idir-1] - unk[c][idir])/uNeg );
    1794                 :            :         }
    1795         [ -  - ]:          0 :         else if (uNeg < -1.0e-6*uref)
    1796                 :            :         {
    1797                 :          0 :           phi_dir =
    1798                 :          0 :             std::min( 1.0, ( uMin[i][idir-1] - unk[c][idir])/uNeg );
    1799                 :            :         }
    1800                 :            :         else
    1801                 :            :         {
    1802                 :          0 :           phi_dir = 1.0;
    1803                 :            :         }
    1804                 :            : 
    1805                 :          0 :         phi[c] = std::min( phi[c], phi_dir );
    1806                 :            :       }
    1807                 :            :     }
    1808                 :            :   }
    1809                 :          0 : }
    1810                 :            : 
    1811                 :    1033067 : void consistentMultiMatLimiting_P1(
    1812                 :            :   std::size_t nmat,
    1813                 :            :   std::size_t rdof,
    1814                 :            :   std::size_t e,
    1815                 :            :   const std::vector< std::size_t >& solidx,
    1816                 :            :   tk::Fields& U,
    1817                 :            :   [[maybe_unused]] tk::Fields& P,
    1818                 :            :   std::vector< tk::real >& phic_p1,
    1819                 :            :   std::vector< tk::real >& phic_p2 )
    1820                 :            : // *****************************************************************************
    1821                 :            : //  Consistent limiter modifications for conservative variables
    1822                 :            : //! \param[in] nmat Number of materials in this PDE system
    1823                 :            : //! \param[in] rdof Total number of reconstructed dofs
    1824                 :            : //! \param[in] e Element being checked for consistency
    1825                 :            : //! \param[in] solidx Solid material index indicator
    1826                 :            : //! \param[in] U Vector of conservative variables
    1827                 :            : //! \param[in] P Vector of primitive variables
    1828                 :            : //! \param[in,out] phic_p1 Vector of limiter functions for P1 dofs of the
    1829                 :            : //!   conserved quantities
    1830                 :            : //! \param[in,out] phip_p2 Vector of limiter functions for P2 dofs of the
    1831                 :            : //!   conserved quantities
    1832                 :            : // *****************************************************************************
    1833                 :            : {
    1834                 :            :   // find the limiter-function for volume-fractions
    1835                 :    1033067 :   auto phi_al_p1(1.0), phi_al_p2(1.0), almax(0.0), dalmax(0.0);
    1836                 :            :   //std::size_t nmax(0);
    1837         [ +  + ]:    3151761 :   for (std::size_t k=0; k<nmat; ++k)
    1838                 :            :   {
    1839                 :    2118694 :     phi_al_p1 = std::min( phi_al_p1, phic_p1[volfracIdx(nmat, k)] );
    1840         [ -  + ]:    2118694 :     if(rdof > 4)
    1841                 :          0 :       phi_al_p2 = std::min( phi_al_p2, phic_p2[volfracIdx(nmat, k)] );
    1842 [ +  - ][ +  + ]:    2118694 :     if (almax < U(e,volfracDofIdx(nmat, k, rdof, 0)))
    1843                 :            :     {
    1844                 :            :       //nmax = k;
    1845         [ +  - ]:    1483800 :       almax = U(e,volfracDofIdx(nmat, k, rdof, 0));
    1846                 :            :     }
    1847                 :    2118694 :     tk::real dmax(0.0);
    1848                 :    2118694 :     dmax = std::max(
    1849                 :            :              std::max(
    1850         [ +  - ]:    2118694 :                std::abs(U(e,volfracDofIdx(nmat, k, rdof, 1))),
    1851         [ +  - ]:    2118694 :                std::abs(U(e,volfracDofIdx(nmat, k, rdof, 2))) ),
    1852         [ +  - ]:    4237388 :                std::abs(U(e,volfracDofIdx(nmat, k, rdof, 3))) );
    1853                 :    2118694 :     dalmax = std::max( dalmax, dmax );
    1854                 :            :   }
    1855                 :            : 
    1856                 :    1033067 :   auto al_band = 1e-4;
    1857                 :            : 
    1858                 :            :   //phi_al = phic[nmax];
    1859                 :            : 
    1860                 :            :   // determine if cell is a material-interface cell based on ad-hoc tolerances.
    1861                 :            :   // if interface-cell, then modify high-order dofs of conserved unknowns
    1862                 :            :   // consistently and use same limiter for all equations.
    1863                 :            :   // Slopes of solution variables \alpha_k \rho_k and \alpha_k \rho_k E_k need
    1864                 :            :   // to be modified in interface cells, such that slopes in the \rho_k and
    1865                 :            :   // \rho_k E_k part are ignored and only slopes in \alpha_k are considered.
    1866                 :            :   // Ideally, we would like to not do this, but this is a necessity to avoid
    1867                 :            :   // limiter-limiter interactions in multiphase CFD (see "K.-M. Shyue, F. Xiao,
    1868                 :            :   // An Eulerian interface sharpening algorithm for compressible two-phase flow:
    1869                 :            :   // the algebraic THINC approach, Journal of Computational Physics 268, 2014,
    1870                 :            :   // 326–354. doi:10.1016/j.jcp.2014.03.010." and "A. Chiapolino, R. Saurel,
    1871                 :            :   // B. Nkonga, Sharpening diffuse interfaces with compressible fluids on
    1872                 :            :   // unstructured meshes, Journal of Computational Physics 340 (2017) 389–417.
    1873                 :            :   // doi:10.1016/j.jcp.2017.03.042."). This approximation should be applied in
    1874                 :            :   // as narrow a band of interface-cells as possible. The following if-test
    1875                 :            :   // defines this band of interface-cells. This tests checks the value of the
    1876                 :            :   // maximum volume-fraction in the cell (almax) and the maximum change in
    1877                 :            :   // volume-fraction in the cell (dalmax, calculated from second-order DOFs),
    1878                 :            :   // to determine the band of interface-cells where the aforementioned fix needs
    1879                 :            :   // to be applied. This if-test says that, the fix is applied when the change
    1880                 :            :   // in volume-fraction across a cell is greater than 0.1, *and* the
    1881                 :            :   // volume-fraction is between 0.1 and 0.9.
    1882         [ +  - ]:    1033067 :   if ( //dalmax > al_band &&
    1883         [ +  + ]:    1033067 :        (almax > al_band && almax < (1.0-al_band)) )
    1884                 :            :   {
    1885                 :            :     // 1. consistent high-order dofs
    1886         [ +  + ]:      78426 :     for (std::size_t k=0; k<nmat; ++k)
    1887                 :            :     {
    1888                 :            :       auto alk =
    1889         [ +  - ]:      52350 :         std::max( 1.0e-14, U(e,volfracDofIdx(nmat, k, rdof, 0)) );
    1890         [ +  - ]:      52350 :       auto rhok = U(e,densityDofIdx(nmat, k, rdof, 0)) / alk;
    1891         [ +  - ]:      52350 :       auto rhoE = U(e,energyDofIdx(nmat, k, rdof, 0)) / alk;
    1892         [ +  + ]:     209400 :       for (std::size_t idof=1; idof<rdof; ++idof)
    1893                 :            :       {
    1894         [ +  - ]:     157050 :           U(e,densityDofIdx(nmat, k, rdof, idof)) = rhok *
    1895         [ +  - ]:     157050 :             U(e,volfracDofIdx(nmat, k, rdof, idof));
    1896         [ +  - ]:     157050 :           U(e,energyDofIdx(nmat, k, rdof, idof)) = rhoE *
    1897         [ +  - ]:     157050 :             U(e,volfracDofIdx(nmat, k, rdof, idof));
    1898                 :            :       }
    1899         [ -  + ]:      52350 :       if (solidx[k] > 0)
    1900         [ -  - ]:          0 :         for (std::size_t i=0; i<3; ++i)
    1901         [ -  - ]:          0 :           for (std::size_t j=0; j<3; ++j)
    1902                 :            :           {
    1903         [ -  - ]:          0 :             for (std::size_t idof=1; idof<rdof; ++idof)
    1904         [ -  - ]:          0 :               U(e,deformDofIdx(nmat,solidx[k],i,j,rdof,idof)) = 0.0;
    1905                 :            :           }
    1906                 :            :     }
    1907                 :            : 
    1908                 :            :     // 2. same limiter for all volume-fractions and densities
    1909         [ +  + ]:      78426 :     for (std::size_t k=0; k<nmat; ++k)
    1910                 :            :     {
    1911                 :      52350 :       phic_p1[volfracIdx(nmat, k)] = phi_al_p1;
    1912                 :      52350 :       phic_p1[densityIdx(nmat, k)] = phi_al_p1;
    1913                 :      52350 :       phic_p1[energyIdx(nmat, k)] = phi_al_p1;
    1914         [ -  + ]:      52350 :       if (solidx[k] > 0)
    1915         [ -  - ]:          0 :         for (std::size_t i=0; i<3; ++i)
    1916         [ -  - ]:          0 :           for (std::size_t j=0; j<3; ++j)
    1917                 :          0 :             phic_p1[deformIdx(nmat,solidx[k],i,j)] = phi_al_p1;
    1918                 :            :     }
    1919         [ -  + ]:      26076 :     if(rdof > 4)
    1920                 :            :     {
    1921         [ -  - ]:          0 :       for (std::size_t k=0; k<nmat; ++k)
    1922                 :            :       {
    1923                 :          0 :         phic_p2[volfracIdx(nmat, k)] = phi_al_p2;
    1924                 :          0 :         phic_p2[densityIdx(nmat, k)] = phi_al_p2;
    1925                 :          0 :         phic_p2[energyIdx(nmat, k)] = phi_al_p2;
    1926         [ -  - ]:          0 :         if (solidx[k] > 0)
    1927         [ -  - ]:          0 :           for (std::size_t i=0; i<3; ++i)
    1928         [ -  - ]:          0 :             for (std::size_t j=0; j<3; ++j)
    1929                 :          0 :               phic_p2[deformIdx(nmat,solidx[k],i,j)] = phi_al_p2;
    1930                 :            :       }
    1931                 :      26076 :     }
    1932                 :            :   }
    1933                 :            :   else
    1934                 :            :   {
    1935                 :            :     // same limiter for all volume-fractions
    1936         [ +  + ]:    3073335 :     for (std::size_t k=0; k<nmat; ++k)
    1937                 :    2066344 :       phic_p1[volfracIdx(nmat, k)] = phi_al_p1;
    1938         [ -  + ]:    1006991 :     if(rdof > 4)
    1939         [ -  - ]:          0 :       for (std::size_t k=0; k<nmat; ++k)
    1940                 :          0 :         phic_p2[volfracIdx(nmat, k)] = phi_al_p2;
    1941                 :            :   }
    1942                 :    1033067 : }
    1943                 :            : 
    1944                 :     830642 : void BoundPreservingLimiting( std::size_t nmat,
    1945                 :            :                               std::size_t ndof,
    1946                 :            :                               std::size_t e,
    1947                 :            :                               const std::vector< std::size_t >& inpoel,
    1948                 :            :                               const tk::UnsMesh::Coords& coord,
    1949                 :            :                               const tk::Fields& U,
    1950                 :            :                               std::vector< tk::real >& phic_p1,
    1951                 :            :                               std::vector< tk::real >& phic_p2 )
    1952                 :            : // *****************************************************************************
    1953                 :            : //  Bound preserving limiter for volume fractions when MulMat scheme is selected
    1954                 :            : //! \param[in] nmat Number of materials in this PDE system
    1955                 :            : //! \param[in] ndof Total number of reconstructed dofs
    1956                 :            : //! \param[in] e Element being checked for consistency
    1957                 :            : //! \param[in] inpoel Element connectivity
    1958                 :            : //! \param[in] coord Array of nodal coordinates
    1959                 :            : //! \param[in,out] U Second-order solution vector which gets modified near
    1960                 :            : //!   material interfaces for consistency
    1961                 :            : //! \param[in] unk Vector of conservative variables based on Taylor basis
    1962                 :            : //! \param[in,out] phic_p1 Vector of limiter functions for P1 dofs of the
    1963                 :            : //!   conserved quantities
    1964                 :            : //! \param[in,out] phic_p2 Vector of limiter functions for P2 dofs of the
    1965                 :            : //!   conserved quantities
    1966                 :            : //! \details This bound-preserving limiter is specifically meant to enforce
    1967                 :            : //!   bounds [0,1], but it does not suppress oscillations like the other 'TVD'
    1968                 :            : //!   limiters. TVD limiters on the other hand, do not preserve such bounds. A
    1969                 :            : //!   combination of oscillation-suppressing and bound-preserving limiters can
    1970                 :            : //!   obtain a non-oscillatory and bounded solution.
    1971                 :            : // *****************************************************************************
    1972                 :            : {
    1973                 :     830642 :   const auto& cx = coord[0];
    1974                 :     830642 :   const auto& cy = coord[1];
    1975                 :     830642 :   const auto& cz = coord[2];
    1976                 :            : 
    1977                 :            :   // Extract the element coordinates
    1978                 :            :   std::array< std::array< tk::real, 3>, 4 > coordel {{
    1979                 :    2491926 :     {{ cx[ inpoel[4*e  ] ], cy[ inpoel[4*e  ] ], cz[ inpoel[4*e  ] ] }},
    1980                 :    2491926 :     {{ cx[ inpoel[4*e+1] ], cy[ inpoel[4*e+1] ], cz[ inpoel[4*e+1] ] }},
    1981                 :    2491926 :     {{ cx[ inpoel[4*e+2] ], cy[ inpoel[4*e+2] ], cz[ inpoel[4*e+2] ] }},
    1982                 :    7475778 :     {{ cx[ inpoel[4*e+3] ], cy[ inpoel[4*e+3] ], cz[ inpoel[4*e+3] ] }} }};
    1983                 :            : 
    1984                 :            :   // Compute the determinant of Jacobian matrix
    1985                 :            :   auto detT =
    1986                 :     830642 :     tk::Jacobian( coordel[0], coordel[1], coordel[2], coordel[3] );
    1987                 :            : 
    1988         [ +  - ]:    1661284 :   std::vector< tk::real > phi_bound(nmat, 1.0);
    1989                 :            : 
    1990                 :            :   // Compute the upper and lower bound for volume fraction
    1991                 :     830642 :   const tk::real min = 1e-14;
    1992                 :     830642 :   const tk::real max = 1.0 - min * static_cast<tk::real>(nmat - 1);
    1993                 :            : 
    1994                 :            :   // loop over all faces of the element e
    1995         [ +  + ]:    4153210 :   for (std::size_t lf=0; lf<4; ++lf)
    1996                 :            :   {
    1997                 :            :     // Extract the face coordinates
    1998                 :    3322568 :     std::array< std::size_t, 3 > inpofa_l {{ inpoel[4*e+tk::lpofa[lf][0]],
    1999                 :    3322568 :                                              inpoel[4*e+tk::lpofa[lf][1]],
    2000                 :    6645136 :                                              inpoel[4*e+tk::lpofa[lf][2]] }};
    2001                 :            : 
    2002                 :            :     std::array< std::array< tk::real, 3>, 3 > coordfa {{
    2003                 :    9967704 :       {{ cx[ inpofa_l[0] ], cy[ inpofa_l[0] ], cz[ inpofa_l[0] ] }},
    2004                 :    9967704 :       {{ cx[ inpofa_l[1] ], cy[ inpofa_l[1] ], cz[ inpofa_l[1] ] }},
    2005                 :   19935408 :       {{ cx[ inpofa_l[2] ], cy[ inpofa_l[2] ], cz[ inpofa_l[2] ] }} }};
    2006                 :            : 
    2007         [ +  - ]:    3322568 :     auto ng = tk::NGfa(ndof);
    2008                 :            : 
    2009                 :            :     // arrays for quadrature points
    2010                 :    6645136 :     std::array< std::vector< tk::real >, 2 > coordgp;
    2011                 :    6645136 :     std::vector< tk::real > wgp;
    2012                 :            : 
    2013         [ +  - ]:    3322568 :     coordgp[0].resize( ng );
    2014         [ +  - ]:    3322568 :     coordgp[1].resize( ng );
    2015         [ +  - ]:    3322568 :     wgp.resize( ng );
    2016                 :            : 
    2017                 :            :     // get quadrature point weights and coordinates for triangle
    2018         [ +  - ]:    3322568 :     tk::GaussQuadratureTri( ng, coordgp, wgp );
    2019                 :            : 
    2020                 :            :     // Gaussian quadrature
    2021         [ +  + ]:   10561472 :     for (std::size_t igp=0; igp<ng; ++igp)
    2022                 :            :     {
    2023                 :            :       // Compute the coordinates of quadrature point at physical domain
    2024         [ +  - ]:    7238904 :       auto gp = tk::eval_gp( igp, coordfa, coordgp );
    2025                 :            : 
    2026                 :            :       //Compute the basis functions
    2027                 :            :       auto B = tk::eval_basis( ndof,
    2028                 :    7238904 :             tk::Jacobian( coordel[0], gp, coordel[2], coordel[3] ) / detT,
    2029                 :    7238904 :             tk::Jacobian( coordel[0], coordel[1], gp, coordel[3] ) / detT,
    2030         [ +  - ]:   28955616 :             tk::Jacobian( coordel[0], coordel[1], coordel[2], gp ) / detT );
    2031                 :            : 
    2032         [ +  - ]:   14477808 :       auto state = eval_state( U.nprop()/ndof, ndof, ndof, e, U, B );
    2033                 :            : 
    2034         [ +  + ]:   21716712 :       for(std::size_t imat = 0; imat < nmat; imat++)
    2035                 :            :       {
    2036         [ +  - ]:   14477808 :         auto phi = BoundPreservingLimitingFunction( min, max,
    2037                 :   14477808 :           state[volfracIdx(nmat, imat)],
    2038         [ +  - ]:   14477808 :           U(e,volfracDofIdx(nmat, imat, ndof, 0)) );
    2039                 :   14477808 :         phi_bound[imat] = std::min( phi_bound[imat], phi );
    2040                 :            :       }
    2041                 :            :     }
    2042                 :            :   }
    2043                 :            : 
    2044                 :            :   // If DG(P2), the bound-preserving limiter should also be applied to the gauss
    2045                 :            :   // point within the element
    2046         [ -  + ]:     830642 :   if(ndof > 4)
    2047                 :            :   {
    2048         [ -  - ]:          0 :     auto ng = tk::NGvol(ndof);
    2049                 :            : 
    2050                 :            :     // arrays for quadrature points
    2051                 :          0 :     std::array< std::vector< tk::real >, 3 > coordgp;
    2052                 :          0 :     std::vector< tk::real > wgp;
    2053                 :            : 
    2054         [ -  - ]:          0 :     coordgp[0].resize( ng );
    2055         [ -  - ]:          0 :     coordgp[1].resize( ng );
    2056         [ -  - ]:          0 :     coordgp[2].resize( ng );
    2057         [ -  - ]:          0 :     wgp.resize( ng );
    2058                 :            : 
    2059         [ -  - ]:          0 :     tk::GaussQuadratureTet( ng, coordgp, wgp );
    2060                 :            : 
    2061         [ -  - ]:          0 :     for (std::size_t igp=0; igp<ng; ++igp)
    2062                 :            :     {
    2063                 :            :       // Compute the basis function
    2064                 :          0 :       auto B = tk::eval_basis( ndof, coordgp[0][igp], coordgp[1][igp],
    2065         [ -  - ]:          0 :         coordgp[2][igp] );
    2066                 :            : 
    2067         [ -  - ]:          0 :       auto state = tk::eval_state(U.nprop()/ndof, ndof, ndof, e, U, B);
    2068                 :            : 
    2069         [ -  - ]:          0 :       for(std::size_t imat = 0; imat < nmat; imat++)
    2070                 :            :       {
    2071         [ -  - ]:          0 :         auto phi = BoundPreservingLimitingFunction(min, max,
    2072                 :          0 :           state[volfracIdx(nmat, imat)],
    2073         [ -  - ]:          0 :           U(e,volfracDofIdx(nmat, imat, ndof, 0)) );
    2074                 :          0 :         phi_bound[imat] = std::min( phi_bound[imat], phi );
    2075                 :            :       }
    2076                 :            :     }
    2077                 :            :   }
    2078                 :            : 
    2079         [ +  + ]:    2491926 :   for(std::size_t k=0; k<nmat; k++)
    2080                 :    3322568 :     phic_p1[volfracIdx(nmat, k)] = std::min(phi_bound[k],
    2081                 :    3322568 :       phic_p1[volfracIdx(nmat, k)]);
    2082                 :            : 
    2083         [ -  + ]:     830642 :   if(ndof > 4)
    2084         [ -  - ]:          0 :     for(std::size_t k=0; k<nmat; k++)
    2085                 :          0 :       phic_p2[volfracIdx(nmat, k)] = std::min(phi_bound[k],
    2086                 :          0 :         phic_p2[volfracIdx(nmat, k)]);
    2087                 :     830642 : }
    2088                 :            : 
    2089                 :            : tk::real
    2090                 :   14477808 : BoundPreservingLimitingFunction( const tk::real min,
    2091                 :            :                                  const tk::real max,
    2092                 :            :                                  const tk::real al_gp,
    2093                 :            :                                  const tk::real al_avg )
    2094                 :            : // *****************************************************************************
    2095                 :            : //  Bound-preserving limiter function for the volume fractions
    2096                 :            : //! \param[in] min Minimum bound for volume fraction
    2097                 :            : //! \param[in] max Maximum bound for volume fraction
    2098                 :            : //! \param[in] al_gp Volume fraction at the quadrature point
    2099                 :            : //! \param[in] al_avg Cell-average volume fraction
    2100                 :            : //! \return The limiting coefficient from the bound-preserving limiter function
    2101                 :            : // *****************************************************************************
    2102                 :            : {
    2103                 :   14477808 :   tk::real phi(1.0), al_diff(0.0);
    2104                 :   14477808 :   al_diff = al_gp - al_avg;
    2105 [ +  + ][ +  - ]:   14477808 :   if(al_gp > max && fabs(al_diff) > 1e-15)
    2106                 :     269653 :     phi = std::fabs( (max - al_avg) / al_diff );
    2107 [ +  + ][ +  + ]:   14208155 :   else if(al_gp < min && fabs(al_diff) > 1e-15)
    2108                 :     269653 :     phi = std::fabs( (min - al_avg) / al_diff );
    2109                 :   14477808 :   return phi;
    2110                 :            : }
    2111                 :            : 
    2112                 :    1523580 : void PositivityLimiting( std::size_t nmat,
    2113                 :            :                          std::size_t nspec,
    2114                 :            :                          const std::vector< inciter::EOS >& mat_blk,
    2115                 :            :                          std::size_t rdof,
    2116                 :            :                          std::size_t ndof_el,
    2117                 :            :                          const std::vector< std::size_t >& ndofel,
    2118                 :            :                          std::size_t e,
    2119                 :            :                          const std::vector< std::size_t >& inpoel,
    2120                 :            :                          const tk::UnsMesh::Coords& coord,
    2121                 :            :                          const std::vector< int >& esuel,
    2122                 :            :                          const tk::Fields& U,
    2123                 :            :                          const tk::Fields& P,
    2124                 :            :                          std::vector< tk::real >& phic_p1,
    2125                 :            :                          std::vector< tk::real >& phic_p2,
    2126                 :            :                          std::vector< tk::real >& phip_p1,
    2127                 :            :                          std::vector< tk::real >& phip_p2 )
    2128                 :            : // *****************************************************************************
    2129                 :            : //  Positivity preserving limiter for multi-material and multspecies solver
    2130                 :            : //! \param[in] nmat Number of materials in this PDE system
    2131                 :            : //! \param[in] nspec Number of species in this PDE system
    2132                 :            : //! \param[in] mat_blk EOS material block
    2133                 :            : //! \param[in] rdof Total number of reconstructed dofs
    2134                 :            : //! \param[in] ndof_el Number of dofs for element e
    2135                 :            : //! \param[in] ndofel Vector of local number of degrees of freedome
    2136                 :            : //! \param[in] e Element being checked for consistency
    2137                 :            : //! \param[in] inpoel Element connectivity
    2138                 :            : //! \param[in] coord Array of nodal coordinates
    2139                 :            : //! \param[in] esuel Elements surrounding elements
    2140                 :            : //! \param[in] U Vector of conservative variables
    2141                 :            : //! \param[in] P Vector of primitive variables
    2142                 :            : //! \param[in,out] phic_p1 Vector of limiter functions for P1 dofs of the
    2143                 :            : //!   conserved quantities
    2144                 :            : //! \param[in,out] phic_p2 Vector of limiter functions for P2 dofs of the
    2145                 :            : //!   conserved quantities
    2146                 :            : //! \param[in,out] phip_p1 Vector of limiter functions for P1 dofs of the
    2147                 :            : //!   primitive quantities
    2148                 :            : //! \param[in,out] phip_p2 Vector of limiter functions for P2 dofs of the
    2149                 :            : //!   primitive quantities
    2150                 :            : // *****************************************************************************
    2151                 :            : {
    2152                 :    1523580 :   const auto ncomp = U.nprop() / rdof;
    2153                 :    1523580 :   const auto nprim = P.nprop() / rdof;
    2154                 :            : 
    2155                 :    1523580 :   const auto& cx = coord[0];
    2156                 :    1523580 :   const auto& cy = coord[1];
    2157                 :    1523580 :   const auto& cz = coord[2];
    2158                 :            : 
    2159                 :            :   // Extract the element coordinates
    2160                 :            :   std::array< std::array< tk::real, 3>, 4 > coordel {{
    2161                 :    4570740 :     {{ cx[ inpoel[4*e  ] ], cy[ inpoel[4*e  ] ], cz[ inpoel[4*e  ] ] }},
    2162                 :    4570740 :     {{ cx[ inpoel[4*e+1] ], cy[ inpoel[4*e+1] ], cz[ inpoel[4*e+1] ] }},
    2163                 :    4570740 :     {{ cx[ inpoel[4*e+2] ], cy[ inpoel[4*e+2] ], cz[ inpoel[4*e+2] ] }},
    2164                 :   13712220 :     {{ cx[ inpoel[4*e+3] ], cy[ inpoel[4*e+3] ], cz[ inpoel[4*e+3] ] }} }};
    2165                 :            : 
    2166                 :            :   // Compute the determinant of Jacobian matrix
    2167                 :            :   auto detT =
    2168                 :    1523580 :     tk::Jacobian( coordel[0], coordel[1], coordel[2], coordel[3] );
    2169                 :            : 
    2170         [ +  - ]:    3047160 :   std::vector< tk::real > phic_bound(ncomp, 1.0);
    2171         [ +  - ]:    3047160 :   std::vector< tk::real > phip_bound(nprim, 1.0);
    2172                 :            : 
    2173         [ +  + ]:    7617900 :   for (std::size_t lf=0; lf<4; ++lf)
    2174                 :            :   {
    2175                 :    6094320 :     std::array< std::size_t, 3 > inpofa_l {{ inpoel[4*e+tk::lpofa[lf][0]],
    2176                 :    6094320 :                                              inpoel[4*e+tk::lpofa[lf][1]],
    2177                 :   12188640 :                                              inpoel[4*e+tk::lpofa[lf][2]] }};
    2178                 :            : 
    2179                 :            :     std::array< std::array< tk::real, 3>, 3 > coordfa {{
    2180                 :   18282960 :       {{ cx[ inpofa_l[0] ], cy[ inpofa_l[0] ], cz[ inpofa_l[0] ] }},
    2181                 :   18282960 :       {{ cx[ inpofa_l[1] ], cy[ inpofa_l[1] ], cz[ inpofa_l[1] ] }},
    2182                 :   36565920 :       {{ cx[ inpofa_l[2] ], cy[ inpofa_l[2] ], cz[ inpofa_l[2] ] }} }};
    2183                 :            : 
    2184                 :            :     std::size_t nel;
    2185         [ +  + ]:    6094320 :     if (esuel[4*e+lf] == -1) nel = e;
    2186                 :    5294340 :     else nel = static_cast< std::size_t >(esuel[4*e+lf]);
    2187                 :            : 
    2188         [ +  - ]:    6094320 :     auto ng = tk::NGfa(std::max(ndofel[e], ndofel[nel]));
    2189                 :            : 
    2190                 :   12188640 :     std::array< std::vector< tk::real >, 2 > coordgp;
    2191                 :   12188640 :     std::vector< tk::real > wgp;
    2192                 :            : 
    2193         [ +  - ]:    6094320 :     coordgp[0].resize( ng );
    2194         [ +  - ]:    6094320 :     coordgp[1].resize( ng );
    2195         [ +  - ]:    6094320 :     wgp.resize( ng );
    2196                 :            : 
    2197         [ +  - ]:    6094320 :     tk::GaussQuadratureTri( ng, coordgp, wgp );
    2198                 :            : 
    2199         [ +  + ]:   16190880 :     for (std::size_t igp=0; igp<ng; ++igp)
    2200                 :            :     {
    2201         [ +  - ]:   10096560 :       auto gp = tk::eval_gp( igp, coordfa, coordgp );
    2202                 :            :       auto B = tk::eval_basis( ndof_el,
    2203                 :   10096560 :             tk::Jacobian( coordel[0], gp, coordel[2], coordel[3] ) / detT,
    2204                 :   10096560 :             tk::Jacobian( coordel[0], coordel[1], gp, coordel[3] ) / detT,
    2205         [ +  - ]:   40386240 :             tk::Jacobian( coordel[0], coordel[1], coordel[2], gp ) / detT );
    2206                 :            : 
    2207         [ +  - ]:   20193120 :       auto state = eval_state(ncomp, rdof, ndof_el, e, U, B);
    2208         [ +  - ]:   20193120 :       auto sprim = eval_state(nprim, rdof, ndof_el, e, P, B);
    2209                 :            : 
    2210         [ +  + ]:   10096560 :       if (nmat > 1) {
    2211                 :            :         // multi-material PDE bounds
    2212         [ +  - ]:    7367760 :         PositivityBoundsMultiMat(nmat, mat_blk, rdof, e, U, P, state, sprim,
    2213                 :            :           phic_bound, phip_bound);
    2214                 :            :       }
    2215                 :            :       else {
    2216                 :            :         // multispecies PDE bounds
    2217         [ +  - ]:    2728800 :         PositivityBoundsMultiSpecies(nspec, mat_blk, rdof, e, U, P, state,
    2218                 :            :           sprim, phic_bound, phip_bound);
    2219                 :            :       }
    2220                 :            :     }
    2221                 :            :   }
    2222                 :            : 
    2223         [ -  + ]:    1523580 :   if(ndofel[e] > 4)
    2224                 :            :   {
    2225         [ -  - ]:          0 :     auto ng = tk::NGvol(ndof_el);
    2226                 :          0 :     std::array< std::vector< tk::real >, 3 > coordgp;
    2227                 :          0 :     std::vector< tk::real > wgp;
    2228                 :            : 
    2229         [ -  - ]:          0 :     coordgp[0].resize( ng );
    2230         [ -  - ]:          0 :     coordgp[1].resize( ng );
    2231         [ -  - ]:          0 :     coordgp[2].resize( ng );
    2232         [ -  - ]:          0 :     wgp.resize( ng );
    2233                 :            : 
    2234         [ -  - ]:          0 :     tk::GaussQuadratureTet( ng, coordgp, wgp );
    2235                 :            : 
    2236         [ -  - ]:          0 :     for (std::size_t igp=0; igp<ng; ++igp)
    2237                 :            :     {
    2238                 :          0 :       auto B = tk::eval_basis( ndof_el, coordgp[0][igp], coordgp[1][igp],
    2239         [ -  - ]:          0 :         coordgp[2][igp] );
    2240                 :            : 
    2241         [ -  - ]:          0 :       auto state = eval_state(ncomp, rdof, ndof_el, e, U, B);
    2242         [ -  - ]:          0 :       auto sprim = eval_state(nprim, rdof, ndof_el, e, P, B);
    2243                 :            : 
    2244         [ -  - ]:          0 :       if (nmat > 1) {
    2245                 :            :         // multi-material PDE bounds
    2246         [ -  - ]:          0 :         PositivityBoundsMultiMat(nmat, mat_blk, rdof, e, U, P, state, sprim,
    2247                 :            :           phic_bound, phip_bound);
    2248                 :            :       }
    2249                 :            :       else {
    2250                 :            :         // multispecies PDE bounds
    2251         [ -  - ]:          0 :         PositivityBoundsMultiSpecies(nspec, mat_blk, rdof, e, U, P, state,
    2252                 :            :           sprim, phic_bound, phip_bound);
    2253                 :            :       }
    2254                 :            :     }
    2255                 :            :   }
    2256                 :            : 
    2257                 :            :   // apply new bounds
    2258         [ +  + ]:   12848100 :   for (std::size_t c=0; c<ncomp; ++c) {
    2259                 :   11324520 :     phic_p1[c] = std::min( phic_bound[c], phic_p1[c] );
    2260         [ -  + ]:   11324520 :     if (ndof_el > 4) phic_p2[c] = std::min( phic_bound[c], phic_p2[c] );
    2261                 :            :   }
    2262         [ +  + ]:    6412680 :   for (std::size_t c=0; c<nprim; ++c) {
    2263                 :    4889100 :     phip_p1[c] = std::min( phip_bound[c], phip_p1[c] );
    2264         [ -  + ]:    4889100 :     if (ndof_el > 4) phip_p2[c] = std::min( phip_bound[c], phip_p2[c] );
    2265                 :            :   }
    2266                 :    1523580 : }
    2267                 :            : 
    2268                 :    7367760 : void PositivityBoundsMultiMat(
    2269                 :            :   std::size_t nmat,
    2270                 :            :   const std::vector< inciter::EOS >& mat_blk,
    2271                 :            :   std::size_t rdof,
    2272                 :            :   std::size_t e,
    2273                 :            :   const tk::Fields& U,
    2274                 :            :   const tk::Fields& P,
    2275                 :            :   const std::vector< tk::real >& state,
    2276                 :            :   const std::vector< tk::real >& sprim,
    2277                 :            :   std::vector< tk::real >& phic_bound,
    2278                 :            :   std::vector< tk::real >& phip_bound )
    2279                 :            : // *****************************************************************************
    2280                 :            : //  Positivity bounds for multi-material PDE system
    2281                 :            : //! \param[in] nmat Number of materials in this system
    2282                 :            : //! \param[in] mat_blk EOS material block
    2283                 :            : //! \param[in] rdof Total number of reconstructed dofs
    2284                 :            : //! \param[in] e Element for which bounds are being calculated
    2285                 :            : //! \param[in] U Vector of conservative variables
    2286                 :            : //! \param[in] P Vector of primitive variables
    2287                 :            : //! \param[in] state Vector of state of conserved quantities at quadrature point
    2288                 :            : //! \param[in] sprim Vector of state of primitive quantities at quadrature point
    2289                 :            : //! \param[in,out] phic_bound Vector of bounding-limiter functions for dofs of
    2290                 :            : //!   the conserved quantities
    2291                 :            : //! \param[in,out] phip_bound Vector of bounding-limiter functions for dofs of
    2292                 :            : //!   the primitive quantities
    2293                 :            : // *****************************************************************************
    2294                 :            : {
    2295                 :    7367760 :   const tk::real min = 1e-15;
    2296                 :            : 
    2297         [ +  + ]:   22103280 :   for(std::size_t k = 0; k < nmat; k++)
    2298                 :            :   {
    2299                 :   14735520 :     tk::real phi_rho(1.0), phi_rhoe(1.0), phi_pre(1.0);
    2300                 :            :     // Evaluate the limiting coefficient for material density
    2301                 :   14735520 :     auto rho = state[densityIdx(nmat, k)];
    2302         [ +  - ]:   14735520 :     auto rho_avg = U(e, densityDofIdx(nmat, k, rdof, 0));
    2303         [ +  - ]:   14735520 :     phi_rho = PositivityFunction(min, rho, rho_avg);
    2304                 :   14735520 :     phic_bound[densityIdx(nmat, k)] =
    2305                 :   14735520 :       std::min(phic_bound[densityIdx(nmat, k)], phi_rho);
    2306                 :            :     // Evaluate the limiting coefficient for material energy
    2307                 :   14735520 :     auto rhoe = state[energyIdx(nmat, k)];
    2308         [ +  - ]:   14735520 :     auto rhoe_avg = U(e, energyDofIdx(nmat, k, rdof, 0));
    2309         [ +  - ]:   14735520 :     phi_rhoe = PositivityFunction(min, rhoe, rhoe_avg);
    2310                 :   14735520 :     phic_bound[energyIdx(nmat, k)] =
    2311                 :   14735520 :       std::min(phic_bound[energyIdx(nmat, k)], phi_rhoe);
    2312                 :            :     // Evaluate the limiting coefficient for material pressure
    2313                 :   14735520 :     auto min_pre = std::max(min, state[volfracIdx(nmat, k)] *
    2314         [ +  - ]:   29471040 :       mat_blk[k].compute< EOS::min_eff_pressure >(min, rho,
    2315                 :   29471040 :       state[volfracIdx(nmat, k)]));
    2316                 :   14735520 :     auto pre = sprim[pressureIdx(nmat, k)];
    2317         [ +  - ]:   14735520 :     auto pre_avg = P(e, pressureDofIdx(nmat, k, rdof, 0));
    2318         [ +  - ]:   14735520 :     phi_pre = PositivityFunction(min_pre, pre, pre_avg);
    2319                 :   14735520 :     phip_bound[pressureIdx(nmat, k)] =
    2320                 :   14735520 :       std::min(phip_bound[pressureIdx(nmat, k)], phi_pre);
    2321                 :            :   }
    2322                 :    7367760 : }
    2323                 :            : 
    2324                 :    2728800 : void PositivityBoundsMultiSpecies(
    2325                 :            :   std::size_t nspec,
    2326                 :            :   const std::vector< inciter::EOS >& /*mat_blk*/,
    2327                 :            :   std::size_t rdof,
    2328                 :            :   std::size_t e,
    2329                 :            :   const tk::Fields& /*U*/,
    2330                 :            :   const tk::Fields& P,
    2331                 :            :   const std::vector< tk::real >& /*state*/,
    2332                 :            :   const std::vector< tk::real >& sprim,
    2333                 :            :   std::vector< tk::real >& /*phic_bound*/,
    2334                 :            :   std::vector< tk::real >& phip_bound )
    2335                 :            : // *****************************************************************************
    2336                 :            : //  Positivity bounds for multispecies PDE system
    2337                 :            : //! \param[in] nmat Number of materials in this system
    2338                 :            : // //! \param[in] mat_blk EOS material block
    2339                 :            : //! \param[in] rdof Total number of reconstructed dofs
    2340                 :            : //! \param[in] e Element for which bounds are being calculated
    2341                 :            : // //! \param[in] U Vector of conservative variables
    2342                 :            : //! \param[in] P Vector of primitive variables
    2343                 :            : // //! \param[in] state Vector of state of conserved quantities at quadrature point
    2344                 :            : //! \param[in] sprim Vector of state of primitive quantities at quadrature point
    2345                 :            : // //! \param[in,out] phic_bound Vector of bounding-limiter functions for dofs of
    2346                 :            : // //!   the conserved quantities
    2347                 :            : //! \param[in,out] phip_bound Vector of bounding-limiter functions for dofs of
    2348                 :            : //!   the primitive quantities
    2349                 :            : // *****************************************************************************
    2350                 :            : {
    2351                 :    2728800 :   const tk::real min = 1e-8;
    2352                 :            : 
    2353                 :    2728800 :   tk::real phi_T(1.0);
    2354                 :            :   // Evaluate the limiting coefficient for mixture temperature
    2355                 :    2728800 :   auto T = sprim[multispecies::temperatureIdx(nspec, 0)];
    2356         [ +  - ]:    2728800 :   auto T_avg = P(e, multispecies::temperatureDofIdx(nspec, 0, rdof, 0));
    2357         [ +  - ]:    2728800 :   phi_T = PositivityFunction(min, T, T_avg);
    2358                 :    2728800 :   phip_bound[multispecies::temperatureIdx(nspec, 0)] =
    2359                 :    2728800 :     std::min(phip_bound[multispecies::temperatureIdx(nspec, 0)], phi_T);
    2360                 :    2728800 : }
    2361                 :            : 
    2362                 :       1268 : void PositivityPreservingMultiMat_FV(
    2363                 :            :   const std::vector< std::size_t >& inpoel,
    2364                 :            :   std::size_t nelem,
    2365                 :            :   std::size_t nmat,
    2366                 :            :   const std::vector< inciter::EOS >& mat_blk,
    2367                 :            :   const tk::UnsMesh::Coords& coord,
    2368                 :            :   const tk::Fields& /*geoFace*/,
    2369                 :            :   tk::Fields& U,
    2370                 :            :   tk::Fields& P )
    2371                 :            : // *****************************************************************************
    2372                 :            : //  Positivity preserving limiter for the FV multi-material solver
    2373                 :            : //! \param[in] inpoel Element connectivity
    2374                 :            : //! \param[in] nelem Number of elements
    2375                 :            : //! \param[in] nmat Number of materials in this PDE system
    2376                 :            : //! \param[in] mat_blk Material EOS block
    2377                 :            : //! \param[in] coord Array of nodal coordinates
    2378                 :            : ////! \param[in] geoFace Face geometry array
    2379                 :            : //! \param[in,out] U High-order solution vector which gets limited
    2380                 :            : //! \param[in,out] P High-order vector of primitives which gets limited
    2381                 :            : //! \details This positivity preserving limiter function should be called for
    2382                 :            : //!   FV multimat.
    2383                 :            : // *****************************************************************************
    2384                 :            : {
    2385                 :       1268 :   const auto rdof = inciter::g_inputdeck.get< tag::rdof >();
    2386                 :       1268 :   const auto ncomp = U.nprop() / rdof;
    2387                 :       1268 :   const auto nprim = P.nprop() / rdof;
    2388                 :            : 
    2389                 :       1268 :   const auto& cx = coord[0];
    2390                 :       1268 :   const auto& cy = coord[1];
    2391                 :       1268 :   const auto& cz = coord[2];
    2392                 :            : 
    2393         [ +  + ]:     205428 :   for (std::size_t e=0; e<nelem; ++e)
    2394                 :            :   {
    2395                 :            :     // Extract the element coordinates
    2396                 :            :     std::array< std::array< tk::real, 3>, 4 > coordel {{
    2397                 :     612480 :       {{ cx[ inpoel[4*e  ] ], cy[ inpoel[4*e  ] ], cz[ inpoel[4*e  ] ] }},
    2398                 :     612480 :       {{ cx[ inpoel[4*e+1] ], cy[ inpoel[4*e+1] ], cz[ inpoel[4*e+1] ] }},
    2399                 :     612480 :       {{ cx[ inpoel[4*e+2] ], cy[ inpoel[4*e+2] ], cz[ inpoel[4*e+2] ] }},
    2400                 :    1837440 :       {{ cx[ inpoel[4*e+3] ], cy[ inpoel[4*e+3] ], cz[ inpoel[4*e+3] ] }} }};
    2401                 :            : 
    2402                 :            :     // Compute the determinant of Jacobian matrix
    2403                 :            :     auto detT =
    2404                 :     204160 :       tk::Jacobian( coordel[0], coordel[1], coordel[2], coordel[3] );
    2405                 :            : 
    2406         [ +  - ]:     408320 :     std::vector< tk::real > phic(ncomp, 1.0);
    2407         [ +  - ]:     408320 :     std::vector< tk::real > phip(nprim, 1.0);
    2408                 :            : 
    2409                 :     204160 :     const tk::real min = 1e-15;
    2410                 :            : 
    2411                 :            :     // 1. Enforce positive density (total energy will be positive if pressure
    2412                 :            :     //    and density are positive)
    2413         [ +  + ]:    1020800 :     for (std::size_t lf=0; lf<4; ++lf)
    2414                 :            :     {
    2415                 :     816640 :       std::array< std::size_t, 3 > inpofa_l {{ inpoel[4*e+tk::lpofa[lf][0]],
    2416                 :     816640 :                                                inpoel[4*e+tk::lpofa[lf][1]],
    2417                 :    1633280 :                                                inpoel[4*e+tk::lpofa[lf][2]] }};
    2418                 :            : 
    2419                 :            :       // face coordinates
    2420                 :            :       std::array< std::array< tk::real, 3>, 3 > coordfa {{
    2421                 :    2449920 :         {{ cx[ inpofa_l[0] ], cy[ inpofa_l[0] ], cz[ inpofa_l[0] ] }},
    2422                 :    2449920 :         {{ cx[ inpofa_l[1] ], cy[ inpofa_l[1] ], cz[ inpofa_l[1] ] }},
    2423                 :    4899840 :         {{ cx[ inpofa_l[2] ], cy[ inpofa_l[2] ], cz[ inpofa_l[2] ] }} }};
    2424                 :            : 
    2425                 :            :       // face centroid
    2426                 :            :       std::array< tk::real, 3 > fc{{
    2427                 :     816640 :         (coordfa[0][0]+coordfa[1][0]+coordfa[2][0])/3.0 ,
    2428                 :     816640 :         (coordfa[0][1]+coordfa[1][1]+coordfa[2][1])/3.0 ,
    2429                 :    1633280 :         (coordfa[0][2]+coordfa[1][2]+coordfa[2][2])/3.0 }};
    2430                 :            : 
    2431                 :            :       auto B = tk::eval_basis( rdof,
    2432                 :     816640 :             tk::Jacobian( coordel[0], fc, coordel[2], coordel[3] ) / detT,
    2433                 :     816640 :             tk::Jacobian( coordel[0], coordel[1], fc, coordel[3] ) / detT,
    2434         [ +  - ]:    3266560 :             tk::Jacobian( coordel[0], coordel[1], coordel[2], fc ) / detT );
    2435         [ +  - ]:    1633280 :       auto state = eval_state(ncomp, rdof, rdof, e, U, B);
    2436                 :            : 
    2437         [ +  + ]:    2660160 :       for(std::size_t i=0; i<nmat; i++)
    2438                 :            :       {
    2439                 :            :         // Evaluate the limiting coefficient for material density
    2440                 :    1843520 :         auto rho = state[densityIdx(nmat, i)];
    2441         [ +  - ]:    1843520 :         auto rho_avg = U(e, densityDofIdx(nmat, i, rdof, 0));
    2442         [ +  - ]:    1843520 :         auto phi_rho = PositivityFunction(min, rho, rho_avg);
    2443                 :    1843520 :         phic[densityIdx(nmat, i)] =
    2444                 :    1843520 :           std::min(phic[densityIdx(nmat, i)], phi_rho);
    2445                 :            :       }
    2446                 :            :     }
    2447                 :            :     // apply limiter coefficient
    2448         [ +  + ]:     665040 :     for(std::size_t i=0; i<nmat; i++)
    2449                 :            :     {
    2450         [ +  - ]:     460880 :       U(e, densityDofIdx(nmat,i,rdof,1)) *= phic[densityIdx(nmat,i)];
    2451         [ +  - ]:     460880 :       U(e, densityDofIdx(nmat,i,rdof,2)) *= phic[densityIdx(nmat,i)];
    2452         [ +  - ]:     460880 :       U(e, densityDofIdx(nmat,i,rdof,3)) *= phic[densityIdx(nmat,i)];
    2453                 :            :     }
    2454                 :            : 
    2455                 :            :     // 2. Enforce positive pressure (assuming density is positive)
    2456         [ +  + ]:    1020800 :     for (std::size_t lf=0; lf<4; ++lf)
    2457                 :            :     {
    2458                 :     816640 :       std::array< std::size_t, 3 > inpofa_l {{ inpoel[4*e+tk::lpofa[lf][0]],
    2459                 :     816640 :                                                inpoel[4*e+tk::lpofa[lf][1]],
    2460                 :    1633280 :                                                inpoel[4*e+tk::lpofa[lf][2]] }};
    2461                 :            : 
    2462                 :            :       // face coordinates
    2463                 :            :       std::array< std::array< tk::real, 3>, 3 > coordfa {{
    2464                 :    2449920 :         {{ cx[ inpofa_l[0] ], cy[ inpofa_l[0] ], cz[ inpofa_l[0] ] }},
    2465                 :    2449920 :         {{ cx[ inpofa_l[1] ], cy[ inpofa_l[1] ], cz[ inpofa_l[1] ] }},
    2466                 :    4899840 :         {{ cx[ inpofa_l[2] ], cy[ inpofa_l[2] ], cz[ inpofa_l[2] ] }} }};
    2467                 :            : 
    2468                 :            :       // face centroid
    2469                 :            :       std::array< tk::real, 3 > fc{{
    2470                 :     816640 :         (coordfa[0][0]+coordfa[1][0]+coordfa[2][0])/3.0 ,
    2471                 :     816640 :         (coordfa[0][1]+coordfa[1][1]+coordfa[2][1])/3.0 ,
    2472                 :    1633280 :         (coordfa[0][2]+coordfa[1][2]+coordfa[2][2])/3.0 }};
    2473                 :            : 
    2474                 :            :       auto B = tk::eval_basis( rdof,
    2475                 :     816640 :             tk::Jacobian( coordel[0], fc, coordel[2], coordel[3] ) / detT,
    2476                 :     816640 :             tk::Jacobian( coordel[0], coordel[1], fc, coordel[3] ) / detT,
    2477         [ +  - ]:    3266560 :             tk::Jacobian( coordel[0], coordel[1], coordel[2], fc ) / detT );
    2478         [ +  - ]:    1633280 :       auto state = eval_state(ncomp, rdof, rdof, e, U, B);
    2479         [ +  - ]:    1633280 :       auto sprim = eval_state(nprim, rdof, rdof, e, P, B);
    2480                 :            : 
    2481         [ +  + ]:    2660160 :       for(std::size_t i=0; i<nmat; i++)
    2482                 :            :       {
    2483                 :    1843520 :         tk::real phi_pre(1.0);
    2484                 :            :         // Evaluate the limiting coefficient for material pressure
    2485                 :    1843520 :         auto rho = state[densityIdx(nmat, i)];
    2486         [ +  - ]:    1843520 :         auto min_pre = std::max(min, U(e,volfracDofIdx(nmat,i,rdof,0)) *
    2487                 :    1843520 :           mat_blk[i].compute< EOS::min_eff_pressure >(min, rho,
    2488 [ +  - ][ +  - ]:    1843520 :           U(e,volfracDofIdx(nmat,i,rdof,0))));
    2489                 :    1843520 :         auto pre = sprim[pressureIdx(nmat, i)];
    2490         [ +  - ]:    1843520 :         auto pre_avg = P(e, pressureDofIdx(nmat, i, rdof, 0));
    2491         [ +  - ]:    1843520 :         phi_pre = PositivityFunction(min_pre, pre, pre_avg);
    2492                 :    1843520 :         phip[pressureIdx(nmat, i)] =
    2493                 :    1843520 :           std::min(phip[pressureIdx(nmat, i)], phi_pre);
    2494                 :            :       }
    2495                 :            :     }
    2496                 :            :     // apply limiter coefficient
    2497         [ +  + ]:     665040 :     for(std::size_t i=0; i<nmat; i++)
    2498                 :            :     {
    2499         [ +  - ]:     460880 :       P(e, pressureDofIdx(nmat,i,rdof,1)) *= phip[pressureIdx(nmat,i)];
    2500         [ +  - ]:     460880 :       P(e, pressureDofIdx(nmat,i,rdof,2)) *= phip[pressureIdx(nmat,i)];
    2501         [ +  - ]:     460880 :       P(e, pressureDofIdx(nmat,i,rdof,3)) *= phip[pressureIdx(nmat,i)];
    2502                 :            :     }
    2503                 :            :   }
    2504                 :       1268 : }
    2505                 :            : 
    2506                 :            : tk::real
    2507                 :   50622400 : PositivityFunction( const tk::real min,
    2508                 :            :                     const tk::real u_gp,
    2509                 :            :                     const tk::real u_avg )
    2510                 :            : // *****************************************************************************
    2511                 :            : //  Positivity-preserving limiter function
    2512                 :            : //! \param[in] min Minimum bound for volume fraction
    2513                 :            : //! \param[in] u_gp Variable quantity at the quadrature point
    2514                 :            : //! \param[in] u_avg Cell-average variable quantitiy
    2515                 :            : //! \return The limiting coefficient from the positivity-preserving limiter
    2516                 :            : //!   function
    2517                 :            : // *****************************************************************************
    2518                 :            : {
    2519                 :   50622400 :   tk::real phi(1.0);
    2520                 :   50622400 :   tk::real diff = u_gp - u_avg;
    2521                 :            :   // Only when u_gp is less than minimum threshold and the high order
    2522                 :            :   // contribution is not zero, the limiting function will be applied
    2523         [ +  + ]:   50622400 :   if(u_gp < min)
    2524                 :     351199 :     phi = std::fabs( (min - u_avg) / (diff+std::copysign(1e-15,diff)) );
    2525                 :   50622400 :   return phi;
    2526                 :            : }
    2527                 :            : 
    2528                 :            : bool
    2529                 :   28176066 : interfaceIndicator( std::size_t nmat,
    2530                 :            :   const std::vector< tk::real >& al,
    2531                 :            :   std::vector< std::size_t >& matInt )
    2532                 :            : // *****************************************************************************
    2533                 :            : //  Interface indicator function, which checks element for material interface
    2534                 :            : //! \param[in] nmat Number of materials in this PDE system
    2535                 :            : //! \param[in] al Cell-averaged volume fractions
    2536                 :            : //! \param[in] matInt Array indicating which material has an interface
    2537                 :            : //! \return Boolean which indicates if the element contains a material interface
    2538                 :            : // *****************************************************************************
    2539                 :            : {
    2540                 :   28176066 :   bool intInd = false;
    2541                 :            : 
    2542                 :            :   // limits under which compression is to be performed
    2543                 :   28176066 :   auto al_eps = 1e-08;
    2544                 :   28176066 :   auto loLim = 2.0 * al_eps;
    2545                 :   28176066 :   auto hiLim = 1.0 - loLim;
    2546                 :            : 
    2547                 :   28176066 :   auto almax = 0.0;
    2548         [ +  + ]:   90367574 :   for (std::size_t k=0; k<nmat; ++k)
    2549                 :            :   {
    2550                 :   62191508 :     almax = std::max(almax, al[k]);
    2551                 :   62191508 :     matInt[k] = 0;
    2552 [ +  + ][ +  + ]:   62191508 :     if ((al[k] > loLim) && (al[k] < hiLim)) matInt[k] = 1;
                 [ +  + ]
    2553                 :            :   }
    2554                 :            : 
    2555 [ +  - ][ +  + ]:   28176066 :   if ((almax > loLim) && (almax < hiLim)) intInd = true;
    2556                 :            : 
    2557                 :   28176066 :   return intInd;
    2558                 :            : }
    2559                 :            : 
    2560                 :       3330 : void MarkShockCells ( const bool pref,
    2561                 :            :                       const std::size_t nelem,
    2562                 :            :                       const std::size_t nmat,
    2563                 :            :                       const std::size_t ndof,
    2564                 :            :                       const std::size_t rdof,
    2565                 :            :                       const std::vector< inciter::EOS >& mat_blk,
    2566                 :            :                       const std::vector< std::size_t >& ndofel,
    2567                 :            :                       const std::vector< std::size_t >& inpoel,
    2568                 :            :                       const tk::UnsMesh::Coords& coord,
    2569                 :            :                       const inciter::FaceData& fd,
    2570                 :            :                       [[maybe_unused]] const tk::Fields& geoFace,
    2571                 :            :                       const tk::Fields& geoElem,
    2572                 :            :                       const tk::FluxFn& flux,
    2573                 :            :                       const std::vector< std::size_t >& solidx,
    2574                 :            :                       const tk::Fields& U,
    2575                 :            :                       const tk::Fields& P,
    2576                 :            :                       const std::set< std::size_t >& vars,
    2577                 :            :                       std::vector< std::size_t >& shockmarker )
    2578                 :            : // *****************************************************************************
    2579                 :            : //  Mark the cells that contain discontinuity according to the interface
    2580                 :            : //    condition
    2581                 :            : //! \param[in] pref Indicator for p-adaptive algorithm
    2582                 :            : //! \param[in] nelem Number of elements
    2583                 :            : //! \param[in] nmat Number of materials in this PDE system
    2584                 :            : //! \param[in] ndof Maximum number of degrees of freedom
    2585                 :            : //! \param[in] rdof Maximum number of reconstructed degrees of freedom
    2586                 :            : //! \param[in] mat_blk EOS material block
    2587                 :            : //! \param[in] ndofel Vector of local number of degrees of freedome
    2588                 :            : //! \param[in] inpoel Element-node connectivity
    2589                 :            : //! \param[in] coord Array of nodal coordinates
    2590                 :            : //! \param[in] fd Face connectivity and boundary conditions object
    2591                 :            : //! \param[in] geoFace Face geometry array
    2592                 :            : //! \param[in] geoElem Element geometry array
    2593                 :            : //! \param[in] flux Flux function to use
    2594                 :            : //! \param[in] solidx Solid material index indicator
    2595                 :            : //! \param[in] U Solution vector at recent time step
    2596                 :            : //! \param[in] P Vector of primitives at recent time step
    2597                 :            : //! \param[in] vars Vector of variable indices to evaluate flux jump
    2598                 :            : //! \param[in, out] shockmarker Vector of the shock indicator
    2599                 :            : //! \details This function computes the discontinuity indicator based on
    2600                 :            : //!   interface conditon. It is based on the following paper:
    2601                 :            : //!   Hong L., Gianni A., Robert N. (2021) A moving discontinuous Galerkin
    2602                 :            : //!   finite element method with interface condition enforcement for
    2603                 :            : //!   compressible flows. Journal of Computational Physics,
    2604                 :            : //!   doi: https://doi.org/10.1016/j.jcp.2021.110618
    2605                 :            : // *****************************************************************************
    2606                 :            : {
    2607                 :       3330 :   const auto coeff = g_inputdeck.get< tag::shock_detector_coeff >();
    2608                 :            : 
    2609         [ +  - ]:       6660 :   std::vector< tk::real > IC(U.nunk(), 0.0);
    2610                 :       3330 :   const auto& esuf = fd.Esuf();
    2611                 :       3330 :   const auto& inpofa = fd.Inpofa();
    2612                 :            : 
    2613                 :       3330 :   const auto& cx = coord[0];
    2614                 :       3330 :   const auto& cy = coord[1];
    2615                 :       3330 :   const auto& cz = coord[2];
    2616                 :            : 
    2617                 :       3330 :   auto ncomp = U.nprop()/rdof;
    2618                 :       3330 :   auto nprim = P.nprop()/rdof;
    2619                 :            : 
    2620                 :            :   // Loop over faces
    2621 [ +  - ][ +  + ]:    1558710 :   for (auto f=fd.Nbfac(); f<esuf.size()/2; ++f) {
    2622 [ +  - ][ +  - ]:    1555380 :     Assert( esuf[2*f] > -1 && esuf[2*f+1] > -1, "Interior element detected "
         [ -  - ][ -  - ]
                 [ -  - ]
    2623                 :            :             "as -1" );
    2624                 :            : 
    2625                 :    1555380 :     std::size_t el = static_cast< std::size_t >(esuf[2*f]);
    2626                 :    1555380 :     std::size_t er = static_cast< std::size_t >(esuf[2*f+1]);
    2627                 :            : 
    2628                 :            :     // When the number of gauss points for the left and right element are
    2629                 :            :     // different, choose the larger ng
    2630         [ +  - ]:    1555380 :     auto ng_l = tk::NGfa(ndofel[el]);
    2631         [ +  - ]:    1555380 :     auto ng_r = tk::NGfa(ndofel[er]);
    2632                 :            : 
    2633                 :    1555380 :     auto ng = std::max( ng_l, ng_r );
    2634                 :            : 
    2635                 :            :     std::array< std::vector< tk::real >, 2 > coordgp
    2636 [ +  - ][ +  - ]:    3110760 :       { std::vector<tk::real>(ng), std::vector<tk::real>(ng) };
    2637         [ +  - ]:    3110760 :     std::vector< tk::real > wgp( ng );
    2638                 :            : 
    2639         [ +  - ]:    1555380 :     tk::GaussQuadratureTri( ng, coordgp, wgp );
    2640                 :            : 
    2641                 :            :     // Extract the element coordinates
    2642                 :            :     std::array< std::array< tk::real, 3>, 4 > coordel_l {{
    2643                 :    4666140 :       {{ cx[ inpoel[4*el  ] ], cy[ inpoel[4*el  ] ], cz[ inpoel[4*el  ] ] }},
    2644                 :    4666140 :       {{ cx[ inpoel[4*el+1] ], cy[ inpoel[4*el+1] ], cz[ inpoel[4*el+1] ] }},
    2645                 :    4666140 :       {{ cx[ inpoel[4*el+2] ], cy[ inpoel[4*el+2] ], cz[ inpoel[4*el+2] ] }},
    2646                 :   13998420 :       {{ cx[ inpoel[4*el+3] ], cy[ inpoel[4*el+3] ], cz[ inpoel[4*el+3] ] }} }};
    2647                 :            : 
    2648                 :            :     std::array< std::array< tk::real, 3>, 4 > coordel_r {{
    2649                 :    4666140 :       {{ cx[ inpoel[4*er  ] ], cy[ inpoel[4*er  ] ], cz[ inpoel[4*er  ] ] }},
    2650                 :    4666140 :       {{ cx[ inpoel[4*er+1] ], cy[ inpoel[4*er+1] ], cz[ inpoel[4*er+1] ] }},
    2651                 :    4666140 :       {{ cx[ inpoel[4*er+2] ], cy[ inpoel[4*er+2] ], cz[ inpoel[4*er+2] ] }},
    2652                 :   13998420 :       {{ cx[ inpoel[4*er+3] ], cy[ inpoel[4*er+3] ], cz[ inpoel[4*er+3] ] }} }};
    2653                 :            : 
    2654                 :            :     // Compute the determinant of Jacobian matrix
    2655                 :            :     auto detT_l =
    2656                 :    1555380 :       tk::Jacobian( coordel_l[0], coordel_l[1], coordel_l[2], coordel_l[3] );
    2657                 :            :     auto detT_r =
    2658                 :    1555380 :       tk::Jacobian( coordel_r[0], coordel_r[1], coordel_r[2], coordel_r[3] );
    2659                 :            : 
    2660                 :            :     std::array< std::array< tk::real, 3>, 3 > coordfa {{
    2661                 :    4666140 :       {{ cx[ inpofa[3*f  ] ], cy[ inpofa[3*f  ] ], cz[ inpofa[3*f  ] ] }},
    2662                 :    4666140 :       {{ cx[ inpofa[3*f+1] ], cy[ inpofa[3*f+1] ], cz[ inpofa[3*f+1] ] }},
    2663                 :    9332280 :       {{ cx[ inpofa[3*f+2] ], cy[ inpofa[3*f+2] ], cz[ inpofa[3*f+2] ] }} }};
    2664                 :            : 
    2665                 :            :     std::array< tk::real, 3 >
    2666 [ +  - ][ +  - ]:    1555380 :       fn{{ geoFace(f,1), geoFace(f,2), geoFace(f,3) }};
                 [ +  - ]
    2667                 :            : 
    2668                 :            :     // Numerator and denominator of the shock indicator
    2669                 :    1555380 :     tk::real numer(0.0), denom(0.0);
    2670                 :    3110760 :     std::vector< tk::real > fl_jump, fl_avg;
    2671         [ +  - ]:    1555380 :     fl_jump.resize(3, 0.0);
    2672         [ +  - ]:    1555380 :     fl_avg.resize(3, 0.0);
    2673                 :            : 
    2674         [ +  + ]:    5394186 :     for (std::size_t igp=0; igp<ng; ++igp) {
    2675         [ +  - ]:    3838806 :       auto gp = tk::eval_gp( igp, coordfa, coordgp );
    2676                 :            :       std::size_t dof_el, dof_er;
    2677         [ -  + ]:    3838806 :       if (rdof > ndof)
    2678                 :            :       {
    2679                 :          0 :         dof_el = rdof;
    2680                 :          0 :         dof_er = rdof;
    2681                 :            :       }
    2682                 :            :       else
    2683                 :            :       {
    2684                 :    3838806 :         dof_el = ndofel[el];
    2685                 :    3838806 :         dof_er = ndofel[er];
    2686                 :            :       }
    2687                 :            :       std::array< tk::real, 3> ref_gp_l{
    2688                 :    3838806 :         tk::Jacobian( coordel_l[0], gp, coordel_l[2], coordel_l[3] ) / detT_l,
    2689                 :    3838806 :         tk::Jacobian( coordel_l[0], coordel_l[1], gp, coordel_l[3] ) / detT_l,
    2690                 :    7677612 :         tk::Jacobian( coordel_l[0], coordel_l[1], coordel_l[2], gp ) / detT_l };
    2691                 :            :       std::array< tk::real, 3> ref_gp_r{
    2692                 :    3838806 :         tk::Jacobian( coordel_r[0], gp, coordel_r[2], coordel_r[3] ) / detT_r,
    2693                 :    3838806 :         tk::Jacobian( coordel_r[0], coordel_r[1], gp, coordel_r[3] ) / detT_r,
    2694                 :    7677612 :         tk::Jacobian( coordel_r[0], coordel_r[1], coordel_r[2], gp ) / detT_r };
    2695         [ +  - ]:    7677612 :       auto B_l = tk::eval_basis( dof_el, ref_gp_l[0], ref_gp_l[1], ref_gp_l[2] );
    2696         [ +  - ]:    7677612 :       auto B_r = tk::eval_basis( dof_er, ref_gp_r[0], ref_gp_r[1], ref_gp_r[2] );
    2697                 :            : 
    2698                 :            :       // Evaluate the high order solution at the qudrature point
    2699                 :    7677612 :       std::array< std::vector< tk::real >, 2 > state;
    2700         [ +  - ]:    7677612 :       state[0] = tk::evalPolynomialSol(mat_blk, 0, ncomp, nprim, rdof,
    2701                 :    3838806 :         nmat, el, dof_el, inpoel, coord, geoElem, ref_gp_l, B_l, U, P);
    2702         [ +  - ]:    7677612 :       state[1] = tk::evalPolynomialSol(mat_blk, 0, ncomp, nprim, rdof,
    2703                 :    3838806 :         nmat, er, dof_er, inpoel, coord, geoElem, ref_gp_r, B_r, U, P);
    2704                 :            : 
    2705 [ -  + ][ -  - ]:    3838806 :       Assert( state[0].size() == ncomp+nprim, "Incorrect size for "
         [ -  - ][ -  - ]
    2706                 :            :               "appended boundary state vector" );
    2707 [ -  + ][ -  - ]:    3838806 :       Assert( state[1].size() == ncomp+nprim, "Incorrect size for "
         [ -  - ][ -  - ]
    2708                 :            :               "appended boundary state vector" );
    2709                 :            : 
    2710                 :            :       // Force deformation unknown to first order
    2711         [ +  + ]:   10113912 :       for (std::size_t k=0; k<nmat; ++k)
    2712         [ -  + ]:    6275106 :         if (solidx[k] > 0)
    2713         [ -  - ]:          0 :           for (std::size_t i=0; i<3; ++i)
    2714         [ -  - ]:          0 :             for (std::size_t j=0; j<3; ++j)
    2715                 :            :             {
    2716                 :          0 :               state[0][deformIdx(nmat, solidx[k], i, j)] = U(el,deformDofIdx(
    2717         [ -  - ]:          0 :                 nmat, solidx[k], i, j, rdof, 0));
    2718                 :          0 :               state[1][deformIdx(nmat, solidx[k], i, j)] = U(er,deformDofIdx(
    2719         [ -  - ]:          0 :                 nmat, solidx[k], i, j, rdof, 0));
    2720                 :            :             }
    2721                 :            : 
    2722                 :            :       // Evaluate the flux
    2723         [ +  - ]:    7677612 :       auto fl = flux( ncomp, mat_blk, state[0], {} );
    2724         [ +  - ]:    7677612 :       auto fr = flux( ncomp, mat_blk, state[1], {} );
    2725                 :            : 
    2726                 :    3838806 :       std::size_t i(0);
    2727         [ +  + ]:   15355224 :       for (const auto& c : vars) {
    2728                 :   11516418 :         tk::real fn_l(0.0), fn_r(0.0);
    2729         [ +  + ]:   46065672 :         for(std::size_t idir = 0; idir < 3; idir++) {
    2730                 :   34549254 :           fn_l += fl[c][idir] * fn[idir];
    2731                 :   34549254 :           fn_r += fr[c][idir] * fn[idir];
    2732                 :            :         }
    2733                 :   11516418 :         fl_jump[i] += wgp[igp] * (fn_l - fn_r) * (fn_l - fn_r);
    2734                 :   11516418 :         fl_avg[i]  += wgp[igp] * (fn_l + fn_r) * (fn_l + fn_r) * 0.25;
    2735                 :   11516418 :         ++i;
    2736                 :            :       }
    2737                 :            :     }
    2738                 :            : 
    2739                 :            :     // Evaluate the numerator and denominator
    2740         [ +  + ]:    6221520 :     for(std::size_t idir = 0; idir < 3; idir++) {
    2741                 :    4666140 :       numer += std::sqrt(fl_jump[idir]);
    2742                 :    4666140 :       denom += std::sqrt(fl_avg[idir]);
    2743                 :            :     }
    2744                 :            : 
    2745                 :    1555380 :     tk::real Ind(0.0);
    2746         [ +  - ]:    1555380 :     if(denom > 1e-8)
    2747                 :    1555380 :       Ind = numer / denom;
    2748                 :    1555380 :     IC[el] = std::max(IC[el], Ind);
    2749                 :    1555380 :     IC[er] = std::max(IC[er], Ind);
    2750                 :            :   }
    2751                 :            : 
    2752                 :            :   // Loop over element to mark shock cell
    2753         [ +  + ]:     821970 :   for (std::size_t e=0; e<nelem; ++e) {
    2754         [ -  + ]:     818640 :     std::size_t dof_el = pref ? ndofel[e] : rdof;
    2755                 :            : 
    2756                 :     818640 :     tk::real power = 0.0;
    2757         [ +  + ]:     818640 :     if(dof_el == 10)  power = 1.5;
    2758                 :     454800 :     else              power = 1.0;
    2759                 :            : 
    2760                 :            :     // Evaluate the threshold
    2761         [ +  - ]:     818640 :     auto thres = coeff * std::pow(geoElem(e, 4), power);
    2762         [ +  + ]:     818640 :     if(IC[e] > thres)
    2763                 :      50304 :       shockmarker[e] = 1;
    2764                 :            :     else
    2765                 :     768336 :       shockmarker[e] = 0;
    2766                 :            :   }
    2767                 :       3330 : }
    2768                 :            : 
    2769                 :            : void
    2770                 :         30 : correctLimConservMultiMat(
    2771                 :            :   std::size_t nelem,
    2772                 :            :   const std::vector< EOS >& mat_blk,
    2773                 :            :   std::size_t nmat,
    2774                 :            :   const std::vector< std::size_t >& inpoel,
    2775                 :            :   const tk::UnsMesh::Coords& coord,
    2776                 :            :   const tk::Fields& geoElem,
    2777                 :            :   const tk::Fields& prim,
    2778                 :            :   tk::Fields& unk )
    2779                 :            : // *****************************************************************************
    2780                 :            : //  Update the conservative quantities after limiting for multi-material systems
    2781                 :            : //! \param[in] nelem Number of internal elements
    2782                 :            : //! \param[in] mat_blk EOS material block
    2783                 :            : //! \param[in] nmat Number of materials in this PDE system
    2784                 :            : //! \param[in] inpoel Element-node connectivity
    2785                 :            : //! \param[in] coord Array of nodal coordinates
    2786                 :            : //! \param[in] geoElem Element geometry array
    2787                 :            : //! \param[in] prim Array of primitive variables
    2788                 :            : //! \param[in,out] unk Array of conservative variables
    2789                 :            : //! \details This function computes the updated dofs for conservative
    2790                 :            : //!   quantities based on the limited primitive quantities, to re-instate
    2791                 :            : //!   consistency between the limited primitive and evolved quantities. For
    2792                 :            : //!   further details, see Pandare et al. (2023). On the Design of Stable,
    2793                 :            : //!   Consistent, and Conservative High-Order Methods for Multi-Material
    2794                 :            : //!   Hydrodynamics. J Comp Phys, 112313.
    2795                 :            : // *****************************************************************************
    2796                 :            : {
    2797                 :         30 :   const auto rdof = g_inputdeck.get< tag::rdof >();
    2798                 :         30 :   std::size_t ncomp = unk.nprop()/rdof;
    2799                 :         30 :   std::size_t nprim = prim.nprop()/rdof;
    2800                 :            :   const auto intsharp = inciter::g_inputdeck.get< tag::multimat,
    2801                 :         30 :     tag::intsharp >();
    2802                 :            : 
    2803         [ +  + ]:      45510 :   for (std::size_t e=0; e<nelem; ++e) {
    2804                 :            :     // Here we pre-compute the right-hand-side vector. The reason that the
    2805                 :            :     // lhs in DG.cpp is not used is that the size of this vector in this
    2806                 :            :     // projection procedure should be rdof instead of ndof.
    2807 [ +  - ][ +  - ]:      90960 :     auto L = tk::massMatrixDubiner(rdof, geoElem(e,0));
    2808                 :            : 
    2809                 :            :     // The right-hand side vector is sized as nprim, i.e. the primitive quantity
    2810                 :            :     // vector. However, it stores the consistently obtained values of evolved
    2811                 :            :     // quantities, since nprim is the number of evolved quantities that need to
    2812                 :            :     // be evaluated consistently. For this reason, accessing R will require
    2813                 :            :     // the primitive quantity accessors. But this access is intended to give
    2814                 :            :     // the corresponding evolved quantites, as follows:
    2815                 :            :     // pressureIdx() - mat. total energy
    2816                 :            :     // velocityIdx() - bulk momentum components
    2817                 :            :     // stressIdx() - mat. inverse deformation gradient tensor components
    2818         [ +  - ]:      90960 :     std::vector< tk::real > R(nprim*rdof, 0.0);
    2819                 :            : 
    2820         [ +  - ]:      45480 :     auto ng = tk::NGvol(rdof);
    2821                 :            : 
    2822                 :            :     // Arrays for quadrature points
    2823                 :      90960 :     std::array< std::vector< tk::real >, 3 > coordgp;
    2824                 :      90960 :     std::vector< tk::real > wgp;
    2825                 :            : 
    2826         [ +  - ]:      45480 :     coordgp[0].resize( ng );
    2827         [ +  - ]:      45480 :     coordgp[1].resize( ng );
    2828         [ +  - ]:      45480 :     coordgp[2].resize( ng );
    2829         [ +  - ]:      45480 :     wgp.resize( ng );
    2830                 :            : 
    2831         [ +  - ]:      45480 :     tk::GaussQuadratureTet( ng, coordgp, wgp );
    2832                 :            : 
    2833                 :            :     // Loop over quadrature points in element e
    2834         [ +  + ]:     272880 :     for (std::size_t igp=0; igp<ng; ++igp) {
    2835                 :            :       // Compute the basis function
    2836                 :     454800 :       auto B = tk::eval_basis( rdof, coordgp[0][igp], coordgp[1][igp],
    2837         [ +  - ]:     909600 :                                coordgp[2][igp] );
    2838                 :            : 
    2839         [ +  - ]:     227400 :       auto w = wgp[igp] * geoElem(e, 0);
    2840                 :            : 
    2841                 :            :       // Evaluate the solution at quadrature point
    2842                 :            :       auto state = evalPolynomialSol(mat_blk, intsharp, ncomp, nprim,
    2843                 :            :         rdof, nmat, e, rdof, inpoel, coord, geoElem,
    2844         [ +  - ]:     454800 :         {{coordgp[0][igp], coordgp[1][igp], coordgp[2][igp]}}, B, unk, prim);
    2845                 :            : 
    2846                 :            :       // Solution vector that stores the material energy and bulk momentum
    2847         [ +  - ]:     454800 :       std::vector< tk::real > s(nprim, 0.0);
    2848                 :            : 
    2849                 :            :       // Bulk density at quadrature point
    2850                 :     227400 :       tk::real rhob(0.0);
    2851         [ +  + ]:     682200 :       for (std::size_t k=0; k<nmat; ++k)
    2852                 :     454800 :         rhob += state[densityIdx(nmat, k)];
    2853                 :            : 
    2854                 :            :       // Velocity vector at quadrature point
    2855                 :            :       std::array< tk::real, 3 >
    2856                 :     227400 :         vel{ state[ncomp+velocityIdx(nmat, 0)],
    2857                 :     227400 :              state[ncomp+velocityIdx(nmat, 1)],
    2858                 :     454800 :              state[ncomp+velocityIdx(nmat, 2)] };
    2859                 :            : 
    2860                 :            :       // Compute and store the bulk momentum
    2861         [ +  + ]:     909600 :       for(std::size_t idir = 0; idir < 3; idir++)
    2862                 :     682200 :         s[velocityIdx(nmat, idir)] = rhob * vel[idir];
    2863                 :            : 
    2864                 :            :       // Compute and store material energy at quadrature point
    2865         [ +  + ]:     682200 :       for(std::size_t imat = 0; imat < nmat; imat++) {
    2866                 :     454800 :         auto alphamat = state[volfracIdx(nmat, imat)];
    2867                 :     454800 :         auto rhomat = state[densityIdx(nmat, imat)]/alphamat;
    2868                 :     454800 :         auto premat = state[ncomp+pressureIdx(nmat, imat)]/alphamat;
    2869         [ +  - ]:     454800 :         auto gmat = getDeformGrad(nmat, imat, state);
    2870                 :     454800 :         s[pressureIdx(nmat,imat)] = alphamat *
    2871         [ +  - ]:     909600 :           mat_blk[imat].compute< EOS::totalenergy >( rhomat, vel[0], vel[1],
    2872                 :     454800 :           vel[2], premat, gmat );
    2873                 :            :       }
    2874                 :            : 
    2875                 :            :       // Evaluate the righ-hand-side vector
    2876         [ +  + ]:    1364400 :       for(std::size_t k = 0; k < nprim; k++) {
    2877                 :    1137000 :         auto mark = k * rdof;
    2878         [ +  + ]:    5685000 :         for(std::size_t idof = 0; idof < rdof; idof++)
    2879                 :    4548000 :           R[mark+idof] += w * s[k] * B[idof];
    2880                 :            :       }
    2881                 :            :     }
    2882                 :            : 
    2883                 :            :     // Update the high order dofs of the material energy
    2884         [ +  + ]:     136440 :     for(std::size_t imat = 0; imat < nmat; imat++) {
    2885         [ +  + ]:     363840 :       for(std::size_t idof = 1; idof < rdof; idof++)
    2886         [ +  - ]:     272880 :         unk(e, energyDofIdx(nmat, imat, rdof, idof)) =
    2887                 :     272880 :           R[pressureDofIdx(nmat,imat,rdof,idof)] / L[idof];
    2888                 :            :     }
    2889                 :            : 
    2890                 :            :     // Update the high order dofs of the bulk momentum
    2891         [ +  + ]:     181920 :     for(std::size_t idir = 0; idir < 3; idir++) {
    2892         [ +  + ]:     545760 :       for(std::size_t idof = 1; idof < rdof; idof++)
    2893         [ +  - ]:     409320 :         unk(e, momentumDofIdx(nmat, idir, rdof, idof)) =
    2894                 :     409320 :           R[velocityDofIdx(nmat,idir,rdof,idof)] / L[idof];
    2895                 :            :     }
    2896                 :            :   }
    2897                 :         30 : }
    2898                 :            : 
    2899                 :            : void
    2900                 :       6600 : correctLimConservMultiSpecies(
    2901                 :            :   std::size_t nelem,
    2902                 :            :   const std::vector< EOS >& mat_blk,
    2903                 :            :   std::size_t nspec,
    2904                 :            :   const std::vector< std::size_t >& inpoel,
    2905                 :            :   const tk::UnsMesh::Coords& coord,
    2906                 :            :   const tk::Fields& geoElem,
    2907                 :            :   const tk::Fields& prim,
    2908                 :            :   tk::Fields& unk )
    2909                 :            : // *****************************************************************************
    2910                 :            : //  Update the conservative quantities after limiting for multispecies systems
    2911                 :            : //! \param[in] nelem Number of internal elements
    2912                 :            : //! \param[in] mat_blk EOS material block
    2913                 :            : //! \param[in] nspec Number of species in this PDE system
    2914                 :            : //! \param[in] inpoel Element-node connectivity
    2915                 :            : //! \param[in] coord Array of nodal coordinates
    2916                 :            : //! \param[in] geoElem Element geometry array
    2917                 :            : //! \param[in] prim Array of primitive variables
    2918                 :            : //! \param[in,out] unk Array of conservative variables
    2919                 :            : //! \details This function computes the updated dofs for conservative
    2920                 :            : //!   quantities based on the limited primitive quantities, to re-instate
    2921                 :            : //!   consistency between the limited primitive and evolved quantities. For
    2922                 :            : //!   further details, see Pandare et al. (2023). On the Design of Stable,
    2923                 :            : //!   Consistent, and Conservative High-Order Methods for Multi-Material
    2924                 :            : //!   Hydrodynamics. J Comp Phys, 112313.
    2925                 :            : // *****************************************************************************
    2926                 :            : {
    2927                 :       6600 :   const auto rdof = g_inputdeck.get< tag::rdof >();
    2928                 :       6600 :   std::size_t ncomp = unk.nprop()/rdof;
    2929                 :       6600 :   std::size_t nprim = prim.nprop()/rdof;
    2930                 :            : 
    2931         [ +  + ]:     688800 :   for (std::size_t e=0; e<nelem; ++e) {
    2932                 :            :     // Here we pre-compute the right-hand-side vector. The reason that the
    2933                 :            :     // lhs in DG.cpp is not used is that the size of this vector in this
    2934                 :            :     // projection procedure should be rdof instead of ndof.
    2935 [ +  - ][ +  - ]:    1364400 :     auto L = tk::massMatrixDubiner(rdof, geoElem(e,0));
    2936                 :            : 
    2937                 :            :     // The right-hand side vector is sized as nprim, i.e. the primitive quantity
    2938                 :            :     // vector. However, it stores the consistently obtained values of evolved
    2939                 :            :     // quantities, since nprim is the number of evolved quantities that need to
    2940                 :            :     // be evaluated consistently. For this reason, accessing R will require
    2941                 :            :     // the primitive quantity accessors. But this access is intended to give
    2942                 :            :     // the corresponding evolved quantites, as follows:
    2943                 :            :     // multispecies::tempratureIdx() - total energy
    2944         [ +  - ]:    1364400 :     std::vector< tk::real > R(nprim*rdof, 0.0);
    2945                 :            : 
    2946         [ +  - ]:     682200 :     auto ng = tk::NGvol(rdof);
    2947                 :            : 
    2948                 :            :     // Arrays for quadrature points
    2949                 :    1364400 :     std::array< std::vector< tk::real >, 3 > coordgp;
    2950                 :    1364400 :     std::vector< tk::real > wgp;
    2951                 :            : 
    2952         [ +  - ]:     682200 :     coordgp[0].resize( ng );
    2953         [ +  - ]:     682200 :     coordgp[1].resize( ng );
    2954         [ +  - ]:     682200 :     coordgp[2].resize( ng );
    2955         [ +  - ]:     682200 :     wgp.resize( ng );
    2956                 :            : 
    2957         [ +  - ]:     682200 :     tk::GaussQuadratureTet( ng, coordgp, wgp );
    2958                 :            : 
    2959                 :            :     // Loop over quadrature points in element e
    2960         [ +  + ]:    4093200 :     for (std::size_t igp=0; igp<ng; ++igp) {
    2961                 :            :       // Compute the basis function
    2962                 :    6822000 :       auto B = tk::eval_basis( rdof, coordgp[0][igp], coordgp[1][igp],
    2963         [ +  - ]:   13644000 :                                coordgp[2][igp] );
    2964                 :            : 
    2965         [ +  - ]:    3411000 :       auto w = wgp[igp] * geoElem(e, 0);
    2966                 :            : 
    2967                 :            :       // Evaluate the solution at quadrature point
    2968                 :            :       auto state = evalPolynomialSol(mat_blk, 0, ncomp, nprim,
    2969                 :            :         rdof, 1, e, rdof, inpoel, coord, geoElem,
    2970         [ +  - ]:    6822000 :         {{coordgp[0][igp], coordgp[1][igp], coordgp[2][igp]}}, B, unk, prim);
    2971                 :            : 
    2972                 :            :       // Solution vector that stores the material energy and bulk momentum
    2973         [ +  - ]:    6822000 :       std::vector< tk::real > s(nprim, 0.0);
    2974                 :            : 
    2975                 :            :       // Mixture state at quadrature point
    2976         [ +  - ]:    6822000 :       Mixture mixgp(nspec, state, mat_blk);
    2977                 :            : 
    2978                 :            :       // Mixture density at quadrature point
    2979                 :    3411000 :       tk::real rhob = mixgp.get_mix_density();
    2980                 :            : 
    2981                 :            :       // velocity vector at quadrature point
    2982                 :            :       std::array< tk::real, 3 >
    2983                 :    3411000 :         vel{ state[multispecies::momentumIdx(nspec,0)]/rhob,
    2984                 :    3411000 :              state[multispecies::momentumIdx(nspec,1)]/rhob,
    2985                 :    6822000 :              state[multispecies::momentumIdx(nspec,2)]/rhob };
    2986                 :            : 
    2987                 :            :       // Compute and store total energy at quadrature point
    2988         [ +  - ]:    3411000 :       s[multispecies::temperatureIdx(nspec,0)] = mixgp.totalenergy(rhob,
    2989                 :    3411000 :         vel[0], vel[1], vel[2],
    2990                 :    3411000 :         state[ncomp+multispecies::temperatureIdx(nspec,0)], mat_blk);
    2991                 :            : 
    2992                 :            :       // Evaluate the right-hand-side vector
    2993         [ +  + ]:    6822000 :       for(std::size_t k = 0; k < nprim; k++) {
    2994                 :    3411000 :         auto mark = k * rdof;
    2995         [ +  + ]:   17055000 :         for(std::size_t idof = 0; idof < rdof; idof++)
    2996                 :   13644000 :           R[mark+idof] += w * s[k] * B[idof];
    2997                 :            :       }
    2998                 :            :     }
    2999                 :            : 
    3000                 :            :     // Update the high order dofs of the total energy
    3001         [ +  + ]:    2728800 :     for(std::size_t idof = 1; idof < rdof; idof++)
    3002         [ +  - ]:    2046600 :       unk(e, multispecies::energyDofIdx(nspec,0,rdof,idof)) =
    3003                 :    2046600 :         R[multispecies::temperatureDofIdx(nspec,0,rdof,idof)] / L[idof];
    3004                 :            :   }
    3005                 :       6600 : }
    3006                 :            : 
    3007                 :            : tk::real
    3008                 :   69630750 : constrain_pressure( const std::vector< EOS >& mat_blk,
    3009                 :            :   tk::real apr,
    3010                 :            :   tk::real arho,
    3011                 :            :   tk::real alpha=1.0,
    3012                 :            :   std::size_t imat=0 )
    3013                 :            : // *****************************************************************************
    3014                 :            : //  Constrain material partial pressure (alpha_k * p_k)
    3015                 :            : //! \param[in] apr Material partial pressure (alpha_k * p_k)
    3016                 :            : //! \param[in] arho Material partial density (alpha_k * rho_k)
    3017                 :            : //! \param[in] alpha Material volume fraction. Default is 1.0, so that for the
    3018                 :            : //!   single-material system, this argument can be left unspecified by the
    3019                 :            : //!   calling code
    3020                 :            : //! \param[in] imat Material-id who's EoS is required. Default is 0, so that
    3021                 :            : //!   for the single-material system, this argument can be left unspecified by
    3022                 :            : //!   the calling code
    3023                 :            : //! \return Constrained material partial pressure (alpha_k * p_k)
    3024                 :            : // *****************************************************************************
    3025                 :            : {
    3026                 :   69630750 :   return std::max(apr, alpha*mat_blk[imat].compute<
    3027         [ +  - ]:   69630750 :     EOS::min_eff_pressure >(1e-12, arho, alpha));
    3028                 :            : }
    3029                 :            : 
    3030                 :            : 
    3031                 :            : } // inciter::

Generated by: LCOV version 1.14