diff --git a/CMakeLists.txt b/CMakeLists.txt index 90a6d40206e5581b1215cfcda80396711690d90f..bb148b202509577630eeb38ae0e632b892cea031 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -143,7 +143,7 @@ set(GMSH_API Solver/thermicSolver.h Post/PView.h Post/PViewData.h Plugin/PluginManager.h Post/OctreePost.h Post/PViewDataList.h Post/PViewDataGModel.h Post/PViewOptions.h Post/ColorTable.h - Numeric/nodalBasis.h + Numeric/nodalBasis.h Post/adaptiveData.h Graphics/drawContext.h contrib/kbipack/gmp_normal_form.h contrib/kbipack/gmp_matrix.h contrib/kbipack/gmp_blas.h contrib/kbipack/mpz.h diff --git a/Common/CreateFile.cpp b/Common/CreateFile.cpp index d12c2dd7b58de8e20d4fc88e8d20355cab2c412d..9eab419c66abcc9ba6798e73e790bdf9a260f26d 100644 --- a/Common/CreateFile.cpp +++ b/Common/CreateFile.cpp @@ -34,6 +34,7 @@ int GetFileFormatFromExtension(const std::string &ext) if (ext == ".geo") return FORMAT_GEO; else if(ext == ".msh") return FORMAT_MSH; else if(ext == ".pos") return FORMAT_POS; + else if(ext == ".pvtu") return FORMAT_PVTU; else if(ext == ".opt") return FORMAT_OPT; else if(ext == ".unv") return FORMAT_UNV; else if(ext == ".vtk") return FORMAT_VTK; @@ -91,6 +92,7 @@ std::string GetDefaultFileName(int format) case FORMAT_GEO: name += ".geo_unrolled"; break; case FORMAT_MSH: name += ".msh"; break; case FORMAT_POS: name += ".pos"; break; + case FORMAT_PVTU: name += ".pvtu"; break; case FORMAT_OPT: name += ".opt"; break; case FORMAT_UNV: name += ".unv"; break; case FORMAT_VTK: name += ".vtk"; break; diff --git a/Common/Gmsh.cpp b/Common/Gmsh.cpp index 3a8c67f2896c1fb7670c26cfb468f0612610859e..a3df72026f5af7196118b9b2f80c12d0c8ebb730 100644 --- a/Common/Gmsh.cpp +++ b/Common/Gmsh.cpp @@ -229,6 +229,20 @@ int GmshWriteFile(const std::string &fileName) int GmshFinalize() { + //michel.rasquin@cenaero.be: + // Cleaning routine critical for ParaView plugin, + // where GMSH can be called several time successively. + // For that purpose, static variables need to be cleared. + + // Clear all PViews + PView::list.clear(); + + // Clear all GModels + GModel::list.clear(); + + // Clear files from CTX::instance() + CTX::instance()->files.clear(); + return 1; } diff --git a/Common/GmshDefines.h b/Common/GmshDefines.h index 8a3b64770bdc54d84d064bb9bd9b188c7d7da64f..31c0e3780caa97882cf46e059fbc3d6c359c8917 100644 --- a/Common/GmshDefines.h +++ b/Common/GmshDefines.h @@ -51,6 +51,7 @@ #define FORMAT_SU2 42 #define FORMAT_MPEG_PREVIEW 43 #define FORMAT_PGF 44 +#define FORMAT_PVTU 45 // Element types #define TYPE_PNT 1 diff --git a/Fltk/fileDialogs.cpp b/Fltk/fileDialogs.cpp index ced12c97b6ea7b8e0eccd87f72296a640df65639..06a84e4868c453a5bb096330902c40d2a36cf072 100644 --- a/Fltk/fileDialogs.cpp +++ b/Fltk/fileDialogs.cpp @@ -1414,6 +1414,168 @@ int posFileDialog(const char *name) return 0; } +static void _saveAdaptedViews(const std::string &name, int useDefaultName, int which, bool isBinary, + int adaptLev, double adaptErr, int npart, bool canAppend) +{ + if(PView::list.empty()){ + Msg::Error("No views to save"); + } + else if(which == 0){ + int iview = FlGui::instance()->options->view.index; + if(iview < 0 || iview >= (int)PView::list.size()){ + Msg::Info("No or invalid current view: saving View[0]"); + iview = 0; + } + PView::list[iview]->writeAdapt(name, useDefaultName, isBinary, adaptLev, adaptErr, npart); + } + else if(which == 1){ + int numVisible = 0; + for(unsigned int i = 0; i < PView::list.size(); i++) + if(PView::list[i]->getOptions()->visible) + numVisible++; + if(!numVisible){ + Msg::Error("No visible view"); + } + else{ + bool first = true; + for(unsigned int i = 0; i < PView::list.size(); i++){ + if(PView::list[i]->getOptions()->visible){ + std::string fileName = name; + if(!canAppend && numVisible > 1){ + std::ostringstream os; + os << "_" << i; + fileName += os.str(); + } + PView::list[i]->writeAdapt(fileName, useDefaultName, isBinary, adaptLev, adaptErr, + npart, first ? false : canAppend); + first = false; + } + } + } + } + else{ + for(unsigned int i = 0; i < PView::list.size(); i++){ + std::string fileName = name; + if(!canAppend && PView::list.size() > 1){ + std::ostringstream os; + os << "_" << i; + fileName += os.str(); + } + PView::list[i]->writeAdapt(fileName, useDefaultName, isBinary, adaptLev, adaptErr, + npart, i ? canAppend : false); + } + } +} + +int pvtuAdaptFileDialog(const char *name) +{ + struct _pvtuAdaptFileDialog{ + Fl_Window *window; + Fl_Choice *c[2]; + Fl_Button *ok, *cancel, *push[2]; + Fl_Value_Input *vi[3]; + Fl_Check_Button *defautName; + }; + static _pvtuAdaptFileDialog *dialog = NULL; + + static Fl_Menu_Item viewmenu[] = { + {"Current", 0, 0, 0}, + {"Visible", 0, 0, 0}, + {"All", 0, 0, 0}, + {0} + }; + static Fl_Menu_Item formatmenu[] = { + {"Binary", 0, 0, 0}, + {"ASCII", 0, 0, 0}, + {0} + }; + + int BBB = BB + 9; // labels too long + + if(!dialog){ + dialog = new _pvtuAdaptFileDialog; + int h = 3 * WB + 3 * BH, w = 2 * BBB + 3 * WB, y = WB; + dialog->window = new Fl_Double_Window(2*w, 2.5*h, "Adaptive View Options"); //FIXME: dimensions of the window + dialog->window->box(GMSH_WINDOW_BOX); + dialog->window->set_modal(); + dialog->c[0] = new Fl_Choice(WB, y, BBB + BBB / 2, BH, "View(s)"); y += BH; + dialog->c[0]->menu(viewmenu); + dialog->c[0]->align(FL_ALIGN_RIGHT); + dialog->c[1] = new Fl_Choice(WB, y, BBB + BBB / 2, BH, "Format"); y += BH; + dialog->c[1]->menu(formatmenu); + dialog->c[1]->align(FL_ALIGN_RIGHT); + + dialog->vi[0] = new Fl_Value_Input(WB, y, BBB + BBB / 2, BH, "Adaptive recursion level"); y += BH; + dialog->vi[0]->align(FL_ALIGN_RIGHT); + dialog->vi[0]->minimum(0); + dialog->vi[0]->maximum(6); + dialog->vi[0]->step(1); + dialog->vi[0]->value(1); + dialog->vi[0]->when(FL_WHEN_RELEASE); + + dialog->vi[1] = new Fl_Value_Input(WB, y, BBB + BBB / 2, BH, "Target error"); y += BH; + dialog->vi[1]->align(FL_ALIGN_RIGHT); + dialog->vi[1]->minimum(-1.e-4); + dialog->vi[1]->maximum(0.1); + dialog->vi[1]->step(1.e-4); + dialog->vi[1]->value(-1.e-4); + dialog->vi[1]->when(FL_WHEN_RELEASE); + + dialog->vi[2] = new Fl_Value_Input(WB, y, BBB + BBB / 2, BH, "Number of viz parts"); y += BH; + dialog->vi[2]->align(FL_ALIGN_RIGHT); + dialog->vi[2]->minimum(1); + dialog->vi[2]->maximum(262144); + dialog->vi[2]->step(1); + dialog->vi[2]->value(4); + dialog->vi[2]->when(FL_WHEN_RELEASE); + + dialog->defautName = new Fl_Check_Button(WB, y, BBB + BBB / 2, BH, "Use default filename (recommended)"); y += BH; + dialog->defautName->value(1); + + + dialog->ok = new Fl_Return_Button(WB, y + WB, BBB, BH, "OK"); + dialog->cancel = new Fl_Button(2 * WB + BBB, y + WB, BBB, BH, "Cancel"); + dialog->window->end(); + dialog->window->hotspot(dialog->window); + } + + dialog->window->show(); + + while(dialog->window->shown()){ + Fl::wait(); + for (;;) { + Fl_Widget* o = Fl::readqueue(); + if (!o) break; + if (o == dialog->ok) { + bool isBinary; + switch(dialog->c[1]->value()){ + case 0: isBinary = true; break; + case 1: isBinary = false; break; + } + + // Only one view can currently be saved at a time in a pvtu file set, with a repetition of the topology structure. + // Views/Fields can then be appended in ParaView using the AppendAttributes filter + // bool canAppend = (format == 2) ? true : false; + + int adaptLev = dialog->vi[0]->value(); + double adaptErr = dialog->vi[1]->value(); + int npart = dialog->vi[2]->value(); + int useDefaultName = dialog->defautName->value(); + bool canAppend = false; // Not yet implemented for VTK format here due to a tradeoff + // to limit memory consumption for high levels of adaptation + _saveAdaptedViews(name, useDefaultName, dialog->c[0]->value(), isBinary, adaptLev, adaptErr, npart, canAppend); + dialog->window->hide(); + return 1; + } + if (o == dialog->window || o == dialog->cancel){ + dialog->window->hide(); + return 0; + } + } + } + return 0; +} + int genericViewFileDialog(const char *name, const char *title, int format) { struct _viewFileDialog{ diff --git a/Fltk/fileDialogs.h b/Fltk/fileDialogs.h index 557b566d35cc940752f591e732feda69fd907c74..393c71053eba1f441fe9389318d204bf649b069a 100644 --- a/Fltk/fileDialogs.h +++ b/Fltk/fileDialogs.h @@ -29,6 +29,7 @@ int pgfBitmapFileDialog(const char *filename, const char *title, int format); int genericMeshFileDialog(const char *filename, const char *title, int format, bool binary_support, bool element_tag_support); int posFileDialog(const char *name); +int pvtuAdaptFileDialog(const char *name); int genericViewFileDialog(const char *name, const char *title, int format); int gl2psFileDialog(const char *filename, const char *title, int format); int optionsFileDialog(const char *filename); diff --git a/Fltk/graphicWindow.cpp b/Fltk/graphicWindow.cpp index 8a382e238377c184c8c3ccc21037d76e5ecab75e..1c43e2652b6b0385544b76d8d79df3a96e40eb66 100644 --- a/Fltk/graphicWindow.cpp +++ b/Fltk/graphicWindow.cpp @@ -318,6 +318,7 @@ static int _save_svg(const char *name){ return gl2psFileDialog static int _save_yuv(const char *name){ return genericBitmapFileDialog (name, "YUV Options", FORMAT_YUV); } static int _save_view_pos(const char *name){ return posFileDialog(name); } +static int _save_view_adapt_pvtu(const char *name){ return pvtuAdaptFileDialog(name); } static int _save_view_med(const char *name){ return genericViewFileDialog (name, "MED Options", 6); } static int _save_view_txt(const char *name){ return genericViewFileDialog @@ -330,6 +331,7 @@ static int _save_auto(const char *name) switch(GuessFileFormatFromFileName(name)){ case FORMAT_MSH : return _save_msh(name); case FORMAT_POS : return _save_view_pos(name); + case FORMAT_PVTU : return _save_view_adapt_pvtu(name); case FORMAT_TXT : return _save_view_txt(name); case FORMAT_OPT : return _save_options(name); case FORMAT_GEO : return _save_geo(name); @@ -413,6 +415,7 @@ static void file_save_as_cb(Fl_Widget *w, void *data) #endif {"Post-processing - Generic TXT" TT "*.txt", _save_view_txt}, {"Post-processing - Mesh Statistics" TT "*.pos", _save_mesh_stat}, + {"Post-processing - Adapted data" TT "*.pvtu", _save_view_adapt_pvtu}, {"Image - Encapsulated PostScript" TT "*.eps", _save_eps}, {"Image - GIF" TT "*.gif", _save_gif}, #if defined(HAVE_LIBJPEG) diff --git a/Post/PView.h b/Post/PView.h index 01f35774834ec6bd640f13881adad98ae13e8dca..64a15f3afe3f4dcc61a627e1d2b6bd48c59006bc 100644 --- a/Post/PView.h +++ b/Post/PView.h @@ -129,6 +129,11 @@ class PView{ static bool writeX3D(const std::string &fileName ); // IO write routine bool write(const std::string &fileName, int format, bool append=false); + + // michel.rasquin@cenaero.be: + // Routines for export of adapted views to pvtu file format for parallel visualization with paraview + bool writeAdapt(const std::string &fileName, int useDefaultName, bool isBinary, + int adaptLev, double adaptErr, int npart, bool append=false); // vertex arrays to draw the elements efficiently VertexArray *va_points, *va_lines, *va_triangles, *va_vectors, *va_ellipses; diff --git a/Post/PViewData.cpp b/Post/PViewData.cpp index c6820feaec05e3a39bb7d453a9d39ecd863be9f7..3cac8a552b25105efb40b283a6606468f33582e2 100644 --- a/Post/PViewData.cpp +++ b/Post/PViewData.cpp @@ -41,6 +41,34 @@ void PViewData::initAdaptiveData(int step, int level, double tol) } } +void PViewData::initAdaptiveDataLight(int step, int level, double tol) +{ + if(!_adaptive){ + Msg::Info("Initializing adaptive data %p interp size=%d", this, _interpolation.size()); + // michel.rasquin@cenaero.be: + // _outData in adaptive.h is only used for visualization of adapted views in the GMSH GUI. + // In some cases (export of adapted views under pvtu format, use of GMSH as external lib), + // this object is not needed so avoid its allocation in order to limit memory consumption + bool outDataInit = false; + _adaptive = new adaptiveData(this,outDataInit); + } +} + +void PViewData::saveAdaptedViewForVTK(const std::string &guifileName, int useDefaultName, + int step, int level, double tol, int npart, bool isBinary) +{ + if(_adaptive) { + // _adaptiveData has already been allocated from the adaptive view panel of the GUI for instance. + _adaptive->changeResolutionForVTK(step, level, tol, npart, isBinary, guifileName, useDefaultName); + } + else { + initAdaptiveDataLight(step, level, tol); + _adaptive->changeResolutionForVTK(step, level, tol, npart, isBinary, guifileName, useDefaultName); + destroyAdaptiveData(); + } +} + + void PViewData::destroyAdaptiveData() { if(_adaptive) delete _adaptive; diff --git a/Post/PViewData.h b/Post/PViewData.h index e9e8a45a052da6102ee025c197fef303c6dd9719..bdaae1e2ea70889f5bfd1a80ad8be780a047bee0 100644 --- a/Post/PViewData.h +++ b/Post/PViewData.h @@ -46,6 +46,8 @@ class PViewData { interpolationMatrices _interpolation; // global map of "named" interpolation matrices static std::map<std::string, interpolationMatrices> _interpolationSchemes; + // string for the name of the interpolation scheme + std::string _interpolationSchemeName; public: PViewData(); @@ -196,6 +198,15 @@ class PViewData { // initialize/destroy adaptive data void initAdaptiveData(int step, int level, double tol); + + // michel.rasquin@cenaero.be: + // Routines for + // - export of adapted views to pvtu file format for parallel visualization with paraview, + // - and/or generation of VTK data structure for ParaView plugin. + void initAdaptiveDataLight(int step, int level, double tol); + void saveAdaptedViewForVTK(const std::string &guifileName, int useDefaultName, + int step, int level, double tol, int npart, bool isBinary); + void destroyAdaptiveData(); // return the adaptive data @@ -218,6 +229,9 @@ class PViewData { static void removeInterpolationScheme(const std::string &name); static void addMatrixToInterpolationScheme(const std::string &name, int type, fullMatrix<double> &mat); + static int getSizeInterpolationScheme() {return _interpolationSchemes.size();} + std::string getInterpolationSchemeName() {return _interpolationSchemeName;} + void setInterpolationSchemeName(std::string name) {_interpolationSchemeName = name;} // smooth the data in the view (makes it C0) virtual void smooth(); diff --git a/Post/PViewDataGModel.cpp b/Post/PViewDataGModel.cpp index 2870f23b1e580601488c5b1f76200155e69e8f86..7225b35b829a1acbe78089c37fa5a8054c91dbd8 100644 --- a/Post/PViewDataGModel.cpp +++ b/Post/PViewDataGModel.cpp @@ -127,6 +127,10 @@ bool PViewDataGModel::finalize(bool computeMinMax, const std::string &interpolat if(!haveInterpolationMatrices()){ GModel *model = _steps[0]->getModel(); + + // michel.rasquin@cenaero.be: + // Required for ParaView plugin linked to GMSH as external library. + setInterpolationSchemeName(interpolationScheme); // if an interpolation scheme is explicitly provided, use it if(interpolationScheme.size()){ diff --git a/Post/PViewIO.cpp b/Post/PViewIO.cpp index f441b89398bab7398d5ff4791631b4dcedda7db6..0435b91ae7707679db95c57f5b27d25473410eec 100644 --- a/Post/PViewIO.cpp +++ b/Post/PViewIO.cpp @@ -14,6 +14,7 @@ #include "StringUtils.h" #include "Context.h" #include "OS.h" +#include "adaptiveData.h" bool PView::readPOS(const std::string &fileName, int fileIndex) { @@ -331,5 +332,12 @@ bool PView::write(const std::string &fileName, int format, bool append) return ret; } - - +// michel.rasquin@cenaero.be: +// Routines for export of adapted views to pvtu file format for parallel visualization with paraview. +bool PView::writeAdapt(const std::string &guifileName, int useDefaultName, bool isBinary, + int adaptLev, double adaptErr, int npart, bool append) +{ + Msg::StatusBar(true, "Writing '%s'...", guifileName.c_str()); + _data->saveAdaptedViewForVTK(guifileName, useDefaultName, _options->timeStep, adaptLev, adaptErr, npart, isBinary); + return true; +} diff --git a/Post/adaptiveData.cpp b/Post/adaptiveData.cpp index dfe854b6b7bbac9d680e0119f8f96dd26dbd401e..d94b795c206404441df7a1663650692e367dd229 100644 --- a/Post/adaptiveData.cpp +++ b/Post/adaptiveData.cpp @@ -50,6 +50,11 @@ int adaptiveTetrahedron::numEdges = 6; int adaptiveHexahedron::numEdges = 12; int adaptivePyramid::numEdges = 8; +std::vector<vectInt> globalVTKData::vtkGlobalConnectivity; +std::vector<int> globalVTKData::vtkGlobalCellType; +std::vector<PCoords> globalVTKData::vtkGlobalCoords; +std::vector<PValues> globalVTKData::vtkGlobalValues; + template <class T> static void cleanElement() { @@ -1497,13 +1502,18 @@ void adaptiveElements<T>::addInView(double tol, int step, } } -adaptiveData::adaptiveData(PViewData *data) +adaptiveData::adaptiveData(PViewData *data, bool outDataInit) : _step(-1), _level(-1), _tol(-1.), _inData(data), _points(0), _lines(0), _triangles(0), _quadrangles(0), _tetrahedra(0), _hexahedra(0), _prisms(0),_pyramids(0) { - _outData = new PViewDataList(true); - _outData->setName(data->getName() + "_adapted"); + if(outDataInit == true) { // For visualization of the adapted view in GMSH GUI only + _outData = new PViewDataList(true); + _outData->setName(data->getName() + "_adapted"); + } + else { + _outData = 0; // For external used + } std::vector<fullMatrix<double>*> p; if(_inData->getNumPoints()){ _inData->getInterpolationMatrices(TYPE_PNT, p); @@ -1537,6 +1547,8 @@ adaptiveData::adaptiveData(PViewData *data) _inData->getInterpolationMatrices(TYPE_PYR, p); _pyramids = new adaptiveElements<adaptivePyramid>(p); } + upWriteVTK(true); // By default, write VTK data if called... + upBuildStaticData(false); // ... and do not generated global static data structure (only useful for ParaView plugin). } adaptiveData::~adaptiveData() @@ -1549,7 +1561,7 @@ adaptiveData::~adaptiveData() if(_prisms) delete _prisms; if(_hexahedra) delete _hexahedra; if(_pyramids) delete _pyramids; - delete _outData; + if(_outData) delete _outData; } double adaptiveData::timerInit = 0.; @@ -1591,3 +1603,1023 @@ void adaptiveData::changeResolution(int step, int level, double tol, printf("adapt time = %g\n", timerAdapt); #endif } + +// michel.rasquin@cenaero.be: +// Routines for +// - export of adapted views to pvtu file format for parallel visualization with paraview, +// - and/or generation of VTK data structure for ParaView plugin. + +bool VTKData::isLittleEndian() +{ + int num = 1; + if(*(char *)&num == 1) + return true; // Little Endian + else + return false; // Big Endian +} + +void VTKData::SwapArrayByteOrder( void* array, int nbytes, int nItems ) +{ + // This swaps the byte order for the array of nItems each of size nbytes + int i,j; + unsigned char* ucDst = (unsigned char*)array; + + for(i=0; i < nItems; i++) { + for(j=0; j < (nbytes/2); j++) + std::swap( ucDst[j] , ucDst[(nbytes - 1) - j] ); + ucDst += nbytes; + } +} + +void VTKData::writeVTKElmData() +{ + // This routine writes vtu files (ascii or binary) from a elemental data base of nodes coordinates, + // cell connectivity, type and offset, and point data (either scalar or vector field) + + // Format choice + if(vtkFormat == "vtu") { + + if(vtkCountTotElmLev0 <= numPartMinElm*minElmPerPart) { + if( (vtkCountTotElmLev0-1)%minElmPerPart == 0) { //new filename + vtkCountFile = (vtkCountTotElmLev0-1)/minElmPerPart; + initVTKFile(); + } + } + else { + if( (vtkCountTotElmLev0-1-numPartMinElm*minElmPerPart)%maxElmPerPart == 0) { //new filename + vtkCountFile = numPartMinElm+(vtkCountTotElmLev0-1-numPartMinElm*minElmPerPart)/maxElmPerPart; + initVTKFile(); + } + } + + if (vtkIsBinary == true) { // Use appended format for raw binary + + // Write raw binary data to separate files first. + // Text headers will be added later, as wall as raw data size (needs to know the size before) + + int counter; + uint64_t *i64array; + uint8_t *i8array; + double *darray; + + // Node value + counter = 0; + darray= new double[vtkNumComp*vtkLocalValues.size()]; + for(std::vector<PValues>::iterator it = vtkLocalValues.begin();it != vtkLocalValues.end(); ++it) { + for(int i=0;i<vtkNumComp;i++) { + darray[counter+i] = it->v[i]; + } + counter+=vtkNumComp; + vtkCountTotVal+=vtkNumComp; + } + assert(counter==vtkNumComp* (int) vtkLocalValues.size()); + fwrite(darray,sizeof(double),vtkNumComp*vtkLocalValues.size(),vtkFileNodVal); + delete[] darray; + + // Points + int sizeArray = (int) vtkLocalCoords.size(); + darray = new double[3*sizeArray]; + counter = 0; + for(std::vector<PCoords>::iterator it = vtkLocalCoords.begin();it != vtkLocalCoords.end(); ++it) { + for(int i=0;i<3;i++) { + darray[counter+i] = (*it).c[i]; + } + counter+=3; + vtkCountCoord+=3; + } + fwrite(darray, sizeof(double),3*sizeArray,vtkFileCoord); + delete[] darray; + + //Cells + + // First count the number of integers that described the cell data in vtkConnectivity + // See http://www.vtk.org/wp-content/uploads/2015/04/file-formats.pdf (page 4) + int cellSizeData = 0; + for(std::vector<vectInt>::iterator it = vtkLocalConnectivity.begin();it != vtkLocalConnectivity.end(); ++it) { + cellSizeData+= (int) it->size(); // Contrary to vtk format, no +1 required for the number of nodes in the element + } + + // Connectivity (and build offset at the same time) + counter = 0; + int cellcounter = 0; + i64array = new uint64_t[cellSizeData]; + uint64_t *cellOffset = new uint64_t[vtkLocalConnectivity.size()]; + for(std::vector<vectInt>::iterator it = vtkLocalConnectivity.begin();it != vtkLocalConnectivity.end(); ++it) { + for(vectInt::iterator jt = it->begin(); jt != it->end(); ++jt) { + i64array[counter] = *jt; + counter++; + vtkCountTotNodConnect++; + } + cellOffset[cellcounter] = vtkCountTotNodConnect; // build the offset + cellcounter++; + } + fwrite(i64array,sizeof(uint64_t),cellSizeData,vtkFileConnect); + delete[] i64array; + + // Cell offset + fwrite(cellOffset,sizeof(uint64_t),vtkLocalConnectivity.size(),vtkFileCellOffset); + delete[] cellOffset; + + // Cell type + counter = 0; + i8array = new uint8_t[vtkLocalConnectivity.size()]; + for(std::vector<int>::iterator it = vtkLocalCellType.begin();it != vtkLocalCellType.end(); it++) { + i8array[counter] = *it; + counter++; + } + fwrite(i8array,sizeof(uint8_t),vtkLocalConnectivity.size(),vtkFileCellType); + delete[] i8array; + + } + else { // ascii + + // Node values + for(std::vector<PValues>::iterator it = vtkLocalValues.begin();it != vtkLocalValues.end(); ++it) { + + for(int i=0;i<vtkNumComp;i++) { + fprintf(vtkFileNodVal,"%23.16e ",(*it).v[i]); + vtkCountTotVal++; + if(vtkCountTotVal%6 == 0) fprintf(vtkFileNodVal,"\n"); + } + } + + for(std::vector<PCoords>::iterator it = vtkLocalCoords.begin();it != vtkLocalCoords.end(); it++) { + fprintf(vtkFileCoord,"%23.16e %23.16e %23.16e ",(*it).c[0],(*it).c[1],(*it).c[2]); + vtkCountCoord+=3; + if(vtkCountCoord%6 == 0) fprintf(vtkFileCoord,"\n"); + } + + // Cells + // Connectivity + int *cellOffset = new int[vtkLocalConnectivity.size()]; + int cellcounter = 0; + for(std::vector<vectInt>::iterator it = vtkLocalConnectivity.begin();it != vtkLocalConnectivity.end(); ++it) { + for(vectInt::iterator jt = it->begin(); jt != it->end(); ++jt) { + fprintf(vtkFileConnect,"%d ",*jt); + vtkCountTotNodConnect++; + if(vtkCountTotNodConnect%6 == 0) fprintf(vtkFileConnect,"\n"); + } + cellOffset[cellcounter] = vtkCountTotNodConnect; // build the offset + cellcounter++; + } + + // Cell offset + for(uint64_t i = 0 ; i<vtkLocalConnectivity.size(); i++) { + fprintf(vtkFileCellOffset,"%d ",cellOffset[i]); + vtkCountCellOffset++; + if(vtkCountCellOffset%6 == 0) fprintf(vtkFileCellOffset,"\n"); + } + delete[] cellOffset; + + // Cell type + for(std::vector<int>::iterator it = vtkLocalCellType.begin();it != vtkLocalCellType.end(); it++) { + fprintf(vtkFileCellType,"%d ",*it); + vtkCountCellType++; + if(vtkCountCellType%6 == 0) fprintf(vtkFileCellType,"\n"); + } + + } //if ascii + + //finalize and close current vtu file + if(vtkCountTotElmLev0 <= numPartMinElm*minElmPerPart) { + if( vtkCountTotElmLev0%minElmPerPart == 0) { + finalizeVTKFile(); + } + } + else { + if( (vtkCountTotElmLev0-numPartMinElm*minElmPerPart)%maxElmPerPart == 0) { + finalizeVTKFile(); + } + } + + } // vtu format + else + printf("ERROR: format unknown\n"); + + clearLocalData(); +} + +void VTKData::initVTKFile() +{ + // Temporary files + vtkFileCoord = fopen("vtkCoords.vtu","wb"); + vtkFileConnect = fopen("vtkConnectivity.vtu","wb"); + vtkFileCellOffset = fopen("vtkCellOffset.vtu","wb"); + vtkFileCellType = fopen("vtkCellType.vtu","wb"); + vtkFileNodVal = fopen("vtkNodeValue.vtu","wb"); + + if(vtkCountFile == 0) { // write the pvtu file and create the corresponding directory for vtu files + + if (vtkUseDefaultName == 1) { + vtkDirName = vtkFieldName + + "_step" + ToString<int>(vtkStep) + + "_level" + ToString<int>(vtkLevel) + + "_tol" + ToString<double>(vtkTol) + + "_npart" + ToString<int>(vtkNpart); + } + else { + // Remove existing extension here to avoid duplicate + std::size_t found = vtkFileName.find_last_of("."); + if(found != std::string::npos) vtkFileName = vtkFileName.substr(0,found); //remove extension + vtkDirName = vtkFileName; + } + + mkdir(vtkDirName.c_str(),S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); //755 + + vtkFileName = vtkDirName + ".p" + vtkFormat; // add pvtu extension to file name + vtkFile = fopen(vtkFileName.c_str(),"w"); + + bool littleEndian = isLittleEndian(); // Determine endianess + if (littleEndian == true) + fprintf(vtkFile,"<VTKFile type=\"PUnstructuredGrid\" version=\"1.0\" byte_order=\"LittleEndian\">\n"); + else + fprintf(vtkFile,"<VTKFile type=\"PUnstructuredGrid\" version=\"1.0\" byte_order=\"BigEndian\">\n"); + + fprintf(vtkFile,"<PUnstructuredGrid GhostLevel=\"0\">\n"); + fprintf(vtkFile,"<PPoints>\n"); + fprintf(vtkFile,"<DataArray type=\"Float64\" Name=\"Points\" NumberOfComponents=\"3\"/>\n"); + fprintf(vtkFile,"</PPoints>\n"); + + fprintf(vtkFile,"<PCells>\n"); + fprintf(vtkFile,"<PDataArray type=\"Int64\" Name=\"connectivity\" NumberOfComponents=\"1\"/>\n"); + fprintf(vtkFile,"<PDataArray type=\"Int64\" Name=\"offsets\" NumberOfComponents=\"1\"/>\n"); + fprintf(vtkFile,"<PDataArray type=\"UInt8\" Name=\"types\" NumberOfComponents=\"1\"/>\n"); + fprintf(vtkFile,"</PCells>\n"); + + fprintf(vtkFile,"<PPointData>\n"); + fprintf(vtkFile,"<PDataArray type=\"Float64\" Name=\"%s\" NumberOfComponents=\"%d\"/>\n",vtkFieldName.c_str(),vtkNumComp); + fprintf(vtkFile,"</PPointData>\n"); + + fprintf(vtkFile,"<PCellData>\n"); + fprintf(vtkFile,"</PCellData>\n"); + + for(int i=0; i<vtkNpart; i++) + fprintf(vtkFile,"<Piece Source=\"%s/data%d.vtu\"/>\n",vtkDirName.c_str(),i); + fprintf(vtkFile,"</PUnstructuredGrid>\n"); + fprintf(vtkFile,"</VTKFile>\n"); + fclose(vtkFile); + } +} + +void VTKData::finalizeVTKFile() +{ + // This routine writes vtu files (ascii or binary) from a complete data base of nodes coordinates, + // cell connectivity, type and offset, and point data (either scalar or vector field) + + // Close first temporary files. + // Todo: Avoid multiple open/close actions and keep the file open to write all information + fclose(vtkFileCoord); + fclose(vtkFileConnect); + fclose(vtkFileCellOffset); + fclose(vtkFileCellType); + fclose(vtkFileNodVal); + + bool littleEndian = isLittleEndian(); // Determine endianess + + // Open final file + std::string filename; + filename = vtkDirName + "/data" + ToString(vtkCountFile) + "." + vtkFormat; + + Msg::StatusBar(true,"Writing VTK data in %s: fieldname = %s - numElm = %d - numNod = %d nodes\n", + filename.c_str(), vtkFieldName.c_str(), vtkCountTotElm, vtkCountTotNod); + + assert(vtkCountTotNod == vtkCountCoord/3); + + // Now concatenate headers with data files + if(vtkFormat == "vtu") { // Format choice + + if (vtkIsBinary == true) { // Binary or ascii + + vtkFile = fopen(filename.c_str(),"wb"); + if(vtkFile == NULL) { + printf("Could not open file %s\n", filename.c_str()); + return; + } + + uint64_t byteoffset = 0; + + // Headers first + + if (littleEndian == true) + fprintf(vtkFile,"<VTKFile type=\"UnstructuredGrid\" version=\"1.0\" byte_order=\"LittleEndian\" header_type=\"UInt64\">\n"); + else + fprintf(vtkFile,"<VTKFile type=\"PUnstructuredGrid\" version=\"1.0\" byte_order=\"BigEndian\" header_type=\"UInt64\">\n"); + fprintf(vtkFile,"<UnstructuredGrid>\n"); + fprintf(vtkFile,"<Piece NumberOfPoints=\"%d\" NumberOfCells=\"%d\">\n",vtkCountTotNod,vtkCountTotElm); + + + // Node value + fprintf(vtkFile,"<PointData>\n"); + fprintf(vtkFile,"<DataArray type=\"Float64\" Name=\"%s\" NumberOfComponents=\"%d\" format=\"appended\" offset=\"%" PRIu64 "\"/>\n",vtkFieldName.c_str(),vtkNumComp,byteoffset); + fprintf(vtkFile,"</PointData>\n"); + byteoffset = byteoffset + (vtkCountTotNod*vtkNumComp+1)*sizeof(double); // +1 for datasize in bytes + + // Cell values (none here but may change) + fprintf(vtkFile,"<CellData>\n"); + fprintf(vtkFile,"</CellData>\n"); // no offset here because empty cell data + + // Nodes + fprintf(vtkFile,"<Points>\n"); + fprintf(vtkFile,"<DataArray type=\"Float64\" Name=\"Points\" NumberOfComponents=\"3\" format=\"appended\" offset=\"%" PRIu64 "\"/>\n",byteoffset); + fprintf(vtkFile,"</Points>\n"); + byteoffset = byteoffset + (vtkCountCoord+1)*sizeof(double); // +1 for datasize in bytes + + // Cells + fprintf(vtkFile,"<Cells>\n"); + fprintf(vtkFile,"<DataArray type=\"Int64\" Name=\"connectivity\" format=\"appended\" offset=\"%" PRIu64 "\"/>\n",byteoffset); + byteoffset = byteoffset + (vtkCountTotNodConnect+1)*sizeof(uint64_t); + fprintf(vtkFile,"<DataArray type=\"Int64\" Name=\"offsets\" format=\"appended\" offset=\"%" PRIu64 "\"/>\n",byteoffset); + byteoffset = byteoffset + (vtkCountTotElm+1)*sizeof(uint64_t); + fprintf(vtkFile,"<DataArray type=\"UInt8\" Name=\"types\" format=\"appended\" offset=\"%" PRIu64 "\"/>\n",byteoffset); + byteoffset = byteoffset + (vtkCountTotElm+1)*sizeof(uint8_t); + fprintf(vtkFile,"</Cells>\n"); + + fprintf(vtkFile,"</Piece>\n"); + fprintf(vtkFile,"</UnstructuredGrid>\n"); + + fprintf(vtkFile,"<AppendedData encoding=\"raw\">\n"); + fprintf(vtkFile,"_"); + + uint64_t datasize; + + // Node values + datasize = vtkNumComp*vtkCountTotNod*sizeof(double); + fwrite(&datasize,sizeof(uint64_t),1,vtkFile); + fclose(vtkFile); + + std::ifstream if_vtkNodeValue("vtkNodeValue.vtu", std::ios_base::binary); + std::ofstream of_vtkfile(filename.c_str(), std::ios_base::binary | std::ios_base::app); + of_vtkfile << if_vtkNodeValue.rdbuf(); + if_vtkNodeValue.close(); + of_vtkfile.close(); + + // Points + vtkFile = fopen(filename.c_str(),"ab"); + datasize = vtkCountTotNod*3*sizeof(double); + fwrite(&datasize,sizeof(uint64_t),1,vtkFile); + fclose(vtkFile); + + std::ifstream if_vtkCoords("vtkCoords.vtu", std::ios_base::binary); + of_vtkfile.open(filename.c_str(), std::ios_base::binary | std::ios_base::app); + of_vtkfile << if_vtkCoords.rdbuf(); + if_vtkCoords.close(); + of_vtkfile.close(); + + // Cells + // Connectivity + vtkFile = fopen(filename.c_str(),"ab"); + datasize = vtkCountTotNodConnect*sizeof(uint64_t); + fwrite(&datasize,sizeof(uint64_t),1,vtkFile); + fclose(vtkFile); + + std::ifstream if_vtkConnectivity("vtkConnectivity.vtu", std::ios_base::binary); + of_vtkfile.open(filename.c_str(), std::ios_base::binary | std::ios_base::app); + of_vtkfile << if_vtkConnectivity.rdbuf(); + if_vtkConnectivity.close(); + of_vtkfile.close(); + + // Cell offset + vtkFile = fopen(filename.c_str(),"ab"); + datasize = vtkCountTotElm*sizeof(uint64_t); + fwrite(&datasize,sizeof(uint64_t),1,vtkFile); + fclose(vtkFile); + + std::ifstream if_vtkCellOffset("vtkCellOffset.vtu", std::ios_base::binary); + of_vtkfile.open(filename.c_str(), std::ios_base::binary | std::ios_base::app); + of_vtkfile << if_vtkCellOffset.rdbuf(); + if_vtkCellOffset.close(); + of_vtkfile.close(); + + // Cell type + vtkFile = fopen(filename.c_str(),"ab"); + datasize = vtkCountTotElm*sizeof(uint8_t); + fwrite(&datasize,sizeof(uint64_t),1,vtkFile); + fclose(vtkFile); + + std::ifstream if_vtkCellType("vtkCellType.vtu", std::ios_base::binary); + of_vtkfile.open(filename.c_str(), std::ios_base::binary | std::ios_base::app); + of_vtkfile << if_vtkCellType.rdbuf(); + if_vtkCellType.close(); + of_vtkfile.close(); + + vtkFile = fopen(filename.c_str(),"ab"); + fprintf(vtkFile,"\n"); + fprintf(vtkFile,"</AppendedData>\n"); + fprintf(vtkFile,"</VTKFile>\n"); // for both binary and ascii + fclose(vtkFile); + + } + else { // ascii + + vtkFile = fopen(filename.c_str(),"w"); + if(vtkFile == NULL) { + printf("Could not open file %s\n", filename.c_str()); + return; + } + + if (littleEndian == true) + fprintf(vtkFile,"<VTKFile type=\"UnstructuredGrid\" version=\"1.0\" byte_order=\"LittleEndian\" header_type=\"UInt64\">\n"); + else + fprintf(vtkFile,"<VTKFile type=\"PUnstructuredGrid\" version=\"1.0\" byte_order=\"BigEndian\" header_type=\"UInt64\">\n"); + fprintf(vtkFile,"<UnstructuredGrid>\n"); + fprintf(vtkFile,"<Piece NumberOfPoints=\"%d\" NumberOfCells=\"%d\">\n",vtkCountTotNod,vtkCountTotElm); + + // Node values + fprintf(vtkFile,"<PointData>\n"); + fprintf(vtkFile,"<DataArray type=\"Float64\" Name=\"%s\" NumberOfComponents=\"%d\" format=\"ascii\">\n",vtkFieldName.c_str(),vtkNumComp); + fclose(vtkFile); //close file for binary concatenation + + std::ifstream if_vtkNodeValue("vtkNodeValue.vtu", std::ios_base::binary); + std::ofstream of_vtkfile(filename.c_str(), std::ios_base::binary | std::ios_base::app); + of_vtkfile << if_vtkNodeValue.rdbuf(); + if_vtkNodeValue.close(); + of_vtkfile.close(); + + vtkFile = fopen(filename.c_str(),"a"); + fprintf(vtkFile,"</DataArray>\n"); + fprintf(vtkFile,"</PointData>\n"); + + //Cell values + fprintf(vtkFile,"<CellData>\n"); + fprintf(vtkFile,"</CellData>\n"); + + //Nodes + fprintf(vtkFile,"<Points>\n"); + fprintf(vtkFile,"<DataArray type=\"Float64\" Name=\"Points\" NumberOfComponents=\"3\" format=\"ascii\">\n"); + fclose(vtkFile); //close file for binary concatenation + + of_vtkfile.open(filename.c_str(), std::ios_base::binary | std::ios_base::app); + std::ifstream if_vtkCoords("vtkCoords.vtu", std::ios_base::binary); + of_vtkfile << if_vtkCoords.rdbuf(); + if_vtkCoords.close(); + of_vtkfile.close(); + + vtkFile = fopen(filename.c_str(),"a"); + fprintf(vtkFile,"</DataArray>\n"); + fprintf(vtkFile,"</Points>\n"); + + // Cells + fprintf(vtkFile,"<Cells>\n"); + fprintf(vtkFile,"<DataArray type=\"Int64\" Name=\"connectivity\" format=\"ascii\">\n"); + fclose(vtkFile); //close file for binary concatenation + + // Connectivity + of_vtkfile.open(filename.c_str(), std::ios_base::binary | std::ios_base::app); + std::ifstream if_vtkConnectivity("vtkConnectivity.vtu", std::ios_base::binary); + of_vtkfile << if_vtkConnectivity.rdbuf(); + if_vtkConnectivity.close(); + of_vtkfile.close(); + + vtkFile = fopen(filename.c_str(),"a"); + fprintf(vtkFile,"</DataArray>\n"); + + // Cell offset + fprintf(vtkFile,"<DataArray type=\"Int64\" Name=\"offsets\" format=\"ascii\">\n"); + fclose(vtkFile); //close file for binary concatenation + + of_vtkfile.open(filename.c_str(), std::ios_base::binary | std::ios_base::app); + std::ifstream if_vtkCellOffset("vtkCellOffset.vtu", std::ios_base::binary); + of_vtkfile << if_vtkCellOffset.rdbuf(); + if_vtkCellOffset.close(); + of_vtkfile.close(); + + vtkFile = fopen(filename.c_str(),"a"); + fprintf(vtkFile,"</DataArray>\n"); + + // Cell type + fprintf(vtkFile,"<DataArray type=\"UInt8\" Name=\"types\" format=\"ascii\">\n"); + fclose(vtkFile); //close file for binary concatenation + + of_vtkfile.open(filename.c_str(), std::ios_base::binary | std::ios_base::app); + std::ifstream if_vtkCellType("vtkCellType.vtu", std::ios_base::binary); + of_vtkfile << if_vtkCellType.rdbuf(); + if_vtkCellType.close(); + of_vtkfile.close(); + + vtkFile = fopen(filename.c_str(),"a"); + fprintf(vtkFile,"</DataArray>\n"); + fprintf(vtkFile,"</Cells>\n"); + + fprintf(vtkFile,"</Piece>\n"); + fprintf(vtkFile,"</UnstructuredGrid>\n"); + + + fprintf(vtkFile,"</VTKFile>\n"); // for both binary and ascii + fclose(vtkFile); + } // if binary/ascii + + // Remove temporary files now + if(remove("vtkCoords.vtu") !=0 ) printf("ERROR: Could not remove vtkCoords.vtu\n"); + if(remove("vtkConnectivity.vtu") !=0 ) printf("ERROR: Could not remove vtkConnectivity.vtu\n"); + if(remove("vtkCellOffset.vtu") !=0 ) printf("ERROR: Could not remove vtkCellOffset.vtu\n"); + if(remove("vtkCellType.vtu") !=0 ) printf("ERROR: Could not remove vtkCellType.vtu\n"); + if(remove("vtkNodeValue.vtu") !=0 ) printf("ERROR: Could not remove vtkNodeValue.vtu\n"); + + // Reset counters for next file + vtkCountTotNod = 0; + vtkCountTotElm = 0; + vtkCountCoord = 0; + vtkCountTotNodConnect = 0; + vtkCountTotVal = 0; + vtkCountCellOffset = 0; + vtkCountCellType = 0; + } + + else + Msg::Error("File format unknown: %s\n",vtkFormat.c_str()); +} + +int VTKData::getPVCellType(int numEdges) +{ + int cellType; // Convention for cell types in ParaView + switch(numEdges){ + case 0: + printf("WARNING: Trying to write a node to the ParaView data base and file\n"); + cellType = -1; + break; + case 1: + printf("WARNING: Trying to write a node to the ParaView data base and file\n"); + cellType = -2; + break; + case 3: + cellType = 5; // 2D VTK triangle + break; + case 4: + cellType = 9; // 2D VTK quadrangle + break; + case 6: + cellType = 10; // 3D VTK tetrahedron + break; + case 9: + cellType = 13; // 3D VTK prism/wedge + break; + case 8: + cellType = 14; // 3D VTK pyramid + break; + case 12: + cellType = 12; // 3D VTK hexahedron + break; + default: + printf("ERROR: No cell type was detected\n"); + cellType = -1; + break; + } + + return cellType; +} + + + +template <class T> +void adaptiveElements<T>::adaptForVTK(double tol, + int numComp, + std::vector<PCoords> &coords, + std::vector<PValues> &values) +{ + int numVertices = T::allVertices.size(); + + if(!numVertices){ + Msg::Error("No adapted vertices to interpolate"); + return; + } + + int numVals = _coeffsVal ? _coeffsVal->size1() : T::numNodes; + if(numVals != (int)values.size()){ + Msg::Error("Wrong number of values in adaptation %d != %i", + numVals, values.size()); + return; + } + + #ifdef TIMER + double t1 = GetTimeInSeconds(); + #endif + + fullVector<double> val(numVals), res(numVertices); + switch (numComp) { + case 1: + { + for(int i = 0; i < numVals; i++) val(i) = values[i].v[0]; + break; + } + case 3: + case 9: + { + for(int i = 0; i < numVals; i++) { + val(i) = 0; + for (int k=0; k < numComp; k++) val(i) += values[i].v[k] * values[i].v[k]; + } + break; + } + default: + { + Msg::Error("Can only adapt scalar, vector or tensor data"); + return; + } + } + + _interpolVal->mult(val, res); + + double minVal = VAL_INF; + double maxVal = -VAL_INF; + for(int i = 0; i < numVertices; i++){ + minVal = std::min(minVal, res(i)); + maxVal = std::max(maxVal, res(i)); + } + + fullMatrix<double> *resxyz = 0; + if(numComp == 3 || numComp == 9){ + fullMatrix<double> valxyz(numVals, numComp); + resxyz = new fullMatrix<double>(numVertices, numComp); + for(int i = 0; i < numVals; i++){ + for (int k = 0; k < numComp; k++) { + valxyz(i,k) = values[i].v[k]; + } + } + _interpolVal->mult(valxyz, *resxyz); + } + + int numNodes = _coeffsGeom ? _coeffsGeom->size1() : T::numNodes; + if(numNodes != (int)coords.size()){ + Msg::Error("Wrong number of nodes in adaptation %d != %i", + numNodes, coords.size()); + if(resxyz) delete resxyz; + return; + } + + fullMatrix<double> xyz(numNodes, 3), XYZ(numVertices, 3); + for(int i = 0; i < numNodes; i++){ + xyz(i, 0) = coords[i].c[0]; + xyz(i, 1) = coords[i].c[1]; + xyz(i, 2) = coords[i].c[2]; + } + _interpolGeom->mult(xyz, XYZ); + + #ifdef TIMER + adaptiveData::timerAdapt += GetTimeInSeconds() - t1; + return; + #endif + + int i = 0; + for(std::set<adaptiveVertex>::iterator it = T::allVertices.begin(); it != T::allVertices.end(); ++it) { + // ok because we know this will not change the set ordering + adaptiveVertex *p = (adaptiveVertex*)&(*it); + p->val = res(i); + if(resxyz){ + p->val = (*resxyz)(i, 0); + p->valy = (*resxyz)(i, 1); + p->valz = (*resxyz)(i, 2); + if (numComp == 9) { + p->valyx = (*resxyz)(i,3); + p->valyy = (*resxyz)(i,4); + p->valyz = (*resxyz)(i,5); + p->valzx = (*resxyz)(i,6); + p->valzy = (*resxyz)(i,7); + p->valzz = (*resxyz)(i,8); + } + } + p->X = XYZ(i, 0); + p->Y = XYZ(i, 1); + p->Z = XYZ(i, 2); + i++; + } + + if(resxyz) delete resxyz; + + for(typename std::list<T*>::iterator it = T::all.begin();it != T::all.end(); it++) + (*it)->visible = false; + + if(tol != 0.){ + double avg = fabs(maxVal - minVal); + if(tol < 0) avg = 1.; // force visibility to the smallest subdivision + T::error(avg, tol); + } + + coords.clear(); + values.clear(); + for(typename std::list<T*>::iterator it = T::all.begin();it != T::all.end(); it++){ + if((*it)->visible){ + adaptiveVertex **p = (*it)->p; + for(int i = 0; i < T::numNodes; i++) { + coords.push_back(PCoords(p[i]->X, p[i]->Y, p[i]->Z)); + switch (numComp) { + case 1: + values.push_back(PValues(p[i]->val)); + break; + case 3: + values.push_back(PValues(p[i]->val, p[i]->valy, p[i]->valz)); + break; + case 9: + values.push_back(PValues(p[i]->val, + p[i]->valy, p[i]->valz, + p[i]->valyx,p[i]->valyy,p[i]->valyz, + p[i]->valzx,p[i]->valzy,p[i]->valzz)); + break; + } + } + } + } +} + + +template <class T> +void adaptiveElements<T>::buildMapping(nodMap<T> &myNodMap, double tol, int &numNodInsert) +{ + + if (tol > 0.0 || myNodMap.getSize() == 0) { + // Either this is not a uniform refinement and we need to rebuild the whole mapping + // for each canonical element, or this is the first time we try to build the mapping + + myNodMap.cleanMapping(); // Required if tol > 0 (local error based adaptation) + + for(typename std::list<T*>::iterator itleaf = T::all.begin();itleaf != T::all.end(); itleaf++) { + // Visit all the leaves of the refined canonical element + + if ((*itleaf)->visible == true) { + // Find the leaves that are flagged for visibility + + for(int i=0;i<T::numNodes;i++) { + // Visit each nodes of the leaf (3 for triangles, 4 for quadrangle, etc) + adaptiveVertex pquery; + pquery.x = (*itleaf)->p[i]->x; + pquery.y = (*itleaf)->p[i]->y; + pquery.z = (*itleaf)->p[i]->z; + std::set<adaptiveVertex>::iterator it = T::allVertices.find(pquery); + if(it == T::allVertices.end()){ + printf("ERROR: Could not find adaptive Vertex in adaptiveElements<T>::buildMapping %f %f %f\n", + pquery.x,pquery.y,pquery.z); + } + else{ + // Compute the distance in the list to get the mapping for + // the canonical element (note std:distance returns long int + int dist = (int) std::distance(T::allVertices.begin(),it); + myNodMap.mapping.push_back(dist); + } + // quit properly if vertex not found - Should not happen though + assert(it != T::allVertices.end()); + } //for + } //if + }//for + + // Count number of unique nodes from the mapping + // Use an ordered set for efficiency + // This set is also used in case of partiel refinement + std::set<int> uniqueNod; + for(std::vector<int>::iterator it = myNodMap.mapping.begin(); it != myNodMap.mapping.end(); it++) { + uniqueNod.insert(*it); + } + numNodInsert = (int) uniqueNod.size(); + + // Renumber the elm in the mapping in case of partial refinement (when vis tolerance > 0) + // so that we have a continuous numbering starting from 0 with no missing node id in the connectivity + // This require a new local and temporary mapping, based on uniqueNod already generated above + if(tol > 0.0) { + std::set<int>::iterator jt; + for(std::vector<int>::iterator it = myNodMap.mapping.begin();it != myNodMap.mapping.end();++it) { + jt = uniqueNod.find(*it); + *it = std::distance(uniqueNod.begin(), jt); + } + } + } +} + +template <class T> +void adaptiveElements<T>::addInViewForVTK(int step, + PViewData *in, + VTKData &myVTKData, + bool writeVTK, + bool buildStaticData) +{ + int numComp = in->getNumComponents(0, 0, 0); + if(numComp != 1 && numComp != 3 && numComp != 9) return; + + int numEle = 0; + switch(T::numEdges){ + case 0: + numEle = in->getNumPoints(); + break; + case 1: + numEle = in->getNumLines(); + break; + case 3: + numEle = in->getNumTriangles(); + break; + case 4: + numEle = in->getNumQuadrangles(); + break; + case 6: + numEle = in->getNumTetrahedra(); + break; + case 9: + numEle = in->getNumPrisms(); + break; + case 8: + numEle = in->getNumPyramids(); + break; + case 12: + numEle = in->getNumHexahedra(); + break; + } + if(!numEle) return; + + // New variables for high order visualiztion through vtk files + int numNodInsert; + nodMap<T> myNodMap; + + for(int ent = 0; ent < in->getNumEntities(step); ent++){ + for(int ele = 0; ele < in->getNumElements(step, ent); ele++){ + if(in->skipElement(step, ent, ele) || + in->getNumEdges(step, ent, ele) != T::numEdges) continue; + int numNodes = in->getNumNodes(step, ent, ele); + std::vector<PCoords> coords; + for(int i = 0; i < numNodes; i++){ + double x, y, z; + in->getNode(step, ent, ele, i, x, y, z); + coords.push_back(PCoords(x, y, z)); + } + int numVal = in->getNumValues(step, ent, ele); + std::vector<PValues> values; + + switch (numComp) { + case 1: + for(int i = 0; i < numVal; i++){ + double val; + in->getValue(step, ent, ele, i, val); + values.push_back(PValues(val)); + } + break; + case 3: + for(int i = 0; i < numVal / 3; i++){ + double vx, vy, vz; + in->getValue(step, ent, ele, 3 * i, vx); + in->getValue(step, ent, ele, 3 * i + 1, vy); + in->getValue(step, ent, ele, 3 * i + 2, vz); + values.push_back(PValues(vx, vy, vz)); + } + break; + case 9: + for(int i = 0; i < numVal / 9; i++){ + double vxx, vxy, vxz,vyx, vyy, vyz,vzx, vzy, vzz ; + in->getValue(step, ent, ele, 9 * i + 0, vxx); + in->getValue(step, ent, ele, 9 * i + 1, vxy); + in->getValue(step, ent, ele, 9 * i + 2, vxz); + in->getValue(step, ent, ele, 9 * i + 3, vyx); + in->getValue(step, ent, ele, 9 * i + 4, vyy); + in->getValue(step, ent, ele, 9 * i + 5, vyz); + in->getValue(step, ent, ele, 9 * i + 6, vzx); + in->getValue(step, ent, ele, 9 * i + 7, vzy); + in->getValue(step, ent, ele, 9 * i + 8, vzz); + values.push_back(PValues(vxx,vxy,vxz, + vyx,vyy,vyz, + vzx,vzy,vzz)); + } + break; + } + + adaptForVTK(myVTKData.vtkTol, numComp, coords, values);//, out->Min, out->Max, plug); + + // Inside initial element, after adapt() has been called + + // Build the mapping of the canonical element, + // or recycle existing one in case of uniform refinement + buildMapping(myNodMap, myVTKData.vtkTol, numNodInsert); + + // Pre-allocate some space for the local coordinates and connectivity + // in order to write to any component of the vector later through vec[i] + + myVTKData.vtkLocalCoords.resize(numNodInsert,PCoords(0.0,0.0,0.0)); + myVTKData.vtkLocalValues.resize(numNodInsert,PValues(numComp)); + + for(unsigned int i = 0; i < coords.size() / T::numNodes; i++){ + + // Loop over + // - all refined elements if refinement level > 0 + // - single initial element when refinement box is checked for the first time (ref level =0) + + // local connectivity for the considered sub triangle + vectInt vtkElmConnectivity; + + for(int k = 0; k < T::numNodes; ++k) { + + // Connectivity of the considered sub-element + int countTotNodloc = T::numNodes * i + k; // Nodes are duplicate here + int vtkNodeId = myVTKData.vtkCountTotNod + myNodMap.mapping[countTotNodloc]; + vtkElmConnectivity.push_back(vtkNodeId); + + // Coordinates of the nodes of the considered sub-element + double px, py, pz; + px = coords[T::numNodes * i + k].c[0]; + py = coords[T::numNodes * i + k].c[1]; + pz = coords[T::numNodes * i + k].c[2]; + PCoords tmpCoords = PCoords(px,py,pz); + myVTKData.vtkLocalCoords[myNodMap.mapping[countTotNodloc]] = tmpCoords; + + // Value associated with each nodes of the sub-element + myVTKData.vtkLocalValues[myNodMap.mapping[countTotNodloc]] = values[T::numNodes * i + k]; + + } + + // Add elm connectivity to vector + myVTKData.vtkLocalConnectivity.push_back(vtkElmConnectivity); + + // Increment global elm number + myVTKData.incrementTotElm(1); + + // Save element type + myVTKData.vtkLocalCellType.push_back(myVTKData.getPVCellType(T::numEdges)); + + + // Global variables + if(buildStaticData == true) { + globalVTKData::vtkGlobalConnectivity.push_back(vtkElmConnectivity); + globalVTKData::vtkGlobalCellType.push_back(myVTKData.getPVCellType(T::numEdges)); + } + + // Clear existing structure (safer) + vtkElmConnectivity.clear(); + } + + // Increment global node and elm-lev0 number + myVTKData.incrementTotNod(numNodInsert); + myVTKData.incrementTotElmLev0(1); + + // Write the VTK data structure of the consider element to vtu file + + if(writeVTK == true) { + myVTKData.writeVTKElmData(); + } + + if(buildStaticData == true) { + + for(int i=0;i<numNodInsert; i++) { + globalVTKData::vtkGlobalCoords.push_back(myVTKData.vtkLocalCoords[i]); + } + + for(int i=0;i<numNodInsert; i++) { + globalVTKData::vtkGlobalValues.push_back(myVTKData.vtkLocalValues[i]); + } + } + + } // loop over mesh element + } + +} + +template <class T> +int adaptiveElements<T>::countElmLev0(int step, PViewData *in) +{ + int sum = 0; + for(int ent = 0; ent < in->getNumEntities(step); ent++){ + for(int ele = 0; ele < in->getNumElements(step, ent); ele++){ + if(in->skipElement(step, ent, ele) || + in->getNumEdges(step, ent, ele) != T::numEdges) continue; + else + sum++; + } + } + return sum; +} + +int adaptiveData::countTotElmLev0(int step, PViewData *in) +{ + + int sumElm = 0; + + if(_triangles) sumElm+=_triangles->countElmLev0(step, in); + if(_quadrangles) sumElm+=_quadrangles->countElmLev0(step, in); + if(_tetrahedra) sumElm+=_tetrahedra->countElmLev0(step, in); + if(_prisms) sumElm+=_prisms->countElmLev0(step, in); + if(_hexahedra) sumElm+=_hexahedra->countElmLev0(step, in); + if(_pyramids) sumElm+=_pyramids->countElmLev0(step, in); + + return sumElm; +} + + +void adaptiveData::changeResolutionForVTK(int step, int level, double tol, int npart, bool isBinary, + const std::string &guiFileName, int useDefaultName) +{ + //clean global VTK data structure before (re)generating it + if(buildStaticData == true) globalVTKData::clearGlobalData(); + + VTKData myVTKData(_inData->getName(), _inData->getNumComponents(0, 0, 0), + step, level, tol, guiFileName, useDefaultName, npart, isBinary); + myVTKData.vtkTotNumElmLev0 = countTotElmLev0(step, _inData); + myVTKData.setFileDistribution(); + + // Views of 2D and 3D elements only supported for VTK. _points and _lines are currently ignored. + if(_triangles) _triangles->init(myVTKData.vtkLevel); + if(_quadrangles) _quadrangles->init(myVTKData.vtkLevel); + if(_tetrahedra) _tetrahedra->init(myVTKData.vtkLevel); + if(_prisms) _prisms->init(myVTKData.vtkLevel); + if(_hexahedra) _hexahedra->init(myVTKData.vtkLevel); + if(_pyramids) _pyramids->init(myVTKData.vtkLevel); + + if(_triangles) _triangles->addInViewForVTK(step, _inData, myVTKData, writeVTK, buildStaticData); + if(_quadrangles) _quadrangles->addInViewForVTK(step, _inData, myVTKData, writeVTK, buildStaticData); + if(_tetrahedra) _tetrahedra->addInViewForVTK(step, _inData, myVTKData, writeVTK, buildStaticData); + if(_prisms) _prisms->addInViewForVTK(step, _inData, myVTKData, writeVTK, buildStaticData); + if(_hexahedra) _hexahedra->addInViewForVTK(step, _inData, myVTKData, writeVTK, buildStaticData); + if(_pyramids) _pyramids->addInViewForVTK(step, _inData, myVTKData, writeVTK, buildStaticData); + + Msg::StatusBar(true,"Done writing VTK data"); +} diff --git a/Post/adaptiveData.h b/Post/adaptiveData.h index adf026f952d06c0ffacd40708b461638cf16f741..80a3399a92efb66d1861c760fc5aebb288b36889 100644 --- a/Post/adaptiveData.h +++ b/Post/adaptiveData.h @@ -11,12 +11,32 @@ #include <vector> #include <cstdlib> #include <algorithm> +#include <sys/stat.h> +#include <assert.h> +#include <fstream> +#include <stdio.h> +#include <string> +#include <sstream> #include "fullMatrix.h" + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> +typedef std::vector<int> vectInt; + class PViewData; class PViewDataList; class GMSH_PostPlugin; +// For old compilers that do not support yet std::to_string() +template <class T> +std::string ToString(const T& val) +{ + std::stringstream stream; + stream << val; + return stream.str(); +} + class adaptiveVertex { public: float x, y, z; //!< parametric coordinates @@ -38,6 +58,23 @@ class adaptiveVertex { } }; +template <class T> +class nodMap { +public: + std::vector<int> mapping; + +public: + void cleanMapping() + { + mapping.clear(); + } + ~nodMap() + { + cleanMapping(); + } + int getSize() {return (int) mapping.size();} +}; + class adaptivePoint { public: bool visible; @@ -352,23 +389,210 @@ class PCoords { class PValues{ public: - double v[9]; + short int sizev; //acceptable values: 1 (scalar), 3 (vector), 9 (tensor) + double *v; + PValues(const PValues& obj) { + sizev = obj.sizev; + v = new double[sizev]; + for(int i=0;i<sizev;i++) { + v[i] = obj.v[i]; + } + } + PValues(int size) + { + sizev = size; + v = new double[sizev]; + for(int i=0;i<sizev;i++) { + v[i] = 0.0; + } + } PValues(double vx) { + sizev = 1; + v = new double[sizev]; v[0] = vx; } PValues(double vx, double vy, double vz) { + sizev = 3; + v = new double[sizev]; v[0] = vx; v[1] = vy; v[2] = vz; } PValues(double vxx, double vxy, double vxz, double vyx, double vyy, double vyz, double vzx, double vzy, double vzz) { + sizev = 9; + v = new double[sizev]; v[0] = vxx; v[1] = vxy; v[2] = vxz; v[3] = vyx; v[4] = vyy; v[5] = vyz; v[6] = vzx; v[7] = vzy; v[8] = vzz; } + ~PValues() { + delete[] v; + } + void operator = (const PValues& obj) { + // Assume PValues object has already been generated + // and v allocated when the operator = is called + if(sizev != obj.sizev) Msg::Error("In PValues overlodaing operator: size mistmatch %d %d",sizev); + for(int i=0;i<sizev;i++) { + v[i] = obj.v[i]; + } + } +}; + +class globalVTKData { + public: + + static std::vector<vectInt> vtkGlobalConnectivity; // conectivity (vector of vector) + static std::vector<int> vtkGlobalCellType; // topology + static std::vector<PCoords> vtkGlobalCoords; // coordinates + static std::vector<PValues> vtkGlobalValues; // nodal values (either scalar or vector) + + globalVTKData(); + + static void clearGlobalConnectivity() { + for(std::vector<vectInt>::iterator it = vtkGlobalConnectivity.begin();it != vtkGlobalConnectivity.end(); ++it) { + it->clear(); + } + vtkGlobalConnectivity.clear(); + } + + static void clearGlobalCellType() { + vtkGlobalCellType.clear(); + } + + static void clearGlobalCoords() { + vtkGlobalCoords.clear(); + } + + static void clearGlobalValues() { + vtkGlobalValues.clear(); + } + + static void clearGlobalData() { + clearGlobalConnectivity(); + clearGlobalCellType(); + clearGlobalCoords(); + clearGlobalValues(); + } + + ~globalVTKData() { + clearGlobalData(); + } + +}; + +class VTKData { + public: + // Data container to write output files readable for ParaView + // vtk legacy and vtu for now + std::string vtkFieldName; + std::string vtkFileName; + std::string vtkFormat; + std::string vtkDirName; + + int vtkStep; + int vtkLevel; + int vtkNumComp; + double vtkTol; + int vtkNpart; + + bool vtkIsBinary; + int vtkUseDefaultName; + int minElmPerPart, maxElmPerPart, numPartMinElm, numPartMaxElm; + + // File variables + FILE *vtkFile; + FILE *vtkFileCoord; + FILE *vtkFileConnect; + FILE *vtkFileCellOffset; + FILE *vtkFileCellType; + FILE *vtkFileNodVal; + int vtkCountFile; + + int vtkTotNumElmLev0; + int vtkCountTotElmLev0; + int vtkCountTotNod; + int vtkCountTotElm; + int vtkCountCoord; + int vtkCountTotNodConnect; + int vtkCountTotVal; + int vtkCountCellOffset; //used only for ascii output + int vtkCountCellType; //used only for ascii output + + std::vector<vectInt> vtkLocalConnectivity; // conectivity (vector of vector) + std::vector<int> vtkLocalCellType; // topology + std::vector<PCoords> vtkLocalCoords; // coordinates + std::vector<PValues> vtkLocalValues; // nodal values (either scalar or vector) + + +public: + VTKData(std::string fieldName="unknown", int numComp = -1, int step = -1, int level = -1, double tol=0.0, + std::string filename="unknown", int useDefaultName = 1, int npart = -1, bool isBinary = true) { + + vtkIsBinary = isBinary; // choice: true, false + vtkFormat = std::string("vtu"); // choice: vtk (VTK legacy), vtu (XML appended) + + vtkFieldName = fieldName; + vtkFileName = filename; + vtkUseDefaultName = useDefaultName; + vtkNumComp = numComp; + vtkStep = step; + vtkLevel = level; + vtkTol = tol; + vtkNpart = npart; + + vtkCountFile = 0; + vtkTotNumElmLev0 = 0; + vtkCountTotElmLev0 = 0; + vtkCountTotNod = 0; + vtkCountTotElm = 0; + vtkCountCoord = 0; + vtkCountTotNodConnect = 0; + vtkCountTotVal = 0; + vtkCountCellOffset = 0; //used only for ascii output + vtkCountCellType = 0; + } + + void clearLocalData() + { + for(std::vector<vectInt>::iterator it = vtkLocalConnectivity.begin();it != vtkLocalConnectivity.end(); ++it) { + it->clear(); + } + vtkLocalConnectivity.clear(); + vtkLocalCellType.clear(); + vtkLocalCoords.clear(); + vtkLocalValues.clear(); + } + + ~VTKData() + { + clearLocalData(); + } + + void incrementTotNod(int increment) {vtkCountTotNod+=increment;} + void incrementTotElm(int increment) {vtkCountTotElm+=increment;} + void incrementTotElmLev0(int increment) {vtkCountTotElmLev0+=increment;} + + bool isLittleEndian(); + void SwapArrayByteOrder(void* array, int nbytes, int nItems); // used only for VTK + int getPVCellType(int numEdges); +// void writeParaViewData(); + void writeVTKElmData(); + void initVTKFile(); + void finalizeVTKFile(); + void setFileDistribution() { + int tmpmod = vtkTotNumElmLev0 % vtkNpart; + minElmPerPart = (vtkTotNumElmLev0-tmpmod)/vtkNpart; + numPartMinElm = vtkNpart - tmpmod; + + if(tmpmod == 0 ) maxElmPerPart = minElmPerPart; + else maxElmPerPart = minElmPerPart+1; + numPartMaxElm = tmpmod; + assert(vtkTotNumElmLev0 == minElmPerPart*numPartMinElm+maxElmPerPart*numPartMaxElm); + + } }; template <class T> @@ -393,6 +617,26 @@ class adaptiveElements { // switch to true on-the-fly local refinement in drawPost()) void addInView(double tol, int step, PViewData *in, PViewDataList *out, GMSH_PostPlugin *plug=0); + + // michel.rasquin@cenaero.be: + // Routines for + // - export of adapted views to pvtu file format for parallel visualization with paraview, + // - and/or generation of VTK data structure for ParaView plugin. + + // Clone of adapt for VTK output files + void adaptForVTK(double tol, int numComp, + std::vector<PCoords> &coords, std::vector<PValues> &values); + + // Clone of addInView for VTK output files + void addInViewForVTK(int step, PViewData *in, VTKData &myVTKData, + bool writeVtk=true, bool buildStaticData=false); + + int countElmLev0(int step, PViewData *in); + + // Build a mapping between all the nodes of the refined element + // and the node of the canonical refined element in order to + // generate a connectivity related to the canonical element + void buildMapping(nodMap<T> &myNodMap, double tol, int &numNodInsert); }; class adaptiveData { @@ -409,12 +653,32 @@ class adaptiveData { adaptiveElements<adaptiveHexahedron> *_hexahedra; adaptiveElements<adaptivePrism> *_prisms; adaptiveElements<adaptivePyramid> *_pyramids; + + // When set to true, this builds a global VTK data structure (connectivity, coords, etc) for the adaptive views. + // This can be very memory consuming for high adaptation levels. Use with caution. + // Usefull when GMSH is used as an external library to provide for instance a GMSH reader in a ParaView plugin. + // By default, set to false in the constructor. + bool buildStaticData; + + // This variable helps limit memory consumption (no global data structure) when GMSH is requested to + // write the data structure of adapted view under pvtu format + // In this case, one adapted element is considered at a time so that it can generate + // billions of adapted elements on a single core, as long as disk space allows it. + // This variable is set to true by default in the constructor. + bool writeVTK; + public: static double timerInit, timerAdapt; - adaptiveData(PViewData *data); + adaptiveData(PViewData *data, bool outDataInit=true); ~adaptiveData(); PViewData *getData(){ return (PViewData*)_outData; } void changeResolution(int step, int level, double tol, GMSH_PostPlugin *plug=0); + int countTotElmLev0(int step, PViewData *in); + void changeResolutionForVTK(int step, int level, double tol, int npart = 1, bool isBinary = true, + const std::string &guifileName = "unknown", int useDefaultName = 1); + void upBuildStaticData(bool newValue) { buildStaticData = newValue; } + void upWriteVTK(bool newValue) { writeVTK = newValue; } + }; #endif