- PAD
- PAD is the default and means that the cursor size supplied by the user is kept fixed. But if the cursor overhangs the Lattice the part that overhangs is filled with a default value that is specified by the Iterator. Currently the default value is zero.
- RESIZE
- RESIZE means that the cursor shape is adjusted whenever it approaches the edges of the Lattice so that it is always the right size to include only the parts of the Lattice that are available. The user specified cursor shape now becomes the default and largest possible cursor shape.
In constructing a LatticeStepper, you specify the Lattice shape and the shape of the "cursor" used to step through the data. The cursor position can be incremented or decremented to retrieve the next portion of the Lattice. The specified cursor shape can (and often will) have fewer dimensions that the Lattice itself. For example if we have a 4-dimensional Lattice with latticeShape = IPosition(4,64,64,4,16), then specifying a cursor of cursorShape = IPosition(1,64), will step through the hypercube row by row. When the cursor shape has fewer dimensions than the Lattice degenerate dimensions are added to the end of the cursor so that in the above example the specified cursor is assumed to mean cursorShape = IPosition(4,64,1,1,1). To access the data spectrum by spectrum (assuming the last axis is the spectral axis), you must use a 1-dimensional cursor of IPosition(4,1,1,1,16). The cursorShape function always returns a shape with as many dimensions as the underlying Lattice.
It is an error (and an exception will be thrown) if the cursor has more
dimensions than the Lattice or if it is larger on any axis than the
Lattice shape.
Also the cursor shape on all axes must be less than or equal to the Lattice
shape on that axis. Otherwise an exception will be thrown.
In principle cursor axes with length 1 are degenerate axes. They are removed from the lattice cursor if the LatticeIterator cursor is accessed using e.g. the matrixCursor function. Using a special LatticeStepper constructor it is, however, possible to specify which cursor axes with length 1 have to be treated as normal axes. In that way one can be sure that a cursor is, for example, always 2D, even if an axis happens to have length 1.
IPosition latticeShape(4,20,16,1,4); IPosition cursorAxes(2,1,2); IPosition cursorShape(2,16,1); IPosition axisPath; LatticeStepper stepper(latticeShape, cursorShape, cursorAxes, axisPath);This results in a cursor with shape [1,16,1,1]. The first and last axis are degenerate, so the cursor can also be accessed using matrixCursor (with shape [16,1]). Note that the cursor shape could also be specified as [1,16,1,1].
The "path" of the cursor through the Lattice can be controlled by specifying an axisPath during construction of the class. This is an IPosition which has exactly as many elements as the Lattice dimension. Each element must contain an integer between 0 -- Lattice_Dimension-1, and must be unique. For example,
axisPath = IPosition(4,0,1,2,3) or axisPath = IPosition(4,3,1,2,0)are valid but
axisPath = IPosition(4,1,2,3,4) or axisPath = IPosition(4,0,1,1,3)are not, given the latticeShape specified above. An exception is thrown if the AxisPath is bad.
The cursor never changes dimensionality as it traverses the Lattice. But it
may change shape if the cursor shape is not a factor of the Lattice
shape. A cursor shape is not a factor of the Lattice shape if the Lattice
shape is not an integer multiple of the cursor shape on all axes.
The integer multiplier need not to be the same for each axes.
For example, for a Lattice of shape [10,10,10] a cursor of shape [8,5,2]
is not a factor but one with a shape of [10,5,1] is.
When the cursor is not congruent with the Lattice moving the cursor through
the Lattice will sometimes result in part of the cursor hanging over the
edge of the Lattice. When this occurs the hangOver member function will
return True. What to do in these situtations is specified by the
hangOverPolicy enumerator.
void averageFluxByChannel(const Lattice<Float>& data) { // for convenience, get the shape into a local variable IPosition latticeShape = data.shape(); cout << "Data has shape: " << latticeShape << endl; // check that the data has 4 axes. DebugAssert(latticeShape.nelements() == 4, AipsError); // specify the cursor, or window shape. Here the cursor is a matrix // that is the shape of the first plane of our Lattice. // For convenience, get the first two axis lengths into local vars uInt nCols = latticeShape(0); uInt nRows = latticeShape(1); IPosition cursorShape(2, nCols, nRows); // construct a stepper, which needs to know the shape of the lattice // and the shape of the iterator's cursor. By using cursorShape, which // is directly determined by the lattice's shape, we can be sure // that the cursor is a factor of the lattice, and thus that // all elements will be picked up efficiently during the traversal. // Because we will not be iterating through the stokes axis this axis // is made the slowest moving one. IPosition axisPath(4, 0, 1, 3, 2) LatticeStepper stepper(latticeShape, cursorShape, axisPath); // Subsection the stepper so that it only iterates through the I // Stokes parameter (assumed to be when the third axis is zero) uInt nFreqs = latticeShape(3); IPosition blc(4, 0, 0, 0, 0), trc(4, nCols-1, nRows-1, 0, nFreqs-1); stepper.subSection(blc, trc); // construct the iterator. Since we only want to read the Data, // use the read-only class, which disallows writing back to the cursor // (and hence is more efficient). RO_LatticeIterator<Float> iterator(data, stepper); Vector<Float> spectrum(nFreqs); spectrum = 0.0; uInt channel = 0; for (iterator.reset(); !iterator.atEnd(); iterator++) { const Matrix<Float>& cursor = iterator.matrixCursor(); for (uInt col = 0; col < nCols; col++) { for (uInt row = 0; row < nRows; row++) { spectrum(channel) += cursor(col, row); } } channel++; } // for iterator cout << "Average spectrum is: " << spectrum / cursorShape.product() << endl; }
Same as the above constructor except that the axis path is explicitly specified. The axis path is described in the synopsis above.
Same as the above constructor except that the cursor axes are
explicitly specified. This can be useful to avoid that cursor axes
with length=1 are treated as degenerated axes by the Iterator classes.
The following rules have to be obeyed:
- cursorAxes.nelements() <= latticeShape.nelements()
- cursorShape.nelements() == latticeShape.nelements()
or cursorShape.nelements() == cursorAxes.nelements()
The latter means that the cursorShape contains the axes mentioned in
cursorAxes.
See also the example in the synopsis.
The copy constructor uses copy semantics.
The assignment operator uses copy semantics.
Increment operator (postfix version) - move the cursor forward one step. Returns True if the cursor was moved.
Decrement operator (postfix version) - move the cursor backwards one step. Returns True if the cursor was moved.
Function to move the cursor to the beginning of the (sub)-Lattice. Also resets the number of steps (nsteps function) to zero.
Function which returns "True" if the cursor is at the beginning of the (sub)-Lattice, otherwise, returns "False"
Function which returns "True" if an attempt has been made to increment the cursor beyond the end of the (sub)-Lattice.
Function to return the number of steps (increments & decrements) taken since construction (or since last reset). This is a running count of all cursor movement (operator++ or operator--), even though N-increments followed by N-decrements will ALWAYS leave the cursor in the original position.
Functions which return the current position of the beginning of the cursor. The position function is relative to the origin in the main Lattice and the relativePosition function is relative to the origin and increment used in the sub-Lattice (defined using the subSection function). If no sub-Lattice is defined the two functions return identical positions.
Functions which return the current position of the end of the cursor. The endPosition function is relative to the origin in the main Lattice and the relativeEndPosition function is relative to the origin and increment used in the sub-Lattice (defined using the subSection function). If no sub-Lattice is defined the two functions return identical positions.
It returns the end position in the lattice and does not take overhang into account.
Functions which return the shape of the Lattice being iterated through. latticeShape always returns the shape of the main Lattice while subLatticeShape returns the shape of any sub-Lattice defined using the subSection function.
Functions to change the cursor shape to a new one. They always reset the cursor to the beginning of the Lattice (and reset the number of steps to zero).
Function which returns the shape of the cursor. This always includes all axes (ie. it includes degenerates axes)
Function which returns the axes of the cursor.
Function which returns "True" if the increment/decrement operators have moved the cursor position such that part of the cursor beginning or end is hanging over the edge of the (sub)-Lattice.
Functions to specify a "section" of the Lattice to step over. A section is defined in terms of the Bottom Left Corner (blc), Top Right Corner (trc), and step size (inc), on ALL of its axes, including degenerate axes. The step size defaults to one if not specified.
Return the bottom left hand corner (blc), top right corner (trc) or step size (increment) used by the current sub-Lattice. If no sub-Lattice has been defined (with the subSection function) these functions return blc=0, trc=latticeShape-1, increment=1, ie. the entire Lattice.
Return the axis path.
Function which returns a pointer to dynamic memory of an exact copy of this instance. The pointer returned by this function must be deleted externally.
Function which checks the internal data of this class for correct dimensionality and consistant values. Returns True if everything is fine otherwise returns False