diff --git a/CMakeLists.txt b/CMakeLists.txt index abae43e9edbb61678f3227eddc79e54f6ccce5df..2c14cd08b501526e9e3a142117b50b65d95b1211 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ option(ENABLE_MATHEX "Enable MathEx expression parser" ON) option(ENABLE_MED "Enable MED mesh and post-processing file formats" ON) option(ENABLE_MESH "Build the mesh module" ON) option(ENABLE_METIS "Enable Metis mesh partitioner" ON) +option(ENABLE_MPI "Enable MPI parallelization" OFF) option(ENABLE_MSVC_STATIC_RUNTIME "Use static Visual C++ runtime" OFF) option(ENABLE_NATIVE_FILE_CHOOSER "Enable native file chooser in GUI" ON) option(ENABLE_NETGEN "Enable Netgen mesh generator" ON) @@ -433,6 +434,19 @@ if(ENABLE_MATHEX) list(APPEND CONFIG_OPTIONS "MathEx") endif(ENABLE_MATHEX) +if(ENABLE_MPI) + find_package(MPI) + if(MPI_FOUND) + message("-- Found MPI") + set(HAVE_MPI TRUE) + list(APPEND CONFIG_OPTIONS "MPI") + list(APPEND EXTERNAL_INCLUDES ${MPI_INCLUDE_DIR}) + list(APPEND EXTERNAL_LIBRARIES ${MPI_LIBRARIES}) + INCLUDE(CMakeForceCompiler) + CMAKE_FORCE_CXX_COMPILER(${MPI_COMPILER} "MPI C++ Compiler") + endif(MPI_FOUND) +endif(ENABLE_MPI) + if(ENABLE_METIS) add_subdirectory(contrib/Metis) set(HAVE_METIS TRUE) diff --git a/Common/GmshRemote.cpp b/Common/GmshRemote.cpp index 56bc3ca593921154390417b3894d4778871c3f43..7eaa3c5428b547f7ae6e20936f89ae6cc7eafff4 100644 --- a/Common/GmshRemote.cpp +++ b/Common/GmshRemote.cpp @@ -9,19 +9,26 @@ #include "OpenFile.h" #include "OS.h" #include "VertexArray.h" +#include "GmshRemote.h" #if defined(HAVE_POST) #include "PView.h" #include "PViewOptions.h" #include "PViewData.h" +#include "PViewDataRemote.h" #endif -static void computeAndSendVertexArrays(GmshClient *client) +#if defined(HAVE_MPI) +#include <mpi.h> +#endif + +static void computeAndSendVertexArrays(GmshClient *client, bool compute=true) { #if defined(HAVE_POST) for(unsigned int i = 0; i < PView::list.size(); i++){ PView *p = PView::list[i]; - p->fillVertexArrays(); + if (compute) + p->fillVertexArrays(); PViewData *data = p->getData(); PViewOptions *opt = p->getOptions(); double min = data->getMin(), max = data->getMax(); @@ -46,65 +53,240 @@ static void computeAndSendVertexArrays(GmshClient *client) #endif } +// This version sends VArrays using MPI +static void computeAndSendVertexArrays() +{ +#if defined(HAVE_POST) && defined(HAVE_MPI) + // compute... + for(unsigned int i = 0; i < PView::list.size(); i++) + PView::list[i]->fillVertexArrays(); + + // ...and send + int nbArrays = PView::list.size()* 4; + MPI_Send(&nbArrays, 1, MPI_INT, 0, MPI_GMSH_DATA_READY, MPI_COMM_WORLD); + + for(unsigned int i = 0; i < PView::list.size(); i++){ + PView *p = PView::list[i]; + PViewData *data = p->getData(); + PViewOptions *opt = p->getOptions(); + double min = data->getMin(), max = data->getMax(); + if(opt->rangeType == PViewOptions::PerTimeStep){ + min = data->getMin(opt->timeStep); + max = data->getMax(opt->timeStep); + } + VertexArray *va[4] = + {p->va_points, p->va_lines, p->va_triangles, p->va_vectors}; + for(int type = 0; type < 4; type++){ + if(va[type]){ + int len; + char *str = va[type]->toChar + (p->getNum(), data->getName(), type + 1, min, max, + data->getNumTimeSteps(), data->getTime(opt->timeStep), + data->getBoundingBox(), len); + MPI_Send(&len, 1, MPI_INT, 0, MPI_GMSH_VARRAY_LEN, MPI_COMM_WORLD); + MPI_Send(str, len, MPI_CHAR, 0, MPI_GMSH_VARRAY, MPI_COMM_WORLD); + delete [] str; + } + } + } +#endif +} + +// Merge the vertex arrays +void addToVertexArrays(const char* bytes, int len) { + + int is = sizeof(int), ds = sizeof(double); + + std::string name; + + int index = 0; + int num; memcpy(&num, &bytes[index], is); index += is; + int ss; memcpy(&ss, &bytes[index], is); index += is; + if(ss){ + std::vector<char> n(ss); + memcpy(&n[0], &bytes[index], ss); index += ss; + for(unsigned int i = 0; i < n.size(); i++) name += n[i]; + } + + int type; memcpy(&type, &bytes[index], is); index += is; + + PView *p = PView::list[num-1]; + PViewData *data = p->getData(); + PViewOptions *opt = p->getOptions(); + + VertexArray *varrays[4] = + {p->va_points, p->va_lines, p->va_triangles, p->va_vectors}; + + VertexArray *va = varrays[type-1]; + + double min; memcpy(&min, &bytes[index], ds); index += ds; + double max; memcpy(&max, &bytes[index], ds); index += ds; + int numsteps; memcpy(&numsteps, &bytes[index], is); index += is; + double time; memcpy(&time, &bytes[index], ds); index += ds; + double xmin; memcpy(&xmin, &bytes[index], ds); index += ds; + double ymin; memcpy(&ymin, &bytes[index], ds); index += ds; + double zmin; memcpy(&zmin, &bytes[index], ds); index += ds; + double xmax; memcpy(&xmax, &bytes[index], ds); index += ds; + double ymax; memcpy(&ymax, &bytes[index], ds); index += ds; + double zmax; memcpy(&zmax, &bytes[index], ds); index += ds; + + if (data->getMin() > min) data->setMin(min); + if (data->getMax() < max) data->setMax(max); + + SBoundingBox3d bbox(xmin, ymin, zmin, xmax, ymax, zmax); + SBoundingBox3d bb = data->getBoundingBox(); + bb += bbox; + + data->setBoundingBox(bb); + + if (type == 4) type = 2; + VertexArray* toAdd = new VertexArray(type,100); + toAdd->fromChar(bytes,0); + va->merge(toAdd); + delete toAdd; +} + int GmshRemote() { GmshClient *client = Msg::GetClient(); - if(!client) return 0; + int rank = Msg::GetCommRank(); + int nbDaemon = Msg::GetCommSize(); + + if(!client && rank == 0) return 0; - computeAndSendVertexArrays(client); + if(client && nbDaemon < 2) computeAndSendVertexArrays(client); while(1){ - // stop if we have no communications for 5 minutes - int ret = client->Select(300, 0); - if(!ret){ - client->Info("Timout: stopping remote Gmsh..."); - break; - } - else if(ret < 0){ - client->Error("Error on select: stopping remote Gmsh..."); - break; - } + // On the node with MPI rank 0, communicate through a + // socket. + if (rank == 0) { + // stop if we have no communications for 5 minutes + int ret = client->Select(300, 0); + if(!ret){ + client->Info("Timout: stopping remote Gmsh..."); + break; + } + else if(ret < 0){ + client->Error("Error on select: stopping remote Gmsh..."); + break; + } - int type, length, swap; - if(!client->ReceiveHeader(&type, &length, &swap)){ - client->Error("Did not receive message header: stopping remote Gmsh..."); - break; - } + int type, length, swap; + if(!client->ReceiveHeader(&type, &length, &swap)){ + client->Error("Did not receive message header: stopping remote Gmsh..."); + break; + } - char *msg = new char[length + 1]; - if(!client->ReceiveString(length, msg)){ - client->Error("Did not receive message body: stopping remote Gmsh..."); - delete [] msg; - break; - } + char *msg = new char[length + 1]; + if(!client->ReceiveString(length, msg)){ + client->Error("Did not receive message body: stopping remote Gmsh..."); + delete [] msg; + break; + } - if(type == GmshSocket::GMSH_STOP){ - client->Info("Stopping remote Gmsh..."); - break; - } - else if(type == GmshSocket::GMSH_VERTEX_ARRAY){ - ParseString(msg); - computeAndSendVertexArrays(client); - } - else if(type == GmshSocket::GMSH_MERGE_FILE){ - MergeFile(msg); - computeAndSendVertexArrays(client); - } - else if(type == GmshSocket::GMSH_PARSE_STRING){ - ParseString(msg); - } - else if(type == GmshSocket::GMSH_SPEED_TEST){ - client->Info("Sending huge array"); - std::string huge(500000000, 'a'); - client->SpeedTest(huge.c_str()); - } - else{ - client->Error("Ignoring unknown message"); - } + if(type == GmshSocket::GMSH_STOP){ + client->Info("Stopping remote Gmsh..."); + break; + } + else if(type == GmshSocket::GMSH_VERTEX_ARRAY){ + +#if defined(HAVE_MPI) + client->Info("Sending vertex arrays..."); + + // Tell every node to start computing + int mpi_msg = MPI_GMSH_COMPUTE_VIEW; + MPI_Bcast(&mpi_msg, 1, MPI_INT, 0, MPI_COMM_WORLD); + + // Fill the arrays on the master node + for(unsigned int i = 0; i < PView::list.size(); i++) + PView::list[i]->fillVertexArrays(); + + // Wait and send the data from every other node + for (int i = 0; i < nbDaemon-1; i++) { + int nbArrays; + MPI_Status status; + MPI_Recv(&nbArrays, 1, MPI_INT, MPI_ANY_SOURCE, + MPI_GMSH_DATA_READY, MPI_COMM_WORLD, &status); + + int source = status.MPI_SOURCE; + // Get each varray in turn, then add it to the varrays of the master node + for (int j = 0; j < nbArrays; j++) { + int len; + MPI_Status status2; + MPI_Recv(&len, 1, MPI_INT, status.MPI_SOURCE, + MPI_GMSH_VARRAY_LEN, MPI_COMM_WORLD, &status2); + char str[len]; + MPI_Recv(str, len, MPI_CHAR, status.MPI_SOURCE, + MPI_GMSH_VARRAY, MPI_COMM_WORLD, &status2); + + addToVertexArrays(str,len); + } + } + computeAndSendVertexArrays(client,false); +#endif + } + else if(type == GmshSocket::GMSH_MERGE_FILE){ + MergeFile(msg); +#if defined(HAVE_MPI) + int mpi_msg = MPI_GMSH_MERGE_FILE; + MPI_Bcast(&mpi_msg, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(&length, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(msg, length, MPI_CHAR, 0, MPI_COMM_WORLD); +#else + computeAndSendVertexArrays(client); +#endif + } + else if(type == GmshSocket::GMSH_PARSE_STRING){ + ParseString(msg); +#if defined(HAVE_MPI) + int mpi_msg = MPI_GMSH_PARSE_STRING; + MPI_Bcast(&mpi_msg, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(&length, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(msg, length, MPI_CHAR, 0, MPI_COMM_WORLD); +#endif + } + else if(type == GmshSocket::GMSH_SPEED_TEST){ + client->Info("Sending huge array"); + std::string huge(500000000, 'a'); + client->SpeedTest(huge.c_str()); + } + else{ + client->Error("Ignoring unknown message"); + } - delete [] msg; + delete [] msg; + } else { +#if defined(HAVE_MPI) + // If we're not on the master node, we wait for him... + int mpi_msg; + MPI_Bcast(&mpi_msg, 1, MPI_INT, 0, MPI_COMM_WORLD); + if (mpi_msg == MPI_GMSH_COMPUTE_VIEW) + computeAndSendVertexArrays(); + else if(mpi_msg == MPI_GMSH_SHUTDOWN) + Msg::Exit(0); + else if(mpi_msg == MPI_GMSH_PARSE_STRING){ + int length; + MPI_Bcast(&length, 1, MPI_INT, 0, MPI_COMM_WORLD); + char msg[length]; + MPI_Bcast(msg, length, MPI_CHAR, 0, MPI_COMM_WORLD); + ParseString(msg); + } + else if (mpi_msg == MPI_GMSH_MERGE_FILE){ + int length; + MPI_Bcast(&length, 1, MPI_INT, 0, MPI_COMM_WORLD); + char msg[length]; + MPI_Bcast(msg, length, MPI_CHAR, 0, MPI_COMM_WORLD); + MergeFile(msg); + } +#endif + }// if (rank != 0) } + +#if defined(HAVE_MPI) + int mpi_msg = MPI_GMSH_SHUTDOWN; + MPI_Bcast(&mpi_msg, 1, MPI_INT, 0, MPI_COMM_WORLD); +#endif return 0; } diff --git a/Common/GmshRemote.h b/Common/GmshRemote.h index b441a76e50852a8009e370a8f01a84a76328b73f..5838e11845bc53f3f2b4f6efbf2921d61c97c7e1 100644 --- a/Common/GmshRemote.h +++ b/Common/GmshRemote.h @@ -8,6 +8,14 @@ #include <string> +#define MPI_GMSH_COMPUTE_VIEW 1 +#define MPI_GMSH_DATA_READY 2 +#define MPI_GMSH_VARRAY 3 +#define MPI_GMSH_VARRAY_LEN 4 +#define MPI_GMSH_SHUTDOWN 5 +#define MPI_GMSH_PARSE_STRING 6 +#define MPI_GMSH_MERGE_FILE 7 + int GmshRemote(); #endif diff --git a/Common/VertexArray.cpp b/Common/VertexArray.cpp index b708ff13fd8c8872417751de9130280eec52bcf6..bbe8837253ec46e9ca030ee5ba233fc91a7fc198 100644 --- a/Common/VertexArray.cpp +++ b/Common/VertexArray.cpp @@ -296,3 +296,16 @@ void VertexArray::fromChar(const char *bytes, int swap) memcpy(&_colors[0], &bytes[index], cs); index += cs; } } + +void VertexArray::merge(VertexArray* arr) { + if(arr->getNumVertices() != 0) { + _vertices.insert(_vertices.end(),arr->firstVertex(), + arr->lastVertex()); + _normals.insert(_normals.end(),arr->firstNormal(), + arr->lastNormal()); + _colors.insert(_colors.end(),arr->firstColor(), + arr->lastColor()); + _elements.insert(_elements.end(),arr->firstElementPointer(), + arr->lastElementPointer()); + } +} diff --git a/Common/VertexArray.h b/Common/VertexArray.h index 49eeb2b2b341276d12d55f0149da6392dfe20915..d84549f3fe84b310849b0009ec88539d9a9c06af 100644 --- a/Common/VertexArray.h +++ b/Common/VertexArray.h @@ -142,12 +142,24 @@ class VertexArray{ // range check 2) calling this if _vertices.size() == 0 will cause // some compilers to throw an exception) float *getVertexArray(int i=0){ return &_vertices[i]; } + std::vector<float>::iterator firstVertex(){return _vertices.begin();} + std::vector<float>::iterator lastVertex(){return _vertices.end();} + // return a pointer to the raw normal array char *getNormalArray(int i=0){ return &_normals[i]; } + std::vector<char>::iterator firstNormal(){return _normals.begin();} + std::vector<char>::iterator lastNormal(){return _normals.end();} + // return a pointer to the raw color array unsigned char *getColorArray(int i=0){ return &_colors[i]; } + std::vector<unsigned char>::iterator firstColor(){return _colors.begin();} + std::vector<unsigned char>::iterator lastColor(){return _colors.end();} + // return a pointer to the raw element array MElement **getElementPointerArray(int i=0){ return &_elements[i]; } + std::vector<MElement*>::iterator firstElementPointer(){return _elements.begin();} + std::vector<MElement*>::iterator lastElementPointer(){return _elements.end();} + // add element data in the arrays (if unique is set, only add the // element if another one with the same barycenter is not already // present) @@ -167,6 +179,8 @@ class VertexArray{ char *toChar(int num, std::string name, int type, double min, double max, int numsteps, double time, SBoundingBox3d bbox, int &len); void fromChar(const char *bytes, int swap); + // merge another vertex array into this one + void merge(VertexArray* arr); }; #endif diff --git a/Fltk/menuWindow.cpp b/Fltk/menuWindow.cpp index 2fc0566ad5d59c102843853a12b121840dd13686..2b2d06bbf0b82f852ab1a6061f74bda74024b668 100644 --- a/Fltk/menuWindow.cpp +++ b/Fltk/menuWindow.cpp @@ -183,6 +183,9 @@ static void file_remote_cb(Fl_Widget *w, void *data) FlGui::instance()->updateViews(); drawContext::global()->draw(); } + else if(str == "varrays"){ + server->SendString(GmshSocket::GMSH_VERTEX_ARRAY, " "); + } else if(str == "test"){ server->SendString(GmshSocket::GMSH_SPEED_TEST, "Speed test"); } @@ -2203,6 +2206,7 @@ static Fl_Menu_Item bar_table[] = { {"Start...", 0, (Fl_Callback *)file_remote_cb, (void*)"start"}, {"Merge...", 0, (Fl_Callback *)file_remote_cb, (void*)"merge"}, {"Clear", 0, (Fl_Callback *)file_remote_cb, (void*)"clear"}, + {"Get vertex arrays", 0, (Fl_Callback *)file_remote_cb, (void*)"varrays"}, {"Stop", 0, (Fl_Callback *)file_remote_cb, (void*)"stop"}, {0}, {"New Window", 0, (Fl_Callback *)file_window_cb, (void*)"new"}, diff --git a/Post/PViewData.h b/Post/PViewData.h index 70398f5dcafbd6a8509227a8cd1ea2dac3a2dd85..907015896110744ae8695326f18414425f1c72a6 100644 --- a/Post/PViewData.h +++ b/Post/PViewData.h @@ -65,12 +65,15 @@ class PViewData { // get the time value associated with the step-th time step virtual double getTime(int step){ return 0.; } - // get min/max for given step (global over all steps if step=-1) + // get/set min/max for given step (global over all steps if step=-1) virtual double getMin(int step=-1) = 0; virtual double getMax(int step=-1) = 0; + virtual void setMin(double min) = 0; + virtual void setMax(double max) = 0; - // get the bounding box + // get/set the bounding box virtual SBoundingBox3d getBoundingBox(int step=-1) = 0; + virtual void setBoundingBox(SBoundingBox3d& box) = 0; // get the number of elements of a given type, for a given step virtual int getNumScalars(int step=-1){ return 0; } diff --git a/Post/PViewDataGModel.h b/Post/PViewDataGModel.h index 64cb163432da76cce8b55a421227a46398754cee..aa9dc9e4fa194690d718e9b37ffa172a20888557 100644 --- a/Post/PViewDataGModel.h +++ b/Post/PViewDataGModel.h @@ -60,9 +60,9 @@ class stepData{ double getTime(){ return _time; } void setTime(double time){ _time = time; } double getMin(){ return _min; } - void setMin(double min ){ _min = min; } + double setMin(double min){ _min = min; } double getMax(){ return _max; } - void setMax(double max){ _max = max; } + double setMax(double max){ _max = max; } int getNumData() { if(!_data) return 0; @@ -132,8 +132,11 @@ class PViewDataGModel : public PViewData { int getNumTimeSteps(); double getTime(int step); double getMin(int step=-1); + void setMin(double min){ _min = min; } double getMax(int step=-1); + void setMax(double max){ _max = max; } SBoundingBox3d getBoundingBox(int step=-1); + void setBoundingBox(SBoundingBox3d& box){} int getNumScalars(int step=-1); int getNumVectors(int step=-1); int getNumTensors(int step=-1); diff --git a/Post/PViewDataList.h b/Post/PViewDataList.h index e626256a0437bb728f00ca943b0c7fa290436ca9..a7ef55bd5072c7240c2ab5ba9d7c734df7f0cb8f 100644 --- a/Post/PViewDataList.h +++ b/Post/PViewDataList.h @@ -1,3 +1,4 @@ + // Gmsh - Copyright (C) 1997-2009 C. Geuzaine, J.-F. Remacle // // See the LICENSE.txt file for license information. Please report all @@ -61,8 +62,11 @@ class PViewDataList : public PViewData { int getNumTimeSteps(){ return NbTimeStep; } double getTime(int step); double getMin(int step=-1); + void setMin(double min) {Min = min;} double getMax(int step=-1); + void setMax(double max) {Max = max;} SBoundingBox3d getBoundingBox(int step=-1){ return BBox; } + void setBoundingBox(SBoundingBox3d& box) {BBox = box;} int getNumScalars(int step=-1); int getNumVectors(int step=-1); int getNumTensors(int step=-1);