|
PSCF v1.4.0
|
C++ Templates (Prev/Up) Concepts and Definitions (Next)
Most uses of C++ class templates in PSCF fall into one or more of the following cases:
Container templates : Arrays and other containers are defined as specializations of class templates in which the type of each element is a template parameter.
Dimension of space : In code for periodic systems, the dimension of space is always treated as a template parameter, denoted by D.
Algorithm templates : Algorithms that are used in two or more PSCF programs are sometimes implemented in skeleton form by a class template.
The cases described above are not mutually exclusive. Specifically, cases C and D may overlap: An algorithm may be implemented in a class template, and specializations of that template may be used as base classes for more specialized subclasses used by different programs.
Each of these cases is discussed in more detail below.
The most conventional use of templates in PSCF is their use to define generic data containers. This is analogous to the use of templates in the C++ standard library to define container templatess such as std::vector. Rather than using the container templates defined in the standard library, however, PSCF relies primarily on a set of container templates that are distributed with the package.
For example, most one-dimensional arrays used in PSCF are defined as specializations of a class template named Util::DArray. This template takes the type name of a single array element as a template parameter. Thus, for example, Util::DArray<double> is an array of double precision real numbers. The source code for DArray and other container templates used in PSCF is located in directory src/util/containers.
The DArray class template defines a simple dynamically allocated array that allows individual elements to be access via an overloaded subscript [] operator, using the same syntax for this as that used by built-in C-arrays. A DArray holds data in a contiguous block of memory that may be dynamically allocated by invoking a member function named "allocate" at some point after construction. The decision to rely primarily on DArray and other home-grown containers throughout PSCF was based in part on a preference for this type of interface for memory management, in which allocation is separate from both object construction and addition of elements. Note that this is different from the interfaces used by either a std::vector (which is a dynamic array whose size can grow at any time by pushing elements to the end of the array) or by std::array (for which the array size must be known at compile time). The container classes used by PSCF also provide a mechanism to either enable run-time checking of array bounds, to facilitate debugging, or disable these additional checks to maximize performance in production code.
Throughout the code for systems with periodic boundary conditions, the dimension of space, denoted by D, is treated as a template parameter.
Almost all of the classes used defined namespaces Prdc, Rpc and Rpg are defined as specializations of class templates in which D is a template parameter, often the only template parameter. For example, classes used by pscf_rpc to represent a block within a block polymer and an entire polymer are defined as specializations of templates that are declared within namespace Pscf::Rpc as "template <int D> class Block" and "template <int D> class Polymer", respectively. For each class template for which D is the only template parameter, the PSCF build system actually explicitly instantiates (i.e., compiles code for) three specializations corresponding to the three possible values of D = 1, 2, and 3.
The ability to use different values for the spatial dimension D is primarily relevant to SCFT calculations of periodic structures. In this context, D is used to specify the number of directions or coordinates along which some structure of interest is periodic. For example, the SCFT solution of a lamellar phase is described by fields that are periodic with respect to one coordinate (the coordinate perpendicular to the layers), and independent of the remaining two orthogonal coordinates. As a result, SCFT calculation for a lamellar phase are normally performed by PSCF using specializations with D=1 for all class templates in which D appears as a template parameter. PS-FTS calculations are instead usually performed using D=3 even for simulations of a lamellar or columnar phases, because the fluctuations that are sampled in such stochastic simulations can depend on all three physical coordinates, even if the average field configuration exhibits a periodic dependence on only one or two coordinates.
The decision to treat D as a template parameter in PSCF was originally made to simplify design and improve performance of parts of the code that deal with unit cells, crystallography, and periodic fields. Treating D as a template parameter allows algorithms that involve description of unit cells, reciprocal lattice vectors, symmetry operations, space groups, and mesh dimensions to be implemented using data structures that represent D-dimensional vectors and D \( \times \) D matrices using memory blocks whose sizes are known at compile time, and that can thus be created on the stack, rather than needing to dynamically allocate memory for all such small data structures on the heap at run time.
Some algorithms and data structures that are used in two or more different programs or contexts are implemented as class templates. The use of a template allows the use of different class types with analogous purposes to be declared as template type name parameters.
For example, the Pscf::AmIteratorTmpl class template implements a skeleton version of the Anderson mixing algorithm for iteratively solving a system of nonlinear equations. Classes that are derived from specializations of this template are used to implement Anderson mixing SCFT iteration algorithms in all three of the PSCF programs. The same template is also used in the pscf_rpc and pscf_rpc programs to implement some of the "compressor" algorithms that are used in PS-FTS simulations find field configurations that satisfy a partial saddle-point condition. Different specializations of this template may, however, use different data types to represent residual vectors and solution vectors.
PSCF currently contains code for three programs (pscf_r1d, pscf_rpc and pscf_rpg, respectively) that are constructed using source code from three corresonding program-level namespaces named Pscf:R1d, Pscf::Rpc, and Pscf::Rpg, respectively. Many of the data structures and algorithms used in different PSCF programs are closely analogous, but are implemented as distinct but analogous classes defined in different program level namespaces. Analogous classes defined in different program-level namespaces are generally given the same name. Name clashes are prevented by a convention requiring that classes and function that are defined in one program-level PSCF namespace may never use or refer to classes or functions that are defined in a different program-level namespace.
For example, each of the three program-level namespaces uses a class or class template named Polymer that represents one block polymer species. The executable pscf_r1d uses a class named Polymer that is defined in namespace Pscf::R1d, while pscf_rpc uses a class template named Polymer that is defined namespaces Pscf::Rpc, which takes D as a single template parameter. Each of the program-level namespaces also contain classes or class templates named Propagator, Block, Mixture, Solvent, System, among other common names.
The analogies between classes defined in the Rpc and Rpg namespaces for use in the pscf_rpc and pscf_rpg are particularly close. These two programs are designed to solve the same class of problems, and provide exactly the same features, but differ simply because on of them (pscf_rpg) is designed to use a GPU, while the other is (pscf_rpc) is not. This difference in hardware causes these classes to use different types of classes to represent fields (functions of position): The pscf_rpc program uses a class template Rpg::Field<D> that wraps a block of memory that is allocated in CPU memory, while the program pscf_rpg uses an analogous template Rpg::Field<D> that wraps a block of memory that is allocated in global GPU memory. The use of different classes to represent fields necessarily effects the design of other classes that compute or manipulate fields, forcing a design in which these two programs are based on distinct but very closely analogous sets of class definitions.
Class templates that are designed to be used as base classes for analogous classes used in different programs appear in the following directories:
C++ Templates (Prev/Up) Concepts and Definitions (Next)