-
Notifications
You must be signed in to change notification settings - Fork 1
/
2d_nonlocal_serial.cpp
415 lines (343 loc) · 12.7 KB
/
2d_nonlocal_serial.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
// Copyright (c) 2014 Hartmut Kaiser
// Copyright (c) 2014 Patricia Grubel
// Copyright (c) 2020 Pranav Gadikar
//
// SPDX-License-Identifier: BSL-1.0
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <hpx/hpx_init.hpp>
#include <hpx/hpx.hpp>
#include <cstddef>
#include <cstdint>
#include <cmath>
#include <iostream>
#include <vector>
#include <math.h>
#include <algorithm>
#include <fstream>
#include "../include/point.h"
#include "../include/print_time_results.hpp"
#include "../include/writer.h"
#include "../include/Config.h"
bool header = 1;
double k = 1; // heat transfer coefficient
double dt = 1.; // time step
double dh = 1.; // grid spacing
class solver {
public:
// 3d coordinate for representation of point
typedef util::Point3 point_3d;
// 3d plane using the 3d point representation
// Note that the plane is actually stored in a 1d fashion
typedef std::vector<point_3d> plane_3d;
// Our partition type
typedef double temperature;
// 2d space data structure
typedef std::vector<temperature> space_2d;
// alternate for space between time steps,
// result of S[t%2] goes to S[(t+1)%2] at every timestep 't'
space_2d S[2];
// vector containining 3d coordinates of the points in the plane
plane_3d P;
// nx = length of grid in x dimension
// ny = length of grid in y dimension
// nt = number of timesteps
// eps = Epsilon for influence zone of a point
// nlog = Number of time steps to log the results
long nx, ny, nt, eps, nlog;
bool current, next, test;
// l2 norm and l infinity norm
double error_l2, error_linf, c_2d;
// file to store the simulation results in csv format
const std::string simulate_fname = "../out_csv/simulate_2d.csv";
// file to store l2 and l infinity norms per timestep
const std::string score_fname = "../out_csv/score_2d.csv";
// constructor to initialize class variables and allocate
solver(long nx, long ny, long nt, long eps, long nlog) {
this->nx = nx;
this->ny = ny;
this->nt = nt;
this->eps = eps;
this->nlog = nlog;
this->c_2d = (k * 8) / pow(eps * dh, 4);
this->current = 0;
this->error_l2 = 0.0;
this->error_linf = 0.0;
this->test = 0;
this->next = 1;
P.resize(nx * ny);
for (long sx = 0; sx < nx; ++sx) {
for (long sy = 0; sy < ny; ++sy) {
P[get_loc(sx, sy)] = point_3d(sx, sy, 0);
}
}
for (auto& s : S) {
s.resize(nx * ny);
}
}
// function to compute l2 norm of the solution
void compute_l2(long time) {
error_l2 = 0;
for (long sx = 0; sx < nx; ++sx)
for (long sy = 0; sy < ny; ++sy)
error_l2 += (S[next][get_loc(sx, sy)] - w(sx, sy, time)) *
(S[next][get_loc(sx, sy)] - w(sx, sy, time));
}
// function to compute l infinity norm of the solution
void compute_linf(long time) {
error_linf = 0;
for (long sx = 0; sx < nx; ++sx)
for (long sy = 0; sy < ny; ++sy)
error_linf = std::max(
std::abs(S[next][get_loc(sx, sy)] - w(sx, sy, time)), error_linf);
}
// print error for testing
void print_error(bool cmp) {
std::cout << "l2: " << error_l2 << " linfinity: " << error_linf
<< std::endl;
if (cmp)
for (long sx = 0; sx < nx; ++sx)
for (long sy = 0; sy < ny; ++sy)
std::cout << "Expected: " << w(sx, sy, nt)
<< " Actual: " << S[nt % 2][get_loc(sx, sy)] << std::endl;
}
// print the solution for the user
void print_soln() {
for (long sx = 0; sx < nx; ++sx) {
for (long sy = 0; sy < ny; ++sy) {
std::cout << "S[" << sx << "][" << sy
<< "] = " << S[nt % 2][get_loc(sx, sy)] << " ";
}
std::cout << std::endl;
}
}
// Function to visualize in csv format and conduct various experiments
void log_vtk(long log_num) {
const std::string fname = "../out_vtk/simulate_" + std::to_string(log_num);
rw::writer::VtkWriter vtk_logger(fname);
vtk_logger.appendNodes(&P);
vtk_logger.appendPointData("Temperature", &S[next]);
vtk_logger.addTimeStep(std::time(0));
vtk_logger.close();
}
// Function to visualize in csv format and conduct various experiments
void log_csv(long time) {
std::ofstream outfile;
outfile.open(simulate_fname, std::ios_base::app);
for (long sx = 0; sx < nx; ++sx) {
for (long sy = 0; sy < ny; ++sy) {
outfile << time << "," << sx << "," << sy << ","
<< S[next][get_loc(sx, sy)] << "," << w(sx, sy, time) << ","
<< (S[next][get_loc(sx, sy)] - w(sx, sy, time)) *
(S[next][get_loc(sx, sy)] - w(sx, sy, time))
<< "," << std::abs(S[next][get_loc(sx, sy)] - w(sx, sy, time))
<< ",\n";
}
}
outfile.close();
// add to the score csv only when it's required
if (test) {
compute_l2(time);
compute_linf(time);
std::ofstream outfile;
outfile.open(score_fname, std::ios_base::app);
outfile << time << "," << error_l2 << "," << error_linf << ",\n";
outfile.close();
}
}
// input the initialization for 2d nonlocal equation
void input_init() {
test = 0;
for (long sx = 0; sx < nx; ++sx) {
for (long sy = 0; sy < ny; ++sy) {
std::cin >> S[0][get_loc(sx, sy)];
}
}
}
// init for testing the 2d nonlocal equation
void test_init() {
test = 1;
for (long sx = 0; sx < nx; ++sx) {
for (long sy = 0; sy < ny; ++sy) {
S[0][get_loc(sx, sy)] =
sin(2 * M_PI * (sx * dh)) * sin(2 * M_PI * (sy * dh));
}
}
}
// our influence function to weigh various points differently
static double influence_function(double distance) { return 1.0; }
// operator for getting the location of 2d point in 1d representation
inline long get_loc(long x, long y) { return x + y * nx; }
// testing operator to verify correctness
inline double w(long pos_x, long pos_y, long time) {
return cos(2 * M_PI * (time * dt)) * sin(2 * M_PI * (pos_x * dh)) *
sin(2 * M_PI * (pos_y * dh));
}
// condition to enforce the boundary conditions
inline double boundary(long pos_x, long pos_y, double val = 2.0) {
if (pos_x >= 0 && pos_x < nx && pos_y >= 0 && pos_y < ny)
if (val != 2.0)
return val;
else
return S[current][get_loc(pos_x, pos_y)];
else
return 0;
}
// Function to find distance in 2d plane given 2 points coordinates
double distance(long cen_x, long cen_y, long pos_x, long pos_y) {
return sqrt(((cen_x - pos_x) * (cen_x - pos_x)) +
((cen_y - pos_y) * (cen_y - pos_y)));
}
// Function to compute length of 3rd side of a right angled triangle
// given hypotenuse and lenght of one side
double len_1d_line(long len_x) { return sqrt((eps * eps) - (len_x * len_x)); }
// Following function adds the external source to the 2d nonlocal heat
// equation
double sum_local_test(long pos_x, long pos_y, long time) {
double result_local =
-(2 * M_PI * sin(2 * M_PI * (time * dt)) *
sin(2 * M_PI * (pos_x * dh)) * sin(2 * M_PI * (pos_y * dh)));
double w_position = w(pos_x, pos_y, time);
long len_line = 0;
for (long sx = pos_x - eps; sx <= pos_x + eps; ++sx) {
len_line = len_1d_line(std::abs((long)pos_x - (long)sx));
for (long sy = pos_y - len_line; sy <= pos_y + len_line; ++sy) {
result_local -=
influence_function(distance(pos_x, pos_y, sx, sy)) * c_2d *
(boundary(sx, sy, w(sx, sy, time)) - w_position) * (dh * dh);
}
}
return result_local;
}
// Our operator to find sum of 'eps' radius circle in vicinity of point P(x,y)
// Represent circle as a series of horizaontal lines of thickness 'dh'
double sum_local(long pos_x, long pos_y) {
double result_local = 0.0;
long len_line = 0;
for (long sx = pos_x - eps; sx <= pos_x + eps; ++sx) {
len_line = len_1d_line(std::abs((long)pos_x - (long)sx));
for (long sy = pos_y - len_line; sy <= pos_y + len_line; ++sy) {
result_local +=
influence_function(distance(pos_x, pos_y, sx, sy)) * c_2d *
(boundary(sx, sy) - S[current][get_loc(pos_x, pos_y)]) * (dh * dh);
}
}
return result_local;
}
// do all the work on 'nx * ny' data points for 'nt' time steps
space_2d do_work() {
// Actual time step loop
for (long t = 0; t < nt; ++t) {
current = t % 2;
next = (t + 1) % 2;
for (long sx = 0; sx < nx; ++sx) {
for (long sy = 0; sy < ny; ++sy) {
S[next][get_loc(sx, sy)] =
S[current][get_loc(sx, sy)] + (sum_local(sx, sy) * dt);
if (test) S[next][get_loc(sx, sy)] += sum_local_test(sx, sy, t) * dt;
}
}
if (t % nlog == 0) {
log_vtk(t / nlog);
log_csv(t);
}
}
next = nt % 2;
// testing the code for correctness
if (test) {
compute_l2(nt);
compute_linf(nt);
}
// Return the solution at time-step 'nt'.
return S[nt % 2];
}
};
int batch_tester(long nlog) {
std::uint64_t nx, ny, nt, eps, num_tests;
bool test_failed = 0;
std::cin >> num_tests;
for (long i = 0; i < num_tests; ++i) {
std::cin >> nx >> ny >> nt >> eps >> k >> dt >> dh;
// Create the solver object
solver solve(nx, ny, nt, eps, nlog);
solve.test_init();
solve.do_work();
if (solve.error_l2 / (double)(nx * ny) > 1e-6) {
test_failed = 1;
break;
}
}
// output regular expression whether the test passed or failed
if (test_failed)
std::cout << "Tests Failed" << std::endl;
else
std::cout << "Tests Passed" << std::endl;
return hpx::finalize();
}
int hpx_main(hpx::program_options::variables_map& vm) {
std::uint64_t nx =
vm["nx"].as<std::uint64_t>(); // Number of grid points in x dimension
std::uint64_t ny =
vm["ny"].as<std::uint64_t>(); // Number of grid points in y dimension
std::uint64_t nt = vm["nt"].as<std::uint64_t>(); // Number of steps.
std::uint64_t eps =
vm["eps"].as<std::uint64_t>(); // Epsilon for influence zone of a point
std::uint64_t nlog =
vm["nlog"]
.as<std::uint64_t>(); // Number of time steps to log the results
if (vm.count("no-header")) header = false;
// batch testing for ctesting
if (vm.count("test_batch")) return batch_tester(nlog);
// Create the solver object
solver solve(nx, ny, nt, eps, nlog);
// Take inputs from stdin for testing
if (vm.count("test"))
solve.test_init();
else
solve.input_init();
// Measure execution time.
std::uint64_t t = hpx::util::high_resolution_clock::now();
// Execute nt time steps on nx grid points.
solve.do_work();
std::uint64_t elapsed = hpx::util::high_resolution_clock::now() - t;
// print the calculated error
// print comparison only when the 'cmp' flag is set
if (vm.count("test")) solve.print_error(vm["cmp"].as<bool>());
// Print the final solution
if (vm.count("results")) solve.print_soln();
std::uint64_t const os_thread_count = hpx::get_os_thread_count();
print_time_results(os_thread_count, elapsed, nx, ny, nt, header);
return hpx::finalize();
}
int main(int argc, char* argv[]) {
std::cout << argv[0] << " (" << MAJOR_VERSION << "." << MINOR_VERSION << "."
<< UPDATE_VERSION << ")" << std::endl;
namespace po = hpx::program_options;
po::options_description desc_commandline;
desc_commandline.add_options()(
"test",
"use arguments from numerical solution for testing (default: false)")(
"test_batch",
"test the solution against numerical solution against batch inputs "
"(default: false)")("results",
"print generated results (default: false)")(
"cmp", po::value<bool>()->default_value(true),
"Print expected versus actual outputs")(
"nx", po::value<std::uint64_t>()->default_value(50), "Local x dimension")(
"ny", po::value<std::uint64_t>()->default_value(50), "Local y dimension")(
"nt", po::value<std::uint64_t>()->default_value(45),
"Number of time steps")("nlog",
po::value<std::uint64_t>()->default_value(5),
"Number of time steps to log the results")(
"eps", po::value<std::uint64_t>()->default_value(5),
"Epsilon for nonlocal equation")(
"k", po::value<double>(&k)->default_value(1),
"Heat transfer coefficient (default: 0.5)")(
"dt", po::value<double>(&dt)->default_value(0.0005),
"Timestep unit (default: 1.0[s])")(
"dh", po::value<double>(&dh)->default_value(0.02),
"Quantization across space")("no-header",
"do not print out the csv header row");
// Initialize and run HPX
return hpx::init(desc_commandline, argc, argv);
}