/** * @file comm_mpi.cpp * @author Julian Iseringhausen * @date Wed Jun 16 13:21:06 2010 * * @brief Class for MPI-based communication. * */ #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_MPI #include #ifdef HAVE_BOOST_FILESYSTEM #include namespace fs = boost::filesystem; #endif #include #include #include "base/helper.hpp" #include "base/linked_cell_list.hpp" #include "base/tuple.hpp" #include "comm/comm_mpi.hpp" #include "grid/grid.hpp" #include "grid/multigrid.hpp" #include "grid/tempgrid.hpp" #include "mg.hpp" #include "base/timer.hpp" static char print_buffer[512]; using namespace VMG; void CommMPI::Isend(Grid& grid, VMG::MPI::Datatype& type, const MPI_Comm& comm, const int& tag) { if (type.Feasible()) MPI_Isend(&grid(0), 1, type.Type(), type.Rank(), tag, comm, &Request()); } void CommMPI::IsendAll(Grid& grid, std::vector& types, const MPI_Comm& comm, const int& tag_start) { for (unsigned int i=0; i& types, const MPI_Comm& comm, const int& tag_start) { for (unsigned int i=0; i& types, const MPI_Comm& comm, const int& tag_start) { receive_buffer.resize(types.size()); for (unsigned int i=0; i& types) { Index ind; for (unsigned int i=0; i neighbors; std::vector buffer_send_left, buffer_send_right; std::vector buffer_recv_left, buffer_recv_right; std::vector::const_iterator iter; const Index halo_size_1 = grid.Local().HaloEnd1() - grid.Local().HaloBegin1(); const Index halo_size_2 = grid.Local().HaloEnd2() - grid.Local().HaloBegin2(); buffer_send_left.clear(); buffer_send_right.clear(); buffer_recv_left.clear(); buffer_recv_right.clear(); MPI_Cart_shift(comm, 2, 1, &neighbors.Left(), &neighbors.Right()); if (halo_size_1.Z()) { buffer_send_left.reserve(grid.Local().Size().X() * grid.Local().Size().Y() * halo_size_1.Z() / 2); for (i.X()=grid.Local().Begin().X(); i.X()& particles) { Factory& factory = MG::GetFactory(); const vmg_int& num_particles_local = factory.GetObjectStorageVal("PARTICLE_NUM_LOCAL"); vmg_float* x = factory.GetObjectStorageArray("PARTICLE_POS_ARRAY"); vmg_float* q = factory.GetObjectStorageArray("PARTICLE_CHARGE_ARRAY"); int rank, size; MPI_Comm_rank(comm_global, &rank); MPI_Comm_size(comm_global, &size); Index index; std::vector global_extent, send_sizes, recv_sizes; std::vector begin_remote, end_remote; std::vector< std::vector > pos_indices, charge_indices; std::vector buffer_x, buffer_q; std::vector buffer_ind; MPI_Datatype dt_temp; global_extent.resize(6*size); send_sizes.resize(size); recv_sizes.resize(size); begin_remote.resize(size); end_remote.resize(size); pos_indices.resize(size); charge_indices.resize(size); std::memcpy(&global_extent[6*rank], grid.Global().BeginLocal().vec(), 3*sizeof(int)); std::memcpy(&global_extent[6*rank+3], grid.Global().EndLocal().vec(), 3*sizeof(int)); MPI_Allgather(MPI_IN_PLACE, 6, MPI_INT, &global_extent.front(), 6, MPI_INT, MPI_COMM_WORLD); for (int i=0; i(&global_extent[6*i]); end_remote[i] = static_cast(&global_extent[6*i+3]); } for (int i=0; i((Vector(&x[3*i]) - grid.Extent().Begin()) / grid.Extent().MeshWidth()); for (int j=0; j 0) { MPI_Type_create_indexed_block(pos_indices[i].size(), 3, &pos_indices[i].front(), MPI_DOUBLE, &dt_temp); MPI_Type_commit(&dt_temp); MPI_Isend(x, 1, dt_temp, i, 0, MPI_COMM_WORLD, &Request()); MPI_Type_free(&dt_temp); MPI_Type_create_indexed_block(charge_indices[i].size(), 1, &charge_indices[i].front(), MPI_DOUBLE, &dt_temp); MPI_Type_commit(&dt_temp); MPI_Isend(q, 1, dt_temp, i, 1, MPI_COMM_WORLD, &Request()); MPI_Type_free(&dt_temp); MPI_Isend(&charge_indices[i].front(), charge_indices[i].size(), MPI_INT, i, 2, MPI_COMM_WORLD, &Request()); } } /* * Receive particles */ for (int i=0; i 0) { buffer_x.resize(3*recv_sizes[i]); buffer_q.resize(recv_sizes[i]); buffer_ind.resize(recv_sizes[i]); MPI_Recv(&buffer_x.front(), 3*recv_sizes[i], MPI_DOUBLE, i, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); MPI_Recv(&buffer_q.front(), recv_sizes[i], MPI_DOUBLE, i, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); MPI_Recv(&buffer_ind.front(), recv_sizes[i], MPI_INT, i, 2, MPI_COMM_WORLD, MPI_STATUS_IGNORE); particles.clear(); for (int j=0; j send_buffer_1, send_buffer_2, recv_buffer; vmg_int send_size_1, send_size_2, recv_size; std::list::iterator p_iter; Grid::iterator g_iter; Tuple neighbors; for (int i=2; i>=0; --i) { /* * Get ranks of neighbors */ MPI_Cart_shift(comm_global, i, 1, &neighbors.Left(), &neighbors.Right()); /* * Send to left */ send_buffer_1.clear(); for (g_iter=lc.Iterators().Boundary1()[i].Begin(); g_iter!=lc.Iterators().Boundary1()[i].End(); ++g_iter) lc(*g_iter).clear(); for (g_iter=lc.Iterators().NearBoundary1()[i].Begin(); g_iter!=lc.Iterators().NearBoundary1()[i].End(); ++g_iter) for (p_iter=lc(*g_iter).begin(); p_iter!=lc(*g_iter).end(); ++p_iter) { if (grid.Global().BeginLocal()[i] == 0) for (int j=0; j<3; ++j) send_buffer_1.push_back(p_iter->Pos()[j] + grid.Extent().Size()[j]); else for (int j=0; j<3; ++j) send_buffer_1.push_back(p_iter->Pos()[j]); send_buffer_1.push_back(p_iter->Charge()); } send_size_1 = send_buffer_1.size(); MPI_Isend(&send_size_1, 1, MPI_INT, neighbors.Left(), 0, comm_global, &Request()); if (send_size_1 > 0) MPI_Isend(&send_buffer_1.front(), send_size_1, MPI_DOUBLE, neighbors.Left(), 1, comm_global, &Request()); /* * Send to right */ send_buffer_2.clear(); for (g_iter=lc.Iterators().Boundary2()[i].Begin(); g_iter!=lc.Iterators().Boundary2()[i].End(); ++g_iter) lc(*g_iter).clear(); for (g_iter=lc.Iterators().NearBoundary2()[i].Begin(); g_iter!=lc.Iterators().NearBoundary2()[i].End(); ++g_iter) for (p_iter=lc(*g_iter).begin(); p_iter!=lc(*g_iter).end(); ++p_iter) { if (grid.Global().EndLocal()[i] == grid.Global().SizeGlobal()[i]) for (int j=0; j<3; ++j) send_buffer_2.push_back(p_iter->Pos()[j] - grid.Extent().Size()[j]); else for (int j=0; j<3; ++j) send_buffer_2.push_back(p_iter->Pos()[j]); send_buffer_2.push_back(p_iter->Charge()); } send_size_2 = send_buffer_2.size(); MPI_Isend(&send_size_2, 1, MPI_INT, neighbors.Right(), 2, comm_global, &Request()); if (send_size_2 > 0) MPI_Isend(&send_buffer_2.front(), send_size_2, MPI_DOUBLE, neighbors.Right(), 3, comm_global, &Request()); /* * Receive from right */ MPI_Recv(&recv_size, 1, MPI_INT, neighbors.Right(), 0, comm_global, MPI_STATUS_IGNORE); if (recv_size > 0) { recv_buffer.resize(recv_size); MPI_Recv(&recv_buffer.front(), recv_size, MPI_DOUBLE, neighbors.Right(), 1, comm_global, MPI_STATUS_IGNORE); for (vmg_int i=0; i 0) { recv_buffer.resize(recv_size); MPI_Recv(&recv_buffer.front(), recv_size, MPI_DOUBLE, neighbors.Left(), 3, comm_global, MPI_STATUS_IGNORE); for (vmg_int i=0; i& particles) { std::list::iterator iter; if (!win_created) { vmg_float* p = MG::GetFactory().GetObjectStorageArray("PARTICLE_POTENTIAL_ARRAY"); const vmg_int& num_particles_local = MG::GetFactory().GetObjectStorageVal("PARTICLE_NUM_LOCAL"); MPI_Win_create(p, num_particles_local*sizeof(vmg_float), sizeof(vmg_float), info, comm_global, &win); win_created = true; } MPI_Win_fence(MPI_MODE_NOPRECEDE, win); for (iter=particles.begin(); iter!=particles.end(); ++iter) MPI_Accumulate(&iter->Pot(), 1, MPI_DOUBLE, iter->Rank(), iter->Index(), 1, MPI_DOUBLE, MPI_SUM, win); MPI_Win_fence(MPI_MODE_NOSTORE | MPI_MODE_NOSUCCEED, win); } void CommMPI::CommAddPotential(Particle::LinkedCellList& lc) { Grid::iterator g_iter; std::list::iterator p_iter; if (!win_created) { vmg_float* p = MG::GetFactory().GetObjectStorageArray("PARTICLE_POTENTIAL_ARRAY"); const vmg_int& num_particles_local = MG::GetFactory().GetObjectStorageVal("PARTICLE_NUM_LOCAL"); MPI_Win_create(p, num_particles_local*sizeof(vmg_float), sizeof(vmg_float), info, comm_global, &win); win_created = true; } MPI_Win_fence(MPI_MODE_NOPRECEDE, win); for (g_iter=lc.Iterators().Local().Begin(); g_iter!=lc.Iterators().Local().End(); ++g_iter) for (p_iter=lc(*g_iter).begin(); p_iter!=lc(*g_iter).end(); ++p_iter) MPI_Accumulate(&p_iter->Pot(), 1, MPI_DOUBLE, p_iter->Rank(), p_iter->Index(), 1, MPI_DOUBLE, MPI_SUM, win); MPI_Win_fence(MPI_MODE_NOSTORE | MPI_MODE_NOSUCCEED, win); } vmg_float CommMPI::GlobalSum(const vmg_float& value) { vmg_float result = value; MPI_Allreduce(MPI_IN_PLACE, &result, 1, MPI_DOUBLE, MPI_SUM, comm_global); return result; } vmg_float CommMPI::GlobalSumRoot(const vmg_float& value) { vmg_float recv_buffer; vmg_float send_buffer = value; MPI_Reduce(&send_buffer, &recv_buffer, 1, MPI_DOUBLE, MPI_SUM, 0, comm_global); return recv_buffer; } void CommMPI::GlobalSumArray(vmg_float* array, const vmg_int& size) { MPI_Allreduce(MPI_IN_PLACE, array, size, MPI_DOUBLE, MPI_SUM, comm_global); } void CommMPI::PrintString(const char* format, ...) { va_list args; va_start(args, format); vsprintf(print_buffer, format, args); printf("VMG: Rank %d: %s\n", GlobalRank(), print_buffer); va_end(args); } void CommMPI::PrintStringOnce(const char* format, ...) { if (GlobalRank() == 0) { va_list args; va_start(args, format); vsprintf(print_buffer, format, args); printf("VMG: Rank %d: %s\n", GlobalRank(), print_buffer); va_end(args); } } void CommMPI::PrintXML(const std::string& filename, const std::string& xml_data) { MPI_File file; std::stringstream path, xml_header; pugi::xml_document doc; pugi::xml_node node_data = doc.append_child("Global").append_child("NumProcesses").append_child(pugi::node_pcdata); node_data.set_value(Helper::ToString(GlobalProcs().Product()).c_str()); doc.save(xml_header); path << OutputPath() << filename; char* filename_array = Helper::GetCharArray(path.str()); char* xml_header_array = Helper::GetCharArray(xml_header.str()); char* str_array = Helper::GetCharArray(xml_data); MPI_File_open(MPI_COMM_SELF, filename_array, MPI_MODE_WRONLY|MPI_MODE_CREATE, MPI_INFO_NULL, &file); MPI_File_set_size(file, 0); MPI_File_write(file, xml_header_array, xml_header.str().size(), MPI_CHAR, MPI_STATUS_IGNORE); MPI_File_write(file, str_array, xml_data.size(), MPI_CHAR, MPI_STATUS_IGNORE); MPI_File_close(&file); delete [] filename_array; delete [] xml_header_array; delete [] str_array; } void CommMPI::PrintXMLAll(const std::string& filename, const std::string& xml_data) { MPI_File file; std::stringstream path; path << OutputPath() << filename; char* filename_array = Helper::GetCharArray(path.str()); char* str_array = Helper::GetCharArray(xml_data); MPI_File_open(comm_global, filename_array, MPI_MODE_WRONLY|MPI_MODE_CREATE, MPI_INFO_NULL, &file); MPI_File_set_size(file, 0); if (GlobalRank() == 0) { std::stringstream xml_header; pugi::xml_document doc; pugi::xml_node node_data = doc.append_child("Global").append_child("NumProcesses").append_child(pugi::node_pcdata); node_data.set_value(Helper::ToString(GlobalProcs().Product()).c_str()); doc.save(xml_header); char* xml_header_array = Helper::GetCharArray(xml_header.str()); MPI_File_write_shared(file, xml_header_array, xml_header.str().size(), MPI_CHAR, MPI_STATUS_IGNORE); delete [] xml_header_array; } MPI_File_write_ordered(file, str_array, xml_data.size(), MPI_CHAR, MPI_STATUS_IGNORE); MPI_File_close(&file); delete [] filename_array; delete [] str_array; } void CommMPI::PrintAllSettings() { std::stringstream buf; MPI_File file; MPI_Status status; Multigrid* mg = MG::GetFactory().Get("SOL")->Cast(); buf << OutputPath() << "settings.txt"; char *filename = Helper::GetCharArray(buf.str()); MPI_File_open(comm_global, filename, MPI_MODE_WRONLY|MPI_MODE_CREATE, MPI_INFO_NULL, &file); MPI_File_set_size(file, 0); MPI_File_close(&file); for (int i=mg->MinLevel(); i<=mg->MaxLevel(); ++i) { MPI_Comm comm = settings.CommunicatorGlobal((*mg)(i)); if (comm != MPI_COMM_NULL) { MPI_File_open(comm, filename, MPI_MODE_WRONLY|MPI_MODE_CREATE|MPI_MODE_APPEND, MPI_INFO_NULL, &file); int comm_rank; MPI_Comm_rank(comm, &comm_rank); if (comm_rank == 0) { buf.str(""); buf << "PROCESS INDEPENDENT INFORMATION:" << std::endl << "GRID LEVEL " << i << std::endl << "BEGINFINEST: " << (*mg)(i).Global().BeginFinest() << std::endl << "ENDFINEST: " << (*mg)(i).Global().EndFinest() << std::endl << "SIZEFINEST: " << (*mg)(i).Global().SizeFinest() << std::endl << "SIZEGLOBAL: " << (*mg)(i).Global().SizeGlobal() << std::endl << "BOUNDARYTYPE: " << (*mg)(i).Global().BoundaryType() << std::endl << "EXTENT" << std::endl << "SIZE: " << (*mg)(i).Extent().Size() << std::endl << "SIZEFACTOR: " << (*mg)(i).Extent().SizeFactor() << std::endl << "BEGIN: " << (*mg)(i).Extent().Begin() << std::endl << "END: " << (*mg)(i).Extent().End() << std::endl << "MESHWIDTH: " << (*mg)(i).Extent().MeshWidth() << std::endl << std::endl << "PROCESS DEPENDENT INFORMATION:" << std::endl; char* char_buf = Helper::GetCharArray(buf.str()); MPI_File_write_shared(file, char_buf, buf.str().size(), MPI_CHAR, &status); delete [] char_buf; } buf.str(""); buf << "PROCESS " << comm_rank << ":" << std::endl << "GLOBAL" << std::endl << "BEGINLOCAL: " << (*mg)(i).Global().BeginLocal() << std::endl << "ENDLOCAL: " << (*mg)(i).Global().EndLocal() << std::endl << "SIZELOCAL: " << (*mg)(i).Global().SizeLocal() << std::endl << "LOCAL" << std::endl << "SIZE: " << (*mg)(i).Local().Size() << std::endl << "SIZETOTAL: " << (*mg)(i).Local().SizeTotal() << std::endl << "BEGIN: " << (*mg)(i).Local().Begin() << std::endl << "END: " << (*mg)(i).Local().End() << std::endl << "HALOBEGIN1: " << (*mg)(i).Local().HaloBegin1() << std::endl << "HALOEND1: " << (*mg)(i).Local().HaloEnd1() << std::endl << "HALOBEGIN2: " << (*mg)(i).Local().HaloBegin2() << std::endl << "HALOEND2: " << (*mg)(i).Local().HaloEnd2() << std::endl << "BOUNDARYBEGIN1: " << (*mg)(i).Local().BoundaryBegin1() << std::endl << "BOUNDARYEND1: " << (*mg)(i).Local().BoundaryEnd1() << std::endl << "BOUNDARYBEGIN2: " << (*mg)(i).Local().BoundaryBegin2() << std::endl << "BOUNDARYEND2: " << (*mg)(i).Local().BoundaryEnd2() << std::endl << "ALIGNMENTBEGIN: " << (*mg)(i).Local().AlignmentBegin() << std::endl << "ALIGNMENTEND: " << (*mg)(i).Local().AlignmentEnd() << std::endl << std::endl; char* char_buf = Helper::GetCharArray(buf.str()); MPI_File_write_ordered(file, char_buf, buf.str().size(), MPI_CHAR, &status); delete [] char_buf; MPI_File_close(&file); } } delete [] filename; } void CommMPI::PrintDefect(Grid& sol, Grid& rhs, const char* information) { TempGrid *temp = MG::GetTempGrid(); temp->SetProperties(sol); temp->ImportFromResidual(sol, rhs); PrintInnerGrid(*temp, information); } void CommMPI::PrintGrid(Grid& grid, const char* information) { Index i; std::stringstream buf; Index begin = 0; Index end = grid.Local().End(); Index begin_local, end_local, begin_global, end_global; CommToGhosts(grid); for (int j=0; j<3; ++j) if (grid.Global().EndLocal()[j] == grid.Global().SizeGlobal()[j]) end[j] = grid.Local().SizeTotal()[j]; for (i.Z()=begin.Z(); i.Z()" << std::endl << "" << std::endl << " " << std::endl << " " << std::endl << " " << std::endl << " " << std::endl; char* char_buf = Helper::GetCharArray(buf.str()); MPI_File_write_shared(file, char_buf, buf.str().size(), MPI_CHAR, MPI_STATUS_IGNORE); delete [] char_buf; } buf.str(""); buf << " " << std::endl; char* char_buf = Helper::GetCharArray(buf.str()); MPI_File_write_ordered(file, char_buf, buf.str().size(), MPI_CHAR, MPI_STATUS_IGNORE); delete [] char_buf; if (rank == 0) { buf.str(""); buf << " " << std::endl << "" << std::endl; char* char_buf = Helper::GetCharArray(buf.str()); MPI_File_write_shared(file, char_buf, buf.str().size(), MPI_CHAR, MPI_STATUS_IGNORE); delete [] char_buf; } MPI_File_close(&file); } MPI_File CommMPI::CreateSerialOutputFile(const Grid& grid, MPI_Comm& comm, const int& output_count, const char* information, const Index& begin_global, const Index& end_global, const Index& begin_local, const Index& end_local) { char serial_filename[513]; int rank; MPI_File file; std::stringstream buf; MPI_Comm_rank(comm_global, &rank); sprintf(serial_filename, "%s%04d_%d.vti", OutputPath().c_str(), output_count, rank); MPI_File_open(MPI_COMM_SELF, serial_filename, MPI_MODE_WRONLY|MPI_MODE_CREATE, MPI_INFO_NULL, &file); buf << "" << std::endl << "" << std::endl << " " << std::endl << " " << std::endl << " " << std::endl << " " << std::endl << " "; char* char_buf = Helper::GetCharArray(buf.str()); MPI_File_write(file, char_buf, buf.str().size(), MPI_CHAR, MPI_STATUS_IGNORE); delete [] char_buf; return file; } void CommMPI::FinalizeSerialOutputFile(MPI_File& file) { std::stringstream buf; buf << std::endl << " " << std::endl << " " << std::endl << " " << std::endl << " " << std::endl << "" << std::endl; char* char_buf = Helper::GetCharArray(buf.str()); MPI_File_write(file, char_buf, buf.str().size(), MPI_CHAR, MPI_STATUS_IGNORE); delete [] char_buf; MPI_File_close(&file); } int CommMPI::GlobalRank() const { int rank; MPI_Comm_rank(comm_global, &rank); return rank; } Index CommMPI::GlobalPos() const { Index dims, periods, coords; MPI_Cart_get(comm_global, 3, dims.vec(), periods.vec(), coords.vec()); return coords; } Index CommMPI::GlobalProcs() const { Index dims, periods, coords; MPI_Cart_get(comm_global, 3, dims.vec(), periods.vec(), coords.vec()); return dims; } void CommMPI::InitCommMPI(const MPI_Comm& comm) { int status, size, rank; int dims[3] = {0, 0, 0}; int periods[3]; for (int i=0; i<3; ++i) periods[i] = (BoundaryConditions()[i] == Periodic ? 1 : 0); MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank); MPI_Topo_test(comm, &status); if (status == MPI_CART) { comm_global = comm; }else { MPI_Dims_create(size, 3, dims); MPI_Cart_create(comm, 3, dims, periods, 1, &comm_global); } MPI_Info_create(&info); char key[] = "no_locks"; char val[] = "true"; MPI_Info_set(info, key, val); } CommMPI::~CommMPI() { MPI_Comm_free(&comm_global); if (win_created) MPI_Win_free(&win); MPI_Info_free(&info); } Grid& CommMPI::GetCoarserGrid(Multigrid& multigrid) { return settings.CoarserGrid(multigrid(multigrid.Level())); } Grid& CommMPI::GetFinerGrid(Multigrid& multigrid) { return settings.FinerGrid(multigrid(multigrid.Level())); } Grid& CommMPI::GetGlobalCoarseGrid(Multigrid& multigrid) { Grid& global_coarse_grid = settings.GlobalCoarseGrid(); CommSubgrid(multigrid(multigrid.MinLevel()), global_coarse_grid); return global_coarse_grid; } std::string CommMPI::CreateOutputDirectory() { #ifdef HAVE_BOOST_FILESYSTEM std::string path, unique_path; std::stringstream unique_suffix; int suffix_counter = 0; char buffer[129]; time_t rawtime; struct tm *timeinfo; int path_size; char* path_array; if (GlobalRank() == 0) { time(&rawtime); timeinfo = localtime(&rawtime); strftime(buffer, 128, "./output/%Y_%m_%d_%H_%M_%S/", timeinfo); path = buffer; if (!fs::exists("output")) fs::create_directory("output"); unique_path = path; while (fs::exists(unique_path.c_str())) { unique_suffix.str(""); unique_suffix << "_" << suffix_counter++ << "/"; unique_path = path; unique_path.replace(unique_path.size()-1, 1, unique_suffix.str()); } fs::create_directory(unique_path.c_str()); path_size = unique_path.size() + 1; path_array = Helper::GetCharArray(unique_path); MPI_Bcast(&path_size, 1, MPI_INT, 0, comm_global); }else { MPI_Bcast(&path_size, 1, MPI_INT, 0, comm_global); path_array = new char[path_size]; } MPI_Bcast(path_array, path_size, MPI_CHAR, 0, comm_global); unique_path = path_array; delete [] path_array; return unique_path; #else return "./"; #endif } #endif /* HAVE_MPI */