src/Control/SystemComponents.hpp file

Operations on numbers of scalar components of systems of equations.


Problem: We are given a type that is a tk::tuple::tagged_tuple that contains an arbitrary number of std::vectors of integers. The number of vectors are fixed at compile-time (accessed via tags) but their (differing) length is only known at run-time (after parsing user input). What we need are functions that operate on this data structure and return, e.g., the total number of components in the whole system or the offset in the whole data structure for a given tag. The functions should thus be able to operate on a list of types, i.e., a double for-loop over all tags and associated vectors - one at compile-time and the other one at run-time.

Example: An example is to define storage for systems of stochastic differential equations as in walker::ctr::ncomps. In walker various types of stochastic differential equations are implemented and numerically integrated in time in order to compute and learn about their statistical behavior. Since different types of systems can be parameterized differently, there can be several systems of the same type (the number is user-defined so only known at run-time). For example, it is possible to numerically integrate two systems of Dirichlet equations, see DiffEq/Dirichlet.h, one with 4, the other one with 5 scalar variables, parameterized differently, and estimate their arbitrary coupled statistics and joint PDFs. This requires that each type of equation system has a vector of integers storing the number of scalar variables.

Solution: Looping through a list of types is done using the Brigand's [MetaProgramming Library]. Such operations on types happen at compile-time, i.e., the code runs inside the compiler and only its result gets compiled into code to be run at run-time. Advantages are abstraction and generic code that is independent of the size and order of the tags in the tuple (and the associated vectors). Though it is not of primary concern for this data structure, this solution also generates efficient code, since part of the algorithm (for computing the offset, the total number of components, etc.) runs inside the compiler. All of this is kept generic, so it does not need to be changed when a new pair of tag and system of equations are added to the underlying tuple. Thus, the main advantage is that changing the order of the vectors in the tuple or adding new ones at arbitrary locations will not require change to client-code.

An alternative approach: Of course this could have been simply done with a purely run-time vector of vector of integers. However, that would not have allowed a tag-based access to the different systems of equations. (Sure, one can define an enum for equation indexing, but that just adds code to what is already need to be changed if a change happens to the underlying data structure.) As a consequence, a component in a vector of vector would have had to be accessed as something like, ncomps[2][3], which is more error-prone to read than ncomps< tag::dirichlet >( 3 ). Furthermore, that design would have resulted in double-loops at run-time, instead of letting the compiler loop over the types at compile-time and do the run-time loops only for what is only known at run-time. Additionally, and most importantly, any time a new system is introduced (or the order is changed), all client-code would have to be changed.

For example client-code, see walker::Dirichlet::Dirichlet() in DiffEq/Dirichlet.h, or walker::Integrator::Integrator in Walker/Integrator.h.


namespace tk
Toolkit declarations and definitions for general purpose utilities.
namespace tk::ctr
Toolkit control, general purpose user input to internal data transfer.


struct tk::ctr::ComponentVector
template<typename... Tags>
class tk::ctr::ncomponents
Number of components storage.