OOF2: The Manual
Before proceeding, it's necessary to take a diversion on the topic of indices and iterators.
It is often necessary to refer to the components of Fields,
Fluxes, Equations, and OutputVals. OOF2's
fieldindex module provides a generic
mechanism for doing this, so that it's possible to loop over all
of the components of an object without even having to know how
many indices are required to specify a component. It takes two
indices to specify a tensor component, one for a vector, and
zero for a scalar, but all three cases can be handled
identically.
The basic machinery contains:
-
The
FieldIndexbase class, which designates a component of aFieldor other object. There are different subclasses for different kinds of objects (scalars, vectors, tensors, etc). -
A
Componentsbase class, which is a container that can be iterated over to obtain theFieldIndexes. There are different subclasses for different kinds of objects, and for different ways of iterating over them (in-plane, out-of-plane, etc). Each indexable class (Field,Flux, etc) has a virtual function that returns the appropriate type ofComponents. -
A
ComponentIteratorbase class that is used for iterating over theComponents. Different subclasses ofComponentIterators iterate over different types ofComponents. -
Generic wrapper classes,
IndexPandComponentIteratorP, that wrap pointers toFieldIndexandComponentIterator.
The base class for indices is FieldIndex. There are subclasses
of FieldIndex for different kinds of Fields, Fluxes,
Equations, and OutputVals. (From now on we'll talk about
Fields, but everything also applies to Fluxes, Equations
and OutputVals, unless explicitly stated otherwise.) To
handle the polymorphism, generic C++ code doesn't use the
FieldIndex classes directly. Instead it uses a wrapper class,
IndexP, which is a light-weight object that contains a pointer
(hence the “P” in the name) to a FieldIndex. The
wrapper frees the user from having to know the actual class
being used. It provides access to its FieldIndex and its
virtual functions, and deallocates it when it goes out of scope.
Note that IndexP does not do reference
counting. When an IndexP is destructed,
its FieldIndex is destructed as well.
When an IndexP is copied, the original
FieldIndex is copied as well.
For looping over the indices of a Field, there are
Components and ComponentIterator class hierarchies. Each
type of Field has a components()
method that returns a Components pointer. The Components classes
are conceptually containers for FieldIndexes. The instances
of the Components subclasses are static immutable objects.
In C++, Components objects have
begin() and end()
methods that return ComponentIterator
objects, allowing them to work like STL iterators for looping
over indexes. For example, you can print the indices of the
components of the Temperature and Displacement Fields like this:
Field *temperature = Field::getField("Temperature");
Components* comps = temperature->components();
for(ComponentIteratorP ip = comps->begin(); ip!=comps->end(); ++ip)
std::cerr << *ip << std::endl;
Field *displacement = Field::getField("Displacement");
Components* comps = displacement->components();
for(ComponentIteratorP ip = comps->begin(); ip!=comps->end(); ++ip)
std::cerr << *ip << std::endl; or, more compactly, using range based for loops like this:
for(IndexP i : *Field::getField("Temperature")->components())
std::cerr << i << std::endl;
for(IndexP i : *Field::getField("Displacement")->components())
std::cerr << i << std::endl; both of which print
IndexP(ScalarFieldIndex()) [The single component of Temperature] IndexP(VectorFieldIndex(0)) [The first component of Displacement] IndexP(VectorFieldIndex(1)) [The second component of Displacement]
In Python, the components()
methods return generator functions that serve the same purpose.
For example, in Python you can print the indices of the
components of the temperature and displacement Fields like
this:
for i in Temperature.components(): print(i) for i in Displacement.components(): print(i)
which prints
ScalarFieldIndex() [The single component of Temperature] VectorFieldIndex(0) [The first component of Displacement] VectorFieldIndex(1) [The second component of Displacement]
In some cases it's necessary to restrict iteration to either the
in-plane or
out-of-plane components of a Field or Flux (but not
an Equation). In those cases, the
components() method takes an argument
of the Planarity class. If no argument is provided, as in the
examples above, the default planarity is
ALL_INDICES. See Field::components,
Field::outOfPlaneComponents,
Flux::components,
and Flux::outOfPlaneComponents
for examples and some important details.



