diff --git a/Common/include/geometry/meshreader/CSU2ASCIIMeshReaderBase.hpp b/Common/include/geometry/meshreader/CSU2ASCIIMeshReaderBase.hpp index f693133d5e81..b888c497efdb 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 000000000000..9564c52a2964 --- /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 000000000000..f4d191b7c876 --- /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 000000000000..550b94371913 --- /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 000000000000..8efef93cce51 --- /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 8375668778c4..65dece68c6b9 100644 --- a/Common/include/option_structure.hpp +++ b/Common/include/option_structure.hpp @@ -2128,12 +2128,14 @@ static const MapType Objective_Map = { */ 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. */ + SU2_BIN = 2, /*!< \brief SU2 binary input format. */ + CGNS_GRID = 3, /*!< \brief CGNS input 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("SU2B", SU2_BIN) MakePair("CGNS", CGNS_GRID) MakePair("RECTANGLE", RECTANGLE) MakePair("BOX", BOX) diff --git a/Common/include/toolboxes/SwapBytes.hpp b/Common/include/toolboxes/SwapBytes.hpp new file mode 100644 index 000000000000..6e524115ca20 --- /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 a608f288f7b3..4198c89f0729 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; @@ -639,6 +640,45 @@ unsigned short CConfig::GetnZone(const string& val_mesh_filename, unsigned short } + case SU2_BIN: { + + /*--- 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(string("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(string("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(string("The file ") + val_mesh_filename + + string(" is not a valid SU2 binary file"), CURRENT_FUNCTION); + + /*--- Read the number of zones. ---*/ + ret = fread(&nZone, sizeof(int), 1, mesh_file); + if (ret != 1) + SU2_MPI::Error(string("Error while reading the file ") + val_mesh_filename, + CURRENT_FUNCTION); + if ( swap_bytes) + SwapBytes((char *) &nZone, sizeof(int), 1); + + fclose(mesh_file); + break; + } + case CGNS_GRID: { #ifdef HAVE_CGNS @@ -722,7 +762,7 @@ 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: { @@ -767,6 +807,51 @@ unsigned short CConfig::GetnDim(const string& val_mesh_filename, unsigned short break; } + case SU2_BIN: { + + /*--- 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(string("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(string("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(string("The file ") + val_mesh_filename + + string(" is not a valid SU2 binary file"), CURRENT_FUNCTION); + + /*--- Skip the number of zones and the zone ID. ---*/ + if( fseek(mesh_file, 2*sizeof(int), SEEK_CUR) ) + SU2_MPI::Error(string("Failed to jump forward in the file") + val_mesh_filename, + CURRENT_FUNCTION); + + /*--- Read the number of dimensions. ---*/ + ret = fread(&nDim, sizeof(int), 1, mesh_file); + if (ret != 1) + SU2_MPI::Error(string("Error while reading the file ") + val_mesh_filename, + CURRENT_FUNCTION); + if ( swap_bytes) + SwapBytes((char *) &nDim, sizeof(int), 1); + + fclose(mesh_file); + + break; + } + case CGNS_GRID: { #ifdef HAVE_CGNS diff --git a/Common/src/geometry/CPhysicalGeometry.cpp b/Common/src/geometry/CPhysicalGeometry.cpp index aaa71428ca4a..1f36bd775972 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 489e7e644ba5..d259b9352abf 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 000000000000..fc937a21d500 --- /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 000000000000..c250eca3e70d --- /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 000000000000..11f4af4311a3 --- /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(string("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 000000000000..568180538580 --- /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 543bdfcf97a7..d6c8342960c9 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/toolboxes/SwapBytes.cpp b/Common/src/toolboxes/SwapBytes.cpp new file mode 100644 index 000000000000..52f7dd0e81f5 --- /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 fa5e3b234c34..3240bac986d5 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 9bef9ecc956c..c9bfda8d45c2 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/src/output/filewriter/CParaviewBinaryFileWriter.cpp b/SU2_CFD/src/output/filewriter/CParaviewBinaryFileWriter.cpp index a67b1712d808..bd6fd52028d8 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/config_template.cfg b/config_template.cfg index 04f8aa6d9059..17841102f33b 100644 --- a/config_template.cfg +++ b/config_template.cfg @@ -2403,7 +2403,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) ).