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/diagcg.decl.h"
84 : : #include "NoWarning/alecg.decl.h"
85 : : #include "NoWarning/distfct.decl.h"
86 : : #include "NoWarning/dg.decl.h"
87 : : #include "NoWarning/ale.decl.h"
88 : : #include "NoWarning/conjugategradients.decl.h"
89 : :
90 : : namespace inciter {
91 : :
92 : : //! Base class for generic forwarding interface to discretization proxies
93 : : class Scheme {
94 : :
95 : : private:
96 : : //! Variant type listing all chare proxy types modeling the same concept
97 : : using Proxy = std::variant< CProxy_DiagCG
98 : : , CProxy_DG
99 : : , CProxy_ALECG >;
100 : :
101 : : public:
102 : : //! Variant type listing all chare element proxy types
103 : : using ProxyElem = std::variant< CProxy_DiagCG::element_t
104 : : , CProxy_DG::element_t
105 : : , CProxy_ALECG::element_t >;
106 : :
107 : : //! Empty constructor for Charm++
108 [ + - ]: 22852 : explicit Scheme() {}
109 : :
110 : : //! Constructor
111 : : //! \param[in] scheme Discretization scheme
112 : : //! \param[in] ale True if enable ALE
113 : : //! \param[in] linearsolver True if enable a linear solver
114 : : //! \details Based on the input enum we create at least two empty chare
115 : : //! arrays: (1) discproxy which contains common functionality and data for
116 : : //! all discretizations, and (2) proxy, which have functionality and data
117 : : //! specific to a given discretization. Note that proxy is bound (in
118 : : //! migration behavior and properties) to discproxy.
119 : : //! \note There may be other bound proxy arrays created depending on the
120 : : //! specific discretization configured by the enum.
121 : 233 : explicit Scheme( ctr::SchemeType scheme,
122 : : bool ale = false,
123 : 233 : bool linearsolver = false ) :
124 [ + - ][ + - ]: 233 : discproxy( CProxy_Discretization::ckNew() )
[ + - ][ - - ]
125 : : {
126 [ + - ]: 233 : bound.bindTo( discproxy );
127 [ + + ]: 233 : if (scheme == ctr::SchemeType::DiagCG) {
128 [ + - ][ + - ]: 166 : proxy = static_cast< CProxy_DiagCG >( CProxy_DiagCG::ckNew(bound) );
[ + - ]
129 [ + - ][ + - ]: 166 : fctproxy = CProxy_DistFCT::ckNew(bound);
130 : 150 : } else if (scheme == ctr::SchemeType::DG ||
131 : : scheme == ctr::SchemeType::P0P1 ||
132 : : scheme == ctr::SchemeType::DGP1 ||
133 [ + + ]: 150 : scheme == ctr::SchemeType::DGP2 ||
134 : : scheme == ctr::SchemeType::PDG)
135 : : {
136 [ + - ][ + - ]: 192 : proxy = static_cast< CProxy_DG >( CProxy_DG::ckNew(bound) );
[ + - ]
137 [ + - ]: 54 : } else if (scheme == ctr::SchemeType::ALECG) {
138 [ + - ][ + - ]: 108 : proxy = static_cast< CProxy_ALECG >( CProxy_ALECG::ckNew(bound) );
[ + - ]
139 [ - - ][ - - ]: 0 : } else Throw( "Unknown discretization scheme" );
[ - - ][ - - ]
[ - - ][ - - ]
140 [ + + ][ + - ]: 243 : if (ale) aleproxy = CProxy_ALE::ckNew(bound);
[ + - ]
141 [ + + ]: 233 : if (linearsolver)
142 [ + - ][ + - ]: 12 : conjugategradientsproxy = tk::CProxy_ConjugateGradients::ckNew(bound);
143 : 233 : }
144 : :
145 : : //! Entry method tags for specific Scheme classes to use with bcast()
146 : : struct setup {};
147 : : struct box {};
148 : : struct advance {};
149 : : struct resized {};
150 : : struct resizeComm {};
151 : : struct refine {};
152 : : struct lhs {};
153 : : struct nodeNeighSetup {};
154 : : struct diag {};
155 : : struct evalLB {};
156 : : struct doneInserting {};
157 : : //! Issue broadcast to Scheme entry method
158 : : //! \tparam Fn Function tag identifying the entry method to call
159 : : //! \tparam Args Types of arguments to pass to entry method
160 : : //! \param[in] args Arguments to member function entry method to be called
161 : : //! \details This function issues a broadcast to a member function entry
162 : : //! method of the Scheme chare array (the child of Discretization) and is
163 : : //! thus equivalent to proxy.Fn(...).
164 : : template< typename Fn, typename... Args >
165 : : void bcast( Args&&... args ) {
166 [ + - ]: 5507 : std::visit( [&]( auto& p ){
167 : : if constexpr( std::is_same_v< Fn, setup > )
168 : 233 : p.setup( std::forward< Args >( args )... );
169 : : if constexpr( std::is_same_v< Fn, box > )
170 : 233 : p.box( std::forward< Args >( args )... );
171 : : else if constexpr( std::is_same_v< Fn, advance > )
172 : : p.advance( std::forward< Args >( args )... );
173 : : else if constexpr( std::is_same_v< Fn, resized > )
174 : 373 : p.resized( std::forward< Args >( args )... );
175 : : else if constexpr( std::is_same_v< Fn, resizeComm > )
176 : 96 : p.resizeComm( std::forward< Args >( args )... );
177 : : else if constexpr( std::is_same_v< Fn, refine > )
178 : 5041 : p.refine( std::forward< Args >( args )... );
179 : : else if constexpr( std::is_same_v< Fn, lhs > )
180 : 389 : p.lhs( std::forward< Args >( args )... );
181 : : else if constexpr( std::is_same_v< Fn, nodeNeighSetup > )
182 : 112 : p.nodeNeighSetup( std::forward< Args >( args )... );
183 : : else if constexpr( std::is_same_v< Fn, diag > )
184 : : p.diag( std::forward< Args >( args )... );
185 : : else if constexpr( std::is_same_v< Fn, evalLB > )
186 : 0 : p.evalLB( std::forward< Args >( args )... );
187 : : else if constexpr( std::is_same_v< Fn, doneInserting > )
188 : : p.doneInserting( std::forward< Args >( args )... );
189 [ + - ][ + - ]: 6710 : }, proxy );
190 : 485 : }
191 : :
192 : : //! Function tags for specific Scheme classes to use with ckLocal()
193 : : struct resizePostAMR {};
194 : : struct extractFieldOutput {};
195 : : struct solution {};
196 : : //! Call Scheme function via Charm++ chare array element's ckLocal()
197 : : //! \tparam Fn Function tag identifying the function to call
198 : : //! \tparam Args Types of arguments to pass to function
199 : : //! \param[in] x Chare array element index
200 : : //! \param[in] args Arguments to member function function to be called
201 : : //! \details This function calls a member function via Charm++'s ckLocal()
202 : : //! behind the element proxy configured, indexed by the array index x.
203 : : //! Since the call is behind ckLocal(), the member function does not have
204 : : //! to be a Charm++ entry method.
205 : : template< typename Fn, typename... Args >
206 : 445 : auto ckLocal( const CkArrayIndex1D& x, Args&&... args ) const {
207 : : auto e = element( x );
208 [ + - ][ - - ]: 434 : return std::visit( [&]( auto& p ){
209 : : if constexpr( std::is_same_v< Fn, resizePostAMR > )
210 : 184 : return p.ckLocal()->resizePostAMR( std::forward<Args>(args)... );
211 : : else if constexpr( std::is_same_v< Fn, extractFieldOutput > )
212 [ + - ]: 66 : return p.ckLocal()->extractFieldOutput(std::forward<Args>(args)...);
213 : : else if constexpr( std::is_same_v< Fn, solution > )
214 : : return p.ckLocal()->solution( std::forward<Args>(args)... );
215 [ + - ][ - - ]: 673 : }, e );
216 : : }
217 : :
218 : : //! Function to call the insert entry method of an element proxy
219 : : //! \param[in] x Chare array element index
220 : : //! \param[in] args Arguments to member function (entry method) to be called
221 : : //! \details This function calls the insert member function of a chare array
222 : : //! element proxy and thus equivalent to proxy[x].insert(...), using the
223 : : //! last argument as default.
224 : : template< typename... Args >
225 : 2106 : void insert( const CkArrayIndex1D& x, Args&&... args ) {
226 : : auto e = element( x );
227 [ + - ][ - - ]: 4212 : std::visit( [&]( auto& p ){ p.insert(std::forward<Args>(args)...); }, e );
228 : 2106 : }
229 : :
230 : : //! Get reference to discretization proxy
231 : : //! \return Discretization Charm++ chare array proxy
232 [ + - ][ + - ]: 9513 : CProxy_Discretization& disc() noexcept { return discproxy; }
233 : :
234 : : //! Get reference to DistFCT proxy
235 : : //! \return DistFCT Charm++ chare array proxy
236 : 2106 : CProxy_DistFCT& fct() noexcept { return fctproxy; }
237 : :
238 : : //! Get reference to ALE proxy
239 : : //! \return ALE Charm++ chare array proxy
240 : 2106 : CProxy_ALE& ale() noexcept { return aleproxy; }
241 : :
242 : : //! Get reference to ConjugateGradients proxy
243 : : //! \return ConjugateGradients Charm++ chare array proxy
244 : : tk::CProxy_ConjugateGradients& conjugategradients() noexcept
245 [ + - ]: 2106 : { return conjugategradientsproxy; }
246 : :
247 : : //! Get reference to scheme proxy
248 : : //! Get reference to scheme proxy
249 : : //! \return Variant storing Charm++ chare array proxy configured
250 : : const Proxy& getProxy() noexcept { return proxy; }
251 : :
252 : : //! Query underlying proxy type
253 : : //! \return Zero-based index into the set of types of Proxy
254 : : std::size_t index() const noexcept { return proxy.index(); }
255 : :
256 : : //! Query underlying proxy element type
257 : : //! \return Zero-based index that can be used, e.g., indexing into the set
258 : : //! of types of ProxyElem
259 : : std::size_t index_element() const noexcept { return element(0).index(); }
260 : :
261 : : //! Charm++ array options accessor for binding external proxies
262 : : //! \return Charm++ array options object reference
263 [ + - ]: 233 : const CkArrayOptions& arrayoptions() { return bound; }
264 : :
265 : : /** @name Charm++ pack/unpack serializer member functions */
266 : : ///@{
267 : : //! \brief Pack/Unpack serialize member function
268 : : //! \param[in,out] p Charm++'s PUP::er serializer object reference
269 : 67690 : void pup( PUP::er &p ) {
270 : 67690 : p | proxy;
271 : : p | discproxy;
272 : : p | fctproxy;
273 : : p | aleproxy;
274 : : p | conjugategradientsproxy;
275 : 67690 : p | bound;
276 : 67690 : }
277 : : //! \brief Pack/Unpack serialize operator|
278 : : //! \param[in,out] p Charm++'s PUP::er serializer object reference
279 : : //! \param[in,out] s Scheme object reference
280 [ + - ]: 67690 : friend void operator|( PUP::er& p, Scheme& s ) { s.pup(p); }
281 : : //@}
282 : :
283 : : private:
284 : : //! Variant storing one proxy to which this class is configured for
285 : : Proxy proxy;
286 : : //! Charm++ proxy to data and code common to all discretizations
287 : : CProxy_Discretization discproxy;
288 : : //! Charm++ proxy to flux-corrected transport (FCT) driver class
289 : : CProxy_DistFCT fctproxy;
290 : : //! Charm++ proxy to ALE class
291 : : CProxy_ALE aleproxy;
292 : : //! Charm++ proxy to conjugate gradients linear solver class
293 : : tk::CProxy_ConjugateGradients conjugategradientsproxy;
294 : : //! Charm++ array options for binding chares
295 : : CkArrayOptions bound;
296 : :
297 : : //! Function dereferencing operator[] of chare proxy inside variant
298 : : //! \param[in] x Chare array element index
299 : : //! \return Chare array element proxy as a variant, defined by ProxyElem
300 : : //! \details The returning element proxy is a variant, depending on the
301 : : //! input proxy.
302 : : ProxyElem element( const CkArrayIndex1D& x ) const {
303 : 2551 : return std::visit( [&]( const auto& p ){
304 [ + - ][ + - ]: 7653 : return static_cast< ProxyElem >( p[x] ); }, proxy );
[ + - ]
305 : : }
306 : : };
307 : :
308 : : } // inciter::
309 : :
310 : : #endif // Scheme_h
|