Skip to content

Onboarding Problem

You will implement two components of a 2D heat diffusion solver:

  1. A grid class that stores the 2D temperature field in memory.
  2. The stencil kernel that advances the simulation by one time step.

Everything else, i.e., the time integrator, boundary conditions, initial conditions, and I/O, is provided. You DO NOT need to know any thermal physics or theory.

Heat spreads across a 2D surface according to the following Partial Differential Equation:

∂T∂t  =  α(∂2T∂x2+∂2T∂y2)\frac{\partial T}{\partial t} \;=\; \alpha \left( \frac{\partial^2 T}{\partial x^2} + \frac{\partial^2 T}{\partial y^2} \right)

where T(x,y,t)T(x,y,t) is the temperature and α\alpha is a constant. This is provided for context only.

Replace the continuous derivatives with finite differences:

Tt+1(i,j)=Tt(i,j)+Δt⋅α ⁣(Tt(i+1,j)−2Tt(i,j)+Tt(i−1,j)Δx2+Tt(i,j+1)−2Tt(i,j)+Tt(i,j−1)Δy2)T_{t+1}(i,j) = T_t(i,j) + \Delta t \cdot \alpha \!\left( \frac{T_t(i{+}1,j) - 2T_t(i,j) + T_t(i{-}1,j)}{\Delta x^2} + \frac{T_t(i,j{+}1) - 2T_t(i,j) + T_t(i,j{-}1)}{\Delta y^2} \right)

With constants α\alpha, Δx\Delta x, Δy\Delta y, and Δt\Delta t chosen for stability, the equation simplifies to the following:

  Tt+1(i,j)  =  0.5⋅Tt(i,j)  +  0.125⋅(Tt(i−1,j)+Tt(i+1,j)+Tt(i,j−1)+Tt(i,j+1))  \boxed{\;T_{t+1}(i,j) \;=\; 0.5 \cdot T_t(i,j) \;+\; 0.125 \cdot \bigl( T_t(i{-}1,j) + T_t(i{+}1,j) + T_t(i,j{-}1) + T_t(i,j{+}1) \bigr)\;}

Notice how this is just a weighted average. In code, the above could look something like this:

new_arr[i][j] = 0.5 * old_arr[i][j] +
0.125 * (old_arr[i-1][j] + old_arr[i+1][j] +
old_arr[i][j-1] + old_arr[i][j+1]);

To provide a more visual understanding and intuition of what is happening, consider the diagram below. You can picture the heat from each “bubble” (grid cell) leaking into its neighbors.

The five-point stencil

The cell at i,j is weighted by 0.5 and each of its four neighbours — i,j+1 above, i,j-1 below, i-1,j to the left and i+1,j to the right — is weighted by 0.125.

i,j+1i,j-1i-1,ji+1,ji,j×0.125×0.125×0.125×0.125×0.5centerneighbor

Applying that update over and over is the whole simulation. Below, a hot block at the centre of a grid spreads into the cells around it as the stencil is applied repeatedly:

Heat spreading across the gridAn 11 by 11 grid seeded with a hot three-by-three block at its centre. Repeatedly applying the five-point stencil spreads that heat outwards into the surrounding cells, and the peak temperature falls as it does. The outermost ring is boundary and never changes.hotcoolboundaryinterior
The same five-point update applied repeatedly. Heat spreads outward from the centre and the peak falls, because every cell is being averaged with its neighbours. Only the interior is updated; the boundary ring is copied unchanged.

Design and implement a Grid class that stores a 2D field of double values with rows rows and cols columns. In the notation above, index i selects the row and index j selects the column.

Both this class and the stencil function go in src/submission.hpp, which ships with the interface declared and no implementations:

#pragma once
#include <cstddef>
class Grid {
private:
std::size_t rows_;
std::size_t cols_;
public:
Grid(std::size_t rows, std::size_t cols);
double& operator()(std::size_t i, std::size_t j);
double operator()(std::size_t i, std::size_t j) const;
};

The two operator() overloads give read/write and read-only access to the cell at row i, column j. The evaluation harness uses only these overloads to set initial conditions and read back your results, so they must work no matter how you store the field internally. Keep this interface; everything else is yours.

Requirements:

  1. Default-initialized to zero.
  2. Implement both operator() overloads.
  3. You are free to choose the memory layout and overall design of the Grid class.

Note that the harness never asks your Grid for its dimensions, so no size accessors are prescribed. Your stencil will still need the dimensions, which makes exposing them part of your design.

Implement the function that applies the five-point stencil over all interior grid points (1≤i<rows−11 \leq i < \text{rows} - 1,   1≤j<cols−1\; 1 \leq j < \text{cols} - 1). It is declared in the same file:

void apply_stencil(const Grid& old_grid, Grid& new_grid);

When it returns, new_grid must hold a complete field:

  1. Every interior point is the weighted average given above, computed from old_grid.
  2. Every boundary point — the outermost row and column on each side — is copied from old_grid unchanged. You are not required to implement any boundary conditions.

old_grid must not be modified.

Submissions are evaluated on correctness, implementation quality, design decisions, and performance (wall-clock execution time on a team benchmark machine). Correctness alone is not sufficient: applications are reviewed holistically, and advancement is not determined by benchmark score alone.

Clearing the score threshold means your code compiles, is correct, and runs at a reasonable speed. It is a floor, not a target. Once you are past it, the performance design guide covers where to look to improve your design.

Your submission must be C++17 — see Complete the exercise for the build details. Within C++17 you may use any standard library facility and add whatever methods, helper functions, or internal data structures you need.

Fork the template, implement your solution in src/submission.hpp, and open a pull request against UWHPC/onboarding-template. The evaluator runs automatically on your pull request and, within about a minute, posts a result comment with your build, test, and benchmark results and your score. A failing comment names the category of failure (for example, “non-square grids”) but never the specific hidden case. Push more commits to iterate; the comment updates in place and the check turns green once your submission passes.

Your pull request is automatically converted to a draft — this is expected. Do not merge it; it stays open as your submission record for us to review.

After you submit, join the UWHPC Discord from our contact page and post in the onboarding forum channel with your name and a link to your pull request.

We will then invite you to a short virtual chat about your design. Be ready to explain how you interpreted the requirements, the design choices you made, how you verified correctness and evaluated the result, and the tradeoffs you considered. This is a discussion of your own reasoning, not a request for a particular implementation.

We encourage using AI to learn and explore ideas. That said, your submission should be your own work; copy-pasting from AI will be obvious, especially during the chat.

Good luck.