Simpatico  v1.10
5.4 Loops and Iterators

5.3 Parameter File I/O (Prev)         5.5 Data Structures (mcSim and mdSim) (Next)

Most loops in Simpatico are written using iterators, rather than simple for-loops.

Iterators

The directory src/util/containers contains several class templates for iterators. Each iterator type allows iteration over the elements of one or more corresponding container types. The interface used for iterators in Simpatico is slightly different from that for C++ standard library iterators. All Simpatico iterators overload *, ->, and (prefix) ++ operators. The meaning of these operators is identical to that for bare C pointers, or for C++ standard library iterators. In addition to these operators, however, each Simpatico iterator provides a bool method named ``notEnd()" that can be used to test for termination of a loop. A single Simaptico iterator can thus be used to implement a loop, whereas a pair of iterators (one of which is used to locate the end of a sequence) is normally requierd with C++ standard library iterators.

If ``iter" is the name of a Simpatico iterator that points to an object A of type T within a container, then

The notEnd() method returns true as long as the iterator points to a valid element in the sequence, and false when the iterator has already been incremented one past the last element in the sequence.

An iterator is initialized to point to the first element in a container by a member function of the container. Each Simpatico container template provides at least one method named begin(), which takes an iterator as its parameter, which is passed by reference. The interface for this method is always:

void begin(Iterator& )

where Iterator denotes an associated iterator type. The begin() method modifies the iterator so as to point at the first element within the sequence stored by the container. It also passes the integrator the information required to identify the end of the sequence (e.g., the address of an element one past the last element, for a simple array container). The usual syntax for a loop that iterates over elements of a container is thus:

Container container;
Iterator iter;
for (container.begin(iter); iter.notEnd(); ++i){
// Do something with iter, using the iterator like a pointer.
}

where Container and Iterator represent associated container and iterator types.

There are actually two types of Iterator associated with each container template:

The name of the class template for a const iterator always begins with the prefix Const. For example, Util::ArrayIterator<T> and Util::ConstArrayIterator<T> are the non-const and const iterator types for instances of one-dimensional arrays that store objects of type T. These iterator types are used by the container types Util::DArray<T> and Util::FArray<T>, and Util::SArray<T>. Two overloaded begin() methods are defined for each container, one of which takes an instance of the non-const iterator as an parameter, and the other of which takes a const iterator.

Loops over atoms, covalent groups (e.g,. bonds) and (in McMd) molecules are also implemented using iterators. Different interfaces are used in the McMd and DdMd namespaces, which are discussed separately below.

Iterators in mcSim and mdSim

In mcSim and mdSim simulations, each McMd::System has a separate container of molecules for each species. The typedefs McMd::System::MoleculeIterator and McMd::System::ConstMoleculeIterator are aliases for iterator types that can iterate over all molecules of a specific Species within one System. The McMd::System class provides overloaded begin() methods for these two iterator types. Each of the System::begin() method takes an integer species index and molecule iterator as parameters, and initialize the iterator to point at the first molecule in the specified species. The syntax for a loop over all molecule of a specific species with a System is thus:

System system;
System::MoleculeIterator molIter:
// Loop over molecules in species
for (system.begin(speciesId, molIter); molIter.notEnd(); ++molIter){
// Do something with molIter, using a syntax similar to that for a pointer.
}

Here, the integer speciesId is the species index for a specific species. In and MC or MD simulation, the system would normally be an instance of the McSystem or MdSystem subclass, respectively, but the required begin() methods are defined by the McMd::System base class.

To iterate over all the atoms in an McMd::System, one must use a nested iteration over species, molecules, and atoms within each molecule. The typedef McMd::Molecule::AtomIterator is an alias for the type of an iterator that can iterate over all Atom objects within a molecule. The McMd::Molecule class provides a begin() method that initializes an instance of Molecule::AtomIterator. The syntax for a loop over all atoms within all molecules of all species in a System is thus:

