DDC 0.0.0

a discrete domain computation library

examples/heat_equation.cpp

1// Copyright (C) The DDC development team, see COPYRIGHT.md file
2//
3// SPDX-License-Identifier: MIT
4
6#include <cmath>
7#include <iomanip>
8#include <iostream>
9#include <numeric>
10
11#include <ddc/ddc.hpp>
13
14
17struct X;
19
22struct DDimX : ddc::UniformPointSampling<X>
23{
24};
26
28// Our second continuous dimension
29struct Y;
30// Its uniform discretization
31struct DDimY : ddc::UniformPointSampling<Y>
32{
33};
35
37// Our simulated time dimension
38struct T;
39// Its uniform discretization
40struct DDimT : ddc::UniformPointSampling<T>
41{
42};
44
45
47
53template <class ChunkType>
54void display(double time, ChunkType temp)
55{
56 // For `Chunk`/`ChunkSpan`, the ()-operator is used to access stored values with a single `DiscreteElement` as
57 // input. So it is used here as a function that maps indices of the temperature domain
58 // to the temperature value at that point
59 double const mean_temp = ddc::transform_reduce(
60 temp.domain(),
61 0.,
63 temp)
64 / temp.domain().size();
65 std::cout << std::fixed << std::setprecision(3);
66 std::cout << "At t = " << time << ",\n";
67 std::cout << " * mean temperature = " << mean_temp << "\n";
68 // take a slice in the middle of the box
69 ddc::ChunkSpan temp_slice
70 = temp[ddc::get_domain<DDimY>(temp).front()
71 + ddc::get_domain<DDimY>(temp).size() / 2];
72 std::cout << " * temperature[y:"
73 << ddc::get_domain<DDimY>(temp).size() / 2 << "] = {";
76 [=](ddc::DiscreteElement<DDimX> const ix) {
77 std::cout << std::setw(6) << temp_slice(ix);
78 });
79 std::cout << " }" << std::endl;
80
81#ifdef DDC_BUILD_PDI_WRAPPER
82 ddc::PdiEvent("display")
83 .with("temp", temp)
84 .and_with("mean_temp", mean_temp)
85 .and_with("temp_slice", temp_slice);
86#endif
87}
89
90
92int main(int argc, char** argv)
93{
94#ifdef DDC_BUILD_PDI_WRAPPER
95 auto pdi_conf = PC_parse_string("");
96 PDI_init(pdi_conf);
97#endif
98 Kokkos::ScopeGuard const kokkos_scope(argc, argv);
99 ddc::ScopeGuard const ddc_scope(argc, argv);
100
101 // some parameters that would typically be read from some form of
102 // configuration file in a more realistic code
103
105 // Start of the domain of interest in the X dimension
106 double const x_start = -1.;
107 // End of the domain of interest in the X dimension
108 double const x_end = 1.;
109 // Number of discretization points in the X dimension
110 size_t const nb_x_points = 10;
111 // Thermal diffusion coefficient
112 double const kx = .01;
113 // Start of the domain of interest in the Y dimension
114 double const y_start = -1.;
115 // End of the domain of interest in the Y dimension
116 double const y_end = 1.;
117 // Number of discretization points in the Y dimension
118 size_t const nb_y_points = 100;
119 // Thermal diffusion coefficient
120 double const ky = .002;
121 // Simulated time at which to start simulation
122 double const start_time = 0.;
123 // Simulated time to reach as target of the simulation
124 double const end_time = 10.;
125 // Number of time-steps between outputs
126 ptrdiff_t const t_output_period = 10;
128
131 // Number of ghost points to use on each side in X
132 ddc::DiscreteVector<DDimX> static constexpr gwx {1};
134
136 // Initialization of the global domain in X with gwx ghost points on
137 // each side
138 auto const [x_domain, ghosted_x_domain, x_pre_ghost, x_post_ghost]
139 = ddc::init_discrete_space<DDimX>(DDimX::init_ghosted<DDimX>(
140 ddc::Coordinate<X>(x_start),
141 ddc::Coordinate<X>(x_end),
142 ddc::DiscreteVector<DDimX>(nb_x_points),
143 gwx));
145
147 // our zone at the start of the domain that will be mirrored to the
148 // ghost
150 x_domain_begin(x_domain.front(), x_post_ghost.extents());
151 // our zone at the end of the domain that will be mirrored to the
152 // ghost
153 ddc::DiscreteDomain<DDimX> const x_domain_end(
154 x_domain.back() - x_pre_ghost.extents() + 1,
155 x_pre_ghost.extents());
157
159 // Number of ghost points to use on each side in Y
160 ddc::DiscreteVector<DDimY> static constexpr gwy {1};
161
162 // Initialization of the global domain in Y with gwy ghost points on
163 // each side
164 auto const [y_domain, ghosted_y_domain, y_pre_ghost, y_post_ghost]
165 = ddc::init_discrete_space<DDimY>(DDimY::init_ghosted<DDimY>(
166 ddc::Coordinate<Y>(y_start),
167 ddc::Coordinate<Y>(y_end),
168 ddc::DiscreteVector<DDimY>(nb_y_points),
169 gwy));
170
171 // our zone at the start of the domain that will be mirrored to the
172 // ghost
174 y_domain_begin(y_domain.front(), y_post_ghost.extents());
175 // our zone at the end of the domain that will be mirrored to the
176 // ghost
177 ddc::DiscreteDomain<DDimY> const y_domain_end(
178 y_domain.back() - y_pre_ghost.extents() + 1,
179 y_pre_ghost.extents());
181
183 // max(1/dx^2)
184 double const invdx2_max = ddc::transform_reduce(
185 x_domain,
186 0.,
189 return 1.
192 });
193 // max(1/dy^2)
194 double const invdy2_max = ddc::transform_reduce(
195 y_domain,
196 0.,
199 return 1.
202 });
203 ddc::Coordinate<T> const max_dt {
204 .5 / (kx * invdx2_max + ky * invdy2_max)};
205
206 // number of time intervals required to reach the end time
207 // The + .2 is used to make sure it is rounded to the correct number of steps
208 ddc::DiscreteVector<DDimT> const nb_time_steps {
209 std::ceil((end_time - start_time) / max_dt) + .2};
210 // Initialization of the global domain in time:
211 // - the number of discrete time-points is equal to the number of
212 // steps + 1
213 // `init` takes required information to initialize the attributes of the dimension.
214 ddc::DiscreteDomain<DDimT> const time_domain
215 = ddc::init_discrete_space<DDimT>(DDimT::init<DDimT>(
216 ddc::Coordinate<T>(start_time),
217 ddc::Coordinate<T>(end_time),
218 nb_time_steps + 1));
220
222 // Maps temperature into the full domain (including ghosts) twice:
223 // - once for the last fully computed time-step
224 ddc::Chunk ghosted_last_temp(
225 "ghosted_last_temp",
227 DDimX,
228 DDimY>(ghosted_x_domain, ghosted_y_domain),
230
231 // - once for time-step being computed
232 // The `DeviceAllocator` is responsible for allocating memory on the default memory space.
233 ddc::Chunk ghosted_next_temp(
234 "ghosted_next_temp",
236 DDimX,
237 DDimY>(ghosted_x_domain, ghosted_y_domain),
240
242 // The const qualifier makes it clear that ghosted_initial_temp always references
243 // the same chunk, `ghosted_last_temp` in this case
244 ddc::ChunkSpan const ghosted_initial_temp
245 = ghosted_last_temp.span_view();
246 // Initialize the temperature on the main domain
248 ddc::DiscreteDomain<DDimX, DDimY>(x_domain, y_domain),
249 KOKKOS_LAMBDA(ddc::DiscreteElement<DDimX, DDimY> const ixy) {
250 double const x
252 double const y
254 ghosted_initial_temp(ixy)
255 = 9.999 * ((x * x + y * y) < 0.25);
256 });
258
259 ddc::Chunk ghosted_temp(
260 "ghost_temp",
262 DDimX,
263 DDimY>(ghosted_x_domain, ghosted_y_domain),
265
266
268 // display the initial data
269 ddc::parallel_deepcopy(ghosted_temp, ghosted_last_temp);
270 display(ddc::coordinate(time_domain.front()),
271 ghosted_temp[x_domain][y_domain]);
272 // time of the iteration where the last output happened
273 ddc::DiscreteElement<DDimT> last_output_iter = time_domain.front();
275
277 for (auto const iter :
278 time_domain.remove_first(ddc::DiscreteVector<DDimT>(1))) {
280
282 // Periodic boundary conditions
284 ghosted_last_temp[x_pre_ghost][y_domain],
285 ghosted_last_temp[y_domain][x_domain_end]);
287 ghosted_last_temp[y_domain][x_post_ghost],
288 ghosted_last_temp[y_domain][x_domain_begin]);
290 ghosted_last_temp[x_domain][y_pre_ghost],
291 ghosted_last_temp[x_domain][y_domain_end]);
293 ghosted_last_temp[x_domain][y_post_ghost],
294 ghosted_last_temp[x_domain][y_domain_begin]);
296
298 // a span excluding ghosts of the temperature at the time-step we
299 // will build
300 ddc::ChunkSpan const next_temp {
301 ghosted_next_temp[x_domain][y_domain]};
302 // a read-only view of the temperature at the previous time-step
303 // span_cview returns a read-only ChunkSpan
304 ddc::ChunkSpan const last_temp {ghosted_last_temp.span_cview()};
306
308 // Stencil computation on the main domain
310 next_temp.domain(),
311 KOKKOS_LAMBDA(
314 = ddc::select<DDimX>(ixy);
316 = ddc::select<DDimY>(ixy);
317 double const dx_l = ddc::distance_at_left(ix);
318 double const dx_r = ddc::distance_at_right(ix);
319 double const dx_m = 0.5 * (dx_l + dx_r);
320 double const dy_l = ddc::distance_at_left(iy);
321 double const dy_r = ddc::distance_at_right(iy);
322 double const dy_m = 0.5 * (dy_l + dy_r);
323 next_temp(ix, iy) = last_temp(ix, iy);
324 next_temp(ix, iy)
325 += kx * ddc::step<DDimT>()
326 * (dx_l * last_temp(ix + 1, iy)
327 - 2.0 * dx_m * last_temp(ix, iy)
328 + dx_r * last_temp(ix - 1, iy))
329 / (dx_l * dx_m * dx_r);
330 next_temp(ix, iy)
331 += ky * ddc::step<DDimT>()
332 * (dy_l * last_temp(ix, iy + 1)
333 - 2.0 * dy_m * last_temp(ix, iy)
334 + dy_r * last_temp(ix, iy - 1))
335 / (dy_l * dy_m * dy_r);
336 });
338
340 if (iter - last_output_iter >= t_output_period) {
341 last_output_iter = iter;
342 ddc::parallel_deepcopy(ghosted_temp, ghosted_last_temp);
343 display(ddc::coordinate(iter),
344 ghosted_temp[x_domain][y_domain]);
345 }
347
349 // Swap our two buffers
350 std::swap(ghosted_last_temp, ghosted_next_temp);
352 }
353
355 if (last_output_iter < time_domain.back()) {
356 ddc::parallel_deepcopy(ghosted_temp, ghosted_last_temp);
357 display(ddc::coordinate(time_domain.back()),
358 ghosted_temp[x_domain][y_domain]);
359 }
361
362#ifdef DDC_BUILD_PDI_WRAPPER
363 PDI_finalize();
364 PC_tree_destroy(&pdi_conf);
365#endif
366}
Definition discrete_domain.hpp:51
KOKKOS_FUNCTION constexpr discrete_element_type front() const noexcept
Definition discrete_domain.hpp:154
KOKKOS_FUNCTION constexpr discrete_element_type back() const noexcept
Definition discrete_domain.hpp:159
A DiscreteElement identifies an element of the discrete dimension.
Definition discrete_element.hpp:146
A DiscreteVector is a vector in the discrete dimension.
Definition discrete_vector.hpp:254
Definition kokkos_allocator.hpp:17
Definition pdi.hpp:29
PdiEvent & and_with(std::string const &name, T &&t)
Definition pdi.hpp:108
PdiEvent & with(std::string const &name, BorrowedChunk &&data)
Definition pdi.hpp:63
Definition scope_guard.hpp:17
UniformPointSampling models a uniform discretization of the provided continuous dimension.
Definition uniform_point_sampling.hpp:36
The top-level namespace of DDC.
Definition aligned_allocator.hpp:11
constexpr bool enable_chunk
Definition chunk_traits.hpp:16
auto parallel_deepcopy(ChunkDst &&dst, ChunkSrc &&src)
Copy the content of a borrowed chunk into another.
Definition parallel_deepcopy.hpp:21
void for_each(DiscreteDomain< DDims... > const &domain, Functor &&f) noexcept
iterates over a nD domain in serial
Definition for_each.hpp:48
KOKKOS_FUNCTION Coordinate< typename DDim::continuous_dimension_type... > coordinate(DiscreteElement< DDim... > const &c)
Definition coordinate.hpp:29
T transform_reduce(DiscreteDomain< DDims... > const &domain, T neutral, BinaryReductionOp &&reduce, UnaryTransformOp &&transform) noexcept
A reduction over a nD domain in serial.
Definition transform_reduce.hpp:72
void parallel_for_each(std::string const &label, ExecSpace const &execution_space, DiscreteDomain< DDims... > const &domain, Functor &&f) noexcept
iterates over a nD domain using a given Kokkos execution space
Definition parallel_for_each.hpp:155
KOKKOS_FUNCTION Coordinate< typename DDim::continuous_dimension_type > distance_at_left(DiscreteElement< DDim > i)
Definition non_uniform_point_sampling.hpp:161
detail::TaggedVector< CoordinateElement, CDims... > Coordinate
A Coordinate represents a coordinate in the continuous space.
Definition coordinate.hpp:26
KOKKOS_FUNCTION Coordinate< typename DDim::continuous_dimension_type > distance_at_right(DiscreteElement< DDim > i)
Definition non_uniform_point_sampling.hpp:168
Definition chunk.hpp:21
Definition chunk_span.hpp:30
Definition reducer.hpp:109
Definition reducer.hpp:15