-
Notifications
You must be signed in to change notification settings - Fork 847
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for Paraview/VTK XML (*.vtu) and Multiblock (*.vtm) visualization files #845
Conversation
The vtm file and the folder where the data is stored are now called like the config file (called "case name"). Futhermore the files inside of the folder are organized like the data. That means after a successful run, the working directory looks like that (omitting history files):
And the structure in Paraview: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very useful feature! My usual nitpicking below :)
Common/src/config_structure.cpp
Outdated
/*--- Set the case name to the base config file name without extension ---*/ | ||
|
||
caseName = string(case_filename); | ||
unsigned short lastindex = caseName.find_last_of("."); | ||
caseName = caseName.substr(0, lastindex); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to have this kind of common operation in a toolbox (whatever the stl does not offer).
for (unsigned short iFiles = 0; iFiles < driver_config->GetnVolumeOutputFiles(); iFiles++){ | ||
if (driver_config->GetVolumeOutputFiles()[iFiles] == PARAVIEW_MULTIBLOCK){ | ||
multiblockDriver = true; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can do std::any_of if you want to be more c++11
*/ | ||
CFEMDataSorter(CConfig *config, CGeometry *geometry, unsigned short nFields); | ||
CFEMDataSorter(CConfig *config, CGeometry *geometry, vector<string> fieldNames); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please pass fieldNames by const&
SU2_CFD/src/output/COutput.cpp
Outdated
if (fileName.empty()) | ||
fileName = config->GetFilename(volumeFilename, "", curTimeIter); | ||
|
||
/*--- Load and sort the output data and connectivity. ---*/ | ||
|
||
volumeDataSorter->SortConnectivity(config, geometry, true); | ||
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remember to strip all blank spaces :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder why CodeFactor did not complain
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reminder to remove whitespaces
vector<string> GetFieldNames(){ | ||
return fieldNames; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can probably be returned by const&, it will still allow a local copy to be created if necessary.
string fieldname = fieldnames[iField]; | ||
string fieldname = fieldNames[iField]; | ||
|
||
fieldname.erase(remove(fieldname.begin(), fieldname.end(), '"'), fieldname.end()); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be my jet lagged eyes but it looks like this local manipulation of the name is not used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you confirm or deny my observation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its used a couple of lines below (line 287).
CParaviewVTMFileWriter::CParaviewVTMFileWriter(string fileName, string folderName, su2double time, | ||
unsigned short iZone, unsigned short nZone) | ||
: CFileWriter(std::move(fileName), fileExt), | ||
folderName(std::move(folderName)), iZone(iZone), nZone(nZone), curTime(time){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would differentiate the argument / member names here (IDK if compilers complain or not, but just in case).
if (rank == MASTER_NODE) | ||
#if defined(_WIN32) || defined(_WIN64) || defined (__WINDOWS__) | ||
_mkdir(this->folderName.c_str()); | ||
_mkdir((this->folderName + "/zone_" + to_string(iZone)).c_str()); | ||
#else | ||
mkdir(this->folderName.c_str(), 0777); | ||
mkdir((this->folderName + "/zone_" + to_string(iZone)).c_str(), 0777); | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we have a "SU2_mkdir" in a toolbox to hide the windows/linux "hacks"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Few more comments (I did not get to the bottom earlier).
CFileWriter::CFileWriter(string fileName, string fileExt): | ||
file_ext(fileExt), | ||
fileName(std::move(fileName)){ | ||
|
||
rank = SU2_MPI::GetRank(); | ||
size = SU2_MPI::GetSize(); | ||
|
||
this->fileName += fileExt; | ||
|
||
file_size = 0.0; | ||
Bandwidth = 0.0; | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment on differentiating names as before, + try to be consistent with the naming convention (underscores vs camel case).
|
||
const vector<string> fieldNames = dataSorter->GetFieldNames(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If GetFieldNames returned by const& (see one of the previous comments) this copy could be avoided.
(I know it is not critical, it is just a matter of following a convention that works well when such aspects are important).
|
||
string fieldname = fieldnames[iField]; | ||
string fieldname = fieldNames[iField]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as before, this variable not being used.
multiBlockFile.open (fileName.c_str()); | ||
else | ||
multiBlockFile.open(fileName.c_str(), ios::app); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The .c_str()
is not needed in c++11, there is an overload for string.
for (iElem = 0; iElem < nParallel_Line; iElem++) { | ||
type_buf[jElem] = LINE; jElem++; | ||
} | ||
for (iElem = 0; iElem < nParallel_Tria; iElem++) { | ||
type_buf[jElem] = TRIANGLE; jElem++; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could use std::fill to make this sections of code more expressive (one line instead of three).
|
||
/*--- Prepare the 1D data buffer on this rank. ---*/ | ||
|
||
float *vec_buf = new float[myPoint*NCOORDS]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would allocate this buffer once outside the loop over variables and re-use it also for the scalar case, since the peak memory usage should not change.
std::string typeStr; | ||
unsigned long typeSize; | ||
switch (type) { | ||
case VTKDatatype::FLOAT32: | ||
typeStr = "\"Float32\""; | ||
typeSize = sizeof(float); | ||
break; | ||
case VTKDatatype::INT32: | ||
typeStr = "\"Int32\""; | ||
typeSize = sizeof(int); | ||
break; | ||
case VTKDatatype::UINT8: | ||
typeStr = "\"UInt8\""; | ||
typeSize = sizeof(char); | ||
break; | ||
default: | ||
SU2_MPI::Error("Unknown Type", CURRENT_FUNCTION); | ||
break; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is done also in AddDataArray, maybe it could be in a function to make maintenance easier if more types are added?
@@ -1119,7 +1133,31 @@ void CSurfaceFVMDataSorter::SortConnectivity(CConfig *config, CGeometry *geometr | |||
|
|||
} | |||
|
|||
void CSurfaceFVMDataSorter::SortSurfaceConnectivity(CConfig *config, CGeometry *geometry, unsigned short Elem_Type) { | |||
void CSurfaceFVMDataSorter::SortConnectivity(CConfig *config, CGeometry *geometry, vector<string> markerList) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another vector that should be passed by const& here and in SortSurfaceConnectivity.
Problems are solved. PR is ready from my side. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks very very clean, I did notice the changes, thanks!
Few more comments below, I think only one might be important.
#ifdef HAVE_MPI | ||
/*! | ||
* \brief The displacement that every process has in the current file view | ||
*/ | ||
MPI_Offset disp; | ||
|
||
/*! | ||
* \brief The file handle for writing | ||
*/ | ||
MPI_File fhw; | ||
#else | ||
|
||
/*! | ||
* \brief The displacement that every process has in the current file view | ||
*/ | ||
unsigned long disp; | ||
|
||
/*! | ||
* \brief The file handle for writing | ||
*/ | ||
FILE* fhw; | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can these be typedefs of the MPI wrapper?
bool connectivitySorted; //!< Boolean to store information on whether the connectivity is sorted | ||
|
||
int *nPoint_Send; //!< Number of points this processor has to send to other processors | ||
int *nPoint_Recv; //!< Number of points this processor receives from other processors | ||
int *nElem_Send; //!< Number of elements this processor has to send to other processors | ||
int *nElem_Cum; //!< Cumulative number of elements | ||
int *nElemConn_Send; //!< Number of element connectivity this processor has to send to other processors | ||
int *nElemConn_Cum; //!< Cumulative number of element connectivity entries |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be a shame to break the pretty alignment :)
* \param[out] typeStr - The string name of the type | ||
* \param[out] typeSize - The size in bytes of the type | ||
*/ | ||
inline void GetTypeInfo(const VTKDatatype type, string &typeStr, unsigned long &typeSize){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick this method can be const
if (nElem_Send != NULL) delete [] nElem_Send; | ||
if (nElem_Cum != NULL) delete [] nElem_Cum; | ||
if (nElemConn_Send != NULL) delete [] nElemConn_Send; | ||
if (nElemConn_Cum != NULL) delete [] nElemConn_Cum; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This thing we do blabla != NULL
is actually not needed. (I only looked for the definitive answer recently)
nGlobalElem = std::accumulate(nGlobalPerElem.begin(), nGlobalPerElem.end(), 0); | ||
nLocalElem = std::accumulate(nLocalPerElem.begin(), nLocalPerElem.end(), 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes... me likes.
#else | ||
|
||
startTime = su2double(clock())/su2double(CLOCKS_PER_SEC); | ||
|
||
unsigned long bytesWritten; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have my eye on moving this timing stuff to the wrapper at some point.
string fieldname = fieldnames[iField]; | ||
string fieldname = fieldNames[iField]; | ||
|
||
fieldname.erase(remove(fieldname.begin(), fieldname.end(), '"'), fieldname.end()); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you confirm or deny my observation?
if (rank == MASTER_NODE) | ||
#if defined(_WIN32) || defined(_WIN64) || defined (__WINDOWS__) | ||
_mkdir(this->folderName.c_str()); | ||
_mkdir((this->folderName + "/zone_" + to_string(iZone)).c_str()); | ||
#else | ||
mkdir(this->folderName.c_str(), 0777); | ||
mkdir((this->folderName + "/zone_" + to_string(valiZone)).c_str(), 0777); | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did not get enough sleep but I don't think you have enough "{}"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh yes you are right :D fortunately if the folders already exists, _mkdir() just does nothing. But I am gonna change this.
SU2_DOT/src/meson.build
Outdated
'output/COutput.cpp', | ||
'output/output_structure_legacy.cpp', | ||
'output/tools/CWindowingTools.cpp', | ||
'output/output_structure_legacy.cpp', | ||
'output/CBaselineOutput.cpp', | ||
'output/filewriter/CParallelDataSorter.cpp', | ||
'output/filewriter/CParallelFileWriter.cpp', | ||
'output/filewriter/CFEMDataSorter.cpp', | ||
'output/filewriter/CSurfaceFEMDataSorter.cpp', | ||
'output/filewriter/CFVMDataSorter.cpp', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please align these.
'output/filewriter/CSU2MeshFileWriter.cpp', | ||
'output/filewriter/CParaviewXMLFileWriter.cpp', | ||
'output/filewriter/CParaviewVTMFileWriter.cpp', | ||
'variables/CBaselineVariable.cpp', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might have missed it, but can you add these to the old build system too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is not addressed yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only formal aspects . I would like to have them addressed though
|
||
#ifdef HAVE_MPI | ||
SU2_MPI::Allreduce(&nLocalPoint_Sort, &nGlobalPoint_Sort, 1, | ||
SU2_MPI::Allreduce(&nLocalPointBeforeSort, &nGlobalPointBeforeSort, 1, | ||
MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 for removing the preprocessor stuff
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am really looking forward to use this (especially for multizone cases) because I am certain this will make visualization (and post-processing in general) in Paraview much more comfortable..
All cases I tested are now running without issues and I do not have any further review comments, so LGTM 💐
../src/output/filewriter/CParaviewXMLFileWriter.cpp \ | ||
../src/output/filewriter/CParaviewVTMFileWriter.cpp \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks LGTM
Proposed Changes
I added new output file formats. First of all support for the VTK XML file format (see this pdf) (file ending *.vtu). This adds support for possible compression, portable binary encoding, random access, big endian and little endian byte order, multiple file representation of piece data. I made this the new default format if
PARAVIEW
has been added toOUTPUT_FILES
. The old *.vtk format can still be used withPARAVIEW_LEGACY
.However, the main reason for this new format is that it is required for new Multiblock file (*.vtm) output. This file is a simple XML file which defines a data hierarchy in order to represent individual parts of the domain (which are stored as *.vtu files). With this it is possible to directly select and visualize the solution in individual zones and/or on boundaries. This new format can be enabled with
PARAVIEW_MULTIBLOCK
as value forOUTPUT_FILES
. At the moment all boundaries are written, I might add an option to disable/enable some of them (maybe re-using theMARKER_PLOTTING
option). In order not to cluster the working directory, the individual *.vtu files are placed in subfolders, since they are more or less opaque to the user.PR Checklist
Put an X by all that apply. You can fill this out after submitting the PR. If you have any questions, don't hesitate to ask! We want to help. These are a guide for you to know what the reviewers will be looking for in your contribution.