namespace McMd
{
System system;
Simulation simulation;
// Loop over all molecular species
int nSpecies = Simulation::nSpecies();
for (int speciesId = 0; speciesId < nSpecies; ++speciesId) {
// Loop over molecules in each species
for (system.begin(species, molIter); !molIter.atEnd(); ++i){
// Loop over atoms in each molecule
for (molItr->begin(species, atomIter); !atomIter.atEnd(); ++atomIter){
// Do something with each Atom, using atomIter as if it were a pointer.
}
}
}
}

Iterators in ddSim

In ddSim simulations, the DdMd::AtomStorage member of the main DdMd::Simulation acts as a container for all of the DdMd::Atom objects owned by a single processor. Their is no notion of a molecule or molecular species in the DdMd namespace: In this context, one must iterates directly over DdMd::Atom objects stored in an AtomStorage. There are two types of atoms stored in a AtomStorage container: "Local" atoms are atoms the are "owned" by this processor, because their positions lay within the domain associated with the processor the last time that ownership was assigned (which occurs whenever the pair list is rebuilt). "Ghost" atoms are atoms that are owned by neighboring processors, but that lie close enough to the boundaries of the spatial domain owned by this processor so that they can interact with local atoms, so that this processor needs to know their locations in order to calculate forces on local atoms. Different iterators types are defined for local and ghost atoms. The classes DdMd::AtomIterator and DdMd::ConstAtomIterator are non-const and const iterators that can iterate over all of local atoms owned by a processor, respectively. The classes DdMd::GhostIterator and DdMd::ConstGhostIterator instead iterate over all of the ghost atoms on a processor.

The DdMd::AtomStorage class defines four overloaded begin(Iterator& ) methods for these four types of iterators, i.e., for non-const and const local and ghost atoms. Which type of atom and access is provided by the iterator is determined by the type of the iterator that is passed to the begin() method. The syntax for a loop that provides read-write access to all of the ``local" atoms on a processor (i.e., all of those owned by this processor) is simply

namespace DdMd
{
AtomStorage atomStorage;
AtomIterator atomIter:
for (atomStorage.begin(atomIter); atomIter.notEnd(); ++atomIter){
// Do something with each Atom, using iterator atomIter
}
}

A corresponding loop over ghost atoms would simply use a DdMd::GhostIterator or DdMd::ConstGhostIterator for the iterator.

The DdMd::GroupStorage class template provides a container for covalent groups such as covalent bonds. Instances of the DdMd::GroupStorage<N> template with N=2, 3 or 4 are containers for bond, angle and dihedral covalent Group<N> objects, respectively. Each Group<N> holds pointers to N DdMd::Atom objects. For example, a Group<N> object represents a 2-body covalent bond, and stores pointers to the two atoms that are connected by the bond. Each atom within a Group<N> may be either a local or ghost atom, but each group must contain at least one local atom. A DdMd::GroupStorage<N> container holds all of the Group<N> objects that include one or more atoms that are owned by this processor. The classes templaets DdMd::GroupIterator<N> and DdMd::ConstGroupIterator<N> are iterators that iterate over all the Group<N> objects in a GroupStorage<N> container. The syntax for iteration is similar to that for other Simpatico iterators and containers. Here is an example for a loop over covalent bonds (N=2):

namespace DdMd
{
GroupStorage<2> bondStorage;
GroupIterator<2> bondIter:
for (bondStorage.begin(bondIter); bondIter.notEnd(); ++bondIter){
// Do something with bondIter
}
}

This could also be written using the typedefs BondStorage and BondIterator, which are simply aliases for GroupStorage<2> and GroupIterator<2>, respectively. A corresponding loop over angles or dihedrals would be similar except for the use of a template argument N = 3 or N = 4, respectively, or the equivalent Angle or Dihedral typedefs (e.g., AngleStorage and AngleIterator).


5.3 Parameter File I/O (Prev)         5 Developer Information (Up)         5.5 Data Structures (mcSim and mdSim) (Next)