Polymorphic glue for calling Charm++ entry methods to base class Discretization, its children implementing specific discretization schemes, and helper classes.
The purpose of this class is to hide, behind a single type, different Charm++ proxy types that model a single concept, i.e., define some common functions as Charm++ entry methods that can be used in either a broadcast and/or in a way of addressing a single array element. As a result, member functions can be invoked by client code without knowing the underlying type or any specifics to the underlying differences of the classes that model the same concept, i.e., expose the same member functions. The idea is very similar to inheritance and runtime polymorphism with base classes and virtual functions: some member functions and data are common to all types modeled (and thus are not repeated and/or copied), while some are specific. A difference is that the "base" and "child" classes are Charm++ proxies. Note that while Charm++ does support inheritance and runtime polymorphism with chare arrays, we still prefer the implementation below because it uses entirely value semantics (inside and in client code) and thus it keeps the complexity of the dispatch behind this class and does not expose it to client code.
The advantages of this class over traditional runtime polymorphism are (1) value semantics (both internally and to client code), (2) not templated, and (3) PUPable, i.e., an instance of Scheme can be sent across the network using Charm++'s pup framework. Also, since the class only holds a couple of chare proxies, it is lightweight.
Example usage from client code:
// Instantiate a Scheme object Scheme s( ctr::SchemeType::DG ); // see Control/Inciter/Options/Scheme.h // Issue broadcast to child scheme entry method s.bcast< Scheme::setup >(...); // Issue broadcast to base (Discretization) entry method s.disc().totalvol();
Organization, implementation details, end extension of the class:
Scheme contains (at least) two Charm++ proxies: discproxy and proxy. The former contains data and functionality common to all discretizations, and this can be considered as an equivalent to a base class in the OOP sense. The latter, proxy, contains data and functionality specific to a particular discretization. When instantiated, Scheme is configured for a single specific discretization which must be selected from the list of types in SchemeBase::Proxy.
The underlying type of proxy is a variant, which allows storing exactly one object. A variant is a type-safe union. An instance of a variant at any given time either holds a value of one of its alternative types. Read more on std::variant on how they work.
Adding a new child scheme is done by (1) Adding a new type of Charm++ chare array proxy to Scheme::Proxy, (2) Adding a new type of Charm++ chare array element proxy to Scheme::ProxyElem, and (3) Adding a new branch to the if test in Scheme's constructor.
- namespace inciter
- Inciter declarations and definitions.