Quinoa all test code coverage report
Current view: top level - Inciter - Scheme.hpp (source / functions) Hit Total Coverage
Commit: -128-NOTFOUND Lines: 56 58 96.6 %
Date: 2024-04-22 13:03:21 Functions: 15 23 65.2 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 60 140 42.9 %

           Branch data     Line data    Source code
       1                 :            : // *****************************************************************************
       2                 :            : /*!
       3                 :            :   \file      src/Inciter/Scheme.hpp
       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     Polymorphic glue for calling Charm++ entry methods to base class
       9                 :            :     Discretization, its children implementing specific discretization schemes,
      10                 :            :     and helper classes
      11                 :            :   \details
      12                 :            :     The purpose of this class is to hide, behind a single type, different
      13                 :            :     Charm++  proxy types that model a single concept, i.e., define some common
      14                 :            :     functions as Charm++ entry methods that can be used in either a broadcast
      15                 :            :     and/or in a way of addressing a single array element. As a result, member
      16                 :            :     functions can be invoked by client code without knowing the underlying type
      17                 :            :     or any specifics to the underlying differences of the classes that model the
      18                 :            :     same concept, i.e., expose the same member functions. The idea is very
      19                 :            :     similar to inheritance and runtime polymorphism with base classes and
      20                 :            :     virtual functions: some member functions and data are common to all types
      21                 :            :     modeled (and thus are not repeated and/or copied), while some are specific.
      22                 :            :     A difference is that the "base" and "child" classes are Charm++ proxies.
      23                 :            :     Note that while Charm++ does support inheritance and runtime polymorphism
      24                 :            :     with chare arrays, we still prefer the implementation below because it uses
      25                 :            :     entirely value semantics (inside and in client code) and thus it keeps the
      26                 :            :     complexity of the dispatch behind this class and does not expose it to
      27                 :            :     client code.
      28                 :            : 
      29                 :            :     The advantages of this class over traditional runtime polymorphism are (1)
      30                 :            :     value semantics (both internally and to client code), (2) not templated,
      31                 :            :     and (3) PUPable, i.e., an instance of Scheme can be sent across the network
      32                 :            :     using Charm++'s pup framework. Also, since the class only holds a couple of
      33                 :            :     chare proxies, it is lightweight.
      34                 :            : 
      35                 :            :     Example usage from client code:
      36                 :            : 
      37                 :            :     \code{.cpp}
      38                 :            :       // Instantiate a Scheme object
      39                 :            :       Scheme s( ctr::SchemeType::DG );  // see Control/Inciter/Options/Scheme.h
      40                 :            : 
      41                 :            :       // Issue broadcast to child scheme entry method
      42                 :            :       s.bcast< Scheme::setup >(...);
      43                 :            : 
      44                 :            :       // Issue broadcast to base (Discretization) entry method
      45                 :            :       s.disc().totalvol();
      46                 :            :     \endcode
      47                 :            : 
      48                 :            :     Organization, implementation details, end extension of the class:
      49                 :            : 
      50                 :            :     Scheme contains (at least) two Charm++ proxies: discproxy and proxy. The
      51                 :            :     former contains data and functionality common to all discretizations, and
      52                 :            :     this can be considered as an equivalent to a base class in the OOP sense.
      53                 :            :     The latter, proxy, contains data and functionality specific to a particular
      54                 :            :     discretization. When instantiated, Scheme is configured for a single
      55                 :            :     specific discretization which must be selected from the list of types in
      56                 :            :     SchemeBase::Proxy.
      57                 :            : 
      58                 :            :     The underlying type of proxy is a variant, which allows storing exactly one
      59                 :            :     object. A variant is a type-safe union. An instance of a variant at any
      60                 :            :     given time either holds a value of one of its alternative types. Read more
      61                 :            :     on std::variant on how they work.
      62                 :            : 
      63                 :            :     Adding a new child scheme is done by
      64                 :            :     (1) Adding a new type of Charm++ chare array proxy to Scheme::Proxy,
      65                 :            :     (2) Adding a new type of Charm++ chare array element proxy to
      66                 :            :         Scheme::ProxyElem, and
      67                 :            :     (3) Adding a new branch to the if test in Scheme's constructor.
      68                 :            : 
      69                 :            :   \see A talk on "Concept-based runtime polymorphism with Charm++ chare arrays
      70                 :            :     using value semantics given by J. Bakosi at the 16th Annual Workshop on
      71                 :            :     Charm++ and its Applications, April 2018, discussing an earlier, more
      72                 :            :     verbose implementation of the idea, using C++11.
      73                 :            : */
      74                 :            : // *****************************************************************************
      75                 :            : #ifndef Scheme_h
      76                 :            : #define Scheme_h
      77                 :            : 
      78                 :            : #include "Exception.hpp"
      79                 :            : #include "PUPUtil.hpp"
      80                 :            : #include "Inciter/Options/Scheme.hpp"
      81                 :            : 
      82                 :            : #include "NoWarning/discretization.decl.h"
      83                 :            : #include "NoWarning/alecg.decl.h"
      84                 :            : #include "NoWarning/oversetfe.decl.h"
      85                 :            : #include "NoWarning/dg.decl.h"
      86                 :            : #include "NoWarning/fv.decl.h"
      87                 :            : #include "NoWarning/ale.decl.h"
      88                 :            : #include "NoWarning/conjugategradients.decl.h"
      89                 :            : #include "NoWarning/ghosts.decl.h"
      90                 :            : 
      91                 :            : namespace inciter {
      92                 :            : 
      93                 :            : //! Base class for generic forwarding interface to discretization proxies
      94                 :            : class Scheme {
      95                 :            : 
      96                 :            :   private:
      97                 :            :     //! Variant type listing all chare proxy types modeling the same concept
      98                 :            :     using Proxy = std::variant< CProxy_DG
      99                 :            :                               , CProxy_ALECG
     100                 :            :                               , CProxy_OversetFE
     101                 :            :                               , CProxy_FV >;
     102                 :            : 
     103                 :            :   public:
     104                 :            :     //! Variant type listing all chare element proxy types
     105                 :            :     using ProxyElem = std::variant< CProxy_DG::element_t
     106                 :            :                                   , CProxy_ALECG::element_t
     107                 :            :                                   , CProxy_OversetFE::element_t
     108                 :            :                                   , CProxy_FV::element_t >;
     109                 :            : 
     110                 :            :     //! Empty constructor for Charm++
     111         [ +  - ]:      20326 :     explicit Scheme() {}
     112                 :            : 
     113                 :            :     //! Constructor
     114                 :            :     //! \param[in] scheme Discretization scheme
     115                 :            :     //! \param[in] ale True if enable ALE
     116                 :            :     //! \param[in] linearsolver True if enable a linear solver
     117                 :            :     //! \details Based on the input enum we create at least two empty chare
     118                 :            :     //!   arrays: (1) discproxy which contains common functionality and data for
     119                 :            :     //!   all discretizations, and (2) proxy, which have functionality and data
     120                 :            :     //!   specific to a given discretization. Note that proxy is bound (in
     121                 :            :     //!   migration behavior and properties) to discproxy.
     122                 :            :     //! \note There may be other bound proxy arrays created depending on the
     123                 :            :     //!   specific discretization configured by the enum.
     124                 :        207 :     explicit Scheme( ctr::SchemeType scheme,
     125                 :            :                      bool ale = false,
     126                 :            :                      bool linearsolver = false,
     127                 :        207 :                      tk::Centering centering = tk::Centering::NODE ) :
     128 [ +  - ][ +  - ]:        207 :       discproxy( CProxy_Discretization::ckNew() )
         [ +  - ][ -  - ]
     129                 :            :     {
     130         [ +  - ]:        207 :       bound.bindTo( discproxy );
     131                 :        207 :       if (scheme == ctr::SchemeType::DG ||
     132                 :            :                  scheme == ctr::SchemeType::P0P1 ||
     133                 :            :                  scheme == ctr::SchemeType::DGP1 ||
     134         [ +  + ]:        207 :                  scheme == ctr::SchemeType::DGP2 ||
     135                 :            :                  scheme == ctr::SchemeType::PDG)
     136                 :            :       {
     137 [ +  - ][ +  - ]:        184 :         proxy = static_cast< CProxy_DG >( CProxy_DG::ckNew(bound) );
                 [ +  - ]
     138         [ +  + ]:        115 :       } else if (scheme == ctr::SchemeType::ALECG) {
     139 [ +  - ][ +  - ]:        132 :         proxy = static_cast< CProxy_ALECG >( CProxy_ALECG::ckNew(bound) );
                 [ +  - ]
     140         [ +  + ]:         49 :       } else if (scheme == ctr::SchemeType::OversetFE) {
     141 [ +  - ][ +  - ]:         52 :         proxy = static_cast< CProxy_OversetFE >( CProxy_OversetFE::ckNew(bound) );
                 [ +  - ]
     142         [ +  - ]:         23 :       } else if (scheme == ctr::SchemeType::FV) {
     143 [ +  - ][ +  - ]:         46 :         proxy = static_cast< CProxy_FV >( CProxy_FV::ckNew(bound) );
                 [ +  - ]
     144 [ -  - ][ -  - ]:          0 :       } else Throw( "Unknown discretization scheme" );
         [ -  - ][ -  - ]
         [ -  - ][ -  - ]
     145 [ +  + ][ +  - ]:        217 :       if (ale) aleproxy = CProxy_ALE::ckNew(bound);
                 [ +  - ]
     146         [ +  + ]:        207 :       if (linearsolver)
     147 [ +  - ][ +  - ]:         12 :         conjugategradientsproxy = tk::CProxy_ConjugateGradients::ckNew(bound);
     148         [ +  + ]:        207 :       if (centering == tk::Centering::ELEM)
     149 [ +  - ][ +  - ]:        218 :         ghostsproxy = CProxy_Ghosts::ckNew(bound);
     150                 :        207 :     }
     151                 :            : 
     152                 :            :     //! Entry method tags for specific Scheme classes to use with bcast()
     153                 :            :     struct setup {};
     154                 :            :     struct box {};
     155                 :            :     struct transferSol {};
     156                 :            :     struct advance {};
     157                 :            :     struct resized {};
     158                 :            :     struct resizeComm {};
     159                 :            :     struct refine {};
     160                 :            :     struct lhs {};
     161                 :            :     struct nodeNeighSetup {};
     162                 :            :     struct diag {};
     163                 :            :     struct evalLB {};
     164                 :            :     struct doneInserting {};
     165                 :            :     //! Issue broadcast to Scheme entry method
     166                 :            :     //! \tparam Fn Function tag identifying the entry method to call
     167                 :            :     //! \tparam Args Types of arguments to pass to entry method
     168                 :            :     //! \param[in] args Arguments to member function entry method to be called
     169                 :            :     //! \details This function issues a broadcast to a member function entry
     170                 :            :     //!   method of the Scheme chare array (the child of Discretization) and is
     171                 :            :     //!   thus equivalent to proxy.Fn(...).
     172                 :            :     template< typename Fn, typename... Args >
     173                 :            :     void bcast( Args&&... args ) {
     174 [ -  + ][ +  + ]:       4427 :       std::visit( [&]( auto& p ){
     175                 :            :           if constexpr( std::is_same_v< Fn, setup > )
     176                 :        195 :             p.setup( std::forward< Args >( args )... );
     177                 :            :           if constexpr( std::is_same_v< Fn, box > )
     178                 :        195 :             p.box( std::forward< Args >( args )... );
     179                 :            :           else if constexpr( std::is_same_v< Fn, transferSol > )
     180                 :        310 :             p.transferSol( std::forward< Args >( args )... );
     181                 :            :           else if constexpr( std::is_same_v< Fn, advance > )
     182                 :        352 :             p.advance( std::forward< Args >( args )... );
     183                 :            :           else if constexpr( std::is_same_v< Fn, resized > )
     184                 :        360 :             p.resized( std::forward< Args >( args )... );
     185                 :            :           else if constexpr( std::is_same_v< Fn, resizeComm > )
     186                 :            :             p.resizeComm( std::forward< Args >( args )... );
     187                 :            :           else if constexpr( std::is_same_v< Fn, refine > )
     188                 :       3373 :             p.refine( std::forward< Args >( args )... );
     189                 :            :           else if constexpr( std::is_same_v< Fn, lhs > )
     190                 :        360 :             p.lhs( std::forward< Args >( args )... );
     191                 :            :           else if constexpr( std::is_same_v< Fn, nodeNeighSetup > )
     192                 :            :             p.nodeNeighSetup( std::forward< Args >( args )... );
     193                 :            :           else if constexpr( std::is_same_v< Fn, diag > )
     194                 :            :             p.diag( std::forward< Args >( args )... );
     195                 :            :           else if constexpr( std::is_same_v< Fn, evalLB > )
     196                 :          2 :             p.evalLB( std::forward< Args >( args )... );
     197                 :            :           else if constexpr( std::is_same_v< Fn, doneInserting > )
     198                 :            :             p.doneInserting( std::forward< Args >( args )... );
     199 [ +  - ][ +  - ]:       5342 :         }, proxy );
     200                 :        360 :     }
     201                 :            : 
     202                 :            :     //! Function tags for specific Scheme classes to use with ckLocal()
     203                 :            :     struct resizePostAMR {};
     204                 :            :     struct extractFieldOutput {};
     205                 :            :     struct solution {};
     206                 :            :     //! Call Scheme function via Charm++ chare array element's ckLocal()
     207                 :            :     //! \tparam Fn Function tag identifying the function to call
     208                 :            :     //! \tparam Args Types of arguments to pass to function
     209                 :            :     //! \param[in] x Chare array element index
     210                 :            :     //! \param[in] args Arguments to member function function to be called
     211                 :            :     //! \details This function calls a member function via Charm++'s ckLocal()
     212                 :            :     //!    behind the element proxy configured, indexed by the array index x.
     213                 :            :     //!    Since the call is behind ckLocal(), the member function does not have
     214                 :            :     //!    to be a Charm++ entry method.
     215                 :            :     template< typename Fn, typename... Args >
     216                 :         33 :     auto ckLocal( const CkArrayIndex1D& x, Args&&... args ) const {
     217                 :            :       auto e = element( x );
     218 [ +  - ][ -  - ]:         66 :       return std::visit( [&]( auto& p ){
     219                 :            :           if constexpr( std::is_same_v< Fn, resizePostAMR > )
     220                 :          0 :             return p.ckLocal()->resizePostAMR( std::forward<Args>(args)... );
     221                 :            :           else if constexpr( std::is_same_v< Fn, extractFieldOutput > )
     222 [ -  - ][ +  - ]:         66 :             return p.ckLocal()->extractFieldOutput(std::forward<Args>(args)...);
     223                 :            :           else if constexpr( std::is_same_v< Fn, solution > )
     224                 :            :             return p.ckLocal()->solution( std::forward<Args>(args)... );
     225 [ -  - ][ -  - ]:         33 :         }, e );
     226                 :            :     }
     227                 :            : 
     228                 :            :     //! Function to call the insert entry method of an element proxy
     229                 :            :     //! \param[in] x Chare array element index
     230                 :            :     //! \param[in] args Arguments to member function (entry method) to be called
     231                 :            :     //! \details This function calls the insert member function of a chare array
     232                 :            :     //!   element proxy and thus equivalent to proxy[x].insert(...), using the
     233                 :            :     //!   last argument as default.
     234                 :            :     template< typename... Args >
     235                 :       1994 :     void insert( const CkArrayIndex1D& x, Args&&... args ) {
     236                 :            :       auto e = element( x );
     237 [ +  - ][ -  - ]:       3988 :       std::visit( [&]( auto& p ){ p.insert(std::forward<Args>(args)...); }, e );
     238                 :       1994 :     }
     239                 :            : 
     240                 :            :     //! Get reference to discretization proxy
     241                 :            :     //! \return Discretization Charm++ chare array proxy
     242 [ +  - ][ +  - ]:       6812 :     CProxy_Discretization& disc() noexcept { return discproxy; }
     243                 :            : 
     244                 :            :     //! Get reference to ALE proxy
     245                 :            :     //! \return ALE Charm++ chare array proxy
     246                 :       1994 :     CProxy_ALE& ale() noexcept { return aleproxy; }
     247                 :            : 
     248                 :            :     //! Get reference to ConjugateGradients proxy
     249                 :            :     //! \return ConjugateGradients Charm++ chare array proxy
     250                 :            :     tk::CProxy_ConjugateGradients& conjugategradients() noexcept
     251         [ +  - ]:       1994 :     { return conjugategradientsproxy; }
     252                 :            : 
     253                 :            :     //! Get reference to Ghosts proxy
     254                 :            :     //! \return Ghosts Charm++ chare array proxy
     255         [ +  - ]:       2321 :     CProxy_Ghosts& ghosts() noexcept { return ghostsproxy; }
     256                 :            : 
     257                 :            :     //! Get reference to scheme proxy
     258                 :            :     //! Get reference to scheme proxy
     259                 :            :     //! \return Variant storing Charm++ chare array proxy configured
     260                 :            :     const Proxy& getProxy() noexcept { return proxy; }
     261                 :            : 
     262                 :            :     //! Query underlying proxy type
     263                 :            :     //! \return Zero-based index into the set of types of Proxy
     264 [ +  - ][ +  - ]:          8 :     std::size_t index() const noexcept { return proxy.index(); }
         [ +  - ][ +  - ]
                 [ +  - ]
     265                 :            : 
     266                 :            :     //! Query underlying proxy element type
     267                 :            :     //! \return Zero-based index that can be used, e.g., indexing into the set
     268                 :            :     //!   of types of ProxyElem
     269                 :          4 :     std::size_t index_element() const noexcept { return element(0).index(); }
     270                 :            : 
     271                 :            :     //! Charm++ array options accessor for binding external proxies
     272                 :            :     //! \return Charm++ array options object reference
     273         [ +  - ]:        195 :     const CkArrayOptions& arrayoptions() { return bound; }
     274                 :            : 
     275                 :            :     /** @name Charm++ pack/unpack serializer member functions */
     276                 :            :     ///@{
     277                 :            :     //! \brief Pack/Unpack serialize member function
     278                 :            :     //! \param[in,out] p Charm++'s PUP::er serializer object reference
     279                 :      62271 :     void pup( PUP::er &p ) {
     280                 :      62271 :       p | proxy;
     281                 :      62271 :       p | discproxy;
     282                 :      62271 :       p | aleproxy;
     283                 :      62271 :       p | conjugategradientsproxy;
     284                 :      62271 :       p | ghostsproxy;
     285                 :      62271 :       p | bound;
     286                 :      62271 :     }
     287                 :            :     //! \brief Pack/Unpack serialize operator|
     288                 :            :     //! \param[in,out] p Charm++'s PUP::er serializer object reference
     289                 :            :     //! \param[in,out] s Scheme object reference
     290 [ +  - ][ -  - ]:      62271 :     friend void operator|( PUP::er& p, Scheme& s ) { s.pup(p); }
         [ -  - ][ +  - ]
         [ -  - ][ -  - ]
         [ +  - ][ +  - ]
     291                 :            :     //@}
     292                 :            : 
     293                 :            :   private:
     294                 :            :     //! Variant storing one proxy to which this class is configured for
     295                 :            :     Proxy proxy;
     296                 :            :     //! Charm++ proxy to data and code common to all discretizations
     297                 :            :     CProxy_Discretization discproxy;
     298                 :            :     //! Charm++ proxy to ALE class
     299                 :            :     CProxy_ALE aleproxy;
     300                 :            :     //! Charm++ proxy to conjugate gradients linear solver class
     301                 :            :     tk::CProxy_ConjugateGradients conjugategradientsproxy;
     302                 :            :     //! Charm++ proxy to Ghosts class
     303                 :            :     CProxy_Ghosts ghostsproxy;
     304                 :            :     //! Charm++ array options for binding chares
     305                 :            :     CkArrayOptions bound;
     306                 :            : 
     307                 :            :     //! Function dereferencing operator[] of chare proxy inside variant
     308                 :            :     //! \param[in] x Chare array element index
     309                 :            :     //! \return Chare array element proxy as a variant, defined by ProxyElem
     310                 :            :     //! \details The returning element proxy is a variant, depending on the
     311                 :            :     //!   input proxy.
     312                 :            :     ProxyElem element( const CkArrayIndex1D& x ) const {
     313                 :       2031 :       return std::visit( [&]( const auto& p ){
     314 [ +  - ][ +  - ]:       4062 :                return static_cast< ProxyElem >( p[x] ); }, proxy );
                 [ -  - ]
     315                 :            :     }
     316                 :            : };
     317                 :            : 
     318                 :            : } // inciter::
     319                 :            : 
     320                 :            : #endif // Scheme_h

Generated by: LCOV version 1.14