Quinoa regression test code coverage report
Current view: top level - PDE - Reconstruction.cpp (source / functions) Hit Total Coverage
Commit: Quinoa_v0.3-957-gb4f0efae0 Lines: 227 331 68.6 %
Date: 2021-11-09 13:40:20 Functions: 9 13 69.2 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 152 334 45.5 %

           Branch data     Line data    Source code
       1                 :            : // *****************************************************************************
       2                 :            : /*!
       3                 :            :   \file      src/PDE/Reconstruction.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     Reconstruction for reconstructed discontinuous Galerkin methods
       9                 :            :   \details   This file contains functions that reconstruct an "n"th order
      10                 :            :     polynomial to an "n+1"th order polynomial using a least-squares
      11                 :            :     reconstruction procedure.
      12                 :            : */
      13                 :            : // *****************************************************************************
      14                 :            : 
      15                 :            : #include <array>
      16                 :            : #include <vector>
      17                 :            : #include <iostream>
      18                 :            : 
      19                 :            : #include "Vector.hpp"
      20                 :            : #include "Around.hpp"
      21                 :            : #include "Base/HashMapReducer.hpp"
      22                 :            : #include "Reconstruction.hpp"
      23                 :            : #include "MultiMat/MultiMatIndexing.hpp"
      24                 :            : #include "Inciter/InputDeck/InputDeck.hpp"
      25                 :            : #include "Limiter.hpp"
      26                 :            : #include "EoS/EoS.hpp"
      27                 :            : 
      28                 :            : namespace inciter {
      29                 :            : extern ctr::InputDeck g_inputdeck;
      30                 :            : }
      31                 :            : 
      32                 :            : namespace tk {
      33                 :            : 
      34                 :            : void
      35                 :       7050 : lhsLeastSq_P0P1( const inciter::FaceData& fd,
      36                 :            :                  const Fields& geoElem,
      37                 :            :                  const Fields& geoFace,
      38                 :            :                  std::vector< std::array< std::array< real, 3 >, 3 > >& lhs_ls )
      39                 :            : // *****************************************************************************
      40                 :            : //  Compute lhs matrix for the least-squares reconstruction
      41                 :            : //! \param[in] fd Face connectivity and boundary conditions object
      42                 :            : //! \param[in] geoElem Element geometry array
      43                 :            : //! \param[in] geoFace Face geometry array
      44                 :            : //! \param[in,out] lhs_ls LHS reconstruction matrix
      45                 :            : //! \details This function computing the lhs matrix for reconstruction, is
      46                 :            : //!   common for primitive and conserved quantities.
      47                 :            : // *****************************************************************************
      48                 :            : {
      49                 :            :   const auto& esuf = fd.Esuf();
      50                 :       7050 :   const auto nelem = fd.Esuel().size()/4;
      51                 :            : 
      52                 :            :   // Compute internal and boundary face contributions
      53         [ +  + ]:    4968450 :   for (std::size_t f=0; f<esuf.size()/2; ++f)
      54                 :            :   {
      55                 :            :     Assert( esuf[2*f] > -1, "Left-side element detected as -1" );
      56                 :            : 
      57         [ +  + ]:    4961400 :     auto el = static_cast< std::size_t >(esuf[2*f]);
      58                 :    4961400 :     auto er = esuf[2*f+1];
      59                 :            : 
      60                 :            :     std::array< real, 3 > geoElemR;
      61                 :            :     std::size_t eR(0);
      62                 :            : 
      63                 :            :     // A second-order (piecewise linear) solution polynomial can be obtained
      64                 :            :     // from the first-order (piecewise constant) FV solutions by using a
      65                 :            :     // least-squares (LS) reconstruction process. LS uses the first-order
      66                 :            :     // solutions from the cell being processed, and the cells surrounding it.
      67                 :            :     // The LS system is obtaining by requiring the following to hold:
      68                 :            :     // 'Taylor expansions of solution from cell-i to the centroids of each of
      69                 :            :     // its neighboring cells should be equal to the cell average solution on
      70                 :            :     // that neighbor cell.'
      71                 :            :     // This gives a system of equations for the three second-order DOFs that are
      72                 :            :     // to be determined. In 3D tetrahedral meshes, this would give four
      73                 :            :     // equations (one for each neighbor )for the three unknown DOFs. This
      74                 :            :     // overdetermined system is solved in the least-squares sense using the
      75                 :            :     // normal equations approach. The normal equations approach involves
      76                 :            :     // pre-multiplying the overdetermined system by the transpose of the system
      77                 :            :     // matrix to obtain a square matrix (3x3 in this case).
      78                 :            : 
      79                 :            :     // get a 3x3 system by applying the normal equation approach to the
      80                 :            :     // least-squares overdetermined system
      81                 :            : 
      82         [ +  + ]:    4961400 :     if (er > -1) {
      83                 :            :     // internal face contribution
      84                 :    3643500 :       eR = static_cast< std::size_t >(er);
      85                 :            :       // Put in cell-centroid coordinates
      86                 :    3643500 :       geoElemR = {{ geoElem(eR,1,0), geoElem(eR,2,0), geoElem(eR,3,0) }};
      87                 :            :     }
      88                 :            :     else {
      89                 :            :     // boundary face contribution
      90                 :            :       // Put in face-centroid coordinates
      91                 :    1317900 :       geoElemR = {{ geoFace(f,4,0), geoFace(f,5,0), geoFace(f,6,0) }};
      92                 :            :     }
      93                 :            : 
      94                 :    4961400 :     std::array< real, 3 > wdeltax{{ geoElemR[0]-geoElem(el,1,0),
      95                 :    4961400 :                                     geoElemR[1]-geoElem(el,2,0),
      96                 :    4961400 :                                     geoElemR[2]-geoElem(el,3,0) }};
      97                 :            : 
      98                 :            :     // define a lambda for contributing to lhs matrix
      99                 :            :     auto lhs = [&]( std::size_t e ){
     100 [ +  + ][ +  + ]:   33506400 :     for (std::size_t idir=0; idir<3; ++idir)
     101 [ +  + ][ +  + ]:  100519200 :       for (std::size_t jdir=0; jdir<3; ++jdir)
     102                 :   75389400 :         lhs_ls[e][idir][jdir] += wdeltax[idir] * wdeltax[jdir];
     103                 :            :     };
     104                 :            : 
     105                 :            :     // always add left element contribution (at a boundary face, the internal
     106                 :            :     // element is always the left element)
     107                 :            :     lhs(el);
     108                 :            :     // add right element contribution for internal faces only
     109         [ +  + ]:    4961400 :     if (er > -1)
     110         [ +  + ]:    3643500 :       if (eR < nelem) lhs(eR);
     111                 :            : 
     112                 :            :   }
     113                 :       7050 : }
     114                 :            : 
     115                 :            : void
     116                 :       7050 : intLeastSq_P0P1( ncomp_t offset,
     117                 :            :                  const std::size_t rdof,
     118                 :            :                  const inciter::FaceData& fd,
     119                 :            :                  const Fields& geoElem,
     120                 :            :                  const Fields& W,
     121                 :            :                  std::vector< std::vector< std::array< real, 3 > > >& rhs_ls,
     122                 :            :                  const std::array< std::size_t, 2 >& varRange )
     123                 :            : // *****************************************************************************
     124                 :            : //  \brief Compute internal surface contributions to rhs vector of the
     125                 :            : //    least-squares reconstruction
     126                 :            : //! \param[in] offset Offset this PDE system operates from
     127                 :            : //! \param[in] rdof Maximum number of reconstructed degrees of freedom
     128                 :            : //! \param[in] fd Face connectivity and boundary conditions object
     129                 :            : //! \param[in] geoElem Element geometry array
     130                 :            : //! \param[in] W Solution vector to be reconstructed at recent time step
     131                 :            : //! \param[in,out] rhs_ls RHS reconstruction vector
     132                 :            : //! \param[in] varRange Range of indices in W, that need to be reconstructed
     133                 :            : //! \details This function computing the internal face contributions to the rhs
     134                 :            : //!   vector for reconstruction, is common for primitive and conserved
     135                 :            : //!   quantities. If `W` == `U`, compute internal face contributions for the
     136                 :            : //!   conserved variables. If `W` == `P`, compute internal face contributions
     137                 :            : //!   for the primitive variables.
     138                 :            : // *****************************************************************************
     139                 :            : {
     140                 :            :   const auto& esuf = fd.Esuf();
     141                 :       7050 :   const auto nelem = fd.Esuel().size()/4;
     142                 :            : 
     143                 :            :   // Compute internal face contributions
     144         [ +  + ]:    3650550 :   for (auto f=fd.Nbfac(); f<esuf.size()/2; ++f)
     145                 :            :   {
     146                 :            :     Assert( esuf[2*f] > -1 && esuf[2*f+1] > -1, "Interior element detected "
     147                 :            :             "as -1" );
     148                 :            : 
     149                 :    3643500 :     auto el = static_cast< std::size_t >(esuf[2*f]);
     150                 :    3643500 :     auto er = static_cast< std::size_t >(esuf[2*f+1]);
     151                 :            : 
     152                 :            :     // get a 3x3 system by applying the normal equation approach to the
     153                 :            :     // least-squares overdetermined system
     154                 :            : 
     155                 :            :     // 'wdeltax' is the distance vector between the centroids of this element
     156                 :            :     // and its neighbor
     157                 :    3643500 :     std::array< real, 3 > wdeltax{{ geoElem(er,1,0)-geoElem(el,1,0),
     158                 :    3643500 :                                     geoElem(er,2,0)-geoElem(el,2,0),
     159                 :    3643500 :                                     geoElem(er,3,0)-geoElem(el,3,0) }};
     160                 :            : 
     161         [ +  + ]:   14574000 :     for (std::size_t idir=0; idir<3; ++idir)
     162                 :            :     {
     163                 :            :       // rhs vector
     164         [ +  + ]:   31343400 :       for (std::size_t c=varRange[0]; c<=varRange[1]; ++c)
     165                 :            :       {
     166                 :   20412900 :         auto mark = c*rdof;
     167         [ +  + ]:   20412900 :         rhs_ls[el][c][idir] +=
     168         [ +  + ]:   20412900 :           wdeltax[idir] * (W(er,mark,offset)-W(el,mark,offset));
     169         [ +  + ]:   20412900 :         if (er < nelem)
     170                 :   19728000 :           rhs_ls[er][c][idir] +=
     171                 :   19728000 :             wdeltax[idir] * (W(er,mark,offset)-W(el,mark,offset));
     172                 :            :       }
     173                 :            :     }
     174                 :            :   }
     175                 :       7050 : }
     176                 :            : 
     177                 :            : void
     178                 :      42300 : bndLeastSqConservedVar_P0P1(
     179                 :            :   ncomp_t system,
     180                 :            :   ncomp_t ncomp,
     181                 :            :   ncomp_t offset,
     182                 :            :   std::size_t rdof,
     183                 :            :   const std::vector< bcconf_t >& bcconfig,
     184                 :            :   const inciter::FaceData& fd,
     185                 :            :   const Fields& geoFace,
     186                 :            :   const Fields& geoElem,
     187                 :            :   real t,
     188                 :            :   const StateFn& state,
     189                 :            :   const Fields& P,
     190                 :            :   const Fields& U,
     191                 :            :   std::vector< std::vector< std::array< real, 3 > > >& rhs_ls,
     192                 :            :   const std::array< std::size_t, 2 >& varRange,
     193                 :            :   std::size_t nprim )
     194                 :            : // *****************************************************************************
     195                 :            : //  \brief Compute boundary surface contributions to rhs vector of the
     196                 :            : //    least-squares reconstruction of conserved quantities of the PDE system
     197                 :            : //! \param[in] system Equation system index
     198                 :            : //! \param[in] ncomp Number of scalar components in this PDE system
     199                 :            : //! \param[in] offset Offset this PDE system operates from
     200                 :            : //! \param[in] rdof Maximum number of reconstructed degrees of freedom
     201                 :            : //! \param[in] bcconfig BC configuration vector for multiple side sets
     202                 :            : //! \param[in] fd Face connectivity and boundary conditions object
     203                 :            : //! \param[in] geoFace Face geometry array
     204                 :            : //! \param[in] geoElem Element geometry array
     205                 :            : //! \param[in] t Physical time
     206                 :            : //! \param[in] state Function to evaluate the left and right solution state at
     207                 :            : //!   boundaries
     208                 :            : //! \param[in] P Primitive vector to be reconstructed at recent time step
     209                 :            : //! \param[in] U Solution vector to be reconstructed at recent time step
     210                 :            : //! \param[in,out] rhs_ls RHS reconstruction vector
     211                 :            : //! \param[in] varRange Range of indices in W, that need to be reconstructed
     212                 :            : //! \param[in] nprim This is the number of primitive quantities stored for this
     213                 :            : //!   PDE system. This is necessary to extend the state vector to the right
     214                 :            : //!   size, so that correct boundary conditions are obtained.
     215                 :            : //!   A default is set to 0, so that calling code for systems that do not store
     216                 :            : //!   primitive quantities does not need to specify this argument.
     217                 :            : //! \details This function computing the boundary face contributions to the rhs
     218                 :            : //!   vector for reconstruction, is used for conserved quantities only.
     219                 :            : // *****************************************************************************
     220                 :            : {
     221                 :            :   const auto& bface = fd.Bface();
     222                 :            :   const auto& esuf = fd.Esuf();
     223                 :            : 
     224         [ +  + ]:      64350 :   for (const auto& s : bcconfig) {       // for all bc sidesets
     225                 :            :     auto bc = bface.find( std::stoi(s) );// faces for side set
     226         [ +  + ]:      22050 :     if (bc != end(bface))
     227                 :            :     {
     228                 :            :       // Compute boundary face contributions
     229         [ +  + ]:    1331550 :       for (const auto& f : bc->second)
     230                 :            :       {
     231                 :            :         Assert( esuf[2*f+1] == -1, "physical boundary element not -1" );
     232                 :            : 
     233         [ +  - ]:    1317900 :         std::size_t el = static_cast< std::size_t >(esuf[2*f]);
     234                 :            : 
     235                 :            :         // arrays for quadrature points
     236                 :            :         std::array< real, 3 >
     237                 :    1317900 :           fc{{ geoFace(f,4,0), geoFace(f,5,0), geoFace(f,6,0) }};
     238                 :            :         std::array< real, 3 >
     239                 :    1317900 :           fn{{ geoFace(f,1,0), geoFace(f,2,0), geoFace(f,3,0) }};
     240                 :            : 
     241                 :            :         // Compute the state variables at the left element
     242         [ +  - ]:    1317900 :         std::vector< real >B(1,1.0);
     243 [ +  - ][ -  - ]:    1317900 :         auto ul = eval_state( ncomp, offset, rdof, 1, el, U, B, {0, ncomp-1} );
     244 [ +  - ][ +  - ]:    1317900 :         auto uprim = eval_state( nprim, offset, rdof, 1, el, P, B, {0, nprim-1} );
                 [ -  - ]
     245                 :            : 
     246                 :            :         // consolidate primitives into state vector
     247 [ +  - ][ -  + ]:    1317900 :         ul.insert(ul.end(), uprim.begin(), uprim.end());
                 [ -  - ]
     248                 :            : 
     249                 :            :         Assert( ul.size() == ncomp+nprim, "Incorrect size for "
     250                 :            :                 "appended state vector" );
     251                 :            : 
     252                 :            :         // Compute the state at the face-center using BC
     253                 :    1317900 :         auto ustate = state( system, ncomp, ul, fc[0], fc[1], fc[2], t, fn );
     254                 :            : 
     255                 :    1317900 :         std::array< real, 3 > wdeltax{{ fc[0]-geoElem(el,1,0),
     256                 :    1317900 :                                         fc[1]-geoElem(el,2,0),
     257                 :    1317900 :                                         fc[2]-geoElem(el,3,0) }};
     258                 :            : 
     259         [ +  + ]:    5271600 :         for (std::size_t idir=0; idir<3; ++idir)
     260                 :            :         {
     261                 :            :           // rhs vector
     262         [ +  + ]:   10773000 :           for (std::size_t c=varRange[0]; c<=varRange[1]; ++c)
     263                 :    6819300 :             rhs_ls[el][c][idir] +=
     264                 :    6819300 :               wdeltax[idir] * (ustate[1][c]-ustate[0][c]);
     265                 :            :         }
     266                 :            :       }
     267                 :            :     }
     268                 :            :   }
     269                 :      42300 : }
     270                 :            : 
     271                 :            : void
     272                 :       7050 : solveLeastSq_P0P1(
     273                 :            :   ncomp_t offset,
     274                 :            :   const std::size_t rdof,
     275                 :            :   const std::vector< std::array< std::array< real, 3 >, 3 > >& lhs,
     276                 :            :   const std::vector< std::vector< std::array< real, 3 > > >& rhs,
     277                 :            :   Fields& W,
     278                 :            :   const std::array< std::size_t, 2 >& varRange )
     279                 :            : // *****************************************************************************
     280                 :            : //  Solve the 3x3 linear system for least-squares reconstruction
     281                 :            : //! \param[in] offset Offset this PDE system operates from
     282                 :            : //! \param[in] rdof Maximum number of reconstructed degrees of freedom
     283                 :            : //! \param[in] lhs LHS reconstruction matrix
     284                 :            : //! \param[in] rhs RHS reconstruction vector
     285                 :            : //! \param[in,out] W Solution vector to be reconstructed at recent time step
     286                 :            : //! \param[in] varRange Range of indices in W, that need to be reconstructed
     287                 :            : //! \details Solves the 3x3 linear system for each element, individually. For
     288                 :            : //!   systems that require reconstructions of primitive quantities, this should
     289                 :            : //!   be called twice, once with the argument 'W' as U (conserved), and again
     290                 :            : //!   with 'W' as P (primitive).
     291                 :            : // *****************************************************************************
     292                 :            : {
     293                 :       7050 :   auto nelem = lhs.size();
     294                 :            : 
     295         [ +  + ]:    2101200 :   for (std::size_t e=0; e<nelem; ++e)
     296                 :            :   {
     297         [ +  + ]:    6007500 :     for (std::size_t c=varRange[0]; c<=varRange[1]; ++c)
     298                 :            :     {
     299                 :    3913350 :       auto mark = c*rdof;
     300                 :            : 
     301                 :            :       // solve system using Cramer's rule
     302                 :    3913350 :       auto ux = tk::cramer( lhs[e], rhs[e][c] );
     303                 :            : 
     304                 :    3913350 :       W(e,mark+1,offset) = ux[0];
     305                 :    3913350 :       W(e,mark+2,offset) = ux[1];
     306                 :    3913350 :       W(e,mark+3,offset) = ux[2];
     307                 :            :     }
     308                 :            :   }
     309                 :       7050 : }
     310                 :            : 
     311                 :            : void
     312                 :    1137000 : recoLeastSqExtStencil(
     313                 :            :   std::size_t rdof,
     314                 :            :   std::size_t offset,
     315                 :            :   std::size_t e,
     316                 :            :   const std::map< std::size_t, std::vector< std::size_t > >& esup,
     317                 :            :   const std::vector< std::size_t >& inpoel,
     318                 :            :   const Fields& geoElem,
     319                 :            :   Fields& W,
     320                 :            :   const std::array< std::size_t, 2 >& varRange )
     321                 :            : // *****************************************************************************
     322                 :            : //  \brief Reconstruct the second-order solution using least-squares approach
     323                 :            : //    from an extended stencil involving the node-neighbors
     324                 :            : //! \param[in] rdof Maximum number of reconstructed degrees of freedom
     325                 :            : //! \param[in] offset Offset this PDE system operates from
     326                 :            : //! \param[in] e Element whoes solution is being reconstructed
     327                 :            : //! \param[in] esup Elements surrounding points
     328                 :            : //! \param[in] inpoel Element-node connectivity
     329                 :            : //! \param[in] geoElem Element geometry array
     330                 :            : //! \param[in,out] W Solution vector to be reconstructed at recent time step
     331                 :            : //! \param[in] varRange Range of indices in W, that need to be reconstructed
     332                 :            : //! \details A second-order (piecewise linear) solution polynomial is obtained
     333                 :            : //!   from the first-order (piecewise constant) FV solutions by using a
     334                 :            : //!   least-squares (LS) reconstruction process. This LS reconstruction function
     335                 :            : //!   using the nodal-neighbors of a cell, to get an overdetermined system of
     336                 :            : //!   equations for the derivatives of the solution. This overdetermined system
     337                 :            : //!   is solved in the least-squares sense using the normal equations approach.
     338                 :            : // *****************************************************************************
     339                 :            : {
     340                 :            :   // lhs matrix
     341                 :            :   std::array< std::array< tk::real, 3 >, 3 >
     342                 :    1137000 :     lhs_ls( {{ {{0.0, 0.0, 0.0}},
     343                 :            :                {{0.0, 0.0, 0.0}},
     344                 :            :                {{0.0, 0.0, 0.0}} }} );
     345                 :            :   // rhs matrix
     346                 :            :   Assert( varRange[0] <= varRange[1], "Incorrect variable range detected" );
     347                 :            :   std::vector< std::array< tk::real, 3 > >
     348                 :    1137000 :   rhs_ls( varRange[1]-varRange[0]+1, {{ 0.0, 0.0, 0.0 }} );
     349                 :            : 
     350                 :            :   // loop over all nodes of the element e
     351         [ +  + ]:    5685000 :   for (std::size_t lp=0; lp<4; ++lp)
     352                 :            :   {
     353                 :    4548000 :     auto p = inpoel[4*e+lp];
     354                 :            :     const auto& pesup = cref_find(esup, p);
     355                 :            : 
     356                 :            :     // loop over all the elements surrounding this node p
     357         [ +  + ]:   75153000 :     for (auto er : pesup)
     358                 :            :     {
     359                 :            :       // centroid distance
     360                 :   70605000 :       std::array< real, 3 > wdeltax{{ geoElem(er,1,0)-geoElem(e,1,0),
     361                 :   70605000 :                                       geoElem(er,2,0)-geoElem(e,2,0),
     362                 :   70605000 :                                       geoElem(er,3,0)-geoElem(e,3,0) }};
     363                 :            : 
     364                 :            :       // contribute to lhs matrix
     365         [ +  + ]:  282420000 :       for (std::size_t idir=0; idir<3; ++idir)
     366         [ +  + ]:  847260000 :         for (std::size_t jdir=0; jdir<3; ++jdir)
     367                 :  635445000 :           lhs_ls[idir][jdir] += wdeltax[idir] * wdeltax[jdir];
     368                 :            : 
     369                 :            :       // compute rhs matrix
     370         [ +  + ]:  494235000 :       for (std::size_t c=varRange[0]; c<=varRange[1]; ++c)
     371                 :            :       {
     372                 :  423630000 :         auto mark = c*rdof;
     373                 :  423630000 :         auto cmark = c - varRange[0];
     374         [ +  + ]: 1694520000 :         for (std::size_t idir=0; idir<3; ++idir)
     375                 : 1270890000 :           rhs_ls[cmark][idir] +=
     376                 : 1270890000 :             wdeltax[idir] * (W(er,mark,offset)-W(e,mark,offset));
     377                 :            :       }
     378                 :            :     }
     379                 :            :   }
     380                 :            : 
     381                 :            :   // solve least-square normal equation system using Cramer's rule
     382         [ +  + ]:    7959000 :   for (ncomp_t c=varRange[0]; c<=varRange[1]; ++c)
     383                 :            :   {
     384                 :    6822000 :     auto mark = c*rdof;
     385                 :    6822000 :     auto cmark = c - varRange[0];
     386                 :            : 
     387                 :    6822000 :     auto ux = tk::cramer( lhs_ls, rhs_ls[cmark] );
     388                 :            : 
     389                 :            :     // Update the P1 dofs with the reconstructioned gradients.
     390                 :            :     // Since this reconstruction does not affect the cell-averaged solution,
     391                 :            :     // W(e,mark+0,offset) is unchanged.
     392                 :    6822000 :     W(e,mark+1,offset) = ux[0];
     393                 :    6822000 :     W(e,mark+2,offset) = ux[1];
     394                 :    6822000 :     W(e,mark+3,offset) = ux[2];
     395                 :            :   }
     396                 :    1137000 : }
     397                 :            : 
     398                 :            : void
     399                 :      14175 : transform_P0P1( ncomp_t offset,
     400                 :            :                 std::size_t rdof,
     401                 :            :                 std::size_t nelem,
     402                 :            :                 const std::vector< std::size_t >& inpoel,
     403                 :            :                 const UnsMesh::Coords& coord,
     404                 :            :                 Fields& W,
     405                 :            :                 const std::array< std::size_t, 2 >& varRange )
     406                 :            : // *****************************************************************************
     407                 :            : //  Transform the reconstructed P1-derivatives to the Dubiner dofs
     408                 :            : //! \param[in] offset Index for equation systems
     409                 :            : //! \param[in] rdof Total number of reconstructed dofs
     410                 :            : //! \param[in] nelem Total number of elements
     411                 :            : //! \param[in] inpoel Element-node connectivity
     412                 :            : //! \param[in] coord Array of nodal coordinates
     413                 :            : //! \param[in,out] W Second-order reconstructed vector which gets transformed to
     414                 :            : //!   the Dubiner reference space
     415                 :            : //! \param[in] varRange Range of indices in W, that need to be reconstructed
     416                 :            : //! \details Since the DG solution (and the primitive quantities) are assumed to
     417                 :            : //!   be stored in the Dubiner space, this transformation from Taylor
     418                 :            : //!   coefficients to Dubiner coefficients is necessary.
     419                 :            : // *****************************************************************************
     420                 :            : {
     421                 :            :   const auto& cx = coord[0];
     422                 :            :   const auto& cy = coord[1];
     423                 :            :   const auto& cz = coord[2];
     424                 :            : 
     425         [ +  + ]:    3245325 :   for (std::size_t e=0; e<nelem; ++e)
     426                 :            :   {
     427                 :            :     // Extract the element coordinates
     428                 :            :     std::array< std::array< real, 3>, 4 > coordel {{
     429                 :    3231150 :       {{ cx[ inpoel[4*e  ] ], cy[ inpoel[4*e  ] ], cz[ inpoel[4*e  ] ] }},
     430                 :    3231150 :       {{ cx[ inpoel[4*e+1] ], cy[ inpoel[4*e+1] ], cz[ inpoel[4*e+1] ] }},
     431                 :    3231150 :       {{ cx[ inpoel[4*e+2] ], cy[ inpoel[4*e+2] ], cz[ inpoel[4*e+2] ] }},
     432                 :    3231150 :       {{ cx[ inpoel[4*e+3] ], cy[ inpoel[4*e+3] ], cz[ inpoel[4*e+3] ] }}
     433                 :    3231150 :     }};
     434                 :            : 
     435                 :            :     auto jacInv =
     436                 :    3231150 :       tk::inverseJacobian( coordel[0], coordel[1], coordel[2], coordel[3] );
     437                 :            : 
     438                 :            :     // Compute the derivatives of basis function for DG(P1)
     439                 :    3231150 :     auto dBdx = tk::eval_dBdx_p1( rdof, jacInv );
     440                 :            : 
     441         [ +  + ]:   13966500 :     for (ncomp_t c=varRange[0]; c<=varRange[1]; ++c)
     442                 :            :     {
     443                 :   10735350 :       auto mark = c*rdof;
     444                 :            : 
     445                 :            :       // solve system using Cramer's rule
     446                 :   10735350 :       auto ux = tk::cramer( {{ {{dBdx[0][1], dBdx[0][2], dBdx[0][3]}},
     447                 :   10735350 :                                {{dBdx[1][1], dBdx[1][2], dBdx[1][3]}},
     448                 :   10735350 :                                {{dBdx[2][1], dBdx[2][2], dBdx[2][3]}} }},
     449                 :   10735350 :                             {{ W(e,mark+1,offset),
     450                 :   10735350 :                                W(e,mark+2,offset),
     451                 :   10735350 :                                W(e,mark+3,offset) }} );
     452                 :            : 
     453                 :            :       // replace physical derivatives with transformed dofs
     454                 :   10735350 :       W(e,mark+1,offset) = ux[0];
     455                 :   10735350 :       W(e,mark+2,offset) = ux[1];
     456                 :   10735350 :       W(e,mark+3,offset) = ux[2];
     457                 :            :     }
     458                 :            :   }
     459                 :      14175 : }
     460                 :            : 
     461                 :            : void
     462                 :    6205500 : THINCReco( std::size_t system,
     463                 :            :            std::size_t offset,
     464                 :            :            std::size_t rdof,
     465                 :            :            std::size_t nmat,
     466                 :            :            std::size_t e,
     467                 :            :            const std::vector< std::size_t >& inpoel,
     468                 :            :            const UnsMesh::Coords& coord,
     469                 :            :            const Fields& geoElem,
     470                 :            :            const std::array< real, 3 >& ref_xp,
     471                 :            :            const Fields& U,
     472                 :            :            const Fields& P,
     473                 :            :            [[maybe_unused]] const std::vector< real >& vfmin,
     474                 :            :            [[maybe_unused]] const std::vector< real >& vfmax,
     475                 :            :            std::vector< real >& state )
     476                 :            : // *****************************************************************************
     477                 :            : //  Compute THINC reconstructions at quadrature point for multi-material flows
     478                 :            : //! \param[in] system Equation system index
     479                 :            : //! \param[in] offset Index for equation systems
     480                 :            : //! \param[in] rdof Total number of reconstructed dofs
     481                 :            : //! \param[in] nmat Total number of materials
     482                 :            : //! \param[in] e Element for which interface reconstruction is being calculated
     483                 :            : //! \param[in] inpoel Element-node connectivity
     484                 :            : //! \param[in] coord Array of nodal coordinates
     485                 :            : //! \param[in] geoElem Element geometry array
     486                 :            : //! \param[in] ref_xp Quadrature point in reference space
     487                 :            : //! \param[in] U Solution vector
     488                 :            : //! \param[in] P Vector of primitives
     489                 :            : //! \param[in] vfmin Vector containing min volume fractions for each material
     490                 :            : //!   in this cell
     491                 :            : //! \param[in] vfmax Vector containing max volume fractions for each material
     492                 :            : //!   in this cell
     493                 :            : //! \param[in,out] state Unknown/state vector at quadrature point, modified
     494                 :            : //!   if near interfaces using THINC
     495                 :            : //! \details This function is an interface for the multimat PDEs that use the
     496                 :            : //!   algebraic multi-material THINC reconstruction. This particular function
     497                 :            : //!   should only be called for multimat.
     498                 :            : // *****************************************************************************
     499                 :            : {
     500                 :            :   using inciter::volfracDofIdx;
     501                 :            :   using inciter::densityDofIdx;
     502                 :            :   using inciter::momentumDofIdx;
     503                 :            :   using inciter::energyDofIdx;
     504                 :            :   using inciter::pressureDofIdx;
     505                 :            :   using inciter::velocityDofIdx;
     506                 :            :   using inciter::volfracIdx;
     507                 :            :   using inciter::densityIdx;
     508                 :            :   using inciter::momentumIdx;
     509                 :            :   using inciter::energyIdx;
     510                 :            :   using inciter::pressureIdx;
     511                 :            :   using inciter::velocityIdx;
     512                 :            : 
     513                 :            :   auto bparam = inciter::g_inputdeck.get< tag::param, tag::multimat,
     514                 :    6205500 :     tag::intsharp_param >()[system];
     515                 :    6205500 :   const auto ncomp = U.nprop()/rdof;
     516                 :            : 
     517                 :            :   // interface detection
     518                 :    6205500 :   std::vector< std::size_t > matInt(nmat, 0);
     519 [ +  - ][ -  - ]:    6205500 :   std::vector< tk::real > alAvg(nmat, 0.0);
     520         [ +  + ]:   18616500 :   for (std::size_t k=0; k<nmat; ++k)
     521                 :   12411000 :     alAvg[k] = U(e, volfracDofIdx(nmat,k,rdof,0), offset);
     522         [ +  - ]:    6205500 :   auto intInd = inciter::interfaceIndicator(nmat, alAvg, matInt);
     523                 :            : 
     524                 :            :   // Step-1: Perform THINC reconstruction
     525                 :            :   // create a vector of volume-fractions and pass it to the THINC function
     526 [ +  - ][ -  - ]:    6205500 :   std::vector< real > alSol(rdof*nmat, 0.0);
     527 [ +  - ][ -  - ]:    6205500 :   std::vector< real > alReco(nmat, 0.0);
     528         [ +  + ]:   18616500 :   for (std::size_t k=0; k<nmat; ++k) {
     529                 :   12411000 :     auto mark = k*rdof;
     530         [ +  + ]:   62055000 :     for (std::size_t i=0; i<rdof; ++i) {
     531                 :   49644000 :       alSol[mark+i] = U(e, volfracDofIdx(nmat,k,rdof,i), offset);
     532                 :            :     }
     533                 :            :     // initialize with TVD reconstructions which will be modified if near
     534                 :            :     // material interface
     535                 :   12411000 :     alReco[k] = state[volfracIdx(nmat,k)];
     536                 :            :   }
     537         [ +  - ]:    6205500 :   THINCFunction(rdof, nmat, e, inpoel, coord, ref_xp, geoElem(e,0,0), bparam,
     538                 :            :     alSol, intInd, matInt, alReco);
     539                 :            : 
     540                 :            :   // check reconstructed volfracs for positivity
     541                 :            :   bool neg_vf = false;
     542         [ +  + ]:   18616500 :   for (std::size_t k=0; k<nmat; ++k) {
     543         [ -  + ]:   12411000 :     if (alReco[k] < 1e-16) neg_vf = true;
     544                 :            :   }
     545         [ +  + ]:   18616500 :   for (std::size_t k=0; k<nmat; ++k) {
     546         [ -  + ]:   12411000 :     if (neg_vf) {
     547                 :            :       std::cout << "Material-id:        " << k << std::endl;
     548         [ -  - ]:          0 :       std::cout << "Volume-fraction:    " << std::setprecision(18) << alReco[k]
     549                 :            :         << std::endl;
     550         [ -  - ]:          0 :       std::cout << "Cell-avg vol-frac:  " << std::setprecision(18) << alAvg[k]
     551                 :            :         << std::endl;
     552                 :            :       std::cout << "Material-interface? " << intInd << std::endl;
     553         [ -  - ]:          0 :       std::cout << "Mat-k-involved?     " << matInt[k] << std::endl;
     554                 :            :     }
     555                 :            :   }
     556 [ -  + ][ -  - ]:    6205500 :   if (neg_vf) Throw("Material has negative volume fraction after THINC "
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
                 [ -  - ]
     557                 :            :     "reconstruction.");
     558                 :            : 
     559                 :            :   // Step-2: Perform consistent reconstruction on other conserved quantities
     560         [ +  + ]:    6205500 :   if (intInd)
     561                 :            :   {
     562                 :            :     auto rhobCC(0.0), rhobHO(0.0);
     563         [ +  + ]:     877365 :     for (std::size_t k=0; k<nmat; ++k)
     564                 :            :     {
     565                 :     584910 :       auto alCC = U(e, volfracDofIdx(nmat,k,rdof,0), offset);
     566         [ +  - ]:     584910 :       alCC = std::max(1e-14, alCC);
     567                 :            : 
     568         [ +  - ]:     584910 :       if (matInt[k])
     569                 :            :       {
     570                 :     584910 :         state[volfracIdx(nmat,k)] = alReco[k];
     571                 :     584910 :         state[densityIdx(nmat,k)] = alReco[k]
     572                 :     584910 :           * U(e, densityDofIdx(nmat,k,rdof,0), offset)/alCC;
     573                 :     584910 :         state[energyIdx(nmat,k)] = alReco[k]
     574                 :     584910 :           * U(e, energyDofIdx(nmat,k,rdof,0), offset)/alCC;
     575                 :     584910 :         state[ncomp+pressureIdx(nmat,k)] = alReco[k]
     576                 :     584910 :           * P(e, pressureDofIdx(nmat,k,rdof,0), offset)/alCC;
     577                 :            :       }
     578                 :            : 
     579                 :     584910 :       rhobCC += U(e, densityDofIdx(nmat,k,rdof,0), offset);
     580                 :     584910 :       rhobHO += state[densityIdx(nmat,k)];
     581                 :            :     }
     582                 :            : 
     583                 :            :     // consistent reconstruction for bulk momentum
     584         [ +  + ]:    1169820 :     for (std::size_t i=0; i<3; ++i)
     585                 :            :     {
     586                 :     877365 :       state[momentumIdx(nmat,i)] = rhobHO
     587                 :     877365 :         * U(e, momentumDofIdx(nmat,i,rdof,0), offset)/rhobCC;
     588                 :     877365 :       state[ncomp+velocityIdx(nmat,i)] =
     589                 :     877365 :         P(e, velocityDofIdx(nmat,i,rdof,0), offset);
     590                 :            :     }
     591                 :            :   }
     592                 :    6205500 : }
     593                 :            : 
     594                 :            : void
     595                 :          0 : THINCRecoTransport( std::size_t system,
     596                 :            :                     std::size_t offset,
     597                 :            :                     std::size_t rdof,
     598                 :            :                     std::size_t,
     599                 :            :                     std::size_t e,
     600                 :            :                     const std::vector< std::size_t >& inpoel,
     601                 :            :                     const UnsMesh::Coords& coord,
     602                 :            :                     const Fields& geoElem,
     603                 :            :                     const std::array< real, 3 >& ref_xp,
     604                 :            :                     const Fields& U,
     605                 :            :                     const Fields&,
     606                 :            :                     [[maybe_unused]] const std::vector< real >& vfmin,
     607                 :            :                     [[maybe_unused]] const std::vector< real >& vfmax,
     608                 :            :                     std::vector< real >& state )
     609                 :            : // *****************************************************************************
     610                 :            : //  Compute THINC reconstructions at quadrature point for transport
     611                 :            : //! \param[in] system Equation system index
     612                 :            : //! \param[in] offset Index for equation systems
     613                 :            : //! \param[in] rdof Total number of reconstructed dofs
     614                 :            : //! \param[in] e Element for which interface reconstruction is being calculated
     615                 :            : //! \param[in] inpoel Element-node connectivity
     616                 :            : //! \param[in] coord Array of nodal coordinates
     617                 :            : //! \param[in] geoElem Element geometry array
     618                 :            : //! \param[in] ref_xp Quadrature point in reference space
     619                 :            : //! \param[in] U Solution vector
     620                 :            : //! \param[in] vfmin Vector containing min volume fractions for each material
     621                 :            : //!   in this cell
     622                 :            : //! \param[in] vfmax Vector containing max volume fractions for each material
     623                 :            : //!   in this cell
     624                 :            : //! \param[in,out] state Unknown/state vector at quadrature point, modified
     625                 :            : //!   if near interfaces using THINC
     626                 :            : //! \details This function is an interface for the transport PDEs that use the
     627                 :            : //!   algebraic multi-material THINC reconstruction. This particular function
     628                 :            : //!   should only be called for transport.
     629                 :            : // *****************************************************************************
     630                 :            : {
     631                 :            :   auto bparam = inciter::g_inputdeck.get< tag::param, tag::transport,
     632                 :          0 :     tag::intsharp_param >()[system];
     633                 :          0 :   auto ncomp = U.nprop()/rdof;
     634                 :            : 
     635                 :            :   // interface detection
     636                 :          0 :   std::vector< std::size_t > matInt(ncomp, 0);
     637 [ -  - ][ -  - ]:          0 :   std::vector< tk::real > alAvg(ncomp, 0.0);
     638         [ -  - ]:          0 :   for (std::size_t k=0; k<ncomp; ++k)
     639                 :          0 :     alAvg[k] = U(e, k*rdof, offset);
     640         [ -  - ]:          0 :   auto intInd = inciter::interfaceIndicator(ncomp, alAvg, matInt);
     641                 :            : 
     642                 :            :   // create a vector of volume-fractions and pass it to the THINC function
     643 [ -  - ][ -  - ]:          0 :   std::vector< real > alSol(rdof*ncomp, 0.0);
     644                 :            :   // initialize with TVD reconstructions (modified if near interface)
     645         [ -  - ]:          0 :   auto alReco = state;
     646         [ -  - ]:          0 :   for (std::size_t k=0; k<ncomp; ++k) {
     647                 :          0 :     auto mark = k*rdof;
     648         [ -  - ]:          0 :     for (std::size_t i=0; i<rdof; ++i) {
     649                 :          0 :       alSol[mark+i] = U(e,mark+i,offset);
     650                 :            :     }
     651                 :            :   }
     652         [ -  - ]:          0 :   THINCFunction(rdof, ncomp, e, inpoel, coord, ref_xp, geoElem(e,0,0), bparam,
     653                 :            :     alSol, intInd, matInt, alReco);
     654                 :            : 
     655         [ -  - ]:          0 :   state = alReco;
     656                 :          0 : }
     657                 :            : 
     658                 :            : void
     659                 :    6205500 : THINCFunction( std::size_t rdof,
     660                 :            :                std::size_t nmat,
     661                 :            :                std::size_t e,
     662                 :            :                const std::vector< std::size_t >& inpoel,
     663                 :            :                const UnsMesh::Coords& coord,
     664                 :            :                const std::array< real, 3 >& ref_xp,
     665                 :            :                real vol,
     666                 :            :                real bparam,
     667                 :            :                const std::vector< real >& alSol,
     668                 :            :                bool intInd,
     669                 :            :                const std::vector< std::size_t >& matInt,
     670                 :            :                std::vector< real >& alReco )
     671                 :            : // *****************************************************************************
     672                 :            : //  Old version of the Multi-Medium THINC reconstruction function
     673                 :            : //! \param[in] rdof Total number of reconstructed dofs
     674                 :            : //! \param[in] nmat Total number of materials
     675                 :            : //! \param[in] e Element for which interface reconstruction is being calculated
     676                 :            : //! \param[in] inpoel Element-node connectivity
     677                 :            : //! \param[in] coord Array of nodal coordinates
     678                 :            : //! \param[in] ref_xp Quadrature point in reference space
     679                 :            : //! \param[in] vol Element volume
     680                 :            : //! \param[in] bparam User specified Beta for THINC, from the input file
     681                 :            : //! \param[in] alSol Volume fraction solution vector for element e
     682                 :            : //! \param[in] intInd Interface indicator, true if e is interface element
     683                 :            : //! \param[in] matInt Vector indicating materials which constitute interface
     684                 :            : //! \param[in,out] alReco Unknown/state vector at quadrature point, which gets
     685                 :            : //!   modified if near interface using MM-THINC
     686                 :            : //! \details This function computes the interface reconstruction using the
     687                 :            : //!   algebraic multi-material THINC reconstruction for each material at the
     688                 :            : //!   given (ref_xp) quadrature point. This function is based on the following:
     689                 :            : //!   Pandare A. K., Waltz J., & Bakosi J. (2021) Multi-Material Hydrodynamics
     690                 :            : //!   with Algebraic Sharp Interface Capturing. Computers & Fluids,
     691                 :            : //!   doi: https://doi.org/10.1016/j.compfluid.2020.104804.
     692                 :            : //!   This function will be removed after the newer version (see
     693                 :            : //!   THINCFunction_new) is sufficiently tested.
     694                 :            : // *****************************************************************************
     695                 :            : {
     696                 :            :   // determine number of materials with interfaces in this cell
     697                 :    6205500 :   auto epsl(1e-4), epsh(1e-1), bred(1.25), bmod(bparam);
     698                 :            :   std::size_t nIntMat(0);
     699         [ +  + ]:   18616500 :   for (std::size_t k=0; k<nmat; ++k)
     700                 :            :   {
     701         [ +  + ]:   12411000 :     auto alk = alSol[k*rdof];
     702         [ +  + ]:   12411000 :     if (alk > epsl)
     703                 :            :     {
     704                 :    6447375 :       ++nIntMat;
     705         [ +  + ]:    6447375 :       if ((alk > epsl) && (alk < epsh))
     706                 :     181935 :         bmod = std::min(bmod,
     707         [ +  + ]:     298170 :           (alk-epsl)/(epsh-epsl) * (bred - bparam) + bparam);
     708         [ +  - ]:    6265440 :       else if (alk > epsh)
     709                 :    6265440 :         bmod = bred;
     710                 :            :     }
     711                 :            :   }
     712                 :            : 
     713         [ -  + ]:    6205500 :   if (nIntMat > 2) bparam = bmod;
     714                 :            : 
     715                 :            :   // compression parameter
     716                 :    6205500 :   auto beta = bparam/std::cbrt(6.0*vol);
     717                 :            : 
     718         [ +  + ]:    6205500 :   if (intInd)
     719                 :            :   {
     720                 :            :     // 1. Get unit normals to material interface
     721                 :            : 
     722                 :            :     // Compute Jacobian matrix for converting Dubiner dofs to derivatives
     723                 :            :     const auto& cx = coord[0];
     724                 :            :     const auto& cy = coord[1];
     725                 :            :     const auto& cz = coord[2];
     726                 :            : 
     727                 :            :     std::array< std::array< real, 3>, 4 > coordel {{
     728                 :     292455 :       {{ cx[ inpoel[4*e  ] ], cy[ inpoel[4*e  ] ], cz[ inpoel[4*e  ] ] }},
     729                 :     292455 :       {{ cx[ inpoel[4*e+1] ], cy[ inpoel[4*e+1] ], cz[ inpoel[4*e+1] ] }},
     730                 :     292455 :       {{ cx[ inpoel[4*e+2] ], cy[ inpoel[4*e+2] ], cz[ inpoel[4*e+2] ] }},
     731                 :     292455 :       {{ cx[ inpoel[4*e+3] ], cy[ inpoel[4*e+3] ], cz[ inpoel[4*e+3] ] }}
     732                 :     292455 :     }};
     733                 :            : 
     734                 :            :     auto jacInv =
     735                 :     292455 :       tk::inverseJacobian( coordel[0], coordel[1], coordel[2], coordel[3] );
     736                 :            : 
     737         [ +  - ]:     292455 :     auto dBdx = tk::eval_dBdx_p1( rdof, jacInv );
     738                 :            : 
     739                 :            :     std::array< real, 3 > nInt;
     740 [ -  + ][ -  - ]:     292455 :     std::vector< std::array< real, 3 > > ref_n(nmat, {{0.0, 0.0, 0.0}});
     741                 :            : 
     742                 :            :     // Get normals
     743         [ +  + ]:     877365 :     for (std::size_t k=0; k<nmat; ++k)
     744                 :            :     {
     745                 :            :       // Get derivatives from moments in Dubiner space
     746         [ +  + ]:    2339640 :       for (std::size_t i=0; i<3; ++i)
     747                 :    1754730 :         nInt[i] = dBdx[i][1] * alSol[k*rdof+1]
     748                 :    1754730 :           + dBdx[i][2] * alSol[k*rdof+2]
     749                 :    1754730 :           + dBdx[i][3] * alSol[k*rdof+3];
     750                 :            : 
     751                 :     584910 :       auto nMag = std::sqrt(tk::dot(nInt, nInt)) + 1e-14;
     752                 :            : 
     753         [ +  + ]:    2339640 :       for (std::size_t i=0; i<3; ++i)
     754                 :    1754730 :         nInt[i] /= nMag;
     755                 :            : 
     756                 :            :       // project interface normal onto local/reference coordinate system
     757         [ +  + ]:    2339640 :       for (std::size_t i=0; i<3; ++i)
     758                 :            :       {
     759                 :            :         std::array< real, 3 > axis{
     760                 :    1754730 :           coordel[i+1][0]-coordel[0][0],
     761                 :    1754730 :           coordel[i+1][1]-coordel[0][1],
     762                 :    1754730 :           coordel[i+1][2]-coordel[0][2] };
     763                 :    1754730 :         ref_n[k][i] = tk::dot(nInt, axis);
     764                 :            :       }
     765                 :            :     }
     766                 :            : 
     767                 :            :     // 2. Reconstruct volume fractions using THINC
     768                 :     292455 :     auto max_lim = 1.0 - (static_cast<tk::real>(nmat-1)*1.0e-12);
     769                 :     292455 :     auto min_lim = 1e-12;
     770                 :            :     auto sum_inter(0.0), sum_non_inter(0.0);
     771         [ +  + ]:     877365 :     for (std::size_t k=0; k<nmat; ++k)
     772                 :            :     {
     773         [ +  - ]:     584910 :       if (matInt[k])
     774                 :            :       {
     775                 :            :         // get location of material interface (volume fraction 0.5) from the
     776                 :            :         // assumed tanh volume fraction distribution, and cell-averaged
     777                 :            :         // volume fraction
     778         [ +  + ]:     584910 :         auto alCC(alSol[k*rdof]);
     779                 :            :         auto Ac(0.0), Bc(0.0), Qc(0.0);
     780         [ +  + ]:     584910 :         if ((std::abs(ref_n[k][0]) > std::abs(ref_n[k][1]))
     781 [ +  + ][ +  + ]:     584910 :           && (std::abs(ref_n[k][0]) > std::abs(ref_n[k][2])))
     782                 :            :         {
     783                 :     239496 :           Ac = std::exp(0.5*beta*ref_n[k][0]);
     784                 :     239496 :           Bc = std::exp(0.5*beta*(ref_n[k][1]+ref_n[k][2]));
     785                 :     239496 :           Qc = std::exp(0.5*beta*ref_n[k][0]*(2.0*alCC-1.0));
     786                 :            :         }
     787                 :            :         else if ((std::abs(ref_n[k][1]) > std::abs(ref_n[k][0]))
     788 [ +  + ][ +  + ]:     345414 :           && (std::abs(ref_n[k][1]) > std::abs(ref_n[k][2])))
     789                 :            :         {
     790                 :     256746 :           Ac = std::exp(0.5*beta*ref_n[k][1]);
     791                 :     256746 :           Bc = std::exp(0.5*beta*(ref_n[k][0]+ref_n[k][2]));
     792                 :     256746 :           Qc = std::exp(0.5*beta*ref_n[k][1]*(2.0*alCC-1.0));
     793                 :            :         }
     794                 :            :         else
     795                 :            :         {
     796                 :      88668 :           Ac = std::exp(0.5*beta*ref_n[k][2]);
     797                 :      88668 :           Bc = std::exp(0.5*beta*(ref_n[k][0]+ref_n[k][1]));
     798                 :      88668 :           Qc = std::exp(0.5*beta*ref_n[k][2]*(2.0*alCC-1.0));
     799                 :            :         }
     800                 :     584910 :         auto d = std::log((1.0-Ac*Qc) / (Ac*Bc*(Qc-Ac))) / (2.0*beta);
     801                 :            : 
     802                 :            :         // THINC reconstruction
     803         [ +  - ]:     584910 :         auto al_c = 0.5 * (1.0 + std::tanh(beta*(tk::dot(ref_n[k], ref_xp) + d)));
     804                 :            : 
     805                 :     584910 :         alReco[k] = std::min(max_lim, std::max(min_lim, al_c));
     806                 :            : 
     807                 :     584910 :         sum_inter += alReco[k];
     808                 :            :       } else
     809                 :            :       {
     810                 :          0 :         sum_non_inter += alReco[k];
     811                 :            :       }
     812                 :            :       // else, if this material does not have an interface close-by, the TVD
     813                 :            :       // reconstructions must be used for state variables. This is ensured by
     814                 :            :       // initializing the alReco vector as the TVD state.
     815                 :            :     }
     816                 :            : 
     817                 :            :     // Rescale volume fractions of interface-materials to ensure unit sum
     818                 :     292455 :     auto sum_rest = 1.0 - sum_non_inter;
     819         [ +  + ]:     877365 :     for (std::size_t k=0; k<nmat; ++k)
     820         [ +  - ]:     584910 :       if(matInt[k])
     821                 :     584910 :         alReco[k] = alReco[k] * sum_rest / sum_inter;
     822                 :            :   }
     823                 :    6205500 : }
     824                 :            : 
     825                 :            : void
     826                 :          0 : THINCFunction_new( std::size_t rdof,
     827                 :            :                    std::size_t nmat,
     828                 :            :                    std::size_t e,
     829                 :            :                    const std::vector< std::size_t >& inpoel,
     830                 :            :                    const UnsMesh::Coords& coord,
     831                 :            :                    const std::array< real, 3 >& ref_xp,
     832                 :            :                    real vol,
     833                 :            :                    real bparam,
     834                 :            :                    const std::vector< real >& alSol,
     835                 :            :                    bool intInd,
     836                 :            :                    const std::vector< std::size_t >& matInt,
     837                 :            :                    std::vector< real >& alReco )
     838                 :            : // *****************************************************************************
     839                 :            : //  New Multi-Medium THINC reconstruction function for volume fractions
     840                 :            : //! \param[in] rdof Total number of reconstructed dofs
     841                 :            : //! \param[in] nmat Total number of materials
     842                 :            : //! \param[in] e Element for which interface reconstruction is being calculated
     843                 :            : //! \param[in] inpoel Element-node connectivity
     844                 :            : //! \param[in] coord Array of nodal coordinates
     845                 :            : //! \param[in] ref_xp Quadrature point in reference space
     846                 :            : //! \param[in] vol Element volume
     847                 :            : //! \param[in] bparam User specified Beta for THINC, from the input file
     848                 :            : //! \param[in] alSol Volume fraction solution vector for element e
     849                 :            : //! \param[in] intInd Interface indicator, true if e is interface element
     850                 :            : //! \param[in] matInt Vector indicating materials which constitute interface
     851                 :            : //! \param[in,out] alReco Unknown/state vector at quadrature point, which gets
     852                 :            : //!   modified if near interface using MM-THINC
     853                 :            : //! \details This function computes the interface reconstruction using the
     854                 :            : //!   algebraic multi-material THINC reconstruction for each material at the
     855                 :            : //!   given (ref_xp) quadrature point. This function succeeds the older version
     856                 :            : //!   of the mm-THINC (see THINCFunction), but is still under testing and is
     857                 :            : //!   currently experimental.
     858                 :            : // *****************************************************************************
     859                 :            : {
     860                 :            :   // compression parameter
     861                 :          0 :   auto beta = bparam/std::cbrt(6.0*vol);
     862                 :            : 
     863                 :            :   // If the cell is not material interface, return this function
     864         [ -  - ]:          0 :   if (not intInd) return;
     865                 :            : 
     866                 :            :   // If the cell is material interface, THINC reconstruction is applied
     867                 :            :   // Step 1. Get unit normals to material interface
     868                 :            :   // -------------------------------------------------------------------------
     869                 :            : 
     870                 :            :   // Compute Jacobian matrix for converting Dubiner dofs to derivatives
     871                 :            :   const auto& cx = coord[0];
     872                 :            :   const auto& cy = coord[1];
     873                 :            :   const auto& cz = coord[2];
     874                 :            : 
     875                 :            :   std::array< std::array< real, 3>, 4 > coordel {{
     876                 :          0 :     {{ cx[ inpoel[4*e  ] ], cy[ inpoel[4*e  ] ], cz[ inpoel[4*e  ] ] }},
     877                 :          0 :     {{ cx[ inpoel[4*e+1] ], cy[ inpoel[4*e+1] ], cz[ inpoel[4*e+1] ] }},
     878                 :          0 :     {{ cx[ inpoel[4*e+2] ], cy[ inpoel[4*e+2] ], cz[ inpoel[4*e+2] ] }},
     879                 :          0 :     {{ cx[ inpoel[4*e+3] ], cy[ inpoel[4*e+3] ], cz[ inpoel[4*e+3] ] }}
     880                 :          0 :   }};
     881                 :            : 
     882                 :            :   auto jacInv =
     883                 :          0 :     tk::inverseJacobian( coordel[0], coordel[1], coordel[2], coordel[3] );
     884                 :            : 
     885                 :          0 :   auto dBdx = tk::eval_dBdx_p1( rdof, jacInv );
     886                 :            : 
     887                 :            :   std::array< real, 3 > nInt;
     888                 :          0 :   std::array< real, 3 > ref_n{0.0, 0.0, 0.0};
     889                 :            :   auto almax(0.0);
     890                 :            :   std::size_t kmax(0);
     891                 :            : 
     892                 :            :   // Determine index of material present in majority
     893         [ -  - ]:          0 :   for (std::size_t k=0; k<nmat; ++k)
     894                 :            :   {
     895         [ -  - ]:          0 :     auto alk = alSol[k*rdof];
     896         [ -  - ]:          0 :     if (alk > almax)
     897                 :            :     {
     898                 :            :       almax = alk;
     899                 :            :       kmax = k;
     900                 :            :     }
     901                 :            :   }
     902                 :            : 
     903                 :            :   // Get normals of material present in majority
     904                 :            :   // Get derivatives from moments in Dubiner space
     905         [ -  - ]:          0 :   for (std::size_t i=0; i<3; ++i)
     906                 :          0 :     nInt[i] = dBdx[i][1] * alSol[kmax*rdof+1]
     907                 :          0 :       + dBdx[i][2] * alSol[kmax*rdof+2]
     908                 :          0 :       + dBdx[i][3] * alSol[kmax*rdof+3];
     909                 :            : 
     910                 :          0 :   auto nMag = std::sqrt(tk::dot(nInt, nInt)) + 1e-14;
     911                 :            : 
     912         [ -  - ]:          0 :   for (std::size_t i=0; i<3; ++i)
     913                 :          0 :     nInt[i] /= nMag;
     914                 :            : 
     915                 :            :   // project interface normal onto local/reference coordinate system
     916         [ -  - ]:          0 :   for (std::size_t i=0; i<3; ++i)
     917                 :            :   {
     918                 :            :     std::array< real, 3 > axis{
     919                 :          0 :       coordel[i+1][0]-coordel[0][0],
     920                 :          0 :       coordel[i+1][1]-coordel[0][1],
     921                 :          0 :       coordel[i+1][2]-coordel[0][2] };
     922                 :          0 :     ref_n[i] = tk::dot(nInt, axis);
     923                 :            :   }
     924                 :            : 
     925                 :            :   // Step 2. Reconstruct volume fraction of majority material using THINC
     926                 :            :   // -------------------------------------------------------------------------
     927                 :            : 
     928                 :          0 :   auto al_max = 1.0 - (static_cast<tk::real>(nmat-1)*1.0e-12);
     929                 :          0 :   auto al_min = 1e-12;
     930                 :            :   auto alsum(0.0);
     931                 :            :   // get location of material interface (volume fraction 0.5) from the
     932                 :            :   // assumed tanh volume fraction distribution, and cell-averaged
     933                 :            :   // volume fraction
     934         [ -  - ]:          0 :   auto alCC(alSol[kmax*rdof]);
     935                 :            :   auto Ac(0.0), Bc(0.0), Qc(0.0);
     936         [ -  - ]:          0 :   if ((std::abs(ref_n[0]) > std::abs(ref_n[1]))
     937 [ -  - ][ -  - ]:          0 :     && (std::abs(ref_n[0]) > std::abs(ref_n[2])))
     938                 :            :   {
     939                 :          0 :     Ac = std::exp(0.5*beta*ref_n[0]);
     940                 :          0 :     Bc = std::exp(0.5*beta*(ref_n[1]+ref_n[2]));
     941                 :          0 :     Qc = std::exp(0.5*beta*ref_n[0]*(2.0*alCC-1.0));
     942                 :            :   }
     943                 :            :   else if ((std::abs(ref_n[1]) > std::abs(ref_n[0]))
     944 [ -  - ][ -  - ]:          0 :     && (std::abs(ref_n[1]) > std::abs(ref_n[2])))
     945                 :            :   {
     946                 :          0 :     Ac = std::exp(0.5*beta*ref_n[1]);
     947                 :          0 :     Bc = std::exp(0.5*beta*(ref_n[0]+ref_n[2]));
     948                 :          0 :     Qc = std::exp(0.5*beta*ref_n[1]*(2.0*alCC-1.0));
     949                 :            :   }
     950                 :            :   else
     951                 :            :   {
     952                 :          0 :     Ac = std::exp(0.5*beta*ref_n[2]);
     953                 :          0 :     Bc = std::exp(0.5*beta*(ref_n[0]+ref_n[1]));
     954                 :          0 :     Qc = std::exp(0.5*beta*ref_n[2]*(2.0*alCC-1.0));
     955                 :            :   }
     956         [ -  - ]:          0 :   auto d = std::log((1.0-Ac*Qc) / (Ac*Bc*(Qc-Ac))) / (2.0*beta);
     957                 :            : 
     958                 :            :   // THINC reconstruction
     959         [ -  - ]:          0 :   auto al_c = 0.5 * (1.0 + std::tanh(beta*(tk::dot(ref_n, ref_xp) + d)));
     960                 :            : 
     961                 :          0 :   alReco[kmax] = std::min(al_max, std::max(al_min, al_c));
     962                 :          0 :   alsum += alReco[kmax];
     963                 :            : 
     964                 :            :   // if this material does not have an interface close-by, the TVD
     965                 :            :   // reconstructions must be used for state variables. This is ensured by
     966                 :            :   // initializing the alReco vector as the TVD state.
     967         [ -  - ]:          0 :   for (std::size_t k=0; k<nmat; ++k) {
     968         [ -  - ]:          0 :     if (!matInt[k]) {
     969                 :          0 :       alsum += alReco[k];
     970                 :            :     }
     971                 :            :   }
     972                 :            : 
     973                 :            :   // Step 3. Do multimaterial cell corrections
     974                 :            :   // -------------------------------------------------------------------------
     975                 :            : 
     976                 :            :   // distribute remaining volume to rest of materials
     977                 :          0 :   auto sum_left = 1.0 - alsum;
     978                 :            :   real den = 0.0;
     979         [ -  - ]:          0 :   for (std::size_t k=0; k<nmat; ++k) {
     980 [ -  - ][ -  - ]:          0 :     if (matInt[k] && k != kmax) {
     981                 :          0 :       auto mark = k * rdof;
     982                 :          0 :       alReco[k] = sum_left * alSol[mark];
     983                 :          0 :       den += alSol[mark];
     984                 :            :     }
     985                 :            :   }
     986                 :            :   // the distributed volfracs might be below al_min, correct that
     987                 :            :   real err = 0.0;
     988         [ -  - ]:          0 :   for (std::size_t k=0; k<nmat; ++k) {
     989 [ -  - ][ -  - ]:          0 :     if (matInt[k] && k != kmax) {
     990                 :          0 :       alReco[k] /= den;
     991         [ -  - ]:          0 :       if (alReco[k] < al_min) {
     992                 :          0 :         err += al_min - alReco[k];
     993                 :          0 :         alReco[k] = al_min;
     994                 :            :       }
     995                 :            :     }
     996                 :            :   }
     997                 :            : 
     998                 :            :   // balance out errors
     999                 :          0 :   alReco[kmax] -= err;
    1000                 :            : }
    1001                 :            : 
    1002                 :            : std::vector< tk::real >
    1003         [ +  - ]:  263924622 : evalPolynomialSol( std::size_t system,
    1004                 :            :                    std::size_t offset,
    1005                 :            :                    int intsharp,
    1006                 :            :                    std::size_t ncomp,
    1007                 :            :                    std::size_t nprim,
    1008                 :            :                    std::size_t rdof,
    1009                 :            :                    std::size_t nmat,
    1010                 :            :                    std::size_t e,
    1011                 :            :                    std::size_t dof_e,
    1012                 :            :                    const std::vector< std::size_t >& inpoel,
    1013                 :            :                    const UnsMesh::Coords& coord,
    1014                 :            :                    const Fields& geoElem,
    1015                 :            :                    const std::array< real, 3 >& ref_gp,
    1016                 :            :                    const std::vector< real >& B,
    1017                 :            :                    const Fields& U,
    1018                 :            :                    const Fields& P )
    1019                 :            : // *****************************************************************************
    1020                 :            : //  Evaluate polynomial solution at quadrature point
    1021                 :            : //! \param[in] system Equation system index
    1022                 :            : //! \param[in] offset Index for equation systems
    1023                 :            : //! \param[in] intsharp Interface reconstruction indicator
    1024                 :            : //! \param[in] ncomp Number of components in the PDE system
    1025                 :            : //! \param[in] nprim Number of primitive quantities
    1026                 :            : //! \param[in] rdof Total number of reconstructed dofs
    1027                 :            : //! \param[in] nmat Total number of materials
    1028                 :            : //! \param[in] e Element for which polynomial solution is being evaluated
    1029                 :            : //! \param[in] dof_e Degrees of freedom for element
    1030                 :            : //! \param[in] inpoel Element-node connectivity
    1031                 :            : //! \param[in] coord Array of nodal coordinates
    1032                 :            : //! \param[in] geoElem Element geometry array
    1033                 :            : //! \param[in] ref_gp Quadrature point in reference space
    1034                 :            : //! \param[in] B Basis function at given quadrature point
    1035                 :            : //! \param[in] U Solution vector
    1036                 :            : //! \param[in] P Vector of primitives
    1037                 :            : //! \return High-order unknown/state vector at quadrature point, modified
    1038                 :            : //!   if near interfaces using THINC
    1039                 :            : // *****************************************************************************
    1040                 :            : {
    1041                 :            :   std::vector< real > state;
    1042                 :            :   std::vector< real > sprim;
    1043                 :            : 
    1044         [ +  - ]:  263924622 :   state = eval_state( ncomp, offset, rdof, dof_e, e, U, B, {0, ncomp-1} );
    1045 [ +  - ][ +  - ]:  263924622 :   sprim = eval_state( nprim, offset, rdof, dof_e, e, P, B, {0, nprim-1} );
    1046                 :            : 
    1047                 :            :   // consolidate primitives into state vector
    1048         [ +  - ]:  263924622 :   state.insert(state.end(), sprim.begin(), sprim.end());
    1049                 :            : 
    1050         [ +  + ]:  263924622 :   if (intsharp > 0)
    1051                 :            :   {
    1052 [ +  - ][ +  - ]:    6205500 :     std::vector< tk::real > vfmax(nmat, 0.0), vfmin(nmat, 0.0);
         [ -  - ][ -  - ]
    1053                 :            : 
    1054                 :            :     // Until the appropriate setup for activating THINC with Transport
    1055                 :            :     // is ready, the following two chunks of code will need to be commented
    1056                 :            :     // for using THINC with Transport
    1057                 :            :     //for (std::size_t k=0; k<nmat; ++k) {
    1058                 :            :     //  vfmin[k] = VolFracMax(el, 2*k, 0);
    1059                 :            :     //  vfmax[k] = VolFracMax(el, 2*k+1, 0);
    1060                 :            :     //}
    1061         [ +  - ]:    6205500 :     tk::THINCReco(system, offset, rdof, nmat, e, inpoel, coord, geoElem,
    1062                 :            :       ref_gp, U, P, vfmin, vfmax, state);
    1063                 :            : 
    1064                 :            :     // Until the appropriate setup for activating THINC with Transport
    1065                 :            :     // is ready, the following lines will need to be uncommented for
    1066                 :            :     // using THINC with Transport
    1067                 :            :     //tk::THINCRecoTransport(system, offset, rdof, nmat, el, inpoel, coord,
    1068                 :            :     //  geoElem, ref_gp_l, U, P, vfmin, vfmax, state[0]);
    1069                 :            :   }
    1070                 :            : 
    1071                 :            :   // physical constraints
    1072         [ +  + ]:  263924622 :   if (nmat > 1) {
    1073                 :            :     using inciter::pressureIdx;
    1074                 :            :     using inciter::volfracIdx;
    1075                 :            : 
    1076         [ +  + ]:   67036050 :     for (std::size_t k=0; k<nmat; ++k) {
    1077                 :   46523100 :       state[ncomp+pressureIdx(nmat,k)] = inciter::constrain_pressure
    1078                 :   46523100 :         < tag::multimat >(system, state[ncomp+pressureIdx(nmat,k)],
    1079                 :   46523100 :         state[volfracIdx(nmat,k)], k);
    1080                 :            :     }
    1081                 :            :   }
    1082                 :            : 
    1083                 :  263924622 :   return state;
    1084                 :            : }
    1085                 :            : 
    1086                 :            : void
    1087                 :          0 : safeReco( std::size_t offset,
    1088                 :            :           std::size_t rdof,
    1089                 :            :           std::size_t nmat,
    1090                 :            :           std::size_t el,
    1091                 :            :           int er,
    1092                 :            :           const Fields& U,
    1093                 :            :           std::array< std::vector< real >, 2 >& state )
    1094                 :            : // *****************************************************************************
    1095                 :            : //  Compute safe reconstructions near material interfaces
    1096                 :            : //! \param[in] offset Index for equation systems
    1097                 :            : //! \param[in] rdof Total number of reconstructed dofs
    1098                 :            : //! \param[in] nmat Total number of material is PDE system
    1099                 :            : //! \param[in] el Element on the left-side of face
    1100                 :            : //! \param[in] er Element on the right-side of face
    1101                 :            : //! \param[in] U Solution vector at recent time-stage
    1102                 :            : //! \param[in,out] state Second-order reconstructed state, at cell-face, that
    1103                 :            : //!   is being modified for safety
    1104                 :            : //! \details When the consistent limiting is applied, there is a possibility
    1105                 :            : //!   that the material densities and energies violate TVD bounds. This function
    1106                 :            : //!   enforces the TVD bounds locally
    1107                 :            : // *****************************************************************************
    1108                 :            : {
    1109                 :            :   using inciter::densityIdx;
    1110                 :            :   using inciter::densityDofIdx;
    1111                 :            : 
    1112 [ -  - ][ -  - ]:          0 :   if (er < 0) Throw("safe limiting cannot be called for boundary cells");
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
                 [ -  - ]
    1113                 :            : 
    1114                 :          0 :   auto eR = static_cast< std::size_t >(er);
    1115                 :            : 
    1116                 :            :   // define a lambda for the safe limiting
    1117                 :          0 :   auto safeLimit = [&]( std::size_t c, real ul, real ur )
    1118                 :            :   {
    1119                 :            :     // find min/max at the face
    1120         [ -  - ]:          0 :     auto uMin = std::min(ul, ur);
    1121                 :          0 :     auto uMax = std::max(ul, ur);
    1122                 :            :     auto uNeg(0.0);
    1123                 :            : 
    1124                 :            :     // left-state limiting
    1125         [ -  - ]:          0 :     uNeg = state[0][c] - ul;
    1126 [ -  - ][ -  - ]:          0 :     if ((state[0][c] < ul) && (state[0][c] < ur) && (uNeg < -1e-2*ul))
                 [ -  - ]
    1127                 :            :     {
    1128                 :          0 :       state[0][c] = uMin;
    1129                 :            :     }
    1130 [ -  - ][ -  - ]:          0 :     else if ((state[0][c] > ul) && (state[0][c] > ur) && (uNeg > 1e-2*ul))
                 [ -  - ]
    1131                 :            :     {
    1132                 :          0 :       state[0][c] = uMax;
    1133                 :            :     }
    1134                 :            : 
    1135                 :            :     // right-state limiting
    1136                 :          0 :     uNeg = state[0][c] - ur;
    1137 [ -  - ][ -  - ]:          0 :     if ((state[1][c] < ul) && (state[1][c] < ur) && (uNeg < -1e-2*ur))
                 [ -  - ]
    1138                 :            :     {
    1139                 :          0 :       state[1][c] = uMin;
    1140                 :            :     }
    1141 [ -  - ][ -  - ]:          0 :     else if ((state[1][c] > ul) && (state[1][c] > ur) && (uNeg > 1e-2*ur))
                 [ -  - ]
    1142                 :            :     {
    1143                 :          0 :       state[1][c] = uMax;
    1144                 :            :     }
    1145                 :          0 :   };
    1146                 :            : 
    1147         [ -  - ]:          0 :   for (std::size_t k=0; k<nmat; ++k)
    1148                 :            :   {
    1149                 :            :     real ul(0.0), ur(0.0);
    1150                 :            : 
    1151                 :            :     // establish left- and right-hand states
    1152                 :          0 :     ul = U(el, densityDofIdx(nmat, k, rdof, 0), offset);
    1153                 :          0 :     ur = U(eR, densityDofIdx(nmat, k, rdof, 0), offset);
    1154                 :            : 
    1155                 :            :     // limit reconstructed density
    1156                 :          0 :     safeLimit(densityIdx(nmat,k), ul, ur);
    1157                 :            :   }
    1158                 :          0 : }
    1159                 :            : 
    1160                 :            : } // tk::

Generated by: LCOV version 1.14