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 [ + - ]: 20143 : 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 : 201 : explicit Scheme( ctr::SchemeType scheme,
125 : : bool ale = false,
126 : : bool linearsolver = false,
127 : 201 : tk::Centering centering = tk::Centering::NODE ) :
128 [ + - ][ + - ]: 201 : discproxy( CProxy_Discretization::ckNew() )
[ + - ]
129 : : {
130 [ + - ][ + - ]: 201 : bound.bindTo( discproxy );
131 [ + + ][ + + ]: 201 : if (scheme == ctr::SchemeType::DG ||
132 [ + + ]: 153 : scheme == ctr::SchemeType::P0P1 ||
133 [ + + ]: 136 : scheme == ctr::SchemeType::DGP1 ||
134 [ + + ]: 128 : scheme == ctr::SchemeType::DGP2 ||
135 : : scheme == ctr::SchemeType::PDG)
136 : : {
137 [ + - ][ + - ]: 85 : proxy = static_cast< CProxy_DG >( CProxy_DG::ckNew(bound) );
[ + - ]
138 [ + + ]: 116 : } else if (scheme == ctr::SchemeType::ALECG) {
139 [ + - ][ + - ]: 66 : proxy = static_cast< CProxy_ALECG >( CProxy_ALECG::ckNew(bound) );
[ + - ]
140 [ + + ]: 50 : } else if (scheme == ctr::SchemeType::OversetFE) {
141 [ + - ][ + - ]: 26 : proxy = static_cast< CProxy_OversetFE >( CProxy_OversetFE::ckNew(bound) );
[ + - ]
142 [ + - ]: 24 : } else if (scheme == ctr::SchemeType::FV) {
143 [ + - ][ + - ]: 24 : proxy = static_cast< CProxy_FV >( CProxy_FV::ckNew(bound) );
[ + - ]
144 [ - - ][ - - ]: 0 : } else Throw( "Unknown discretization scheme" );
[ - - ]
145 [ + + ][ + - ]: 201 : if (ale) aleproxy = CProxy_ALE::ckNew(bound);
[ + - ][ + - ]
146 [ + + ]: 201 : if (linearsolver)
147 [ + - ][ + - ]: 6 : conjugategradientsproxy = tk::CProxy_ConjugateGradients::ckNew(bound);
[ + - ]
148 [ + + ]: 201 : if (centering == tk::Centering::ELEM)
149 [ + - ][ + - ]: 103 : ghostsproxy = CProxy_Ghosts::ckNew(bound);
[ + - ]
150 : 201 : }
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 : 5137 : void bcast( Args&&... args ) {
174 : 20548 : std::visit( [&]( auto& p ){
175 : : if constexpr( std::is_same_v< Fn, setup > )
176 : 189 : p.setup( std::forward< Args >( args )... );
177 : : if constexpr( std::is_same_v< Fn, box > )
178 : 189 : 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 : 3186 : 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 : 189 : p.doneInserting( std::forward< Args >( args )... );
199 [ + - ]: 5137 : }, proxy );
200 : 5137 : }
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 [ + - ]: 66 : 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 [ - - ][ + - ]: 33 : return p.ckLocal()->extractFieldOutput(std::forward<Args>(args)...);
223 : : else if constexpr( std::is_same_v< Fn, solution > )
224 : 0 : return p.ckLocal()->solution( std::forward<Args>(args)... );
225 [ - - ]: 66 : }, 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 : 1943 : void insert( const CkArrayIndex1D& x, Args&&... args ) {
236 [ + - ]: 1943 : auto e = element( x );
237 [ + - ]: 3886 : std::visit( [&]( auto& p ){ p.insert(std::forward<Args>(args)...); }, e );
238 : 1943 : }
239 : :
240 : : //! Get reference to discretization proxy
241 : : //! \return Discretization Charm++ chare array proxy
242 : 12665 : CProxy_Discretization& disc() noexcept { return discproxy; }
243 : :
244 : : //! Get reference to ALE proxy
245 : : //! \return ALE Charm++ chare array proxy
246 : 1953 : CProxy_ALE& ale() noexcept { return aleproxy; }
247 : :
248 : : //! Get reference to ConjugateGradients proxy
249 : : //! \return ConjugateGradients Charm++ chare array proxy
250 : 1949 : tk::CProxy_ConjugateGradients& conjugategradients() noexcept
251 : 1949 : { return conjugategradientsproxy; }
252 : :
253 : : //! Get reference to Ghosts proxy
254 : : //! \return Ghosts Charm++ chare array proxy
255 : 2355 : 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 : 189 : 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 : 61617 : void pup( PUP::er &p ) {
280 : 61617 : p | proxy;
281 : 61617 : p | discproxy;
282 : 61617 : p | aleproxy;
283 : 61617 : p | conjugategradientsproxy;
284 : 61617 : p | ghostsproxy;
285 : 61617 : p | bound;
286 : 61617 : }
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 : 61617 : 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 : 1980 : ProxyElem element( const CkArrayIndex1D& x ) const {
313 : 1980 : return std::visit( [&]( const auto& p ){
314 [ + - ]: 3960 : return static_cast< ProxyElem >( p[x] ); }, proxy );
315 : : }
316 : : };
317 : :
318 : : } // inciter::
319 : :
320 : : #endif // Scheme_h
|