30#include <taskflow/taskflow/taskflow.hpp>
31#include <taskflow/taskflow/algorithm/for_each.hpp>
35 void printMsg(std::string msg,
int mpiRank,
int printMpiRank) {
36 if (printMpiRank < 0) {
39 if (mpiRank == printMpiRank)
44 double f1(
const double &x){
45 return x*x*x + std::exp(x) - std::sin(x);
48 double f2(
const double &x){
49 return 2*(x-0.5)*(x-0.5)*(x-0.5) + std::exp(x-0.5) - std::cos(x-0.5);
54 const std::vector<size_t> &nodePartition,
55 const std::vector<std::vector<size_t>> &nodeNeighs,
56 std::vector<size_t> &ownedNodes,
57 std::vector<size_t> &ownedInternalNodes,
58 std::vector<size_t> &ownedBdryNodes,
59 std::vector<std::pair<std::vector<size_t>, std::vector<size_t>>> &ghostData) {
61 auto numNodes = nodePartition.size();
65 ownedInternalNodes.clear();
66 ownedBdryNodes.clear();
67 ghostData.resize(mpiSize);
68 for (
size_t i_proc=0; i_proc<mpiSize; i_proc++) {
69 ghostData[i_proc].first.clear();
70 ghostData[i_proc].second.clear();
74 for (
size_t i=0; i<numNodes; i++) {
75 if (nodePartition[i] == mpiRank) {
77 ownedNodes.push_back(i);
80 bool ghostExist =
false;
81 for (
auto j : nodeNeighs[i]) {
82 auto j_proc = nodePartition[j];
83 if (j_proc != mpiRank) {
87 ghostData[j_proc].first.push_back(j);
90 ghostData[j_proc].second.push_back(i);
95 ownedBdryNodes.push_back(i);
97 ownedInternalNodes.push_back(i);
102 bool debugGhostData =
false;
103 if (debugGhostData and mpiRank == 0) {
104 std::vector<std::vector<std::pair<std::vector<size_t>, std::vector<size_t>>>> ghostDataAllProc(
106 std::vector<std::vector<size_t>> numGhostDataAllProc(mpiSize);
107 for (
size_t i_proc = 0; i_proc < mpiSize; i_proc++) {
108 numGhostDataAllProc[i_proc].resize(mpiSize);
109 ghostDataAllProc[i_proc].resize(mpiSize);
112 for (
size_t i_proc = 0; i_proc < mpiSize; i_proc++) {
114 for (
size_t i = 0; i < numNodes; i++) {
115 if (nodePartition[i] == i_proc) {
117 for (
auto j: nodeNeighs[i]) {
118 auto j_proc = nodePartition[j];
119 if (j_proc != i_proc) {
121 ghostDataAllProc[i_proc][j_proc].first.push_back(j);
128 for (
size_t j_proc = 0; j_proc < mpiSize; j_proc++)
129 numGhostDataAllProc[i_proc][j_proc] = ghostDataAllProc[i_proc][j_proc].first.size();
133 std::cout <<
"\n\nGhost data debug output\n\n";
134 bool found_asym =
false;
135 for (
size_t i_proc = 0; i_proc < mpiSize; i_proc++) {
136 for (
size_t j_proc = 0; j_proc < mpiSize; j_proc++) {
137 std::cout << std::format(
"(i,j) = ({}, {}), num data = {}\n",
139 numGhostDataAllProc[i_proc][j_proc]);
141 if (j_proc > i_proc) {
142 if (numGhostDataAllProc[i_proc][j_proc] == numGhostDataAllProc[j_proc][i_proc])
143 std::cout << std::format(
" symmetric: data ({}, {}) = data ({}, {})\n",
144 i_proc, j_proc, j_proc, i_proc);
147 std::cout << std::format(
148 " asymmetric: data ({}, {}) != data ({}, {})\n",
149 i_proc, j_proc, j_proc, i_proc);
155 std::cout <<
"Found asymetric ghost data\n";
157 std::cout <<
"No asymetric ghost data\n";
165 const std::vector<std::pair<std::vector<size_t>, std::vector<size_t>>> &ghostData,
166 std::vector<std::pair<std::vector<util::Point>, std::vector<util::Point>>> &dispGhostData,
167 std::vector<util::Point> &dispNodes) {
169 dispGhostData.resize(mpiSize);
170 for (
size_t j_proc = 0; j_proc < mpiSize; j_proc++) {
171 auto j_data_size = ghostData[j_proc].first.size();
172 dispGhostData[j_proc].first.resize(j_data_size);
173 dispGhostData[j_proc].second.resize(j_data_size);
179 MPI_Request mpiRequests[2*(mpiSize-1)];
180 size_t requestCounter = 0;
181 for (
size_t j_proc=0; j_proc<mpiSize; j_proc++) {
182 auto & sendIds = ghostData[j_proc].second;
183 auto & recvIds = ghostData[j_proc].first;
185 if (j_proc != mpiRank and recvIds.size() != 0) {
190 for (
size_t k = 0; k<sendIds.size(); k++)
191 dispGhostData[j_proc].second[k] = dispNodes[sendIds[k]];
194 MPI_Isend(dispGhostData[j_proc].second.data(), 3*sendIds.size(),
195 MPI_DOUBLE, j_proc, 0, MPI_COMM_WORLD,
196 &mpiRequests[requestCounter++]);
199 MPI_Irecv(dispGhostData[j_proc].first.data(), 3*recvIds.size(),
200 MPI_DOUBLE, j_proc, 0, MPI_COMM_WORLD,
201 &mpiRequests[requestCounter++]);
206 MPI_Waitall(requestCounter, mpiRequests, MPI_STATUSES_IGNORE);
210 for (
size_t j_proc=0; j_proc<mpiSize; j_proc++) {
211 auto & recvIds = ghostData[j_proc].first;
212 if (j_proc != mpiRank and recvIds.size() != 0) {
213 for (
size_t k = 0; k<recvIds.size(); k++)
214 dispNodes[recvIds[k]] = dispGhostData[j_proc].first[k];
224 util::io::print(std::format(
"\n\ntestTaskflow(): Number of threads = {}\n\n", nThreads));
232 std::vector<double> x(N);
233 std::vector<double> y1(N);
234 std::vector<double> y2(N);
235 std::generate(std::begin(x), std::end(x), [&] {
return dist(); });
238 auto t1 = steady_clock::now();
239 for (
size_t i = 0; i < N; i++) {
245 auto t2 = steady_clock::now();
249 tf::Executor executor(nThreads);
250 tf::Taskflow taskflow;
252 taskflow.for_each_index((std::size_t) 0, N, (std::size_t) 1, [&x, &y2](std::size_t i) {
260 executor.run(taskflow).get();
261 auto t3 = steady_clock::now();
266 for (
size_t i=0; i<N; i++)
267 y_err += std::pow(y1[i] - y2[i], 2);
269 if (y_err > 1.e-10) {
270 std::cerr << std::format(
"Error: Serial and taskflow computation results do not match (squared error = {})\n",
276 std::ostringstream msg;
277 msg << std::format(
" Serial computation took = {}ms\n", dt12);
278 msg << std::format(
" Taskflow computation took = {}ms\n", dt23);
279 msg << std::format(
" Speed-up factor = {}\n\n\n", dt12/dt23);
285 size_t testOption, std::string meshFilename) {
286 int mpiSize, mpiRank;
287 MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
288 MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
291 size_t nPart(mpiSize);
296 mesh.d_spatialDiscretization =
"finite_difference";
299 std::string outMeshFilename =
"";
300 if (testOption == 1) {
302 std::pair<std::vector<double>, std::vector<double>> box;
303 std::vector<size_t> nGridVec;
304 for (
size_t i=0; i<dim; i++) {
305 box.first.push_back(0.);
306 box.second.push_back(1.);
307 nGridVec.push_back(nGrid);
315 outMeshFilename = std::format(
"uniform_mesh_Lx_{}_Ly_{}_Nx_{}_Ny_{}",
316 box.second[0], box.second[1],
317 nGridVec[0], nGridVec[1]);
319 else if (testOption == 2) {
320 if (meshFilename.empty()) {
321 std::cerr <<
"testGraphPartitioning(): mesh filename is empty.\n";
327 mesh.createData(meshFilename);
333 std::cerr <<
"testMPI() accepts either 1 or 2 for testOption. The value "
334 << testOption <<
" is invalid.\n";
342 double horizon = mHorizon*
mesh.d_h;
343 std::vector<std::vector<size_t>> nodeNeighs(
mesh.d_numNodes);
347 mesh.d_nodePartition.resize(
mesh.d_numNodes);
353 MPI_Bcast(
mesh.d_nodePartition.data(),
mesh.d_numNodes,
354 MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD);
364 std::vector<size_t> ownedNodes, ownedInternalNodes, ownedBdryNodes;
367 std::vector<std::pair<std::vector<size_t>, std::vector<size_t>>> ghostData;
372 mesh.d_nodePartition, nodeNeighs,
373 ownedNodes, ownedInternalNodes, ownedBdryNodes,
377 std::vector<util::Point> dispNodes(
mesh.d_numNodes,
util::Point(-1., -1., -1.));
385 for (
auto i: ownedNodes)
386 dispNodes[i] =
util::Point(mpiRank + 1, (mpiRank + 1)*100, (mpiRank + 1)*10000);
390 printMsg(
"\n\nCalling exchangeDispData()\n\n", mpiRank, 0);
391 std::vector<std::pair<std::vector<util::Point>, std::vector<util::Point>>> dispGhostData;
396 printMsg(
"\n\nDebugging dispGhostData()\n\n", mpiRank, -1);
397 bool debug_failed =
false;
398 for (
size_t j_proc = 0; j_proc < mpiSize; j_proc++) {
399 auto &recvIds = ghostData[j_proc].first;
401 if (j_proc != mpiRank and recvIds.size() != 0) {
402 for (
size_t k = 0; k < recvIds.size(); k++) {
403 auto &uk = dispNodes[recvIds[k]];
406 if (uk[0] != j_proc + 1 or uk[1] != 100 * (j_proc + 1)
407 or uk[2] != 10000 * (j_proc + 1)) {
410 "uk = ({}, {}, {})\n", j_proc,
411 uk[0], uk[1], uk[2]),
Templated probability distribution.
double f1(const double &x)
void printMsg(std::string msg, int mpiRank, int printMpiRank)
void exchangeDispData(size_t mpiSize, size_t mpiRank, const std::vector< std::pair< std::vector< size_t >, std::vector< size_t > > > &ghostData, std::vector< std::pair< std::vector< util::Point >, std::vector< util::Point > > > &dispGhostData, std::vector< util::Point > &dispNodes)
double f2(const double &x)
void setupOwnerAndGhost(size_t mpiSize, size_t mpiRank, const std::vector< size_t > &nodePartition, const std::vector< std::vector< size_t > > &nodeNeighs, std::vector< size_t > &ownedNodes, std::vector< size_t > &ownedInternalNodes, std::vector< size_t > &ownedBdryNodes, std::vector< std::pair< std::vector< size_t >, std::vector< size_t > > > &ghostData)
void computeNonlocalNeighborhood(const std::vector< util::Point > &nodes, double horizon, std::vector< std::vector< size_t > > &nodeNeighs)
Partitions the nodes based on node neighborlist supplied. Function first creates a graph with nodes a...
Collection of methods and data related to finite element and mesh.
void metisGraphPartition(std::string partitionMethod, const std::vector< std::vector< size_t > > &nodeNeighs, std::vector< size_t > &nodePartition, size_t nPartitions)
Partitions the nodes based on node neighborlist supplied. Function first creates a graph with nodes a...
void createUniformMesh(mesh::Mesh *mesh_p, size_t dim, std::pair< std::vector< double >, std::vector< double > > box, std::vector< size_t > nGrid)
Creates uniform mesh for rectangle/cuboid domain.
std::string testTaskflow(size_t N, int seed)
Perform test on taskflow.
void testMPI(size_t nGrid=10, size_t mHorizon=3, size_t testOption=0, std::string meshFilename="")
Perform parallelization test using MPI on mesh partition based on metis.
const int print_default_tab
Default value of tab used in outputting formatted information.
void print(const T &msg, int nt=print_default_tab, int printMpiRank=print_default_mpi_rank)
Prints formatted information.
std::string removeExtensionFromFile(std::string const &filename)
Remove extension from the filename Source - https://stackoverflow.com/a/24386991.
std::string getFilenameFromPath(std::string const &path, std::string const &delims="/\\")
Get filename removing path from the string Source - https://stackoverflow.com/a/24386991.
float timeDiff(std::chrono::steady_clock::time_point begin, std::chrono::steady_clock::time_point end, std::string unit="microseconds")
Returns difference between two times.
unsigned int getNThreads()
Get number of threads to be used by taskflow.
RandGenerator get_rd_gen(int seed=-1)
Return random number generator.
std::mt19937 RandGenerator
A structure to represent 3d vectors.
std::uniform_real_distribution UniformDistribution
std::mt19937 RandGenerator