diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp
index 58918bfdc2c..d55416ed681 100644
--- a/Common/include/CConfig.hpp
+++ b/Common/include/CConfig.hpp
@@ -804,6 +804,7 @@ class CConfig {
nRefOriginMoment_Z; /*!< \brief Number of Z-coordinate moment computation origins. */
unsigned short nMesh_Box_Size;
short *Mesh_Box_Size; /*!< \brief Array containing the number of grid points in the x-, y-, and z-directions for the analytic RECTANGLE and BOX grid formats. */
+ unsigned short Mesh_Box_PSolFEM; /*!< \brief FEM polynomial degree of the solution for the RECTANGLE and BOX grid formats. */
string Mesh_FileName, /*!< \brief Mesh input file. */
Mesh_Out_FileName, /*!< \brief Mesh output file. */
Solution_FileName, /*!< \brief Flow solution input file. */
@@ -9606,6 +9607,12 @@ class CConfig {
*/
su2double GetMeshBoxOffset(unsigned short val_iDim) const { return mesh_box_offset[val_iDim]; }
+ /*!
+ * \brief Get the polynomial degree of the FEM solution for the analytic RECTANGLE or BOX.
+ * \return The polynomial degree of the FEM solution.
+ */
+ unsigned short GetMeshBoxPSolFEM(void) const { return Mesh_Box_PSolFEM; }
+
/*!
* \brief Get the number of screen output variables requested (maximum 6)
*/
diff --git a/Common/include/fem/fem_cgns_elements.hpp b/Common/include/fem/fem_cgns_elements.hpp
deleted file mode 100644
index f3debaeb0ea..00000000000
--- a/Common/include/fem/fem_cgns_elements.hpp
+++ /dev/null
@@ -1,198 +0,0 @@
-/*!
- * \file fem_cgns_elements.hpp
- * \brief Headers of the classes and functions for reading CGNS files
- * with high order elements.
- * The functions are in the cgns_elements.cpp file.
- * \author E. van der Weide
- * \version 8.1.0 "Harrier"
- *
- * SU2 Project Website: https://su2code.github.io
- *
- * The SU2 Project is maintained by the SU2 Foundation
- * (http://su2foundation.org)
- *
- * Copyright 2012-2024, 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 "../parallelization/mpi_structure.hpp"
-
-#ifdef HAVE_CGNS
-#include "cgnslib.h"
-#endif
-
-#include "../geometry/primal_grid/CPrimalGridFEM.hpp"
-
-/* Forward declaration of CBoundaryFace to avoid problems. */
-class CBoundaryFace;
-
-#ifdef HAVE_CGNS
-#if CGNS_VERSION >= 3300
-
-/*!
- * \class CCGNSElementType
- * \brief Class which stores the CGNS element type info for a connectivity section.
- */
-
-class CCGNSElementType {
- public:
- int connID; /*!< \brief CGNS connectivity ID of this connectivity. */
- ElementType_t elemType; /*!< \brief Element type according to the CGNS convention,
- possibly MIXED. */
- cgsize_t indBeg; /*!< \brief Index of the first element in the CGNS connectivity. */
- cgsize_t indEnd; /*!< \brief Index of the last element in the CGNS connectivity. */
- cgsize_t nElem; /*!< \brief Number of elements present for this element type. */
-
- std::string connName; /*!< \brief Name of this connectivity. */
-
- bool volumeConn; /*!< \brief Whether or not this is a volume connectivity. */
- bool surfaceConn; /*!< \brief Whether or not this is a surface connectivity. */
-
- /* Standard constructor, nothing to be done. */
- CCGNSElementType() {}
-
- /* Destructor, nothing to be done. */
- ~CCGNSElementType() {}
-
- /*--- Member function, which determines the meta data for this element type. ---*/
- void DetermineMetaData(const unsigned short nDim, const int fn, const int iBase, const int iZone, const int iConn);
-
- /*--- Member function, which reads the required boundary connectivity range. ---*/
- void ReadBoundaryConnectivityRange(const int fn, const int iBase, const int iZone, const unsigned long offsetRank,
- const unsigned long nBoundElemRank, const unsigned long startingBoundElemIDRank,
- unsigned long& locBoundElemCount, std::vector& boundElems);
-
- /*--- Member function, which reads the required connectivity range. ---*/
- void ReadConnectivityRange(const int fn, const int iBase, const int iZone, const unsigned long offsetRank,
- const unsigned long nElemRank, const unsigned long startingElemIDRank, CPrimalGrid**& elem,
- unsigned long& locElemCount, unsigned long& nDOFsLoc);
-
- private:
- /*--- Member function, which creates the required data for the given
- element type. ---*/
- void CreateDataElementType(const ElementType_t typeElem, unsigned short& VTK_Type, unsigned short& nPoly,
- unsigned short& nDOFs, std::vector& SU2ToCGNS);
-
- /*--- Member function, which determines the element dimension, i.e. the
- number of parametric coordinates. ---*/
- unsigned short DetermineElementDimension(const int fn, const int iBase, const int iZone);
-
- /*--- Member function, which determines the element dimension when the
- connectivity is mixed. ---*/
- unsigned short DetermineElementDimensionMixed(const int fn, const int iBase, const int iZone);
-
- /*--- Member function, which determines the corresponding index of the
- given element in the stored types. If not present, a new index
- is created. ---*/
- unsigned short IndexInStoredTypes(const ElementType_t typeElem, std::vector& CGNS_Type,
- std::vector& VTK_Type, std::vector& nPoly,
- std::vector& nDOFs,
- std::vector >& SU2ToCGNS);
-
- /*--- Functions to create the conversion data from CGNS format to SU2 format
- for all the supported CGNS elements. ---*/
- void CreateDataNODE(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataBAR_2(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataBAR_3(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataBAR_4(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataBAR_5(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataTRI_3(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataTRI_6(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataTRI_10(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataTRI_15(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataQUAD_4(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataQUAD_9(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataQUAD_16(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataQUAD_25(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataTETRA_4(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataTETRA_10(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataTETRA_20(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataTETRA_35(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataPYRA_5(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataPYRA_14(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataPYRA_30(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataPYRA_55(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataPENTA_6(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataPENTA_18(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataPENTA_40(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataPENTA_75(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataHEXA_8(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataHEXA_27(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataHEXA_64(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-
- void CreateDataHEXA_125(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
- std::vector& SU2ToCGNS);
-};
-#endif
-#endif
-
-using namespace std;
diff --git a/Common/include/fem/fem_geometry_structure.hpp b/Common/include/fem/fem_geometry_structure.hpp
index 4d3e9cc367d..ab4cc457f40 100644
--- a/Common/include/fem/fem_geometry_structure.hpp
+++ b/Common/include/fem/fem_geometry_structure.hpp
@@ -30,9 +30,6 @@
#include "../geometry/CGeometry.hpp"
#include "fem_standard_element.hpp"
-#ifdef HAVE_CGNS
-#include "fem_cgns_elements.hpp"
-#endif
#include "../wall_model.hpp"
#include "../linear_algebra/blas_structure.hpp"
diff --git a/Common/include/geometry/CPhysicalGeometry.hpp b/Common/include/geometry/CPhysicalGeometry.hpp
index 5671ceae247..e6a871bb977 100644
--- a/Common/include/geometry/CPhysicalGeometry.hpp
+++ b/Common/include/geometry/CPhysicalGeometry.hpp
@@ -28,7 +28,7 @@
#pragma once
#include "CGeometry.hpp"
-#include "meshreader/CMeshReaderFVM.hpp"
+#include "meshreader/CMeshReaderBase.hpp"
#include "../containers/C2DContainer.hpp"
/*!
@@ -285,51 +285,49 @@ class CPhysicalGeometry final : public CGeometry {
* \param[in] val_iZone - Domain to be read from the grid file.
* \param[in] val_nZone - Total number of domains in the grid file.
*/
- void Read_Mesh_FVM(CConfig* config, const string& val_mesh_filename, unsigned short val_iZone,
- unsigned short val_nZone);
+ void Read_Mesh(CConfig* config, const string& val_mesh_filename, unsigned short val_iZone, unsigned short val_nZone);
/*!
- * \brief Reads for the FEM solver the geometry of the grid and adjust the boundary
- * conditions with the configuration file in parallel (for parmetis).
- * \param[in] config - Definition of the particular problem.
- * \param[in] val_mesh_filename - Name of the file with the grid information.
- * \param[in] val_iZone - Domain to be read from the grid file.
- * \param[in] val_nZone - Total number of domains in the grid file.
- */
- void Read_SU2_Format_Parallel_FEM(CConfig* config, const string& val_mesh_filename, unsigned short val_iZone,
- unsigned short val_nZone);
-
- /*!
- * \brief Reads for the FEM solver the geometry of the grid and adjust the boundary
- * conditions with the configuration file in parallel (for parmetis).
- * \param[in] config - Definition of the particular problem.
- * \param[in] val_mesh_filename - Name of the file with the grid information.
- * \param[in] val_iZone - Domain to be read from the grid file.
- * \param[in] val_nZone - Total number of domains in the grid file.
+ * \brief Routine to load the CGNS grid points from a single zone into the proper SU2 data structures.
+ * \param[in] config - definition of the particular problem.
+ * \param[in] mesh - mesh reader object containing the current zone data.
*/
- void Read_CGNS_Format_Parallel_FEM(CConfig* config, const string& val_mesh_filename, unsigned short val_iZone,
- unsigned short val_nZone);
+ void LoadLinearlyPartitionedPoints(CConfig* config, CMeshReaderBase* mesh);
/*!
- * \brief Routine to load the CGNS grid points from a single zone into the proper SU2 data structures.
+ * \brief Routine to load the grid points from a single zone into the proper SU2 data structures for the FEM solver.
* \param[in] config - definition of the particular problem.
* \param[in] mesh - mesh reader object containing the current zone data.
*/
- void LoadLinearlyPartitionedPoints(CConfig* config, CMeshReaderFVM* mesh);
+ void LoadLinearlyPartitionedPointsFEM(CConfig* config, CMeshReaderBase* mesh);
/*!
* \brief Loads the interior volume elements from the mesh reader object into the primal element data structures.
* \param[in] config - definition of the particular problem.
* \param[in] mesh - mesh reader object containing the current zone data.
*/
- void LoadLinearlyPartitionedVolumeElements(CConfig* config, CMeshReaderFVM* mesh);
+ void LoadLinearlyPartitionedVolumeElements(CConfig* config, CMeshReaderBase* mesh);
+
+ /*!
+ * \brief Loads the interior volume elements from the mesh reader object into the primal element data structures for
+ * the FEM solver. \param[in] config - definition of the particular problem. \param[in] mesh - mesh reader object
+ * containing the current zone data.
+ */
+ void LoadLinearlyPartitionedVolumeElementsFEM(CConfig* config, CMeshReaderBase* mesh);
/*!
* \brief Loads the boundary elements (markers) from the mesh reader object into the primal element data structures.
* \param[in] config - definition of the particular problem.
* \param[in] mesh - mesh reader object containing the current zone data.
*/
- void LoadUnpartitionedSurfaceElements(CConfig* config, CMeshReaderFVM* mesh);
+ void LoadUnpartitionedSurfaceElements(CConfig* config, CMeshReaderBase* mesh);
+
+ /*!
+ * \brief Loads the boundary elements (markers) from the mesh reader object into the primal element data structures
+ * for the FEM solver. \param[in] config - definition of the particular problem. \param[in] mesh - mesh reader
+ * object containing the current zone data.
+ */
+ void LoadLinearlyPartitionedSurfaceElementsFEM(CConfig* config, CMeshReaderBase* mesh);
/*!
* \brief Prepares the grid point adjacency based on a linearly partitioned mesh object needed by ParMETIS for graph
diff --git a/Common/include/geometry/meshreader/CBoxMeshReaderFEM.hpp b/Common/include/geometry/meshreader/CBoxMeshReaderFEM.hpp
new file mode 100644
index 00000000000..f5a0e53934b
--- /dev/null
+++ b/Common/include/geometry/meshreader/CBoxMeshReaderFEM.hpp
@@ -0,0 +1,82 @@
+/*!
+ * \file CBoxMeshReaderFEM.hpp
+ * \brief Header file for the class CBoxMeshReaderFEM.
+ * The implementations are in the CBoxMeshReaderFEM.cpp file.
+ * \author T. Economon
+ * \version 8.1.0 "Harrier"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2024, 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 "CMeshReaderBase.hpp"
+
+/*!
+ * \class CBoxMeshReaderFEM
+ * \brief Reads a 3D box grid into linear partitions for the finite element solver (FEM).
+ * \author: T. Economon, E. van der Weide
+ */
+class CBoxMeshReaderFEM final : public CMeshReaderBase {
+ private:
+ unsigned long nNode; /*!< \brief Number of grid nodes in the x-direction. */
+ unsigned long mNode; /*!< \brief Number of grid nodes in the y-direction. */
+ unsigned long pNode; /*!< \brief Number of grid nodes in the z-direction. */
+
+ su2double Lx; /*!< \brief Length of the domain in the x-direction. */
+ su2double Ly; /*!< \brief Length of the domain in the y-direction. */
+ su2double Lz; /*!< \brief Length of the domain in the z-direction. */
+
+ su2double Ox; /*!< \brief Offset of the domain from 0.0 in the x-direction. */
+ su2double Oy; /*!< \brief Offset of the domain from 0.0 in the y-direction. */
+ su2double Oz; /*!< \brief Offset of the domain from 0.0 in the z-direction. */
+
+ unsigned short KindElem; /*!< \brief VTK identifier of the interior elements. */
+ unsigned short KindBound; /*!< \brief VTK identifier of the surface elements. */
+
+ unsigned short nPolySol; /*!< \brief Polynomial degree of the solution. */
+
+ /*!
+ * \brief Computes and stores the grid points based on an analytic definition of a box grid.
+ */
+ void ComputeBoxPointCoordinates();
+
+ /*!
+ * \brief Computes and stores the volume element connectivity based on an analytic definition of a box grid.
+ */
+ void ComputeBoxVolumeConnectivity();
+
+ /*!
+ * \brief Computes and stores the surface element connectivity based on an analytic definition of a box grid.
+ */
+ void ComputeBoxSurfaceConnectivity();
+
+ public:
+ /*!
+ * \brief Constructor of the CBoxMeshReaderFEM class.
+ */
+ CBoxMeshReaderFEM(const CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone);
+
+ /*!
+ * \brief Destructor of the CBoxMeshReaderFEM class.
+ */
+ ~CBoxMeshReaderFEM(void) override;
+};
diff --git a/Common/include/geometry/meshreader/CBoxMeshReaderFVM.hpp b/Common/include/geometry/meshreader/CBoxMeshReaderFVM.hpp
index 4c97369ec35..a557630e7c8 100644
--- a/Common/include/geometry/meshreader/CBoxMeshReaderFVM.hpp
+++ b/Common/include/geometry/meshreader/CBoxMeshReaderFVM.hpp
@@ -28,14 +28,14 @@
#pragma once
-#include "CMeshReaderFVM.hpp"
+#include "CMeshReaderBase.hpp"
/*!
* \class CBoxMeshReaderFVM
* \brief Reads a 3D box grid into linear partitions for the finite volume solver (FVM).
* \author: T. Economon
*/
-class CBoxMeshReaderFVM : public CMeshReaderFVM {
+class CBoxMeshReaderFVM : public CMeshReaderBase {
private:
unsigned long nNode; /*!< \brief Number of grid nodes in the x-direction. */
unsigned long mNode; /*!< \brief Number of grid nodes in the y-direction. */
@@ -71,7 +71,7 @@ class CBoxMeshReaderFVM : public CMeshReaderFVM {
/*!
* \brief Constructor of the CBoxMeshReaderFVM class.
*/
- CBoxMeshReaderFVM(CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone);
+ CBoxMeshReaderFVM(const CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone);
/*!
* \brief Destructor of the CBoxMeshReaderFVM class.
diff --git a/Common/include/geometry/meshreader/CCGNSElementType.hpp b/Common/include/geometry/meshreader/CCGNSElementType.hpp
new file mode 100644
index 00000000000..fbe944f0114
--- /dev/null
+++ b/Common/include/geometry/meshreader/CCGNSElementType.hpp
@@ -0,0 +1,362 @@
+/*!
+ * \file CCGNSElementType.hpp
+ * \brief Header file for the class CCGNSElementType.
+ * The implementations are in the CCGNSElementType.cpp file.
+ * \author E. van der Weide
+ * \version 8.1.0 "Harrier"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2024, 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
+
+#ifdef HAVE_CGNS
+#include "cgnslib.h"
+#endif
+
+#ifdef HAVE_CGNS
+
+using namespace std;
+
+/*!
+ * \class CCGNSElementType
+ * \brief Class used to convert the CGNS format to SU2 format for high order elements.
+ * \author: E. van der Weide
+ */
+class CCGNSElementType {
+ private:
+ vector CGNSTypeStored; /*!< \brief CGNS element types for which the data is stored. */
+ vector VTKTypeStored; /*!< \brief VTK type of the element. */
+ vector nPolyStored; /*!< \brief Polynomial degree of the element. */
+ vector nDOFsStored; /*!< \brief Number of DOFs of the element. */
+ vector > SU2ToCGNSStored; /*!< \brief Double vector, which stores the conversion
+ from SU2 to CGNS for the type in local numbering. */
+ public:
+ /*--- Standard constructor, nothing to be done. ---*/
+ CCGNSElementType() = default;
+
+ /*--- Destructor, nothing to be done. ---*/
+ ~CCGNSElementType() = default;
+
+ /*!
+ * \brief Converts the connectivity information from CGNS to SU2 format.
+ * \param[in] val_elemType - CGNS elements type to be converted.
+ * \param[in] val_globalID - Global ID of this element.
+ * \param[in] connCGNS - Array with the connectivity of the element in CGNS format.
+ * \param[out] connSU2 - Vector with the connectivity and meta data in SU2 format.
+ */
+ void CGNSToSU2(const ElementType_t val_elemType, const unsigned long val_globalID, const cgsize_t* connCGNS,
+ std::vector& connSU2);
+
+ private:
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a node.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataNODE(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Bar2 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataBAR_2(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Bar3 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataBAR_3(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Bar4 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataBAR_4(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Bar5 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataBAR_5(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Tri3 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataTRI_3(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Tri6 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataTRI_6(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Tri10 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataTRI_10(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Tri15 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataTRI_15(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Quad4 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataQUAD_4(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Quad9 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataQUAD_9(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Quad16 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataQUAD_16(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Quad25 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataQUAD_25(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Tetra4 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataTETRA_4(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Tetra10 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataTETRA_10(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Tetra20 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataTETRA_20(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Tetra35 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataTETRA_35(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Pyra5 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataPYRA_5(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Pyra14 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataPYRA_14(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Pyra30 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataPYRA_30(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Pyra55 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataPYRA_55(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Penta6 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataPENTA_6(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Penta18 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataPENTA_18(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Penta40 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataPENTA_40(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Penta75 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataPENTA_75(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Hexa8 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataHEXA_8(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Hexa27 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataHEXA_27(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Hexa64 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataHEXA_64(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+
+ /*!
+ * \brief Converts the connectivity from CGNS to SU2 for a Hexa125 element.
+ * \param[out] VTK_Type - Corresponding VTK type
+ * \param[out] nPoly - Polynomial degree
+ * \param[out] nDOFs - Number of DOFs of the element.
+ * \param[out] SU2ToCGNS - Vector containing the mapping from SU2 to CGNS.
+ */
+ void CreateDataHEXA_125(unsigned short& VTK_Type, unsigned short& nPoly, unsigned short& nDOFs,
+ vector& SU2ToCGNS);
+};
+#endif
diff --git a/Common/include/geometry/meshreader/CCGNSMeshReaderBase.hpp b/Common/include/geometry/meshreader/CCGNSMeshReaderBase.hpp
new file mode 100644
index 00000000000..01574423fcc
--- /dev/null
+++ b/Common/include/geometry/meshreader/CCGNSMeshReaderBase.hpp
@@ -0,0 +1,108 @@
+/*!
+ * \file CCGNSMeshReaderBase.hpp
+ * \brief Header file for the class CCGNSMeshReaderBase.
+ * The implementations are in the CCGNSMeshReaderBase.cpp file.
+ * \author T. Economon
+ * \version 8.1.0 "Harrier"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2024, 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
+
+#ifdef HAVE_CGNS
+#include "cgnslib.h"
+#endif
+
+#include "CMeshReaderBase.hpp"
+
+/*!
+ * \class CCGNSMeshReaderBase
+ * \brief Base class for the reading of a CGNS zone.
+ * \author: T. Economon
+ */
+class CCGNSMeshReaderBase : public CMeshReaderBase {
+ protected:
+#ifdef HAVE_CGNS
+ int cgnsFileID; /*!< \brief CGNS file identifier. */
+ const int cgnsBase = 1; /*!< \brief CGNS database index (the CGNS reader currently assumes a single database). */
+ const int cgnsZone = 1; /*!< \brief CGNS zone index (and 1 zone in that database). */
+
+ int nSections; /*!< \brief Total number of sections in the CGNS file. */
+
+ vector isInterior; /*!< \brief Vector of booleans to store whether each section in the CGNS file is an interior
+ or boundary section. */
+ vector
+ nElems; /*!< \brief Vector containing the local number of elements found within each CGNS section. */
+ vector elemOffset; /*!< \brief Global ID offset for each interior section (i.e., the total number of
+ global elements that came before it). */
+ vector > connElems; /*!< \brief Vector containing the local element connectivity found within each
+ CGNS section. First index is the section, second contains the connectivity in
+ format [globalID VTK n1 n2 n3 n4 n5 n6 n7 n8] for each element. */
+ vector > sectionNames; /*!< \brief Vector for storing the names of each boundary section (marker). */
+
+ /*!
+ * \brief Open the CGNS file and checks for errors.
+ * \param[in] val_filename - string name of the CGNS file to be read.
+ */
+ void OpenCGNSFile(const string& val_filename);
+
+ /*!
+ * \brief Reads all CGNS database metadata and checks for errors.
+ */
+ void ReadCGNSDatabaseMetadata();
+
+ /*!
+ * \brief Reads all CGNS zone metadata and checks for errors.
+ */
+ void ReadCGNSZoneMetadata();
+
+ /*!
+ * \brief Reads the grid points from a CGNS zone into linear partitions across all ranks.
+ */
+ void ReadCGNSPointCoordinates();
+
+ /*!
+ * \brief Reads the metadata for each CGNS section in a zone and collect information, including the size and whether
+ * it is an interior or boundary section.
+ */
+ void ReadCGNSSectionMetadata();
+
+ /*!
+ * \brief Get the VTK type and string name for a CGNS element type.
+ * \param[in] val_elem_type - CGNS element type.
+ * \param[out] val_vtk_type - VTK type identifier index.
+ * \returns String containing the name of the element type.
+ */
+ string GetCGNSElementType(ElementType_t val_elem_type, int& val_vtk_type);
+#endif
+
+ public:
+ /*!
+ * \brief Constructor of the CCGNSMeshReaderBase class.
+ */
+ CCGNSMeshReaderBase(const CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone);
+
+ /*!
+ * \brief Destructor of the CCGNSMeshReaderBase class.
+ */
+ virtual ~CCGNSMeshReaderBase(void) override;
+};
diff --git a/Common/include/geometry/meshreader/CCGNSMeshReaderFEM.hpp b/Common/include/geometry/meshreader/CCGNSMeshReaderFEM.hpp
new file mode 100644
index 00000000000..cb7fcd404e8
--- /dev/null
+++ b/Common/include/geometry/meshreader/CCGNSMeshReaderFEM.hpp
@@ -0,0 +1,92 @@
+/*!
+ * \file CCGNSMeshReaderFEM.hpp
+ * \brief Header file for the class CCGNSMeshReaderFEM.
+ * The implementations are in the CCGNSMeshReaderFEM.cpp file.
+ * \author T. Economon
+ * \version 8.1.0 "Harrier"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2024, 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 "CCGNSMeshReaderBase.hpp"
+
+/*!
+ * \class CCGNSMeshReaderFEM
+ * \brief Reads a CGNS zone into linear partitions for the finite element solver (FEM).
+ * \author: T. Economon
+ */
+class CCGNSMeshReaderFEM final : public CCGNSMeshReaderBase {
+ private:
+ /*!
+ * \brief Communicates the grid points to the MPI rank where they are needed.
+ */
+ void CommPointCoordinates();
+
+#ifdef HAVE_CGNS
+
+ /*!
+ * \brief Reads the connectivity range from a CGNS section and convert it to the internal format.
+ * \param[in] val_section - CGNS section index.
+ * \param[in] val_firstIndex - Global index of the first element to be stored on this rank.
+ * \param[in] val_lastIndex - Global index of the last element (not included) to be stored on this rank.
+ * \param[inout] elemCount - Counter, which keeps track how many global elements are stored.
+ * \param[inout] localElemCount - Counter, which keeps track how many local elements are stored.
+ * \param[inout] localConn - Vector where the connectivity must be stored.
+ */
+ void ReadCGNSConnectivityRangeSection(const int val_section, const unsigned long val_firstIndex,
+ const unsigned long val_lastIndex, unsigned long& elemCount,
+ unsigned long& localElemCount, vector& localConn);
+
+ /*!
+ * \brief Reads the interior volume elements from one section of a CGNS zone into linear partitions across all ranks.
+ */
+ void ReadCGNSVolumeElementConnectivity();
+
+ /*!
+ * \brief Reads the surface (boundary) elements from one section of a CGNS zone into linear partitions across all
+ * ranks.
+ */
+ void ReadCGNSSurfaceElementConnectivity();
+
+ /*!
+ * \brief Reads the connectivity from a CGNS surface section and select the relevant faces.
+ * \param[in] val_section - CGNS section index.
+ * \param[in] localFaces - The faces of the locally stored volume elements.
+ * \param[out] nSurfElem - Number of local surface elements stored for this surface section.
+ * \param[out] surfConn - Vector to store the connectivity of the surface elements to be stored.
+ */
+ void ReadCGNSSurfaceSection(const int val_section, const vector& localFaces, unsigned long& nSurfElem,
+ vector& surfConn);
+#endif
+
+ public:
+ /*!
+ * \brief Constructor of the CCGNSMeshReaderFEM class.
+ */
+ CCGNSMeshReaderFEM(const CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone);
+
+ /*!
+ * \brief Destructor of the CCGNSMeshReaderFEM class.
+ */
+ ~CCGNSMeshReaderFEM(void) override;
+};
diff --git a/Common/include/geometry/meshreader/CCGNSMeshReaderFVM.hpp b/Common/include/geometry/meshreader/CCGNSMeshReaderFVM.hpp
index 6199cb540d5..fe8612b3bbb 100644
--- a/Common/include/geometry/meshreader/CCGNSMeshReaderFVM.hpp
+++ b/Common/include/geometry/meshreader/CCGNSMeshReaderFVM.hpp
@@ -28,64 +28,16 @@
#pragma once
-#ifdef HAVE_CGNS
-#include "cgnslib.h"
-#endif
-
-#include "CMeshReaderFVM.hpp"
+#include "CCGNSMeshReaderBase.hpp"
/*!
* \class CCGNSMeshReaderFVM
* \brief Reads a CGNS zone into linear partitions for the finite volume solver (FVM).
* \author: T. Economon
*/
-class CCGNSMeshReaderFVM : public CMeshReaderFVM {
+class CCGNSMeshReaderFVM final : public CCGNSMeshReaderBase {
private:
#ifdef HAVE_CGNS
- int cgnsFileID; /*!< \brief CGNS file identifier. */
- const int cgnsBase = 1; /*!< \brief CGNS database index (the CGNS reader currently assumes a single database). */
- const int cgnsZone = 1; /*!< \brief CGNS zone index (and 1 zone in that database). */
-
- int nSections; /*!< \brief Total number of sections in the CGNS file. */
-
- vector isInterior; /*!< \brief Vector of booleans to store whether each section in the CGNS file is an interior
- or boundary section. */
- vector
- nElems; /*!< \brief Vector containing the local number of elements found within each CGNS section. */
- vector elemOffset; /*!< \brief Global ID offset for each interior section (i.e., the total number of
- global elements that came before it). */
- vector > connElems; /*!< \brief Vector containing the local element connectivity found within each
- CGNS section. First index is the section, second contains the connectivity in
- format [globalID VTK n1 n2 n3 n4 n5 n6 n7 n8] for each element. */
- vector > sectionNames; /*!< \brief Vector for storing the names of each boundary section (marker). */
-
- /*!
- * \brief Open the CGNS file and checks for errors.
- * \param[in] val_filename - string name of the CGNS file to be read.
- */
- void OpenCGNSFile(const string& val_filename);
-
- /*!
- * \brief Reads all CGNS database metadata and checks for errors.
- */
- void ReadCGNSDatabaseMetadata();
-
- /*!
- * \brief Reads all CGNS zone metadata and checks for errors.
- */
- void ReadCGNSZoneMetadata();
-
- /*!
- * \brief Reads the grid points from a CGNS zone into linear partitions across all ranks.
- */
- void ReadCGNSPointCoordinates();
-
- /*!
- * \brief Reads the metadata for each CGNS section in a zone and collect information, including the size and whether
- * it is an interior or boundary section.
- */
- void ReadCGNSSectionMetadata();
-
/*!
* \brief Reads the interior volume elements from one section of a CGNS zone into linear partitions across all ranks.
* \param[in] val_section - CGNS section index.
@@ -108,13 +60,6 @@ class CCGNSMeshReaderFVM : public CMeshReaderFVM {
*/
void ReformatCGNSSurfaceConnectivity();
- /*!
- * \brief Get the VTK type and string name for a CGNS element type.
- * \param[in] val_elem_type - CGNS element type.
- * \param[out] val_vtk_type - VTK type identifier index.
- * \returns String containing the name of the element type.
- */
- string GetCGNSElementType(ElementType_t val_elem_type, int& val_vtk_type);
#endif
/*!
@@ -146,7 +91,7 @@ class CCGNSMeshReaderFVM : public CMeshReaderFVM {
/*!
* \brief Constructor of the CCGNSMeshReaderFVM class.
*/
- CCGNSMeshReaderFVM(CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone);
+ CCGNSMeshReaderFVM(const CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone);
/*!
* \brief Destructor of the CCGNSMeshReaderFVM class.
diff --git a/Common/include/geometry/meshreader/CMeshReaderFVM.hpp b/Common/include/geometry/meshreader/CMeshReaderBase.hpp
similarity index 71%
rename from Common/include/geometry/meshreader/CMeshReaderFVM.hpp
rename to Common/include/geometry/meshreader/CMeshReaderBase.hpp
index 7980acf39fd..622b48db4c8 100644
--- a/Common/include/geometry/meshreader/CMeshReaderFVM.hpp
+++ b/Common/include/geometry/meshreader/CMeshReaderBase.hpp
@@ -1,8 +1,8 @@
/*!
- * \file CMeshReaderFVM.hpp
- * \brief Header file for the class CMeshReaderFVM.
- * The implementations are in the CMeshReaderFVM.cpp file.
+ * \file CMeshReaderBase.hpp
+ * \brief Header file for the class CMeshReaderBase.
+ * The implementations are in the CMeshReaderBase.cpp file.
* \author T. Economon
* \version 8.1.0 "Harrier"
*
@@ -31,15 +31,17 @@
#include
+#include "../primal_grid/CPrimalGridFEM.hpp"
+#include "../../fem/geometry_structure_fem_part.hpp"
#include "../../parallelization/mpi_structure.hpp"
#include "../../CConfig.hpp"
/*!
- * \class CMeshReaderFVM
- * \brief Base class for the mesh zone readers of the finite volume solver (FVM).
+ * \class CMeshReaderBase
+ * \brief Base class for the mesh zone readers.
* \author T. Economon
*/
-class CMeshReaderFVM {
+class CMeshReaderBase {
protected:
const int rank; /*!< \brief MPI Rank. */
const int size; /*!< \brief MPI Size. */
@@ -54,6 +56,7 @@ class CMeshReaderFVM {
vector >
localPointCoordinates; /*!< \brief Vector holding the coordinates from the mesh file for the local grid points.
First index is dimension, second is point index. */
+ vector globalPointIDs; /*!< \brief Vector holding the global IDs of the local grid points. */
unsigned long numberOfLocalElements =
0; /*!< \brief Number of local elements within the linear partition on this rank. */
@@ -63,20 +66,39 @@ class CMeshReaderFVM {
unsigned long numberOfMarkers = 0; /*!< \brief Total number of markers contained within the mesh file. */
vector markerNames; /*!< \brief String names for all markers in the mesh file. */
+ vector
+ numberOfLocalSurfaceElements; /*!< \brief Vector containing the number of local surface elements. */
vector >
surfaceElementConnectivity; /*!< \brief Vector containing the surface element connectivity from the mesh file on a
- per-marker basis. Only the master node reads and stores this connectivity. */
+ per-marker basis. For FVM, only the master node reads and stores this connectivity.
+ */
+
+ /*!
+ * \brief Function, which determines the faces of the local volume elements.
+ * \param[out] localFaces - The faces of the locally stored volume elements.
+ */
+ void DetermineFacesVolumeElements(vector& localFaces);
+
+ /*!
+ * \brief Get all the corner points of all the faces of the given element. It must
+ * \param[in] elemInfo - Array, which contains the info of the given element.
+ * \param[out] nFaces - Number of faces of the element.
+ * \param[out] nPointsPerFace - Number of corner points for each of the faces.
+ * \param[out] faceConn - Global IDs of the corner points of the faces.
+ */
+ void GetCornerPointsAllFaces(const unsigned long* elemInfo, unsigned short& numFaces, unsigned short nPointsPerFace[],
+ unsigned long faceConn[6][4]);
public:
/*!
- * \brief Constructor of the CMeshReaderFVM class.
+ * \brief Constructor of the CMeshReaderBase class.
* \param[in] val_config - config object for the current zone.
* \param[in] val_iZone - Current zone index.
* \param[in] val_nZone - Total number of zones.
*/
- CMeshReaderFVM(const CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone);
+ CMeshReaderBase(const CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone);
- virtual ~CMeshReaderFVM() = default;
+ virtual ~CMeshReaderBase() = default;
/*!
* \brief Get the physical dimension of the problem (2 or 3).
@@ -84,6 +106,12 @@ class CMeshReaderFVM {
*/
inline unsigned short GetDimension() const { return dimension; }
+ /*!
+ * \brief Get the global IDs of the local points.
+ * \returns Reference to the vector containing the global points IDs.
+ */
+ inline const vector& GetGlobalPointIDs() const { return globalPointIDs; }
+
/*!
* \brief Get the local point coordinates (linearly partitioned).
* \returns Local point coordinates (linear partitioned).
@@ -99,6 +127,14 @@ class CMeshReaderFVM {
return surfaceElementConnectivity[val_iMarker];
}
+ /*!
+ * \brief Get the number surface elements for all markers.
+ * \returns Reference to the vector containing the number of surface elements for all markers.
+ */
+ inline const vector& GetNumberOfSurfaceElementsAllMarkers() const {
+ return numberOfLocalSurfaceElements;
+ }
+
/*!
* \brief Get the number surface elements for the specified marker.
* \param[in] val_iMarker - current marker index.
diff --git a/Common/include/geometry/meshreader/CRectangularMeshReaderFEM.hpp b/Common/include/geometry/meshreader/CRectangularMeshReaderFEM.hpp
new file mode 100644
index 00000000000..cc4745659a9
--- /dev/null
+++ b/Common/include/geometry/meshreader/CRectangularMeshReaderFEM.hpp
@@ -0,0 +1,79 @@
+/*!
+ * \file CRectangularMeshReaderFEM.hpp
+ * \brief Header file for the class CRectangularMeshReaderFEM.
+ * The implementations are in the CRectangularMeshReaderFEM.cpp file.
+ * \author T. Economon
+ * \version 8.1.0 "Harrier"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2024, 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 "CMeshReaderBase.hpp"
+
+/*!
+ * \class CRectangularMeshReaderFEM
+ * \brief Reads a 2D rectangular grid into linear partitions for the finite element solver (FEM).
+ * \author: T. Economon, E. van der Weide
+ */
+class CRectangularMeshReaderFEM : public CMeshReaderBase {
+ private:
+ unsigned long nNode; /*!< \brief Number of grid nodes in the x-direction. */
+ unsigned long mNode; /*!< \brief Number of grid nodes in the y-direction. */
+
+ su2double Lx; /*!< \brief Length of the domain in the x-direction. */
+ su2double Ly; /*!< \brief Length of the domain in the y-direction. */
+
+ su2double Ox; /*!< \brief Offset of the domain from 0.0 in the x-direction. */
+ su2double Oy; /*!< \brief Offset of the domain from 0.0 in the y-direction. */
+
+ unsigned short KindElem; /*!< \brief VTK identifier of the interior elements. */
+ unsigned short KindBound; /*!< \brief VTK identifier of the surface elements. */
+
+ unsigned short nPolySol; /*!< \brief Polynomial degree of the solution. */
+
+ /*!
+ * \brief Computes and stores the grid points based on an analytic definition of a rectangular grid.
+ */
+ void ComputeRectangularPointCoordinates();
+
+ /*!
+ * \brief Computes and stores the volume element connectivity based on an analytic definition of a rectangular grid.
+ */
+ void ComputeRectangularVolumeConnectivity();
+
+ /*!
+ * \brief Computes and stores the surface element connectivity based on an analytic definition of a rectangular grid.
+ */
+ void ComputeRectangularSurfaceConnectivity();
+
+ public:
+ /*!
+ * \brief Constructor of the CRectangularMeshReaderFEM class.
+ */
+ CRectangularMeshReaderFEM(const CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone);
+
+ /*!
+ * \brief Destructor of the CRectangularMeshReaderFEM class.
+ */
+ ~CRectangularMeshReaderFEM(void) override;
+};
diff --git a/Common/include/geometry/meshreader/CRectangularMeshReaderFVM.hpp b/Common/include/geometry/meshreader/CRectangularMeshReaderFVM.hpp
index 54f393b88d8..30f28354d12 100644
--- a/Common/include/geometry/meshreader/CRectangularMeshReaderFVM.hpp
+++ b/Common/include/geometry/meshreader/CRectangularMeshReaderFVM.hpp
@@ -28,14 +28,14 @@
#pragma once
-#include "CMeshReaderFVM.hpp"
+#include "CMeshReaderBase.hpp"
/*!
* \class CRectangularMeshReaderFVM
* \brief Reads a 2D rectangular grid into linear partitions for the finite volume solver (FVM).
* \author: T. Economon
*/
-class CRectangularMeshReaderFVM : public CMeshReaderFVM {
+class CRectangularMeshReaderFVM : public CMeshReaderBase {
private:
unsigned long nNode; /*!< \brief Number of grid nodes in the x-direction. */
unsigned long mNode; /*!< \brief Number of grid nodes in the y-direction. */
@@ -69,4 +69,9 @@ class CRectangularMeshReaderFVM : public CMeshReaderFVM {
* \brief Constructor of the CRectangularMeshReaderFVM class.
*/
CRectangularMeshReaderFVM(const CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone);
+
+ /*!
+ * \brief Destructor of the CRectangularMeshReaderFVM class.
+ */
+ ~CRectangularMeshReaderFVM(void) override;
};
diff --git a/Common/include/geometry/meshreader/CSU2ASCIIMeshReaderBase.hpp b/Common/include/geometry/meshreader/CSU2ASCIIMeshReaderBase.hpp
new file mode 100644
index 00000000000..66ecbf97095
--- /dev/null
+++ b/Common/include/geometry/meshreader/CSU2ASCIIMeshReaderBase.hpp
@@ -0,0 +1,116 @@
+/*!
+ * \file CSU2ASCIIMeshReaderBase.hpp
+ * \brief Header file for the class CSU2ASCIIMeshReaderBase.
+ * The implementations are in the CSU2ASCIIMeshReaderBase.cpp file.
+ * \author T. Economon
+ * \version 8.1.0 "Harrier"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2024, 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 CSU2ASCIIMeshReaderBase
+ * \brief Base class for the reading of a native SU2 ASCII grid.
+ * \author T. Economon
+ */
+class CSU2ASCIIMeshReaderBase : public CMeshReaderBase {
+ 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. */
+
+ /*!
+ * \brief Reads all SU2 ASCII mesh metadata and checks for errors.
+ * \param[in] single_pass - Try to read the contents together with the metadata if the order allows (points before
+ * elements). \param[in,out] config - Problem configuration where some metadata is updated (e.g. AoA). \returns True
+ * if single_pass was successful.
+ */
+ bool ReadMetadata(const bool single_pass, CConfig* config);
+
+ /*!
+ * \brief Reads the grid points from an SU2 zone into linear partitions across all ranks.
+ */
+ virtual void ReadPointCoordinates(const bool single_pass = false);
+
+ /*!
+ * \brief Reads the interior volume elements from one section of an SU2 zone into linear partitions across all ranks.
+ */
+ virtual void ReadVolumeElementConnectivity(const bool single_pass = false);
+
+ /*!
+ * \brief Reads the surface (boundary) elements from the SU2 zone.
+ */
+ virtual void ReadSurfaceElementConnectivity(const bool single_pass = false);
+
+ /*!
+ * \brief Helper function to find the current zone in an SU2 ASCII mesh object.
+ */
+ void FastForwardToMyZone();
+
+ public:
+ /*!
+ * \brief Constructor of the CSU2ASCIIMeshReaderBase class.
+ */
+ CSU2ASCIIMeshReaderBase(CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone);
+
+ /*!
+ * \brief Destructor of the CSU2ASCIIMeshReaderBase class.
+ */
+ virtual ~CSU2ASCIIMeshReaderBase(void) override;
+};
diff --git a/Common/include/geometry/meshreader/CSU2ASCIIMeshReaderFEM.hpp b/Common/include/geometry/meshreader/CSU2ASCIIMeshReaderFEM.hpp
new file mode 100644
index 00000000000..d92cf336103
--- /dev/null
+++ b/Common/include/geometry/meshreader/CSU2ASCIIMeshReaderFEM.hpp
@@ -0,0 +1,68 @@
+/*!
+ * \file CSU2ASCIIMeshReaderFEM.hpp
+ * \brief Header file for the class CSU2ASCIIMeshReaderFEM.
+ * The implementations are in the CSU2ASCIIMeshReaderFEM.cpp file.
+ * \author T. Economon, E. van der Weide
+ * \version 8.1.0 "Harrier"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2024, 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 "CSU2ASCIIMeshReaderBase.hpp"
+
+/*!
+ * \class CSU2ASCIIMeshReaderFEM
+ * \brief Reads a native SU2 ASCII grid into linear partitions for the finite element solver (FEM).
+ * \author T. Economon, E. van der Weide
+ */
+class CSU2ASCIIMeshReaderFEM : public CSU2ASCIIMeshReaderBase {
+ 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 CSU2ASCIIMeshReaderFEM class.
+ */
+ CSU2ASCIIMeshReaderFEM(CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone);
+
+ /*!
+ * \brief Destructor of the CSU2ASCIIMeshReaderFEM class.
+ */
+ ~CSU2ASCIIMeshReaderFEM(void) override;
+};
diff --git a/Common/include/geometry/meshreader/CSU2ASCIIMeshReaderFVM.hpp b/Common/include/geometry/meshreader/CSU2ASCIIMeshReaderFVM.hpp
index 1be0177b435..20f88bc66bc 100644
--- a/Common/include/geometry/meshreader/CSU2ASCIIMeshReaderFVM.hpp
+++ b/Common/include/geometry/meshreader/CSU2ASCIIMeshReaderFVM.hpp
@@ -30,87 +30,28 @@
#include
-#include "CMeshReaderFVM.hpp"
+#include "CSU2ASCIIMeshReaderBase.hpp"
/*!
* \class CSU2ASCIIMeshReaderFVM
* \brief Reads a native SU2 ASCII grid into linear partitions for the finite volume solver (FVM).
* \author T. Economon
*/
-class CSU2ASCIIMeshReaderFVM : public CMeshReaderFVM {
+class CSU2ASCIIMeshReaderFVM : public CSU2ASCIIMeshReaderBase {
private:
- 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. */
-
- /*!
- * \brief Reads all SU2 ASCII mesh metadata and checks for errors.
- * \param[in] single_pass - Try to read the contents together with the metadata if the order allows (points before
- * elements). \param[in,out] config - Problem configuration where some metadata is updated (e.g. AoA). \returns True
- * if single_pass was successful.
- */
- bool ReadMetadata(const bool single_pass, CConfig* config);
-
/*!
* \brief Splits a single surface actuator disk boundary into two separate markers (repeated points).
*/
void SplitActuatorDiskSurface();
- /*!
- * \brief Reads the grid points from an SU2 zone into linear partitions across all ranks.
- */
- void ReadPointCoordinates(const bool single_pass = false);
-
- /*!
- * \brief Reads the interior volume elements from one section of an SU2 zone into linear partitions across all ranks.
- */
- void ReadVolumeElementConnectivity(const bool single_pass = false);
-
- /*!
- * \brief Reads the surface (boundary) elements from the SU2 zone.
- */
- void ReadSurfaceElementConnectivity(const bool single_pass = false);
-
- /*!
- * \brief Helper function to find the current zone in an SU2 ASCII mesh object.
- */
- void FastForwardToMyZone();
-
public:
/*!
* \brief Constructor of the CSU2ASCIIMeshReaderFVM class.
*/
CSU2ASCIIMeshReaderFVM(CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone);
+
+ /*!
+ * \brief Destructor of the CSU2ASCIIMeshReaderFVM class.
+ */
+ ~CSU2ASCIIMeshReaderFVM(void) override;
};
diff --git a/Common/include/geometry/primal_grid/CPrimalGridBoundFEM.hpp b/Common/include/geometry/primal_grid/CPrimalGridBoundFEM.hpp
index fd7db594f6f..3975d1debba 100644
--- a/Common/include/geometry/primal_grid/CPrimalGridBoundFEM.hpp
+++ b/Common/include/geometry/primal_grid/CPrimalGridBoundFEM.hpp
@@ -51,16 +51,9 @@ class CPrimalGridBoundFEM final : public CPrimalGrid {
public:
/*!
* \brief Constructor using data to initialize the boundary element.
- * \param[in] val_elemGlobalID - Global boundary element ID of this element.
- * \param[in] val_domainElementID - Global ID of the corresponding domain element.
- * \param[in] val_VTK_Type - VTK type to indicate the element type
- * \param[in] val_nPolyGrid - Polynomial degree to describe the geometry of the element.
- * \param[in] val_nDOFsGrid - Number of DOFs used to describe the geometry of the element.
- * \param[in] val_nodes - Vector, which contains the global node IDs of the element.
- */
- CPrimalGridBoundFEM(unsigned long val_elemGlobalID, unsigned long val_domainElementID, unsigned short val_VTK_Type,
- unsigned short val_nPolyGrid, unsigned short val_nDOFsGrid,
- std::vector& val_nodes);
+ * \param[in] dataElem - Meta and connectivity data for this element.
+ */
+ CPrimalGridBoundFEM(const unsigned long* dataElem);
/*!
* \brief Get the number of nodes that composes a face of an element.
diff --git a/Common/include/geometry/primal_grid/CPrimalGridFEM.hpp b/Common/include/geometry/primal_grid/CPrimalGridFEM.hpp
index 81c214c78ff..77e1a5fc3b9 100644
--- a/Common/include/geometry/primal_grid/CPrimalGridFEM.hpp
+++ b/Common/include/geometry/primal_grid/CPrimalGridFEM.hpp
@@ -55,33 +55,10 @@ class CPrimalGridFEM final : public CPrimalGrid {
public:
/*!
* \brief Constructor using data to initialize the element.
- * \param[in] val_elemGlobalID - Global element ID of this element.
- * \param[in] val_VTK_Type - VTK type to indicate the element type
- * \param[in] val_nPolyGrid - Polynomial degree to describe the geometry of the element.
- * \param[in] val_nPolySol - Polynomial degree to describe the solution of the element.
- * \param[in] val_nDOFsGrid - Number of DOFs used to describe the geometry of the element.
- * \param[in] val_nDOFsSol - Number of DOFs used to describe the solution of the element.
- * \param[in] val_offDOfsSol - Global offset of the solution DOFs of the element.
- * \param[in] elem_line - istringstream, which contains the grid node numbers of the element.
+ * \param[in] dataElem - Meta and connectivity data for this element.
+ * \param[in,out] offsetDOFs - The offset of the solution DOFs for this element.
*/
- CPrimalGridFEM(unsigned long val_elemGlobalID, unsigned short val_VTK_Type, unsigned short val_nPolyGrid,
- unsigned short val_nPolySol, unsigned short val_nDOFsGrid, unsigned short val_nDOFsSol,
- unsigned long val_offDOfsSol, std::istringstream& elem_line);
-
- /*!
- * \brief Constructor using data to initialize the element.
- * \param[in] val_elemGlobalID - Global element ID of this element.
- * \param[in] val_VTK_Type - VTK type to indicate the element type
- * \param[in] val_nPolyGrid - Polynomial degree to describe the geometry of the element.
- * \param[in] val_nPolySol - Polynomial degree to describe the solution of the element.
- * \param[in] val_nDOFsGrid - Number of DOFs used to describe the geometry of the element.
- * \param[in] val_nDOFsSol - Number of DOFs used to describe the solution of the element.
- * \param[in] val_offDOfsSol - Global offset of the solution DOFs of the element.
- * \param[in] connGrid - Array, which contains the grid node numbers of the element.
- */
- CPrimalGridFEM(unsigned long val_elemGlobalID, unsigned short val_VTK_Type, unsigned short val_nPolyGrid,
- unsigned short val_nPolySol, unsigned short val_nDOFsGrid, unsigned short val_nDOFsSol,
- unsigned long val_offDOfsSol, const unsigned long* connGrid);
+ CPrimalGridFEM(const unsigned long* dataElem, unsigned long& offsetSolDOFs);
/*!
* \brief Get the number of nodes that composes a face of an element.
diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp
index cc5bd3faca6..d54d1dc3a6c 100644
--- a/Common/src/CConfig.cpp
+++ b/Common/src/CConfig.cpp
@@ -2145,6 +2145,9 @@ void CConfig::SetConfig_Options() {
mesh_box_offset[0] = 0.0; mesh_box_offset[1] = 0.0; mesh_box_offset[2] = 0.0;
addDoubleArrayOption("MESH_BOX_OFFSET", 3, mesh_box_offset);
+ /* DESCRIPTION: Polynomial degree of the FEM solution for the RECTANGLE or BOX grid. (default: 1). */
+ addUnsignedShortOption("MESH_BOX_POLY_SOL_FEM", Mesh_Box_PSolFEM, 1);
+
/* DESCRIPTION: Determine if the mesh file supports multizone. \n DEFAULT: true (temporarily) */
addBoolOption("MULTIZONE_MESH", Multizone_Mesh, true);
/* DESCRIPTION: Determine if we need to allocate memory to store the multizone residual. \n DEFAULT: false (temporarily) */
diff --git a/Common/src/fem/fem_geometry_structure.cpp b/Common/src/fem/fem_geometry_structure.cpp
index e671a2d0265..c6e52dbea81 100644
--- a/Common/src/fem/fem_geometry_structure.cpp
+++ b/Common/src/fem/fem_geometry_structure.cpp
@@ -25,6 +25,7 @@
* License along with SU2. If not, see .
*/
+#include "../../include/toolboxes/CLinearPartitioner.hpp"
#include "../../include/fem/fem_geometry_structure.hpp"
#include "../../include/geometry/primal_grid/CPrimalGridFEM.hpp"
#include "../../include/geometry/primal_grid/CPrimalGridBoundFEM.hpp"
@@ -221,6 +222,10 @@ CMeshFEM::CMeshFEM(CGeometry* geometry, CConfig* config) {
/*--- Allocate the memory for blasFunctions. ---*/
blasFunctions = new CBlasStructure;
+ /*--- Define the linear partitioning of the elements. ---*/
+ Global_nElem = geometry->GetGlobal_nElem();
+ CLinearPartitioner elemPartitioner(Global_nElem, 0);
+
/*--- The new FEM mesh class has the same problem dimension/zone. ---*/
nDim = geometry->GetnDim();
nZone = geometry->GetnZone();
@@ -262,14 +267,14 @@ CMeshFEM::CMeshFEM(CGeometry* geometry, CConfig* config) {
map rankToIndCommBuf;
for (int i = 0; i < size; ++i) {
if (sendToRank[i]) {
- int ind = (int)rankToIndCommBuf.size();
+ int ind = static_cast(rankToIndCommBuf.size());
rankToIndCommBuf[i] = ind;
}
}
/*--- Definition of the communication buffers, used to send the element data
to the correct ranks. ---*/
- int nRankSend = (int)rankToIndCommBuf.size();
+ int nRankSend = static_cast(rankToIndCommBuf.size());
vector > shortSendBuf(nRankSend, vector(0));
vector > longSendBuf(nRankSend, vector(0));
vector > doubleSendBuf(nRankSend, vector(0));
@@ -289,7 +294,7 @@ CMeshFEM::CMeshFEM(CGeometry* geometry, CConfig* config) {
/*--- Loop over the local elements to fill the communication buffers with element data. ---*/
for (unsigned long i = 0; i < geometry->GetnElem(); ++i) {
- int ind = (int)geometry->elem[i]->GetColor();
+ int ind = static_cast(geometry->elem[i]->GetColor());
map::const_iterator MI = rankToIndCommBuf.find(ind);
ind = MI->second;
@@ -374,13 +379,14 @@ CMeshFEM::CMeshFEM(CGeometry* geometry, CConfig* config) {
/* Loop over the local boundary elements in geometry for this marker. */
for (unsigned long i = 0; i < geometry->GetnElem_Bound(iMarker); ++i) {
/* Determine the local ID of the corresponding domain element. */
- unsigned long elemID = geometry->bound[iMarker][i]->GetDomainElement() - geometry->beg_node[rank];
+ unsigned long elemID =
+ geometry->bound[iMarker][i]->GetDomainElement() - elemPartitioner.GetFirstIndexOnRank(rank);
/* Determine to which rank this boundary element must be sent.
That is the same as its corresponding domain element.
Update the corresponding index in longSendBuf. */
- int ind = (int)geometry->elem[elemID]->GetColor();
- map::const_iterator MI = rankToIndCommBuf.find(ind);
+ int ind = static_cast(geometry->elem[elemID]->GetColor());
+ const auto MI = rankToIndCommBuf.find(ind);
ind = MI->second;
++longSendBuf[ind][indLongBuf[ind]];
@@ -560,8 +566,10 @@ CMeshFEM::CMeshFEM(CGeometry* geometry, CConfig* config) {
the elements with constant and non-constant Jacobians are
considered the same. */
if (JacConstant) {
- const auto orderExactStraight = (unsigned short)ceil(nPolySol * config->GetQuadrature_Factor_Straight());
- const auto orderExactCurved = (unsigned short)ceil(nPolySol * config->GetQuadrature_Factor_Curved());
+ const auto orderExactStraight =
+ static_cast(ceil(nPolySol * config->GetQuadrature_Factor_Straight()));
+ const auto orderExactCurved =
+ static_cast(ceil(nPolySol * config->GetQuadrature_Factor_Curved()));
if (orderExactStraight == orderExactCurved) JacConstant = false;
}
@@ -666,9 +674,7 @@ CMeshFEM::CMeshFEM(CGeometry* geometry, CConfig* config) {
/* Determine the number of elements per rank of the originally partitioned grid
stored in cumulative storage format. */
vector nElemPerRankOr(size + 1);
-
- for (int i = 0; i < size; ++i) nElemPerRankOr[i] = geometry->beg_node[i];
- nElemPerRankOr[size] = geometry->end_node[size - 1];
+ for (int i = 0; i <= size; ++i) nElemPerRankOr[i] = elemPartitioner.GetCumulativeSizeBeforeRank(i);
/* Determine to which ranks I have to send messages to find out the information
of the halos stored on this rank. */
@@ -687,14 +693,14 @@ CMeshFEM::CMeshFEM(CGeometry* geometry, CConfig* config) {
rankToIndCommBuf.clear();
for (int i = 0; i < size; ++i) {
if (sendToRank[i]) {
- int ind = (int)rankToIndCommBuf.size();
+ int ind = static_cast(rankToIndCommBuf.size());
rankToIndCommBuf[i] = ind;
}
}
/* Resize the first index of the long send buffers for the communication of
the halo data. */
- nRankSend = (int)rankToIndCommBuf.size();
+ nRankSend = static_cast(rankToIndCommBuf.size());
longSendBuf.resize(nRankSend);
/* Determine the number of ranks, from which this rank will receive elements. */
@@ -713,7 +719,7 @@ CMeshFEM::CMeshFEM(CGeometry* geometry, CConfig* config) {
if (*low > haloElements[i].long0) --ind;
/* Convert this rank to the index in the send buffer. */
- MI = rankToIndCommBuf.find((int)ind);
+ MI = rankToIndCommBuf.find(static_cast(ind));
ind = MI->second;
/* Store the global element ID and the periodic index in the long buffer.
@@ -803,11 +809,10 @@ CMeshFEM::CMeshFEM(CGeometry* geometry, CConfig* config) {
/* Determine the local index of the element in the original partitioning.
Check if the index is valid. */
- const long localID = globalID - geometry->beg_node[rank];
- if (localID < 0 || localID >= (long)geometry->nPointLinear[rank]) {
+ const long localID = globalID - elemPartitioner.GetFirstIndexOnRank(rank);
+ if (elemPartitioner.GetRankContainingIndex(globalID) != static_cast(rank)) {
ostringstream message;
- message << localID << " " << geometry->nPointLinear[rank] << endl;
- message << "Invalid local element ID";
+ message << "Invalid local element ID: " << localID;
SU2_MPI::Error(message.str(), CURRENT_FUNCTION);
}
@@ -908,12 +913,12 @@ CMeshFEM::CMeshFEM(CGeometry* geometry, CConfig* config) {
for (int i = 0; i < size; ++i) {
if (nHaloElemPerRank[i + 1] > nHaloElemPerRank[i]) {
sendToRank[i] = 1;
- int ind = (int)rankToIndCommBuf.size();
+ int ind = static_cast(rankToIndCommBuf.size());
rankToIndCommBuf[i] = ind;
}
}
- nRankSend = (int)rankToIndCommBuf.size();
+ nRankSend = static_cast(rankToIndCommBuf.size());
/* Store the value of nRankSend for later use. */
const int nRankSendHaloInfo = nRankSend;
@@ -1176,7 +1181,7 @@ CMeshFEM::CMeshFEM(CGeometry* geometry, CConfig* config) {
/*--- Create the graph of local elements. The halo elements are ignored. ---*/
vector > neighElem(nVolElemOwned, vector(0));
- nRankRecv = (int)longRecvBuf.size();
+ nRankRecv = static_cast(longRecvBuf.size());
for (int i = 0; i < nRankRecv; ++i) {
unsigned long indL = 1, indS = 0;
for (long j = 0; j < longRecvBuf[i][0]; ++j) {
@@ -1449,7 +1454,7 @@ CMeshFEM::CMeshFEM(CGeometry* geometry, CConfig* config) {
/*--- Resize the first index of the send buffers to nRankRecv, because this
number of messages must be sent back to the sending ranks with halo
information. ---*/
- nRankRecv = (int)longSecondRecvBuf.size();
+ nRankRecv = static_cast(longSecondRecvBuf.size());
shortSendBuf.resize(nRankRecv);
longSendBuf.resize(nRankRecv);
doubleSendBuf.resize(nRankRecv);
@@ -2452,12 +2457,16 @@ void CMeshFEM_DG::CreateFaces(CConfig* config) {
is set to false. Hence it is only needed to carry out this check for faces
with a constant Jacobian. This is done to reduce the number of standard elements. */
if (thisFace.JacFaceIsConsideredConstant) {
- auto orderExactStraight = (unsigned short)ceil(thisFace.nPolyGrid0 * config->GetQuadrature_Factor_Straight());
- auto orderExactCurved = (unsigned short)ceil(thisFace.nPolyGrid0 * config->GetQuadrature_Factor_Curved());
+ auto orderExactStraight =
+ static_cast(ceil(thisFace.nPolyGrid0 * config->GetQuadrature_Factor_Straight()));
+ auto orderExactCurved =
+ static_cast(ceil(thisFace.nPolyGrid0 * config->GetQuadrature_Factor_Curved()));
if (orderExactStraight == orderExactCurved) {
- orderExactStraight = (unsigned short)ceil(thisFace.nPolySol0 * config->GetQuadrature_Factor_Straight());
- orderExactCurved = (unsigned short)ceil(thisFace.nPolySol0 * config->GetQuadrature_Factor_Curved());
+ orderExactStraight =
+ static_cast(ceil(thisFace.nPolySol0 * config->GetQuadrature_Factor_Straight()));
+ orderExactCurved =
+ static_cast(ceil(thisFace.nPolySol0 * config->GetQuadrature_Factor_Curved()));
if (orderExactStraight == orderExactCurved) thisFace.JacFaceIsConsideredConstant = false;
}
}
@@ -3156,7 +3165,7 @@ void CMeshFEM_DG::SetSendReceive(const CConfig* config) {
map rankToIndRecvBuf;
for (int i = 0; i < size; ++i) {
if (recvFromRank[i]) {
- int ind = (int)rankToIndRecvBuf.size();
+ int ind = static_cast(rankToIndRecvBuf.size());
rankToIndRecvBuf[i] = ind;
}
}
diff --git a/Common/src/fem/fem_standard_element.cpp b/Common/src/fem/fem_standard_element.cpp
index b24c7cacfaf..ab8e537ad9c 100644
--- a/Common/src/fem/fem_standard_element.cpp
+++ b/Common/src/fem/fem_standard_element.cpp
@@ -186,9 +186,9 @@ CFEMStandardElementBase::CFEMStandardElementBase(unsigned short val_VTK_Type, un
orderExact = val_orderExact;
} else {
if (constJacobian)
- orderExact = (unsigned short)ceil(val_nPoly * config->GetQuadrature_Factor_Straight());
+ orderExact = static_cast(ceil(val_nPoly * config->GetQuadrature_Factor_Straight()));
else
- orderExact = (unsigned short)ceil(val_nPoly * config->GetQuadrature_Factor_Curved());
+ orderExact = static_cast(ceil(val_nPoly * config->GetQuadrature_Factor_Curved()));
}
/*--- Determine the integration points. This depends on the element type. ---*/
@@ -903,7 +903,7 @@ void CFEMStandardElementBase::SubConnForPlottingLine(const unsigned short nPoly,
/*--- Determine the local subconnectivity of the line element used for plotting
purposes. This is rather trivial, because the line element is subdivided
into nPoly linear line elements. ---*/
- unsigned short nnPoly = max(nPoly, (unsigned short)1);
+ unsigned short nnPoly = max(nPoly, static_cast(1));
for (unsigned short i = 0; i < nnPoly; ++i) {
subConn.push_back(i);
subConn.push_back(i + 1);
@@ -916,7 +916,7 @@ void CFEMStandardElementBase::SubConnForPlottingQuadrilateral(const unsigned sho
plotting purposes. Note that the connectivity of the linear subelements
obey the VTK connectivity rule of a quadrilateral, which is different
from the connectivity for the high order quadrilateral. ---*/
- unsigned short nnPoly = max(nPoly, (unsigned short)1);
+ unsigned short nnPoly = max(nPoly, static_cast(1));
for (unsigned short j = 0; j < nnPoly; ++j) {
unsigned short jj = j * (nnPoly + 1);
for (unsigned short i = 0; i < nnPoly; ++i) {
diff --git a/Common/src/fem/geometry_structure_fem_part.cpp b/Common/src/fem/geometry_structure_fem_part.cpp
index d9536a67903..f4734eed85d 100644
--- a/Common/src/fem/geometry_structure_fem_part.cpp
+++ b/Common/src/fem/geometry_structure_fem_part.cpp
@@ -25,14 +25,12 @@
* License along with SU2. If not, see .
*/
+#include "../../include/toolboxes/CLinearPartitioner.hpp"
#include "../../include/geometry/CPhysicalGeometry.hpp"
#include "../../include/fem/fem_standard_element.hpp"
#include "../../include/geometry/primal_grid/CPrimalGridFEM.hpp"
#include "../../include/geometry/primal_grid/CPrimalGridBoundFEM.hpp"
-#ifdef HAVE_CGNS
-#include "../../include/fem/fem_cgns_elements.hpp"
-#endif
#include "../../include/adt/CADTElemClass.hpp"
#include "../../include/linear_algebra/blas_structure.hpp"
@@ -363,1473 +361,109 @@ void CMatchingFace::Copy(const CMatchingFace& other) {
tolForMatching = other.tolForMatching;
}
-void CPhysicalGeometry::Read_SU2_Format_Parallel_FEM(CConfig* config, const string& val_mesh_filename,
- unsigned short val_iZone, unsigned short val_nZone) {
- string text_line, Marker_Tag;
- ifstream mesh_file;
- string::size_type position;
- unsigned long nDOFsGrid_Local = 0, loc_element_count = 0;
- bool domain_flag = false;
- bool time_spectral = config->GetTime_Marching() == TIME_MARCHING::HARMONIC_BALANCE;
- unsigned short nMarker_Max = config->GetnMarker_Max();
- nZone = val_nZone;
-
- /*--- Initialize counters for local/global points & elements ---*/
- Global_nPoint = 0;
- Global_nPointDomain = 0;
- Global_nElem = 0;
- nelem_edge = 0;
- Global_nelem_edge = 0;
- nelem_triangle = 0;
- Global_nelem_triangle = 0;
- nelem_quad = 0;
- Global_nelem_quad = 0;
- nelem_tetra = 0;
- Global_nelem_tetra = 0;
- nelem_hexa = 0;
- Global_nelem_hexa = 0;
- nelem_prism = 0;
- Global_nelem_prism = 0;
- nelem_pyramid = 0;
- Global_nelem_pyramid = 0;
-
- /*--- Allocate memory for the linear partition of the elements of the mesh.
- These arrays are the size of the number of ranks. ---*/
-
- beg_node = new unsigned long[size];
- end_node = new unsigned long[size];
- nPointLinear = new unsigned long[size];
-
- /*--- Open grid file ---*/
-
- mesh_file.open(val_mesh_filename.c_str(), ios::in);
-
- /*--- Check the grid ---*/
-
- if (mesh_file.fail())
- SU2_MPI::Error(string("There is no mesh file (CPhysicalGeometry)!! ") + val_mesh_filename, CURRENT_FUNCTION);
-
- /*--- If more than one, find the zone in the mesh file ---*/
-
- if (val_nZone > 1 || time_spectral) {
- if (time_spectral) {
- if (rank == MASTER_NODE) cout << "Reading time spectral instance " << val_iZone + 1 << ":" << endl;
- } else {
- while (getline(mesh_file, text_line)) {
- /*--- Search for the current domain ---*/
- position = text_line.find("IZONE=", 0);
- if (position != string::npos) {
- text_line.erase(0, 6);
- unsigned short jDomain = atoi(text_line.c_str());
- if (jDomain == val_iZone + 1) {
- if (rank == MASTER_NODE) cout << "Reading zone " << val_iZone + 1 << " points:" << endl;
- break;
- }
- }
- }
- }
- }
-
- /*--- Read grid file with format SU2 ---*/
-
- while (getline(mesh_file, text_line)) {
- /*--- Read the dimension of the problem ---*/
-
- position = text_line.find("NDIME=", 0);
- if (position != string::npos) {
- if (!domain_flag) {
- text_line.erase(0, 6);
- nDim = atoi(text_line.c_str());
- if (rank == MASTER_NODE) {
- if (nDim == 2) cout << "Two dimensional problem." << endl;
- if (nDim == 3) cout << "Three dimensional problem." << endl;
- }
- domain_flag = true;
- } else {
- break;
- }
- }
-
- /*--- Read the information about inner elements ---*/
-
- position = text_line.find("NELEM=", 0);
- if (position != string::npos) {
- text_line.erase(0, 6);
- stringstream stream_line(text_line);
- stream_line >> Global_nElem;
-
- if ((rank == MASTER_NODE) && (size > SINGLE_NODE))
- cout << Global_nElem << " interior elements before parallel partitioning." << endl;
-
- /*--- Check if the number of cores used is larger than the number of
- elements. Terminate if this is the case, because it does not make
- sense to do this. ---*/
- unsigned long nCores = size; // Correct for the number of cores per rank.
- if (nCores > Global_nElem) {
- ostringstream message;
- message << "The number of cores, " << nCores;
- message << ", is larger than the number of elements, " << Global_nElem << "." << endl;
- message << "This is not an efficient use of the resources and therefore "
- << "SU2 will terminate.";
-
- SU2_MPI::Error(message.str(), CURRENT_FUNCTION);
- }
-
- /*--- Compute the number of elements that will be on each processor.
- This is a linear partitioning with the addition of a simple load
- balancing for any remainder elements. ---*/
- unsigned long total_elem_accounted = 0;
- for (unsigned long i = 0; i < (unsigned long)size; i++) {
- nPointLinear[i] = Global_nElem / size;
- total_elem_accounted = total_elem_accounted + nPointLinear[i];
- }
-
- /*--- Get the number of remainder elements after the even division ---*/
- unsigned long rem_elem = Global_nElem - total_elem_accounted;
- for (unsigned long i = 0; i < rem_elem; i++) {
- ++nPointLinear[i];
- }
-
- /*--- Store the local number of elements and the beginning/end index ---*/
- nElem = nPointLinear[rank];
- beg_node[0] = 0;
- end_node[0] = beg_node[0] + nPointLinear[0];
- for (unsigned long i = 1; i < (unsigned long)size; i++) {
- beg_node[i] = end_node[i - 1];
- end_node[i] = beg_node[i] + nPointLinear[i];
- }
-
- /*--- Allocate space for elements ---*/
- elem = new CPrimalGrid*[nElem]();
-
- /*--- Loop over all the elements and store the elements to be stored on
- this rank. Furthermore, determine the total amount of DOFs for
- the solution (which may differ from the number of DOFS for the
- grid). ---*/
- unsigned long nDOFs_tot = 0;
- for (unsigned long i = 0; i < Global_nElem; i++) {
- getline(mesh_file, text_line);
- istringstream elem_line(text_line);
-
- /*--- Read the value that defines the element type, the polynomial
- degree of the geometry and the polynomial degree of the
- solution. Extract this info as well. ---*/
- unsigned long typeRead;
- elem_line >> typeRead;
- unsigned long typeReadErrorMessage = typeRead;
- unsigned short nPolySol, nPolyGrid;
- if (typeRead > 10000) {
- nPolySol = typeRead / 10000 - 1;
- typeRead = typeRead % 10000;
- nPolyGrid = typeRead / 100 + 1;
- } else {
- nPolyGrid = typeRead / 100 + 1;
- nPolySol = nPolyGrid;
- }
-
- unsigned short VTK_Type = typeRead % 100;
-
- unsigned short nDOFsGrid = CFEMStandardElementBase::GetNDOFsStatic(VTK_Type, nPolyGrid, typeReadErrorMessage);
- unsigned short nDOFsSol = CFEMStandardElementBase::GetNDOFsStatic(VTK_Type, nPolySol, typeReadErrorMessage);
-
- /*--- Allocate the memory for a new primary grid FEM element if
- this element must be stored on this rank. ---*/
- if ((i >= beg_node[rank]) && (i < end_node[rank])) {
- elem[loc_element_count] =
- new CPrimalGridFEM(i, VTK_Type, nPolyGrid, nPolySol, nDOFsGrid, nDOFsSol, nDOFs_tot, elem_line);
- nDOFsGrid_Local += nDOFsGrid;
- loc_element_count++;
- }
-
- /*--- Update the total value of the number of DOFs. ---*/
- nDOFs_tot += nDOFsSol;
- }
-
- /*--- Break from the loop to find the element information. ---*/
- break;
- }
- }
-
- mesh_file.close();
-
- /*--- Create a vector, which contains the global node IDs of the local elements. ---*/
- vector nodeIDsElemLoc;
- nodeIDsElemLoc.reserve(nDOFsGrid_Local);
- for (unsigned long i = 0; i < loc_element_count; i++) {
- unsigned short nDOFsElem = elem[i]->GetnNodes();
- for (unsigned short j = 0; j < nDOFsElem; ++j) nodeIDsElemLoc.push_back(elem[i]->GetNode(j));
- }
+void CPhysicalGeometry::LoadLinearlyPartitionedPointsFEM(CConfig* config, CMeshReaderBase* mesh) {
+ /*--- Get the partitioned coordinates and their global IDs from the mesh object. ---*/
+ const auto& gridCoords = mesh->GetLocalPointCoordinates();
+ const auto& globalPointIDs = mesh->GetGlobalPointIDs();
- sort(nodeIDsElemLoc.begin(), nodeIDsElemLoc.end());
- vector::iterator lastNode;
- lastNode = unique(nodeIDsElemLoc.begin(), nodeIDsElemLoc.end());
- nodeIDsElemLoc.erase(lastNode, nodeIDsElemLoc.end());
+ /*--- Initialize point counts and the grid node data structure. ---*/
- /*--- Allocate the memory for the coordinates to be stored on this rank. ---*/
- nPoint = nodeIDsElemLoc.size();
nodes = new CPoint(nPoint, nDim);
- /*--- Open the grid file again and go to the position where
- the correct zone is stored. ---*/
- mesh_file.open(val_mesh_filename.c_str(), ios::in);
-
- if (val_nZone > 1 && !time_spectral) {
- while (getline(mesh_file, text_line)) {
- position = text_line.find("IZONE=", 0);
- if (position != string::npos) {
- text_line.erase(0, 6);
- unsigned short jDomain = atoi(text_line.c_str());
- if (jDomain == val_iZone + 1) break;
- }
- }
- }
-
- /*--- While loop to read the point information. ---*/
- while (getline(mesh_file, text_line)) {
- position = text_line.find("NPOIN=", 0);
- if (position != string::npos) {
- text_line.erase(0, 6);
- stringstream stream_line(text_line);
- stream_line >> Global_nPoint;
-
- /*--- Loop over the global number of points and store the
- ones that are needed on this processor. ---*/
- unsigned long ii = 0;
- for (unsigned long i = 0; i < Global_nPoint; ++i) {
- getline(mesh_file, text_line);
-
- if (binary_search(nodeIDsElemLoc.begin(), nodeIDsElemLoc.end(), i)) {
- istringstream point_line(text_line);
- su2double Coord[3] = {0.0};
- point_line >> Coord[0];
- point_line >> Coord[1];
- if (nDim == 3) point_line >> Coord[2];
- nodes->SetCoord(ii, Coord);
- nodes->SetGlobalIndex(ii, i);
- ++ii;
- }
- }
-
- break;
- }
- }
-
- mesh_file.close();
-
- /*--- Determine the faces of the local elements. --- */
- vector localFaces;
- for (unsigned long k = 0; k < loc_element_count; k++) {
- /*--- Get the global IDs of the corner points of all the faces of this elements. ---*/
- unsigned short nFaces;
- unsigned short nPointsPerFace[6];
- unsigned long faceConn[6][4];
-
- elem[k]->GetCornerPointsAllFaces(nFaces, nPointsPerFace, faceConn);
-
- /*--- Loop over the faces and add them to localFaces. ---*/
- for (unsigned short i = 0; i < nFaces; ++i) {
- CFaceOfElement thisFace;
- thisFace.nCornerPoints = nPointsPerFace[i];
- for (unsigned short j = 0; j < nPointsPerFace[i]; ++j) thisFace.cornerPoints[j] = faceConn[i][j];
- thisFace.elemID0 = k + beg_node[rank];
-
- thisFace.CreateUniqueNumbering();
- localFaces.push_back(thisFace);
- }
- }
-
- /*--- Sort localFaces in increasing order and remove the double entities,
- such that the binary search later on is a bit more efficient. ---*/
- sort(localFaces.begin(), localFaces.end());
- vector::iterator lastFace;
- lastFace = unique(localFaces.begin(), localFaces.end());
- localFaces.erase(lastFace, localFaces.end());
-
- /*--- Open the grid file again and go to the position where
- the correct zone is stored. ---*/
- mesh_file.open(val_mesh_filename.c_str(), ios::in);
-
- if (val_nZone > 1 && !time_spectral) {
- while (getline(mesh_file, text_line)) {
- position = text_line.find("IZONE=", 0);
- if (position != string::npos) {
- text_line.erase(0, 6);
- unsigned short jDomain = atoi(text_line.c_str());
- if (jDomain == val_iZone + 1) break;
- }
- }
- }
-
- /*--- While loop to read the boundary information. ---*/
- while (getline(mesh_file, text_line)) {
- /*--- Read number of markers ---*/
- position = text_line.find("NMARK=", 0);
- if (position != string::npos) {
- text_line.erase(0, 6);
- istringstream stream_line(text_line);
- stream_line >> nMarker;
-
- if (rank == MASTER_NODE) cout << nMarker << " surface markers." << endl;
- config->SetnMarker_All(nMarker);
- bound = new CPrimalGrid**[nMarker];
- nElem_Bound = new unsigned long[nMarker];
- Tag_to_Marker = new string[nMarker_Max];
-
- for (unsigned short iMarker = 0; iMarker < nMarker; iMarker++) {
- getline(mesh_file, text_line);
- text_line.erase(0, 11);
- text_line.erase(remove(text_line.begin(), text_line.end(), ' '), text_line.end());
- text_line.erase(remove(text_line.begin(), text_line.end(), '\r'), text_line.end());
- text_line.erase(remove(text_line.begin(), text_line.end(), '\n'), text_line.end());
-
- Marker_Tag = text_line;
-
- /*--- Read the number of elements for this marker. ---*/
- getline(mesh_file, text_line);
- text_line.erase(0, 13);
- istringstream nmark_line(text_line);
- unsigned long nElem_Bound_Global;
- nmark_line >> nElem_Bound_Global;
- if (rank == MASTER_NODE)
- cout << nElem_Bound_Global << " boundary elements in index " << iMarker << " (Marker = " << Marker_Tag << ")."
- << endl;
-
- /*--- Define a vector of FEM boundary elements to store the local boundary faces. ---*/
- vector boundElems;
-
- /*--- Loop over the global boundary faces. ---*/
- for (unsigned long i = 0; i < nElem_Bound_Global; ++i) {
- getline(mesh_file, text_line);
- istringstream bound_line(text_line);
-
- /*--- Determine the element type, its number of DOFs and read
- its node IDs. ---*/
- unsigned long typeRead;
- bound_line >> typeRead;
- unsigned short nPolyGrid = typeRead / 100 + 1;
- unsigned short VTK_Type = typeRead % 100;
- unsigned short nDOFEdgeGrid = nPolyGrid + 1;
-
- unsigned short nDOFsGrid = 0;
- CFaceOfElement thisFace;
- thisFace.cornerPoints[0] = 0;
- thisFace.cornerPoints[1] = nPolyGrid;
- switch (VTK_Type) {
- case LINE:
- nDOFsGrid = nDOFEdgeGrid;
- thisFace.nCornerPoints = 2;
- break;
-
- case TRIANGLE:
- nDOFsGrid = nDOFEdgeGrid * (nDOFEdgeGrid + 1) / 2;
- thisFace.nCornerPoints = 3;
- thisFace.cornerPoints[2] = nDOFsGrid - 1;
- break;
-
- case QUADRILATERAL:
- nDOFsGrid = nDOFEdgeGrid * nDOFEdgeGrid;
- thisFace.nCornerPoints = 4;
- thisFace.cornerPoints[2] = nPolyGrid * nDOFEdgeGrid;
- thisFace.cornerPoints[3] = nDOFsGrid - 1;
- break;
-
- default:
- ostringstream message;
- message << "Unknown FEM boundary element value, " << typeRead << ", in " << val_mesh_filename;
- SU2_MPI::Error(message.str(), CURRENT_FUNCTION);
- }
-
- vector nodeIDs(nDOFsGrid);
- for (unsigned short j = 0; j < nDOFsGrid; ++j) bound_line >> nodeIDs[j];
-
- /*--- Convert the local numbering of thisFace to global numbering
- and create a unique numbering of these nodes. ---*/
- for (unsigned short j = 0; j < thisFace.nCornerPoints; ++j)
- thisFace.cornerPoints[j] = nodeIDs[thisFace.cornerPoints[j]];
- thisFace.CreateUniqueNumbering();
-
- /*--- Check if this boundary face must be stored on this rank.
- If so, create an object of CBoundaryFace and add it
- to boundElems. ---*/
- vector::iterator low;
- low = lower_bound(localFaces.begin(), localFaces.end(), thisFace);
- if (low != localFaces.end()) {
- if (!(thisFace < *low)) {
- CBoundaryFace thisBoundFace;
- thisBoundFace.VTK_Type = VTK_Type;
- thisBoundFace.nPolyGrid = nPolyGrid;
- thisBoundFace.nDOFsGrid = nDOFsGrid;
- thisBoundFace.globalBoundElemID = i;
- thisBoundFace.domainElementID = low->elemID0;
- thisBoundFace.Nodes = nodeIDs;
-
- boundElems.push_back(thisBoundFace);
- }
- }
- }
-
- /*--- Allocate space for the boundary elements and store the ones
- whose parent element is stored on this rank. ---*/
- nElem_Bound[iMarker] = boundElems.size();
- bound[iMarker] = new CPrimalGrid*[nElem_Bound[iMarker]];
-
- for (unsigned long i = 0; i < nElem_Bound[iMarker]; ++i)
- bound[iMarker][i] = new CPrimalGridBoundFEM(boundElems[i].globalBoundElemID, boundElems[i].domainElementID,
- boundElems[i].VTK_Type, boundElems[i].nPolyGrid,
- boundElems[i].nDOFsGrid, boundElems[i].Nodes);
-
- /*--- Update config information storing the boundary information in the right place ---*/
- Tag_to_Marker[config->GetMarker_CfgFile_TagBound(Marker_Tag)] = Marker_Tag;
- config->SetMarker_All_TagBound(iMarker, Marker_Tag);
- config->SetMarker_All_KindBC(iMarker, config->GetMarker_CfgFile_KindBC(Marker_Tag));
- config->SetMarker_All_Monitoring(iMarker, config->GetMarker_CfgFile_Monitoring(Marker_Tag));
- config->SetMarker_All_GeoEval(iMarker, config->GetMarker_CfgFile_GeoEval(Marker_Tag));
- config->SetMarker_All_Designing(iMarker, config->GetMarker_CfgFile_Designing(Marker_Tag));
- config->SetMarker_All_Plotting(iMarker, config->GetMarker_CfgFile_Plotting(Marker_Tag));
- config->SetMarker_All_Analyze(iMarker, config->GetMarker_CfgFile_Analyze(Marker_Tag));
- config->SetMarker_All_ZoneInterface(iMarker, config->GetMarker_CfgFile_ZoneInterface(Marker_Tag));
- config->SetMarker_All_DV(iMarker, config->GetMarker_CfgFile_DV(Marker_Tag));
- config->SetMarker_All_Moving(iMarker, config->GetMarker_CfgFile_Moving(Marker_Tag));
- config->SetMarker_All_SobolevBC(iMarker, config->GetMarker_CfgFile_SobolevBC(Marker_Tag));
- config->SetMarker_All_PerBound(iMarker, config->GetMarker_CfgFile_PerBound(Marker_Tag));
- config->SetMarker_All_SendRecv(iMarker, NONE);
- }
-
- break;
- }
+ /*--- Loop over the points and set the coordinates and global index. ---*/
+ for (unsigned long iPoint = 0; iPoint < nPoint; iPoint++) {
+ for (unsigned short iDim = 0; iDim < nDim; ++iDim) nodes->SetCoord(iPoint, iDim, gridCoords[iDim][iPoint]);
+ nodes->SetGlobalIndex(iPoint, globalPointIDs[iPoint]);
}
-
- mesh_file.close();
}
-void CPhysicalGeometry::Read_CGNS_Format_Parallel_FEM(CConfig* config, const string& val_mesh_filename,
- unsigned short val_iZone, unsigned short val_nZone) {
-#ifdef HAVE_CGNS
-
- /*--- For proper support of the high order elements, at least version 3.3
- of CGNS must be used. Check this. ---*/
-#if CGNS_VERSION >= 3300
-
- /*--- Check whether the supplied file is truly a CGNS file. ---*/
- int file_type;
- if (cg_is_cgns(val_mesh_filename.c_str(), &file_type) != CG_OK)
- SU2_MPI::Error(val_mesh_filename + string(" is not a CGNS file that can be read."), CURRENT_FUNCTION);
-
- /*--- Initialize counters for local/global points & elements ---*/
- Global_nPoint = 0;
- Global_nPointDomain = 0;
- Global_nElem = 0;
- nelem_edge = 0;
- Global_nelem_edge = 0;
- nelem_triangle = 0;
- Global_nelem_triangle = 0;
- nelem_quad = 0;
- Global_nelem_quad = 0;
- nelem_tetra = 0;
- Global_nelem_tetra = 0;
- nelem_hexa = 0;
- Global_nelem_hexa = 0;
- nelem_prism = 0;
- Global_nelem_prism = 0;
- nelem_pyramid = 0;
- Global_nelem_pyramid = 0;
-
- /*--------------------------------------------------------------------------*/
- /*--- Checking of the file, determine the dimensions, etc. ---*/
- /*--------------------------------------------------------------------------*/
+void CPhysicalGeometry::LoadLinearlyPartitionedVolumeElementsFEM(CConfig* config, CMeshReaderBase* mesh) {
+ /*--- Reset the global to local element mapping. ---*/
+ Global_to_Local_Elem.clear();
- /*--- Allocate memory for the linear partition of the elements of the mesh.
- These arrays are the size of the number of ranks. ---*/
- beg_node = new unsigned long[size];
- end_node = new unsigned long[size];
- nPointLinear = new unsigned long[size];
-
- /* Open the CGNS file for reading and check if it went OK. */
- int fn;
- if (cg_open(val_mesh_filename.c_str(), CG_MODE_READ, &fn) != CG_OK) cg_error_exit();
- if (rank == MASTER_NODE) {
- cout << "Reading the CGNS file: " << val_mesh_filename << "." << endl;
- }
-
- /* Get the number of databases. This is the highest node in the CGNS heirarchy.
- The current implementation assumes that there is only one database. */
- int nbases;
- if (cg_nbases(fn, &nbases) != CG_OK) cg_error_exit();
- if (nbases > 1) {
- ostringstream message;
- message << "CGNS file contains " << nbases << " databases." << endl;
- message << "CGNS reader can handle only 1 at the moment." << endl;
+ /*--- Get the volume connectivity from the mesh object. ---*/
+ const auto& dataElems = mesh->GetLocalVolumeElementConnectivity();
- SU2_MPI::Error(message.str(), CURRENT_FUNCTION);
- }
-
- /* Read the information of the base, especially the number of dimensions. */
- char cgnsname[CGNS_STRING_SIZE];
- int cellDim, physDim;
- const int iBase = 1;
- if (cg_base_read(fn, iBase, cgnsname, &cellDim, &physDim)) cg_error_exit();
- nDim = physDim;
-
- if (cellDim != physDim) {
- ostringstream message;
- message << "The element dimension, " << cellDim << ", differs from the physical dimension, " << physDim << "."
- << endl;
- message << "These should be the same for the DG-FEM solver." << endl;
-
- SU2_MPI::Error(message.str(), CURRENT_FUNCTION);
- }
-
- /* Write the info about the number of dimensions. */
- if (rank == MASTER_NODE) {
- if (nDim == 2) cout << "Two dimensional problem." << endl;
- if (nDim == 3) cout << "Three dimensional problem." << endl;
- }
-
- /* Set the zone ID for CGNS, CGNS indices starts at 1, and check
- if the requested zone ID is a valid one. */
- const int iZone = val_iZone + 1;
-
- int nzones;
- if (cg_nzones(fn, iBase, &nzones) != CG_OK) cg_error_exit();
- if (iZone < 1 || iZone > nzones) {
- ostringstream message;
- message << "Zone " << iZone << " requested for reading, but there are only " << nzones
- << " zones present in the CGNS file." << endl;
-
- SU2_MPI::Error(message.str(), CURRENT_FUNCTION);
- }
-
- /* Determine the zone type for the requested zone and check if it is
- unstructured. */
- ZoneType_t zoneType;
- if (cg_zone_type(fn, iBase, iZone, &zoneType) != CG_OK) cg_error_exit();
- if (zoneType != Unstructured)
- SU2_MPI::Error("Structured CGNS zone found while unstructured expected.", CURRENT_FUNCTION);
-
- /* Determine the number of sections for the connectivities in this zone. */
- int nsections;
- if (cg_nsections(fn, iBase, iZone, &nsections) != CG_OK) cg_error_exit();
-
- /*--------------------------------------------------------------------------*/
- /*--- Reading and distributing the volume elements. ---*/
- /*--------------------------------------------------------------------------*/
-
- /* Loop over the sections to store the meta data in CGNSElemTypes
- and to determine the number of volume elements.
- Note that the indices start at 1 in CGNS. */
- vector CGNSElemTypes(nsections);
-
- for (int iConn = 1; iConn <= nsections; ++iConn) {
- CGNSElemTypes[iConn - 1].DetermineMetaData(nDim, fn, iBase, iZone, iConn);
- if (CGNSElemTypes[iConn - 1].volumeConn) Global_nElem += CGNSElemTypes[iConn - 1].nElem;
- }
-
- if ((rank == MASTER_NODE) && (size > SINGLE_NODE))
- cout << Global_nElem << " interior elements before parallel partitioning." << endl;
-
- /*--- Check if the number of cores used is larger than the number of
- elements. Terminate if this is the case, because it does not make
- sense to do this. ---*/
- unsigned long nCores = size; // Correct for the number of cores per rank.
- if (nCores > Global_nElem) {
- ostringstream message;
- message << "The number of cores, " << nCores;
- message << ", is larger than the number of elements, " << Global_nElem << "." << endl;
- message << "This is not an efficient use of the resources and therefore "
- << "SU2 will terminate.";
-
- SU2_MPI::Error(message.str(), CURRENT_FUNCTION);
- }
-
- /*--- Compute the number of elements that will be stored on each rank.
- This is a linear partitioning with the addition of a simple load
- balancing for any remainder elements. ---*/
- unsigned long total_elem_accounted = 0;
- for (unsigned long i = 0; i < (unsigned long)size; i++) {
- nPointLinear[i] = Global_nElem / size;
- total_elem_accounted = total_elem_accounted + nPointLinear[i];
- }
-
- /*--- Get the number of remainder elements after the even division ---*/
- const unsigned long rem_elem = Global_nElem - total_elem_accounted;
- for (unsigned long i = 0; i < rem_elem; i++) {
- ++nPointLinear[i];
- }
+ /*--- Allocate space for the interior elements in our SU2 data
+ structure. Note that we only instantiate our rank's local set. ---*/
+ elem = new CPrimalGrid*[nElem]();
- /*--- Store the local number of elements and the beginning/end index ---*/
- nElem = nPointLinear[rank];
- beg_node[0] = 0;
- end_node[0] = beg_node[0] + nPointLinear[0];
- for (int i = 1; i < size; i++) {
- beg_node[i] = end_node[i - 1];
- end_node[i] = beg_node[i] + nPointLinear[i];
- }
+ /*--- Loop over all of the internal, local volumetric elements. ---*/
+ unsigned long ind = 0;
+ unsigned long offsetSolDOFs = 0;
+ for (unsigned long jElem = 0; jElem < nElem; ++jElem) {
+ /*--- Create a FEM element from the data dataElems. ---*/
+ const auto* dataElem = dataElems.data() + ind;
+ elem[jElem] = new CPrimalGridFEM(dataElem, offsetSolDOFs);
- /*--- Allocate space for elements ---*/
- elem = new CPrimalGrid*[nElem]();
+ /*--- Store the global to local mapping in Global_to_Local_Elem. ---*/
+ Global_to_Local_Elem[dataElem[4]] = jElem;
- /*--- Loop over over the connectivities and read the elements to be stored on
- this rank. Furthermore, determine the local amount of DOFs for the
- solution (which may differ from the number of DOFS for the grid). ---*/
- unsigned long nDOFsLoc = 0, elemCount = 0, locElemCount = 0;
-
- for (int iConn = 0; iConn < nsections; ++iConn) {
- if (CGNSElemTypes[iConn].volumeConn) {
- /* Determine the global volume element range for this connectivity. */
- const unsigned long elemCountOld = elemCount;
- elemCount += CGNSElemTypes[iConn].nElem;
-
- /* Check for overlap with the element range this rank is responsible for. */
- const unsigned long indBegOverlap = max(elemCountOld, beg_node[rank]);
- const unsigned long indEndOverlap = min(elemCount, end_node[rank]);
-
- if (indEndOverlap > indBegOverlap) {
- /* This rank must read element data from this connectivity section.
- Determine the offset relative to the start of this section and the
- number of elements to be read by this rank. */
- const unsigned long offsetRank = indBegOverlap - elemCountOld;
- const unsigned long nElemRank = indEndOverlap - indBegOverlap;
-
- /* Read the connectivity range determined above. */
- CGNSElemTypes[iConn].ReadConnectivityRange(fn, iBase, iZone, offsetRank, nElemRank, beg_node[rank], elem,
- locElemCount, nDOFsLoc);
- }
- }
+ /*--- Update ind for the next element. ---*/
+ ind += dataElem[3] + 5;
}
#ifdef HAVE_MPI
- /* The global offset of the DOFs must be corrected when running in
+ /* The global offset of the solution DOFs must be corrected when running in
parallel. Therefore gather the number of DOFs of all the ranks. */
- vector nDOFsPerRank(size);
- SU2_MPI::Allgather(&nDOFsLoc, 1, MPI_UNSIGNED_LONG, nDOFsPerRank.data(), 1, MPI_UNSIGNED_LONG, SU2_MPI::GetComm());
+ vector nSolDOFsPerRank(size);
+ SU2_MPI::Allgather(&offsetSolDOFs, 1, MPI_UNSIGNED_LONG, nSolDOFsPerRank.data(), 1, MPI_UNSIGNED_LONG,
+ SU2_MPI::GetComm());
/* Determine the offset for the DOFs on this rank. */
unsigned long offsetRank = 0;
- for (int i = 0; i < rank; ++i) offsetRank += nDOFsPerRank[i];
+ for (int i = 0; i < rank; ++i) offsetRank += nSolDOFsPerRank[i];
/* Loop over the local elements to correct the global offset of the DOFs. */
- for (unsigned long i = 0; i < nElem; ++i) elem[i]->AddOffsetGlobalDOFs(offsetRank);
-#endif
-
- /*--------------------------------------------------------------------------*/
- /*--- Reading and distributing the coordinates. ---*/
- /*--------------------------------------------------------------------------*/
-
- /* Determine the global number of vertices in the requested zone.
- The other size information is not used. */
- cgsize_t sizes[3];
- if (cg_zone_read(fn, iBase, iZone, cgnsname, sizes) != CG_OK) cg_error_exit();
- Global_nPoint = sizes[0];
-
- /*--- Determine the number of points per rank in cumulative storage format.
- This is done to avoid that every rank reads all the coordinates.
- The required coordinates for each rank are later obtained via
- communication. ---*/
- unsigned long totalPointsAccounted = 0;
- vector nPointsPerRank(size + 1);
- for (int i = 1; i <= size; ++i) {
- nPointsPerRank[i] = Global_nPoint / size;
- totalPointsAccounted += nPointsPerRank[i];
- }
-
- const unsigned long nPointsRem = Global_nPoint - totalPointsAccounted;
- for (unsigned long i = 1; i <= nPointsRem; ++i) ++nPointsPerRank[i];
-
- nPointsPerRank[0] = 0;
- for (int i = 0; i < size; ++i) nPointsPerRank[i + 1] += nPointsPerRank[i];
-
- /* Determine the number of points that must be read by this rank and
- allocate the memory for the coordinate buffers. */
- const cgsize_t nPointsRead = nPointsPerRank[rank + 1] - nPointsPerRank[rank];
- vector > coorBuf(nDim, vector(nPointsRead));
-
- /* Loop over the number of dimensions to read the coordinates. Note that
- the loop starts at 1 and ends at nDim because CGNS requires this. */
- for (unsigned short iDim = 1; iDim <= nDim; ++iDim) {
- /* Determine the data type and name of the coordinate. Copy the name
- of the coordinate in a string for easier comparison. */
- DataType_t datatype;
- if (cg_coord_info(fn, iBase, iZone, iDim, &datatype, cgnsname) != CG_OK) cg_error_exit();
- string coorname = cgnsname;
-
- /* Check the name of the coordinate and determine the index in coorBuf
- where to store this coordinate. Normally this should be iDim-1. */
- unsigned short indC = 0;
- if (coorname == "CoordinateX")
- indC = 0;
- else if (coorname == "CoordinateY")
- indC = 1;
- else if (coorname == "CoordinateZ")
- indC = 2;
- else
- SU2_MPI::Error(string("Unknown coordinate name, ") + coorname + string(", encountered in the CGNS file."),
- CURRENT_FUNCTION);
-
- /* Easier storage of the range in the CGNS file. */
- cgsize_t range_min = nPointsPerRank[rank] + 1;
- cgsize_t range_max = nPointsPerRank[rank + 1];
-
- /*--- Read the coordinate with the required precision and copy
- this data to the correct index in coorBuf. ---*/
- switch (datatype) {
- case RealSingle: {
- /* Single precision used. */
- vector buf(nPointsRead);
- if (cg_coord_read(fn, iBase, iZone, cgnsname, datatype, &range_min, &range_max, buf.data()) != CG_OK)
- cg_error_exit();
-
- for (cgsize_t i = 0; i < nPointsRead; ++i) coorBuf[indC][i] = buf[i];
- break;
- }
-
- case RealDouble: {
- /* Double precision used. */
- vector buf(nPointsRead);
- if (cg_coord_read(fn, iBase, iZone, cgnsname, datatype, &range_min, &range_max, buf.data()) != CG_OK)
- cg_error_exit();
-
- for (cgsize_t i = 0; i < nPointsRead; ++i) coorBuf[indC][i] = buf[i];
- break;
- }
-
- default: {
- ostringstream message;
- message << "Datatype for coordinates must be RealSingle or RealDouble, "
- << "not " << datatype << endl;
- SU2_MPI::Error(message.str(), CURRENT_FUNCTION);
- }
- }
- }
-
- /* Make a distinction between sequential and parallel mode. */
-#ifdef HAVE_MPI
- /*--- Parallel mode. Create a vector, which contains the global
- node IDs of the local elements. ---*/
- vector nodeIDsElemLoc;
- nodeIDsElemLoc.reserve(nDOFsLoc);
- for (unsigned long i = 0; i < locElemCount; ++i) {
- unsigned short nDOFsElem = elem[i]->GetnNodes();
- for (unsigned short j = 0; j < nDOFsElem; ++j) nodeIDsElemLoc.push_back(elem[i]->GetNode(j));
- }
-
- sort(nodeIDsElemLoc.begin(), nodeIDsElemLoc.end());
- vector::iterator lastNode;
- lastNode = unique(nodeIDsElemLoc.begin(), nodeIDsElemLoc.end());
- nodeIDsElemLoc.erase(lastNode, nodeIDsElemLoc.end());
-
- /*--- Allocate the memory for the coordinates to be stored on this rank. ---*/
- nPoint = nodeIDsElemLoc.size();
- nodes = new CPoint(nPoint, nDim);
-
- /*--- Store the global ID's of the nodes in such a way that they can
- be sent to the rank that actually stores the coordinates.. ---*/
- vector > nodeBuf(size, vector(0));
- for (unsigned long i = 0; i < nodeIDsElemLoc.size(); ++i) {
- const cgsize_t nodeID = nodeIDsElemLoc[i];
- vector::iterator low;
- low = lower_bound(nPointsPerRank.begin(), nPointsPerRank.end(), nodeID);
- cgsize_t rankNode = low - nPointsPerRank.begin();
- if (*low > nodeID) --rankNode;
-
- nodeBuf[rankNode].push_back(nodeIDsElemLoc[i]);
- }
-
- /*--- Determine the total number of ranks to which this rank will send
- a message and also determine the number of ranks from which this
- rank will receive a message. Furthermore, determine the starting
- indices where data from the different ranks should be stored in
- node. ---*/
- int nRankSend = 0;
- vector sendToRank(size, 0);
- vector startingIndRanksInNode(size + 1);
- startingIndRanksInNode[0] = 0;
-
- for (int i = 0; i < size; ++i) {
- startingIndRanksInNode[i + 1] = startingIndRanksInNode[i] + nodeBuf[i].size();
-
- if (!nodeBuf[i].empty()) {
- ++nRankSend;
- sendToRank[i] = 1;
- }
- }
-
- int nRankRecv;
- vector sizeRecv(size, 1);
- SU2_MPI::Reduce_scatter(sendToRank.data(), &nRankRecv, sizeRecv.data(), MPI_INT, MPI_SUM, SU2_MPI::GetComm());
-
- /*--- Send out the messages with the global node numbers. Use nonblocking
- sends to avoid deadlock. ---*/
- vector sendReqs(nRankSend);
- nRankSend = 0;
- for (int i = 0; i < size; ++i) {
- if (!nodeBuf[i].empty()) {
- SU2_MPI::Isend(nodeBuf[i].data(), nodeBuf[i].size(), MPI_UNSIGNED_LONG, i, i, SU2_MPI::GetComm(),
- &sendReqs[nRankSend]);
- ++nRankSend;
- }
- }
-
- /* Define the communication buffer for the coordinates and the vector
- for the return communication requests. */
- vector returnReqs(nRankRecv);
- vector > coorReturnBuf(nRankRecv, vector(0));
-
- /*--- Loop over the number of ranks from which this rank receives global
- point numbers that should be stored on this rank. ---*/
- for (int i = 0; i < nRankRecv; ++i) {
- /* Block until a message arrives. Determine the source and size
- of the message. */
- SU2_MPI::Status status;
- SU2_MPI::Probe(MPI_ANY_SOURCE, rank, SU2_MPI::GetComm(), &status);
- int source = status.MPI_SOURCE;
-
- int sizeMess;
- SU2_MPI::Get_count(&status, MPI_UNSIGNED_LONG, &sizeMess);
-
- /* Allocate the memory for a buffer to receive this message and also
- for the buffer to return to coordinates. */
- vector nodeRecvBuf(sizeMess);
- coorReturnBuf[i].resize(nDim * sizeMess);
-
- /* Receive the message using a blocking receive. */
- SU2_MPI::Recv(nodeRecvBuf.data(), sizeMess, MPI_UNSIGNED_LONG, source, rank, SU2_MPI::GetComm(), &status);
-
- /*--- Loop over the nodes just received and fill the return communication
- buffer with the coordinates of the requested nodes. ---*/
- for (int j = 0; j < sizeMess; ++j) {
- const int jj = nDim * j;
- const long kk = nodeRecvBuf[j] - nPointsPerRank[rank];
- if (kk < 0 || kk >= nPointsRead)
- SU2_MPI::Error("Invalid point requested. This should not happen.", CURRENT_FUNCTION);
-
- for (unsigned short k = 0; k < nDim; ++k) coorReturnBuf[i][jj + k] = coorBuf[k][kk];
- }
-
- /* Send the buffer just filled back to the requesting rank.
- Use a non-blocking send to avoid deadlock. */
- SU2_MPI::Isend(coorReturnBuf[i].data(), coorReturnBuf[i].size(), MPI_DOUBLE, source, source + 1, SU2_MPI::GetComm(),
- &returnReqs[i]);
- }
-
- /* Loop over the ranks from which this rank has requested coordinates. */
- for (int i = 0; i < nRankSend; ++i) {
- /* Block until a message arrives. Determine the source of the message. */
- SU2_MPI::Status status;
- SU2_MPI::Probe(MPI_ANY_SOURCE, rank + 1, SU2_MPI::GetComm(), &status);
- int source = status.MPI_SOURCE;
-
- /* Allocate the memory for the coordinate receive buffer. */
- vector coorRecvBuf(nDim * nodeBuf[source].size());
-
- /* Receive the message using a blocking receive. */
- SU2_MPI::Recv(coorRecvBuf.data(), coorRecvBuf.size(), MPI_DOUBLE, source, rank + 1, SU2_MPI::GetComm(), &status);
-
- /*--- Make a distinction between 2D and 3D to store the data of the nodes.
- This data is created by taking the offset of the source rank into
- account. In this way the nodes are numbered with increading
- global node ID. ---*/
- for (unsigned long j = 0; j < nodeBuf[source].size(); ++j) {
- const unsigned long jj = nDim * j;
- const unsigned long kk = startingIndRanksInNode[source] + j;
-
- nodes->SetCoord(kk, &coorRecvBuf[jj]);
- nodes->SetGlobalIndex(kk, nodeBuf[source][j]);
- }
- }
-
- /* Complete the non-blocking sends of both rounds. */
- SU2_MPI::Waitall(sendReqs.size(), sendReqs.data(), MPI_STATUSES_IGNORE);
- SU2_MPI::Waitall(returnReqs.size(), returnReqs.data(), MPI_STATUSES_IGNORE);
-
- /* Wild cards have been used in the communication,
- so synchronize the ranks to avoid problems. */
- SU2_MPI::Barrier(SU2_MPI::GetComm());
-
-#else
- /*--- Sequential mode. Create the data for the points. The global
- number of points equals the local number of points. ---*/
- nPoint = Global_nPoint;
- nodes = new CPoint(nPoint, nDim);
-
- for (unsigned long i = 0; i < nPoint; ++i) {
- for (unsigned short iDim = 0; iDim < nDim; ++iDim) nodes->SetCoord(i, iDim, coorBuf[iDim][i]);
- nodes->SetGlobalIndex(i, i);
- }
-
-#endif
-
- /*--------------------------------------------------------------------------*/
- /*--- Determine and distribute the single faces of the elements. These ---*/
- /*--- faces are distributed over the ranks such that later the boundary ---*/
- /*--- can retrieve the info on which rank they must be stored without ---*/
- /*--- each rank having to read the entire connectivity data. ---*/
- /*--------------------------------------------------------------------------*/
-
- /*--- Determine the faces of the local elements. --- */
- vector localFaces;
- for (unsigned long k = 0; k < nElem; ++k) {
- /*--- Get the global IDs of the corner points of all the faces of this elements. ---*/
- unsigned short nFaces;
- unsigned short nPointsPerFace[6];
- unsigned long faceConn[6][4];
-
- elem[k]->GetCornerPointsAllFaces(nFaces, nPointsPerFace, faceConn);
-
- /*--- Loop over the faces and add them to localFaces. For consistency
- between sequential and parallel mode the rank is stored at the
- position for the second element ID. ---*/
- for (unsigned short i = 0; i < nFaces; ++i) {
- CFaceOfElement thisFace;
- thisFace.nCornerPoints = nPointsPerFace[i];
- for (unsigned short j = 0; j < nPointsPerFace[i]; ++j) thisFace.cornerPoints[j] = faceConn[i][j];
- thisFace.elemID0 = k + beg_node[rank];
- thisFace.elemID1 = rank;
-
- thisFace.CreateUniqueNumbering();
- localFaces.push_back(thisFace);
- }
- }
-
- /*--- Sort localFaces in increasing order and remove the double entities,
- such that unnecessary data is not communicated later on. ---*/
- sort(localFaces.begin(), localFaces.end());
- vector::iterator lastFace;
- lastFace = unique(localFaces.begin(), localFaces.end());
- localFaces.erase(lastFace, localFaces.end());
-
-#ifdef HAVE_MPI
-
- /*--- In parallel mode these faces must be distributed over the ranks.
- A face is stored on the rank where its first node ID is located
- based on nPointsPerRank. Define the communication buffers and
- determine their contents. ---*/
- vector > faceBuf(size, vector(0));
- for (unsigned long i = 0; i < localFaces.size(); ++i) {
- const cgsize_t nodeID = localFaces[i].cornerPoints[0];
- vector::iterator low;
- low = lower_bound(nPointsPerRank.begin(), nPointsPerRank.end(), nodeID);
- cgsize_t rankNode = low - nPointsPerRank.begin();
- if (*low > nodeID) --rankNode;
-
- faceBuf[rankNode].push_back(localFaces[i].nCornerPoints);
- for (unsigned short j = 0; j < localFaces[i].nCornerPoints; ++j)
- faceBuf[rankNode].push_back(localFaces[i].cornerPoints[j]);
- faceBuf[rankNode].push_back(localFaces[i].elemID0);
- }
-
- /* Delete the memory of localFaces again, because its contents will be
- build from the messages that this rank will receive. */
- localFaces.clear();
-
- /*--- Determine the number of messages this rank will send and receive. ---*/
- nRankSend = 0;
- for (int i = 0; i < size; ++i) {
- if (!faceBuf[i].empty()) {
- ++nRankSend;
- sendToRank[i] = 1;
- } else {
- sendToRank[i] = 0;
- }
- }
-
- SU2_MPI::Reduce_scatter(sendToRank.data(), &nRankRecv, sizeRecv.data(), MPI_INT, MPI_SUM, SU2_MPI::GetComm());
-
- /*--- Send the messages using non-blocking sends to avoid deadlock. ---*/
- sendReqs.resize(nRankSend);
- nRankSend = 0;
- for (int i = 0; i < size; ++i) {
- if (!faceBuf[i].empty()) {
- SU2_MPI::Isend(faceBuf[i].data(), faceBuf[i].size(), MPI_UNSIGNED_LONG, i, i + 4, SU2_MPI::GetComm(),
- &sendReqs[nRankSend]);
- ++nRankSend;
- }
- }
-
- /* Loop over the number of ranks from which this rank will receive data. */
- for (int i = 0; i < nRankRecv; ++i) {
- /* Block until a message arrives and determine the source and size
- of the message. */
- SU2_MPI::Status status;
- SU2_MPI::Probe(MPI_ANY_SOURCE, rank + 4, SU2_MPI::GetComm(), &status);
- int source = status.MPI_SOURCE;
-
- int sizeMess;
- SU2_MPI::Get_count(&status, MPI_UNSIGNED_LONG, &sizeMess);
-
- /* Allocate the memory for the receive buffer and receive the
- message using a non-blocking receive. */
- vector faceRecvBuf(sizeMess);
- SU2_MPI::Recv(faceRecvBuf.data(), faceRecvBuf.size(), MPI_UNSIGNED_LONG, source, rank + 4, SU2_MPI::GetComm(),
- &status);
-
- /* Loop to extract the data from the receive buffer. */
- int ii = 0;
- while (ii < sizeMess) {
- /* Store the data for this face in localFaces. The rank where the
- corresponding element is physically present is stored in the
- second element ID. Note that it is not necessary to create a unique
- numbering anymore, because this has already been done before the
- communication buffer was created. */
- CFaceOfElement thisFace;
- thisFace.nCornerPoints = (unsigned short)faceRecvBuf[ii++];
- for (unsigned short j = 0; j < thisFace.nCornerPoints; ++j, ++ii) thisFace.cornerPoints[j] = faceRecvBuf[ii];
- thisFace.elemID0 = faceRecvBuf[ii++];
-
- thisFace.elemID1 = source;
- localFaces.push_back(thisFace);
- }
- }
-
- /*--- Sort localFaces in increasing order and remove the double entities,
- such that searching is a bit more efficient later on. ---*/
- sort(localFaces.begin(), localFaces.end());
- lastFace = unique(localFaces.begin(), localFaces.end());
- localFaces.erase(lastFace, localFaces.end());
-
- /* Complete the non-blocking sends. Afterwards, synchronize the ranks,
- because wild cards have been used. */
- SU2_MPI::Waitall(sendReqs.size(), sendReqs.data(), MPI_STATUSES_IGNORE);
- SU2_MPI::Barrier(SU2_MPI::GetComm());
-
+ for (unsigned long jElem = 0; jElem < nElem; ++jElem) elem[jElem]->AddOffsetGlobalDOFs(offsetRank);
#endif
+}
- /*--------------------------------------------------------------------------*/
- /*--- Reading and distributing the surface elements. ---*/
- /*--------------------------------------------------------------------------*/
-
- /* Determine the number of families in this base and read their names.
- Note that when multiple zones are present, this step is repeated for
- every zone. */
- int nFamilies;
- if (cg_nfamilies(fn, iBase, &nFamilies) != CG_OK) cg_error_exit();
-
- vector familyNames(nFamilies);
- for (int i = 1; i <= nFamilies; ++i) {
- int nFamBC, nGeo;
- if (cg_family_read(fn, iBase, i, cgnsname, &nFamBC, &nGeo) != CG_OK) cg_error_exit();
- familyNames[i - 1] = cgnsname;
- }
-
- /* Determine the number of boundary conditions for this zone. */
- int nBCs;
- if (cg_nbocos(fn, iBase, iZone, &nBCs) != CG_OK) cg_error_exit();
-
- /* Read the names of the boundary conditions and determine their family names.
- If not family name is specified for a boundary condition, the family name
- is set to the name of the boundary condition. */
- vector BCNames(nBCs), BCFamilyNames(nBCs);
- for (int i = 1; i <= nBCs; ++i) {
- /* Read the info for this boundary condition. */
- BCType_t BCType;
- PointSetType_t ptsetType;
- cgsize_t npnts, NormalListSize;
- int NormalIndex, nDataSet;
- DataType_t NormalDataType;
- if (cg_boco_info(fn, iBase, iZone, i, cgnsname, &BCType, &ptsetType, &npnts, &NormalIndex, &NormalListSize,
- &NormalDataType, &nDataSet) != CG_OK)
- cg_error_exit();
- BCNames[i - 1] = cgnsname;
-
- /* Read the possibly family name and set it. If not present, it is
- equal to BCName. */
- if (cg_goto(fn, iBase, "Zone_t", iZone, "ZoneBC_t", 1, "BC_t", i, "end") != CG_OK) cg_error_exit();
-
- int ierr = cg_famname_read(cgnsname);
- if (ierr == CG_ERROR)
- cg_error_exit();
- else if (ierr == CG_OK)
- BCFamilyNames[i - 1] = cgnsname;
- else
- BCFamilyNames[i - 1] = BCNames[i - 1];
- }
-
- /*--- Determine the number of different surface connectivities. It is
- possible to specify a family name for a surface connectivity. If that
- family name is the same for multiple surface connectivities, these
- connectivities are merged together for the boundary condition
- treatment in the DG-FEM solver. ---*/
- vector surfaceNames;
- vector > surfaceConnIDs;
-
- for (int i = 0; i < nsections; ++i) {
- if (CGNSElemTypes[i].surfaceConn) {
- /*--- Determine the surface name to use for this connectivity.
- This name is determined as follows (in terms of importance).
- 1) Family name specified for this connectivity.
- 2) Family name of the corresponding boundary condition,
- if present.
- 3) Name of the connectivity. ---*/
- string thisSurfaceName;
- const int connID = CGNSElemTypes[i].connID;
-
- /* First try to read the family name for the connectivity. */
- if (cg_goto(fn, iBase, "Zone_t", iZone, "Elements_t", connID, "end") != CG_OK) cg_error_exit();
- int ierr = cg_famname_read(cgnsname);
- if (ierr == CG_ERROR)
- cg_error_exit();
- else if (ierr == CG_OK)
- thisSurfaceName = cgnsname;
- else {
- /* No family name. Check the boundary conditions. It is assumed that
- the boundary conditions have the same name as the connectivities. */
- int j;
- for (j = 0; j < nBCs; ++j) {
- if (BCNames[j] == CGNSElemTypes[i].connName) {
- thisSurfaceName = BCFamilyNames[j];
- break;
- }
- }
-
- /* If the name is not found in the boundary conditions, set the name
- of this surface connectivity to the name of this connectivity. */
- if (j == nBCs) thisSurfaceName = CGNSElemTypes[i].connName;
- }
-
- /* Loop over the previously stored surface names and check if this
- surface name is already present. */
- unsigned long j;
- for (j = 0; j < surfaceNames.size(); ++j) {
- if (thisSurfaceName == surfaceNames[j]) {
- surfaceConnIDs[j].push_back(i);
- }
- }
-
- /* If the surface name is not stored yet, create new entries in
- surfaceNames and surfaceConnIDs. */
- if (j == surfaceNames.size()) {
- surfaceNames.push_back(thisSurfaceName);
- vector thisSurfaceConn(1, i);
- surfaceConnIDs.push_back(thisSurfaceConn);
- }
- }
- }
-
- /* Write a message about the number of surface markers and allocate the
- memory for the data structures to store the required information. */
- nMarker = surfaceNames.size();
- if (rank == MASTER_NODE) cout << nMarker << " surface markers." << endl;
+void CPhysicalGeometry::LoadLinearlyPartitionedSurfaceElementsFEM(CConfig* config, CMeshReaderBase* mesh) {
+ /*--- Store the number of markers and print to the screen. ---*/
+ nMarker = mesh->GetNumberOfMarkers();
config->SetnMarker_All(nMarker);
+ if (rank == MASTER_NODE) cout << nMarker << " surface markers." << endl;
- unsigned short nMarker_Max = config->GetnMarker_Max();
-
+ /*--- Create the data structure for boundary elements. ---*/
bound = new CPrimalGrid**[nMarker];
nElem_Bound = new unsigned long[nMarker];
- Tag_to_Marker = new string[nMarker_Max];
-
- /* Loop over the number of markers to read and distribute the connectivities. */
- for (unsigned short iMarker = 0; iMarker < nMarker; ++iMarker) {
- /* Easier storage of the entries in CGNSElemTypes that contribute
- to this boundary marker. */
- const int nEntries = surfaceConnIDs[iMarker].size();
- const int* entries = surfaceConnIDs[iMarker].data();
-
- /* Determine the global number of elements for this boundary marker. */
- cgsize_t nElem_Bound_Global = 0;
- for (int iConn = 0; iConn < nEntries; ++iConn) nElem_Bound_Global += CGNSElemTypes[entries[iConn]].nElem;
-
- /* Write a message about the global number of surface elements
- present in this marker. */
- string Marker_Tag = surfaceNames[iMarker];
- if (rank == MASTER_NODE)
- cout << nElem_Bound_Global << " boundary elements in index " << iMarker << " (Marker = " << Marker_Tag << ")."
- << endl;
-
- /* Determine the number of surface elements per rank in cumulative storage
- format. This is done to avoid that every rank reads all the elements.
- This is to avoid that every rank reads all the elements. The correct
- rank for storage is determined later via communication. */
- unsigned long totalBoundElemAccounted = 0;
- vector nBoundElemPerRank(size + 1);
- for (int i = 1; i <= size; ++i) {
- nBoundElemPerRank[i] = nElem_Bound_Global / size;
- totalBoundElemAccounted += nBoundElemPerRank[i];
- }
-
- const unsigned long nBoundElemRem = nElem_Bound_Global - totalBoundElemAccounted;
- for (unsigned long i = 1; i <= nBoundElemRem; ++i) ++nBoundElemPerRank[i];
-
- nBoundElemPerRank[0] = 0;
- for (int i = 0; i < size; ++i) nBoundElemPerRank[i + 1] += nBoundElemPerRank[i];
-
- /* Define a vector of FEM boundary elements to store the local
- boundary faces to be read. */
- vector boundElems;
-
- /* Loop over the connectivity sections that contribute. */
- elemCount = locElemCount = 0;
- for (int iConn = 0; iConn < nEntries; ++iConn) {
- /* Determine the global range for this connectivity. */
- const unsigned long elemCountOld = elemCount;
- elemCount += CGNSElemTypes[entries[iConn]].nElem;
-
- /* Check for overlap with the element range this rank is responsible for. */
- const unsigned long indBegOverlap = max(elemCountOld, nBoundElemPerRank[rank]);
- const unsigned long indEndOverlap = min(elemCount, nBoundElemPerRank[rank + 1]);
-
- if (indEndOverlap > indBegOverlap) {
- /* This rank must read boundary element data from this connectivity
- section. Determine the offset relative to the start of this section
- and the number of elements to be read by this rank. */
- const unsigned long offsetRank = indBegOverlap - elemCountOld;
- const unsigned long nElemRank = indEndOverlap - indBegOverlap;
-
- /* Read the connectivity range determined above. */
- CGNSElemTypes[entries[iConn]].ReadBoundaryConnectivityRange(fn, iBase, iZone, offsetRank, nElemRank,
- nBoundElemPerRank[rank], locElemCount, boundElems);
- }
- }
-
- /* Make a distinction between sequential and parallel mode. */
-#ifdef HAVE_MPI
- /*--- Parallel mode. The information stored in boundElems must be
- communicated to find out where it must be stored. First clear the
- contents of the faceBuf, such that it can be used again to send
- data to the appropiate rank. ---*/
- for (int i = 0; i < size; ++i) faceBuf[i].clear();
-
- /*-- Loop over the locally read boundary elements and store its contents
- in the appropiate location in faceBuf. ---*/
- for (unsigned long i = 0; i < boundElems.size(); ++i) {
- /* Create an object of the class CFaceOfElement to determine the
- node number that determines on which rank the corresponding face
- in localFaces is stored. */
- CFaceOfElement thisFace(boundElems[i].VTK_Type, boundElems[i].nPolyGrid, boundElems[i].Nodes.data());
- thisFace.CreateUniqueNumbering();
-
- /* Determine the rank to which this face must be sent to. */
- const cgsize_t nodeID = thisFace.cornerPoints[0];
- vector::iterator low;
- low = lower_bound(nPointsPerRank.begin(), nPointsPerRank.end(), nodeID);
- cgsize_t rankNode = low - nPointsPerRank.begin();
- if (*low > nodeID) --rankNode;
-
- /*--- Copy the relevant data of this boundary element into faceBuf. ---*/
- faceBuf[rankNode].push_back(boundElems[i].VTK_Type);
- faceBuf[rankNode].push_back(boundElems[i].nPolyGrid);
- faceBuf[rankNode].push_back(boundElems[i].nDOFsGrid);
- faceBuf[rankNode].push_back(boundElems[i].globalBoundElemID);
- faceBuf[rankNode].insert(faceBuf[rankNode].end(), boundElems[i].Nodes.begin(), boundElems[i].Nodes.end());
- }
-
- /* The contents of boundElems is copied into faceBuf, so it can
- be released. */
- boundElems.clear();
-
- /*--- Determine the number of messages this rank will send and receive. ---*/
- nRankSend = 0;
- for (int i = 0; i < size; ++i) {
- if (!faceBuf[i].empty()) {
- ++nRankSend;
- sendToRank[i] = 1;
- } else {
- sendToRank[i] = 0;
- }
- }
-
- SU2_MPI::Reduce_scatter(sendToRank.data(), &nRankRecv, sizeRecv.data(), MPI_INT, MPI_SUM, SU2_MPI::GetComm());
-
- /*--- Send the messages using non-blocking sends to avoid deadlock. ---*/
- sendReqs.resize(nRankSend);
- nRankSend = 0;
- for (int i = 0; i < size; ++i) {
- if (!faceBuf[i].empty()) {
- SU2_MPI::Isend(faceBuf[i].data(), faceBuf[i].size(), MPI_UNSIGNED_LONG, i, i + 5, SU2_MPI::GetComm(),
- &sendReqs[nRankSend]);
- ++nRankSend;
- }
- }
-
- /* Use nodeBuf as storage for the sending of the surface element data
- to the correct rank. First clear its contents. */
- for (int i = 0; i < size; ++i) nodeBuf[i].clear();
-
- /*--- Loop over the number of ranks from which this rank receives
- surface elements to be processed. ---*/
- for (int i = 0; i < nRankRecv; ++i) {
- /* Block until a message arrives. Determine the source and size
- of the message. */
- SU2_MPI::Status status;
- SU2_MPI::Probe(MPI_ANY_SOURCE, rank + 5, SU2_MPI::GetComm(), &status);
- int source = status.MPI_SOURCE;
-
- int sizeMess;
- SU2_MPI::Get_count(&status, MPI_UNSIGNED_LONG, &sizeMess);
-
- /* Allocate the memory for the receive buffer and receive the message
- using a blocking send. */
- vector boundElemRecvBuf(sizeMess);
- SU2_MPI::Recv(boundElemRecvBuf.data(), sizeMess, MPI_UNSIGNED_LONG, source, rank + 5, SU2_MPI::GetComm(),
- &status);
-
- /* Loop to extract the data from the receive buffer. */
- int ii = 0;
- while (ii < sizeMess) {
- /* Store the data for this boundary element. */
- const auto VTK_Type = (unsigned short)boundElemRecvBuf[ii++];
- const auto nPolyGrid = (unsigned short)boundElemRecvBuf[ii++];
- const auto nDOFsGrid = (unsigned short)boundElemRecvBuf[ii++];
-
- const unsigned long globalBoundElemID = boundElemRecvBuf[ii++];
- const unsigned long* Nodes = boundElemRecvBuf.data() + ii;
- ii += nDOFsGrid;
-
- /* Determine the corner nodes and store them in an object of
- the class CFaceOfElement to carry out the search in localFaces. */
- CFaceOfElement thisFace(VTK_Type, nPolyGrid, Nodes);
-
- /* Check if the face is actually present. If not, print an error message
- and exit. */
- thisFace.CreateUniqueNumbering();
- vector::iterator low;
- low = lower_bound(localFaces.begin(), localFaces.end(), thisFace);
-
- bool thisFaceFound = false;
- if (low != localFaces.end()) {
- if (!(thisFace < *low)) thisFaceFound = true;
- }
-
- if (!thisFaceFound)
- SU2_MPI::Error("Boundary element not found in list of faces. This is a bug.", CURRENT_FUNCTION);
-
- /* Determine the domain element and the rank where
- this boundary element should be sent to.. */
- const unsigned long domainElementID = low->elemID0;
- const int rankBoundElem = (int)low->elemID1;
-
- /*--- Store the data for this element in the communication buffer
- for rankBoundElem. ---*/
- nodeBuf[rankBoundElem].push_back(VTK_Type);
- nodeBuf[rankBoundElem].push_back(nPolyGrid);
- nodeBuf[rankBoundElem].push_back(nDOFsGrid);
- nodeBuf[rankBoundElem].push_back(globalBoundElemID);
- nodeBuf[rankBoundElem].push_back(domainElementID);
-
- for (unsigned short j = 0; j < nDOFsGrid; ++j) nodeBuf[rankBoundElem].push_back(Nodes[j]);
- }
- }
-
- /* Complete the non-blocking sends. */
- SU2_MPI::Waitall(sendReqs.size(), sendReqs.data(), MPI_STATUSES_IGNORE);
-
- /*--- Determine the number of messages this rank will send and receive. ---*/
- nRankSend = 0;
- for (int i = 0; i < size; ++i) {
- if (!nodeBuf[i].empty()) {
- ++nRankSend;
- sendToRank[i] = 1;
- } else {
- sendToRank[i] = 0;
- }
- }
-
- SU2_MPI::Reduce_scatter(sendToRank.data(), &nRankRecv, sizeRecv.data(), MPI_INT, MPI_SUM, SU2_MPI::GetComm());
-
- /*--- Send the messages using non-blocking sends to avoid deadlock. ---*/
- sendReqs.resize(nRankSend);
- nRankSend = 0;
- for (int i = 0; i < size; ++i) {
- if (!nodeBuf[i].empty()) {
- SU2_MPI::Isend(nodeBuf[i].data(), nodeBuf[i].size(), MPI_UNSIGNED_LONG, i, i + 6, SU2_MPI::GetComm(),
- &sendReqs[nRankSend]);
- ++nRankSend;
- }
- }
-
- /*--- Loop over the number of ranks from which this rank receives
- surface elements to be stored on this rank. ---*/
- for (int i = 0; i < nRankRecv; ++i) {
- /* Block until a message arrives. Determine the source and size
- of the message. */
- SU2_MPI::Status status;
- SU2_MPI::Probe(MPI_ANY_SOURCE, rank + 6, SU2_MPI::GetComm(), &status);
- int source = status.MPI_SOURCE;
+ Tag_to_Marker = new string[config->GetnMarker_Max()];
+
+ /*--- Retrieve the name of the surface markers as well as
+ the number of surface elements for every marker. ---*/
+ const auto& sectionNames = mesh->GetMarkerNames();
+ const auto& nSurfElemPerMarker = mesh->GetNumberOfSurfaceElementsAllMarkers();
+
+ /*--- Loop over all sections that we extracted from the grid file
+ that were identified as boundary element sections so that we can
+ store those elements into our SU2 data structures. ---*/
+ for (int iMarker = 0; iMarker < nMarker; ++iMarker) {
+ /*--- Get the string name and set the number of surface elements
+ for this marker. ---*/
+ string Marker_Tag = sectionNames[iMarker];
+ nElem_Bound[iMarker] = nSurfElemPerMarker[iMarker];
+
+ /*--- Allocate the memory of the pointers for the surface
+ elements for this marker. ---*/
+ bound[iMarker] = new CPrimalGrid*[nElem_Bound[iMarker]];
- int sizeMess;
- SU2_MPI::Get_count(&status, MPI_UNSIGNED_LONG, &sizeMess);
+ /*--- Retrieve the boundary element data for this marker. ---*/
+ const auto& dataElems = mesh->GetSurfaceElementConnectivityForMarker(iMarker);
- /* Allocate the memory for the receive buffer and receive the message
- using a blocking send. */
- vector boundElemRecvBuf(sizeMess);
- SU2_MPI::Recv(boundElemRecvBuf.data(), sizeMess, MPI_UNSIGNED_LONG, source, rank + 6, SU2_MPI::GetComm(),
- &status);
+ /*--- Loop over the number of boundary elements for this marker. ---*/
+ unsigned long ind = 0;
+ for (unsigned long jElem = 0; jElem < nElem_Bound[iMarker]; ++jElem) {
+ /*--- Create a boundary FEM element from the data dataElems. ---*/
+ const auto* dataElem = dataElems.data() + ind;
+ bound[iMarker][jElem] = new CPrimalGridBoundFEM(dataElem);
- /* Loop to extract the data from the receive buffer. */
- int ii = 0;
- while (ii < sizeMess) {
- /* Store the data for this boundary element. */
- const auto VTK_Type = (unsigned short)boundElemRecvBuf[ii++];
- const auto nPolyGrid = (unsigned short)boundElemRecvBuf[ii++];
- const auto nDOFsGrid = (unsigned short)boundElemRecvBuf[ii++];
-
- const unsigned long globalBoundElemID = boundElemRecvBuf[ii++];
- const unsigned long domainElementID = boundElemRecvBuf[ii++];
- const unsigned long* Nodes = boundElemRecvBuf.data() + ii;
- ii += nDOFsGrid;
-
- /* Create an object of CBoundaryFace and store it in boundElems. */
- CBoundaryFace thisBoundFace;
- thisBoundFace.VTK_Type = VTK_Type;
- thisBoundFace.nPolyGrid = nPolyGrid;
- thisBoundFace.nDOFsGrid = nDOFsGrid;
- thisBoundFace.globalBoundElemID = globalBoundElemID;
- thisBoundFace.domainElementID = domainElementID;
-
- thisBoundFace.Nodes.resize(nDOFsGrid);
- for (unsigned short j = 0; j < nDOFsGrid; ++j) thisBoundFace.Nodes[j] = Nodes[j];
-
- boundElems.push_back(thisBoundFace);
- }
+ /*--- Update ind for the next element. ---*/
+ ind += dataElem[2] + 5;
}
- /* Sort boundElems in increasing order, where the less than operator
- is a comparison between the globalBoundElemID's. */
- sort(boundElems.begin(), boundElems.end());
-
- /* Complete the non-blocking sends and synchronize the processors,
- because wild cards have been used. */
- SU2_MPI::Waitall(sendReqs.size(), sendReqs.data(), MPI_STATUSES_IGNORE);
-
- SU2_MPI::Barrier(SU2_MPI::GetComm());
-
-#else
- /*--- Sequential mode. All boundary elements read must be stored on this
- rank. The only information missing is the global ID of the domain
- element of the boundary elements. This is created below. ---*/
- for (unsigned long i = 0; i < boundElems.size(); ++i) {
- /* Determine the corner nodes and store them in an object of
- the class CFaceOfElement to carry out the search in localFaces. */
- CFaceOfElement thisFace(boundElems[i].VTK_Type, boundElems[i].nPolyGrid, boundElems[i].Nodes.data());
-
- /* Check if the face is actually present. If not, print an error message
- and exit. */
- thisFace.CreateUniqueNumbering();
- vector::iterator low;
- low = lower_bound(localFaces.begin(), localFaces.end(), thisFace);
-
- bool thisFaceFound = false;
- if (low != localFaces.end()) {
- if (!(thisFace < *low)) thisFaceFound = true;
- }
-
- if (!thisFaceFound)
- SU2_MPI::Error("Boundary element not found in list of faces. This is a bug.", CURRENT_FUNCTION);
-
- /* Set the domain element. */
- boundElems[i].domainElementID = low->elemID0;
- }
-#endif
-
- /*--- Allocate space for the local boundary elements and copy the data
- from boundElems into bound. ---*/
- nElem_Bound[iMarker] = boundElems.size();
- bound[iMarker] = new CPrimalGrid*[nElem_Bound[iMarker]];
-
- for (unsigned long i = 0; i < nElem_Bound[iMarker]; ++i)
- bound[iMarker][i] = new CPrimalGridBoundFEM(boundElems[i].globalBoundElemID, boundElems[i].domainElementID,
- boundElems[i].VTK_Type, boundElems[i].nPolyGrid,
- boundElems[i].nDOFsGrid, boundElems[i].Nodes);
-
- /*--- Update config information storing the boundary information in the right place ---*/
+ /*--- Update config file lists in order to store the boundary
+ information for this marker in the correct place. ---*/
Tag_to_Marker[config->GetMarker_CfgFile_TagBound(Marker_Tag)] = Marker_Tag;
config->SetMarker_All_TagBound(iMarker, Marker_Tag);
config->SetMarker_All_KindBC(iMarker, config->GetMarker_CfgFile_KindBC(Marker_Tag));
@@ -1841,26 +475,15 @@ void CPhysicalGeometry::Read_CGNS_Format_Parallel_FEM(CConfig* config, const str
config->SetMarker_All_ZoneInterface(iMarker, config->GetMarker_CfgFile_ZoneInterface(Marker_Tag));
config->SetMarker_All_DV(iMarker, config->GetMarker_CfgFile_DV(Marker_Tag));
config->SetMarker_All_Moving(iMarker, config->GetMarker_CfgFile_Moving(Marker_Tag));
- config->SetMarker_All_SobolevBC(iMarker, config->GetMarker_CfgFile_SobolevBC(Marker_Tag));
+ config->SetMarker_All_Deform_Mesh(iMarker, config->GetMarker_CfgFile_Deform_Mesh(Marker_Tag));
+ config->SetMarker_All_Fluid_Load(iMarker, config->GetMarker_CfgFile_Fluid_Load(Marker_Tag));
+ config->SetMarker_All_PyCustom(iMarker, config->GetMarker_CfgFile_PyCustom(Marker_Tag));
config->SetMarker_All_PerBound(iMarker, config->GetMarker_CfgFile_PerBound(Marker_Tag));
config->SetMarker_All_SendRecv(iMarker, NONE);
+ config->SetMarker_All_Turbomachinery(iMarker, config->GetMarker_CfgFile_Turbomachinery(Marker_Tag));
+ config->SetMarker_All_TurbomachineryFlag(iMarker, config->GetMarker_CfgFile_TurbomachineryFlag(Marker_Tag));
+ config->SetMarker_All_MixingPlaneInterface(iMarker, config->GetMarker_CfgFile_MixingPlaneInterface(Marker_Tag));
}
-
- /* Close the CGNS file again. */
- if (cg_close(fn) != CG_OK) cg_error_exit();
- if (rank == MASTER_NODE) cout << "Successfully closed the CGNS file." << endl;
-
-#else /* CGNS_VERSION >= 3300 */
-
- SU2_MPI::Error("CGNS version 3.3 or higher is necessary for the DG FEM solver", CURRENT_FUNCTION);
-
-#endif /* CGNS_VERSION >= 3300 */
-
-#else /* HAVE_CGNS. */
-
- SU2_MPI::Error("SU2 built without CGNS support!!\nTo use CGNS, build SU2 accordingly.", CURRENT_FUNCTION);
-
-#endif /* HAVE_CGNS. */
}
void CPhysicalGeometry::SetColorFEMGrid_Parallel(CConfig* config) {
@@ -1882,7 +505,7 @@ void CPhysicalGeometry::SetColorFEMGrid_Parallel(CConfig* config) {
CFaceOfElement thisFace;
thisFace.nCornerPoints = nPointsPerFace[i];
for (unsigned short j = 0; j < nPointsPerFace[i]; ++j) thisFace.cornerPoints[j] = faceConn[i][j];
- thisFace.elemID0 = beg_node[rank] + k;
+ thisFace.elemID0 = elem[k]->GetGlobalElemID();
thisFace.nPolySol0 = elem[k]->GetNPolySol();
thisFace.nDOFsElem0 = elem[k]->GetNDOFsSol();
thisFace.elemType0 = elem[k]->GetVTK_Type();
@@ -1986,7 +609,7 @@ void CPhysicalGeometry::SetColorFEMGrid_Parallel(CConfig* config) {
the points that occur in the faces of localFacesComm. ---*/
vector facePointsProc(size + 1, 0);
unsigned long total_point_accounted = 0;
- for (unsigned long i = 1; i <= (unsigned long)size; ++i) {
+ for (unsigned long i = 1; i <= static_cast(size); ++i) {
facePointsProc[i] = maxPointID / size;
total_point_accounted += facePointsProc[i];
}
@@ -1994,7 +617,7 @@ void CPhysicalGeometry::SetColorFEMGrid_Parallel(CConfig* config) {
unsigned long rem_point = maxPointID - total_point_accounted;
for (unsigned long i = 1; i <= rem_point; ++i) ++facePointsProc[i];
- for (unsigned long i = 0; i < (unsigned long)size; ++i) facePointsProc[i + 1] += facePointsProc[i];
+ for (unsigned long i = 0; i < static_cast(size); ++i) facePointsProc[i + 1] += facePointsProc[i];
/*--- Determine the number of faces that has to be sent to each rank.
Note that the rank is stored in elemID1, such that the search
@@ -2014,7 +637,8 @@ void CPhysicalGeometry::SetColorFEMGrid_Parallel(CConfig* config) {
vector sendBufFace(9 * nFacesLocComm);
vector counter(size);
counter[0] = 0;
- for (unsigned long i = 1; i < (unsigned long)size; ++i) counter[i] = counter[i - 1] + 9 * nFacesComm[i - 1];
+ for (unsigned long i = 1; i < static_cast(size); ++i)
+ counter[i] = counter[i - 1] + 9 * nFacesComm[i - 1];
for (unsigned long i = 0; i < nFacesLocComm; ++i) {
unsigned long rankFace = localFacesComm[i].elemID1;
@@ -2034,7 +658,7 @@ void CPhysicalGeometry::SetColorFEMGrid_Parallel(CConfig* config) {
/*--- Determine the number of ranks from which I receive a message. */
unsigned long nMessSend = 0;
- for (unsigned long i = 0; i < (unsigned long)size; ++i) {
+ for (unsigned long i = 0; i < static_cast(size); ++i) {
if (nFacesComm[i]) {
counter[i] = 1;
++nMessSend;
@@ -2051,7 +675,7 @@ void CPhysicalGeometry::SetColorFEMGrid_Parallel(CConfig* config) {
nMessSend = 0;
unsigned long indSend = 0;
- for (unsigned long i = 0; i < (unsigned long)size; ++i) {
+ for (unsigned long i = 0; i < static_cast(size); ++i) {
if (nFacesComm[i]) {
unsigned long count = 9 * nFacesComm[i];
SU2_MPI::Isend(&sendBufFace[indSend], count, MPI_UNSIGNED_LONG, i, i, SU2_MPI::GetComm(), &commReqs[nMessSend]);
@@ -2169,7 +793,7 @@ void CPhysicalGeometry::SetColorFEMGrid_Parallel(CConfig* config) {
sizeMess /= 9;
unsigned long jj = 0;
- for (unsigned long j = 0; j < (unsigned long)sizeMess; ++j, jj += 9) {
+ for (unsigned long j = 0; j < static_cast(sizeMess); ++j, jj += 9) {
CFaceOfElement thisFace;
thisFace.nCornerPoints = recvBuf[jj];
thisFace.cornerPoints[0] = recvBuf[jj + 1];
@@ -2241,24 +865,26 @@ void CPhysicalGeometry::SetColorFEMGrid_Parallel(CConfig* config) {
map mapExternalElemIDToTimeLevel;
DetermineTimeLevelElements(config, localFaces, mapExternalElemIDToTimeLevel);
+ /*--- Define the linear partitioning of the elements. ---*/
+ CLinearPartitioner elemPartitioner(Global_nElem, 0);
+
/*--- Determine the ownership of the internal faces, i.e. which adjacent
element is responsible for computing the fluxes through the face. ---*/
for (unsigned long i = 0; i < nFacesLoc; ++i) {
/* Check for a matching face. */
if (localFaces[i].elemID1 < Global_nElem) {
- /* Determine the time level for both elements. Elem0 is always owned, while
- elem1 is either owned or external. The data for external elements is
- stored in mapExternalElemIDToTimeLevel. */
- unsigned long elemID0 = localFaces[i].elemID0 - beg_node[rank];
- unsigned short timeLevel0 = elem[elemID0]->GetTimeLevel();
+ /*--- Determine the time level of Elem0, which is always owned. ---*/
+ const unsigned long elemID0 = localFaces[i].elemID0 - elemPartitioner.GetFirstIndexOnRank(rank);
+ const unsigned short timeLevel0 = elem[elemID0]->GetTimeLevel();
+ /*--- Determine the time level of Elem1, which is either owned or
+ external. Hence a distinction must be made. ---*/
unsigned short timeLevel1;
- if (localFaces[i].elemID1 >= beg_node[rank] && localFaces[i].elemID1 < beg_node[rank] + nElem) {
- unsigned long elemID1 = localFaces[i].elemID1 - beg_node[rank];
+ if (elemPartitioner.GetRankContainingIndex(localFaces[i].elemID1) == static_cast(rank)) {
+ const unsigned long elemID1 = localFaces[i].elemID1 - elemPartitioner.GetFirstIndexOnRank(rank);
timeLevel1 = elem[elemID1]->GetTimeLevel();
} else {
- map::const_iterator MI;
- MI = mapExternalElemIDToTimeLevel.find(localFaces[i].elemID1);
+ const auto MI = mapExternalElemIDToTimeLevel.find(localFaces[i].elemID1);
if (MI == mapExternalElemIDToTimeLevel.end())
SU2_MPI::Error("Entry not found in mapExternalElemIDToTimeLevel", CURRENT_FUNCTION);
timeLevel1 = MI->second.short0;
@@ -2308,7 +934,7 @@ void CPhysicalGeometry::SetColorFEMGrid_Parallel(CConfig* config) {
CFaceOfElement thisFace;
thisFace.nCornerPoints = nPointsPerFace[i];
for (unsigned short j = 0; j < nPointsPerFace[i]; ++j) thisFace.cornerPoints[j] = faceConn[i][j];
- thisFace.elemID0 = beg_node[rank] + k;
+ thisFace.elemID0 = k + elemPartitioner.GetFirstIndexOnRank(rank);
thisFace.nPolySol0 = elem[k]->GetNPolySol();
thisFace.nDOFsElem0 = elem[k]->GetNDOFsSol();
@@ -2337,12 +963,18 @@ void CPhysicalGeometry::SetColorFEMGrid_Parallel(CConfig* config) {
of the graph. First the faces. ---*/
vector > adjacency(nElem, vector(0));
for (unsigned long i = 0; i < nFacesLoc; ++i) {
- long ii = localFaces[i].elemID0 - beg_node[rank];
- adjacency[ii].push_back(localFaces[i].elemID1);
+ /*--- Determine the local index of elem0, which is always stored locally,
+ and add elemID1 to the adjacency list. ---*/
+ const unsigned long elem0 = localFaces[i].elemID0 - elemPartitioner.GetFirstIndexOnRank(rank);
+ adjacency[elem0].push_back(localFaces[i].elemID1);
+ /*--- Check if this is not a periodic face and if the second element is
+ also a local element. If so, add elemID0 to the adjacency list ---*/
if (localFaces[i].periodicIndex == 0) {
- ii = localFaces[i].elemID1 - beg_node[rank];
- if (ii >= 0 && ii < (long)nElem) adjacency[ii].push_back(localFaces[i].elemID0);
+ if (elemPartitioner.GetRankContainingIndex(localFaces[i].elemID1) == static_cast(rank)) {
+ const unsigned long elem1 = localFaces[i].elemID1 - elemPartitioner.GetFirstIndexOnRank(rank);
+ adjacency[elem1].push_back(localFaces[i].elemID0);
+ }
}
}
@@ -2360,7 +992,7 @@ void CPhysicalGeometry::SetColorFEMGrid_Parallel(CConfig* config) {
are present. ParMETIS is not able to deal with self entries, hence
they must be removed as well. */
for (unsigned long i = 0; i < nElem; ++i) {
- const unsigned long globalElemID = i + beg_node[rank];
+ const unsigned long globalElemID = i + +elemPartitioner.GetFirstIndexOnRank(rank);
unsigned long nEntriesNew = adjacency[i].size();
for (unsigned long j = 0; j < adjacency[i].size(); ++j) {
@@ -2383,7 +1015,7 @@ void CPhysicalGeometry::SetColorFEMGrid_Parallel(CConfig* config) {
for (unsigned long l = 0; l < nElem_Bound[iMarker]; ++l) {
/* Get the global and local element ID adjacent to this boundary face. */
const unsigned long globalElemID = bound[iMarker][l]->GetDomainElement();
- const unsigned long elemID = globalElemID - beg_node[rank];
+ const unsigned long elemID = globalElemID - elemPartitioner.GetFirstIndexOnRank(rank);
/* Get the number of donor elements for the wall function treatment
and the pointer to the array which stores this info. */
@@ -2401,10 +1033,10 @@ void CPhysicalGeometry::SetColorFEMGrid_Parallel(CConfig* config) {
sort(adjacency[elemID].begin(), adjacency[elemID].end());
/* Check if the donor element is stored locally. */
- if (donors[i] >= beg_node[rank] && donors[i] < beg_node[rank] + nElem) {
+ if (elemPartitioner.GetRankContainingIndex(donors[i]) == static_cast(rank)) {
/* Donor is stored locally. Add the entry to the graph
and sort it afterwards. */
- const unsigned long localDonorID = donors[i] - beg_node[rank];
+ const unsigned long localDonorID = donors[i] - elemPartitioner.GetFirstIndexOnRank(rank);
adjacency[localDonorID].push_back(globalElemID);
sort(adjacency[localDonorID].begin(), adjacency[localDonorID].end());
} else {
@@ -2426,18 +1058,9 @@ void CPhysicalGeometry::SetColorFEMGrid_Parallel(CConfig* config) {
vector sendToRank(size, 0);
for (unsigned long i = 0; i < additionalExternalEntriesGraph.size(); i += 2) {
- /* Determine the rank where this external is stored. */
- const unsigned long elemID = additionalExternalEntriesGraph[i];
- int rankElem;
- if (elemID >= beg_node[size - 1])
- rankElem = size - 1;
- else {
- const unsigned long* low;
- low = lower_bound(beg_node, beg_node + size, elemID);
-
- rankElem = low - beg_node;
- if (*low > elemID) --rankElem;
- }
+ /*--- Determine the rank where this external is stored and update
+ the corresponding communication buffers accordingly. ---*/
+ const unsigned long rankElem = elemPartitioner.GetRankContainingIndex(additionalExternalEntriesGraph[i]);
sendBufsGraphData[rankElem].push_back(additionalExternalEntriesGraph[i]);
sendBufsGraphData[rankElem].push_back(additionalExternalEntriesGraph[i + 1]);
@@ -2483,7 +1106,7 @@ void CPhysicalGeometry::SetColorFEMGrid_Parallel(CConfig* config) {
/* Loop over the contents of the receive buffer and update the
graph accordingly. */
for (int j = 0; j < sizeMess; j += 2) {
- const unsigned long elemID = recvBuf[j] - beg_node[rank];
+ const unsigned long elemID = recvBuf[j] - elemPartitioner.GetFirstIndexOnRank(rank);
adjacency[elemID].push_back(recvBuf[j + 1]);
sort(adjacency[elemID].begin(), adjacency[elemID].end());
}
@@ -2526,8 +1149,7 @@ void CPhysicalGeometry::SetColorFEMGrid_Parallel(CConfig* config) {
/*--- Determine the array, which stores the distribution of the graph nodes
over the ranks. ---*/
vector vtxdist(size + 1);
- vtxdist[0] = 0;
- for (int i = 0; i < size; ++i) vtxdist[i + 1] = (idx_t)end_node[i];
+ for (int i = 0; i <= size; ++i) vtxdist[i] = static_cast(elemPartitioner.GetCumulativeSizeBeforeRank(i));
/* Create the array xadjPar, which contains the number of edges for each
vertex of the graph in ParMETIS format. */
@@ -3329,6 +1951,9 @@ void CPhysicalGeometry::DetermineDonorElementsWallFunctions(CConfig* config) {
/*--- the local elements. ---*/
/*--------------------------------------------------------------------------*/
+ /*--- Define the linear partitioning of the elements. ---*/
+ CLinearPartitioner elemPartitioner(Global_nElem, 0);
+
/* Define the vectors, which store the boundary marker, boundary element ID
and exchange coordinates for the integration points for which no donor
element was found in the locally stored volume elements. */
@@ -3356,7 +1981,8 @@ void CPhysicalGeometry::DetermineDonorElementsWallFunctions(CConfig* config) {
/* Easier storage of the element type, the corresponding volume
element and the polynomial degree for the solution and grid. */
const unsigned short VTK_Type = bound[iMarker][l]->GetVTK_Type();
- const unsigned long elemID = bound[iMarker][l]->GetDomainElement() - beg_node[rank];
+ const unsigned long elemID =
+ bound[iMarker][l]->GetDomainElement() - elemPartitioner.GetFirstIndexOnRank(rank);
const unsigned short nPolyGrid = bound[iMarker][l]->GetNPolyGrid();
const unsigned short nPolySol = elem[elemID]->GetNPolySol();
const unsigned short VTK_Elem = elem[elemID]->GetVTK_Type();
@@ -3615,7 +2241,7 @@ void CPhysicalGeometry::DetermineDonorElementsWallFunctions(CConfig* config) {
carried out for each rank and store them in such a way that the info can
be used directly in Allgatherv. */
vector recvCounts(size), displs(size);
- int nLocalSearchPoints = (int)markerIDGlobalSearch.size();
+ int nLocalSearchPoints = static_cast(markerIDGlobalSearch.size());
SU2_MPI::Allgather(&nLocalSearchPoints, 1, MPI_INT, recvCounts.data(), 1, MPI_INT, SU2_MPI::GetComm());
displs[0] = 0;
@@ -3787,6 +2413,9 @@ void CPhysicalGeometry::DetermineDonorElementsWallFunctions(CConfig* config) {
void CPhysicalGeometry::DetermineTimeLevelElements(CConfig* config, const vector& localFaces,
map& mapExternalElemIDToTimeLevel) {
+ /*--- Define the linear partitioning of the elements. ---*/
+ CLinearPartitioner elemPartitioner(Global_nElem, 0);
+
/*--------------------------------------------------------------------------*/
/*--- Step 1: Initialize the map mapExternalElemIDToTimeLevel. ---*/
/*--------------------------------------------------------------------------*/
@@ -3796,7 +2425,10 @@ void CPhysicalGeometry::DetermineTimeLevelElements(CConfig* config, const vector
for (auto FI = localFaces.begin(); FI != localFaces.end(); ++FI) {
if (FI->elemID1 < Global_nElem) { // Safeguard against non-matching faces.
- if (FI->elemID1 < beg_node[rank] || FI->elemID1 >= beg_node[rank] + nElem) {
+ /*--- Check for external element. This is done by checking the
+ local elements and if it is not found, it is an external. ---*/
+ const auto UMI = Global_to_Local_Elem.find(FI->elemID1);
+ if (UMI == Global_to_Local_Elem.end()) {
/* This element is an external element. Store it in the map
mapExternalElemIDToTimeLevel if not already done so. */
map::iterator MI;
@@ -3823,30 +2455,22 @@ void CPhysicalGeometry::DetermineTimeLevelElements(CConfig* config, const vector
const unsigned short nDonors = bound[iMarker][l]->GetNDonorsWallFunctions();
const unsigned long* donors = bound[iMarker][l]->GetDonorsWallFunctions();
- /* Loop over the number of donors and add the externals to
- mapExternalElemIDToTimeLevel, if not already present. */
+ /*--- Loop over the number of donors for this boundary element. ---*/
for (unsigned short i = 0; i < nDonors; ++i) {
- if (donors[i] < beg_node[rank] || donors[i] >= beg_node[rank] + nElem) {
- map::iterator MI;
- MI = mapExternalElemIDToTimeLevel.find(donors[i]);
+ /*--- Check if the donor element is an external element. ---*/
+ const auto UMI = Global_to_Local_Elem.find(donors[i]);
+ if (UMI == Global_to_Local_Elem.end()) {
+ /*--- Check if element is not already present in
+ mapExternalElemIDToTimeLevel. If not, add it. ---*/
+ const auto MI = mapExternalElemIDToTimeLevel.find(donors[i]);
if (MI == mapExternalElemIDToTimeLevel.end()) {
- /* Element not present in external. Add it. */
mapExternalElemIDToTimeLevel[donors[i]] = CUnsignedShort2T(0, 0);
}
- /* The reverse connection may not be present either. Store the global
- ID of this element in the send buffers for the additional
- externals. */
- int rankDonor;
- if (donors[i] >= beg_node[size - 1])
- rankDonor = size - 1;
- else {
- const unsigned long* low;
- low = lower_bound(beg_node, beg_node + size, donors[i]);
-
- rankDonor = (int)(low - beg_node);
- if (*low > donors[i]) --rankDonor;
- }
+ /*--- The reverse connection may not be present either. Store the global
+ ID of this element in the send buffers for the additional
+ externals. ---*/
+ const unsigned long rankDonor = elemPartitioner.GetRankContainingIndex(donors[i]);
sendBufAddExternals[rankDonor].push_back(bound[iMarker][l]->GetDomainElement());
recvFromRank[rankDonor] = 1;
@@ -4006,20 +2630,9 @@ void CPhysicalGeometry::DetermineTimeLevelElements(CConfig* config, const vector
recvFromRank.assign(size, 0);
for (MI = mapExternalElemIDToTimeLevel.begin(); MI != mapExternalElemIDToTimeLevel.end(); ++MI) {
- /* Determine the rank where this external is stored. */
- const unsigned long elemID = MI->first;
- int rankElem;
- if (elemID >= beg_node[size - 1])
- rankElem = size - 1;
- else {
- const unsigned long* low;
- low = lower_bound(beg_node, beg_node + size, elemID);
-
- rankElem = low - beg_node;
- if (*low > elemID) --rankElem;
- }
-
- /* Set the corresponding index of recvFromRank to 1. */
+ /*--- Determine the rank where this external is stored and set
+ the corresponding index of recvFromRank to 1. ---*/
+ const unsigned long rankElem = elemPartitioner.GetRankContainingIndex(MI->first);
recvFromRank[rankElem] = 1;
}
@@ -4042,18 +2655,9 @@ void CPhysicalGeometry::DetermineTimeLevelElements(CConfig* config, const vector
for (MI = mapExternalElemIDToTimeLevel.begin(); MI != mapExternalElemIDToTimeLevel.end(); ++MI) {
const unsigned long elemID = MI->first;
- int rankElem;
- if (elemID >= beg_node[size - 1])
- rankElem = size - 1;
- else {
- const unsigned long* low;
- low = lower_bound(beg_node, beg_node + size, elemID);
-
- rankElem = low - beg_node;
- if (*low > elemID) --rankElem;
- }
+ const unsigned long rankElem = elemPartitioner.GetRankContainingIndex(elemID);
- map::const_iterator MRI = mapRankToIndRecv.find(rankElem);
+ const auto MRI = mapRankToIndRecv.find(rankElem);
recvElem[MRI->second].push_back(elemID);
}
@@ -4089,7 +2693,7 @@ void CPhysicalGeometry::DetermineTimeLevelElements(CConfig* config, const vector
SU2_MPI::Recv(sendElem[i].data(), sizeMess, MPI_UNSIGNED_LONG, sendRank[i], rank, SU2_MPI::GetComm(), &status);
- for (int j = 0; j < sizeMess; ++j) sendElem[i][j] -= beg_node[rank];
+ for (int j = 0; j < sizeMess; ++j) sendElem[i][j] -= elemPartitioner.GetFirstIndexOnRank(rank);
}
/* Complete the non-blocking sends. Synchronize the processors afterwards,
@@ -4180,7 +2784,8 @@ void CPhysicalGeometry::DetermineTimeLevelElements(CConfig* config, const vector
for (unsigned short iMarker = 0; iMarker < nMarker; ++iMarker) {
for (unsigned long l = 0; l < nElem_Bound[iMarker]; ++l) {
/* Determine the ID of the adjacent element. */
- const unsigned long elemID = bound[iMarker][l]->GetDomainElement() - beg_node[rank];
+ const unsigned long elemID =
+ bound[iMarker][l]->GetDomainElement() - elemPartitioner.GetFirstIndexOnRank(rank);
/* Get the number of donor elements for the wall function treatment
and the pointer to the array which stores this info. */
@@ -4190,10 +2795,10 @@ void CPhysicalGeometry::DetermineTimeLevelElements(CConfig* config, const vector
/* Loop over the number of donors and check the time levels. */
for (unsigned short i = 0; i < nDonors; ++i) {
/* Determine the status of the donor element. */
- if (donors[i] >= beg_node[rank] && donors[i] < beg_node[rank] + nElem) {
+ if (elemPartitioner.GetRankContainingIndex(donors[i]) == static_cast(rank)) {
/* Donor is stored locally. Determine its local ID and
get the time levels of both elements. */
- const unsigned long donorID = donors[i] - beg_node[rank];
+ const unsigned long donorID = donors[i] - elemPartitioner.GetFirstIndexOnRank(rank);
const unsigned short timeLevelB = elem[elemID]->GetTimeLevel();
const unsigned short timeLevelD = elem[donorID]->GetTimeLevel();
const unsigned short timeLevel = min(timeLevelB, timeLevelD);
@@ -4244,15 +2849,15 @@ void CPhysicalGeometry::DetermineTimeLevelElements(CConfig* config, const vector
if (FI->elemID1 < Global_nElem) {
/* Local element ID of the first element. Per definition this is
always a locally stored element. Also store its time level. */
- const unsigned long elemID0 = FI->elemID0 - beg_node[rank];
+ const unsigned long elemID0 = FI->elemID0 - elemPartitioner.GetFirstIndexOnRank(rank);
const unsigned short timeLevel0 = elem[elemID0]->GetTimeLevel();
/* Determine the status of the second element. */
- if (FI->elemID1 >= beg_node[rank] && FI->elemID1 < beg_node[rank] + nElem) {
+ if (elemPartitioner.GetRankContainingIndex(FI->elemID1) == static_cast(rank)) {
/* Both elements are stored locally. Determine the local
element of the second element and determine the minimum
time level. */
- const unsigned long elemID1 = FI->elemID1 - beg_node[rank];
+ const unsigned long elemID1 = FI->elemID1 - elemPartitioner.GetFirstIndexOnRank(rank);
const unsigned short timeLevel1 = elem[elemID1]->GetTimeLevel();
const unsigned short timeLevel = min(timeLevel0, timeLevel1);
@@ -4400,6 +3005,9 @@ void CPhysicalGeometry::ComputeFEMGraphWeights(CConfig* config, const vector >& adjacency,
const map& mapExternalElemIDToTimeLevel,
vector& vwgt, vector >& adjwgt) {
+ /*--- Define the linear partitioning of the elements. ---*/
+ CLinearPartitioner elemPartitioner(Global_nElem, 0);
+
/*--- Determine the maximum time level that occurs in the grid. ---*/
unsigned short maxTimeLevel = 0;
for (unsigned long i = 0; i < nElem; ++i) maxTimeLevel = max(maxTimeLevel, elem[i]->GetTimeLevel());
@@ -4461,7 +3069,7 @@ void CPhysicalGeometry::ComputeFEMGraphWeights(CConfig* config, const vectorGetVTK_Type();
- const unsigned long elemID = bound[iMarker][l]->GetDomainElement() - beg_node[rank];
+ const unsigned long elemID =
+ bound[iMarker][l]->GetDomainElement() - elemPartitioner.GetFirstIndexOnRank(rank);
const unsigned short nPolySol = elem[elemID]->GetNPolySol();
const unsigned short VTK_Type_Elem = elem[elemID]->GetVTK_Type();
const bool JacIsConstant = bound[iMarker][l]->GetJacobianConsideredConstant();
@@ -4661,17 +3270,16 @@ void CPhysicalGeometry::ComputeFEMGraphWeights(CConfig* config, const vector= beg_node[rank] && adjacency[i][j] < beg_node[rank] + nElem) {
+ if (elemPartitioner.GetRankContainingIndex(adjacency[i][j]) == static_cast(rank)) {
/* Locally stored element. Determine its local ID and set the
time level and number of solution DOFs. */
- unsigned long elemID1 = adjacency[i][j] - beg_node[rank];
+ unsigned long elemID1 = adjacency[i][j] - elemPartitioner.GetFirstIndexOnRank(rank);
timeLevel1 = elem[elemID1]->GetTimeLevel();
nDOFs1 = elem[elemID1]->GetNDOFsSol();
} else {
/* The neighbor is an external element. Find it in mapExternalElemIDToTimeLevel
and set the time level and number of solution DOFs accordingly. */
- map::const_iterator MI;
- MI = mapExternalElemIDToTimeLevel.find(adjacency[i][j]);
+ const auto MI = mapExternalElemIDToTimeLevel.find(adjacency[i][j]);
if (MI == mapExternalElemIDToTimeLevel.end())
SU2_MPI::Error("Entry not found in mapExternalElemIDToTimeLevel", CURRENT_FUNCTION);
timeLevel1 = MI->second.short0;
diff --git a/Common/src/fem/meson.build b/Common/src/fem/meson.build
index 028f179d6e4..263fccc0e84 100644
--- a/Common/src/fem/meson.build
+++ b/Common/src/fem/meson.build
@@ -4,5 +4,4 @@ common_src += files(['geometry_structure_fem_part.cpp',
'fem_work_estimate_metis.cpp',
'fem_standard_element.cpp',
'fem_wall_distance.cpp',
- 'fem_gauss_jacobi_quadrature.cpp',
- 'fem_cgns_elements.cpp'])
+ 'fem_gauss_jacobi_quadrature.cpp'])
diff --git a/Common/src/geometry/CGeometry.cpp b/Common/src/geometry/CGeometry.cpp
index cf1a6948c5f..e771960b638 100644
--- a/Common/src/geometry/CGeometry.cpp
+++ b/Common/src/geometry/CGeometry.cpp
@@ -183,8 +183,8 @@ void CGeometry::PreprocessP2PComms(CGeometry* geometry, CConfig* config) {
/*--- If we have not visited this element yet, increment our
number of elements that must be sent to a particular proc. ---*/
- if ((nPoint_Flag[iRank] != (int)iMarker)) {
- nPoint_Flag[iRank] = (int)iMarker;
+ if ((nPoint_Flag[iRank] != static_cast(iMarker))) {
+ nPoint_Flag[iRank] = static_cast(iMarker);
nPoint_Send_All[iRank + 1] += nVertexS;
}
}
@@ -819,13 +819,13 @@ void CGeometry::PreprocessPeriodicComms(CGeometry* geometry, CConfig* config) {
/*--- Get the rank that holds the matching periodic point
on the other marker in the periodic pair. ---*/
- iRank = (int)geometry->vertex[iMarker][iVertex]->GetDonorProcessor();
+ iRank = static_cast(geometry->vertex[iMarker][iVertex]->GetDonorProcessor());
/*--- If we have not visited this point last, increment our
number of points that must be sent to a particular proc. ---*/
- if ((nPoint_Flag[iRank] != (int)iPoint)) {
- nPoint_Flag[iRank] = (int)iPoint;
+ if ((nPoint_Flag[iRank] != static_cast(iPoint))) {
+ nPoint_Flag[iRank] = static_cast(iPoint);
nPoint_Send_All[iRank + 1] += 1;
}
}
@@ -962,7 +962,7 @@ void CGeometry::PreprocessPeriodicComms(CGeometry* geometry, CConfig* config) {
/*--- Get the rank that holds the matching periodic point
on the other marker in the periodic pair. ---*/
- iRank = (int)geometry->vertex[iMarker][iVertex]->GetDonorProcessor();
+ iRank = static_cast(geometry->vertex[iMarker][iVertex]->GetDonorProcessor());
/*--- If the rank for the current periodic point matches the
rank of the current send message, then store the local point
@@ -971,11 +971,11 @@ void CGeometry::PreprocessPeriodicComms(CGeometry* geometry, CConfig* config) {
if (iRank == Neighbors_PeriodicSend[iSend]) {
Local_Point_PeriodicSend[ii] = iPoint;
- Local_Marker_PeriodicSend[ii] = (unsigned long)iMarker;
+ Local_Marker_PeriodicSend[ii] = static_cast(iMarker);
jj = ii * nPackets;
idSend[jj] = geometry->vertex[iMarker][iVertex]->GetDonorPoint();
jj++;
- idSend[jj] = (unsigned long)iPeriodic;
+ idSend[jj] = static_cast(iPeriodic);
ii++;
}
}
diff --git a/Common/src/geometry/CPhysicalGeometry.cpp b/Common/src/geometry/CPhysicalGeometry.cpp
index 6bbbaa38e3d..eab91fca43c 100644
--- a/Common/src/geometry/CPhysicalGeometry.cpp
+++ b/Common/src/geometry/CPhysicalGeometry.cpp
@@ -31,9 +31,13 @@
#include "../../include/toolboxes/CLinearPartitioner.hpp"
#include "../../include/toolboxes/C1DInterpolation.hpp"
#include "../../include/toolboxes/geometry_toolbox.hpp"
+#include "../../include/geometry/meshreader/CSU2ASCIIMeshReaderFEM.hpp"
#include "../../include/geometry/meshreader/CSU2ASCIIMeshReaderFVM.hpp"
#include "../../include/geometry/meshreader/CCGNSMeshReaderFVM.hpp"
+#include "../../include/geometry/meshreader/CCGNSMeshReaderFEM.hpp"
+#include "../../include/geometry/meshreader/CRectangularMeshReaderFEM.hpp"
#include "../../include/geometry/meshreader/CRectangularMeshReaderFVM.hpp"
+#include "../../include/geometry/meshreader/CBoxMeshReaderFEM.hpp"
#include "../../include/geometry/meshreader/CBoxMeshReaderFVM.hpp"
#include "../../include/geometry/primal_grid/CPrimalGrid.hpp"
@@ -72,38 +76,18 @@ CPhysicalGeometry::CPhysicalGeometry(CConfig* config, unsigned short val_iZone,
string val_mesh_filename = config->GetMesh_FileName();
unsigned short val_format = config->GetMesh_FileFormat();
- /*--- Determine whether or not a FEM discretization is used ---*/
+ /*--- Check for a valid mesh format ---*/
- const bool fem_solver = config->GetFEMSolver();
-
- /*--- Initialize counters for local/global points & elements ---*/
-
- if (fem_solver) {
- switch (val_format) {
- case SU2:
- Read_SU2_Format_Parallel_FEM(config, val_mesh_filename, val_iZone, val_nZone);
- break;
-
- case CGNS_GRID:
- Read_CGNS_Format_Parallel_FEM(config, val_mesh_filename, val_iZone, val_nZone);
- break;
-
- default:
- SU2_MPI::Error("Unrecognized mesh format specified for the FEM solver!", CURRENT_FUNCTION);
- break;
- }
- } else {
- switch (val_format) {
- case SU2:
- case CGNS_GRID:
- case RECTANGLE:
- case BOX:
- Read_Mesh_FVM(config, val_mesh_filename, val_iZone, val_nZone);
- break;
- default:
- SU2_MPI::Error("Unrecognized mesh format specified!", CURRENT_FUNCTION);
- break;
- }
+ switch (val_format) {
+ case SU2:
+ case CGNS_GRID:
+ case RECTANGLE:
+ case BOX:
+ Read_Mesh(config, val_mesh_filename, val_iZone, val_nZone);
+ break;
+ default:
+ SU2_MPI::Error("Unrecognized mesh format specified!", CURRENT_FUNCTION);
+ break;
}
/*--- After reading the mesh, assert that the dimension is equal to 2 or 3. ---*/
@@ -563,8 +547,8 @@ void CPhysicalGeometry::DistributeColoring(const CConfig* config, CGeometry* geo
/*--- If we have not visited this node yet, increment our
number of points that must be sent to a particular proc. ---*/
- if (nPoint_Flag[iProcessor] != (int)iPoint) {
- nPoint_Flag[iProcessor] = (int)iPoint;
+ if (nPoint_Flag[iProcessor] != static_cast(iPoint)) {
+ nPoint_Flag[iProcessor] = static_cast(iPoint);
nPoint_Send[iProcessor + 1]++;
}
}
@@ -623,8 +607,8 @@ void CPhysicalGeometry::DistributeColoring(const CConfig* config, CGeometry* geo
/*--- If we have not visited this node yet, increment our
counters and load up the global ID and color. ---*/
- if (nPoint_Flag[iProcessor] != (int)iPoint) {
- nPoint_Flag[iProcessor] = (int)iPoint;
+ if (nPoint_Flag[iProcessor] != static_cast(iPoint)) {
+ nPoint_Flag[iProcessor] = static_cast(iPoint);
unsigned long nn = index[iProcessor];
/*--- Load the data values. ---*/
@@ -796,8 +780,8 @@ void CPhysicalGeometry::DistributeVolumeConnectivity(const CConfig* config, CGeo
/*--- If we have not visited this element yet, increment our
number of elements that must be sent to a particular proc. ---*/
- if ((nElem_Flag[iProcessor] != (int)iElem)) {
- nElem_Flag[iProcessor] = (int)iElem;
+ if ((nElem_Flag[iProcessor] != static_cast(iElem))) {
+ nElem_Flag[iProcessor] = static_cast(iElem);
nElem_Send[iProcessor + 1]++;
}
}
@@ -863,8 +847,8 @@ void CPhysicalGeometry::DistributeVolumeConnectivity(const CConfig* config, CGeo
/*--- Load connectivity and IDs into the buffer for sending ---*/
- if (nElem_Flag[iProcessor] != (int)iElem) {
- nElem_Flag[iProcessor] = (int)iElem;
+ if (nElem_Flag[iProcessor] != static_cast(iElem)) {
+ nElem_Flag[iProcessor] = static_cast(iElem);
unsigned long nn = index[iProcessor];
unsigned long mm = idIndex[iProcessor];
@@ -1087,8 +1071,8 @@ void CPhysicalGeometry::DistributePoints(const CConfig* config, CGeometry* geome
/*--- If we have not visited this node yet, increment our
number of points that must be sent to a particular proc. ---*/
- if (nPoint_Flag[iProcessor] != (int)iPoint) {
- nPoint_Flag[iProcessor] = (int)iPoint;
+ if (nPoint_Flag[iProcessor] != static_cast(iPoint)) {
+ nPoint_Flag[iProcessor] = static_cast