diff --git a/.gitmodules b/.gitmodules index 2c655cb36da..4c6d8a819f8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -27,3 +27,6 @@ [submodule "subprojects/MLPCpp"] path = subprojects/MLPCpp url = https://github.com/EvertBunschoten/MLPCpp.git +[submodule "externals/FADO"] + path = externals/FADO + url = https://github.com/pcarruscag/FADO.git diff --git a/AUTHORS.md b/AUTHORS.md index 511ebcf4b33..48b2140480a 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -136,6 +136,7 @@ Thomas D. Economon Tim Albring TobiKattmann Trent Lukaczyk +Vikram Bharadwaj Vinzenz Götz VivaanKhatri Wally Maier diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp index 65b9fe09bad..1fdb2e2d00a 100644 --- a/Common/include/CConfig.hpp +++ b/Common/include/CConfig.hpp @@ -108,7 +108,6 @@ class CConfig { su2double Opt_RelaxFactor; /*!< \brief Scale factor for the line search. */ su2double Opt_LineSearch_Bound; /*!< \brief Bounds for the line search. */ su2double StartTime; - unsigned short SmoothNumGrid; /*!< \brief Smooth the numerical grid. */ bool ContinuousAdjoint, /*!< \brief Flag to know if the code is solving an adjoint problem. */ Viscous, /*!< \brief Flag to know if the code is solving a viscous problem. */ EquivArea, /*!< \brief Flag to know if the code is going to compute and plot the equivalent area. */ @@ -701,6 +700,7 @@ class CConfig { unsigned long StartConv_Iter; /*!< \brief Start convergence criteria at iteration. */ su2double Cauchy_Eps; /*!< \brief Epsilon used for the convergence. */ bool Restart, /*!< \brief Restart solution (for direct, adjoint, and linearized problems).*/ + Wrt_Restart_Compact, /*!< \brief Write compact restart files with minimum nr. of variables. */ Read_Binary_Restart, /*!< \brief Read binary SU2 native restart files.*/ Wrt_Restart_Overwrite, /*!< \brief Overwrite restart files or append iteration number.*/ Wrt_Surface_Overwrite, /*!< \brief Overwrite surface output files or append iteration number.*/ @@ -807,6 +807,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. */ @@ -1008,7 +1009,7 @@ class CConfig { bool ExtraOutput; /*!< \brief Check if extra output need. */ bool Wall_Functions; /*!< \brief Use wall functions with the turbulence model */ long ExtraHeatOutputZone; /*!< \brief Heat solver zone with extra screen output */ - bool DeadLoad; /*!< \brief Application of dead loads to the FE analysis */ + bool CentrifugalForce; /*!< \brief Application of centrifugal forces to the FE analysis */ bool PseudoStatic; /*!< \brief Application of dead loads to the FE analysis */ bool SteadyRestart; /*!< \brief Restart from a steady state for FSI problems. */ su2double Newmark_beta, /*!< \brief Parameter alpha for Newmark method. */ @@ -1017,10 +1018,13 @@ class CConfig { su2double *Int_Coeffs; /*!< \brief Time integration coefficients for structural method. */ unsigned short nElasticityMod, /*!< \brief Number of different values for the elasticity modulus. */ nPoissonRatio, /*!< \brief Number of different values for the Poisson ratio modulus. */ - nMaterialDensity; /*!< \brief Number of different values for the Material density. */ + nMaterialDensity, /*!< \brief Number of different values for the Material density. */ + nMaterialThermalExpansion; /*!< \brief Number of different values for thermal expansion coefficient. */ su2double *ElasticityMod, /*!< \brief Value of the elasticity moduli. */ *PoissonRatio, /*!< \brief Value of the Poisson ratios. */ - *MaterialDensity; /*!< \brief Value of the Material densities. */ + *MaterialDensity, /*!< \brief Value of the Material densities. */ + *MaterialThermalExpansion, /*!< \brief Value of the thermal expansion coefficients. */ + MaterialReferenceTemperature; /*!< \brief Value of the reference temperature for thermal expansion. */ unsigned short nElectric_Field, /*!< \brief Number of different values for the electric field in the membrane. */ nDim_Electric_Field; /*!< \brief Dimensionality of the problem. */ unsigned short nDim_RefNode; /*!< \brief Dimensionality of the vector . */ @@ -2400,6 +2404,16 @@ class CConfig { */ su2double GetMaterialDensity(unsigned short id_val) const { return MaterialDensity[id_val]; } + /*! + * \brief Get the thermal expansion coefficient. + */ + su2double GetMaterialThermalExpansion(unsigned short id_val) const { return MaterialThermalExpansion[id_val]; } + + /*! + * \brief Temperature at which there is no stress from thermal expansion. + */ + su2double GetMaterialReferenceTemperature() const { return MaterialReferenceTemperature; } + /*! * \brief Compressibility/incompressibility of the solids analysed using the structural solver. * \return Compressible or incompressible. @@ -5507,6 +5521,12 @@ class CConfig { */ bool GetRead_Binary_Restart(void) const { return Read_Binary_Restart; } + /*! + * \brief Flag for whether restart files contain only necessary variables. + * \return Flag TRUE then the code will write compact restart files. + */ + bool GetWrt_Restart_Compact(void) const { return Wrt_Restart_Compact; } + /*! * \brief Flag for whether restart solution files are overwritten. * \return Flag for overwriting. If Flag=false, iteration nr is appended to filename @@ -6335,12 +6355,6 @@ class CConfig { */ bool GetAxisymmetric(void) const { return Axisymmetric; } - /*! - * \brief Get information about there is a smoothing of the grid coordinates. - * \return TRUE if there is smoothing of the grid coordinates; otherwise FALSE. - */ - unsigned short GetSmoothNumGrid(void) const { return SmoothNumGrid; } - /*! * \brief Subtract one to the index of the finest grid (full multigrid strategy). * \return Change the index of the finest grid. @@ -8946,10 +8960,9 @@ class CConfig { su2double GetAitkenDynMinInit(void) const { return AitkenDynMinInit; } /*! - * \brief Decide whether to apply dead loads to the model. - * \return TRUE if the dead loads are to be applied, FALSE otherwise. + * \brief Decide whether to apply centrifugal forces to the model. */ - bool GetDeadLoad(void) const { return DeadLoad; } + bool GetCentrifugalForce(void) const { return CentrifugalForce; } /*! * \brief Identifies if the mesh is matching or not (temporary, while implementing interpolation procedures). @@ -9611,6 +9624,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/CGeometry.hpp b/Common/include/geometry/CGeometry.hpp index 738618ba2cd..dd79546f56b 100644 --- a/Common/include/geometry/CGeometry.hpp +++ b/Common/include/geometry/CGeometry.hpp @@ -913,14 +913,6 @@ class CGeometry { */ inline virtual void SetMultiGridWallTemperature(const CGeometry* fine_grid, unsigned short val_marker) {} - /*! - * \brief A virtual member. - * \param[in] val_nSmooth - Number of smoothing iterations. - * \param[in] val_smooth_coeff - Relaxation factor. - * \param[in] config - Definition of the particular problem. - */ - inline virtual void SetCoord_Smoothing(unsigned short val_nSmooth, su2double val_smooth_coeff, CConfig* config) {} - /*! * \brief A virtual member. * \param[in] fine_grid - Geometrical definition of the child grid (for multigrid). diff --git a/Common/include/geometry/CPhysicalGeometry.hpp b/Common/include/geometry/CPhysicalGeometry.hpp index 8214e4ea7f0..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 @@ -545,14 +543,6 @@ class CPhysicalGeometry final : public CGeometry { void DetermineTimeLevelElements(CConfig* config, const vector& localFaces, map& mapExternalElemIDToTimeLevel); - /*! - * \brief Do an implicit smoothing of the grid coordinates. - * \param[in] val_nSmooth - Number of smoothing iterations. - * \param[in] val_smooth_coeff - Relaxation factor. - * \param[in] config - Definition of the particular problem. - */ - void SetCoord_Smoothing(unsigned short val_nSmooth, su2double val_smooth_coeff, CConfig* config) override; - /*! * \brief Compute 3 grid quality metrics: orthogonality angle, dual cell aspect ratio, and dual cell volume ratio. * \param[in] config - Definition of the particular problem. diff --git a/Common/include/geometry/dual_grid/CPoint.hpp b/Common/include/geometry/dual_grid/CPoint.hpp index 17d540a2a7b..e497df35307 100644 --- a/Common/include/geometry/dual_grid/CPoint.hpp +++ b/Common/include/geometry/dual_grid/CPoint.hpp @@ -771,14 +771,14 @@ class CPoint { } /*! - * \brief Get the value of the old coordinates for implicit smoothing. + * \brief Get the value of the old coordinates. * \param[in] iPoint - Index of the point. * \return Old coordinates at a point. */ inline su2double* GetCoord_Old(unsigned long iPoint) { return Coord_Old[iPoint]; } /*! - * \brief Set the value of the vector Coord_Old for implicit smoothing. + * \brief Set the value of the vector Coord_Old. * \param[in] iPoint - Index of the point. * \param[in] coord_old - Value of the coordinates. */ @@ -791,27 +791,6 @@ class CPoint { */ void SetCoord_Old(); - /*! - * \brief Get the value of the summed coordinates for implicit smoothing. - * \param[in] iPoint - Index of the point. - * \return Sum of coordinates at a point. - */ - inline su2double* GetCoord_Sum(unsigned long iPoint) { return Coord_Sum[iPoint]; } - - /*! - * \brief Add the value of the coordinates to the Coord_Sum vector for implicit smoothing. - * \param[in] iPoint - Index of the point. - * \param[in] coord_sum - Value of the coordinates to add. - */ - inline void AddCoord_Sum(unsigned long iPoint, const su2double* coord_sum) { - for (unsigned long iDim = 0; iDim < nDim; iDim++) Coord_Sum(iPoint, iDim) += coord_sum[iDim]; - } - - /*! - * \brief Initialize the vector Coord_Sum. - */ - void SetCoord_SumZero(); - /*! * \brief Get the value of the grid velocity at the point. * \param[in] iPoint - Index of the point. diff --git a/Common/include/geometry/elements/CElement.hpp b/Common/include/geometry/elements/CElement.hpp index 535323ec0c0..6f7bd435560 100644 --- a/Common/include/geometry/elements/CElement.hpp +++ b/Common/include/geometry/elements/CElement.hpp @@ -67,6 +67,8 @@ class CElement { su2activematrix NodalExtrap; /*!< \brief Coordinates of the nodal points for Gaussian extrapolation. */ su2activematrix NodalStress; /*!< \brief Stress at the nodes. */ + su2activevector NodalTemperature; /*!< \brief Temperature at the nodes. */ + /*--- Stiffness and load matrices. ---*/ std::vector Kab; /*!< \brief Structure for the constitutive component of the tangent matrix. */ su2activematrix Mab; /*!< \brief Structure for the nodal components of the mass matrix. */ @@ -151,7 +153,7 @@ class CElement { * \param[in] iDim - Dimension. * \param[in] val_CoordRef - Value of the coordinate. */ - inline void SetRef_Coord(unsigned short iNode, unsigned short iDim, su2double val_CoordRef) { + inline void SetRef_Coord(unsigned short iNode, unsigned short iDim, const su2double& val_CoordRef) { RefCoord(iNode, iDim) = val_CoordRef; } @@ -161,10 +163,22 @@ class CElement { * \param[in] iDim - Dimension. * \param[in] val_CoordRef - Value of the coordinate. */ - inline void SetCurr_Coord(unsigned short iNode, unsigned short iDim, su2double val_CoordCurr) { + inline void SetCurr_Coord(unsigned short iNode, unsigned short iDim, const su2double& val_CoordCurr) { CurrentCoord(iNode, iDim) = val_CoordCurr; } + /*! + * \brief Set the value of the temperature of a node. + */ + inline void SetTemperature(unsigned short iNode, const su2double& val_Temperature) { + NodalTemperature[iNode] = val_Temperature; + } + + /*! + * \brief Set the value of the temperature of all nodes. + */ + inline void SetTemperature(const su2double& val_Temperature) { NodalTemperature = val_Temperature; } + /*! * \brief Get the value of the coordinate of the nodes in the reference configuration. * \param[in] iNode - Index of node. @@ -181,6 +195,17 @@ class CElement { */ inline su2double GetCurr_Coord(unsigned short iNode, unsigned short iDim) const { return CurrentCoord(iNode, iDim); } + /*! + * \brief Get the value of the temperature at a Gaussian integration point. + */ + inline su2double GetTemperature(unsigned short iGauss) const { + su2double Temperature = 0; + for (auto iNode = 0u; iNode < nNodes; ++iNode) { + Temperature += GetNi(iNode, iGauss) * NodalTemperature[iNode]; + } + return Temperature; + } + /*! * \brief Get the weight of the corresponding Gaussian Point. * \param[in] iGauss - index of the Gaussian point. @@ -214,7 +239,9 @@ class CElement { * \param[in] nodeB - index of Node b. * \param[in] val_Ks_ab - value of the term that will constitute the diagonal of the stress contribution. */ - inline void Add_Mab(unsigned short nodeA, unsigned short nodeB, su2double val_Mab) { Mab(nodeA, nodeB) += val_Mab; } + inline void Add_Mab(unsigned short nodeA, unsigned short nodeB, const su2double& val_Mab) { + Mab(nodeA, nodeB) += val_Mab; + } /*! * \brief Add the value of a submatrix K relating nodes a and b, for the constitutive term. @@ -243,7 +270,7 @@ class CElement { * \param[in] nodeB - index of Node b. * \param[in] val_Ks_ab - value of the term that will constitute the diagonal of the stress contribution. */ - inline void Add_Ks_ab(unsigned short nodeA, unsigned short nodeB, su2double val_Ks_ab) { + inline void Add_Ks_ab(unsigned short nodeA, unsigned short nodeB, const su2double& val_Ks_ab) { Ks_ab(nodeA, nodeB) += val_Ks_ab; } @@ -354,7 +381,7 @@ class CElement { * \param[in] iVar - Variable index. * \param[in] val_Stress - Value of the stress added. */ - inline void Add_NodalStress(unsigned short iNode, unsigned short iVar, su2double val_Stress) { + inline void Add_NodalStress(unsigned short iNode, unsigned short iVar, const su2double& val_Stress) { NodalStress(iNode, iVar) += val_Stress; } @@ -789,7 +816,7 @@ class CQUAD4 final : public CElementWithKnownSizes<4, 4, 2> { /*! * \brief Shape functions (Ni) evaluated at point Xi,Eta. */ - inline static void ShapeFunctions(su2double Xi, su2double Eta, su2double* Ni) { + inline static void ShapeFunctions(const su2double& Xi, const su2double& Eta, su2double* Ni) { Ni[0] = 0.25 * (1.0 - Xi) * (1.0 - Eta); Ni[1] = 0.25 * (1.0 + Xi) * (1.0 - Eta); Ni[2] = 0.25 * (1.0 + Xi) * (1.0 + Eta); @@ -799,7 +826,7 @@ class CQUAD4 final : public CElementWithKnownSizes<4, 4, 2> { /*! * \brief Shape function Jacobian (dNi) evaluated at point Xi,Eta. */ - inline static void ShapeFunctionJacobian(su2double Xi, su2double Eta, su2double dNi[][2]) { + inline static void ShapeFunctionJacobian(const su2double& Xi, const su2double& Eta, su2double dNi[][2]) { dNi[0][0] = -0.25 * (1.0 - Eta); dNi[0][1] = -0.25 * (1.0 - Xi); dNi[1][0] = 0.25 * (1.0 - Eta); 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/include/linear_algebra/CSysMatrix.hpp b/Common/include/linear_algebra/CSysMatrix.hpp index 815d52e0708..422a2068a88 100644 --- a/Common/include/linear_algebra/CSysMatrix.hpp +++ b/Common/include/linear_algebra/CSysMatrix.hpp @@ -805,10 +805,10 @@ class CSysMatrix { void EnforceSolutionAtNode(unsigned long node_i, const OtherType* x_i, CSysVector& b); /*! - * \brief Version of EnforceSolutionAtNode for a single degree of freedom. + * \brief Similar to EnforceSolutionAtNode, but for 0 projection in a given direction. */ template - void EnforceSolutionAtDOF(unsigned long node_i, unsigned long iVar, OtherType x_i, CSysVector& b); + void EnforceZeroProjection(unsigned long node_i, const OtherType* n, CSysVector& b); /*! * \brief Sets the diagonal entries of the matrix as the sum of the blocks in the corresponding column. diff --git a/Common/include/toolboxes/geometry_toolbox.hpp b/Common/include/toolboxes/geometry_toolbox.hpp index 773f1d553dc..ddc2fd4434a 100644 --- a/Common/include/toolboxes/geometry_toolbox.hpp +++ b/Common/include/toolboxes/geometry_toolbox.hpp @@ -79,6 +79,14 @@ inline T Norm(Int nDim, const T* a) { return sqrt(SquaredNorm(nDim, a)); } +/*! \brief dn = max(abs(n.(a-b)), 0.05 * ||a-b|| */ +template +inline T NormalDistance(Int nDim, const T* n, const T* a, const T* b) { + T d[3] = {0}; + Distance(nDim, a, b, d); + return fmax(fabs(DotProduct(nDim, n, d)), 0.05 * Norm(nDim, d)); +} + /*! \brief c = a x b */ template inline void CrossProduct(const T* a, const T* b, T* c) { diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index 4aa7e3bc0c0..fc3fe20a643 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -914,7 +914,10 @@ void CConfig::SetPointersNull() { Inlet_Velocity = nullptr; Inflow_Mach = nullptr; Inflow_Pressure = nullptr; Outlet_Pressure = nullptr; Isothermal_Temperature = nullptr; - ElasticityMod = nullptr; PoissonRatio = nullptr; MaterialDensity = nullptr; + ElasticityMod = nullptr; + PoissonRatio = nullptr; + MaterialDensity = nullptr; + MaterialThermalExpansion = nullptr; Load_Dir = nullptr; Load_Dir_Value = nullptr; Load_Dir_Multiplier = nullptr; Disp_Dir = nullptr; Disp_Dir_Value = nullptr; Disp_Dir_Multiplier = nullptr; @@ -1171,6 +1174,8 @@ void CConfig::SetConfig_Options() { /*!\brief RESTART_SOL \n DESCRIPTION: Restart solution from native solution file \n Options: NO, YES \ingroup Config */ addBoolOption("RESTART_SOL", Restart, false); + /*!\brief WRT_RESTART_COMPACT \n DESCRIPTION: Minimize the size of restart files \n Options: NO, YES \ingroup Config */ + addBoolOption("WRT_RESTART_COMPACT", Wrt_Restart_Compact, true); /*!\brief BINARY_RESTART \n DESCRIPTION: Read binary SU2 native restart files. \n Options: YES, NO \ingroup Config */ addBoolOption("READ_BINARY_RESTART", Read_Binary_Restart, true); /*!\brief WRT_RESTART_OVERWRITE \n DESCRIPTION: overwrite restart files or append iteration number. \n Options: YES, NO \ingroup Config */ @@ -2144,6 +2149,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) */ @@ -2263,9 +2271,6 @@ void CConfig::SetConfig_Options() { /* DESCRIPTION: Value to move motion origins (1 or 0) */ addUShortListOption("MOVE_MOTION_ORIGIN", nMoveMotion_Origin, MoveMotion_Origin); - /* DESCRIPTION: Before each computation, implicitly smooth the nodal coordinates */ - addUnsignedShortOption("SMOOTH_GEOMETRY", SmoothNumGrid, 0); - /*!\par CONFIG_CATEGORY: Aeroelastic Simulation (Typical Section Model) \ingroup Config*/ /*--- Options related to aeroelastic simulations using the Typical Section Model) ---*/ /* DESCRIPTION: The flutter speed index (modifies the freestream condition) */ @@ -2444,6 +2449,10 @@ void CConfig::SetConfig_Options() { addDoubleListOption("POISSON_RATIO", nPoissonRatio, PoissonRatio); /* DESCRIPTION: Material density */ addDoubleListOption("MATERIAL_DENSITY", nMaterialDensity, MaterialDensity); + /* DESCRIPTION: Material thermal expansion coefficient */ + addDoubleListOption("MATERIAL_THERMAL_EXPANSION_COEFF", nMaterialThermalExpansion, MaterialThermalExpansion); + /* DESCRIPTION: Temperature at which there is no stress from thermal expansion */ + addDoubleOption("MATERIAL_REFERENCE_TEMPERATURE", MaterialReferenceTemperature, 288.15); /* DESCRIPTION: Knowles B constant */ addDoubleOption("KNOWLES_B", Knowles_B, 1.0); /* DESCRIPTION: Knowles N constant */ @@ -2504,11 +2513,11 @@ void CConfig::SetConfig_Options() { addEnumOption("NONLINEAR_FEM_SOLUTION_METHOD", Kind_SpaceIteScheme_FEA, Space_Ite_Map_FEA, STRUCT_SPACE_ITE::NEWTON); /* DESCRIPTION: Formulation for bidimensional elasticity solver */ addEnumOption("FORMULATION_ELASTICITY_2D", Kind_2DElasForm, ElasForm_2D, STRUCT_2DFORM::PLANE_STRAIN); - /* DESCRIPTION: Apply dead loads - * Options: NO, YES \ingroup Config */ - addBoolOption("DEAD_LOAD", DeadLoad, false); + /* DESCRIPTION: Apply centrifugal forces + * Options: NO, YES \ingroup Config */ + addBoolOption("CENTRIFUGAL_FORCE", CentrifugalForce, false); /* DESCRIPTION: Temporary: pseudo static analysis (no density in dynamic analysis) - * Options: NO, YES \ingroup Config */ + * Options: NO, YES \ingroup Config */ addBoolOption("PSEUDO_STATIC", PseudoStatic, false); /* DESCRIPTION: Parameter alpha for Newmark scheme (s) */ addDoubleOption("NEWMARK_BETA", Newmark_beta, 0.25); @@ -3067,6 +3076,8 @@ void CConfig::SetConfig_Parsing(istream& config_buffer){ newString.append("DYNAMIC_ANALYSIS is deprecated. Use TIME_DOMAIN instead.\n\n"); else if (!option_name.compare("SPECIES_USE_STRONG_BC")) newString.append("SPECIES_USE_STRONG_BC is deprecated. Use MARKER_SPECIES_STRONG_BC= (marker1, ...) instead.\n\n"); + else if (!option_name.compare("DEAD_LOAD")) + newString.append("DEAD_LOAD is deprecated. Use GRAVITY_FORCE or BODY_FORCE instead.\n\n"); else { /*--- Find the most likely candidate for the unrecognized option, based on the length of start and end character sequences shared by candidates and the option. ---*/ @@ -4840,9 +4851,15 @@ void CConfig::SetPostprocessing(SU2_COMPONENT val_software, unsigned short val_i MaterialDensity = new su2double[1]; MaterialDensity[0] = 7854; } - if (nElasticityMod != nPoissonRatio || nElasticityMod != nMaterialDensity) { - SU2_MPI::Error("ELASTICITY_MODULUS, POISSON_RATIO, and MATERIAL_DENSITY need to have the same number " - "of entries (the number of materials).", CURRENT_FUNCTION); + if (nMaterialThermalExpansion == 0) { + nMaterialThermalExpansion = 1; + MaterialThermalExpansion = new su2double[1](); + } + + if (nElasticityMod != nPoissonRatio || nElasticityMod != nMaterialDensity || + nElasticityMod != nMaterialThermalExpansion) { + SU2_MPI::Error("ELASTICITY_MODULUS, POISSON_RATIO, MATERIAL_DENSITY, and THERMAL_EXPANSION_COEFF need " + "to have the same number of entries (the number of materials).", CURRENT_FUNCTION); } if (nElectric_Constant == 0) { @@ -6890,8 +6907,6 @@ void CConfig::SetOutput(SU2_COMPONENT val_software, unsigned short val_izone) { cout << endl <<"--------------- Space Numerical Integration ( Zone " << iZone << " ) ------------------" << endl; - if (SmoothNumGrid) cout << "There are some smoothing iterations on the grid coordinates." << endl; - if ((Kind_Solver == MAIN_SOLVER::EULER) || (Kind_Solver == MAIN_SOLVER::NAVIER_STOKES) || (Kind_Solver == MAIN_SOLVER::RANS) || (Kind_Solver == MAIN_SOLVER::INC_EULER) || (Kind_Solver == MAIN_SOLVER::INC_NAVIER_STOKES) || (Kind_Solver == MAIN_SOLVER::INC_RANS) || (Kind_Solver == MAIN_SOLVER::NEMO_EULER) || (Kind_Solver == MAIN_SOLVER::NEMO_NAVIER_STOKES) || 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 6e34e715be5..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(iPoint); nPoint_Send[iProcessor + 1]++; } } @@ -1156,8 +1140,8 @@ void CPhysicalGeometry::DistributePoints(const CConfig* config, CGeometry* geome /*--- If we have not visited this node yet, increment our counters and load up the colors, ids, and coords. ---*/ - 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 global ID, color, and coordinate values. ---*/ @@ -1264,7 +1248,7 @@ void CPhysicalGeometry::DistributePoints(const CConfig* config, CGeometry* geome Local_Points[iRecv] = idRecv[iRecv]; Local_Colors[iRecv] = colorRecv[iRecv]; for (iDim = 0; iDim < nDim; iDim++) Local_Coords[iRecv * nDim + iDim] = coordRecv[iRecv * nDim + iDim]; - if (Local_Colors[iRecv] == (unsigned long)rank) + if (Local_Colors[iRecv] == static_cast(rank)) nLocal_PointDomain++; else nLocal_PointGhost++; @@ -1375,8 +1359,8 @@ void CPhysicalGeometry::PartitionSurfaceConnectivity(CConfig* config, CGeometry* /*--- 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]++; } } @@ -1456,8 +1440,8 @@ void CPhysicalGeometry::PartitionSurfaceConnectivity(CConfig* config, CGeometry* /*--- Load connectivity 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 = markerIndex[iProcessor]; @@ -1728,8 +1712,8 @@ void CPhysicalGeometry::DistributeSurfaceConnectivity(CConfig* config, CGeometry /*--- 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]++; } } @@ -1797,8 +1781,8 @@ void CPhysicalGeometry::DistributeSurfaceConnectivity(CConfig* config, CGeometry /*--- If we have not visited this element yet, load up the data 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 = markerIndex[iProcessor]; @@ -2006,7 +1990,8 @@ void CPhysicalGeometry::DistributeMarkerTags(CConfig* config, CGeometry* geometr /*--- Broadcast the string names of the variables. ---*/ - SU2_MPI::Bcast(mpi_str_buf, (int)nMarker_Global * MAX_STRING_SIZE, MPI_CHAR, MASTER_NODE, SU2_MPI::GetComm()); + SU2_MPI::Bcast(mpi_str_buf, static_cast(nMarker_Global) * MAX_STRING_SIZE, MPI_CHAR, MASTER_NODE, + SU2_MPI::GetComm()); /*--- Now parse the string names and load into our marker tag vector. We also need to set the values of all markers into the config. ---*/ @@ -2058,7 +2043,7 @@ void CPhysicalGeometry::LoadPoints(CConfig* config, CGeometry* geometry) { for (iPoint = 0; iPoint < nPoint; iPoint++) { /*--- Set the starting point to the correct counter for this point. ---*/ - if (Local_Colors[iPoint] == (unsigned long)rank) { + if (Local_Colors[iPoint] == static_cast(rank)) { if (Local_Points[iPoint] < geometry->GetGlobal_nPointDomain()) jPoint = iOwned; else @@ -2082,7 +2067,7 @@ void CPhysicalGeometry::LoadPoints(CConfig* config, CGeometry* geometry) { /*--- Increment the correct counter before moving to the next point. ---*/ - if (Local_Colors[iPoint] == (unsigned long)rank) { + if (Local_Colors[iPoint] == static_cast(rank)) { if (Local_Points[iPoint] < geometry->GetGlobal_nPointDomain()) iOwned++; else @@ -2891,7 +2876,7 @@ unsigned long CPhysicalGeometry::GetLinearPartition(unsigned long val_global_ind /*--- Guard against going over size. ---*/ - if (iProcessor >= (unsigned long)size) iProcessor = (unsigned long)size - 1; + if (iProcessor >= static_cast(size)) iProcessor = static_cast(size) - 1; /*--- Move up or down until we find the processor. ---*/ @@ -3006,7 +2991,7 @@ void CPhysicalGeometry::SetSendReceive(const CConfig* config) { iPoint = elem[iElem]->GetNode(iNode); iDomain = nodes->GetColor(iPoint); - if (iDomain == (unsigned long)rank) { + if (iDomain == static_cast(rank)) { for (jNode = 0; jNode < elem[iElem]->GetnNodes(); jNode++) { jPoint = elem[iElem]->GetNode(jNode); jDomain = nodes->GetColor(jPoint); @@ -3433,8 +3418,8 @@ void CPhysicalGeometry::SetBoundaries(CConfig* config) { delete[] nElem_Bound_Copy; } -void CPhysicalGeometry::Read_Mesh_FVM(CConfig* config, const string& val_mesh_filename, unsigned short val_iZone, - unsigned short val_nZone) { +void CPhysicalGeometry::Read_Mesh(CConfig* config, const string& val_mesh_filename, unsigned short val_iZone, + unsigned short val_nZone) { /*--- Initialize counters for local/global points & elements ---*/ Global_nPoint = 0; @@ -3463,20 +3448,33 @@ void CPhysicalGeometry::Read_Mesh_FVM(CConfig* config, const string& val_mesh_fi /*--- Create a mesh reader to read a CGNS grid into linear partitions. ---*/ unsigned short val_format = config->GetMesh_FileFormat(); + const bool fem_solver = config->GetFEMSolver(); - CMeshReaderFVM* MeshFVM = nullptr; + CMeshReaderBase* Mesh = nullptr; switch (val_format) { case SU2: - MeshFVM = new CSU2ASCIIMeshReaderFVM(config, val_iZone, val_nZone); + if (fem_solver) + Mesh = new CSU2ASCIIMeshReaderFEM(config, val_iZone, val_nZone); + else + Mesh = new CSU2ASCIIMeshReaderFVM(config, val_iZone, val_nZone); break; case CGNS_GRID: - MeshFVM = new CCGNSMeshReaderFVM(config, val_iZone, val_nZone); + if (fem_solver) + Mesh = new CCGNSMeshReaderFEM(config, val_iZone, val_nZone); + else + Mesh = new CCGNSMeshReaderFVM(config, val_iZone, val_nZone); break; case RECTANGLE: - MeshFVM = new CRectangularMeshReaderFVM(config, val_iZone, val_nZone); + if (fem_solver) + Mesh = new CRectangularMeshReaderFEM(config, val_iZone, val_nZone); + else + Mesh = new CRectangularMeshReaderFVM(config, val_iZone, val_nZone); break; case BOX: - MeshFVM = new CBoxMeshReaderFVM(config, val_iZone, val_nZone); + if (fem_solver) + Mesh = new CBoxMeshReaderFEM(config, val_iZone, val_nZone); + else + Mesh = new CBoxMeshReaderFVM(config, val_iZone, val_nZone); break; default: SU2_MPI::Error("Unrecognized mesh format specified!", CURRENT_FUNCTION); @@ -3485,7 +3483,7 @@ void CPhysicalGeometry::Read_Mesh_FVM(CConfig* config, const string& val_mesh_fi /*--- Store the dimension of the problem ---*/ - nDim = MeshFVM->GetDimension(); + nDim = Mesh->GetDimension(); if (rank == MASTER_NODE) { if (nDim == 2) cout << "Two dimensional problem." << endl; if (nDim == 3) cout << "Three dimensional problem." << endl; @@ -3493,10 +3491,10 @@ void CPhysicalGeometry::Read_Mesh_FVM(CConfig* config, const string& val_mesh_fi /*--- Store the local and global number of nodes for this rank. ---*/ - nPoint = MeshFVM->GetNumberOfLocalPoints(); - nPointDomain = MeshFVM->GetNumberOfLocalPoints(); - Global_nPoint = MeshFVM->GetNumberOfGlobalPoints(); - Global_nPointDomain = MeshFVM->GetNumberOfGlobalPoints(); + nPoint = Mesh->GetNumberOfLocalPoints(); + nPointDomain = Mesh->GetNumberOfLocalPoints(); + Global_nPoint = Mesh->GetNumberOfGlobalPoints(); + Global_nPointDomain = Mesh->GetNumberOfGlobalPoints(); if ((rank == MASTER_NODE) && (size > SINGLE_NODE)) { cout << Global_nPoint << " grid points before partitioning." << endl; @@ -3506,9 +3504,9 @@ void CPhysicalGeometry::Read_Mesh_FVM(CConfig* config, const string& val_mesh_fi /*--- Store the local and global number of interior elements. ---*/ - nElem = MeshFVM->GetNumberOfLocalElements(); - Global_nElem = MeshFVM->GetNumberOfGlobalElements(); - Global_nElemDomain = MeshFVM->GetNumberOfGlobalElements(); + nElem = Mesh->GetNumberOfLocalElements(); + Global_nElem = Mesh->GetNumberOfGlobalElements(); + Global_nElemDomain = Mesh->GetNumberOfGlobalElements(); if ((rank == MASTER_NODE) && (size > SINGLE_NODE)) { cout << Global_nElem << " volume elements before partitioning." << endl; @@ -3516,24 +3514,35 @@ void CPhysicalGeometry::Read_Mesh_FVM(CConfig* config, const string& val_mesh_fi cout << Global_nElem << " volume elements." << endl; } - /*--- Load the grid points, volume elements, and surface elements - from the mesh object into the proper SU2 data structures. ---*/ + /*--- Make a distinction between the FVM solver and FEM solver how to load + the grid data in the member variables of CPhysicalGeometry. ---*/ + if (fem_solver) { + /*--- Load the grid points, volume elements, and surface elements + from the mesh object into the proper SU2 data structures. ---*/ + + LoadLinearlyPartitionedPointsFEM(config, Mesh); + LoadLinearlyPartitionedVolumeElementsFEM(config, Mesh); + LoadLinearlyPartitionedSurfaceElementsFEM(config, Mesh); + } else { + /*--- Load the grid points, volume elements, and surface elements + from the mesh object into the proper SU2 data structures. ---*/ - LoadLinearlyPartitionedPoints(config, MeshFVM); - LoadLinearlyPartitionedVolumeElements(config, MeshFVM); - LoadUnpartitionedSurfaceElements(config, MeshFVM); + LoadLinearlyPartitionedPoints(config, Mesh); + LoadLinearlyPartitionedVolumeElements(config, Mesh); + LoadUnpartitionedSurfaceElements(config, Mesh); - /*--- Prepare the nodal adjacency structures for ParMETIS. ---*/ + /*--- Prepare the nodal adjacency structures for ParMETIS. ---*/ - PrepareAdjacency(config); + PrepareAdjacency(config); + } /*--- Now that we have loaded all information from the mesh, delete the mesh reader object. ---*/ - delete MeshFVM; + delete Mesh; } -void CPhysicalGeometry::LoadLinearlyPartitionedPoints(CConfig* config, CMeshReaderFVM* mesh) { +void CPhysicalGeometry::LoadLinearlyPartitionedPoints(CConfig* config, CMeshReaderBase* mesh) { /*--- Get the linearly partitioned coordinates from the mesh object. ---*/ const auto& gridCoords = mesh->GetLocalPointCoordinates(); @@ -3556,7 +3565,7 @@ void CPhysicalGeometry::LoadLinearlyPartitionedPoints(CConfig* config, CMeshRead } } -void CPhysicalGeometry::LoadLinearlyPartitionedVolumeElements(CConfig* config, CMeshReaderFVM* mesh) { +void CPhysicalGeometry::LoadLinearlyPartitionedVolumeElements(CConfig* config, CMeshReaderBase* mesh) { /*--- Reset the global to local element mapping. ---*/ Global_to_Local_Elem.clear(); @@ -3643,7 +3652,7 @@ void CPhysicalGeometry::LoadLinearlyPartitionedVolumeElements(CConfig* config, C reduce(nelem_pyramid, Global_nelem_pyramid); } -void CPhysicalGeometry::LoadUnpartitionedSurfaceElements(CConfig* config, CMeshReaderFVM* mesh) { +void CPhysicalGeometry::LoadUnpartitionedSurfaceElements(CConfig* config, CMeshReaderBase* mesh) { /*--- The master node takes care of loading all markers and surface elements from the file. This information is later put into linear partitions to make its redistribution easier @@ -3709,11 +3718,11 @@ void CPhysicalGeometry::LoadUnpartitionedSurfaceElements(CConfig* config, CMeshR /*--- Not a mixed section. We already know the element type, which is stored ---*/ - vtk_type = (int)connElems[jElem * SU2_CONN_SIZE + 1]; + vtk_type = static_cast(connElems[jElem * SU2_CONN_SIZE + 1]); /*--- Store the loop size more easily. ---*/ - npe = (int)(SU2_CONN_SIZE - SU2_CONN_SKIP); + npe = static_cast(SU2_CONN_SIZE - SU2_CONN_SKIP); /*--- Store the nodes for this element more clearly. ---*/ @@ -5089,7 +5098,7 @@ void CPhysicalGeometry::SetTurboVertex(CConfig* config, unsigned short val_iZone if (config->GetMarker_All_TurbomachineryFlag(iMarker) == marker_flag) { /*--- compute the amount of vertexes for each span-wise section to initialize the CTurboVertex pointers and * auxiliary pointers ---*/ - for (iVertex = 0; (unsigned long)iVertex < nVertex[iMarker]; iVertex++) { + for (iVertex = 0; static_cast(iVertex) < nVertex[iMarker]; iVertex++) { iPoint = vertex[iMarker][iVertex]->GetNode(); if (nDim == 3) { dist = 10E+06; @@ -5189,7 +5198,7 @@ void CPhysicalGeometry::SetTurboVertex(CConfig* config, unsigned short val_iZone } /*--- store the vertexes in a ordered manner in span-wise directions but not yet ordered pitch-wise ---*/ - for (iVertex = 0; (unsigned long)iVertex < nVertex[iMarker]; iVertex++) { + for (iVertex = 0; static_cast(iVertex) < nVertex[iMarker]; iVertex++) { iPoint = vertex[iMarker][iVertex]->GetNode(); if (nDim == 3) { dist = 10E+06; @@ -5515,7 +5524,7 @@ void CPhysicalGeometry::SetTurboVertex(CConfig* config, unsigned short val_iZone SetnVertexSpanMax(marker_flag, nVert); } /*--- for all the processor should be known the amount of total turbovertex per span ---*/ - nTotVertex_gb[iSpan] = (int)nVert; + nTotVertex_gb[iSpan] = static_cast(nVert); for (iMarker = 0; iMarker < nMarker; iMarker++) { for (iMarkerTP = 1; iMarkerTP < config->GetnMarker_Turbomachinery() + 1; iMarkerTP++) { @@ -7417,70 +7426,6 @@ void CPhysicalGeometry::VisualizeControlVolume(const CConfig* config) const { } } -void CPhysicalGeometry::SetCoord_Smoothing(unsigned short val_nSmooth, su2double val_smooth_coeff, CConfig* config) { - unsigned short iSmooth, nneigh, iMarker; - su2double *Coord_Old, *Coord_Sum, *Coord, *Coord_i, *Coord_j, Position_Plane = 0.0; - unsigned long iEdge, iPoint, jPoint, iVertex; - su2double eps = 1E-6; - bool NearField = false; - - Coord = new su2double[nDim]; - - nodes->SetCoord_Old(); - - /*--- Jacobi iterations ---*/ - for (iSmooth = 0; iSmooth < val_nSmooth; iSmooth++) { - nodes->SetCoord_SumZero(); - - /*--- Loop over Interior edges ---*/ - for (iEdge = 0; iEdge < nEdge; iEdge++) { - iPoint = edges->GetNode(iEdge, 0); - Coord_i = nodes->GetCoord(iPoint); - - jPoint = edges->GetNode(iEdge, 1); - Coord_j = nodes->GetCoord(jPoint); - - /*--- Accumulate nearest neighbor Coord to Res_sum for each variable ---*/ - nodes->AddCoord_Sum(iPoint, Coord_j); - nodes->AddCoord_Sum(jPoint, Coord_i); - } - - /*--- Loop over all mesh points (Update Coords with averaged sum) ---*/ - for (iPoint = 0; iPoint < nPoint; iPoint++) { - nneigh = nodes->GetnPoint(iPoint); - Coord_Sum = nodes->GetCoord_Sum(iPoint); - Coord_Old = nodes->GetCoord_Old(iPoint); - - if (nDim == 2) { - Coord[0] = (Coord_Old[0] + val_smooth_coeff * Coord_Sum[0]) / (1.0 + val_smooth_coeff * su2double(nneigh)); - Coord[1] = (Coord_Old[1] + val_smooth_coeff * Coord_Sum[1]) / (1.0 + val_smooth_coeff * su2double(nneigh)); - if ((NearField) && ((Coord_Old[1] > Position_Plane - eps) && (Coord_Old[1] < Position_Plane + eps))) - Coord[1] = Coord_Old[1]; - } - - if (nDim == 3) { - Coord[0] = (Coord_Old[0] + val_smooth_coeff * Coord_Sum[0]) / (1.0 + val_smooth_coeff * su2double(nneigh)); - Coord[1] = (Coord_Old[1] + val_smooth_coeff * Coord_Sum[1]) / (1.0 + val_smooth_coeff * su2double(nneigh)); - Coord[2] = (Coord_Old[2] + val_smooth_coeff * Coord_Sum[2]) / (1.0 + val_smooth_coeff * su2double(nneigh)); - if ((NearField) && ((Coord_Old[2] > Position_Plane - eps) && (Coord_Old[2] < Position_Plane + eps))) - Coord[2] = Coord_Old[2]; - } - - nodes->SetCoord(iPoint, Coord); - } - - /*--- Copy boundary values ---*/ - for (iMarker = 0; iMarker < nMarker; iMarker++) - for (iVertex = 0; iVertex < nVertex[iMarker]; iVertex++) { - iPoint = vertex[iMarker][iVertex]->GetNode(); - Coord_Old = nodes->GetCoord_Old(iPoint); - nodes->SetCoord(iPoint, Coord_Old); - } - } - - delete[] Coord; -} - bool CPhysicalGeometry::FindFace(unsigned long first_elem, unsigned long second_elem, unsigned short& face_first_elem, unsigned short& face_second_elem) { if (first_elem == second_elem) return false; @@ -8331,7 +8276,7 @@ void CPhysicalGeometry::SetSensitivity(CConfig* config) { /*--- First, read the number of variables and points. ---*/ ret = fread(Restart_Vars, sizeof(int), nRestart_Vars, fhw); - if (ret != (unsigned long)nRestart_Vars) { + if (ret != static_cast(nRestart_Vars)) { SU2_MPI::Error("Error reading restart file.", CURRENT_FUNCTION); } @@ -8358,7 +8303,7 @@ void CPhysicalGeometry::SetSensitivity(CConfig* config) { config->fields.push_back("Point_ID"); for (iVar = 0; iVar < nFields; iVar++) { ret = fread(str_buf, sizeof(char), CGNS_STRING_SIZE, fhw); - if (ret != (unsigned long)CGNS_STRING_SIZE) { + if (ret != static_cast(CGNS_STRING_SIZE)) { SU2_MPI::Error("Error reading restart file.", CURRENT_FUNCTION); } config->fields.push_back(str_buf); @@ -8371,7 +8316,7 @@ void CPhysicalGeometry::SetSensitivity(CConfig* config) { /*--- Read in the data for the restart at all local points. ---*/ ret = fread(Restart_Data, sizeof(passivedouble), nFields * GetnPointDomain(), fhw); - if (ret != (unsigned long)nFields * GetnPointDomain()) { + if (ret != static_cast(nFields) * GetnPointDomain()) { SU2_MPI::Error("Error reading restart file.", CURRENT_FUNCTION); } @@ -8466,7 +8411,7 @@ void CPhysicalGeometry::SetSensitivity(CConfig* config) { config->fields.emplace_back("Point_ID"); for (iVar = 0; iVar < nFields; iVar++) { index = iVar * CGNS_STRING_SIZE; - for (iChar = 0; iChar < (unsigned long)CGNS_STRING_SIZE; iChar++) { + for (iChar = 0; iChar < static_cast(CGNS_STRING_SIZE); iChar++) { str_buf[iChar] = mpi_str_buf[index + iChar]; } field_buf.append(str_buf); diff --git a/Common/src/geometry/dual_grid/CPoint.cpp b/Common/src/geometry/dual_grid/CPoint.cpp index 35c45a826b4..7096dfa6443 100644 --- a/Common/src/geometry/dual_grid/CPoint.cpp +++ b/Common/src/geometry/dual_grid/CPoint.cpp @@ -94,12 +94,6 @@ void CPoint::FullAllocation(unsigned short imesh, const CConfig* config) { Vertex.resize(npoint); - /*--- For smoothing the numerical grid coordinates ---*/ - if (config->GetSmoothNumGrid()) { - Coord_Old.resize(npoint, nDim) = su2double(0.0); - Coord_Sum.resize(npoint, nDim) = su2double(0.0); - } - /*--- Storage of grid velocities for dynamic meshes. ---*/ if (config->GetDynamic_Grid()) { @@ -190,5 +184,3 @@ void CPoint::SetCoord_Old() { assert(Coord_Old.size() == Coord.size()); parallelCopy(Coord.size(), Coord.data(), Coord_Old.data()); } - -void CPoint::SetCoord_SumZero() { parallelSet(Coord_Sum.size(), 0.0, Coord_Sum.data()); } diff --git a/Common/src/geometry/elements/CElement.cpp b/Common/src/geometry/elements/CElement.cpp index c42a3707562..5acf96a3ac9 100644 --- a/Common/src/geometry/elements/CElement.cpp +++ b/Common/src/geometry/elements/CElement.cpp @@ -47,6 +47,8 @@ CElement::CElement(unsigned short ngauss, unsigned short nnodes, unsigned short NodalStress.resize(nNodes, 6) = su2double(0.0); + NodalTemperature.resize(nNodes) = su2double(0.0); + Mab.resize(nNodes, nNodes); Ks_ab.resize(nNodes, nNodes); Kab.resize(nNodes); diff --git a/Common/src/geometry/elements/CQUAD4.cpp b/Common/src/geometry/elements/CQUAD4.cpp index 07725a3af19..29d3a621281 100644 --- a/Common/src/geometry/elements/CQUAD4.cpp +++ b/Common/src/geometry/elements/CQUAD4.cpp @@ -67,7 +67,6 @@ CQUAD4::CQUAD4() : CElementWithKnownSizes() { /*--- Store the extrapolation functions (used to compute nodal stresses) ---*/ su2double ExtrapCoord[4][2], sqrt3 = 1.732050807568877; - ; ExtrapCoord[0][0] = -sqrt3; ExtrapCoord[0][1] = -sqrt3; diff --git a/Common/src/geometry/meshreader/CBoxMeshReaderFEM.cpp b/Common/src/geometry/meshreader/CBoxMeshReaderFEM.cpp new file mode 100644 index 00000000000..44dfde1dc7f --- /dev/null +++ b/Common/src/geometry/meshreader/CBoxMeshReaderFEM.cpp @@ -0,0 +1,348 @@ +/*! + * \file CBoxMeshReaderFEM.cpp + * \brief Reads a 3D box grid into linear partitions for the + * finite element solver (FEM). + * \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 . + */ + +#include "../../../include/toolboxes/CLinearPartitioner.hpp" +#include "../../../include/geometry/meshreader/CBoxMeshReaderFEM.hpp" + +CBoxMeshReaderFEM::CBoxMeshReaderFEM(const CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone) + : CMeshReaderBase(val_config, val_iZone, val_nZone) { + /* The box mesh is always 3D. */ + dimension = 3; + + /* Set the VTK type for the interior elements and the boundary elements. */ + KindElem = HEXAHEDRON; + KindBound = QUADRILATERAL; + + /* The number of grid nodes in the i, j and k directions. */ + nNode = config->GetMeshBoxSize(0); + mNode = config->GetMeshBoxSize(1); + pNode = config->GetMeshBoxSize(2); + + /* Lengths for non-square domains. */ + Lx = config->GetMeshBoxLength(0); + Ly = config->GetMeshBoxLength(1); + Lz = config->GetMeshBoxLength(2); + + /* Offsets in x, y and z directions from 0.0. */ + Ox = config->GetMeshBoxOffset(0); + Oy = config->GetMeshBoxOffset(1); + Oz = config->GetMeshBoxOffset(2); + + /* Polynomial degree of the solution. */ + nPolySol = config->GetMeshBoxPSolFEM(); + + /*--- Compute and store the interior elements, points, and surface elements + for this rank, for which simple analytic formulae can be used. ---*/ + ComputeBoxVolumeConnectivity(); + ComputeBoxPointCoordinates(); + ComputeBoxSurfaceConnectivity(); +} + +CBoxMeshReaderFEM::~CBoxMeshReaderFEM() = default; + +void CBoxMeshReaderFEM::ComputeBoxPointCoordinates() { + /*--- Set the global count of points based on the grid dimensions. ---*/ + numberOfGlobalPoints = nNode * mNode * pNode; + + /*--- Loop over the local elements to determine the global + point IDs to be stored on this rank. --*/ + unsigned long ind = 0; + for (unsigned long i = 0; i < numberOfLocalElements; ++i) { + /*--- Store the number of grid DOFs for this element and + skip the meta data for this element (5 entries). ---*/ + const unsigned long nDOFsGrid = localVolumeElementConnectivity[ind + 3]; + ind += 5; + + /*--- Copy the connectivity to globalPointIDs. ---*/ + unsigned long* conn = localVolumeElementConnectivity.data() + ind; + ind += nDOFsGrid; + globalPointIDs.insert(globalPointIDs.end(), conn, conn + nDOFsGrid); + } + + /*--- Sort the globalPointIDs and remove the duplicate entries. ---*/ + sort(globalPointIDs.begin(), globalPointIDs.end()); + vector::iterator lastNode; + lastNode = unique(globalPointIDs.begin(), globalPointIDs.end()); + globalPointIDs.erase(lastNode, globalPointIDs.end()); + + /*--- Determine the number of locally stored points. ---*/ + numberOfLocalPoints = globalPointIDs.size(); + + /*--- Allocate the memory for the locally stored points. ---*/ + localPointCoordinates.resize(dimension); + for (int k = 0; k < dimension; k++) localPointCoordinates[k].resize(numberOfLocalPoints); + + /*--- Loop over the locally stored points. ---*/ + const unsigned long nPointIJ = nNode * mNode; + for (unsigned long i = 0; i < numberOfLocalPoints; ++i) { + /*--- Convert the global index to i,j,k indices. ---*/ + const unsigned long kNode = globalPointIDs[i] / nPointIJ; + const unsigned long rem = globalPointIDs[i] - kNode * nPointIJ; + const unsigned long jNode = rem / nNode; + const unsigned long iNode = rem - jNode * nNode; + + /*--- Store the coordinates of the point. ---*/ + localPointCoordinates[0][i] = SU2_TYPE::GetValue(Lx * ((su2double)iNode) / ((su2double)(nNode - 1)) + Ox); + localPointCoordinates[1][i] = SU2_TYPE::GetValue(Ly * ((su2double)jNode) / ((su2double)(mNode - 1)) + Oy); + localPointCoordinates[2][i] = SU2_TYPE::GetValue(Lz * ((su2double)kNode) / ((su2double)(pNode - 1)) + Oz); + } +} + +void CBoxMeshReaderFEM::ComputeBoxVolumeConnectivity() { + /*--- Set the global count of elements based on the grid dimensions. ---*/ + const unsigned long nElemI = nNode - 1; + const unsigned long nElemIJ = nElemI * (mNode - 1); + numberOfGlobalElements = nElemIJ * (pNode - 1); + + /*--- Get a partitioner to help with linear partitioning. ---*/ + CLinearPartitioner elemPartitioner(numberOfGlobalElements, 0); + + /*--- Determine the index of the first and last element to be stored + on this rank and the number of local elements. ---*/ + const unsigned long firstIndex = elemPartitioner.GetFirstIndexOnRank(rank); + const unsigned long lastIndex = elemPartitioner.GetLastIndexOnRank(rank); + numberOfLocalElements = elemPartitioner.GetSizeOnRank(rank); + + /*--- Loop over the owned element range. ---*/ + for (unsigned long elem = firstIndex; elem < lastIndex; ++elem) { + /*--- Retrieve the i,j,k indices of this element, which are the indices + of the lower, left, back point of the element. --*/ + const unsigned long kNode = elem / nElemIJ; + const unsigned long rem = elem - kNode * nElemIJ; + const unsigned long jNode = rem / nElemI; + const unsigned long iNode = rem - jNode * nElemI; + + /*--- Store the meta data of this element. ---*/ + localVolumeElementConnectivity.push_back(KindElem); + localVolumeElementConnectivity.push_back(1); // Pol. degree grid. + localVolumeElementConnectivity.push_back(nPolySol); + localVolumeElementConnectivity.push_back(8); // Number of grid DOFs. + localVolumeElementConnectivity.push_back(elem); // Global elem ID. + + /*--- Store the volume connectivity in the format used + by the FEM solver. ---*/ + localVolumeElementConnectivity.push_back(kNode * mNode * nNode + jNode * nNode + iNode); + localVolumeElementConnectivity.push_back(kNode * mNode * nNode + jNode * nNode + iNode + 1); + localVolumeElementConnectivity.push_back(kNode * mNode * nNode + (jNode + 1) * nNode + iNode); + localVolumeElementConnectivity.push_back(kNode * mNode * nNode + (jNode + 1) * nNode + iNode + 1); + localVolumeElementConnectivity.push_back((kNode + 1) * mNode * nNode + jNode * nNode + iNode); + localVolumeElementConnectivity.push_back((kNode + 1) * mNode * nNode + jNode * nNode + iNode + 1); + localVolumeElementConnectivity.push_back((kNode + 1) * mNode * nNode + (jNode + 1) * nNode + iNode); + localVolumeElementConnectivity.push_back((kNode + 1) * mNode * nNode + (jNode + 1) * nNode + iNode + 1); + } +} + +void CBoxMeshReaderFEM::ComputeBoxSurfaceConnectivity() { + /*--- Determine the number of elements in i-direction and the + number of elements on a k-face. ---*/ + const unsigned long nElemI = nNode - 1; + const unsigned long nElemIJ = nElemI * (mNode - 1); + + /*--- Get a partitioner to help with linear partitioning. ---*/ + CLinearPartitioner elemPartitioner(numberOfGlobalElements, 0); + + /*--- The box always has 6 markers. Allocate the required memory. ---*/ + numberOfMarkers = 6; + numberOfLocalSurfaceElements.resize(numberOfMarkers, 0); + surfaceElementConnectivity.resize(numberOfMarkers); + markerNames.resize(numberOfMarkers); + + /*--- Loop over all faces on the xMin (= iMin) boundary. ---*/ + markerNames[0] = "x_minus"; + + unsigned long ind = 0; + for (unsigned long kNode = 0; kNode < pNode - 1; ++kNode) { + for (unsigned long jNode = 0; jNode < mNode - 1; ++jNode, ++ind) { + /*--- Determine the corresponding global element ID and check + if it is stored on this rank. ---*/ + const unsigned long globalElemID = kNode * nElemIJ + jNode * nElemI; + if (elemPartitioner.GetRankContainingIndex(globalElemID) == static_cast(rank)) { + /*--- The corresponding volume element is stored on this rank, + hence store the surface element as well. ---*/ + surfaceElementConnectivity[0].push_back(KindBound); // VTK type. + surfaceElementConnectivity[0].push_back(1); // Poly degree grid. + surfaceElementConnectivity[0].push_back(4); // Number of grid DOFs. + surfaceElementConnectivity[0].push_back(ind); // Global surface element ID. + surfaceElementConnectivity[0].push_back(globalElemID); // Global volume element ID. + + surfaceElementConnectivity[0].push_back(kNode * mNode * nNode + jNode * nNode); + surfaceElementConnectivity[0].push_back((kNode + 1) * mNode * nNode + jNode * nNode); + surfaceElementConnectivity[0].push_back(kNode * mNode * nNode + (jNode + 1) * nNode); + surfaceElementConnectivity[0].push_back((kNode + 1) * mNode * nNode + (jNode + 1) * nNode); + + /*--- Update the number of surface elements for this marker. ---*/ + ++numberOfLocalSurfaceElements[0]; + } + } + } + + /*--- Loop over all faces on the xMax (= iMax) boundary. ---*/ + markerNames[1] = "x_plus"; + + ind = 0; + for (unsigned long kNode = 0; kNode < pNode - 1; ++kNode) { + for (unsigned long jNode = 0; jNode < mNode - 1; ++jNode, ++ind) { + /*--- Determine the corresponding global element ID and check + if it is stored on this rank. ---*/ + const unsigned long globalElemID = kNode * nElemIJ + jNode * nElemI + nElemI - 1; + if (elemPartitioner.GetRankContainingIndex(globalElemID) == static_cast(rank)) { + /*--- The corresponding volume element is stored on this rank, + hence store the surface element as well. ---*/ + surfaceElementConnectivity[1].push_back(KindBound); // VTK type. + surfaceElementConnectivity[1].push_back(1); // Poly degree grid. + surfaceElementConnectivity[1].push_back(4); // Number of grid DOFs. + surfaceElementConnectivity[1].push_back(ind); // Global surface element ID. + surfaceElementConnectivity[1].push_back(globalElemID); // Global volume element ID. + + surfaceElementConnectivity[1].push_back(kNode * mNode * nNode + jNode * nNode + (nNode - 1)); + surfaceElementConnectivity[1].push_back(kNode * mNode * nNode + (jNode + 1) * nNode + (nNode - 1)); + surfaceElementConnectivity[1].push_back((kNode + 1) * mNode * nNode + jNode * nNode + (nNode - 1)); + surfaceElementConnectivity[1].push_back((kNode + 1) * mNode * nNode + (jNode + 1) * nNode + (nNode - 1)); + + /*--- Update the number of surface elements for this marker. ---*/ + ++numberOfLocalSurfaceElements[1]; + } + } + } + + /*--- Loop over all faces on the yMin (= jMin) boundary. ---*/ + markerNames[2] = "y_minus"; + + ind = 0; + for (unsigned long kNode = 0; kNode < pNode - 1; ++kNode) { + for (unsigned long iNode = 0; iNode < nNode - 1; ++iNode, ++ind) { + /*--- Determine the corresponding global element ID and check + if it is stored on this rank. ---*/ + const unsigned long globalElemID = kNode * nElemIJ + iNode; + if (elemPartitioner.GetRankContainingIndex(globalElemID) == static_cast(rank)) { + /*--- The corresponding volume element is stored on this rank, + hence store the surface element as well. ---*/ + surfaceElementConnectivity[2].push_back(KindBound); // VTK type. + surfaceElementConnectivity[2].push_back(1); // Poly degree grid. + surfaceElementConnectivity[2].push_back(4); // Number of grid DOFs. + surfaceElementConnectivity[2].push_back(ind); // Global surface element ID. + surfaceElementConnectivity[2].push_back(globalElemID); // Global volume element ID. + + surfaceElementConnectivity[2].push_back(kNode * mNode * nNode + iNode); + surfaceElementConnectivity[2].push_back(kNode * mNode * nNode + iNode + 1); + surfaceElementConnectivity[2].push_back((kNode + 1) * mNode * nNode + iNode); + surfaceElementConnectivity[2].push_back((kNode + 1) * mNode * nNode + iNode + 1); + + /*--- Update the number of surface elements for this marker. ---*/ + ++numberOfLocalSurfaceElements[2]; + } + } + } + + /*--- Loop over all faces on the yMax (= jMax) boundary. ---*/ + markerNames[3] = "y_plus"; + + ind = 0; + for (unsigned long kNode = 0; kNode < pNode - 1; ++kNode) { + for (unsigned long iNode = 0; iNode < nNode - 1; ++iNode, ++ind) { + /*--- Determine the corresponding global element ID and check + if it is stored on this rank. ---*/ + const unsigned long globalElemID = kNode * nElemIJ + (mNode - 2) * nElemI + iNode; + if (elemPartitioner.GetRankContainingIndex(globalElemID) == static_cast(rank)) { + /*--- The corresponding volume element is stored on this rank, + hence store the surface element as well. ---*/ + surfaceElementConnectivity[3].push_back(KindBound); // VTK type. + surfaceElementConnectivity[3].push_back(1); // Poly degree grid. + surfaceElementConnectivity[3].push_back(4); // Number of grid DOFs. + surfaceElementConnectivity[3].push_back(ind); // Global surface element ID. + surfaceElementConnectivity[3].push_back(globalElemID); // Global volume element ID. + + surfaceElementConnectivity[3].push_back(kNode * mNode * nNode + (mNode - 1) * nNode + iNode); + surfaceElementConnectivity[3].push_back(kNode * mNode * nNode + (mNode - 1) * nNode + iNode + 1); + surfaceElementConnectivity[3].push_back((kNode + 1) * mNode * nNode + (mNode - 1) * nNode + iNode); + surfaceElementConnectivity[3].push_back((kNode + 1) * mNode * nNode + (mNode - 1) * nNode + iNode + 1); + + /*--- Update the number of surface elements for this marker. ---*/ + ++numberOfLocalSurfaceElements[3]; + } + } + } + + /*--- Loop over all faces on the zMin (= kMin) boundary. ---*/ + markerNames[4] = "z_minus"; + + ind = 0; + for (unsigned long jNode = 0; jNode < mNode - 1; ++jNode) { + for (unsigned long iNode = 0; iNode < nNode - 1; ++iNode, ++ind) { + /*--- Determine the corresponding global element ID and check + if it is stored on this rank. ---*/ + const unsigned long globalElemID = jNode * nElemI + iNode; + if (elemPartitioner.GetRankContainingIndex(globalElemID) == static_cast(rank)) { + /*--- The corresponding volume element is stored on this rank, + hence store the surface element as well. ---*/ + surfaceElementConnectivity[4].push_back(KindBound); // VTK type. + surfaceElementConnectivity[4].push_back(1); // Poly degree grid. + surfaceElementConnectivity[4].push_back(4); // Number of grid DOFs. + surfaceElementConnectivity[4].push_back(ind); // Global surface element ID. + surfaceElementConnectivity[4].push_back(globalElemID); // Global volume element ID. + + surfaceElementConnectivity[4].push_back(jNode * nNode + iNode); + surfaceElementConnectivity[4].push_back(jNode * nNode + iNode + 1); + surfaceElementConnectivity[4].push_back((jNode + 1) * nNode + iNode); + surfaceElementConnectivity[4].push_back((jNode + 1) * nNode + iNode + 1); + + /*--- Update the number of surface elements for this marker. ---*/ + ++numberOfLocalSurfaceElements[4]; + } + } + } + + /*--- Loop over all faces on the zMax (= kMax) boundary. ---*/ + markerNames[5] = "z_plus"; + + ind = 0; + for (unsigned long jNode = 0; jNode < mNode - 1; ++jNode) { + for (unsigned long iNode = 0; iNode < nNode - 1; ++iNode, ++ind) { + /*--- Determine the corresponding global element ID and check + if it is stored on this rank. ---*/ + const unsigned long globalElemID = (pNode - 2) * nElemIJ + jNode * nElemI + iNode; + if (elemPartitioner.GetRankContainingIndex(globalElemID) == static_cast(rank)) { + /*--- The corresponding volume element is stored on this rank, + hence store the surface element as well. ---*/ + surfaceElementConnectivity[5].push_back(KindBound); // VTK type. + surfaceElementConnectivity[5].push_back(1); // Poly degree grid. + surfaceElementConnectivity[5].push_back(4); // Number of grid DOFs. + surfaceElementConnectivity[5].push_back(ind); // Global surface element ID. + surfaceElementConnectivity[5].push_back(globalElemID); // Global volume element ID. + + surfaceElementConnectivity[5].push_back((pNode - 1) * mNode * nNode + jNode * nNode + iNode); + surfaceElementConnectivity[5].push_back((pNode - 1) * mNode * nNode + jNode * nNode + iNode + 1); + surfaceElementConnectivity[5].push_back((pNode - 1) * mNode * nNode + (jNode + 1) * nNode + iNode); + surfaceElementConnectivity[5].push_back((pNode - 1) * mNode * nNode + (jNode + 1) * nNode + iNode + 1); + + /*--- Update the number of surface elements for this marker. ---*/ + ++numberOfLocalSurfaceElements[5]; + } + } + } +} diff --git a/Common/src/geometry/meshreader/CBoxMeshReaderFVM.cpp b/Common/src/geometry/meshreader/CBoxMeshReaderFVM.cpp index e4ee811eaec..e2c0f52c3b1 100644 --- a/Common/src/geometry/meshreader/CBoxMeshReaderFVM.cpp +++ b/Common/src/geometry/meshreader/CBoxMeshReaderFVM.cpp @@ -29,8 +29,8 @@ #include "../../../include/toolboxes/CLinearPartitioner.hpp" #include "../../../include/geometry/meshreader/CBoxMeshReaderFVM.hpp" -CBoxMeshReaderFVM::CBoxMeshReaderFVM(CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone) - : CMeshReaderFVM(val_config, val_iZone, val_nZone) { +CBoxMeshReaderFVM::CBoxMeshReaderFVM(const CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone) + : CMeshReaderBase(val_config, val_iZone, val_nZone) { /* The box mesh is always 3D. */ dimension = 3; @@ -74,7 +74,7 @@ void CBoxMeshReaderFVM::ComputeBoxPointCoordinates() { /* Determine number of local points */ for (unsigned long globalIndex = 0; globalIndex < numberOfGlobalPoints; globalIndex++) { - if ((int)pointPartitioner.GetRankContainingIndex(globalIndex) == rank) { + if (static_cast(pointPartitioner.GetRankContainingIndex(globalIndex)) == rank) { numberOfLocalPoints++; } } @@ -88,7 +88,7 @@ void CBoxMeshReaderFVM::ComputeBoxPointCoordinates() { for (unsigned long kNode = 0; kNode < pNode; kNode++) { for (unsigned long jNode = 0; jNode < mNode; jNode++) { for (unsigned long iNode = 0; iNode < nNode; iNode++) { - if ((int)pointPartitioner.GetRankContainingIndex(globalIndex) == rank) { + if (static_cast(pointPartitioner.GetRankContainingIndex(globalIndex)) == rank) { /* Store the coordinates more clearly. */ const passivedouble x = SU2_TYPE::GetValue(Lx * ((su2double)iNode) / ((su2double)(nNode - 1)) + Ox); const passivedouble y = SU2_TYPE::GetValue(Ly * ((su2double)jNode) / ((su2double)(mNode - 1)) + Oy); @@ -133,7 +133,7 @@ void CBoxMeshReaderFVM::ComputeBoxVolumeConnectivity() { /* Check whether any of the points is in our linear partition. */ bool isOwned = false; for (unsigned short i = 0; i < N_POINTS_HEXAHEDRON; i++) { - if ((int)pointPartitioner.GetRankContainingIndex(connectivity[i]) == rank) { + if (static_cast(pointPartitioner.GetRankContainingIndex(connectivity[i])) == rank) { isOwned = true; } } diff --git a/Common/src/fem/fem_cgns_elements.cpp b/Common/src/geometry/meshreader/CCGNSElementType.cpp similarity index 68% rename from Common/src/fem/fem_cgns_elements.cpp rename to Common/src/geometry/meshreader/CCGNSElementType.cpp index e4df7f0393b..53a549c8c78 100644 --- a/Common/src/fem/fem_cgns_elements.cpp +++ b/Common/src/geometry/meshreader/CCGNSElementType.cpp @@ -1,6 +1,6 @@ /*! - * \file fem_cgns_elements.cpp - * \brief CGNS element definitions and conversions to the SU2 standard. + * \file CCGNSElementType.cpp + * \brief Class that converts the CGNS element definitions to the SU2 standard. * \author E. van der Weide * \version 8.1.0 "Harrier" * @@ -26,9 +26,8 @@ */ #ifdef HAVE_CGNS -#include "../../include/fem/fem_cgns_elements.hpp" -#include "../../include/fem/geometry_structure_fem_part.hpp" -#include "../../include/parallelization/mpi_structure.hpp" +#include "../../../include/geometry/meshreader/CCGNSElementType.hpp" +#include "../../../include/option_structure.hpp" #include #include @@ -36,434 +35,139 @@ using namespace std; -#if CGNS_VERSION >= 3300 - -void CCGNSElementType::DetermineMetaData(const unsigned short nDim, const int fn, const int iBase, const int iZone, - const int iConn) { - /* Store the connectivity ID. */ - connID = iConn; - - /* Read the element type and range from the CGNS file. */ - char cgnsname[CGNS_STRING_SIZE]; - int nBndry, parentFlag; - if (cg_section_read(fn, iBase, iZone, iConn, cgnsname, &elemType, &indBeg, &indEnd, &nBndry, &parentFlag) != CG_OK) - cg_error_exit(); - - /* Store the name of this connectivity. */ - connName = cgnsname; - - /* Determine the number of elements present in this connectivity section. */ - nElem = indEnd - indBeg + 1; - - /* Determine the dimension of this element type, i.e. the number of - parametric coordinates and conclude whether or not this is a volume or - surface connectivity. Note that it is possible that it is neither of these, - e.g. line elements for 3D. This information is not needed for the DG - flow solver and is therefore ignored. */ - const unsigned short nDimElem = DetermineElementDimension(fn, iBase, iZone); - volumeConn = (nDimElem == nDim); - surfaceConn = (nDimElem == nDim - 1); -} - -void CCGNSElementType::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, - vector& boundElems) { - /* Determine the index range to be read for this rank. */ - const cgsize_t iBeg = indBeg + offsetRank; - const cgsize_t iEnd = iBeg + nBoundElemRank - 1; - - /* Determine the size of the vector needed to read the connectivity - data from the CGNS file. */ - cgsize_t sizeNeeded; - if (cg_ElementPartialSize(fn, iBase, iZone, connID, iBeg, iEnd, &sizeNeeded) != CG_OK) cg_error_exit(); - - /* Allocate the memory for the connectivity and read the data. */ - vector connCGNSVec(sizeNeeded); - if (elemType == MIXED) { - vector connCGNSOffsetVec(iEnd - iBeg + 2); - if (cg_poly_elements_partial_read(fn, iBase, iZone, connID, iBeg, iEnd, connCGNSVec.data(), - connCGNSOffsetVec.data(), nullptr) != CG_OK) - cg_error_exit(); - } else { - if (cg_elements_partial_read(fn, iBase, iZone, connID, iBeg, iEnd, connCGNSVec.data(), nullptr) != CG_OK) - cg_error_exit(); - } - - /* Define the variables needed to convert the connectivities from CGNS to - SU2 format. Note that the vectors are needed to support a connectivity - section with mixed element types. */ - vector CGNS_Type; - vector VTK_Type; - vector nPoly; - vector nDOFs; - - vector > SU2ToCGNS; - - /* Definition of variables used in the loop below. */ - cgsize_t* connCGNS = connCGNSVec.data(); - ElementType_t typeElem = elemType; - vector connSU2; - - /* Loop over the elements just read. */ - for (unsigned long i = 0; i < nBoundElemRank; ++i, ++locBoundElemCount) { - /* Determine the element type for this element if this is a mixed - connectivity and set the pointer to the actual connectivity data. */ - if (elemType == MIXED) { - typeElem = (ElementType_t)connCGNS[0]; - ++connCGNS; +void CCGNSElementType::CGNSToSU2(const ElementType_t val_elemType, const unsigned long val_globalID, + const cgsize_t* connCGNS, std::vector& connSU2) { + /*--- Clear the contents of connSU2. ---*/ + connSU2.clear(); + + /*--- Search in the stored elements if the current element type is present. ---*/ + unsigned long ind; + for (ind = 0; ind < CGNSTypeStored.size(); ++ind) + if (val_elemType == CGNSTypeStored[ind]) break; + + /*--- If the element type is not present in the stored element types, + create the data. ---*/ + if (ind == CGNSTypeStored.size()) { + /*--- Call the appropriate routine to determine the actual data. ---*/ + unsigned short VTK_Type, nPoly, nDOFs; + vector SU2ToCGNS; + + switch (val_elemType) { + case NODE: + CreateDataNODE(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case BAR_2: + CreateDataBAR_2(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case BAR_3: + CreateDataBAR_3(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case BAR_4: + CreateDataBAR_4(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case BAR_5: + CreateDataBAR_5(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case TRI_3: + CreateDataTRI_3(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case TRI_6: + CreateDataTRI_6(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case TRI_10: + CreateDataTRI_10(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case TRI_15: + CreateDataTRI_15(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case QUAD_4: + CreateDataQUAD_4(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case QUAD_9: + CreateDataQUAD_9(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case QUAD_16: + CreateDataQUAD_16(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case QUAD_25: + CreateDataQUAD_25(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case TETRA_4: + CreateDataTETRA_4(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case TETRA_10: + CreateDataTETRA_10(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case TETRA_20: + CreateDataTETRA_20(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case TETRA_35: + CreateDataTETRA_35(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case PYRA_5: + CreateDataPYRA_5(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case PYRA_14: + CreateDataPYRA_14(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case PYRA_30: + CreateDataPYRA_30(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case PYRA_55: + CreateDataPYRA_55(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case PENTA_6: + CreateDataPENTA_6(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case PENTA_18: + CreateDataPENTA_18(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case PENTA_40: + CreateDataPENTA_40(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case PENTA_75: + CreateDataPENTA_75(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case HEXA_8: + CreateDataHEXA_8(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case HEXA_27: + CreateDataHEXA_27(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case HEXA_64: + CreateDataHEXA_64(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + case HEXA_125: + CreateDataHEXA_125(VTK_Type, nPoly, nDOFs, SU2ToCGNS); + break; + + default: + ostringstream message; + message << "CGNS element type " << val_elemType << " not supported."; + SU2_MPI::Error(message.str(), CURRENT_FUNCTION); } - /* Determine the index in the stored vectors (CGNS_Type, VTK_Type, etc), - which corresponds to this element. If the type is not stored yet, - a new entry in these vectors will be created. */ - const unsigned short ind = IndexInStoredTypes(typeElem, CGNS_Type, VTK_Type, nPoly, nDOFs, SU2ToCGNS); - - /* Resize the connSU2 vector to the appropriate size. */ - connSU2.resize(nDOFs[ind]); - - /* Create the connectivity used in SU2 by carrying out the renumbering. - Note that in CGNS the numbering starts at 1, while in SU2 it starts - at 0. This explains the addition of -1. */ - for (unsigned short j = 0; j < nDOFs[ind]; ++j) connSU2[j] = connCGNS[SU2ToCGNS[ind][j]] - 1; - - /* Set the pointer for connCGNS for the data of the next element. */ - connCGNS += nDOFs[ind]; - - /* Determine the global boundary element ID of this element. */ - const unsigned long globBoundElemID = startingBoundElemIDRank + locBoundElemCount; - - /* Create an object of CBoundaryFace and store it in boundElems. - Note that the corresponding domain element is not known yet and - is therefore set to ULONG_MAX. */ - CBoundaryFace thisBoundFace; - thisBoundFace.VTK_Type = VTK_Type[ind]; - thisBoundFace.nPolyGrid = nPoly[ind]; - thisBoundFace.nDOFsGrid = nDOFs[ind]; - thisBoundFace.globalBoundElemID = globBoundElemID; - thisBoundFace.domainElementID = ULONG_MAX; - thisBoundFace.Nodes = connSU2; - - boundElems.push_back(thisBoundFace); - } -} - -void CCGNSElementType::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) { - /* Determine the index range to be read for this rank. */ - const cgsize_t iBeg = indBeg + offsetRank; - const cgsize_t iEnd = iBeg + nElemRank - 1; - - /* Determine the size of the vector needed to read the connectivity - data from the CGNS file. */ - cgsize_t sizeNeeded; - if (cg_ElementPartialSize(fn, iBase, iZone, connID, iBeg, iEnd, &sizeNeeded) != CG_OK) cg_error_exit(); - - /* Allocate the memory for the connectivity and read the data. */ - vector connCGNSVec(sizeNeeded); - if (elemType == MIXED) { - vector connCGNSOffsetVec(iEnd - iBeg + 2); - if (cg_poly_elements_partial_read(fn, iBase, iZone, connID, iBeg, iEnd, connCGNSVec.data(), - connCGNSOffsetVec.data(), nullptr) != CG_OK) - cg_error_exit(); - - } else { - if (cg_elements_partial_read(fn, iBase, iZone, connID, iBeg, iEnd, connCGNSVec.data(), nullptr) != CG_OK) - cg_error_exit(); + /*--- Store the data just created at the end of the corresponding vectors. ---*/ + CGNSTypeStored.push_back(val_elemType); + VTKTypeStored.push_back(VTK_Type); + nPolyStored.push_back(nPoly); + nDOFsStored.push_back(nDOFs); + SU2ToCGNSStored.push_back(SU2ToCGNS); } - /* Define the variables needed to convert the connectivities from CGNS to - SU2 format. Note that the vectors are needed to support a connectivity - section with mixed element types. */ - vector CGNS_Type; - vector VTK_Type; - vector nPoly; - vector nDOFs; - - vector > SU2ToCGNS; - - /* Definition of variables used in the loop below. */ - cgsize_t* connCGNS = connCGNSVec.data(); - ElementType_t typeElem = elemType; - vector connSU2; - - /* Loop over the elements just read. */ - for (unsigned long i = 0; i < nElemRank; ++i, ++locElemCount) { - /* Determine the element type for this element if this is a mixed - connectivity and set the pointer to the actual connectivity data. */ - if (elemType == MIXED) { - typeElem = (ElementType_t)connCGNS[0]; - ++connCGNS; - } + /*--- Allocate the memory for connSU2 and store the meta data. ---*/ + connSU2.resize(nDOFsStored[ind] + 5); - /* Determine the index in the stored vectors (CGNS_Type, VTK_Type, etc), - which corresponds to this element. If the type is not stored yet, - a new entry in these vectors will be created. */ - const unsigned short ind = IndexInStoredTypes(typeElem, CGNS_Type, VTK_Type, nPoly, nDOFs, SU2ToCGNS); + connSU2[0] = VTKTypeStored[ind]; + connSU2[1] = nPolyStored[ind]; + connSU2[2] = nPolyStored[ind]; + connSU2[3] = nDOFsStored[ind]; + connSU2[4] = val_globalID; - /* Resize the connSU2 vector to the appropriate size. */ - connSU2.resize(nDOFs[ind]); - - /* Create the connectivity used in SU2 by carrying out the renumbering. - Note that in CGNS the numbering starts at 1, while in SU2 it starts - at 0. This explains the addition of -1. */ - for (unsigned short j = 0; j < nDOFs[ind]; ++j) connSU2[j] = connCGNS[SU2ToCGNS[ind][j]] - 1; - - /* Set the pointer for connCGNS for the data of the next element. */ - connCGNS += nDOFs[ind]; - - /* Determine the global element ID of this element. */ - const unsigned long globElemID = startingElemIDRank + locElemCount; - - /* Create a new FEM primal grid element, which stores the required - information. Note that the second to last argument contains the local - offset of the DOFs. This will be corrected to the global offset - afterwards in CPhysicalGeometry::Read_CGNS_Format_Parallel_FEM. - Also note that in CGNS it is not possible (at least at the moment) - to specify a different polynomial degree for the grid and solution. */ - elem[locElemCount] = new CPrimalGridFEM(globElemID, VTK_Type[ind], nPoly[ind], nPoly[ind], nDOFs[ind], nDOFs[ind], - nDOFsLoc, connSU2.data()); - - /* Update the counter nDOFsLoc. */ - nDOFsLoc += nDOFs[ind]; - } -} - -unsigned short CCGNSElementType::DetermineElementDimension(const int fn, const int iBase, const int iZone) { - /*--- Determine the element type and return the appropriate element - dimension, i.e. the number of parametric coordinates needed to - describe the element. ---*/ - switch (elemType) { - case NODE: - return 0; - - case BAR_2: - case BAR_3: - case BAR_4: - case BAR_5: - return 1; - - case TRI_3: - case TRI_6: - case TRI_9: - case TRI_10: - case TRI_12: - case TRI_15: - case QUAD_4: - case QUAD_8: - case QUAD_9: - case QUAD_12: - case QUAD_16: - case QUAD_P4_16: - case QUAD_25: - return 2; - - case TETRA_4: - case TETRA_10: - case TETRA_16: - case TETRA_20: - case TETRA_22: - case TETRA_34: - case TETRA_35: - case PYRA_5: - case PYRA_14: - case PYRA_13: - case PYRA_21: - case PYRA_29: - case PYRA_30: - case PYRA_P4_29: - case PYRA_50: - case PYRA_55: - case PENTA_6: - case PENTA_15: - case PENTA_18: - case PENTA_24: - case PENTA_38: - case PENTA_40: - case PENTA_33: - case PENTA_66: - case PENTA_75: - case HEXA_8: - case HEXA_20: - case HEXA_27: - case HEXA_32: - case HEXA_56: - case HEXA_64: - case HEXA_44: - case HEXA_98: - case HEXA_125: - return 3; - - case MIXED: - return DetermineElementDimensionMixed(fn, iBase, iZone); - - default: - ostringstream message; - message << "Unsupported CGNS element type, " << elemType << ", encountered."; - SU2_MPI::Error(message.str(), CURRENT_FUNCTION); - } - - /* Just return a value to avoid a compiler warning. The program should - never reach this position. */ - return 0; -} - -unsigned short CCGNSElementType::DetermineElementDimensionMixed(const int fn, const int iBase, const int iZone) { - /* Determine the required size of the vector to read the - data of the first element of this connectivity. */ - cgsize_t sizeNeeded; - if (cg_ElementPartialSize(fn, iBase, iZone, connID, indBeg, indBeg, &sizeNeeded) != CG_OK) cg_error_exit(); - - /* Read the data of the first element in this section. */ - vector buf(sizeNeeded); - vector buf_offset(2, 0); - if (cg_poly_elements_partial_read(fn, iBase, iZone, connID, indBeg, indBeg, buf.data(), buf_offset.data(), nullptr) != - CG_OK) - cg_error_exit(); - - /* The first entry of buf contains the element type. Copy this value - temporarily into the member variable elemType and determine the - element dimension of this element. */ - elemType = (ElementType_t)buf[0]; - - unsigned short nDimElem = DetermineElementDimension(fn, iBase, iZone); - - /* Reset the element type to MIXED and return the element dimension. */ - elemType = MIXED; - return nDimElem; -} - -unsigned short CCGNSElementType::IndexInStoredTypes(const ElementType_t typeElem, vector& CGNS_Type, - vector& VTK_Type, vector& nPoly, - vector& nDOFs, - vector >& SU2ToCGNS) { - /* Loop over the available types and check if the current type is present. - If so, break the loop, such that the correct index is stored. */ - unsigned short ind; - for (ind = 0; ind < CGNS_Type.size(); ++ind) - if (typeElem == CGNS_Type[ind]) break; - - /* If the current element type is not present yet, the data for this - type must be created. */ - if (ind == CGNS_Type.size()) { - unsigned short VTKElem, nPolyElem, nDOFsElem; - vector SU2ToCGNSElem; - CreateDataElementType(typeElem, VTKElem, nPolyElem, nDOFsElem, SU2ToCGNSElem); - - CGNS_Type.push_back(typeElem); - VTK_Type.push_back(VTKElem); - nPoly.push_back(nPolyElem); - nDOFs.push_back(nDOFsElem); - SU2ToCGNS.push_back(SU2ToCGNSElem); - } - - /* Return the index. */ - return ind; -} - -void CCGNSElementType::CreateDataElementType(const ElementType_t typeElem, unsigned short& VTK_Type, - unsigned short& nPoly, unsigned short& nDOFs, - vector& SU2ToCGNS) { - /*--- Determine the element type and call the corresponding function - to determine the actual data. ---*/ - switch (typeElem) { - case NODE: - CreateDataNODE(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case BAR_2: - CreateDataBAR_2(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case BAR_3: - CreateDataBAR_3(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case BAR_4: - CreateDataBAR_4(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case BAR_5: - CreateDataBAR_5(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case TRI_3: - CreateDataTRI_3(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case TRI_6: - CreateDataTRI_6(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case TRI_10: - CreateDataTRI_10(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case TRI_15: - CreateDataTRI_15(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case QUAD_4: - CreateDataQUAD_4(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case QUAD_9: - CreateDataQUAD_9(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case QUAD_16: - CreateDataQUAD_16(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case QUAD_25: - CreateDataQUAD_25(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case TETRA_4: - CreateDataTETRA_4(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case TETRA_10: - CreateDataTETRA_10(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case TETRA_20: - CreateDataTETRA_20(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case TETRA_35: - CreateDataTETRA_35(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case PYRA_5: - CreateDataPYRA_5(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case PYRA_14: - CreateDataPYRA_14(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case PYRA_30: - CreateDataPYRA_30(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case PYRA_55: - CreateDataPYRA_55(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case PENTA_6: - CreateDataPENTA_6(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case PENTA_18: - CreateDataPENTA_18(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case PENTA_40: - CreateDataPENTA_40(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case PENTA_75: - CreateDataPENTA_75(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case HEXA_8: - CreateDataHEXA_8(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case HEXA_27: - CreateDataHEXA_27(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case HEXA_64: - CreateDataHEXA_64(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - case HEXA_125: - CreateDataHEXA_125(VTK_Type, nPoly, nDOFs, SU2ToCGNS); - break; - - default: - /* Print an error message that this element type is not supported and exit. */ - ostringstream message; - message << "CGNS element type " << typeElem << " not supported."; - SU2_MPI::Error(message.str(), CURRENT_FUNCTION); - } + /*--- Loop over the DOFs and convert the connectivity from CGNS to SU2 + format. Keep in mind that CGNS start the numbering at 1 while + SU2 starts at 0. ---*/ + for (unsigned short i = 0; i < nDOFsStored[ind]; ++i) connSU2[i + 5] = connCGNS[SU2ToCGNSStored[ind][i]] - 1; } /*------------------------------------------------------------------------*/ @@ -976,9 +680,9 @@ void CCGNSElementType::CreateDataPYRA_30(unsigned short& VTK_Type, unsigned shor SU2ToCGNS[10] = 23; SU2ToCGNS[11] = 8; SU2ToCGNS[12] = 3; - SU2ToCGNS[13] = 20; + SU2ToCGNS[13] = 10; SU2ToCGNS[14] = 9; - SU2ToCGNS[15] = 3; + SU2ToCGNS[15] = 2; SU2ToCGNS[16] = 13; SU2ToCGNS[17] = 25; SU2ToCGNS[18] = 15; @@ -1575,4 +1279,3 @@ void CCGNSElementType::CreateDataHEXA_125(unsigned short& VTK_Type, unsigned sho } #endif -#endif diff --git a/Common/src/geometry/meshreader/CCGNSMeshReaderBase.cpp b/Common/src/geometry/meshreader/CCGNSMeshReaderBase.cpp new file mode 100644 index 00000000000..042ef3f9111 --- /dev/null +++ b/Common/src/geometry/meshreader/CCGNSMeshReaderBase.cpp @@ -0,0 +1,443 @@ +/*! + * \file CCGNSMeshReaderBase.cpp + * \brief Helper class for the reading of a CGNS grid file. + * linear partitions across all ranks. + * \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 . + */ + +#include "../../../include/toolboxes/CLinearPartitioner.hpp" +#include "../../../include/geometry/meshreader/CCGNSMeshReaderBase.hpp" + +CCGNSMeshReaderBase::CCGNSMeshReaderBase(const CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone) + : CMeshReaderBase(val_config, val_iZone, val_nZone) { +#ifdef HAVE_CGNS + /*--- Leave the option to do something in the future here. ---*/ +#else + SU2_MPI::Error(string(" SU2 built without CGNS support. \n") + string(" To use CGNS, build SU2 accordingly."), + CURRENT_FUNCTION); +#endif +} + +CCGNSMeshReaderBase::~CCGNSMeshReaderBase() = default; + +#ifdef HAVE_CGNS +void CCGNSMeshReaderBase::OpenCGNSFile(const string& val_filename) { + /*--- Check whether the supplied file is truly a CGNS file. ---*/ + + int file_type; + float file_version; + if (cg_is_cgns(val_filename.c_str(), &file_type) != CG_OK) { + SU2_MPI::Error(val_filename + string(" was not found or is not a properly formatted") + + string(" CGNS file.\nNote that SU2 expects unstructured") + + string(" CGNS files in ADF data format."), + CURRENT_FUNCTION); + } + + /*--- Open the CGNS file for reading. The value of cgnsFileID returned + is the specific index number for this file and will be + repeatedly used in the function calls. ---*/ + + if (cg_open(val_filename.c_str(), CG_MODE_READ, &cgnsFileID)) cg_error_exit(); + if (rank == MASTER_NODE) { + cout << "Reading the CGNS file: "; + cout << val_filename.c_str() << "." << endl; + } + if (cg_version(cgnsFileID, &file_version)) cg_error_exit(); + if (rank == MASTER_NODE) { + if (file_version < 4.0) { + cout + << "WARNING: The CGNS file version (" << file_version + << ") is old and may cause high memory usage issues, consider updating the file with the cgnsupdate tool.\n"; + } + } +} + +void CCGNSMeshReaderBase::ReadCGNSDatabaseMetadata() { + /*--- Get the number of databases. This is the highest node + in the CGNS heirarchy. ---*/ + + int nbases; + if (cg_nbases(cgnsFileID, &nbases)) cg_error_exit(); + if (rank == MASTER_NODE) cout << "CGNS file contains " << nbases << " database(s)." << endl; + + /*--- Check if there is more than one database. Throw an + error if there is because this reader can currently + only handle one database. ---*/ + + if (nbases > 1) { + SU2_MPI::Error("CGNS reader currently can only handle 1 database.", CURRENT_FUNCTION); + } + + /*--- Read the database. Note that the CGNS indexing starts at 1. ---*/ + + int cell_dim, phys_dim; + char basename[CGNS_STRING_SIZE]; + if (cg_base_read(cgnsFileID, cgnsBase, basename, &cell_dim, &phys_dim)) cg_error_exit(); + if (rank == MASTER_NODE) { + cout << "Database " << cgnsBase << ", " << basename << ": "; + cout << " cell dimension of " << cell_dim << ", physical "; + cout << "dimension of " << phys_dim << "." << endl; + } + + /*--- Set the number of dimensions baed on cell_dim. ---*/ + + dimension = static_cast(cell_dim); +} + +void CCGNSMeshReaderBase::ReadCGNSZoneMetadata() { + /*--- First, check all sections to find the element types and to + classify them as either surface or volume elements. We will also + perform some error checks here to avoid partitioning issues. ---*/ + + /*--- Get the number of zones for this base. ---*/ + + int nzones; + if (cg_nzones(cgnsFileID, cgnsBase, &nzones)) cg_error_exit(); + if (rank == MASTER_NODE) { + cout << nzones << " total zone(s)." << endl; + } + + /*--- Check if there is more than one zone. Until we enable it, we + will require a single zone CGNS file. Multizone problems can still + be run with CGNS by using separate CGNS files for each zone. ---*/ + + if (nzones > 1) { + SU2_MPI::Error(string("CGNS reader currently expects only 1 zone per CGNS file.") + + string("Multizone problems can be run with separate CGNS files for each zone."), + CURRENT_FUNCTION); + } + + /*--- Read the basic information for this zone, including + the name and the number of vertices, cells, and + boundary cells which are stored in the cgsize variable. ---*/ + + vector cgsize(3); + ZoneType_t zonetype; + char zonename[CGNS_STRING_SIZE]; + if (cg_zone_read(cgnsFileID, cgnsBase, cgnsZone, zonename, cgsize.data())) cg_error_exit(); + + /*--- Rename the zone size information for clarity. + NOTE: The number of cells here may be only the number of + interior elements or it may be the total. This needs to + be counted explicitly later. ---*/ + + numberOfGlobalPoints = cgsize[0]; + int nElemCGNS = cgsize[1]; + + /*--- Get some additional information about the current zone. ---*/ + + if (cg_zone_type(cgnsFileID, cgnsBase, cgnsZone, &zonetype)) cg_error_exit(); + + /*--- Check for an unstructured mesh. Throw an error if not found. ---*/ + + if (zonetype != Unstructured) + SU2_MPI::Error("Structured CGNS zone found while unstructured expected.", CURRENT_FUNCTION); + + /*--- Print current zone info to the console. ---*/ + + if (rank == MASTER_NODE) { + cout << "Zone " << cgnsZone << ", " << zonename << ": "; + cout << numberOfGlobalPoints << " total vertices, "; + cout << nElemCGNS << " total elements." << endl; + } + + /*--- Retrieve the number of grids in this zone. For now, we know + this is one, but to be more general, this will need to check and + allow for a loop over all grids. ---*/ + + int ngrids; + if (cg_ngrids(cgnsFileID, cgnsBase, cgnsZone, &ngrids)) cg_error_exit(); + if (ngrids > 1) { + SU2_MPI::Error("CGNS reader currently handles only 1 grid per zone.", CURRENT_FUNCTION); + } +} + +void CCGNSMeshReaderBase::ReadCGNSPointCoordinates() { + /*--- Compute the number of points that will be on each processor. + This is a linear partitioning with the addition of a simple load + balancing for any remainder points. ---*/ + + CLinearPartitioner pointPartitioner(numberOfGlobalPoints, 0); + + /*--- Store the local number of nodes for this rank. ---*/ + + numberOfLocalPoints = pointPartitioner.GetSizeOnRank(rank); + + /*--- Create buffer to hold the grid coordinates for our rank. ---*/ + + localPointCoordinates.resize(dimension); + for (int k = 0; k < dimension; k++) localPointCoordinates[k].resize(numberOfLocalPoints, 0.0); + + /*--- Set the value of range_max to the total number of nodes in + the unstructured mesh. Also allocate memory for the temporary array + that will hold the grid coordinates as they are extracted. Note the + +1 for CGNS convention. ---*/ + + cgsize_t range_min = (cgsize_t)pointPartitioner.GetFirstIndexOnRank(rank) + 1; + auto range_max = (cgsize_t)pointPartitioner.GetLastIndexOnRank(rank); + + /*--- Loop over each set of coordinates. ---*/ + + for (int k = 0; k < dimension; k++) { + /*--- Read the coordinate info. This will retrieve the + data type (either RealSingle or RealDouble) as + well as the coordname which will specify the + type of data that it is based in the SIDS convention. + This might be "CoordinateX," for instance. ---*/ + + char coordname[CGNS_STRING_SIZE]; + DataType_t datatype; + if (cg_coord_info(cgnsFileID, cgnsBase, cgnsZone, k + 1, &datatype, coordname)) cg_error_exit(); + if (rank == MASTER_NODE) { + cout << "Loading " << coordname; + if (size > SINGLE_NODE) { + cout << " values into linear partitions." << endl; + } else { + cout << " values." << endl; + } + } + + /*--- Check the coordinate name to decide the index for storage. ---*/ + + unsigned short indC = 0; + if (string(coordname) == "CoordinateX") + indC = 0; + else if (string(coordname) == "CoordinateY") + indC = 1; + else if (string(coordname) == "CoordinateZ") + indC = 2; + else + SU2_MPI::Error(string("Unknown coordinate name, ") + coordname + string(", in the CGNS file."), CURRENT_FUNCTION); + + /*--- Now read our rank's chunk of coordinates from the file. + Ask for datatype RealDouble and let CGNS library do the translation + when RealSingle is found. ---*/ + + if (cg_coord_read(cgnsFileID, cgnsBase, cgnsZone, coordname, RealDouble, &range_min, &range_max, + localPointCoordinates[indC].data())) + cg_error_exit(); + } +} + +void CCGNSMeshReaderBase::ReadCGNSSectionMetadata() { + /*--- Begin section for retrieving the connectivity info. ---*/ + + if ((rank == MASTER_NODE) && (size > SINGLE_NODE)) cout << "Distributing connectivity across all ranks." << endl; + + /*--- First check the number of sections. ---*/ + + if (cg_nsections(cgnsFileID, cgnsBase, cgnsZone, &nSections)) cg_error_exit(); + if (rank == MASTER_NODE) { + cout << "Number of connectivity sections is "; + cout << nSections << "." << endl; + } + + /*--- Prepare several data structures to hold the various + pieces of information describing each section. ---*/ + + isInterior.resize(nSections); + nElems.resize(nSections, 0); + elemOffset.resize(nSections + 1, 0); + elemOffset[0] = 0; + connElems.resize(nSections); + sectionNames.resize(nSections, vector(CGNS_STRING_SIZE)); + numberOfGlobalElements = 0; + + for (int s = 0; s < nSections; s++) { + /*--- Read the connectivity details for this section. ---*/ + + int nbndry, parent_flag, vtk_type; + cgsize_t startE, endE, sizeNeeded; + ElementType_t elemType; + if (cg_section_read(cgnsFileID, cgnsBase, cgnsZone, s + 1, sectionNames[s].data(), &elemType, &startE, &endE, + &nbndry, &parent_flag)) + cg_error_exit(); + + /*--- Compute the total element count in this section (global). ---*/ + + unsigned long element_count = (endE - startE + 1); + + /* Get the details for the CGNS element type in this section. */ + + string elem_name = GetCGNSElementType(elemType, vtk_type); + + /* We assume that each section contains interior elements by default. + If we find 1D elements in a 2D problem or 2D elements in a 3D + problem, then we know the section must contain boundary elements. + We assume that each section is composed of either entirely interior + or entirely boundary elements. */ + + isInterior[s] = true; + + if (elemType == MIXED) { + /* For a mixed section, we check the type of the first element + so that we can correctly label this section as an interior or + boundary element section. Here, we also assume that a section + can not hold both interior and boundary elements. First, get + the size required to read a single element from the section. */ + + if (cg_ElementPartialSize(cgnsFileID, cgnsBase, cgnsZone, s + 1, startE, startE, &sizeNeeded) != CG_OK) + cg_error_exit(); + + /* A couple of auxiliary vectors for mixed element sections. */ + + vector connElemCGNS(sizeNeeded); + vector connOffsetCGNS(2, 0); + + /* Retrieve the connectivity information for the first element. */ + + if (cg_poly_elements_partial_read(cgnsFileID, cgnsBase, cgnsZone, s + 1, startE, startE, connElemCGNS.data(), + connOffsetCGNS.data(), nullptr) != CG_OK) + cg_error_exit(); + + /* The element type is in the first position of the connectivity + information that we retrieved from the CGNS file. */ + + elemType = ElementType_t(connElemCGNS[0]); + } + + /* Check for 1D elements in 2D problems, or for 2D elements in + 3D problems. If found, mark the section as a boundary section. */ + + if ((dimension == 2) && (elemType == BAR_2 || elemType == BAR_3)) isInterior[s] = false; + if ((dimension == 3) && (elemType == TRI_3 || elemType == QUAD_4)) isInterior[s] = false; + + /*--- Increment the global element offset for each section + based on whether or not this is a surface or volume section. + We also keep a running count of the total elements globally. ---*/ + + elemOffset[s + 1] = elemOffset[s]; + if (!isInterior[s]) + elemOffset[s + 1] += element_count; + else + numberOfGlobalElements += element_count; + + /*--- Print some information to the console. ---*/ + + if (rank == MASTER_NODE) { + cout << "Section " << string(sectionNames[s].data()); + cout << " contains " << element_count << " elements"; + cout << " of type " << elem_name << "." << endl; + } + } +} + +string CCGNSMeshReaderBase::GetCGNSElementType(ElementType_t val_elem_type, int& val_vtk_type) { + /* Check the CGNS element type and return the string name + for the element and the associated VTK type index. */ + + string elem_name; + switch (val_elem_type) { + case NODE: + elem_name = "Vertex"; + val_vtk_type = 1; + SU2_MPI::Error("Vertex elements detected. Please remove.", CURRENT_FUNCTION); + break; + case BAR_2: + case BAR_3: + case BAR_4: + case BAR_5: + elem_name = "Line"; + val_vtk_type = 3; + if (dimension == 3) SU2_MPI::Error("Line elements detected in a 3D mesh. Please remove.", CURRENT_FUNCTION); + break; + case TRI_3: + case TRI_6: + case TRI_9: + case TRI_10: + case TRI_12: + case TRI_15: + elem_name = "Triangle"; + val_vtk_type = 5; + break; + case QUAD_4: + case QUAD_8: + case QUAD_9: + case QUAD_12: + case QUAD_16: + case QUAD_P4_16: + case QUAD_25: + elem_name = "Quadrilateral"; + val_vtk_type = 9; + break; + case TETRA_4: + case TETRA_10: + case TETRA_16: + case TETRA_20: + case TETRA_22: + case TETRA_34: + case TETRA_35: + elem_name = "Tetrahedron"; + val_vtk_type = 10; + break; + case HEXA_8: + case HEXA_20: + case HEXA_27: + case HEXA_32: + case HEXA_56: + case HEXA_64: + case HEXA_44: + case HEXA_98: + case HEXA_125: + elem_name = "Hexahedron"; + val_vtk_type = 12; + break; + case PENTA_6: + case PENTA_15: + case PENTA_18: + case PENTA_24: + case PENTA_38: + case PENTA_40: + case PENTA_33: + case PENTA_66: + case PENTA_75: + elem_name = "Prism"; + val_vtk_type = 13; + break; + case PYRA_5: + case PYRA_14: + case PYRA_13: + case PYRA_21: + case PYRA_29: + case PYRA_30: + case PYRA_P4_29: + case PYRA_50: + case PYRA_55: + elem_name = "Pyramid"; + val_vtk_type = 14; + break; + case MIXED: + elem_name = "Mixed"; + val_vtk_type = -1; + break; + default: + char buf[100]; + SPRINTF(buf, "Unsupported or unknown CGNS element type: (type %d)\n", val_elem_type); + SU2_MPI::Error(string(buf), CURRENT_FUNCTION); + break; + } + + return elem_name; +} +#endif diff --git a/Common/src/geometry/meshreader/CCGNSMeshReaderFEM.cpp b/Common/src/geometry/meshreader/CCGNSMeshReaderFEM.cpp new file mode 100644 index 00000000000..32cb790291c --- /dev/null +++ b/Common/src/geometry/meshreader/CCGNSMeshReaderFEM.cpp @@ -0,0 +1,516 @@ +/*! + * \file CCGNSMeshReaderFEM.cpp + * \brief Class that reads a single zone of a CGNS mesh file from disk into + * linear partitions across all ranks. + * \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 . + */ + +#include "../../../include/toolboxes/CLinearPartitioner.hpp" +#include "../../../include/geometry/meshreader/CCGNSMeshReaderFEM.hpp" +#include "../../../include/geometry/meshreader/CCGNSElementType.hpp" + +CCGNSMeshReaderFEM::CCGNSMeshReaderFEM(const CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone) + : CCGNSMeshReaderBase(val_config, val_iZone, val_nZone) { +#ifdef HAVE_CGNS + OpenCGNSFile(config->GetMesh_FileName()); + + /*--- Read the basic information about the database and zone(s). ---*/ + ReadCGNSDatabaseMetadata(); + ReadCGNSZoneMetadata(); + + /*--- Read the volume connectivity and distribute it + linearly over the MPI ranks. ---*/ + ReadCGNSVolumeElementConnectivity(); + + /*--- Read the coordinates of the points and communicate the ones that + are needed on this MPI rank. ---*/ + ReadCGNSPointCoordinates(); + CommPointCoordinates(); + + /*--- Read the surface connectivity and store the surface elements whose + corresponding volume element is stored on this MPI rank. ---*/ + ReadCGNSSurfaceElementConnectivity(); + + /*--- We have extracted all CGNS data. Close the CGNS file. ---*/ + if (cg_close(cgnsFileID)) cg_error_exit(); + +#else + SU2_MPI::Error(string(" SU2 built without CGNS support. \n") + string(" To use CGNS, build SU2 accordingly."), + CURRENT_FUNCTION); +#endif +} + +CCGNSMeshReaderFEM::~CCGNSMeshReaderFEM() = default; + +#ifdef HAVE_CGNS +void CCGNSMeshReaderFEM::ReadCGNSConnectivityRangeSection(const int val_section, const unsigned long val_firstIndex, + const unsigned long val_lastIndex, unsigned long& elemCount, + unsigned long& localElemCount, + vector& localConn) { + /*--- Read the connectivity details for this section. ---*/ + int nbndry, parent_flag; + cgsize_t startE, endE; + ElementType_t elemType; + char sectionName[CGNS_STRING_SIZE]; + + if (cg_section_read(cgnsFileID, cgnsBase, cgnsZone, val_section + 1, sectionName, &elemType, &startE, &endE, &nbndry, + &parent_flag)) + cg_error_exit(); + + /*--- Determine the number of elements in this section and update + the element counters accordingly. ---*/ + const unsigned long nElemSection = (endE - startE + 1); + const unsigned long elemCountOld = elemCount; + elemCount += nElemSection; + + /*--- Check for overlap with the element range this rank is responsible for. ---*/ + const unsigned long indBegOverlap = max(elemCountOld, val_firstIndex); + const unsigned long indEndOverlap = min(elemCount, val_lastIndex); + + 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; + nElems[val_section] = nElemRank; + + /*--- Determine the index range to be read for this rank. ---*/ + const cgsize_t iBeg = startE + offsetRank; + const cgsize_t iEnd = iBeg + nElemRank - 1; + + /*--- Determine the size of the vector needed to read + the connectivity data from the CGNS file. ---*/ + cgsize_t sizeNeeded; + if (cg_ElementPartialSize(cgnsFileID, cgnsBase, cgnsZone, val_section + 1, iBeg, iEnd, &sizeNeeded) != CG_OK) + cg_error_exit(); + + /*--- Allocate the memory for the connectivity and read the data. ---*/ + vector connCGNSVec(sizeNeeded); + if (elemType == MIXED) { + vector connCGNSOffsetVec(iEnd - iBeg + 2); + if (cg_poly_elements_partial_read(cgnsFileID, cgnsBase, cgnsZone, val_section + 1, iBeg, iEnd, connCGNSVec.data(), + connCGNSOffsetVec.data(), NULL) != CG_OK) + cg_error_exit(); + + } else { + if (cg_elements_partial_read(cgnsFileID, cgnsBase, cgnsZone, val_section + 1, iBeg, iEnd, connCGNSVec.data(), + NULL) != CG_OK) + cg_error_exit(); + } + + /*--- Define the variables needed for the conversion of the CGNS + format to the SU2 internal format. ---*/ + CCGNSElementType CGNSElem; + std::vector connSU2; + ElementType_t typeElem = elemType; + const cgsize_t* connCGNS = connCGNSVec.data(); + + /*--- Loop over the elements just read. ---*/ + for (unsigned long i = 0; i < nElemRank; ++i, ++localElemCount) { + /*--- Determine the element type for this element if this is a mixed + connectivity and set the pointer to the actual connectivity data. ---*/ + if (elemType == MIXED) { + typeElem = (ElementType_t)connCGNS[0]; + ++connCGNS; + } + + /*--- Convert the CGNS connectivity to SU2 connectivity. ---*/ + const unsigned long globalID = val_firstIndex + localElemCount; + CGNSElem.CGNSToSU2(typeElem, globalID, connCGNS, connSU2); + + /*--- Update the pointer for the connectivity for the next + element and store the SU2 connectivity in localConn. ---*/ + connCGNS += connSU2[3]; + + localConn.insert(localConn.end(), connSU2.begin(), connSU2.end()); + } + } +} + +void CCGNSMeshReaderFEM::ReadCGNSVolumeElementConnectivity(void) { + /*--- Write a message that the volume elements are loaded. ---*/ + if (rank == MASTER_NODE) { + if (size > SINGLE_NODE) + cout << "Loading volume elements into linear partitions." << endl; + else + cout << "Loading volume elements." << endl; + } + + /*--- Get a partitioner to help with linear partitioning. ---*/ + CLinearPartitioner elemPartitioner(numberOfGlobalElements, 0); + + /*--- Determine the index of the first and last element to be stored + on this rank and the number of local elements. ---*/ + const unsigned long firstIndex = elemPartitioner.GetFirstIndexOnRank(rank); + const unsigned long lastIndex = elemPartitioner.GetLastIndexOnRank(rank); + numberOfLocalElements = elemPartitioner.GetSizeOnRank(rank); + + /*--- Loop over the section and check for a section with volume elements. ---*/ + unsigned long elemCount = 0, localElemCount = 0; + for (int s = 0; s < nSections; ++s) { + if (isInterior[s]) { + /*--- Read the connectivity of this section and store the + data in localVolumeElementConnectivity. ---*/ + ReadCGNSConnectivityRangeSection(s, firstIndex, lastIndex, elemCount, localElemCount, + localVolumeElementConnectivity); + } + } +} + +void CCGNSMeshReaderFEM::ReadCGNSSurfaceElementConnectivity(void) { + /*--- Write a message that the surface elements are loaded. ---*/ + if (rank == MASTER_NODE) cout << "Loading surface elements." << endl; + + /*--- Determine the vector to hold the faces of the local elements. ---*/ + vector localFaces; + DetermineFacesVolumeElements(localFaces); + + /*--- Determine the number of markers. ---*/ + numberOfMarkers = 0; + for (int s = 0; s < nSections; s++) + if (!isInterior[s]) ++numberOfMarkers; + + /*--- Allocate the memory for the number of markers and local surface elements. + Also allocate the first index of surfaceElementConnectivity. ---*/ + markerNames.resize(numberOfMarkers); + numberOfLocalSurfaceElements.resize(numberOfMarkers); + surfaceElementConnectivity.resize(numberOfMarkers); + + /*--- Loop over the number of sections and check for a surface. ---*/ + int markerCount = 0; + for (int s = 0; s < nSections; ++s) { + if (!isInterior[s]) { + /*--- Create the marker name. ---*/ + string Marker_Tag = string(sectionNames[s].data()); + Marker_Tag.erase(remove(Marker_Tag.begin(), Marker_Tag.end(), ' '), Marker_Tag.end()); + markerNames[markerCount] = Marker_Tag; + + /*--- Call the function ReadCGNSSurfaceSection to carry out + the actual reading and storing of the required faces. ---*/ + ReadCGNSSurfaceSection(s, localFaces, numberOfLocalSurfaceElements[markerCount], + surfaceElementConnectivity[markerCount]); + + /*--- Update the marker counter. ---*/ + ++markerCount; + } + } +} + +void CCGNSMeshReaderFEM::ReadCGNSSurfaceSection(const int val_section, const vector& localFaces, + unsigned long& nSurfElem, vector& surfConn) { + /*--- Initialize nSurfElem to zero. ---*/ + nSurfElem = 0; + + /*--- Read the connectivity details for this section and determine the number + of elements present in this section. ---*/ + int nbndry, parent_flag; + cgsize_t startE, endE; + ElementType_t elemType; + char sectionName[CGNS_STRING_SIZE]; + + if (cg_section_read(cgnsFileID, cgnsBase, cgnsZone, val_section + 1, sectionName, &elemType, &startE, &endE, &nbndry, + &parent_flag)) + cg_error_exit(); + + const unsigned long nElemSection = (endE - startE + 1); + + /*--- Determine the number of chunks used for the reading of the surface + elements. This is done to avoid a memory bottleneck for extremely + big cases. For reasonably sized grids this connectivity can be + read in a single call. ---*/ + unsigned long nChunks = nElemSection / localFaces.size(); + if (nChunks * localFaces.size() != nElemSection) ++nChunks; + const unsigned long nElemChunk = nElemSection / nChunks; + + /*--- Loop over the number of chunks. ---*/ + for (unsigned long iChunk = 0; iChunk < nChunks; ++iChunk) { + /*--- Determine the start and end index for this chunk. ---*/ + const cgsize_t iBeg = startE + iChunk * nElemChunk; + const cgsize_t iEnd = (iChunk == (nChunks - 1)) ? endE : iBeg + nElemChunk - 1; + + /*--- Determine the size of the vector needed to read + the connectivity data from the CGNS file. ---*/ + cgsize_t sizeNeeded; + if (cg_ElementPartialSize(cgnsFileID, cgnsBase, cgnsZone, val_section + 1, iBeg, iEnd, &sizeNeeded) != CG_OK) + cg_error_exit(); + + /*--- Allocate the memory for the connectivity and read the data. ---*/ + vector connCGNSVec(sizeNeeded); + if (elemType == MIXED) { + vector connCGNSOffsetVec(iEnd - iBeg + 2); + if (cg_poly_elements_partial_read(cgnsFileID, cgnsBase, cgnsZone, val_section + 1, iBeg, iEnd, connCGNSVec.data(), + connCGNSOffsetVec.data(), NULL) != CG_OK) + cg_error_exit(); + + } else { + if (cg_elements_partial_read(cgnsFileID, cgnsBase, cgnsZone, val_section + 1, iBeg, iEnd, connCGNSVec.data(), + NULL) != CG_OK) + cg_error_exit(); + } + + /*--- Define the variables needed for the conversion of the CGNS + format to the SU2 internal format. ---*/ + CCGNSElementType CGNSElem; + std::vector connSU2; + ElementType_t typeElem = elemType; + const cgsize_t* connCGNS = connCGNSVec.data(); + + /*--- Loop over the elements just read. ---*/ + for (cgsize_t i = iBeg; i <= iEnd; ++i) { + /*--- Determine the element type for this element if this is a mixed + connectivity and set the pointer to the actual connectivity data. ---*/ + if (elemType == MIXED) { + typeElem = (ElementType_t)connCGNS[0]; + ++connCGNS; + } + + /*--- Convert the CGNS connectivity to SU2 connectivity and update + the pointer for the next surface element. ---*/ + const unsigned long globalID = i - 1; + CGNSElem.CGNSToSU2(typeElem, globalID, connCGNS, connSU2); + connCGNS += connSU2[3]; + + /*--- Easier storage of the VTK type, polynomial degree + and number of DOFs of the surface element. ---*/ + const unsigned short VTK_Type = static_cast(connSU2[0]); + const unsigned short nPolyGrid = static_cast(connSU2[1]); + const unsigned short nDOFsGrid = static_cast(connSU2[3]); + + /*--- Make a distinction between the possible element surface types and + determine the corner points in local numbering of the element. ---*/ + const unsigned short nDOFEdgeGrid = nPolyGrid + 1; + + CFaceOfElement thisFace; + thisFace.cornerPoints[0] = 0; + thisFace.cornerPoints[1] = nPolyGrid; + + switch (VTK_Type) { + case LINE: + thisFace.nCornerPoints = 2; + break; + + case TRIANGLE: + thisFace.nCornerPoints = 3; + thisFace.cornerPoints[2] = nDOFsGrid - 1; + break; + + case QUADRILATERAL: + thisFace.nCornerPoints = 4; + thisFace.cornerPoints[2] = static_cast(nPolyGrid) * nDOFEdgeGrid; + thisFace.cornerPoints[3] = nDOFsGrid - 1; + break; + + default: + ostringstream message; + message << "Unsupported FEM boundary element value, " << typeElem << ", in surface section " << sectionName; + SU2_MPI::Error(message.str(), CURRENT_FUNCTION); + } + + /*--- Convert the local numbering of thisFace to global numbering + and create a unique numbering of corner points. ---*/ + for (unsigned short j = 0; j < thisFace.nCornerPoints; ++j) + thisFace.cornerPoints[j] = connSU2[thisFace.cornerPoints[j] + 5]; + thisFace.CreateUniqueNumbering(); + + /*--- Check if this boundary face must be stored on this rank. ---*/ + vector::const_iterator low; + low = lower_bound(localFaces.begin(), localFaces.end(), thisFace); + if (low != localFaces.end()) { + if (!(thisFace < *low)) { + /*--- Update the number of local surface elements. ---*/ + ++nSurfElem; + + /*--- Store the meta data in the first 5 locations of connSU2. ---*/ + connSU2[0] = VTK_Type; + connSU2[1] = nPolyGrid; + connSU2[2] = nDOFsGrid; + connSU2[3] = globalID; // Global surface elem ID. + connSU2[4] = low->elemID0; // Global volume elem ID. + + /*--- Store the connectivity data in surfConn. ---*/ + surfConn.insert(surfConn.end(), connSU2.begin(), connSU2.end()); + } + } + } + } +} +#endif + +void CCGNSMeshReaderFEM::CommPointCoordinates(void) { + /*--- Determine the linear partitioning of the points. ---*/ + CLinearPartitioner pointPartitioner(numberOfGlobalPoints, 0); + + /*--- Loop over the local elements to determine the global + point IDs to be stored on this rank. --*/ + unsigned long ind = 0; + for (unsigned long i = 0; i < numberOfLocalElements; ++i) { + /*--- Store the number of grid DOFs for this element and + skip the meta data for this element (5 entries). ---*/ + const unsigned long nDOFsGrid = localVolumeElementConnectivity[ind + 3]; + ind += 5; + + /*--- Copy the connectivity to globalPointIDs. ---*/ + unsigned long* conn = localVolumeElementConnectivity.data() + ind; + ind += nDOFsGrid; + globalPointIDs.insert(globalPointIDs.end(), conn, conn + nDOFsGrid); + } + + /*--- Sort the globalPointIDs and remove the duplicate entries. ---*/ + sort(globalPointIDs.begin(), globalPointIDs.end()); + vector::iterator lastNode; + lastNode = unique(globalPointIDs.begin(), globalPointIDs.end()); + globalPointIDs.erase(lastNode, globalPointIDs.end()); + + /*--- Determine the number of locally stored points. ---*/ + numberOfLocalPoints = globalPointIDs.size(); + + /*--- This rest of this function only needs to done when MPI is used. ---*/ +#ifdef HAVE_MPI + + /*--- Store the global ID's of the points in such a way that they can + be sent to the rank that actually stores the coordinates.. ---*/ + vector > pointBuf(size, vector(0)); + + for (unsigned long i = 0; i < globalPointIDs.size(); ++i) + pointBuf[pointPartitioner.GetRankContainingIndex(globalPointIDs[i])].push_back(globalPointIDs[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 + localPointCoordinates. ---*/ + int nRankSend = 0; + vector sendToRank(size, 0); + vector startingIndRanksInPoint(size + 1); + startingIndRanksInPoint[0] = 0; + + for (int i = 0; i < size; ++i) { + startingIndRanksInPoint[i + 1] = startingIndRanksInPoint[i] + pointBuf[i].size(); + + if (pointBuf[i].size()) { + ++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 point numbers. Use nonblocking + sends to avoid deadlock. ---*/ + vector sendReqs(nRankSend); + nRankSend = 0; + for (int i = 0; i < size; ++i) { + if (pointBuf[i].size()) { + SU2_MPI::Isend(pointBuf[i].data(), pointBuf[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)); + + /*--- Get the first index of the points as well as the number of points + currently stored on this rank. ---*/ + const unsigned long firstIndex = pointPartitioner.GetFirstIndexOnRank(rank); + const unsigned long nPointsRead = pointPartitioner.GetSizeOnRank(rank); + + /*--- 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 pointRecvBuf(sizeMess); + coorReturnBuf[i].resize(static_cast(dimension) * sizeMess); + + /* Receive the message using a blocking receive. */ + SU2_MPI::Recv(pointRecvBuf.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 = dimension * j; + const long kk = pointRecvBuf[j] - firstIndex; + if (kk < 0 || kk >= static_cast(nPointsRead)) + SU2_MPI::Error("Invalid point requested. This should not happen.", CURRENT_FUNCTION); + + for (int k = 0; k < dimension; ++k) coorReturnBuf[i][jj + k] = localPointCoordinates[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]); + } + + /*--- Resize the second indices of localPointCoordinates. ---*/ + for (int k = 0; k < dimension; ++k) localPointCoordinates[k].resize(numberOfLocalPoints); + + /*--- 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(dimension * pointBuf[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); + + /*--- Loop over the points just received. ---*/ + for (unsigned long j = 0; j < pointBuf[source].size(); ++j) { + /*--- Copy the data into the correct location of localPointCoordinates. ---*/ + const unsigned long jj = dimension * j; + const unsigned long kk = startingIndRanksInPoint[source] + j; + + for (int k = 0; k < dimension; ++k) localPointCoordinates[k][kk] = SU2_TYPE::GetValue(coorRecvBuf[jj + k]); + } + } + + /* 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()); + +#endif +} diff --git a/Common/src/geometry/meshreader/CCGNSMeshReaderFVM.cpp b/Common/src/geometry/meshreader/CCGNSMeshReaderFVM.cpp index cdd06c285d6..2c0dabc8236 100644 --- a/Common/src/geometry/meshreader/CCGNSMeshReaderFVM.cpp +++ b/Common/src/geometry/meshreader/CCGNSMeshReaderFVM.cpp @@ -29,8 +29,8 @@ #include "../../../include/toolboxes/CLinearPartitioner.hpp" #include "../../../include/geometry/meshreader/CCGNSMeshReaderFVM.hpp" -CCGNSMeshReaderFVM::CCGNSMeshReaderFVM(CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone) - : CMeshReaderFVM(val_config, val_iZone, val_nZone) { +CCGNSMeshReaderFVM::CCGNSMeshReaderFVM(const CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone) + : CCGNSMeshReaderBase(val_config, val_iZone, val_nZone) { #ifdef HAVE_CGNS OpenCGNSFile(config->GetMesh_FileName()); @@ -74,307 +74,6 @@ CCGNSMeshReaderFVM::CCGNSMeshReaderFVM(CConfig* val_config, unsigned short val_i CCGNSMeshReaderFVM::~CCGNSMeshReaderFVM() = default; #ifdef HAVE_CGNS -void CCGNSMeshReaderFVM::OpenCGNSFile(const string& val_filename) { - /*--- Check whether the supplied file is truly a CGNS file. ---*/ - - int file_type; - float file_version; - if (cg_is_cgns(val_filename.c_str(), &file_type) != CG_OK) { - SU2_MPI::Error(val_filename + string(" was not found or is not a properly formatted") + - string(" CGNS file.\nNote that SU2 expects unstructured") + - string(" CGNS files in ADF data format."), - CURRENT_FUNCTION); - } - - /*--- Open the CGNS file for reading. The value of cgnsFileID returned - is the specific index number for this file and will be - repeatedly used in the function calls. ---*/ - - if (cg_open(val_filename.c_str(), CG_MODE_READ, &cgnsFileID)) cg_error_exit(); - if (rank == MASTER_NODE) { - cout << "Reading the CGNS file: "; - cout << val_filename.c_str() << "." << endl; - } - if (cg_version(cgnsFileID, &file_version)) cg_error_exit(); - if (rank == MASTER_NODE) { - if (file_version < 4.0) { - cout - << "WARNING: The CGNS file version (" << file_version - << ") is old and may cause high memory usage issues, consider updating the file with the cgnsupdate tool.\n"; - } - } -} - -void CCGNSMeshReaderFVM::ReadCGNSDatabaseMetadata() { - /*--- Get the number of databases. This is the highest node - in the CGNS heirarchy. ---*/ - - int nbases; - if (cg_nbases(cgnsFileID, &nbases)) cg_error_exit(); - if (rank == MASTER_NODE) cout << "CGNS file contains " << nbases << " database(s)." << endl; - - /*--- Check if there is more than one database. Throw an - error if there is because this reader can currently - only handle one database. ---*/ - - if (nbases > 1) { - SU2_MPI::Error("CGNS reader currently can only handle 1 database.", CURRENT_FUNCTION); - } - - /*--- Read the database. Note that the CGNS indexing starts at 1. ---*/ - - int cell_dim, phys_dim; - char basename[CGNS_STRING_SIZE]; - if (cg_base_read(cgnsFileID, cgnsBase, basename, &cell_dim, &phys_dim)) cg_error_exit(); - if (rank == MASTER_NODE) { - cout << "Database " << cgnsBase << ", " << basename << ": "; - cout << " cell dimension of " << cell_dim << ", physical "; - cout << "dimension of " << phys_dim << "." << endl; - } - - /*--- Set the number of dimensions baed on cell_dim. ---*/ - - dimension = (unsigned short)cell_dim; -} - -void CCGNSMeshReaderFVM::ReadCGNSZoneMetadata() { - /*--- First, check all sections to find the element types and to - classify them as either surface or volume elements. We will also - perform some error checks here to avoid partitioning issues. ---*/ - - /*--- Get the number of zones for this base. ---*/ - - int nzones; - if (cg_nzones(cgnsFileID, cgnsBase, &nzones)) cg_error_exit(); - if (rank == MASTER_NODE) { - cout << nzones << " total zone(s)." << endl; - } - - /*--- Check if there is more than one zone. Until we enable it, we - will require a single zone CGNS file. Multizone problems can still - be run with CGNS by using separate CGNS files for each zone. ---*/ - - if (nzones > 1) { - SU2_MPI::Error(string("CGNS reader currently expects only 1 zone per CGNS file.") + - string("Multizone problems can be run with separate CGNS files for each zone."), - CURRENT_FUNCTION); - } - - /*--- Read the basic information for this zone, including - the name and the number of vertices, cells, and - boundary cells which are stored in the cgsize variable. ---*/ - - vector cgsize(3); - ZoneType_t zonetype; - char zonename[CGNS_STRING_SIZE]; - if (cg_zone_read(cgnsFileID, cgnsBase, cgnsZone, zonename, cgsize.data())) cg_error_exit(); - - /*--- Rename the zone size information for clarity. - NOTE: The number of cells here may be only the number of - interior elements or it may be the total. This needs to - be counted explicitly later. ---*/ - - numberOfGlobalPoints = cgsize[0]; - int nElemCGNS = cgsize[1]; - - /*--- Get some additional information about the current zone. ---*/ - - if (cg_zone_type(cgnsFileID, cgnsBase, cgnsZone, &zonetype)) cg_error_exit(); - - /*--- Check for an unstructured mesh. Throw an error if not found. ---*/ - - if (zonetype != Unstructured) - SU2_MPI::Error("Structured CGNS zone found while unstructured expected.", CURRENT_FUNCTION); - - /*--- Print current zone info to the console. ---*/ - - if (rank == MASTER_NODE) { - cout << "Zone " << cgnsZone << ", " << zonename << ": "; - cout << numberOfGlobalPoints << " total vertices, "; - cout << nElemCGNS << " total elements." << endl; - } - - /*--- Retrieve the number of grids in this zone. For now, we know - this is one, but to be more general, this will need to check and - allow for a loop over all grids. ---*/ - - int ngrids; - if (cg_ngrids(cgnsFileID, cgnsBase, cgnsZone, &ngrids)) cg_error_exit(); - if (ngrids > 1) { - SU2_MPI::Error("CGNS reader currently handles only 1 grid per zone.", CURRENT_FUNCTION); - } -} - -void CCGNSMeshReaderFVM::ReadCGNSPointCoordinates() { - /*--- Compute the number of points that will be on each processor. - This is a linear partitioning with the addition of a simple load - balancing for any remainder points. ---*/ - - CLinearPartitioner pointPartitioner(numberOfGlobalPoints, 0); - - /*--- Store the local number of nodes for this rank. ---*/ - - numberOfLocalPoints = pointPartitioner.GetSizeOnRank(rank); - - /*--- Create buffer to hold the grid coordinates for our rank. ---*/ - - localPointCoordinates.resize(dimension); - for (int k = 0; k < dimension; k++) localPointCoordinates[k].resize(numberOfLocalPoints, 0.0); - - /*--- Set the value of range_max to the total number of nodes in - the unstructured mesh. Also allocate memory for the temporary array - that will hold the grid coordinates as they are extracted. Note the - +1 for CGNS convention. ---*/ - - cgsize_t range_min = (cgsize_t)pointPartitioner.GetFirstIndexOnRank(rank) + 1; - auto range_max = (cgsize_t)pointPartitioner.GetLastIndexOnRank(rank); - - /*--- Loop over each set of coordinates. ---*/ - - for (int k = 0; k < dimension; k++) { - /*--- Read the coordinate info. This will retrieve the - data type (either RealSingle or RealDouble) as - well as the coordname which will specify the - type of data that it is based in the SIDS convention. - This might be "CoordinateX," for instance. ---*/ - - char coordname[CGNS_STRING_SIZE]; - DataType_t datatype; - if (cg_coord_info(cgnsFileID, cgnsBase, cgnsZone, k + 1, &datatype, coordname)) cg_error_exit(); - if (rank == MASTER_NODE) { - cout << "Loading " << coordname; - if (size > SINGLE_NODE) { - cout << " values into linear partitions." << endl; - } else { - cout << " values." << endl; - } - } - - /*--- Check the coordinate name to decide the index for storage. ---*/ - - unsigned short indC = 0; - if (string(coordname) == "CoordinateX") - indC = 0; - else if (string(coordname) == "CoordinateY") - indC = 1; - else if (string(coordname) == "CoordinateZ") - indC = 2; - else - SU2_MPI::Error(string("Unknown coordinate name, ") + coordname + string(", in the CGNS file."), CURRENT_FUNCTION); - - /*--- Now read our rank's chunk of coordinates from the file. - Ask for datatype RealDouble and let CGNS library do the translation - when RealSingle is found. ---*/ - - if (cg_coord_read(cgnsFileID, cgnsBase, cgnsZone, coordname, RealDouble, &range_min, &range_max, - localPointCoordinates[indC].data())) - cg_error_exit(); - } -} - -void CCGNSMeshReaderFVM::ReadCGNSSectionMetadata() { - /*--- Begin section for retrieving the connectivity info. ---*/ - - if ((rank == MASTER_NODE) && (size > SINGLE_NODE)) cout << "Distributing connectivity across all ranks." << endl; - - /*--- First check the number of sections. ---*/ - - if (cg_nsections(cgnsFileID, cgnsBase, cgnsZone, &nSections)) cg_error_exit(); - if (rank == MASTER_NODE) { - cout << "Number of connectivity sections is "; - cout << nSections << "." << endl; - } - - /*--- Prepare several data structures to hold the various - pieces of information describing each section. ---*/ - - isInterior.resize(nSections); - nElems.resize(nSections, 0); - elemOffset.resize(nSections + 1, 0); - elemOffset[0] = 0; - connElems.resize(nSections); - sectionNames.resize(nSections, vector(CGNS_STRING_SIZE)); - numberOfGlobalElements = 0; - - for (int s = 0; s < nSections; s++) { - /*--- Read the connectivity details for this section. ---*/ - - int nbndry, parent_flag, vtk_type; - cgsize_t startE, endE, sizeNeeded; - ElementType_t elemType; - if (cg_section_read(cgnsFileID, cgnsBase, cgnsZone, s + 1, sectionNames[s].data(), &elemType, &startE, &endE, - &nbndry, &parent_flag)) - cg_error_exit(); - - /*--- Compute the total element count in this section (global). ---*/ - - unsigned long element_count = (endE - startE + 1); - - /* Get the details for the CGNS element type in this section. */ - - string elem_name = GetCGNSElementType(elemType, vtk_type); - - /* We assume that each section contains interior elements by default. - If we find 1D elements in a 2D problem or 2D elements in a 3D - problem, then we know the section must contain boundary elements. - We assume that each section is composed of either entirely interior - or entirely boundary elements. */ - - isInterior[s] = true; - - if (elemType == MIXED) { - /* For a mixed section, we check the type of the first element - so that we can correctly label this section as an interior or - boundary element section. Here, we also assume that a section - can not hold both interior and boundary elements. First, get - the size required to read a single element from the section. */ - - if (cg_ElementPartialSize(cgnsFileID, cgnsBase, cgnsZone, s + 1, startE, startE, &sizeNeeded) != CG_OK) - cg_error_exit(); - - /* A couple of auxiliary vectors for mixed element sections. */ - - vector connElemCGNS(sizeNeeded); - vector connOffsetCGNS(2, 0); - - /* Retrieve the connectivity information for the first element. */ - - if (cg_poly_elements_partial_read(cgnsFileID, cgnsBase, cgnsZone, s + 1, startE, startE, connElemCGNS.data(), - connOffsetCGNS.data(), nullptr) != CG_OK) - cg_error_exit(); - - /* The element type is in the first position of the connectivity - information that we retrieved from the CGNS file. */ - - elemType = ElementType_t(connElemCGNS[0]); - } - - /* Check for 1D elements in 2D problems, or for 2D elements in - 3D problems. If found, mark the section as a boundary section. */ - - if ((dimension == 2) && (elemType == BAR_2 || elemType == BAR_3)) isInterior[s] = false; - if ((dimension == 3) && (elemType == TRI_3 || elemType == QUAD_4)) isInterior[s] = false; - - /*--- Increment the global element offset for each section - based on whether or not this is a surface or volume section. - We also keep a running count of the total elements globally. ---*/ - - elemOffset[s + 1] = elemOffset[s]; - if (!isInterior[s]) - elemOffset[s + 1] += element_count; - else - numberOfGlobalElements += element_count; - - /*--- Print some information to the console. ---*/ - - if (rank == MASTER_NODE) { - cout << "Section " << string(sectionNames[s].data()); - cout << " contains " << element_count << " elements"; - cout << " of type " << elem_name << "." << endl; - } - } -} - void CCGNSMeshReaderFVM::ReadCGNSVolumeSection(int val_section) { /*--- In this routine, each rank will read a chunk of the element connectivity for a single specified section of the CGNS mesh file. @@ -545,7 +244,7 @@ void CCGNSMeshReaderFVM::ReadCGNSVolumeSection(int val_section) { entry if this is a mixed element section. ---*/ if (isMixed) counterCGNS++; - for (iNode = 0; iNode < (unsigned long)nPoinPerElem[iElem]; iNode++) { + for (iNode = 0; iNode < static_cast(nPoinPerElem[iElem]); iNode++) { connElemTemp[nn] = connElemCGNS[counterCGNS + iNode] - 1; nn++; } @@ -582,7 +281,7 @@ void CCGNSMeshReaderFVM::ReadCGNSVolumeSection(int val_section) { CLinearPartitioner pointPartitioner(numberOfGlobalPoints, 0); for (iElem = 0; iElem < nElems[val_section]; iElem++) { - for (iNode = 0; iNode < (unsigned long)nPoinPerElem[iElem]; iNode++) { + for (iNode = 0; iNode < static_cast(nPoinPerElem[iElem]); iNode++) { /*--- Get the index of the current point. ---*/ iPoint = connElemTemp[iElem * SU2_CONN_SIZE + SU2_CONN_SKIP + iNode]; @@ -594,7 +293,7 @@ void CCGNSMeshReaderFVM::ReadCGNSVolumeSection(int val_section) { /*--- 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)) { + if ((nElem_Flag[iProcessor] != static_cast(iElem))) { nElem_Flag[iProcessor] = iElem; nElem_Send[iProcessor + 1]++; } @@ -630,7 +329,7 @@ void CCGNSMeshReaderFVM::ReadCGNSVolumeSection(int val_section) { + 2 extra values for the ID and VTK. ---*/ unsigned long *connSend = nullptr, iSend = 0; - unsigned long sendSize = (unsigned long)SU2_CONN_SIZE * nElem_Send[size]; + unsigned long sendSize = static_cast(SU2_CONN_SIZE) * nElem_Send[size]; connSend = new unsigned long[sendSize]; for (iSend = 0; iSend < sendSize; iSend++) connSend[iSend] = 0; @@ -643,8 +342,8 @@ void CCGNSMeshReaderFVM::ReadCGNSVolumeSection(int val_section) { /*--- Loop through our elements and load the elems and their additional data that we will send to the other procs. ---*/ - for (iElem = 0; iElem < (unsigned long)nElems[val_section]; iElem++) { - for (iNode = 0; iNode < (unsigned long)nPoinPerElem[iElem]; iNode++) { + for (iElem = 0; iElem < static_cast(nElems[val_section]); iElem++) { + for (iNode = 0; iNode < static_cast(nPoinPerElem[iElem]); iNode++) { /*--- Get the index of the current point. ---*/ iPoint = connElemTemp[iElem * SU2_CONN_SIZE + SU2_CONN_SKIP + iNode]; @@ -655,7 +354,7 @@ void CCGNSMeshReaderFVM::ReadCGNSVolumeSection(int val_section) { /*--- Load connectivity into the buffer for sending ---*/ - if (nElem_Flag[iProcessor] != (int)iElem) { + if (nElem_Flag[iProcessor] != static_cast(iElem)) { nElem_Flag[iProcessor] = iElem; unsigned long nn = index[iProcessor]; @@ -689,7 +388,7 @@ void CCGNSMeshReaderFVM::ReadCGNSVolumeSection(int val_section) { directly copy our own data later. ---*/ unsigned long *connRecv = nullptr, iRecv = 0; - unsigned long recvSize = (unsigned long)SU2_CONN_SIZE * nElem_Recv[size]; + unsigned long recvSize = static_cast(SU2_CONN_SIZE) * nElem_Recv[size]; connRecv = new unsigned long[recvSize]; for (iRecv = 0; iRecv < recvSize; iRecv++) connRecv[iRecv] = 0; @@ -731,7 +430,7 @@ void CCGNSMeshReaderFVM::ReadCGNSVolumeSection(int val_section) { if (nElem_Recv[size] > 0) { connElems[val_section].resize(nElem_Recv[size] * SU2_CONN_SIZE, 0); unsigned long count = 0; - for (iElem = 0; iElem < (unsigned long)nElem_Recv[size]; iElem++) { + for (iElem = 0; iElem < static_cast(nElem_Recv[size]); iElem++) { for (iNode = 0; iNode < SU2_CONN_SIZE; iNode++) { unsigned long nn = iElem * SU2_CONN_SIZE + iNode; connElems[val_section][count] = (cgsize_t)connRecv[nn]; @@ -874,7 +573,7 @@ void CCGNSMeshReaderFVM::ReadCGNSSurfaceSection(int val_section) { connElems[val_section][iElem * SU2_CONN_SIZE + 0] = 0; connElems[val_section][iElem * SU2_CONN_SIZE + 1] = vtk_type; - for (iNode = 0; iNode < (unsigned long)npe; iNode++) { + for (iNode = 0; iNode < static_cast(npe); iNode++) { unsigned long nn = iElem * SU2_CONN_SIZE + SU2_CONN_SKIP + iNode; connElems[val_section][nn] = connElemTemp[counterCGNS] - 1; counterCGNS++; @@ -907,7 +606,7 @@ void CCGNSMeshReaderFVM::ReformatCGNSVolumeConnectivity() { for (unsigned long iElem = 0; iElem < nElems[s]; iElem++) { for (unsigned long iNode = 0; iNode < SU2_CONN_SIZE; iNode++) { unsigned long nn = iElem * SU2_CONN_SIZE + iNode; - localVolumeElementConnectivity[count] = (unsigned long)connElems[s][nn]; + localVolumeElementConnectivity[count] = static_cast(connElems[s][nn]); count++; } } @@ -941,7 +640,7 @@ void CCGNSMeshReaderFVM::ReformatCGNSSurfaceConnectivity() { for (unsigned long iElem = 0; iElem < nElems[s]; iElem++) { for (unsigned long iNode = 0; iNode < SU2_CONN_SIZE; iNode++) { unsigned long nn = iElem * SU2_CONN_SIZE + iNode; - surfaceElementConnectivity[markerCount][elementCount] = (unsigned long)connElems[s][nn]; + surfaceElementConnectivity[markerCount][elementCount] = static_cast(connElems[s][nn]); elementCount++; } } @@ -951,65 +650,6 @@ void CCGNSMeshReaderFVM::ReformatCGNSSurfaceConnectivity() { } } } - -string CCGNSMeshReaderFVM::GetCGNSElementType(ElementType_t val_elem_type, int& val_vtk_type) { - /* Check the CGNS element type and return the string name - for the element and the associated VTK type index. */ - - string elem_name; - switch (val_elem_type) { - case NODE: - elem_name = "Vertex"; - val_vtk_type = 1; - SU2_MPI::Error("Vertex elements detected. Please remove.", CURRENT_FUNCTION); - break; - case BAR_2: - elem_name = "Line"; - val_vtk_type = 3; - if (dimension == 3) SU2_MPI::Error("Line elements detected in a 3D mesh. Please remove.", CURRENT_FUNCTION); - break; - case BAR_3: - elem_name = "Line"; - val_vtk_type = 3; - if (dimension == 3) SU2_MPI::Error("Line elements detected in a 3D mesh. Please remove.", CURRENT_FUNCTION); - break; - case TRI_3: - elem_name = "Triangle"; - val_vtk_type = 5; - break; - case QUAD_4: - elem_name = "Quadrilateral"; - val_vtk_type = 9; - break; - case TETRA_4: - elem_name = "Tetrahedron"; - val_vtk_type = 10; - break; - case HEXA_8: - elem_name = "Hexahedron"; - val_vtk_type = 12; - break; - case PENTA_6: - elem_name = "Prism"; - val_vtk_type = 13; - break; - case PYRA_5: - elem_name = "Pyramid"; - val_vtk_type = 14; - break; - case MIXED: - elem_name = "Mixed"; - val_vtk_type = -1; - break; - default: - char buf[100]; - SPRINTF(buf, "Unsupported or unknown CGNS element type: (type %d)\n", val_elem_type); - SU2_MPI::Error(string(buf), CURRENT_FUNCTION); - break; - } - - return elem_name; -} #endif void CCGNSMeshReaderFVM::InitiateCommsAll(void* bufSend, const int* nElemSend, SU2_MPI::Request* sendReq, void* bufRecv, diff --git a/Common/src/geometry/meshreader/CMeshReaderBase.cpp b/Common/src/geometry/meshreader/CMeshReaderBase.cpp new file mode 100644 index 00000000000..bb20e0d97b9 --- /dev/null +++ b/Common/src/geometry/meshreader/CMeshReaderBase.cpp @@ -0,0 +1,95 @@ +/*! + * \file CMeshReaderBase.cpp + * \brief Helper class that provides the counts for each rank in a linear + * partitioning given the global count as input. + * \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 . + */ + +#include "../../../include/geometry/meshreader/CMeshReaderBase.hpp" + +CMeshReaderBase::CMeshReaderBase(const CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone) + : rank(SU2_MPI::GetRank()), size(SU2_MPI::GetSize()), config(val_config) {} + +void CMeshReaderBase::DetermineFacesVolumeElements(vector& localFaces) { + /*--- Loop over the locally stored volume elements. ---*/ + unsigned long ind = 0; + for (unsigned long k = 0; k < numberOfLocalElements; ++k) { + /*--- Set the pointer where the information of this element + is stored and determine the number of faces as well + as the corner points of these faces. Note that 6 faces + is the maximum for the elements considered. ---*/ + unsigned short nFaces; + unsigned short nPointsPerFace[6]; + unsigned long faceConn[6][4]; + + const unsigned long* elemInfo = localVolumeElementConnectivity.data() + ind; + + GetCornerPointsAllFaces(elemInfo, 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 = elemInfo[4]; + + thisFace.CreateUniqueNumbering(); + localFaces.push_back(thisFace); + } + + /*--- Update the index for the next element. ---*/ + const unsigned long nDOFsGrid = localVolumeElementConnectivity[ind + 3]; + ind += nDOFsGrid + 5; + } + + /*--- 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()); +} + +void CMeshReaderBase::GetCornerPointsAllFaces(const unsigned long* elemInfo, unsigned short& numFaces, + unsigned short nPointsPerFace[], unsigned long faceConn[6][4]) { + /*--- Retrieve the element type, polynomial degree of the grid and + number of DOFs for this element and set the pointer for the + connectivity information. ---*/ + const unsigned short VTK_Type = (const unsigned short)elemInfo[0]; + const unsigned short nPolyGrid = (const unsigned short)elemInfo[1]; + const unsigned short nDOFsGrid = (const unsigned short)elemInfo[3]; + const unsigned long* conn = elemInfo + 5; + + /*--- Call the static function GetLocalCornerPointsAllFaces of CPrimalGridFEM + to determine the local numbering of the corner points of the faces. ---*/ + CPrimalGridFEM::GetLocalCornerPointsAllFaces(VTK_Type, nPolyGrid, nDOFsGrid, numFaces, nPointsPerFace, faceConn); + + /*--- Convert the local values of faceConn to global values. ---*/ + for (unsigned short i = 0; i < numFaces; ++i) { + for (unsigned short j = 0; j < nPointsPerFace[i]; ++j) { + unsigned long nn = faceConn[i][j]; + faceConn[i][j] = conn[nn]; + } + } +} diff --git a/Common/src/geometry/meshreader/CMeshReaderFVM.cpp b/Common/src/geometry/meshreader/CMeshReaderFVM.cpp deleted file mode 100644 index 17069bf7b25..00000000000 --- a/Common/src/geometry/meshreader/CMeshReaderFVM.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/*! - * \file CMeshReaderFVM.cpp - * \brief Helper class that provides the counts for each rank in a linear - * partitioning given the global count as input. - * \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 . - */ - -#include "../../../include/geometry/meshreader/CMeshReaderFVM.hpp" - -CMeshReaderFVM::CMeshReaderFVM(const CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone) - : rank(SU2_MPI::GetRank()), size(SU2_MPI::GetSize()), config(val_config) {} diff --git a/Common/src/geometry/meshreader/CRectangularMeshReaderFEM.cpp b/Common/src/geometry/meshreader/CRectangularMeshReaderFEM.cpp new file mode 100644 index 00000000000..8b6c6cf8400 --- /dev/null +++ b/Common/src/geometry/meshreader/CRectangularMeshReaderFEM.cpp @@ -0,0 +1,255 @@ +/*! + * \file CRectangularMeshReaderFEM.cpp + * \brief Reads a 2D rectangular grid into linear partitions for the + * finite element solver (FEM). + * \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 . + */ + +#include "../../../include/toolboxes/CLinearPartitioner.hpp" +#include "../../../include/geometry/meshreader/CRectangularMeshReaderFEM.hpp" + +CRectangularMeshReaderFEM::CRectangularMeshReaderFEM(const CConfig* val_config, unsigned short val_iZone, + unsigned short val_nZone) + : CMeshReaderBase(val_config, val_iZone, val_nZone) { + /* The rectangular mesh is always 2D. */ + dimension = 2; + + /* Set the VTK type for the interior elements and the boundary elements. */ + KindElem = QUADRILATERAL; + KindBound = LINE; + + /* The number of nodes in the i and j directions. */ + nNode = config->GetMeshBoxSize(0); + mNode = config->GetMeshBoxSize(1); + + /* Lengths for non-square domains. */ + Lx = config->GetMeshBoxLength(0); + Ly = config->GetMeshBoxLength(1); + + /* Offsets in x and y directions from 0.0. */ + Ox = config->GetMeshBoxOffset(0); + Oy = config->GetMeshBoxOffset(1); + + /* Polynomial degree of the solution. */ + nPolySol = config->GetMeshBoxPSolFEM(); + + /*--- Compute and store the interior elements, points, and surface elements + for this rank, for which simple analytic formulae can be used. ---*/ + ComputeRectangularVolumeConnectivity(); + ComputeRectangularPointCoordinates(); + ComputeRectangularSurfaceConnectivity(); +} + +CRectangularMeshReaderFEM::~CRectangularMeshReaderFEM() = default; + +void CRectangularMeshReaderFEM::ComputeRectangularPointCoordinates() { + /*--- Set the global count of points based on the grid dimensions. ---*/ + numberOfGlobalPoints = nNode * mNode; + + /*--- Loop over the local elements to determine the global + point IDs to be stored on this rank. --*/ + unsigned long ind = 0; + for (unsigned long i = 0; i < numberOfLocalElements; ++i) { + /*--- Store the number of grid DOFs for this element and + skip the meta data for this element (5 entries). ---*/ + const unsigned long nDOFsGrid = localVolumeElementConnectivity[ind + 3]; + ind += 5; + + /*--- Copy the connectivity to globalPointIDs. ---*/ + unsigned long* conn = localVolumeElementConnectivity.data() + ind; + ind += nDOFsGrid; + globalPointIDs.insert(globalPointIDs.end(), conn, conn + nDOFsGrid); + } + + /*--- Sort the globalPointIDs and remove the duplicate entries. ---*/ + sort(globalPointIDs.begin(), globalPointIDs.end()); + vector::iterator lastNode; + lastNode = unique(globalPointIDs.begin(), globalPointIDs.end()); + globalPointIDs.erase(lastNode, globalPointIDs.end()); + + /*--- Determine the number of locally stored points. ---*/ + numberOfLocalPoints = globalPointIDs.size(); + + /*--- Allocate the memory for the locally stored points. ---*/ + localPointCoordinates.resize(dimension); + for (int k = 0; k < dimension; k++) localPointCoordinates[k].resize(numberOfLocalPoints); + + /*--- Loop over the locally stored points. ---*/ + for (unsigned long i = 0; i < numberOfLocalPoints; ++i) { + /*--- Convert the global index to i,j indices. ---*/ + const unsigned long jNode = globalPointIDs[i] / nNode; + const unsigned long iNode = globalPointIDs[i] - jNode * nNode; + + /*--- Store the coordinates of the point. ---*/ + localPointCoordinates[0][i] = SU2_TYPE::GetValue(Lx * ((su2double)iNode) / ((su2double)(nNode - 1)) + Ox); + localPointCoordinates[1][i] = SU2_TYPE::GetValue(Ly * ((su2double)jNode) / ((su2double)(mNode - 1)) + Oy); + } +} + +void CRectangularMeshReaderFEM::ComputeRectangularVolumeConnectivity() { + /*--- Set the global count of elements based on the grid dimensions. ---*/ + const unsigned long nElemI = nNode - 1; + numberOfGlobalElements = nElemI * (mNode - 1); + + /*--- Get a partitioner to help with linear partitioning. ---*/ + CLinearPartitioner elemPartitioner(numberOfGlobalElements, 0); + + /*--- Determine the index of the first and last element to be stored + on this rank and the number of local elements. ---*/ + const unsigned long firstIndex = elemPartitioner.GetFirstIndexOnRank(rank); + const unsigned long lastIndex = elemPartitioner.GetLastIndexOnRank(rank); + numberOfLocalElements = elemPartitioner.GetSizeOnRank(rank); + + /*--- Loop over the owned element range. ---*/ + for (unsigned long elem = firstIndex; elem < lastIndex; ++elem) { + /*--- Retrieve the i,j indices of this element, which are the indices + of the lower, left point of the element. --*/ + const unsigned long jNode = elem / nElemI; + const unsigned long iNode = elem - jNode * nElemI; + + /*--- Store the meta data of this element. ---*/ + localVolumeElementConnectivity.push_back(KindElem); + localVolumeElementConnectivity.push_back(1); // Pol. degree grid. + localVolumeElementConnectivity.push_back(nPolySol); + localVolumeElementConnectivity.push_back(4); // Number of grid DOFs. + localVolumeElementConnectivity.push_back(elem); // Global elem ID. + + /*--- Store the volume connectivity in the format used + by the FEM solver. ---*/ + localVolumeElementConnectivity.push_back(jNode * nNode + iNode); + localVolumeElementConnectivity.push_back(jNode * nNode + iNode + 1); + localVolumeElementConnectivity.push_back((jNode + 1) * nNode + iNode); + localVolumeElementConnectivity.push_back((jNode + 1) * nNode + iNode + 1); + } +} + +void CRectangularMeshReaderFEM::ComputeRectangularSurfaceConnectivity() { + /*--- Determine the number of elements in i-direction. ---*/ + const unsigned long nElemI = nNode - 1; + + /*--- Get a partitioner to help with linear partitioning. ---*/ + CLinearPartitioner elemPartitioner(numberOfGlobalElements, 0); + + /*--- The rectangle always has 4 markers. Allocate the required memory. ---*/ + numberOfMarkers = 4; + numberOfLocalSurfaceElements.resize(numberOfMarkers, 0); + surfaceElementConnectivity.resize(numberOfMarkers); + markerNames.resize(numberOfMarkers); + + /*--- Loop over all faces on the yMin (= jMin) boundary. ---*/ + markerNames[0] = "y_minus"; + + for (unsigned long iNode = 0; iNode < nNode - 1; ++iNode) { + /*--- Determine the corresponding global element ID and check + if it is stored on this rank. ---*/ + const unsigned long globalElemID = iNode; + if (elemPartitioner.GetRankContainingIndex(globalElemID) == static_cast(rank)) { + /*--- The corresponding volume element is stored on this rank, + hence store the surface element as well. ---*/ + surfaceElementConnectivity[0].push_back(KindBound); // VTK type. + surfaceElementConnectivity[0].push_back(1); // Poly degree grid. + surfaceElementConnectivity[0].push_back(2); // Number of grid DOFs. + surfaceElementConnectivity[0].push_back(iNode); // Global surface element ID. + surfaceElementConnectivity[0].push_back(globalElemID); // Global volume element ID. + + surfaceElementConnectivity[0].push_back(iNode); + surfaceElementConnectivity[0].push_back(iNode + 1); + + /*--- Update the number of surface elements for this marker. ---*/ + ++numberOfLocalSurfaceElements[0]; + } + } + + /*--- Loop over all faces on the xMax (= iMax) boundary. ---*/ + markerNames[1] = "x_plus"; + + for (unsigned long jNode = 0; jNode < mNode - 1; ++jNode) { + /*--- Determine the corresponding global element ID and check + if it is stored on this rank. ---*/ + const unsigned long globalElemID = jNode * nElemI + nElemI - 1; + if (elemPartitioner.GetRankContainingIndex(globalElemID) == static_cast(rank)) { + /*--- The corresponding volume element is stored on this rank, + hence store the surface element as well. ---*/ + surfaceElementConnectivity[1].push_back(KindBound); // VTK type. + surfaceElementConnectivity[1].push_back(1); // Poly degree grid. + surfaceElementConnectivity[1].push_back(2); // Number of grid DOFs. + surfaceElementConnectivity[1].push_back(jNode); // Global surface element ID. + surfaceElementConnectivity[1].push_back(globalElemID); // Global volume element ID. + + surfaceElementConnectivity[1].push_back(jNode * nNode + (nNode - 1)); + surfaceElementConnectivity[1].push_back((jNode + 1) * nNode + (nNode - 1)); + + /*--- Update the number of surface elements for this marker. ---*/ + ++numberOfLocalSurfaceElements[1]; + } + } + + /*--- Loop over all faces on the yMax (= jMax) boundary. ---*/ + markerNames[2] = "y_plus"; + + for (unsigned long iNode = 0; iNode < nNode - 1; ++iNode) { + /*--- Determine the corresponding global element ID and check + if it is stored on this rank. ---*/ + const unsigned long globalElemID = (mNode - 2) * nElemI + iNode; + if (elemPartitioner.GetRankContainingIndex(globalElemID) == static_cast(rank)) { + /*--- The corresponding volume element is stored on this rank, + hence store the surface element as well. ---*/ + surfaceElementConnectivity[2].push_back(KindBound); // VTK type. + surfaceElementConnectivity[2].push_back(1); // Poly degree grid. + surfaceElementConnectivity[2].push_back(2); // Number of grid DOFs. + surfaceElementConnectivity[2].push_back(iNode); // Global surface element ID. + surfaceElementConnectivity[2].push_back(globalElemID); // Global volume element ID. + + surfaceElementConnectivity[2].push_back((mNode - 1) * nNode + iNode + 1); + surfaceElementConnectivity[2].push_back((mNode - 1) * nNode + iNode); + + /*--- Update the number of surface elements for this marker. ---*/ + ++numberOfLocalSurfaceElements[2]; + } + } + + /*--- Loop over all faces on the xMin (= iMin) boundary. ---*/ + markerNames[3] = "x_minus"; + + for (unsigned long jNode = 0; jNode < mNode - 1; ++jNode) { + /*--- Determine the corresponding global element ID and check + if it is stored on this rank. ---*/ + const unsigned long globalElemID = jNode * nElemI; + if (elemPartitioner.GetRankContainingIndex(globalElemID) == static_cast(rank)) { + /*--- The corresponding volume element is stored on this rank, + hence store the surface element as well. ---*/ + surfaceElementConnectivity[3].push_back(KindBound); // VTK type. + surfaceElementConnectivity[3].push_back(1); // Poly degree grid. + surfaceElementConnectivity[3].push_back(2); // Number of grid DOFs. + surfaceElementConnectivity[3].push_back(jNode); // Global surface element ID. + surfaceElementConnectivity[3].push_back(globalElemID); // Global volume element ID. + + surfaceElementConnectivity[3].push_back((jNode + 1) * nNode); + surfaceElementConnectivity[3].push_back(jNode * nNode); + + /*--- Update the number of surface elements for this marker. ---*/ + ++numberOfLocalSurfaceElements[3]; + } + } +} diff --git a/Common/src/geometry/meshreader/CRectangularMeshReaderFVM.cpp b/Common/src/geometry/meshreader/CRectangularMeshReaderFVM.cpp index 5b89744bf2a..f1e8f6177a2 100644 --- a/Common/src/geometry/meshreader/CRectangularMeshReaderFVM.cpp +++ b/Common/src/geometry/meshreader/CRectangularMeshReaderFVM.cpp @@ -31,7 +31,7 @@ CRectangularMeshReaderFVM::CRectangularMeshReaderFVM(const CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone) - : CMeshReaderFVM(val_config, val_iZone, val_nZone) { + : CMeshReaderBase(val_config, val_iZone, val_nZone) { /* The rectangular mesh is always 2D. */ dimension = 2; @@ -61,6 +61,8 @@ CRectangularMeshReaderFVM::CRectangularMeshReaderFVM(const CConfig* val_config, ComputeRectangularSurfaceConnectivity(); } +CRectangularMeshReaderFVM::~CRectangularMeshReaderFVM() = default; + void CRectangularMeshReaderFVM::ComputeRectangularPointCoordinates() { /* Set the global count of points based on the grid dimensions. */ numberOfGlobalPoints = (nNode) * (mNode); @@ -70,7 +72,7 @@ void CRectangularMeshReaderFVM::ComputeRectangularPointCoordinates() { /* Determine number of local points */ for (unsigned long globalIndex = 0; globalIndex < numberOfGlobalPoints; globalIndex++) { - if ((int)pointPartitioner.GetRankContainingIndex(globalIndex) == rank) { + if (static_cast(pointPartitioner.GetRankContainingIndex(globalIndex)) == rank) { numberOfLocalPoints++; } } @@ -82,7 +84,7 @@ void CRectangularMeshReaderFVM::ComputeRectangularPointCoordinates() { unsigned long globalIndex = 0; for (unsigned long jNode = 0; jNode < mNode; jNode++) { for (unsigned long iNode = 0; iNode < nNode; iNode++) { - if ((int)pointPartitioner.GetRankContainingIndex(globalIndex) == rank) { + if (static_cast(pointPartitioner.GetRankContainingIndex(globalIndex)) == rank) { /* Store the coordinates more clearly. */ const passivedouble x = SU2_TYPE::GetValue(Lx * ((su2double)iNode) / ((su2double)(nNode - 1)) + Ox); const passivedouble y = SU2_TYPE::GetValue(Ly * ((su2double)jNode) / ((su2double)(mNode - 1)) + Oy); @@ -119,7 +121,7 @@ void CRectangularMeshReaderFVM::ComputeRectangularVolumeConnectivity() { /* Check whether any of the points is in our linear partition. */ bool isOwned = false; for (unsigned short i = 0; i < N_POINTS_QUADRILATERAL; i++) { - if ((int)pointPartitioner.GetRankContainingIndex(connectivity[i]) == rank) { + if (static_cast(pointPartitioner.GetRankContainingIndex(connectivity[i])) == rank) { isOwned = true; } } diff --git a/Common/src/geometry/meshreader/CSU2ASCIIMeshReaderBase.cpp b/Common/src/geometry/meshreader/CSU2ASCIIMeshReaderBase.cpp new file mode 100644 index 00000000000..d8c524eff4c --- /dev/null +++ b/Common/src/geometry/meshreader/CSU2ASCIIMeshReaderBase.cpp @@ -0,0 +1,519 @@ +/*! + * \file CSU2ASCIIMeshReaderBase.cpp + * \brief Helper class for the reading of a native SU2 ASCII grid 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 . + */ + +#include "../../../include/toolboxes/CLinearPartitioner.hpp" +#include "../../../include/geometry/meshreader/CSU2ASCIIMeshReaderBase.hpp" + +CSU2ASCIIMeshReaderBase::CSU2ASCIIMeshReaderBase(CConfig* val_config, unsigned short val_iZone, + unsigned short val_nZone) + : CMeshReaderBase(val_config, val_iZone, val_nZone), + myZone(val_iZone), + nZones(val_nZone), + meshFilename(config->GetMesh_FileName()) {} + +CSU2ASCIIMeshReaderBase::~CSU2ASCIIMeshReaderBase(void) = default; + +bool CSU2ASCIIMeshReaderBase::ReadMetadata(const bool single_pass, CConfig* config) { + const bool harmonic_balance = config->GetTime_Marching() == TIME_MARCHING::HARMONIC_BALANCE; + const bool multizone_file = config->GetMultizone_Mesh(); + + /*--- Open grid file ---*/ + + mesh_file.open(meshFilename); + if (mesh_file.fail()) { + SU2_MPI::Error( + "Error opening SU2 ASCII grid.\n" + "Check that the file exists.", + CURRENT_FUNCTION); + } + + /*--- If more than one, find the curent zone in the mesh file. ---*/ + + string text_line; + if ((nZones > 1 && multizone_file) || harmonic_balance) { + if (harmonic_balance) { + if (rank == MASTER_NODE) cout << "Reading time instance " << config->GetiInst() + 1 << "." << endl; + } else { + bool foundZone = false; + while (getline(mesh_file, text_line)) { + /*--- Search for the current domain ---*/ + if (text_line.find("IZONE=", 0) != string::npos) { + text_line.erase(0, 6); + unsigned short jZone = atoi(text_line.c_str()); + if (jZone == myZone + 1) { + if (rank == MASTER_NODE) cout << "Reading zone " << myZone << " from native SU2 ASCII mesh." << endl; + foundZone = true; + break; + } + } + } + if (!foundZone) { + SU2_MPI::Error( + "Could not find the IZONE= keyword or the zone contents.\n" + "Check the SU2 ASCII file format.", + CURRENT_FUNCTION); + } + } + } + + /*--- Read the metadata: problem dimension, offsets for angle + of attack and angle of sideslip, global points, global elements, + and number of markers. Perform error checks as we go. ---*/ + + bool foundNDIME = false, foundNPOIN = false; + bool foundNELEM = false, foundNMARK = false; + + int current_section_idx = 0; + bool single_pass_active = false; + + while (getline(mesh_file, text_line)) { + /*--- Read the dimension of the problem ---*/ + + if (!foundNDIME && text_line.find("NDIME=", 0) != string::npos) { + text_line.erase(0, 6); + dimension = atoi(text_line.c_str()); + foundNDIME = true; + continue; + } + + /*--- The AoA and AoS offset values are optional. ---*/ + + if (text_line.find("AOA_OFFSET=", 0) != string::npos) { + text_line.erase(0, 11); + su2double AoA_Offset = atof(text_line.c_str()); + + /*--- The offset is in deg ---*/ + const su2double AoA_Current = config->GetAoA() + AoA_Offset; + config->SetAoA_Offset(AoA_Offset); + config->SetAoA(AoA_Current); + + if (AoA_Offset != 0.0) { + if (!config->GetDiscard_InFiles()) { + cout << "WARNING: AoA in the config file (" << config->GetAoA() << " deg.) +\n"; + cout << " AoA offset in mesh file (" << AoA_Offset << " deg.) = " << AoA_Current << " deg." << endl; + } else { + cout << "WARNING: Discarding the AoA offset in the mesh file." << endl; + } + } + continue; + } + + if (text_line.find("AOS_OFFSET=", 0) != string::npos) { + text_line.erase(0, 11); + su2double AoS_Offset = atof(text_line.c_str()); + + /*--- The offset is in deg ---*/ + const su2double AoS_Current = config->GetAoS() + AoS_Offset; + config->SetAoS_Offset(AoS_Offset); + config->SetAoS(AoS_Current); + + if (AoS_Offset != 0.0) { + if (!config->GetDiscard_InFiles()) { + cout << "WARNING: AoS in the config file (" << config->GetAoS() << " deg.) +\n"; + cout << " AoS offset in mesh file (" << AoS_Offset << " deg.) = " << AoS_Current << " deg." << endl; + } else { + cout << "WARNING: Discarding the AoS offset in the mesh file." << endl; + } + } + continue; + } + + if (!foundNPOIN && text_line.find("NPOIN=", 0) != string::npos) { + text_line.erase(0, 6); + numberOfGlobalPoints = atoi(text_line.c_str()); + + /* If the points were found first, read them, otherwise just consume the lines. */ + if (single_pass && foundNDIME && current_section_idx == 0) { + single_pass_active = true; + ReadPointCoordinates(true); + } else { + for (auto iPoint = 0ul; iPoint < numberOfGlobalPoints; iPoint++) getline(mesh_file, text_line); + } + SectionOrder[current_section_idx++] = FileSection::POINTS; + foundNPOIN = true; + continue; + } + + if (!foundNELEM && text_line.find("NELEM=", 0) != string::npos) { + text_line.erase(0, 6); + numberOfGlobalElements = atoi(text_line.c_str()); + + if (single_pass_active) { + ReadVolumeElementConnectivity(true); + } else { + for (auto iElem = 0ul; iElem < numberOfGlobalElements; iElem++) getline(mesh_file, text_line); + } + SectionOrder[current_section_idx++] = FileSection::ELEMENTS; + foundNELEM = true; + continue; + } + + if (!foundNMARK && text_line.find("NMARK=", 0) != string::npos) { + text_line.erase(0, 6); + numberOfMarkers = atoi(text_line.c_str()); + + if (current_section_idx != 2) { + SU2_MPI::Error("Markers must be listed after points and elements in the SU2 mesh file.", CURRENT_FUNCTION); + } + + if (single_pass_active) ReadSurfaceElementConnectivity(true); + + SectionOrder[current_section_idx++] = FileSection::MARKERS; + foundNMARK = true; + continue; + } + + /* Stop before we reach the next zone then check for errors below. */ + if (text_line.find("IZONE=", 0) != string::npos) { + break; + } + } + + mesh_file.close(); + + /* Throw an error if any of the keywords was not found. */ + if (!foundNDIME) { + SU2_MPI::Error( + "Could not find the keyword \"NDIME=\".\n" + "Check the SU2 ASCII file format.", + CURRENT_FUNCTION); + } + if (!foundNPOIN) { + SU2_MPI::Error( + "Could not find the keyword \"NPOIN=\".\n" + "Check the SU2 ASCII file format.", + CURRENT_FUNCTION); + } + if (!foundNELEM) { + SU2_MPI::Error( + "Could not find the keyword \"NELEM=\".\n" + "Check the SU2 ASCII file format.", + CURRENT_FUNCTION); + } + if (!foundNMARK) { + SU2_MPI::Error( + "Could not find the keyword \"NMARK=\".\n" + "Check the SU2 ASCII file format.", + CURRENT_FUNCTION); + } + + return single_pass_active; +} + +void CSU2ASCIIMeshReaderBase::ReadPointCoordinates(const bool single_pass) { + /* Get a partitioner to help with linear partitioning. */ + CLinearPartitioner pointPartitioner(numberOfGlobalPoints, 0); + + /* Determine number of local points */ + numberOfLocalPoints = pointPartitioner.GetSizeOnRank(rank); + + /* Prepare our data structure for the point coordinates. */ + localPointCoordinates.resize(dimension); + for (int k = 0; k < dimension; k++) localPointCoordinates[k].reserve(numberOfLocalPoints); + + /*--- Read the point coordinates into our data structure. ---*/ + + while (true) { + string text_line; + if (!single_pass) { + getline(mesh_file, text_line); + if (text_line.find("NPOIN=", 0) == string::npos) continue; + } + + for (unsigned long GlobalIndex = 0; GlobalIndex < numberOfGlobalPoints; ++GlobalIndex) { + if (!actuator_disk) { + getline(mesh_file, text_line); + } else { + if (GlobalIndex < numberOfGlobalPoints - ActDiskNewPoints) { + getline(mesh_file, text_line); + } else { + /* This is a new actuator disk point, so we must construct a + string with the new point's coordinates. */ + ostringstream strsX, strsY, strsZ; + unsigned long BackActDisk_Index = GlobalIndex; + unsigned long LocalIndex = BackActDisk_Index - (numberOfGlobalPoints - ActDiskNewPoints); + strsX.precision(20); + strsY.precision(20); + strsZ.precision(20); + su2double CoordX = CoordXActDisk[LocalIndex]; + strsX << scientific << CoordX; + su2double CoordY = CoordYActDisk[LocalIndex]; + strsY << scientific << CoordY; + su2double CoordZ = CoordZActDisk[LocalIndex]; + strsZ << scientific << CoordZ; + text_line = strsX.str() + "\t" + strsY.str() + "\t" + strsZ.str(); + } + } + + /*--- We only read information for this node if it is owned by this + rank based upon our initial linear partitioning. ---*/ + + passivedouble Coords[3] = {0.0, 0.0, 0.0}; + if (pointPartitioner.IndexBelongsToRank(GlobalIndex, rank)) { + istringstream point_line(text_line); + + /* Store the coordinates more clearly. */ + point_line >> Coords[0]; + point_line >> Coords[1]; + if (dimension == 3) { + point_line >> Coords[2]; + } + + /* Load into the coordinate class data structure. */ + for (unsigned short iDim = 0; iDim < dimension; iDim++) { + localPointCoordinates[iDim].push_back(Coords[iDim]); + } + } + } + break; + } +} + +void CSU2ASCIIMeshReaderBase::ReadVolumeElementConnectivity(const bool single_pass) { + /* Get a partitioner to help with linear partitioning. */ + CLinearPartitioner pointPartitioner(numberOfGlobalPoints, 0); + + /* Loop over our analytically defined of elements and store only those + that contain a node within our linear partition of points. */ + numberOfLocalElements = 0; + array connectivity{}; + + while (true) { + string text_line; + if (!single_pass) { + if (!getline(mesh_file, text_line)) break; + if (text_line.find("NELEM=", 0) == string::npos) continue; + } + + /*--- Loop over all the volumetric elements and store any element that + contains at least one of an owned node for this rank (i.e., there will + be element redundancy, since multiple ranks will store the same elems + on the boundaries of the initial linear partitioning. ---*/ + + numberOfLocalElements = 0; + + for (unsigned long GlobalIndex = 0; GlobalIndex < numberOfGlobalElements; ++GlobalIndex) { + getline(mesh_file, text_line); + istringstream elem_line(text_line); + + /*--- Decide whether this rank needs each element. ---*/ + + unsigned short VTK_Type; + elem_line >> VTK_Type; + + const auto nPointsElem = nPointsOfElementType(VTK_Type); + + for (unsigned short i = 0; i < nPointsElem; i++) { + elem_line >> connectivity[i]; + } + + if (actuator_disk) { + for (unsigned short i = 0; i < nPointsElem; i++) { + if (ActDisk_Bool[connectivity[i]]) { + su2double Xcg = 0.0; + unsigned long Counter = 0; + for (unsigned short j = 0; j < nPointsElem; j++) { + if (connectivity[j] < numberOfGlobalPoints - ActDiskNewPoints) { + Xcg += CoordXVolumePoint[VolumePoint_Inv[connectivity[j]]]; + Counter++; + } + } + Xcg = Xcg / su2double(Counter); + + if (Counter != 0 && Xcg > Xloc) { + connectivity[i] = ActDiskPoint_Back[connectivity[i]]; + } + } + } + } + + /* Check whether any of the points reside in our linear partition. */ + bool isOwned = false; + for (unsigned short i = 0; i < nPointsElem; i++) { + if (pointPartitioner.IndexBelongsToRank(connectivity[i], rank)) { + isOwned = true; + break; + } + } + + /* If element is owned, we need to store it locally. */ + if (isOwned) { + localVolumeElementConnectivity.push_back(GlobalIndex); + localVolumeElementConnectivity.push_back(VTK_Type); + /// TODO: Use a compressed format. + for (unsigned short i = 0; i < N_POINTS_HEXAHEDRON; i++) { + localVolumeElementConnectivity.push_back(connectivity[i]); + } + numberOfLocalElements++; + } + } + break; + } +} + +void CSU2ASCIIMeshReaderBase::ReadSurfaceElementConnectivity(const bool single_pass) { + /* We already read in the number of markers with the metadata. */ + surfaceElementConnectivity.resize(numberOfMarkers); + markerNames.resize(numberOfMarkers); + + array connectivity{}; + + /*--- In this routine, the boundary info is read by all ranks, + however, the surface connectivity is still handled by the + master node (and eventually distributed by the master as well). ---*/ + + while (true) { + string text_line; + if (!single_pass) { + if (!getline(mesh_file, text_line)) break; + if (text_line.find("NMARK=", 0) == string::npos) continue; + } + + for (unsigned long iMarker = 0; iMarker < numberOfMarkers; ++iMarker) { + getline(mesh_file, text_line); + text_line.erase(0, 11); + string::size_type position; + + for (unsigned short iChar = 0; iChar < 20; iChar++) { + position = text_line.find(' ', 0); + if (position != string::npos) text_line.erase(position, 1); + position = text_line.find('\r', 0); + if (position != string::npos) text_line.erase(position, 1); + position = text_line.find('\n', 0); + if (position != string::npos) text_line.erase(position, 1); + } + markerNames[iMarker] = text_line; + + bool duplicate = false; + if ((actuator_disk) && (markerNames[iMarker] == config->GetMarker_ActDiskInlet_TagBound(0))) { + duplicate = true; + markerNames[iMarker + 1] = config->GetMarker_ActDiskOutlet_TagBound(0); + } + + /*--- Physical boundaries definition ---*/ + + if (markerNames[iMarker] == "SEND_RECEIVE") { + /*--- Throw an error if we find deprecated references to SEND_RECEIVE + boundaries in the mesh. ---*/ + SU2_MPI::Error( + "Mesh file contains deprecated SEND_RECEIVE marker!\n" + "Please remove any SEND_RECEIVE markers from the SU2 ASCII mesh.", + CURRENT_FUNCTION); + } + + getline(mesh_file, text_line); + text_line.erase(0, 13); + unsigned long nElem_Bound = atoi(text_line.c_str()); + + /*--- Allocate space for elements ---*/ + + for (unsigned long iElem_Bound = 0; iElem_Bound < nElem_Bound; iElem_Bound++) { + getline(mesh_file, text_line); + istringstream bound_line(text_line); + + unsigned short VTK_Type; + bound_line >> VTK_Type; + + const auto nPointsElem = nPointsOfElementType(VTK_Type); + + if (dimension == 3 && VTK_Type == LINE) { + SU2_MPI::Error( + "Line boundary conditions are not possible for 3D calculations.\n" + "Please check the SU2 ASCII mesh file.", + CURRENT_FUNCTION); + } + + for (unsigned short i = 0; i < nPointsElem; i++) { + bound_line >> connectivity[i]; + } + + surfaceElementConnectivity[iMarker].push_back(0); + surfaceElementConnectivity[iMarker].push_back(VTK_Type); + for (unsigned short i = 0; i < N_POINTS_HEXAHEDRON; i++) { + surfaceElementConnectivity[iMarker].push_back(connectivity[i]); + } + + if (duplicate) { + for (unsigned short i = 0; i < nPointsElem; i++) { + if (ActDisk_Bool[connectivity[i]]) { + connectivity[i] = ActDiskPoint_Back[connectivity[i]]; + } + } + surfaceElementConnectivity[iMarker + 1].push_back(0); + surfaceElementConnectivity[iMarker + 1].push_back(VTK_Type); + for (unsigned short i = 0; i < N_POINTS_HEXAHEDRON; i++) { + surfaceElementConnectivity[iMarker + 1].push_back(connectivity[i]); + } + } + } + /*--- Increment the counter an extra time if we stored a duplicate. ---*/ + iMarker += duplicate; + } + break; + } + + if (rank != MASTER_NODE) return; + + /*--- Final error check for deprecated periodic BC format. ---*/ + + string text_line; + while (getline(mesh_file, text_line)) { + /*--- Find any periodic transformation information. ---*/ + + if (text_line.find("NPERIODIC=", 0) != string::npos) { + /*--- Read and store the number of transformations. ---*/ + text_line.erase(0, 10); + unsigned short nPeriodic = atoi(text_line.c_str()); + if (nPeriodic - 1 != 0) { + SU2_MPI::Error( + "Mesh file contains deprecated periodic format!\n\n" + "For SU2 v7.0.0 and later, preprocessing of periodic grids by SU2_MSH\n" + "is no longer necessary. Please use the original mesh file (prior to SU2_MSH)\n" + "with the same MARKER_PERIODIC definition in the configuration file.", + CURRENT_FUNCTION); + } + } + + /*--- Stop before we reach the next zone. ---*/ + if (text_line.find("IZONE=", 0) != string::npos) break; + } +} + +void CSU2ASCIIMeshReaderBase::FastForwardToMyZone() { + /*--- If more than one, fast-forward to my zone in the mesh file. ---*/ + + if (nZones == 1 || !config->GetMultizone_Mesh()) return; + + string text_line; + while (getline(mesh_file, text_line)) { + /*--- Search for the current domain ---*/ + if (text_line.find("IZONE=", 0) == string::npos) continue; + text_line.erase(0, 6); + unsigned short jZone = atoi(text_line.c_str()); + if (jZone == myZone + 1) break; + } +} diff --git a/Common/src/geometry/meshreader/CSU2ASCIIMeshReaderFEM.cpp b/Common/src/geometry/meshreader/CSU2ASCIIMeshReaderFEM.cpp new file mode 100644 index 00000000000..78dc46b3f10 --- /dev/null +++ b/Common/src/geometry/meshreader/CSU2ASCIIMeshReaderFEM.cpp @@ -0,0 +1,360 @@ +/*! + * \file CSU2ASCIIMeshReaderFEM.cpp + * \brief Reads a native SU2 ASCII grid into linear partitions for the + * finite element solver (FEM). + * \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 . + */ + +#include "../../../include/toolboxes/CLinearPartitioner.hpp" +#include "../../../include/geometry/meshreader/CSU2ASCIIMeshReaderFEM.hpp" +#include "../../../include/fem/fem_standard_element.hpp" + +CSU2ASCIIMeshReaderFEM::CSU2ASCIIMeshReaderFEM(CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone) + : CSU2ASCIIMeshReaderBase(val_config, val_iZone, val_nZone) { + /* Read the basic metadata and perform some basic error checks. */ + ReadMetadata(true, val_config); + + /*--- Read the volume connectivity and distribute it + linearly over the MPI ranks. ---*/ + ReadVolumeElementConnectivity(); + + /*--- Read the coordinates of the points that are needed + on this MPI rank. ---*/ + ReadPointCoordinates(); + + /*--- Read the surface connectivity and store the surface elements whose + corresponding volume element is stored on this MPI rank. ---*/ + ReadSurfaceElementConnectivity(); +} + +CSU2ASCIIMeshReaderFEM::~CSU2ASCIIMeshReaderFEM() = default; + +void CSU2ASCIIMeshReaderFEM::ReadPointCoordinates() { + /*--- Loop over the local elements to determine the global + point IDs to be stored on this rank. --*/ + unsigned long ind = 0; + for (unsigned long i = 0; i < numberOfLocalElements; ++i) { + /*--- Store the number of grid DOFs for this element and + skip the meta data for this element (5 entries). ---*/ + const unsigned long nDOFsGrid = localVolumeElementConnectivity[ind + 3]; + ind += 5; + + /*--- Copy the connectivity to globalPointIDs. ---*/ + unsigned long* conn = localVolumeElementConnectivity.data() + ind; + ind += nDOFsGrid; + globalPointIDs.insert(globalPointIDs.end(), conn, conn + nDOFsGrid); + } + + /*--- Sort the globalPointIDs and remove the duplicate entries. ---*/ + sort(globalPointIDs.begin(), globalPointIDs.end()); + vector::iterator lastNode; + lastNode = unique(globalPointIDs.begin(), globalPointIDs.end()); + globalPointIDs.erase(lastNode, globalPointIDs.end()); + + /*--- Determine the number of locally stored points. ---*/ + numberOfLocalPoints = globalPointIDs.size(); + + /*--- Prepare our data structure for the point coordinates. ---*/ + localPointCoordinates.resize(dimension); + for (int k = 0; k < dimension; ++k) localPointCoordinates[k].reserve(numberOfLocalPoints); + + /*--- Open the mesh file and jump to our zone. ---*/ + mesh_file.open(meshFilename, ios::in); + FastForwardToMyZone(); + + /*--- Find the section containing the coordinates. ---*/ + string text_line; + while (getline(mesh_file, text_line)) { + string::size_type position = text_line.find("NPOIN=", 0); + if (position != string::npos) break; + } + + /*--- Loop over the global number of points in the grid. ---*/ + for (unsigned long i = 0; i < numberOfGlobalPoints; ++i) { + /*--- Read the line in the grid file. This line must always + be read, even if the point is not stored on this rank. ---*/ + getline(mesh_file, text_line); + + /*--- Determine whether this point must be stored on this rank. ---*/ + if (binary_search(globalPointIDs.begin(), globalPointIDs.end(), i)) { + /*--- Read the coordinates from the string and store them + in localPointCoordinates. ---*/ + istringstream point_line(text_line); + for (unsigned short iDim = 0; iDim < dimension; ++iDim) { + passivedouble Coord; + point_line >> Coord; + localPointCoordinates[iDim].push_back(Coord); + } + } + } + + /*--- Close the mesh file again. ---*/ + mesh_file.close(); +} + +void CSU2ASCIIMeshReaderFEM::ReadVolumeElementConnectivity() { + /* Get a partitioner to help with linear partitioning. */ + CLinearPartitioner elemPartitioner(numberOfGlobalElements, 0); + + /*--- Open the mesh file and jump to our zone. ---*/ + mesh_file.open(meshFilename, ios::in); + FastForwardToMyZone(); + + /*--- Find the section containing the interior elements. ---*/ + string text_line; + while (getline(mesh_file, text_line)) { + string::size_type position = text_line.find("NELEM=", 0); + if (position != string::npos) break; + } + + /*--- Skip the elements, which are read by ranks lower than my rank. ---*/ + const unsigned long firstIndex = elemPartitioner.GetFirstIndexOnRank(rank); + for (unsigned long i = 0; i < firstIndex; ++i) getline(mesh_file, text_line); + + /*--- Determine the number of local elements. ---*/ + numberOfLocalElements = elemPartitioner.GetSizeOnRank(rank); + + /*--- Loop over the elements that must be stored on this rank. ---*/ + for (unsigned long i = 0; i < numberOfLocalElements; ++i) { + /*--- Read the line for this element and couple it to an istringstream + to enable the actual reading of the data. ---*/ + getline(mesh_file, text_line); + istringstream elem_line(text_line); + + /*--- Read the value that defines the element type and the polynomial degree + of the geometry and 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; + + /*--- Determine the number of grid DOFs for this element. ---*/ + unsigned short nDOFsGrid = CFEMStandardElementBase::GetNDOFsStatic(VTK_Type, nPolyGrid); + if (nDOFsGrid == 0) { + ostringstream message; + message << "Unknown FEM element type, " << typeReadErrorMessage << ", encountered."; + SU2_MPI::Error(message.str(), CURRENT_FUNCTION); + } + + /*--- Store the data just created in localVolumeElementConnectivity. ---*/ + localVolumeElementConnectivity.push_back(VTK_Type); + localVolumeElementConnectivity.push_back(nPolyGrid); + localVolumeElementConnectivity.push_back(nPolySol); + localVolumeElementConnectivity.push_back(nDOFsGrid); + localVolumeElementConnectivity.push_back(firstIndex + i); // Global elem ID. + + /*--- Store the current index, as this is needed when the + connectivity of linear elements is swapped. ---*/ + const unsigned long ind = localVolumeElementConnectivity.size(); + + /*--- Read the connectivity from elem_line and store it in + localVolumeElementConnectivity. ---*/ + for (unsigned short j = 0; j < nDOFsGrid; ++j) { + unsigned long nodeID; + elem_line >> nodeID; + localVolumeElementConnectivity.push_back(nodeID); + } + + /*--- If a linear element is used, the node numbering for non-simplices + must be adapted. The reason is that compatability with the original + SU2 format is maintained for linear elements, but for the FEM solver + the nodes of the elements are stored row-wise. ---*/ + if (nPolyGrid == 1) { + switch (VTK_Type) { + case QUADRILATERAL: + swap(localVolumeElementConnectivity[ind + 2], localVolumeElementConnectivity[ind + 3]); + break; + + case HEXAHEDRON: + swap(localVolumeElementConnectivity[ind + 2], localVolumeElementConnectivity[ind + 3]); + swap(localVolumeElementConnectivity[ind + 6], localVolumeElementConnectivity[ind + 7]); + break; + + case PYRAMID: + swap(localVolumeElementConnectivity[ind + 2], localVolumeElementConnectivity[ind + 3]); + break; + } + } + } + + /*--- Close the mesh file again. ---*/ + mesh_file.close(); +} + +void CSU2ASCIIMeshReaderFEM::ReadSurfaceElementConnectivity() { + /*--- Determine the vector to hold the faces of the local elements. ---*/ + vector localFaces; + DetermineFacesVolumeElements(localFaces); + + /*--- We already read in the number of markers with the metadata. + Allocate the memory for the marker names, number of local surface + elements and the first index of the surface element connectivity. ---*/ + surfaceElementConnectivity.resize(numberOfMarkers); + markerNames.resize(numberOfMarkers); + numberOfLocalSurfaceElements.resize(numberOfMarkers, 0); + + /*--- Open the mesh file and jump to our zone. ---*/ + mesh_file.open(meshFilename, ios::in); + FastForwardToMyZone(); + + /*--- Find the section containing the markers. ---*/ + string text_line; + while (getline(mesh_file, text_line)) { + string::size_type position = text_line.find("NMARK=", 0); + if (position != string::npos) break; + } + + /*--- Loop over the number of boundary markers. ---*/ + for (unsigned long iMarker = 0; iMarker < numberOfMarkers; ++iMarker) { + /*--- Find the section containing the marker name. ---*/ + while (getline(mesh_file, text_line)) { + string::size_type position = text_line.find("MARKER_TAG=", 0); + if (position != string::npos) break; + } + + /*--- Extract the marker name. Remove spaces returns and tabs + and store the name in markerNames. ---*/ + text_line.erase(0, 11); + + for (unsigned short iChar = 0; iChar < 20; iChar++) { + auto position = text_line.find(" ", 0); + if (position != string::npos) text_line.erase(position, 1); + position = text_line.find("\r", 0); + if (position != string::npos) text_line.erase(position, 1); + position = text_line.find("\n", 0); + if (position != string::npos) text_line.erase(position, 1); + } + markerNames[iMarker] = text_line.c_str(); + + /*--- Find the section containing the number of surface elements + for this marker and determine this number. ---*/ + while (getline(mesh_file, text_line)) { + auto position = text_line.find("MARKER_ELEMS=", 0); + if (position != string::npos) break; + } + + text_line.erase(0, 13); + unsigned long nElem_Bound = atoi(text_line.c_str()); + + /*--- Loop over the surface elements for this marker. ---*/ + for (unsigned long i = 0; i < nElem_Bound; ++i) { + /*--- Read the line for this element and couple it to an istringstream + to enable the actual reading of the data. ---*/ + getline(mesh_file, text_line); + istringstream bound_line(text_line); + + /*--- Determine the element type and polynomial degree. ---*/ + unsigned long typeRead; + bound_line >> typeRead; + + const unsigned short nPolyGrid = typeRead / 100 + 1; + const unsigned short VTK_Type = typeRead % 100; + + /*--- Make a distinction between the possible element surface types and + determine the corner points in local numbering of the element. ---*/ + const 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] = static_cast(nPolyGrid) * nDOFEdgeGrid; + thisFace.cornerPoints[3] = nDOFsGrid - 1; + break; + + default: + ostringstream message; + message << "Unknown FEM boundary element value, " << typeRead << ", in " << meshFilename; + SU2_MPI::Error(message.str(), CURRENT_FUNCTION); + } + + /*--- Read the connectivity information. ---*/ + vector connFace(nDOFsGrid); + for (unsigned short j = 0; j < nDOFsGrid; ++j) bound_line >> connFace[j]; + + /*--- If a linear quadrilateral is used, the node numbering must be adapted. + The reason is that compatability with the original SU2 format is + maintained for linear elements, but for the FEM solver the nodes + of the elements are stored row-wise. ---*/ + if ((nPolyGrid == 1) && (VTK_Type == QUADRILATERAL)) swap(connFace[2], connFace[3]); + + /*--- Convert the local numbering of thisFace to global numbering + and create a unique numbering of corner points. ---*/ + for (unsigned short j = 0; j < thisFace.nCornerPoints; ++j) + thisFace.cornerPoints[j] = connFace[thisFace.cornerPoints[j]]; + thisFace.CreateUniqueNumbering(); + + /*--- Check if this boundary face must be stored on this rank. ---*/ + vector::iterator low; + low = lower_bound(localFaces.begin(), localFaces.end(), thisFace); + if (low != localFaces.end()) { + if (!(thisFace < *low)) { + /*--- Update the counter for this boundary marker and store + the meta data in surfaceElementConnectivity. ---*/ + ++numberOfLocalSurfaceElements[iMarker]; + + surfaceElementConnectivity[iMarker].push_back(VTK_Type); + surfaceElementConnectivity[iMarker].push_back(nPolyGrid); + surfaceElementConnectivity[iMarker].push_back(nDOFsGrid); + surfaceElementConnectivity[iMarker].push_back(i); // Global surface elem ID. + surfaceElementConnectivity[iMarker].push_back(low->elemID0); // Global volume elem ID. + + /*--- Copy the connectivity to surfaceElementConnectivity. ---*/ + surfaceElementConnectivity[iMarker].insert(surfaceElementConnectivity[iMarker].end(), connFace.begin(), + connFace.end()); + } + } + } + } + + /*--- Close the mesh file again. ---*/ + mesh_file.close(); +} diff --git a/Common/src/geometry/meshreader/CSU2ASCIIMeshReaderFVM.cpp b/Common/src/geometry/meshreader/CSU2ASCIIMeshReaderFVM.cpp index e0ebfcc5acb..6801123dd03 100644 --- a/Common/src/geometry/meshreader/CSU2ASCIIMeshReaderFVM.cpp +++ b/Common/src/geometry/meshreader/CSU2ASCIIMeshReaderFVM.cpp @@ -26,14 +26,10 @@ * License along with SU2. If not, see . */ -#include "../../../include/toolboxes/CLinearPartitioner.hpp" #include "../../../include/geometry/meshreader/CSU2ASCIIMeshReaderFVM.hpp" CSU2ASCIIMeshReaderFVM::CSU2ASCIIMeshReaderFVM(CConfig* val_config, unsigned short val_iZone, unsigned short val_nZone) - : CMeshReaderFVM(val_config, val_iZone, val_nZone), - myZone(val_iZone), - nZones(val_nZone), - meshFilename(config->GetMesh_FileName()) { + : CSU2ASCIIMeshReaderBase(val_config, val_iZone, val_nZone) { actuator_disk = (((config->GetnMarker_ActDiskInlet() != 0) || (config->GetnMarker_ActDiskOutlet() != 0)) && ((config->GetKind_SU2() == SU2_COMPONENT::SU2_CFD) || ((config->GetKind_SU2() == SU2_COMPONENT::SU2_DEF) && (config->GetActDisk_SU2_DEF())))); @@ -75,192 +71,7 @@ CSU2ASCIIMeshReaderFVM::CSU2ASCIIMeshReaderFVM(CConfig* val_config, unsigned sho mesh_file.close(); } -bool CSU2ASCIIMeshReaderFVM::ReadMetadata(const bool single_pass, CConfig* config) { - const bool harmonic_balance = config->GetTime_Marching() == TIME_MARCHING::HARMONIC_BALANCE; - const bool multizone_file = config->GetMultizone_Mesh(); - - /*--- Open grid file ---*/ - - mesh_file.open(meshFilename); - if (mesh_file.fail()) { - SU2_MPI::Error( - "Error opening SU2 ASCII grid.\n" - "Check that the file exists.", - CURRENT_FUNCTION); - } - - /*--- If more than one, find the curent zone in the mesh file. ---*/ - - string text_line; - if ((nZones > 1 && multizone_file) || harmonic_balance) { - if (harmonic_balance) { - if (rank == MASTER_NODE) cout << "Reading time instance " << config->GetiInst() + 1 << "." << endl; - } else { - bool foundZone = false; - while (getline(mesh_file, text_line)) { - /*--- Search for the current domain ---*/ - if (text_line.find("IZONE=", 0) != string::npos) { - text_line.erase(0, 6); - unsigned short jZone = atoi(text_line.c_str()); - if (jZone == myZone + 1) { - if (rank == MASTER_NODE) cout << "Reading zone " << myZone << " from native SU2 ASCII mesh." << endl; - foundZone = true; - break; - } - } - } - if (!foundZone) { - SU2_MPI::Error( - "Could not find the IZONE= keyword or the zone contents.\n" - "Check the SU2 ASCII file format.", - CURRENT_FUNCTION); - } - } - } - - /*--- Read the metadata: problem dimension, offsets for angle - of attack and angle of sideslip, global points, global elements, - and number of markers. Perform error checks as we go. ---*/ - - bool foundNDIME = false, foundNPOIN = false; - bool foundNELEM = false, foundNMARK = false; - - int current_section_idx = 0; - bool single_pass_active = false; - - while (getline(mesh_file, text_line)) { - /*--- Read the dimension of the problem ---*/ - - if (!foundNDIME && text_line.find("NDIME=", 0) != string::npos) { - text_line.erase(0, 6); - dimension = atoi(text_line.c_str()); - foundNDIME = true; - continue; - } - - /*--- The AoA and AoS offset values are optional. ---*/ - - if (text_line.find("AOA_OFFSET=", 0) != string::npos) { - text_line.erase(0, 11); - su2double AoA_Offset = atof(text_line.c_str()); - - /*--- The offset is in deg ---*/ - const su2double AoA_Current = config->GetAoA() + AoA_Offset; - config->SetAoA_Offset(AoA_Offset); - config->SetAoA(AoA_Current); - - if (AoA_Offset != 0.0) { - if (!config->GetDiscard_InFiles()) { - cout << "WARNING: AoA in the config file (" << config->GetAoA() << " deg.) +\n"; - cout << " AoA offset in mesh file (" << AoA_Offset << " deg.) = " << AoA_Current << " deg." << endl; - } else { - cout << "WARNING: Discarding the AoA offset in the mesh file." << endl; - } - } - continue; - } - - if (text_line.find("AOS_OFFSET=", 0) != string::npos) { - text_line.erase(0, 11); - su2double AoS_Offset = atof(text_line.c_str()); - - /*--- The offset is in deg ---*/ - const su2double AoS_Current = config->GetAoS() + AoS_Offset; - config->SetAoS_Offset(AoS_Offset); - config->SetAoS(AoS_Current); - - if (AoS_Offset != 0.0) { - if (!config->GetDiscard_InFiles()) { - cout << "WARNING: AoS in the config file (" << config->GetAoS() << " deg.) +\n"; - cout << " AoS offset in mesh file (" << AoS_Offset << " deg.) = " << AoS_Current << " deg." << endl; - } else { - cout << "WARNING: Discarding the AoS offset in the mesh file." << endl; - } - } - continue; - } - - if (!foundNPOIN && text_line.find("NPOIN=", 0) != string::npos) { - text_line.erase(0, 6); - numberOfGlobalPoints = atoi(text_line.c_str()); - - /* If the points were found first, read them, otherwise just consume the lines. */ - if (single_pass && foundNDIME && current_section_idx == 0) { - single_pass_active = true; - ReadPointCoordinates(true); - } else { - for (auto iPoint = 0ul; iPoint < numberOfGlobalPoints; iPoint++) getline(mesh_file, text_line); - } - SectionOrder[current_section_idx++] = FileSection::POINTS; - foundNPOIN = true; - continue; - } - - if (!foundNELEM && text_line.find("NELEM=", 0) != string::npos) { - text_line.erase(0, 6); - numberOfGlobalElements = atoi(text_line.c_str()); - - if (single_pass_active) { - ReadVolumeElementConnectivity(true); - } else { - for (auto iElem = 0ul; iElem < numberOfGlobalElements; iElem++) getline(mesh_file, text_line); - } - SectionOrder[current_section_idx++] = FileSection::ELEMENTS; - foundNELEM = true; - continue; - } - - if (!foundNMARK && text_line.find("NMARK=", 0) != string::npos) { - text_line.erase(0, 6); - numberOfMarkers = atoi(text_line.c_str()); - - if (current_section_idx != 2) { - SU2_MPI::Error("Markers must be listed after points and elements in the SU2 mesh file.", CURRENT_FUNCTION); - } - - if (single_pass_active) ReadSurfaceElementConnectivity(true); - - SectionOrder[current_section_idx++] = FileSection::MARKERS; - foundNMARK = true; - continue; - } - - /* Stop before we reach the next zone then check for errors below. */ - if (text_line.find("IZONE=", 0) != string::npos) { - break; - } - } - - mesh_file.close(); - - /* Throw an error if any of the keywords was not found. */ - if (!foundNDIME) { - SU2_MPI::Error( - "Could not find the keyword \"NDIME=\".\n" - "Check the SU2 ASCII file format.", - CURRENT_FUNCTION); - } - if (!foundNPOIN) { - SU2_MPI::Error( - "Could not find the keyword \"NPOIN=\".\n" - "Check the SU2 ASCII file format.", - CURRENT_FUNCTION); - } - if (!foundNELEM) { - SU2_MPI::Error( - "Could not find the keyword \"NELEM=\".\n" - "Check the SU2 ASCII file format.", - CURRENT_FUNCTION); - } - if (!foundNMARK) { - SU2_MPI::Error( - "Could not find the keyword \"NMARK=\".\n" - "Check the SU2 ASCII file format.", - CURRENT_FUNCTION); - } - - return single_pass_active; -} +CSU2ASCIIMeshReaderFVM::~CSU2ASCIIMeshReaderFVM() = default; void CSU2ASCIIMeshReaderFVM::SplitActuatorDiskSurface() { /*--- Actuator disk preprocesing ---*/ @@ -594,14 +405,14 @@ void CSU2ASCIIMeshReaderFVM::SplitActuatorDiskSurface() { elem_line >> connectivity[1]; elem_line >> connectivity[2]; InElem = false; - for (unsigned long i = 0; i < (unsigned long)N_POINTS_TRIANGLE; i++) { + for (unsigned long i = 0; i < static_cast(N_POINTS_TRIANGLE); i++) { if (ActDisk_Bool[connectivity[i]]) { InElem = true; break; } } if (InElem) { - for (unsigned long i = 0; i < (unsigned long)N_POINTS_TRIANGLE; i++) { + for (unsigned long i = 0; i < static_cast(N_POINTS_TRIANGLE); i++) { VolumePoint.push_back(connectivity[i]); } } @@ -612,14 +423,14 @@ void CSU2ASCIIMeshReaderFVM::SplitActuatorDiskSurface() { elem_line >> connectivity[2]; elem_line >> connectivity[3]; InElem = false; - for (unsigned long i = 0; i < (unsigned long)N_POINTS_QUADRILATERAL; i++) { + for (unsigned long i = 0; i < static_cast(N_POINTS_QUADRILATERAL); i++) { if (ActDisk_Bool[connectivity[i]]) { InElem = true; break; } } if (InElem) { - for (unsigned long i = 0; i < (unsigned long)N_POINTS_QUADRILATERAL; i++) { + for (unsigned long i = 0; i < static_cast(N_POINTS_QUADRILATERAL); i++) { VolumePoint.push_back(connectivity[i]); } } @@ -630,14 +441,14 @@ void CSU2ASCIIMeshReaderFVM::SplitActuatorDiskSurface() { elem_line >> connectivity[2]; elem_line >> connectivity[3]; InElem = false; - for (unsigned long i = 0; i < (unsigned long)N_POINTS_TETRAHEDRON; i++) { + for (unsigned long i = 0; i < static_cast(N_POINTS_TETRAHEDRON); i++) { if (ActDisk_Bool[connectivity[i]]) { InElem = true; break; } } if (InElem) { - for (unsigned long i = 0; i < (unsigned long)N_POINTS_TETRAHEDRON; i++) { + for (unsigned long i = 0; i < static_cast(N_POINTS_TETRAHEDRON); i++) { VolumePoint.push_back(connectivity[i]); } } @@ -652,14 +463,14 @@ void CSU2ASCIIMeshReaderFVM::SplitActuatorDiskSurface() { elem_line >> connectivity[6]; elem_line >> connectivity[7]; InElem = false; - for (unsigned long i = 0; i < (unsigned long)N_POINTS_HEXAHEDRON; i++) { + for (unsigned long i = 0; i < static_cast(N_POINTS_HEXAHEDRON); i++) { if (ActDisk_Bool[connectivity[i]]) { InElem = true; break; } } if (InElem) { - for (unsigned long i = 0; i < (unsigned long)N_POINTS_HEXAHEDRON; i++) { + for (unsigned long i = 0; i < static_cast(N_POINTS_HEXAHEDRON); i++) { VolumePoint.push_back(connectivity[i]); } } @@ -672,14 +483,14 @@ void CSU2ASCIIMeshReaderFVM::SplitActuatorDiskSurface() { elem_line >> connectivity[4]; elem_line >> connectivity[5]; InElem = false; - for (unsigned long i = 0; i < (unsigned long)N_POINTS_PRISM; i++) { + for (unsigned long i = 0; i < static_cast(N_POINTS_PRISM); i++) { if (ActDisk_Bool[connectivity[i]]) { InElem = true; break; } } if (InElem) { - for (unsigned long i = 0; i < (unsigned long)N_POINTS_PRISM; i++) { + for (unsigned long i = 0; i < static_cast(N_POINTS_PRISM); i++) { VolumePoint.push_back(connectivity[i]); } } @@ -691,14 +502,14 @@ void CSU2ASCIIMeshReaderFVM::SplitActuatorDiskSurface() { elem_line >> connectivity[3]; elem_line >> connectivity[4]; InElem = false; - for (unsigned long i = 0; i < (unsigned long)N_POINTS_PYRAMID; i++) { + for (unsigned long i = 0; i < static_cast(N_POINTS_PYRAMID); i++) { if (ActDisk_Bool[connectivity[i]]) { InElem = true; break; } } if (InElem) { - for (unsigned long i = 0; i < (unsigned long)N_POINTS_PYRAMID; i++) { + for (unsigned long i = 0; i < static_cast(N_POINTS_PYRAMID); i++) { VolumePoint.push_back(connectivity[i]); } } @@ -783,297 +594,3 @@ void CSU2ASCIIMeshReaderFVM::SplitActuatorDiskSurface() { mesh_file.close(); } - -void CSU2ASCIIMeshReaderFVM::ReadPointCoordinates(const bool single_pass) { - /* Get a partitioner to help with linear partitioning. */ - CLinearPartitioner pointPartitioner(numberOfGlobalPoints, 0); - - /* Determine number of local points */ - numberOfLocalPoints = pointPartitioner.GetSizeOnRank(rank); - - /* Prepare our data structure for the point coordinates. */ - localPointCoordinates.resize(dimension); - for (int k = 0; k < dimension; k++) localPointCoordinates[k].reserve(numberOfLocalPoints); - - /*--- Read the point coordinates into our data structure. ---*/ - - while (true) { - string text_line; - if (!single_pass) { - getline(mesh_file, text_line); - if (text_line.find("NPOIN=", 0) == string::npos) continue; - } - - for (unsigned long GlobalIndex = 0; GlobalIndex < numberOfGlobalPoints; ++GlobalIndex) { - if (!actuator_disk) { - getline(mesh_file, text_line); - } else { - if (GlobalIndex < numberOfGlobalPoints - ActDiskNewPoints) { - getline(mesh_file, text_line); - } else { - /* This is a new actuator disk point, so we must construct a - string with the new point's coordinates. */ - ostringstream strsX, strsY, strsZ; - unsigned long BackActDisk_Index = GlobalIndex; - unsigned long LocalIndex = BackActDisk_Index - (numberOfGlobalPoints - ActDiskNewPoints); - strsX.precision(20); - strsY.precision(20); - strsZ.precision(20); - su2double CoordX = CoordXActDisk[LocalIndex]; - strsX << scientific << CoordX; - su2double CoordY = CoordYActDisk[LocalIndex]; - strsY << scientific << CoordY; - su2double CoordZ = CoordZActDisk[LocalIndex]; - strsZ << scientific << CoordZ; - text_line = strsX.str() + "\t" + strsY.str() + "\t" + strsZ.str(); - } - } - - /*--- We only read information for this node if it is owned by this - rank based upon our initial linear partitioning. ---*/ - - passivedouble Coords[3] = {0.0, 0.0, 0.0}; - if (pointPartitioner.IndexBelongsToRank(GlobalIndex, rank)) { - istringstream point_line(text_line); - - /* Store the coordinates more clearly. */ - point_line >> Coords[0]; - point_line >> Coords[1]; - if (dimension == 3) { - point_line >> Coords[2]; - } - - /* Load into the coordinate class data structure. */ - for (unsigned short iDim = 0; iDim < dimension; iDim++) { - localPointCoordinates[iDim].push_back(Coords[iDim]); - } - } - } - break; - } -} - -void CSU2ASCIIMeshReaderFVM::ReadVolumeElementConnectivity(const bool single_pass) { - /* Get a partitioner to help with linear partitioning. */ - CLinearPartitioner pointPartitioner(numberOfGlobalPoints, 0); - - /* Loop over our analytically defined of elements and store only those - that contain a node within our linear partition of points. */ - numberOfLocalElements = 0; - array connectivity{}; - - while (true) { - string text_line; - if (!single_pass) { - if (!getline(mesh_file, text_line)) break; - if (text_line.find("NELEM=", 0) == string::npos) continue; - } - - /*--- Loop over all the volumetric elements and store any element that - contains at least one of an owned node for this rank (i.e., there will - be element redundancy, since multiple ranks will store the same elems - on the boundaries of the initial linear partitioning. ---*/ - - numberOfLocalElements = 0; - - for (unsigned long GlobalIndex = 0; GlobalIndex < numberOfGlobalElements; ++GlobalIndex) { - getline(mesh_file, text_line); - istringstream elem_line(text_line); - - /*--- Decide whether this rank needs each element. ---*/ - - unsigned short VTK_Type; - elem_line >> VTK_Type; - - const auto nPointsElem = nPointsOfElementType(VTK_Type); - - for (unsigned short i = 0; i < nPointsElem; i++) { - elem_line >> connectivity[i]; - } - - if (actuator_disk) { - for (unsigned short i = 0; i < nPointsElem; i++) { - if (ActDisk_Bool[connectivity[i]]) { - su2double Xcg = 0.0; - unsigned long Counter = 0; - for (unsigned short j = 0; j < nPointsElem; j++) { - if (connectivity[j] < numberOfGlobalPoints - ActDiskNewPoints) { - Xcg += CoordXVolumePoint[VolumePoint_Inv[connectivity[j]]]; - Counter++; - } - } - Xcg = Xcg / su2double(Counter); - - if (Counter != 0 && Xcg > Xloc) { - connectivity[i] = ActDiskPoint_Back[connectivity[i]]; - } - } - } - } - - /* Check whether any of the points reside in our linear partition. */ - bool isOwned = false; - for (unsigned short i = 0; i < nPointsElem; i++) { - if (pointPartitioner.IndexBelongsToRank(connectivity[i], rank)) { - isOwned = true; - break; - } - } - - /* If element is owned, we need to store it locally. */ - if (isOwned) { - localVolumeElementConnectivity.push_back(GlobalIndex); - localVolumeElementConnectivity.push_back(VTK_Type); - /// TODO: Use a compressed format. - for (unsigned short i = 0; i < N_POINTS_HEXAHEDRON; i++) { - localVolumeElementConnectivity.push_back(connectivity[i]); - } - numberOfLocalElements++; - } - } - break; - } -} - -void CSU2ASCIIMeshReaderFVM::ReadSurfaceElementConnectivity(const bool single_pass) { - /* We already read in the number of markers with the metadata. */ - surfaceElementConnectivity.resize(numberOfMarkers); - markerNames.resize(numberOfMarkers); - - array connectivity{}; - - /*--- In this routine, the boundary info is read by all ranks, - however, the surface connectivity is still handled by the - master node (and eventually distributed by the master as well). ---*/ - - while (true) { - string text_line; - if (!single_pass) { - if (!getline(mesh_file, text_line)) break; - if (text_line.find("NMARK=", 0) == string::npos) continue; - } - - for (unsigned short iMarker = 0; iMarker < numberOfMarkers; ++iMarker) { - getline(mesh_file, text_line); - text_line.erase(0, 11); - string::size_type position; - - for (unsigned short iChar = 0; iChar < 20; iChar++) { - position = text_line.find(' ', 0); - if (position != string::npos) text_line.erase(position, 1); - position = text_line.find('\r', 0); - if (position != string::npos) text_line.erase(position, 1); - position = text_line.find('\n', 0); - if (position != string::npos) text_line.erase(position, 1); - } - markerNames[iMarker] = text_line; - - bool duplicate = false; - if ((actuator_disk) && (markerNames[iMarker] == config->GetMarker_ActDiskInlet_TagBound(0))) { - duplicate = true; - markerNames[iMarker + 1] = config->GetMarker_ActDiskOutlet_TagBound(0); - } - - /*--- Physical boundaries definition ---*/ - - if (markerNames[iMarker] == "SEND_RECEIVE") { - /*--- Throw an error if we find deprecated references to SEND_RECEIVE - boundaries in the mesh. ---*/ - SU2_MPI::Error( - "Mesh file contains deprecated SEND_RECEIVE marker!\n" - "Please remove any SEND_RECEIVE markers from the SU2 ASCII mesh.", - CURRENT_FUNCTION); - } - - getline(mesh_file, text_line); - text_line.erase(0, 13); - unsigned long nElem_Bound = atoi(text_line.c_str()); - - /*--- Allocate space for elements ---*/ - - for (unsigned long iElem_Bound = 0; iElem_Bound < nElem_Bound; iElem_Bound++) { - getline(mesh_file, text_line); - istringstream bound_line(text_line); - - unsigned short VTK_Type; - bound_line >> VTK_Type; - - const auto nPointsElem = nPointsOfElementType(VTK_Type); - - if (dimension == 3 && VTK_Type == LINE) { - SU2_MPI::Error( - "Line boundary conditions are not possible for 3D calculations.\n" - "Please check the SU2 ASCII mesh file.", - CURRENT_FUNCTION); - } - - for (unsigned short i = 0; i < nPointsElem; i++) { - bound_line >> connectivity[i]; - } - - surfaceElementConnectivity[iMarker].push_back(0); - surfaceElementConnectivity[iMarker].push_back(VTK_Type); - for (unsigned short i = 0; i < N_POINTS_HEXAHEDRON; i++) { - surfaceElementConnectivity[iMarker].push_back(connectivity[i]); - } - - if (duplicate) { - for (unsigned short i = 0; i < nPointsElem; i++) { - if (ActDisk_Bool[connectivity[i]]) { - connectivity[i] = ActDiskPoint_Back[connectivity[i]]; - } - } - surfaceElementConnectivity[iMarker + 1].push_back(0); - surfaceElementConnectivity[iMarker + 1].push_back(VTK_Type); - for (unsigned short i = 0; i < N_POINTS_HEXAHEDRON; i++) { - surfaceElementConnectivity[iMarker + 1].push_back(connectivity[i]); - } - } - } - /*--- Increment the counter an extra time if we stored a duplicate. ---*/ - iMarker += duplicate; - } - break; - } - - if (rank != MASTER_NODE) return; - - /*--- Final error check for deprecated periodic BC format. ---*/ - - string text_line; - while (getline(mesh_file, text_line)) { - /*--- Find any periodic transformation information. ---*/ - - if (text_line.find("NPERIODIC=", 0) != string::npos) { - /*--- Read and store the number of transformations. ---*/ - text_line.erase(0, 10); - unsigned short nPeriodic = atoi(text_line.c_str()); - if (nPeriodic - 1 != 0) { - SU2_MPI::Error( - "Mesh file contains deprecated periodic format!\n\n" - "For SU2 v7.0.0 and later, preprocessing of periodic grids by SU2_MSH\n" - "is no longer necessary. Please use the original mesh file (prior to SU2_MSH)\n" - "with the same MARKER_PERIODIC definition in the configuration file.", - CURRENT_FUNCTION); - } - } - - /*--- Stop before we reach the next zone. ---*/ - if (text_line.find("IZONE=", 0) != string::npos) break; - } -} - -void CSU2ASCIIMeshReaderFVM::FastForwardToMyZone() { - /*--- If more than one, fast-forward to my zone in the mesh file. ---*/ - - if (nZones == 1 || !config->GetMultizone_Mesh()) return; - - string text_line; - while (getline(mesh_file, text_line)) { - /*--- Search for the current domain ---*/ - if (text_line.find("IZONE=", 0) == string::npos) continue; - text_line.erase(0, 6); - unsigned short jZone = atoi(text_line.c_str()); - if (jZone == myZone + 1) break; - } -} diff --git a/Common/src/geometry/meshreader/meson.build b/Common/src/geometry/meshreader/meson.build index 3f1e12595de..543bdfcf97a 100644 --- a/Common/src/geometry/meshreader/meson.build +++ b/Common/src/geometry/meshreader/meson.build @@ -1,5 +1,12 @@ -common_src += files(['CBoxMeshReaderFVM.cpp', +common_src += files(['CBoxMeshReaderFEM.cpp', + 'CBoxMeshReaderFVM.cpp', + 'CCGNSElementType.cpp', + 'CCGNSMeshReaderBase.cpp', + 'CCGNSMeshReaderFEM.cpp', 'CCGNSMeshReaderFVM.cpp', - 'CMeshReaderFVM.cpp', + 'CMeshReaderBase.cpp', + 'CRectangularMeshReaderFEM.cpp', 'CRectangularMeshReaderFVM.cpp', + 'CSU2ASCIIMeshReaderBase.cpp', + 'CSU2ASCIIMeshReaderFEM.cpp', 'CSU2ASCIIMeshReaderFVM.cpp']) diff --git a/Common/src/geometry/primal_grid/CPrimalGridBoundFEM.cpp b/Common/src/geometry/primal_grid/CPrimalGridBoundFEM.cpp index cebb3aa2ebf..43fd3606a09 100644 --- a/Common/src/geometry/primal_grid/CPrimalGridBoundFEM.cpp +++ b/Common/src/geometry/primal_grid/CPrimalGridBoundFEM.cpp @@ -27,27 +27,17 @@ #include "../../../include/geometry/primal_grid/CPrimalGridBoundFEM.hpp" -CPrimalGridBoundFEM::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) - : CPrimalGrid(true, val_nDOFsGrid, 1) { - /*--- Store the integer data in the member variables of this object. ---*/ - VTK_Type = val_VTK_Type; - - nPolyGrid = val_nPolyGrid; - nDOFsGrid = val_nDOFsGrid; - - boundElemIDGlobal = val_elemGlobalID; - GlobalIndex_DomainElement = val_domainElementID; - - /*--- Copy face structure of the element from val_nodes. ---*/ - - for (unsigned short i = 0; i < nDOFsGrid; i++) Nodes[i] = val_nodes[i]; - - /*--- For a linear quadrilateral the two last node numbers must be swapped, - such that the element numbering is consistent with the FEM solver. ---*/ - - if (nPolyGrid == 1 && VTK_Type == QUADRILATERAL) std::swap(Nodes[2], Nodes[3]); +CPrimalGridBoundFEM::CPrimalGridBoundFEM(const unsigned long* dataElem) : CPrimalGrid(true, dataElem[2], 1) { + /*--- Store the meta data for this element. ---*/ + VTK_Type = static_cast(dataElem[0]); + nPolyGrid = static_cast(dataElem[1]); + nDOFsGrid = static_cast(dataElem[2]); + boundElemIDGlobal = dataElem[3]; + GlobalIndex_DomainElement = dataElem[4]; + + /*--- Allocate the memory for the global nodes of the element to define + the geometry and copy them from val_nodes. ---*/ + for (unsigned short i = 0; i < nDOFsGrid; i++) Nodes[i] = dataElem[i + 5]; } void CPrimalGridBoundFEM::GetLocalCornerPointsFace(unsigned short elementType, unsigned short nPoly, diff --git a/Common/src/geometry/primal_grid/CPrimalGridFEM.cpp b/Common/src/geometry/primal_grid/CPrimalGridFEM.cpp index 1d1405ddbb8..738c443f3e8 100644 --- a/Common/src/geometry/primal_grid/CPrimalGridFEM.cpp +++ b/Common/src/geometry/primal_grid/CPrimalGridFEM.cpp @@ -26,67 +26,27 @@ */ #include "../../../include/geometry/primal_grid/CPrimalGridFEM.hpp" +#include "../../../include/fem/fem_standard_element.hpp" -CPrimalGridFEM::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) - : CPrimalGrid(true, val_nDOFsGrid, nFacesOfElementType(val_VTK_Type)) { - /*--- Store the integer data in the member variables of this object. ---*/ - VTK_Type = val_VTK_Type; - nFaces = nFacesOfElementType(VTK_Type); - - nPolyGrid = val_nPolyGrid; - nPolySol = val_nPolySol; - nDOFsGrid = val_nDOFsGrid; - nDOFsSol = val_nDOFsSol; - - elemIDGlobal = val_elemGlobalID; - offsetDOFsSolGlobal = val_offDOfsSol; - - /*--- Read face structure of the element from elem_line. ---*/ - - for (unsigned short i = 0; i < nDOFsGrid; i++) elem_line >> Nodes[i]; - - /*--- If a linear element is used, the node numbering for non-simplices - must be adapted. The reason is that compatability with the original - SU2 format is maintained for linear elements, but for the FEM solver - the nodes of the elements are stored row-wise. ---*/ - if (nPolyGrid == 1) { - switch (VTK_Type) { - case QUADRILATERAL: - std::swap(Nodes[2], Nodes[3]); - break; +CPrimalGridFEM::CPrimalGridFEM(const unsigned long* dataElem, unsigned long& offsetSolDOFs) + : CPrimalGrid(true, dataElem[3], nFacesOfElementType(dataElem[0])) { + /*--- Store the meta data for this element. ---*/ + VTK_Type = static_cast(dataElem[0]); + nPolyGrid = static_cast(dataElem[1]); + nPolySol = static_cast(dataElem[2]); + nDOFsGrid = static_cast(dataElem[3]); + nDOFsSol = CFEMStandardElementBase::GetNDOFsStatic(VTK_Type, nPolySol); + elemIDGlobal = dataElem[4]; - case HEXAHEDRON: - std::swap(Nodes[2], Nodes[3]); - std::swap(Nodes[6], Nodes[7]); - break; + offsetDOFsSolGlobal = offsetSolDOFs; + offsetSolDOFs += nDOFsSol; - case PYRAMID: - std::swap(Nodes[2], Nodes[3]); - break; - } - } -} - -CPrimalGridFEM::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) - : CPrimalGrid(true, val_nDOFsGrid, nFacesOfElementType(val_VTK_Type)) { - /*--- Store the integer data in the member variables of this object. ---*/ - VTK_Type = val_VTK_Type; nFaces = nFacesOfElementType(VTK_Type); - nPolyGrid = val_nPolyGrid; - nPolySol = val_nPolySol; - nDOFsGrid = val_nDOFsGrid; - nDOFsSol = val_nDOFsSol; - - elemIDGlobal = val_elemGlobalID; - offsetDOFsSolGlobal = val_offDOfsSol; + /*--- Allocate the memory for the global nodes of the element to define + the geometry and copy the data from dataElem. ---*/ - /*--- Copy face structure of the element from connGrid. ---*/ - for (unsigned short i = 0; i < nDOFsGrid; i++) Nodes[i] = connGrid[i]; + for (unsigned short i = 0; i < nDOFsGrid; ++i) Nodes[i] = dataElem[i + 5]; } void CPrimalGridFEM::GetLocalCornerPointsAllFaces(unsigned short elementType, unsigned short nPoly, diff --git a/Common/src/linear_algebra/CSysMatrix.cpp b/Common/src/linear_algebra/CSysMatrix.cpp index 4196f638678..9ef0c7b88de 100644 --- a/Common/src/linear_algebra/CSysMatrix.cpp +++ b/Common/src/linear_algebra/CSysMatrix.cpp @@ -1027,36 +1027,59 @@ void CSysMatrix::EnforceSolutionAtNode(const unsigned long node_i, c template template -void CSysMatrix::EnforceSolutionAtDOF(unsigned long node_i, unsigned long iVar, OtherType x_i, - CSysVector& b) { +void CSysMatrix::EnforceZeroProjection(unsigned long node_i, const OtherType* n, CSysVector& b) { for (auto index = row_ptr[node_i]; index < row_ptr[node_i + 1]; ++index) { const auto node_j = col_ind[index]; - /*--- Delete row iVar of block j on row i (bij) and ATTEMPT - * to delete column iVar block i on row j (bji). ---*/ + /*--- Remove product components of block j on row i (bij) and ATTEMPT + * to remove solution components of block i on row j (bji). + * This is identical to symmetry correction applied to gradients + * but extended to the entire matrix. ---*/ auto bij = &matrix[index * nVar * nVar]; auto bji = GetBlock(node_j, node_i); - /*--- The "attempt" part. ---*/ + /*--- Attempt to remove solution components. ---*/ + ScalarType nbn{}; if (bji != nullptr) { - for (auto jVar = 0ul; jVar < nVar; ++jVar) { - /*--- Column product. ---*/ - b[node_j * nVar + jVar] -= bji[jVar * nVar + iVar] * x_i; - /*--- Delete entries. ---*/ - bji[jVar * nVar + iVar] = 0.0; + for (auto iVar = 0ul; iVar < nVar; ++iVar) { + ScalarType proj{}; + for (auto jVar = 0ul; jVar < nVar; ++jVar) { + proj += bji[iVar * nVar + jVar] * PassiveAssign(n[jVar]); + } + for (auto jVar = 0ul; jVar < nVar; ++jVar) { + bji[iVar * nVar + jVar] -= proj * PassiveAssign(n[jVar]); + } + nbn += proj * PassiveAssign(n[iVar]); } } - /*--- Delete row. ---*/ - for (auto jVar = 0ul; jVar < nVar; ++jVar) bij[iVar * nVar + jVar] = 0.0; + /*--- Product components. ---*/ + for (auto jVar = 0ul; jVar < nVar; ++jVar) { + ScalarType proj{}; + for (auto iVar = 0ul; iVar < nVar; ++iVar) { + proj += bij[iVar * nVar + jVar] * PassiveAssign(n[iVar]); + } + for (auto iVar = 0ul; iVar < nVar; ++iVar) { + bij[iVar * nVar + jVar] -= proj * PassiveAssign(n[iVar]); + } + } - /*--- Set the diagonal entry of the block to 1. ---*/ - if (node_j == node_i) bij[iVar * (nVar + 1)] = 1.0; + /*--- This part doesn't have the "*2" factor because the product components + * were removed from the result of removing the solution components + * instead of from the original block (bji == bij). ---*/ + if (node_i == node_j) { + for (auto iVar = 0ul; iVar < nVar; ++iVar) { + for (auto jVar = 0ul; jVar < nVar; ++jVar) { + bij[iVar * nVar + jVar] += PassiveAssign(n[iVar]) * nbn * PassiveAssign(n[jVar]); + } + } + } } - /*--- Set known solution in rhs vector. ---*/ - b(node_i, iVar) = x_i; + OtherType proj{}; + for (auto iVar = 0ul; iVar < nVar; ++iVar) proj += b(node_i, iVar) * n[iVar]; + for (auto iVar = 0ul; iVar < nVar; ++iVar) b(node_i, iVar) -= proj * n[iVar]; } template @@ -1203,8 +1226,7 @@ void CSysMatrix::ComputePastixPreconditioner(const CSysVector; \ template void CSysMatrix::EnforceSolutionAtNode(unsigned long, const su2double*, CSysVector&); \ - template void CSysMatrix::EnforceSolutionAtDOF(unsigned long, unsigned long, su2double, \ - CSysVector&); \ + template void CSysMatrix::EnforceZeroProjection(unsigned long, const su2double*, CSysVector&); \ INSTANTIATE_COMMS(TYPE) #ifdef CODI_FORWARD_TYPE diff --git a/SU2_CFD/include/numerics/CNumerics.hpp b/SU2_CFD/include/numerics/CNumerics.hpp index 535335947ca..2e45916edb1 100644 --- a/SU2_CFD/include/numerics/CNumerics.hpp +++ b/SU2_CFD/include/numerics/CNumerics.hpp @@ -1483,10 +1483,10 @@ class CNumerics { inline virtual void Compute_Mass_Matrix(CElement *element_container, const CConfig* config) { } /*! - * \brief A virtual member to compute the residual component due to dead loads + * \brief A virtual member to compute the residual component due to body forces. * \param[in] element_container - Element structure for the particular element integrated. */ - inline virtual void Compute_Dead_Load(CElement *element_container, const CConfig* config) { } + inline virtual void Compute_Body_Forces(CElement *element_container, const CConfig* config) { } /*! * \brief A virtual member to compute the averaged nodal stresses diff --git a/SU2_CFD/include/numerics/elasticity/CFEAElasticity.hpp b/SU2_CFD/include/numerics/elasticity/CFEAElasticity.hpp index 11470fd0fba..a1395fdf130 100644 --- a/SU2_CFD/include/numerics/elasticity/CFEAElasticity.hpp +++ b/SU2_CFD/include/numerics/elasticity/CFEAElasticity.hpp @@ -54,15 +54,20 @@ class CFEAElasticity : public CNumerics { su2double Nu = 0.0; /*!< \brief Aux. variable, Poisson's ratio. */ su2double Rho_s = 0.0; /*!< \brief Aux. variable, Structural density. */ su2double Rho_s_DL = 0.0; /*!< \brief Aux. variable, Structural density (for dead loads). */ + su2double Alpha = 0.0; /*!< \brief Aux. variable, thermal expansion coefficient. */ su2double Mu = 0.0; /*!< \brief Aux. variable, Lame's coeficient. */ su2double Lambda = 0.0; /*!< \brief Aux. variable, Lame's coeficient. */ su2double Kappa = 0.0; /*!< \brief Aux. variable, Compressibility constant. */ + su2double ThermalStressTerm = 0.0; /*!< \brief Aux. variable, Relationship between stress and delta T. */ su2double *E_i = nullptr; /*!< \brief Young's modulus of elasticity. */ su2double *Nu_i = nullptr; /*!< \brief Poisson's ratio. */ su2double *Rho_s_i = nullptr; /*!< \brief Structural density. */ su2double *Rho_s_DL_i = nullptr; /*!< \brief Structural density (for dead loads). */ + su2double *Alpha_i = nullptr; /*!< \brief Thermal expansion coefficient. */ + + su2double ReferenceTemperature = 0.0; /*!< \brief Reference temperature for thermal expansion. */ su2double **Ba_Mat = nullptr; /*!< \brief Matrix B for node a - Auxiliary. */ su2double **Bb_Mat = nullptr; /*!< \brief Matrix B for node b - Auxiliary. */ @@ -72,8 +77,6 @@ class CFEAElasticity : public CNumerics { su2double **GradNi_Ref_Mat = nullptr; /*!< \brief Gradients of Ni - Auxiliary. */ su2double **GradNi_Curr_Mat = nullptr; /*!< \brief Gradients of Ni - Auxiliary. */ - su2double *FAux_Dead_Load = nullptr; /*!< \brief Auxiliar vector for the dead loads */ - su2double *DV_Val = nullptr; /*!< \brief For optimization cases, value of the design variables. */ unsigned short n_DV = 0; /*!< \brief For optimization cases, number of design variables. */ @@ -156,11 +159,11 @@ class CFEAElasticity : public CNumerics { void Compute_Mass_Matrix(CElement *element_container, const CConfig *config) final; /*! - * \brief Compute the nodal gravity loads for an element. - * \param[in,out] element_container - The element for which the dead loads are computed. + * \brief Compute the nodal inertial loads for an element. + * \param[in,out] element_container - The element for which the inertial loads are computed. * \param[in] config - Definition of the problem. */ - void Compute_Dead_Load(CElement *element_container, const CConfig *config) final; + void Compute_Body_Forces(CElement *element_container, const CConfig *config) final; /*! * \brief Build the tangent stiffness matrix of an element. @@ -230,6 +233,8 @@ class CFEAElasticity : public CNumerics { Mu = E / (2.0*(1.0 + Nu)); Lambda = Nu*E/((1.0+Nu)*(1.0-2.0*Nu)); Kappa = Lambda + (2/3)*Mu; + /*--- https://solidmechanics.org/Text/Chapter3_2/Chapter3_2.php ---*/ + ThermalStressTerm = -Alpha * E / (1 - (plane_stress ? 1 : 2) * Nu); } /*! @@ -238,8 +243,8 @@ class CFEAElasticity : public CNumerics { * \param[in] jVar - Index j. * \return 1 if i=j, 0 otherwise. */ - inline static su2double deltaij(unsigned short iVar, unsigned short jVar) { - return su2double(iVar==jVar); + inline static passivedouble deltaij(unsigned short iVar, unsigned short jVar) { + return static_cast(iVar == jVar); } }; diff --git a/SU2_CFD/include/numerics/elasticity/CFEANonlinearElasticity.hpp b/SU2_CFD/include/numerics/elasticity/CFEANonlinearElasticity.hpp index 0e57f54e529..49ba78c09a2 100644 --- a/SU2_CFD/include/numerics/elasticity/CFEANonlinearElasticity.hpp +++ b/SU2_CFD/include/numerics/elasticity/CFEANonlinearElasticity.hpp @@ -141,8 +141,9 @@ class CFEANonlinearElasticity : public CFEAElasticity { * \brief Compute the stress tensor. * \param[in,out] element_container - The finite element. * \param[in] config - Definition of the problem. + * \param[in] iGauss - Index of Gaussian integration point. */ - virtual void Compute_Stress_Tensor(CElement *element_container, const CConfig *config) = 0; + virtual void Compute_Stress_Tensor(CElement *element_container, const CConfig *config, unsigned short iGauss) = 0; /*! * \brief Update an element with Maxwell's stress. diff --git a/SU2_CFD/include/numerics/elasticity/nonlinear_models.hpp b/SU2_CFD/include/numerics/elasticity/nonlinear_models.hpp index a36eee0a67d..dbd7b4db422 100644 --- a/SU2_CFD/include/numerics/elasticity/nonlinear_models.hpp +++ b/SU2_CFD/include/numerics/elasticity/nonlinear_models.hpp @@ -73,7 +73,7 @@ class CFEM_NeoHookean_Comp final : public CFEANonlinearElasticity { * \param[in,out] element_container - The finite element. * \param[in] config - Definition of the problem. */ - void Compute_Stress_Tensor(CElement *element_container, const CConfig *config) override; + void Compute_Stress_Tensor(CElement *element_container, const CConfig *config, unsigned short iGauss) override; }; @@ -124,7 +124,7 @@ class CFEM_Knowles_NearInc final : public CFEANonlinearElasticity { * \param[in,out] element_container - The finite element. * \param[in] config - Definition of the problem. */ - void Compute_Stress_Tensor(CElement *element_container, const CConfig *config) override; + void Compute_Stress_Tensor(CElement *element_container, const CConfig *config, unsigned short iGauss) override; }; @@ -172,7 +172,7 @@ class CFEM_DielectricElastomer final : public CFEANonlinearElasticity { * \param[in,out] element_container - The finite element. * \param[in] config - Definition of the problem. */ - void Compute_Stress_Tensor(CElement *element_container, const CConfig *config) override; + void Compute_Stress_Tensor(CElement *element_container, const CConfig *config, unsigned short iGauss) override; }; @@ -222,6 +222,6 @@ class CFEM_IdealDE final : public CFEANonlinearElasticity { * \param[in,out] element_container - The finite element. * \param[in] config - Definition of the problem. */ - void Compute_Stress_Tensor(CElement *element_container, const CConfig *config) override; + void Compute_Stress_Tensor(CElement *element_container, const CConfig *config, unsigned short iGauss) override; }; diff --git a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp index 6ad99b0f2fe..21d698f914d 100644 --- a/SU2_CFD/include/numerics/turbulent/turb_sources.hpp +++ b/SU2_CFD/include/numerics/turbulent/turb_sources.hpp @@ -360,18 +360,19 @@ struct Bsl { /*--- Limiting of \hat{S} based on "Modifications and Clarifications for the Implementation of the Spalart-Allmaras Turbulence Model" * Note 1 option c in https://turbmodels.larc.nasa.gov/spalart.html ---*/ + const su2double d_Sbar = (var.fv2 + nue * var.d_fv2) * var.inv_k2_d2; if (Sbar >= - c2 * var.Omega) { var.Shat = var.Omega + Sbar; + var.d_Shat = d_Sbar; } else { const su2double Num = var.Omega * (c2 * c2 * var.Omega + c3 * Sbar); const su2double Den = (c3 - 2 * c2) * var.Omega - Sbar; var.Shat = var.Omega + Num / Den; + var.d_Shat = d_Sbar * (c3 * var.Omega + Num / Den) / Den; } if (var.Shat <= 1e-10) { var.Shat = 1e-10; var.d_Shat = 0.0; - } else { - var.d_Shat = (var.fv2 + nue * var.d_fv2) * var.inv_k2_d2; } } }; diff --git a/SU2_CFD/include/output/COutput.hpp b/SU2_CFD/include/output/COutput.hpp index a549a6c5541..14b0bbec9b6 100644 --- a/SU2_CFD/include/output/COutput.hpp +++ b/SU2_CFD/include/output/COutput.hpp @@ -241,15 +241,16 @@ class COutput { /*----------------------------- Volume output ----------------------------*/ - CParallelDataSorter* volumeDataSorter; //!< Volume data sorter - CParallelDataSorter* surfaceDataSorter; //!< Surface data sorter + CParallelDataSorter* volumeDataSorter; //!< Volume data sorter. + CParallelDataSorter* volumeDataSorterCompact; //!< Volume data sorter for compact files. + CParallelDataSorter* surfaceDataSorter; //!< Surface data sorter. - vector volumeFieldNames; //!< Vector containing the volume field names - unsigned short nVolumeFields; //!< Number of fields in the volume output + vector volumeFieldNames; //!< Vector containing the volume field names. + vector requiredVolumeFieldNames; //!< Vector containing the minimum required volume field names. - string volumeFilename, //!< Volume output filename - surfaceFilename, //!< Surface output filename - restartFilename; //!< Restart output filename + string volumeFilename, //!< Volume output filename. + surfaceFilename, //!< Surface output filename. + restartFilename; //!< Restart output filename. /** \brief Structure to store information for a volume output field. * @@ -259,40 +260,48 @@ class COutput { /*! \brief The name of the field, i.e. the name that is printed in the file header.*/ string fieldName; /*! \brief This value identifies the position of the values of this field at each node in the ::Local_Data array. */ - short offset; + short offset = -1; + /*! \brief This offset is used for the compact formulation. */ + short offsetCompact = -1; /*! \brief The group this field belongs to. */ string outputGroup; - /*! \brief String containing the description of the field */ + /*! \brief String containing the description of the field. */ string description; /*! \brief Default constructor. */ - VolumeOutputField () {} + VolumeOutputField() = default; /*! \brief Constructor to initialize all members. */ - VolumeOutputField(string fieldName_, int offset_, string volumeOutputGroup_, string description_): - fieldName(std::move(fieldName_)), offset(std::move(offset_)), - outputGroup(std::move(volumeOutputGroup_)), description(std::move(description_)){} + VolumeOutputField(string fieldName_, string volumeOutputGroup_, string description_): + fieldName(std::move(fieldName_)), + outputGroup(std::move(volumeOutputGroup_)), + description(std::move(description_)) {} }; /*! \brief Associative map to access data stored in the volume output fields by a string identifier. */ - std::map volumeOutput_Map; + std::map volumeOutput_Map; /*! \brief Vector that contains the keys of the ::volumeOutput_Map in the order of their insertion. */ - std::vector volumeOutput_List; - - /*! \brief Vector to cache the positions of the field in the data array */ - std::vector fieldIndexCache; - /*! \brief Current value of the cache index */ - unsigned short cachePosition; - /*! \brief Boolean to store whether the field index cache should be build. */ - bool buildFieldIndexCache; - /*! \brief Vector to cache the positions of the field in the data array */ - std::vector fieldGetIndexCache; - /*! \brief Current value of the cache index */ - unsigned short curGetFieldIndex; + std::vector volumeOutput_List; + + /*! \brief Whether the field index caches should be build. */ + bool buildFieldIndexCache; + + /*! \brief Vectors to cache the positions of the fields in the data array. */ + std::vector fieldIndexCache, fieldIndexCacheCompact; + /*! \brief Current value of the cache indices. */ + unsigned short cachePosition; + + /*! \brief Vector to cache the positions of the field in the data array. */ + std::vector fieldGetIndexCache; + /*! \brief Current value of the cache index. */ + unsigned short curGetFieldIndex; /*! \brief Requested volume field names in the config file. */ std::vector requestedVolumeFields; /*! \brief Number of requested volume field names in the config file. */ unsigned short nRequestedVolumeFields; + /*! \brief Minimum required volume fields for restart file. */ + const std::vector restartVolumeFields = {"COORDINATES", "SOLUTION", "SENSITIVITY", "GRID_VELOCITY"}; + /*----------------------------- Convergence monitoring ----------------------------*/ su2double cauchyValue, /*!< \brief Summed value of the convergence indicator. */ @@ -736,8 +745,9 @@ class COutput { * \param[in] groupname - The name of the group this field belongs to. * \param[in] description - Description of the volume field. */ - inline void AddVolumeOutput(string name, string field_name, string groupname, string description){ - volumeOutput_Map[name] = VolumeOutputField(field_name, -1, groupname, description); + inline void AddVolumeOutput(const string& name, const string& field_name, + const string& group_name, const string& description) { + volumeOutput_Map[name] = VolumeOutputField(field_name, group_name, description); volumeOutput_List.push_back(name); } @@ -959,14 +969,14 @@ class COutput { /*! * \brief Sets the turboperformance screen output - * \param[in] TurboPerf - Turboperformance class + * \param[in] TurboPerf - Turboperformance class * \param[in] config - Definition of the particular problem * \param[in] TimeIter - Index of the current time-step * \param[in] OuterIter - Index of current outer iteration * \param[in] InnerIter - Index of current inner iteration */ inline virtual void SetTurboPerformance_Output(std::shared_ptr TurboPerf, CConfig *config, unsigned long TimeIter, unsigned long OuterIter, unsigned long InnerIter) {} - + /*! * \brief Sets the multizone turboperformacne screen output * \param[in] TurboStagePerf - Stage turboperformance class @@ -982,7 +992,7 @@ class COutput { * \param[in] config - Definition of the particular problem */ inline virtual void LoadTurboHistoryData(std::shared_ptr TurboStagePerf, std::shared_ptr TurboPerf, CConfig *config) {} - + /*! * \brief Write the kinematic and thermodynamic variables at each spanwise division * \param[in] solver - The container hold all solution data diff --git a/SU2_CFD/include/output/filewriter/CParallelDataSorter.hpp b/SU2_CFD/include/output/filewriter/CParallelDataSorter.hpp index da2f6a1f3bf..4882165a770 100644 --- a/SU2_CFD/include/output/filewriter/CParallelDataSorter.hpp +++ b/SU2_CFD/include/output/filewriter/CParallelDataSorter.hpp @@ -108,7 +108,8 @@ class CParallelDataSorter{ int nSends, //!< Number of sends nRecvs; //!< Number of receives - vector fieldNames; //!< Vector with names of the output fields + vector fieldNames; //!< Vector with names of all the output fields + vector requiredFieldNames; //!< Vector with names of the required output fields that we write to file unsigned short nDim; //!< Spatial dimension of the data @@ -340,6 +341,22 @@ class CParallelDataSorter{ return fieldNames; } + /*! + * \brief Get the vector containing the names of the required output fields + * \return Vector of strings containing the required field names + */ + const vector& GetRequiredFieldNames() const{ + return requiredFieldNames; + } + + /*! + * \brief Set the vector of required output fields. + * \return None. + */ + void SetRequiredFieldNames(const vector& req_field_names) { + requiredFieldNames = req_field_names; + } + /*! * \brief Get the spatial dimension * \return The spatial dimension diff --git a/SU2_CFD/include/solvers/CFEASolver.hpp b/SU2_CFD/include/solvers/CFEASolver.hpp index 4d8bf74be8e..9442fa190d7 100644 --- a/SU2_CFD/include/solvers/CFEASolver.hpp +++ b/SU2_CFD/include/solvers/CFEASolver.hpp @@ -98,6 +98,7 @@ class CFEASolver : public CFEASolverBase { bool element_based; /*!< \brief Bool to determine if an element-based file is used. */ bool topol_filter_applied; /*!< \brief True if density filtering has been performed. */ bool initial_calc = true; /*!< \brief Becomes false after first call to Preprocessing. */ + bool body_forces = false; /*!< \brief Whether any body force is active. */ /*! * \brief The highest level in the variable hierarchy this solver can safely use, @@ -335,14 +336,14 @@ class CFEASolver : public CFEASolverBase { const CConfig *config); /*! - * \brief Compute the dead loads. + * \brief Compute the inertial loads. * \param[in] geometry - Geometrical definition of the problem. * \param[in] numerics - Description of the numerical method. * \param[in] config - Definition of the particular problem. */ - void Compute_DeadLoad(CGeometry *geometry, - CNumerics **numerics, - const CConfig *config) final; + void Compute_BodyForces(CGeometry *geometry, + CNumerics **numerics, + const CConfig *config) final; /*! * \brief Clamped boundary conditions. diff --git a/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl b/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl index 4797293529f..d7a634e8227 100644 --- a/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl +++ b/SU2_CFD/include/solvers/CFVMFlowSolverBase.inl @@ -2357,12 +2357,11 @@ void CFVMFlowSolverBase::Friction_Forces(const CGeometry* geometr const su2double *Coord = nullptr, *Coord_Normal = nullptr, *Normal = nullptr; const su2double minYPlus = config->GetwallModel_MinYPlus(); - string Marker_Tag, Monitoring_Tag; - const su2double Alpha = config->GetAoA() * PI_NUMBER / 180.0; const su2double Beta = config->GetAoS() * PI_NUMBER / 180.0; const su2double RefLength = config->GetRefLength(); const su2double RefHeatFlux = config->GetHeat_Flux_Ref(); + const su2double RefTemperature = config->GetTemperature_Ref(); const su2double Gas_Constant = config->GetGas_ConstantND(); auto Origin = config->GetRefOriginMoment(0); @@ -2393,15 +2392,17 @@ void CFVMFlowSolverBase::Friction_Forces(const CGeometry* geometr for (iMarker = 0; iMarker < nMarker; iMarker++) { - Marker_Tag = config->GetMarker_All_TagBound(iMarker); if (!config->GetViscous_Wall(iMarker)) continue; + const auto Marker_Tag = config->GetMarker_All_TagBound(iMarker); + + const bool py_custom = config->GetMarker_All_PyCustom(iMarker); /*--- Obtain the origin for the moment computation for a particular marker ---*/ const auto Monitoring = config->GetMarker_All_Monitoring(iMarker); if (Monitoring == YES) { for (iMarker_Monitoring = 0; iMarker_Monitoring < config->GetnMarker_Monitoring(); iMarker_Monitoring++) { - Monitoring_Tag = config->GetMarker_Monitoring_TagBound(iMarker_Monitoring); + const auto Monitoring_Tag = config->GetMarker_Monitoring_TagBound(iMarker_Monitoring); if (Marker_Tag == Monitoring_Tag) Origin = config->GetRefOriginMoment(iMarker_Monitoring); } } @@ -2506,26 +2507,47 @@ void CFVMFlowSolverBase::Friction_Forces(const CGeometry* geometr /*--- Compute total and maximum heat flux on the wall ---*/ - su2double dTdn = -GeometryToolbox::DotProduct(nDim, Grad_Temp, UnitNormal); - - if (!nemo){ - + if (!nemo) { if (FlowRegime == ENUM_REGIME::COMPRESSIBLE) { - Cp = (Gamma / Gamma_Minus_One) * Gas_Constant; thermal_conductivity = Cp * Viscosity / Prandtl_Lam; } if (FlowRegime == ENUM_REGIME::INCOMPRESSIBLE) { - if (!energy) dTdn = 0.0; thermal_conductivity = nodes->GetThermalConductivity(iPoint); } - HeatFlux[iMarker][iVertex] = -thermal_conductivity * dTdn * RefHeatFlux; + if (config->GetMarker_All_KindBC(iMarker) == BC_TYPE::HEAT_FLUX) { + if (py_custom) { + HeatFlux[iMarker][iVertex] = -geometry->GetCustomBoundaryHeatFlux(iMarker, iVertex); + } else { + HeatFlux[iMarker][iVertex] = -config->GetWall_HeatFlux(Marker_Tag); + if (config->GetIntegrated_HeatFlux()) { + HeatFlux[iMarker][iVertex] /= geometry->GetSurfaceArea(config, iMarker); + } + } + } else if (config->GetMarker_All_KindBC(iMarker) == BC_TYPE::ISOTHERMAL) { + su2double Twall = 0.0; + if (py_custom) { + Twall = geometry->GetCustomBoundaryTemperature(iMarker, iVertex) / RefTemperature; + } else { + Twall = config->GetIsothermal_Temperature(Marker_Tag) / RefTemperature; + } + iPointNormal = geometry->vertex[iMarker][iVertex]->GetNormal_Neighbor(); + Coord_Normal = geometry->nodes->GetCoord(iPointNormal); + const su2double dist_ij = GeometryToolbox::NormalDistance(nDim, UnitNormal, Coord, Coord_Normal); + const su2double There = nodes->GetTemperature(iPointNormal); + HeatFlux[iMarker][iVertex] = thermal_conductivity * (There - Twall) / dist_ij * RefHeatFlux; + } else { + su2double dTdn = GeometryToolbox::DotProduct(nDim, Grad_Temp, UnitNormal); + if (FlowRegime == ENUM_REGIME::INCOMPRESSIBLE && !energy) dTdn = 0.0; + HeatFlux[iMarker][iVertex] = thermal_conductivity * dTdn * RefHeatFlux; + } } else { const auto& thermal_conductivity_tr = nodes->GetThermalConductivity(iPoint); const auto& thermal_conductivity_ve = nodes->GetThermalConductivity_ve(iPoint); + const su2double dTdn = -GeometryToolbox::DotProduct(nDim, Grad_Temp, UnitNormal); const su2double dTvedn = -GeometryToolbox::DotProduct(nDim, Grad_Temp_ve, UnitNormal); /*--- Surface energy balance: trans-rot heat flux, vib-el heat flux ---*/ @@ -2653,8 +2675,7 @@ void CFVMFlowSolverBase::Friction_Forces(const CGeometry* geometr /*--- Compute the coefficients per surface ---*/ for (iMarker_Monitoring = 0; iMarker_Monitoring < config->GetnMarker_Monitoring(); iMarker_Monitoring++) { - Monitoring_Tag = config->GetMarker_Monitoring_TagBound(iMarker_Monitoring); - Marker_Tag = config->GetMarker_All_TagBound(iMarker); + const auto Monitoring_Tag = config->GetMarker_Monitoring_TagBound(iMarker_Monitoring); if (Marker_Tag == Monitoring_Tag) { SurfaceViscCoeff.CL[iMarker_Monitoring] += ViscCoeff.CL[iMarker]; SurfaceViscCoeff.CD[iMarker_Monitoring] += ViscCoeff.CD[iMarker]; diff --git a/SU2_CFD/include/solvers/CSolver.hpp b/SU2_CFD/include/solvers/CSolver.hpp index d968d05c6e3..3410360be8a 100644 --- a/SU2_CFD/include/solvers/CSolver.hpp +++ b/SU2_CFD/include/solvers/CSolver.hpp @@ -3663,9 +3663,9 @@ class CSolver { * \param[in] numerics - Description of the numerical method. * \param[in] config - Definition of the particular problem. */ - inline virtual void Compute_DeadLoad(CGeometry *geometry, - CNumerics **numerics, - const CConfig *config) { } + inline virtual void Compute_BodyForces(CGeometry *geometry, + CNumerics **numerics, + const CConfig *config) { } /*! * \brief A virtual member. Set the volumetric heat source diff --git a/SU2_CFD/src/numerics/elasticity/CFEAElasticity.cpp b/SU2_CFD/src/numerics/elasticity/CFEAElasticity.cpp index 44014591204..efe0e19a0b2 100644 --- a/SU2_CFD/src/numerics/elasticity/CFEAElasticity.cpp +++ b/SU2_CFD/src/numerics/elasticity/CFEAElasticity.cpp @@ -27,6 +27,7 @@ #include "../../../include/numerics/elasticity/CFEAElasticity.hpp" #include "../../../../Common/include/parallelization/omp_structure.hpp" +#include "../../../../Common/include/toolboxes/geometry_toolbox.hpp" CFEAElasticity::CFEAElasticity(unsigned short val_nDim, unsigned short val_nVar, @@ -35,7 +36,6 @@ CFEAElasticity::CFEAElasticity(unsigned short val_nDim, unsigned short val_nVar, nDim = val_nDim; nVar = val_nVar; - bool body_forces = config->GetDeadLoad(); // Body forces (dead loads). bool pseudo_static = config->GetPseudoStatic(); unsigned short iVar; @@ -44,19 +44,16 @@ CFEAElasticity::CFEAElasticity(unsigned short val_nDim, unsigned short val_nVar, const auto nProp = config->GetnElasticityMat(); E_i = new su2double[nProp]; - for (iVar = 0; iVar < nProp; iVar++) - E_i[iVar] = config->GetElasticyMod(iVar); - Nu_i = new su2double[nProp]; - for (iVar = 0; iVar < nProp; iVar++) - Nu_i[iVar] = config->GetPoissonRatio(iVar); - Rho_s_i = new su2double[nProp]; // For inertial effects Rho_s_DL_i = new su2double[nProp]; // For dead loads - + Alpha_i = new su2double[nProp]; for (iVar = 0; iVar < nProp; iVar++) { + E_i[iVar] = config->GetElasticyMod(iVar); + Nu_i[iVar] = config->GetPoissonRatio(iVar); Rho_s_DL_i[iVar] = config->GetMaterialDensity(iVar); Rho_s_i[iVar] = pseudo_static ? 0.0 : config->GetMaterialDensity(iVar); + Alpha_i[iVar] = config->GetMaterialThermalExpansion(iVar); } // Initialization @@ -64,14 +61,12 @@ CFEAElasticity::CFEAElasticity(unsigned short val_nDim, unsigned short val_nVar, Nu = Nu_i[0]; Rho_s = Rho_s_i[0]; Rho_s_DL = Rho_s_DL_i[0]; + Alpha = Alpha_i[0]; + ReferenceTemperature = config->GetMaterialReferenceTemperature(); Compute_Lame_Parameters(); - // Auxiliary vector for body forces (dead load) - FAux_Dead_Load = nullptr; - if (body_forces) FAux_Dead_Load = new su2double [nDim]; - - plane_stress = (config->GetElas2D_Formulation() == STRUCT_2DFORM::PLANE_STRESS); + plane_stress = (nDim == 2) && (config->GetElas2D_Formulation() == STRUCT_2DFORM::PLANE_STRESS); KAux_ab = new su2double* [nDim]; for (iVar = 0; iVar < nDim; iVar++) { @@ -164,12 +159,11 @@ CFEAElasticity::~CFEAElasticity() { delete[] DV_Val; - delete [] FAux_Dead_Load; - delete [] E_i; delete [] Nu_i; delete [] Rho_s_i; delete [] Rho_s_DL_i; + delete [] Alpha_i; delete [] Ni_Vec; } @@ -233,7 +227,11 @@ void CFEAElasticity::Compute_Mass_Matrix(CElement *element, const CConfig *confi } -void CFEAElasticity::Compute_Dead_Load(CElement *element, const CConfig *config) { +void CFEAElasticity::Compute_Body_Forces(CElement *element, const CConfig *config) { + + const bool gravity = config->GetGravityForce(); + const bool body_force = config->GetBody_Force(); + const bool centrifugal = config->GetCentrifugalForce(); /*--- Initialize values for the material model considered ---*/ SetElement_Properties(element, config); @@ -244,45 +242,56 @@ void CFEAElasticity::Compute_Dead_Load(CElement *element, const CConfig *config) AD::SetPreaccIn(Rho_s_DL); element->SetPreaccIn_Coords(false); - unsigned short iGauss, nGauss; - unsigned short iNode, iDim, nNode; - - su2double Weight, Jac_X; - - /* -- Gravity directionality: - * -- For 2D problems, we assume the direction for gravity is -y - * -- For 3D problems, we assume the direction for gravity is -z - */ - su2double g_force[3] = {0.0,0.0,0.0}; - - if (nDim == 2) g_force[1] = -1*STANDARD_GRAVITY; - else if (nDim == 3) g_force[2] = -1*STANDARD_GRAVITY; + std::array acceleration{}; + if (gravity) { + /*--- For 2D problems, we assume gravity is in the -y direction, + * and for 3D problems in the -z direction. ---*/ + acceleration[nDim - 1] = -STANDARD_GRAVITY; + } else if (body_force) { + for (auto iDim = 0u; iDim < nDim; iDim++) { + acceleration[iDim] = config->GetBody_Force_Vector()[iDim]; + } + } + std::array center{}, omega{}; + if (centrifugal) { + for (auto iDim = 0u; iDim < nDim; iDim++) { + center[iDim] = config->GetMotion_Origin(iDim); + omega[iDim] = config->GetRotation_Rate(iDim); + } + } element->ClearElement(); /*--- Restart the element to avoid adding over previous results. --*/ element->ComputeGrad_Linear(); /*--- Need to compute the gradients to obtain the Jacobian. ---*/ - nNode = element->GetnNodes(); - nGauss = element->GetnGaussPoints(); - - for (iGauss = 0; iGauss < nGauss; iGauss++) { + const auto nNode = element->GetnNodes(); + const auto nGauss = element->GetnGaussPoints(); - Weight = element->GetWeight(iGauss); - Jac_X = element->GetJ_X(iGauss); /*--- The dead load is computed in the reference configuration ---*/ + for (auto iGauss = 0u; iGauss < nGauss; iGauss++) { - /*--- Retrieve the values of the shape functions for each node ---*/ - /*--- This avoids repeated operations ---*/ - for (iNode = 0; iNode < nNode; iNode++) { - Ni_Vec[iNode] = element->GetNi(iNode,iGauss); - } + const auto Weight = element->GetWeight(iGauss); + /*--- The dead load is computed in the reference configuration ---*/ + const auto Jac_X = element->GetJ_X(iGauss); - for (iNode = 0; iNode < nNode; iNode++) { + for (auto iNode = 0u; iNode < nNode; iNode++) { + const auto Ni = element->GetNi(iNode,iGauss); - for (iDim = 0; iDim < nDim; iDim++) { - FAux_Dead_Load[iDim] = Weight * Ni_Vec[iNode] * Jac_X * Rho_s_DL * g_force[iDim]; + auto total_accel = acceleration; + if (centrifugal) { + /*--- For nonlinear this should probably use the current (deformed) + * coordinates, but it should not make a big difference. ---*/ + std::array r{}, wr{}, w2r{}; + for (auto iDim = 0u; iDim < nDim; iDim++) { + r[iDim] = element->GetRef_Coord(iNode, iDim) - center[iDim]; + } + GeometryToolbox::CrossProduct(omega.data(), r.data(), wr.data()); + GeometryToolbox::CrossProduct(omega.data(), wr.data(), w2r.data()); + for (auto iDim = 0; iDim < 3; ++iDim) total_accel[iDim] -= w2r[iDim]; } - - element->Add_FDL_a(iNode, FAux_Dead_Load); - + std::array force{}; + for (auto iDim = 0u; iDim < nDim; iDim++) { + force[iDim] = Weight * Ni * Jac_X * Rho_s_DL * total_accel[iDim]; + } + element->Add_FDL_a(iNode, force.data()); } } @@ -302,6 +311,7 @@ void CFEAElasticity::SetElement_Properties(const CElement *element, const CConfi Nu = Nu_i[element->Get_iProp()]; Rho_s = Rho_s_i[element->Get_iProp()]; Rho_s_DL = Rho_s_DL_i[element->Get_iProp()]; + Alpha = Alpha_i[element->Get_iProp()]; switch (config->GetDV_FEA()) { case YOUNG_MODULUS: diff --git a/SU2_CFD/src/numerics/elasticity/CFEALinearElasticity.cpp b/SU2_CFD/src/numerics/elasticity/CFEALinearElasticity.cpp index a6763bed0b8..8e15ed0cf6b 100644 --- a/SU2_CFD/src/numerics/elasticity/CFEALinearElasticity.cpp +++ b/SU2_CFD/src/numerics/elasticity/CFEALinearElasticity.cpp @@ -97,8 +97,16 @@ void CFEALinearElasticity::Compute_Tangent_Matrix(CElement *element, const CConf } } + const su2double thermalStress = ThermalStressTerm * (element->GetTemperature(iGauss) - ReferenceTemperature); + for (iNode = 0; iNode < nNode; iNode++) { + su2double KAux_t_a[3] = {0.0}; + for (iVar = 0; iVar < nDim; iVar++) { + KAux_t_a[iVar] += Weight * thermalStress * GradNi_Ref_Mat[iNode][iVar] * Jac_X; + } + element->Add_Kt_a(iNode, KAux_t_a); + if (nDim == 2) { Ba_Mat[0][0] = GradNi_Ref_Mat[iNode][0]; Ba_Mat[1][1] = GradNi_Ref_Mat[iNode][1]; @@ -314,14 +322,17 @@ su2double CFEALinearElasticity::Compute_Averaged_NodalStress(CElement *element, } - /*--- Compute the Stress Vector as D*epsilon ---*/ + /*--- Compute the Stress Vector as D*epsilon + thermal stress ---*/ su2double Stress[DIM_STRAIN_3D] = {0.0}; + const su2double thermalStress = ThermalStressTerm * (element->GetTemperature(iGauss) - ReferenceTemperature); + for (iVar = 0; iVar < bDim; iVar++) { for (jVar = 0; jVar < bDim; jVar++) { Stress[iVar] += D_Mat[iVar][jVar]*Strain[jVar]; } + if (iVar < nDim) Stress[iVar] += thermalStress; avgStress[iVar] += Stress[iVar] / nGauss; } @@ -363,10 +374,10 @@ CFEAMeshElasticity::CFEAMeshElasticity(unsigned short val_nDim, unsigned short v unsigned long val_nElem, const CConfig *config) : CFEALinearElasticity() { DV_Val = nullptr; - FAux_Dead_Load = nullptr; Rho_s_i = nullptr; Rho_s_DL_i = nullptr; Nu_i = nullptr; + Alpha_i = nullptr; nDim = val_nDim; nVar = val_nVar; diff --git a/SU2_CFD/src/numerics/elasticity/CFEANonlinearElasticity.cpp b/SU2_CFD/src/numerics/elasticity/CFEANonlinearElasticity.cpp index 0b8ed7cb855..8f74c164dd6 100644 --- a/SU2_CFD/src/numerics/elasticity/CFEANonlinearElasticity.cpp +++ b/SU2_CFD/src/numerics/elasticity/CFEANonlinearElasticity.cpp @@ -349,7 +349,7 @@ void CFEANonlinearElasticity::Compute_Tangent_Matrix(CElement *element, const CC /*--- Compute the constitutive matrix ---*/ - Compute_Stress_Tensor(element, config); + Compute_Stress_Tensor(element, config, iGauss); // if (maxwell_stress) Add_MaxwellStress(element, config); Compute_Constitutive_Matrix(element, config); @@ -571,7 +571,7 @@ void CFEANonlinearElasticity::Compute_NodalStress_Term(CElement *element, const /*--- Compute the stress tensor ---*/ - Compute_Stress_Tensor(element, config); + Compute_Stress_Tensor(element, config, iGauss); // if (maxwell_stress) Add_MaxwellStress(element, config); for (iNode = 0; iNode < nNode; iNode++) { @@ -850,7 +850,7 @@ su2double CFEANonlinearElasticity::Compute_Averaged_NodalStress(CElement *elemen /*--- Compute the stress tensor ---*/ - Compute_Stress_Tensor(element, config); + Compute_Stress_Tensor(element, config, iGauss); if (maxwell_stress) Add_MaxwellStress(element, config); avgStress[0] += Stress_Tensor[0][0] / nGauss; diff --git a/SU2_CFD/src/numerics/elasticity/nonlinear_models.cpp b/SU2_CFD/src/numerics/elasticity/nonlinear_models.cpp index d21fc228300..4b787db3416 100644 --- a/SU2_CFD/src/numerics/elasticity/nonlinear_models.cpp +++ b/SU2_CFD/src/numerics/elasticity/nonlinear_models.cpp @@ -98,7 +98,7 @@ void CFEM_NeoHookean_Comp::Compute_Constitutive_Matrix(CElement *element, const } -void CFEM_NeoHookean_Comp::Compute_Stress_Tensor(CElement *element, const CConfig *config) { +void CFEM_NeoHookean_Comp::Compute_Stress_Tensor(CElement *element, const CConfig *config, unsigned short iGauss) { unsigned short iVar,jVar; su2double Mu_J = 0.0, Lambda_J = 0.0; @@ -109,10 +109,12 @@ void CFEM_NeoHookean_Comp::Compute_Stress_Tensor(CElement *element, const CConfi Lambda_J = Lambda/J_F; } + const su2double thermalStress = ThermalStressTerm * (element->GetTemperature(iGauss) - ReferenceTemperature); + for (iVar = 0; iVar < 3; iVar++) { for (jVar = 0; jVar < 3; jVar++) { su2double dij = deltaij(iVar,jVar); - Stress_Tensor[iVar][jVar] = Mu_J * (b_Mat[iVar][jVar] - dij) + Lambda_J * log(J_F) * dij; + Stress_Tensor[iVar][jVar] = Mu_J * (b_Mat[iVar][jVar] - dij) + (Lambda_J * log(J_F) + thermalStress) * dij; } } @@ -182,7 +184,7 @@ void CFEM_Knowles_NearInc::Compute_Constitutive_Matrix(CElement *element, const } -void CFEM_Knowles_NearInc::Compute_Stress_Tensor(CElement *element, const CConfig *config) { +void CFEM_Knowles_NearInc::Compute_Stress_Tensor(CElement *element, const CConfig *config, unsigned short iGauss) { /* -- Suchocki (2011) (full reference in class constructor). ---*/ @@ -199,10 +201,12 @@ void CFEM_Knowles_NearInc::Compute_Stress_Tensor(CElement *element, const CConfi Ek = Kappa * (2.0 * J_F - 1.0); Pr = Kappa * (J_F - 1.0); + const su2double thermalStress = ThermalStressTerm * (element->GetTemperature(iGauss) - ReferenceTemperature); + for (iVar = 0; iVar < 3; iVar++){ for (jVar = 0; jVar < 3; jVar++){ Stress_Tensor[iVar][jVar] = term1 * (b_Mat_Iso[iVar][jVar] - deltaij(iVar,jVar)*trbbar) + - deltaij(iVar,jVar) * Pr; + deltaij(iVar,jVar) * (Pr + thermalStress); } } @@ -234,7 +238,7 @@ void CFEM_DielectricElastomer::Compute_Constitutive_Matrix(CElement *element, co } -void CFEM_DielectricElastomer::Compute_Stress_Tensor(CElement *element, const CConfig *config) { +void CFEM_DielectricElastomer::Compute_Stress_Tensor(CElement *element, const CConfig *config, unsigned short iGauss) { unsigned short iDim, jDim; @@ -315,12 +319,11 @@ void CFEM_IdealDE::Compute_Constitutive_Matrix(CElement *element, const CConfig } -void CFEM_IdealDE::Compute_Stress_Tensor(CElement *element, const CConfig *config) { +void CFEM_IdealDE::Compute_Stress_Tensor(CElement *element, const CConfig *config, unsigned short iGauss) { /* -- Zhao, X. and Suo, Z. (2008) (full reference in class constructor). ---*/ unsigned short iVar, jVar; - su2double dij = 0.0; /*--- Compute the isochoric deformation gradient Fbar and left Cauchy-Green tensor bbar ---*/ Compute_Isochoric_F_b(); @@ -333,13 +336,12 @@ void CFEM_IdealDE::Compute_Stress_Tensor(CElement *element, const CConfig *confi Pr = Kappa * (J_F - 1.0); Eg23 = 2.0 * Eg / 3.0; - // Stress tensor + const su2double thermalStress = ThermalStressTerm * (element->GetTemperature(iGauss) - ReferenceTemperature); for (iVar = 0; iVar < 3; iVar++){ - for (jVar = 0; jVar < 3; jVar++){ - if (iVar == jVar) dij = 1.0; - else if (iVar != jVar) dij = 0.0; - Stress_Tensor[iVar][jVar] = Eg * ( b_Mat_Iso[iVar][jVar] - dij * trbbar) + dij * Pr ; + for (jVar = 0; jVar < 3; jVar++) { + const su2double dij = deltaij(iVar, jVar); + Stress_Tensor[iVar][jVar] = Eg * ( b_Mat_Iso[iVar][jVar] - dij * trbbar) + dij * (Pr + thermalStress); } } diff --git a/SU2_CFD/src/output/CAdjElasticityOutput.cpp b/SU2_CFD/src/output/CAdjElasticityOutput.cpp index 7466b506d60..add084006ea 100644 --- a/SU2_CFD/src/output/CAdjElasticityOutput.cpp +++ b/SU2_CFD/src/output/CAdjElasticityOutput.cpp @@ -113,7 +113,7 @@ void CAdjElasticityOutput::SetHistoryOutputFields(CConfig *config){ if (config->GetTime_Domain() && !config->GetPseudoStatic()) { AddHistoryOutput("SENS_RHO_" + iVarS, "Sens[Rho" + iVarS + ']', ScreenOutputFormat::SCIENTIFIC, "SENSITIVITY", "d Objective / d Material density"); } - if (config->GetDeadLoad()) { + if (config->GetGravityForce() || config->GetBody_Force() || config->GetCentrifugalForce()) { AddHistoryOutput("SENS_RHO_DL_" + iVarS, "Sens[RhoDL" + iVarS + ']', ScreenOutputFormat::SCIENTIFIC, "SENSITIVITY", "d Objective / d Dead load density"); } } @@ -151,7 +151,7 @@ inline void CAdjElasticityOutput::LoadHistoryData(CConfig *config, CGeometry *ge if (config->GetTime_Domain() && !config->GetPseudoStatic()) { SetHistoryOutputValue("SENS_RHO_" + iVarS, solver[ADJFEA_SOL]->GetTotal_Sens_Rho(iVar)); } - if (config->GetDeadLoad()) { + if (config->GetGravityForce() || config->GetBody_Force() || config->GetCentrifugalForce()) { SetHistoryOutputValue("SENS_RHO_DL_" + iVarS, solver[ADJFEA_SOL]->GetTotal_Sens_Rho_DL(iVar)); } } diff --git a/SU2_CFD/src/output/CAdjFlowCompOutput.cpp b/SU2_CFD/src/output/CAdjFlowCompOutput.cpp index 108856156e7..f1e492e1a37 100644 --- a/SU2_CFD/src/output/CAdjFlowCompOutput.cpp +++ b/SU2_CFD/src/output/CAdjFlowCompOutput.cpp @@ -61,7 +61,7 @@ CAdjFlowCompOutput::CAdjFlowCompOutput(CConfig *config, unsigned short nDim) : C if (find(requestedVolumeFields.begin(), requestedVolumeFields.end(), string("SENSITIVITY")) == requestedVolumeFields.end()) { requestedVolumeFields.emplace_back("SENSITIVITY"); - nRequestedVolumeFields ++; + nRequestedVolumeFields++; } stringstream ss; diff --git a/SU2_CFD/src/output/CAdjFlowIncOutput.cpp b/SU2_CFD/src/output/CAdjFlowIncOutput.cpp index be86c12a494..3fff6de99e5 100644 --- a/SU2_CFD/src/output/CAdjFlowIncOutput.cpp +++ b/SU2_CFD/src/output/CAdjFlowIncOutput.cpp @@ -67,7 +67,7 @@ CAdjFlowIncOutput::CAdjFlowIncOutput(CConfig *config, unsigned short nDim) : CAd if (find(requestedVolumeFields.begin(), requestedVolumeFields.end(), string("SENSITIVITY")) == requestedVolumeFields.end()) { requestedVolumeFields.emplace_back("SENSITIVITY"); - nRequestedVolumeFields ++; + nRequestedVolumeFields++; } stringstream ss; diff --git a/SU2_CFD/src/output/CAdjHeatOutput.cpp b/SU2_CFD/src/output/CAdjHeatOutput.cpp index 578175fccad..9c8a85e2f5d 100644 --- a/SU2_CFD/src/output/CAdjHeatOutput.cpp +++ b/SU2_CFD/src/output/CAdjHeatOutput.cpp @@ -58,7 +58,7 @@ CAdjHeatOutput::CAdjHeatOutput(CConfig *config, unsigned short nDim) : COutput(c if (find(requestedVolumeFields.begin(), requestedVolumeFields.end(), string("SENSITIVITY")) == requestedVolumeFields.end()) { requestedVolumeFields.emplace_back("SENSITIVITY"); - nRequestedVolumeFields ++; + nRequestedVolumeFields++; } stringstream ss; diff --git a/SU2_CFD/src/output/CElasticityOutput.cpp b/SU2_CFD/src/output/CElasticityOutput.cpp index a2abc4e6d12..1af62426cce 100644 --- a/SU2_CFD/src/output/CElasticityOutput.cpp +++ b/SU2_CFD/src/output/CElasticityOutput.cpp @@ -235,21 +235,20 @@ void CElasticityOutput::SetVolumeOutputFields(CConfig *config){ // Grid coordinates AddVolumeOutput("COORD-X", "x", "COORDINATES", "x-component of the coordinate vector"); AddVolumeOutput("COORD-Y", "y", "COORDINATES", "y-component of the coordinate vector"); - if (nDim == 3) - AddVolumeOutput("COORD-Z", "z", "COORDINATES", "z-component of the coordinate vector"); + if (nDim == 3) AddVolumeOutput("COORD-Z", "z", "COORDINATES", "z-component of the coordinate vector"); AddVolumeOutput("DISPLACEMENT-X", "Displacement_x", "SOLUTION", "x-component of the displacement vector"); AddVolumeOutput("DISPLACEMENT-Y", "Displacement_y", "SOLUTION", "y-component of the displacement vector"); if (nDim == 3) AddVolumeOutput("DISPLACEMENT-Z", "Displacement_z", "SOLUTION", "z-component of the displacement vector"); - if(dynamic){ - AddVolumeOutput("VELOCITY-X", "Velocity_x", "VELOCITY", "x-component of the velocity vector"); - AddVolumeOutput("VELOCITY-Y", "Velocity_y", "VELOCITY", "y-component of the velocity vector"); - if (nDim == 3) AddVolumeOutput("VELOCITY-Z", "Velocity_z", "VELOCITY", "z-component of the velocity vector"); + if (dynamic) { + AddVolumeOutput("VELOCITY-X", "Velocity_x", "SOLUTION", "x-component of the velocity vector"); + AddVolumeOutput("VELOCITY-Y", "Velocity_y", "SOLUTION", "y-component of the velocity vector"); + if (nDim == 3) AddVolumeOutput("VELOCITY-Z", "Velocity_z", "SOLUTION", "z-component of the velocity vector"); - AddVolumeOutput("ACCELERATION-X", "Acceleration_x", "ACCELERATION", "x-component of the acceleration vector"); - AddVolumeOutput("ACCELERATION-Y", "Acceleration_y", "ACCELERATION", "y-component of the acceleration vector"); - if (nDim == 3) AddVolumeOutput("ACCELERATION-Z", "Acceleration_z", "ACCELERATION", "z-component of the acceleration vector"); + AddVolumeOutput("ACCELERATION-X", "Acceleration_x", "SOLUTION", "x-component of the acceleration vector"); + AddVolumeOutput("ACCELERATION-Y", "Acceleration_y", "SOLUTION", "y-component of the acceleration vector"); + if (nDim == 3) AddVolumeOutput("ACCELERATION-Z", "Acceleration_z", "SOLUTION", "z-component of the acceleration vector"); } AddVolumeOutput("STRESS-XX", "Sxx", "STRESS", "x-component of the normal stress vector"); diff --git a/SU2_CFD/src/output/CFlowCompOutput.cpp b/SU2_CFD/src/output/CFlowCompOutput.cpp index e460109c8b5..d3c76e75a36 100644 --- a/SU2_CFD/src/output/CFlowCompOutput.cpp +++ b/SU2_CFD/src/output/CFlowCompOutput.cpp @@ -61,7 +61,7 @@ CFlowCompOutput::CFlowCompOutput(const CConfig *config, unsigned short nDim) : C auto notFound = requestedVolumeFields.end(); if (find(requestedVolumeFields.begin(), notFound, string("GRID_VELOCITY")) == notFound) { requestedVolumeFields.emplace_back("GRID_VELOCITY"); - nRequestedVolumeFields ++; + nRequestedVolumeFields++; } } @@ -219,7 +219,7 @@ void CFlowCompOutput::SetVolumeOutputFields(CConfig *config){ AddVolumeOutput("DENSITY", "Density", "SOLUTION", "Density"); AddVolumeOutput("MOMENTUM-X", "Momentum_x", "SOLUTION", "x-component of the momentum vector"); AddVolumeOutput("MOMENTUM-Y", "Momentum_y", "SOLUTION", "y-component of the momentum vector"); - + if (nDim == 3) AddVolumeOutput("MOMENTUM-Z", "Momentum_z", "SOLUTION", "z-component of the momentum vector"); AddVolumeOutput("ENERGY", "Energy", "SOLUTION", "Energy"); @@ -241,7 +241,7 @@ void CFlowCompOutput::SetVolumeOutputFields(CConfig *config){ AddVolumeOutput("PRESSURE_COEFF", "Pressure_Coefficient", "PRIMITIVE", "Pressure coefficient"); AddVolumeOutput("VELOCITY-X", "Velocity_x", "PRIMITIVE", "x-component of the velocity vector"); AddVolumeOutput("VELOCITY-Y", "Velocity_y", "PRIMITIVE", "y-component of the velocity vector"); - + if (nDim == 3) AddVolumeOutput("VELOCITY-Z", "Velocity_z", "PRIMITIVE", "z-component of the velocity vector"); @@ -525,7 +525,7 @@ void CFlowCompOutput::SetTurboPerformance_Output(std::shared_ptr T for (unsigned short iZone = 0; iZone <= config->GetnZone()-1; iZone++) { auto nSpan = config->GetnSpan_iZones(iZone); - const auto& BladePerf = BladePerformance.at(iZone).at(nSpan); + const auto& BladePerf = BladePerformance.at(iZone).at(nSpan); TurboInOut<<" BLADE ROW INDEX "<GetInletState().GetVelocity()[iDim]*config[ZONE_0]->GetVelocity_Ref(); } file.width(30); file << BladePerf->GetInletState().GetVelocityValue()*config[ZONE_0]->GetVelocity_Ref(); - // This captures NaNs + // This captures NaNs if(isnan(BladePerf->GetInletState().GetAbsFlowAngle())){ file.width(30); file << "0.0000"; } @@ -792,11 +792,11 @@ void CFlowCompOutput::WriteTurboSpanwisePerformance(std::shared_ptr 1) { - spanwise_performance_filename.append("_" + std::to_string(val_iZone) + ".dat"); - } else { - spanwise_performance_filename.append(".dat"); - } + if (nZone > 1) { + spanwise_performance_filename.append("_" + std::to_string(val_iZone) + ".dat"); + } else { + spanwise_performance_filename.append(".dat"); + } file.open (spanwise_performance_filename.data(), ios::out | ios::trunc); file.setf(ios::scientific); file.precision(12); diff --git a/SU2_CFD/src/output/CFlowIncOutput.cpp b/SU2_CFD/src/output/CFlowIncOutput.cpp index eeffb6a3486..1f4c0809d1b 100644 --- a/SU2_CFD/src/output/CFlowIncOutput.cpp +++ b/SU2_CFD/src/output/CFlowIncOutput.cpp @@ -70,7 +70,7 @@ CFlowIncOutput::CFlowIncOutput(CConfig *config, unsigned short nDim) : CFlowOutp auto notFound = requestedVolumeFields.end(); if (find(requestedVolumeFields.begin(), notFound, string("GRID_VELOCITY")) == notFound) { requestedVolumeFields.emplace_back("GRID_VELOCITY"); - nRequestedVolumeFields ++; + nRequestedVolumeFields++; } } diff --git a/SU2_CFD/src/output/CNEMOCompOutput.cpp b/SU2_CFD/src/output/CNEMOCompOutput.cpp index 7b75ac646d2..2994dd4345e 100644 --- a/SU2_CFD/src/output/CNEMOCompOutput.cpp +++ b/SU2_CFD/src/output/CNEMOCompOutput.cpp @@ -65,7 +65,7 @@ CNEMOCompOutput::CNEMOCompOutput(const CConfig *config, unsigned short nDim) : C auto notFound = requestedVolumeFields.end(); if (find(requestedVolumeFields.begin(), notFound, string("GRID_VELOCITY")) == notFound) { requestedVolumeFields.emplace_back("GRID_VELOCITY"); - nRequestedVolumeFields ++; + nRequestedVolumeFields++; } } diff --git a/SU2_CFD/src/output/COutput.cpp b/SU2_CFD/src/output/COutput.cpp index adc6f942145..631a4ba6bde 100644 --- a/SU2_CFD/src/output/COutput.cpp +++ b/SU2_CFD/src/output/COutput.cpp @@ -173,12 +173,12 @@ COutput::COutput(const CConfig *config, unsigned short ndim, bool fem_output): convergence = false; buildFieldIndexCache = false; - curInnerIter = 0; curOuterIter = 0; curTimeIter = 0; volumeDataSorter = nullptr; + volumeDataSorterCompact = nullptr; surfaceDataSorter = nullptr; headerNeeded = false; @@ -195,6 +195,7 @@ COutput::~COutput() { delete fileWritingTable; delete historyFileTable; delete volumeDataSorter; + delete volumeDataSorterCompact; delete surfaceDataSorter; } @@ -205,8 +206,7 @@ void COutput::SetHistoryOutput(CGeometry *geometry, unsigned long TimeIter, unsigned long OuterIter, unsigned long InnerIter) { - - curTimeIter = TimeIter; + curTimeIter = TimeIter; curAbsTimeIter = max(TimeIter, config->GetStartWindowIteration()) - config->GetStartWindowIteration(); curOuterIter = OuterIter; curInnerIter = InnerIter; @@ -272,7 +272,7 @@ void COutput::SetHistoryOutput(CGeometry ****geometry, CSolver *****solver, CCon void COutput::SetMultizoneHistoryOutput(COutput **output, CConfig **config, CConfig *driver_config, unsigned long TimeIter, unsigned long OuterIter){ - curTimeIter = TimeIter; + curTimeIter = TimeIter; curAbsTimeIter = max(TimeIter, driver_config->GetStartWindowIteration()) - driver_config->GetStartWindowIteration(); curOuterIter = OuterIter; @@ -343,6 +343,9 @@ void COutput::AllocateDataSorters(CConfig *config, CGeometry *geometry){ if (volumeDataSorter == nullptr) volumeDataSorter = new CFEMDataSorter(config, geometry, volumeFieldNames); + if (config->GetWrt_Restart_Compact() && volumeDataSorterCompact == nullptr) + volumeDataSorterCompact = new CFEMDataSorter(config, geometry, requiredVolumeFieldNames); + if (surfaceDataSorter == nullptr) surfaceDataSorter = new CSurfaceFEMDataSorter(config, geometry, dynamic_cast(volumeDataSorter)); @@ -352,6 +355,9 @@ void COutput::AllocateDataSorters(CConfig *config, CGeometry *geometry){ if (volumeDataSorter == nullptr) volumeDataSorter = new CFVMDataSorter(config, geometry, volumeFieldNames); + if (config->GetWrt_Restart_Compact() && volumeDataSorterCompact == nullptr) + volumeDataSorterCompact = new CFVMDataSorter(config, geometry, requiredVolumeFieldNames); + if (surfaceDataSorter == nullptr) surfaceDataSorter = new CSurfaceFVMDataSorter(config, geometry, dynamic_cast(volumeDataSorter)); @@ -373,6 +379,7 @@ void COutput::LoadData(CGeometry *geometry, CConfig *config, CSolver** solver_co /*--- Partition and sort the volume output data -- */ volumeDataSorter->SortOutputData(); + if (volumeDataSorterCompact != nullptr) volumeDataSorterCompact->SortOutputData(); } @@ -381,6 +388,9 @@ void COutput::WriteToFile(CConfig *config, CGeometry *geometry, OUTPUT_TYPE form /*--- File writer that will later be used to write the file to disk. Created below in the "switch" ---*/ CFileWriter *fileWriter = nullptr; + /*--- Set current time iter even if history file is not written ---*/ + curTimeIter = config->GetTimeIter(); + /*--- If it is still present, strip the extension (suffix) from the filename ---*/ const auto lastindex = fileName.find_last_of('.'); fileName = fileName.substr(0, lastindex); @@ -412,6 +422,10 @@ void COutput::WriteToFile(CConfig *config, CGeometry *geometry, OUTPUT_TYPE form if (!config->GetWrt_Surface_Overwrite()) filename_iter = config->GetFilename_Iter(fileName, curInnerIter, curOuterIter); + /*--- If we have compact restarts, we use only the required fields. ---*/ + if (config->GetWrt_Restart_Compact()) + surfaceDataSorter->SetRequiredFieldNames(requiredVolumeFieldNames); + surfaceDataSorter->SortConnectivity(config, geometry); surfaceDataSorter->SortOutputData(); @@ -430,6 +444,10 @@ void COutput::WriteToFile(CConfig *config, CGeometry *geometry, OUTPUT_TYPE form if (!config->GetWrt_Restart_Overwrite()) filename_iter = config->GetFilename_Iter(fileName, curInnerIter, curOuterIter); + /*--- If we have compact restarts, we use only the required fields. ---*/ + if (config->GetWrt_Restart_Compact()) + volumeDataSorter->SetRequiredFieldNames(requiredVolumeFieldNames); + LogOutputFiles("SU2 ASCII restart"); fileWriter = new CSU2FileWriter(volumeDataSorter); @@ -446,8 +464,13 @@ void COutput::WriteToFile(CConfig *config, CGeometry *geometry, OUTPUT_TYPE form filename_iter = config->GetFilename_Iter(fileName, curInnerIter, curOuterIter); LogOutputFiles("SU2 binary restart"); - fileWriter = new CSU2BinaryFileWriter(volumeDataSorter); - + if (config->GetWrt_Restart_Compact()) { + /*--- If we have compact restarts, we use only the required fields. ---*/ + volumeDataSorterCompact->SetRequiredFieldNames(requiredVolumeFieldNames); + fileWriter = new CSU2BinaryFileWriter(volumeDataSorterCompact); + } else { + fileWriter = new CSU2BinaryFileWriter(volumeDataSorter); + } break; case OUTPUT_TYPE::MESH: @@ -455,7 +478,7 @@ void COutput::WriteToFile(CConfig *config, CGeometry *geometry, OUTPUT_TYPE form extension = CSU2MeshFileWriter::fileExt; if (fileName.empty()) - fileName = volumeFilename; + fileName = config->GetFilename(volumeFilename, "", curTimeIter); if (!config->GetWrt_Volume_Overwrite()) filename_iter = config->GetFilename_Iter(fileName, curInnerIter, curOuterIter); @@ -826,6 +849,7 @@ bool COutput::SetResultFiles(CGeometry *geometry, CConfig *config, CSolver** sol /*--- Partition and sort the data --- */ volumeDataSorter->SortOutputData(); + if (volumeDataSorterCompact != nullptr) volumeDataSorterCompact->SortOutputData(); if (rank == MASTER_NODE && !isFileWrite) { fileWritingTable->SetAlign(PrintingToolbox::CTablePrinter::CENTER); @@ -1504,58 +1528,64 @@ void COutput::PreprocessVolumeOutput(CConfig *config){ SetVolumeOutputFields(config); - /*---Coordinates and solution groups must be always in the output. - * If they are not requested, add them here. ---*/ - - auto itCoord = std::find(requestedVolumeFields.begin(), - requestedVolumeFields.end(), "COORDINATES"); - if (itCoord == requestedVolumeFields.end()){ + /*--- Coordinates must be always in the output. If they are not requested, add them here. ---*/ + auto itCoord = std::find(requestedVolumeFields.begin(), requestedVolumeFields.end(), "COORDINATES"); + if (itCoord == requestedVolumeFields.end()) { requestedVolumeFields.emplace_back("COORDINATES"); nRequestedVolumeFields++; } - auto itSol = std::find(requestedVolumeFields.begin(), - requestedVolumeFields.end(), "SOLUTION"); - if (itSol == requestedVolumeFields.end()){ - requestedVolumeFields.emplace_back("SOLUTION"); - nRequestedVolumeFields++; - } - nVolumeFields = 0; + /*--- Add the solution if it was not requested for backwards compatibility, unless the COMPACT keyword was used to request exclusively the specified fields. ---*/ + auto itSol = std::find(requestedVolumeFields.begin(), requestedVolumeFields.end(), "SOLUTION"); + if (itSol == requestedVolumeFields.end()) { + auto itCompact = std::find(requestedVolumeFields.begin(), requestedVolumeFields.end(), "COMPACT"); + if (itCompact == requestedVolumeFields.end()) { + requestedVolumeFields.emplace_back("SOLUTION"); + nRequestedVolumeFields++; + } + } - string RequestedField; std::vector FoundField(nRequestedVolumeFields, false); vector FieldsToRemove; - /*--- Loop through all fields defined in the corresponding SetVolumeOutputFields(). - * If it is also defined in the config (either as part of a group or a single field), the field - * object gets an offset so that we know where to find the data in the Local_Data() array. - * Note that the default offset is -1. An index !=-1 defines this field as part of the output. ---*/ + * If it is also defined in the config (either as part of a group or a single field), the field + * object gets an offset so that we know where to find the data in the Local_Data() array. + * Note that the default offset is -1. An index !=-1 defines this field as part of the output. ---*/ + + unsigned short nVolumeFields = 0, nVolumeFieldsCompact = 0; - for (unsigned short iField_Output = 0; iField_Output < volumeOutput_List.size(); iField_Output++){ + for (size_t iField_Output = 0; iField_Output < volumeOutput_List.size(); iField_Output++) { const string &fieldReference = volumeOutput_List[iField_Output]; - if (volumeOutput_Map.count(fieldReference) > 0){ - VolumeOutputField &Field = volumeOutput_Map.at(fieldReference); + const auto it = volumeOutput_Map.find(fieldReference); + if (it != volumeOutput_Map.end()) { + VolumeOutputField &Field = it->second; - /*--- Loop through all fields specified in the config ---*/ + /*--- Loop through the minimum required fields for restarts. ---*/ - for (unsigned short iReqField = 0; iReqField < nRequestedVolumeFields; iReqField++){ + for (const auto& RequiredField : restartVolumeFields) { + if ((RequiredField == Field.outputGroup || RequiredField == fieldReference) && Field.offsetCompact == -1) { + Field.offsetCompact = nVolumeFieldsCompact++; + requiredVolumeFieldNames.push_back(Field.fieldName); + } + } - RequestedField = requestedVolumeFields[iReqField]; + /*--- Loop through all fields specified in the config. ---*/ - if (((RequestedField == Field.outputGroup) || (RequestedField == fieldReference)) && (Field.offset == -1)){ - Field.offset = nVolumeFields; - volumeFieldNames.push_back(Field.fieldName); - nVolumeFields++; + for (size_t iReqField = 0; iReqField < nRequestedVolumeFields; iReqField++) { + const auto &RequestedField = requestedVolumeFields[iReqField]; + if ((RequestedField == Field.outputGroup || RequestedField == fieldReference) && Field.offset == -1) { + Field.offset = nVolumeFields++; + volumeFieldNames.push_back(Field.fieldName); FoundField[iReqField] = true; } } } } - for (unsigned short iReqField = 0; iReqField < nRequestedVolumeFields; iReqField++){ + for (size_t iReqField = 0; iReqField < nRequestedVolumeFields; iReqField++){ if (!FoundField[iReqField]){ FieldsToRemove.push_back(requestedVolumeFields[iReqField]); } @@ -1563,7 +1593,7 @@ void COutput::PreprocessVolumeOutput(CConfig *config){ /*--- Remove fields which are not defined --- */ - for (unsigned short iReqField = 0; iReqField < FieldsToRemove.size(); iReqField++){ + for (size_t iReqField = 0; iReqField < FieldsToRemove.size(); iReqField++){ if (rank == MASTER_NODE) { if (iReqField == 0){ cout << " Info: Ignoring the following volume output fields/groups:" << endl; @@ -1585,7 +1615,6 @@ void COutput::PreprocessVolumeOutput(CConfig *config){ if (rank == MASTER_NODE){ cout <<"Volume output fields: "; for (unsigned short iReqField = 0; iReqField < nRequestedVolumeFields; iReqField++){ - RequestedField = requestedVolumeFields[iReqField]; cout << requestedVolumeFields[iReqField]; if (iReqField != nRequestedVolumeFields - 1) cout << ", "; } @@ -1602,10 +1631,11 @@ void COutput::LoadDataIntoSorter(CConfig* config, CGeometry* geometry, CSolver** /*--- Reset the offset cache and index --- */ cachePosition = 0; fieldIndexCache.clear(); + fieldIndexCacheCompact.clear(); curGetFieldIndex = 0; fieldGetIndexCache.clear(); - if (femOutput){ + if (femOutput) { /*--- Create an object of the class CMeshFEM_DG and retrieve the necessary geometrical information for the FEM DG solver. ---*/ @@ -1619,33 +1649,24 @@ void COutput::LoadDataIntoSorter(CConfig* config, CGeometry* geometry, CSolver** /*--- Access the solution by looping over the owned volume elements. ---*/ for(unsigned long l=0; lGetnPointDomain(); iPoint++) { - - /*--- Load the volume data into the data sorter. --- */ - buildFieldIndexCache = fieldIndexCache.empty(); - LoadVolumeData(config, geometry, solver, iPoint); - } /*--- Reset the offset cache and index --- */ cachePosition = 0; fieldIndexCache.clear(); + fieldIndexCacheCompact.clear(); curGetFieldIndex = 0; fieldGetIndexCache.clear(); @@ -1653,19 +1674,16 @@ void COutput::LoadDataIntoSorter(CConfig* config, CGeometry* geometry, CSolver** /*--- We only want to have surface values on solid walls ---*/ - if (config->GetSolid_Wall(iMarker)){ - for (iVertex = 0; iVertex < geometry->GetnVertex(iMarker); iVertex++){ + if (config->GetSolid_Wall(iMarker)) { + for (iVertex = 0; iVertex < geometry->GetnVertex(iMarker); iVertex++) { iPoint = geometry->vertex[iMarker][iVertex]->GetNode(); /*--- Load the surface data into the data sorter. --- */ - if(geometry->nodes->GetDomain(iPoint)){ - + if (geometry->nodes->GetDomain(iPoint)) { buildFieldIndexCache = fieldIndexCache.empty(); - LoadSurfaceData(config, geometry, solver, iPoint, iMarker, iVertex); - } } } @@ -1675,67 +1693,73 @@ void COutput::LoadDataIntoSorter(CConfig* config, CGeometry* geometry, CSolver** void COutput::SetVolumeOutputValue(const string& name, unsigned long iPoint, su2double value){ - if (buildFieldIndexCache){ - + if (buildFieldIndexCache) { /*--- Build up the offset cache to speed up subsequent * calls of this routine since the order of calls is - * the same for every value of iPoint --- */ + * the same for every value of iPoint. ---*/ - if (volumeOutput_Map.count(name) > 0){ - const short Offset = volumeOutput_Map.at(name).offset; + const auto it = volumeOutput_Map.find(name); + if (it != volumeOutput_Map.end()) { + const short Offset = it->second.offset; fieldIndexCache.push_back(Offset); - if (Offset != -1){ + if (Offset != -1) { volumeDataSorter->SetUnsortedData(iPoint, Offset, value); } + /*--- Note that the compact fields are a subset of the full fields. ---*/ + const short OffsetCompact = it->second.offsetCompact; + fieldIndexCacheCompact.push_back(OffsetCompact); + if (volumeDataSorterCompact != nullptr && OffsetCompact != -1) { + volumeDataSorterCompact->SetUnsortedData(iPoint, OffsetCompact, value); + } } else { - SU2_MPI::Error(string("Cannot find output field with name ") + name, CURRENT_FUNCTION); + SU2_MPI::Error("Cannot find output field with name " + name, CURRENT_FUNCTION); } } else { - - /*--- Use the offset cache for the access ---*/ - - const short Offset = fieldIndexCache[cachePosition++]; - if (Offset != -1){ + /*--- Use the offset caches for the access. ---*/ + const short Offset = fieldIndexCache[cachePosition]; + const short OffsetCompact = fieldIndexCacheCompact[cachePosition++]; + if (cachePosition == fieldIndexCache.size()) { + cachePosition = 0; + } + if (Offset != -1) { volumeDataSorter->SetUnsortedData(iPoint, Offset, value); } - if (cachePosition == fieldIndexCache.size()){ - cachePosition = 0; + if (volumeDataSorterCompact != nullptr && OffsetCompact != -1) { + volumeDataSorterCompact->SetUnsortedData(iPoint, OffsetCompact, value); } } } -su2double COutput::GetVolumeOutputValue(const string& name, unsigned long iPoint){ - - if (buildFieldIndexCache){ +su2double COutput::GetVolumeOutputValue(const string& name, unsigned long iPoint) { + if (buildFieldIndexCache) { /*--- Build up the offset cache to speed up subsequent * calls of this routine since the order of calls is - * the same for every value of iPoint --- */ + * the same for every value of iPoint. ---*/ - if (volumeOutput_Map.count(name) > 0){ - const short Offset = volumeOutput_Map.at(name).offset; + const auto it = volumeOutput_Map.find(name); + if (it != volumeOutput_Map.end()) { + const short Offset = it->second.offset; fieldGetIndexCache.push_back(Offset); - if (Offset != -1){ + if (Offset != -1) { return volumeDataSorter->GetUnsortedData(iPoint, Offset); } } else { - SU2_MPI::Error(string("Cannot find output field with name ") + name, CURRENT_FUNCTION); + SU2_MPI::Error("Cannot find output field with name " + name, CURRENT_FUNCTION); } } else { - - /*--- Use the offset cache for the access ---*/ + /*--- Use the offset cache for the access, ---*/ const short Offset = fieldGetIndexCache[curGetFieldIndex++]; - if (curGetFieldIndex == fieldGetIndexCache.size()){ + if (curGetFieldIndex == fieldGetIndexCache.size()) { curGetFieldIndex = 0; } - if (Offset != -1){ + if (Offset != -1) { return volumeDataSorter->GetUnsortedData(iPoint, Offset); } } - return 0.0; } @@ -1743,38 +1767,39 @@ void COutput::SetAvgVolumeOutputValue(const string& name, unsigned long iPoint, const su2double scaling = 1.0 / su2double(curAbsTimeIter + 1); - if (buildFieldIndexCache){ - + if (buildFieldIndexCache) { /*--- Build up the offset cache to speed up subsequent * calls of this routine since the order of calls is - * the same for every value of iPoint --- */ + * the same for every value of iPoint. ---*/ - if (volumeOutput_Map.count(name) > 0){ - const short Offset = volumeOutput_Map.at(name).offset; + const auto it = volumeOutput_Map.find(name); + if (it != volumeOutput_Map.end()) { + const short Offset = it->second.offset; fieldIndexCache.push_back(Offset); - if (Offset != -1){ - + /*--- This function is used for time-averaged fields and we know + * those are not part of the compact restart fields. ---*/ + fieldIndexCacheCompact.push_back(-1); + if (Offset != -1) { const su2double old_value = volumeDataSorter->GetUnsortedData(iPoint, Offset); - const su2double new_value = value * scaling + old_value *( 1.0 - scaling); + const su2double new_value = value * scaling + old_value * (1.0 - scaling); volumeDataSorter->SetUnsortedData(iPoint, Offset, new_value); } } else { - SU2_MPI::Error(string("Cannot find output field with name ") + name, CURRENT_FUNCTION); + SU2_MPI::Error("Cannot find output field with name " + name, CURRENT_FUNCTION); } } else { /*--- Use the offset cache for the access ---*/ const short Offset = fieldIndexCache[cachePosition++]; - if (Offset != -1){ - + if (Offset != -1) { const su2double old_value = volumeDataSorter->GetUnsortedData(iPoint, Offset); - const su2double new_value = value * scaling + old_value *( 1.0 - scaling); + const su2double new_value = value * scaling + old_value * (1.0 - scaling); volumeDataSorter->SetUnsortedData(iPoint, Offset, new_value); } - if (cachePosition == fieldIndexCache.size()){ + if (cachePosition == fieldIndexCache.size()) { cachePosition = 0; } } @@ -2414,7 +2439,7 @@ void COutput::PrintVolumeFields(){ } cout << "Available volume output fields for the current configuration in " << multiZoneHeaderString << ":" << endl; - cout << "Note: COORDINATES and SOLUTION groups are always in the volume output." << endl; + cout << "Note: COORDINATES are always included, and so is SOLUTION unless you add the keyword COMPACT to the list of fields." << endl; VolumeFieldTable.AddColumn("Name", NameSize); VolumeFieldTable.AddColumn("Group Name", GroupSize); VolumeFieldTable.AddColumn("Description", DescrSize); diff --git a/SU2_CFD/src/output/filewriter/CParallelDataSorter.cpp b/SU2_CFD/src/output/filewriter/CParallelDataSorter.cpp index 0290c20b025..2bc2778ec24 100644 --- a/SU2_CFD/src/output/filewriter/CParallelDataSorter.cpp +++ b/SU2_CFD/src/output/filewriter/CParallelDataSorter.cpp @@ -32,7 +32,8 @@ CParallelDataSorter::CParallelDataSorter(CConfig *config, const vector &valFieldNames) : rank(SU2_MPI::GetRank()), size(SU2_MPI::GetSize()), - fieldNames(valFieldNames) { + fieldNames(valFieldNames), + requiredFieldNames(valFieldNames) { GlobalField_Counter = fieldNames.size(); diff --git a/SU2_CFD/src/output/filewriter/CSU2BinaryFileWriter.cpp b/SU2_CFD/src/output/filewriter/CSU2BinaryFileWriter.cpp index 8e7a869f1bf..fb8c660c740 100644 --- a/SU2_CFD/src/output/filewriter/CSU2BinaryFileWriter.cpp +++ b/SU2_CFD/src/output/filewriter/CSU2BinaryFileWriter.cpp @@ -41,7 +41,7 @@ void CSU2BinaryFileWriter::WriteData(string val_filename){ unsigned short iVar; - const vector& fieldNames = dataSorter->GetFieldNames(); + const vector& fieldNames = dataSorter->GetRequiredFieldNames(); unsigned short nVar = fieldNames.size(); unsigned long nParallel_Poin = dataSorter->GetnPoints(); unsigned long nPoint_Global = dataSorter->GetnPointsGlobal(); diff --git a/SU2_CFD/src/output/filewriter/CSU2FileWriter.cpp b/SU2_CFD/src/output/filewriter/CSU2FileWriter.cpp index 4c04cf0e70d..ff498b1d357 100644 --- a/SU2_CFD/src/output/filewriter/CSU2FileWriter.cpp +++ b/SU2_CFD/src/output/filewriter/CSU2FileWriter.cpp @@ -35,7 +35,8 @@ CSU2FileWriter::CSU2FileWriter(CParallelDataSorter *valDataSorter) : void CSU2FileWriter::WriteData(string val_filename){ ofstream restart_file; - const vector fieldNames = dataSorter->GetFieldNames(); + const vector fieldNames = dataSorter->GetRequiredFieldNames(); + /*--- We append the pre-defined suffix (extension) to the filename (prefix) ---*/ val_filename.append(fileExt); diff --git a/SU2_CFD/src/solvers/CFEASolver.cpp b/SU2_CFD/src/solvers/CFEASolver.cpp index 4cd718d6ce1..9d19aa7c5a0 100644 --- a/SU2_CFD/src/solvers/CFEASolver.cpp +++ b/SU2_CFD/src/solvers/CFEASolver.cpp @@ -49,7 +49,7 @@ CFEASolver::CFEASolver(LINEAR_SOLVER_MODE mesh_deform_mode) : CFEASolverBase(mes CFEASolver::CFEASolver(CGeometry *geometry, CConfig *config) : CFEASolverBase(geometry, config) { - bool dynamic = (config->GetTime_Domain()); + bool dynamic = config->GetTime_Domain(); config->SetDelta_UnstTimeND(config->GetDelta_UnstTime()); /*--- Test whether we consider dielectric elastomers ---*/ @@ -59,6 +59,7 @@ CFEASolver::CFEASolver(CGeometry *geometry, CConfig *config) : CFEASolverBase(ge element_based = false; topol_filter_applied = false; initial_calc = true; + body_forces = config->GetGravityForce() || config->GetBody_Force() || config->GetCentrifugalForce(); /*--- Here is where we assign the kind of each element ---*/ @@ -71,6 +72,10 @@ CFEASolver::CFEASolver(CGeometry *geometry, CConfig *config) : CFEASolverBase(ge element_container[FEA_TERM][EL_TRIA+offset] = new CTRIA1(); element_container[FEA_TERM][EL_QUAD+offset] = new CQUAD4(); + /*--- Initialize temperature ---*/ + element_container[FEA_TERM][EL_TRIA+offset]->SetTemperature(config->GetTemperature_FreeStream()); + element_container[FEA_TERM][EL_QUAD+offset]->SetTemperature(config->GetTemperature_FreeStream()); + if (de_effects) { element_container[DE_TERM][EL_TRIA+offset] = new CTRIA1(); element_container[DE_TERM][EL_QUAD+offset] = new CQUAD4(); @@ -82,6 +87,10 @@ CFEASolver::CFEASolver(CGeometry *geometry, CConfig *config) : CFEASolverBase(ge element_container[FEA_TERM][EL_PYRAM+offset] = new CPYRAM5(); element_container[FEA_TERM][EL_PRISM+offset] = new CPRISM6(); + for (const auto el : {EL_TETRA, EL_HEXA, EL_PYRAM, EL_PRISM}) { + element_container[FEA_TERM][el+offset]->SetTemperature(config->GetTemperature_FreeStream()); + } + if (de_effects) { element_container[DE_TERM][EL_TETRA+offset] = new CTETRA1(); element_container[DE_TERM][EL_HEXA +offset] = new CHEXA8 (); @@ -113,22 +122,15 @@ CFEASolver::CFEASolver(CGeometry *geometry, CConfig *config) : CFEASolverBase(ge /*--- The length of the solution vector depends on whether the problem is static or dynamic ---*/ - unsigned short nSolVar; string text_line, filename; ifstream restart_file; - if (dynamic) nSolVar = 3 * nVar; - else nSolVar = nVar; - - auto* SolInit = new su2double[nSolVar](); - /*--- Initialize from zero everywhere ---*/ - nodes = new CFEABoundVariable(SolInit, nPoint, nDim, nVar, config); + std::array zeros{}; + nodes = new CFEABoundVariable(zeros.data(), nPoint, nDim, nVar, config); SetBaseClassPointerToNodes(); - delete [] SolInit; - /*--- Set which points are vertices and allocate boundary data. ---*/ for (unsigned long iPoint = 0; iPoint < nPoint; iPoint++) @@ -559,7 +561,6 @@ void CFEASolver::Preprocessing(CGeometry *geometry, CSolver **solver_container, const bool dynamic = config->GetTime_Domain(); const bool disc_adj_fem = (config->GetKind_Solver() == MAIN_SOLVER::DISC_ADJ_FEM); - const bool body_forces = config->GetDeadLoad(); const bool topology_mode = config->GetTopology_Optimization(); /* @@ -594,7 +595,7 @@ void CFEASolver::Preprocessing(CGeometry *geometry, CSolver **solver_container, * Only initialized once, at the first iteration of the first time step. */ if (body_forces && (initial_calc || disc_adj_fem)) - Compute_DeadLoad(geometry, numerics, config); + Compute_BodyForces(geometry, numerics, config); /*--- Clear the linear system solution. ---*/ SU2_OMP_PARALLEL @@ -1407,7 +1408,7 @@ void CFEASolver::Compute_NodalStress(CGeometry *geometry, CNumerics **numerics, } -void CFEASolver::Compute_DeadLoad(CGeometry *geometry, CNumerics **numerics, const CConfig *config) { +void CFEASolver::Compute_BodyForces(CGeometry *geometry, CNumerics **numerics, const CConfig *config) { /*--- Start OpenMP parallel region. ---*/ @@ -1457,7 +1458,7 @@ void CFEASolver::Compute_DeadLoad(CGeometry *geometry, CNumerics **numerics, con /*--- Set the properties of the element and compute its mass matrix. ---*/ element->Set_ElProperties(element_properties[iElem]); - numerics[FEA_TERM + thread*MAX_TERMS]->Compute_Dead_Load(element, config); + numerics[FEA_TERM + thread*MAX_TERMS]->Compute_Body_Forces(element, config); /*--- Add contributions of this element to the mass matrix. ---*/ for (iNode = 0; iNode < nNodes; iNode++) { @@ -1587,6 +1588,20 @@ void CFEASolver::BC_Clamped_Post(CGeometry *geometry, const CConfig *config, uns } +namespace { +/*--- Helper for BC_Sym_Plane ---*/ +template +void SubtractProjection(unsigned short nDim, const su2double* n, const Read& read, const Write& write) { + su2double tmp[3] = {}; + for (auto iDim = 0u; iDim < nDim; ++iDim) tmp[iDim] = read(iDim); + const su2double proj = DotProduct(nDim, tmp, n); + for (auto iDim = 0u; iDim < nDim; ++iDim) { + tmp[iDim] -= proj * n[iDim]; + write(iDim, tmp[iDim]); + } +} +} + void CFEASolver::BC_Sym_Plane(CGeometry *geometry, const CConfig *config, unsigned short val_marker) { if (geometry->GetnElem_Bound(val_marker) == 0) return; @@ -1609,14 +1624,8 @@ void CFEASolver::BC_Sym_Plane(CGeometry *geometry, const CConfig *config, unsign case 3: TriangleNormal(nodeCoord, normal); break; case 4: QuadrilateralNormal(nodeCoord, normal); break; } - - auto axis = 0u; - for (auto iDim = 1u; iDim < MAXNDIM; ++iDim) - axis = (fabs(normal[iDim]) > fabs(normal[axis]))? iDim : axis; - - if (fabs(normal[axis]) < 0.99*Norm(int(MAXNDIM),normal)) { - SU2_MPI::Error("The structural solver only supports axis-aligned symmetry planes.",CURRENT_FUNCTION); - } + const su2double area = Norm(int(MAXNDIM), normal); + for (auto iDim = 0u; iDim < MAXNDIM; ++iDim) normal[iDim] /= area; /*--- Impose zero displacement perpendicular to the symmetry plane. ---*/ @@ -1626,20 +1635,29 @@ void CFEASolver::BC_Sym_Plane(CGeometry *geometry, const CConfig *config, unsign const auto iPoint = geometry->vertex[val_marker][iVertex]->GetNode(); /*--- Set and enforce solution at current and previous time-step ---*/ - nodes->SetSolution(iPoint, axis, 0.0); +#define SUBTRACT_PROJECTION(READ, WRITE) \ + SubtractProjection(nDim, normal, [&](unsigned short iDim) { return nodes->READ(iPoint, iDim); }, \ + [&](unsigned short iDim, const su2double& x) { nodes->WRITE(iPoint, iDim, x); }); + SUBTRACT_PROJECTION(GetSolution, SetSolution) if (dynamic) { - nodes->SetSolution_Vel(iPoint, axis, 0.0); - nodes->SetSolution_Accel(iPoint, axis, 0.0); - nodes->Set_Solution_time_n(iPoint, axis, 0.0); - nodes->SetSolution_Vel_time_n(iPoint, axis, 0.0); - nodes->SetSolution_Accel_time_n(iPoint, axis, 0.0); + SUBTRACT_PROJECTION(GetSolution_Vel, SetSolution_Vel) + SUBTRACT_PROJECTION(GetSolution_Accel, SetSolution_Accel) + SUBTRACT_PROJECTION(GetSolution_time_n, Set_Solution_time_n) + SUBTRACT_PROJECTION(GetSolution_Vel_time_n, SetSolution_Vel_time_n) + SUBTRACT_PROJECTION(GetSolution_Accel_time_n, SetSolution_Accel_time_n) } /*--- Set and enforce 0 solution for mesh deformation ---*/ - nodes->SetBound_Disp(iPoint, axis, 0.0); - LinSysSol(iPoint, axis) = 0.0; - if (LinSysReact.GetLocSize() > 0) LinSysReact(iPoint, axis) = 0.0; - Jacobian.EnforceSolutionAtDOF(iPoint, axis, su2double(0.0), LinSysRes); + SUBTRACT_PROJECTION(GetBound_Disp, SetBound_Disp) +#undef SUBTRACT_PROJECTION + + SubtractProjection(nDim, normal, [&](unsigned short iDim) { return LinSysSol(iPoint, iDim); }, + [&](unsigned short iDim, const su2double& x) { LinSysSol(iPoint, iDim) = x; }); + if (LinSysReact.GetLocSize() > 0) { + SubtractProjection(nDim, normal, [&](unsigned short iDim) { return LinSysReact(iPoint, iDim); }, + [&](unsigned short iDim, const su2double& x) { LinSysReact(iPoint, iDim) = x; }); + } + Jacobian.EnforceZeroProjection(iPoint, normal, LinSysRes); } @@ -2105,7 +2123,6 @@ void CFEASolver::ImplicitNewmark_Iteration(const CGeometry *geometry, CNumerics const bool linear_analysis = (config->GetGeometricConditions() == STRUCT_DEFORMATION::SMALL); const bool nonlinear_analysis = (config->GetGeometricConditions() == STRUCT_DEFORMATION::LARGE); const bool newton_raphson = (config->GetKind_SpaceIteScheme_FEA() == STRUCT_SPACE_ITE::NEWTON); - const bool body_forces = config->GetDeadLoad(); /*--- For simplicity, no incremental loading is handled with increment of 1. ---*/ const su2double loadIncr = config->GetIncrementalLoad()? loadIncrement : su2double(1.0); @@ -2293,7 +2310,6 @@ void CFEASolver::GeneralizedAlpha_Iteration(const CGeometry *geometry, CNumerics const bool linear_analysis = (config->GetGeometricConditions() == STRUCT_DEFORMATION::SMALL); const bool nonlinear_analysis = (config->GetGeometricConditions() == STRUCT_DEFORMATION::LARGE); const bool newton_raphson = (config->GetKind_SpaceIteScheme_FEA() == STRUCT_SPACE_ITE::NEWTON); - const bool body_forces = config->GetDeadLoad(); /*--- Blend between previous and current timestep. ---*/ const su2double alpha_f = config->Get_Int_Coeffs(2); @@ -2917,9 +2933,6 @@ void CFEASolver::Compute_OFVolFrac(CGeometry *geometry, const CConfig *config) void CFEASolver::Compute_OFCompliance(CGeometry *geometry, const CConfig *config) { - /*--- Types of loads to consider ---*/ - const bool body_forces = config->GetDeadLoad(); - /*--- If the loads are being applied incrementaly ---*/ const bool incremental_load = config->GetIncrementalLoad(); diff --git a/SU2_CFD/src/solvers/CIncNSSolver.cpp b/SU2_CFD/src/solvers/CIncNSSolver.cpp index 41cedcdb5f5..e0a4fda96ef 100644 --- a/SU2_CFD/src/solvers/CIncNSSolver.cpp +++ b/SU2_CFD/src/solvers/CIncNSSolver.cpp @@ -341,7 +341,6 @@ void CIncNSSolver::BC_Wall_Generic(const CGeometry *geometry, const CConfig *con /*--- Variables for streamwise periodicity ---*/ const bool streamwise_periodic = (config->GetKind_Streamwise_Periodic() != ENUM_STREAMWISE_PERIODIC::NONE); const bool streamwise_periodic_temperature = config->GetStreamwise_Periodic_Temperature(); - su2double Cp, thermal_conductivity, dot_product, scalar_factor; /*--- Identify the boundary by string name ---*/ @@ -434,15 +433,17 @@ void CIncNSSolver::BC_Wall_Generic(const CGeometry *geometry, const CConfig *con /*--- With streamwise periodic flow and heatflux walls an additional term is introduced in the boundary formulation ---*/ if (streamwise_periodic && streamwise_periodic_temperature) { - Cp = nodes->GetSpecificHeatCp(iPoint); - thermal_conductivity = nodes->GetThermalConductivity(iPoint); + const su2double Cp = nodes->GetSpecificHeatCp(iPoint); + const su2double thermal_conductivity = nodes->GetThermalConductivity(iPoint); /*--- Scalar factor of the residual contribution ---*/ const su2double norm2_translation = GeometryToolbox::SquaredNorm(nDim, config->GetPeriodic_Translation(0)); - scalar_factor = SPvals.Streamwise_Periodic_IntegratedHeatFlow*thermal_conductivity / (SPvals.Streamwise_Periodic_MassFlow * Cp * norm2_translation); + const su2double scalar_factor = + SPvals.Streamwise_Periodic_IntegratedHeatFlow*thermal_conductivity / + (SPvals.Streamwise_Periodic_MassFlow * Cp * norm2_translation); /*--- Dot product ---*/ - dot_product = GeometryToolbox::DotProduct(nDim, config->GetPeriodic_Translation(0), Normal); + const su2double dot_product = GeometryToolbox::DotProduct(nDim, config->GetPeriodic_Translation(0), Normal); LinSysRes(iPoint, nDim+1) += scalar_factor*dot_product; } // if streamwise_periodic @@ -472,18 +473,17 @@ void CIncNSSolver::BC_Wall_Generic(const CGeometry *geometry, const CConfig *con const auto Coord_i = geometry->nodes->GetCoord(iPoint); const auto Coord_j = geometry->nodes->GetCoord(Point_Normal); - su2double Edge_Vector[MAXNDIM]; - GeometryToolbox::Distance(nDim, Coord_j, Coord_i, Edge_Vector); - su2double dist_ij_2 = GeometryToolbox::SquaredNorm(nDim, Edge_Vector); - su2double dist_ij = sqrt(dist_ij_2); + su2double UnitNormal[MAXNDIM] = {0.0}; + for (auto iDim = 0u; iDim < nDim; ++iDim) UnitNormal[iDim] = Normal[iDim] / Area; + const su2double dist_ij = GeometryToolbox::NormalDistance(nDim, UnitNormal, Coord_i, Coord_j); /*--- Compute the normal gradient in temperature using Twall ---*/ - su2double dTdn = -(nodes->GetTemperature(Point_Normal) - Twall)/dist_ij; + const su2double dTdn = -(nodes->GetTemperature(Point_Normal) - Twall)/dist_ij; /*--- Get thermal conductivity ---*/ - su2double thermal_conductivity = nodes->GetThermalConductivity(iPoint); + const su2double thermal_conductivity = nodes->GetThermalConductivity(iPoint); /*--- Apply a weak boundary condition for the energy equation. Compute the residual due to the prescribed heat flux. ---*/ @@ -493,10 +493,7 @@ void CIncNSSolver::BC_Wall_Generic(const CGeometry *geometry, const CConfig *con /*--- Jacobian contribution for temperature equation. ---*/ if (implicit) { - su2double proj_vector_ij = 0.0; - if (dist_ij_2 > 0.0) - proj_vector_ij = GeometryToolbox::DotProduct(nDim, Edge_Vector, Normal) / dist_ij_2; - Jacobian.AddVal2Diag(iPoint, nDim+1, thermal_conductivity*proj_vector_ij); + Jacobian.AddVal2Diag(iPoint, nDim+1, thermal_conductivity * Area / dist_ij); } break; } // switch diff --git a/SU2_CFD/src/solvers/CMeshSolver.cpp b/SU2_CFD/src/solvers/CMeshSolver.cpp index 0335e1759d0..b3f0043847d 100644 --- a/SU2_CFD/src/solvers/CMeshSolver.cpp +++ b/SU2_CFD/src/solvers/CMeshSolver.cpp @@ -1450,7 +1450,7 @@ void CMeshSolver::Surface_Translating(CGeometry *geometry, CConfig *config, unsi } for (iDim = 0; iDim < 3; iDim++) { - xDot[iDim] = config->GetMarkerTranslationRate(jMarker, iDim); + xDot[iDim] = config->GetMarkerTranslationRate(jMarker, iDim)/config->GetVelocity_Ref(); Center[iDim] = config->GetMarkerMotion_Origin(jMarker, iDim); } diff --git a/SU2_CFD/src/solvers/CNSSolver.cpp b/SU2_CFD/src/solvers/CNSSolver.cpp index 127ff3f5b08..50f243c7f0c 100644 --- a/SU2_CFD/src/solvers/CNSSolver.cpp +++ b/SU2_CFD/src/solvers/CNSSolver.cpp @@ -672,22 +672,19 @@ void CNSSolver::BC_Isothermal_Wall_Generic(CGeometry *geometry, CSolver **solver const auto Coord_i = geometry->nodes->GetCoord(iPoint); const auto Coord_j = geometry->nodes->GetCoord(Point_Normal); - - su2double dist_ij = GeometryToolbox::Distance(nDim, Coord_i, Coord_j); + const su2double dist_ij = GeometryToolbox::NormalDistance(nDim, UnitNormal, Coord_i, Coord_j); /*--- Store the corrected velocity at the wall which will be zero (v = 0), unless there is grid motion (v = u_wall)---*/ if (dynamic_grid) { nodes->SetVelocity_Old(iPoint, geometry->nodes->GetGridVel(iPoint)); - } - else { + } else { su2double zero[MAXNDIM] = {0.0}; nodes->SetVelocity_Old(iPoint, zero); } - for (auto iDim = 0u; iDim < nDim; iDim++) - LinSysRes(iPoint, iDim+1) = 0.0; + for (auto iDim = 0u; iDim < nDim; iDim++) LinSysRes(iPoint, iDim+1) = 0.0; nodes->SetVel_ResTruncError_Zero(iPoint); /*--- Get transport coefficients ---*/ @@ -708,8 +705,7 @@ void CNSSolver::BC_Isothermal_Wall_Generic(CGeometry *geometry, CSolver **solver if (cht_mode) { Twall = GetCHTWallTemperature(config, val_marker, iVertex, dist_ij, thermal_conductivity, There, Temperature_Ref); - } - else if (config->GetMarker_All_PyCustom(val_marker)) { + } else if (config->GetMarker_All_PyCustom(val_marker)) { Twall = geometry->GetCustomBoundaryTemperature(val_marker, iVertex) / Temperature_Ref; } diff --git a/SU2_CFD/src/solvers/CSolver.cpp b/SU2_CFD/src/solvers/CSolver.cpp index e446c3f1133..2c6322392c7 100644 --- a/SU2_CFD/src/solvers/CSolver.cpp +++ b/SU2_CFD/src/solvers/CSolver.cpp @@ -3163,7 +3163,8 @@ void CSolver::InterpolateRestartData(const CGeometry *geometry, const CConfig *c const unsigned long nFields = Restart_Vars[1]; const unsigned long nPointFile = Restart_Vars[2]; const auto t0 = SU2_MPI::Wtime(); - auto nRecurse = 0; + int nRecurse = 0; + const int maxNRecurse = 128; if (rank == MASTER_NODE) { cout << "\nThe number of points in the restart file (" << nPointFile << ") does not match " @@ -3262,7 +3263,7 @@ void CSolver::InterpolateRestartData(const CGeometry *geometry, const CConfig *c bool done = false; SU2_OMP_PARALLEL - while (!done) { + while (!done && nRecurse < maxNRecurse) { SU2_OMP_FOR_DYN(roundUpDiv(nPointDomain,2*omp_get_num_threads())) for (auto iPoint = 0ul; iPoint < nPointDomain; ++iPoint) { /*--- Do not change points that are already interpolated. ---*/ @@ -3273,7 +3274,8 @@ void CSolver::InterpolateRestartData(const CGeometry *geometry, const CConfig *c for (const auto jPoint : geometry->nodes->GetPoints(iPoint)) { if (!isMapped[jPoint]) continue; - if (boundary_i != geometry->nodes->GetSolidBoundary(jPoint)) continue; + /*--- Take data from anywhere if we are looping too many times. ---*/ + if (boundary_i != geometry->nodes->GetSolidBoundary(jPoint) && nRecurse < 8) continue; nDonor[iPoint]++; @@ -3315,6 +3317,10 @@ void CSolver::InterpolateRestartData(const CGeometry *geometry, const CConfig *c } // everything goes out of scope except "localVars" + if (nRecurse == maxNRecurse) { + SU2_MPI::Error("Limit number of recursions reached, the meshes may be too different.", CURRENT_FUNCTION); + } + /*--- Move to Restart_Data in ascending order of global index, which is how a matching restart would have been read. ---*/ Restart_Data.resize(nPointDomain*nFields); @@ -3329,9 +3335,11 @@ void CSolver::InterpolateRestartData(const CGeometry *geometry, const CConfig *c counter++; } } + int nRecurseMax = 0; + SU2_MPI::Reduce(&nRecurse, &nRecurseMax, 1, MPI_INT, MPI_MAX, MASTER_NODE, SU2_MPI::GetComm()); if (rank == MASTER_NODE) { - cout << "Number of recursions: " << nRecurse << ".\n" + cout << "Number of recursions: " << nRecurseMax << ".\n" "Elapsed time: " << SU2_MPI::Wtime()-t0 << "s.\n" << endl; } } diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index ed4b56d9eed..19693a0ab8b 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -502,7 +502,7 @@ void CTurbSASolver::BC_HeatFlux_Wall(CGeometry *geometry, CSolver **solver_conta Res_Wall = coeff*RoughWallBC*Area; LinSysRes.SubtractBlock(iPoint, &Res_Wall); - su2double Jacobian_i = (laminar_viscosity*Area)/(0.03*Roughness_Height*sigma); + su2double Jacobian_i = (laminar_viscosity /density *Area)/(0.03*Roughness_Height*sigma); Jacobian_i += 2.0*RoughWallBC*Area/sigma; if (implicit) Jacobian.AddVal2Diag(iPoint, -Jacobian_i); } diff --git a/SU2_CFD/src/variables/CFEAVariable.cpp b/SU2_CFD/src/variables/CFEAVariable.cpp index 7e486af3a21..4760706a814 100644 --- a/SU2_CFD/src/variables/CFEAVariable.cpp +++ b/SU2_CFD/src/variables/CFEAVariable.cpp @@ -46,7 +46,7 @@ CFEAVariable::CFEAVariable(const su2double *val_fea, unsigned long npoint, unsig * still work as expected for the primal solver). ---*/ const bool dynamic_analysis = config->GetTime_Domain(); - const bool body_forces = config->GetDeadLoad(); + const bool body_forces = config->GetGravityForce() || config->GetBody_Force() || config->GetCentrifugalForce(); const bool prestretch_fem = config->GetPrestretch(); // Structure is prestretched const bool discrete_adjoint = config->GetDiscrete_Adjoint(); const bool refgeom = config->GetRefGeom(); // Reference geometry needs to be stored diff --git a/SU2_PY/FSI_tools/FSIInterface.py b/SU2_PY/FSI_tools/FSIInterface.py index 61148d805c7..267e6660aaf 100644 --- a/SU2_PY/FSI_tools/FSIInterface.py +++ b/SU2_PY/FSI_tools/FSIInterface.py @@ -307,7 +307,7 @@ def connect(self, FSI_config, FluidSolver, SolidSolver): print("Fluid solver is initialized on process {}".format(myid)) self.haveFluidSolver = True allMovingMarkersTags = FluidSolver.GetDeformableMarkerTags() - allMarkersID = FluidSolver.GetMarkerTags() + allMarkersID = FluidSolver.GetMarkerIndices() if not allMovingMarkersTags: raise Exception("No interface for FSI was defined.") else: diff --git a/SU2_PY/meson.build b/SU2_PY/meson.build index 85ae325f3f0..ced81290346 100644 --- a/SU2_PY/meson.build +++ b/SU2_PY/meson.build @@ -70,3 +70,5 @@ install_data(['FSI_tools/__init__.py', install_data(['SU2_Nastran/__init__.py', 'SU2_Nastran/pysu2_nastran.py'], install_dir: join_paths(get_option('bindir'), 'SU2_Nastran')) + +install_subdir(['../externals/FADO'], install_dir: get_option('bindir')) diff --git a/TestCases/TestCase.py b/TestCases/TestCase.py index 320c913ebd2..91b08199f5e 100644 --- a/TestCases/TestCase.py +++ b/TestCases/TestCase.py @@ -361,7 +361,14 @@ def run_filediff(self, with_tsan=False, with_asan=False): # Assert that both files have the same number of lines if len(fromlines) != len(tolines): - diff = ["ERROR: Number of lines in " + fromfile + " and " + tofile + " differ: " + len(fromlines) + " vs " + len(tolines) + "."] + stringerr = "ERROR: Number of lines in " + str(fromfile) + " and " + str(tofile) + " differ: " + str(len(fromlines)) + " vs " + str(len(tolines)) + "." + diff = [stringerr] + print("generated file = ") + for i_line in tolines: + print(i_line) + for i_line in fromlines: + print(i_line) + passed = False # Loop through all lines diff --git a/TestCases/Tutorials/design/Inc_Turbulent_Bend_Wallfunctions/optim.csv.ref b/TestCases/Tutorials/design/Inc_Turbulent_Bend_Wallfunctions/optim.csv.ref new file mode 100644 index 00000000000..2f0fb6fc297 --- /dev/null +++ b/TestCases/Tutorials/design/Inc_Turbulent_Bend_Wallfunctions/optim.csv.ref @@ -0,0 +1,2 @@ +ITER, avg_dp +1, 89.03358290403737 diff --git a/TestCases/disc_adj_fea/configAD_fem.cfg b/TestCases/disc_adj_fea/configAD_fem.cfg index 188c7e451bf..fe1d2c6c5b7 100644 --- a/TestCases/disc_adj_fea/configAD_fem.cfg +++ b/TestCases/disc_adj_fea/configAD_fem.cfg @@ -16,7 +16,7 @@ RESTART_SOL= NO ITER = 10 -OBJECTIVE_FUNCTION = REFERENCE_GEOMETRY +OBJECTIVE_FUNCTION = REFERENCE_GEOMETRY REFERENCE_GEOMETRY = YES REFERENCE_GEOMETRY_FILENAME = reference_geometry.dat @@ -39,7 +39,6 @@ MATERIAL_COMPRESSIBILITY= COMPRESSIBLE ELASTICITY_MODULUS=21000 POISSON_RATIO=0.4 MATERIAL_DENSITY=100 -DEAD_LOAD=NO FORMULATION_ELASTICITY_2D = PLANE_STRAIN NONLINEAR_FEM_SOLUTION_METHOD = NEWTON_RAPHSON diff --git a/TestCases/fea_fsi/StatBeam_3d/configBeam_3d.cfg b/TestCases/fea_fsi/StatBeam_3d/configBeam_3d.cfg index a6c9cde3c84..56b57777a55 100644 --- a/TestCases/fea_fsi/StatBeam_3d/configBeam_3d.cfg +++ b/TestCases/fea_fsi/StatBeam_3d/configBeam_3d.cfg @@ -14,14 +14,17 @@ MATERIAL_MODEL= LINEAR_ELASTIC MESH_FILENAME= meshBeam_3d.su2 ELASTICITY_MODULUS=3E7 POISSON_RATIO=0.3 +MATERIAL_THERMAL_EXPANSION_COEFF= 2e-5 +MATERIAL_REFERENCE_TEMPERATURE= 288.15 +FREESTREAM_TEMPERATURE= 350 MATERIAL_DENSITY=7854 -MARKER_CLAMPED = ( left , right ) -MARKER_PRESSURE= ( lower, 0 , symleft, 0, symright, 0) -MARKER_LOAD= ( upper, 1, 1000, 0, -1, 0) -LINEAR_SOLVER= FGMRES -LINEAR_SOLVER_PREC= LU_SGS +MARKER_CLAMPED = ( left, right ) +MARKER_PRESSURE= ( lower, 0 , symleft, 0, symright, 0 ) +MARKER_LOAD= ( upper, 1, 1000, 0, -1, 0 ) +LINEAR_SOLVER= CONJUGATE_GRADIENT +LINEAR_SOLVER_PREC= ILU LINEAR_SOLVER_ERROR= 1E-8 -LINEAR_SOLVER_ITER= 500 +LINEAR_SOLVER_ITER= 1000 MESH_FORMAT= SU2 TABULAR_FORMAT= CSV CONV_FILENAME= history_beam diff --git a/TestCases/fea_fsi/rotating_cylinder/config.cfg b/TestCases/fea_fsi/rotating_cylinder/config.cfg new file mode 100644 index 00000000000..7a9d124da76 --- /dev/null +++ b/TestCases/fea_fsi/rotating_cylinder/config.cfg @@ -0,0 +1,34 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SU2 configuration file % +% Case description: Spinning Cylinder % +% Author: Pedro Gomes % +% Institution: SU2 Foundation % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +SOLVER= ELASTICITY +MATH_PROBLEM= DIRECT +GEOMETRIC_CONDITIONS= SMALL_DEFORMATIONS +MATERIAL_MODEL= LINEAR_ELASTIC + +ELASTICITY_MODULUS= 2E11 +POISSON_RATIO= 0.3 +MATERIAL_DENSITY= 7854 + +MARKER_SYM= ( x_minus, per_1, per_2 ) +MARKER_PRESSURE= ( inner, 0, outer, 0, x_plus, 0 ) + +CENTRIFUGAL_FORCE= YES +ROTATION_RATE= ( 1500, 0, 0 ) +MOTION_ORIGIN= ( 0, 0, 0 ) + +LINEAR_SOLVER= CONJUGATE_GRADIENT +LINEAR_SOLVER_PREC= ILU +LINEAR_SOLVER_ERROR= 1E-8 +LINEAR_SOLVER_ITER= 1000 + +SCREEN_OUTPUT= INNER_ITER, RMS_RES, LINSOL, VMS +OUTPUT_WRT_FREQ= 1 +INNER_ITER= 1 + +MESH_FILENAME= cylinder.su2 + diff --git a/TestCases/flamelet/02_laminar_premixed_ch4_flame_hx_ad/lam_prem_ch4_hx_ad.cfg b/TestCases/flamelet/02_laminar_premixed_ch4_flame_hx_ad/lam_prem_ch4_hx_ad.cfg index ff52e8d58b6..b79c1f3b367 100644 --- a/TestCases/flamelet/02_laminar_premixed_ch4_flame_hx_ad/lam_prem_ch4_hx_ad.cfg +++ b/TestCases/flamelet/02_laminar_premixed_ch4_flame_hx_ad/lam_prem_ch4_hx_ad.cfg @@ -128,7 +128,7 @@ MARKER_ANALYZE_AVERAGE= MASSFLUX OUTPUT_FILES= RESTART, RESTART_ASCII, PARAVIEW VOLUME_OUTPUT= RESIDUAL, PRIMITIVE VOLUME_ADJ_FILENAME= ch4_flame_hx_ad - +WRT_RESTART_COMPACT = NO OUTPUT_WRT_FREQ= 100 % GRAD_OBJFUNC_FILENAME= of_grad.csv diff --git a/TestCases/flamelet/03_laminar_premixed_ch4_flame_cht_cfd/lam_prem_ch4_cht_cfd_fluid.cfg b/TestCases/flamelet/03_laminar_premixed_ch4_flame_cht_cfd/lam_prem_ch4_cht_cfd_fluid.cfg index adf9a65020f..af8e562b0cd 100644 --- a/TestCases/flamelet/03_laminar_premixed_ch4_flame_cht_cfd/lam_prem_ch4_cht_cfd_fluid.cfg +++ b/TestCases/flamelet/03_laminar_premixed_ch4_flame_cht_cfd/lam_prem_ch4_cht_cfd_fluid.cfg @@ -19,6 +19,8 @@ HISTORY_OUTPUT= RMS_RES AERO_COEFF FLOW_COEFF FLOW_COEFF_SURF VOLUME_OUTPUT= SOLUTION PRIMITIVE SOURCE RESIDUAL LOOKUP +WRT_RESTART_COMPACT = NO + OUTPUT_FILES= (RESTART_ASCII, PARAVIEW) INNER_ITER= 1 diff --git a/TestCases/flamelet/04_laminar_premixed_ch4_flame_cht_ad/lam_prem_ch4_cht_ad_fluid.cfg b/TestCases/flamelet/04_laminar_premixed_ch4_flame_cht_ad/lam_prem_ch4_cht_ad_fluid.cfg index 7824d6780fd..b251e54182a 100644 --- a/TestCases/flamelet/04_laminar_premixed_ch4_flame_cht_ad/lam_prem_ch4_cht_ad_fluid.cfg +++ b/TestCases/flamelet/04_laminar_premixed_ch4_flame_cht_ad/lam_prem_ch4_cht_ad_fluid.cfg @@ -23,6 +23,8 @@ VOLUME_OUTPUT= SOLUTION PRIMITIVE SOURCE RESIDUAL LOOKUP OUTPUT_FILES= ( RESTART_ASCII) +WRT_RESTART_COMPACT= NO + INNER_ITER= 1 % -------------------- BOUNDARY CONDITION DEFINITION --------------------------% diff --git a/TestCases/hybrid_regression.py b/TestCases/hybrid_regression.py index fe2634fb2a5..da3af4f5c9d 100644 --- a/TestCases/hybrid_regression.py +++ b/TestCases/hybrid_regression.py @@ -103,7 +103,7 @@ def main(): flatplate.cfg_dir = "navierstokes/flatplate" flatplate.cfg_file = "lam_flatplate.cfg" flatplate.test_iter = 100 - flatplate.test_vals = [-7.679131, -2.206953, 0.001084, 0.036233, 2.361500, -2.325300, -1.984700, -1.984700] + flatplate.test_vals = [-7.679131, -2.206953, 0.001084, 0.036233, 2.361500, -2.325300, 0, 0] test_list.append(flatplate) # Laminar cylinder (steady) @@ -111,7 +111,7 @@ def main(): cylinder.cfg_dir = "navierstokes/cylinder" cylinder.cfg_file = "lam_cylinder.cfg" cylinder.test_iter = 25 - cylinder.test_vals = [-8.266513, -2.783904, -0.019899, 1.615668, -0.010207] + cylinder.test_vals = [-8.266513, -2.783904, -0.019899, 1.615668, 0] test_list.append(cylinder) # Laminar cylinder (low Mach correction) @@ -119,8 +119,8 @@ def main(): cylinder_lowmach.cfg_dir = "navierstokes/cylinder" cylinder_lowmach.cfg_file = "cylinder_lowmach.cfg" cylinder_lowmach.test_iter = 25 - cylinder_lowmach.test_vals = [-6.830996, -1.368850, -0.143956, 73.963354, 0.002457] - cylinder_lowmach.test_vals_aarch64 = [-6.830996, -1.368850, -0.143956, 73.963354, 0.002457] + cylinder_lowmach.test_vals = [-6.830996, -1.368850, -0.143956, 73.963354, 0] + cylinder_lowmach.test_vals_aarch64 = [-6.830996, -1.368850, -0.143956, 73.963354, 0] test_list.append(cylinder_lowmach) # 2D Poiseuille flow (body force driven with periodic inlet / outlet) @@ -128,7 +128,7 @@ def main(): poiseuille.cfg_dir = "navierstokes/poiseuille" poiseuille.cfg_file = "lam_poiseuille.cfg" poiseuille.test_iter = 10 - poiseuille.test_vals = [-5.046131, 0.652984, 0.008355, 13.735818, -2.142500] + poiseuille.test_vals = [-5.046131, 0.652984, 0.008355, 13.735818, 0] test_list.append(poiseuille) # 2D Poiseuille flow (inlet profile file) @@ -157,7 +157,7 @@ def main(): rae2822_sa.cfg_dir = "rans/rae2822" rae2822_sa.cfg_file = "turb_SA_RAE2822.cfg" rae2822_sa.test_iter = 20 - rae2822_sa.test_vals = [-2.020123, -5.269324, 0.807147, 0.060499, -80602.000000] + rae2822_sa.test_vals = [-2.020123, -5.269339, 0.807147, 0.060499, 0] test_list.append(rae2822_sa) # RAE2822 SST @@ -165,7 +165,7 @@ def main(): rae2822_sst.cfg_dir = "rans/rae2822" rae2822_sst.cfg_file = "turb_SST_RAE2822.cfg" rae2822_sst.test_iter = 20 - rae2822_sst.test_vals = [-0.510363, 4.872736, 0.815617, 0.060920, -73391.000000] + rae2822_sst.test_vals = [-0.510363, 4.872736, 0.815617, 0.060920, 0] test_list.append(rae2822_sst) # RAE2822 SST_SUST @@ -181,7 +181,7 @@ def main(): turb_flatplate.cfg_dir = "rans/flatplate" turb_flatplate.cfg_file = "turb_SA_flatplate.cfg" turb_flatplate.test_iter = 20 - turb_flatplate.test_vals = [-4.312826, -6.736053, -0.187467, 0.057454] + turb_flatplate.test_vals = [-4.312725, -6.737976, -0.187467, 0.057468] test_list.append(turb_flatplate) # ONERA M6 Wing @@ -189,7 +189,7 @@ def main(): turb_oneram6.cfg_dir = "rans/oneram6" turb_oneram6.cfg_file = "turb_ONERAM6.cfg" turb_oneram6.test_iter = 10 - turb_oneram6.test_vals = [-2.408523, -6.662833, 0.238333, 0.158910, -52718] + turb_oneram6.test_vals = [-2.408523, -6.662833, 0.238333, 0.158910, 0] test_list.append(turb_oneram6) # NACA0012 (SA, FUN3D finest grid results: CL=1.0983, CD=0.01242) @@ -197,8 +197,8 @@ def main(): turb_naca0012_sa.cfg_dir = "rans/naca0012" turb_naca0012_sa.cfg_file = "turb_NACA0012_sa.cfg" turb_naca0012_sa.test_iter = 5 - turb_naca0012_sa.test_vals = [-12.098325, -14.149988, 1.057665, 0.022971, 20.000000, -2.292707, 0.000000, -12.068169, -44.871000] - turb_naca0012_sa.test_vals_aarch64 = [-12.098325, -14.149988, 1.057665, 0.022971, 20.000000, -2.292707, 0.000000, -12.068169, -44.871000] + turb_naca0012_sa.test_vals = [-12.098325, -14.149988, 1.057665, 0.022971, 20.000000, -2.292707, 0.000000, -12.068169, 0] + turb_naca0012_sa.test_vals_aarch64 = [-12.098325, -14.149988, 1.057665, 0.022971, 20.000000, -2.292707, 0.000000, -12.068169, 0] test_list.append(turb_naca0012_sa) # NACA0012 (SST, FUN3D finest grid results: CL=1.0840, CD=0.01253) @@ -206,7 +206,7 @@ def main(): turb_naca0012_sst.cfg_dir = "rans/naca0012" turb_naca0012_sst.cfg_file = "turb_NACA0012_sst.cfg" turb_naca0012_sst.test_iter = 10 - turb_naca0012_sst.test_vals = [-12.105781, -15.277738, -6.210248, 1.049757, 0.019249, -2.807857, -38.976000] + turb_naca0012_sst.test_vals = [-12.105781, -15.277738, -6.210248, 1.049757, 0.019249, -2.807857, 0] test_list.append(turb_naca0012_sst) # NACA0012 (SST_SUST, FUN3D finest grid results: CL=1.0840, CD=0.01253) @@ -238,7 +238,7 @@ def main(): propeller.cfg_dir = "rans/propeller" propeller.cfg_file = "propeller.cfg" propeller.test_iter = 10 - propeller.test_vals = [-3.389724, -8.409223, 0.000048, 0.056344] + propeller.test_vals = [-3.389724, -8.409502, 0.000048, 0.056344] test_list.append(propeller) ####################################### @@ -250,7 +250,7 @@ def main(): axi_rans_air_nozzle_restart.cfg_dir = "axisymmetric_rans/air_nozzle" axi_rans_air_nozzle_restart.cfg_file = "air_nozzle_restart.cfg" axi_rans_air_nozzle_restart.test_iter = 10 - axi_rans_air_nozzle_restart.test_vals = [-12.070954, -7.407644, -8.698118, -4.008751, -3572.100000] + axi_rans_air_nozzle_restart.test_vals = [-12.070954, -7.407644, -8.698118, -4.008751, 0] test_list.append(axi_rans_air_nozzle_restart) ################################# @@ -381,8 +381,8 @@ def main(): inc_poly_cylinder.cfg_dir = "incomp_navierstokes/cylinder" inc_poly_cylinder.cfg_file = "poly_cylinder.cfg" inc_poly_cylinder.test_iter = 20 - inc_poly_cylinder.test_vals = [-7.851512, -2.093420, 0.029974, 1.921595, -175.300000] - inc_poly_cylinder.test_vals_aarch64 = [-7.851510, -2.093419, 0.029974, 1.921595, -175.300000] + inc_poly_cylinder.test_vals = [-7.827942, -2.061513, 0.029525, 1.953498, -174.780000] + inc_poly_cylinder.test_vals_aarch64 = [-7.827942, -2.061513, 0.029525, 1.953498, -174.780000] test_list.append(inc_poly_cylinder) # X-coarse laminar bend as a mixed element CGNS test @@ -403,7 +403,7 @@ def main(): inc_turb_naca0012.cfg_dir = "incomp_rans/naca0012" inc_turb_naca0012.cfg_file = "naca0012.cfg" inc_turb_naca0012.test_iter = 20 - inc_turb_naca0012.test_vals = [-4.788405, -11.039465, 0.000008, 0.309490] + inc_turb_naca0012.test_vals = [-4.788405, -11.040560, 0.000008, 0.309505] test_list.append(inc_turb_naca0012) # NACA0012, SST_SUST @@ -452,8 +452,8 @@ def main(): square_cylinder.cfg_dir = "unsteady/square_cylinder" square_cylinder.cfg_file = "turb_square.cfg" square_cylinder.test_iter = 3 - square_cylinder.test_vals = [-2.560839, -1.173497, 0.061188, 1.399403, 2.220575, 1.399351, 2.218781, -0.584690] - square_cylinder.test_vals_aarch64 = [-2.557902, -1.173574, 0.058050, 1.399794, 2.220402, 1.399748, 2.218604, -0.453270] + square_cylinder.test_vals = [-2.560839, -1.173497, 0.061188, 1.399403, 2.220575, 1.399351, 2.218781, 0] + square_cylinder.test_vals_aarch64 = [-2.557902, -1.173574, 0.058050, 1.399794, 2.220402, 1.399748, 2.218604, 0] square_cylinder.unsteady = True test_list.append(square_cylinder) @@ -503,7 +503,7 @@ def main(): ddes_flatplate.cfg_dir = "ddes/flatplate" ddes_flatplate.cfg_file = "ddes_flatplate.cfg" ddes_flatplate.test_iter = 10 - ddes_flatplate.test_vals = [-2.714785, -5.882681, -0.215041, 0.023758, -617.440000] + ddes_flatplate.test_vals = [-2.714786, -5.882652, -0.215041, 0.023758, 0] ddes_flatplate.unsteady = True test_list.append(ddes_flatplate) @@ -680,8 +680,8 @@ def main(): statbeam3d.cfg_dir = "fea_fsi/StatBeam_3d" statbeam3d.cfg_file = "configBeam_3d.cfg" statbeam3d.test_iter = 0 - statbeam3d.test_vals = [-2.378370, -1.585252, -2.028505, 64359.000000] - statbeam3d.test_vals_aarch64 = [-2.382650, -1.561882, -2.045083, 64433.000000] + statbeam3d.test_vals = [-2.787802, -1.721974, -2.438436, 110350] + statbeam3d.test_vals_aarch64 = [-2.787802, -1.721974, -2.438436, 110350] test_list.append(statbeam3d) # Dynamic beam, 2d diff --git a/TestCases/hybrid_regression_AD.py b/TestCases/hybrid_regression_AD.py index a0e29d54c84..3cc8adf1a68 100644 --- a/TestCases/hybrid_regression_AD.py +++ b/TestCases/hybrid_regression_AD.py @@ -110,8 +110,8 @@ def main(): discadj_incomp_cylinder.cfg_dir = "disc_adj_incomp_navierstokes/cylinder" discadj_incomp_cylinder.cfg_file = "heated_cylinder.cfg" discadj_incomp_cylinder.test_iter = 20 - discadj_incomp_cylinder.test_vals = [20.000000, -2.705921, -2.837904, 0.000000] - discadj_incomp_cylinder.test_vals_aarch64 = [20.000000, -2.705918, -2.837766, 0.000000] + discadj_incomp_cylinder.test_vals = [20.000000, -2.746353, -2.934792, 0.000000] + discadj_incomp_cylinder.test_vals_aarch64 = [20.000000, -2.746353, -2.934792, 0.000000] test_list.append(discadj_incomp_cylinder) ###################################### diff --git a/TestCases/parallel_regression.py b/TestCases/parallel_regression.py index 5462560119e..530328b52a6 100644 --- a/TestCases/parallel_regression.py +++ b/TestCases/parallel_regression.py @@ -303,7 +303,7 @@ def main(): flatplate.cfg_dir = "navierstokes/flatplate" flatplate.cfg_file = "lam_flatplate.cfg" flatplate.test_iter = 100 - flatplate.test_vals = [-7.613122, -2.140941, 0.001084, 0.036230, 2.361500, -2.325300, -1.975600, -1.975600] + flatplate.test_vals = [-7.613122, -2.140941, 0.001084, 0.036230, 2.361500, -2.325300, 0.000000, 0.000000] test_list.append(flatplate) # Custom objective function @@ -319,7 +319,7 @@ def main(): cylinder.cfg_dir = "navierstokes/cylinder" cylinder.cfg_file = "lam_cylinder.cfg" cylinder.test_iter = 25 - cylinder.test_vals = [-8.422091, -2.930561, -0.003396, 1.608418, -0.009920] + cylinder.test_vals = [-8.422091, -2.930561, -0.003396, 1.608418, 0.000000] test_list.append(cylinder) # Laminar cylinder (low Mach correction) @@ -327,7 +327,7 @@ def main(): cylinder_lowmach.cfg_dir = "navierstokes/cylinder" cylinder_lowmach.cfg_file = "cylinder_lowmach.cfg" cylinder_lowmach.test_iter = 25 - cylinder_lowmach.test_vals = [-6.841604, -1.379532, -1.266739, 76.118218, 0.000274] + cylinder_lowmach.test_vals = [-6.841604, -1.379532, -1.266739, 76.118218, 0.000000] test_list.append(cylinder_lowmach) # 2D Poiseuille flow (body force driven with periodic inlet / outlet) @@ -335,7 +335,7 @@ def main(): poiseuille.cfg_dir = "navierstokes/poiseuille" poiseuille.cfg_file = "lam_poiseuille.cfg" poiseuille.test_iter = 10 - poiseuille.test_vals = [-5.050889, 0.648196, 0.000199, 13.639173, -2.114500] + poiseuille.test_vals = [-5.050889, 0.648196, 0.000199, 13.639173, 0.000000] poiseuille.tol = 0.001 test_list.append(poiseuille) @@ -357,7 +357,7 @@ def main(): rae2822_sa.cfg_dir = "rans/rae2822" rae2822_sa.cfg_file = "turb_SA_RAE2822.cfg" rae2822_sa.test_iter = 20 - rae2822_sa.test_vals = [-2.004689, -5.265782, 0.809463, 0.062016, -80576.000000] + rae2822_sa.test_vals = [-2.004689, -5.265797, 0.809463, 0.062016, 0] test_list.append(rae2822_sa) # RAE2822 SST @@ -365,7 +365,7 @@ def main(): rae2822_sst.cfg_dir = "rans/rae2822" rae2822_sst.cfg_file = "turb_SST_RAE2822.cfg" rae2822_sst.test_iter = 20 - rae2822_sst.test_vals = [-0.510369, 4.870643, 0.816647, 0.061833, -71779.000000] + rae2822_sst.test_vals = [-0.510369, 4.870643, 0.816647, 0.061833, 0.000000] test_list.append(rae2822_sst) # RAE2822 SST_SUST @@ -381,7 +381,7 @@ def main(): turb_flatplate.cfg_dir = "rans/flatplate" turb_flatplate.cfg_file = "turb_SA_flatplate.cfg" turb_flatplate.test_iter = 20 - turb_flatplate.test_vals = [-4.293562, -6.728390, -0.187643, 0.057686] + turb_flatplate.test_vals = [-4.293470, -6.730438, -0.187644, 0.057700] test_list.append(turb_flatplate) # Flat plate (compressible) with species inlet @@ -389,7 +389,7 @@ def main(): turb_flatplate_species.cfg_dir = "rans/flatplate" turb_flatplate_species.cfg_file = "turb_SA_flatplate_species.cfg" turb_flatplate_species.test_iter = 20 - turb_flatplate_species.test_vals = [-4.243136, -0.634954, -1.706721, 1.231179, -3.266212, 9.000000, -6.632862, 5.000000, -6.979197, 10.000000, -6.007240, 0.996237, 0.996237] + turb_flatplate_species.test_vals = [-4.243064, -0.634797, -1.706652, 1.231264, -3.266203, 9.000000, -6.632972, 5.000000, -6.985977, 10.000000, -6.007208, 0.996237, 0.996237] test_list.append(turb_flatplate_species) # Flat plate SST compressibility correction Wilcox @@ -413,7 +413,7 @@ def main(): turb_oneram6.cfg_dir = "rans/oneram6" turb_oneram6.cfg_file = "turb_ONERAM6.cfg" turb_oneram6.test_iter = 10 - turb_oneram6.test_vals = [-2.408532, -6.662836, 0.238334, 0.158910, -52718] + turb_oneram6.test_vals = [-2.408532, -6.662836, 0.238334, 0.158910, 0] turb_oneram6.timeout = 3200 test_list.append(turb_oneram6) @@ -422,7 +422,7 @@ def main(): turb_oneram6_vc.cfg_dir = "rans/oneram6" turb_oneram6_vc.cfg_file = "turb_ONERAM6_vc.cfg" turb_oneram6_vc.test_iter = 15 - turb_oneram6_vc.test_vals = [-2.281896, -6.614616, 0.233785, 0.142861, -47942] + turb_oneram6_vc.test_vals = [-2.281896, -6.614616, 0.233785, 0.142861, 0] turb_oneram6_vc.timeout = 3200 test_list.append(turb_oneram6_vc) @@ -431,7 +431,7 @@ def main(): turb_oneram6_nk.cfg_dir = "rans/oneram6" turb_oneram6_nk.cfg_file = "turb_ONERAM6_nk.cfg" turb_oneram6_nk.test_iter = 20 - turb_oneram6_nk.test_vals = [-4.843772, -4.444210, -11.473964, 0.216337, 0.049646, 5.000000, -0.613234, 23.568000] + turb_oneram6_nk.test_vals = [-4.851388, -4.457414, -11.468726, 0.217228, 0.049043, 5.000000, -0.533763, 23.567000] turb_oneram6_nk.timeout = 600 turb_oneram6_nk.tol = 0.0001 test_list.append(turb_oneram6_nk) @@ -441,7 +441,7 @@ def main(): turb_naca0012_sa.cfg_dir = "rans/naca0012" turb_naca0012_sa.cfg_file = "turb_NACA0012_sa.cfg" turb_naca0012_sa.test_iter = 5 - turb_naca0012_sa.test_vals = [-12.094721, -14.685268, 1.057665, 0.022971, 20.000000, -1.692925, 20.000000, -4.037672, -44.871000] + turb_naca0012_sa.test_vals = [-12.094695, -14.685268, 1.057665, 0.022971, 20.000000, -1.692967, 20.000000, -4.037673, 0] turb_naca0012_sa.timeout = 3200 test_list.append(turb_naca0012_sa) @@ -450,8 +450,8 @@ def main(): turb_naca0012_sst.cfg_dir = "rans/naca0012" turb_naca0012_sst.cfg_file = "turb_NACA0012_sst.cfg" turb_naca0012_sst.test_iter = 10 - turb_naca0012_sst.test_vals = [-12.107692, -15.277743, -6.210238, 1.049757, 0.019249, -2.357984, -38.976000] - turb_naca0012_sst.test_vals_aarch64 = [-12.107692, -15.277743, -6.210238, 1.049757, 0.019249, -2.357984, -38.976000] + turb_naca0012_sst.test_vals = [-12.107692, -15.277743, -6.210238, 1.049757, 0.019249, -2.357984, 0] + turb_naca0012_sst.test_vals_aarch64 = [-12.107692, -15.277743, -6.210238, 1.049757, 0.019249, -2.357984, 0] turb_naca0012_sst.timeout = 3200 test_list.append(turb_naca0012_sst) @@ -507,7 +507,7 @@ def main(): propeller.cfg_dir = "rans/propeller" propeller.cfg_file = "propeller.cfg" propeller.test_iter = 10 - propeller.test_vals = [-3.389724, -8.409223, 0.000048, 0.056344] + propeller.test_vals = [-3.389724, -8.409502, 0.000048, 0.056344] propeller.timeout = 3200 test_list.append(propeller) @@ -530,7 +530,7 @@ def main(): axi_rans_air_nozzle_restart.cfg_dir = "axisymmetric_rans/air_nozzle" axi_rans_air_nozzle_restart.cfg_file = "air_nozzle_restart.cfg" axi_rans_air_nozzle_restart.test_iter = 10 - axi_rans_air_nozzle_restart.test_vals = [-12.071395, -7.467871, -8.649076, -3.995810, -3572.100000] + axi_rans_air_nozzle_restart.test_vals = [-12.071395, -7.467871, -8.649076, -3.995810, 0] axi_rans_air_nozzle_restart.tol = 0.0001 test_list.append(axi_rans_air_nozzle_restart) @@ -602,7 +602,7 @@ def main(): inc_poly_cylinder.cfg_dir = "incomp_navierstokes/cylinder" inc_poly_cylinder.cfg_file = "poly_cylinder.cfg" inc_poly_cylinder.test_iter = 20 - inc_poly_cylinder.test_vals = [-7.791831, -2.062292, 0.013040, 1.913997, -171.120000] + inc_poly_cylinder.test_vals = [-7.786564, -2.036735, 0.012980, 1.944887, -170.930000] test_list.append(inc_poly_cylinder) # X-coarse laminar bend as a mixed element CGNS test @@ -638,7 +638,7 @@ def main(): inc_turb_naca0012.cfg_dir = "incomp_rans/naca0012" inc_turb_naca0012.cfg_file = "naca0012.cfg" inc_turb_naca0012.test_iter = 20 - inc_turb_naca0012.test_vals = [-4.788596, -11.039529, -0.000002, 0.309504] + inc_turb_naca0012.test_vals = [-4.788595, -11.040625, -0.000002, 0.309519] test_list.append(inc_turb_naca0012) # NACA0012, SST_SUST @@ -724,7 +724,7 @@ def main(): turbmod_sa_bsl_rae2822.cfg_dir = "turbulence_models/sa/rae2822" turbmod_sa_bsl_rae2822.cfg_file = "turb_SA_BSL_RAE2822.cfg" turbmod_sa_bsl_rae2822.test_iter = 20 - turbmod_sa_bsl_rae2822.test_vals = [-2.004689, 0.742306, 0.497308, -5.265782, 0.809463, 0.062016] + turbmod_sa_bsl_rae2822.test_vals = [-2.004689, 0.742306, 0.497308, -5.265797, 0.809463, 0.062016] test_list.append(turbmod_sa_bsl_rae2822) # SA Negative @@ -732,8 +732,8 @@ def main(): turbmod_sa_neg_rae2822.cfg_dir = "turbulence_models/sa/rae2822" turbmod_sa_neg_rae2822.cfg_file = "turb_SA_NEG_RAE2822.cfg" turbmod_sa_neg_rae2822.test_iter = 10 - turbmod_sa_neg_rae2822.test_vals = [-1.466238, 3.169232, 2.756518, 4.722767, 1.120253, 0.378675, -83444.000000] - turbmod_sa_neg_rae2822.test_vals_aarch64 = [-1.359612, 1.493629, 1.218367, -1.441703, 1.248499, 0.457987, -86467.000000] + turbmod_sa_neg_rae2822.test_vals = [-1.204800, 1.611685, 1.349330, 1.489602, 1.263603, 0.466487, 0] + turbmod_sa_neg_rae2822.test_vals_aarch64 = [-1.359612, 1.493629, 1.218367, -1.441703, 1.248499, 0.457987, 0] test_list.append(turbmod_sa_neg_rae2822) # SA Compressibility Correction @@ -741,7 +741,7 @@ def main(): turbmod_sa_comp_rae2822.cfg_dir = "turbulence_models/sa/rae2822" turbmod_sa_comp_rae2822.cfg_file = "turb_SA_COMP_RAE2822.cfg" turbmod_sa_comp_rae2822.test_iter = 20 - turbmod_sa_comp_rae2822.test_vals = [-2.004687, 0.742304, 0.497309, -5.266067, 0.809467, 0.062029] + turbmod_sa_comp_rae2822.test_vals = [-2.004687, 0.742304, 0.497309, -5.266084, 0.809467, 0.062029] test_list.append(turbmod_sa_comp_rae2822) # SA Edwards @@ -765,7 +765,7 @@ def main(): turbmod_sa_qcr_rae2822.cfg_dir = "turbulence_models/sa/rae2822" turbmod_sa_qcr_rae2822.cfg_file = "turb_SA_QCR_RAE2822.cfg" turbmod_sa_qcr_rae2822.test_iter = 20 - turbmod_sa_qcr_rae2822.test_vals = [-2.004793, 0.742353, 0.497315, -5.265962, 0.807841, 0.062027] + turbmod_sa_qcr_rae2822.test_vals = [-2.004793, 0.742353, 0.497315, -5.265977, 0.807841, 0.062027] test_list.append(turbmod_sa_qcr_rae2822) ############################ @@ -974,7 +974,7 @@ def main(): square_cylinder.cfg_dir = "unsteady/square_cylinder" square_cylinder.cfg_file = "turb_square.cfg" square_cylinder.test_iter = 3 - square_cylinder.test_vals = [-1.173495, 0.061186, 1.399404, 2.220578, 1.399352, 2.218783, -0.584750] + square_cylinder.test_vals = [-1.173495, 0.061186, 1.399404, 2.220578, 1.399352, 2.218783, 0] square_cylinder.unsteady = True test_list.append(square_cylinder) @@ -1001,7 +1001,7 @@ def main(): ddes_flatplate.cfg_dir = "ddes/flatplate" ddes_flatplate.cfg_file = "ddes_flatplate.cfg" ddes_flatplate.test_iter = 10 - ddes_flatplate.test_vals = [-2.714785, -5.882679, -0.215041, 0.023758, -617.450000] + ddes_flatplate.test_vals = [-2.714786, -5.882637, -0.215041, 0.023758, 0] ddes_flatplate.unsteady = True test_list.append(ddes_flatplate) @@ -1010,7 +1010,7 @@ def main(): unst_inc_turb_naca0015_sa.cfg_dir = "unsteady/pitching_naca0015_rans_inc" unst_inc_turb_naca0015_sa.cfg_file = "config_incomp_turb_sa.cfg" unst_inc_turb_naca0015_sa.test_iter = 1 - unst_inc_turb_naca0015_sa.test_vals = [-3.004012, -6.876220, 1.487882, 0.421861] + unst_inc_turb_naca0015_sa.test_vals = [-3.004011, -6.876250, 1.487888, 0.421869] unst_inc_turb_naca0015_sa.unsteady = True test_list.append(unst_inc_turb_naca0015_sa) @@ -1220,11 +1220,22 @@ def main(): statbeam3d.cfg_dir = "fea_fsi/StatBeam_3d" statbeam3d.cfg_file = "configBeam_3d.cfg" statbeam3d.test_iter = 0 - statbeam3d.test_vals = [-8.396797, -8.162206, -8.156102, 64095.000000] - statbeam3d.test_vals_aarch64 = [-8.396793, -8.162255, -8.156118, 64095.0] #last 4 columns + statbeam3d.test_vals = [-6.058758, -5.750933, -5.892188, 110190] + statbeam3d.test_vals_aarch64 = [-6.058758, -5.750933, -5.892188, 110190] statbeam3d.command = TestCase.Command(exec = "parallel_computation_fsi.py", param = "-f") test_list.append(statbeam3d) + # Rotating cylinder, 3d + rotating_cylinder_fea = TestCase('rotating_cylinder_fea') + rotating_cylinder_fea.cfg_dir = "fea_fsi/rotating_cylinder" + rotating_cylinder_fea.cfg_file = "config.cfg" + rotating_cylinder_fea.test_iter = 0 + # For a thin disk with the inner and outer radius of this geometry, from + # "Formulas for Stress, Strain, and Structural Matrices", 2nd Edition, figure 19-4, + # the maximum stress is 165.6MPa, we get a Von Misses stress very close to that. + rotating_cylinder_fea.test_vals = [-6.861940, -6.835545, -6.895500, 22, -8.313847, 1.6502e+08] + test_list.append(rotating_cylinder_fea) + # Dynamic beam, 2d dynbeam2d = TestCase('dynbeam2d') dynbeam2d.cfg_dir = "fea_fsi/DynBeam_2d" @@ -1282,7 +1293,7 @@ def main(): p1rad.cfg_dir = "radiation/p1model" p1rad.cfg_file = "configp1.cfg" p1rad.test_iter = 100 - p1rad.test_vals = [-7.743666, -7.921411, -2.111848, 0.098302, -45.023000] + p1rad.test_vals = [-7.743666, -7.921411, -2.111848, 0.098302, -47.897000] test_list.append(p1rad) @@ -1359,8 +1370,8 @@ def main(): pywrapper_turb_naca0012_sst.cfg_dir = "rans/naca0012" pywrapper_turb_naca0012_sst.cfg_file = "turb_NACA0012_sst.cfg" pywrapper_turb_naca0012_sst.test_iter = 10 - pywrapper_turb_naca0012_sst.test_vals = [-12.107692, -15.277743, -6.210238, 1.049757, 0.019249, -2.357984, -38.976000] - pywrapper_turb_naca0012_sst.test_vals_aarch64 = [-12.107692, -15.277743, -6.210238, 1.049757, 0.019249, -2.357984, -38.976000] + pywrapper_turb_naca0012_sst.test_vals = [-12.107692, -15.277743, -6.210238, 1.049757, 0.019249, -2.357984, 0] + pywrapper_turb_naca0012_sst.test_vals_aarch64 = [-12.107692, -15.277743, -6.210238, 1.049757, 0.019249, -2.357984, 0] pywrapper_turb_naca0012_sst.command = TestCase.Command("mpirun -np 2", "SU2_CFD.py", "--parallel -f") pywrapper_turb_naca0012_sst.timeout = 3200 test_list.append(pywrapper_turb_naca0012_sst) @@ -1370,7 +1381,7 @@ def main(): pywrapper_square_cylinder.cfg_dir = "unsteady/square_cylinder" pywrapper_square_cylinder.cfg_file = "turb_square.cfg" pywrapper_square_cylinder.test_iter = 10 - pywrapper_square_cylinder.test_vals = [-1.176405, -0.354027, 1.407859, 2.360784, 1.404715, 2.302615, -0.394750] + pywrapper_square_cylinder.test_vals = [-1.176405, -0.354027, 1.407859, 2.360784, 1.404715, 2.302615, 0] pywrapper_square_cylinder.command = TestCase.Command("mpirun -np 2", "SU2_CFD.py", "--parallel -f") pywrapper_square_cylinder.unsteady = True test_list.append(pywrapper_square_cylinder) @@ -1422,7 +1433,7 @@ def main(): pywrapper_unsteadyCHT.cfg_dir = "py_wrapper/flatPlate_unsteady_CHT" pywrapper_unsteadyCHT.cfg_file = "unsteady_CHT_FlatPlate_Conf.cfg" pywrapper_unsteadyCHT.test_iter = 5 - pywrapper_unsteadyCHT.test_vals = [-1.614167, 2.240663, -0.001316, 0.177491] + pywrapper_unsteadyCHT.test_vals = [-1.614167, 2.240663, -0.001392, 0.177499] pywrapper_unsteadyCHT.command = TestCase.Command("mpirun -np 2", "python", "launch_unsteady_CHT_FlatPlate.py --parallel -f") pywrapper_unsteadyCHT.unsteady = True test_list.append(pywrapper_unsteadyCHT) @@ -1530,7 +1541,7 @@ def main(): species2_primitiveVenturi_mixingmodel_viscosity.cfg_dir = "species_transport/venturi_primitive_3species" species2_primitiveVenturi_mixingmodel_viscosity.cfg_file = "species2_primitiveVenturi_mixingmodel_viscosity.cfg" species2_primitiveVenturi_mixingmodel_viscosity.test_iter = 50 - species2_primitiveVenturi_mixingmodel_viscosity.test_vals = [-4.857397, -3.646605, -3.737462, -7.602922, -5.008846, 5.000000, -1.756226, 5.000000, -3.163353, 5.000000, -2.189723, 2.476808, 0.976999, 0.609280, 0.890529] + species2_primitiveVenturi_mixingmodel_viscosity.test_vals = [-4.857405, -3.646534, -3.737422, -7.602756, -5.008835, 5.000000, -1.756256, 5.000000, -3.163548, 5.000000, -2.189690, 2.476807, 0.977000, 0.609279, 0.890528] test_list.append(species2_primitiveVenturi_mixingmodel_viscosity) # 2 species (1 eq) primitive venturi mixing using mixing model including heat capacity and mass diffusivity diff --git a/TestCases/parallel_regression_AD.py b/TestCases/parallel_regression_AD.py index 7d0f0d10049..f54849610f4 100644 --- a/TestCases/parallel_regression_AD.py +++ b/TestCases/parallel_regression_AD.py @@ -83,7 +83,7 @@ def main(): discadj_rans_naca0012_sa.cfg_dir = "disc_adj_rans/naca0012" discadj_rans_naca0012_sa.cfg_file = "turb_NACA0012_sa.cfg" discadj_rans_naca0012_sa.test_iter = 10 - discadj_rans_naca0012_sa.test_vals = [-2.996963, -0.196020, 0.000004, -0.000000, 5.000000, -3.430615, 5.000000, -7.411396] + discadj_rans_naca0012_sa.test_vals = [-2.996963, -0.196020, 0.000004, -0.000000, 5.000000, -3.430615, 5.000000, -7.411381] test_list.append(discadj_rans_naca0012_sa) # Adjoint turbulent NACA0012 SST @@ -116,7 +116,7 @@ def main(): discadj_incomp_cylinder.cfg_dir = "disc_adj_incomp_navierstokes/cylinder" discadj_incomp_cylinder.cfg_file = "heated_cylinder.cfg" discadj_incomp_cylinder.test_iter = 20 - discadj_incomp_cylinder.test_vals = [20.000000, -2.195581, -2.162081, 0.000000] + discadj_incomp_cylinder.test_vals = [20.000000, -2.082673, -2.013587, 0.000000] test_list.append(discadj_incomp_cylinder) ###################################### @@ -256,7 +256,7 @@ def main(): discadj_heat.cfg_dir = "disc_adj_heat" discadj_heat.cfg_file = "disc_adj_heat.cfg" discadj_heat.test_iter = 10 - discadj_heat.test_vals = [-2.117791, 0.784475, 0.000000, -0.574700] + discadj_heat.test_vals = [-1.840134, 0.750337, 0.000000, 0.006760] discadj_heat.test_vals_aarch64 = [-2.226539, 0.605868, 0.000000, -6.256400] test_list.append(discadj_heat) diff --git a/TestCases/py_wrapper/custom_load_fea/run_ad.py b/TestCases/py_wrapper/custom_load_fea/run_ad.py index dfb9fab4fab..dc643ad67a1 100644 --- a/TestCases/py_wrapper/custom_load_fea/run_ad.py +++ b/TestCases/py_wrapper/custom_load_fea/run_ad.py @@ -61,6 +61,7 @@ OUTPUT_WRT_FREQ= 1 OBJECTIVE_FUNCTION= STRESS_PENALTY STRESS_PENALTY_PARAM= ( 500, 20 ) +WRT_RESTART_COMPACT= NO INNER_ITER= 20 CONV_RESIDUAL_MINVAL= -4 diff --git a/TestCases/py_wrapper/disc_adj_fea/flow_load_sens/configAD_fem.cfg b/TestCases/py_wrapper/disc_adj_fea/flow_load_sens/configAD_fem.cfg index 63edf6869cf..b5d0f15d944 100644 --- a/TestCases/py_wrapper/disc_adj_fea/flow_load_sens/configAD_fem.cfg +++ b/TestCases/py_wrapper/disc_adj_fea/flow_load_sens/configAD_fem.cfg @@ -35,7 +35,6 @@ MATERIAL_COMPRESSIBILITY= COMPRESSIBLE ELASTICITY_MODULUS=21000 POISSON_RATIO=0.4 MATERIAL_DENSITY=100 -DEAD_LOAD=NO FORMULATION_ELASTICITY_2D = PLANE_STRAIN NONLINEAR_FEM_SOLUTION_METHOD = NEWTON_RAPHSON diff --git a/TestCases/radiation/p1adjoint/of_grad_cd.csv.ref b/TestCases/radiation/p1adjoint/of_grad_cd.csv.ref index 8c422aae719..0d3e4c92dbc 100644 --- a/TestCases/radiation/p1adjoint/of_grad_cd.csv.ref +++ b/TestCases/radiation/p1adjoint/of_grad_cd.csv.ref @@ -1,51 +1,51 @@ "VARIABLE" , "GRADIENT" , "FINDIFF_STEP" -0 , -0.000954218 , 0.001 -1 , -0.00432384 , 0.001 -2 , -0.0111485 , 0.001 -3 , -0.0214656 , 0.001 -4 , -0.0344212 , 0.001 -5 , -0.0490221 , 0.001 -6 , -0.0643278 , 0.001 -7 , -0.0792041 , 0.001 -8 , -0.0924318 , 0.001 -9 , -0.103025 , 0.001 -10 , -0.110239 , 0.001 -11 , -0.113383 , 0.001 -12 , -0.111898 , 0.001 -13 , -0.105745 , 0.001 -14 , -0.0956773 , 0.001 -15 , -0.0830288 , 0.001 -16 , -0.069201 , 0.001 -17 , -0.0553022 , 0.001 -18 , -0.0421316 , 0.001 -19 , -0.0303252 , 0.001 -20 , -0.0203976 , 0.001 -21 , -0.0126387 , 0.001 -22 , -0.00701749 , 0.001 -23 , -0.0032361 , 0.001 -24 , -0.000955756 , 0.001 -25 , 0.00128953 , 0.001 -26 , 0.00518424 , 0.001 -27 , 0.0125712 , 0.001 -28 , 0.0234942 , 0.001 -29 , 0.0370801 , 0.001 -30 , 0.052231 , 0.001 -31 , 0.06787 , 0.001 -32 , 0.0827503 , 0.001 -33 , 0.0955927 , 0.001 -34 , 0.105415 , 0.001 -35 , 0.11154 , 0.001 -36 , 0.113399 , 0.001 -37 , 0.110583 , 0.001 -38 , 0.103215 , 0.001 -39 , 0.0921957 , 0.001 -40 , 0.0789703 , 0.001 -41 , 0.0650043 , 0.001 -42 , 0.0514107 , 0.001 -43 , 0.0389322 , 0.001 -44 , 0.0280921 , 0.001 -45 , 0.0192441 , 0.001 -46 , 0.0124768 , 0.001 -47 , 0.00752972 , 0.001 -48 , 0.00391057 , 0.001 -49 , 0.00131276 , 0.001 +0 , -0.000958205 , 0.001 +1 , -0.00432733 , 0.001 +2 , -0.0111443 , 0.001 +3 , -0.0214515 , 0.001 +4 , -0.0343997 , 0.001 +5 , -0.0489968 , 0.001 +6 , -0.0643023 , 0.001 +7 , -0.0791818 , 0.001 +8 , -0.092416 , 0.001 +9 , -0.103017 , 0.001 +10 , -0.11024 , 0.001 +11 , -0.113392 , 0.001 +12 , -0.111913 , 0.001 +13 , -0.105766 , 0.001 +14 , -0.0957015 , 0.001 +15 , -0.0830517 , 0.001 +16 , -0.0692182 , 0.001 +17 , -0.0553101 , 0.001 +18 , -0.042129 , 0.001 +19 , -0.0303137 , 0.001 +20 , -0.0203813 , 0.001 +21 , -0.0126226 , 0.001 +22 , -0.00700441 , 0.001 +23 , -0.00322726 , 0.001 +24 , -0.000951904 , 0.001 +25 , 0.00128803 , 0.001 +26 , 0.0051774 , 0.001 +27 , 0.0125547 , 0.001 +28 , 0.0234674 , 0.001 +29 , 0.0370465 , 0.001 +30 , 0.0521956 , 0.001 +31 , 0.0678379 , 0.001 +32 , 0.0827257 , 0.001 +33 , 0.0955787 , 0.001 +34 , 0.105412 , 0.001 +35 , 0.111548 , 0.001 +36 , 0.113415 , 0.001 +37 , 0.110605 , 0.001 +38 , 0.103242 , 0.001 +39 , 0.0922244 , 0.001 +40 , 0.0789963 , 0.001 +41 , 0.0650234 , 0.001 +42 , 0.0514199 , 0.001 +43 , 0.0389312 , 0.001 +44 , 0.0280831 , 0.001 +45 , 0.0192316 , 0.001 +46 , 0.0124655 , 0.001 +47 , 0.00752232 , 0.001 +48 , 0.0039072 , 0.001 +49 , 0.00131195 , 0.001 diff --git a/TestCases/serial_regression.py b/TestCases/serial_regression.py index 10312c92cc3..b4b9cd473a9 100644 --- a/TestCases/serial_regression.py +++ b/TestCases/serial_regression.py @@ -166,7 +166,7 @@ def main(): flatplate.cfg_dir = "navierstokes/flatplate" flatplate.cfg_file = "lam_flatplate.cfg" flatplate.test_iter = 20 - flatplate.test_vals = [-5.097707, 0.381809, 0.001324, 0.027932, 2.361600, -2.333600, -2.845200, -2.845200] + flatplate.test_vals = [-5.097707, 0.381809, 0.001324, 0.027932, 2.361600, -2.333600, 0, 0] test_list.append(flatplate) # Laminar cylinder (steady) @@ -174,7 +174,7 @@ def main(): cylinder.cfg_dir = "navierstokes/cylinder" cylinder.cfg_file = "lam_cylinder.cfg" cylinder.test_iter = 25 - cylinder.test_vals = [-8.363995, -2.882536, -0.017780, 1.607979, -0.010080] + cylinder.test_vals = [-8.363995, -2.882536, -0.017780, 1.607979, 0] test_list.append(cylinder) # Laminar cylinder (low Mach correction) @@ -182,7 +182,7 @@ def main(): cylinder_lowmach.cfg_dir = "navierstokes/cylinder" cylinder_lowmach.cfg_file = "cylinder_lowmach.cfg" cylinder_lowmach.test_iter = 25 - cylinder_lowmach.test_vals = [-6.830989, -1.368842, -0.143838, 73.962440, 0.002454] + cylinder_lowmach.test_vals = [-6.830989, -1.368842, -0.143838, 73.962440, 0] test_list.append(cylinder_lowmach) # 2D Poiseuille flow (body force driven with periodic inlet / outlet) @@ -190,7 +190,7 @@ def main(): poiseuille.cfg_dir = "navierstokes/poiseuille" poiseuille.cfg_file = "lam_poiseuille.cfg" poiseuille.test_iter = 10 - poiseuille.test_vals = [-5.050753, 0.648333, 0.012273, 13.643141, -2.114700] + poiseuille.test_vals = [-5.050753, 0.648333, 0.012273, 13.643141, 0] test_list.append(poiseuille) # 2D Poiseuille flow (inlet profile file) @@ -218,7 +218,7 @@ def main(): rae2822_sa.cfg_dir = "rans/rae2822" rae2822_sa.cfg_file = "turb_SA_RAE2822.cfg" rae2822_sa.test_iter = 20 - rae2822_sa.test_vals = [-2.020123, -5.269324, 0.807147, 0.060499, -80602.000000] + rae2822_sa.test_vals = [-2.020123, -5.269339, 0.807147, 0.060499, 0] test_list.append(rae2822_sa) # RAE2822 SST @@ -226,7 +226,7 @@ def main(): rae2822_sst.cfg_dir = "rans/rae2822" rae2822_sst.cfg_file = "turb_SST_RAE2822.cfg" rae2822_sst.test_iter = 20 - rae2822_sst.test_vals = [-0.510376, 4.873617, 0.816197, 0.060504, -71886.000000] + rae2822_sst.test_vals = [-0.510376, 4.873617, 0.816197, 0.060504, 0] test_list.append(rae2822_sst) # RAE2822 SST_SUST @@ -242,7 +242,7 @@ def main(): turb_flatplate.cfg_dir = "rans/flatplate" turb_flatplate.cfg_file = "turb_SA_flatplate.cfg" turb_flatplate.test_iter = 20 - turb_flatplate.test_vals = [-4.312826, -6.736053, -0.187467, 0.057454] + turb_flatplate.test_vals = [-4.312724, -6.737976, -0.187467, 0.057468] test_list.append(turb_flatplate) # FLAT PLATE, WALL FUNCTIONS, COMPRESSIBLE SST @@ -258,7 +258,7 @@ def main(): turb_wallfunction_flatplate_sa.cfg_dir = "wallfunctions/flatplate/compressible_SA" turb_wallfunction_flatplate_sa.cfg_file = "turb_SA_flatplate.cfg" turb_wallfunction_flatplate_sa.test_iter = 10 - turb_wallfunction_flatplate_sa.test_vals = [-4.460537, -2.033605, -2.117564, 0.889626, -5.381903, 10.000000, -1.517487, 0.034212, 0.002636] + turb_wallfunction_flatplate_sa.test_vals = [-4.460657, -2.033641, -2.118149, 0.889562, -5.382249, 10.000000, -1.517453, 0.034213, 0.002636] test_list.append(turb_wallfunction_flatplate_sa) # ONERA M6 Wing @@ -266,7 +266,7 @@ def main(): turb_oneram6.cfg_dir = "rans/oneram6" turb_oneram6.cfg_file = "turb_ONERAM6.cfg" turb_oneram6.test_iter = 10 - turb_oneram6.test_vals = [-2.408533, -6.662837, 0.238334, 0.158910, -52718] + turb_oneram6.test_vals = [-2.408533, -6.662837, 0.238334, 0.158910, 0] turb_oneram6.timeout = 3200 test_list.append(turb_oneram6) @@ -275,7 +275,7 @@ def main(): turb_naca0012_sa.cfg_dir = "rans/naca0012" turb_naca0012_sa.cfg_file = "turb_NACA0012_sa.cfg" turb_naca0012_sa.test_iter = 5 - turb_naca0012_sa.test_vals = [-12.091696, -14.685322, 1.057665, 0.022971, 20.000000, -2.686306, 20.000000, -4.459916, -44.871000] + turb_naca0012_sa.test_vals = [-12.091778, -14.685322, 1.057665, 0.022971, 20.000000, -2.686203, 20.000000, -4.459916, 0] turb_naca0012_sa.timeout = 3200 test_list.append(turb_naca0012_sa) @@ -284,8 +284,8 @@ def main(): turb_naca0012_sst.cfg_dir = "rans/naca0012" turb_naca0012_sst.cfg_file = "turb_NACA0012_sst.cfg" turb_naca0012_sst.test_iter = 10 - turb_naca0012_sst.test_vals = [-12.107132, -15.277740, -6.210248, 1.049757, 0.019249, -3.173936, -38.97600] - turb_naca0012_sst.test_vals_aarch64 = [-12.107132, -15.277740, -6.210248, 1.049757, 0.019249, -3.173936, -38.97600] + turb_naca0012_sst.test_vals = [-12.107132, -15.277740, -6.210248, 1.049757, 0.019249, -3.173936, 0] + turb_naca0012_sst.test_vals_aarch64 = [-12.107132, -15.277740, -6.210248, 1.049757, 0.019249, -3.173936, 0] turb_naca0012_sst.timeout = 3200 test_list.append(turb_naca0012_sst) @@ -294,7 +294,7 @@ def main(): turb_naca0012_sst_2003m.cfg_dir = "rans/naca0012" turb_naca0012_sst_2003m.cfg_file = "turb_NACA0012_sst_2003m.cfg" turb_naca0012_sst_2003m.test_iter = 10 - turb_naca0012_sst_2003m.test_vals = [-7.688139, -10.046053, -3.410061, 1.048970, 0.019798, -2.208236, -45.678000] + turb_naca0012_sst_2003m.test_vals = [-7.688139, -10.046053, -3.410061, 1.048970, 0.019798, -2.208236, 0] turb_naca0012_sst_2003m.timeout = 3200 test_list.append(turb_naca0012_sst_2003m) @@ -322,7 +322,7 @@ def main(): propeller.cfg_dir = "rans/propeller" propeller.cfg_file = "propeller.cfg" propeller.test_iter = 10 - propeller.test_vals = [-3.389724, -8.409223, 0.000048, 0.056344] + propeller.test_vals = [-3.389724, -8.409502, 0.000048, 0.056344] propeller.timeout = 3200 test_list.append(propeller) @@ -335,8 +335,8 @@ def main(): axi_rans_air_nozzle_restart.cfg_dir = "axisymmetric_rans/air_nozzle" axi_rans_air_nozzle_restart.cfg_file = "air_nozzle_restart.cfg" axi_rans_air_nozzle_restart.test_iter = 10 - axi_rans_air_nozzle_restart.test_vals = [-12.071702, -7.474599, -8.646498, -3.988633, -3572.100000] - axi_rans_air_nozzle_restart.test_vals_aarch64 = [-12.071702, -7.474599, -8.646498, -3.988633, -3572.100000] + axi_rans_air_nozzle_restart.test_vals = [-12.071702, -7.474599, -8.646498, -3.988633, 0] + axi_rans_air_nozzle_restart.test_vals_aarch64 = [-12.071702, -7.474599, -8.646498, -3.988633, 0] axi_rans_air_nozzle_restart.tol = 0.0001 test_list.append(axi_rans_air_nozzle_restart) @@ -414,7 +414,7 @@ def main(): inc_poly_cylinder.cfg_dir = "incomp_navierstokes/cylinder" inc_poly_cylinder.cfg_file = "poly_cylinder.cfg" inc_poly_cylinder.test_iter = 20 - inc_poly_cylinder.test_vals = [-8.106741, -2.160042, 0.019225, 1.902421, -172.750000] + inc_poly_cylinder.test_vals = [-8.083556, -2.134369, 0.018999, 1.932938, -173.730000] test_list.append(inc_poly_cylinder) # X-coarse laminar bend as a mixed element CGNS test @@ -441,7 +441,7 @@ def main(): inc_turb_naca0012.cfg_dir = "incomp_rans/naca0012" inc_turb_naca0012.cfg_file = "naca0012.cfg" inc_turb_naca0012.test_iter = 20 - inc_turb_naca0012.test_vals = [-4.788496, -11.039482, 0.000023, 0.309488] + inc_turb_naca0012.test_vals = [-4.788495, -11.040578, 0.000023, 0.309503] test_list.append(inc_turb_naca0012) # NACA0012, SST_SUST @@ -457,7 +457,7 @@ def main(): inc_turb_wallfunction_flatplate_sst.cfg_dir = "wallfunctions/flatplate/incompressible_SST" inc_turb_wallfunction_flatplate_sst.cfg_file = "turb_SST_flatplate.cfg" inc_turb_wallfunction_flatplate_sst.test_iter = 10 - inc_turb_wallfunction_flatplate_sst.test_vals = [-6.507362, -5.693894, -6.434063, -4.223774, -7.008049, -1.954102, 10.000000, -3.047554, 0.001081, 0.003644, 0.618140] + inc_turb_wallfunction_flatplate_sst.test_vals = [-6.507362, -5.693894, -6.434063, -4.223774, -7.008049, -1.954102, 10.000000, -3.047554, 0.001081, 0.003644, 0] test_list.append(inc_turb_wallfunction_flatplate_sst) # FLAT PLATE, WALL FUNCTIONS, INCOMPRESSIBLE SA @@ -769,7 +769,7 @@ def main(): square_cylinder.cfg_dir = "unsteady/square_cylinder" square_cylinder.cfg_file = "turb_square.cfg" square_cylinder.test_iter = 3 - square_cylinder.test_vals = [-2.560840, -1.173495, 0.061186, 1.399403, 2.220585, 1.399351, 2.218790, -0.584750] + square_cylinder.test_vals = [-2.560840, -1.173495, 0.061186, 1.399403, 2.220585, 1.399351, 2.218790, 0] square_cylinder.unsteady = True test_list.append(square_cylinder) @@ -796,7 +796,7 @@ def main(): ddes_flatplate.cfg_dir = "ddes/flatplate" ddes_flatplate.cfg_file = "ddes_flatplate.cfg" ddes_flatplate.test_iter = 10 - ddes_flatplate.test_vals = [-2.714785, -5.882680, -0.215041, 0.023758, -617.450000] + ddes_flatplate.test_vals = [-2.714786, -5.882646, -0.215041, 0.023758, 0] ddes_flatplate.unsteady = True test_list.append(ddes_flatplate) @@ -805,7 +805,7 @@ def main(): unst_inc_turb_naca0015_sa.cfg_dir = "unsteady/pitching_naca0015_rans_inc" unst_inc_turb_naca0015_sa.cfg_file = "config_incomp_turb_sa.cfg" unst_inc_turb_naca0015_sa.test_iter = 1 - unst_inc_turb_naca0015_sa.test_vals = [-3.007635, -6.879778, 1.445293, 0.419274] + unst_inc_turb_naca0015_sa.test_vals = [-3.007635, -6.879809, 1.445300, 0.419281] unst_inc_turb_naca0015_sa.unsteady = True test_list.append(unst_inc_turb_naca0015_sa) @@ -1012,8 +1012,8 @@ def main(): statbeam3d.cfg_dir = "fea_fsi/StatBeam_3d" statbeam3d.cfg_file = "configBeam_3d.cfg" statbeam3d.test_iter = 0 - statbeam3d.test_vals = [-8.498245, -8.230816, -8.123810, 64095.000000] - statbeam3d.test_vals_aarch64 = [-8.498254, -8.230683, -8.123819, 64095.0] #last 4 columns + statbeam3d.test_vals = [-6.168640, -5.939035, -6.071159, 110190] + statbeam3d.test_vals_aarch64 = [-6.168640, -5.939035, -6.071159, 110190] #last 4 columns test_list.append(statbeam3d) # Mix elem, 3d beam, Knowles @@ -1093,7 +1093,7 @@ def main(): p1rad.cfg_dir = "radiation/p1model" p1rad.cfg_file = "configp1.cfg" p1rad.test_iter = 100 - p1rad.test_vals = [-7.751309, -7.923059, -2.119084, 0.091733, -44.537000] + p1rad.test_vals = [-7.751309, -7.923059, -2.119084, 0.091733, -47.387000] test_list.append(p1rad) # ############################### @@ -1520,8 +1520,8 @@ def main(): pywrapper_turb_naca0012_sst.cfg_dir = "rans/naca0012" pywrapper_turb_naca0012_sst.cfg_file = "turb_NACA0012_sst.cfg" pywrapper_turb_naca0012_sst.test_iter = 10 - pywrapper_turb_naca0012_sst.test_vals = [-12.107132, -15.277740, -6.210248, 1.049757, 0.019249, -3.173936, -38.976000] - pywrapper_turb_naca0012_sst.test_vals_aarch64 = [-12.107132, -15.277740, -6.210248, 1.049757, 0.019249, -3.173936, -38.976000] + pywrapper_turb_naca0012_sst.test_vals = [-12.107132, -15.277740, -6.210248, 1.049757, 0.019249, -3.173936, 0] + pywrapper_turb_naca0012_sst.test_vals_aarch64 = [-12.107132, -15.277740, -6.210248, 1.049757, 0.019249, -3.173936, 0] pywrapper_turb_naca0012_sst.command = TestCase.Command(exec = "SU2_CFD.py", param = "-f") pywrapper_turb_naca0012_sst.timeout = 3200 pywrapper_turb_naca0012_sst.tol = 0.00001 @@ -1534,7 +1534,7 @@ def main(): pywrapper_square_cylinder.cfg_dir = "unsteady/square_cylinder" pywrapper_square_cylinder.cfg_file = "turb_square.cfg" pywrapper_square_cylinder.test_iter = 3 - pywrapper_square_cylinder.test_vals = [-2.560840, -1.173495, 0.061186, 1.399403, 2.220585, 1.399351, 2.218790, -0.584750] + pywrapper_square_cylinder.test_vals = [-2.560840, -1.173495, 0.061186, 1.399403, 2.220585, 1.399351, 2.218790, 0] pywrapper_square_cylinder.command = TestCase.Command(exec = "SU2_CFD.py", param = "-f") pywrapper_square_cylinder.timeout = 1600 pywrapper_square_cylinder.tol = 0.00001 diff --git a/TestCases/serial_regression_AD.py b/TestCases/serial_regression_AD.py index 9817708b300..620a3b20e8f 100644 --- a/TestCases/serial_regression_AD.py +++ b/TestCases/serial_regression_AD.py @@ -78,7 +78,7 @@ def main(): discadj_rans_naca0012_sa.cfg_dir = "disc_adj_rans/naca0012" discadj_rans_naca0012_sa.cfg_file = "turb_NACA0012_sa.cfg" discadj_rans_naca0012_sa.test_iter = 10 - discadj_rans_naca0012_sa.test_vals = [-2.996976, -0.196055, 0.000004, -0.000000, 5.000000, -3.971736, 5.000000, -10.464337] + discadj_rans_naca0012_sa.test_vals = [-2.996976, -0.196055, 0.000004, -0.000000, 5.000000, -3.971736, 5.000000, -10.464319] test_list.append(discadj_rans_naca0012_sa) # Adjoint turbulent NACA0012 SST @@ -110,7 +110,7 @@ def main(): discadj_incomp_cylinder.cfg_dir = "disc_adj_incomp_navierstokes/cylinder" discadj_incomp_cylinder.cfg_file = "heated_cylinder.cfg" discadj_incomp_cylinder.test_iter = 20 - discadj_incomp_cylinder.test_vals = [20.000000, -2.373367, -2.368305, 0.000000] + discadj_incomp_cylinder.test_vals = [20.000000, -2.386480, -2.408986, 0.000000] test_list.append(discadj_incomp_cylinder) ####################################################### @@ -183,7 +183,7 @@ def main(): discadj_heat.cfg_dir = "disc_adj_heat" discadj_heat.cfg_file = "disc_adj_heat.cfg" discadj_heat.test_iter = 10 - discadj_heat.test_vals = [-2.122406, 0.693852, 0.000000, -0.869010] + discadj_heat.test_vals = [-1.956346, 0.721746, 0.000000, 0.007024] test_list.append(discadj_heat) ################################### diff --git a/TestCases/tutorials.py b/TestCases/tutorials.py index eb018f65e73..f4a151bdaad 100644 --- a/TestCases/tutorials.py +++ b/TestCases/tutorials.py @@ -91,6 +91,34 @@ def main(): sp_pinArray_2d_dp_hf_tp.test_vals = [-4.640621, 1.436697, -0.707302, 208.023676] test_list.append(sp_pinArray_2d_dp_hf_tp) + # 90 degree pipe bend with wall functions from the experiments of Sudo et al. + sudo_tutorial = TestCase('sudo_bend') + sudo_tutorial.cfg_dir = "../Tutorials/incompressible_flow/Inc_Turbulent_Bend_Wallfunctions" + sudo_tutorial.cfg_file = "sudo.cfg" + sudo_tutorial.test_iter = 10 + sudo_tutorial.test_vals = [-14.579462, -13.203791, -13.601782, -12.616876, -14.005299, -10.817605, 15.000000, -2.296083] + sudo_tutorial.command = TestCase.Command("mpirun -n 2", "SU2_CFD") + test_list.append(sudo_tutorial) + + # design-primal: 90 degree pipe bend with wall functions from the experiments of Sudo et al. + sudo_design_primal = TestCase('sudo_bend_design_primal') + sudo_design_primal.cfg_dir = "../Tutorials/design/Inc_Turbulent_Bend_Wallfunctions" + sudo_design_primal.cfg_file = "sudo_primal.cfg" + sudo_design_primal.test_iter = 10 + sudo_design_primal.test_vals = [-13.474698, -12.487574, -12.441102, -11.418111, -12.552674, -9.712569, 89.034000] + sudo_design_primal.command = TestCase.Command("mpirun -n 2", "SU2_CFD") + test_list.append(sudo_design_primal) + + # design-adjoint: 90 degree pipe bend with wall functions from the experiments of Sudo et al. + sudo_design_adjoint = TestCase('sudo_bend_design_adjoint') + sudo_design_adjoint.cfg_dir = "../Tutorials/design/Inc_Turbulent_Bend_Wallfunctions" + sudo_design_adjoint.cfg_file = "sudo_adjoint.cfg" + sudo_design_adjoint.test_iter = 10 + sudo_design_adjoint.test_vals = [-4.133194, -3.691046, -2.581693, -3.476472, -3.837900, -6.900137] + sudo_design_adjoint.command = TestCase.Command("mpirun -n 2", "SU2_CFD_AD") + test_list.append(sudo_design_adjoint) + + ### Species Transport # 3 species (2 eq) primitive venturi mixing @@ -120,14 +148,6 @@ def main(): kenics_mixer_tutorial.command = TestCase.Command("mpirun -n 2", "SU2_CFD") test_list.append(kenics_mixer_tutorial) - # 90 degree pipe bend with wall functions from the experiments of Sudo et al. - sudo_tutorial = TestCase('sudo_bend') - sudo_tutorial.cfg_dir = "../Tutorials/incompressible_flow/Inc_Turbulent_Bend_Wallfunctions" - sudo_tutorial.cfg_file = "sudo.cfg" - sudo_tutorial.test_iter = 10 - sudo_tutorial.test_vals = [-14.579462, -13.203791, -13.601782, -12.616876, -14.005299, -10.817605, 15.000000, -2.296083] - sudo_tutorial.command = TestCase.Command("mpirun -n 2", "SU2_CFD") - test_list.append(sudo_tutorial) ### Incompressible Combustion @@ -228,7 +248,7 @@ def main(): tutorial_trans_e387_sa.cfg_dir = "../Tutorials/compressible_flow/Transitional_Airfoil/Langtry_and_Menter/E387" tutorial_trans_e387_sa.cfg_file = "transitional_SA_LM_model_ConfigFile.cfg" tutorial_trans_e387_sa.test_iter = 20 - tutorial_trans_e387_sa.test_vals = [-6.527027, -5.081558, -0.795261, 1.022606, 0.150125, 2.000000, -9.580659] + tutorial_trans_e387_sa.test_vals = [-6.527027, -5.081541, -0.795261, 1.022607, 0.150175, 2.000000, -9.580660] tutorial_trans_e387_sa.no_restart = True test_list.append(tutorial_trans_e387_sa) @@ -263,7 +283,7 @@ def main(): tutorial_unst_naca0012.cfg_dir = "../Tutorials/compressible_flow/Unsteady_NACA0012" tutorial_unst_naca0012.cfg_file = "unsteady_naca0012.cfg" tutorial_unst_naca0012.test_iter = 520 - tutorial_unst_naca0012.test_vals = [520.000000, 0.000000, -5.291711, 0.000000, 0.305248, 0.810326, 0.001814, 0.006573] + tutorial_unst_naca0012.test_vals = [520.000000, 0.000000, -5.290694, 0.000000, 0.317272, 0.820972, 0.002144, 0.012805] tutorial_unst_naca0012.test_vals_aarch64 = [520.000000, 0.000000, -5.298777, 0.000000, 0.288956, 0.736706, 0.002419, 0.007134] tutorial_unst_naca0012.unsteady = True test_list.append(tutorial_unst_naca0012) @@ -273,7 +293,7 @@ def main(): propeller_var_load.cfg_dir = "../Tutorials/compressible_flow/ActuatorDisk_VariableLoad" propeller_var_load.cfg_file = "propeller_variable_load.cfg" propeller_var_load.test_iter = 20 - propeller_var_load.test_vals = [-1.830276, -4.535127, -0.000323, 0.171623] + propeller_var_load.test_vals = [-1.830257, -4.535041, -0.000323, 0.171647] propeller_var_load.timeout = 3200 test_list.append(propeller_var_load) @@ -324,6 +344,22 @@ def main(): pass_list = [ test.run_test() for test in test_list ] + + # design-FADO: 90 degree pipe bend optimization + sudo_design_fado = TestCase('sudo_bend_design_fado') + sudo_design_fado.command = TestCase.Command(exec = "python", param = "optimization.py") + sudo_design_fado.cfg_dir = "../Tutorials/design/Inc_Turbulent_Bend_Wallfunctions" + sudo_design_fado.cfg_file = "sudo.cfg" + sudo_design_fado.multizone = False + sudo_design_fado.test_iter = 10 + sudo_design_fado.timeout = 1600 + sudo_design_fado.reference_file = "../../../TestCases/Tutorials/design/Inc_Turbulent_Bend_Wallfunctions/optim.csv.ref" + sudo_design_fado.test_file = "optim.csv" + sudo_design_fado.comp_threshold = 1e-6 + sudo_design_fado.tol_file_percent = 0.1 + pass_list.append(sudo_design_fado.run_filediff()) + test_list.append(sudo_design_fado) + # Tests summary print('==================================================================') print('Summary of the parallel tests') diff --git a/TestCases/vandv.py b/TestCases/vandv.py index 01359163885..f9da6969994 100644 --- a/TestCases/vandv.py +++ b/TestCases/vandv.py @@ -45,8 +45,8 @@ def main(): p30n30.cfg_dir = "vandv/rans/30p30n" p30n30.cfg_file = "config.cfg" p30n30.test_iter = 20 - p30n30.test_vals = [-10.582171, -10.106603, -10.474926, -10.182536, -12.679336, 0.052181, 2.829820, 1.318613, -0.221214] - p30n30.test_vals_aarch64 = [-10.582171, -10.106603, -10.474926, -10.182536, -12.679336, 0.052181, 2.829820, 1.318613, -0.221214] + p30n30.test_vals = [-10.582183, -10.106601, -10.474910, -10.182549, -12.679336, 0.052181, 2.829820, 1.318613, -0.221374] + p30n30.test_vals_aarch64 = [-10.582183, -10.106601, -10.474910, -10.182549, -12.679336, 0.052181, 2.829820, 1.318613, -0.221374] test_list.append(p30n30) # flat plate - sst-v1994m diff --git a/config_template.cfg b/config_template.cfg index 025fd3a5e45..50f48950fc9 100644 --- a/config_template.cfg +++ b/config_template.cfg @@ -53,7 +53,7 @@ KIND_VERIFICATION_SOLUTION= NO_VERIFICATION_SOLUTION % Defaults to DISCRETE_ADJOINT for the SU2_*_AD codes, and to DIRECT otherwise. MATH_PROBLEM= DIRECT % -% Axisymmetric simulation, only compressible flows (NO, YES) +% Axisymmetric simulation for 2D problems. (NO, YES) AXISYMMETRIC= NO % % Gravity force @@ -62,6 +62,12 @@ GRAVITY_FORCE= NO % Restart solution (NO, YES) RESTART_SOL= NO % +% Only save the minimum required variables for restarting to restart files (NO, YES). +% If this option is set to NO, then all fields will be written to all output files. +% To minimize the size of other output files (e.g. paraview, tecplot) by not including +% default restart fields in them, add the keyword COMPACT to VOLUME_OUTPUT. +WRT_RESTART_COMPACT= YES +% % Discard the data storaged in the solution and geometry files % e.g. AOA, dCL/dAoA, dCD/dCL, iter, etc. % Note that AoA in the solution and geometry files is critical @@ -1732,6 +1738,12 @@ ELASTICITY_MODULUS= 1000.0 % Poisson ratio POISSON_RATIO= 0.35 % +% Thermal expansion coefficient +MATERIAL_THERMAL_EXPANSION_COEFF= 0 +% +% Freestream temperature at which there is no stress from thermal expansion +MATERIAL_REFERENCE_TEMPERATURE= 288.15 +% % Knowles B constant KNOWLES_B= 1.0 % @@ -1741,12 +1753,17 @@ KNOWLES_N= 1.0 % ID of the region we want to compute the sensitivities using direct differentiation FEA_ID_DIRECTDIFF= 0 % -% RESTART_STEADY_STATE= NO % % Time discretization TIME_DISCRE_FEA= NEWMARK_IMPLICIT % +% Iterative method for non-linear structural analysis +NONLINEAR_FEM_SOLUTION_METHOD= NEWTON_RAPHSON +% +% Pseudo static analysis (no density in dynamic analysis) +PSEUDO_STATIC= NO +% % Parameter alpha for Newmark scheme (s) NEWMARK_BETA= 0.25 % @@ -1780,6 +1797,9 @@ MATERIAL_MODEL= LINEAR_ELASTIC % Compressibility of the material MATERIAL_COMPRESSIBILITY= COMPRESSIBLE % +% Formulation for 2-dimensional elasticity solver +FORMULATION_ELASTICITY_2D= PLANE_STRAIN +% % -------------------- Dielectric effects ------------------% % % Include DE effects @@ -1793,6 +1813,20 @@ ELECTRIC_FIELD_MOD= 20e5 % % Direction of the Electic Fields ELECTRIC_FIELD_DIR= (0.0, 1.0) +% +% ------------------------ Prestretch -----------------------% +% +% Consider a prestretch in the structural domain +PRESTRETCH= NO +% +% Filename to input for prestretching membranes +PRESTRETCH_FILENAME= prestretch_file.dat +% +% ----------------------- Body Forces -----------------------% +% +% Centrifugal forces due to ROTATION_RATE around MOTION_ORIGIN. +% GRAVITY_FORCE and BODY_FORCE can also be used with the FEA solver. +CENTRIFUGAL_FORCE= NO % -------------------- Weakly Coupled Heat ------------------% % @@ -2009,34 +2043,6 @@ STRESS_PENALTY_PARAM= (1.0, 10.0) % Preaccumulation in the AD mode. PREACC= YES -% ---------------- PRESTRETCH FOR STRUCTURES -------------------% -% Consider a prestretch in the structural domain -PRESTRETCH= NO -% -% Filename to input for prestretching membranes -PRESTRETCH_FILENAME= prestretch_file.dat -% -% Iterative method for non-linear structural analysis -NONLINEAR_FEM_SOLUTION_METHOD= NEWTON_RAPHSON -% -% Formulation for bidimensional elasticity solver -FORMULATION_ELASTICITY_2D= PLANE_STRAIN -% -% Apply dead loads -DEAD_LOAD= NO -% -% pseudo static analysis (no density in dynamic analysis) -PSEUDO_STATIC= NO -% -% Dynamic or static structural analysis (deprecated -> use TIME_DOMAIN) -DYNAMIC_ANALYSIS= NO -% -% Time Step for dynamic analysis (s) (deprecated -> use TIME_STEP) -DYN_TIMESTEP= 0.0 -% -% Total Physical Time for dual time stepping simulations (s) (deprecated -> use MAX_TIME) -DYN_TIME= 1.0 - % ---------------- MESH DEFORMATION PARAMETERS (NEW SOLVER) -------------------% % % Use the reformatted pseudo-elastic solver for grid deformation diff --git a/externals/FADO b/externals/FADO new file mode 160000 index 00000000000..ce7ee018e4e --- /dev/null +++ b/externals/FADO @@ -0,0 +1 @@ +Subproject commit ce7ee018e4e699af5028d69baa1939fea290e18a diff --git a/meson_scripts/init.py b/meson_scripts/init.py index e3df3e023ba..67a2e41a2ec 100755 --- a/meson_scripts/init.py +++ b/meson_scripts/init.py @@ -48,6 +48,7 @@ def init_submodules( own_mpp=True, own_cool=True, own_mel=True, + own_fado=True, own_mlpcpp=True, ): @@ -71,6 +72,8 @@ def init_submodules( github_repo_coolprop = "https://github.com/CoolProp/CoolProp" sha_version_mel = "46205ab019e5224559091375a6d71aabae6bc5b9" github_repo_mel = "https://github.com/pcarruscag/MEL" + sha_version_fado = "ce7ee018e4e699af5028d69baa1939fea290e18a" + github_repo_fado = "https://github.com/pcarruscag/FADO" sha_version_mlpcpp = "6865a58b22f21a92977839d9c93eae9522402f55" github_repo_mlpcpp = "https://github.com/EvertBunschoten/MLPCpp" @@ -82,6 +85,7 @@ def init_submodules( mpp_name = "Mutationpp" coolprop_name = "CoolProp" mel_name = "MEL" + fado_name = "FADO" mlpcpp_name = "MLPCpp" base_path = cur_dir + os.path.sep + "externals" + os.path.sep @@ -91,6 +95,7 @@ def init_submodules( alt_name_meson = base_path + "meson" alt_name_ninja = base_path + "ninja" alt_name_mel = base_path + "mel" + alt_name_fado = base_path + "FADO" alt_name_mpp = cur_dir + os.path.sep + "subprojects" + os.path.sep + "Mutationpp" alt_name_coolprop = cur_dir + os.path.sep + "subprojects" + os.path.sep + "CoolProp" alt_name_mlpcpp = cur_dir + os.path.sep + "subprojects" + os.path.sep + "MLPCpp" @@ -123,6 +128,8 @@ def init_submodules( submodule_status(alt_name_coolprop, sha_version_coolprop) if own_mel: submodule_status(alt_name_mel, sha_version_mel) + if own_fado: + submodule_status(alt_name_fado, sha_version_fado) if own_mlpcpp: submodule_status(alt_name_mlpcpp, sha_version_mlpcpp) # Otherwise download the zip file from git @@ -157,6 +164,10 @@ def init_submodules( ) if own_mel: download_module(mel_name, alt_name_mel, github_repo_mel, sha_version_mel) + if own_fado: + download_module( + fado_name, alt_name_fado, github_repo_fado, sha_version_fado + ) if own_mlpcpp: download_module( mlpcpp_name, alt_name_mlpcpp, github_repo_mlpcpp, sha_version_mlpcpp diff --git a/preconfigure.py b/preconfigure.py index 51cb0599e6e..5dfe8acd8a9 100755 --- a/preconfigure.py +++ b/preconfigure.py @@ -81,6 +81,7 @@ def run( own_mpp=True, own_cool=True, own_mel=True, + own_fado=True, own_mlpcpp=True, ): @@ -94,6 +95,7 @@ def run( own_mpp=own_mpp, own_cool=own_cool, own_mel=own_mel, + own_fado=own_fado, own_mlpcpp=own_mlpcpp, ) @@ -137,6 +139,9 @@ def run( parser.add_argument( "--no-mel", help="do not download own copy of MEL", action="store_false" ) + parser.add_argument( + "--no-fado", help="do not download own copy of FADO", action="store_false" + ) parser.add_argument( "--no-mlpcpp", help="do not download copy of MLpCpp", @@ -152,5 +157,6 @@ def run( own_mpp=args.no_mpp, own_cool=args.no_coolprop, own_mel=args.no_mel, + own_fado=args.no_fado, own_mlpcpp=args.no_mlpcpp, )