diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp index 71f780bbed0..5f5c47f2775 100644 --- a/Common/include/CConfig.hpp +++ b/Common/include/CConfig.hpp @@ -810,6 +810,7 @@ class CConfig { su2double *nBlades; /*!< \brief number of blades for turbomachinery computation. */ unsigned short Geo_Description; /*!< \brief Description of the geometry. */ unsigned short Mesh_FileFormat; /*!< \brief Mesh input format. */ + unsigned short Mesh_Out_FileFormat; /*!< \brief Mesh output format. */ TAB_OUTPUT Tab_FileFormat; /*!< \brief Format of the output files. */ unsigned short output_precision; /*!< \brief .precision(value) for SU2_DOT and HISTORY output */ unsigned short ActDisk_Jump; /*!< \brief Format of the output files. */ @@ -1527,6 +1528,14 @@ class CConfig { */ void SetMPICommunicator(SU2_MPI::Comm Communicator); + /*! + * \brief Helper function, which checks and opens a binary SU2 file. + * \param[in] val_mesh_filename - Name of the file with the grid information. + * \param[in] readnDim = Whether or not nDim must be read. If false nZone is read. + * \return Number of dimensions or number of zones in the grid. + */ + static unsigned short CheckOpenSU2BinFile(const string& val_mesh_filename, bool readnDim); + /*! * \brief Gets the number of zones in the mesh file. * \param[in] val_mesh_filename - Name of the file with the grid information. @@ -5703,17 +5712,21 @@ class CConfig { /*--- we keep the original Mesh_FileName ---*/ string meshFilename = Mesh_FileName; - /*--- strip the extension, only if it is .su2 or .cgns ---*/ + /*--- strip the extension, only if it is .su2, .su2b or .cgns ---*/ PrintingToolbox::TrimExtension(".su2",meshFilename); + PrintingToolbox::TrimExtension(".su2b",meshFilename); PrintingToolbox::TrimExtension(".cgns",meshFilename); switch (GetMesh_FileFormat()) { - case SU2: - case RECTANGLE: - case BOX: + case ENUM_GRID::SU2: + case ENUM_GRID::RECTANGLE: + case ENUM_GRID::BOX: meshFilename += ".su2"; break; - case CGNS_GRID: + case ENUM_GRID::SU2_BIN: + meshFilename += ".su2b"; + break; + case ENUM_GRID::CGNS_GRID: meshFilename += ".cgns"; break; default: @@ -5734,10 +5747,23 @@ class CConfig { /*--- we keep the original Mesh_Out_FileName ---*/ string meshFilename = Mesh_Out_FileName; - /*--- strip the extension, only if it is .su2 or .cgns ---*/ + /*--- strip the extension, only if it is .su2, .su2b or .cgns ---*/ PrintingToolbox::TrimExtension(".su2",meshFilename); + PrintingToolbox::TrimExtension(".su2b",meshFilename); PrintingToolbox::TrimExtension(".cgns",meshFilename); + switch (GetMesh_Out_FileFormat()) { + case ENUM_GRID::SU2: + meshFilename += ".su2"; + break; + case ENUM_GRID::SU2_BIN: + meshFilename += ".su2b"; + break; + default: + SU2_MPI::Error("Unrecognized mesh_out format specified!", CURRENT_FUNCTION); + break; + } + return meshFilename; } @@ -5776,11 +5802,17 @@ class CConfig { } /*! - * \brief Get the format of the input/output grid. - * \return Format of the input/output grid. + * \brief Get the format of the input grid. + * \return Format of the input grid. */ unsigned short GetMesh_FileFormat(void) const { return Mesh_FileFormat; } + /*! + * \brief Get the format of the output grid. + * \return Format of the output grid. + */ + unsigned short GetMesh_Out_FileFormat(void) const { return Mesh_Out_FileFormat; } + /*! * \brief Get the format of the output solution. * \return Format of the output solution. diff --git a/Common/include/geometry/meshreader/CSU2ASCIIMeshReaderBase.hpp b/Common/include/geometry/meshreader/CSU2ASCIIMeshReaderBase.hpp index f1c35b2d939..8d4cbb4dd38 100644 --- a/Common/include/geometry/meshreader/CSU2ASCIIMeshReaderBase.hpp +++ b/Common/include/geometry/meshreader/CSU2ASCIIMeshReaderBase.hpp @@ -30,50 +30,19 @@ #include -#include "CMeshReaderBase.hpp" +#include "CSU2MeshReaderBase.hpp" /*! * \class CSU2ASCIIMeshReaderBase * \brief Base class for the reading of a native SU2 ASCII grid. * \author T. Economon */ -class CSU2ASCIIMeshReaderBase : public CMeshReaderBase { +class CSU2ASCIIMeshReaderBase : public CSU2MeshReaderBase { protected: enum class FileSection { POINTS, ELEMENTS, MARKERS }; /*!< \brief Different sections of the file. */ std::array SectionOrder{}; /*!< \brief Order of the sections in the file. */ - const unsigned short myZone; /*!< \brief Current SU2 zone index. */ - const unsigned short nZones; /*!< \brief Total number of zones in the SU2 file. */ - - const string meshFilename; /*!< \brief Name of the SU2 ASCII mesh file being read. */ - ifstream mesh_file; /*!< \brief File object for the SU2 ASCII mesh file. */ - - bool actuator_disk; /*!< \brief Boolean for whether we have an actuator disk to split. */ - - unsigned long ActDiskNewPoints = - 0; /*!< \brief Total number of new grid points to add due to actuator disk splitting. */ - - su2double Xloc = 0.0; /*!< \brief X-coordinate of the CG of the actuator disk surface. */ - su2double Yloc = 0.0; /*!< \brief X-coordinate of the CG of the actuator disk surface. */ - su2double Zloc = 0.0; /*!< \brief X-coordinate of the CG of the actuator disk surface. */ - - vector ActDisk_Bool; /*!< \brief Flag to identify the grid points on the actuator disk. */ - - vector ActDiskPoint_Back; /*!< \brief Vector containing the global index for the new grid points added - to the back of the actuator disk. */ - vector VolumePoint_Inv; /*!< \brief Vector containing the inverse mapping from the global index to the - added point index for the actuator disk. */ - - vector CoordXActDisk; /*!< \brief X-coordinates of the new grid points added by splitting the actuator disk - (size = ActDiskNewPoints). */ - vector CoordYActDisk; /*!< \brief Y-coordinates of the new grid points added by splitting the actuator disk - (size = ActDiskNewPoints). */ - vector CoordZActDisk; /*!< \brief Z-coordinates of the new grid points added by splitting the actuator disk - (size = ActDiskNewPoints). */ - - vector CoordXVolumePoint; /*!< \brief X-coordinates of the volume elements touching the actuator disk. */ - vector CoordYVolumePoint; /*!< \brief Y-coordinates of the volume elements touching the actuator disk. */ - vector CoordZVolumePoint; /*!< \brief Z-coordinates of the volume elements touching the actuator disk. */ + ifstream mesh_file; /*!< \brief File object for the SU2 ASCII mesh file. */ /*! * \brief Reads all SU2 ASCII mesh metadata and checks for errors. diff --git a/Common/include/geometry/meshreader/CSU2BinaryMeshReaderBase.hpp b/Common/include/geometry/meshreader/CSU2BinaryMeshReaderBase.hpp new file mode 100644 index 00000000000..9564c52a296 --- /dev/null +++ b/Common/include/geometry/meshreader/CSU2BinaryMeshReaderBase.hpp @@ -0,0 +1,114 @@ +/*! + * \file CSU2BinaryMeshReaderBase.hpp + * \brief Header file for the class CSU2BinaryMeshReaderBase. + * The implementations are in the CSU2BinaryMeshReaderBase.cpp file. + * \author T. Economon, E. van der Weide + * \version 8.2.0 "Harrier" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2025, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ + +#pragma once + +#include "CSU2MeshReaderBase.hpp" +#include "../../../include/toolboxes/SwapBytes.hpp" + +/*! + * \class CSU2BinaryMeshReaderBase + * \brief Base class for the reading of a native SU2 binary grid. + * \author T. Economon, E. van der Weide + */ +class CSU2BinaryMeshReaderBase : public CSU2MeshReaderBase { + protected: + constexpr static int SU2_STRING_SIZE = 33; /*!< \brief Size of the strings in the SU2 binary mesh file. */ + + FILE* mesh_file; /*!< \brief File object for the SU2 binary mesh file. */ + bool swap_bytes; /*!< \brief Whether or not byte swapping must be used. */ + int size_conn_type; /*!< \brief Size, in bytes of the connectivity type. */ + + /*! + * \brief Reads the connectivity type used in the binary file and check if + * byte swapping must be applied. + */ + void ReadConnectivityType(); + + /*! + * \brief Reads all SU2 binary mesh metadata and checks for errors. + * \param[in,out] config - Problem configuration where some metadata is updated (e.g. AoA). + */ + void ReadMetadata(CConfig* config); + + /*! + * \brief Reads the grid points from an SU2 zone into linear partitions across all ranks. + */ + virtual void ReadPointCoordinates(); + + /*! + * \brief Reads the interior volume elements from one section of an SU2 zone into linear partitions across all ranks. + */ + virtual void ReadVolumeElementConnectivity(); + + /*! + * \brief Reads the surface (boundary) elements from the SU2 zone. + */ + virtual void ReadSurfaceElementConnectivity(); + + /*! + * \brief Helper function to find the current zone in an SU2 binary mesh object. + */ + void FastForwardToMyZone(); + + /*! + * \brief Function to read one entity of the connectivity type from the binary file. + * \return uint64_t version of the the data. + */ + uint64_t ReadBinaryNEntities(); + + /*! + * \brief Template function to read data from the binary file. + */ + template + void ReadBinaryData(T* data, const size_t nItems) { + /*--- Read the actual data. ---*/ + auto ret = fread(data, sizeof(T), nItems, mesh_file); + if (ret != nItems) SU2_MPI::Error(string("Error while reading the file ") + meshFilename, CURRENT_FUNCTION); + + /*--- Apply byte swapping, if needed. ---*/ + if (swap_bytes) SwapBytes((char*)data, sizeof(T), nItems); + } + + private: + /*! + * \brief Read the meta data for a zone. + */ + void ReadMetadataZone(); + + public: + /*! + * \brief Constructor of the CSU2BinaryMeshReaderBase class. + */ + CSU2BinaryMeshReaderBase(CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone); + + /*! + * \brief Destructor of the CSU2BinaryMeshReaderBase class. + */ + ~CSU2BinaryMeshReaderBase(void) override; +}; diff --git a/Common/include/geometry/meshreader/CSU2BinaryMeshReaderFEM.hpp b/Common/include/geometry/meshreader/CSU2BinaryMeshReaderFEM.hpp new file mode 100644 index 00000000000..f4d191b7c87 --- /dev/null +++ b/Common/include/geometry/meshreader/CSU2BinaryMeshReaderFEM.hpp @@ -0,0 +1,66 @@ +/*! + * \file CSU2BinaryMeshReaderFEM.hpp + * \brief Header file for the class CSU2BinaryMeshReaderFEM. + * The implementations are in the CSU2BinaryMeshReaderFEM.cpp file. + * \author T. Economon, E. van der Weide + * \version 8.2.0 "Harrier" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2025, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ + +#pragma once + +#include "CSU2BinaryMeshReaderBase.hpp" + +/*! + * \class CSU2BinaryMeshReaderFEM + * \brief Reads a native SU2 binary grid into linear partitions for the finite element solver (FEM). + * \author T. Economon, E. van der Weide + */ +class CSU2BinaryMeshReaderFEM : public CSU2BinaryMeshReaderBase { + private: + /*! + * \brief Reads the grid points from an SU2 zone into linear partitions across all ranks. + */ + void ReadPointCoordinates(); + + /*! + * \brief Reads the interior volume elements from one section of an SU2 zone into linear partitions across all ranks. + */ + void ReadVolumeElementConnectivity(); + + /*! + * \brief Reads the surface (boundary) elements from one section of an SU2 zone into linear partitions across all + * ranks. + */ + void ReadSurfaceElementConnectivity(); + + public: + /*! + * \brief Constructor of the CSU2BinaryMeshReaderFEM class. + */ + CSU2BinaryMeshReaderFEM(CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone); + + /*! + * \brief Destructor of the CSU2BinaryMeshReaderFEM class. + */ + ~CSU2BinaryMeshReaderFEM(void) override; +}; diff --git a/Common/include/geometry/meshreader/CSU2BinaryMeshReaderFVM.hpp b/Common/include/geometry/meshreader/CSU2BinaryMeshReaderFVM.hpp new file mode 100644 index 00000000000..550b9437191 --- /dev/null +++ b/Common/include/geometry/meshreader/CSU2BinaryMeshReaderFVM.hpp @@ -0,0 +1,55 @@ +/*! + * \file CSU2BinaryMeshReaderFVM.hpp + * \brief Header file for the class CSU2BinaryMeshReaderFVM. + * The implementations are in the CSU2BinaryMeshReaderFVM.cpp file. + * \author T. Econonmon, E. van der Weide + * \version 8.2.0 "Harrier" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2025, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ + +#pragma once + +#include "CSU2BinaryMeshReaderBase.hpp" + +/*! + * \class CSU2BinaryMeshReaderFVM + * \brief Reads a native SU2 binary grid into linear partitions for the finite volume solver (FVM). + * \author T. Econonmon, E. van der Weide + */ +class CSU2BinaryMeshReaderFVM : public CSU2BinaryMeshReaderBase { + private: + /*! + * \brief Splits a single surface actuator disk boundary into two separate markers (repeated points). + */ + void SplitActuatorDiskSurface(); + + public: + /*! + * \brief Constructor of the CSU2BinaryMeshReaderFVM class. + */ + CSU2BinaryMeshReaderFVM(CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone); + + /*! + * \brief Destructor of the CSU2BinaryMeshReaderFVM class. + */ + ~CSU2BinaryMeshReaderFVM(void) override; +}; diff --git a/Common/include/geometry/meshreader/CSU2MeshReaderBase.hpp b/Common/include/geometry/meshreader/CSU2MeshReaderBase.hpp new file mode 100644 index 00000000000..8efef93cce5 --- /dev/null +++ b/Common/include/geometry/meshreader/CSU2MeshReaderBase.hpp @@ -0,0 +1,84 @@ +/*! + * \file CSU2MeshReaderBase.hpp + * \brief Header file for the class CSU2MeshReaderBase. + * The implementations are in the CSU2MeshReaderBase.cpp file. + * \author T. Economon + * \version 8.2.0 "Harrier" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2025, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ + +#pragma once + +#include + +#include "CMeshReaderBase.hpp" + +/*! + * \class CSU2MeshReaderBase + * \brief Base class for the reading of a native SU2 grid. + * \author T. Economon + */ +class CSU2MeshReaderBase : public CMeshReaderBase { + protected: + const unsigned short myZone; /*!< \brief Current SU2 zone index. */ + const unsigned short nZones; /*!< \brief Total number of zones in the SU2 file. */ + + const string meshFilename; /*!< \brief Name of the SU2 ASCII mesh file being read. */ + + bool actuator_disk; /*!< \brief Boolean for whether we have an actuator disk to split. */ + + unsigned long ActDiskNewPoints = + 0; /*!< \brief Total number of new grid points to add due to actuator disk splitting. */ + + su2double Xloc = 0.0; /*!< \brief X-coordinate of the CG of the actuator disk surface. */ + su2double Yloc = 0.0; /*!< \brief X-coordinate of the CG of the actuator disk surface. */ + su2double Zloc = 0.0; /*!< \brief X-coordinate of the CG of the actuator disk surface. */ + + vector ActDisk_Bool; /*!< \brief Flag to identify the grid points on the actuator disk. */ + + vector ActDiskPoint_Back; /*!< \brief Vector containing the global index for the new grid points added + to the back of the actuator disk. */ + vector VolumePoint_Inv; /*!< \brief Vector containing the inverse mapping from the global index to the + added point index for the actuator disk. */ + + vector CoordXActDisk; /*!< \brief X-coordinates of the new grid points added by splitting the actuator disk + (size = ActDiskNewPoints). */ + vector CoordYActDisk; /*!< \brief Y-coordinates of the new grid points added by splitting the actuator disk + (size = ActDiskNewPoints). */ + vector CoordZActDisk; /*!< \brief Z-coordinates of the new grid points added by splitting the actuator disk + (size = ActDiskNewPoints). */ + + vector CoordXVolumePoint; /*!< \brief X-coordinates of the volume elements touching the actuator disk. */ + vector CoordYVolumePoint; /*!< \brief Y-coordinates of the volume elements touching the actuator disk. */ + vector CoordZVolumePoint; /*!< \brief Z-coordinates of the volume elements touching the actuator disk. */ + + public: + /*! + * \brief Constructor of the CSU2MeshReaderBase class. + */ + CSU2MeshReaderBase(CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone); + + /*! + * \brief Destructor of the CSU2MeshReaderBase class. + */ + ~CSU2MeshReaderBase(void) override; +}; diff --git a/Common/include/option_structure.hpp b/Common/include/option_structure.hpp index 73266c7dacd..a73f6d7867a 100644 --- a/Common/include/option_structure.hpp +++ b/Common/include/option_structure.hpp @@ -2168,21 +2168,26 @@ static const MapType Objective_Map = { }; /*! - * \brief Types of input file formats + * \brief Types of grid file formats */ -enum ENUM_INPUT { - SU2 = 1, /*!< \brief SU2 input format. */ - CGNS_GRID = 2, /*!< \brief CGNS input format for the computational grid. */ - RECTANGLE = 3, /*!< \brief 2D rectangular mesh with N x M points of size Lx x Ly. */ - BOX = 4 /*!< \brief 3D box mesh with N x M x L points of size Lx x Ly x Lz. */ +enum ENUM_GRID { + SU2 = 1, /*!< \brief SU2 ascii format. */ + SU2_BIN = 2, /*!< \brief SU2 binary format. */ + CGNS_GRID = 3, /*!< \brief CGNS format for the computational grid. */ + RECTANGLE = 4, /*!< \brief 2D rectangular mesh with N x M points of size Lx x Ly. */ + BOX = 5 /*!< \brief 3D box mesh with N x M x L points of size Lx x Ly x Lz. */ }; -static const MapType Input_Map = { - MakePair("SU2", SU2) - MakePair("CGNS", CGNS_GRID) - MakePair("RECTANGLE", RECTANGLE) - MakePair("BOX", BOX) +static const MapType Input_Map = { + MakePair("SU2", ENUM_GRID::SU2) + MakePair("SU2B", ENUM_GRID::SU2_BIN) + MakePair("CGNS", ENUM_GRID::CGNS_GRID) + MakePair("RECTANGLE", ENUM_GRID::RECTANGLE) + MakePair("BOX", ENUM_GRID::BOX) +}; +static const MapType OutputMesh_Map = { + MakePair("SU2", ENUM_GRID::SU2) + MakePair("SU2B", ENUM_GRID::SU2_BIN) }; - /*! * \brief Type of solution output file formats @@ -2198,7 +2203,8 @@ enum class OUTPUT_TYPE { PARAVIEW_LEGACY_BINARY, /*!< \brief Paraview binary format for the solution output. */ SURFACE_PARAVIEW_ASCII, /*!< \brief Paraview ASCII format for the solution output. */ SURFACE_PARAVIEW_LEGACY_BINARY, /*!< \brief Paraview binary format for the solution output. */ - MESH, /*!< \brief SU2 mesh format. */ + MESH, /*!< \brief SU2 ASCII mesh format. */ + MESH_BINARY, /*!< \brief SU2 binary mesh format. */ RESTART_BINARY, /*!< \brief SU2 binary restart format. */ RESTART_ASCII, /*!< \brief SU2 ASCII restart format. */ PARAVIEW_XML, /*!< \brief Paraview XML with binary data format */ @@ -2224,6 +2230,7 @@ static const MapType Output_Map = { MakePair("SURFACE_PARAVIEW", OUTPUT_TYPE::SURFACE_PARAVIEW_XML) MakePair("PARAVIEW_MULTIBLOCK", OUTPUT_TYPE::PARAVIEW_MULTIBLOCK) MakePair("MESH", OUTPUT_TYPE::MESH) + MakePair("MESH_BINARY", OUTPUT_TYPE::MESH_BINARY) MakePair("RESTART_ASCII", OUTPUT_TYPE::RESTART_ASCII) MakePair("RESTART", OUTPUT_TYPE::RESTART_BINARY) MakePair("CGNS", OUTPUT_TYPE::CGNS) diff --git a/Common/include/toolboxes/SwapBytes.hpp b/Common/include/toolboxes/SwapBytes.hpp new file mode 100644 index 00000000000..6e524115ca2 --- /dev/null +++ b/Common/include/toolboxes/SwapBytes.hpp @@ -0,0 +1,39 @@ +/*! + * \file SwapBytes.hpp + * \brief Function to swap bytes of primitive data types. + * \author P. Gomes + * \version 8.2.0 "Harrier" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2025, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ + +#pragma once + +#include +#include + +/*! + * \brief Change storage of buffer to/from big endian from/to little endian + * \param buffer - Pointer to the beginning of the buffer + * \param nBytes - The size in bytes of an data entry + * \param nVar - The number of entries + */ +void SwapBytes(char* buffer, size_t nBytes, unsigned long nVar); diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index e6374b314ad..9b121af1f06 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -37,6 +37,7 @@ #include "../include/basic_types/ad_structure.hpp" #include "../include/toolboxes/printing_toolbox.hpp" +#include "../include/toolboxes/SwapBytes.hpp" using namespace PrintingToolbox; @@ -598,12 +599,60 @@ void CConfig::addPythonOption(const string& name) { option_map.insert(pair(name, val)); } +unsigned short CConfig::CheckOpenSU2BinFile(const string& val_mesh_filename, bool readnDim) { + /*--- Check if the mesh file can be opened for binary reading. ---*/ + FILE *mesh_file = fopen(val_mesh_filename.c_str(), "rb"); + if ( !mesh_file ) + SU2_MPI::Error("There is no geometry file called " + val_mesh_filename, + CURRENT_FUNCTION); + + /*--- Read the size of the connectivity type and determine whether + or not byte swapping must be applied. The size of the connectivity + type must be either 4 or 8. ---*/ + int size_conn_type; + auto ret = fread(&size_conn_type, sizeof(int), 1, mesh_file); + if (ret != 1) + SU2_MPI::Error("Error while reading the file " + val_mesh_filename, + CURRENT_FUNCTION); + + bool swap_bytes = false; + if ((size_conn_type != 4) && (size_conn_type != 8)) { + SwapBytes((char *) &size_conn_type, sizeof(int), 1); + swap_bytes = true; + } + + if ((size_conn_type != 4) && (size_conn_type != 8)) + SU2_MPI::Error("The file " + val_mesh_filename + + " is not a valid SU2 binary file", CURRENT_FUNCTION); + + /*--- Skip the number of zones and the zone ID, + if the number of dimensions must be read. ---*/ + if( readnDim ) { + if( fseek(mesh_file, 2*sizeof(int), SEEK_CUR) ) + SU2_MPI::Error("Failed to jump forward in the file" + val_mesh_filename, + CURRENT_FUNCTION); + } + + /*--- Read the information to be returned. ---*/ + int info; + ret = fread(&info, sizeof(int), 1, mesh_file); + if (ret != 1) + SU2_MPI::Error("Error while reading the file " + val_mesh_filename, + CURRENT_FUNCTION); + if ( swap_bytes) + SwapBytes((char *) &info, sizeof(int), 1); + + fclose(mesh_file); + + return (unsigned short) info; +} + unsigned short CConfig::GetnZone(const string& val_mesh_filename, unsigned short val_format) { int nZone = 1; /* Default value if nothing is specified. */ switch (val_format) { - case SU2: { + case ENUM_GRID::SU2: { /*--- Local variables for reading the SU2 file. ---*/ string text_line; @@ -612,8 +661,8 @@ unsigned short CConfig::GetnZone(const string& val_mesh_filename, unsigned short /*--- Check if the mesh file can be opened for reading. ---*/ mesh_file.open(val_mesh_filename.c_str(), ios::in); if (mesh_file.fail()) - SU2_MPI::Error(string("There is no geometry file called ") + val_mesh_filename, - CURRENT_FUNCTION); + SU2_MPI::Error("There is no geometry file called " + val_mesh_filename, + CURRENT_FUNCTION); /*--- Read the SU2 mesh file until the zone data is reached or when it can be decided that it is not present. ---*/ @@ -639,7 +688,15 @@ unsigned short CConfig::GetnZone(const string& val_mesh_filename, unsigned short } - case CGNS_GRID: { + case ENUM_GRID::SU2_BIN: { + + /*--- Open and check the grid file and read the number of zones + at the correct location. */ + nZone = CheckOpenSU2BinFile(val_mesh_filename, false); + break; + } + + case ENUM_GRID::CGNS_GRID: { #ifdef HAVE_CGNS @@ -706,11 +763,11 @@ unsigned short CConfig::GetnZone(const string& val_mesh_filename, unsigned short break; } - case RECTANGLE: { + case ENUM_GRID::RECTANGLE: { nZone = 1; break; } - case BOX: { + case ENUM_GRID::BOX: { nZone = 1; break; } @@ -722,10 +779,10 @@ unsigned short CConfig::GetnZone(const string& val_mesh_filename, unsigned short unsigned short CConfig::GetnDim(const string& val_mesh_filename, unsigned short val_format) { - short nDim = -1; + int nDim = -1; switch (val_format) { - case SU2: { + case ENUM_GRID::SU2: { /*--- Local variables for reading the SU2 file. ---*/ string text_line; @@ -734,7 +791,7 @@ unsigned short CConfig::GetnDim(const string& val_mesh_filename, unsigned short /*--- Open grid file ---*/ mesh_file.open(val_mesh_filename.c_str(), ios::in); if (mesh_file.fail()) { - SU2_MPI::Error(string("The SU2 mesh file named ") + val_mesh_filename + string(" was not found."), CURRENT_FUNCTION); + SU2_MPI::Error("The SU2 mesh file named " + val_mesh_filename + " was not found.", CURRENT_FUNCTION); } /*--- Read the SU2 mesh file until the dimension data is reached @@ -760,14 +817,22 @@ unsigned short CConfig::GetnDim(const string& val_mesh_filename, unsigned short /*--- Throw an error if the dimension was not found. ---*/ if (nDim == -1) { - SU2_MPI::Error(val_mesh_filename + string(" is not an SU2 mesh file or has the wrong format \n ('NDIME=' not found). Please check."), + SU2_MPI::Error(val_mesh_filename + " is not an SU2 mesh file or has the wrong format \n ('NDIME=' not found). Please check.", CURRENT_FUNCTION); } break; } - case CGNS_GRID: { + case ENUM_GRID::SU2_BIN: { + + /*--- Open and check the grid file and read the number of dimensions + at the correct location. */ + nDim = CheckOpenSU2BinFile(val_mesh_filename, true); + break; + } + + case ENUM_GRID::CGNS_GRID: { #ifdef HAVE_CGNS @@ -816,11 +881,11 @@ unsigned short CConfig::GetnDim(const string& val_mesh_filename, unsigned short break; } - case RECTANGLE: { + case ENUM_GRID::RECTANGLE: { nDim = 2; break; } - case BOX: { + case ENUM_GRID::BOX: { nDim = 3; break; } @@ -2212,9 +2277,11 @@ void CConfig::SetConfig_Options() { /*!\brief ACTDISK_JUMP \n DESCRIPTION: The jump is given by the difference in values or a ratio */ addEnumOption("ACTDISK_JUMP", ActDisk_Jump, Jump_Map, DIFFERENCE); /*!\brief MESH_FORMAT \n DESCRIPTION: Mesh input file format \n OPTIONS: see \link Input_Map \endlink \n DEFAULT: SU2 \ingroup Config*/ - addEnumOption("MESH_FORMAT", Mesh_FileFormat, Input_Map, SU2); + addEnumOption("MESH_FORMAT", Mesh_FileFormat, Input_Map, ENUM_GRID::SU2); /* DESCRIPTION: Mesh input file */ addStringOption("MESH_FILENAME", Mesh_FileName, string("mesh")); + /*!\brief MESH_OUT_FORMAT \n DESCRIPTION: Mesh output file format \n OPTIONS: see \link OutputMesh_Map \endlink \n DEFAULT: SU2 \ingroup Config*/ + addEnumOption("MESH_OUT_FORMAT", Mesh_Out_FileFormat, OutputMesh_Map, ENUM_GRID::SU2); /*!\brief MESH_OUT_FILENAME \n DESCRIPTION: Mesh output file name. Used when converting, scaling, or deforming a mesh. \n DEFAULT: mesh_out \ingroup Config*/ addStringOption("MESH_OUT_FILENAME", Mesh_Out_FileName, string("mesh_out")); @@ -7655,7 +7722,7 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) { } if (val_software == SU2_COMPONENT::SU2_DEF) { - cout << "Output mesh file name: " << GetMesh_Out_FileName() << ".su2. " << endl; + cout << "Output mesh file name: " << GetMesh_Out_FileName() << ". " << endl; switch (GetDeform_Stiffness_Type()) { case INVERSE_VOLUME: cout << "Cell stiffness scaled by inverse of the cell volume." << endl; diff --git a/Common/src/geometry/CPhysicalGeometry.cpp b/Common/src/geometry/CPhysicalGeometry.cpp index 53720985fe2..448784e016c 100644 --- a/Common/src/geometry/CPhysicalGeometry.cpp +++ b/Common/src/geometry/CPhysicalGeometry.cpp @@ -33,6 +33,8 @@ #include "../../include/toolboxes/geometry_toolbox.hpp" #include "../../include/geometry/meshreader/CSU2ASCIIMeshReaderFEM.hpp" #include "../../include/geometry/meshreader/CSU2ASCIIMeshReaderFVM.hpp" +#include "../../include/geometry/meshreader/CSU2BinaryMeshReaderFEM.hpp" +#include "../../include/geometry/meshreader/CSU2BinaryMeshReaderFVM.hpp" #include "../../include/geometry/meshreader/CCGNSMeshReaderFVM.hpp" #include "../../include/geometry/meshreader/CCGNSMeshReaderFEM.hpp" #include "../../include/geometry/meshreader/CRectangularMeshReaderFEM.hpp" @@ -80,6 +82,7 @@ CPhysicalGeometry::CPhysicalGeometry(CConfig* config, unsigned short val_iZone, switch (val_format) { case SU2: + case SU2_BIN: case CGNS_GRID: case RECTANGLE: case BOX: @@ -3460,6 +3463,12 @@ void CPhysicalGeometry::Read_Mesh(CConfig* config, const string& val_mesh_filena else Mesh = new CSU2ASCIIMeshReaderFVM(config, val_iZone, val_nZone); break; + case SU2_BIN: + if (fem_solver) + Mesh = new CSU2BinaryMeshReaderFEM(config, val_iZone, val_nZone); + else + Mesh = new CSU2BinaryMeshReaderFVM(config, val_iZone, val_nZone); + break; case CGNS_GRID: if (fem_solver) Mesh = new CCGNSMeshReaderFEM(config, val_iZone, val_nZone); diff --git a/Common/src/geometry/meshreader/CSU2ASCIIMeshReaderBase.cpp b/Common/src/geometry/meshreader/CSU2ASCIIMeshReaderBase.cpp index 5a9a6da1fb4..a0eb8417456 100644 --- a/Common/src/geometry/meshreader/CSU2ASCIIMeshReaderBase.cpp +++ b/Common/src/geometry/meshreader/CSU2ASCIIMeshReaderBase.cpp @@ -30,10 +30,7 @@ CSU2ASCIIMeshReaderBase::CSU2ASCIIMeshReaderBase(CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone) - : CMeshReaderBase(val_config, val_iZone, val_nZone), - myZone(val_iZone), - nZones(val_nZone), - meshFilename(config->GetMesh_FileName()) {} + : CSU2MeshReaderBase(val_config, val_iZone, val_nZone) {} CSU2ASCIIMeshReaderBase::~CSU2ASCIIMeshReaderBase(void) = default; diff --git a/Common/src/geometry/meshreader/CSU2BinaryMeshReaderBase.cpp b/Common/src/geometry/meshreader/CSU2BinaryMeshReaderBase.cpp new file mode 100644 index 00000000000..fc937a21d50 --- /dev/null +++ b/Common/src/geometry/meshreader/CSU2BinaryMeshReaderBase.cpp @@ -0,0 +1,470 @@ +/*! + * \file CSU2BinaryMeshReaderBase.cpp + * \brief Helper class for the reading of a native SU2 binary grid file. + * \author T. Economon, E. van der Weide + * \version 8.2.0 "Harrier" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2025, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ + +#include "../../../include/toolboxes/CLinearPartitioner.hpp" +#include "../../../include/geometry/meshreader/CSU2BinaryMeshReaderBase.hpp" + +#include + +CSU2BinaryMeshReaderBase::CSU2BinaryMeshReaderBase(CConfig* val_config, unsigned short val_iZone, + unsigned short val_nZone) + : CSU2MeshReaderBase(val_config, val_iZone, val_nZone) {} + +CSU2BinaryMeshReaderBase::~CSU2BinaryMeshReaderBase(void) = default; + +void CSU2BinaryMeshReaderBase::ReadConnectivityType() { + /*--- Initialize the byte swapping to false and + read the size of the connectivity type. ---*/ + swap_bytes = false; + ReadBinaryData(&size_conn_type, 1); + + /*--- Check if byte swapping must be applied. ---*/ + if ((size_conn_type != 4) && (size_conn_type != 8)) { + SwapBytes((char*)&size_conn_type, sizeof(int), 1); + swap_bytes = true; + } + + /*--- The size of the connectivity type must be either 4 or 8. ---*/ + if ((size_conn_type != 4) && (size_conn_type != 8)) + SU2_MPI::Error(string("The file ") + meshFilename + string(" is not a valid SU2 binary file"), CURRENT_FUNCTION); +} + +void CSU2BinaryMeshReaderBase::ReadMetadata(CConfig* config) { + const bool harmonic_balance = config->GetTime_Marching() == TIME_MARCHING::HARMONIC_BALANCE; + const bool multizone_file = config->GetMultizone_Mesh(); + + /*--- Open the grid file and check if it went OK. ---*/ + mesh_file = fopen(meshFilename.c_str(), "rb"); + if (!mesh_file) + SU2_MPI::Error( + string("Error opening SU2 binary grid file ") + meshFilename + string(". Check that the file exists"), + CURRENT_FUNCTION); + + /*--- Read the size of the connectivity type and check if byte swapping + must be applied. ---*/ + ReadConnectivityType(); + + /*--- Check for a harmonic balance simulation. If So, the data of the + first zone can be read. Otherwise jump to the location of the + current zone. ---*/ + if (harmonic_balance) { + if (rank == MASTER_NODE) cout << "Reading time instance " << config->GetiInst() + 1 << "." << endl; + fseek(mesh_file, 2 * sizeof(int), SEEK_SET); + } else { + FastForwardToMyZone(); + if (nZones > 1 && multizone_file) { + if (rank == MASTER_NODE) cout << "Reading zone " << myZone << " from native SU2 binary mesh." << endl; + } + } + + /*--- Read the meta data from the current position. ---*/ + ReadMetadataZone(); + + /*--- Close the grid file again. ---*/ + fclose(mesh_file); +} + +void CSU2BinaryMeshReaderBase::ReadPointCoordinates() { + /* No support yet for actuator disks */ + if (actuator_disk) SU2_MPI::Error("No support for actuator disks yet", CURRENT_FUNCTION); + + /* Jump over the number of points, because it is already known, and + determine the position in the file where the point section ends. */ + fseek(mesh_file, size_conn_type, SEEK_CUR); + auto pos_end_point = ftell(mesh_file) + numberOfGlobalPoints * (dimension * sizeof(double) + size_conn_type); + + /* Define a linear partitioner for the points. */ + CLinearPartitioner pointPartitioner(numberOfGlobalPoints, 0); + + /* Jump to the position in the file where the points are stored + that this rank must read. */ + const auto firstIndex = pointPartitioner.GetFirstIndexOnRank(rank); + fseek(mesh_file, firstIndex * (dimension * sizeof(double) + size_conn_type), SEEK_CUR); + + /* Determine the number of local points and prepare the local + data structure to store the point coordinates. */ + numberOfLocalPoints = pointPartitioner.GetSizeOnRank(rank); + localPointCoordinates.resize(dimension); + for (int k = 0; k < dimension; k++) localPointCoordinates[k].resize(numberOfLocalPoints); + + /*--- Read the point coordinates into our data structure. ---*/ + for (unsigned long i = 0; i < numberOfLocalPoints; ++i) { + double Coords[3]; + ReadBinaryData(Coords, dimension); + fseek(mesh_file, size_conn_type, SEEK_CUR); + + for (unsigned short iDim = 0; iDim < dimension; iDim++) { + localPointCoordinates[iDim][i] = Coords[iDim]; + } + } + + /* Jump to the end of the coordinate section. */ + fseek(mesh_file, pos_end_point, SEEK_SET); +} + +void CSU2BinaryMeshReaderBase::ReadVolumeElementConnectivity() { + /* Jump over the zone ID, number of dimensions and number of elements, + because this information is already known. */ + fseek(mesh_file, size_conn_type + 2 * sizeof(int), SEEK_CUR); + + /* Get a linear partitioner of the elements. */ + CLinearPartitioner elemPartitioner(numberOfGlobalElements, 0); + + /* Determine the position at the end of the offset array, where + the total size of the connectivity is stored. */ + const auto first_index = elemPartitioner.GetFirstIndexOnRank(rank); + const auto pos_size_global_conn = ftell(mesh_file) + numberOfGlobalElements * size_conn_type; + + /* Jump to position in the file where the offset data is stored for + the element ragne this rank will read. Allocate the memory for + this offset array. */ + fseek(mesh_file, first_index * size_conn_type, SEEK_CUR); + numberOfLocalElements = elemPartitioner.GetSizeOnRank(rank); + vector offset(numberOfLocalElements + 1); + + /* Read the offset array from the file. It will be stored as uint64_t, + but it may be stored differently in the file. */ + if (size_conn_type == 4) { + vector tmp(offset.size()); + ReadBinaryData(tmp.data(), tmp.size()); + for (size_t i = 0; i < tmp.size(); ++i) offset[i] = static_cast(tmp[i]); + } else { + ReadBinaryData(offset.data(), offset.size()); + } + + /* Jump to the location where the total size of the connectivity array + is stored and read the size. Determine the location of the end of + the connectivity array. */ + fseek(mesh_file, pos_size_global_conn, SEEK_SET); + const auto size__global_conn = ReadBinaryNEntities(); + const auto pos_end_conn = ftell(mesh_file) + size__global_conn * size_conn_type; + + /* Read the connectivity data of the elements this rank should read. + Store the data in uint64_t. */ + const auto size_conn = offset.back() - offset[0]; + vector conn_buff(size_conn); + fseek(mesh_file, offset[0] * size_conn_type, SEEK_CUR); + + if (size_conn_type == 4) { + vector tmp(conn_buff.size()); + ReadBinaryData(tmp.data(), tmp.size()); + for (size_t i = 0; i < tmp.size(); ++i) conn_buff[i] = static_cast(tmp[i]); + } else { + ReadBinaryData(conn_buff.data(), conn_buff.size()); + } + + /* Jump to the end of the connectivity data. */ + fseek(mesh_file, pos_end_conn, SEEK_SET); + +#ifdef HAVE_MPI + + /* Update the offset, such that it corresponds to the data in the + local connectivity buffer. */ + for (size_t i = 1; i < offset.size(); ++i) offset[i] -= offset[0]; + offset[0] = 0; + + /* Get a linear partitioner of the points. */ + CLinearPartitioner pointPartitioner(numberOfGlobalPoints, 0); + + /*--- Determine the ranks on which the elements must actually be stored. + Note that an element can be stored on multiple ranks, as the points + must be surrounded by all its elements. ---*/ + std::vector ranks_elements; + ranks_elements.reserve(numberOfLocalElements); + std::vector number_of_ranks_elements(numberOfLocalElements + 1); + number_of_ranks_elements[0] = 0; + + for (unsigned long i = 0; i < numberOfLocalElements; ++i) { + /* Determine the ranks where this element must be stored by looping + over its nodes. */ + set ranks_this_elem; + for (uint64_t j = offset[i] + 1; j < (offset[i + 1] - 1); ++j) { + auto rank_node = pointPartitioner.GetRankContainingIndex(conn_buff[j]); + ranks_this_elem.insert(static_cast(rank_node)); + } + + /* Store the data. */ + number_of_ranks_elements[i + 1] = number_of_ranks_elements[i] + ranks_this_elem.size(); + for (auto rank_elem : ranks_this_elem) ranks_elements.push_back(rank_elem); + } + + /* Create the send buffers. Both the size of each connectivity + information and the connectivity information itself is stored.*/ + std::vector> send_buf; + send_buf.resize(size); + + for (unsigned long i = 0; i < numberOfLocalElements; ++i) { + for (uint64_t j = number_of_ranks_elements[i]; j < number_of_ranks_elements[i + 1]; ++j) { + const int ii = ranks_elements[j]; + + auto size_this_conn = offset[i + 1] - offset[i]; + send_buf[ii].push_back(size_this_conn); + for (uint64_t k = offset[i]; k < offset[i + 1]; ++k) send_buf[ii].push_back(conn_buff[k]); + } + } + + /* Determine the number of ranks from which this rank will receive data. + Allow for self communication. */ + int nRankRecv; + vector sendToRank(size, 0), sizeSend(size, 1); + for (auto rank_elem : ranks_elements) sendToRank[rank_elem] = 1; + SU2_MPI::Reduce_scatter(sendToRank.data(), &nRankRecv, sizeSend.data(), MPI_INT, MPI_SUM, SU2_MPI::GetComm()); + + /* Explicitly delete the memory that is not needed anymore. */ + vector().swap(offset); + vector().swap(conn_buff); + vector().swap(sizeSend); + + /* Determine the number of ranks to which this rank will send data + and allocate the memory for the send requests. */ + int nRankSend = 0; + for (int i = 0; i < size; ++i) { + if (sendToRank[i]) ++nRankSend; + } + + vector sendReqs(nRankSend); + + /* Send the data using non-blocking sends. */ + nRankSend = 0; + for (int i = 0; i < size; ++i) { + if (sendToRank[i]) { + SU2_MPI::Isend(send_buf[i].data(), send_buf[i].size(), MPI_UNSIGNED_LONG, i, i, SU2_MPI::GetComm(), + &sendReqs[nRankSend]); + ++nRankSend; + } + } + + /* Define the receive buffers and receive the messages. */ + std::vector> recv_buf; + recv_buf.resize(size); + + for (int i = 0; i < nRankRecv; ++i) { + SU2_MPI::Status status; + SU2_MPI::Probe(MPI_ANY_SOURCE, rank, SU2_MPI::GetComm(), &status); + int rankRecv = status.MPI_SOURCE; + + int sizeMess; + SU2_MPI::Get_count(&status, MPI_UNSIGNED_LONG, &sizeMess); + recv_buf[rankRecv].resize(sizeMess); + SU2_MPI::Recv(recv_buf[rankRecv].data(), sizeMess, MPI_UNSIGNED_LONG, rankRecv, rank, SU2_MPI::GetComm(), &status); + } + + /* Complete the non-blocking sends and release the memory of the send buffers. */ + SU2_MPI::Waitall(nRankSend, sendReqs.data(), MPI_STATUSES_IGNORE); + for (int i = 0; i < size; ++i) { + if (sendToRank[i]) vector().swap(send_buf[i]); + } + + /* Synchronize the MPI ranks, because wild cards have been used. */ + SU2_MPI::Barrier(SU2_MPI::GetComm()); + + /*--- Store the information in the receive buffers in the offset and conn_buff + vectors, such that it is consistent with the information without MPI. + Release the memory of the receive buffers afterwards. ---*/ + offset.push_back(0); + for (int i = 0; i < size; ++i) { + if (recv_buf[i].size() > 0) { + size_t ind = 0; + while (ind < recv_buf[i].size()) { + auto n_items = recv_buf[i][ind++]; + offset.push_back(offset.back() + n_items); + for (unsigned long j = 0; j < n_items; ++j, ++ind) conn_buff.push_back(recv_buf[i][ind]); + } + vector().swap(recv_buf[i]); + } + } + +#endif + + /*--- Extract the connectivity data from conn_buf and store the data + in the appropriate member variables. ---*/ + numberOfLocalElements = offset.size() - 1; + array connectivity{}; + + for (unsigned long i = 0; i < numberOfLocalElements; ++i) { + auto ind = offset[i]; + auto size_this_elem = static_cast(offset[i + 1] - offset[i]); + auto VTK_Type = conn_buff[ind++]; + const auto nPointsElem = nPointsOfElementType(static_cast(VTK_Type)); + if (size_this_elem < (nPointsElem + 2)) SU2_MPI::Error("Not enough items in volume connectivity", CURRENT_FUNCTION); + + for (unsigned short j = 0; j < nPointsElem; ++j, ++ind) connectivity[j] = conn_buff[ind]; + auto GlobalIndex = conn_buff[ind]; + + localVolumeElementConnectivity.push_back(GlobalIndex); + localVolumeElementConnectivity.push_back(VTK_Type); + /// TODO: Use a compressed format. + for (unsigned short j = 0; j < N_POINTS_HEXAHEDRON; ++j) { + localVolumeElementConnectivity.push_back(connectivity[j]); + } + } +} + +void CSU2BinaryMeshReaderBase::ReadSurfaceElementConnectivity() { + /* The number of surface markers is already known, so jump over it. */ + fseek(mesh_file, sizeof(int), SEEK_CUR); + + /* Allocate the memory for the first index of the connectivty of the + surface elements and the marker names. Note that all ranks store + the entire surface connectivity. */ + surfaceElementConnectivity.resize(numberOfMarkers); + markerNames.resize(numberOfMarkers); + + array connectivity{}; + + /* Loop over the number of markers. */ + for (unsigned long iMarker = 0; iMarker < numberOfMarkers; ++iMarker) { + /* Read the name of the surface marker. */ + char charStr[SU2_STRING_SIZE]; + ReadBinaryData(charStr, SU2_STRING_SIZE); + markerNames[iMarker] = string(charStr); + + /*--- Throw an error if we find deprecated references to SEND_RECEIVE + boundaries in the mesh. ---*/ + if (markerNames[iMarker] == "SEND_RECEIVE") + SU2_MPI::Error( + "Mesh file contains deprecated SEND_RECEIVE marker!\n" + "Please remove any SEND_RECEIVE markers from the SU2 binary mesh.", + CURRENT_FUNCTION); + + /* Read the number of elements for this boundary marker. */ + const auto nElem_Bound = ReadBinaryNEntities(); + + /*--- Read the offset array from the file. It will be stored as + uint64_t, but it may be stored differently in the file. ---*/ + vector offset(nElem_Bound + 1); + if (size_conn_type == 4) { + vector tmp(offset.size()); + ReadBinaryData(tmp.data(), tmp.size()); + for (size_t i = 0; i < tmp.size(); ++i) offset[i] = static_cast(tmp[i]); + } else { + ReadBinaryData(offset.data(), offset.size()); + } + + /*--- Read the connectivity and store it in a buffer. + Always use uint64_t for this internally. ---*/ + vector conn_buff(offset.back()); + if (size_conn_type == 4) { + vector tmp(conn_buff.size()); + ReadBinaryData(tmp.data(), tmp.size()); + for (size_t i = 0; i < tmp.size(); ++i) conn_buff[i] = static_cast(tmp[i]); + } else { + ReadBinaryData(conn_buff.data(), conn_buff.size()); + } + + /*--- Loop over the surface elements to store the connectivity + in the reequired data structures. ---*/ + for (unsigned long i = 0; i < nElem_Bound; ++i) { + auto ind = offset[i]; + auto size_this_elem = static_cast(offset[i + 1] - offset[i]); + auto VTK_Type = conn_buff[ind++]; + const auto nPointsElem = nPointsOfElementType(static_cast(VTK_Type)); + if (size_this_elem < (nPointsElem + 1)) + SU2_MPI::Error("Not enough items in surface connectivity", CURRENT_FUNCTION); + + if (dimension == 3 && VTK_Type == LINE) { + SU2_MPI::Error( + "Line boundary conditions are not possible for 3D calculations.\n" + "Please check the SU2 binary file.", + CURRENT_FUNCTION); + } + + for (unsigned short j = 0; j < nPointsElem; ++j, ++ind) connectivity[j] = conn_buff[ind]; + + surfaceElementConnectivity[iMarker].push_back(0); + surfaceElementConnectivity[iMarker].push_back(VTK_Type); + for (unsigned short j = 0; j < N_POINTS_HEXAHEDRON; ++j) { + surfaceElementConnectivity[iMarker].push_back(connectivity[j]); + } + } + } +} + +void CSU2BinaryMeshReaderBase::FastForwardToMyZone() { + /*--- Jump to the position where the data starts for the first zone. ---*/ + fseek(mesh_file, 2 * sizeof(int), SEEK_SET); + + /*--- Loop over the lower numbered zones and read their meta data. ---*/ + for (int zone = 0; zone < myZone; ++zone) ReadMetadataZone(); +} + +uint64_t CSU2BinaryMeshReaderBase::ReadBinaryNEntities() { + /*--- Define the return value as an uint64_t. ---*/ + uint64_t nEntities; + + /*--- Read the actual data, depending on the connectivity type. ---*/ + if (size_conn_type == 4) { + uint32_t dummy; + ReadBinaryData(&dummy, 1); + nEntities = static_cast(dummy); + } else { + ReadBinaryData(&nEntities, 1); + } + + return nEntities; +} + +void CSU2BinaryMeshReaderBase::ReadMetadataZone() { + /*--- Skip the zone ID and read the number of dimensions. ---*/ + int nDim; + fseek(mesh_file, sizeof(int), SEEK_CUR); + ReadBinaryData(&nDim, 1); + dimension = static_cast(nDim); + + /*--- Read the number of elements. ---*/ + const auto nElem = ReadBinaryNEntities(); + numberOfGlobalElements = static_cast(nElem); + + /*--- Jump to the end of the offset section, read the size of + the connectivity and jump over it. ---*/ + fseek(mesh_file, nElem * size_conn_type, SEEK_CUR); + const auto size_conn = ReadBinaryNEntities(); + fseek(mesh_file, size_conn * size_conn_type, SEEK_CUR); + + /*--- Read the number of points and jump over the coordinate section. ---*/ + const auto nPoints = ReadBinaryNEntities(); + numberOfGlobalPoints = static_cast(nPoints); + fseek(mesh_file, nPoints * (nDim * sizeof(double) + size_conn_type), SEEK_CUR); + + /*--- Read the number of markers and loop over them. ---*/ + int nMark; + ReadBinaryData(&nMark, 1); + numberOfMarkers = static_cast(nMark); + + for (int mark = 0; mark < nMark; ++mark) { + /*--- Jump over the name of the marker and read + the number of surface elements. ---*/ + fseek(mesh_file, SU2_STRING_SIZE * sizeof(char), SEEK_CUR); + const auto nElemMark = ReadBinaryNEntities(); + + /*--- Jump to the end of the offset section of this marker, read + the size of the connectivity and jump over it. ---*/ + fseek(mesh_file, nElemMark * size_conn_type, SEEK_CUR); + const auto size_conn_mark = ReadBinaryNEntities(); + fseek(mesh_file, size_conn_mark * size_conn_type, SEEK_CUR); + } +} diff --git a/Common/src/geometry/meshreader/CSU2BinaryMeshReaderFEM.cpp b/Common/src/geometry/meshreader/CSU2BinaryMeshReaderFEM.cpp new file mode 100644 index 00000000000..c250eca3e70 --- /dev/null +++ b/Common/src/geometry/meshreader/CSU2BinaryMeshReaderFEM.cpp @@ -0,0 +1,69 @@ +/*! + * \file CSU2BinaryMeshReaderFEM.cpp + * \brief Reads a native SU2 binary grid into linear partitions for the + * finite element solver (FEM). + * \author T. Economon, E. van der Weide + * \version 8.2.0 "Harrier" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2025, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ + +#include "../../../include/toolboxes/CLinearPartitioner.hpp" +#include "../../../include/geometry/meshreader/CSU2BinaryMeshReaderFEM.hpp" +#include "../../../include/fem/fem_standard_element.hpp" + +CSU2BinaryMeshReaderFEM::CSU2BinaryMeshReaderFEM(CConfig* val_config, unsigned short val_iZone, + unsigned short val_nZone) + : CSU2BinaryMeshReaderBase(val_config, val_iZone, val_nZone) { + /* Read the basic metadata and perform some basic error checks. */ + ReadMetadata(val_config); + + /*--- Open the file with the mesh and go to the place where the data + of the current zone is stored. ---*/ + mesh_file = fopen(meshFilename.c_str(), "rb"); + FastForwardToMyZone(); + + /*--- Read the volume connectivity and distribute it + linearly over the MPI ranks. ---*/ + ReadVolumeElementConnectivity(); + + /*--- Read the coordinates of the points that are needed + on this MPI rank. ---*/ + ReadPointCoordinates(); + + /*--- Read the surface connectivity and store the surface elements whose + corresponding volume element is stored on this MPI rank. ---*/ + ReadSurfaceElementConnectivity(); + + fclose(mesh_file); +} + +CSU2BinaryMeshReaderFEM::~CSU2BinaryMeshReaderFEM() = default; + +void CSU2BinaryMeshReaderFEM::ReadPointCoordinates() { SU2_MPI::Error("Not implemented yet", CURRENT_FUNCTION); } + +void CSU2BinaryMeshReaderFEM::ReadVolumeElementConnectivity() { + SU2_MPI::Error("Not implemented yet", CURRENT_FUNCTION); +} + +void CSU2BinaryMeshReaderFEM::ReadSurfaceElementConnectivity() { + SU2_MPI::Error("Not implemented yet", CURRENT_FUNCTION); +} diff --git a/Common/src/geometry/meshreader/CSU2BinaryMeshReaderFVM.cpp b/Common/src/geometry/meshreader/CSU2BinaryMeshReaderFVM.cpp new file mode 100644 index 00000000000..eecdddda399 --- /dev/null +++ b/Common/src/geometry/meshreader/CSU2BinaryMeshReaderFVM.cpp @@ -0,0 +1,64 @@ +/*! + * \file CSU2BinaryMeshReaderFVM.cpp + * \brief Reads a native SU2 binary grid into linear partitions for the + * finite volume solver (FVM). + * \author T. Economon + * \version 8.2.0 "Harrier" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2025, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ + +#include "../../../include/geometry/meshreader/CSU2BinaryMeshReaderFVM.hpp" + +CSU2BinaryMeshReaderFVM::CSU2BinaryMeshReaderFVM(CConfig* val_config, unsigned short val_iZone, + unsigned short val_nZone) + : CSU2BinaryMeshReaderBase(val_config, val_iZone, val_nZone) { + actuator_disk = (((config->GetnMarker_ActDiskInlet() != 0) || (config->GetnMarker_ActDiskOutlet() != 0)) && + ((config->GetKind_SU2() == SU2_COMPONENT::SU2_CFD) || + ((config->GetKind_SU2() == SU2_COMPONENT::SU2_DEF) && (config->GetActDisk_SU2_DEF())))); + if (config->GetActDisk_DoubleSurface()) actuator_disk = false; + + /* Read the basic metadata and perform some basic error checks. */ + ReadMetadata(val_config); + + /* If the mesh contains an actuator disk as a single surface, + we need to first split the surface into repeated points and update + the connectivity for each element touching the surface. */ + if (actuator_disk) SplitActuatorDiskSurface(); + + /* Read and store the points, interior elements, and surface elements. + We store only the points and interior elements on our rank's linear + partition, but the master stores the entire set of surface connectivity. */ + mesh_file = fopen(meshFilename.c_str(), "rb"); + + FastForwardToMyZone(); + ReadVolumeElementConnectivity(); + ReadPointCoordinates(); + ReadSurfaceElementConnectivity(); + + fclose(mesh_file); +} + +CSU2BinaryMeshReaderFVM::~CSU2BinaryMeshReaderFVM() = default; + +void CSU2BinaryMeshReaderFVM::SplitActuatorDiskSurface() { + SU2_MPI::Error("Not implemented yet", CURRENT_FUNCTION); +} diff --git a/Common/src/geometry/meshreader/CSU2MeshReaderBase.cpp b/Common/src/geometry/meshreader/CSU2MeshReaderBase.cpp new file mode 100644 index 00000000000..56818053858 --- /dev/null +++ b/Common/src/geometry/meshreader/CSU2MeshReaderBase.cpp @@ -0,0 +1,37 @@ +/*! + * \file CSU2MeshReaderBase.cpp + * \brief Helper class for the reading of a native SU2 grid file. + * \author T. Economon + * \version 8.2.0 "Harrier" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2025, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ + +#include "../../../include/toolboxes/CLinearPartitioner.hpp" +#include "../../../include/geometry/meshreader/CSU2MeshReaderBase.hpp" + +CSU2MeshReaderBase::CSU2MeshReaderBase(CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone) + : CMeshReaderBase(val_config, val_iZone, val_nZone), + myZone(val_iZone), + nZones(val_nZone), + meshFilename(config->GetMesh_FileName()) {} + +CSU2MeshReaderBase::~CSU2MeshReaderBase(void) = default; diff --git a/Common/src/geometry/meshreader/meson.build b/Common/src/geometry/meshreader/meson.build index 543bdfcf97a..d6c8342960c 100644 --- a/Common/src/geometry/meshreader/meson.build +++ b/Common/src/geometry/meshreader/meson.build @@ -9,4 +9,8 @@ common_src += files(['CBoxMeshReaderFEM.cpp', 'CRectangularMeshReaderFVM.cpp', 'CSU2ASCIIMeshReaderBase.cpp', 'CSU2ASCIIMeshReaderFEM.cpp', - 'CSU2ASCIIMeshReaderFVM.cpp']) + 'CSU2ASCIIMeshReaderFVM.cpp', + 'CSU2BinaryMeshReaderBase.cpp', + 'CSU2BinaryMeshReaderFEM.cpp', + 'CSU2BinaryMeshReaderFVM.cpp', + 'CSU2MeshReaderBase.cpp']) diff --git a/Common/src/grid_movement/CSurfaceMovement.cpp b/Common/src/grid_movement/CSurfaceMovement.cpp index 77f5110540e..b1cc4f8392c 100644 --- a/Common/src/grid_movement/CSurfaceMovement.cpp +++ b/Common/src/grid_movement/CSurfaceMovement.cpp @@ -5046,7 +5046,7 @@ void CSurfaceMovement::WriteFFDInfo(CSurfaceMovement** surface_movement, CGeomet if (rank == MASTER_NODE) { /*--- Read the name of the output file ---*/ - auto str = config[ZONE_0]->GetMesh_Out_FileName() + ".su2"; + auto str = config[ZONE_0]->GetMesh_Out_FileName(); output_file.precision(15); output_file.open(str, ios::out | ios::app); diff --git a/Common/src/toolboxes/SwapBytes.cpp b/Common/src/toolboxes/SwapBytes.cpp new file mode 100644 index 00000000000..52f7dd0e81f --- /dev/null +++ b/Common/src/toolboxes/SwapBytes.cpp @@ -0,0 +1,53 @@ +/*! + * \file SwapBytes.cpp + * \brief Function to swap bytes of primitive data types + * \author P. Gomes + * \version 8.2.0 "Harrier" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2025, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ + +#include "../../include/toolboxes/SwapBytes.hpp" + +/*--- Function to swap bytes, in case we need to convert between + big and little endian storage. ---*/ +void SwapBytes(char* buffer, size_t nBytes, unsigned long nVar) { + /*--- Store half the number of bytes in kk. ---*/ + const int kk = (int)nBytes / 2; + + /*--- Loop over the number of variables in the buffer. ---*/ + for (unsigned long j = 0; j < nVar; j++) { + /*--- Initialize ii and jj, which are used to store the + indices of the bytes to be swapped. ---*/ + unsigned long ii = j * nBytes; + unsigned long jj = ii + nBytes - 1; + + /*--- Swap the bytes. ---*/ + for (int i = 0; i < kk; i++) { + char tmp = buffer[jj]; + buffer[jj] = buffer[ii]; + buffer[ii] = tmp; + + ii++; + jj--; + } + } +} diff --git a/Common/src/toolboxes/meson.build b/Common/src/toolboxes/meson.build index fa5e3b234c3..3240bac986d 100644 --- a/Common/src/toolboxes/meson.build +++ b/Common/src/toolboxes/meson.build @@ -2,7 +2,8 @@ common_src += files(['CLinearPartitioner.cpp', 'printing_toolbox.cpp', 'C1DInterpolation.cpp', 'CSquareMatrixCM.cpp', - 'CSymmetricMatrix.cpp']) + 'CSymmetricMatrix.cpp', + 'SwapBytes.cpp']) subdir('MMS') subdir('fem') diff --git a/SU2_CFD/include/output/filewriter/CParaviewBinaryFileWriter.hpp b/SU2_CFD/include/output/filewriter/CParaviewBinaryFileWriter.hpp index 53617a633d4..507741a0e58 100644 --- a/SU2_CFD/include/output/filewriter/CParaviewBinaryFileWriter.hpp +++ b/SU2_CFD/include/output/filewriter/CParaviewBinaryFileWriter.hpp @@ -60,16 +60,5 @@ class CParaviewBinaryFileWriter final: public CFileWriter{ * \param[in] val_filename - The name of the file */ void WriteData(string val_filename) override ; - -private: - - /*! - * \brief Change storage of buffer from big endian to little endian - * \param buffer - Pointer to the beginning of the buffer - * \param nBytes - The size in bytes of an data entry - * \param nVar - The number of entries - */ - void SwapBytes(char *buffer, size_t nBytes, unsigned long nVar); - }; diff --git a/SU2_CFD/include/output/filewriter/CSU2MeshBinaryFileWriter.hpp b/SU2_CFD/include/output/filewriter/CSU2MeshBinaryFileWriter.hpp new file mode 100644 index 00000000000..64a7e59a83e --- /dev/null +++ b/SU2_CFD/include/output/filewriter/CSU2MeshBinaryFileWriter.hpp @@ -0,0 +1,59 @@ +/*! + * \file CSU2MeshBinaryFileWriter.hpp + * \brief Headers fo the CSV file writer class. + * \author E. van der Weide + * \version 8.4.0 "Harrier" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2026, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ +#pragma once +#include "CFileWriter.hpp" + +class CSU2MeshBinaryFileWriter final: public CFileWriter{ + +private: + unsigned short iZone, //!< Index of the current zone + nZone; //!< Number of zones + +public: + + /*! + * \brief File extension + */ + const static string fileExt; + + /*! + * \brief Construct a file writer using field names, dimension. + * \param[in] valDataSorter - The parallel sorted data to write + * \param[in] valiZone - The index of the current zone + * \param[in] valnZone - The total number of zones + */ + CSU2MeshBinaryFileWriter(CParallelDataSorter* valDataSorter, + unsigned short valiZone, unsigned short valnZone); + + /*! + * \brief Write sorted data to file in SU2 mesh file format + * \param[in] val_filename - The name of the file + */ + void WriteData(string val_filename) override ; + +}; + diff --git a/SU2_CFD/src/meson.build b/SU2_CFD/src/meson.build index 3b40822a34f..d4db53843e9 100644 --- a/SU2_CFD/src/meson.build +++ b/SU2_CFD/src/meson.build @@ -51,6 +51,7 @@ su2_cfd_src += files(['output/COutputFactory.cpp', 'output/filewriter/CParaviewXMLFileWriter.cpp', 'output/filewriter/CParaviewVTMFileWriter.cpp', 'output/filewriter/CSU2MeshFileWriter.cpp', + 'output/filewriter/CSU2MeshBinaryFileWriter.cpp', 'output/filewriter/CCGNSFileWriter.cpp', 'output/tools/CWindowingTools.cpp']) diff --git a/SU2_CFD/src/output/COutput.cpp b/SU2_CFD/src/output/COutput.cpp index 5cc9a1fdd20..e210fbb59c4 100644 --- a/SU2_CFD/src/output/COutput.cpp +++ b/SU2_CFD/src/output/COutput.cpp @@ -49,6 +49,7 @@ #include "../../include/output/filewriter/CSU2FileWriter.hpp" #include "../../include/output/filewriter/CSU2BinaryFileWriter.hpp" #include "../../include/output/filewriter/CSU2MeshFileWriter.hpp" +#include "../../include/output/filewriter/CSU2MeshBinaryFileWriter.hpp" namespace { volatile sig_atomic_t STOP; @@ -475,6 +476,25 @@ void COutput::WriteToFile(CConfig *config, CGeometry *geometry, OUTPUT_TYPE form break; + case OUTPUT_TYPE::MESH_BINARY: + + extension = CSU2MeshBinaryFileWriter::fileExt; + + if (fileName.empty()) + fileName = config->GetFilename(volumeFilename, "", curTimeIter); + + if (!config->GetWrt_Volume_Overwrite()) + filename_iter = config->GetFilename_Iter(fileName, curInnerIter, curOuterIter); + + /*--- Load and sort the output data and connectivity. ---*/ + + volumeDataSorter->SortConnectivity(config, geometry, true); + + LogOutputFiles("SU2 binary mesh"); + fileWriter = new CSU2MeshBinaryFileWriter(volumeDataSorter, config->GetiZone(), config->GetnZone()); + + break; + case OUTPUT_TYPE::TECPLOT_BINARY: extension = CTecplotBinaryFileWriter::fileExt; diff --git a/SU2_CFD/src/output/filewriter/CParaviewBinaryFileWriter.cpp b/SU2_CFD/src/output/filewriter/CParaviewBinaryFileWriter.cpp index 7a9f3378a4a..bde27d26bc6 100644 --- a/SU2_CFD/src/output/filewriter/CParaviewBinaryFileWriter.cpp +++ b/SU2_CFD/src/output/filewriter/CParaviewBinaryFileWriter.cpp @@ -26,6 +26,7 @@ */ #include "../../../include/output/filewriter/CParaviewBinaryFileWriter.hpp" +#include "../../../../Common/include/toolboxes/SwapBytes.hpp" const string CParaviewBinaryFileWriter::fileExt = ".vtk"; @@ -306,37 +307,3 @@ void CParaviewBinaryFileWriter::WriteData(string val_filename){ CloseMPIFile(); } - - -/*--- Subroutine to swap bytes, in case we need to convert to - big endian, which is expected for ParaView binary legacy format. ---*/ - -void CParaviewBinaryFileWriter::SwapBytes(char *buffer, size_t nBytes, unsigned long nVar) { - - /*--- Store half the number of bytes in kk. ---*/ - - const int kk = (int)nBytes/2; - - /*--- Loop over the number of variables in the buffer. ---*/ - - for (int j = 0; j < (int)nVar; j++) { - - /*--- Initialize ii and jj, which are used to store the - indices of the bytes to be swapped. ---*/ - - int ii = j*(int)nBytes; - int jj = ii + (int)nBytes - 1; - - /*--- Swap the bytes. ---*/ - - for (int i = 0; i < kk; i++) { - char tmp = buffer[jj]; - buffer[jj] = buffer[ii]; - buffer[ii] = tmp; - - ii++; - jj--; - - } - } -} diff --git a/SU2_CFD/src/output/filewriter/CSU2MeshBinaryFileWriter.cpp b/SU2_CFD/src/output/filewriter/CSU2MeshBinaryFileWriter.cpp new file mode 100644 index 00000000000..fc508660cba --- /dev/null +++ b/SU2_CFD/src/output/filewriter/CSU2MeshBinaryFileWriter.cpp @@ -0,0 +1,38 @@ +/*! + * \file CSU2MeshBinaryFileWriter.cpp + * \brief Filewriter class SU2 binary mesh format. + * \author E. van der Weide + * \version 8.4.0 "Harrier" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2026, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ +#include "../../../include/output/filewriter/CSU2MeshBinaryFileWriter.hpp" +#include "../../../../Common/include/toolboxes/printing_toolbox.hpp" + +const string CSU2MeshBinaryFileWriter::fileExt = ".su2b"; + +CSU2MeshBinaryFileWriter::CSU2MeshBinaryFileWriter(CParallelDataSorter *valDataSorter, + unsigned short valiZone, unsigned short valnZone) : + CFileWriter(valDataSorter, fileExt), iZone(valiZone), nZone(valnZone) {} + +void CSU2MeshBinaryFileWriter::WriteData(string val_filename) { + SU2_MPI::Error("Function not implemented yet.",CURRENT_FUNCTION); +} \ No newline at end of file diff --git a/SU2_DEF/src/drivers/CDeformationDriver.cpp b/SU2_DEF/src/drivers/CDeformationDriver.cpp index c5573f5d530..0680b1de111 100644 --- a/SU2_DEF/src/drivers/CDeformationDriver.cpp +++ b/SU2_DEF/src/drivers/CDeformationDriver.cpp @@ -495,12 +495,23 @@ void CDeformationDriver::OutputFiles() { geometry_container[iZone][INST_0][MESH_0]->ComputeMeshQualityStatistics(config_container[iZone]); } - /*--- Load the data. --- */ + /*--- Load the data and write the mesh. The format is either + ASCII or binary su2 format. --- */ output_container[iZone]->LoadData(geometry_container[iZone][INST_0][MESH_0], config_container[iZone], nullptr); + OUTPUT_TYPE output_type; + switch( config_container[iZone]->GetMesh_Out_FileFormat() ) { + case ENUM_GRID::SU2: + output_type = OUTPUT_TYPE::MESH; + break; + case ENUM_GRID::SU2_BIN: + output_type = OUTPUT_TYPE::MESH_BINARY; + break; + } + output_container[iZone]->WriteToFile(config_container[iZone], geometry_container[iZone][INST_0][MESH_0], - OUTPUT_TYPE::MESH, driver_config->GetMesh_Out_FileName()); + output_type, driver_config->GetMesh_Out_FileName()); /*--- Set the file names for the visualization files. ---*/ diff --git a/config_template.cfg b/config_template.cfg index b801ef95fd3..0573dc90c7e 100644 --- a/config_template.cfg +++ b/config_template.cfg @@ -2507,7 +2507,7 @@ EXTRA_HEAT_ZONE_OUTPUT= -1 % Mesh input file MESH_FILENAME= mesh_NACA0012_inv % -% Mesh input file format (SU2, CGNS) +% Mesh input file format (SU2, SU2B, CGNS, RECTANGLE, BOX) MESH_FORMAT= SU2 % % List of the number of grid points in the RECTANGLE or BOX grid in the x,y,z directions. (default: (33,33,33) ).