-
Notifications
You must be signed in to change notification settings - Fork 5
Stencil
Defined in header "allscale/api/user/algorithm/stencil.h"
The stencil
operator is applied to a container and executes an update function a given number of times.
The iterations are in parallel but executions are synchronized with the neighbor cells.
The below signature is the most general signature of the stencil
operator.
template<
typename Impl = implementation::fine_grained_iterative, typename Container, typename InnerUpdate, typename BoundaryUpdate,
typename ... ObserverTimeFilters, typename ... ObserverLocationFilters, typename ... ObserverActions
>
std::enable_if_t<!is_observer<BoundaryUpdate>::value,stencil_reference<Impl>> stencil(
// container to which the update function is applied
Container& res,
// number of iterations
std::size_t steps,
// update function for the inner cells of the container
const InnerUpdate& innerUpdate,
// update function for the boundary cells of the container
const BoundaryUpdate& boundaryUpdate,
// observers
const Observer<ObserverTimeFilters,ObserverLocationFilters,ObserverActions>& ... observers
);
There is also a signature that only allows for a single update function for all cells of the container.
If you use the update
function as the innerUpdate
and boundaryUpdate
function in the signature above you will get the same result.
template<
typename Impl = implementation::fine_grained_iterative, typename Container, typename Update,
typename ... ObserverTimeFilters, typename ... ObserverLocationFilters, typename ... ObserverActions
>
stencil_reference<Impl> stencil(
// container to which the update function is applied
Container& res,
// number of iterations
std::size_t steps,
// update function for the all cells of the container
const Update& update,
// observers
const Observer<ObserverTimeFilters,ObserverLocationFilters,ObserverActions>& ... observers
);
The stencil takes optional observers which can be created by using the observer function. The observer will be executed after the update function of the particular cell.
template<typename TimeStampFilter, typename LocationFilter, typename Action>
Observer<TimeStampFilter,LocationFilter,Action> observer(
// function that takes the current time step and returns true if the observer should be executed
const TimeStampFilter& timeFilter,
// function that takes a location of the container and returns true if the observer should be executed
const LocationFilter& locationFilter,
// function that is executed if the timeFilter and locationFilter return true
// function takes the time step, location and value as arguments
const Action& action
);
The following examples use a StaticGrid
as the container elements.
const int N = 200;
const int T = 100;
using Grid = data::StaticGrid<int,N,N>;
using Point = Grid::coordinate_type;
Grid temp;
temp.pforEach([](int& value) { value = 0; });
// compute simulation steps
stencil(
temp,
T,
[](time_t, const Point& p, const Grid& temp) {
return temp[p] + 1;
}
);
temp.pforEach([](int& value) {
assert_eq(T, value);
});
The code above initializes each cell of the grid with value zero. The update function for the stencil operator increases each element of the grid by one. In the end we check if each value is equal to the number of iterations we executed the update function.
const int N = 20;
const int T = 100;
using Grid = data::StaticGrid<int,N,N>;
using Point = Grid::coordinate_type;
Grid temp;
int observer_count = 0;
temp.pforEach([](int& value) { value = 0; });
// compute simulation steps
stencil(
temp,
T,
// inner elements
[](time_t, const Point& p, const Grid& temp) {
return temp[p] + 1;
},
// boundaries
[](time_t, const Point&, const Grid&) {
return 0;
},
observer(
[](time_t t) { return t % 10 == 0; },
[N](const Point& loc) { return loc.x == N / 2 && loc.y == N / 2; },
[&observer_count,N](time_t t, const Point& loc, int& value) {
assert_eq(0, t % 10);
assert_eq(N / 2, loc.x);
assert_eq(N / 2, loc.y);
assert_eq(observer_count * 10, t);
observer_count++;
}
)
);
pfor(Point(0), temp.size(), [&](const Point& p) {
if(p.x == 0 || p.y == 0 || p.x == temp.size()[0] - 1 || p.y == temp.size()[1] - 1) {
assert_eq(0, temp[p]);
}
else {
assert_eq(T, temp[p]);
}
});
assert_eq(10, observer_count);
The code above extends the previous code. We add an extra function for boundary values and set them to zero. After the stencil operator the values of the inner elements are still equal to the number of iterations but zero for the boundary elements.
In addition to the new boundary update function there is also an observer. It is only active every 10th iteration and only executes on the center element of the grid. The observer updates a counter which contains the expected value after the call to the stencil function.
Part of the AllScale project - http://www.allscale.eu