# Sub Grids and Grid Transformations

## Sub Grids

Let’s imagine you have a function that takes a `Grid`

as an argument and modifies its values by iterating over all its entries and applying a transformation. An example is given below.

template<class GridType> void squareValues(GridType &grid) { Array<int, 2> lo = grid.getLo(); Array<int, 2> hi = grid.getHi(); for (int i=lo[0]; i<=hi[0]; ++i) for (int j=lo[1]; j<=hi[1]; ++j) grid(i,j) = grid(i,j)*grid(i,j); }

Note how in this example the function is templated using `GridType`

and we do not explicitely fix the type of the grid. This is not just needed for the sake of what comes next. As we have seen in the previous sections, the behaviour `Grid`

class can be fine tuned by its template arguments, such as storage policies and argument checking policies. Changing the parameters results in a change of the C++ type of the grid. In order to be flexible, it is often advisable to use template arguments to create a generic function.

Back to the real motivation for this section. The function above takes the range of indices from the `grid`

parameter using the `getLo()`

and `getHi()`

functions. Now, let’s assume next that you have a `Grid`

object with values such as the following.

Array<int,2> lo(10,10); Array<int,2> hi(20,20); Grid<int, 2> grid(lo,hi); grid = 2;

All the values in the grid are now set to 2. Finally imagine that you want to apply the `squareValues()`

function to the grid, but not to all its values. Instead you want to apply it only to the interior, leaving a border of 3 untouched.

Range<int, 2> range(grid.getLo(), grid.getHi()); range.grow(-3);

The `range`

in the code snippet above now includes the region in the grid that should be modified. Using the `SubGrid`

class template it is now possible to construct a grid object that provides a window only to the values in the range.

SubGrid<Grid<int, 2> > subGrid(range,grid); squareValues(subGrid);

The `SubGrid`

takes two template arguments. The first argument is the grid type that should be wrapped. The second argument is optional and provides the checking policy that should be used. This defaults to `GridNoArgCheck`

but can be any checking policy that can also be applied to the `Grid`

class.

The result of the example is a grid with the following values.

2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 4 4 4 4 4 2 2 2 2 2 2 4 4 4 4 4 2 2 2 2 2 2 4 4 4 4 4 2 2 2 2 2 2 4 4 4 4 4 2 2 2 2 2 2 4 4 4 4 4 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2

The code for this example on subgrids can be found here.

## Grid Transformations

Schnek also allows you to apply *on-the-fly* transformations to values in a grid. A transformation is a wrapper around a `Grid`

object that calculates modified values on demand. This can be useful if you have a large grid with data that you don’t want to copy. If some function needs to operate on modified data derived from that grid then transformations can be helpful.

Let’s start with such a function. The example below defines a function that calculates the square of the discrete Laplace operator of an input grid and stores it in an output grid.

template<class GridTypeIn, class GridTypeOut> void laplace_sq(GridTypeIn &gridIn, GridTypeOut &gridOut) { Array<int,2> lo = gridIn.getLo(); Array<int,2> hi = gridIn.getHi(); for (int i=lo[0]+1; i<hi[0]; ++i) for (int j=lo[1]+1; j<hi[1]; ++j) { double laplace = gridIn(i-1,j) + gridIn(i+1,j) + gridIn(i,j-1) + gridIn(i,j+1) - 4*gridIn(i,j); gridOut(i,j) = laplace*laplace; } }

This function uses a standard five point stencil to apply the discrete Laplace operator to the `gridIn`

parameter. It then stores the squared result in the `gridOut`

parameter. Let’s construct an input grid and fill it with some values.

const int N = 200; Array<int,2> lo(-N,-N); Array<int,2> hi( N, N); Grid<double, 2> grid(lo, hi); for (int i=lo[0]; i<=hi[0]; ++i) for (int j=lo[1]; j<=hi[1]; ++j) grid(i,j) = sqrt(i*i +j*j);

The values in this grid contain the distance to the `(0,0)`

coordinate. We could apply the `laplace_sq()`

function to this grid straight away, but this would not be very interesting. Instead, we want to first apply a transformation to it. Transformations are encapsulated in function object, such as the following.

class SuperGauss { private: double r; public: SuperGauss(double r_ = 1.0) : r(r_) {} double operator()(double x) const { return exp(-std::pow(x/r, 4)); } };

The class `SuperGauss`

defines a function operator that takes a `double x`

as parameter and returns a transformed `double`

. In our case the transformation is given by a super-Gaussian function of order 4. We are now ready to construct a transformed view onto the data stored in the `grid`

object.

GridTransform<Grid<double, 2>, SuperGauss> gridT(grid);

`gridT`

is a `GridTransform`

object that returns the values of the underlying `grid`

, after they have passed through the `SuperGauss`

transformation. The transformation has been created using its default constructor. If you wish to replace this default constructed transformation you can replace it using the `setTransformation()`

method.

gridT.setTransformation(SuperGauss(100.0));

Note that `gridT`

does not store any transformed values. The function operator is evaluated every time a grid value is accessed. If the transformation is a lengthy calculation this means that there is a penalty for re-calculating elements in the grid multiple times.

The transformed grid can now be fed into the `laplace_sq()`

function. This will result in the calculation of the squared Laplacian of the super-Gaussian, as seen below.

The code for this example on grid transformations can be found here.