Skip to content

Stencil

Martin Hartl edited this page Jan 3, 2018 · 1 revision

allscale::api::user::algorithm::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.

Signatures

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
);

Examples

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.

Clone this wiki locally