An engine performs the low-level value storage, computation, and element-wise access for a container. An engine has a domain and accessor functions returning individual elements. The POOMA Engine class and its specializations implement the engine concept. Given an index within the domain, an Engine's operator() function returns the associated value, which can be used or changed. Its read member function returns the same value but permitting only use, not modification. The acceptable indices are determined by each Engine. Most accept indices specified using int and Loc<D> parameters, but an Engine might accept string or floating-point parameters. An Engine's layout specifies maps its domain indices to the processors and memory used to store and compute the associated values.
Since an engine's main role is to return the individual values associated with specific domain indices, any implementation performing this task is an engine. POOMA Engines fall into three categories:
Engines that store values.
Engines that compute their values using other Engines' values.
Engines that support distributed computation.
For example, the Brick Engine explicitly stores all its values, while the CompressibleBrick engine adds the feature of reducing its storage requirements if all these values are identical. A UserFunction Engine yields values by applying a function object to each value returned by another Engine. A CompFwd Engine projects components from another Engine. For example, CompFwd will use the second components of each Vector in an Array to form its own Array. Since each container has at least one Engine, we can also describe the latter category as containers that compute their values using other containers' values. A MultiPatch Engine distributes its domain among various processors and memory spaces, each responsible for computing values associated with a portion, or patch, of the domain. The Remote Engine also supports distributed computation.
Just as multiple containers can use the same engine, multiple Engines can use the same underlying data. As we mentioned in Section 5.5, Engines have reference semantics. A copy of an Engine has a reference-counted pointer to the Engine's data (if any exists). Thus, copying an Engine or a container requires little execution time. If an Engine has the same data as another Engine but it needs its own data to modify, the makeOwnCopy member function creates such a copy.
Engines are rarely explicitly declared. Instead a container is declared using an Engine tag, and the container creates the specified Engine to deal with its values. For example, a Brick Engine is explicitly declared as Engine<D,T,Brick>, but they are more frequently created by containers, e.g., Array<D,T,Brick>. An Engine's first two template parameters specify the domain's dimensionality and the value type, as described in Section 5.4. Unlike container declarations, the third template parameter, the Engine tag, specifies which Engine specialization to use. For example, the Brick Engine tag indicates a Brick Engine should be used. Some Engines, such as CompFwd, are rarely declared even using Engine tags. Instead the Array's comp and readComp member functions return views of containers using CompFwd Engines.