From 2ba52b0454a61b5bb90177027448727fe1c0c013 Mon Sep 17 00:00:00 2001 From: Diogo Nardelli Siebert Date: Wed, 18 Feb 2026 09:04:10 -0300 Subject: [PATCH 1/2] Adding VTI output (VTK XML Imagedata format) support --- IO/Mesh.h | 2 +- IO/VtiWriter.cpp | 117 ++++++++++ IO/Writer.cpp | 49 ++++- IO/Writer.h | 6 +- IO/xmlvtk.cpp | 558 +++++++++++++++++++++++++++++++++++++++++++++++ IO/xmlvtk.h | 185 ++++++++++++++++ 6 files changed, 909 insertions(+), 8 deletions(-) create mode 100755 IO/VtiWriter.cpp create mode 100644 IO/xmlvtk.cpp create mode 100644 IO/xmlvtk.h diff --git a/IO/Mesh.h b/IO/Mesh.h index 9e5f32e6..2ada354c 100644 --- a/IO/Mesh.h +++ b/IO/Mesh.h @@ -24,7 +24,7 @@ enum class VariableType { }; enum class DataType { Double, Float, Int, Null }; enum class MeshType { PointMesh, SurfaceMesh, VolumeMesh, Unknown }; -enum class FileFormat { OLD, NEW, NEW_SINGLE, SILO, HDF5 }; +enum class FileFormat { OLD, NEW, NEW_SINGLE, SILO, HDF5 , VTK}; //! Convert enums to/from strings (more future-proof than static_cast) diff --git a/IO/VtiWriter.cpp b/IO/VtiWriter.cpp new file mode 100755 index 00000000..1e6223e0 --- /dev/null +++ b/IO/VtiWriter.cpp @@ -0,0 +1,117 @@ +#include "IO/HDF5_IO.h" +#include "IO/IOHelpers.h" +#include "IO/MeshDatabase.h" +#include "IO/Writer.h" +#include "IO/silo.h" +#include "IO/xmlvtk.h" +#include "common/MPI.h" +#include "common/Utilities.h" + +#include +#include +#include +#include +#include + +static void writeVti( + const std::string &fullpath, const IO::MeshDataStruct &meshData) +{ + const IO::DomainMesh &mesh = dynamic_cast( *meshData.mesh ); + RankInfoStruct info( mesh.rank, mesh.nprocx, mesh.nprocy, mesh.nprocz ); + + VTIWriter vti = VTIWriter(std::string(fullpath)); + vti.setWholeExtent( info.ix * mesh.nx, info.jy * mesh.ny , info.kz * mesh.nz, + ( info.ix + 1 ) * mesh.nx, ( info.jy + 1 ) * mesh.ny, ( info.kz + 1 ) * mesh.nz); + + vti.setSpacing( 1.0 , 1.0 , 1.0 ); + vti.setOrigin(0,0,0); + vti.setCompress(); + + for ( size_t i = 0; i < meshData.vars.size(); i++ ) + { + const auto &var = *meshData.vars[i]; + if ( var.precision == IO::DataType::Double ) { + vti.addCellData( var.name , "Float64" , "binary" , var.dim , (unsigned char*) var.data.begin() ); + } else if ( var.precision == IO::DataType::Float ) { + Array data2( var.data.size() ); + data2.copy( var.data ); + vti.addCellData( var.name , "Float32" , "binary" , var.dim , (unsigned char*) data2.begin()); + } else if ( var.precision == IO::DataType::Int ) { + Array data2( var.data.size() ); + data2.copy( var.data ); + vti.addCellData( var.name , "Int32" , "binary" , var.dim , (unsigned char*) var.data.begin() ); + } else { + ERROR( "Unsupported format" ); + } + } + + vti.write(); +} + +void writeVtiSummary( + const std::vector &meshes_written,const IO::MeshDataStruct &meshData, const std::string &filename ) +{ + const IO::DomainMesh &mesh = dynamic_cast( *meshData.mesh ); + RankInfoStruct info( mesh.rank, mesh.nprocx, mesh.nprocy, mesh.nprocz ); + PVTIWriter pvti = PVTIWriter( filename ); + int rank = 0; + for ( const auto &data : meshes_written ) + { + for ( const auto &tmp : data.domains ) + { + RankInfoStruct info( rank, mesh.nprocx, mesh.nprocy, mesh.nprocz ); + char filename[100]; + sprintf( filename, "%05i.vti", rank ); + + VTIWriter vti = VTIWriter( filename ); + vti.setWholeExtent( info.ix * mesh.nx, info.jy * mesh.ny , info.kz * mesh.nz, + ( info.ix + 1 ) * mesh.nx, ( info.jy + 1 ) * mesh.ny, ( info.kz + 1 ) * mesh.nz); + vti.setSpacing( 1.0 , 1.0 , 1.0 ); + vti.setOrigin(0,0,0); + vti.setCompress(); + + for ( size_t i = 0; i < meshData.vars.size(); i++ ) + { + const auto &var = *meshData.vars[i]; + if ( var.precision == IO::DataType::Double ) { + vti.addCellData( var.name , "Float64" , "binary" , var.dim , nullptr ); + } else if ( var.precision == IO::DataType::Float ) { + vti.addCellData( var.name , "Float32" , "binary" , var.dim , nullptr ); + } else if ( var.precision == IO::DataType::Int ) { + vti.addCellData( var.name , "Int32" , "binary" , var.dim , nullptr ); + } else { + ERROR( "Unsupported format" ); + } + } + + pvti.addVTIWriter(vti); + rank++; + + } + } + pvti.write(); +} + +std::vector writeMeshesVti( const std::vector &meshData, + const std::string &path, int rank ) +{ + std::vector meshes_written; + char filename[100], fullpath[200]; + sprintf( filename, "%05i.vti", rank ); + sprintf( fullpath, "%s/%s", path.c_str(), filename ); + + for ( size_t i = 0; i < meshData.size(); i++ ) { +// auto mesh = meshData[i].mesh; + auto database = getDatabase( fullpath , meshData[i], IO::FileFormat::VTK, rank ); + + if ( database.meshClass == "DomainMesh" ) { + writeVti( fullpath, meshData[i] ); + } else { + ERROR( "Unknown mesh class or not implemented for vtk/vti output" ); + } + + meshes_written.push_back( database ); + } + return meshes_written; +} + diff --git a/IO/Writer.cpp b/IO/Writer.cpp index 0fb8d135..1ed3cdfa 100644 --- a/IO/Writer.cpp +++ b/IO/Writer.cpp @@ -15,7 +15,7 @@ #include -enum class Format { OLD, NEW, SILO, HDF5, UNKNOWN }; +enum class Format { OLD, NEW, SILO, HDF5, UNKNOWN, VTK }; /**************************************************** @@ -26,7 +26,10 @@ std::vector writeMeshesSilo( void writeSiloSummary( const std::vector &, const std::string & ); std::vector writeMeshesHDF5( const std::vector &, const std::string &, IO::FileFormat, int, Xdmf & ); - +std::vector writeMeshesVti( const std::vector &meshData, + const std::string &path, int rank ); +void writeVtiSummary( + const std::vector &meshes_written,const IO::MeshDataStruct &meshData, const std::string &filename ); /**************************************************** * Recursively create the subdirectory * @@ -90,6 +93,8 @@ void IO::initialize( const std::string &path, const std::string &format, bool ap global_IO_format = Format::SILO; else if ( format == "hdf5" ) global_IO_format = Format::HDF5; + else if ( format == "vtk" ) + global_IO_format = Format::VTK; else ERROR( "Unknown format" ); int rank = Utilities::MPI( MPI_COMM_WORLD ).getRank(); @@ -100,9 +105,13 @@ void IO::initialize( const std::string &path, const std::string &format, bool ap filename = global_IO_path + "/summary.LBM"; else if ( global_IO_format == Format::SILO || global_IO_format == Format::HDF5 ) filename = global_IO_path + "/LBM.visit"; + else if ( global_IO_format == Format::VTK) + filename = global_IO_path + "/LBM.pvd"; else ERROR( "Unknown format" ); auto fid = fopen( filename.c_str(), "wb" ); + if ( global_IO_format == Format::VTK) + fprintf( fid, "\n\n \n \n\n" ); fclose( fid ); } } @@ -278,13 +287,12 @@ static std::vector writeMeshesNewFormat( return meshes_written; } - /**************************************************** * Write the mesh data * ****************************************************/ void IO::writeData( const std::string &subdir, const std::vector &meshData, - const Utilities::MPI &comm ) -{ + const Utilities::MPI &comm , int timestep ) +{ if ( global_IO_path.empty() ) IO::initialize(); PROFILE_START( "writeData" ); @@ -310,6 +318,9 @@ void IO::writeData( const std::string &subdir, const std::vector\n \n\n", timestep, subdir.c_str()); + + char *pos = strstr(buffer, " \n\n"); + pos[0] = '\0'; + + fid = fopen( filename.c_str(), "w" ); + fputs(buffer,fid); + fputs(newrow,fid); + fclose(fid); + + free(buffer); + } else { ERROR( "Unknown format" ); } diff --git a/IO/Writer.h b/IO/Writer.h index 3844f3b2..e1d2f8f4 100644 --- a/IO/Writer.h +++ b/IO/Writer.h @@ -38,7 +38,7 @@ void initialize( * @param[in] comm The comm to use for writing (usually MPI_COMM_WORLD or a dup thereof) */ void writeData( const std::string &subdir, const std::vector &meshData, - const Utilities::MPI &comm ); + const Utilities::MPI &comm , int timestep = -1); /*! @@ -48,12 +48,14 @@ void writeData( const std::string &subdir, const std::vector * @param[in] meshData The data to write * @param[in] comm The comm to use for writing (usually MPI_COMM_WORLD or a dup thereof) */ + + inline void writeData( int timestep, const std::vector &meshData, const Utilities::MPI &comm ) { char subdir[100]; sprintf( subdir, "vis%03i", timestep ); - writeData( subdir, meshData, comm ); + writeData( subdir, meshData, comm , timestep); } diff --git a/IO/xmlvtk.cpp b/IO/xmlvtk.cpp new file mode 100644 index 00000000..cf2e1a4a --- /dev/null +++ b/IO/xmlvtk.cpp @@ -0,0 +1,558 @@ +/* + * Copyright (c) 2025 Diogo Nardelli Siebert + * + * Licensed under either of + * - Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) + * - GNU General Public License, Version 3.0 or later (https://www.gnu.org/licenses/gpl-3.0.html) + * + * SPDX-License-Identifier: (Apache-2.0 OR GPL-3.0-or-later) + */ + +#include "xmlvtk.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +/** @brief Indicates whether data compression is used by default **/ +bool Element::compress = false; +/** @brief Default cache size used during data processing. */ +headerType cacheSize = 1500; + +/** + * @brief Formats a string using printf-style syntax. + * @tparam Args Variadic arguments for formatting. + * @param fmt Format string. + * @return Formatted string. + */ +template +std::string format_string(const char* fmt, Args&&... args) +{ + int size = std::snprintf(nullptr, 0, fmt, std::forward(args)...); + if (size < 0) { + throw std::runtime_error("format_string: snprintf error"); + } + std::vector buf(size + 1); + int size2 = std::snprintf(buf.data(), buf.size(), fmt, std::forward(args)...); + if (size2 < 0) { + throw std::runtime_error("format_string: snprintf error"); + } + + return std::string(buf.data(), buf.data() + size2); +} + +/** + * @brief Encodes binary input to Base64 string representation. + * @param input Input binary data. + * @param len Length of input data. + * @return Encoded Base64 string. + */ +std::string spc_base64_encode(const unsigned char* input, size_t len) +{ + static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + std::string output; + output.reserve((len / 3 + (len % 3 != 0)) * 4); + + for (size_t i = 0; i < len; i += 3) { + uint32_t n = (input[i] << 16) | (i + 1 < len ? input[i + 1] << 8 : 0) | (i + 2 < len ? input[i + 2] : 0); // AAAAAABB BBBBCCCC CCDDDDDD + output.push_back(table[(n >> 18) & 0x3F]); + output.push_back(table[(n >> 12) & 0x3F]); + output.push_back(i + 1 < len ? table[(n >> 6) & 0x3F] : '='); + output.push_back(i + 2 < len ? table[n & 0x3F] : '='); + } + return output; +} + +std::string CellData::header() +{ + return "\n"; +} + +std::string CellData::footer() +{ + return "\n"; +} + +std::string PointData::header() +{ + return "\n"; +} + +std::string PointData::footer() +{ + return "\n"; +} + +std::string AppendData::header() +{ + return "\n"; +} + +std::string AppendData::footer() +{ + return "\n"; +} + +AppendData::AppendData() +{ + this -> totalSize = 0; +} + +unsigned int AppendData::addData(unsigned char* pointer, unsigned int size) +{ + int offset = this->totalSize; + this->sizeList.push_back(size); + this->pointerList.push_back(pointer); + this->totalSize += (size+4); + return offset; +} + +DataArray::DataArray(const std::string& name_,const std::string& type_, const std::string& format_, int components_, unsigned char* pointer_, uint64_t points_) + : components(components_), name(name_), type(type_), format(format_), pointer(pointer_), points(points_) + +{ + if ( (this -> type == "Int8") || (this -> type == "UInt8") ) this -> typeSize = 1; + else if ( (this -> type == "Int16") || (this -> type == "UInt16") ) this -> typeSize = 2; + else if ( (this -> type == "Int32") || (this -> type == "UInt32") || (this -> type == "Float32") ) this -> typeSize = 4; + else if ( (this -> type == "Int64") || (this -> type == "UInt64") || (this -> type == "Float64") ) this -> typeSize = 8; + this -> dataSize = components * points; +} + +std::string DataArray::header() +{ + std::ostringstream stringStream; + stringStream << format_string("", offset).c_str() : ">"); + return stringStream.str(); +} + +std::string DataArray::footer() +{ + return "\n\n"; +} + +void VTIWriter::setCompress() +{ + this->compress = true; +} + +PVTIWriter::PVTIWriter(const std::string& filename_) + : filename(filename_), + originX(0), originY(0), originZ(0), + wholeMinX(0), wholeMinY(0), wholeMinZ(0), wholeMaxX(0), wholeMaxY(0), wholeMaxZ(0), pieceCounter(0) +{ +} + +VTIWriter::VTIWriter(const std::string& filename_) + : filename(filename_) , vtkVersion("0.1"), + originX(0), originY(0), originZ(0) +{ + this -> compress = false; + if (sizeof(headerType) == 8) this -> headerTypeName = "UInt64"; + else if (sizeof(headerType) == 4) this -> headerTypeName = "UInt32"; + this -> byteOrder = "LittleEndian"; +} + +void VTIWriter::addPointData(const std::string& name,const std::string& type, const std::string& format, int components, unsigned char* pointer) +{ + std::unique_ptr data = std::make_unique( name , type , format , components , (unsigned char*) pointer , pd.points ); + data -> compress = this -> compress; + + if (format == "appended") + { + data -> offset = ad.addData( data->pointer , data->dataSize * data->typeSize ); + ad.compress = this->compress; + } + + pd.addChild( std::move(data) ); +} + +void VTIWriter::addCellData(const std::string& name,const std::string& type, const std::string& format, int components, unsigned char* pointer) +{ + std::unique_ptr data = std::make_unique( name , type , format , components , (unsigned char*) pointer , cd.cells ); + data -> compress = this -> compress; + + if (format == "appended") + { + data -> offset = ad.addData( data->pointer , data->dataSize * data->typeSize ); + ad.compress = this->compress; + } + + cd.addChild( std::move(data) ); +} + +std::string VTIWriter::header() +{ + std::ostringstream stringStream; + stringStream << "" << endl; + stringStream << format_string("", "1.0", byteOrder.c_str() , headerTypeName.c_str() , compress ? "compressor=\"vtkZLibDataCompressor\"" : "" ) << endl; + stringStream << format_string("", spaceX, spaceY, spaceZ) << endl; + stringStream << format_string("" , pieceMinX , pieceMaxX , pieceMinY , pieceMaxY, pieceMinZ , pieceMaxZ) << endl; + return stringStream.str(); +} + +std::string PVTIWriter::header() +{ + std::ostringstream stringStream; + stringStream << "" << endl; + stringStream << "" << endl; + stringStream << format_string("", spaceX, spaceY, spaceZ) << endl; + + return stringStream.str(); +} + +std::string VTIWriter::footer() +{ + return "\n"; +} + +std::string PVTIWriter::footer() +{ + return "\n\n"; +} + +void VTIWriter::setWholeExtent(int64_t minX_,int64_t minY_, int64_t minZ_,int64_t maxX_,int64_t maxY_, int64_t maxZ_) +{ + wholeMinX = minX_; + wholeMinY = minY_; + wholeMinZ = minZ_; + wholeMaxX = maxX_; + wholeMaxY = maxY_; + wholeMaxZ = maxZ_; + setPiece(minX_,minY_,minZ_,maxX_,maxY_, maxZ_); +} + +std::ostream& operator<<(std::ostream& os, AppendData& obj) +{ + if (obj.totalSize > 0) + { + os << obj.header(); + os << "_"; + for (int k = 0; k < obj.pointerList.size(); k++) + { + if (obj.compress == false) + { + os.write( (char*) &obj.sizeList[k], sizeof(headerType) ); + os.write( (char*) obj.pointerList[k], obj.sizeList[k] ); + } + else + { + streampos beginPos = os.tellp(); + + std::vectorcompressedData (cacheSize); + + headerType totalByteSize = obj.sizeList[k]; + headerType numberOfBlocks = totalByteSize / cacheSize + (totalByteSize % cacheSize != 0 ); + + size_t infoSize = sizeof(headerType) * (3 + numberOfBlocks); + std::vectorcompressedInfo(infoSize ); + + compressedInfo[0] = numberOfBlocks; + compressedInfo[1] = (numberOfBlocks > 1) ? cacheSize : totalByteSize; + compressedInfo[2] = (totalByteSize % cacheSize == 0) ? cacheSize : totalByteSize % cacheSize ; + + os.write((char*) compressedInfo.data() , infoSize ); + + for (int n = 1; n <= numberOfBlocks; n++) + { + uLongf compressedLength = cacheSize; + int numberOfBytesInBlock = (n < numberOfBlocks) ? compressedInfo[1] : compressedInfo[2]; + const Bytef* src = reinterpret_cast( obj.pointerList[k] + (n - 1) * cacheSize ); + int ret = compress( (Bytef *) (compressedData.data() ), &compressedLength, src, numberOfBytesInBlock ); + if (ret != Z_OK) + { + std::cerr << "Error compressing data , code =" << ret << std::endl; + throw std::runtime_error("Failed compressing data using zlib in AppendData"); + } + compressedInfo[n+2] = compressedLength; + + os.write( reinterpret_cast( compressedData.data() ) , compressedLength ); + } + + streampos endPos = os.tellp(); + + os.seekp( beginPos ); + os.write( (char*) compressedInfo.data() , infoSize ); + os.seekp( endPos ); + } + } + os << endl; + os << obj.footer(); + + } + return os; +} + +std::ostream& operator<<(std::ostream& os, DataArray& obj) +{ + os << obj.header() << endl; + + headerType totalByteSize = static_cast( (obj.dataSize) * (obj.typeSize) ); + headerType count = min( 12 - sizeof(headerType) , totalByteSize ); + + if (obj.format == "binary") + { + if (obj.compress == false) + { + os << spc_base64_encode( (unsigned char *) &totalByteSize, sizeof(headerType) ); + count = 0; + + int leftOverSize = 0; + unsigned char leftOverBuffer[3]; + + for (; count < totalByteSize ; count += cacheSize) + { + unsigned char* pointer = obj.pointer + count; + size_t size = min( cacheSize, totalByteSize - count); + if (leftOverSize > 0) + { + for (; leftOverSize < 3; leftOverSize++) + { + if (size > 0) + { + leftOverBuffer[leftOverSize] = *(pointer++); + size--; + } + else break; + } + + os << spc_base64_encode( leftOverBuffer , leftOverSize ); + leftOverSize = 0; + } + + leftOverSize = (size % 3); + size -= leftOverSize; + for (int i = 0; i < leftOverSize; i++) + { + leftOverBuffer[i] = pointer[size + i]; + } + + os << spc_base64_encode( pointer , size ); + + } + + if (leftOverSize > 0) + { + os << spc_base64_encode( leftOverBuffer , leftOverSize ); + leftOverSize = 0; + } + } + else + { + streampos beginPos = os.tellp(); + + std::vectorcompressedData (cacheSize + 3); + int leftOver = 0; + + headerType numberOfBlocks = totalByteSize / cacheSize + (totalByteSize % cacheSize != 0 ); + size_t infoSize = sizeof(headerType) * (3 + numberOfBlocks); + std::vectorcompressedInfo(infoSize ); + + compressedInfo[0] = numberOfBlocks; + compressedInfo[1] = (numberOfBlocks > 1) ? cacheSize : totalByteSize; + compressedInfo[2] = (totalByteSize % cacheSize == 0) ? cacheSize : totalByteSize % cacheSize ; + + os << spc_base64_encode( reinterpret_cast(compressedInfo.data()) , infoSize ); + + for (int n = 1; n <= numberOfBlocks; n++) + { + uLongf compressedLength = cacheSize; + int numberOfBytesInBlock = (n < numberOfBlocks) ? compressedInfo[1] : compressedInfo[2]; + int ret = compress( reinterpret_cast(compressedData.data() + 3) , &compressedLength, obj.pointer + (n-1) * cacheSize, numberOfBytesInBlock ); + if (ret != Z_OK) + { + std::cerr << "Error compressing data , code =" << ret << std::endl; + throw std::runtime_error("Failed compressing data using zlib in AppendData"); + } + compressedInfo[n+2] = compressedLength; + + int encodeSize = 3* ( (compressedLength + leftOver)/3 ); + os << spc_base64_encode( compressedData.data() + 3 - leftOver , encodeSize ); + + int newLeftOver = compressedLength + leftOver - encodeSize ; + if (newLeftOver > 0) + { + memcpy( compressedData.data() + 3 - newLeftOver, compressedData.data() + 3 - leftOver + encodeSize, newLeftOver); + } + leftOver = newLeftOver; + } + + if (leftOver > 0) + { + os << spc_base64_encode(compressedData.data() + 3 - leftOver , leftOver ); + } + + streampos endPos = os.tellp(); + os.seekp( beginPos ); + + os << spc_base64_encode( reinterpret_cast(compressedInfo.data()) , infoSize ); + os.seekp( endPos ); + } + + os << obj.footer(); + } + + return os; +} + +void VTIWriter::setPiece(int64_t minX_,int64_t minY_, int64_t minZ_,int64_t maxX_,int64_t maxY_, int64_t maxZ_) +{ + pieceMinX = minX_; + pieceMinY = minY_; + pieceMinZ = minZ_; + pieceMaxX = maxX_; + pieceMaxY = maxY_; + pieceMaxZ = maxZ_; + sizeX = maxX_ - minX_ + 1; + sizeY = maxY_ - minY_ + 1; + sizeZ = maxZ_ - minZ_ + 1; + pd.points = sizeX * sizeY * sizeZ; + cd.cells = (sizeX-1) * (sizeY-1) * (sizeZ-1); +} + +void VTIWriter::setOrigin(double x_,double y_, double z_) +{ + originX = x_; + originY = y_; + originZ = z_; +} + +void VTIWriter::setSpacing(double sx_,double sy_, double sz_) +{ + spaceX = sx_; + spaceY = sy_; + spaceZ = sz_; +} + +void PVTIWriter::write() +{ + std::ofstream file; + file.open(this -> filename); + file << header(); + + for (int i = 0; i< cellDataName.size(); i++) + { + if (i==0) file << "" << endl; + file << format_string( "" ,cellDataName[i].c_str() ,cellDataComponents[i] ,cellDataType[i].c_str() ) << endl; + if (i== cellDataName.size()-1) file << "" << endl; + } + + for (int i = 0; i< pointDataName.size(); i++) + { + if (i==0) file << "" << endl; + file << format_string( "" ,pointDataName[i].c_str() ,pointDataComponents[i] ,pointDataType[i].c_str() ) << endl; + if (i== pointDataName.size()-1) file << "" << endl; + } + + + for (int i = 0; i< pieceFilename.size(); i++) + { + file << format_string("", pieceMinX[i], pieceMaxX[i], pieceMinY[i] , pieceMaxY[i] , pieceMinZ[i] , pieceMaxZ[i] , pieceFilename[i].c_str() ) << endl; + } + + file << footer(); + file.close(); +} + +void VTIWriter::write() +{ + file.open(this -> filename); + file << header(); + + if (pd.sizeChild() > 0) + { + file << pd.header(); + for (int n = 0; n < pd.sizeChild() ; n++) + { + DataArray* array = (DataArray*) pd.getChild(n); + file << *array; + } + file << pd.footer(); + } + + if (cd.sizeChild() > 0) + { + file << cd.header(); + for (int n = 0; n < cd.sizeChild() ; n++) + { + DataArray* array = (DataArray*) cd.getChild(n); + file << *array; + } + file << cd.footer(); + } + + file << "" << endl; + file << "" << endl; + + file << ad; + file << footer(); + file.close(); +} + +void PVTIWriter::addVTIWriter(VTIWriter& write) +{ + pieceFilename.push_back( write.filename ); + + pieceMaxX.push_back( write.pieceMaxX ); + pieceMaxY.push_back( write.pieceMaxY ); + pieceMaxZ.push_back( write.pieceMaxZ ); + pieceMinX.push_back( write.pieceMinX ); + pieceMinY.push_back( write.pieceMinY ); + pieceMinZ.push_back( write.pieceMinZ ); + + if (pieceFilename.size() == 1) + { + for (int n = 0; n < write.pd.sizeChild() ; n++) + { + DataArray* data = (DataArray*) write.pd.getChild(n); + pointDataName.push_back( data -> name ); + pointDataType.push_back( data -> type); + pointDataComponents.push_back( data -> components ); + } + + for (int n = 0; n < write.cd.sizeChild() ; n++) + { + DataArray* data = (DataArray*) write.cd.getChild(n); + cellDataName.push_back( data -> name ); + cellDataType.push_back( data -> type); + cellDataComponents.push_back( data -> components ); + } + + originX = write.originX; + originY = write.originX; + originZ = write.originX; + + spaceX = write.spaceX; + spaceY = write.spaceY; + spaceZ = write.spaceZ; + + wholeMaxX = write.wholeMaxX; + wholeMaxY = write.wholeMaxY; + wholeMaxZ = write.wholeMaxZ; + wholeMinX = write.wholeMinX; + wholeMinY = write.wholeMinY; + wholeMinZ = write.wholeMinZ; + } + else + { + wholeMaxX = max(wholeMaxX,write.wholeMaxX); + wholeMaxY = max(wholeMaxY,write.wholeMaxY); + wholeMaxZ = max(wholeMaxZ,write.wholeMaxZ); + wholeMinX = min(wholeMinX,write.wholeMinX); + wholeMinY = min(wholeMinY,write.wholeMinY); + wholeMinZ = min(wholeMinZ,write.wholeMinZ); + } + + +} diff --git a/IO/xmlvtk.h b/IO/xmlvtk.h new file mode 100644 index 00000000..9538eda4 --- /dev/null +++ b/IO/xmlvtk.h @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2025 Diogo Nardelli Siebert + * + * Licensed under either of + * - Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0) + * - GNU General Public License, Version 3.0 or later (https://www.gnu.org/licenses/gpl-3.0.html) + * + * SPDX-License-Identifier: (Apache-2.0 OR GPL-3.0-or-later) + */ + +#ifndef __XMLVTK_H_INCLUDED__ +#define __XMLVTK_H_INCLUDED__ + +#include +#include +#include +#include +#include + +#define headerType u_int64_t + + +/** + * @brief Encodes binary data into Base64 format. + * @param input Pointer to the input data. + * @param len Length of the input data. + * @return Base64-encoded string. + */ +std::string spc_base64_encode(const unsigned char* input, size_t len); + +/** + * @class Element + * @brief Represents a hierarchical XML-like element supporting compression and VTK serialization. + */ +class Element +{ + public: + static bool compress; +/** @brief Returns the parent element. */ + Element* getParent() { return parent ;} +/** @brief Returns the nth child element. *//** ... */ + Element* getChild(int n) { return this->child[n].get(); } +/** @brief Adds a new child element and sets its parent. */ + void addChild(std::unique_ptr e) { + e -> parent = this; + child.push_back(std::move( e ) ); + } + +/** @brief Returns the number of child elements. */ + int sizeChild( ) { return child.size(); } +/** @brief Generates a header string for VTK output. */ + virtual std::string header() = 0; +/** @brief Generates a footer string for VTK output. */ + virtual std::string footer() = 0; + private: + Element* parent; + std::vector< std::unique_ptr > child; +}; + +class PointData: public Element +{ + public: + std::string header(); + std::string footer(); + unsigned int points; +}; + +class CellData: public Element +{ + public: + std::string header(); + std::string footer(); + unsigned int cells; +}; + +class DataArray : public Element +{ + public: + DataArray(const std::string& name,const std::string& type, const std::string& format, int components, unsigned char* pointer, uint64_t size); + unsigned char* pointer; + std::string header(); + std::string footer(); + void write( std::ofstream& file ); + friend std::ostream& operator<<(std::ostream& os, DataArray& obj); + uint64_t typeSize; + uint64_t dataSize ; + uint64_t offset; + uint64_t points; + std::string name; + std::string format; + std::string type; + int components; + private: + int mode = 0; + + +}; + +class AppendData : public Element +{ + public: + AppendData(); + std::string header(); + std::string footer(); + unsigned int addData(unsigned char* pointer, unsigned int size); + friend std::ostream& operator<<(std::ostream& os, AppendData& obj); + private: + std::vector pointerList; + std::vector sizeList; + headerType totalSize; +}; + +class VTIWriter : public Element +{ + public: + VTIWriter(const std::string& filename); + std::string footer(); + std::string header(); + + void write(); + void setWholeExtent(int64_t minX_,int64_t minY_, int64_t minZ_,int64_t maxX_,int64_t maxY_, int64_t maxZ_); + void setPiece(int64_t minX_,int64_t minY_, int64_t minZ_,int64_t maxX_,int64_t maxY_, int64_t maxZ_); + void setOrigin(double x_,double y_, double z_); + void setSpacing(double sx_,double sy_, double sz_); + void addPointData(const std::string& name,const std::string& type, const std::string& format, int components, unsigned char* pointer); + void addCellData(const std::string& name,const std::string& type, const std::string& format, int components, unsigned char* pointer); + void setCompress(); + + std::string filename; + std::string vtkVersion; /*!< The Vtk File Format Version of the file */ + std::string fileTitle; /*!< The title of the file (do not confuse with the name of the file) */ + std::string dataSetType; /*!< The type of geometry (grid) that data is associeted to (STRUCTURED GRID for LBM applications) */ + std::string headerTypeName; + + int64_t wholeMinX, wholeMinY, wholeMinZ; + int64_t wholeMaxX, wholeMaxY, wholeMaxZ; + int64_t pieceMinX, pieceMinY, pieceMinZ; + int64_t pieceMaxX, pieceMaxY, pieceMaxZ; + int64_t sizeX, sizeY, sizeZ; /*!< Lenght in pixels of the image in each axis */ + + /*!< Lenght in pixels of the image in each axis */ + double spaceX, spaceY, spaceZ; /*!< Ratio of the different axis */ + double originX,originY,originZ; /*!< Position of the origin of the image */ + + std::string byteOrder; /*!< Position of the origin of the image */ + std::ofstream file; + CellData cd; + PointData pd; + AppendData ad; +}; + +class PVTIWriter : public Element +{ + public: + PVTIWriter(const std::string& filename); + std::string filename; + std::string footer(); + std::string header(); + + double spaceX, spaceY, spaceZ; + double originX,originY,originZ; + + int pieceCounter; + + int64_t wholeMinX, wholeMinY, wholeMinZ; + int64_t wholeMaxX, wholeMaxY, wholeMaxZ; + + std::vector pieceMinX, pieceMinY, pieceMinZ; + std::vector pieceMaxX, pieceMaxY, pieceMaxZ; + std::vector pieceFilename; + + std::vector cellDataComponents; + std::vector cellDataName; + std::vector cellDataType; + + std::vector pointDataComponents; + std::vector pointDataName; + std::vector pointDataType; + + void addVTIWriter(VTIWriter& write); + void write(); +}; + + +#endif From 9bd4691097fbb060e7c889c11a14ad9c02bc08c2 Mon Sep 17 00:00:00 2001 From: Diogo Nardelli Siebert Date: Wed, 18 Feb 2026 13:43:32 -0300 Subject: [PATCH 2/2] Adding VTI (VTK XML Imagedata) output support --- IO/Mesh.cpp | 2 ++ IO/xmlvtk.cpp | 9 +++++---- models/MRTModel.cpp | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/IO/Mesh.cpp b/IO/Mesh.cpp index 5e5eb96a..0eb9283f 100644 --- a/IO/Mesh.cpp +++ b/IO/Mesh.cpp @@ -598,6 +598,8 @@ std::string getString( FileFormat type ) return "silo"; else if ( type == FileFormat::HDF5 ) return "hdf5"; + else if ( type == FileFormat::VTK ) + return "vtk"; else ERROR( "Invalid type" ); return ""; diff --git a/IO/xmlvtk.cpp b/IO/xmlvtk.cpp index cf2e1a4a..aceb4e30 100644 --- a/IO/xmlvtk.cpp +++ b/IO/xmlvtk.cpp @@ -23,7 +23,7 @@ using namespace std; /** @brief Indicates whether data compression is used by default **/ bool Element::compress = false; /** @brief Default cache size used during data processing. */ -headerType cacheSize = 1500; +headerType cacheSize = 10000; /** * @brief Formats a string using printf-style syntax. @@ -250,8 +250,6 @@ std::ostream& operator<<(std::ostream& os, AppendData& obj) { streampos beginPos = os.tellp(); - std::vectorcompressedData (cacheSize); - headerType totalByteSize = obj.sizeList[k]; headerType numberOfBlocks = totalByteSize / cacheSize + (totalByteSize % cacheSize != 0 ); @@ -266,8 +264,11 @@ std::ostream& operator<<(std::ostream& os, AppendData& obj) for (int n = 1; n <= numberOfBlocks; n++) { - uLongf compressedLength = cacheSize; int numberOfBytesInBlock = (n < numberOfBlocks) ? compressedInfo[1] : compressedInfo[2]; + + uLongf compressedLength = compressBound(numberOfBytesInBlock); + std::vectorcompressedData (compressedLength); + const Bytef* src = reinterpret_cast( obj.pointerList[k] + (n - 1) * cacheSize ); int ret = compress( (Bytef *) (compressedData.data() ), &compressedLength, src, numberOfBytesInBlock ); if (ret != Z_OK) diff --git a/models/MRTModel.cpp b/models/MRTModel.cpp index 41d68cc7..674132f1 100644 --- a/models/MRTModel.cpp +++ b/models/MRTModel.cpp @@ -483,7 +483,7 @@ void ScaLBL_MRTModel::VelocityField() { auto VzVar = std::make_shared(); auto SignDistVar = std::make_shared(); - IO::initialize("", format, "false"); + IO::initialize("", format, false); // Create the MeshDataStruct visData.resize(1); visData[0].meshName = "domain"; @@ -526,7 +526,7 @@ void ScaLBL_MRTModel::VelocityField() { fillData.copy(Velocity_x, VelxData); fillData.copy(Velocity_y, VelyData); fillData.copy(Velocity_z, VelzData); - + IO::writeData(timestep, visData, Dm->Comm); } }