diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp index e316b4cb8d01..604f83e10f9b 100644 --- a/Common/include/CConfig.hpp +++ b/Common/include/CConfig.hpp @@ -988,9 +988,11 @@ class CConfig { unsigned short Pred_Order; /*!< \brief Order of the predictor for FSI applications. */ unsigned short Kind_Interpolation; /*!< \brief type of interpolation to use for FSI applications. */ bool ConservativeInterpolation; /*!< \brief Conservative approach for non matching mesh interpolation. */ + unsigned short NumNearestNeighbors; /*!< \brief Number of neighbors used for Nearest Neighbor interpolation. */ unsigned short Kind_RadialBasisFunction; /*!< \brief type of radial basis function to use for radial basis FSI. */ bool RadialBasisFunction_PolynomialOption; /*!< \brief Option of whether to include polynomial terms in Radial Basis Function Interpolation or not. */ - su2double RadialBasisFunction_Parameter; /*!< \brief Radial basis function parameter. */ + su2double RadialBasisFunction_Parameter; /*!< \brief Radial basis function parameter (radius). */ + su2double RadialBasisFunction_PruneTol; /*!< \brief Tolerance to prune the RBF interpolation matrix. */ bool Prestretch; /*!< \brief Read a reference geometry for optimization purposes. */ string Prestretch_FEMFileName; /*!< \brief File name for reference geometry. */ string FEA_FileName; /*!< \brief File name for element-based properties. */ @@ -3460,27 +3462,27 @@ class CConfig { unsigned short GetMarker_All_ZoneInterface(unsigned short val_marker) const { return Marker_All_ZoneInterface[val_marker]; } /*! - * \brief Get the MixingPlane interface information for a marker val_marker. - * \param[in] val_marker value of the marker on the grid. - * \return 0 if is not part of the MixingPlane Interface and greater than 1 if it is part. - */ + * \brief Get the MixingPlane interface information for a marker val_marker. + * \param[in] val_marker value of the marker on the grid. + * \return 0 if is not part of the MixingPlane Interface and greater than 1 if it is part. + */ unsigned short GetMarker_All_MixingPlaneInterface(unsigned short val_marker) const { return Marker_All_MixingPlaneInterface[val_marker]; } - /*! - * \brief Get the Turbomachinery information for a marker val_marker. - * \param[in] val_marker value of the marker on the grid. - * \return 0 if is not part of the Turbomachinery and greater than 1 if it is part. - */ + /*! + * \brief Get the Turbomachinery information for a marker val_marker. + * \param[in] val_marker value of the marker on the grid. + * \return 0 if is not part of the Turbomachinery and greater than 1 if it is part. + */ unsigned short GetMarker_All_Turbomachinery(unsigned short val_marker) const { return Marker_All_Turbomachinery[val_marker]; } - /*! - * \brief Get the Turbomachinery flag information for a marker val_marker. - * \param[in] val_marker value of the marker on the grid. - * \return 0 if is not part of the Turbomachinery, flag INFLOW or OUTFLOW if it is part. - */ + /*! + * \brief Get the Turbomachinery flag information for a marker val_marker. + * \param[in] val_marker value of the marker on the grid. + * \return 0 if is not part of the Turbomachinery, flag INFLOW or OUTFLOW if it is part. + */ unsigned short GetMarker_All_TurbomachineryFlag(unsigned short val_marker) const { return Marker_All_TurbomachineryFlag[val_marker]; } - /*! + /*! * \brief Get the number of FSI interface markers val_marker. * \param[in] void. * \return Number of markers belonging to the FSI interface. @@ -8787,6 +8789,16 @@ class CConfig { */ su2double GetRadialBasisFunctionParameter(void) const { return RadialBasisFunction_Parameter; } + /*! + * \brief Get the tolerance used to prune the interpolation matrix (making it sparser). + */ + su2double GetRadialBasisFunctionPruneTol(void) const { return RadialBasisFunction_PruneTol; } + + /*! + * \brief Get the number of donor points to use in Nearest Neighbor interpolation. + */ + unsigned short GetNumNearestNeighbors(void) const { return NumNearestNeighbors; } + /*! * \brief Get the kind of inlet face interpolation function to use. */ diff --git a/Common/include/geometry/CGeometry.hpp b/Common/include/geometry/CGeometry.hpp index 3066dcd97fd6..4473ac72b72a 100644 --- a/Common/include/geometry/CGeometry.hpp +++ b/Common/include/geometry/CGeometry.hpp @@ -1621,7 +1621,7 @@ class CGeometry { * \param[in] fillLvl - Level of fill of the pattern. * \return Reference to the sparse pattern. */ - const CCompressedSparsePatternUL& GetSparsePattern(ConnectivityType type, unsigned long fillLvl); + const CCompressedSparsePatternUL& GetSparsePattern(ConnectivityType type, unsigned long fillLvl = 0); /*! * \brief Get the edge to sparse pattern map. @@ -1630,12 +1630,25 @@ class CGeometry { */ const CEdgeToNonZeroMapUL& GetEdgeToSparsePatternMap(void); + /*! + * \brief Get the transpose of the (main, i.e 0 fill) sparse pattern (e.g. CSR becomes CSC). + * \param[in] type - Finite volume or finite element. + * \return Reference to the map. + */ + const su2vector& GetTransposeSparsePatternMap(ConnectivityType type); + /*! * \brief Get the edge coloring. * \note This method computes the coloring if that has not been done yet. + * \param[out] efficiency - optional output of the coloring efficiency. * \return Reference to the coloring. */ - const CCompressedSparsePatternUL& GetEdgeColoring(void); + const CCompressedSparsePatternUL& GetEdgeColoring(su2double* efficiency = nullptr); + + /*! + * \brief Force the natural (sequential) edge coloring. + */ + void SetNaturalEdgeColoring(); /*! * \brief Get the group size used in edge coloring. @@ -1646,9 +1659,15 @@ class CGeometry { /*! * \brief Get the element coloring. * \note This method computes the coloring if that has not been done yet. + * \param[out] efficiency - optional output of the coloring efficiency. * \return Reference to the coloring. */ - const CCompressedSparsePatternUL& GetElementColoring(void); + const CCompressedSparsePatternUL& GetElementColoring(su2double* efficiency = nullptr); + + /*! + * \brief Force the natural (sequential) element coloring. + */ + void SetNaturalElementColoring(); /*! * \brief Get the group size used in element coloring. diff --git a/Common/include/geometry/dual_grid/CVertex.hpp b/Common/include/geometry/dual_grid/CVertex.hpp index 70bc0dd2492c..084e6e0137ca 100644 --- a/Common/include/geometry/dual_grid/CVertex.hpp +++ b/Common/include/geometry/dual_grid/CVertex.hpp @@ -37,26 +37,25 @@ */ class CVertex : public CDualGrid { protected: - unsigned long *Nodes; /*!< \brief Vector to store the global nodes of an element. */ - su2double *Normal; /*!< \brief Normal coordinates of the element and its center of gravity. */ - su2double Aux_Var; /*!< \brief Auxiliar variable defined only on the surface. */ - su2double CartCoord[3]; /*!< \brief Vertex cartesians coordinates. */ - su2double VarCoord[3]; /*!< \brief Used for storing the coordinate variation due to a surface modification. */ - su2double *VarRot; /*!< \brief Used for storing the rotation variation due to a surface modification. */ - long PeriodicPoint[5]; /*!< \brief Store the periodic point of a boundary (iProcessor, iPoint) */ - bool ActDisk_Perimeter; /*!< \brief Identify nodes at the perimeter of the actuator disk */ - short Rotation_Type; /*!< \brief Type of rotation associated with the vertex (MPI and periodic) */ + unsigned long Nodes[1]; /*!< \brief Vector to store the global nodes of an element. */ + su2double Normal[3]; /*!< \brief Normal coordinates of the element and its center of gravity. */ + su2double Aux_Var; /*!< \brief Auxiliar variable defined only on the surface. */ + su2double CartCoord[3]; /*!< \brief Vertex cartesians coordinates. */ + su2double VarCoord[3]; /*!< \brief Used for storing the coordinate variation due to a surface modification. */ + su2double *VarRot; /*!< \brief Used for storing the rotation variation due to a surface modification. */ + long PeriodicPoint[5]; /*!< \brief Store the periodic point of a boundary (iProcessor, iPoint) */ + bool ActDisk_Perimeter; /*!< \brief Identify nodes at the perimeter of the actuator disk */ + short Rotation_Type; /*!< \brief Type of rotation associated with the vertex (MPI and periodic) */ unsigned long Normal_Neighbor; /*!< \brief Index of the closest neighbor. */ - unsigned long *Donor_Points; /*!< \brief indices of donor points for interpolation across zones */ - unsigned long *Donor_Proc; /*!< \brief indices of donor processor for interpolation across zones in parallel */ - unsigned long Donor_Elem; /*!< \brief Store the donor element for interpolation across zones/ */ - unsigned short Donor_Face; /*!<\brief Store the donor face (w/in donor element) for interpolation across zones */ - su2double Basis_Function[3]; /*!< \brief Basis function values for interpolation across zones. */ - su2double *Donor_Coeff; /*!\brief Store a list of coefficients corresponding to the donor points. */ - unsigned short nDonor_Points; /*!\brief Number of points in Donor_Points; at least there will be one donor point (if the mesh is matching)*/ + unsigned long *Donor_Points; /*!< \brief indices of donor points for interpolation across zones */ + unsigned long *Donor_Proc; /*!< \brief indices of donor processor for interpolation across zones in parallel */ + unsigned long Donor_Elem; /*!< \brief Store the donor element for interpolation across zones/ */ + unsigned short Donor_Face; /*!< \brief Store the donor face (w/in donor element) for interpolation across zones */ + su2double Basis_Function[3]; /*!< \brief Basis function values for interpolation across zones. */ + su2double *Donor_Coeff; /*!< \brief Store a list of coefficients corresponding to the donor points. */ + unsigned short nDonor_Points; /*!< \brief Number of points in Donor_Coeff. */ public: - /*! * \brief Constructor of the class. * \param[in] val_point - Node of the vertex. @@ -358,17 +357,6 @@ class CVertex : public CDualGrid { */ inline unsigned long GetNormal_Neighbor(void) const { return Normal_Neighbor; } - /*! - * \brief Increment the number of donor points by 1. - */ - inline void IncrementnDonor(void) {nDonor_Points++;} - - /*! - * \brief Set the value of nDonor_Points - * \param[in] nDonor - the number of donor points - */ - inline void SetnDonorPoints(unsigned short nDonor) {nDonor_Points = nDonor;} - /*! * \brief Return the value of nDonor_Points * \return nDonor - the number of donor points @@ -419,9 +407,9 @@ class CVertex : public CDualGrid { /*! * \brief Allocate memory based on how many donor points need to be stored. - * Uses nDonor_Points + * \param[in] nDonor - the number of donor points */ - void Allocate_DonorInfo(void); + void Allocate_DonorInfo(unsigned short nDonor); /*! * \brief Get the rotation variation diff --git a/Common/include/interface_interpolation/CInterpolator.hpp b/Common/include/interface_interpolation/CInterpolator.hpp new file mode 100644 index 000000000000..af95221275f0 --- /dev/null +++ b/Common/include/interface_interpolation/CInterpolator.hpp @@ -0,0 +1,193 @@ +/*! + * \file CInterpolator.hpp + * \brief Base class for multiphysics interpolation. + * \author H. Kline + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, 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/datatype_structure.hpp" + +class CConfig; +class CGeometry; + +/*! + * \class CInterpolator + * \brief Main class for defining the interpolator, it requires + * a child class for each particular interpolation method + * \author H. Kline + */ +class CInterpolator { +protected: + const int rank; /*!< \brief MPI Rank. */ + const int size; /*!< \brief MPI Size. */ + const unsigned donorZone; /*!< \brief Index of donor zone. */ + const unsigned targetZone; /*!< \brief Index of target zone. */ + + unsigned long + MaxLocalVertex_Donor, /*!< \brief Maximum vertices per processor*/ + nGlobalFace_Donor, /*!< \brief Number of global donor faces*/ + nGlobalFaceNodes_Donor, /*!< \brief Number of global donor face nodes*/ + MaxFace_Donor, /*!< \brief Maximum faces per processor*/ + MaxFaceNodes_Donor; /*!< \brief Maximum nodes associated with faces per processor*/ + + unsigned long + *Buffer_Receive_nVertex_Donor, /*!< \brief Buffer to store the number of vertices per processor on the Donor domain */ + *Buffer_Receive_nFace_Donor, /*!< \brief Buffer to store the number of faces per processor*/ + *Buffer_Receive_nFaceNodes_Donor, /*!< \brief Buffer to store the number of nodes associated with faces per processor*/ + Buffer_Send_nVertex_Donor[1], /*!< \brief Buffer to send number of vertices on the local processor*/ + Buffer_Send_nFace_Donor[1], /*!< \brief Buffer to send number of faces on the local processor*/ + Buffer_Send_nFaceNodes_Donor[1], /*!< \brief Buffer to send the number of nodes assocated with faces per processor*/ + *Buffer_Send_FaceIndex, /*!< \brief Buffer to send indices pointing to the node indices that define the faces*/ + *Buffer_Receive_FaceIndex, /*!< \brief Buffer to receive indices pointing to the node indices that define the faces*/ + *Buffer_Send_FaceNodes, /*!< \brief Buffer to send indices pointing to the location of node information in other buffers, defining faces*/ + *Buffer_Receive_FaceNodes, /*!< \brief Buffer to receive indices pointing to the location of node information in other buffers, defining faces*/ + *Buffer_Send_FaceProc, /*!< \brief Buffer to send processor which stores the node indicated in Buffer_Receive_FaceNodes*/ + *Buffer_Receive_FaceProc; /*!< \brief Buffer to receive processor which stores the node indicated in Buffer_Receive_FaceNodes*/ + + long *Buffer_Send_GlobalPoint, /*!< \brief Buffer to send global point indices*/ + *Buffer_Receive_GlobalPoint; /*!< \brief Buffer to receive global point indices*/ + + su2double *Buffer_Send_Coord, /*!< \brief Buffer to send coordinate values*/ + *Buffer_Send_Normal, /*!< \brief Buffer to send normal vector values */ + *Buffer_Receive_Coord, /*!< \brief Buffer to receive coordinate values*/ + *Buffer_Receive_Normal; /*!< \brief Buffer to receive normal vector values*/ + + unsigned long + *Receive_GlobalPoint, /*!< \brief Buffer to receive Global point indexes*/ + *Buffer_Receive_nLinkedNodes, /*!< \brief Buffer to receive the number of edges connected to each node*/ + *Buffer_Receive_LinkedNodes, /*!< \brief Buffer to receive the list of notes connected to the nodes through an edge*/ + *Buffer_Receive_StartLinkedNodes, /*!< \brief Buffer to receive the index of the Receive_LinkedNodes buffer where corresponding list of linked nodes begins */ + *Buffer_Receive_Proc; /*!< \brief Buffer to receive the thread that owns the node*/ + + unsigned long + nGlobalVertex_Target, /*!< \brief Global number of vertex of the target boundary*/ + nLocalVertex_Target, /*!< \brief Number of vertex of the target boundary owned by the thread*/ + nGlobalVertex_Donor, /*!< \brief Global number of vertex of the donor boundary*/ + nLocalVertex_Donor, /*!< \brief Number of vertex of the donor boundary owned by the thread*/ + nGlobalVertex, /*!< \brief Dummy variable to temporarily store the global number of vertex of a boundary*/ + nLocalLinkedNodes; /*!< \brief Dummy variable to temporarily store the number of vertex of a boundary*/ + + CGeometry**** const Geometry; /*! \brief Vector which stores n zones of geometry. */ + CGeometry* const donor_geometry; /*! \brief Donor geometry. */ + CGeometry* const target_geometry; /*! \brief Target geometry. */ + +public: + /*! + * \brief Constructor of the class. + * \param[in] geometry - Geometrical definition of the problem. + * \param[in] config - Definition of the particular problem. + * \param[in] iZone - index of the donor zone + * \param[in] jZone - index of the target zone + */ + CInterpolator(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone); + + /*! + * \brief No default construction allowed to force zones and geometry to always be set. + */ + CInterpolator(void) = delete; + + /*! + * \brief Destructor of the class, nothing is deleted, derived classes need to manage the MPI buffers. + */ + virtual ~CInterpolator(void) = default; + + /*! + * \brief Set up transfer matrix defining relation between two meshes + * \note Main method that derived classes must implement. + * \param[in] config - Definition of the particular problem. + */ + virtual void Set_TransferCoeff(const CConfig* const* config) = 0; + + /*! + * \brief Print information about the interpolation. + */ + virtual void PrintStatistics(void) const { } + + /*! + * \brief Find the index of the interface marker shared by that zone + * \param[in] config - Definition of the particular problem. + * \param[in] val_marker_interface - Interface tag. + */ + static int Find_InterfaceMarker(const CConfig *config, unsigned short val_marker_interface); + + /*! + * \brief Check whether an interface should be processed or not, i.e. if it is part of the zones. + * \param[in] val_markDonor - Marker tag from donor zone. + * \param[in] val_markTarget - Marker tag from target zone. + */ + static bool CheckInterfaceBoundary(int val_markDonor, int val_markTarget); + +protected: + /*! + * \brief Recontstruct the boundary connectivity from parallel partitioning and broadcasts it to all threads + * \param[in] val_zone - index of the zone + * \param[in] val_marker - index of the marker + */ + void ReconstructBoundary(unsigned long val_zone, int val_marker); + + /*! + * \brief compute squared distance between 2 points + * \param[in] nDim - number of dimensions + * \param[in] point_i - coordinates of point i + * \param[in] point_j - coordinates of point j + */ + static inline su2double PointsSquareDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) { + su2double d = 0.0; + for(unsigned short iDim = 0; iDim < nDim; iDim++) + d += pow(point_j[iDim] - point_i[iDim], 2); + return d; + } + + /*! + * \brief compute distance between 2 points + * \param[in] nDim - number of dimensions + * \param[in] point_i - coordinates of point i + * \param[in] point_j - coordinates of point j + */ + static inline su2double PointsDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) { + return sqrt(PointsSquareDistance(nDim, point_i, point_j)); + } + + /*! + * \brief Determine array sizes used to collect and send coordinate and global point + * information. + * \param[in] faces - boolean that determines whether or not to set face information as well + * \param[in] markDonor - Index of the boundary on the donor domain. + * \param[in] markTarget - Index of the boundary on the target domain. + * \param[in] nVertexDonor - Number of vertices on the donor boundary. + * \param[in] nDim - number of physical dimensions. + */ + void Determine_ArraySize(bool faces, int markDonor, int markTarget, unsigned long nVertexDonor, unsigned short nDim); + + /*! + * \brief Collect and communicate vertex info: coord, global point, and if faces=true the normal vector + * \param[in] faces - boolean that determines whether or not to set face information as well + * \param[in] markDonor - Index of the boundary on the donor domain. + * \param[in] markTarget - Index of the boundary on the target domain. + * \param[in] nVertexDonor - Number of vertices on the donor boundary. + * \param[in] nDim - number of physical dimensions. + */ + void Collect_VertexInfo(bool faces, int markDonor, int markTarget, unsigned long nVertexDonor, unsigned short nDim); + +}; diff --git a/Common/include/interface_interpolation/CIsoparametric.hpp b/Common/include/interface_interpolation/CIsoparametric.hpp new file mode 100644 index 000000000000..1175e3890f2e --- /dev/null +++ b/Common/include/interface_interpolation/CIsoparametric.hpp @@ -0,0 +1,70 @@ +/*! + * \file CIsoparametric.hpp + * \brief Isoparametric interpolation using FE shape functions. + * \author H. Kline + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, 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 "CInterpolator.hpp" + +/*! + * \brief Isoparametric interpolation. + */ +class CIsoparametric final : public CInterpolator { +public: + /*! + * \brief Constructor of the class. + * \param[in] geometry - Geometrical definition of the problem. + * \param[in] config - Definition of the particular problem. + * \param[in] iZone - index of the donor zone + * \param[in] jZone - index of the target zone + */ + CIsoparametric(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone); + + /*! + * \brief Set up transfer matrix defining relation between two meshes + * \param[in] config - Definition of the particular problem. + */ + void Set_TransferCoeff(const CConfig* const* config) override; + +private: + /*! + * \brief Calculate the isoparametric representation of point iVertex in marker iZone_0 by + * nodes of element donor_elem in marker jMarker of zone iZone_1. + * \param[in] iVertex - vertex index of the point being interpolated. + * \param[in] nDim - the dimension of the coordinates. + * \param[in] iZone_1 - zone index of the element to use for interpolation (the DONOR zone) + * \param[in] donor_elem - element index of the element to use for interpolation (or global index of a point in 2D) + * \param[in] nDonorPoints - number of donor points in the element. + * \param[in] xj - point projected onto the plane of the donor element. + * \param[out] isoparams - isoparametric coefficients. Must be allocated to size nNodes ahead of time. (size> nDonors) + * + * \note If the problem is 2D, the 'face' projected onto is actually an edge; the local index + * of the edge is then stored in iFace, and the global index of the node (from which the edge + * is referenced) + */ + void Isoparameters(unsigned short nDim, unsigned short nDonor, const su2double *X, + const su2double *xj, su2double* isoparams) const; + +}; diff --git a/Common/include/interface_interpolation/CMirror.hpp b/Common/include/interface_interpolation/CMirror.hpp new file mode 100644 index 000000000000..311d54d96a60 --- /dev/null +++ b/Common/include/interface_interpolation/CMirror.hpp @@ -0,0 +1,53 @@ +/*! + * \file CMirror.hpp + * \brief Mirror interpolation for the conservative (work-wise) approach in FSI problems. + * \author H. Kline + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, 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 "CInterpolator.hpp" + +/*! + * \brief Mirror interpolation: copy point linking and coefficient values from the opposing mesh. + * \note Assumes that the oppoosing mesh has already run interpolation, otherwise will result in empty/trivial interpolation. + */ +class CMirror final : public CInterpolator { +public: + /*! + * \brief Constructor of the class. + * \note Data is set in geometry[targetZone]. + * \param[in] geometry_container + * \param[in] config - config container + * \param[in] iZone - First zone + * \param[in] jZone - Second zone + */ + CMirror(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone); + + /*! + * \brief Set up transfer matrix defining relation between two meshes + * \param[in] config - Definition of the particular problem. + */ + void Set_TransferCoeff(const CConfig* const* config) override; + +}; diff --git a/Common/include/interface_interpolation/CNearestNeighbor.hpp b/Common/include/interface_interpolation/CNearestNeighbor.hpp new file mode 100644 index 000000000000..c86d50add484 --- /dev/null +++ b/Common/include/interface_interpolation/CNearestNeighbor.hpp @@ -0,0 +1,62 @@ +/*! + * \file CNearestNeighbor.hpp + * \brief Nearest Neighbor interpolation class. + * \author H. Kline + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, 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 "CInterpolator.hpp" + +/*! + * \brief Nearest Neighbor(s) interpolation. + * \note The closest k neighbors are used for IDW interpolation, the computational + * cost of setting up the interpolation is O(N^2 log(k)), this can be improved + * by using a kd-tree. + */ +class CNearestNeighbor final : public CInterpolator { +private: + su2double AvgDistance = 0.0, MaxDistance = 0.0; + +public: + /*! + * \brief Constructor of the class. + * \param[in] geometry - Geometrical definition of the problem. + * \param[in] config - Definition of the particular problem. + * \param[in] iZone - index of the donor zone + * \param[in] jZone - index of the target zone + */ + CNearestNeighbor(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone); + + /*! + * \brief Set up transfer matrix defining relation between two meshes. + * \param[in] config - Definition of the particular problem. + */ + void Set_TransferCoeff(const CConfig* const* config) override; + + /*! + * \brief Print interpolation statistics. + */ + void PrintStatistics(void) const override; + +}; diff --git a/Common/include/interface_interpolation/CRadialBasisFunction.hpp b/Common/include/interface_interpolation/CRadialBasisFunction.hpp new file mode 100644 index 000000000000..48416ae33053 --- /dev/null +++ b/Common/include/interface_interpolation/CRadialBasisFunction.hpp @@ -0,0 +1,139 @@ +/*! + * \file CRadialBasisFunction.hpp + * \brief Radial basis function interpolation. + * \author Joel Ho, P. Gomes + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, 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 "CInterpolator.hpp" +#include "../option_structure.hpp" +#include "../toolboxes/C2DContainer.hpp" + +/*! + * \brief Radial basis function interpolation. + */ +class CRadialBasisFunction final : public CInterpolator { + static_assert(su2passivematrix::Storage == StorageType::RowMajor, + "This class relies on row major storage throughout."); +private: + unsigned long MinDonors = 0, AvgDonors = 0, MaxDonors = 0; + passivedouble Density = 0.0, AvgCorrection = 0.0, MaxCorrection = 0.0; + +public: + /*! + * \brief Constructor of the class. + * \param[in] geometry - Geometrical definition of the problem. + * \param[in] config - Definition of the particular problem. + * \param[in] iZone - index of the donor zone + * \param[in] jZone - index of the target zone + */ + CRadialBasisFunction(CGeometry ****geometry_container, const CConfig* const* config, + unsigned int iZone, unsigned int jZone); + + /*! + * \brief Set up transfer matrix defining relation between two meshes + * \param[in] config - Definition of the particular problem. + */ + void Set_TransferCoeff(const CConfig* const* config) override; + + /*! + * \brief Print information about the interpolation. + */ + void PrintStatistics(void) const override; + + /*! + * \brief Compute the value of a radial basis function, this is static so it can be re-used. + * \param[in] type - of radial basis function + * \param[in] radius - the characteristic dimension + * \param[in] dist - distance + * \return value of the RBF. + */ + static su2double Get_RadialBasisValue(ENUM_RADIALBASIS type, const su2double radius, const su2double dist); + + /*! + * \brief Compute the RBF "generator" matrix with or without polynomial terms. + * \note Multiplying C_inv_trunc by a column vector gives specific coefficients for given "known values", + * conversely, multiplying (on the left) by a row vector of polynomial and RBF values gives generic + * interpolation coefficients for a given target evaluation point. + * \param[in] type - Type of radial basis function. + * \param[in] usePolynomial - Whether to use polynomial terms. + * \param[in] radius - Normalizes point-to-point distance when computing RBF values. + * \param[in] coords - Coordinates of the donor points. + * \param[out] nPolynomial - Num of poly terms, -1 if !usePolynomial, nDim-1 if coords lie on plane, else nDim. + * \param[out] keepPolynomialRow - Size nDim, signals which (if any) iDim was removed from polynomial term. + * \param[out] C_inv_trunc - The generator matrix as described above. + */ + static void ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, su2double radius, + const su2activematrix& coords, int& nPolynomial, + vector& keepPolynomialRow, su2passivematrix& C_inv_trunc); + + /*! + * \brief If the polynomial term is included in the interpolation, and the points lie on a plane, the matrix + * becomes rank deficient and cannot be inverted. This method detects that condition and corrects it by + * removing a row from P (the polynomial part of the interpolation matrix). + * \param[in] max_diff_tol - Tolerance to detect whether points are on a plane. + * \param[out] keep_row - Marks the dimensions of P kept. + * \param[in,out] P - Polynomial part of the interpolation matrix, one row may be eliminated. + * \return n_polynomial - Size of the polynomial part on exit (in practice nDim or nDim-1). + */ + static int CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, su2passivematrix &P); + +private: + /*! + * \brief Helper function, prunes (by setting to zero) small interpolation coefficients, + * i.e. <= tolerance*max(abs(coeffs)). The vector is re-scaled such that sum(coeffs)==1. + * \param[in] tolerance - Relative pruning tolerance. + * \param[in] size - Size of the coefficient vector. + * \param[in,out] coeffs - Iterator to start of vector of interpolation coefficients. + * \return Number of non-zero coefficients after pruning and correction factor. + */ + template + static pair PruneSmallCoefficients(Float tolerance, Int size, ForwardIt coeffs) { + + /*--- Determine the pruning threshold. ---*/ + Float thresh = 0.0; + auto end = coeffs; + for (Int i = 0; i < size; ++i) + thresh = max(thresh, fabs(*(end++))); + thresh *= tolerance; + + /*--- Prune and count non-zeros. ---*/ + Int numNonZeros = 0; + Float coeffSum = 0.0; + for (auto it = coeffs; it != end; ++it) { + if (fabs(*it) > thresh) { // keep + coeffSum += *it; + ++numNonZeros; + } + else { *it = 0.0; } // prune + } + + /*--- Correct remaining coefficients, sum must be 1 for conservation. ---*/ + Float correction = 1.0 / coeffSum; + while (coeffs != end) *(coeffs++) *= correction; + + return make_pair(numNonZeros, correction); + } + +}; diff --git a/Common/include/interface_interpolation/CSlidingMesh.hpp b/Common/include/interface_interpolation/CSlidingMesh.hpp new file mode 100644 index 000000000000..74ae1d9ae18f --- /dev/null +++ b/Common/include/interface_interpolation/CSlidingMesh.hpp @@ -0,0 +1,129 @@ +/*! + * \file CSlidingMesh.hpp + * \brief Sliding mesh interpolation. + * \author H. Kline + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, 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 "CInterpolator.hpp" + +/*! + * \brief Sliding mesh approach. + * \note The algorithm is based on Rinaldi et al. "Flux-conserving treatment of non-conformal interfaces + * for finite-volume discritization of conservation laws" 2015, Comp. Fluids, 120, pp 126-139 + */ +class CSlidingMesh final : public CInterpolator { +public: + /*! + * \brief Constructor of the class. + * \param[in] geometry - Geometrical definition of the problem. + * \param[in] config - Definition of the particular problem. + * \param[in] iZone - index of the donor zone + * \param[in] jZone - index of the target zone + */ + CSlidingMesh(CGeometry ****geometry_container, const CConfig* const* config, + unsigned int iZone, unsigned int jZone); + + /*! + * \brief Set up transfer matrix defining relation between two meshes + * \param[in] config - Definition of the particular problem. + */ + void Set_TransferCoeff(const CConfig* const* config) override; + +private: + /*! + * \brief For 3-Dimensional grids, build the dual surface element + * \param[in] map - array containing the index of the boundary points connected to the node + * \param[in] startIndex - for each vertex specifies the corresponding index in the global + * array containing the indexes of all its neighbouring vertexes + * \param[in] nNeighbour - for each vertex specifies the number of its neighbouring vertexes (on the boundary) + * \param[in] coord - array containing the coordinates of all the boundary vertexes + * \param[in] centralNode - label of the vertex around which the dual surface element is built + * \param[out] element - double array where element node coordinates will be stored + * \return Number of points included in the element. + */ + static int Build_3D_surface_element(const unsigned long *map, const unsigned long *startIndex, + const unsigned long* nNeighbor, const su2double *coord, + unsigned long centralNode, su2double** element); + + /*! + * \brief For 2-Dimensional grids, compute intersection length of two segments projected along a given direction + * \param[in] nDim - Number of dimensions + * \param[in] A1 - first point of segment A + * \param[in] A2 - second point of segment A + * \param[in] B1 - first point of segment B + * \param[in] B2 - second point of segment B + * \param[in] Direction - along which segments are projected + */ + static su2double ComputeLineIntersectionLength(unsigned short nDim, const su2double* A1, const su2double* A2, + const su2double* B1, const su2double* B2, const su2double* Direction); + + /*! + * \brief For 3-Dimensional grids, compute intersection area between two triangle projected on a given plane + * \param[in] A1 - first point of triangle A + * \param[in] A2 - second point of triangle A + * \param[in] A3 - third point of triangle A + * \param[in] B1 - first point of triangle B + * \param[in] B2 - second point of triangle B + * \param[in] B3 - third point of triangle B + * \param[in] Direction - vector normal to projection plane + */ + static su2double Compute_Triangle_Intersection(const su2double* A1, const su2double* A2, const su2double* A3, + const su2double* B1, const su2double* B2, const su2double* B3, + const su2double* Direction); + + /*! + * \brief For 3-Dimensional grids, compute intersection area between two triangle projected on a given plane + * P1 from triangle P MUST be inside triangle Q, points order doesn't matter + * \param[in] P1 - first point of triangle A + * \param[in] P2 - second point of triangle A + * \param[in] P3 - third point of triangle A + * \param[in] Q1 - first point of triangle B + * \param[in] Q2 - second point of triangle B + * \param[in] Q3 - third point of triangle B + */ + static su2double ComputeIntersectionArea(const su2double* P1, const su2double* P2, const su2double* P3, + const su2double* Q1, const su2double* Q2, const su2double* Q3); + + /*! + * \brief For 2-Dimensional grids, check whether, and compute, two lines are intersecting + * \param[in] A1 - first defining first line + * \param[in] A2 - second defining first line + * \param[in] B1 - first defining second line + * \param[in] B2 - second defining second line + * \param[out] IntersectionPoint - Container for intersection coordinates + */ + static void ComputeLineIntersectionPoint(const su2double* A1, const su2double* A2, const su2double* B1, + const su2double* B2, su2double* IntersectionPoint); + + /*! + * \brief For N-Dimensional grids, check whether a point is inside a triangle specified by 3 T points + * \param[in] Point - query point + * \param[in] T1 - first point of triangle T + * \param[in] T2 - second point of triangle T + * \param[in] T3 - third point of triangle T + */ + static bool CheckPointInsideTriangle(const su2double* Point, const su2double* T1, + const su2double* T2, const su2double* T3); +}; diff --git a/Common/include/interpolation_structure.hpp b/Common/include/interpolation_structure.hpp deleted file mode 100644 index 28bdf3af8677..000000000000 --- a/Common/include/interpolation_structure.hpp +++ /dev/null @@ -1,491 +0,0 @@ -/*! - * \file interpolation_structure.hpp - * \brief Headers of classes used for multiphysics interpolation. - * The implementation is in the interpolation_structure.cpp file. - * \author H. Kline - * \version 7.0.2 "Blackbird" - * - * SU2 Project Website: https://su2code.github.io - * - * The SU2 Project is maintained by the SU2 Foundation - * (http://su2foundation.org) - * - * Copyright 2012-2020, 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 "../../Common/include/mpi_structure.hpp" - -#include -#include -#include -#include -#include - -#include "CConfig.hpp" -#include "geometry/CGeometry.hpp" - -using namespace std; - - -/*! - * \class CInterpolator - * \brief Main class for defining the interpolator, it requires - * a child class for each particular interpolation method - * \author H. Kline - */ -class CInterpolator { -protected: - int rank, /*!< \brief MPI Rank. */ - size; /*!< \brief MPI Size. */ - unsigned int nZone; /*!< \brief Number of zones*/ - unsigned int donorZone, - targetZone; /*!< \brief Type of MPI zone */ - - unsigned long - MaxLocalVertex_Donor, /*!< \brief Maximum vertices per processor*/ - nGlobalFace_Donor, /*!< \brief Number of global donor faces*/ - nGlobalFaceNodes_Donor, /*!< \brief Number of global donor face nodes*/ - MaxFace_Donor, /*!< \brief Maximum faces per processor*/ - MaxFaceNodes_Donor; /*!< \brief Maximum nodes associated with faces per processor*/ - - unsigned long - *Buffer_Receive_nVertex_Donor, /*!< \brief Buffer to store the number of vertices per processor on the Donor domain */ - *Buffer_Receive_nFace_Donor, /*!< \brief Buffer to store the number of faces per processor*/ - *Buffer_Receive_nFaceNodes_Donor, /*!< \brief Buffer to store the number of nodes associated with faces per processor*/ - *Buffer_Send_nVertex_Donor, /*!< \brief Buffer to send number of vertices on the local processor*/ - *Buffer_Send_nFace_Donor, /*!< \brief Buffer to send number of faces on the local processor*/ - *Buffer_Send_nFaceNodes_Donor, /*!< \brief Buffer to send the number of nodes assocated with faces per processor*/ - *Buffer_Send_FaceIndex, /*!< \brief Buffer to send indices pointing to the node indices that define the faces*/ - *Buffer_Receive_FaceIndex, /*!< \brief Buffer to receive indices pointing to the node indices that define the faces*/ - *Buffer_Send_FaceNodes, /*!< \brief Buffer to send indices pointing to the location of node information in other buffers, defining faces*/ - *Buffer_Receive_FaceNodes, /*!< \brief Buffer to receive indices pointing to the location of node information in other buffers, defining faces*/ - *Buffer_Send_FaceProc, /*!< \brief Buffer to send processor which stores the node indicated in Buffer_Receive_FaceNodes*/ - *Buffer_Receive_FaceProc; /*!< \brief Buffer to receive processor which stores the node indicated in Buffer_Receive_FaceNodes*/ - - long *Buffer_Send_GlobalPoint, /*!< \brief Buffer to send global point indices*/ - *Buffer_Receive_GlobalPoint; /*!< \brief Buffer to receive global point indices*/ - - su2double *Buffer_Send_Coord, /*!< \brief Buffer to send coordinate values*/ - *Buffer_Send_Normal, /*!< \brief Buffer to send normal vector values */ - *Buffer_Receive_Coord, /*!< \brief Buffer to receive coordinate values*/ - *Buffer_Receive_Normal; /*!< \brief Buffer to receive normal vector values*/ - - unsigned long *Receive_GlobalPoint, /*!< \brief Buffer to receive Global point indexes*/ - *Buffer_Receive_nLinkedNodes, /*!< \brief Buffer to receive the number of edges connected to each node*/ - *Buffer_Receive_LinkedNodes, /*!< \brief Buffer to receive the list of notes connected to the nodes through an edge*/ - *Buffer_Receive_StartLinkedNodes, /*!< \brief Buffer to receive the index of the Receive_LinkedNodes buffer where corresponding list of linked nodes begins */ - *Buffer_Receive_Proc; /*!< \brief Buffer to receive the thread that owns the node*/ - - unsigned long nGlobalVertex_Target, /*!< \brief Global number of vertex of the target boundary*/ - nLocalVertex_Target, /*!< \brief Number of vertex of the target boundary owned by the thread*/ - nGlobalVertex_Donor, /*!< \brief Global number of vertex of the donor boundary*/ - nLocalVertex_Donor, /*!< \brief Number of vertex of the donor boundary owned by the thread*/ - nGlobalVertex, /*!< \brief Dummy variable to temporarily store the global number of vertex of a boundary*/ - nLocalLinkedNodes; /*!< \brief Dummy variable to temporarily store the number of vertex of a boundary*/ - -public: - CGeometry**** Geometry; /*! \brief Vector which stores n zones of geometry. */ - CGeometry* donor_geometry; /*! \brief Vector which stores the donor geometry. */ - CGeometry* target_geometry; /*! \brief Vector which stores the target geometry. */ - - /*! - * \brief Constructor of the class. - */ - CInterpolator(void); - - /*! - * \brief Constructor of the class. - * \param[in] geometry - Geometrical definition of the problem. - * \param[in] config - Definition of the particular problem. - * \param[in] iZone - index of the donor zone - * \param[in] jZone - index of the target zone - */ - CInterpolator(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); - - /*! - * \brief Destructor of the class. - */ - virtual ~CInterpolator(void); - - /*! - * \brief Find the index of the interface marker shared by that zone - * \param[in] config - Definition of the particular problem. - * \param[in] val_marker_interface - Interface tag. - */ - int Find_InterfaceMarker(CConfig *config, unsigned short val_marker_interface); - - /*! - * \brief Check whether the interface should be processed or not - * \param[in] val_markDonor - Marker tag from donor zone. - * \param[in] val_markTarget - Marker tag from target zone. - */ - bool CheckInterfaceBoundary(int val_markDonor, int val_markTarget); - - /*! - * \brief Recontstruct the boundary connectivity from parallel partitioning and broadcasts it to all threads - * \param[in] val_zone - index of the zone - * \param[in] val_marker - index of the marker - */ - void ReconstructBoundary(unsigned long val_zone, int val_marker); - - /*! - * \brief compute distance between 2 points - * \param[in] point_i - * \param[in] point_i - */ - su2double PointsDistance(su2double *point_i, su2double *point_j); - - /*! - * \brief Set up transfer matrix defining relation between two meshes - * \param[in] config - Definition of the particular problem. - */ - virtual void Set_TransferCoeff(CConfig **config); - - /*! - * \brief Determine array sizes used to collect and send coordinate and global point - * information. - * \param[in] faces - boolean that determines whether or not to set face information as well - * \param[in] markDonor - Index of the boundary on the donor domain. - * \param[in] markTarget - Index of the boundary on the target domain. - * \param[in] nVertexDonor - Number of vertices on the donor boundary. - * \param[in] nDim - number of physical dimensions. - */ - void Determine_ArraySize(bool faces, int markDonor, int markTarget, unsigned long nVertexDonor, unsigned short nDim); - - /*! - * \brief Collect and communicate vertex info: coord, global point, and if faces=true the normal vector - * \param[in] faces - boolean that determines whether or not to set face information as well - * \param[in] markDonor - Index of the boundary on the donor domain. - * \param[in] markTarget - Index of the boundary on the target domain. - * \param[in] nVertexDonor - Number of vertices on the donor boundary. - * \param[in] nDim - number of physical dimensions. - */ - void Collect_VertexInfo(bool faces, int markDonor, int markTarget, unsigned long nVertexDonor, unsigned short nDim); - -}; - -/*! - * \brief Nearest Neighbor interpolation - */ -class CNearestNeighbor : public CInterpolator { -public: - - /*! - * \brief Constructor of the class. - */ - CNearestNeighbor(void); - - /*! - * \brief Constructor of the class. - * \param[in] geometry - Geometrical definition of the problem. - * \param[in] config - Definition of the particular problem. - * \param[in] iZone - index of the donor zone - * \param[in] jZone - index of the target zone - */ - CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); - - /*! - * \brief Destructor of the class. - */ - ~CNearestNeighbor(void); - - /*! - * \brief Set up transfer matrix defining relation between two meshes - * \param[in] config - Definition of the particular problem. - */ - void Set_TransferCoeff(CConfig **config); - -}; - -/*! - * \brief Isoparametric interpolation - */ -class CIsoparametric : public CInterpolator { -public: - - /*! - * \brief Constructor of the class. - * \param[in] geometry - Geometrical definition of the problem. - * \param[in] config - Definition of the particular problem. - * \param[in] iZone - index of the donor zone - * \param[in] jZone - index of the target zone - */ - CIsoparametric(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); - - /*! - * \brief Destructor of the class. - */ - ~CIsoparametric(void); - - /*! - * \brief Set up transfer matrix defining relation between two meshes - * \param[in] config - Definition of the particular problem. - */ - void Set_TransferCoeff(CConfig **config); - - /*! - * \brief Calculate the isoparametric representation of point iVertex in marker iZone_0 by nodes of element donor_elem in marker jMarker of zone iZone_1. - * \param[in] iVertex - vertex index of the point being interpolated. - * \param[in] nDim - the dimension of the coordinates. - * \param[in] iZone_1 - zone index of the element to use for interpolation (the DONOR zone) - * \param[in] donor_elem - element index of the element to use for interpolation (or global index of a point in 2D) - * \param[in] nDonorPoints - number of donor points in the element. - * \param[in] xj - point projected onto the plane of the donor element. - * \param[out] isoparams - isoparametric coefficients. Must be allocated to size nNodes ahead of time. (size> nDonors) - * - * If the problem is 2D, the 'face' projected onto is actually an edge; the local index - * of the edge is then stored in iFace, and the global index of the node (from which the edge - * is referenced) - */ - void Isoparameters(unsigned short nDim, unsigned short nDonor, su2double *X, su2double *xj,su2double* isoparams); - -}; - -/*! - * \brief Mirror interpolation: copy point linking and coefficient values from the opposing mesh - * Assumes that the oppoosing mesh has already run interpolation. (otherwise this will result in empty/trivial interpolation) - */ -class CMirror : public CInterpolator { -public: - - /*! - * \brief Constructor of the class. - * \param[in] geometry_container - * \param[in] config - config container - * \param[in] iZone - First zone - * \param[in] jZone - Second zone - * - * Data is set in geometry[targetZone] - * - */ - CMirror(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); - - /*! - * \brief Destructor of the class. - */ - ~CMirror(void); - - /*! - * \brief Set up transfer matrix defining relation between two meshes - * \param[in] config - Definition of the particular problem. - */ - void Set_TransferCoeff(CConfig **config); - -}; - -/*! - * \brief Sliding mesh approach - */ -class CSlidingMesh : public CInterpolator { -public: - - /*! - * \brief Constructor of the class. - * \param[in] geometry - Geometrical definition of the problem. - * \param[in] config - Definition of the particular problem. - * \param[in] iZone - index of the donor zone - * \param[in] jZone - index of the target zone - */ - CSlidingMesh(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); - - /*! - * \brief Destructor of the class. - */ - ~CSlidingMesh(void); - - /*! - * \brief Set up transfer matrix defining relation between two meshes - * \param[in] config - Definition of the particular problem. - */ - void Set_TransferCoeff(CConfig **config); - - /*! - * \brief For 3-Dimensional grids, build the dual surface element - * \param[in] map - array containing the index of the boundary points connected to the node - * \param[in] startIndex - for each vertex specifies the corresponding index in the global array containing the indexes of all its neighbouring vertexes - * \param[in] nNeighbour - for each vertex specifies the number of its neighbouring vertexes (on the boundary) - * \param[in] coord - array containing the coordinates of all the boundary vertexes - * \param[in] centralNode - label of the vertex around which the dual surface element is built - * \param[in] element - double array where element node coordinates will be stored - */ - int Build_3D_surface_element(unsigned long *map, unsigned long *startIndex, unsigned long* nNeighbor, su2double *coord, unsigned long centralNode, su2double** element); - - /*! - * \brief For 2-Dimensional grids, compute intersection length of two segments projected along a given direction - * \param[in] A1 - first point of segment A - * \param[in] A2 - second point of segment A - * \param[in] B1 - first point of segment B - * \param[in] B2 - second point of segment B - * \param[in] Direction - along which segments are projected - */ - su2double ComputeLineIntersectionLength(su2double* A1, su2double* A2, su2double* B1, su2double* B2, su2double* Direction); - - /*! - * \brief For 3-Dimensional grids, compute intersection area between two triangle projected on a given plane - * \param[in] A1 - first point of triangle A - * \param[in] A2 - second point of triangle A - * \param[in] A3 - third point of triangle A - * \param[in] B1 - first point of triangle B - * \param[in] B2 - second point of triangle B - * \param[in] B3 - third point of triangle B - * \param[in] Direction - vector normal to projection plane - */ - su2double Compute_Triangle_Intersection(su2double* A1, su2double* A2, su2double* A3, su2double* B1, su2double* B2, su2double* B3, su2double* Direction); - - /*! - * \brief For 3-Dimensional grids, compute intersection area between two triangle projected on a given plane - * P1 from triangle P MUST be inside triangle Q, points order doesn't matter - * \param[in] P1 - first point of triangle A - * \param[in] P2 - second point of triangle A - * \param[in] P3 - third point of triangle A - * \param[in] Q1 - first point of triangle B - * \param[in] Q2 - second point of triangle B - * \param[in] Q3 - third point of triangle B - */ - su2double ComputeIntersectionArea( su2double* P1, su2double* P2, su2double* P3, su2double* Q1, su2double* Q2, su2double* Q3 ); - - /*! - * \brief For 2-Dimensional grids, check whether, and compute, two lines are intersecting - * \param[in] A1 - first defining first line - * \param[in] A2 - second defining first line - * \param[in] B1 - first defining second line - * \param[in] B2 - second defining second line - * \param[in] IntersectionPoint - Container for intersection coordinates - */ - void ComputeLineIntersectionPoint( su2double* A1, su2double* A2, su2double* B1, su2double* B2, su2double* IntersectionPoint ); - - /*! - * \brief For N-Dimensional grids, check whether a point is inside a triangle specified by 3 T points - * \param[in] Point - query point - * \param[in] T1 - first point of triangle T - * \param[in] T2 - second point of triangle T - * \param[in] T3 - third point of triangle T - */ - bool CheckPointInsideTriangle(su2double* Point, su2double* T1, su2double* T2, su2double* T3); -}; - -/*! - * \brief Radial basis function interpolation - */ -class CRadialBasisFunction : public CInterpolator { -public: - - /*! - * \brief Constructor of the class. - */ - CRadialBasisFunction(void); - - /*! - * \brief Constructor of the class. - * \param[in] geometry - Geometrical definition of the problem. - * \param[in] config - Definition of the particular problem. - * \param[in] iZone - index of the donor zone - * \param[in] jZone - index of the target zone - */ - CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); - - /*! - * \brief Destructor of the class. - */ - ~CRadialBasisFunction(void); - - /*! - * \brief Set up transfer matrix defining relation between two meshes - * \param[in] config - Definition of the particular problem. - */ - void Set_TransferCoeff(CConfig **config); - - /*! - * \brief Compute the value of a radial basis function, this is static so it can be re-used. - * \param[in] type - of radial basis function - * \param[in] radius - the characteristic dimension - * \param[in] dist - distance - */ - static su2double Get_RadialBasisValue(const short unsigned int type, const su2double &radius, const su2double &dist); - -private: - /*! - * \brief If the polynomial term is included in the interpolation, and the points lie on a plane, the matrix becomes rank deficient - * and cannot be inverted. This method detects that condition and corrects it by removing a row from P (the polynomial part of the matrix). - * \param[in] m - number of rows of P - * \param[in] n - number of columns of P - * \param[in] skip_row - marks the row of P which is all ones (by construction) - * \param[in] max_diff_tol_in - tolerance to detect points are on a plane - * \param[out] keep_row - marks the rows of P kept - * \param[out] n_polynomial - size of the polynomial part on exit (i.e. new number of rows) - * \param[in,out] P - polynomial part of the matrix, may be changed or not! - */ - void Check_PolynomialTerms(int m, unsigned long n, const int *skip_row, su2double max_diff_tol_in, int *keep_row, int &n_polynomial, su2double *P); - -}; - -/*! - * \brief Helper class used by CRadialBasisFunction to calculate the interpolation weights. - * This does not inherit from CSysMatrix because: it is a dense format rather than block sparse; - * as the interpolation is done on a single core there are no methods for communication. - * The code can be compiled with LAPACK to use optimized matrix inversion and multiplication routines. - * CPPFLAGS="-DHAVE_LAPACK" LDFLAGS=-L/path/to/lapack_lib LIBS="-llapack -lrefblas -lgfortran" - */ -class CSymmetricMatrix{ - - private: - - bool initialized, inversed; - int sz, num_val; - int *perm_vec; - passivedouble *val_vec, *decompose_vec, *inv_val_vec; - - enum DecompositionType { none, cholesky, lu }; - - DecompositionType decomposed; - - inline int CalcIdx(int i, int j); - inline int CalcIdxFull(int i, int j); - inline void CheckBounds(int i, int j); - - passivedouble ReadL(int i, int j); - passivedouble ReadU(int i, int j); - passivedouble ReadInv(int i,int j); - - // not optimized dense matrix factorization and inversion for portability - void CholeskyDecompose(bool overwrite); - void LUDecompose(); - void CalcInv(bool overwrite); - // matrix inversion using LAPACK routines (LDLT factorization) - void CalcInv_sptri(); - void CalcInv_potri() {}; // LLT not implemented yet - - public: - - /*--- Methods ---*/ - CSymmetricMatrix(); - ~CSymmetricMatrix(); - - void Initialize(int N); - void Initialize(int N, su2double *formed_val_vec); - - inline int GetSize(); - - void Write(int i, int j, const su2double& val); - passivedouble Read(int i, int j); - - void MatVecMult(passivedouble *v); - void MatMatMult(bool left_mult, su2double *mat_vec, int N); - void Invert(const bool is_spd); - -}; diff --git a/Common/include/linear_algebra/CSysMatrix.hpp b/Common/include/linear_algebra/CSysMatrix.hpp index b01be62fe13f..73d803a4fbff 100644 --- a/Common/include/linear_algebra/CSysMatrix.hpp +++ b/Common/include/linear_algebra/CSysMatrix.hpp @@ -95,7 +95,7 @@ class CSysMatrix { const unsigned long *row_ptr; /*!< \brief Pointers to the first element in each row. */ const unsigned long *dia_ptr; /*!< \brief Pointers to the diagonal element in each row. */ const unsigned long *col_ind; /*!< \brief Column index for each of the elements in val(). */ - vector col_ptr;/*!< \brief The transpose of col_ind, pointer to blocks with the same column index. */ + const unsigned long *col_ptr; /*!< \brief The transpose of col_ind, pointer to blocks with the same column index. */ ScalarType *ILU_matrix; /*!< \brief Entries of the ILU sparse matrix. */ unsigned long nnz_ilu; /*!< \brief Number of possible nonzero entries in the matrix (ILU). */ @@ -349,10 +349,12 @@ class CSysMatrix { * \param[in] neqn - Number of equations. * \param[in] geometry - Geometrical definition of the problem. * \param[in] config - Definition of the particular problem. + * \param[in] needTranspPtr - If "col_ptr" should be created. */ void Initialize(unsigned long npoint, unsigned long npointdomain, unsigned short nvar, unsigned short neqn, - bool EdgeConnect, CGeometry *geometry, CConfig *config); + bool EdgeConnect, CGeometry *geometry, + CConfig *config, bool needTranspPtr = false); /*! * \brief Sets to zero all the entries of the sparse matrix. @@ -584,11 +586,13 @@ class CSysMatrix { * \brief Update 2 blocks ij and ji (add to i* sub from j*). * \note The template parameter Sign, can be used create a "subtractive" * update i.e. subtract from row i and add to row j instead. + * The parameter Overwrite allows completely writing over the + * current values held by the matrix. * \param[in] edge - Index of edge that connects iPoint and jPoint. * \param[in] block_i - Subs from ji. * \param[in] block_j - Adds to ij. */ - template + template inline void UpdateBlocks(unsigned long iEdge, const OtherType* const* block_i, const OtherType* const* block_j) { ScalarType *bij = &matrix[edge_ptr(iEdge,0)*nVar*nEqn]; @@ -598,8 +602,8 @@ class CSysMatrix { for (iVar = 0; iVar < nVar; iVar++) { for (jVar = 0; jVar < nEqn; jVar++) { - bij[offset] += PassiveAssign(block_j[iVar][jVar]) * Sign; - bji[offset] -= PassiveAssign(block_i[iVar][jVar]) * Sign; + bij[offset] = (Overwrite? ScalarType(0) : bij[offset]) + PassiveAssign(block_j[iVar][jVar]) * Sign; + bji[offset] = (Overwrite? ScalarType(0) : bji[offset]) - PassiveAssign(block_i[iVar][jVar]) * Sign; ++offset; } } @@ -613,6 +617,14 @@ class CSysMatrix { UpdateBlocks(iEdge, block_i, block_j); } + /*! + * \brief Short-hand for the "additive overwrite" version of UpdateBlocks. + */ + template + inline void SetBlocks(unsigned long iEdge, const OtherType* const* block_i, const OtherType* const* block_j) { + UpdateBlocks(iEdge, block_i, block_j); + } + /*! * \brief Adds the specified block to the (i, i) subblock of the matrix-by-blocks structure. * \param[in] block_i - Diagonal index. diff --git a/Common/include/omp_structure.hpp b/Common/include/omp_structure.hpp index 0a4ac692ae92..ad6e1c29d57b 100644 --- a/Common/include/omp_structure.hpp +++ b/Common/include/omp_structure.hpp @@ -80,6 +80,19 @@ inline void omp_set_num_threads(int) { } */ inline constexpr int omp_get_thread_num(void) {return 0;} +/*! + * \brief Dummy lock type and associated functions. + */ +struct omp_lock_t {}; +struct DummyVectorOfLocks { + omp_lock_t l; + inline omp_lock_t& operator[](int) {return l;} +}; +inline void omp_init_lock(omp_lock_t*){} +inline void omp_set_lock(omp_lock_t*){} +inline void omp_unset_lock(omp_lock_t*){} +inline void omp_destroy_lock(omp_lock_t*){} + #endif /*--- Convenience macros (do not use excessive nesting of macros). ---*/ @@ -108,6 +121,14 @@ inline constexpr size_t roundUpDiv(size_t numerator, size_t denominator) return (numerator+denominator-1)/denominator; } +/*! + * \brief Round up to next multiple. + */ +inline constexpr size_t nextMultiple(size_t argument, size_t multiple) +{ + return roundUpDiv(argument, multiple) * multiple; +} + /*! * \brief Compute a chunk size based on totalWork and number of threads such that * all threads get the same number of chunks (with limited size). diff --git a/Common/include/option_structure.hpp b/Common/include/option_structure.hpp index f2a115236b8c..1387b3227064 100644 --- a/Common/include/option_structure.hpp +++ b/Common/include/option_structure.hpp @@ -133,6 +133,8 @@ const int SU2_CONN_SIZE = 10; /*!< \brief Size of the connectivity array that that we read from a mesh file in the format [[globalID vtkType n0 n1 n2 n3 n4 n5 n6 n7 n8]. */ const int SU2_CONN_SKIP = 2; /*!< \brief Offset to skip the globalID and VTK type at the start of the element connectivity list for each CGNS element. */ +const su2double COLORING_EFF_THRESH = 0.875; /*!< \brief Below this value fallback strategies are used instead. */ + /*! * \brief Boolean answers */ diff --git a/Common/include/toolboxes/C1DInterpolation.hpp b/Common/include/toolboxes/C1DInterpolation.hpp index a336b33a2448..c5653de37f27 100644 --- a/Common/include/toolboxes/C1DInterpolation.hpp +++ b/Common/include/toolboxes/C1DInterpolation.hpp @@ -84,11 +84,6 @@ class CAkimaInterpolation final: public C1DInterpolation{ SetSpline(X,Data); } - /*! - * \brief Destructor of the CAkimaInterpolation class. - */ - ~CAkimaInterpolation(){} - /*! * \brief for setting the cofficients for the Akima spline. * \param[in] X - the x values. @@ -119,11 +114,6 @@ class CLinearInterpolation final: public C1DInterpolation{ SetSpline(X,Data); } - /*! - * \brief Destructor of the CInletInterpolation class. - */ - ~CLinearInterpolation(){} - /*! * \brief for setting the cofficients for Linear 'spline'. * \param[in] X - the x values. diff --git a/Common/include/toolboxes/C2DContainer.hpp b/Common/include/toolboxes/C2DContainer.hpp index a5c978f2bff3..4b3dfcad14f1 100644 --- a/Common/include/toolboxes/C2DContainer.hpp +++ b/Common/include/toolboxes/C2DContainer.hpp @@ -113,15 +113,14 @@ class AccessorImpl \ AccessorImpl& operator= (AccessorImpl&& other) noexcept \ { \ - if(m_data!=nullptr) free(m_data); \ + MemoryAllocation::aligned_free(m_data); \ MOVE; m_data=other.m_data; other.m_data=nullptr; \ return *this; \ } \ \ ~AccessorImpl() \ { \ - if(m_data!=nullptr) \ - MemoryAllocation::aligned_free(m_data); \ + MemoryAllocation::aligned_free(m_data); \ } /*! * Shorthand for when specialization has only one more member than m_data. @@ -371,6 +370,7 @@ class C2DContainer : using Base::size; using Index = Index_t; using Scalar = Scalar_t; + static constexpr StorageType Storage = Store; private: /*! @@ -380,7 +380,7 @@ class C2DContainer : { /*--- fully static, no allocation needed ---*/ if(StaticRows!=DynamicSize && StaticCols!=DynamicSize) - return StaticRows*StaticCols; + return StaticRows*StaticCols; /*--- dynamic row vector, swap size specification ---*/ if(StaticRows==1 && StaticCols==DynamicSize) {cols = rows; rows = 1;} @@ -399,12 +399,10 @@ class C2DContainer : /*--- compare with current dimensions to determine if deallocation is needed, also makes the container safe against self assignment no need to check for 0 size as the allocators handle that ---*/ - if(m_data!=nullptr) - { - if(rows==this->rows() && cols==this->cols()) - return reqSize; - free(m_data); - } + if(rows==this->rows() && cols==this->cols()) + return reqSize; + + MemoryAllocation::aligned_free(m_data); /*--- request actual allocation to base class as it needs specialization ---*/ size_t bytes = reqSize*sizeof(Scalar_t); diff --git a/Common/include/toolboxes/CSymmetricMatrix.hpp b/Common/include/toolboxes/CSymmetricMatrix.hpp new file mode 100644 index 000000000000..bc15264c7049 --- /dev/null +++ b/Common/include/toolboxes/CSymmetricMatrix.hpp @@ -0,0 +1,87 @@ +/*! + * \file CSymmetricMatrix.hpp + * \brief Dense symmetric matrix, used for example in RBF interpolation. + * \author Joel Ho, P. Gomes + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, 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 "C2DContainer.hpp" + +using namespace std; + +/*! + * \brief The matrix is symmetric but full storage is used as that gives much better + * performance for some BLAS libraries (notably OpenBLAS). The code should be compiled + * with LAPACK to use optimized matrix inversion and multiplication routines. + */ +class CSymmetricMatrix { + static_assert(su2passivematrix::Storage == StorageType::RowMajor, + "Row major storage is assumed for LAPACK."); +private: + su2passivematrix mat; + + // Not optimized dense matrix factorization and inversion for portability. + void CalcInv(bool is_spd); + void CholeskyDecompose(); + void LUDecompose(su2passivematrix& decomp, vector& perm) const; + // Matrix inversion using LAPACK routines (LDLT and LLT factorization). + void CalcInv_sytri(); + void CalcInv_potri(); + +public: + CSymmetricMatrix() = default; + CSymmetricMatrix(int N) {Initialize(N);} + + void Initialize(int N); + + inline int Size() const { return mat.rows(); } + + inline passivedouble Get(int i, int j) const { return mat(min(i,j),max(i,j)); } + + inline void Set(int i, int j, passivedouble val) { mat(min(i,j),max(i,j)) = val; } + + inline passivedouble& operator() (int i, int j) { return mat(min(i,j),max(i,j)); } + + inline const passivedouble& operator() (int i, int j) const { return mat(min(i,j),max(i,j)); } + + template + void MatVecMult(ForwardIt vec_in, ForwardIt vec_out) const + { + for (int i = 0; i < Size(); ++i) { + *vec_out = 0.0; + auto vec = vec_in; + for (int k = 0; k < Size(); ++k) + *vec_out += *(vec++) * Get(i,k); + ++vec_out; + } + } + + void MatMatMult(const char side, const su2passivematrix& mat_in, su2passivematrix& mat_out) const; + + void Invert(bool is_spd = false); + + su2passivematrix StealData(); + +}; diff --git a/Common/include/toolboxes/graph_toolbox.hpp b/Common/include/toolboxes/graph_toolbox.hpp index 5f5ea4cdf5bc..e87d48402f9a 100644 --- a/Common/include/toolboxes/graph_toolbox.hpp +++ b/Common/include/toolboxes/graph_toolbox.hpp @@ -28,6 +28,7 @@ #pragma once #include "C2DContainer.hpp" +#include "../omp_structure.hpp" #include #include @@ -59,6 +60,7 @@ class CCompressedSparsePattern { su2vector m_outerPtr; /*!< \brief Start positions of the inner indices for each outer index. */ su2vector m_innerIdx; /*!< \brief Inner indices of the non zero entries. */ su2vector m_diagPtr; /*!< \brief Position of the diagonal entry. */ + su2vector m_innerIdxTransp; /*!< \brief Position of the transpose non zero entries, requires symmetry. */ public: using IndexType = Index_t; @@ -107,10 +109,30 @@ class CCompressedSparsePattern { if(!m_diagPtr.empty()) return; m_diagPtr.resize(getOuterSize()); + + SU2_OMP_PARALLEL_(for schedule(static,roundUpDiv(getOuterSize(),omp_get_max_threads()))) for(Index_t k = 0; k < getOuterSize(); ++k) m_diagPtr(k) = findInnerIdx(k,k); } + /*! + * \brief Build a list of pointers to the transpose entries of the pattern, requires symmetry. + */ + void buildTransposePtr() { + if(!m_innerIdxTransp.empty()) return; + + m_innerIdxTransp.resize(getNumNonZeros()); + + SU2_OMP_PARALLEL_(for schedule(static,roundUpDiv(getOuterSize(),omp_get_max_threads()))) + for(Index_t i = 0; i < getOuterSize(); ++i) { + for(Index_t k = m_outerPtr(i); k < m_outerPtr(i+1); ++k) { + auto j = m_innerIdx(k); + m_innerIdxTransp(k) = findInnerIdx(j,i); + assert(m_innerIdxTransp(k) != m_innerIdx.size() && "The pattern is not symmetric."); + } + } + } + /*! * \return True if the pattern is empty, i.e. has not been built yet. */ @@ -224,6 +246,14 @@ class CCompressedSparsePattern { return m_diagPtr.data(); } + /*! + * \return Raw pointer to the transpose pointer vector. + */ + inline const su2vector& transposePtr() const { + assert(!m_innerIdxTransp.empty() && "Transpose map has not been built."); + return m_innerIdxTransp; + } + /*! * \return The minimum inner index. */ @@ -384,6 +414,30 @@ CEdgeToNonZeroMap mapEdgesToSparsePattern(Geometry_t& geometry, } +/*! + * \brief Create the natural coloring (equivalent to the normal sequential loop + * order) for a given number of inner indexes. + * \note This is to reduce overhead in "OpenMP-ready" code when only 1 thread is used. + * \param[in] numInnerIndexes - Number of indexes that are to be colored. + * \return Natural (sequential) coloring of the inner indices. + */ +template +T createNaturalColoring(Index_t numInnerIndexes) +{ + /*--- One color. ---*/ + su2vector outerPtr(2); + outerPtr(0) = 0; + outerPtr(1) = numInnerIndexes; + + /*--- Containing all indexes in ascending order. ---*/ + su2vector innerIdx(numInnerIndexes); + std::iota(innerIdx.data(), innerIdx.data()+numInnerIndexes, 0); + + return T(std::move(outerPtr), std::move(innerIdx)); +} + + /*! * \brief Color contiguous groups of outer indices of a sparse pattern such that * within each color, any two groups do not have inner indices in common. @@ -404,7 +458,7 @@ CEdgeToNonZeroMap mapEdgesToSparsePattern(Geometry_t& geometry, * \param[out] indexColor - Optional, vector with colors given to the outer indices. * \return Coloring in the same type of the input pattern. */ -template +template T colorSparsePattern(const T& pattern, size_t groupSize = 1, bool balanceColors = false, std::vector* indexColor = nullptr) { @@ -415,6 +469,10 @@ T colorSparsePattern(const T& pattern, size_t groupSize = 1, bool balanceColors const Index_t grpSz = groupSize; const Index_t nOuter = pattern.getOuterSize(); + + /*--- Trivial case. ---*/ + if(groupSize >= nOuter) return createNaturalColoring(nOuter); + const Index_t minIdx = pattern.getMinInnerIdx(); const Index_t nInner = pattern.getMaxInnerIdx()+1-minIdx; @@ -520,30 +578,6 @@ T colorSparsePattern(const T& pattern, size_t groupSize = 1, bool balanceColors } -/*! - * \brief Create the natural coloring (equivalent to the normal sequential loop - * order) for a given number of inner indexes. - * \note This is to reduce overhead in "OpenMP-ready" code when only 1 thread is used. - * \param[in] numInnerIndexes - Number of indexes that are to be colored. - * \return Natural (sequential) coloring of the inner indices. - */ -template -T createNaturalColoring(Index_t numInnerIndexes) -{ - /*--- One color. ---*/ - su2vector outerPtr(2); - outerPtr(0) = 0; - outerPtr(1) = numInnerIndexes; - - /*--- Containing all indexes in ascending order. ---*/ - su2vector innerIdx(numInnerIndexes); - std::iota(innerIdx.data(), innerIdx.data()+numInnerIndexes, 0); - - return T(std::move(outerPtr), std::move(innerIdx)); -} - - /*! * \brief A way to represent one grid color that allows range-for syntax. */ @@ -553,9 +587,11 @@ struct GridColor static_assert(std::is_integral::value,""); const T size; + T groupSize; const T* const indices; - GridColor(const T* idx = nullptr, T sz = 0) : size(sz), indices(idx) { } + GridColor(const T* idx = nullptr, T sz = 0, T grp = 0) : + size(sz), groupSize(grp), indices(idx) { } inline const T* begin() const {return indices;} inline const T* end() const {return indices+size;} @@ -592,3 +628,23 @@ struct DummyGridColor inline IteratorLikeInt begin() const {return IteratorLikeInt(0);} inline IteratorLikeInt end() const {return IteratorLikeInt(size);} }; + + +/*! + * \brief Computes the efficiency of a grid coloring for given number of threads and chunk size. + */ +template +su2double coloringEfficiency(const SparsePattern& coloring, int numThreads, int chunkSize) +{ + using Index_t = typename SparsePattern::IndexType; + + /*--- Ideally compute time is proportional to total work over number of threads. ---*/ + su2double ideal = coloring.getNumNonZeros() / su2double(numThreads); + + /*--- In practice the total work is quantized first by colors and then by chunks. ---*/ + Index_t real = 0; + for(Index_t color = 0; color < coloring.getOuterSize(); ++color) + real += chunkSize * roundUpDiv(roundUpDiv(coloring.getNumNonZeros(color), chunkSize), numThreads); + + return ideal / real; +} diff --git a/Common/lib/Makefile.am b/Common/lib/Makefile.am index 4ef40da28d37..f1a6f0d1b11f 100644 --- a/Common/lib/Makefile.am +++ b/Common/lib/Makefile.am @@ -93,12 +93,18 @@ lib_sources = \ ../src/geometry/primal_grid/CTetrahedron.cpp \ ../src/geometry/primal_grid/CQuadrilateral.cpp \ ../src/geometry/primal_grid/CVertexMPI.cpp \ - ../src/interpolation_structure.cpp \ + ../src/interface_interpolation/CInterpolator.cpp \ + ../src/interface_interpolation/CMirror.cpp \ + ../src/interface_interpolation/CSlidingMesh.cpp \ + ../src/interface_interpolation/CIsoparametric.cpp \ + ../src/interface_interpolation/CNearestNeighbor.cpp \ + ../src/interface_interpolation/CRadialBasisFunction.cpp \ ../src/adt_structure.cpp \ ../src/wall_model.cpp \ ../src/toolboxes/printing_toolbox.cpp \ ../src/toolboxes/CLinearPartitioner.cpp \ ../src/toolboxes/C1DInterpolation.cpp \ + ../src/toolboxes/CSymmetricMatrix.cpp \ ../src/toolboxes/MMS/CVerificationSolution.cpp \ ../src/toolboxes/MMS/CIncTGVSolution.cpp \ ../src/toolboxes/MMS/CInviscidVortexSolution.cpp \ diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index 283c0768abd5..cf65fc655c53 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -935,7 +935,7 @@ void CConfig::SetPointersNull(void) { Kind_SurfaceMovement = NULL; LocationStations = NULL; Motion_Origin = NULL; - Translation_Rate = NULL; + Translation_Rate = NULL; Rotation_Rate = NULL; Pitching_Omega = NULL; Pitching_Ampl = NULL; @@ -943,7 +943,7 @@ void CConfig::SetPointersNull(void) { Plunging_Omega = NULL; Plunging_Ampl = NULL; MarkerMotion_Origin = NULL; - MarkerTranslation_Rate = NULL; + MarkerTranslation_Rate = NULL; MarkerRotation_Rate = NULL; MarkerPitching_Omega = NULL; MarkerPitching_Ampl = NULL; @@ -2457,17 +2457,17 @@ void CConfig::SetConfig_Options() { addBoolOption("WRT_FORCES_BREAKDOWN", Wrt_ForcesBreakdown, false); - - /* DESCRIPTION: Use conservative approach for interpolating between meshes. - * Options: NO, YES \ingroup Config */ - addBoolOption("CONSERVATIVE_INTERPOLATION", ConservativeInterpolation, true); - /*!\par KIND_INTERPOLATION \n * DESCRIPTION: Type of interpolation to use for multi-zone problems. \n OPTIONS: see \link Interpolator_Map \endlink * Sets Kind_Interpolation \ingroup Config */ addEnumOption("KIND_INTERPOLATION", Kind_Interpolation, Interpolator_Map, NEAREST_NEIGHBOR); + /* DESCRIPTION: Use conservative approach for interpolating between meshes. */ + addBoolOption("CONSERVATIVE_INTERPOLATION", ConservativeInterpolation, true); + + addUnsignedShortOption("NUM_NEAREST_NEIGHBORS", NumNearestNeighbors, 1); + /*!\par KIND_INTERPOLATION \n * DESCRIPTION: Type of radial basis function to use for radial basis function interpolation. \n OPTIONS: see \link RadialBasis_Map \endlink * Sets Kind_RadialBasis \ingroup Config @@ -2478,8 +2478,11 @@ void CConfig::SetConfig_Options() { * Options: NO, YES \ingroup Config */ addBoolOption("RADIAL_BASIS_FUNCTION_POLYNOMIAL_TERM", RadialBasisFunction_PolynomialOption, true); - /* DESCRIPTION: Radius for radial basis function */ - addDoubleOption("RADIAL_BASIS_FUNCTION_PARAMETER", RadialBasisFunction_Parameter, 1); + /* DESCRIPTION: Radius for radial basis function. */ + addDoubleOption("RADIAL_BASIS_FUNCTION_PARAMETER", RadialBasisFunction_Parameter, 1.0); + + /* DESCRIPTION: Tolerance to prune small coefficients from the RBF interpolation matrix. */ + addDoubleOption("RADIAL_BASIS_FUNCTION_PRUNE_TOLERANCE", RadialBasisFunction_PruneTol, 1e-6); /*!\par INLETINTERPOLATION \n * DESCRIPTION: Type of spanwise interpolation to use for the inlet face. \n OPTIONS: see \link Inlet_SpanwiseInterpolation_Map \endlink @@ -2807,7 +2810,7 @@ void CConfig::SetConfig_Options() { /* DESCRIPTION: Level of fill for PaStiX incomplete LU factorization. */ addUnsignedShortOption("PASTIX_FILL_LEVEL", pastix_fill_lvl, 1); - /* DESCRIPTION: Size of the edge groups colored for OpenMP parallelization of edge loops. */ + /* DESCRIPTION: Size of the edge groups colored for thread parallel edge loops (0 forces the reducer strategy). */ addUnsignedLongOption("EDGE_COLORING_GROUP_SIZE", edgeColorGroupSize, 512); /* END_CONFIG_OPTIONS */ @@ -3677,96 +3680,96 @@ void CConfig::SetPostprocessing(unsigned short val_software, unsigned short val_ for (iMarker = 0; iMarker < nMarker_Moving; iMarker++){ for (iDim = 0; iDim < 3; iDim++){ MarkerMotion_Origin[3*iMarker+iDim] = 0.0; - } - } + } + } } if (nMarkerMotion_Origin/3 != nMarker_Moving){ SU2_MPI::Error("Number of SURFACE_MOTION_ORIGIN must be three times the number of MARKER_MOVING, (x,y,z) per marker.", CURRENT_FUNCTION); - } + } if (nMarkerTranslation == 0){ nMarkerTranslation = 3*nMarker_Moving; MarkerTranslation_Rate = new su2double[nMarkerTranslation]; for (iMarker = 0; iMarker < nMarker_Moving; iMarker++){ for (iDim = 0; iDim < 3; iDim++){ MarkerTranslation_Rate[3*iMarker+iDim] = 0.0; - } - } + } + } } if (nMarkerTranslation/3 != nMarker_Moving){ SU2_MPI::Error("Number of SURFACE_TRANSLATION_RATE must be three times the number of MARKER_MOVING, (x,y,z) per marker.", CURRENT_FUNCTION); - } + } if (nMarkerRotation_Rate == 0){ nMarkerRotation_Rate = 3*nMarker_Moving; MarkerRotation_Rate = new su2double[nMarkerRotation_Rate]; for (iMarker = 0; iMarker < nMarker_Moving; iMarker++){ for (iDim = 0; iDim < 3; iDim++){ MarkerRotation_Rate[3*iMarker+iDim] = 0.0; - } - } + } + } } if (nMarkerRotation_Rate/3 != nMarker_Moving){ SU2_MPI::Error("Number of SURFACE_ROTATION_RATE must be three times the number of MARKER_MOVING, (x,y,z) per marker.", CURRENT_FUNCTION); - } + } if (nMarkerPlunging_Ampl == 0){ nMarkerPlunging_Ampl = 3*nMarker_Moving; MarkerPlunging_Ampl = new su2double[nMarkerPlunging_Ampl]; for (iMarker = 0; iMarker < nMarker_Moving; iMarker++){ for (iDim = 0; iDim < 3; iDim++){ MarkerPlunging_Ampl[3*iMarker+iDim] = 0.0; - } - } + } + } } if (nMarkerPlunging_Ampl/3 != nMarker_Moving){ SU2_MPI::Error("Number of SURFACE_PLUNGING_AMPL must be three times the number of MARKER_MOVING, (x,y,z) per marker.", CURRENT_FUNCTION); - } + } if (nMarkerPlunging_Omega == 0){ nMarkerPlunging_Omega = 3*nMarker_Moving; MarkerPlunging_Omega = new su2double[nMarkerPlunging_Omega]; for (iMarker = 0; iMarker < nMarker_Moving; iMarker++){ for (iDim = 0; iDim < 3; iDim++){ MarkerPlunging_Omega[3*iMarker+iDim] = 0.0; - } - } + } + } } if (nMarkerPlunging_Omega/3 != nMarker_Moving){ SU2_MPI::Error("Number of SURFACE_PLUNGING_OMEGA must be three times the number of MARKER_MOVING, (x,y,z) per marker.", CURRENT_FUNCTION); - } + } if (nMarkerPitching_Ampl == 0){ nMarkerPitching_Ampl = 3*nMarker_Moving; MarkerPitching_Ampl = new su2double[nMarkerPitching_Ampl]; for (iMarker = 0; iMarker < nMarker_Moving; iMarker++){ for (iDim = 0; iDim < 3; iDim++){ MarkerPitching_Ampl[3*iMarker+iDim] = 0.0; - } - } + } + } } if (nMarkerPitching_Ampl/3 != nMarker_Moving){ SU2_MPI::Error("Number of SURFACE_PITCHING_AMPL must be three times the number of MARKER_MOVING, (x,y,z) per marker.", CURRENT_FUNCTION); - } + } if (nMarkerPitching_Omega == 0){ nMarkerPitching_Omega = 3*nMarker_Moving; MarkerPitching_Omega = new su2double[nMarkerPitching_Omega]; for (iMarker = 0; iMarker < nMarker_Moving; iMarker++){ for (iDim = 0; iDim < 3; iDim++){ MarkerPitching_Omega[3*iMarker+iDim] = 0.0; - } - } + } + } } if (nMarkerPitching_Omega/3 != nMarker_Moving){ SU2_MPI::Error("Number of SURFACE_PITCHING_OMEGA must be three times the number of MARKER_MOVING, (x,y,z) per marker.", CURRENT_FUNCTION); - } + } if (nMarkerPitching_Phase == 0){ nMarkerPitching_Phase = 3*nMarker_Moving; MarkerPitching_Phase = new su2double[nMarkerPitching_Phase]; for (iMarker = 0; iMarker < nMarker_Moving; iMarker++){ for (iDim = 0; iDim < 3; iDim++){ MarkerPitching_Phase[3*iMarker+iDim] = 0.0; - } - } + } + } } if (nMarkerPitching_Phase/3 != nMarker_Moving){ SU2_MPI::Error("Number of SURFACE_PITCHING_PHASE must be three times the number of MARKER_MOVING, (x,y,z) per marker.", CURRENT_FUNCTION); - } + } if (nMoveMotion_Origin == 0){ nMoveMotion_Origin = nMarker_Moving; @@ -3824,11 +3827,10 @@ void CConfig::SetPostprocessing(unsigned short val_software, unsigned short val_ /*--- Set number of TurboPerformance markers ---*/ if(GetGrid_Movement() && RampRotatingFrame && !DiscreteAdjoint){ - FinalRotation_Rate_Z = Rotation_Rate[2]; - if(abs(FinalRotation_Rate_Z) > 0.0){ - Rotation_Rate[2] = RampRotatingFrame_Coeff[0]; - } - + FinalRotation_Rate_Z = Rotation_Rate[2]; + if(abs(FinalRotation_Rate_Z) > 0.0){ + Rotation_Rate[2] = RampRotatingFrame_Coeff[0]; + } } if(RampOutletPressure && !DiscreteAdjoint){ @@ -4289,39 +4291,39 @@ void CConfig::SetPostprocessing(unsigned short val_software, unsigned short val_ } if (nElasticityMod == 0) { - nElasticityMod = 1; - ElasticityMod = new su2double[1]; ElasticityMod[0] = 2E11; + nElasticityMod = 1; + ElasticityMod = new su2double[1]; ElasticityMod[0] = 2E11; } if (nPoissonRatio == 0) { - nPoissonRatio = 1; - PoissonRatio = new su2double[1]; PoissonRatio[0] = 0.30; + nPoissonRatio = 1; + PoissonRatio = new su2double[1]; PoissonRatio[0] = 0.30; } if (nMaterialDensity == 0) { - nMaterialDensity = 1; - MaterialDensity = new su2double[1]; MaterialDensity[0] = 7854; + nMaterialDensity = 1; + MaterialDensity = new su2double[1]; MaterialDensity[0] = 7854; } if (nElectric_Constant == 0) { - nElectric_Constant = 1; - Electric_Constant = new su2double[1]; Electric_Constant[0] = 0.0; + nElectric_Constant = 1; + Electric_Constant = new su2double[1]; Electric_Constant[0] = 0.0; } if (nElectric_Field == 0) { - nElectric_Field = 1; - Electric_Field_Mod = new su2double[1]; Electric_Field_Mod[0] = 0.0; + nElectric_Field = 1; + Electric_Field_Mod = new su2double[1]; Electric_Field_Mod[0] = 0.0; } if (nDim_RefNode == 0) { - nDim_RefNode = 3; - RefNode_Displacement = new su2double[3]; - RefNode_Displacement[0] = 0.0; RefNode_Displacement[1] = 0.0; RefNode_Displacement[2] = 0.0; + nDim_RefNode = 3; + RefNode_Displacement = new su2double[3]; + RefNode_Displacement[0] = 0.0; RefNode_Displacement[1] = 0.0; RefNode_Displacement[2] = 0.0; } if (nDim_Electric_Field == 0) { - nDim_Electric_Field = 2; - Electric_Field_Dir = new su2double[2]; Electric_Field_Dir[0] = 0.0; Electric_Field_Dir[1] = 1.0; + nDim_Electric_Field = 2; + Electric_Field_Dir = new su2double[2]; Electric_Field_Dir[0] = 0.0; Electric_Field_Dir[1] = 1.0; } if ((Kind_SU2 == SU2_CFD) && (Kind_Solver == NO_SOLVER)) { @@ -4920,6 +4922,9 @@ void CConfig::SetPostprocessing(unsigned short val_software, unsigned short val_ CURRENT_FUNCTION); } + /*--- 0 in the config file means "disable" which can be done using a very large group. ---*/ + if (edgeColorGroupSize==0) edgeColorGroupSize = 1<<30; + } void CConfig::SetMarkers(unsigned short val_software) { diff --git a/Common/src/geometry/CGeometry.cpp b/Common/src/geometry/CGeometry.cpp index 05af0f1c15c0..a7a502b30b63 100644 --- a/Common/src/geometry/CGeometry.cpp +++ b/Common/src/geometry/CGeometry.cpp @@ -4036,13 +4036,26 @@ const CEdgeToNonZeroMapUL& CGeometry::GetEdgeToSparsePatternMap(void) return edgeToCSRMap; } -const CCompressedSparsePatternUL& CGeometry::GetEdgeColoring(void) +const su2vector& CGeometry::GetTransposeSparsePatternMap(ConnectivityType type) { - if (edgeColoring.empty() && nEdge) { + /*--- Yes the const cast is weird but it is still better than repeating code. ---*/ + auto& pattern = const_cast(GetSparsePattern(type)); + pattern.buildTransposePtr(); + return pattern.transposePtr(); +} + +const CCompressedSparsePatternUL& CGeometry::GetEdgeColoring(su2double* efficiency) +{ + /*--- Check for dry run mode with dummy geometry. ---*/ + if (nEdge==0) return edgeColoring; + + /*--- Build if required. ---*/ + if (edgeColoring.empty()) { - /*--- When not using threading, and for coarse grids, use the natural coloring. ---*/ - if ((omp_get_max_threads() == 1) || (MGLevel != MESH_0)) { - edgeColoring = createNaturalColoring(nEdge); + /*--- When not using threading use the natural coloring to reduce overhead. ---*/ + if (omp_get_max_threads() == 1) { + SetNaturalEdgeColoring(); + if (efficiency != nullptr) *efficiency = 1.0; // by definition return edgeColoring; } @@ -4051,7 +4064,7 @@ const CCompressedSparsePatternUL& CGeometry::GetEdgeColoring(void) su2vector outerPtr(nEdge+1); su2vector innerIdx(nEdge*2); - for(unsigned long iEdge = 0; iEdge < nEdge; ++iEdge) { + for (unsigned long iEdge = 0; iEdge < nEdge; ++iEdge) { outerPtr(iEdge) = 2*iEdge; innerIdx(iEdge*2+0) = edge[iEdge]->GetNode(0); innerIdx(iEdge*2+1) = edge[iEdge]->GetNode(1); @@ -4060,23 +4073,42 @@ const CCompressedSparsePatternUL& CGeometry::GetEdgeColoring(void) CCompressedSparsePatternUL pattern(move(outerPtr), move(innerIdx)); - /*--- Color the edges, only balance sizes on coarse levels. ---*/ - bool balanceColors = (MGLevel != MESH_0); + /*--- Color the edges. ---*/ + constexpr bool balanceColors = true; edgeColoring = colorSparsePattern(pattern, edgeColorGroupSize, balanceColors); - if(edgeColoring.empty()) - SU2_MPI::Error("Edge coloring failed.", CURRENT_FUNCTION); + /*--- If the coloring fails use the natural coloring. This is a + * "soft" failure as this "bad" coloring should be detected + * downstream and a fallback strategy put in place. ---*/ + if (edgeColoring.empty()) SetNaturalEdgeColoring(); + } + + if (efficiency != nullptr) { + *efficiency = coloringEfficiency(edgeColoring, omp_get_max_threads(), edgeColorGroupSize); } return edgeColoring; } -const CCompressedSparsePatternUL& CGeometry::GetElementColoring(void) +void CGeometry::SetNaturalEdgeColoring() +{ + if (nEdge == 0) return; + edgeColoring = createNaturalColoring(nEdge); + /*--- In parallel, set the group size to nEdge to protect client code. ---*/ + if (omp_get_max_threads() > 1) edgeColorGroupSize = nEdge; +} + +const CCompressedSparsePatternUL& CGeometry::GetElementColoring(su2double* efficiency) { - if (elemColoring.empty() && nElem) { + /*--- Check for dry run mode with dummy geometry. ---*/ + if (nElem==0) return elemColoring; + + /*--- Build if required. ---*/ + if (elemColoring.empty()) { /*--- When not using threading use the natural coloring. ---*/ if (omp_get_max_threads() == 1) { - elemColoring = createNaturalColoring(nElem); + SetNaturalElementColoring(); + if (efficiency != nullptr) *efficiency = 1.0; // by definition return elemColoring; } @@ -4085,10 +4117,10 @@ const CCompressedSparsePatternUL& CGeometry::GetElementColoring(void) vector outerPtr(nElem+1); vector innerIdx; innerIdx.reserve(nElem); - for(unsigned long iElem = 0; iElem < nElem; ++iElem) { + for (unsigned long iElem = 0; iElem < nElem; ++iElem) { outerPtr[iElem] = innerIdx.size(); - for(unsigned short iNode = 0; iNode < elem[iElem]->GetnNodes(); ++iNode) { + for (unsigned short iNode = 0; iNode < elem[iElem]->GetnNodes(); ++iNode) { innerIdx.push_back(elem[iElem]->GetNode(iNode)); } } @@ -4097,10 +4129,23 @@ const CCompressedSparsePatternUL& CGeometry::GetElementColoring(void) CCompressedSparsePatternUL pattern(outerPtr, innerIdx); /*--- Color the elements. ---*/ - elemColoring = colorSparsePattern(pattern, elemColorGroupSize); + constexpr bool balanceColors = true; + elemColoring = colorSparsePattern(pattern, elemColorGroupSize, balanceColors); + + /*--- Same as for the edge coloring. ---*/ + if (elemColoring.empty()) SetNaturalElementColoring(); + } - if(elemColoring.empty()) - SU2_MPI::Error("Element coloring failed.", CURRENT_FUNCTION); + if (efficiency != nullptr) { + *efficiency = coloringEfficiency(elemColoring, omp_get_max_threads(), elemColorGroupSize); } return elemColoring; } + +void CGeometry::SetNaturalElementColoring() +{ + if (nElem == 0) return; + elemColoring = createNaturalColoring(nElem); + /*--- In parallel, set the group size to nElem to protect client code. ---*/ + if (omp_get_max_threads() > 1) elemColorGroupSize = nElem; +} diff --git a/Common/src/geometry/CMultiGridGeometry.cpp b/Common/src/geometry/CMultiGridGeometry.cpp index 05746f165dc4..830d49b4fdcd 100644 --- a/Common/src/geometry/CMultiGridGeometry.cpp +++ b/Common/src/geometry/CMultiGridGeometry.cpp @@ -624,13 +624,8 @@ CMultiGridGeometry::CMultiGridGeometry(CGeometry **geometry, CConfig *config_con Local_nPointCoarse = nPoint; Local_nPointFine = fine_grid->GetnPoint(); -#ifdef HAVE_MPI SU2_MPI::Allreduce(&Local_nPointCoarse, &Global_nPointCoarse, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); SU2_MPI::Allreduce(&Local_nPointFine, &Global_nPointFine, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); -#else - Global_nPointCoarse = Local_nPointCoarse; - Global_nPointFine = Local_nPointFine; -#endif su2double Coeff = 1.0, CFL = 0.0, factor = 1.5; @@ -670,6 +665,8 @@ CMultiGridGeometry::CMultiGridGeometry(CGeometry **geometry, CConfig *config_con } } + edgeColorGroupSize = config->GetEdgeColoringGroupSize(); + delete [] copy_marker; } diff --git a/Common/src/geometry/CPhysicalGeometry.cpp b/Common/src/geometry/CPhysicalGeometry.cpp index cf4375463fb8..8f8ea74fe17a 100644 --- a/Common/src/geometry/CPhysicalGeometry.cpp +++ b/Common/src/geometry/CPhysicalGeometry.cpp @@ -468,15 +468,11 @@ CPhysicalGeometry::CPhysicalGeometry(CGeometry *geometry, nLocal_Pris + nLocal_Pyra); nLocal_Bound_Elem = nLocal_Line + nLocal_BoundTria + nLocal_BoundQuad; -#ifndef HAVE_MPI - nGlobal_Elem = nLocal_Elem; - nGlobal_Bound_Elem = nLocal_Bound_Elem; -#else + SU2_MPI::Allreduce(&nLocal_Elem, &nGlobal_Elem, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); SU2_MPI::Allreduce(&nLocal_Bound_Elem, &nGlobal_Bound_Elem, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); -#endif /*--- With the distribution of all points, elements, and markers based on the ParMETIS coloring complete, as a final step, load this data into diff --git a/Common/src/geometry/dual_grid/CVertex.cpp b/Common/src/geometry/dual_grid/CVertex.cpp index 4cf14bf19f39..a65c22e63460 100644 --- a/Common/src/geometry/dual_grid/CVertex.cpp +++ b/Common/src/geometry/dual_grid/CVertex.cpp @@ -40,52 +40,35 @@ CVertex::CVertex(unsigned long val_point, unsigned short val_nDim) : CDualGrid(v ActDisk_Perimeter = false; - /*--- Pointers initialization ---*/ - - Nodes = NULL; - Normal = NULL; - - /*--- Allocate node, and face normal ---*/ - - Nodes = new unsigned long[1]; - Normal = new su2double [nDim]; - /*--- Initializate the structure ---*/ Nodes[0] = val_point; - for (iDim = 0; iDim < nDim; iDim ++) - Normal[iDim] = 0.0; + + for (iDim = 0; iDim < 3; iDim ++) Normal[iDim] = 0.0; /*--- Set to zero the variation of the coordinates ---*/ - VarCoord[0] = 0.0; - VarCoord[1] = 0.0; - VarCoord[2] = 0.0; + for (iDim = 0; iDim < 3; iDim ++) VarCoord[iDim] = 0.0; - /*--- Set to NULL variation of the rotation ---*/ + /*--- Set to nullptr variation of the rotation ---*/ - VarRot = NULL; + VarRot = nullptr; - /*--- Set to NULL donor arrays for interpolation ---*/ + /*--- Set to nullptr donor arrays for interpolation ---*/ - Donor_Points = NULL; - Donor_Proc = NULL; - Donor_Coeff = NULL; + Donor_Points = nullptr; + Donor_Proc = nullptr; + Donor_Coeff = nullptr; nDonor_Points = 1; } CVertex::~CVertex() { - if (Normal != NULL) delete[] Normal; - if (Nodes != NULL) delete[] Nodes; - - /*--- donor arrays for interpolation ---*/ - - if (VarRot != NULL) delete[] VarRot; - if (Donor_Coeff != NULL) delete[] Donor_Coeff; - if (Donor_Proc != NULL) delete[] Donor_Proc; - if (Donor_Points != NULL) delete[] Donor_Points; + delete[] VarRot; + delete[] Donor_Coeff; + delete[] Donor_Proc; + delete[] Donor_Points; } @@ -131,13 +114,16 @@ void CVertex::SetNodes_Coord(su2double *val_coord_Edge_CG, su2double *val_coord_ } -void CVertex::Allocate_DonorInfo(void){ +void CVertex::Allocate_DonorInfo(unsigned short nDonor) { + + nDonor_Points = nDonor; + + delete [] Donor_Points; + delete [] Donor_Proc; + delete [] Donor_Coeff; - if( Donor_Points != NULL ) delete [] Donor_Points; - if( Donor_Proc != NULL ) delete [] Donor_Proc; - if( Donor_Coeff != NULL ) delete [] Donor_Coeff; + Donor_Points = new unsigned long [nDonor_Points]; + Donor_Proc = new unsigned long [nDonor_Points]; + Donor_Coeff = new su2double [nDonor_Points]; - Donor_Points = new unsigned long[nDonor_Points]; - Donor_Proc = new unsigned long[nDonor_Points]; - Donor_Coeff = new su2double[nDonor_Points]; } diff --git a/Common/src/interface_interpolation/CInterpolator.cpp b/Common/src/interface_interpolation/CInterpolator.cpp new file mode 100644 index 000000000000..2c2c0089bb9e --- /dev/null +++ b/Common/src/interface_interpolation/CInterpolator.cpp @@ -0,0 +1,416 @@ +/*! + * \file CInterpolator.cpp + * \brief Definition of the base class for interface interpolation. + * \author H. Kline + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, 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/interface_interpolation/CInterpolator.hpp" +#include "../../include/CConfig.hpp" +#include "../../include/geometry/CGeometry.hpp" + + +CInterpolator::CInterpolator(CGeometry ****geometry_container, const CConfig* const* config, + unsigned int iZone, unsigned int jZone) : + rank(SU2_MPI::GetRank()), + size(SU2_MPI::GetSize()), + donorZone(iZone), + targetZone(jZone), + Geometry(geometry_container), + donor_geometry(geometry_container[iZone][INST_0][MESH_0]), + target_geometry(geometry_container[jZone][INST_0][MESH_0]) { +} + +int CInterpolator::Find_InterfaceMarker(const CConfig *config, unsigned short val_marker_interface) { + + for (unsigned short iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { + /*--- If the tag GetMarker_All_ZoneInterface(iMarker) equals the interface we are looking for. ---*/ + if (config->GetMarker_All_ZoneInterface(iMarker) == val_marker_interface) return iMarker; + } + return -1; +} + +bool CInterpolator::CheckInterfaceBoundary(int markDonor, int markTarget) { + + /*--- Determine whether the boundary is not on the rank because of + * the partition or because it is not part of the zone. ---*/ + int donorCheck = -1, targetCheck = -1; + SU2_MPI::Allreduce(&markDonor, &donorCheck, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); + SU2_MPI::Allreduce(&markTarget, &targetCheck, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); + return (donorCheck != -1) && (targetCheck != -1); +} + +void CInterpolator::Determine_ArraySize(bool faces, int markDonor, int markTarget, + unsigned long nVertexDonor, unsigned short nDim) { + + unsigned long nLocalVertex_Donor = 0, nLocalFaceNodes_Donor=0, nLocalFace_Donor=0; + unsigned long iVertex, iPointDonor = 0; + /* Only needed if face data is also collected */ + unsigned long inode; + unsigned long donor_elem, jElem, jPoint; + unsigned short iDonor; + unsigned int nFaces=0, iFace, nNodes=0; + bool face_on_marker = true; + + for (iVertex = 0; iVertex < nVertexDonor; iVertex++) { + iPointDonor = donor_geometry->vertex[markDonor][iVertex]->GetNode(); + + if (!donor_geometry->node[iPointDonor]->GetDomain()) continue; + + nLocalVertex_Donor++; + + if (!faces) continue; + + /*--- On Donor geometry also communicate face info ---*/ + if (nDim==3) { + for (jElem=0; jElemnode[iPointDonor]->GetnElem(); jElem++) { + donor_elem = donor_geometry->node[iPointDonor]->GetElem(jElem); + nFaces = donor_geometry->elem[donor_elem]->GetnFaces(); + for (iFace=0; iFaceelem[donor_elem]->GetnNodesFace(iFace); + for (iDonor=0; iDonorelem[donor_elem]->GetFaces(iFace, iDonor); + jPoint = donor_geometry->elem[donor_elem]->GetNode(inode); + face_on_marker = (face_on_marker && (donor_geometry->node[jPoint]->GetVertex(markDonor) !=-1)); + } + if (face_on_marker ) { + nLocalFace_Donor++; + nLocalFaceNodes_Donor+=nNodes; + } + } + } + } + else { + /*--- in 2D we use the edges ---*/ + nNodes=2; + nFaces = donor_geometry->node[iPointDonor]->GetnPoint(); + for (iFace=0; iFacenode[iPointDonor]->GetEdge(iFace); + jPoint = donor_geometry->edge[inode]->GetNode(iDonor); + face_on_marker = (face_on_marker && (donor_geometry->node[jPoint]->GetVertex(markDonor) !=-1)); + } + if (face_on_marker ) { + nLocalFace_Donor++; + nLocalFaceNodes_Donor+=nNodes; + } + } + } + } + + Buffer_Send_nVertex_Donor[0] = nLocalVertex_Donor; + if (faces) { + Buffer_Send_nFace_Donor[0] = nLocalFace_Donor; + Buffer_Send_nFaceNodes_Donor[0] = nLocalFaceNodes_Donor; + } + + /*--- Send Interface vertex information --*/ + SU2_MPI::Allreduce(&nLocalVertex_Donor, &MaxLocalVertex_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_nVertex_Donor, 1, MPI_UNSIGNED_LONG, + Buffer_Receive_nVertex_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + if (faces) { + SU2_MPI::Allreduce(&nLocalFace_Donor, &nGlobalFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); + SU2_MPI::Allreduce(&nLocalFace_Donor, &MaxFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); + SU2_MPI::Allreduce(&nLocalFaceNodes_Donor, &nGlobalFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); + SU2_MPI::Allreduce(&nLocalFaceNodes_Donor, &MaxFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_nFace_Donor, 1, MPI_UNSIGNED_LONG, + Buffer_Receive_nFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, + Buffer_Receive_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + MaxFace_Donor++; + } +} + +void CInterpolator::Collect_VertexInfo(bool faces, int markDonor, int markTarget, + unsigned long nVertexDonor, unsigned short nDim) { + + unsigned long iVertex, iPointDonor = 0, iVertexDonor, nBuffer_Coord, nBuffer_Point, nLocalVertex_Donor; + unsigned short iDim; + + for (iVertex = 0; iVertex < MaxLocalVertex_Donor; iVertex++) Buffer_Send_GlobalPoint[iVertex] = -1; + + for (iVertex = 0; iVertex < MaxLocalVertex_Donor*nDim; iVertex++) Buffer_Send_Coord[iVertex] = 0.0; + + if(faces) + for (iVertex = 0; iVertex < MaxLocalVertex_Donor*nDim; iVertex++) Buffer_Send_Normal[iVertex] = 0.0; + + /*--- Copy coordinates and point to the auxiliar vector --*/ + nLocalVertex_Donor = 0; + + for (iVertexDonor = 0; iVertexDonor < nVertexDonor; iVertexDonor++) { + iPointDonor = donor_geometry->vertex[markDonor][iVertexDonor]->GetNode(); + if (donor_geometry->node[iPointDonor]->GetDomain()) { + Buffer_Send_GlobalPoint[nLocalVertex_Donor] = donor_geometry->node[iPointDonor]->GetGlobalIndex(); + for (iDim = 0; iDim < nDim; iDim++) + Buffer_Send_Coord[nLocalVertex_Donor*nDim+iDim] = donor_geometry->node[iPointDonor]->GetCoord(iDim); + + if (faces) { + const su2double* Normal = donor_geometry->vertex[markDonor][iVertexDonor]->GetNormal(); + for (iDim = 0; iDim < nDim; iDim++) + Buffer_Send_Normal[nLocalVertex_Donor*nDim+iDim] = Normal[iDim]; + } + nLocalVertex_Donor++; + } + } + nBuffer_Coord = MaxLocalVertex_Donor*nDim; + nBuffer_Point = MaxLocalVertex_Donor; + + SU2_MPI::Allgather(Buffer_Send_Coord, nBuffer_Coord, MPI_DOUBLE, + Buffer_Receive_Coord, nBuffer_Coord, MPI_DOUBLE, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_GlobalPoint, nBuffer_Point, MPI_LONG, + Buffer_Receive_GlobalPoint, nBuffer_Point, MPI_LONG, MPI_COMM_WORLD); + if (faces) { + SU2_MPI::Allgather(Buffer_Send_Normal, nBuffer_Coord, MPI_DOUBLE, + Buffer_Receive_Normal, nBuffer_Coord, MPI_DOUBLE, MPI_COMM_WORLD); + } +} + +void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ + + CGeometry *geom = Geometry[val_zone][INST_0][MESH_0]; + + unsigned long iVertex, jVertex, kVertex; + + unsigned long count, iTmp, *uptr, dPoint, EdgeIndex, jEdge, nEdges, nNodes, nVertex, iDim, nDim, iPoint; + + unsigned long nGlobalLinkedNodes, nLocalVertex, nLocalLinkedNodes; + + nDim = geom->GetnDim(); + + if( val_marker != -1 ) + nVertex = geom->GetnVertex( val_marker ); + else + nVertex = 0; + + + su2double *Buffer_Send_Coord = new su2double [ nVertex * nDim ]; + unsigned long *Buffer_Send_GlobalPoint = new unsigned long [ nVertex ]; + + unsigned long *Buffer_Send_nLinkedNodes = new unsigned long [ nVertex ]; + unsigned long *Buffer_Send_StartLinkedNodes = new unsigned long [ nVertex ]; + unsigned long **Aux_Send_Map = new unsigned long*[ nVertex ]; + +#ifdef HAVE_MPI + int nProcessor = size, iRank; + unsigned long iTmp2, tmp_index, tmp_index_2; +#endif + + /*--- Copy coordinates and point to the auxiliar vector ---*/ + + nGlobalVertex = 0; + nLocalVertex = 0; + nLocalLinkedNodes = 0; + + for (iVertex = 0; iVertex < nVertex; iVertex++) { + + Buffer_Send_nLinkedNodes[iVertex] = 0; + Aux_Send_Map[iVertex] = NULL; + + iPoint = geom->vertex[val_marker][iVertex]->GetNode(); + + if (geom->node[iPoint]->GetDomain()) { + Buffer_Send_GlobalPoint[nLocalVertex] = geom->node[iPoint]->GetGlobalIndex(); + + for (iDim = 0; iDim < nDim; iDim++) + Buffer_Send_Coord[nLocalVertex*nDim+iDim] = geom->node[iPoint]->GetCoord(iDim); + + nNodes = 0; + nEdges = geom->node[iPoint]->GetnPoint(); + + for (jEdge = 0; jEdge < nEdges; jEdge++){ + EdgeIndex = geom->node[iPoint]->GetEdge(jEdge); + + if( iPoint == geom->edge[EdgeIndex]->GetNode(0) ) + dPoint = geom->edge[EdgeIndex]->GetNode(1); + else + dPoint = geom->edge[EdgeIndex]->GetNode(0); + + if ( geom->node[dPoint]->GetVertex(val_marker) != -1 ) + nNodes++; + } + + Buffer_Send_StartLinkedNodes[nLocalVertex] = nLocalLinkedNodes; + Buffer_Send_nLinkedNodes[nLocalVertex] = nNodes; + + nLocalLinkedNodes += nNodes; + + Aux_Send_Map[nLocalVertex] = new unsigned long[ nNodes ]; + nNodes = 0; + + for (jEdge = 0; jEdge < nEdges; jEdge++){ + EdgeIndex = geom->node[iPoint]->GetEdge(jEdge); + + if( iPoint == geom->edge[EdgeIndex]->GetNode(0) ) + dPoint = geom->edge[EdgeIndex]->GetNode(1); + else + dPoint = geom->edge[EdgeIndex]->GetNode(0); + + if ( geom->node[dPoint]->GetVertex(val_marker) != -1 ){ + Aux_Send_Map[nLocalVertex][nNodes] = geom->node[dPoint]->GetGlobalIndex(); + nNodes++; + } + } + nLocalVertex++; + } + } + + unsigned long *Buffer_Send_LinkedNodes = new unsigned long [ nLocalLinkedNodes ]; + + nLocalLinkedNodes = 0; + + for (iVertex = 0; iVertex < nLocalVertex; iVertex++){ + for (jEdge = 0; jEdge < Buffer_Send_nLinkedNodes[iVertex]; jEdge++){ + Buffer_Send_LinkedNodes[nLocalLinkedNodes] = Aux_Send_Map[iVertex][jEdge]; + nLocalLinkedNodes++; + } + } + + for (iVertex = 0; iVertex < nVertex; iVertex++){ + if( Aux_Send_Map[iVertex] != NULL ) + delete [] Aux_Send_Map[iVertex]; + } + delete [] Aux_Send_Map; Aux_Send_Map = NULL; + + /*--- Reconstruct boundary by gathering data from all ranks ---*/ + + SU2_MPI::Allreduce( &nLocalVertex, &nGlobalVertex, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); + SU2_MPI::Allreduce(&nLocalLinkedNodes, &nGlobalLinkedNodes, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); + + Buffer_Receive_Coord = new su2double [ nGlobalVertex * nDim ]; + Buffer_Receive_GlobalPoint = new long[ nGlobalVertex ]; + Buffer_Receive_Proc = new unsigned long[ nGlobalVertex ]; + + Buffer_Receive_nLinkedNodes = new unsigned long[ nGlobalVertex ]; + Buffer_Receive_LinkedNodes = new unsigned long[ nGlobalLinkedNodes ]; + Buffer_Receive_StartLinkedNodes = new unsigned long[ nGlobalVertex ]; + +#ifdef HAVE_MPI + if (rank == MASTER_NODE){ + + for (iVertex = 0; iVertex < nDim*nLocalVertex; iVertex++) + Buffer_Receive_Coord[iVertex] = Buffer_Send_Coord[iVertex]; + + for (iVertex = 0; iVertex < nLocalVertex; iVertex++){ + Buffer_Receive_GlobalPoint[iVertex] = Buffer_Send_GlobalPoint[iVertex]; + Buffer_Receive_Proc[iVertex] = MASTER_NODE; + Buffer_Receive_nLinkedNodes[iVertex] = Buffer_Send_nLinkedNodes[iVertex]; + Buffer_Receive_StartLinkedNodes[iVertex] = Buffer_Send_StartLinkedNodes[iVertex]; + } + + for (iVertex = 0; iVertex < nLocalLinkedNodes; iVertex++) + Buffer_Receive_LinkedNodes[iVertex] = Buffer_Send_LinkedNodes[iVertex]; + + tmp_index = nLocalVertex; + tmp_index_2 = nLocalLinkedNodes; + + for(iRank = 1; iRank < nProcessor; iRank++){ + + SU2_MPI::Recv( &iTmp2, 1, MPI_UNSIGNED_LONG, iRank, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + SU2_MPI::Recv(&Buffer_Receive_LinkedNodes[tmp_index_2], iTmp2, MPI_UNSIGNED_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + + SU2_MPI::Recv( &iTmp, 1, MPI_UNSIGNED_LONG, iRank, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + SU2_MPI::Recv(&Buffer_Receive_Coord[tmp_index*nDim], nDim*iTmp, MPI_DOUBLE, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + + SU2_MPI::Recv( &Buffer_Receive_GlobalPoint[tmp_index], iTmp, MPI_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + SU2_MPI::Recv( &Buffer_Receive_nLinkedNodes[tmp_index], iTmp, MPI_UNSIGNED_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + SU2_MPI::Recv(&Buffer_Receive_StartLinkedNodes[tmp_index], iTmp, MPI_UNSIGNED_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + + for (iVertex = 0; iVertex < iTmp; iVertex++){ + Buffer_Receive_Proc[ tmp_index + iVertex ] = iRank; + Buffer_Receive_StartLinkedNodes[ tmp_index + iVertex ] += tmp_index_2; + } + + tmp_index += iTmp; + tmp_index_2 += iTmp2; + } + } + else{ + SU2_MPI::Send( &nLocalLinkedNodes, 1, MPI_UNSIGNED_LONG, 0, 0, MPI_COMM_WORLD); + SU2_MPI::Send(Buffer_Send_LinkedNodes, nLocalLinkedNodes, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD); + + SU2_MPI::Send( &nLocalVertex, 1, MPI_UNSIGNED_LONG, 0, 0, MPI_COMM_WORLD); + SU2_MPI::Send(Buffer_Send_Coord, nDim * nLocalVertex, MPI_DOUBLE, 0, 1, MPI_COMM_WORLD); + + SU2_MPI::Send( Buffer_Send_GlobalPoint, nLocalVertex, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD); + SU2_MPI::Send( Buffer_Send_nLinkedNodes, nLocalVertex, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD); + SU2_MPI::Send(Buffer_Send_StartLinkedNodes, nLocalVertex, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD); + } +#else + for (iVertex = 0; iVertex < nDim * nGlobalVertex; iVertex++) + Buffer_Receive_Coord[iVertex] = Buffer_Send_Coord[iVertex]; + + for (iVertex = 0; iVertex < nGlobalVertex; iVertex++){ + Buffer_Receive_GlobalPoint[iVertex] = Buffer_Send_GlobalPoint[iVertex]; + Buffer_Receive_Proc[iVertex] = MASTER_NODE; + Buffer_Receive_nLinkedNodes[iVertex] = Buffer_Send_nLinkedNodes[iVertex]; + Buffer_Receive_StartLinkedNodes[iVertex] = Buffer_Send_StartLinkedNodes[iVertex]; + } + + for (iVertex = 0; iVertex < nGlobalLinkedNodes; iVertex++) + Buffer_Receive_LinkedNodes[iVertex] = Buffer_Send_LinkedNodes[iVertex]; +#endif + + if (rank == MASTER_NODE){ + for (iVertex = 0; iVertex < nGlobalVertex; iVertex++){ + count = 0; + uptr = &Buffer_Receive_LinkedNodes[ Buffer_Receive_StartLinkedNodes[iVertex] ]; + + for (jVertex = 0; jVertex < Buffer_Receive_nLinkedNodes[iVertex]; jVertex++){ + iTmp = uptr[ jVertex ]; + for (kVertex = 0; kVertex < nGlobalVertex; kVertex++){ + if( Buffer_Receive_GlobalPoint[kVertex] == long(iTmp) ){ + uptr[ jVertex ] = kVertex; + count++; + break; + } + } + + if( count != (jVertex+1) ){ + for (kVertex = jVertex; kVertex < Buffer_Receive_nLinkedNodes[iVertex]-1; kVertex++){ + uptr[ kVertex ] = uptr[ kVertex + 1]; + } + Buffer_Receive_nLinkedNodes[iVertex]--; + jVertex--; + } + } + } + } + + SU2_MPI::Bcast(Buffer_Receive_GlobalPoint, nGlobalVertex, MPI_LONG, 0, MPI_COMM_WORLD); + SU2_MPI::Bcast(Buffer_Receive_Coord, nGlobalVertex*nDim, MPI_DOUBLE, 0, MPI_COMM_WORLD); + SU2_MPI::Bcast(Buffer_Receive_Proc, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + + SU2_MPI::Bcast(Buffer_Receive_nLinkedNodes, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + SU2_MPI::Bcast(Buffer_Receive_StartLinkedNodes, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + SU2_MPI::Bcast(Buffer_Receive_LinkedNodes, nGlobalLinkedNodes, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + + delete [] Buffer_Send_Coord; Buffer_Send_Coord = NULL; + delete [] Buffer_Send_GlobalPoint; Buffer_Send_GlobalPoint = NULL; + delete [] Buffer_Send_LinkedNodes; Buffer_Send_LinkedNodes = NULL; + delete [] Buffer_Send_nLinkedNodes; Buffer_Send_nLinkedNodes = NULL; + delete [] Buffer_Send_StartLinkedNodes; Buffer_Send_StartLinkedNodes = NULL; + +} diff --git a/Common/src/interface_interpolation/CIsoparametric.cpp b/Common/src/interface_interpolation/CIsoparametric.cpp new file mode 100644 index 000000000000..d1032f26495b --- /dev/null +++ b/Common/src/interface_interpolation/CIsoparametric.cpp @@ -0,0 +1,554 @@ +/*! + * \file CIsoparametric.cpp + * \brief Implementation isoparametric interpolation (using FE shape functions). + * \author H. Kline + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, 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/interface_interpolation/CIsoparametric.hpp" +#include "../../include/CConfig.hpp" +#include "../../include/geometry/CGeometry.hpp" + + +CIsoparametric::CIsoparametric(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { + Set_TransferCoeff(config); +} + +void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { + + unsigned long iVertex, jVertex; + unsigned long dPoint, inode, jElem, nElem; + unsigned short iDim, iDonor=0, iFace; + + unsigned short nDim = donor_geometry->GetnDim(); + + unsigned short nMarkerInt; + unsigned short iMarkerInt; + + int markDonor=0, markTarget=0; + + long donor_elem=0, temp_donor=0; + unsigned int nNodes=0; + /*--- Restricted to 2-zone for now ---*/ + unsigned int nFaces=1; //For 2D cases, we want to look at edges, not faces, as the 'interface' + bool face_on_marker=true; + + unsigned long nVertexDonor = 0, nVertexTarget= 0; + unsigned long Point_Target = 0; + + unsigned long iVertexDonor, iPointDonor = 0; + int iProcessor; + + unsigned long nLocalFace_Donor = 0, nLocalFaceNodes_Donor=0; + + unsigned long faceindex; + + su2double dist = 0.0, mindist=1E6, *Coord, *Coord_i; + su2double myCoeff[10]; // Maximum # of donor points + su2double *Normal; + su2double *projected_point = new su2double[nDim]; + su2double tmp, tmp2; + su2double storeCoeff[10]; + unsigned long storeGlobal[10]; + int storeProc[10]; + + int nProcessor = size; + Coord = new su2double[nDim]; + Normal = new su2double[nDim]; + + Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; + Buffer_Receive_nFace_Donor = new unsigned long [nProcessor]; + Buffer_Receive_nFaceNodes_Donor = new unsigned long [nProcessor]; + + nMarkerInt = (config[donorZone]->GetMarker_n_ZoneInterface())/2; + + /*--- For the number of markers on the interface... ---*/ + for (iMarkerInt=1; iMarkerInt <= nMarkerInt; iMarkerInt++) { + /*--- Procedure: + * -Loop through vertices of the aero grid + * -Find nearest element and allocate enough space in the aero grid donor point info + * -set the transfer coefficient values + */ + + /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ + markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); + + /*--- On the target side: find the tag of the boundary sharing the interface ---*/ + markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); + + /*--- Checks if the zone contains the interface, if not continue to the next step ---*/ + if(!CheckInterfaceBoundary(markDonor, markTarget)) continue; + + if(markDonor != -1) + nVertexDonor = donor_geometry->GetnVertex( markDonor ); + else + nVertexDonor = 0; + + if(markTarget != -1) + nVertexTarget = target_geometry->GetnVertex( markTarget ); + else + nVertexTarget = 0; + + /* Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor */ + Determine_ArraySize(true, markDonor, markTarget, nVertexDonor, nDim); + + Buffer_Send_Coord = new su2double [MaxLocalVertex_Donor*nDim]; + Buffer_Send_Normal = new su2double [MaxLocalVertex_Donor*nDim]; + Buffer_Send_GlobalPoint = new long [MaxLocalVertex_Donor]; + + Buffer_Receive_Coord = new su2double [nProcessor*MaxLocalVertex_Donor*nDim]; + Buffer_Receive_Normal = new su2double [nProcessor*MaxLocalVertex_Donor*nDim]; + Buffer_Receive_GlobalPoint = new long [nProcessor*MaxLocalVertex_Donor]; + + /*-- Collect coordinates, global points, and normal vectors ---*/ + Collect_VertexInfo(true, markDonor,markTarget,nVertexDonor,nDim); + + Buffer_Send_FaceIndex = new unsigned long[MaxFace_Donor]; + Buffer_Send_FaceNodes = new unsigned long[MaxFaceNodes_Donor]; + Buffer_Send_FaceProc = new unsigned long[MaxFaceNodes_Donor]; + + Buffer_Receive_FaceIndex = new unsigned long[MaxFace_Donor*nProcessor]; + Buffer_Receive_FaceNodes = new unsigned long[MaxFaceNodes_Donor*nProcessor]; + Buffer_Receive_FaceProc = new unsigned long[MaxFaceNodes_Donor*nProcessor]; + + nLocalFace_Donor=0; + nLocalFaceNodes_Donor=0; + + /*--- Collect Face info ---*/ + + for (iVertex = 0; iVertex < MaxFace_Donor; iVertex++) { + Buffer_Send_FaceIndex[iVertex] = 0; + } + for (iVertex=0; iVertexvertex[markDonor][iVertexDonor]->GetNode(); + + if (donor_geometry->node[iPointDonor]->GetDomain()) { + + if (nDim==3) nElem = donor_geometry->node[iPointDonor]->GetnElem(); + else nElem =donor_geometry->node[iPointDonor]->GetnPoint(); + + for (jElem=0; jElem < nElem; jElem++) { + if (nDim==3) { + temp_donor = donor_geometry->node[iPointDonor]->GetElem(jElem); + nFaces = donor_geometry->elem[temp_donor]->GetnFaces(); + for (iFace=0; iFaceelem[temp_donor]->GetnNodesFace(iFace); + for (iDonor=0; iDonorelem[temp_donor]->GetFaces(iFace, iDonor); + dPoint = donor_geometry->elem[temp_donor]->GetNode(inode); + face_on_marker = (face_on_marker && (donor_geometry->node[dPoint]->GetVertex(markDonor) !=-1)); + } + + if (face_on_marker ) { + for (iDonor=0; iDonorelem[temp_donor]->GetFaces(iFace, iDonor); + dPoint = donor_geometry->elem[temp_donor]->GetNode(inode); + // Match node on the face to the correct global index + long jGlobalPoint = donor_geometry->node[dPoint]->GetGlobalIndex(); + for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { + for (jVertex = 0; jVertex < Buffer_Receive_nVertex_Donor[iProcessor]; jVertex++) { + if (jGlobalPoint == Buffer_Receive_GlobalPoint[MaxLocalVertex_Donor*iProcessor+jVertex]) { + Buffer_Send_FaceNodes[nLocalFaceNodes_Donor]=MaxLocalVertex_Donor*iProcessor+jVertex; + Buffer_Send_FaceProc[nLocalFaceNodes_Donor]=iProcessor; + } + } + } + nLocalFaceNodes_Donor++; // Increment total number of face-nodes / processor + } + /* Store the indices */ + Buffer_Send_FaceIndex[nLocalFace_Donor+1] = Buffer_Send_FaceIndex[nLocalFace_Donor]+nNodes; + nLocalFace_Donor++; // Increment number of faces / processor + } + } + } + else { + /*-- Determine whether this face/edge is on the marker --*/ + face_on_marker=true; + for (iDonor=0; iDonornode[iPointDonor]->GetEdge(jElem); + dPoint = donor_geometry->edge[inode]->GetNode(iDonor); + face_on_marker = (face_on_marker && (donor_geometry->node[dPoint]->GetVertex(markDonor) !=-1)); + } + if (face_on_marker ) { + for (iDonor=0; iDonornode[iPointDonor]->GetEdge(jElem); + dPoint = donor_geometry->edge[inode]->GetNode(iDonor); + // Match node on the face to the correct global index + long jGlobalPoint = donor_geometry->node[dPoint]->GetGlobalIndex(); + for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { + for (jVertex = 0; jVertex < Buffer_Receive_nVertex_Donor[iProcessor]; jVertex++) { + if (jGlobalPoint == Buffer_Receive_GlobalPoint[MaxLocalVertex_Donor*iProcessor+jVertex]) { + Buffer_Send_FaceNodes[nLocalFaceNodes_Donor]=MaxLocalVertex_Donor*iProcessor+jVertex; + Buffer_Send_FaceProc[nLocalFaceNodes_Donor]=iProcessor; + } + } + } + nLocalFaceNodes_Donor++; // Increment total number of face-nodes / processor + } + /* Store the indices */ + Buffer_Send_FaceIndex[nLocalFace_Donor+1] = Buffer_Send_FaceIndex[nLocalFace_Donor]+nNodes; + nLocalFace_Donor++; // Increment number of faces / processor + } + } + } + } + } + + //Buffer_Send_FaceIndex[nLocalFace_Donor+1] = MaxFaceNodes_Donor*rank+nLocalFaceNodes_Donor; + + SU2_MPI::Allgather(Buffer_Send_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, + Buffer_Receive_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_FaceProc, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, + Buffer_Receive_FaceProc, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, + Buffer_Receive_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + + /*--- Loop over the vertices on the target Marker ---*/ + for (iVertex = 0; iVertexvertex[markTarget][iVertex]->GetNode(); + + if (!target_geometry->node[Point_Target]->GetDomain()) continue; + + Coord_i = target_geometry->node[Point_Target]->GetCoord(); + /*---Loop over the faces previously communicated/stored ---*/ + for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { + + nFaces = (unsigned int)Buffer_Receive_nFace_Donor[iProcessor]; + + for (iFace = 0; iFace< nFaces; iFace++) { + /*--- ---*/ + + nNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1] - + (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; + + su2double *X = new su2double[nNodes*(nDim+1)]; + faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face + for (iDonor=0; iDonorvertex[markTarget][iVertex]->SetDonorElem(donor_elem); // in 2D is nearest neighbor + for (iDonor=0; iDonorvertex[markTarget][iVertex]->Allocate_DonorInfo(nNodes); + + for (iDonor=0; iDonorvertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor,storeGlobal[iDonor]); + target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor,storeCoeff[iDonor]); + target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]); + } + + } + + delete[] Buffer_Send_Coord; + delete[] Buffer_Send_Normal; + delete[] Buffer_Send_GlobalPoint; + + delete[] Buffer_Receive_Coord; + delete[] Buffer_Receive_Normal; + delete[] Buffer_Receive_GlobalPoint; + + delete[] Buffer_Send_FaceIndex; + delete[] Buffer_Send_FaceNodes; + delete[] Buffer_Send_FaceProc; + + delete[] Buffer_Receive_FaceIndex; + delete[] Buffer_Receive_FaceNodes; + delete[] Buffer_Receive_FaceProc; + } + + delete[] Buffer_Receive_nVertex_Donor; + delete[] Buffer_Receive_nFace_Donor; + delete[] Buffer_Receive_nFaceNodes_Donor; + + delete [] Coord; + delete [] Normal; + + delete [] projected_point; +} + +void CIsoparametric::Isoparameters(unsigned short nDim, unsigned short nDonor, + const su2double *X, const su2double *xj, su2double *isoparams) const { + + short iDonor,iDim,k; // indices + su2double tmp, tmp2; + + su2double *x = new su2double[nDim+1]; + su2double *x_tmp = new su2double[nDim+1]; + su2double *Q = new su2double[nDonor*nDonor]; + su2double *R = new su2double[nDonor*nDonor]; + su2double *A = new su2double[(nDim+2)*nDonor]; + su2double *A2 = NULL; + su2double *x2 = new su2double[nDim+1]; + + bool *test = new bool[nDim+1]; + bool *testi = new bool[nDim+1]; + + su2double eps = 1E-10; + + short n = nDim+1; + + if (nDonor>2) { + /*--- Create Matrix A: 1st row all 1's, 2nd row x coordinates, 3rd row y coordinates, etc ---*/ + /*--- Right hand side is [1, \vec{x}']'---*/ + for (iDonor=0; iDonoreps && iDonor=0; iDonor--) { + if (R[iDonor*nDonor+iDonor]>eps) + isoparams[iDonor]=x_tmp[iDonor]/R[iDonor*nDonor+iDonor]; + else + isoparams[iDonor]=0; + for (k=0; k1.0) xi=1.0; + if (xi<-1.0) xi=-1.0; + if (eta>1.0) eta=1.0; + if (eta<-1.0) eta=-1.0; + isoparams[0]=0.25*(1-xi)*(1-eta); + isoparams[1]=0.25*(1+xi)*(1-eta); + isoparams[2]=0.25*(1+xi)*(1+eta); + isoparams[3]=0.25*(1-xi)*(1+eta); + + } + if (nDonor<4) { + tmp = 0.0; // value for normalization + tmp2=0; // check for maximum value, to be used to id nearest neighbor if necessary + k=0; // index for maximum value + for (iDonor=0; iDonor< nDonor; iDonor++) { + if (isoparams[iDonor]>tmp2) { + k=iDonor; + tmp2=isoparams[iDonor]; + } + // [0,1] + if (isoparams[iDonor]<0) isoparams[iDonor]=0; + if (isoparams[iDonor]>1) isoparams[iDonor] = 1; + tmp +=isoparams[iDonor]; + } + if (tmp>0) + for (iDonor=0; iDonor< nDonor; iDonor++) + isoparams[iDonor]=isoparams[iDonor]/tmp; + else { + isoparams[k] = 1.0; + } + } + + delete [] x; + delete [] x_tmp; + delete [] Q; + delete [] R; + delete [] A; + delete [] A2; + delete [] x2; + + delete [] test; + delete [] testi; + +} diff --git a/Common/src/interface_interpolation/CMirror.cpp b/Common/src/interface_interpolation/CMirror.cpp new file mode 100644 index 000000000000..089af9eceec1 --- /dev/null +++ b/Common/src/interface_interpolation/CMirror.cpp @@ -0,0 +1,194 @@ +/*! + * \file CMirror.cpp + * \brief Implementation of mirror interpolation (conservative approach in FSI problems). + * \author H. Kline + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, 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/interface_interpolation/CMirror.hpp" +#include "../../include/CConfig.hpp" +#include "../../include/geometry/CGeometry.hpp" + + +CMirror::CMirror(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { + Set_TransferCoeff(config); +} + +void CMirror::Set_TransferCoeff(const CConfig* const* config) { + + const int nProcessor = size; + + Buffer_Receive_nFace_Donor = new unsigned long [nProcessor]; + Buffer_Receive_nFaceNodes_Donor = new unsigned long [nProcessor]; + + /*--- Number of markers on the interface ---*/ + const auto nMarkerInt = (config[targetZone]->GetMarker_n_ZoneInterface())/2; + + /*--- For the number of markers on the interface... ---*/ + for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { + /*--- Procedure: + * - Loop through vertices of the aero grid + * - Find nearest element and allocate enough space in the aero grid donor point info + * - Set the transfer coefficient values + */ + + /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ + const auto markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); + + /*--- On the target side: find the tag of the boundary sharing the interface ---*/ + const auto markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); + + /*--- Checks if the zone contains the interface, if not continue to the next step ---*/ + if(!CheckInterfaceBoundary(markDonor, markTarget)) continue; + + unsigned long nVertexDonor = 0, nVertexTarget = 0; + if (markDonor != -1) nVertexDonor = donor_geometry->GetnVertex( markDonor ); + if (markTarget != -1) nVertexTarget = target_geometry->GetnVertex( markTarget ); + + /*-- Collect the number of donor nodes: re-use 'Face' containers --*/ + auto nLocalFace_Donor = 0ul; + auto nLocalFaceNodes_Donor = 0ul; + for (auto jVertex = 0ul; jVertexvertex[markDonor][jVertex]->GetNode(); // Local index of jVertex + + if (donor_geometry->node[Point_Donor]->GetDomain()) { + auto nNodes = donor_geometry->vertex[markDonor][jVertex]->GetnDonorPoints(); + nLocalFaceNodes_Donor += nNodes; + nLocalFace_Donor++; + } + } + + Buffer_Send_nFace_Donor[0] = nLocalFace_Donor; + Buffer_Send_nFaceNodes_Donor[0] = nLocalFaceNodes_Donor; + + /*--- Send Interface vertex information --*/ + SU2_MPI::Allreduce(&nLocalFaceNodes_Donor, &MaxFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); + SU2_MPI::Allreduce(&nLocalFace_Donor, &MaxFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_nFace_Donor, 1, MPI_UNSIGNED_LONG, + Buffer_Receive_nFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, + Buffer_Receive_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + MaxFace_Donor++; + + /*-- Send donor info and init to 0 --*/ + Buffer_Send_FaceIndex = new unsigned long[MaxFace_Donor] (); + Buffer_Send_FaceNodes = new unsigned long[MaxFaceNodes_Donor] (); + Buffer_Send_GlobalPoint = new long[MaxFaceNodes_Donor] (); + auto Buffer_Send_Coeff = new su2double[MaxFaceNodes_Donor] (); + + Buffer_Receive_FaceIndex = new unsigned long[MaxFace_Donor*nProcessor]; + Buffer_Receive_FaceNodes = new unsigned long[MaxFaceNodes_Donor*nProcessor]; + Buffer_Receive_GlobalPoint = new long[MaxFaceNodes_Donor*nProcessor]; + auto Buffer_Receive_Coeff = new su2double[MaxFaceNodes_Donor*nProcessor]; + + Buffer_Send_FaceIndex[0]=rank*MaxFaceNodes_Donor; + nLocalFace_Donor=0; + nLocalFaceNodes_Donor=0; + + for (auto jVertex = 0ul; jVertex < nVertexDonor; jVertex++) { + + auto Point_Donor = donor_geometry->vertex[markDonor][jVertex]->GetNode(); // Local index of jVertex + + if (!donor_geometry->node[Point_Donor]->GetDomain()) continue; + + auto nNodes = donor_geometry->vertex[markDonor][jVertex]->GetnDonorPoints(); + for (auto iDonor = 0; iDonor < nNodes; iDonor++) { + Buffer_Send_FaceNodes[nLocalFaceNodes_Donor] = donor_geometry->node[Point_Donor]->GetGlobalIndex(); + Buffer_Send_GlobalPoint[nLocalFaceNodes_Donor] = + donor_geometry->vertex[markDonor][jVertex]->GetInterpDonorPoint(iDonor); + Buffer_Send_Coeff[nLocalFaceNodes_Donor] = + donor_geometry->vertex[markDonor][jVertex]->GetDonorCoeff(iDonor); + nLocalFaceNodes_Donor++; + } + Buffer_Send_FaceIndex[nLocalFace_Donor+1] =Buffer_Send_FaceIndex[nLocalFace_Donor]+nNodes; + nLocalFace_Donor++; + } + + SU2_MPI::Allgather(Buffer_Send_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, + Buffer_Receive_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_GlobalPoint, MaxFaceNodes_Donor, MPI_LONG, + Buffer_Receive_GlobalPoint, MaxFaceNodes_Donor, MPI_LONG, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_Coeff, MaxFaceNodes_Donor, MPI_DOUBLE, + Buffer_Receive_Coeff, MaxFaceNodes_Donor, MPI_DOUBLE, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, + Buffer_Receive_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + + /*--- Loop over the vertices on the target Marker ---*/ + SU2_OMP_PARALLEL_(for schedule(dynamic,roundUpDiv(nVertexTarget,2*omp_get_max_threads()))) + for (auto iVertex = 0ul; iVertex < nVertexTarget; iVertex++) { + + auto target_vertex = target_geometry->vertex[markTarget][iVertex]; + auto iPoint = target_vertex->GetNode(); + + if (!target_geometry->node[iPoint]->GetDomain()) continue; + + const long Global_Point = target_geometry->node[iPoint]->GetGlobalIndex(); + + auto nNodes = 0; + for (int iProcessor = 0; iProcessor < nProcessor; iProcessor++) { + for (auto iFace = 0ul; iFace < Buffer_Receive_nFace_Donor[iProcessor]; iFace++) { + auto faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face + auto iNodes = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1] - faceindex; + for (auto iTarget = 0ul; iTargetAllocate_DonorInfo(nNodes); + + for (int iProcessor = 0, iDonor = 0; iProcessor < nProcessor; iProcessor++) { + for (auto iFace = 0ul; iFace < Buffer_Receive_nFace_Donor[iProcessor]; iFace++) { + + auto faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face + auto iNodes = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1] - faceindex; + + for (auto iTarget = 0ul; iTarget < iNodes; iTarget++) { + if (Global_Point == long(Buffer_Receive_GlobalPoint[faceindex+iTarget])) { + auto coeff = Buffer_Receive_Coeff[faceindex+iTarget]; + auto pGlobalPoint = Buffer_Receive_FaceNodes[faceindex+iTarget]; + target_vertex->SetInterpDonorPoint(iDonor,pGlobalPoint); + target_vertex->SetDonorCoeff(iDonor,coeff); + target_vertex->SetInterpDonorProcessor(iDonor, iProcessor); + iDonor++; + } + } + } + } + } + + delete[] Buffer_Send_FaceIndex; + delete[] Buffer_Send_FaceNodes; + delete[] Buffer_Send_GlobalPoint; + delete[] Buffer_Send_Coeff; + + delete[] Buffer_Receive_FaceIndex; + delete[] Buffer_Receive_FaceNodes; + delete[] Buffer_Receive_GlobalPoint; + delete[] Buffer_Receive_Coeff; + } + + delete[] Buffer_Receive_nFace_Donor; + delete[] Buffer_Receive_nFaceNodes_Donor; + +} diff --git a/Common/src/interface_interpolation/CNearestNeighbor.cpp b/Common/src/interface_interpolation/CNearestNeighbor.cpp new file mode 100644 index 000000000000..e1369f19aef0 --- /dev/null +++ b/Common/src/interface_interpolation/CNearestNeighbor.cpp @@ -0,0 +1,188 @@ +/*! + * \file CNearestNeighbor.cpp + * \brief Implementation of nearest neighbor interpolation. + * \author H. Kline + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, 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/interface_interpolation/CNearestNeighbor.hpp" +#include "../../include/CConfig.hpp" +#include "../../include/geometry/CGeometry.hpp" + + +/*! \brief Helper struct to (partially) sort neighbours according to distance while + * keeping track of the origin of the point (i.e. index and processor). */ +struct DonorInfo { + su2double dist; + unsigned pidx; + int proc; + DonorInfo(su2double d = 0.0, unsigned i = 0, int p = 0) : + dist(d), pidx(i), proc(p) { } +}; + +CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { + Set_TransferCoeff(config); +} + +void CNearestNeighbor::PrintStatistics() const { + if (rank != MASTER_NODE) return; + cout << " Avg/max distance to closest donor point: " << AvgDistance << "/" << MaxDistance << endl; +} + +void CNearestNeighbor::Set_TransferCoeff(const CConfig* const* config) { + + /*--- Desired number of donor points. ---*/ + const auto nDonor = max(config[donorZone]->GetNumNearestNeighbors(), 1); + + /*--- Epsilon used to avoid division by zero. ---*/ + const su2double eps = numeric_limits::epsilon(); + + const int nProcessor = size; + const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2; + const auto nDim = donor_geometry->GetnDim(); + + Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; + + vector > DonorInfoVec(omp_get_max_threads()); + + /*--- Cycle over nMarkersInt interface to determine communication pattern. ---*/ + + AvgDistance = MaxDistance = 0.0; + unsigned long totalTargetPoints = 0; + + for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { + + /*--- On the donor side: find the tag of the boundary sharing the interface. ---*/ + const auto markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); + + /*--- On the target side: find the tag of the boundary sharing the interface. ---*/ + const auto markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); + + /*--- Checks if the zone contains the interface, if not continue to the next step. ---*/ + if (!CheckInterfaceBoundary(markDonor, markTarget)) continue; + + unsigned long nVertexDonor = 0, nVertexTarget = 0; + if(markDonor != -1) nVertexDonor = donor_geometry->GetnVertex( markDonor ); + if(markTarget != -1) nVertexTarget = target_geometry->GetnVertex( markTarget ); + + /* Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor. */ + Determine_ArraySize(false, markDonor, markTarget, nVertexDonor, nDim); + + const auto nPossibleDonor = accumulate(Buffer_Receive_nVertex_Donor, + Buffer_Receive_nVertex_Donor+nProcessor, 0ul); + + Buffer_Send_Coord = new su2double [ MaxLocalVertex_Donor * nDim ]; + Buffer_Send_GlobalPoint = new long [ MaxLocalVertex_Donor ]; + Buffer_Receive_Coord = new su2double [ nProcessor * MaxLocalVertex_Donor * nDim ]; + Buffer_Receive_GlobalPoint = new long [ nProcessor * MaxLocalVertex_Donor ]; + + /*-- Collect coordinates and global point indices. ---*/ + Collect_VertexInfo( false, markDonor, markTarget, nVertexDonor, nDim ); + + /*--- Find the closest donor points to each target. ---*/ + SU2_OMP_PARALLEL + { + /*--- Working array for this thread. ---*/ + auto& donorInfo = DonorInfoVec[omp_get_thread_num()]; + donorInfo.resize(nPossibleDonor); + + su2double avgDist = 0.0, maxDist = 0.0; + unsigned long numTarget = 0; + + SU2_OMP_FOR_DYN(roundUpDiv(nVertexTarget,2*omp_get_max_threads())) + for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget++) { + + auto target_vertex = target_geometry->vertex[markTarget][iVertexTarget]; + const auto Point_Target = target_vertex->GetNode(); + + if (!target_geometry->node[Point_Target]->GetDomain()) continue; + + /*--- Coordinates of the target point. ---*/ + const su2double* Coord_i = target_geometry->node[Point_Target]->GetCoord(); + + /*--- Compute all distances. ---*/ + for (int iProcessor = 0, iDonor = 0; iProcessor < nProcessor; ++iProcessor) { + for (auto jVertex = 0ul; jVertex < Buffer_Receive_nVertex_Donor[iProcessor]; ++jVertex) { + + const auto idx = iProcessor*MaxLocalVertex_Donor + jVertex; + const auto pGlobalPoint = Buffer_Receive_GlobalPoint[idx]; + const su2double* Coord_j = &Buffer_Receive_Coord[idx*nDim]; + const auto dist2 = PointsSquareDistance(nDim, Coord_i, Coord_j); + + donorInfo[iDonor++] = DonorInfo(dist2, pGlobalPoint, iProcessor); + } + } + + /*--- Find k closest points. ---*/ + partial_sort(donorInfo.begin(), donorInfo.begin()+nDonor, donorInfo.end(), + [](const DonorInfo& a, const DonorInfo& b){return a.dist < b.dist;}); + + /*--- Update stats. ---*/ + numTarget += 1; + su2double d = sqrt(donorInfo[0].dist); + avgDist += d; + maxDist = max(maxDist, d); + + /*--- Compute interpolation numerators and denominator. ---*/ + su2double denom = 0.0; + for (auto iDonor = 0ul; iDonor < nDonor; ++iDonor) { + donorInfo[iDonor].dist = 1.0 / (donorInfo[iDonor].dist + eps); + denom += donorInfo[iDonor].dist; + } + + /*--- Set interpolation coefficients. ---*/ + target_vertex->Allocate_DonorInfo(nDonor); + + for (auto iDonor = 0ul; iDonor < nDonor; ++iDonor) { + target_vertex->SetInterpDonorPoint(iDonor, donorInfo[iDonor].pidx); + target_vertex->SetInterpDonorProcessor(iDonor, donorInfo[iDonor].proc); + target_vertex->SetDonorCoeff(iDonor, donorInfo[iDonor].dist/denom); + } + } + SU2_OMP_CRITICAL + { + totalTargetPoints += numTarget; + AvgDistance += avgDist; + MaxDistance = max(MaxDistance, maxDist); + } + } // end SU2_OMP_PARALLEL + + delete[] Buffer_Send_Coord; + delete[] Buffer_Send_GlobalPoint; + + delete[] Buffer_Receive_Coord; + delete[] Buffer_Receive_GlobalPoint; + + } + + delete[] Buffer_Receive_nVertex_Donor; + + unsigned long tmp = totalTargetPoints; + SU2_MPI::Allreduce(&tmp, &totalTargetPoints, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); + su2double tmp1 = AvgDistance, tmp2 = MaxDistance; + SU2_MPI::Allreduce(&tmp1, &AvgDistance, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + SU2_MPI::Allreduce(&tmp2, &MaxDistance, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); + AvgDistance /= totalTargetPoints; + +} diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp new file mode 100644 index 000000000000..7938efb4a364 --- /dev/null +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -0,0 +1,577 @@ +/*! + * \file CRadialBasisFunction.cpp + * \brief Implementation of RBF interpolation. + * \author Joel Ho, P. Gomes + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, 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/interface_interpolation/CRadialBasisFunction.hpp" +#include "../../include/CConfig.hpp" +#include "../../include/geometry/CGeometry.hpp" +#include "../../include/toolboxes/CSymmetricMatrix.hpp" + +#if defined(HAVE_MKL) +#include "mkl.h" +#ifndef HAVE_LAPACK +#define HAVE_LAPACK +#endif +#elif defined(HAVE_LAPACK) +// dgemm(opA, opB, m, n, k, alpha, A, lda, B, ldb, beta, C, ldc) +extern "C" void dgemm_(const char*, const char*, const int*, const int*, const int*, + const passivedouble*, const passivedouble*, const int*, const passivedouble*, + const int*, const passivedouble*, passivedouble*, const int*); +#define DGEMM dgemm_ +#endif + + +CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { + Set_TransferCoeff(config); +} + +void CRadialBasisFunction::PrintStatistics() const { + if (rank != MASTER_NODE) return; + cout.precision(3); + cout << " Min/avg/max number of RBF donors per target point: " + << MinDonors << "/" << AvgDonors << "/" << MaxDonors << "\n" + << " Avg/max correction factor after pruning: " << AvgCorrection << "/" << MaxCorrection; + if (MaxCorrection < 1.1 || AvgCorrection < 1.02) cout << " (ok)\n"; + else if (MaxCorrection < 2.0 && AvgCorrection < 1.05) cout << " (warning)\n"; + else cout << " <<< WARNING >>>\n"; + cout << " Interpolation matrix is " << Density << "% dense." << endl; + cout.unsetf(ios::floatfield); +} + +su2double CRadialBasisFunction::Get_RadialBasisValue(ENUM_RADIALBASIS type, const su2double radius, const su2double dist) +{ + su2double rbf = dist/radius; + + switch (type) { + + case WENDLAND_C2: + if(rbf < 1) rbf = pow(pow((1-rbf),2),2)*(4*rbf+1); // double use of pow(x,2) for optimization + else rbf = 0.0; + break; + + case GAUSSIAN: + rbf = exp(-rbf*rbf); + break; + + case THIN_PLATE_SPLINE: + if(rbf < numeric_limits::min()) rbf = 0.0; + else rbf *= rbf*log(rbf); + break; + + case MULTI_QUADRIC: + case INV_MULTI_QUADRIC: + rbf = sqrt(1.0+rbf*rbf); + if(type == INV_MULTI_QUADRIC) rbf = 1.0/rbf; + break; + } + + return rbf; +} + +void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { + + /*--- RBF options. ---*/ + const auto kindRBF = static_cast(config[donorZone]->GetKindRadialBasisFunction()); + const bool usePolynomial = config[donorZone]->GetRadialBasisFunctionPolynomialOption(); + const su2double paramRBF = config[donorZone]->GetRadialBasisFunctionParameter(); + const su2double pruneTol = config[donorZone]->GetRadialBasisFunctionPruneTol(); + + const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2; + const int nDim = donor_geometry->GetnDim(); + + const int nProcessor = size; + Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; + + /*--- Process interface patches in parallel, fetch all donor point coordinates, + * then distribute interpolation matrix computation over ranks and threads. + * To avoid repeating calls to Collect_VertexInfo we also save the global + * indices of the donor points and the mpi rank index that owns them. ---*/ + + vector donorCoordinates(nMarkerInt); + vector > donorGlobalPoint(nMarkerInt); + vector > donorProcessor(nMarkerInt); + vector assignedProcessor(nMarkerInt,-1); + vector totalWork(nProcessor,0); + + for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; ++iMarkerInt) { + + /*--- On the donor side: find the tag of the boundary sharing the interface. ---*/ + const auto markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt+1); + + /*--- On the target side: find the tag of the boundary sharing the interface. ---*/ + const auto markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt+1); + + /*--- If the zone does not contain the interface continue to the next pair of markers. ---*/ + if(!CheckInterfaceBoundary(markDonor,markTarget)) continue; + + unsigned long nVertexDonor = 0; + if(markDonor != -1) nVertexDonor = donor_geometry->GetnVertex(markDonor); + + /*--- Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor. ---*/ + Determine_ArraySize(false, markDonor, markTarget, nVertexDonor, nDim); + + /*--- Compute total number of donor vertices. ---*/ + const auto nGlobalVertexDonor = accumulate(Buffer_Receive_nVertex_Donor, + Buffer_Receive_nVertex_Donor+nProcessor, 0ul); + + /*--- Gather coordinates and global point indices. ---*/ + Buffer_Send_Coord = new su2double [ MaxLocalVertex_Donor * nDim ]; + Buffer_Send_GlobalPoint = new long [ MaxLocalVertex_Donor ]; + Buffer_Receive_Coord = new su2double [ nProcessor * MaxLocalVertex_Donor * nDim ]; + Buffer_Receive_GlobalPoint = new long [ nProcessor * MaxLocalVertex_Donor ]; + + Collect_VertexInfo(false, markDonor, markTarget, nVertexDonor, nDim); + + /*--- Compresses the gathered donor point information to simplify computations. ---*/ + auto& donorCoord = donorCoordinates[iMarkerInt]; + auto& donorPoint = donorGlobalPoint[iMarkerInt]; + auto& donorProc = donorProcessor[iMarkerInt]; + donorCoord.resize(nGlobalVertexDonor, nDim); + donorPoint.resize(nGlobalVertexDonor); + donorProc.resize(nGlobalVertexDonor); + + auto iCount = 0ul; + for (int iProcessor = 0; iProcessor < nProcessor; ++iProcessor) { + auto offset = iProcessor * MaxLocalVertex_Donor; + for (auto iVertex = 0ul; iVertex < Buffer_Receive_nVertex_Donor[iProcessor]; ++iVertex) { + for (int iDim = 0; iDim < nDim; ++iDim) + donorCoord(iCount,iDim) = Buffer_Receive_Coord[(offset+iVertex)*nDim + iDim]; + donorPoint[iCount] = Buffer_Receive_GlobalPoint[offset+iVertex]; + donorProc[iCount] = iProcessor; + ++iCount; + } + } + assert((iCount == nGlobalVertexDonor) && "Global donor point count mismatch."); + + delete[] Buffer_Send_Coord; + delete[] Buffer_Send_GlobalPoint; + delete[] Buffer_Receive_Coord; + delete[] Buffer_Receive_GlobalPoint; + + /*--- Static work scheduling over ranks based on which one has less work currently. ---*/ + int iProcessor = 0; + for (int i = 1; i < nProcessor; ++i) + if (totalWork[i] < totalWork[iProcessor]) iProcessor = i; + + totalWork[iProcessor] += pow(nGlobalVertexDonor,3); // based on matrix inversion. + + assignedProcessor[iMarkerInt] = iProcessor; + + } + delete[] Buffer_Receive_nVertex_Donor; + + /*--- Compute the interpolation matrices for each patch of coordinates + * assigned to the rank. Subdivide work further by threads. ---*/ + vector nPolynomialVec(nMarkerInt,-1); + vector > keepPolynomialRowVec(nMarkerInt, vector(nDim,1)); + vector CinvTrucVec(nMarkerInt); + + SU2_OMP_PARALLEL_(for schedule(dynamic,1)) + for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; ++iMarkerInt) { + if (rank == assignedProcessor[iMarkerInt]) { + ComputeGeneratorMatrix(kindRBF, usePolynomial, paramRBF, + donorCoordinates[iMarkerInt], nPolynomialVec[iMarkerInt], + keepPolynomialRowVec[iMarkerInt], CinvTrucVec[iMarkerInt]); + } + } + + /*--- Final loop over interface markers to compute the interpolation coefficients. ---*/ + + /*--- Initialize variables for interpolation statistics. ---*/ + unsigned long totalTargetPoints = 0, totalDonorPoints = 0, denseSize = 0; + MinDonors = 1<<30; MaxDonors = 0; MaxCorrection = 0.0; AvgCorrection = 0.0; + + for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; iMarkerInt++) { + + /*--- Identify the rank that computed the interpolation matrix for this marker. ---*/ + const int iProcessor = assignedProcessor[iMarkerInt]; + /*--- If no processor was assigned to work, the zone does not contain the interface. ---*/ + if (iProcessor < 0) continue; + + /*--- Setup target information. ---*/ + const int markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt+1); + unsigned long nVertexTarget = 0; + if (markTarget != -1) nVertexTarget = target_geometry->GetnVertex(markTarget); + + /*--- Set references to donor information. ---*/ + auto& donorCoord = donorCoordinates[iMarkerInt]; + auto& donorPoint = donorGlobalPoint[iMarkerInt]; + auto& donorProc = donorProcessor[iMarkerInt]; + + auto& C_inv_trunc = CinvTrucVec[iMarkerInt]; + auto& nPolynomial = nPolynomialVec[iMarkerInt]; + auto& keepPolynomialRow = keepPolynomialRowVec[iMarkerInt]; + + const auto nGlobalVertexDonor = donorCoord.rows(); + +#ifdef HAVE_MPI + /*--- For simplicity, broadcast small information about the interpolation matrix. ---*/ + SU2_MPI::Bcast(&nPolynomial, 1, MPI_INT, iProcessor, MPI_COMM_WORLD); + SU2_MPI::Bcast(keepPolynomialRow.data(), nDim, MPI_INT, iProcessor, MPI_COMM_WORLD); + + /*--- Send C_inv_trunc only to the ranks that need it (those with target points), + * partial broadcast. MPI wrapper not used due to passive double. ---*/ + vector allNumVertex(nProcessor); + SU2_MPI::Allgather(&nVertexTarget, 1, MPI_UNSIGNED_LONG, + allNumVertex.data(), 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + + if (rank == iProcessor) { + for (int jProcessor = 0; jProcessor < nProcessor; ++jProcessor) + if ((jProcessor != iProcessor) && (allNumVertex[jProcessor] != 0)) + MPI_Send(C_inv_trunc.data(), C_inv_trunc.size(), + MPI_DOUBLE, jProcessor, 0, MPI_COMM_WORLD); + } + else if (nVertexTarget != 0) { + C_inv_trunc.resize(1+nPolynomial+nGlobalVertexDonor, nGlobalVertexDonor); + MPI_Recv(C_inv_trunc.data(), C_inv_trunc.size(), MPI_DOUBLE, + iProcessor, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + } +#endif + + /*--- Compute interpolation matrix (H). This is a large matrix-matrix product with + * the generator matrix (C_inv_trunc) on the right. We avoid instantiation + * of the entire function matrix (A) and of the result (H), but work + * on a slab (set of rows) of A/H to amortize accesses to C_inv_trunc. ---*/ + + /*--- Fetch domain target vertices. ---*/ + + vector targetVertices; targetVertices.reserve(nVertexTarget); + vector targetCoord; targetCoord.reserve(nVertexTarget); + + for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; ++iVertexTarget) { + + auto targetVertex = target_geometry->vertex[markTarget][iVertexTarget]; + auto pointTarget = targetVertex->GetNode(); + + if (target_geometry->node[pointTarget]->GetDomain()) { + targetVertices.push_back(targetVertex); + targetCoord.push_back(target_geometry->node[pointTarget]->GetCoord()); + } + } + nVertexTarget = targetVertices.size(); + totalTargetPoints += nVertexTarget; + denseSize += nVertexTarget*nGlobalVertexDonor; + + /*--- Distribute target slabs over the threads in the rank for processing. ---*/ + + SU2_OMP_PARALLEL + if (nVertexTarget > 0) { + + constexpr unsigned long targetSlabSize = 32; + + su2passivematrix funcMat(targetSlabSize, 1+nPolynomial+nGlobalVertexDonor); + su2passivematrix interpMat(targetSlabSize, nGlobalVertexDonor); + + /*--- Thread-local variables for statistics. ---*/ + unsigned long minDonors = 1<<30, maxDonors = 0, totalDonors = 0; + passivedouble sumCorr = 0.0, maxCorr = 0.0; + + SU2_OMP_FOR_DYN(1) + for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget += targetSlabSize) { + + const auto iLastVertex = min(nVertexTarget, iVertexTarget+targetSlabSize); + const auto slabSize = iLastVertex - iVertexTarget; + + /*--- Prepare matrix of functions A (the targets to donors matrix). ---*/ + + /*--- Polynominal part: ---*/ + if (usePolynomial) { + /*--- Constant term. ---*/ + for (auto k = 0ul; k < slabSize; ++k) funcMat(k,0) = 1.0; + + /*--- Linear terms. ---*/ + for (int iDim = 0, idx = 1; iDim < nDim; ++iDim) { + /*--- Of which one may have been excluded. ---*/ + if (!keepPolynomialRow[iDim]) continue; + for (auto k = 0ul; k < slabSize; ++k) + funcMat(k, idx) = SU2_TYPE::GetValue(targetCoord[iVertexTarget+k][iDim]); + idx += 1; + } + } + /*--- RBF terms: ---*/ + for (auto iVertexDonor = 0ul; iVertexDonor < nGlobalVertexDonor; ++iVertexDonor) { + for (auto k = 0ul; k < slabSize; ++k) { + auto dist = PointsDistance(nDim, targetCoord[iVertexTarget+k], donorCoord[iVertexDonor]); + auto rbf = Get_RadialBasisValue(kindRBF, paramRBF, dist); + funcMat(k, 1+nPolynomial+iVertexDonor) = SU2_TYPE::GetValue(rbf); + } + } + + /*--- Compute slab of the interpolation matrix. ---*/ +#ifdef HAVE_LAPACK + /*--- interpMat = funcMat * C_inv_trunc, but order of gemm arguments + * is swapped due to row-major storage of su2passivematrix. ---*/ + const char op = 'N'; + const int M = interpMat.cols(), N = slabSize, K = funcMat.cols(); + // lda = C_inv_trunc.cols() = M; ldb = funcMat.cols() = K; ldc = interpMat.cols() = M; + const passivedouble alpha = 1.0, beta = 0.0; + DGEMM(&op, &op, &M, &N, &K, &alpha, C_inv_trunc[0], &M, funcMat[0], &K, &beta, interpMat[0], &M); +#else + /*--- Naive product, loop order considers short-wide + * nature of funcMat and interpMat. ---*/ + interpMat = 0.0; + for (auto k = 0ul; k < funcMat.cols(); ++k) + for (auto i = 0ul; i < slabSize; ++i) + for (auto j = 0ul; j < interpMat.cols(); ++j) + interpMat(i,j) += funcMat(i,k) * C_inv_trunc(k,j); +#endif + /*--- Set interpolation coefficients. ---*/ + + for (auto k = 0ul; k < slabSize; ++k) { + auto targetVertex = targetVertices[iVertexTarget+k]; + + /*--- Prune small coefficients. ---*/ + auto info = PruneSmallCoefficients(SU2_TYPE::GetValue(pruneTol), interpMat.cols(), interpMat[k]); + auto nnz = info.first; + totalDonors += nnz; + minDonors = min(minDonors, nnz); + maxDonors = max(maxDonors, nnz); + auto corr = fabs(info.second-1.0); // far from 1 either way is bad; + sumCorr += corr; + maxCorr = max(maxCorr, corr); + + /*--- Allocate and set donor information for this target point. ---*/ + targetVertex->Allocate_DonorInfo(nnz); + + for (unsigned long iVertex = 0, iSet = 0; iVertex < nGlobalVertexDonor; ++iVertex) { + auto coeff = interpMat(k,iVertex); + if (fabs(coeff) > 0.0) { + targetVertex->SetInterpDonorProcessor(iSet, donorProc[iVertex]); + targetVertex->SetInterpDonorPoint(iSet, donorPoint[iVertex]); + targetVertex->SetDonorCoeff(iSet, coeff); + ++iSet; + } + } + } + } // end target vertex loop + + SU2_OMP_CRITICAL + { + totalDonorPoints += totalDonors; + MinDonors = min(MinDonors, minDonors); + MaxDonors = max(MaxDonors, maxDonors); + AvgCorrection += sumCorr; + MaxCorrection = max(MaxCorrection, maxCorr); + } + } // end SU2_OMP_PARALLEL + + /*--- Free global data that will no longer be used. ---*/ + donorCoord.resize(0,0); + vector().swap(donorPoint); + vector().swap(donorProc); + C_inv_trunc.resize(0,0); + + } // end loop over interface markers + + /*--- Final reduction of interpolation statistics and basic sanity checks. ---*/ + auto Reduce = [](SU2_MPI::Op op, unsigned long &val) { + auto tmp = val; + SU2_MPI::Allreduce(&tmp, &val, 1, MPI_UNSIGNED_LONG, op, MPI_COMM_WORLD); + }; + Reduce(MPI_SUM, totalTargetPoints); + Reduce(MPI_SUM, totalDonorPoints); + Reduce(MPI_SUM, denseSize); + Reduce(MPI_MIN, MinDonors); + Reduce(MPI_MAX, MaxDonors); +#ifdef HAVE_MPI + passivedouble tmp1 = AvgCorrection, tmp2 = MaxCorrection; + MPI_Allreduce(&tmp1, &AvgCorrection, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(&tmp2, &MaxCorrection, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); +#endif + if (totalTargetPoints == 0) + SU2_MPI::Error("Somehow there are no target interpolation points.", CURRENT_FUNCTION); + + if (MinDonors == 0) + SU2_MPI::Error("One or more target points have no donors, either:\n" + " - The interface surfaces are not in contact.\n" + " - The RBF radius is too small.\n" + " - The pruning tolerance is too aggressive.", CURRENT_FUNCTION); + + MaxCorrection += 1.0; // put back the reference "1" + AvgCorrection = AvgCorrection / totalTargetPoints + 1.0; + AvgDonors = totalDonorPoints / totalTargetPoints; + Density = totalDonorPoints / (0.01*denseSize); + +} + +void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, + su2double radius, const su2activematrix& coords, int& nPolynomial, + vector& keepPolynomialRow, su2passivematrix& C_inv_trunc) { + + const su2double interfaceCoordTol = 1e6 * numeric_limits::epsilon(); + + const int nVertexDonor = coords.rows(); + const int nDim = coords.cols(); + + /*--- Populate interpolation kernel. ---*/ + CSymmetricMatrix global_M(nVertexDonor); + + for (int iVertex = 0; iVertex < nVertexDonor; ++iVertex) + for (int jVertex = iVertex; jVertex < nVertexDonor; ++jVertex) + global_M(iVertex, jVertex) = SU2_TYPE::GetValue(Get_RadialBasisValue(type, radius, + PointsDistance(nDim, coords[iVertex], coords[jVertex]))); + + /*--- Invert M matrix (operation is in-place). ---*/ + const bool kernelIsSPD = (type==WENDLAND_C2) || (type==GAUSSIAN) || (type==INV_MULTI_QUADRIC); + global_M.Invert(kernelIsSPD); + + /*--- Compute C_inv_trunc. ---*/ + if (usePolynomial) { + + /*--- Fill P matrix (P for points, with an extra top row of ones). ---*/ + su2passivematrix P(1+nDim, nVertexDonor); + + for (int iVertex = 0; iVertex < nVertexDonor; iVertex++) { + P(0, iVertex) = 1.0; + for (int iDim = 0; iDim < nDim; ++iDim) + P(1+iDim, iVertex) = SU2_TYPE::GetValue(coords(iVertex, iDim)); + } + + /*--- Check if points lie on a plane and remove one coordinate from P if so. ---*/ + nPolynomial = CheckPolynomialTerms(interfaceCoordTol, keepPolynomialRow, P); + + /*--- Compute Q = P * M^-1 ---*/ + su2passivematrix Q; + global_M.MatMatMult('R', P, Q); + + /*--- Compute Mp = (Q * P^T)^-1 ---*/ + CSymmetricMatrix Mp(nPolynomial+1); + + for (int i = 0; i <= nPolynomial; ++i) + for (int j = i; j <= nPolynomial; ++j) { + Mp(i,j) = 0.0; + for (int k = 0; k < nVertexDonor; ++k) + Mp(i,j) += Q(i,k) * P(j,k); + } + Mp.Invert(false); + + /*--- Compute M_p * Q, the top part of C_inv_trunc. ---*/ + su2passivematrix C_inv_top; + Mp.MatMatMult('L', Q, C_inv_top); + + /*--- Compute M^-1 - Q^T * C_inv_top, the bottom (main) part of C_inv_trunc. ---*/ + su2passivematrix C_inv_bot = global_M.StealData(); +#ifdef HAVE_LAPACK + /*--- Order of gemm arguments swapped due to row-major storage. ---*/ + const char opa = 'N', opb = 'T'; + const int M = nVertexDonor, N = nVertexDonor, K = nPolynomial+1; + // lda = C_inv_top.cols() = M; ldb = Q.cols() = M; ldc = C_inv_bot.cols() = M; + const passivedouble alpha = -1.0, beta = 1.0; + DGEMM(&opa, &opb, &M, &N, &K, &alpha, C_inv_top[0], &M, Q[0], &M, &beta, C_inv_bot[0], &M); +#else // naive product + for (int i = 0; i < nVertexDonor; ++i) + for (int j = 0; j < nVertexDonor; ++j) + for (int k = 0; k <= nPolynomial; ++k) + C_inv_bot(i,j) -= Q(k,i) * C_inv_top(k,j); +#endif + + /*--- Merge top and bottom of C_inv_trunc. More intrusive memory + * management, or separate handling of top and bottom, would + * avoid these copies (and associated temporary vars). ---*/ + C_inv_trunc.resize(1+nPolynomial+nVertexDonor, nVertexDonor); + memcpy(C_inv_trunc[0], C_inv_top.data(), C_inv_top.size()*sizeof(passivedouble)); + memcpy(C_inv_trunc[1+nPolynomial], C_inv_bot.data(), C_inv_bot.size()*sizeof(passivedouble)); + } + else { + /*--- No polynomial term used in the interpolation, C_inv_trunc = M^-1. ---*/ + + C_inv_trunc = global_M.StealData(); + + } // end usePolynomial + +} + +int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, + su2passivematrix &P) { + const int m = P.rows(); + const int n = P.cols(); + + /*--- The first row of P is all ones and we do not care about it for this analysis. ---*/ + const int n_rows = m-1; + keep_row.resize(n_rows); + + /*--- By default assume points are not on a plane (all rows kept). ---*/ + int n_polynomial = n_rows; + keep_row.resize(n_rows); + for (int i = 0; i < n_rows; ++i) keep_row[i] = 1; + + /*--- Fit a plane through the points in P. ---*/ + + /*--- Compute P times its transpose and invert. ---*/ + CSymmetricMatrix PPT(n_rows); + + for (int i = 0; i < n_rows; ++i) + for (int j = i; j < n_rows; ++j) { + PPT(i,j) = 0.0; + for (int k = 0; k < n; ++k) PPT(i,j) += P(i+1,k) * P(j+1,k); + } + PPT.Invert(true); + + /*--- RHS for the least squares fit (vector of ones times P). ---*/ + vector rhs(n_rows,0.0), coeff(n_rows); + + for (int i = 0; i < n_rows; ++i) + for (int j = 0; j < n; ++j) + rhs[i] += P(i+1,j); + + /*--- Multiply the RHS by the inverse thus obtaining the coefficients. ---*/ + PPT.MatVecMult(rhs.begin(), coeff.begin()); + + /*--- Determine the maximum deviation of the points from the fitted plane. ---*/ + passivedouble max_diff = 0.0; + + for (int j = 0; j < n; ++j) + { + passivedouble sum = 0.0; + for (int i = 0; i < n_rows; ++i) sum += coeff[i] * P(i+1,j); + + /*--- 1.0 is the arbitrary constant we are assuming when fitting + the plane, i.e. the vector of ones used to generate the RHS. ---*/ + max_diff = max(abs(1.0-sum), max_diff); + } + + /*--- If points lie on plane remove row associated with the maximum coefficient. ---*/ + if (max_diff < max_diff_tol) + { + /*--- Find the max coeff and mark the corresponding row for removal. ---*/ + int remove_row = 0; + for (int i = 1; i < n_rows; ++i) + if (abs(coeff[i]) > abs(coeff[remove_row])) + remove_row = i; + + /*--- Mark row as removed and adjust number of polynomial terms. ---*/ + n_polynomial = n_rows-1; + keep_row[remove_row] = 0; + + /*--- Truncated P by shifting rows "up". ---*/ + for (auto i = remove_row+1; i < m-1; ++i) + for (int j = 0; j < n; ++j) + P(i,j) = P(i+1,j); + } + + return n_polynomial; +} diff --git a/Common/src/interface_interpolation/CSlidingMesh.cpp b/Common/src/interface_interpolation/CSlidingMesh.cpp new file mode 100644 index 000000000000..3fe75ad5c033 --- /dev/null +++ b/Common/src/interface_interpolation/CSlidingMesh.cpp @@ -0,0 +1,1264 @@ +/*! + * \file CSlidingMesh.cpp + * \brief Implementation of sliding mesh interpolation. + * \author H. Kline + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, 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/interface_interpolation/CSlidingMesh.hpp" +#include "../../include/CConfig.hpp" +#include "../../include/geometry/CGeometry.hpp" + + +CSlidingMesh::CSlidingMesh(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { + Set_TransferCoeff(config); +} + +void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { + + /* 0 - Variable declaration */ + + /* --- General variables --- */ + + bool check; + + unsigned short iDim; + + unsigned long ii, jj, *uptr; + unsigned long vPoint; + unsigned long iEdgeVisited, nEdgeVisited, iNodeVisited; + unsigned long nAlreadyVisited, nToVisit, StartVisited; + + unsigned long *alreadyVisitedDonor, *ToVisit, *tmpVect; + unsigned long *storeProc, *tmp_storeProc; + + su2double dTMP; + su2double *Coeff_Vect, *tmp_Coeff_Vect; + + /* --- Geometrical variables --- */ + + su2double *Coord_i, *Coord_j, dist, mindist, *Normal; + su2double Area, Area_old, tmp_Area; + su2double LineIntersectionLength, *Direction, length; + + + /* --- Markers Variables --- */ + + unsigned short iMarkerInt, nMarkerInt; + + unsigned long iVertex, nVertexTarget; + + int markDonor, markTarget; + + /* --- Target variables --- */ + + unsigned long target_iPoint, jVertexTarget; + unsigned long nEdges_target, nNode_target; + + unsigned long *Target_nLinkedNodes, *Target_LinkedNodes, *Target_StartLinkedNodes, *target_segment; + unsigned long *Target_Proc; + long *Target_GlobalPoint, *Donor_GlobalPoint; + + su2double *TargetPoint_Coord, *target_iMidEdge_point, *target_jMidEdge_point, **target_element; + + /* --- Donor variables --- */ + + unsigned long donor_StartIndex, donor_forward_point, donor_backward_point, donor_iPoint, donor_OldiPoint; + unsigned long nEdges_donor, nNode_donor, nGlobalVertex_Donor; + + unsigned long nDonorPoints, iDonor; + unsigned long *Donor_Vect, *tmp_Donor_Vect; + unsigned long *Donor_nLinkedNodes, *Donor_LinkedNodes, *Donor_StartLinkedNodes; + unsigned long *Donor_Proc; + + su2double *donor_iMidEdge_point, *donor_jMidEdge_point; + su2double **donor_element, *DonorPoint_Coord; + + /* 1 - Variable pre-processing */ + + const unsigned short nDim = donor_geometry->GetnDim(); + + /*--- Setting up auxiliary vectors ---*/ + + Donor_Vect = nullptr; + Coeff_Vect = nullptr; + storeProc = nullptr; + + tmp_Donor_Vect = nullptr; + tmp_Coeff_Vect = nullptr; + tmp_storeProc = nullptr; + + Normal = new su2double[nDim]; + Direction = new su2double[nDim]; + + + /* 2 - Find boundary tag between touching grids */ + + /*--- Number of markers on the FSI interface ---*/ + nMarkerInt = (int)( config[ donorZone ]->GetMarker_n_ZoneInterface() ) / 2; + + /*--- For the number of markers on the interface... ---*/ + for ( iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++ ){ + + /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ + markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); + + /*--- On the target side: find the tag of the boundary sharing the interface ---*/ + markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); + + /*--- Checks if the zone contains the interface, if not continue to the next step ---*/ + if(!CheckInterfaceBoundary(markDonor, markTarget)) continue; + + if(markTarget != -1) + nVertexTarget = target_geometry->GetnVertex( markTarget ); + else + nVertexTarget = 0; + + /* + 3 -Reconstruct the boundaries from parallel partitioning + */ + + /*--- Target boundary ---*/ + ReconstructBoundary(targetZone, markTarget); + + nGlobalVertex_Target = nGlobalVertex; + + TargetPoint_Coord = Buffer_Receive_Coord; + Target_GlobalPoint = Buffer_Receive_GlobalPoint; + Target_nLinkedNodes = Buffer_Receive_nLinkedNodes; + Target_StartLinkedNodes = Buffer_Receive_StartLinkedNodes; + Target_LinkedNodes = Buffer_Receive_LinkedNodes; + Target_Proc = Buffer_Receive_Proc; + + /*--- Donor boundary ---*/ + ReconstructBoundary(donorZone, markDonor); + + nGlobalVertex_Donor = nGlobalVertex; + + DonorPoint_Coord = Buffer_Receive_Coord; + Donor_GlobalPoint = Buffer_Receive_GlobalPoint; + Donor_nLinkedNodes = Buffer_Receive_nLinkedNodes; + Donor_StartLinkedNodes = Buffer_Receive_StartLinkedNodes; + Donor_LinkedNodes = Buffer_Receive_LinkedNodes; + Donor_Proc = Buffer_Receive_Proc; + + /*--- Starts building the supermesh layer (2D or 3D) ---*/ + /* - For each target node, it first finds the closest donor point + * - Then it creates the supermesh in the close proximity of the target point: + * - Starting from the closest donor node, it expands the supermesh by including + * donor elements neighboring the initial one, until the overall target area is fully covered. + */ + + if(nDim == 2){ + + target_iMidEdge_point = new su2double[nDim]; + target_jMidEdge_point = new su2double[nDim]; + + donor_iMidEdge_point = new su2double[nDim]; + donor_jMidEdge_point = new su2double[nDim]; + + /*--- Starts with supermesh reconstruction ---*/ + + target_segment = new unsigned long[2]; + + for (iVertex = 0; iVertex < nVertexTarget; iVertex++) { + + nDonorPoints = 0; + + /*--- Stores coordinates of the target node ---*/ + + target_iPoint = target_geometry->vertex[markTarget][iVertex]->GetNode(); + + if (target_geometry->node[target_iPoint]->GetDomain()){ + + Coord_i = target_geometry->node[target_iPoint]->GetCoord(); + + /*--- Brute force to find the closest donor_node ---*/ + + mindist = 1E6; + donor_StartIndex = 0; + + for (donor_iPoint = 0; donor_iPoint < nGlobalVertex_Donor; donor_iPoint++) { + + Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ]; + + dist = PointsDistance(nDim, Coord_i, Coord_j); + + if (dist < mindist) { + mindist = dist; + donor_StartIndex = donor_iPoint; + } + + if (dist == 0.0){ + donor_StartIndex = donor_iPoint; + break; + } + } + + donor_iPoint = donor_StartIndex; + donor_OldiPoint = donor_iPoint; + + /*--- Contruct information regarding the target cell ---*/ + + long dPoint = target_geometry->node[target_iPoint]->GetGlobalIndex(); + for (jVertexTarget = 0; jVertexTarget < nGlobalVertex_Target; jVertexTarget++) + if( dPoint == Target_GlobalPoint[jVertexTarget] ) + break; + + if ( Target_nLinkedNodes[jVertexTarget] == 1 ){ + target_segment[0] = Target_LinkedNodes[ Target_StartLinkedNodes[jVertexTarget] ]; + target_segment[1] = jVertexTarget; + } + else{ + target_segment[0] = Target_LinkedNodes[ Target_StartLinkedNodes[jVertexTarget] ]; + target_segment[1] = Target_LinkedNodes[ Target_StartLinkedNodes[jVertexTarget] + 1]; + } + + dTMP = 0; + for(iDim = 0; iDim < nDim; iDim++){ + target_iMidEdge_point[iDim] = ( TargetPoint_Coord[ nDim * target_segment[0] + iDim ] + + target_geometry->node[ target_iPoint ]->GetCoord(iDim) ) / 2; + target_jMidEdge_point[iDim] = ( TargetPoint_Coord[ nDim * target_segment[1] + iDim ] + + target_geometry->node[ target_iPoint ]->GetCoord(iDim) ) / 2; + + Direction[iDim] = target_jMidEdge_point[iDim] - target_iMidEdge_point[iDim]; + dTMP += Direction[iDim] * Direction[iDim]; + } + + dTMP = sqrt(dTMP); + for(iDim = 0; iDim < nDim; iDim++) + Direction[iDim] /= dTMP; + + length = PointsDistance(nDim, target_iMidEdge_point, target_jMidEdge_point); + + check = false; + + /*--- Proceeds along the forward direction (depending on which connected boundary node is found first) ---*/ + + while( !check ){ + + /*--- Proceeds until the value of the intersection area is null ---*/ + + if ( Donor_nLinkedNodes[donor_iPoint] == 1 ){ + donor_forward_point = Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_iPoint] ]; + donor_backward_point = donor_iPoint; + } + else{ + uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_iPoint] ]; + + if( donor_OldiPoint != uptr[0] ){ + donor_forward_point = uptr[0]; + donor_backward_point = uptr[1]; + } + else{ + donor_forward_point = uptr[1]; + donor_backward_point = uptr[0]; + } + } + + if(donor_iPoint >= nGlobalVertex_Donor){ + check = true; + continue; + } + + for(iDim = 0; iDim < nDim; iDim++){ + donor_iMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_forward_point * nDim + iDim] + + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; + donor_jMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_backward_point * nDim + iDim] + + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; + } + + LineIntersectionLength = ComputeLineIntersectionLength(nDim, target_iMidEdge_point, target_jMidEdge_point, + donor_iMidEdge_point, donor_jMidEdge_point, Direction); + + if ( LineIntersectionLength == 0.0 ){ + check = true; + continue; + } + + /*--- In case the element intersects the target cell, update the auxiliary communication data structure ---*/ + + tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ]; + tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ]; + tmp_storeProc = new unsigned long[ nDonorPoints + 1 ]; + + for( iDonor = 0; iDonor < nDonorPoints; iDonor++){ + tmp_Donor_Vect[iDonor] = Donor_Vect[iDonor]; + tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor]; + tmp_storeProc[iDonor] = storeProc[iDonor]; + } + + tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint; + tmp_Coeff_Vect[ nDonorPoints ] = LineIntersectionLength / length; + tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint]; + + if (Donor_Vect != nullptr) delete [] Donor_Vect; + if (Coeff_Vect != nullptr) delete [] Coeff_Vect; + if (storeProc != nullptr) delete [] storeProc; + + Donor_Vect = tmp_Donor_Vect; + Coeff_Vect = tmp_Coeff_Vect; + storeProc = tmp_storeProc; + + donor_OldiPoint = donor_iPoint; + donor_iPoint = donor_forward_point; + + nDonorPoints++; + } + + if ( Donor_nLinkedNodes[donor_StartIndex] == 2 ){ + check = false; + + uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_StartIndex] ]; + + donor_iPoint = uptr[1]; + donor_OldiPoint = donor_StartIndex; + } + else + check = true; + + /*--- Proceeds along the backward direction (depending on which connected boundary node is found first) ---*/ + + while( !check ){ + + /*--- Proceeds until the value of the intersection length is null ---*/ + if ( Donor_nLinkedNodes[donor_iPoint] == 1 ){ + donor_forward_point = donor_OldiPoint; + donor_backward_point = donor_iPoint; + } + else{ + uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_iPoint] ]; + + if( donor_OldiPoint != uptr[0] ){ + donor_forward_point = uptr[0]; + donor_backward_point = uptr[1]; + } + else{ + donor_forward_point = uptr[1]; + donor_backward_point = uptr[0]; + } + } + + if(donor_iPoint >= nGlobalVertex_Donor){ + check = true; + continue; + } + + for(iDim = 0; iDim < nDim; iDim++){ + donor_iMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_forward_point * nDim + iDim] + + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; + donor_jMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_backward_point * nDim + iDim] + + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; + } + + LineIntersectionLength = ComputeLineIntersectionLength(nDim, target_iMidEdge_point, target_jMidEdge_point, + donor_iMidEdge_point, donor_jMidEdge_point, Direction); + + if ( LineIntersectionLength == 0.0 ){ + check = true; + continue; + } + + /*--- In case the element intersects the target cell, update the auxiliary communication data structure ---*/ + + tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ]; + tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ]; + tmp_storeProc = new unsigned long[ nDonorPoints + 1 ]; + + for( iDonor = 0; iDonor < nDonorPoints; iDonor++){ + tmp_Donor_Vect[iDonor] = Donor_Vect[iDonor]; + tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor]; + tmp_storeProc[iDonor] = storeProc[iDonor]; + } + + tmp_Coeff_Vect[ nDonorPoints ] = LineIntersectionLength / length; + tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint; + tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint]; + + if (Donor_Vect != nullptr) delete [] Donor_Vect; + if (Coeff_Vect != nullptr) delete [] Coeff_Vect; + if (storeProc != nullptr) delete [] storeProc; + + Donor_Vect = tmp_Donor_Vect; + Coeff_Vect = tmp_Coeff_Vect; + storeProc = tmp_storeProc; + + donor_OldiPoint = donor_iPoint; + donor_iPoint = donor_forward_point; + + nDonorPoints++; + } + + /*--- Set the communication data structure and copy data from the auxiliary vectors ---*/ + + target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(nDonorPoints); + + for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){ + target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor, Coeff_Vect[iDonor]); + target_geometry->vertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor, Donor_GlobalPoint[Donor_Vect[iDonor]]); + target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]); + } + } + } + + delete [] target_segment; + + delete [] target_iMidEdge_point; + delete [] target_jMidEdge_point; + + delete [] donor_iMidEdge_point; + delete [] donor_jMidEdge_point; + } + else{ + /* --- 3D geometry, creates a superficial super-mesh --- */ + + for (iVertex = 0; iVertex < nVertexTarget; iVertex++) { + + nDonorPoints = 0; + + /*--- Stores coordinates of the target node ---*/ + + target_iPoint = target_geometry->vertex[markTarget][iVertex]->GetNode(); + + if (!target_geometry->node[target_iPoint]->GetDomain()) continue; + + Coord_i = target_geometry->node[target_iPoint]->GetCoord(); + + target_geometry->vertex[markTarget][iVertex]->GetNormal(Normal); + + /*--- The value of Area computed here includes also portion of boundary belonging to different marker ---*/ + Area = 0.0; + for (iDim = 0; iDim < nDim; iDim++) + Area += Normal[iDim]*Normal[iDim]; + Area = sqrt(Area); + + for (iDim = 0; iDim < nDim; iDim++) + Normal[iDim] /= Area; + + for (iDim = 0; iDim < nDim; iDim++) + Coord_i[iDim] = target_geometry->node[target_iPoint]->GetCoord(iDim); + + long dPoint = target_geometry->node[target_iPoint]->GetGlobalIndex(); + for (target_iPoint = 0; target_iPoint < nGlobalVertex_Target; target_iPoint++){ + if( dPoint == Target_GlobalPoint[target_iPoint] ) + break; + } + + /*--- Build local surface dual mesh for target element ---*/ + + nEdges_target = Target_nLinkedNodes[target_iPoint]; + + nNode_target = 2*(nEdges_target + 1); + + target_element = new su2double*[nNode_target]; + for (ii = 0; ii < nNode_target; ii++) + target_element[ii] = new su2double[nDim]; + + nNode_target = Build_3D_surface_element(Target_LinkedNodes, Target_StartLinkedNodes, Target_nLinkedNodes, + TargetPoint_Coord, target_iPoint, target_element); + + /*--- Brute force to find the closest donor_node ---*/ + + mindist = 1E6; + donor_StartIndex = 0; + + for (donor_iPoint = 0; donor_iPoint < nGlobalVertex_Donor; donor_iPoint++) { + + Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ]; + + dist = PointsDistance(nDim, Coord_i, Coord_j); + + if (dist < mindist) { + mindist = dist; + donor_StartIndex = donor_iPoint; + } + + if (dist == 0.0){ + donor_StartIndex = donor_iPoint; + break; + } + } + + donor_iPoint = donor_StartIndex; + + nEdges_donor = Donor_nLinkedNodes[donor_iPoint]; + + donor_element = new su2double*[ 2*nEdges_donor + 2 ]; + for (ii = 0; ii < 2*nEdges_donor + 2; ii++) + donor_element[ii] = new su2double[nDim]; + + nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, + DonorPoint_Coord, donor_iPoint, donor_element); + + Area = 0; + for (ii = 1; ii < nNode_target-1; ii++){ + for (jj = 1; jj < nNode_donor-1; jj++){ + Area += Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], + donor_element[0], donor_element[jj], donor_element[jj+1], Normal); + } + } + + for (ii = 0; ii < 2*nEdges_donor + 2; ii++) + delete [] donor_element[ii]; + delete [] donor_element; + + nDonorPoints = 1; + + /*--- In case the element intersect the target cell update the auxiliary communication data structure ---*/ + + Coeff_Vect = new su2double[ nDonorPoints ]; + Donor_Vect = new unsigned long[ nDonorPoints ]; + storeProc = new unsigned long[ nDonorPoints ]; + + Coeff_Vect[0] = Area; + Donor_Vect[0] = donor_iPoint; + storeProc[0] = Donor_Proc[donor_iPoint]; + + alreadyVisitedDonor = new unsigned long[1]; + + alreadyVisitedDonor[0] = donor_iPoint; + nAlreadyVisited = 1; + StartVisited = 0; + + Area_old = -1; + + while( Area > Area_old ){ + + /* + * - Starting from the closest donor_point, it expands the supermesh by a countour search pattern. + * - The closest donor element becomes the core, at each iteration a new layer of elements around the core is taken into account + */ + + Area_old = Area; + + ToVisit = nullptr; + nToVisit = 0; + + for( iNodeVisited = StartVisited; iNodeVisited < nAlreadyVisited; iNodeVisited++ ){ + + vPoint = alreadyVisitedDonor[ iNodeVisited ]; + + nEdgeVisited = Donor_nLinkedNodes[vPoint]; + + for (iEdgeVisited = 0; iEdgeVisited < nEdgeVisited; iEdgeVisited++){ + + donor_iPoint = Donor_LinkedNodes[ Donor_StartLinkedNodes[vPoint] + iEdgeVisited]; + + /*--- Check if the node to visit is already listed in the data structure to avoid double visits ---*/ + + check = 0; + + for( jj = 0; jj < nAlreadyVisited; jj++ ){ + if( donor_iPoint == alreadyVisitedDonor[jj] ){ + check = 1; + break; + } + } + + if( check == 0 && ToVisit != nullptr){ + for( jj = 0; jj < nToVisit; jj++ ) + if( donor_iPoint == ToVisit[jj] ){ + check = 1; + break; + } + } + + if( check == 0 ){ + /*--- If the node was not already visited, visit it and list it into data structure ---*/ + + tmpVect = new unsigned long[ nToVisit + 1 ]; + + for( jj = 0; jj < nToVisit; jj++ ) + tmpVect[jj] = ToVisit[jj]; + tmpVect[nToVisit] = donor_iPoint; + + if( ToVisit != nullptr ) + delete [] ToVisit; + + ToVisit = tmpVect; + tmpVect = nullptr; + + nToVisit++; + + /*--- Find the value of the intersection area between the current donor element and the target element --- */ + + nEdges_donor = Donor_nLinkedNodes[donor_iPoint]; + + donor_element = new su2double*[ 2*nEdges_donor + 2 ]; + for (ii = 0; ii < 2*nEdges_donor + 2; ii++) + donor_element[ii] = new su2double[nDim]; + + nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, + DonorPoint_Coord, donor_iPoint, donor_element); + + tmp_Area = 0; + for (ii = 1; ii < nNode_target-1; ii++) + for (jj = 1; jj < nNode_donor-1; jj++) + tmp_Area += Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], + donor_element[0], donor_element[jj], donor_element[jj+1], Normal); + + for (ii = 0; ii < 2*nEdges_donor + 2; ii++) + delete [] donor_element[ii]; + delete [] donor_element; + + /*--- In case the element intersect the target cell update the auxiliary communication data structure ---*/ + + tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ]; + tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ]; + tmp_storeProc = new unsigned long[ nDonorPoints + 1 ]; + + for( iDonor = 0; iDonor < nDonorPoints; iDonor++){ + tmp_Donor_Vect[iDonor] = Donor_Vect[iDonor]; + tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor]; + tmp_storeProc[iDonor] = storeProc[iDonor]; + } + + tmp_Coeff_Vect[ nDonorPoints ] = tmp_Area; + tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint; + tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint]; + + if (Donor_Vect != nullptr) {delete [] Donor_Vect; } + if (Coeff_Vect != nullptr) {delete [] Coeff_Vect; } + if (storeProc != nullptr) {delete [] storeProc; } + + Donor_Vect = tmp_Donor_Vect; + Coeff_Vect = tmp_Coeff_Vect; + storeProc = tmp_storeProc; + + tmp_Coeff_Vect = nullptr; + tmp_Donor_Vect = nullptr; + tmp_storeProc = nullptr; + + nDonorPoints++; + + Area += tmp_Area; + } + } + } + + /*--- Update auxiliary data structure ---*/ + + StartVisited = nAlreadyVisited; + + tmpVect = new unsigned long[ nAlreadyVisited + nToVisit ]; + + for( jj = 0; jj < nAlreadyVisited; jj++ ) + tmpVect[jj] = alreadyVisitedDonor[jj]; + + for( jj = 0; jj < nToVisit; jj++ ) + tmpVect[ nAlreadyVisited + jj ] = ToVisit[jj]; + + if( alreadyVisitedDonor != nullptr ) + delete [] alreadyVisitedDonor; + + alreadyVisitedDonor = tmpVect; + + nAlreadyVisited += nToVisit; + + delete [] ToVisit; + } + + delete [] alreadyVisitedDonor; + + /*--- Set the communication data structure and copy data from the auxiliary vectors ---*/ + + target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(nDonorPoints); + + for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){ + target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor, Coeff_Vect[iDonor]/Area); + target_geometry->vertex[markTarget][iVertex]->SetInterpDonorPoint( iDonor, Donor_GlobalPoint[ Donor_Vect[iDonor] ] ); + target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]); + } + + for (ii = 0; ii < 2*nEdges_target + 2; ii++) + delete [] target_element[ii]; + delete [] target_element; + + delete [] Donor_Vect; Donor_Vect = nullptr; + delete [] Coeff_Vect; Coeff_Vect = nullptr; + delete [] storeProc; storeProc = nullptr; + } + } + + delete [] TargetPoint_Coord; + delete [] Target_GlobalPoint; + delete [] Target_Proc; + delete [] Target_nLinkedNodes; + delete [] Target_LinkedNodes; + delete [] Target_StartLinkedNodes; + + delete [] DonorPoint_Coord; + delete [] Donor_GlobalPoint; + delete [] Donor_Proc; + delete [] Donor_nLinkedNodes; + delete [] Donor_StartLinkedNodes; + delete [] Donor_LinkedNodes; + + } + + delete [] Normal; + delete [] Direction; + + if (Donor_Vect != nullptr) delete [] Donor_Vect; + if (Coeff_Vect != nullptr) delete [] Coeff_Vect; + if (storeProc != nullptr) delete [] storeProc; +} + +int CSlidingMesh::Build_3D_surface_element(const unsigned long *map, const unsigned long *startIndex, + const unsigned long* nNeighbor, const su2double *coord, + unsigned long centralNode, su2double** element) { + + /*--- Given a node "centralNode", this routines reconstruct the vertex centered + * surface element around the node and store it into "element" ---*/ + + unsigned long iNode, jNode, kNode, iElementNode, iPoint, jPoint, nOuterNodes; + + constexpr unsigned short nDim = 3; + unsigned short iDim, nTmp; + + int NextNode, **OuterNodesNeighbour, CurrentNode, StartIndex, count; + const unsigned long *OuterNodes, *ptr; + + /* --- Store central node as element first point --- */ + + for (iDim = 0; iDim < nDim; iDim++) + element[0][iDim] = coord[centralNode * nDim + iDim]; + + nOuterNodes = nNeighbor[centralNode]; + + OuterNodes = &map[ startIndex[centralNode] ]; + + /* --- Allocate auxiliary structure, vectors are longer than needed but this avoid further re-allocations due to length variation --- */ + + OuterNodesNeighbour = new int*[nOuterNodes]; + for ( iNode = 0; iNode < nOuterNodes; iNode++ ) + OuterNodesNeighbour[ iNode ] = new int[2]; + + /* --- Finds which and how many nodes belong to the specified marker, initialize some variables --- */ + + for ( iNode = 0; iNode < nOuterNodes; iNode++ ){ + OuterNodesNeighbour[ iNode ][0] = -1; + OuterNodesNeighbour[ iNode ][1] = -1; + } + + /* --- For each outer node, the program finds the two neighbouring outer nodes --- */ + + StartIndex = 0; + for( iNode = 0; iNode < nOuterNodes; iNode++ ){ + + count = 0; + iPoint = OuterNodes[ iNode ]; + ptr = &map[ startIndex[iPoint] ]; + nTmp = nNeighbor[iPoint]; + + for ( jNode = 0; jNode < nTmp; jNode++ ){ + jPoint = ptr[jNode]; + for( kNode = 0; kNode < nOuterNodes; kNode++ ){ + if ( jPoint == OuterNodes[ kNode ] && jPoint != centralNode){ + OuterNodesNeighbour[iNode][count] = (int)kNode; + count++; + break; + } + } + } + + // If the central node belongs to two different markers, ie at corners, makes this outer node the starting point for reconstructing the element + if( count == 1 ) + StartIndex = (int)iNode; + } + + /* --- Build element, starts from one outer node and loops along the external edges until the element is reconstructed --- */ + + CurrentNode = StartIndex; + NextNode = OuterNodesNeighbour[ CurrentNode ][0]; + iElementNode = 1; + + while( NextNode != -1 ){ + + for (iDim = 0; iDim < nDim; iDim++) + element[ iElementNode ][iDim] = ( element[0][iDim] + coord[ OuterNodes[ CurrentNode ] * nDim + iDim ])/2; + + iElementNode++; + + for (iDim = 0; iDim < nDim; iDim++) + element[ iElementNode ][iDim] = ( element[0][iDim] + coord[ OuterNodes[ CurrentNode ] * nDim + iDim] + + coord[ OuterNodes[ NextNode ] * nDim + iDim] )/3; + iElementNode++; + + if( OuterNodesNeighbour[ NextNode ][0] == CurrentNode){ + CurrentNode = NextNode; + NextNode = OuterNodesNeighbour[ NextNode ][1]; + } + else{ + CurrentNode = NextNode; + NextNode = OuterNodesNeighbour[ NextNode ][0]; + } + + if (CurrentNode == StartIndex) + break; + } + + if( CurrentNode == StartIndex ){ // This is a closed element, so add again element 1 to the end of the structure, useful later + + for (iDim = 0; iDim < nDim; iDim++) + element[ iElementNode ][iDim] = element[1][iDim]; + iElementNode++; + } + else{ + for (iDim = 0; iDim < nDim; iDim++) + element[ iElementNode ][iDim] = ( element[0][iDim] + coord[ OuterNodes[ CurrentNode ] * nDim + iDim] )/2; + iElementNode++; + } + + for ( iNode = 0; iNode < nOuterNodes; iNode++ ) + delete [] OuterNodesNeighbour[ iNode ]; + delete [] OuterNodesNeighbour; + + return (int)iElementNode; + +} + +su2double CSlidingMesh::ComputeLineIntersectionLength(unsigned short nDim, const su2double* A1, const su2double* A2, + const su2double* B1, const su2double* B2, const su2double* Direction) { + + /*--- Given 2 segments, each defined by 2 points, it projects them along a given direction + * and it computes the length of the segment resulting from their intersection ---*/ + /*--- The algorithm works for both 2D and 3D problems ---*/ + + unsigned short iDim; + + su2double dotA2, dotB1, dotB2; + + dotA2 = 0; + for(iDim = 0; iDim < nDim; iDim++) + dotA2 += ( A2[iDim] - A1[iDim] ) * Direction[iDim]; + + if( dotA2 >= 0 ){ + dotB1 = 0; + dotB2 = 0; + for(iDim = 0; iDim < nDim; iDim++){ + dotB1 += ( B1[iDim] - A1[iDim] ) * Direction[iDim]; + dotB2 += ( B2[iDim] - A1[iDim] ) * Direction[iDim]; + } + } + else{ + dotA2 *= -1; + + dotB1 = 0; + dotB2 = 0; + for(iDim = 0; iDim < nDim; iDim++){ + dotB1 -= ( B1[iDim] - A1[iDim] ) * Direction[iDim]; + dotB2 -= ( B2[iDim] - A1[iDim] ) * Direction[iDim]; + } + } + + if( dotB1 >= 0 && dotB1 <= dotA2 ){ + if ( dotB2 < 0 ) + return fabs( dotB1 ); + if ( dotB2 > dotA2 ) + return fabs( dotA2 - dotB1 ); + + return fabs( dotB1 - dotB2 ); + } + + if( dotB2 >= 0 && dotB2 <= dotA2 ){ + if ( dotB1 < 0 ) + return fabs(dotB2); + if ( dotB1 > dotA2 ) + return fabs( dotA2 - dotB2 ); + } + + if( ( dotB1 <= 0 && dotA2 <= dotB2 ) || ( dotB2 <= 0 && dotA2 <= dotB1 ) ) + return fabs( dotA2 ); + + return 0.0; +} + +su2double CSlidingMesh::Compute_Triangle_Intersection(const su2double* A1, const su2double* A2, const su2double* A3, + const su2double* B1, const su2double* B2, const su2double* B3, + const su2double* Direction) { + + /* --- This routine is ONLY for 3D grids --- */ + /* --- Projects triangle points onto a plane, specified by its normal "Direction", and calls the ComputeIntersectionArea routine --- */ + + unsigned short iDim; + constexpr unsigned short nDim = 3; + + su2double I[3], J[3], K[3]; + su2double a1[3], a2[3], a3[3]; + su2double b1[3], b2[3], b3[3]; + su2double m1, m2; + + /* --- Reference frame is determined by: x = A1A2 y = x ^ ( -Direction ) --- */ + + for(iDim = 0; iDim < 3; iDim++){ + a1[iDim] = 0; + a2[iDim] = 0; + a3[iDim] = 0; + + b1[iDim] = 0; + b2[iDim] = 0; + b3[iDim] = 0; + } + + m1 = 0; + for(iDim = 0; iDim < nDim; iDim++){ + K[iDim] = Direction[iDim]; + + m1 += K[iDim] * K[iDim]; + } + + for(iDim = 0; iDim < nDim; iDim++) + K[iDim] /= sqrt(m1); + + m2 = 0; + for(iDim = 0; iDim < nDim; iDim++) + m2 += (A2[iDim] - A1[iDim]) * K[iDim]; + + m1 = 0; + for(iDim = 0; iDim < nDim; iDim++){ + I[iDim] = (A2[iDim] - A1[iDim]) - m2 * K[iDim]; + m1 += I[iDim] * I[iDim]; + } + + for(iDim = 0; iDim < nDim; iDim++) + I[iDim] /= sqrt(m1); + + // Cross product to find Y + J[0] = K[1]*I[2] - K[2]*I[1]; + J[1] = -(K[0]*I[2] - K[2]*I[0]); + J[2] = K[0]*I[1] - K[1]*I[0]; + + /* --- Project all points on the plane specified by Direction and change their reference frame taking A1 as origin --- */ + + for(iDim = 0; iDim < nDim; iDim++){ + a2[0] += (A2[iDim] - A1[iDim]) * I[iDim]; + a2[1] += (A2[iDim] - A1[iDim]) * J[iDim]; + a2[2] += (A2[iDim] - A1[iDim]) * K[iDim]; + + a3[0] += (A3[iDim] - A1[iDim]) * I[iDim]; + a3[1] += (A3[iDim] - A1[iDim]) * J[iDim]; + a3[2] += (A3[iDim] - A1[iDim]) * K[iDim]; + + b1[0] += (B1[iDim] - A1[iDim]) * I[iDim]; + b1[1] += (B1[iDim] - A1[iDim]) * J[iDim]; + b1[2] += (B1[iDim] - A1[iDim]) * K[iDim]; + + b2[0] += (B2[iDim] - A1[iDim]) * I[iDim]; + b2[1] += (B2[iDim] - A1[iDim]) * J[iDim]; + b2[2] += (B2[iDim] - A1[iDim]) * K[iDim]; + + b3[0] += (B3[iDim] - A1[iDim]) * I[iDim]; + b3[1] += (B3[iDim] - A1[iDim]) * J[iDim]; + b3[2] += (B3[iDim] - A1[iDim]) * K[iDim]; + } + + /*--- Compute intersection area ---*/ + + return ComputeIntersectionArea( a1, a2, a3, b1, b2, b3 ); +} + +su2double CSlidingMesh::ComputeIntersectionArea(const su2double* P1, const su2double* P2, const su2double* P3, + const su2double* Q1, const su2double* Q2, const su2double* Q3) { + + /* --- This routines computes the area of the polygonal element generated by the superimposition of 2 planar triangle --- */ + /* --- The 2 triangle must lie on the same plane --- */ + + unsigned short iDim, nPoints = 0, i, j, k; + unsigned short min_theta_index; + + su2double points[16][2], IntersectionPoint[2], theta[6]; + su2double TriangleP[4][2], TriangleQ[4][2]; + su2double Area, det, dot1, dot2, dtmp, min_theta; + + constexpr unsigned short nDim = 2; + + for(iDim = 0; iDim < nDim; iDim++){ + TriangleP[0][iDim] = 0; + TriangleP[1][iDim] = P2[iDim] - P1[iDim]; + TriangleP[2][iDim] = P3[iDim] - P1[iDim]; + TriangleP[3][iDim] = 0; + + TriangleQ[0][iDim] = Q1[iDim] - P1[iDim]; + TriangleQ[1][iDim] = Q2[iDim] - P1[iDim]; + TriangleQ[2][iDim] = Q3[iDim] - P1[iDim]; + TriangleQ[3][iDim] = Q1[iDim] - P1[iDim]; + } + + + for( j = 0; j < 3; j++){ + if( CheckPointInsideTriangle(TriangleP[j], TriangleQ[0], TriangleQ[1], TriangleQ[2]) ){ + + // Then P1 is also inside triangle Q, so store it + for(iDim = 0; iDim < nDim; iDim++) + points[nPoints][iDim] = TriangleP[j][iDim]; + + nPoints++; + } + } + + for( j = 0; j < 3; j++){ + if( CheckPointInsideTriangle(TriangleQ[j], TriangleP[0], TriangleP[1], TriangleP[2]) ){ + + // Then Q1 is also inside triangle P, so store it + for(iDim = 0; iDim < nDim; iDim++) + points[nPoints][iDim] = TriangleQ[j][iDim]; + + nPoints++; + } + } + + + // Compute all edge intersections + + for( j = 0; j < 3; j++){ + for( i = 0; i < 3; i++){ + + det = (TriangleP[j][0] - TriangleP[j+1][0]) * (TriangleQ[i][1] - TriangleQ[i+1][1]) - + (TriangleP[j][1] - TriangleP[j+1][1]) * (TriangleQ[i][0] - TriangleQ[i+1][0]); + + if ( det != 0.0 ){ + ComputeLineIntersectionPoint( TriangleP[j], TriangleP[j+1], TriangleQ[i], TriangleQ[i+1], IntersectionPoint ); + + dot1 = 0; + dot2 = 0; + for(iDim = 0; iDim < nDim; iDim++){ + dot1 += ( TriangleP[j][iDim] - IntersectionPoint[iDim] ) * ( TriangleP[j+1][iDim] - IntersectionPoint[iDim] ); + dot2 += ( TriangleQ[i][iDim] - IntersectionPoint[iDim] ) * ( TriangleQ[i+1][iDim] - IntersectionPoint[iDim] ); + } + + if( dot1 <= 0 && dot2 <= 0 ){ // It found one intersection + + // Store temporarily the intersection point + + for(iDim = 0; iDim < nDim; iDim++) + points[nPoints][iDim] = IntersectionPoint[iDim]; + + nPoints++; + } + } + } + } + + // Remove double points, if any + + for( i = 0; i < nPoints; i++){ + for( j = i+1; j < nPoints; j++){ + if(points[j][0] == points[i][0] && points[j][1] == points[i][1]){ + for( k = j; k < nPoints-1; k++){ + points[k][0] = points[k+1][0]; + points[k][1] = points[k+1][1]; + } + nPoints--; + j--; + } + } + } + + // Re-order nodes + + for( i = 1; i < nPoints; i++){ // Change again reference frame + for(iDim = 0; iDim < nDim; iDim++) + points[i][iDim] -= points[0][iDim]; + + // Compute polar azimuth for each node but the first + theta[i] = atan2(points[i][1], points[i][0]); + } + + for(iDim = 0; iDim < nDim; iDim++) + points[0][iDim] = 0; + + for( i = 1; i < nPoints; i++){ + + min_theta = theta[i]; + min_theta_index = 0; + + for( j = i + 1; j < nPoints; j++){ + + if( theta[j] < min_theta ){ + min_theta = theta[j]; + min_theta_index = j; + } + } + + if( min_theta_index != 0 ){ + dtmp = theta[i]; + theta[i] = theta[min_theta_index]; + theta[min_theta_index] = dtmp; + + dtmp = points[i][0]; + points[i][0] = points[min_theta_index][0]; + points[min_theta_index][0] = dtmp; + + dtmp = points[i][1]; + points[i][1] = points[min_theta_index][1]; + points[min_theta_index][1] = dtmp; + } + } + + // compute area using cross product rule, points position are referred to the + // 2-dimensional, local, reference frame centered in points[0] + + Area = 0; + + if (nPoints > 2){ + for( i = 1; i < nPoints-1; i++ ){ + + // Ax*By + Area += ( points[i][0] - points[0][0] ) * ( points[i+1][1] - points[0][1] ); + + // Ay*Bx + Area -= ( points[i][1] - points[0][1] ) * ( points[i+1][0] - points[0][0] ); + } + } + + return fabs(Area)/2; +} + +void CSlidingMesh::ComputeLineIntersectionPoint(const su2double* A1, const su2double* A2, const su2double* B1, + const su2double* B2, su2double* IntersectionPoint ){ + + /* --- Uses determinant rule to compute the intersection point between 2 straight segments --- */ + /* This works only for lines on a 2D plane, A1, A2 and B1, B2 are respectively the head and the tail points of each segment, + * since they're on a 2D plane they are defined by a 2-elements array containing their coordinates */ + + su2double det; + + det = (A1[0] - A2[0]) * (B1[1] - B2[1]) - (A1[1] - A2[1]) * (B1[0] - B2[0]); + + if ( det != 0.0 ){ // else there is no intersection point + IntersectionPoint[0] = ( ( A1[0]*A2[1] - A1[1]*A2[0] ) * ( B1[0] - B2[0] ) - ( B1[0]*B2[1] - B1[1]*B2[0] ) * ( A1[0] - A2[0] ) ) / det; + IntersectionPoint[1] = ( ( A1[0]*A2[1] - A1[1]*A2[0] ) * ( B1[1] - B2[1] ) - ( B1[0]*B2[1] - B1[1]*B2[0] ) * ( A1[1] - A2[1] ) ) / det; + } +} + +bool CSlidingMesh::CheckPointInsideTriangle(const su2double* Point, const su2double* T1, const su2double* T2, const su2double* T3) { + + /* --- Check whether a point "Point" lies inside or outside a triangle defined by 3 points "T1", "T2", "T3" --- */ + /* For each edge it checks on which side the point lies: + * - Computes the unit vector pointing at the internal side of the edge + * - Comutes the vector that connects the point to a point along the edge + * - If the dot product is positive it means that the point is on the internal side of the edge + * - If the check is positive for all the 3 edges, then the point lies within the triangle + */ + + unsigned short iDim, check = 0; + + su2double vect1[2], vect2[2], r[2]; + su2double dot; + + constexpr unsigned short nDim = 2; + + /* --- Check first edge --- */ + + dot = 0; + for(iDim = 0; iDim < nDim; iDim++){ + vect1[iDim] = T3[iDim] - T1[iDim]; // vec 1 is aligned to the edge + vect2[iDim] = T2[iDim] - T1[iDim]; // vect 2 is the vector connecting one edge point to the third triangle vertex + + r[iDim] = Point[iDim] - T1[iDim]; // Connects point to vertex T1 + + dot += vect2[iDim] * vect2[iDim]; + } + dot = sqrt(dot); + + for(iDim = 0; iDim < nDim; iDim++) + vect2[iDim] /= dot; + + dot = 0; + for(iDim = 0; iDim < nDim; iDim++) + dot += vect1[iDim] * vect2[iDim]; + + for(iDim = 0; iDim < nDim; iDim++) + vect1[iDim] = T3[iDim] - (T1[iDim] + dot * vect2[iDim]); // Computes the inward unit vector + + dot = 0; + for(iDim = 0; iDim < nDim; iDim++) // Checs that the point lies on the internal plane + dot += vect1[iDim] * r[iDim]; + + if (dot >= 0) + check++; + + /* --- Check second edge --- */ + + dot = 0; + for(iDim = 0; iDim < nDim; iDim++){ + vect1[iDim] = T1[iDim] - T2[iDim]; + vect2[iDim] = T3[iDim] - T2[iDim]; + + r[iDim] = Point[iDim] - T2[iDim]; + + dot += vect2[iDim] * vect2[iDim]; + } + dot = sqrt(dot); + + for(iDim = 0; iDim < nDim; iDim++) + vect2[iDim] /= dot; + + dot = 0; + for(iDim = 0; iDim < nDim; iDim++) + dot += vect1[iDim] * vect2[iDim]; + + for(iDim = 0; iDim < nDim; iDim++) + vect1[iDim] = T1[iDim] - (T2[iDim] + dot * vect2[iDim]); + + dot = 0; + for(iDim = 0; iDim < nDim; iDim++) + dot += vect1[iDim] * r[iDim]; + + if (dot >= 0) + check++; + + /* --- Check third edge --- */ + + dot = 0; + for(iDim = 0; iDim < nDim; iDim++){ + vect1[iDim] = T2[iDim] - T3[iDim]; + vect2[iDim] = T1[iDim] - T3[iDim]; + + r[iDim] = Point[iDim] - T3[iDim]; + + dot += vect2[iDim] * vect2[iDim]; + } + dot = sqrt(dot); + + for(iDim = 0; iDim < nDim; iDim++) + vect2[iDim] /= dot; + + dot = 0; + for(iDim = 0; iDim < nDim; iDim++) + dot += vect1[iDim] * vect2[iDim]; + + for(iDim = 0; iDim < nDim; iDim++) + vect1[iDim] = T2[iDim] - (T3[iDim] + dot * vect2[iDim]); + + dot = 0; + for(iDim = 0; iDim < nDim; iDim++) + dot += vect1[iDim] * r[iDim]; + + if (dot >= 0) + check++; + + return (check == 3); +} diff --git a/Common/src/interface_interpolation/meson.build b/Common/src/interface_interpolation/meson.build new file mode 100644 index 000000000000..3e1d1a49b2e4 --- /dev/null +++ b/Common/src/interface_interpolation/meson.build @@ -0,0 +1,6 @@ +common_src += files(['CInterpolator.cpp', + 'CMirror.cpp', + 'CSlidingMesh.cpp', + 'CIsoparametric.cpp', + 'CNearestNeighbor.cpp', + 'CRadialBasisFunction.cpp']) diff --git a/Common/src/interpolation_structure.cpp b/Common/src/interpolation_structure.cpp deleted file mode 100644 index c0107c8b89ed..000000000000 --- a/Common/src/interpolation_structure.cpp +++ /dev/null @@ -1,3849 +0,0 @@ -/*! - * \file interpolation_structure.cpp - * \brief Main subroutines used by SU2_FSI - * \author H. Kline - * \version 7.0.2 "Blackbird" - * - * SU2 Project Website: https://su2code.github.io - * - * The SU2 Project is maintained by the SU2 Foundation - * (http://su2foundation.org) - * - * Copyright 2012-2020, 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/interpolation_structure.hpp" - -#if defined(HAVE_MKL) -#include "mkl.h" -#ifndef HAVE_LAPACK -#define HAVE_LAPACK -#endif -#elif defined(HAVE_LAPACK) -/*--- Lapack / Blas routines used in RBF interpolation. ---*/ -extern "C" void dsptrf_(char*, int*, passivedouble*, int*, int*); -extern "C" void dsptri_(char*, int*, passivedouble*, int*, passivedouble*, int*); -extern "C" void dsymm_(char*, char*, int*, int*, passivedouble*, passivedouble*, int*, - passivedouble*, int*, passivedouble*, passivedouble*, int*); -#endif - -CInterpolator::CInterpolator(void) { - - size = SU2_MPI::GetSize(); - rank = SU2_MPI::GetRank(); - - nZone = 0; - Geometry = NULL; - - donor_geometry = NULL; - target_geometry = NULL; - - donorZone = 0; - targetZone = 0; - - Buffer_Receive_nVertex_Donor = NULL; - Buffer_Receive_nFace_Donor = NULL; - Buffer_Receive_nFaceNodes_Donor = NULL; - Buffer_Send_nVertex_Donor = NULL; - Buffer_Send_nFace_Donor = NULL; - Buffer_Send_nFaceNodes_Donor = NULL; - Buffer_Receive_GlobalPoint = NULL; - Buffer_Send_GlobalPoint = NULL; - Buffer_Send_FaceIndex = NULL; - Buffer_Receive_FaceIndex = NULL; - Buffer_Send_FaceNodes = NULL; - Buffer_Receive_FaceNodes = NULL; - Buffer_Send_FaceProc = NULL; - Buffer_Receive_FaceProc = NULL; - - Buffer_Send_Coord = NULL; - Buffer_Send_Normal = NULL; - Buffer_Receive_Coord = NULL; - Buffer_Receive_Normal = NULL; - - Receive_GlobalPoint = NULL; - Buffer_Receive_nLinkedNodes = NULL; - Buffer_Receive_LinkedNodes = NULL; - Buffer_Receive_StartLinkedNodes = NULL; - Buffer_Receive_Proc = NULL; - -} - -CInterpolator::~CInterpolator(void) { - - //if (Buffer_Receive_nVertex_Donor!= NULL) delete[] Buffer_Receive_nVertex_Donor; -} - - -CInterpolator::CInterpolator(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) { - - size = SU2_MPI::GetSize(); - rank = SU2_MPI::GetRank(); - - /* Store pointers*/ - Geometry = geometry_container; - - donorZone = iZone; - targetZone = jZone; - - donor_geometry = geometry_container[donorZone][INST_0][MESH_0]; - target_geometry = geometry_container[targetZone][INST_0][MESH_0]; - - /*--- Initialize transfer coefficients between the zones ---*/ - /* Since this is a virtual function, call it in the child class constructor */ - //Set_TransferCoeff(targetZone,donorZone,config); - /*--- Initialize transfer coefficients between the zones ---*/ - //Set_TransferCoeff(Zones,config); - - //Buffer_Receive_nVertex_Donor = NULL; - -} - -inline void CInterpolator::Set_TransferCoeff(CConfig **config) { } - -void CInterpolator::Determine_ArraySize(bool faces, int markDonor, int markTarget, unsigned long nVertexDonor, unsigned short nDim) { - unsigned long nLocalVertex_Donor = 0, nLocalFaceNodes_Donor=0, nLocalFace_Donor=0; - unsigned long iVertex, iPointDonor = 0; - /* Only needed if face data is also collected */ - unsigned long inode; - unsigned long donor_elem, jElem, jPoint; - unsigned short iDonor; - unsigned int nFaces=0, iFace, nNodes=0; - bool face_on_marker = true; - - for (iVertex = 0; iVertex < nVertexDonor; iVertex++) { - iPointDonor = donor_geometry->vertex[markDonor][iVertex]->GetNode(); - if (donor_geometry->node[iPointDonor]->GetDomain()) { - nLocalVertex_Donor++; - if (faces) { - /*--- On Donor geometry also communicate face info ---*/ - if (nDim==3) { - for (jElem=0; jElemnode[iPointDonor]->GetnElem(); jElem++) { - donor_elem = donor_geometry->node[iPointDonor]->GetElem(jElem); - nFaces = donor_geometry->elem[donor_elem]->GetnFaces(); - for (iFace=0; iFaceelem[donor_elem]->GetnNodesFace(iFace); - for (iDonor=0; iDonorelem[donor_elem]->GetFaces(iFace, iDonor); - jPoint = donor_geometry->elem[donor_elem]->GetNode(inode); - face_on_marker = (face_on_marker && (donor_geometry->node[jPoint]->GetVertex(markDonor) !=-1)); - } - if (face_on_marker ) { - nLocalFace_Donor++; - nLocalFaceNodes_Donor+=nNodes; - } - } - } - } - else { - /*--- in 2D we use the edges ---*/ - nNodes=2; - nFaces = donor_geometry->node[iPointDonor]->GetnPoint(); - for (iFace=0; iFacenode[iPointDonor]->GetEdge(iFace); - jPoint = donor_geometry->edge[inode]->GetNode(iDonor); - face_on_marker = (face_on_marker && (donor_geometry->node[jPoint]->GetVertex(markDonor) !=-1)); - } - if (face_on_marker ) { - nLocalFace_Donor++; - nLocalFaceNodes_Donor+=nNodes; - } - } - } - } - } - } - - Buffer_Send_nVertex_Donor[0] = nLocalVertex_Donor; - if (faces) { - Buffer_Send_nFace_Donor[0] = nLocalFace_Donor; - Buffer_Send_nFaceNodes_Donor[0] = nLocalFaceNodes_Donor; - } - - /*--- Send Interface vertex information --*/ -#ifdef HAVE_MPI - SU2_MPI::Allreduce(&nLocalVertex_Donor, &MaxLocalVertex_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_nVertex_Donor, 1, MPI_UNSIGNED_LONG, Buffer_Receive_nVertex_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - if (faces) { - SU2_MPI::Allreduce(&nLocalFace_Donor, &nGlobalFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); - SU2_MPI::Allreduce(&nLocalFace_Donor, &MaxFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); - SU2_MPI::Allreduce(&nLocalFaceNodes_Donor, &nGlobalFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); - SU2_MPI::Allreduce(&nLocalFaceNodes_Donor, &MaxFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_nFace_Donor, 1, MPI_UNSIGNED_LONG, Buffer_Receive_nFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, Buffer_Receive_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - MaxFace_Donor++; - } -#else - MaxLocalVertex_Donor = nLocalVertex_Donor; - Buffer_Receive_nVertex_Donor[0] = Buffer_Send_nVertex_Donor[0]; - if (faces) { - nGlobalFace_Donor = nLocalFace_Donor; - nGlobalFaceNodes_Donor = nLocalFaceNodes_Donor; - MaxFaceNodes_Donor = nLocalFaceNodes_Donor; - MaxFace_Donor = nLocalFace_Donor+1; - Buffer_Receive_nFace_Donor[0] = Buffer_Send_nFace_Donor[0]; - Buffer_Receive_nFaceNodes_Donor[0] = Buffer_Send_nFaceNodes_Donor[0]; - } -#endif - -} - -void CInterpolator::Collect_VertexInfo(bool faces, int markDonor, int markTarget, unsigned long nVertexDonor, unsigned short nDim) -{ - unsigned long iVertex, iPointDonor = 0, iVertexDonor, nBuffer_Coord, nBuffer_Point, nLocalVertex_Donor; - unsigned short iDim; - - /* Only needed if face data is also collected */ - su2double *Normal; - - for (iVertex = 0; iVertex < MaxLocalVertex_Donor; iVertex++) { - Buffer_Send_GlobalPoint[iVertex] = -1; - for (iDim = 0; iDim < nDim; iDim++) { - Buffer_Send_Coord[iVertex*nDim+iDim] = 0.0; - if (faces) - Buffer_Send_Normal[iVertex*nDim+iDim] = 0.0; - } - } - - /*--- Copy coordinates and point to the auxiliar vector --*/ - nLocalVertex_Donor = 0; - - for (iVertexDonor = 0; iVertexDonor < nVertexDonor; iVertexDonor++) { - iPointDonor = donor_geometry->vertex[markDonor][iVertexDonor]->GetNode(); - if (donor_geometry->node[iPointDonor]->GetDomain()) { - Buffer_Send_GlobalPoint[nLocalVertex_Donor] = donor_geometry->node[iPointDonor]->GetGlobalIndex(); - for (iDim = 0; iDim < nDim; iDim++) - Buffer_Send_Coord[nLocalVertex_Donor*nDim+iDim] = donor_geometry->node[iPointDonor]->GetCoord(iDim); - - if (faces) { - Normal = donor_geometry->vertex[markDonor][iVertexDonor]->GetNormal(); - for (iDim = 0; iDim < nDim; iDim++) - Buffer_Send_Normal[nLocalVertex_Donor*nDim+iDim] = Normal[iDim]; - } - nLocalVertex_Donor++; - } - } - nBuffer_Coord = MaxLocalVertex_Donor*nDim; - nBuffer_Point = MaxLocalVertex_Donor; - -#ifdef HAVE_MPI - SU2_MPI::Allgather(Buffer_Send_Coord, nBuffer_Coord, MPI_DOUBLE, Buffer_Receive_Coord, nBuffer_Coord, MPI_DOUBLE, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_GlobalPoint, nBuffer_Point, MPI_LONG, Buffer_Receive_GlobalPoint, nBuffer_Point, MPI_LONG, MPI_COMM_WORLD); - if (faces) { - SU2_MPI::Allgather(Buffer_Send_Normal, nBuffer_Coord, MPI_DOUBLE, Buffer_Receive_Normal, nBuffer_Coord, MPI_DOUBLE, MPI_COMM_WORLD); - } -#else - for (iVertex = 0; iVertex < nBuffer_Coord; iVertex++) - Buffer_Receive_Coord[iVertex] = Buffer_Send_Coord[iVertex]; - - for (iVertex = 0; iVertex < nBuffer_Point; iVertex++) - Buffer_Receive_GlobalPoint[iVertex] = Buffer_Send_GlobalPoint[iVertex]; - - if (faces) { - for (iVertex = 0; iVertex < nBuffer_Coord; iVertex++) - Buffer_Receive_Normal[iVertex] = Buffer_Send_Normal[iVertex]; - } -#endif -} - -int CInterpolator::Find_InterfaceMarker(CConfig *config, unsigned short val_marker_interface) { - - unsigned short nMarker = config->GetnMarker_All(); - unsigned short iMarker; - - for (iMarker = 0; iMarker < nMarker; iMarker++) { - - /*--- If the tag GetMarker_All_ZoneInterface(iMarker) equals the index we are looping at ---*/ - if (config->GetMarker_All_ZoneInterface(iMarker) == val_marker_interface ) { - - /*--- We have identified the identifier for the interface marker ---*/ - return iMarker; - } - } - - return -1; -} - - -void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ - - CGeometry *geom = Geometry[val_zone][INST_0][MESH_0]; - - unsigned long iVertex, jVertex, kVertex; - - unsigned long count, iTmp, *uptr, dPoint, EdgeIndex, jEdge, nEdges, nNodes, nVertex, iDim, nDim, iPoint; - - unsigned long nGlobalLinkedNodes, nLocalVertex, nLocalLinkedNodes; - - nDim = geom->GetnDim(); - - if( val_marker != -1 ) - nVertex = geom->GetnVertex( val_marker ); - else - nVertex = 0; - - - su2double *Buffer_Send_Coord = new su2double [ nVertex * nDim ]; - unsigned long *Buffer_Send_GlobalPoint = new unsigned long [ nVertex ]; - - unsigned long *Buffer_Send_nLinkedNodes = new unsigned long [ nVertex ]; - unsigned long *Buffer_Send_StartLinkedNodes = new unsigned long [ nVertex ]; - unsigned long **Aux_Send_Map = new unsigned long*[ nVertex ]; - -#ifdef HAVE_MPI - int nProcessor = size, iRank; - unsigned long iTmp2, tmp_index, tmp_index_2; -#endif - - /*--- Copy coordinates and point to the auxiliar vector ---*/ - - nGlobalVertex = 0; - nLocalVertex = 0; - nLocalLinkedNodes = 0; - - for (iVertex = 0; iVertex < nVertex; iVertex++) { - - Buffer_Send_nLinkedNodes[iVertex] = 0; - Aux_Send_Map[iVertex] = NULL; - - iPoint = geom->vertex[val_marker][iVertex]->GetNode(); - - if (geom->node[iPoint]->GetDomain()) { - Buffer_Send_GlobalPoint[nLocalVertex] = geom->node[iPoint]->GetGlobalIndex(); - - for (iDim = 0; iDim < nDim; iDim++) - Buffer_Send_Coord[nLocalVertex*nDim+iDim] = geom->node[iPoint]->GetCoord(iDim); - - nNodes = 0; - nEdges = geom->node[iPoint]->GetnPoint(); - - for (jEdge = 0; jEdge < nEdges; jEdge++){ - EdgeIndex = geom->node[iPoint]->GetEdge(jEdge); - - if( iPoint == geom->edge[EdgeIndex]->GetNode(0) ) - dPoint = geom->edge[EdgeIndex]->GetNode(1); - else - dPoint = geom->edge[EdgeIndex]->GetNode(0); - - if ( geom->node[dPoint]->GetVertex(val_marker) != -1 ) - nNodes++; - } - - Buffer_Send_StartLinkedNodes[nLocalVertex] = nLocalLinkedNodes; - Buffer_Send_nLinkedNodes[nLocalVertex] = nNodes; - - nLocalLinkedNodes += nNodes; - - Aux_Send_Map[nLocalVertex] = new unsigned long[ nNodes ]; - nNodes = 0; - - for (jEdge = 0; jEdge < nEdges; jEdge++){ - EdgeIndex = geom->node[iPoint]->GetEdge(jEdge); - - if( iPoint == geom->edge[EdgeIndex]->GetNode(0) ) - dPoint = geom->edge[EdgeIndex]->GetNode(1); - else - dPoint = geom->edge[EdgeIndex]->GetNode(0); - - if ( geom->node[dPoint]->GetVertex(val_marker) != -1 ){ - Aux_Send_Map[nLocalVertex][nNodes] = geom->node[dPoint]->GetGlobalIndex(); - nNodes++; - } - } - nLocalVertex++; - } - } - - unsigned long *Buffer_Send_LinkedNodes = new unsigned long [ nLocalLinkedNodes ]; - - nLocalLinkedNodes = 0; - - for (iVertex = 0; iVertex < nLocalVertex; iVertex++){ - for (jEdge = 0; jEdge < Buffer_Send_nLinkedNodes[iVertex]; jEdge++){ - Buffer_Send_LinkedNodes[nLocalLinkedNodes] = Aux_Send_Map[iVertex][jEdge]; - nLocalLinkedNodes++; - } - } - - for (iVertex = 0; iVertex < nVertex; iVertex++){ - if( Aux_Send_Map[iVertex] != NULL ) - delete [] Aux_Send_Map[iVertex]; - } - delete [] Aux_Send_Map; Aux_Send_Map = NULL; - - /*--- Reconstruct boundary by gathering data from all ranks ---*/ - -#ifdef HAVE_MPI - SU2_MPI::Allreduce( &nLocalVertex, &nGlobalVertex, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); - SU2_MPI::Allreduce(&nLocalLinkedNodes, &nGlobalLinkedNodes, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); -#else - nGlobalVertex = nLocalVertex; - nGlobalLinkedNodes = nLocalLinkedNodes; -#endif - - Buffer_Receive_Coord = new su2double [ nGlobalVertex * nDim ]; - Buffer_Receive_GlobalPoint = new long[ nGlobalVertex ]; - Buffer_Receive_Proc = new unsigned long[ nGlobalVertex ]; - - Buffer_Receive_nLinkedNodes = new unsigned long[ nGlobalVertex ]; - Buffer_Receive_LinkedNodes = new unsigned long[ nGlobalLinkedNodes ]; - Buffer_Receive_StartLinkedNodes = new unsigned long[ nGlobalVertex ]; - -#ifdef HAVE_MPI - if (rank == MASTER_NODE){ - - for (iVertex = 0; iVertex < nDim*nLocalVertex; iVertex++) - Buffer_Receive_Coord[iVertex] = Buffer_Send_Coord[iVertex]; - - for (iVertex = 0; iVertex < nLocalVertex; iVertex++){ - Buffer_Receive_GlobalPoint[iVertex] = Buffer_Send_GlobalPoint[iVertex]; - Buffer_Receive_Proc[iVertex] = MASTER_NODE; - Buffer_Receive_nLinkedNodes[iVertex] = Buffer_Send_nLinkedNodes[iVertex]; - Buffer_Receive_StartLinkedNodes[iVertex] = Buffer_Send_StartLinkedNodes[iVertex]; - } - - for (iVertex = 0; iVertex < nLocalLinkedNodes; iVertex++) - Buffer_Receive_LinkedNodes[iVertex] = Buffer_Send_LinkedNodes[iVertex]; - - tmp_index = nLocalVertex; - tmp_index_2 = nLocalLinkedNodes; - - for(iRank = 1; iRank < nProcessor; iRank++){ - - SU2_MPI::Recv( &iTmp2, 1, MPI_UNSIGNED_LONG, iRank, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - SU2_MPI::Recv(&Buffer_Receive_LinkedNodes[tmp_index_2], iTmp2, MPI_UNSIGNED_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - - SU2_MPI::Recv( &iTmp, 1, MPI_UNSIGNED_LONG, iRank, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - SU2_MPI::Recv(&Buffer_Receive_Coord[tmp_index*nDim], nDim*iTmp, MPI_DOUBLE, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - - SU2_MPI::Recv( &Buffer_Receive_GlobalPoint[tmp_index], iTmp, MPI_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - SU2_MPI::Recv( &Buffer_Receive_nLinkedNodes[tmp_index], iTmp, MPI_UNSIGNED_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - SU2_MPI::Recv(&Buffer_Receive_StartLinkedNodes[tmp_index], iTmp, MPI_UNSIGNED_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - - for (iVertex = 0; iVertex < iTmp; iVertex++){ - Buffer_Receive_Proc[ tmp_index + iVertex ] = iRank; - Buffer_Receive_StartLinkedNodes[ tmp_index + iVertex ] += tmp_index_2; - } - - tmp_index += iTmp; - tmp_index_2 += iTmp2; - } - } - else{ - SU2_MPI::Send( &nLocalLinkedNodes, 1, MPI_UNSIGNED_LONG, 0, 0, MPI_COMM_WORLD); - SU2_MPI::Send(Buffer_Send_LinkedNodes, nLocalLinkedNodes, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD); - - SU2_MPI::Send( &nLocalVertex, 1, MPI_UNSIGNED_LONG, 0, 0, MPI_COMM_WORLD); - SU2_MPI::Send(Buffer_Send_Coord, nDim * nLocalVertex, MPI_DOUBLE, 0, 1, MPI_COMM_WORLD); - - SU2_MPI::Send( Buffer_Send_GlobalPoint, nLocalVertex, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD); - SU2_MPI::Send( Buffer_Send_nLinkedNodes, nLocalVertex, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD); - SU2_MPI::Send(Buffer_Send_StartLinkedNodes, nLocalVertex, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD); - } -#else - for (iVertex = 0; iVertex < nDim * nGlobalVertex; iVertex++) - Buffer_Receive_Coord[iVertex] = Buffer_Send_Coord[iVertex]; - - for (iVertex = 0; iVertex < nGlobalVertex; iVertex++){ - Buffer_Receive_GlobalPoint[iVertex] = Buffer_Send_GlobalPoint[iVertex]; - Buffer_Receive_Proc[iVertex] = MASTER_NODE; - Buffer_Receive_nLinkedNodes[iVertex] = Buffer_Send_nLinkedNodes[iVertex]; - Buffer_Receive_StartLinkedNodes[iVertex] = Buffer_Send_StartLinkedNodes[iVertex]; - } - - for (iVertex = 0; iVertex < nGlobalLinkedNodes; iVertex++) - Buffer_Receive_LinkedNodes[iVertex] = Buffer_Send_LinkedNodes[iVertex]; -#endif - - if (rank == MASTER_NODE){ - for (iVertex = 0; iVertex < nGlobalVertex; iVertex++){ - count = 0; - uptr = &Buffer_Receive_LinkedNodes[ Buffer_Receive_StartLinkedNodes[iVertex] ]; - - for (jVertex = 0; jVertex < Buffer_Receive_nLinkedNodes[iVertex]; jVertex++){ - iTmp = uptr[ jVertex ]; - for (kVertex = 0; kVertex < nGlobalVertex; kVertex++){ - if( Buffer_Receive_GlobalPoint[kVertex] == long(iTmp) ){ - uptr[ jVertex ] = kVertex; - count++; - break; - } - } - - if( count != (jVertex+1) ){ - for (kVertex = jVertex; kVertex < Buffer_Receive_nLinkedNodes[iVertex]-1; kVertex++){ - uptr[ kVertex ] = uptr[ kVertex + 1]; - } - Buffer_Receive_nLinkedNodes[iVertex]--; - jVertex--; - } - } - } - } - -#ifdef HAVE_MPI - SU2_MPI::Bcast( Buffer_Receive_Coord, nGlobalVertex * nDim, MPI_DOUBLE, 0, MPI_COMM_WORLD); - SU2_MPI::Bcast(Buffer_Receive_GlobalPoint, nGlobalVertex, MPI_LONG, 0, MPI_COMM_WORLD); - SU2_MPI::Bcast( Buffer_Receive_Proc, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD ); - - SU2_MPI::Bcast( Buffer_Receive_nLinkedNodes, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); - SU2_MPI::Bcast(Buffer_Receive_StartLinkedNodes, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); - SU2_MPI::Bcast( Buffer_Receive_LinkedNodes, nGlobalLinkedNodes, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); -#endif - - if( Buffer_Send_Coord != NULL) {delete [] Buffer_Send_Coord; Buffer_Send_Coord = NULL;} - if( Buffer_Send_GlobalPoint != NULL) {delete [] Buffer_Send_GlobalPoint; Buffer_Send_GlobalPoint = NULL;} - if( Buffer_Send_LinkedNodes != NULL) {delete [] Buffer_Send_LinkedNodes; Buffer_Send_LinkedNodes = NULL;} - if( Buffer_Send_nLinkedNodes != NULL) {delete [] Buffer_Send_nLinkedNodes; Buffer_Send_nLinkedNodes = NULL;} - if( Buffer_Send_StartLinkedNodes != NULL) {delete [] Buffer_Send_StartLinkedNodes; Buffer_Send_StartLinkedNodes = NULL;} -} - -bool CInterpolator::CheckInterfaceBoundary(int markDonor, int markTarget){ - - int Donor_check, Target_check; - - #ifdef HAVE_MPI - - int *Buffer_Recv_mark = NULL; - int iRank, nProcessor = size; - - if (rank == MASTER_NODE) - Buffer_Recv_mark = new int[nProcessor]; - - Donor_check = -1; - Target_check = -1; - - /*--- We gather a vector in MASTER_NODE to determine whether the boundary is not on the processor because of the partition or because the zone does not include it ---*/ - - SU2_MPI::Gather(&markDonor , 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - - if (rank == MASTER_NODE) - for (iRank = 0; iRank < nProcessor; iRank++) - if( Buffer_Recv_mark[iRank] != -1 ){ - Donor_check = Buffer_Recv_mark[iRank]; - break; - } - - SU2_MPI::Bcast(&Donor_check , 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - - - SU2_MPI::Gather(&markTarget, 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - - if (rank == MASTER_NODE) - for (iRank = 0; iRank < nProcessor; iRank++) - if( Buffer_Recv_mark[iRank] != -1 ){ - Target_check = Buffer_Recv_mark[iRank]; - break; - } - - - SU2_MPI::Bcast(&Target_check, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - - if (rank == MASTER_NODE) - delete [] Buffer_Recv_mark; - -#else - Donor_check = markDonor; - Target_check = markTarget; -#endif - - if(Target_check == -1 || Donor_check == -1) - return false; - else - return true; -} - -su2double CInterpolator::PointsDistance(su2double *point_i, su2double *point_j){ - - /*--- Compute distance between 2 points ---*/ - - unsigned short iDim, nDim = donor_geometry->GetnDim(); - su2double m; - - m = 0 ; - for(iDim = 0; iDim < nDim; iDim++) - m += (point_j[iDim] - point_i[iDim])*(point_j[iDim] - point_i[iDim]); - - return sqrt(m); -} - -/* Nearest Neighbor Interpolator */ -CNearestNeighbor::CNearestNeighbor(void): CInterpolator() { } - -CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { - - /*--- Initialize transfer coefficients between the zones ---*/ - Set_TransferCoeff(config); -} - -CNearestNeighbor::~CNearestNeighbor() {} - -void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { - - int iProcessor, pProcessor, nProcessor = size; - int markDonor, markTarget; - - unsigned short nDim, iMarkerInt, nMarkerInt, iDonor; - - unsigned long nVertexDonor, nVertexTarget, Point_Target, jVertex, iVertexTarget; - unsigned long Global_Point_Donor; - long pGlobalPoint = 0; - - su2double *Coord_i, *Coord_j, dist, mindist, maxdist; - - /*--- Initialize variables --- */ - - nMarkerInt = (int) ( config[donorZone]->GetMarker_n_ZoneInterface() / 2 ); - - nDim = donor_geometry->GetnDim(); - - iDonor = 0; - - Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; - - - /*--- Cycle over nMarkersInt interface to determine communication pattern ---*/ - - for (iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { - - - /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ - markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); - - /*--- On the target side: find the tag of the boundary sharing the interface ---*/ - markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); - - /*--- Checks if the zone contains the interface, if not continue to the next step ---*/ - if( !CheckInterfaceBoundary(markDonor, markTarget) ) - continue; - - if(markDonor != -1) - nVertexDonor = donor_geometry->GetnVertex( markDonor ); - else - nVertexDonor = 0; - - if(markTarget != -1) - nVertexTarget = target_geometry->GetnVertex( markTarget ); - else - nVertexTarget = 0; - - Buffer_Send_nVertex_Donor = new unsigned long [ 1 ]; - - /* Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor */ - Determine_ArraySize(false, markDonor, markTarget, nVertexDonor, nDim); - - Buffer_Send_Coord = new su2double [ MaxLocalVertex_Donor * nDim ]; - Buffer_Send_GlobalPoint = new long [ MaxLocalVertex_Donor ]; - Buffer_Receive_Coord = new su2double [ nProcessor * MaxLocalVertex_Donor * nDim ]; - Buffer_Receive_GlobalPoint = new long [ nProcessor * MaxLocalVertex_Donor ]; - - /*-- Collect coordinates, global points, and normal vectors ---*/ - Collect_VertexInfo( false, markDonor, markTarget, nVertexDonor, nDim ); - - /*--- Compute the closest point to a Near-Field boundary point ---*/ - maxdist = 0.0; - - for (iVertexTarget = 0; iVertexTarget < nVertexTarget; iVertexTarget++) { - - Point_Target = target_geometry->vertex[markTarget][iVertexTarget]->GetNode(); - - if ( target_geometry->node[Point_Target]->GetDomain() ) { - - target_geometry->vertex[markTarget][iVertexTarget]->SetnDonorPoints(1); - target_geometry->vertex[markTarget][iVertexTarget]->Allocate_DonorInfo(); // Possible meme leak? - - /*--- Coordinates of the boundary point ---*/ - Coord_i = target_geometry->node[Point_Target]->GetCoord(); - - mindist = 1E6; - pProcessor = 0; - - /*--- Loop over all the boundaries to find the pair ---*/ - - for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { - for (jVertex = 0; jVertex < MaxLocalVertex_Donor; jVertex++) { - - Global_Point_Donor = iProcessor*MaxLocalVertex_Donor+jVertex; - - if (Buffer_Receive_GlobalPoint[Global_Point_Donor] != -1){ - - Coord_j = &Buffer_Receive_Coord[ Global_Point_Donor*nDim]; - - dist = PointsDistance(Coord_i, Coord_j); - - if (dist < mindist) { - mindist = dist; pProcessor = iProcessor; - pGlobalPoint = Buffer_Receive_GlobalPoint[Global_Point_Donor]; - } - - if (dist == 0.0) break; - } - } - } - - /*--- Store the value of the pair ---*/ - maxdist = max(maxdist, mindist); - target_geometry->vertex[markTarget][iVertexTarget]->SetInterpDonorPoint(iDonor, pGlobalPoint); - target_geometry->vertex[markTarget][iVertexTarget]->SetInterpDonorProcessor(iDonor, pProcessor); - target_geometry->vertex[markTarget][iVertexTarget]->SetDonorCoeff(iDonor, 1.0); - } - } - - delete[] Buffer_Send_Coord; - delete[] Buffer_Send_GlobalPoint; - - delete[] Buffer_Receive_Coord; - delete[] Buffer_Receive_GlobalPoint; - - delete[] Buffer_Send_nVertex_Donor; - - } - - delete[] Buffer_Receive_nVertex_Donor; -} - - - -CIsoparametric::CIsoparametric(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { - - /*--- Initialize transfer coefficients between the zones ---*/ - Set_TransferCoeff(config); - - /*--- For fluid-structure interaction data interpolated with have nDim dimensions ---*/ - // InitializeData(Zones,nDim); -} - -CIsoparametric::~CIsoparametric() {} - -void CIsoparametric::Set_TransferCoeff(CConfig **config) { - unsigned long iVertex, jVertex; - unsigned long dPoint, inode, jElem, nElem; - unsigned short iDim, iDonor=0, iFace; - - unsigned short nDim = donor_geometry->GetnDim(); - - unsigned short nMarkerInt; - unsigned short iMarkerInt; - - int markDonor=0, markTarget=0; - - long donor_elem=0, temp_donor=0; - unsigned int nNodes=0; - /*--- Restricted to 2-zone for now ---*/ - unsigned int nFaces=1; //For 2D cases, we want to look at edges, not faces, as the 'interface' - bool face_on_marker=true; - - unsigned long nVertexDonor = 0, nVertexTarget= 0; - unsigned long Point_Target = 0; - - unsigned long iVertexDonor, iPointDonor = 0; - int iProcessor; - - unsigned long nLocalFace_Donor = 0, nLocalFaceNodes_Donor=0; - - unsigned long faceindex; - - su2double dist = 0.0, mindist=1E6, *Coord, *Coord_i; - su2double myCoeff[10]; // Maximum # of donor points - su2double *Normal; - su2double *projected_point = new su2double[nDim]; - su2double tmp, tmp2; - su2double storeCoeff[10]; - unsigned long storeGlobal[10]; - int storeProc[10]; - - int nProcessor = size; - Coord = new su2double[nDim]; - Normal = new su2double[nDim]; - - nMarkerInt = (config[donorZone]->GetMarker_n_ZoneInterface())/2; - - /*--- For the number of markers on the interface... ---*/ - for (iMarkerInt=1; iMarkerInt <= nMarkerInt; iMarkerInt++) { - /*--- Procedure: - * -Loop through vertices of the aero grid - * -Find nearest element and allocate enough space in the aero grid donor point info - * -set the transfer coefficient values - */ - - /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ - markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); - - /*--- On the target side: find the tag of the boundary sharing the interface ---*/ - markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); - - /*--- Checks if the zone contains the interface, if not continue to the next step ---*/ - if( !CheckInterfaceBoundary(markDonor, markTarget) ) - continue; - - if(markDonor != -1) - nVertexDonor = donor_geometry->GetnVertex( markDonor ); - else - nVertexDonor = 0; - - if(markTarget != -1) - nVertexTarget = target_geometry->GetnVertex( markTarget ); - else - nVertexTarget = 0; - - Buffer_Send_nVertex_Donor = new unsigned long [1]; - Buffer_Send_nFace_Donor = new unsigned long [1]; - Buffer_Send_nFaceNodes_Donor = new unsigned long [1]; - - Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; - Buffer_Receive_nFace_Donor = new unsigned long [nProcessor]; - Buffer_Receive_nFaceNodes_Donor = new unsigned long [nProcessor]; - - /* Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor */ - Determine_ArraySize(true, markDonor, markTarget, nVertexDonor, nDim); - - Buffer_Send_Coord = new su2double [MaxLocalVertex_Donor*nDim]; - Buffer_Send_Normal = new su2double [MaxLocalVertex_Donor*nDim]; - Buffer_Send_GlobalPoint = new long [MaxLocalVertex_Donor]; - - Buffer_Receive_Coord = new su2double [nProcessor*MaxLocalVertex_Donor*nDim]; - Buffer_Receive_Normal = new su2double [nProcessor*MaxLocalVertex_Donor*nDim]; - Buffer_Receive_GlobalPoint = new long [nProcessor*MaxLocalVertex_Donor]; - - /*-- Collect coordinates, global points, and normal vectors ---*/ - Collect_VertexInfo(true, markDonor,markTarget,nVertexDonor,nDim); - - Buffer_Send_FaceIndex = new unsigned long[MaxFace_Donor]; - Buffer_Send_FaceNodes = new unsigned long[MaxFaceNodes_Donor]; - Buffer_Send_FaceProc = new unsigned long[MaxFaceNodes_Donor]; - - Buffer_Receive_FaceIndex = new unsigned long[MaxFace_Donor*nProcessor]; - Buffer_Receive_FaceNodes = new unsigned long[MaxFaceNodes_Donor*nProcessor]; - Buffer_Receive_FaceProc = new unsigned long[MaxFaceNodes_Donor*nProcessor]; - - nLocalFace_Donor=0; - nLocalFaceNodes_Donor=0; - - /*--- Collect Face info ---*/ - - for (iVertex = 0; iVertex < MaxFace_Donor; iVertex++) { - Buffer_Send_FaceIndex[iVertex] = 0; - } - for (iVertex=0; iVertexvertex[markDonor][iVertexDonor]->GetNode(); - - if (donor_geometry->node[iPointDonor]->GetDomain()) { - - if (nDim==3) nElem = donor_geometry->node[iPointDonor]->GetnElem(); - else nElem =donor_geometry->node[iPointDonor]->GetnPoint(); - - for (jElem=0; jElem < nElem; jElem++) { - if (nDim==3) { - temp_donor = donor_geometry->node[iPointDonor]->GetElem(jElem); - nFaces = donor_geometry->elem[temp_donor]->GetnFaces(); - for (iFace=0; iFaceelem[temp_donor]->GetnNodesFace(iFace); - for (iDonor=0; iDonorelem[temp_donor]->GetFaces(iFace, iDonor); - dPoint = donor_geometry->elem[temp_donor]->GetNode(inode); - face_on_marker = (face_on_marker && (donor_geometry->node[dPoint]->GetVertex(markDonor) !=-1)); - } - - if (face_on_marker ) { - for (iDonor=0; iDonorelem[temp_donor]->GetFaces(iFace, iDonor); - dPoint = donor_geometry->elem[temp_donor]->GetNode(inode); - // Match node on the face to the correct global index - long jGlobalPoint = donor_geometry->node[dPoint]->GetGlobalIndex(); - for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { - for (jVertex = 0; jVertex < Buffer_Receive_nVertex_Donor[iProcessor]; jVertex++) { - if (jGlobalPoint == Buffer_Receive_GlobalPoint[MaxLocalVertex_Donor*iProcessor+jVertex]) { - Buffer_Send_FaceNodes[nLocalFaceNodes_Donor]=MaxLocalVertex_Donor*iProcessor+jVertex; - Buffer_Send_FaceProc[nLocalFaceNodes_Donor]=iProcessor; - } - } - } - nLocalFaceNodes_Donor++; // Increment total number of face-nodes / processor - } - /* Store the indices */ - Buffer_Send_FaceIndex[nLocalFace_Donor+1] = Buffer_Send_FaceIndex[nLocalFace_Donor]+nNodes; - nLocalFace_Donor++; // Increment number of faces / processor - } - } - } - else { - /*-- Determine whether this face/edge is on the marker --*/ - face_on_marker=true; - for (iDonor=0; iDonornode[iPointDonor]->GetEdge(jElem); - dPoint = donor_geometry->edge[inode]->GetNode(iDonor); - face_on_marker = (face_on_marker && (donor_geometry->node[dPoint]->GetVertex(markDonor) !=-1)); - } - if (face_on_marker ) { - for (iDonor=0; iDonornode[iPointDonor]->GetEdge(jElem); - dPoint = donor_geometry->edge[inode]->GetNode(iDonor); - // Match node on the face to the correct global index - long jGlobalPoint = donor_geometry->node[dPoint]->GetGlobalIndex(); - for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { - for (jVertex = 0; jVertex < Buffer_Receive_nVertex_Donor[iProcessor]; jVertex++) { - if (jGlobalPoint == Buffer_Receive_GlobalPoint[MaxLocalVertex_Donor*iProcessor+jVertex]) { - Buffer_Send_FaceNodes[nLocalFaceNodes_Donor]=MaxLocalVertex_Donor*iProcessor+jVertex; - Buffer_Send_FaceProc[nLocalFaceNodes_Donor]=iProcessor; - } - } - } - nLocalFaceNodes_Donor++; // Increment total number of face-nodes / processor - } - /* Store the indices */ - Buffer_Send_FaceIndex[nLocalFace_Donor+1] = Buffer_Send_FaceIndex[nLocalFace_Donor]+nNodes; - nLocalFace_Donor++; // Increment number of faces / processor - } - } - } - } - } - - //Buffer_Send_FaceIndex[nLocalFace_Donor+1] = MaxFaceNodes_Donor*rank+nLocalFaceNodes_Donor; -#ifdef HAVE_MPI - SU2_MPI::Allgather(Buffer_Send_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, Buffer_Receive_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_FaceProc, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, Buffer_Receive_FaceProc, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, Buffer_Receive_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); -#else - for (iFace=0; iFacevertex[markTarget][iVertex]->GetNode(); - - if (target_geometry->node[Point_Target]->GetDomain()) { - - Coord_i = target_geometry->node[Point_Target]->GetCoord(); - /*---Loop over the faces previously communicated/stored ---*/ - for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { - - nFaces = (unsigned int)Buffer_Receive_nFace_Donor[iProcessor]; - - for (iFace = 0; iFace< nFaces; iFace++) { - /*--- ---*/ - - nNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1] - - (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; - - su2double *X = new su2double[nNodes*(nDim+1)]; - faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face - for (iDonor=0; iDonorvertex[markTarget][iVertex]->SetDonorElem(donor_elem); // in 2D is nearest neighbor - target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nNodes); - for (iDonor=0; iDonorvertex[markTarget][iVertex]->GetnDonorPoints(); - target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); - - for (iDonor=0; iDonorvertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor,storeGlobal[iDonor]); - //cout <vertex[markTarget][iVertex]->SetDonorCoeff(iDonor,storeCoeff[iDonor]); - target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]); - } - } - } - - delete[] Buffer_Send_nVertex_Donor; - delete[] Buffer_Send_nFace_Donor; - delete[] Buffer_Send_nFaceNodes_Donor; - - delete[] Buffer_Receive_nVertex_Donor; - delete[] Buffer_Receive_nFace_Donor; - delete[] Buffer_Receive_nFaceNodes_Donor; - - delete[] Buffer_Send_Coord; - delete[] Buffer_Send_Normal; - delete[] Buffer_Send_GlobalPoint; - - delete[] Buffer_Receive_Coord; - delete[] Buffer_Receive_Normal; - delete[] Buffer_Receive_GlobalPoint; - - delete[] Buffer_Send_FaceIndex; - delete[] Buffer_Send_FaceNodes; - delete[] Buffer_Send_FaceProc; - - delete[] Buffer_Receive_FaceIndex; - delete[] Buffer_Receive_FaceNodes; - delete[] Buffer_Receive_FaceProc; - } - delete [] Coord; - delete [] Normal; - - delete [] projected_point; -} - -void CIsoparametric::Isoparameters(unsigned short nDim, unsigned short nDonor, - su2double *X, su2double *xj, su2double *isoparams) { - short iDonor,iDim,k; // indices - su2double tmp, tmp2; - - su2double *x = new su2double[nDim+1]; - su2double *x_tmp = new su2double[nDim+1]; - su2double *Q = new su2double[nDonor*nDonor]; - su2double *R = new su2double[nDonor*nDonor]; - su2double *A = new su2double[(nDim+2)*nDonor]; - su2double *A2 = NULL; - su2double *x2 = new su2double[nDim+1]; - - bool *test = new bool[nDim+1]; - bool *testi = new bool[nDim+1]; - - su2double eps = 1E-10; - - short n = nDim+1; - - if (nDonor>2) { - /*--- Create Matrix A: 1st row all 1's, 2nd row x coordinates, 3rd row y coordinates, etc ---*/ - /*--- Right hand side is [1, \vec{x}']'---*/ - for (iDonor=0; iDonoreps && iDonor=0; iDonor--) { - if (R[iDonor*nDonor+iDonor]>eps) - isoparams[iDonor]=x_tmp[iDonor]/R[iDonor*nDonor+iDonor]; - else - isoparams[iDonor]=0; - for (k=0; k1.0) xi=1.0; - if (xi<-1.0) xi=-1.0; - if (eta>1.0) eta=1.0; - if (eta<-1.0) eta=-1.0; - isoparams[0]=0.25*(1-xi)*(1-eta); - isoparams[1]=0.25*(1+xi)*(1-eta); - isoparams[2]=0.25*(1+xi)*(1+eta); - isoparams[3]=0.25*(1-xi)*(1+eta); - - } - if (nDonor<4) { - tmp = 0.0; // value for normalization - tmp2=0; // check for maximum value, to be used to id nearest neighbor if necessary - k=0; // index for maximum value - for (iDonor=0; iDonor< nDonor; iDonor++) { - if (isoparams[iDonor]>tmp2) { - k=iDonor; - tmp2=isoparams[iDonor]; - } - // [0,1] - if (isoparams[iDonor]<0) isoparams[iDonor]=0; - if (isoparams[iDonor]>1) isoparams[iDonor] = 1; - tmp +=isoparams[iDonor]; - } - if (tmp>0) - for (iDonor=0; iDonor< nDonor; iDonor++) - isoparams[iDonor]=isoparams[iDonor]/tmp; - else { - isoparams[k] = 1.0; - } - } - - delete [] x; - delete [] x_tmp; - delete [] Q; - delete [] R; - delete [] A; - if (A2 != NULL) delete [] A2; - delete [] x2; - - delete [] test; - delete [] testi; - -} - - -/* Mirror Interpolator */ -CMirror::CMirror(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { - - /*--- Initialize transfer coefficients between the zones ---*/ - Set_TransferCoeff(config); - -} - -CMirror::~CMirror() {} - -void CMirror::Set_TransferCoeff(CConfig **config) { - unsigned long iVertex, jVertex; - unsigned long iPoint; - unsigned short iDonor=0, iFace=0, iTarget=0; - - unsigned short nMarkerInt; - unsigned short iMarkerInt; - - int markDonor=0, markTarget=0; - - unsigned int nNodes=0, iNodes=0; - unsigned long nVertexDonor = 0, nVertexTarget= 0; - unsigned long Point_Donor = 0; - unsigned long pGlobalPoint = 0; - int iProcessor; - - unsigned long nLocalFace_Donor = 0, nLocalFaceNodes_Donor=0; - - unsigned long faceindex; - - int nProcessor = size; - - su2double *Buffer_Send_Coeff, *Buffer_Receive_Coeff; - su2double coeff; - - /*--- Number of markers on the interface ---*/ - nMarkerInt = (config[targetZone]->GetMarker_n_ZoneInterface())/2; - - /*--- For the number of markers on the interface... ---*/ - for (iMarkerInt=1; iMarkerInt <= nMarkerInt; iMarkerInt++) { - /*--- Procedure: - * -Loop through vertices of the aero grid - * -Find nearest element and allocate enough space in the aero grid donor point info - * -set the transfer coefficient values - */ - - /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ - markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); - - /*--- On the target side: find the tag of the boundary sharing the interface ---*/ - markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); - - /*--- Checks if the zone contains the interface, if not continue to the next step ---*/ - if( !CheckInterfaceBoundary(markDonor, markTarget) ) - continue; - - if(markDonor != -1) - nVertexDonor = donor_geometry->GetnVertex( markDonor ); - else - nVertexDonor = 0; - - if(markTarget != -1) - nVertexTarget = target_geometry->GetnVertex( markTarget ); - else - nVertexTarget = 0; - - /*-- Collect the number of donor nodes: re-use 'Face' containers --*/ - nLocalFace_Donor=0; - nLocalFaceNodes_Donor=0; - for (jVertex = 0; jVertexvertex[markDonor][jVertex]->GetNode(); // Local index of jVertex - - if (donor_geometry->node[Point_Donor]->GetDomain()) { - nNodes = donor_geometry->vertex[markDonor][jVertex]->GetnDonorPoints(); - nLocalFaceNodes_Donor+=nNodes; - nLocalFace_Donor++; - } - } - Buffer_Send_nFace_Donor= new unsigned long [1]; - Buffer_Send_nFaceNodes_Donor= new unsigned long [1]; - - Buffer_Receive_nFace_Donor = new unsigned long [nProcessor]; - Buffer_Receive_nFaceNodes_Donor = new unsigned long [nProcessor]; - - Buffer_Send_nFace_Donor[0] = nLocalFace_Donor; - Buffer_Send_nFaceNodes_Donor[0] = nLocalFaceNodes_Donor; - - /*--- Send Interface vertex information --*/ -#ifdef HAVE_MPI - SU2_MPI::Allreduce(&nLocalFaceNodes_Donor, &MaxFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); - SU2_MPI::Allreduce(&nLocalFace_Donor, &MaxFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_nFace_Donor, 1, MPI_UNSIGNED_LONG, Buffer_Receive_nFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, Buffer_Receive_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - MaxFace_Donor++; -#else - nGlobalFace_Donor = nLocalFace_Donor; - nGlobalFaceNodes_Donor = nLocalFaceNodes_Donor; - MaxFaceNodes_Donor = nLocalFaceNodes_Donor; - MaxFace_Donor = nLocalFace_Donor+1; - Buffer_Receive_nFace_Donor[0] = Buffer_Send_nFace_Donor[0]; - Buffer_Receive_nFaceNodes_Donor[0] = Buffer_Send_nFaceNodes_Donor[0]; -#endif - - /*-- Send donor info --*/ - Buffer_Send_FaceIndex = new unsigned long[MaxFace_Donor]; - Buffer_Send_FaceNodes = new unsigned long[MaxFaceNodes_Donor]; - Buffer_Send_GlobalPoint = new long[MaxFaceNodes_Donor]; - Buffer_Send_Coeff = new su2double[MaxFaceNodes_Donor]; - - Buffer_Receive_FaceIndex= new unsigned long[MaxFace_Donor*nProcessor]; - Buffer_Receive_FaceNodes= new unsigned long[MaxFaceNodes_Donor*nProcessor]; - Buffer_Receive_GlobalPoint = new long[MaxFaceNodes_Donor*nProcessor]; - Buffer_Receive_Coeff = new su2double[MaxFaceNodes_Donor*nProcessor]; - - for (iVertex=0; iVertexvertex[markDonor][jVertex]->GetNode(); // Local index of jVertex - if (donor_geometry->node[Point_Donor]->GetDomain()) { - nNodes = donor_geometry->vertex[markDonor][jVertex]->GetnDonorPoints(); - for (iDonor=0; iDonornode[Point_Donor]->GetGlobalIndex(); - Buffer_Send_GlobalPoint[nLocalFaceNodes_Donor] = - donor_geometry->vertex[markDonor][jVertex]->GetInterpDonorPoint(iDonor); - Buffer_Send_Coeff[nLocalFaceNodes_Donor] = - donor_geometry->vertex[markDonor][jVertex]->GetDonorCoeff(iDonor); - nLocalFaceNodes_Donor++; - } - Buffer_Send_FaceIndex[nLocalFace_Donor+1] =Buffer_Send_FaceIndex[nLocalFace_Donor]+nNodes; - nLocalFace_Donor++; - } - } - -#ifdef HAVE_MPI - SU2_MPI::Allgather(Buffer_Send_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, Buffer_Receive_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_GlobalPoint, MaxFaceNodes_Donor, MPI_LONG,Buffer_Receive_GlobalPoint, MaxFaceNodes_Donor, MPI_LONG, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_Coeff, MaxFaceNodes_Donor, MPI_DOUBLE,Buffer_Receive_Coeff, MaxFaceNodes_Donor, MPI_DOUBLE, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, Buffer_Receive_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); -#else - for (iFace=0; iFacevertex[markTarget][iVertex]->GetNode(); - if (target_geometry->node[iPoint]->GetDomain()) { - long Global_Point = target_geometry->node[iPoint]->GetGlobalIndex(); - nNodes = 0; - for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { - for (iFace = 0; iFace < Buffer_Receive_nFace_Donor[iProcessor]; iFace++) { - faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face - iNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1]- (unsigned int)faceindex; - for (iTarget=0; iTargetvertex[markTarget][iVertex]->SetnDonorPoints(nNodes); - target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); - - iDonor = 0; - for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { - for (iFace = 0; iFace < Buffer_Receive_nFace_Donor[iProcessor]; iFace++) { - - faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face - iNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1]- (unsigned int)faceindex; - for (iTarget=0; iTargetvertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor,pGlobalPoint); - target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor,coeff); - target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, iProcessor); - iDonor++; - } - } - } - } - } - } - delete[] Buffer_Send_nFace_Donor; - delete[] Buffer_Send_nFaceNodes_Donor; - - delete[] Buffer_Receive_nFace_Donor; - delete[] Buffer_Receive_nFaceNodes_Donor; - - delete[] Buffer_Send_FaceIndex; - delete[] Buffer_Send_FaceNodes; - delete[] Buffer_Send_GlobalPoint; - delete[] Buffer_Send_Coeff; - - delete[] Buffer_Receive_FaceIndex; - delete[] Buffer_Receive_FaceNodes; - delete[] Buffer_Receive_GlobalPoint; - delete[] Buffer_Receive_Coeff; - - } -} - -CSlidingMesh::CSlidingMesh(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone){ - - /*--- Initialize transfer coefficients between the zones ---*/ - Set_TransferCoeff(config); - - /*--- For fluid-structure interaction data interpolated with have nDim dimensions ---*/ - // InitializeData(Zones,nDim); -} - -CSlidingMesh::~CSlidingMesh(){} - -void CSlidingMesh::Set_TransferCoeff(CConfig **config){ - - /* --- This routine sets the transfer coefficient for sliding mesh approach --- */ - - /* - * The algorithm is based on Rinaldi et al. "Flux-conserving treatment of non-conformal interfaces - * for finite-volume discritization of conservaation laws" 2015, Comp. Fluids, 120, pp 126-139 - */ - - /* 0 - Variable declaration - */ - - /* --- General variables --- */ - - bool check; - - unsigned short iDim, nDim; - - unsigned long ii, jj, *uptr; - unsigned long vPoint; - unsigned long iEdgeVisited, nEdgeVisited, iNodeVisited; - unsigned long nAlreadyVisited, nToVisit, StartVisited; - - unsigned long *alreadyVisitedDonor, *ToVisit, *tmpVect; - unsigned long *storeProc, *tmp_storeProc; - - su2double dTMP; - su2double *Coeff_Vect, *tmp_Coeff_Vect; - - /* --- Geometrical variables --- */ - - su2double *Coord_i, *Coord_j, dist, mindist, *Normal; - su2double Area, Area_old, tmp_Area; - su2double LineIntersectionLength, *Direction, length; - - - /* --- Markers Variables --- */ - - unsigned short iMarkerInt, nMarkerInt; - - unsigned long iVertex, nVertexTarget; - - int markDonor, markTarget; - - /* --- Target variables --- */ - - unsigned long target_iPoint, jVertexTarget; - unsigned long nEdges_target, nNode_target; - - unsigned long *Target_nLinkedNodes, *Target_LinkedNodes, *Target_StartLinkedNodes, *target_segment; - unsigned long *Target_Proc; - long *Target_GlobalPoint, *Donor_GlobalPoint; - - su2double *TargetPoint_Coord, *target_iMidEdge_point, *target_jMidEdge_point, **target_element; - - /* --- Donor variables --- */ - - unsigned long donor_StartIndex, donor_forward_point, donor_backward_point, donor_iPoint, donor_OldiPoint; - unsigned long nEdges_donor, nNode_donor, nGlobalVertex_Donor; - - unsigned long nDonorPoints, iDonor; - unsigned long *Donor_Vect, *tmp_Donor_Vect; - unsigned long *Donor_nLinkedNodes, *Donor_LinkedNodes, *Donor_StartLinkedNodes; - unsigned long *Donor_Proc; - - su2double *donor_iMidEdge_point, *donor_jMidEdge_point; - su2double **donor_element, *DonorPoint_Coord; - - /* 1 - Variable pre-processing - */ - - nDim = donor_geometry->GetnDim(); - - /*--- Setting up auxiliary vectors ---*/ - - Donor_Vect = NULL; - Coeff_Vect = NULL; - storeProc = NULL; - - tmp_Donor_Vect = NULL; - tmp_Coeff_Vect = NULL; - tmp_storeProc = NULL; - - Normal = new su2double[nDim]; - Direction = new su2double[nDim]; - - - /* 2 - Find boundary tag between touching grids */ - - /*--- Number of markers on the FSI interface ---*/ - nMarkerInt = (int)( config[ donorZone ]->GetMarker_n_ZoneInterface() ) / 2; - - /*--- For the number of markers on the interface... ---*/ - for ( iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++ ){ - - /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ - markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); - - /*--- On the target side: find the tag of the boundary sharing the interface ---*/ - markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); - - /*--- Checks if the zone contains the interface, if not continue to the next step ---*/ - if( !CheckInterfaceBoundary(markDonor, markTarget) ) - continue; - - if(markTarget != -1) - nVertexTarget = target_geometry->GetnVertex( markTarget ); - else - nVertexTarget = 0; - - /* - 3 -Reconstruct the boundaries from parallel partitioning - */ - - /*--- Target boundary ---*/ - ReconstructBoundary(targetZone, markTarget); - - nGlobalVertex_Target = nGlobalVertex; - - TargetPoint_Coord = Buffer_Receive_Coord; - Target_GlobalPoint = Buffer_Receive_GlobalPoint; - Target_nLinkedNodes = Buffer_Receive_nLinkedNodes; - Target_StartLinkedNodes = Buffer_Receive_StartLinkedNodes; - Target_LinkedNodes = Buffer_Receive_LinkedNodes; - Target_Proc = Buffer_Receive_Proc; - - /*--- Donor boundary ---*/ - ReconstructBoundary(donorZone, markDonor); - - nGlobalVertex_Donor = nGlobalVertex; - - DonorPoint_Coord = Buffer_Receive_Coord; - Donor_GlobalPoint = Buffer_Receive_GlobalPoint; - Donor_nLinkedNodes = Buffer_Receive_nLinkedNodes; - Donor_StartLinkedNodes = Buffer_Receive_StartLinkedNodes; - Donor_LinkedNodes = Buffer_Receive_LinkedNodes; - Donor_Proc = Buffer_Receive_Proc; - - /*--- Starts building the supermesh layer (2D or 3D) ---*/ - /* - For each target node, it first finds the closest donor point - * - Then it creates the supermesh in the close proximity of the target point: - * - Starting from the closest donor node, it expands the supermesh by including - * donor elements neighboring the initial one, until the overall target area is fully covered. - */ - - if(nDim == 2){ - - target_iMidEdge_point = new su2double[nDim]; - target_jMidEdge_point = new su2double[nDim]; - - donor_iMidEdge_point = new su2double[nDim]; - donor_jMidEdge_point = new su2double[nDim]; - - /*--- Starts with supermesh reconstruction ---*/ - - target_segment = new unsigned long[2]; - - for (iVertex = 0; iVertex < nVertexTarget; iVertex++) { - - nDonorPoints = 0; - - /*--- Stores coordinates of the target node ---*/ - - target_iPoint = target_geometry->vertex[markTarget][iVertex]->GetNode(); - - if (target_geometry->node[target_iPoint]->GetDomain()){ - - Coord_i = target_geometry->node[target_iPoint]->GetCoord(); - - /*--- Brute force to find the closest donor_node ---*/ - - mindist = 1E6; - donor_StartIndex = 0; - - for (donor_iPoint = 0; donor_iPoint < nGlobalVertex_Donor; donor_iPoint++) { - - Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ]; - - dist = PointsDistance(Coord_i, Coord_j); - - if (dist < mindist) { - mindist = dist; - donor_StartIndex = donor_iPoint; - } - - if (dist == 0.0){ - donor_StartIndex = donor_iPoint; - break; - } - } - - donor_iPoint = donor_StartIndex; - donor_OldiPoint = donor_iPoint; - - /*--- Contruct information regarding the target cell ---*/ - - long dPoint = target_geometry->node[target_iPoint]->GetGlobalIndex(); - for (jVertexTarget = 0; jVertexTarget < nGlobalVertex_Target; jVertexTarget++) - if( dPoint == Target_GlobalPoint[jVertexTarget] ) - break; - - if ( Target_nLinkedNodes[jVertexTarget] == 1 ){ - target_segment[0] = Target_LinkedNodes[ Target_StartLinkedNodes[jVertexTarget] ]; - target_segment[1] = jVertexTarget; - } - else{ - target_segment[0] = Target_LinkedNodes[ Target_StartLinkedNodes[jVertexTarget] ]; - target_segment[1] = Target_LinkedNodes[ Target_StartLinkedNodes[jVertexTarget] + 1]; - } - - dTMP = 0; - for(iDim = 0; iDim < nDim; iDim++){ - target_iMidEdge_point[iDim] = ( TargetPoint_Coord[ nDim * target_segment[0] + iDim ] + target_geometry->node[ target_iPoint ]->GetCoord(iDim) ) / 2; - target_jMidEdge_point[iDim] = ( TargetPoint_Coord[ nDim * target_segment[1] + iDim ] + target_geometry->node[ target_iPoint ]->GetCoord(iDim) ) / 2; - - Direction[iDim] = target_jMidEdge_point[iDim] - target_iMidEdge_point[iDim]; - dTMP += Direction[iDim] * Direction[iDim]; - } - - dTMP = sqrt(dTMP); - for(iDim = 0; iDim < nDim; iDim++) - Direction[iDim] /= dTMP; - - length = PointsDistance(target_iMidEdge_point, target_jMidEdge_point); - - check = false; - - /*--- Proceeds along the forward direction (depending on which connected boundary node is found first) ---*/ - - while( !check ){ - - /*--- Proceeds until the value of the intersection area is null ---*/ - - if ( Donor_nLinkedNodes[donor_iPoint] == 1 ){ - donor_forward_point = Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_iPoint] ]; - donor_backward_point = donor_iPoint; - } - else{ - uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_iPoint] ]; - - if( donor_OldiPoint != uptr[0] ){ - donor_forward_point = uptr[0]; - donor_backward_point = uptr[1]; - } - else{ - donor_forward_point = uptr[1]; - donor_backward_point = uptr[0]; - } - } - - if(donor_iPoint >= nGlobalVertex_Donor){ - check = true; - continue; - } - - for(iDim = 0; iDim < nDim; iDim++){ - donor_iMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_forward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; - donor_jMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_backward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; - } - - LineIntersectionLength = ComputeLineIntersectionLength(target_iMidEdge_point, target_jMidEdge_point, donor_iMidEdge_point, donor_jMidEdge_point, Direction); - - if ( LineIntersectionLength == 0.0 ){ - check = true; - continue; - } - - /*--- In case the element intersects the target cell, update the auxiliary communication data structure ---*/ - - tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ]; - tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ]; - tmp_storeProc = new unsigned long[ nDonorPoints + 1 ]; - - for( iDonor = 0; iDonor < nDonorPoints; iDonor++){ - tmp_Donor_Vect[iDonor] = Donor_Vect[iDonor]; - tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor]; - tmp_storeProc[iDonor] = storeProc[iDonor]; - } - - tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint; - tmp_Coeff_Vect[ nDonorPoints ] = LineIntersectionLength / length; - tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint]; - - if (Donor_Vect != NULL) delete [] Donor_Vect; - if (Coeff_Vect != NULL) delete [] Coeff_Vect; - if (storeProc != NULL) delete [] storeProc; - - Donor_Vect = tmp_Donor_Vect; - Coeff_Vect = tmp_Coeff_Vect; - storeProc = tmp_storeProc; - - donor_OldiPoint = donor_iPoint; - donor_iPoint = donor_forward_point; - - nDonorPoints++; - } - - if ( Donor_nLinkedNodes[donor_StartIndex] == 2 ){ - check = false; - - uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_StartIndex] ]; - - donor_iPoint = uptr[1]; - donor_OldiPoint = donor_StartIndex; - } - else - check = true; - - /*--- Proceeds along the backward direction (depending on which connected boundary node is found first) ---*/ - - while( !check ){ - - /*--- Proceeds until the value of the intersection length is null ---*/ - if ( Donor_nLinkedNodes[donor_iPoint] == 1 ){ - donor_forward_point = donor_OldiPoint; - donor_backward_point = donor_iPoint; - } - else{ - uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_iPoint] ]; - - if( donor_OldiPoint != uptr[0] ){ - donor_forward_point = uptr[0]; - donor_backward_point = uptr[1]; - } - else{ - donor_forward_point = uptr[1]; - donor_backward_point = uptr[0]; - } - } - - if(donor_iPoint >= nGlobalVertex_Donor){ - check = true; - continue; - } - - for(iDim = 0; iDim < nDim; iDim++){ - donor_iMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_forward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; - donor_jMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_backward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; - } - - LineIntersectionLength = ComputeLineIntersectionLength(target_iMidEdge_point, target_jMidEdge_point, donor_iMidEdge_point, donor_jMidEdge_point, Direction); - - if ( LineIntersectionLength == 0.0 ){ - check = true; - continue; - } - - /*--- In case the element intersects the target cell, update the auxiliary communication data structure ---*/ - - tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ]; - tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ]; - tmp_storeProc = new unsigned long[ nDonorPoints + 1 ]; - - for( iDonor = 0; iDonor < nDonorPoints; iDonor++){ - tmp_Donor_Vect[iDonor] = Donor_Vect[iDonor]; - tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor]; - tmp_storeProc[iDonor] = storeProc[iDonor]; - } - - tmp_Coeff_Vect[ nDonorPoints ] = LineIntersectionLength / length; - tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint; - tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint]; - - if (Donor_Vect != NULL) delete [] Donor_Vect; - if (Coeff_Vect != NULL) delete [] Coeff_Vect; - if (storeProc != NULL) delete [] storeProc; - - Donor_Vect = tmp_Donor_Vect; - Coeff_Vect = tmp_Coeff_Vect; - storeProc = tmp_storeProc; - - donor_OldiPoint = donor_iPoint; - donor_iPoint = donor_forward_point; - - nDonorPoints++; - } - - /*--- Set the communication data structure and copy data from the auxiliary vectors ---*/ - - target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nDonorPoints); - - target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); - - for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){ - target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff( iDonor, Coeff_Vect[iDonor]); - target_geometry->vertex[markTarget][iVertex]->SetInterpDonorPoint( iDonor, Donor_GlobalPoint[ Donor_Vect[iDonor] ]); - target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]); - } - } - } - - delete [] target_segment; - - delete [] target_iMidEdge_point; - delete [] target_jMidEdge_point; - - delete [] donor_iMidEdge_point; - delete [] donor_jMidEdge_point; - } - else{ - /* --- 3D geometry, creates a superficial super-mesh --- */ - - for (iVertex = 0; iVertex < nVertexTarget; iVertex++) { - - nDonorPoints = 0; - - /*--- Stores coordinates of the target node ---*/ - - target_iPoint = target_geometry->vertex[markTarget][iVertex]->GetNode(); - - if (target_geometry->node[target_iPoint]->GetDomain()){ - - Coord_i = target_geometry->node[target_iPoint]->GetCoord(); - - target_geometry->vertex[markTarget][iVertex]->GetNormal(Normal); - - /*--- The value of Area computed here includes also portion of boundary belonging to different marker ---*/ - Area = 0.0; - for (iDim = 0; iDim < nDim; iDim++) - Area += Normal[iDim]*Normal[iDim]; - Area = sqrt(Area); - - for (iDim = 0; iDim < nDim; iDim++) - Normal[iDim] /= Area; - - for (iDim = 0; iDim < nDim; iDim++) - Coord_i[iDim] = target_geometry->node[target_iPoint]->GetCoord(iDim); - - long dPoint = target_geometry->node[target_iPoint]->GetGlobalIndex(); - for (target_iPoint = 0; target_iPoint < nGlobalVertex_Target; target_iPoint++){ - if( dPoint == Target_GlobalPoint[target_iPoint] ) - break; - } - - /*--- Build local surface dual mesh for target element ---*/ - - nEdges_target = Target_nLinkedNodes[target_iPoint]; - - nNode_target = 2*(nEdges_target + 1); - - target_element = new su2double*[nNode_target]; - for (ii = 0; ii < nNode_target; ii++) - target_element[ii] = new su2double[nDim]; - - nNode_target = Build_3D_surface_element(Target_LinkedNodes, Target_StartLinkedNodes, Target_nLinkedNodes, TargetPoint_Coord, target_iPoint, target_element); - - /*--- Brute force to find the closest donor_node ---*/ - - mindist = 1E6; - donor_StartIndex = 0; - - for (donor_iPoint = 0; donor_iPoint < nGlobalVertex_Donor; donor_iPoint++) { - - Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ]; - - dist = PointsDistance(Coord_i, Coord_j); - - if (dist < mindist) { - mindist = dist; - donor_StartIndex = donor_iPoint; - } - - if (dist == 0.0){ - donor_StartIndex = donor_iPoint; - break; - } - } - - donor_iPoint = donor_StartIndex; - - nEdges_donor = Donor_nLinkedNodes[donor_iPoint]; - - donor_element = new su2double*[ 2*nEdges_donor + 2 ]; - for (ii = 0; ii < 2*nEdges_donor + 2; ii++) - donor_element[ii] = new su2double[nDim]; - - nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, DonorPoint_Coord, donor_iPoint, donor_element); - - Area = 0; - for (ii = 1; ii < nNode_target-1; ii++){ - for (jj = 1; jj < nNode_donor-1; jj++){ - Area += Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], donor_element[0], donor_element[jj], donor_element[jj+1], Normal); - //cout << Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], donor_element[0], donor_element[jj], donor_element[jj+1], Normal) << endl; - } - } - - for (ii = 0; ii < 2*nEdges_donor + 2; ii++) - delete [] donor_element[ii]; - delete [] donor_element; - - nDonorPoints = 1; - - /*--- In case the element intersect the target cell update the auxiliary communication data structure ---*/ - - Coeff_Vect = new su2double[ nDonorPoints ]; - Donor_Vect = new unsigned long[ nDonorPoints ]; - storeProc = new unsigned long[ nDonorPoints ]; - - Coeff_Vect[0] = Area; - Donor_Vect[0] = donor_iPoint; - storeProc[0] = Donor_Proc[donor_iPoint]; - - alreadyVisitedDonor = new unsigned long[1]; - - alreadyVisitedDonor[0] = donor_iPoint; - nAlreadyVisited = 1; - StartVisited = 0; - - Area_old = -1; - - while( Area > Area_old ){ - - /* - * - Starting from the closest donor_point, it expands the supermesh by a countour search pattern. - * - The closest donor element becomes the core, at each iteration a new layer of elements around the core is taken into account - */ - - Area_old = Area; - - ToVisit = NULL; - nToVisit = 0; - - for( iNodeVisited = StartVisited; iNodeVisited < nAlreadyVisited; iNodeVisited++ ){ - - vPoint = alreadyVisitedDonor[ iNodeVisited ]; - - nEdgeVisited = Donor_nLinkedNodes[vPoint]; - - for (iEdgeVisited = 0; iEdgeVisited < nEdgeVisited; iEdgeVisited++){ - - donor_iPoint = Donor_LinkedNodes[ Donor_StartLinkedNodes[vPoint] + iEdgeVisited]; - - /*--- Check if the node to visit is already listed in the data structure to avoid double visits ---*/ - - check = 0; - - for( jj = 0; jj < nAlreadyVisited; jj++ ){ - if( donor_iPoint == alreadyVisitedDonor[jj] ){ - check = 1; - break; - } - } - - if( check == 0 && ToVisit != NULL){ - for( jj = 0; jj < nToVisit; jj++ ) - if( donor_iPoint == ToVisit[jj] ){ - check = 1; - break; - } - } - - if( check == 0 ){ - /*--- If the node was not already visited, visit it and list it into data structure ---*/ - - tmpVect = new unsigned long[ nToVisit + 1 ]; - - for( jj = 0; jj < nToVisit; jj++ ) - tmpVect[jj] = ToVisit[jj]; - tmpVect[nToVisit] = donor_iPoint; - - if( ToVisit != NULL ) - delete [] ToVisit; - - ToVisit = tmpVect; - tmpVect = NULL; - - nToVisit++; - - /*--- Find the value of the intersection area between the current donor element and the target element --- */ - - nEdges_donor = Donor_nLinkedNodes[donor_iPoint]; - - donor_element = new su2double*[ 2*nEdges_donor + 2 ]; - for (ii = 0; ii < 2*nEdges_donor + 2; ii++) - donor_element[ii] = new su2double[nDim]; - - nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, DonorPoint_Coord, donor_iPoint, donor_element); - - tmp_Area = 0; - for (ii = 1; ii < nNode_target-1; ii++) - for (jj = 1; jj < nNode_donor-1; jj++) - tmp_Area += Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], donor_element[0], donor_element[jj], donor_element[jj+1], Normal); - - for (ii = 0; ii < 2*nEdges_donor + 2; ii++) - delete [] donor_element[ii]; - delete [] donor_element; - - /*--- In case the element intersect the target cell update the auxiliary communication data structure ---*/ - - tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ]; - tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ]; - tmp_storeProc = new unsigned long[ nDonorPoints + 1 ]; - - for( iDonor = 0; iDonor < nDonorPoints; iDonor++){ - tmp_Donor_Vect[iDonor] = Donor_Vect[iDonor]; - tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor]; - tmp_storeProc[iDonor] = storeProc[iDonor]; - } - - tmp_Coeff_Vect[ nDonorPoints ] = tmp_Area; - tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint; - tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint]; - - if (Donor_Vect != NULL) {delete [] Donor_Vect; } - if (Coeff_Vect != NULL) {delete [] Coeff_Vect; } - if (storeProc != NULL) {delete [] storeProc; } - - Donor_Vect = tmp_Donor_Vect; - Coeff_Vect = tmp_Coeff_Vect; - storeProc = tmp_storeProc; - - tmp_Coeff_Vect = NULL; - tmp_Donor_Vect = NULL; - tmp_storeProc = NULL; - - nDonorPoints++; - - Area += tmp_Area; - } - } - } - - /*--- Update auxiliary data structure ---*/ - - StartVisited = nAlreadyVisited; - - tmpVect = new unsigned long[ nAlreadyVisited + nToVisit ]; - - for( jj = 0; jj < nAlreadyVisited; jj++ ) - tmpVect[jj] = alreadyVisitedDonor[jj]; - - for( jj = 0; jj < nToVisit; jj++ ) - tmpVect[ nAlreadyVisited + jj ] = ToVisit[jj]; - - if( alreadyVisitedDonor != NULL ) - delete [] alreadyVisitedDonor; - - alreadyVisitedDonor = tmpVect; - - nAlreadyVisited += nToVisit; - - delete [] ToVisit; - } - - delete [] alreadyVisitedDonor; - - /*--- Set the communication data structure and copy data from the auxiliary vectors ---*/ - - target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nDonorPoints); - target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); - - for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){ - target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor, Coeff_Vect[iDonor]/Area); - target_geometry->vertex[markTarget][iVertex]->SetInterpDonorPoint( iDonor, Donor_GlobalPoint[ Donor_Vect[iDonor] ] ); - target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]); - //cout <GetnDim(); - - su2double dotA2, dotB1, dotB2; - - dotA2 = 0; - for(iDim = 0; iDim < nDim; iDim++) - dotA2 += ( A2[iDim] - A1[iDim] ) * Direction[iDim]; - - if( dotA2 >= 0 ){ - dotB1 = 0; - dotB2 = 0; - for(iDim = 0; iDim < nDim; iDim++){ - dotB1 += ( B1[iDim] - A1[iDim] ) * Direction[iDim]; - dotB2 += ( B2[iDim] - A1[iDim] ) * Direction[iDim]; - } - } - else{ - dotA2 *= -1; - - dotB1 = 0; - dotB2 = 0; - for(iDim = 0; iDim < nDim; iDim++){ - dotB1 -= ( B1[iDim] - A1[iDim] ) * Direction[iDim]; - dotB2 -= ( B2[iDim] - A1[iDim] ) * Direction[iDim]; - } - } - - if( dotB1 >= 0 && dotB1 <= dotA2 ){ - if ( dotB2 < 0 ) - return fabs( dotB1 ); - if ( dotB2 > dotA2 ) - return fabs( dotA2 - dotB1 ); - - return fabs( dotB1 - dotB2 ); - } - - if( dotB2 >= 0 && dotB2 <= dotA2 ){ - if ( dotB1 < 0 ) - return fabs(dotB2); - if ( dotB1 > dotA2 ) - return fabs( dotA2 - dotB2 ); - } - - if( ( dotB1 <= 0 && dotA2 <= dotB2 ) || ( dotB2 <= 0 && dotA2 <= dotB1 ) ) - return fabs( dotA2 ); - - return 0.0; -} - -su2double CSlidingMesh::Compute_Triangle_Intersection(su2double* A1, su2double* A2, su2double* A3, su2double* B1, su2double* B2, su2double* B3, su2double* Direction){ - - /* --- This routine is ONLY for 3D grids --- */ - /* --- Projects triangle points onto a plane, specified by its normal "Direction", and calls the ComputeIntersectionArea routine --- */ - - unsigned short iDim; - unsigned short nDim = 3; - - su2double I[3], J[3], K[3]; - su2double a1[3], a2[3], a3[3]; - su2double b1[3], b2[3], b3[3]; - su2double m1, m2; - - /* --- Reference frame is determined by: x = A1A2 y = x ^ ( -Direction ) --- */ - - for(iDim = 0; iDim < 3; iDim++){ - a1[iDim] = 0; - a2[iDim] = 0; - a3[iDim] = 0; - - b1[iDim] = 0; - b2[iDim] = 0; - b3[iDim] = 0; - } - - m1 = 0; - for(iDim = 0; iDim < nDim; iDim++){ - K[iDim] = Direction[iDim]; - - m1 += K[iDim] * K[iDim]; - } - - for(iDim = 0; iDim < nDim; iDim++) - K[iDim] /= sqrt(m1); - - m2 = 0; - for(iDim = 0; iDim < nDim; iDim++) - m2 += (A2[iDim] - A1[iDim]) * K[iDim]; - - m1 = 0; - for(iDim = 0; iDim < nDim; iDim++){ - I[iDim] = (A2[iDim] - A1[iDim]) - m2 * K[iDim]; - m1 += I[iDim] * I[iDim]; - } - - for(iDim = 0; iDim < nDim; iDim++) - I[iDim] /= sqrt(m1); - - // Cross product to find Y - J[0] = K[1]*I[2] - K[2]*I[1]; - J[1] = -(K[0]*I[2] - K[2]*I[0]); - J[2] = K[0]*I[1] - K[1]*I[0]; - - /* --- Project all points on the plane specified by Direction and change their reference frame taking A1 as origin --- */ - - for(iDim = 0; iDim < nDim; iDim++){ - a2[0] += (A2[iDim] - A1[iDim]) * I[iDim]; - a2[1] += (A2[iDim] - A1[iDim]) * J[iDim]; - a2[2] += (A2[iDim] - A1[iDim]) * K[iDim]; - - a3[0] += (A3[iDim] - A1[iDim]) * I[iDim]; - a3[1] += (A3[iDim] - A1[iDim]) * J[iDim]; - a3[2] += (A3[iDim] - A1[iDim]) * K[iDim]; - - b1[0] += (B1[iDim] - A1[iDim]) * I[iDim]; - b1[1] += (B1[iDim] - A1[iDim]) * J[iDim]; - b1[2] += (B1[iDim] - A1[iDim]) * K[iDim]; - - b2[0] += (B2[iDim] - A1[iDim]) * I[iDim]; - b2[1] += (B2[iDim] - A1[iDim]) * J[iDim]; - b2[2] += (B2[iDim] - A1[iDim]) * K[iDim]; - - b3[0] += (B3[iDim] - A1[iDim]) * I[iDim]; - b3[1] += (B3[iDim] - A1[iDim]) * J[iDim]; - b3[2] += (B3[iDim] - A1[iDim]) * K[iDim]; - } - - /* --- Compute intersection area --- */ - - return ComputeIntersectionArea( a1, a2, a3, b1, b2, b3 ); -} - -su2double CSlidingMesh::ComputeIntersectionArea( su2double* P1, su2double* P2, su2double* P3, su2double* Q1, su2double* Q2, su2double* Q3 ){ - - /* --- This routines computes the area of the polygonal element generated by the superimposition of 2 planar triangle --- */ - /* --- The 2 triangle must lie on the same plane --- */ - - unsigned short iDim, nPoints, i, j, k; - unsigned short nDim, min_theta_index; - - su2double points[16][2], IntersectionPoint[2], theta[6]; - su2double TriangleP[4][2], TriangleQ[4][2]; - su2double Area, det, dot1, dot2, dtmp, min_theta; - - nDim = 2; - nPoints = 0; - - for(iDim = 0; iDim < nDim; iDim++){ - TriangleP[0][iDim] = 0; - TriangleP[1][iDim] = P2[iDim] - P1[iDim]; - TriangleP[2][iDim] = P3[iDim] - P1[iDim]; - TriangleP[3][iDim] = 0; - - TriangleQ[0][iDim] = Q1[iDim] - P1[iDim]; - TriangleQ[1][iDim] = Q2[iDim] - P1[iDim]; - TriangleQ[2][iDim] = Q3[iDim] - P1[iDim]; - TriangleQ[3][iDim] = Q1[iDim] - P1[iDim]; - } - - - for( j = 0; j < 3; j++){ - if( CheckPointInsideTriangle(TriangleP[j], TriangleQ[0], TriangleQ[1], TriangleQ[2]) ){ - - // Then P1 is also inside triangle Q, so store it - for(iDim = 0; iDim < nDim; iDim++) - points[nPoints][iDim] = TriangleP[j][iDim]; - - nPoints++; - } - } - - for( j = 0; j < 3; j++){ - if( CheckPointInsideTriangle(TriangleQ[j], TriangleP[0], TriangleP[1], TriangleP[2]) ){ - - // Then Q1 is also inside triangle P, so store it - for(iDim = 0; iDim < nDim; iDim++) - points[nPoints][iDim] = TriangleQ[j][iDim]; - - nPoints++; - } - } - - - // Compute all edge intersections - - for( j = 0; j < 3; j++){ - for( i = 0; i < 3; i++){ - - det = (TriangleP[j][0] - TriangleP[j+1][0]) * ( TriangleQ[i][1] - TriangleQ[i+1][1] ) - (TriangleP[j][1] - TriangleP[j+1][1]) * (TriangleQ[i][0] - TriangleQ[i+1][0]); - - if ( det != 0.0 ){ - ComputeLineIntersectionPoint( TriangleP[j], TriangleP[j+1], TriangleQ[i], TriangleQ[i+1], IntersectionPoint ); - - dot1 = 0; - dot2 = 0; - for(iDim = 0; iDim < nDim; iDim++){ - dot1 += ( TriangleP[j][iDim] - IntersectionPoint[iDim] ) * ( TriangleP[j+1][iDim] - IntersectionPoint[iDim] ); - dot2 += ( TriangleQ[i][iDim] - IntersectionPoint[iDim] ) * ( TriangleQ[i+1][iDim] - IntersectionPoint[iDim] ); - } - - if( dot1 <= 0 && dot2 <= 0 ){ // It found one intersection - - // Store temporarily the intersection point - - for(iDim = 0; iDim < nDim; iDim++) - points[nPoints][iDim] = IntersectionPoint[iDim]; - - nPoints++; - } - } - } - } - - // Remove double points, if any - - for( i = 0; i < nPoints; i++){ - for( j = i+1; j < nPoints; j++){ - if(points[j][0] == points[i][0] && points[j][1] == points[i][1]){ - for( k = j; k < nPoints-1; k++){ - points[k][0] = points[k+1][0]; - points[k][1] = points[k+1][1]; - } - nPoints--; - j--; - } - } - } - - // Re-order nodes - - for( i = 1; i < nPoints; i++){ // Change again reference frame - for(iDim = 0; iDim < nDim; iDim++) - points[i][iDim] -= points[0][iDim]; - - // Compute polar azimuth for each node but the first - theta[i] = atan2(points[i][1], points[i][0]); - } - - for(iDim = 0; iDim < nDim; iDim++) - points[0][iDim] = 0; - - for( i = 1; i < nPoints; i++){ - - min_theta = theta[i]; - min_theta_index = 0; - - for( j = i + 1; j < nPoints; j++){ - - if( theta[j] < min_theta ){ - min_theta = theta[j]; - min_theta_index = j; - } - } - - if( min_theta_index != 0 ){ - dtmp = theta[i]; - theta[i] = theta[min_theta_index]; - theta[min_theta_index] = dtmp; - - dtmp = points[i][0]; - points[i][0] = points[min_theta_index][0]; - points[min_theta_index][0] = dtmp; - - dtmp = points[i][1]; - points[i][1] = points[min_theta_index][1]; - points[min_theta_index][1] = dtmp; - } - } - - // compute area using cross product rule, points position are referred to the 2-dimensional, local, reference frame centered in points[0] - - Area = 0; - - if (nPoints > 2){ - for( i = 1; i < nPoints-1; i++ ){ - - // Ax*By - Area += ( points[i][0] - points[0][0] ) * ( points[i+1][1] - points[0][1] ); - - // Ay*Bx - Area -= ( points[i][1] - points[0][1] ) * ( points[i+1][0] - points[0][0] ); - } - } - - return fabs(Area)/2; -} - -void CSlidingMesh::ComputeLineIntersectionPoint( su2double* A1, su2double* A2, su2double* B1, su2double* B2, su2double* IntersectionPoint ){ - - /* --- Uses determinant rule to compute the intersection point between 2 straight segments --- */ - /* This works only for lines on a 2D plane, A1, A2 and B1, B2 are respectively the head and the tail points of each segment, - * since they're on a 2D plane they are defined by a 2-elements array containing their coordinates */ - - su2double det; - - det = (A1[0] - A2[0]) * (B1[1] - B2[1]) - (A1[1] - A2[1]) * (B1[0] - B2[0]); - - if ( det != 0.0 ){ // else there is no intersection point - IntersectionPoint[0] = ( ( A1[0]*A2[1] - A1[1]*A2[0] ) * ( B1[0] - B2[0] ) - ( B1[0]*B2[1] - B1[1]*B2[0] ) * ( A1[0] - A2[0] ) ) / det; - IntersectionPoint[1] = ( ( A1[0]*A2[1] - A1[1]*A2[0] ) * ( B1[1] - B2[1] ) - ( B1[0]*B2[1] - B1[1]*B2[0] ) * ( A1[1] - A2[1] ) ) / det; - } - - return; -} - -bool CSlidingMesh::CheckPointInsideTriangle(su2double* Point, su2double* T1, su2double* T2, su2double* T3){ - - /* --- Check whether a point "Point" lies inside or outside a triangle defined by 3 points "T1", "T2", "T3" --- */ - /* For each edge it checks on which side the point lies: - * - Computes the unit vector pointing at the internal side of the edge - * - Comutes the vector that connects the point to a point along the edge - * - If the dot product is positive it means that the point is on the internal side of the edge - * - If the check is positive for all the 3 edges, then the point lies within the triangle - */ - - unsigned short iDim, nDim, check; - - su2double vect1[2], vect2[2], r[2]; - su2double dot; - - check = 0; - nDim = 2; - - /* --- Check first edge --- */ - - dot = 0; - for(iDim = 0; iDim < nDim; iDim++){ - vect1[iDim] = T3[iDim] - T1[iDim]; // vec 1 is aligned to the edge - vect2[iDim] = T2[iDim] - T1[iDim]; // vect 2 is the vector connecting one edge point to the third triangle vertex - - r[iDim] = Point[iDim] - T1[iDim]; // Connects point to vertex T1 - - dot += vect2[iDim] * vect2[iDim]; - } - dot = sqrt(dot); - - for(iDim = 0; iDim < nDim; iDim++) - vect2[iDim] /= dot; - - dot = 0; - for(iDim = 0; iDim < nDim; iDim++) - dot += vect1[iDim] * vect2[iDim]; - - for(iDim = 0; iDim < nDim; iDim++) - vect1[iDim] = T3[iDim] - (T1[iDim] + dot * vect2[iDim]); // Computes the inward unit vector - - dot = 0; - for(iDim = 0; iDim < nDim; iDim++) // Checs that the point lies on the internal plane - dot += vect1[iDim] * r[iDim]; - - if (dot >= 0) - check++; - - /* --- Check second edge --- */ - - dot = 0; - for(iDim = 0; iDim < nDim; iDim++){ - vect1[iDim] = T1[iDim] - T2[iDim]; - vect2[iDim] = T3[iDim] - T2[iDim]; - - r[iDim] = Point[iDim] - T2[iDim]; - - dot += vect2[iDim] * vect2[iDim]; - } - dot = sqrt(dot); - - for(iDim = 0; iDim < nDim; iDim++) - vect2[iDim] /= dot; - - dot = 0; - for(iDim = 0; iDim < nDim; iDim++) - dot += vect1[iDim] * vect2[iDim]; - - for(iDim = 0; iDim < nDim; iDim++) - vect1[iDim] = T1[iDim] - (T2[iDim] + dot * vect2[iDim]); - - dot = 0; - for(iDim = 0; iDim < nDim; iDim++) - dot += vect1[iDim] * r[iDim]; - - if (dot >= 0) - check++; - - /* --- Check third edge --- */ - - dot = 0; - for(iDim = 0; iDim < nDim; iDim++){ - vect1[iDim] = T2[iDim] - T3[iDim]; - vect2[iDim] = T1[iDim] - T3[iDim]; - - r[iDim] = Point[iDim] - T3[iDim]; - - dot += vect2[iDim] * vect2[iDim]; - } - dot = sqrt(dot); - - for(iDim = 0; iDim < nDim; iDim++) - vect2[iDim] /= dot; - - dot = 0; - for(iDim = 0; iDim < nDim; iDim++) - dot += vect1[iDim] * vect2[iDim]; - - for(iDim = 0; iDim < nDim; iDim++) - vect1[iDim] = T2[iDim] - (T3[iDim] + dot * vect2[iDim]); - - dot = 0; - for(iDim = 0; iDim < nDim; iDim++) - dot += vect1[iDim] * r[iDim]; - - if (dot >= 0) - check++; - - return (check == 3); -} - -/*--- Radial Basis Function Interpolator ---*/ -CRadialBasisFunction::CRadialBasisFunction(void): CInterpolator() { } - -CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { - - /*--- Initialize transfer coefficients between the zones ---*/ - Set_TransferCoeff(config); - -} - -CRadialBasisFunction::~CRadialBasisFunction() {} - -void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { - - int iProcessor, nProcessor = size; - int nPolynomial = 0; - int mark_donor, mark_target, target_check, donor_check; - int *skip_row = NULL, *calc_polynomial_check; - - unsigned short iDim, nDim, iMarkerInt, nMarkerInt; - - unsigned long iVertexDonor, jVertexDonor, iVertexTarget, iCount, jCount; - unsigned long nVertexDonor, nVertexTarget, nVertexDonorInDomain; - unsigned long nGlobalVertexDonor, iGlobalVertexDonor_end, nLocalM; - unsigned long point_donor, point_target; - unsigned long *nLocalM_arr; - - su2double val_i, val_j; - su2double interface_coord_tol=1e6*numeric_limits::epsilon(); - su2double *Coord_i, *Coord_j; - su2double *local_M; - su2double *P = NULL; - su2double *C_inv_trunc = NULL, *C_tmp = NULL; - su2double *target_vec, *coeff_vec; - - CSymmetricMatrix *global_M = NULL, *Mp = NULL; - -#ifdef HAVE_MPI - unsigned long iLocalM; - su2double *global_M_val_arr = NULL, *Buffer_recv_local_M; - int *Buffer_Recv_mark = new int[nProcessor], iRank; -#endif - - /*--- Initialize variables --- */ - - nMarkerInt = (int) ( config[donorZone]->GetMarker_n_ZoneInterface() / 2 ); - - nDim = donor_geometry->GetnDim(); - - - Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; - - - /*--- Cycle over nMarkersInt interface to determine communication pattern ---*/ - - for (iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { - - /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ - mark_donor = Find_InterfaceMarker(config[donorZone], iMarkerInt); - - /*--- On the target side: find the tag of the boundary sharing the interface ---*/ - mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt); - -#ifdef HAVE_MPI - - donor_check = -1; - target_check = -1; - - /*--- We gather a vector in MASTER_NODE to determines whether the boundary is not on the processor because of the partition or because the zone does not include it ---*/ - - SU2_MPI::Gather(&mark_donor , 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - - if (rank == MASTER_NODE) - for (iRank = 0; iRank < nProcessor; iRank++) - if( Buffer_Recv_mark[iRank] != -1 ) { - donor_check = Buffer_Recv_mark[iRank]; - break; - } - - SU2_MPI::Bcast(&donor_check , 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - - - SU2_MPI::Gather(&mark_target, 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - - if (rank == MASTER_NODE) - for (iRank = 0; iRank < nProcessor; iRank++) - if( Buffer_Recv_mark[iRank] != -1 ) { - target_check = Buffer_Recv_mark[iRank]; - break; - } - - SU2_MPI::Bcast(&target_check, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - -#else - donor_check = mark_donor; - target_check = mark_target; -#endif - - /*--- Checks if the zone contains the interface, if not continue to the next step ---*/ - if(target_check == -1 || donor_check == -1) - continue; - - if(mark_donor != -1) - nVertexDonor = donor_geometry->GetnVertex( mark_donor ); - else - nVertexDonor = 0; - - if(mark_target != -1) - nVertexTarget = target_geometry->GetnVertex( mark_target ); - else - nVertexTarget = 0; - - Buffer_Send_nVertex_Donor = new unsigned long [ 1 ]; - - /*--- Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor ---*/ - Determine_ArraySize(false, mark_donor, mark_target, nVertexDonor, nDim); - - /*--- Collect information about number of donor vertices in domain. - Calculate total number of donor vertices across all ranks and - number of vertices on boundary prior to current rank. ---*/ - nVertexDonorInDomain = Buffer_Send_nVertex_Donor[0]; - iGlobalVertexDonor_end = nGlobalVertexDonor = 0; - for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) - { - nGlobalVertexDonor += Buffer_Receive_nVertex_Donor[iProcessor]; - if (iProcessor<=rank) iGlobalVertexDonor_end += Buffer_Receive_nVertex_Donor[iProcessor]; - } - - /*-- Collect coordinates, global points, and normal vectors ---*/ - Buffer_Send_Coord = new su2double [ MaxLocalVertex_Donor * nDim ]; - Buffer_Send_GlobalPoint = new long [ MaxLocalVertex_Donor ]; - Buffer_Receive_Coord = new su2double [ nProcessor * MaxLocalVertex_Donor * nDim ]; - Buffer_Receive_GlobalPoint = new long [ nProcessor * MaxLocalVertex_Donor ]; - - Collect_VertexInfo( false, mark_donor, mark_target, nVertexDonor, nDim); - - /*--- Send information about size of local_M array ---*/ - nLocalM = nVertexDonorInDomain*(nVertexDonorInDomain+1)/2 \ - + nVertexDonorInDomain*(nGlobalVertexDonor-iGlobalVertexDonor_end); - - nLocalM_arr = new unsigned long [nProcessor]; -#ifdef HAVE_MPI - SU2_MPI::Allgather(&nLocalM, 1, MPI_UNSIGNED_LONG, nLocalM_arr, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); -#else - nLocalM_arr[MASTER_NODE] = nLocalM; -#endif - - /*--- Initialize local M array and calculate values ---*/ - local_M = new su2double [nLocalM]; - Coord_i = new su2double [nDim]; - Coord_j = new su2double [nDim]; - iCount=0; - for (iVertexDonor=0; iVertexDonorGetKindRadialBasisFunction(), - config[donorZone]->GetRadialBasisFunctionParameter(), - PointsDistance(Coord_i, Coord_j)); - } - - for (iProcessor=rank+1; iProcessorGetKindRadialBasisFunction(), - config[donorZone]->GetRadialBasisFunctionParameter(), - PointsDistance(Coord_i, Coord_j)); - } - } - } - -#ifdef HAVE_MPI - if (rank != MASTER_NODE) { - SU2_MPI::Send(local_M, nLocalM, MPI_DOUBLE, MASTER_NODE, 0, MPI_COMM_WORLD); - } - - /*--- Assemble global_M ---*/ - if (rank == MASTER_NODE) { - global_M_val_arr = new su2double [nGlobalVertexDonor*(nGlobalVertexDonor+1)/2]; - - /*--- Copy master node local_M to global_M ---*/ - iCount = 0; - for (iLocalM=0; iLocalM SINGLE_NODE) { - for (iProcessor=1; iProcessorInitialize(nGlobalVertexDonor, global_M_val_arr); - } - -#else - global_M = new CSymmetricMatrix; - global_M->Initialize((int)nVertexDonorInDomain, local_M); -#endif - - /*--- Invert M matrix ---*/ - if (rank == MASTER_NODE) { - switch (config[donorZone]->GetKindRadialBasisFunction()) - { - /*--- Basis functions that make M positive definite ---*/ - case WENDLAND_C2: - case INV_MULTI_QUADRIC: - case GAUSSIAN: - global_M->Invert(true); - break; - - case THIN_PLATE_SPLINE: - case MULTI_QUADRIC: - global_M->Invert(false); - break; - } - } - - calc_polynomial_check = new int [nDim]; - - /*--- Calculate C_inv_trunc ---*/ - if (rank == MASTER_NODE) { - - if ( config[donorZone]->GetRadialBasisFunctionPolynomialOption() ) { - - /*--- Fill P matrix and get minimum and maximum values ---*/ - P = new su2double [nGlobalVertexDonor*(nDim+1)]; - iCount = 0; - for (iProcessor=MASTER_NODE; iProcessorInitialize(nPolynomial+1); - for (int m=0; mRead((int)iVertexDonor, (int)jVertexDonor)*P[jVertexDonor*(nPolynomial+1)+n]; - } - val_i += val_j*P[iVertexDonor*(nPolynomial+1)+m]; - } - Mp->Write(m, n, val_i); - } - } - Mp->Invert(false); - - /*--- Calculate M_p*P*M_inv ---*/ - C_inv_trunc = new su2double [(nGlobalVertexDonor+nPolynomial+1)*nGlobalVertexDonor]; - for (int m=0; mRead((int)jVertexDonor, (int)iVertexDonor); - } - val_i += val_j*Mp->Read(m, n); - } - /*--- Save in row major order ---*/ - C_inv_trunc[m*nGlobalVertexDonor+iVertexDonor] = val_i; - } - } - - /*--- Calculate (I - P'*M_p*P*M_inv) ---*/ - C_tmp = new su2double [nGlobalVertexDonor*nGlobalVertexDonor]; - for (iVertexDonor=0; iVertexDonorMatMatMult(true, C_tmp, (int)nGlobalVertexDonor); - - /*--- Write to C_inv_trunc matrix ---*/ - for (iVertexDonor=0; iVertexDonorRead((int)iVertexDonor, (int)jVertexDonor); - - } // endif GetRadialBasisFunctionPolynomialOption - } // endif (rank == MASTER_NODE) - -#ifdef HAVE_MPI - SU2_MPI::Bcast(&nPolynomial, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - SU2_MPI::Bcast(calc_polynomial_check, nDim, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - - if (rank != MASTER_NODE) { - C_inv_trunc = new su2double [(nGlobalVertexDonor+nPolynomial+1)*nGlobalVertexDonor]; - } - - SU2_MPI::Bcast(C_inv_trunc, (nGlobalVertexDonor+nPolynomial+1)*nGlobalVertexDonor, MPI_DOUBLE, MASTER_NODE, MPI_COMM_WORLD); -#endif - - /*--- Calculate H matrix ---*/ - if (config[donorZone]->GetRadialBasisFunctionPolynomialOption()) - target_vec = new su2double [nGlobalVertexDonor+nPolynomial+1]; - else - target_vec = new su2double [nGlobalVertexDonor]; - - coeff_vec = new su2double [nGlobalVertexDonor]; - - for (iVertexTarget = 0; iVertexTarget < nVertexTarget; iVertexTarget++) { - - point_target = target_geometry->vertex[mark_target][iVertexTarget]->GetNode(); - - if ( target_geometry->node[point_target]->GetDomain() ) { - iCount = 0; - if (config[donorZone]->GetRadialBasisFunctionPolynomialOption()) { - target_vec[iCount] = 1; - iCount++; - } - - for (iDim=0; iDimnode[point_target]->GetCoord(iDim); - if (config[donorZone]->GetRadialBasisFunctionPolynomialOption()) { - if (calc_polynomial_check[iDim] == 1) { - target_vec[iCount] = Coord_i[iDim]; - iCount++; - } - } - } - - for (iProcessor=0; iProcessorGetKindRadialBasisFunction(), - config[donorZone]->GetRadialBasisFunctionParameter(), - PointsDistance(Coord_i, Coord_j)); - } - } - - for (iVertexDonor=0; iVertexDonorvertex[mark_target][iVertexTarget]->SetnDonorPoints(iCount); - target_geometry->vertex[mark_target][iVertexTarget]->Allocate_DonorInfo(); - - iCount = 0; - jCount = 0; - for (iProcessor=0; iProcessorvertex[mark_target][iVertexTarget]->SetInterpDonorPoint(jCount, point_donor); - target_geometry->vertex[mark_target][iVertexTarget]->SetInterpDonorProcessor(jCount, iProcessor); - target_geometry->vertex[mark_target][iVertexTarget]->SetDonorCoeff(jCount, coeff_vec[iCount]); - jCount++; - } - iCount++; - } - } - } // endif - } // endfor - - /*--- Memory management ---*/ - delete [] nLocalM_arr; - delete [] local_M; - delete [] Coord_i; - delete [] Coord_j; - delete [] calc_polynomial_check; - delete [] C_inv_trunc; - delete [] target_vec; - delete [] coeff_vec; - - if ( rank == MASTER_NODE ) { - delete global_M; - - if ( config[donorZone]->GetRadialBasisFunctionPolynomialOption() ) { - delete [] skip_row; - delete [] P; - delete Mp; - delete [] C_tmp; - } - } - - delete[] Buffer_Send_Coord; - delete[] Buffer_Send_GlobalPoint; - - delete[] Buffer_Receive_Coord; - delete[] Buffer_Receive_GlobalPoint; - - delete[] Buffer_Send_nVertex_Donor; - -#ifdef HAVE_MPI - if (rank == MASTER_NODE) - delete [] global_M_val_arr; -#endif - } // end loop over markers - - delete[] Buffer_Receive_nVertex_Donor; - -#ifdef HAVE_MPI - if (rank == MASTER_NODE) - delete [] Buffer_Recv_mark; -#endif -} - -void CRadialBasisFunction::Check_PolynomialTerms(int m, unsigned long n, const int *skip_row, su2double max_diff_tol_in, int *keep_row, int &n_polynomial, su2double *P) -{ - /*--- This routine keeps the AD information in P but the calculations are done in passivedouble as their purpose - is to decide which (if any) row of P to remove, and that process is not differentiable anyway. ---*/ - - int *write_row = NULL; - unsigned long iCount, jCount, n_rows; - passivedouble sum, max_diff, max_coeff, *coeff = NULL, max_diff_tol = SU2_TYPE::GetValue(max_diff_tol_in); - CSymmetricMatrix *PPT; - su2double *P_tmp = NULL; - - n_rows = 0; - for (int i=0; iInitialize((int)n_rows); - - iCount = 0; - for (int i = 0; i < m; i ++) { - if (skip_row[i] == 0) { - - jCount = 0; - for (int j = 0; j < m; j ++){ - if (skip_row[j] == 0) { - - sum = 0.0; - for (unsigned long k = 0; k < n; k ++) - { - sum += SU2_TYPE::GetValue(P[k*m+i]*P[k*m+j]); - } - PPT->Write((int)iCount, (int)jCount, sum); - - jCount++; - } - } - - iCount++; - } - } - - PPT->Invert(true); - - /*--- RHS for the least squares fit (vector of ones times P) ---*/ - coeff = new passivedouble [n_rows]; - iCount = 0; - for (int i = 0; i < m; i ++) { - if (skip_row[i] == 0) { - coeff[iCount] = 0; - for (unsigned long j = 0; j < n; j += 1) - { - coeff[iCount] += SU2_TYPE::GetValue(P[j*m+i]); - } - iCount++; - } - } - - /*--- Multiply the RHS by the inverse thus obtaining the coefficients ---*/ - PPT->MatVecMult(coeff); - - /*--- Determine the maximum deviation of the points from the fitted plane ---*/ - max_diff = 0; - for (unsigned long i = 0; i < n; i ++) - { - sum = 0; - iCount = 0; - for (int j = 0; j < m; j ++) - { - if (skip_row[j] == 0) { - sum += coeff[iCount]*SU2_TYPE::GetValue(P[i*m+j]); - iCount++; - } - } - /*--- 1.0 is the arbitrary constant we are assuming when fitting the plane ---*/ - max_diff = max(abs(1.0-sum), max_diff); - } - - for (unsigned long i=0; i max_coeff) iCount = i; - } - - for (unsigned long i=0; i::min()) rbf = 0.0; - else rbf *= rbf*log(rbf); - break; - - case MULTI_QUADRIC: - case INV_MULTI_QUADRIC: - rbf = sqrt(1.0+rbf*rbf); - if(type == INV_MULTI_QUADRIC) rbf = 1.0/rbf; - break; - } - - return rbf; -} - -/*--- Symmetric matrix class definitions ---*/ -CSymmetricMatrix::CSymmetricMatrix() -{ - initialized = false; - inversed = false; - decomposed = none; - - val_vec = NULL; - decompose_vec = NULL; - inv_val_vec = NULL; - perm_vec = NULL; -} - -CSymmetricMatrix::~CSymmetricMatrix() -{ - if(val_vec) {delete [] val_vec;} - if(decompose_vec) {delete [] decompose_vec;} - if(inv_val_vec) {delete [] inv_val_vec;} - if(perm_vec) {delete [] perm_vec;} -} - -void CSymmetricMatrix::Initialize(int N) -{ - int i; - - sz = N; - num_val = sz*(sz+1)/2; - val_vec = new passivedouble [num_val]; - for (i=0; i abs(pivot) ) { - pivot = decompose_vec[CalcIdxFull(i, j)]; - pivot_idx = i; - interchange_row = true; - } - } - - if ( interchange_row ) { - for ( k=0; k=sz || j<0 || j>=sz) { - throw out_of_range("Index to access matrix out of bounds."); - } -} - -void CSymmetricMatrix::Write(int i, int j, const su2double& val) -{ - CheckBounds(i,j); - val_vec[CalcIdx(i, j)] = SU2_TYPE::GetValue(val); -} - -passivedouble CSymmetricMatrix::Read(int i, int j) -{ - CheckBounds(i,j); - return val_vec[CalcIdx(i, j)]; -} - -passivedouble CSymmetricMatrix::ReadL(int i, int j) -{ - passivedouble *p = NULL; - - CheckBounds(i,j); - - if (decompose_vec) { p = decompose_vec; } - else { p = val_vec; } - - switch (decomposed) { - case cholesky: - if (i>=j) return p[CalcIdx(i, j)]; - else return 0.0; - - case lu: - if (i>j) return p[CalcIdxFull(i, j)]; - else return passivedouble(i==j); - - default: - throw invalid_argument("Matrix not decomposed yet or results have been deleted."); - } -} - -passivedouble CSymmetricMatrix::ReadU(int i, int j) -{ - passivedouble *p = NULL; - - CheckBounds(i,j); - - if (decompose_vec){ p = decompose_vec; } - else {p = val_vec;} - - switch (decomposed) { - case cholesky: - return 0.0; - - case lu: - if (j>=i) return p[CalcIdxFull(j, i)]; - else return 0.0; - - default: - throw invalid_argument("Matrix not decomposed yet or results have been deleted."); - } -} - -double CSymmetricMatrix::ReadInv(int i, int j) -{ - passivedouble *p = NULL; - - CheckBounds(i,j); - - if (inversed) { - if (inv_val_vec) { p = inv_val_vec; } - else { p = val_vec; } - - return p[CalcIdx(i, j)]; - } - else { - throw invalid_argument("Matrix inverse not calculated yet."); - } -} diff --git a/Common/src/linear_algebra/CSysMatrix.cpp b/Common/src/linear_algebra/CSysMatrix.cpp index 577caff65fc7..60a42b635428 100644 --- a/Common/src/linear_algebra/CSysMatrix.cpp +++ b/Common/src/linear_algebra/CSysMatrix.cpp @@ -89,8 +89,9 @@ CSysMatrix::~CSysMatrix(void) { template void CSysMatrix::Initialize(unsigned long npoint, unsigned long npointdomain, - unsigned short nvar, unsigned short neqn, - bool EdgeConnect, CGeometry *geometry, CConfig *config) { + unsigned short nvar, unsigned short neqn, + bool EdgeConnect, CGeometry *geometry, + CConfig *config, bool needTranspPtr) { assert(omp_get_thread_num()==0 && "Only the master thread is allowed to initialize the matrix."); @@ -126,10 +127,13 @@ void CSysMatrix::Initialize(unsigned long npoint, unsigned long npoi const auto& csr = geometry->GetSparsePattern(type,0); + nnz = csr.getNumNonZeros(); row_ptr = csr.outerPtr(); col_ind = csr.innerIdx(); dia_ptr = csr.diagPtr(); - nnz = csr.getNumNonZeros(); + + if (needTranspPtr) + col_ptr = geometry->GetTransposeSparsePatternMap(type).data(); if (type == ConnectivityType::FiniteVolume) edge_ptr.ptr = geometry->GetEdgeToSparsePatternMap().data(); @@ -189,24 +193,6 @@ void CSysMatrix::Initialize(unsigned long npoint, unsigned long npoi omp_partitions[part] = part * pts_per_part; omp_partitions[omp_num_parts] = nPointDomain; - /*--- For coarse grid levels setup a structure that allows doing a column sum efficiently, - * essentially the transpose of the col_ind, this allows populating the matrix by setting - * the off-diagonal entries and then setting the diagonal ones as the sum of column - * (excluding the diagonal itself). We use the fact that the pattern is symmetric. ---*/ - - if ((geometry->GetMGLevel() != MESH_0) && (omp_get_max_threads() > 1)) { - - col_ptr.resize(nnz, nullptr); - - SU2_OMP_PARALLEL_(for schedule(static,omp_heavy_size)) - for (auto iPoint = 0ul; iPoint < nPoint; ++iPoint) { - for (auto k = row_ptr[iPoint]; k < row_ptr[iPoint+1]; ++k) { - auto jPoint = col_ind[k]; - col_ptr[k] = GetBlock(jPoint, iPoint); - } - } - } - /*--- Generate MKL Kernels ---*/ #ifdef USE_MKL @@ -1333,8 +1319,10 @@ void CSysMatrix::SetDiagonalAsColumnSum() { auto block_ii = &matrix[dia_ptr[iPoint]*nVar*nEqn]; + for (auto k = 0ul; k < nVar*nEqn; ++k) block_ii[k] = 0.0; + for (auto k = row_ptr[iPoint]; k < row_ptr[iPoint+1]; ++k) { - auto block_ji = col_ptr[k]; + auto block_ji = &matrix[col_ptr[k]*nVar*nEqn]; if (block_ji != block_ii) MatrixSubtraction(block_ii, block_ji, block_ii); } } @@ -1395,7 +1383,7 @@ void CSysMatrix::ComputePastixPreconditioner(const CSysVector void CSysMatrix::BuildPastixPreconditioner(CGeometry *geometry, CConfig *config, unsigned short kind_fact, bool transposed) { diff --git a/Common/src/linear_algebra/CSysSolve.cpp b/Common/src/linear_algebra/CSysSolve.cpp index 9bafc45d8620..6f041e89f8bb 100644 --- a/Common/src/linear_algebra/CSysSolve.cpp +++ b/Common/src/linear_algebra/CSysSolve.cpp @@ -206,6 +206,7 @@ unsigned long CSysSolve::CG_LinSolver(const CSysVector & * do this since the working vectors are shared. ---*/ if (!cg_ready) { + SU2_OMP_BARRIER SU2_OMP_MASTER { auto nVar = b.GetNVar(); @@ -348,6 +349,7 @@ unsigned long CSysSolve::FGMRES_LinSolver(const CSysVector::BCGSTAB_LinSolver(const CSysVector::Smoother_LinSolver(const CSysVector::Solve(CSysMatrix & Jacobian, co /*--- Enforce a hard limit on total number of iterations ---*/ unsigned long IterLimit = min(RestartIter, MaxIter-IterLinSol); IterLinSol += FGMRES_LinSolver(*LinSysRes_ptr, *LinSysSol_ptr, mat_vec, *precond, SolverTol, IterLimit, residual, ScreenOutput, config); - if ( residual < SolverTol*norm0 ) break; + if ( residual <= SolverTol*norm0 ) break; } break; case SMOOTHER: diff --git a/Common/src/meson.build b/Common/src/meson.build index 37eca9d0d903..ca9c048e70d4 100644 --- a/Common/src/meson.build +++ b/Common/src/meson.build @@ -14,7 +14,6 @@ common_src =files(['geometry_structure_fem_part.cpp', 'fem_gauss_jacobi_quadrature.cpp', 'wall_model.cpp', 'adt_structure.cpp', - 'interpolation_structure.cpp', 'mpi_structure.cpp', 'fem_cgns_elements.cpp', 'CMultiGridQueue.cpp']) @@ -26,6 +25,7 @@ subdir('geometry/elements') subdir('geometry/dual_grid') subdir('geometry/primal_grid') subdir('geometry/meshreader') +subdir('interface_interpolation') if get_option('enable-normal') common = static_library('SU2Common', diff --git a/Common/src/toolboxes/CSymmetricMatrix.cpp b/Common/src/toolboxes/CSymmetricMatrix.cpp new file mode 100644 index 000000000000..5845daac5496 --- /dev/null +++ b/Common/src/toolboxes/CSymmetricMatrix.cpp @@ -0,0 +1,293 @@ +/*! + * \file CSymmetricMatrix.cpp + * \brief Implementation of dense symmetric matrix helper class (see hpp). + * \author Joel Ho, P. Gomes + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, 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/CSymmetricMatrix.hpp" +#include "../../include/mpi_structure.hpp" + +#if defined(HAVE_MKL) +#include "mkl.h" +#ifndef HAVE_LAPACK +#define HAVE_LAPACK +#endif +#elif defined(HAVE_LAPACK) +/*--- Lapack / Blas routines used in CSymmetricMatrix. ---*/ +extern "C" void dsytrf_(const char*, const int*, passivedouble*, const int*, int*, passivedouble*, const int*, int*); +extern "C" void dsytri_(const char*, const int*, passivedouble*, const int*, const int*, passivedouble*, int*); +extern "C" void dpotrf_(const char*, const int*, passivedouble*, const int*, int*); +extern "C" void dpotri_(const char*, const int*, passivedouble*, const int*, int*); +extern "C" void dsymm_(const char*, const char*, const int*, const int*, const passivedouble*, const passivedouble*, + const int*, const passivedouble*, const int*, const passivedouble*, passivedouble*, const int*); +#define DSYMM dsymm_ +#endif + +void CSymmetricMatrix::Initialize(int N) { mat.resize(N,N); } + +void CSymmetricMatrix::CholeskyDecompose() +{ +#ifndef HAVE_LAPACK + int j; + for (j = 0; j < Size(); ++j) { + passivedouble sum = 0.0; + for (int k = 0; k < j; ++k) sum -= pow(Get(j,k), 2); + sum += Get(j,j); + if (sum < 0.0) break; // not SPD + Set(j, j, sqrt(sum)); + + for (int i = j+1; i < Size(); ++i) { + passivedouble sum = 0.0; + for (int k = 0; k < j; ++k) sum -= Get(i,k) * Get(j,k); + sum += Get(i,j); + Set(i, j, sum / Get(j,j)); + } + } + if (j!=Size()) SU2_MPI::Error("LLT factorization failed.", CURRENT_FUNCTION); +#endif +} + +void CSymmetricMatrix::LUDecompose(su2passivematrix& decomp, vector& perm) const +{ +#ifndef HAVE_LAPACK + const int sz = Size(); + + /*--- Copy matrix values to LU matrix, init permutation vec. ---*/ + decomp.resize(sz,sz); + perm.resize(sz); + + for (int i = 0; i < sz; ++i) { + perm[i] = i; + for (int j = i; j < sz; ++j) decomp(j,i) = decomp(i,j) = Get(i,j); + } + + /*--- Decompose LU matrix. ---*/ + for (int j = 0; j < sz-1; ++j) { + /*--- Search for maximum pivot and interchange rows. ---*/ + passivedouble pivot = decomp(j,j); + int pivot_idx = j; + for (int i = j+1; i < sz; ++i) + if (abs(decomp(i,j)) > abs(pivot)) { + pivot = decomp(i,j); + pivot_idx = i; + } + + if (pivot_idx != j) { + swap(perm[j], perm[pivot_idx]); + for (int k = 0; k < sz; ++k) + swap(decomp(j,k), decomp(pivot_idx,k)); + } + + /*--- Perform elimination. ---*/ + for (int k = j+1; k < sz; ++k) decomp(k,j) /= pivot; + + for (int k = j+1; k < sz; ++k) + for (int i = j+1; i < sz; ++i) + decomp(i,k) -= decomp(j,k)*decomp(i,j); + } +#endif +} + +void CSymmetricMatrix::CalcInv(bool is_spd) +{ +#ifndef HAVE_LAPACK + const int sz = Size(); + + /*--- Compute inverse from decomposed matrices. ---*/ + if (is_spd) + { + CholeskyDecompose(); + + /*--- Initialize inverse matrix. ---*/ + CSymmetricMatrix inv(sz); + + /*--- Compute L inverse. ---*/ + /*--- Solve smaller and smaller systems. ---*/ + for (int j = 0; j < sz; ++j) { + /*--- Forward substitution. ---*/ + inv(j,j) = 1.0 / Get(j,j); + + for (int i = j+1; i < sz; ++i) { + passivedouble sum = 0.0; + for (int k = j; k < i; ++k) sum -= Get(i,k) * inv(k,j); + inv(i,j) = sum / Get(i,i); + } + } // L inverse in inv + + /*--- Multiply inversed matrices overwrite mat. ---*/ + for (int j = 0; j < sz; ++j) + for (int i = j; i < sz; ++i) { + passivedouble sum = 0.0; + for (int k = i; k < sz; ++k) sum += inv(k,i) * inv(k,j); + Set(i, j, sum); + } + } + else + { + su2passivematrix decomp; + vector perm; + LUDecompose(decomp, perm); + + /*--- Alias mat. ---*/ + auto& inv = mat; + + /*--- Invert L and U matrices in place. ---*/ + for (int j = 0; j < sz; ++j) { + inv(j,j) = 1.0 / decomp(j,j); + + for (int i = j+1; i < sz; ++i) { + inv(i,j) = -decomp(i,j); + inv(j,i) = -decomp(j,i) * inv(j,j); + + for (int k = j+1; k < i; ++k) { + inv(i,j) -= decomp(i,k) * inv(k,j); + inv(j,i) -= decomp(k,i) * inv(j,k); + } + if (j+1 <= i) inv(j,i) /= decomp(i,i); + } + } + // inverses in mat + + /*--- Multiply U_inv by L_inv, overwrite decomp_vec. ---*/ + for (int i = 0; i < sz; ++i) + for (int j = 0; j < sz; ++j) { + decomp(i,j) = 0.0; + for (int k = max(i,j); k < sz; ++k) + decomp(i,j) += inv(i,k) * ((k==j)? 1.0 : inv(k,j)); + } + + /*--- Permute multiplied matrix to recover A_inv, overwrite mat. ---*/ + for (int j = 0; j < sz; ++j) { + int k = perm[j]; + for (int i = k; i < sz; ++i) Set(i, k, decomp(i,j)); + } + } +#endif +} + +void CSymmetricMatrix::CalcInv_sytri() +{ +#ifdef HAVE_LAPACK + const char uplo = 'L'; + const int sz = Size(); + int info; + vector ipiv(sz); + + /*--- Query the optimum work size. ---*/ + int query = -1; passivedouble tmp; + dsytrf_(&uplo, &sz, mat.data(), &sz, ipiv.data(), &tmp, &query, &info); + query = static_cast(tmp); + vector work(query); + + /*--- Factorize and invert. ---*/ + dsytrf_(&uplo, &sz, mat.data(), &sz, ipiv.data(), work.data(), &query, &info); + if (info!=0) SU2_MPI::Error("LDLT factorization failed.", CURRENT_FUNCTION); + dsytri_(&uplo, &sz, mat.data(), &sz, ipiv.data(), work.data(), &info); + if (info!=0) SU2_MPI::Error("Inversion with LDLT factorization failed.", CURRENT_FUNCTION); +#endif +} + +void CSymmetricMatrix::CalcInv_potri() +{ +#ifdef HAVE_LAPACK + const char uplo = 'L'; + const int sz = Size(); + int info; + + dpotrf_(&uplo, &sz, mat.data(), &sz, &info); + if (info!=0) SU2_MPI::Error("LLT factorization failed.", CURRENT_FUNCTION); + dpotri_(&uplo, &sz, mat.data(), &sz, &info); + if (info!=0) SU2_MPI::Error("Inversion with LLT factorization failed.", CURRENT_FUNCTION); +#endif +} + +void CSymmetricMatrix::Invert(const bool is_spd) +{ +#ifdef HAVE_LAPACK + if(is_spd) CalcInv_potri(); + else CalcInv_sytri(); +#else + CalcInv(is_spd); +#endif +} + +void CSymmetricMatrix::MatMatMult(const char side, + const su2passivematrix& mat_in, + su2passivematrix& mat_out) const +{ + /*--- Left side: mat_out = this * mat_in. ---*/ + if (side == 'L' || side == 'l') { + const int M = Size(), N = mat_in.cols(); + assert(M == mat_in.rows()); + + mat_out.resize(M,N); + +#ifdef HAVE_LAPACK + /*--- Right and lower because matrices are in row major order. ---*/ + const char side = 'R', uplo = 'L'; + const passivedouble alpha = 1.0, beta = 0.0; + DSYMM(&side, &uplo, &N, &M, &alpha, mat.data(), &M, + mat_in.data(), &N, &beta, mat_out.data(), &N); +#else // Naive product + for (int i = 0; i < M; ++i) + for (int j = 0; j < N; ++j) { + mat_out(i,j) = 0.0; + for (int k = 0; k < M; ++k) + mat_out(i,j) += Get(i,k) * mat_in(k,j); + } +#endif + } + /*--- Right_side: mat_out = mat_in * this. ---*/ + else { + const int M = mat_in.rows(), N = Size(); + assert(N == mat_in.cols()); + + mat_out.resize(M,N); + +#ifdef HAVE_LAPACK + /*--- Left and lower because matrices are in row major order. ---*/ + const char side = 'L', uplo = 'L'; + const passivedouble alpha = 1.0, beta = 0.0; + DSYMM(&side, &uplo, &N, &M, &alpha, mat.data(), &N, + mat_in.data(), &N, &beta, mat_out.data(), &N); +#else // Naive product + for (int i = 0; i < M; ++i) + for (int j = 0; j < N; ++j) { + mat_out(i,j) = 0.0; + for (int k = 0; k < N; ++k) + mat_out(i,j) += mat_in(i,k) * Get(k,j); + } +#endif + } +} + +su2passivematrix CSymmetricMatrix::StealData() +{ + /*--- Fill lower triangular part. ---*/ + for (int i = 1; i < Size(); ++i) + for (int j = 0; j < i; ++j) + mat(i,j) = mat(j,i); + + return move(mat); +} diff --git a/Common/src/toolboxes/meson.build b/Common/src/toolboxes/meson.build index c753d5142564..123713157639 100644 --- a/Common/src/toolboxes/meson.build +++ b/Common/src/toolboxes/meson.build @@ -1,5 +1,6 @@ common_src += files(['CLinearPartitioner.cpp', 'printing_toolbox.cpp', - 'C1DInterpolation.cpp']) + 'C1DInterpolation.cpp', + 'CSymmetricMatrix.cpp']) subdir('MMS') diff --git a/SU2_CFD/include/SU2_CFD.hpp b/SU2_CFD/include/SU2_CFD.hpp index e16bc95833b7..5ab8379ddd60 100644 --- a/SU2_CFD/include/SU2_CFD.hpp +++ b/SU2_CFD/include/SU2_CFD.hpp @@ -44,7 +44,6 @@ #include "../../Common/include/geometry/CGeometry.hpp" #include "../../Common/include/grid_movement_structure.hpp" #include "../../Common/include/CConfig.hpp" -#include "../../Common/include/interpolation_structure.hpp" #include "../include/definition_structure.hpp" #include "../include/iteration_structure.hpp" #include "../include/interfaces/CInterface.hpp" diff --git a/SU2_CFD/include/drivers/CDriver.hpp b/SU2_CFD/include/drivers/CDriver.hpp index 2c5a05905a67..5889aa6cee53 100644 --- a/SU2_CFD/include/drivers/CDriver.hpp +++ b/SU2_CFD/include/drivers/CDriver.hpp @@ -37,11 +37,11 @@ #include "../../../Common/include/geometry/CGeometry.hpp" #include "../../../Common/include/grid_movement_structure.hpp" -#include "../../../Common/include/interpolation_structure.hpp" using namespace std; class COutputLegacy; +class CInterpolator; /*! * \class CDriver diff --git a/SU2_CFD/include/interfaces/CInterface.hpp b/SU2_CFD/include/interfaces/CInterface.hpp index 6c3a2c8cab84..ecd5b540a4bd 100644 --- a/SU2_CFD/include/interfaces/CInterface.hpp +++ b/SU2_CFD/include/interfaces/CInterface.hpp @@ -7,7 +7,7 @@ * * SU2 Project Website: https://su2code.github.io * - * The SU2 Project is maintained by the SU2 Foundation + * The SU2 Project is maintained by the SU2 Foundation * (http://su2foundation.org) * * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md) @@ -56,20 +56,20 @@ using namespace std; class CInterface { protected: - int rank, /*!< \brief MPI Rank. */ - size; /*!< \brief MPI Size. */ + const int rank; /*!< \brief MPI Rank. */ + const int size; /*!< \brief MPI Size. */ - su2double *Physical_Constants; - su2double *Donor_Variable; - su2double *Target_Variable; - bool valAggregated; + su2double *Physical_Constants = nullptr; + su2double *Donor_Variable = nullptr; + su2double *Target_Variable = nullptr; + bool valAggregated = false; /*--- Mixing Plane interface variable ---*/ - su2double *SpanValueCoeffTarget; - unsigned short *SpanLevelDonor; - unsigned short nSpanMaxAllZones; + su2double *SpanValueCoeffTarget = nullptr; + unsigned short *SpanLevelDonor = nullptr; + unsigned short nSpanMaxAllZones = 0; - unsigned short nVar; + unsigned short nVar = 0; public: /*! diff --git a/SU2_CFD/include/numerics/elasticity/CFEALinearElasticity.hpp b/SU2_CFD/include/numerics/elasticity/CFEALinearElasticity.hpp index acda3944b9b2..dd564505bdc4 100644 --- a/SU2_CFD/include/numerics/elasticity/CFEALinearElasticity.hpp +++ b/SU2_CFD/include/numerics/elasticity/CFEALinearElasticity.hpp @@ -109,7 +109,7 @@ class CFEAMeshElasticity final : public CFEALinearElasticity { * \param[in] val_nVar - Number of variables of the problem. * \param[in] config - Definition of the particular problem. */ - CFEAMeshElasticity(unsigned short val_nDim, unsigned short val_nVar, unsigned long val_nElem, CConfig *config); + CFEAMeshElasticity(unsigned short val_nDim, unsigned short val_nVar, unsigned long val_nElem, const CConfig *config); /*! * \brief Destructor of the class. diff --git a/SU2_CFD/include/solvers/CEulerSolver.hpp b/SU2_CFD/include/solvers/CEulerSolver.hpp index df1944c6129a..8b335d7b3384 100644 --- a/SU2_CFD/include/solvers/CEulerSolver.hpp +++ b/SU2_CFD/include/solvers/CEulerSolver.hpp @@ -264,16 +264,18 @@ class CEulerSolver : public CSolver { #ifdef HAVE_OMP vector > EdgeColoring; /*!< \brief Edge colors. */ + bool ReducerStrategy = false; /*!< \brief If the reducer strategy is in use. */ #else array,1> EdgeColoring; + /*--- Never use the reducer strategy if compiling for MPI-only. ---*/ + static constexpr bool ReducerStrategy = false; #endif - unsigned long ColorGroupSize; /*!< \brief Group size used for coloring, chunk size in edge loops must be a multiple of this. */ - /*--- Edge fluxes, for OpenMP parallelization on coarse grids. As it is difficult to - * color them, we first store the fluxes and then compute the sum for each cell. - * This strategy is thread-safe but lower performance than writting to both end - * points of each edge, so we only use it when necessary, i.e. coarse grids and - * with more than one thread per MPI rank. ---*/ + /*--- Edge fluxes, for OpenMP parallelization off difficult-to-color grids. + * We first store the fluxes and then compute the sum for each cell. + * This strategy is thread-safe but lower performance than writting to both + * end points of each edge, so we only use it when necessary, i.e. when the + * coloring does not allow "enough" parallelism. ---*/ CSysVector EdgeFluxes; /*!< \brief Flux across each edge. */ @@ -300,23 +302,27 @@ class CEulerSolver : public CSolver { void SumEdgeFluxes(CGeometry* geometry); /*! - * \brief Update the AoA and freestream velocity at the farfield. + * \brief Preprocessing actions common to the Euler and NS solvers. * \param[in] geometry - Geometrical definition of the problem. * \param[in] solver_container - Container vector with all the solutions. * \param[in] config - Definition of the particular problem. - * \param[in] iMesh - current mesh level for the multigrid. + * \param[in] iRKStep - Current step of the Runge-Kutta iteration. + * \param[in] RunTime_EqSystem - System of equations which is going to be solved. * \param[in] Output - boolean to determine whether to print output. */ - void SetFarfield_AoA(CGeometry *geometry, CSolver **solver_container, - CConfig *config, unsigned short iMesh, bool Output); + void CommonPreprocessing(CGeometry *geometry, CSolver **solver_container, CConfig *config, unsigned short iMesh, + unsigned short iRKStep, unsigned short RunTime_EqSystem, bool Output); /*! - * \brief Compute a pressure sensor switch. + * \brief Update the AoA and freestream velocity at the farfield. * \param[in] geometry - Geometrical definition of the problem. * \param[in] solver_container - Container vector with all the solutions. * \param[in] config - Definition of the particular problem. + * \param[in] iMesh - current mesh level for the multigrid. + * \param[in] Output - boolean to determine whether to print output. */ - void SetCentered_Dissipation_Sensor(CGeometry *geometry, CConfig *config); + void SetFarfield_AoA(CGeometry *geometry, CSolver **solver_container, + CConfig *config, unsigned short iMesh, bool Output); /*! * \brief Compute Ducros Sensor for Roe Dissipation. @@ -360,11 +366,13 @@ class CEulerSolver : public CSolver { void SetMax_Eigenvalue(CGeometry *geometry, CConfig *config); /*! - * \brief Compute the undivided laplacian for the solution, except the energy equation. + * \brief Compute the undivided laplacian for the solution and the + * dissipation sensor for centered schemes. * \param[in] geometry - Geometrical definition of the problem. * \param[in] config - Definition of the particular problem. */ - void SetUndivided_Laplacian(CGeometry *geometry, CConfig *config); + void SetUndivided_Laplacian_And_Centered_Dissipation_Sensor(CGeometry *geometry, + CConfig *config); /*! * \brief A virtual member. @@ -373,7 +381,6 @@ class CEulerSolver : public CSolver { */ inline virtual void SetRoe_Dissipation(CGeometry *geometry, CConfig *config) { } -private: /*! * \brief Compute the velocity^2, SoundSpeed, Pressure, Enthalpy, Viscosity. * \param[in] solver_container - Container vector with all the solutions. @@ -381,9 +388,8 @@ class CEulerSolver : public CSolver { * \param[in] Output - boolean to determine whether to print output. * \return - The number of non-physical points. */ - unsigned long SetPrimitive_Variables(CSolver **solver_container, - CConfig *config, - bool Output); + virtual unsigned long SetPrimitive_Variables(CSolver **solver_container, + CConfig *config, bool Output); protected: @@ -438,7 +444,6 @@ class CEulerSolver : public CSolver { return sqrt(Vel2); } - /*! * \brief Compute the density multiply by energy at the infinity. * \return Value of the density multiply by energy at the infinity. diff --git a/SU2_CFD/include/solvers/CFEASolver.hpp b/SU2_CFD/include/solvers/CFEASolver.hpp index eac3ff18a3bb..eae891063a05 100644 --- a/SU2_CFD/include/solvers/CFEASolver.hpp +++ b/SU2_CFD/include/solvers/CFEASolver.hpp @@ -46,7 +46,7 @@ class CFEASolver : public CSolver { su2double Total_CFEA; /*!< \brief Total FEA coefficient for all the boundaries. */ - unsigned short *iElem_iDe; /*!< \brief For DE cases, ID of the region considered for each iElem. */ + unsigned short *iElem_iDe = nullptr; /*!< \brief For DE cases, ID of the region considered for each iElem. */ su2double a_dt[9]; /*!< \brief Integration constants. */ @@ -77,15 +77,19 @@ class CFEASolver : public CSolver { #ifdef HAVE_OMP vector > ElemColoring; /*!< \brief Element colors. */ + bool LockStrategy = false; /*!< \brief Whether to use an OpenMP lock to guard updates of the Jacobian. */ + vector UpdateLocks; /*!< \brief Locks that may be used to protect accesses to CSysMatrix/Vector in element loops. */ #else - array,1> ElemColoring; + array,1> ElemColoring; /*--- Behaves like a normal integer type. ---*/ + static constexpr bool LockStrategy = false; /*--- Lock strategy is never needed for MPI-only. ---*/ + DummyVectorOfLocks UpdateLocks; #endif - unsigned long ColorGroupSize; /*!< \brief Group size used for coloring, chunk size must be a multiple of this. */ - 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 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. */ - unsigned long nElement; /*!< \brief Number of elements. */ + unsigned long nElement; /*!< \brief Number of elements. */ /*! * \brief The highest level in the variable hierarchy this solver can safely use, @@ -116,6 +120,12 @@ class CFEASolver : public CSolver { } } + /*! + * \brief Actions required to initialize the supporting variables for hybrid parallel execution. + * \param[in] geometry - Geometrical definition of the problem. + */ + void HybridParallelInitialization(CGeometry* geometry); + /*! * \brief Set container of element properties. * \param[in] geometry - Geometrical definition of the problem. @@ -170,8 +180,8 @@ class CFEASolver : public CSolver { CSysMatrix MassMatrix; /*!< \brief Sparse structure for storing the mass matrix. */ - CElement*** element_container; /*!< \brief Vector which the define the finite element structure for each problem. */ - CProperty** element_properties; /*!< \brief Vector which stores the properties of each element */ + CElement*** element_container = nullptr; /*!< \brief Vector which the define the finite element structure for each problem. */ + CProperty** element_properties = nullptr; /*!< \brief Vector which stores the properties of each element */ /*! * \brief Constructor of the class. diff --git a/SU2_CFD/include/solvers/CMeshSolver.hpp b/SU2_CFD/include/solvers/CMeshSolver.hpp index a39c571bd3e1..0c33951be053 100644 --- a/SU2_CFD/include/solvers/CMeshSolver.hpp +++ b/SU2_CFD/include/solvers/CMeshSolver.hpp @@ -48,12 +48,6 @@ class CMeshSolver final : public CFEASolver { su2double MinDistance; su2double MaxDistance; - su2double E; /*!< \brief Young's modulus of elasticity. */ - su2double Nu; /*!< \brief Poisson's ratio. */ - - su2double Mu; /*!< \brief Lame's coeficient. */ - su2double Lambda; /*!< \brief Lame's coeficient. */ - vector element; /*!< \brief Vector which stores element information for each problem. */ /*! diff --git a/SU2_CFD/include/solvers/CNSSolver.hpp b/SU2_CFD/include/solvers/CNSSolver.hpp index 0566e637c363..aa0bcfe59b6d 100644 --- a/SU2_CFD/include/solvers/CNSSolver.hpp +++ b/SU2_CFD/include/solvers/CNSSolver.hpp @@ -68,7 +68,6 @@ class CNSSolver final : public CEulerSolver { */ void SetRoe_Dissipation(CGeometry *geometry, CConfig *config) override; -private: /*! * \brief Compute the velocity^2, SoundSpeed, Pressure, Enthalpy, Viscosity. * \param[in] solver_container - Container vector with all the solutions. @@ -77,8 +76,7 @@ class CNSSolver final : public CEulerSolver { * \return - The number of non-physical points. */ unsigned long SetPrimitive_Variables(CSolver **solver_container, - CConfig *config, - bool Output); + CConfig *config, bool Output) override; protected: diff --git a/SU2_CFD/include/solvers/CTurbSolver.hpp b/SU2_CFD/include/solvers/CTurbSolver.hpp index cfbc0aa44bae..ce4adf446c8f 100644 --- a/SU2_CFD/include/solvers/CTurbSolver.hpp +++ b/SU2_CFD/include/solvers/CTurbSolver.hpp @@ -55,7 +55,7 @@ class CTurbSolver : public CSolver { Gamma_Minus_One, /*!< \brief Fluids's Gamma - 1.0 . */ ***Inlet_TurbVars = nullptr; /*!< \brief Turbulence variables at inlet profiles */ - /* Sliding meshes variables */ + /*--- Sliding meshes variables. ---*/ su2double ****SlidingState = nullptr; int **SlidingStateNodes = nullptr; @@ -64,10 +64,15 @@ class CTurbSolver : public CSolver { #ifdef HAVE_OMP vector > EdgeColoring; /*!< \brief Edge colors. */ + bool ReducerStrategy = false; /*!< \brief If the reducer strategy is in use. */ #else array,1> EdgeColoring; + /*--- Never use the reducer strategy if compiling for MPI-only. ---*/ + static constexpr bool ReducerStrategy = false; #endif - unsigned long ColorGroupSize; /*!< \brief Group size used for coloring, chunk size in edge loops must be a multiple of this. */ + + /*--- Edge fluxes for reducer strategy (see the notes in CEulerSolver.hpp). ---*/ + CSysVector EdgeFluxes; /*!< \brief Flux across each edge. */ /*! * \brief The highest level in the variable hierarchy this solver can safely use. @@ -79,6 +84,28 @@ class CTurbSolver : public CSolver { */ inline CVariable* GetBaseClassPointerToNodes() final { return nodes; } +private: + + /*! + * \brief Compute the viscous flux for the turbulent equation at a particular edge. + * \param[in] iEdge - Edge for which we want to compute the flux + * \param[in] geometry - Geometrical definition of the problem. + * \param[in] solver_container - Container vector with all the solutions. + * \param[in] numerics - Description of the numerical method. + * \param[in] config - Definition of the particular problem. + */ + void Viscous_Residual(unsigned long iEdge, + CGeometry *geometry, + CSolver **solver_container, + CNumerics *numerics, + CConfig *config); + + /*! + * \brief Sum the edge fluxes for each cell to populate the residual vector, only used on coarse grids. + * \param[in] geometry - Geometrical definition of the problem. + */ + void SumEdgeFluxes(CGeometry* geometry); + public: /*! @@ -106,29 +133,12 @@ class CTurbSolver : public CSolver { * \param[in] config - Definition of the particular problem. * \param[in] iMesh - Index of the mesh in multigrid computations. */ - void Upwind_Residual(CGeometry *geometry, CSolver **solver_container, CNumerics **numerics_container, CConfig *config, unsigned short iMesh) override; - /*! - * \brief Compute the viscous residuals for the turbulent equation. - * \param[in] geometry - Geometrical definition of the problem. - * \param[in] solver_container - Container vector with all the solutions. - * \param[in] numerics_container - Description of the numerical method. - * \param[in] config - Definition of the particular problem. - * \param[in] iMesh - Index of the mesh in multigrid computations. - * \param[in] iRKStep - Current step of the Runge-Kutta iteration. - */ - void Viscous_Residual(CGeometry *geometry, - CSolver **solver_container, - CNumerics **numerics_container, - CConfig *config, - unsigned short iMesh, - unsigned short iRKStep) override; - /*! * \brief Impose the Symmetry Plane boundary condition. * \param[in] geometry - Geometrical definition of the problem. diff --git a/SU2_CFD/include/variables/CVariable.hpp b/SU2_CFD/include/variables/CVariable.hpp index c9da45da7762..5e28e972abc8 100644 --- a/SU2_CFD/include/variables/CVariable.hpp +++ b/SU2_CFD/include/variables/CVariable.hpp @@ -1076,16 +1076,6 @@ class CVariable { */ inline virtual su2double GetSensor(unsigned long iPoint, unsigned long iSpecies) const { return 0.0; } - /*! - * \brief Set the value of the undivided laplacian of the solution. - * \param[in] iPoint - Point index. - * \param[in] iVar - Index of the variable. - * \param[in] val_undivided_laplacian - Value of the undivided solution for the index iVar. - */ - inline void SetUndivided_Laplacian(unsigned long iPoint, unsigned long iVar, su2double val_undivided_laplacian) { - Undivided_Laplacian(iPoint,iVar) = val_undivided_laplacian; - } - /*! * \brief Add the value of the undivided laplacian of the solution. * \param[in] iPoint - Point index. @@ -1107,13 +1097,13 @@ class CVariable { } /*! - * \brief Subtract the value of the undivided laplacian of the solution. + * \brief Increment the value of the undivided laplacian of the solution. * \param[in] iPoint - Point index. * \param[in] iVar - Variable of the undivided laplacian. * \param[in] val_und_lapl - Value of the undivided solution. */ - inline void SubtractUnd_Lapl(unsigned long iPoint, unsigned long iVar, su2double val_und_lapl) { - Undivided_Laplacian(iPoint, iVar) -= val_und_lapl; + inline void AddUnd_Lapl(unsigned long iPoint, unsigned long iVar, su2double val_und_lapl) { + Undivided_Laplacian(iPoint, iVar) += val_und_lapl; } /*! diff --git a/SU2_CFD/obj/Makefile.am b/SU2_CFD/obj/Makefile.am index 039a27cf3fca..a46d07ef0b79 100644 --- a/SU2_CFD/obj/Makefile.am +++ b/SU2_CFD/obj/Makefile.am @@ -124,8 +124,8 @@ libSU2Core_sources = ../src/definition_structure.cpp \ ../src/output/CAdjFlowCompOutput.cpp \ ../src/output/CAdjFlowIncOutput.cpp \ ../src/output/CMultizoneOutput.cpp \ - ../src/output/output_structure_legacy.cpp \ ../src/output/COutputFactory.cpp \ + ../src/output/output_structure_legacy.cpp \ ../src/python_wrapper_structure.cpp \ ../src/solvers/CAdjEulerSolver.cpp \ ../src/solvers/CAdjNSSolver.cpp \ diff --git a/SU2_CFD/src/SU2_CFD.cpp b/SU2_CFD/src/SU2_CFD.cpp index 9fac0ec50e99..229aaab1f7e1 100644 --- a/SU2_CFD/src/SU2_CFD.cpp +++ b/SU2_CFD/src/SU2_CFD.cpp @@ -25,7 +25,6 @@ * License along with SU2. If not, see . */ - #include "../include/SU2_CFD.hpp" /* LIBXSMM include files, if supported. */ diff --git a/SU2_CFD/src/drivers/CDriver.cpp b/SU2_CFD/src/drivers/CDriver.cpp index 85205cffa674..aec0ab14f9d2 100644 --- a/SU2_CFD/src/drivers/CDriver.cpp +++ b/SU2_CFD/src/drivers/CDriver.cpp @@ -38,6 +38,12 @@ #include "../../include/output/COutputFactory.hpp" #include "../../include/output/COutputLegacy.hpp" +#include "../../../Common/include/interface_interpolation/CMirror.hpp" +#include "../../../Common/include/interface_interpolation/CSlidingMesh.hpp" +#include "../../../Common/include/interface_interpolation/CIsoparametric.hpp" +#include "../../../Common/include/interface_interpolation/CNearestNeighbor.hpp" +#include "../../../Common/include/interface_interpolation/CRadialBasisFunction.hpp" + #include "../../include/interfaces/cfd/CConservativeVarsInterface.hpp" #include "../../include/interfaces/cfd/CMixingPlaneInterface.hpp" #include "../../include/interfaces/cfd/CSlidingInterface.hpp" @@ -83,10 +89,9 @@ #endif #include -CDriver::CDriver(char* confFile, - unsigned short val_nZone, - SU2_Comm MPICommunicator, bool dummy_geo):config_file_name(confFile), StartTime(0.0), StopTime(0.0), UsedTime(0.0), - TimeIter(0), nZone(val_nZone), StopCalc(false), fsi(false), fem_solver(false), dry_run(dummy_geo) { +CDriver::CDriver(char* confFile, unsigned short val_nZone, SU2_Comm MPICommunicator, bool dummy_geo) : + config_file_name(confFile), StartTime(0.0), StopTime(0.0), UsedTime(0.0), + TimeIter(0), nZone(val_nZone), StopCalc(false), fsi(false), fem_solver(false), dry_run(dummy_geo) { /*--- Initialize Medipack (must also be here so it is initialized from python) ---*/ #ifdef HAVE_MPI @@ -95,8 +100,6 @@ CDriver::CDriver(char* confFile, #endif #endif - unsigned short jZone; - SU2_MPI::SetComm(MPICommunicator); rank = SU2_MPI::GetRank(); @@ -144,13 +147,8 @@ CDriver::CDriver(char* confFile, /*--- Allocate transfer and interpolation container --- */ - interface_container[iZone] = new CInterface*[nZone]; - interpolator_container[iZone] = new CInterpolator*[nZone]; - - for (jZone = 0; jZone < nZone; jZone++){ - interface_container[iZone][jZone] = NULL; - interpolator_container[iZone][jZone] = NULL; - } + interface_container[iZone] = new CInterface*[nZone] (); + interpolator_container[iZone] = new CInterpolator*[nZone] (); for (iInst = 0; iInst < nInst[iZone]; iInst++){ @@ -207,7 +205,7 @@ CDriver::CDriver(char* confFile, /*--- Dynamic mesh processing. ---*/ DynamicMesh_Preprocessing(config_container[iZone], geometry_container[iZone][iInst], solver_container[iZone][iInst], - iteration_container[iZone][iInst], grid_movement[iZone][iInst], surface_movement[iZone]); + iteration_container[iZone][iInst], grid_movement[iZone][iInst], surface_movement[iZone]); /*--- Static mesh processing. ---*/ StaticMesh_Preprocessing(config_container[iZone], geometry_container[iZone][iInst], surface_movement[iZone]); @@ -2526,22 +2524,11 @@ void CDriver::Interface_Preprocessing(CConfig **config, CSolver***** solver, CGe unsigned short donorZone, targetZone; unsigned short nVar, nVarTransfer; - unsigned short nMarkerTarget, iMarkerTarget, nMarkerDonor, iMarkerDonor; - /*--- Initialize some useful booleans ---*/ bool fluid_donor, structural_donor, heat_donor; bool fluid_target, structural_target, heat_target; - bool discrete_adjoint = config[ZONE_0]->GetDiscrete_Adjoint(); - - int markDonor, markTarget, Donor_check, Target_check, iMarkerInt, nMarkerInt; - -#ifdef HAVE_MPI - int *Buffer_Recv_mark = NULL, iRank, nProcessor = size; - - if (rank == MASTER_NODE) - Buffer_Recv_mark = new int[nProcessor]; -#endif + const bool discrete_adjoint = config[ZONE_0]->GetDiscrete_Adjoint(); /*--- Coupling between zones ---*/ // There's a limit here, the interface boundary must connect only 2 zones @@ -2560,157 +2547,86 @@ void CDriver::Interface_Preprocessing(CConfig **config, CSolver***** solver, CGe continue; } - nMarkerInt = (int) ( config[donorZone]->GetMarker_n_ZoneInterface() / 2 ); + const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface() / 2; /*--- Loops on Interface markers to find if the 2 zones are sharing the boundary and to *--- determine donor and target marker tag ---*/ - for (iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { - - markDonor = -1; - markTarget = -1; - - /*--- On the donor side ---*/ - nMarkerDonor = config[donorZone]->GetnMarker_All(); - - for (iMarkerDonor = 0; iMarkerDonor < nMarkerDonor; iMarkerDonor++) { + for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { - /*--- If the tag GetMarker_All_ZoneInterface(iMarker) equals the index we are looping at ---*/ - if ( config[donorZone]->GetMarker_All_ZoneInterface(iMarkerDonor) == iMarkerInt ) { - /*--- We have identified the identifier for the interface marker ---*/ - markDonor = iMarkerDonor; - - break; - } + /* --- Check if zones are actually sharing the interface boundary, if not skip. ---*/ + if(!CInterpolator::CheckInterfaceBoundary(CInterpolator::Find_InterfaceMarker(config[donorZone],iMarkerInt), + CInterpolator::Find_InterfaceMarker(config[targetZone],iMarkerInt))) { + interface_types[donorZone][targetZone] = NO_COMMON_INTERFACE; + continue; } - /*--- On the target side ---*/ - nMarkerTarget = config[targetZone]->GetnMarker_All(); - - for (iMarkerTarget = 0; iMarkerTarget < nMarkerTarget; iMarkerTarget++) { - - /*--- If the tag GetMarker_All_ZoneInterface(iMarker) equals the index we are looping at ---*/ - if ( config[targetZone]->GetMarker_All_ZoneInterface(iMarkerTarget) == iMarkerInt ) { - /*--- We have identified the identifier for the interface marker ---*/ - markTarget = iMarkerTarget; - - break; - } - } + /*--- Set some boolean to properly allocate data structure later ---*/ + fluid_target = false; + structural_target = false; -#ifdef HAVE_MPI + fluid_donor = false; + structural_donor = false; - Donor_check = -1; - Target_check = -1; + heat_donor = false; + heat_target = false; - /*--- We gather a vector in MASTER_NODE that determines if the boundary is not on the processor because - * of the partition or because the zone does not include it ---*/ + switch ( config[targetZone]->GetKind_Solver() ) { - SU2_MPI::Gather(&markDonor , 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); + case EULER : case NAVIER_STOKES: case RANS: + case INC_EULER : case INC_NAVIER_STOKES: case INC_RANS: + case DISC_ADJ_INC_EULER: case DISC_ADJ_INC_NAVIER_STOKES: case DISC_ADJ_INC_RANS: + case DISC_ADJ_EULER: case DISC_ADJ_NAVIER_STOKES: case DISC_ADJ_RANS: + fluid_target = true; + break; - if (rank == MASTER_NODE) { - for (iRank = 0; iRank < nProcessor; iRank++) { - if( Buffer_Recv_mark[iRank] != -1 ) { - Donor_check = Buffer_Recv_mark[iRank]; + case FEM_ELASTICITY: case DISC_ADJ_FEM: + structural_target = true; + break; - break; - } - } + case HEAT_EQUATION: case DISC_ADJ_HEAT: + heat_target = true; + break; } - SU2_MPI::Bcast(&Donor_check , 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); + switch ( config[donorZone]->GetKind_Solver() ) { - SU2_MPI::Gather(&markTarget, 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); + case EULER : case NAVIER_STOKES: case RANS: + case INC_EULER : case INC_NAVIER_STOKES: case INC_RANS: + case DISC_ADJ_INC_EULER: case DISC_ADJ_INC_NAVIER_STOKES: case DISC_ADJ_INC_RANS: + case DISC_ADJ_EULER: case DISC_ADJ_NAVIER_STOKES: case DISC_ADJ_RANS: + fluid_donor = true; + break; - if (rank == MASTER_NODE){ - for (iRank = 0; iRank < nProcessor; iRank++){ - if( Buffer_Recv_mark[iRank] != -1 ){ - Target_check = Buffer_Recv_mark[iRank]; + case FEM_ELASTICITY: case DISC_ADJ_FEM: + structural_donor = true; + break; - break; - } - } + case HEAT_EQUATION : case DISC_ADJ_HEAT: + heat_donor = true; + break; } - SU2_MPI::Bcast(&Target_check, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - -#else - Donor_check = markDonor; - Target_check = markTarget; -#endif - - /* --- Check ifzones are actually sharing the interface boundary, if not skip ---*/ - if(Target_check == -1 || Donor_check == -1) { - interface_types[donorZone][targetZone] = NO_COMMON_INTERFACE; - continue; - } - - /*--- Set some boolean to properly allocate data structure later ---*/ - fluid_target = false; - structural_target = false; - - fluid_donor = false; - structural_donor = false; - - heat_donor = false; - heat_target = false; - - switch ( config[targetZone]->GetKind_Solver() ) { - - case EULER : case NAVIER_STOKES: case RANS: - case INC_EULER : case INC_NAVIER_STOKES: case INC_RANS: - case DISC_ADJ_INC_EULER: case DISC_ADJ_INC_NAVIER_STOKES: case DISC_ADJ_INC_RANS: - case DISC_ADJ_EULER: case DISC_ADJ_NAVIER_STOKES: case DISC_ADJ_RANS: - fluid_target = true; - break; - - case FEM_ELASTICITY: case DISC_ADJ_FEM: - structural_target = true; - break; - - case HEAT_EQUATION: case DISC_ADJ_HEAT: - heat_target = true; - break; - } - - switch ( config[donorZone]->GetKind_Solver() ) { - - case EULER : case NAVIER_STOKES: case RANS: - case INC_EULER : case INC_NAVIER_STOKES: case INC_RANS: - case DISC_ADJ_INC_EULER: case DISC_ADJ_INC_NAVIER_STOKES: case DISC_ADJ_INC_RANS: - case DISC_ADJ_EULER: case DISC_ADJ_NAVIER_STOKES: case DISC_ADJ_RANS: - fluid_donor = true; - break; - - case FEM_ELASTICITY: case DISC_ADJ_FEM: - structural_donor = true; - break; - - case HEAT_EQUATION : case DISC_ADJ_HEAT: - heat_donor = true; - break; - } - - /*--- Begin the creation of the communication pattern among zones ---*/ + /*--- Begin the creation of the communication pattern among zones ---*/ - /*--- Retrieve the number of conservative variables (for problems not involving structural analysis ---*/ - if (fluid_donor && fluid_target) - nVar = solver[donorZone][INST_0][MESH_0][FLOW_SOL]->GetnVar(); - else - /*--- If at least one of the components is structural ---*/ - nVar = nDim; + /*--- Retrieve the number of conservative variables (for problems not involving structural analysis ---*/ + if (fluid_donor && fluid_target) + nVar = solver[donorZone][INST_0][MESH_0][FLOW_SOL]->GetnVar(); + else + /*--- If at least one of the components is structural ---*/ + nVar = nDim; - if (rank == MASTER_NODE) cout << "From zone " << donorZone << " to zone " << targetZone << ": "; + if (rank == MASTER_NODE) cout << "From zone " << donorZone << " to zone " << targetZone << ": "; /*--- Match Zones ---*/ - if (rank == MASTER_NODE) cout << "Setting coupling "; + if (rank == MASTER_NODE) cout << "Setting coupling "; - bool conservative_interp = config[donorZone]->GetConservativeInterpolation(); + bool conservative_interp = config[donorZone]->GetConservativeInterpolation(); - /*--- Conditions for conservative interpolation are not met, we cannot fallback on the consistent approach - because CFlowTractionInterface relies on the information in config to be correct. ---*/ - if ( conservative_interp && targetZone == 0 && structural_target ) - SU2_MPI::Error("Conservative interpolation assumes the structural model mesh is evaluated second, " - "somehow this has not happened.",CURRENT_FUNCTION); + /*--- Conditions for conservative interpolation are not met, we cannot fallback on the consistent approach + because CFlowTractionInterface relies on the information in config to be correct. ---*/ + if ( conservative_interp && targetZone == 0 && structural_target ) + SU2_MPI::Error("Conservative interpolation assumes the structural model mesh is evaluated second, " + "somehow this has not happened.",CURRENT_FUNCTION); switch (config[donorZone]->GetKindInterpolation()) { @@ -2721,8 +2637,8 @@ void CDriver::Interface_Preprocessing(CConfig **config, CSolver***** solver, CGe "from opposite mesh." << endl; } else { - interpolation[donorZone][targetZone] = new CNearestNeighbor(geometry, config, donorZone, targetZone); - if (rank == MASTER_NODE) cout << "using a nearest-neighbor approach." << endl; + interpolation[donorZone][targetZone] = new CNearestNeighbor(geometry, config, donorZone, targetZone); + if (rank == MASTER_NODE) cout << "using a nearest-neighbor approach." << endl; } break; @@ -2733,107 +2649,110 @@ void CDriver::Interface_Preprocessing(CConfig **config, CSolver***** solver, CGe "from opposite mesh." << endl; } else { - interpolation[donorZone][targetZone] = new CIsoparametric(geometry, config, donorZone, targetZone); - if (rank == MASTER_NODE) cout << "using an isoparametric approach." << endl; + interpolation[donorZone][targetZone] = new CIsoparametric(geometry, config, donorZone, targetZone); + if (rank == MASTER_NODE) cout << "using an isoparametric approach." << endl; } break; - case WEIGHTED_AVERAGE: - interpolation[donorZone][targetZone] = new CSlidingMesh(geometry, config, donorZone, targetZone); - if (rank == MASTER_NODE) cout << "using an sliding mesh approach." << endl; + case WEIGHTED_AVERAGE: + interpolation[donorZone][targetZone] = new CSlidingMesh(geometry, config, donorZone, targetZone); + if (rank == MASTER_NODE) cout << "using an sliding mesh approach." << endl; - break; + break; case RADIAL_BASIS_FUNCTION: if ( conservative_interp && targetZone > 0 && structural_target ) { interpolation[donorZone][targetZone] = new CMirror(geometry, config, donorZone, targetZone); if (rank == MASTER_NODE) cout << "using a mirror approach: matching coefficients " "from opposite mesh." << endl; - } - else { - interpolation[donorZone][targetZone] = new CRadialBasisFunction(geometry, config, - donorZone, targetZone); - if (rank == MASTER_NODE) cout << "using a radial basis function approach." << endl; - } - break; } + else { + interpolation[donorZone][targetZone] = new CRadialBasisFunction(geometry, config, + donorZone, targetZone); + if (rank == MASTER_NODE) cout << "using a radial basis function approach." << endl; + } + break; + } + + if (interpolation[donorZone][targetZone]) + interpolation[donorZone][targetZone]->PrintStatistics(); /*--- Initialize the appropriate transfer strategy ---*/ - if (rank == MASTER_NODE) cout << "Transferring "; - - if (fluid_donor && structural_target) { - interface_types[donorZone][targetZone] = FLOW_TRACTION; - nVarTransfer = 2; - if(!discrete_adjoint) { - interface[donorZone][targetZone] = new CFlowTractionInterface(nVar, nVarTransfer, config[donorZone]); - } else { - interface[donorZone][targetZone] = new CDiscAdjFlowTractionInterface(nVar, nVarTransfer, config[donorZone]); + if (rank == MASTER_NODE) cout << "Transferring "; + + if (fluid_donor && structural_target) { + interface_types[donorZone][targetZone] = FLOW_TRACTION; + nVarTransfer = 2; + if(!discrete_adjoint) { + interface[donorZone][targetZone] = new CFlowTractionInterface(nVar, nVarTransfer, config[donorZone]); + } else { + interface[donorZone][targetZone] = new CDiscAdjFlowTractionInterface(nVar, nVarTransfer, config[donorZone]); + } + if (rank == MASTER_NODE) cout << "flow tractions. " << endl; } - if (rank == MASTER_NODE) cout << "flow tractions. " << endl; - } - else if (structural_donor && fluid_target) { - /*--- If we are using the new mesh solver, we transfer the total boundary displacements (not incremental) --*/ - if (solver_container[targetZone][INST_0][MESH_0][MESH_SOL] != NULL) { - interface_types[donorZone][targetZone] = BOUNDARY_DISPLACEMENTS; + else if (structural_donor && fluid_target) { + /*--- If we are using the new mesh solver, we transfer the total boundary displacements (not incremental) --*/ + if (solver_container[targetZone][INST_0][MESH_0][MESH_SOL] != NULL) { + interface_types[donorZone][targetZone] = BOUNDARY_DISPLACEMENTS; + nVarTransfer = 0; + interface[donorZone][targetZone] = new CDisplacementsInterface(nVar, nVarTransfer, config[donorZone]); + if (rank == MASTER_NODE) cout << "boundary displacements from the structural solver. " << endl; + } + /*--- We keep the legacy method temporarily until FSI-adjoint has been adapted ---*/ + /// TODO: LEGACY CLEANUP remove the "else" part and every class and enum referenced there, + /// add a check above to make sure MESH_SOL has been instantiated. + else { + nVarTransfer = 0; + if(!discrete_adjoint) { + interface_types[donorZone][targetZone] = STRUCTURAL_DISPLACEMENTS_LEGACY; + interface[donorZone][targetZone] = new CDisplacementsInterfaceLegacy(nVar, nVarTransfer, config[donorZone]); + } else { + interface_types[donorZone][targetZone] = STRUCTURAL_DISPLACEMENTS_DISC_ADJ; + interface[donorZone][targetZone] = new CDiscAdjDisplacementsInterfaceLegacy(nVar, nVarTransfer, config[donorZone]); + } + if (rank == MASTER_NODE) cout << "structural displacements (legacy). " << endl; + } + } + else if (fluid_donor && fluid_target) { + interface_types[donorZone][targetZone] = SLIDING_INTERFACE; + nVarTransfer = 0; + nVar = solver[donorZone][INST_0][MESH_0][FLOW_SOL]->GetnPrimVar(); + interface[donorZone][targetZone] = new CSlidingInterface(nVar, nVarTransfer, config[donorZone]); + if (rank == MASTER_NODE) cout << "sliding interface. " << endl; + } + else if (fluid_donor && heat_target) { + nVarTransfer = 0; + nVar = 4; + if(config[donorZone]->GetEnergy_Equation() || (config[donorZone]->GetKind_Regime() == COMPRESSIBLE)) + interface_types[donorZone][targetZone] = CONJUGATE_HEAT_FS; + else if (config[donorZone]->GetWeakly_Coupled_Heat()) + interface_types[donorZone][targetZone] = CONJUGATE_HEAT_WEAKLY_FS; + else { } + interface[donorZone][targetZone] = new CConjugateHeatInterface(nVar, nVarTransfer, config[donorZone]); + if (rank == MASTER_NODE) cout << "conjugate heat variables. " << endl; + } + else if (heat_donor && fluid_target) { nVarTransfer = 0; - interface[donorZone][targetZone] = new CDisplacementsInterface(nVar, nVarTransfer, config[donorZone]); - if (rank == MASTER_NODE) cout << "boundary displacements from the structural solver. " << endl; + nVar = 4; + if(config[targetZone]->GetEnergy_Equation() || (config[targetZone]->GetKind_Regime() == COMPRESSIBLE)) + interface_types[donorZone][targetZone] = CONJUGATE_HEAT_SF; + else if (config[targetZone]->GetWeakly_Coupled_Heat()) + interface_types[donorZone][targetZone] = CONJUGATE_HEAT_WEAKLY_SF; + else { } + interface[donorZone][targetZone] = new CConjugateHeatInterface(nVar, nVarTransfer, config[donorZone]); + if (rank == MASTER_NODE) cout << "conjugate heat variables. " << endl; + } + else if (heat_donor && heat_target) { + SU2_MPI::Error("Conjugate heat transfer between solids not implemented yet.", CURRENT_FUNCTION); } - /*--- We keep the legacy method temporarily until FSI-adjoint has been adapted ---*/ - /// TODO: LEGACY CLEANUP remove the "else" part and every class and enum referenced there, - /// add a check above to make sure MESH_SOL has been instantiated. else { + interface_types[donorZone][targetZone] = CONSERVATIVE_VARIABLES; nVarTransfer = 0; - if(!discrete_adjoint) { - interface_types[donorZone][targetZone] = STRUCTURAL_DISPLACEMENTS_LEGACY; - interface[donorZone][targetZone] = new CDisplacementsInterfaceLegacy(nVar, nVarTransfer, config[donorZone]); - } else { - interface_types[donorZone][targetZone] = STRUCTURAL_DISPLACEMENTS_DISC_ADJ; - interface[donorZone][targetZone] = new CDiscAdjDisplacementsInterfaceLegacy(nVar, nVarTransfer, config[donorZone]); - } - if (rank == MASTER_NODE) cout << "structural displacements (legacy). " << endl; + interface[donorZone][targetZone] = new CConservativeVarsInterface(nVar, nVarTransfer, config[donorZone]); + if (rank == MASTER_NODE) cout << "generic conservative variables. " << endl; } - } - else if (fluid_donor && fluid_target) { - interface_types[donorZone][targetZone] = SLIDING_INTERFACE; - nVarTransfer = 0; - nVar = solver[donorZone][INST_0][MESH_0][FLOW_SOL]->GetnPrimVar(); - interface[donorZone][targetZone] = new CSlidingInterface(nVar, nVarTransfer, config[donorZone]); - if (rank == MASTER_NODE) cout << "sliding interface. " << endl; - } - else if (fluid_donor && heat_target) { - nVarTransfer = 0; - nVar = 4; - if(config[donorZone]->GetEnergy_Equation() || (config[donorZone]->GetKind_Regime() == COMPRESSIBLE)) - interface_types[donorZone][targetZone] = CONJUGATE_HEAT_FS; - else if (config[donorZone]->GetWeakly_Coupled_Heat()) - interface_types[donorZone][targetZone] = CONJUGATE_HEAT_WEAKLY_FS; - else { } - interface[donorZone][targetZone] = new CConjugateHeatInterface(nVar, nVarTransfer, config[donorZone]); - if (rank == MASTER_NODE) cout << "conjugate heat variables. " << endl; - } - else if (heat_donor && fluid_target) { - nVarTransfer = 0; - nVar = 4; - if(config[targetZone]->GetEnergy_Equation() || (config[targetZone]->GetKind_Regime() == COMPRESSIBLE)) - interface_types[donorZone][targetZone] = CONJUGATE_HEAT_SF; - else if (config[targetZone]->GetWeakly_Coupled_Heat()) - interface_types[donorZone][targetZone] = CONJUGATE_HEAT_WEAKLY_SF; - else { } - interface[donorZone][targetZone] = new CConjugateHeatInterface(nVar, nVarTransfer, config[donorZone]); - if (rank == MASTER_NODE) cout << "conjugate heat variables. " << endl; - } - else if (heat_donor && heat_target) { - SU2_MPI::Error("Conjugate heat transfer between solids not implemented yet.", CURRENT_FUNCTION); - } - else { - interface_types[donorZone][targetZone] = CONSERVATIVE_VARIABLES; - nVarTransfer = 0; - interface[donorZone][targetZone] = new CConservativeVarsInterface(nVar, nVarTransfer, config[donorZone]); - if (rank == MASTER_NODE) cout << "generic conservative variables. " << endl; - } - break; + break; } @@ -2851,10 +2770,6 @@ void CDriver::Interface_Preprocessing(CConfig **config, CSolver***** solver, CGe } -#ifdef HAVE_MPI - if (rank == MASTER_NODE) - delete [] Buffer_Recv_mark; -#endif } void CDriver::StaticMesh_Preprocessing(CConfig *config, CGeometry** geometry, CSurfaceMovement* surface_movement){ @@ -3124,11 +3039,9 @@ void CDriver::Turbomachinery_Preprocessing(CConfig** config, CGeometry**** geome } - - - CDriver::~CDriver(void) {} + CFluidDriver::CFluidDriver(char* confFile, unsigned short val_nZone, SU2_Comm MPICommunicator) : CDriver(confFile, val_nZone, MPICommunicator, false) { Max_Iter = config_container[ZONE_0]->GetnInner_Iter(); } diff --git a/SU2_CFD/src/drivers/CMultizoneDriver.cpp b/SU2_CFD/src/drivers/CMultizoneDriver.cpp index 99984bbce32e..66db6a2544cc 100644 --- a/SU2_CFD/src/drivers/CMultizoneDriver.cpp +++ b/SU2_CFD/src/drivers/CMultizoneDriver.cpp @@ -6,7 +6,7 @@ * * SU2 Project Website: https://su2code.github.io * - * The SU2 Project is maintained by the SU2 Foundation + * The SU2 Project is maintained by the SU2 Foundation * (http://su2foundation.org) * * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md) @@ -27,14 +27,10 @@ #include "../../include/drivers/CMultizoneDriver.hpp" #include "../../include/definition_structure.hpp" +#include "../../../Common/include/interface_interpolation/CInterpolator.hpp" - -CMultizoneDriver::CMultizoneDriver(char* confFile, - unsigned short val_nZone, - SU2_Comm MPICommunicator) : CDriver(confFile, - val_nZone, - MPICommunicator, - false) { +CMultizoneDriver::CMultizoneDriver(char* confFile, unsigned short val_nZone, SU2_Comm MPICommunicator) : + CDriver(confFile, val_nZone, MPICommunicator, false) { /*--- Initialize the counter for TimeIter ---*/ TimeIter = 0; diff --git a/SU2_CFD/src/integration/CStructuralIntegration.cpp b/SU2_CFD/src/integration/CStructuralIntegration.cpp index 1c6ac815f5ef..18bc767675fc 100644 --- a/SU2_CFD/src/integration/CStructuralIntegration.cpp +++ b/SU2_CFD/src/integration/CStructuralIntegration.cpp @@ -71,32 +71,20 @@ void CStructuralIntegration::Structural_Iteration(CGeometry ****geometry, CSolve void CStructuralIntegration::Space_Integration_FEM(CGeometry *geometry, CSolver **solver_container, CNumerics **numerics, CConfig *config, unsigned short RunTime_EqSystem) { - bool dynamic = config->GetTime_Domain(); bool first_iter = (config->GetInnerIter() == 0); bool linear_analysis = (config->GetGeometricConditions() == SMALL_DEFORMATIONS); - bool nonlinear_analysis = (config->GetGeometricConditions() == LARGE_DEFORMATIONS); unsigned short IterativeScheme = config->GetKind_SpaceIteScheme_FEA(); unsigned short MainSolver = config->GetContainerPosition(RunTime_EqSystem); CSolver* solver = solver_container[MainSolver]; - /*--- Initial calculation, different logic for restarted simulations. ---*/ - bool initial_calc = false; - if (config->GetRestart()) - initial_calc = (config->GetTimeIter() == config->GetRestart_Iter()) && first_iter; - else - initial_calc = (config->GetTimeIter() == 0) && first_iter; + /*--- Mass Matrix was computed during preprocessing, see notes therein. ---*/ - /*--- Mass Matrix computed during preprocessing, see notes therein. ---*/ - - /*--- If the analysis is linear, only a the constitutive term of the stiffness matrix has to be computed. ---*/ - /*--- This is done only once, at the beginning of the calculation. From then on, K is constant. ---*/ - /*--- For correct differentiation of dynamic cases the matrix needs to be computed every time. ---*/ - if (linear_analysis && (dynamic || initial_calc)) + if (linear_analysis) { + /*--- If the analysis is linear, only a the constitutive term of the stiffness matrix has to be computed. ---*/ solver->Compute_StiffMatrix(geometry, numerics, config); - - if (nonlinear_analysis) { - + } + else { /*--- If the analysis is nonlinear the stress terms also need to be computed. ---*/ /*--- For full Newton-Raphson the stiffness matrix and the nodal term are updated every time. ---*/ if (IterativeScheme == NEWTON_RAPHSON) { @@ -111,7 +99,6 @@ void CStructuralIntegration::Space_Integration_FEM(CGeometry *geometry, CSolver else solver->Compute_NodalStressRes(geometry, numerics, config); } - } /*--- Apply the NATURAL BOUNDARY CONDITIONS (loads). ---*/ diff --git a/SU2_CFD/src/interfaces/CInterface.cpp b/SU2_CFD/src/interfaces/CInterface.cpp index 33b5ef6ad62a..df0d1ab46490 100644 --- a/SU2_CFD/src/interfaces/CInterface.cpp +++ b/SU2_CFD/src/interfaces/CInterface.cpp @@ -6,7 +6,7 @@ * * SU2 Project Website: https://su2code.github.io * - * The SU2 Project is maintained by the SU2 Foundation + * The SU2 Project is maintained by the SU2 Foundation * (http://su2foundation.org) * * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md) @@ -26,79 +26,42 @@ */ #include "../../include/interfaces/CInterface.hpp" +#include "../../../Common/include/interface_interpolation/CInterpolator.hpp" -CInterface::CInterface(void) { - - rank = SU2_MPI::GetRank(); - size = SU2_MPI::GetSize(); - - Physical_Constants = NULL; - Donor_Variable = NULL; - Target_Variable = NULL; - SpanLevelDonor = NULL; - SpanValueCoeffTarget = NULL; - - nVar = 0; - +CInterface::CInterface(void) : + rank(SU2_MPI::GetRank()), + size(SU2_MPI::GetSize()) { } -CInterface::CInterface(unsigned short val_nVar, unsigned short val_nConst, CConfig *config) { - - rank = SU2_MPI::GetRank(); - size = SU2_MPI::GetSize(); +CInterface::CInterface(unsigned short val_nVar, unsigned short val_nConst, CConfig *config) : + rank(SU2_MPI::GetRank()), + size(SU2_MPI::GetSize()), + nVar(val_nVar) { - Physical_Constants = NULL; - Donor_Variable = NULL; - Target_Variable = NULL; - - unsigned short iVar; - - Physical_Constants = new su2double[val_nConst]; - Donor_Variable = new su2double[val_nVar]; - Target_Variable = new su2double[val_nVar]; + Physical_Constants = new su2double[val_nConst] (); + Donor_Variable = new su2double[val_nVar] (); + Target_Variable = new su2double[val_nVar] (); /*--- By default, the value is aggregated in the transfer routine ---*/ valAggregated = true; - - nVar = val_nVar; - - for (iVar = 0; iVar < nVar; iVar++) { - Donor_Variable[iVar] = 0.0; - Target_Variable[iVar] = 0.0; - } - - for (iVar = 0; iVar < val_nConst; iVar++) { - Physical_Constants[iVar] = 0.0; - } - - SpanLevelDonor = NULL; - SpanValueCoeffTarget = NULL; - } CInterface::~CInterface(void) { - if (Physical_Constants != NULL) delete [] Physical_Constants; - if (Donor_Variable != NULL) delete [] Donor_Variable; - if (Target_Variable != NULL) delete [] Target_Variable; - - if (SpanValueCoeffTarget != NULL) delete[] SpanValueCoeffTarget; - if (SpanLevelDonor != NULL) delete[] SpanLevelDonor; + delete [] Physical_Constants; + delete [] Donor_Variable; + delete [] Target_Variable; + delete[] SpanValueCoeffTarget; + delete[] SpanLevelDonor; } void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution, CGeometry *donor_geometry, CGeometry *target_geometry, CConfig *donor_config, CConfig *target_config) { - - unsigned short nMarkerInt, nMarkerDonor, nMarkerTarget; // Number of markers on the interface, donor and target side - unsigned short iMarkerInt, iMarkerDonor, iMarkerTarget; // Variables for iteration over markers int Marker_Donor, Marker_Target; - int Target_check, Donor_check; - - unsigned long iVertex; // Variables for iteration over vertices and nodes - + unsigned long iVertex; unsigned short iVar; GetPhysical_Constants(donor_solution, target_solution, donor_geometry, target_geometry, @@ -107,14 +70,8 @@ void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution unsigned long Point_Donor_Global, Donor_Global_Index; unsigned long Point_Donor, Point_Target; -#ifdef HAVE_MPI - int *Buffer_Recv_mark = NULL, iRank; - - if (rank == MASTER_NODE) - Buffer_Recv_mark = new int[size]; -#endif - - unsigned long Buffer_Send_nVertexDonor[1], *Buffer_Recv_nVertexDonor; + unsigned long Buffer_Send_nVertexDonor[1]; + unsigned long *Buffer_Recv_nVertexDonor = new unsigned long[size]; unsigned long iLocalVertex = 0; unsigned long nLocalVertexDonor = 0, nLocalVertexDonorOwned = 0; @@ -125,98 +82,23 @@ void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution unsigned long nBuffer_BcastVariables = 0, nBuffer_BcastIndices = 0; - int nProcessor = 0; + const int nProcessor = size; /*--- Number of markers on the FSI interface ---*/ - nMarkerInt = (donor_config->GetMarker_n_ZoneInterface())/2; - nMarkerTarget = target_config->GetnMarker_All(); - nMarkerDonor = donor_config->GetnMarker_All(); - - nProcessor = size; + const auto nMarkerInt = donor_config->GetMarker_n_ZoneInterface()/2; /*--- Outer loop over the markers on the FSI interface: compute one by one ---*/ /*--- The tags are always an integer greater than 1: loop from 1 to nMarkerFSI ---*/ - for (iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { + for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { - Buffer_Recv_nVertexDonor = NULL; + /*--- Check if this interface connects the two zones, if not continue. ---*/ - Marker_Donor = -1; - Marker_Target = -1; + Marker_Donor = CInterpolator::Find_InterfaceMarker(donor_config, iMarkerInt); + Marker_Target = CInterpolator::Find_InterfaceMarker(target_config, iMarkerInt); - /*--- The donor and target markers are tagged with the same index. - *--- This is independent of the MPI domain decomposition. - *--- We need to loop over all markers on both sides and get the number of nodes - *--- that belong to each FSI marker for each processor ---*/ - - /*--- On the donor side ---*/ - - for (iMarkerDonor = 0; iMarkerDonor < nMarkerDonor; iMarkerDonor++) { - /*--- If the tag GetMarker_All_ZoneInterface(iMarkerDonor) equals the index we are looping at ---*/ - if ( donor_config->GetMarker_All_ZoneInterface(iMarkerDonor) == iMarkerInt ) { - /*--- Store the identifier for the structural marker ---*/ - Marker_Donor = iMarkerDonor; - /*--- Exit the for loop: we have found the local index for iMarkerFSI on the FEA side ---*/ - break; - } - } - - /*--- On the target side we only have to identify the marker; - * then we'll loop over it and retrieve from the donor points ---*/ - - for (iMarkerTarget = 0; iMarkerTarget < nMarkerTarget; iMarkerTarget++) { - /*--- If the tag GetMarker_All_ZoneInterface(iMarkerFlow) equals the index we are looping at ---*/ - if ( target_config->GetMarker_All_ZoneInterface(iMarkerTarget) == iMarkerInt ) { - /*--- Store the identifier for the fluid marker ---*/ - Marker_Target = iMarkerTarget; - /*--- Exit the for loop: we have found the local index for iMarkerFSI on the FEA side ---*/ - break; - } - } - -#ifdef HAVE_MPI - - Donor_check = -1; - Target_check = -1; - - /*--- We gather a vector in MASTER_NODE that determines if the boundary is not on the processor - * because of the partition or because the zone does not include it ---*/ - - SU2_MPI::Gather(&Marker_Donor , 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - - if (rank == MASTER_NODE) { - for (iRank = 0; iRank < nProcessor; iRank++) { - if( Buffer_Recv_mark[iRank] != -1 ) { - Donor_check = Buffer_Recv_mark[iRank]; - break; - } - } - } - - SU2_MPI::Bcast(&Donor_check , 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - - SU2_MPI::Gather(&Marker_Target, 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - - if (rank == MASTER_NODE) { - for (iRank = 0; iRank < nProcessor; iRank++) { - if( Buffer_Recv_mark[iRank] != -1 ) { - Target_check = Buffer_Recv_mark[iRank]; - break; - } - } - } - - SU2_MPI::Bcast(&Target_check, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - -#else - Donor_check = Marker_Donor; - Target_check = Marker_Target; -#endif - - if(Target_check == -1 || Donor_check == -1) { - continue; - } + if(!CInterpolator::CheckInterfaceBoundary(Marker_Donor, Marker_Target)) continue; nLocalVertexDonorOwned = 0; nLocalVertexDonor = 0; @@ -233,10 +115,6 @@ void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution Buffer_Send_nVertexDonor[0] = nLocalVertexDonor; // Retrieve total number of vertices on Donor marker - // Allocate memory to receive how many vertices are on each rank on the structural side - if (rank == MASTER_NODE) Buffer_Recv_nVertexDonor = new unsigned long[size]; - -#ifdef HAVE_MPI /*--- We receive MaxLocalVertexDonor as the maximum number of vertices * in one single processor on the donor side---*/ SU2_MPI::Allreduce(&nLocalVertexDonor, &MaxLocalVertexDonor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); @@ -245,13 +123,8 @@ void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution SU2_MPI::Allreduce(&nLocalVertexDonorOwned, &TotalVertexDonor, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); /*--- We gather a vector in MASTER_NODE that determines how many elements are there * on each processor on the structural side ---*/ - SU2_MPI::Gather(&Buffer_Send_nVertexDonor, 1, MPI_UNSIGNED_LONG, Buffer_Recv_nVertexDonor, 1, - MPI_UNSIGNED_LONG, MASTER_NODE, MPI_COMM_WORLD); -#else - MaxLocalVertexDonor = nLocalVertexDonor; - TotalVertexDonor = nLocalVertexDonorOwned; - Buffer_Recv_nVertexDonor[0] = Buffer_Send_nVertexDonor[0]; -#endif + SU2_MPI::Gather(&Buffer_Send_nVertexDonor, 1, MPI_UNSIGNED_LONG, + Buffer_Recv_nVertexDonor, 1, MPI_UNSIGNED_LONG, MASTER_NODE, MPI_COMM_WORLD); /*--- We will be gathering the donor information into the master node ---*/ nBuffer_DonorVariables = MaxLocalVertexDonor * nVar; @@ -308,20 +181,12 @@ void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution } -#ifdef HAVE_MPI /*--- Once all the messages have been prepared, we gather them all into the MASTER_NODE ---*/ SU2_MPI::Gather(Buffer_Send_DonorVariables, nBuffer_DonorVariables, MPI_DOUBLE, Buffer_Recv_DonorVariables, nBuffer_DonorVariables, MPI_DOUBLE, MASTER_NODE, MPI_COMM_WORLD); SU2_MPI::Gather(Buffer_Send_DonorIndices, nBuffer_DonorIndices, MPI_LONG, Buffer_Recv_DonorIndices, nBuffer_DonorIndices, MPI_LONG, MASTER_NODE, MPI_COMM_WORLD); -#else - for (unsigned long iVariable = 0; iVariable < nBuffer_DonorVariables; iVariable++) - Buffer_Recv_DonorVariables[iVariable] = Buffer_Send_DonorVariables[iVariable]; - for (unsigned long iVariable = 0; iVariable < nBuffer_DonorIndices; iVariable++) - Buffer_Recv_DonorIndices[iVariable] = Buffer_Send_DonorIndices[iVariable]; -#endif - /*--- Now we pack the information to send it over to the different processors ---*/ if (rank == MASTER_NODE) { @@ -352,10 +217,8 @@ void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution } -#ifdef HAVE_MPI SU2_MPI::Bcast(Buffer_Bcast_Variables, nBuffer_BcastVariables, MPI_DOUBLE, MASTER_NODE, MPI_COMM_WORLD); SU2_MPI::Bcast(Buffer_Bcast_Indices, nBuffer_BcastIndices, MPI_LONG, MASTER_NODE, MPI_COMM_WORLD); -#endif long indexPoint_iVertex; unsigned short iDonorPoint, nDonorPoints; @@ -418,18 +281,11 @@ void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution delete [] Buffer_Bcast_Variables; delete [] Buffer_Bcast_Indices; - if (rank == MASTER_NODE) { - delete [] Buffer_Recv_nVertexDonor; - delete [] Buffer_Recv_DonorVariables; - delete [] Buffer_Recv_DonorIndices; - } - + delete [] Buffer_Recv_DonorVariables; + delete [] Buffer_Recv_DonorIndices; } -#ifdef HAVE_MPI - if (rank == MASTER_NODE && Buffer_Recv_mark != NULL) - delete [] Buffer_Recv_mark; -#endif + delete [] Buffer_Recv_nVertexDonor; } void CInterface::PreprocessAverage(CGeometry *donor_geometry, CGeometry *target_geometry, @@ -444,12 +300,6 @@ void CInterface::PreprocessAverage(CGeometry *donor_geometry, CGeometry *target_ const su2double *SpanValuesDonor, *SpanValuesTarget; su2double dist, test, dist2, test2; -#ifdef HAVE_MPI - int iSize; - int *BuffMarkerDonor, *BuffDonorFlag; -#endif - - nMarkerDonor = donor_geometry->GetnMarker(); nMarkerTarget = target_geometry->GetnMarker(); //TODO turbo this approach only works if all the turboamchinery marker @@ -478,25 +328,23 @@ void CInterface::PreprocessAverage(CGeometry *donor_geometry, CGeometry *target_ } #ifdef HAVE_MPI - BuffMarkerDonor = new int[size]; - BuffDonorFlag = new int[size]; - for (iSize=0; iSize 0.0){ Marker_Donor = BuffMarkerDonor[iSize]; - Donor_Flag = BuffDonorFlag[iSize]; + Donor_Flag = BuffDonorFlag[iSize]; break; } } @@ -574,6 +422,7 @@ void CInterface::PreprocessAverage(CGeometry *donor_geometry, CGeometry *target_ void CInterface::AllgatherAverage(CSolver *donor_solution, CSolver *target_solution, CGeometry *donor_geometry, CGeometry *target_geometry, CConfig *donor_config, CConfig *target_config, unsigned short iMarkerInt){ + unsigned short nMarkerDonor, nMarkerTarget; // Number of markers on the interface, donor and target side unsigned short iMarkerDonor, iMarkerTarget; // Variables for iteration over markers unsigned short iSpan, nSpanDonor, nSpanTarget; @@ -767,7 +616,6 @@ void CInterface::AllgatherAverage(CSolver *donor_solution, CSolver *target_solut delete [] BuffAvgKineDonor; delete [] BuffAvgOmegaDonor; delete [] BuffMarkerDonor; - #endif /*--- On the target side we have to identify the marker as well ---*/ @@ -904,4 +752,3 @@ void CInterface::GatherAverageTurboGeoValues(CGeometry *donor_geometry, CGeometr SetAverageTurboGeoValues(donor_geometry, target_geometry, donorZone); } - diff --git a/SU2_CFD/src/interfaces/cfd/CSlidingInterface.cpp b/SU2_CFD/src/interfaces/cfd/CSlidingInterface.cpp index 242a5d6bddc7..a8cf13a71dd5 100644 --- a/SU2_CFD/src/interfaces/cfd/CSlidingInterface.cpp +++ b/SU2_CFD/src/interfaces/cfd/CSlidingInterface.cpp @@ -7,7 +7,7 @@ * * SU2 Project Website: https://su2code.github.io * - * The SU2 Project is maintained by the SU2 Foundation + * The SU2 Project is maintained by the SU2 Foundation * (http://su2foundation.org) * * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md) @@ -29,52 +29,25 @@ #include "../../../include/interfaces/cfd/CSlidingInterface.hpp" -CSlidingInterface::CSlidingInterface(void) : CInterface() { - -} +CSlidingInterface::CSlidingInterface(void) : CInterface() { } CSlidingInterface::CSlidingInterface(unsigned short val_nVar, unsigned short val_nConst, CConfig *config) : CInterface() { - rank = SU2_MPI::GetRank(); - size = SU2_MPI::GetSize(); - - Physical_Constants = NULL; - Donor_Variable = NULL; - Target_Variable = NULL; - - unsigned short iVar; - - Physical_Constants = new su2double[val_nConst]; - Donor_Variable = new su2double[val_nVar]; - - Target_Variable = new su2double[val_nVar+1]; + Physical_Constants = new su2double[val_nConst] (); + Donor_Variable = new su2double[val_nVar] (); + Target_Variable = new su2double[val_nVar+1] (); valAggregated = false; nVar = val_nVar; - - for (iVar = 0; iVar < nVar; iVar++) { - Donor_Variable[iVar] = 0.0; - Target_Variable[iVar] = 0.0; - } - - for (iVar = 0; iVar < val_nConst; iVar++) { - Physical_Constants[iVar] = 0.0; - } - -} - -CSlidingInterface::~CSlidingInterface(void) { - } +CSlidingInterface::~CSlidingInterface(void) { } void CSlidingInterface::GetPhysical_Constants(CSolver *donor_solution, CSolver *target_solution, CGeometry *donor_geometry, CGeometry *target_geometry, - CConfig *donor_config, CConfig *target_config) { - -} + CConfig *donor_config, CConfig *target_config) { } void CSlidingInterface::GetDonor_Variable(CSolver *donor_solution, CGeometry *donor_geometry, CConfig *donor_config, unsigned long Marker_Donor, diff --git a/SU2_CFD/src/iteration_structure.cpp b/SU2_CFD/src/iteration_structure.cpp index 353816cac7d2..b4f1e6615d67 100644 --- a/SU2_CFD/src/iteration_structure.cpp +++ b/SU2_CFD/src/iteration_structure.cpp @@ -1308,17 +1308,16 @@ CFEAIteration::CFEAIteration(CConfig *config) : CIteration(config) { } CFEAIteration::~CFEAIteration(void) { } void CFEAIteration::Preprocess() { } void CFEAIteration::Iterate(COutput *output, - CIntegration ****integration, - CGeometry ****geometry, - CSolver *****solver, - CNumerics ******numerics, - CConfig **config, - CSurfaceMovement **surface_movement, - CVolumetricMovement ***grid_movement, - CFreeFormDefBox*** FFDBox, - unsigned short val_iZone, - unsigned short val_iInst - ) { + CIntegration ****integration, + CGeometry ****geometry, + CSolver *****solver, + CNumerics ******numerics, + CConfig **config, + CSurfaceMovement **surface_movement, + CVolumetricMovement ***grid_movement, + CFreeFormDefBox*** FFDBox, + unsigned short val_iZone, + unsigned short val_iInst) { su2double loadIncrement; unsigned long IntIter = 0; @@ -1327,227 +1326,176 @@ void CFEAIteration::Iterate(COutput *output, unsigned long iIncrement; unsigned long nIncrements = config[val_iZone]->GetNumberIncrements(); - bool nonlinear = (config[val_iZone]->GetGeometricConditions() == LARGE_DEFORMATIONS); // Geometrically non-linear problems - bool linear = (config[val_iZone]->GetGeometricConditions() == SMALL_DEFORMATIONS); // Geometrically non-linear problems - + bool nonlinear = (config[val_iZone]->GetGeometricConditions() == LARGE_DEFORMATIONS); + bool linear = (config[val_iZone]->GetGeometricConditions() == SMALL_DEFORMATIONS); bool disc_adj_fem = (config[val_iZone]->GetKind_Solver() == DISC_ADJ_FEM); + bool incremental_load = config[val_iZone]->GetIncrementalLoad(); // Loads applied in steps - bool incremental_load = config[val_iZone]->GetIncrementalLoad(); // If an incremental load is applied - - /*--- This is to prevent problems when running a linear solver ---*/ - if (!nonlinear) incremental_load = false; + CIntegration* feaIntegration = integration[val_iZone][val_iInst][FEA_SOL]; + CSolver* feaSolver = solver[val_iZone][val_iInst][MESH_0][FEA_SOL]; /*--- Set the convergence monitor to false, to prevent the solver to stop in intermediate FSI subiterations ---*/ - integration[val_iZone][val_iInst][FEA_SOL]->SetConvergence(false); + feaIntegration->SetConvergence(false); + + /*--- FEA equations ---*/ + config[val_iZone]->SetGlobalParam(FEM_ELASTICITY, RUNTIME_FEA_SYS); if (linear) { config[val_iZone]->SetInnerIter(0); - /*--- FEA equations ---*/ - - config[val_iZone]->SetGlobalParam(FEM_ELASTICITY, RUNTIME_FEA_SYS); - /*--- Run the iteration ---*/ - integration[val_iZone][val_iInst][FEA_SOL]->Structural_Iteration(geometry, solver, numerics, - config, RUNTIME_FEA_SYS, val_iZone, val_iInst); + feaIntegration->Structural_Iteration(geometry, solver, numerics, config, + RUNTIME_FEA_SYS, val_iZone, val_iInst); - if (!disc_adj_fem){ - Monitor(output, integration, geometry, solver, numerics, config, surface_movement, grid_movement, FFDBox, val_iZone, INST_0); + if (!disc_adj_fem) { + Monitor(output, integration, geometry, solver, numerics, config, + surface_movement, grid_movement, FFDBox, val_iZone, INST_0); /*--- Set the convergence monitor to true, to prevent the solver to stop in intermediate FSI subiterations ---*/ output->SetConvergence(true); } } - /*--- If the structure is held static and the solver is nonlinear, we don't need to solve for static time, but we need to compute Mass Matrix and Integration constants ---*/ - else if (nonlinear) { + else if (nonlinear && !incremental_load) { /*--- THIS IS THE DIRECT APPROACH (NO INCREMENTAL LOAD APPLIED) ---*/ - if (!incremental_load) { - - /*--- Keep the current inner iter, we need to restore it in discrete adjoint cases as file output depends on it ---*/ - unsigned long CurIter = config[val_iZone]->GetInnerIter(); - - IntIter = 0; - config[val_iZone]->SetInnerIter(IntIter); - - /*--- FEA equations ---*/ + /*--- Keep the current inner iter, we need to restore it in discrete adjoint cases as file output depends on it ---*/ + const auto CurIter = config[val_iZone]->GetInnerIter(); - config[val_iZone]->SetGlobalParam(FEM_ELASTICITY, RUNTIME_FEA_SYS); - - /*--- Run the iteration ---*/ - - integration[val_iZone][val_iInst][FEA_SOL]->Structural_Iteration(geometry, solver, numerics, - config, RUNTIME_FEA_SYS, val_iZone, val_iInst); - - if (!disc_adj_fem) - Monitor(output, integration, geometry, solver, numerics, config, surface_movement, grid_movement, FFDBox, val_iZone, INST_0); - - /*----------------- If the solver is non-linear, we need to subiterate using a Newton-Raphson approach ----------------------*/ - - for (IntIter = 1; IntIter < config[val_iZone]->GetnInner_Iter(); IntIter++) { - - /*--- Limit to only one iteration for the discrete adjoint recording, restore inner iter (see above) ---*/ - if (disc_adj_fem) { - config[val_iZone]->SetInnerIter(CurIter); - break; - } - - config[val_iZone]->SetInnerIter(IntIter); + /*--- Newton-Raphson subiterations ---*/ - integration[val_iZone][val_iInst][FEA_SOL]->Structural_Iteration(geometry, solver, numerics, - config, RUNTIME_FEA_SYS, val_iZone, val_iInst); - - StopCalc = Monitor(output, integration, geometry, solver, numerics, config, surface_movement, grid_movement, FFDBox, val_iZone, INST_0); + for (IntIter = 0; IntIter < config[val_iZone]->GetnInner_Iter(); IntIter++) { + config[val_iZone]->SetInnerIter(IntIter); - if (StopCalc) break; + feaIntegration->Structural_Iteration(geometry, solver, numerics, config, + RUNTIME_FEA_SYS, val_iZone, val_iInst); + /*--- Limit to only one iteration for the discrete adjoint recording, restore inner iter (see above) ---*/ + if (disc_adj_fem) { + config[val_iZone]->SetInnerIter(CurIter); + break; } + else { + StopCalc = Monitor(output, integration, geometry, solver, numerics, config, + surface_movement, grid_movement, FFDBox, val_iZone, INST_0); + if (StopCalc && (IntIter > 0)) break; + } } - /*--- The incremental load is only used in nonlinear cases ---*/ - else if (incremental_load) { - /*--- Set the initial condition: store the current solution as Solution_Old ---*/ + } + else { + + /*--- THIS IS THE INCREMENTAL LOAD APPROACH (only makes sense for nonlinear) ---*/ - solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->SetInitialCondition(geometry[val_iZone][val_iInst], solver[val_iZone][val_iInst], config[val_iZone], TimeIter); + /*--- Set the initial condition: store the current solution as Solution_Old ---*/ - /*--- The load increment is 1.0 ---*/ + feaSolver->SetInitialCondition(geometry[val_iZone][val_iInst], + solver[val_iZone][val_iInst], config[val_iZone], TimeIter); - loadIncrement = 1.0; - solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->SetLoad_Increment(loadIncrement); - solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->SetForceCoeff(loadIncrement); + /*--- Assume the initial load increment as 1.0 ---*/ - /*--- Set the value of the internal iteration ---*/ + loadIncrement = 1.0; + feaSolver->SetLoad_Increment(loadIncrement); + feaSolver->SetForceCoeff(loadIncrement); + + /*--- Run two nonlinear iterations to check if incremental loading can be skipped ---*/ + + for (IntIter = 0; IntIter < 2; ++IntIter) { - IntIter = 0; config[val_iZone]->SetInnerIter(IntIter); - /*--- FEA equations ---*/ - - config[val_iZone]->SetGlobalParam(FEM_ELASTICITY, RUNTIME_FEA_SYS); - /*--- Run the first iteration ---*/ - integration[val_iZone][val_iInst][FEA_SOL]->Structural_Iteration(geometry, solver, numerics, - config, RUNTIME_FEA_SYS, val_iZone, val_iInst); - - /*--- Write the convergence history (first, compute Von Mises stress) ---*/ - - Monitor(output, integration, geometry, solver, numerics, config, surface_movement, grid_movement, FFDBox, val_iZone, INST_0); + feaIntegration->Structural_Iteration(geometry, solver, numerics, config, + RUNTIME_FEA_SYS, val_iZone, val_iInst); - /*--- Run the second iteration ---*/ - - IntIter = 1; - config[val_iZone]->SetInnerIter(IntIter); + /*--- Write the convergence history (first computes Von Mises stress) ---*/ - integration[val_iZone][val_iInst][FEA_SOL]->Structural_Iteration(geometry, solver, numerics, - config, RUNTIME_FEA_SYS, val_iZone, val_iInst); + Monitor(output, integration, geometry, solver, numerics, config, + surface_movement, grid_movement, FFDBox, val_iZone, INST_0); + } - /*--- Write the convergence history (first, compute Von Mises stress) ---*/ - Monitor(output, integration, geometry, solver, numerics, config, surface_movement, grid_movement, FFDBox, val_iZone, INST_0); + bool meetCriteria; + su2double Residual_UTOL, Residual_RTOL, Residual_ETOL; + su2double Criteria_UTOL, Criteria_RTOL, Criteria_ETOL; - bool meetCriteria; - su2double Residual_UTOL, Residual_RTOL, Residual_ETOL; - su2double Criteria_UTOL, Criteria_RTOL, Criteria_ETOL; + Criteria_UTOL = config[val_iZone]->GetIncLoad_Criteria(0); + Criteria_RTOL = config[val_iZone]->GetIncLoad_Criteria(1); + Criteria_ETOL = config[val_iZone]->GetIncLoad_Criteria(2); - Criteria_UTOL = config[val_iZone]->GetIncLoad_Criteria(0); - Criteria_RTOL = config[val_iZone]->GetIncLoad_Criteria(1); - Criteria_ETOL = config[val_iZone]->GetIncLoad_Criteria(2); + Residual_UTOL = log10(feaSolver->LinSysSol.norm()); + Residual_RTOL = log10(feaSolver->LinSysRes.norm()); + Residual_ETOL = log10(feaSolver->LinSysSol.dot(feaSolver->LinSysRes)); - Residual_UTOL = log10(solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->LinSysSol.norm()); - Residual_RTOL = log10(solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->LinSysRes.norm()); - Residual_ETOL = log10(solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->LinSysSol.dot( - solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->LinSysRes)); + meetCriteria = ( ( Residual_UTOL < Criteria_UTOL ) && + ( Residual_RTOL < Criteria_RTOL ) && + ( Residual_ETOL < Criteria_ETOL ) ); - meetCriteria = ( ( Residual_UTOL < Criteria_UTOL ) && - ( Residual_RTOL < Criteria_RTOL ) && - ( Residual_ETOL < Criteria_ETOL ) ); + /*--- If the criteria is met, i.e. the load is not "too big", continue the regular calculation ---*/ - /*--- If the criteria is met and the load is not "too big", do the regular calculation ---*/ - if (meetCriteria) { + if (meetCriteria) { - for (IntIter = 2; IntIter < config[val_iZone]->GetnInner_Iter(); IntIter++) { + /*--- Newton-Raphson subiterations ---*/ - integration[val_iZone][val_iInst][FEA_SOL]->Structural_Iteration(geometry, solver, numerics, - config, RUNTIME_FEA_SYS, val_iZone, val_iInst); + for (IntIter = 2; IntIter < config[val_iZone]->GetnInner_Iter(); IntIter++) { - /*--- Write the convergence history (first, compute Von Mises stress) ---*/ - StopCalc = Monitor(output, integration, geometry, solver, numerics, config, - surface_movement, grid_movement, FFDBox, val_iZone, INST_0); + config[val_iZone]->SetInnerIter(IntIter); - if (StopCalc) break; + feaIntegration->Structural_Iteration(geometry, solver, numerics, config, + RUNTIME_FEA_SYS, val_iZone, val_iInst); - } + StopCalc = Monitor(output, integration, geometry, solver, numerics, config, + surface_movement, grid_movement, FFDBox, val_iZone, INST_0); + if (StopCalc) break; } - /*--- If the criteria is not met, a whole set of subiterations for the different loads must be done ---*/ + } - else { + /*--- If the criteria is not met, a whole set of subiterations for the different loads must be done ---*/ - /*--- Here we have to restart the solution to the original one of the iteration ---*/ - /*--- Retrieve the Solution_Old as the current solution before subiterating ---*/ + else { - solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->ResetInitialCondition(geometry[val_iZone][val_iInst], - solver[val_iZone][val_iInst], config[val_iZone], TimeIter); + /*--- Here we have to restore the solution to the one before testing the criteria ---*/ + /*--- Retrieve the Solution_Old as the current solution before subiterating ---*/ - /*--- For the number of increments ---*/ - for (iIncrement = 0; iIncrement < nIncrements; iIncrement++) { + feaSolver->ResetInitialCondition(geometry[val_iZone][val_iInst], + solver[val_iZone][val_iInst], config[val_iZone], TimeIter); - loadIncrement = (iIncrement + 1.0) * (1.0 / nIncrements); + /*--- For the number of increments ---*/ + for (iIncrement = 0; iIncrement < nIncrements; iIncrement++) { - /*--- Set the load increment and the initial condition, and output the parameters of UTOL, RTOL, ETOL for the previous iteration ---*/ + /*--- Set the load increment and the initial condition, and output the + * parameters of UTOL, RTOL, ETOL for the previous iteration ---*/ - /*--- Set the convergence monitor to false, to force se solver to converge every subiteration ---*/ - output->SetConvergence(false); + loadIncrement = (iIncrement + 1.0) * (1.0 / nIncrements); + feaSolver->SetLoad_Increment(loadIncrement); - /*--- FEA equations ---*/ + /*--- Set the convergence monitor to false, to force the solver to converge every subiteration ---*/ + output->SetConvergence(false); - config[val_iZone]->SetGlobalParam(FEM_ELASTICITY, RUNTIME_FEA_SYS); + if (rank == MASTER_NODE) + cout << "\nIncremental load: increment " << iIncrement + 1 << endl; - solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->SetLoad_Increment(loadIncrement); + /*--- Newton-Raphson subiterations ---*/ - if (rank == MASTER_NODE) { - cout << endl; - cout << "Incremental load: increment " << iIncrement + 1 << endl; - } + for (IntIter = 0; IntIter < config[val_iZone]->GetnInner_Iter(); IntIter++) { - /*--- Set the value of the internal iteration ---*/ - IntIter = 0; config[val_iZone]->SetInnerIter(IntIter); - /*--- FEA equations ---*/ - - config[val_iZone]->SetGlobalParam(FEM_ELASTICITY, RUNTIME_FEA_SYS); - - /*--- Run the iteration ---*/ - - integration[val_iZone][val_iInst][FEA_SOL]->Structural_Iteration(geometry, solver, numerics, - config, RUNTIME_FEA_SYS, val_iZone, val_iInst); - - Monitor(output, integration, geometry, solver, numerics, config, surface_movement, grid_movement, FFDBox, val_iZone, INST_0); - - - /*----------------- If the solver is non-linear, we need to subiterate using a Newton-Raphson approach ----------------------*/ - - for (IntIter = 1; IntIter < config[val_iZone]->GetnInner_Iter(); IntIter++) { - - config[val_iZone]->SetInnerIter(IntIter); - - integration[val_iZone][val_iInst][FEA_SOL]->Structural_Iteration(geometry, solver, numerics, - config, RUNTIME_FEA_SYS, val_iZone, val_iInst); + feaIntegration->Structural_Iteration(geometry, solver, numerics, config, + RUNTIME_FEA_SYS, val_iZone, val_iInst); - /*--- Write the convergence history (first, compute Von Mises stress) ---*/ - StopCalc = Monitor(output, integration, geometry, solver, numerics, config, surface_movement, grid_movement, FFDBox, val_iZone, INST_0); - - if (StopCalc) break; + /*--- Write the convergence history (first, compute Von Mises stress) ---*/ + StopCalc = Monitor(output, integration, geometry, solver, numerics, config, + surface_movement, grid_movement, FFDBox, val_iZone, INST_0); - } + if (StopCalc && (IntIter > 0)) break; } @@ -1559,59 +1507,66 @@ void CFEAIteration::Iterate(COutput *output, /*--- Finally, we need to compute the objective function, in case that we are running a discrete adjoint solver... ---*/ - switch (config[val_iZone]->GetKind_ObjFunc()){ + switch (config[val_iZone]->GetKind_ObjFunc()) { case REFERENCE_GEOMETRY: - if ((config[val_iZone]->GetDV_FEA() == YOUNG_MODULUS) || (config[val_iZone]->GetDV_FEA() == DENSITY_VAL)){ - solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->Stiffness_Penalty(geometry[val_iZone][val_iInst][MESH_0],solver[val_iZone][val_iInst][MESH_0], - numerics[val_iZone][val_iInst][MESH_0][FEA_SOL], config[val_iZone]); + if ((config[val_iZone]->GetDV_FEA() == YOUNG_MODULUS) || (config[val_iZone]->GetDV_FEA() == DENSITY_VAL)) { + feaSolver->Stiffness_Penalty(geometry[val_iZone][val_iInst][MESH_0], solver[val_iZone][val_iInst][MESH_0], + numerics[val_iZone][val_iInst][MESH_0][FEA_SOL], config[val_iZone]); } - solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->Compute_OFRefGeom(geometry[val_iZone][val_iInst][MESH_0],solver[val_iZone][val_iInst][MESH_0], config[val_iZone]); + feaSolver->Compute_OFRefGeom(geometry[val_iZone][val_iInst][MESH_0], + solver[val_iZone][val_iInst][MESH_0], config[val_iZone]); break; case REFERENCE_NODE: - if ((config[val_iZone]->GetDV_FEA() == YOUNG_MODULUS) || (config[val_iZone]->GetDV_FEA() == DENSITY_VAL)){ - solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->Stiffness_Penalty(geometry[val_iZone][val_iInst][MESH_0],solver[val_iZone][val_iInst][MESH_0], - numerics[val_iZone][val_iInst][MESH_0][FEA_SOL], config[val_iZone]); + if ((config[val_iZone]->GetDV_FEA() == YOUNG_MODULUS) || (config[val_iZone]->GetDV_FEA() == DENSITY_VAL)) { + feaSolver->Stiffness_Penalty(geometry[val_iZone][val_iInst][MESH_0], solver[val_iZone][val_iInst][MESH_0], + numerics[val_iZone][val_iInst][MESH_0][FEA_SOL], config[val_iZone]); } - solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->Compute_OFRefNode(geometry[val_iZone][val_iInst][MESH_0],solver[val_iZone][val_iInst][MESH_0], config[val_iZone]); + feaSolver->Compute_OFRefNode(geometry[val_iZone][val_iInst][MESH_0], + solver[val_iZone][val_iInst][MESH_0], config[val_iZone]); break; case VOLUME_FRACTION: case TOPOL_DISCRETENESS: - solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->Compute_OFVolFrac(geometry[val_iZone][val_iInst][MESH_0],solver[val_iZone][val_iInst][MESH_0], config[val_iZone]); + feaSolver->Compute_OFVolFrac(geometry[val_iZone][val_iInst][MESH_0], + solver[val_iZone][val_iInst][MESH_0], config[val_iZone]); break; case TOPOL_COMPLIANCE: - solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->Compute_OFCompliance(geometry[val_iZone][val_iInst][MESH_0], solver[val_iZone][val_iInst][MESH_0], config[val_iZone]); + feaSolver->Compute_OFCompliance(geometry[val_iZone][val_iInst][MESH_0], + solver[val_iZone][val_iInst][MESH_0], config[val_iZone]); break; } } void CFEAIteration::Update(COutput *output, - CIntegration ****integration, - CGeometry ****geometry, - CSolver *****solver, - CNumerics ******numerics, - CConfig **config, - CSurfaceMovement **surface_movement, - CVolumetricMovement ***grid_movement, - CFreeFormDefBox*** FFDBox, - unsigned short val_iZone, - unsigned short val_iInst) { + CIntegration ****integration, + CGeometry ****geometry, + CSolver *****solver, + CNumerics ******numerics, + CConfig **config, + CSurfaceMovement **surface_movement, + CVolumetricMovement ***grid_movement, + CFreeFormDefBox*** FFDBox, + unsigned short val_iZone, + unsigned short val_iInst) { su2double Physical_dt, Physical_t; - unsigned long TimeIter = config[val_iZone]->GetTimeIter(); - bool dynamic = (config[val_iZone]->GetTime_Domain()); // Dynamic problems - bool static_fem = (!config[val_iZone]->GetTime_Domain()); // Static problems - bool fsi = config[val_iZone]->GetFSI_Simulation(); // Fluid-Structure Interaction problems + unsigned long TimeIter = config[val_iZone]->GetTimeIter(); + bool dynamic = (config[val_iZone]->GetTime_Domain()); // Dynamic problems + bool fsi = config[val_iZone]->GetFSI_Simulation(); // Fluid-Structure Interaction problems + + CSolver* feaSolver = solver[val_iZone][val_iInst][MESH_0][FEA_SOL]; /*----------------- Compute averaged nodal stress and reactions ------------------------*/ - solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->Compute_NodalStress(geometry[val_iZone][val_iInst][MESH_0], numerics[val_iZone][val_iInst][MESH_0][FEA_SOL], config[val_iZone]); + feaSolver->Compute_NodalStress(geometry[val_iZone][val_iInst][MESH_0], + numerics[val_iZone][val_iInst][MESH_0][FEA_SOL], config[val_iZone]); /*----------------- Update structural solver ----------------------*/ if (dynamic) { - integration[val_iZone][val_iInst][FEA_SOL]->SetStructural_Solver(geometry[val_iZone][val_iInst][MESH_0], solver[val_iZone][val_iInst][MESH_0], config[val_iZone], MESH_0); + integration[val_iZone][val_iInst][FEA_SOL]->SetStructural_Solver(geometry[val_iZone][val_iInst][MESH_0], + solver[val_iZone][val_iInst][MESH_0], config[val_iZone], MESH_0); integration[val_iZone][val_iInst][FEA_SOL]->SetConvergence(false); /*--- Verify convergence criteria (based on total time) ---*/ @@ -1621,87 +1576,84 @@ void CFEAIteration::Update(COutput *output, if (Physical_t >= config[val_iZone]->GetTotal_DynTime()) integration[val_iZone][val_iInst][FEA_SOL]->SetConvergence(true); - } else if ( static_fem && fsi) { + } else if (fsi) { /*--- For FSI problems, output the relaxed result, which is the one transferred into the fluid domain (for restart purposes) ---*/ - switch (config[val_iZone]->GetKind_TimeIntScheme_FEA()) { - case (NEWMARK_IMPLICIT): - solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->ImplicitNewmark_Relaxation(geometry[val_iZone][val_iInst][MESH_0], solver[val_iZone][val_iInst][MESH_0], config[val_iZone]); - break; + if (config[val_iZone]->GetKind_TimeIntScheme_FEA() == NEWMARK_IMPLICIT) { + feaSolver->ImplicitNewmark_Relaxation(geometry[val_iZone][val_iInst][MESH_0], + solver[val_iZone][val_iInst][MESH_0], config[val_iZone]); } } } void CFEAIteration::Predictor(COutput *output, - CIntegration ****integration, - CGeometry ****geometry, - CSolver *****solver, - CNumerics ******numerics, - CConfig **config, - CSurfaceMovement **surface_movement, - CVolumetricMovement ***grid_movement, - CFreeFormDefBox*** FFDBox, - unsigned short val_iZone, - unsigned short val_iInst) { - + CIntegration ****integration, + CGeometry ****geometry, + CSolver *****solver, + CNumerics ******numerics, + CConfig **config, + CSurfaceMovement **surface_movement, + CVolumetricMovement ***grid_movement, + CFreeFormDefBox*** FFDBox, + unsigned short val_iZone, + unsigned short val_iInst) { + + CSolver* feaSolver = solver[val_iZone][val_iInst][MESH_0][FEA_SOL]; + /*--- Predict displacements ---*/ - solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->PredictStruct_Displacement(geometry[val_iZone][val_iInst], config[val_iZone], - solver[val_iZone][val_iInst]); + feaSolver->PredictStruct_Displacement(geometry[val_iZone][val_iInst], config[val_iZone], solver[val_iZone][val_iInst]); /*--- For parallel simulations we need to communicate the predicted solution before updating the fluid mesh ---*/ - solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->InitiateComms(geometry[val_iZone][val_iInst][MESH_0], config[val_iZone], SOLUTION_PRED); - solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->CompleteComms(geometry[val_iZone][val_iInst][MESH_0], config[val_iZone], SOLUTION_PRED); + feaSolver->InitiateComms(geometry[val_iZone][val_iInst][MESH_0], config[val_iZone], SOLUTION_PRED); + feaSolver->CompleteComms(geometry[val_iZone][val_iInst][MESH_0], config[val_iZone], SOLUTION_PRED); } void CFEAIteration::Relaxation(COutput *output, - CIntegration ****integration, - CGeometry ****geometry, - CSolver *****solver, - CNumerics ******numerics, - CConfig **config, - CSurfaceMovement **surface_movement, - CVolumetricMovement ***grid_movement, - CFreeFormDefBox*** FFDBox, - unsigned short val_iZone, - unsigned short val_iInst) { - - unsigned long OuterIter = config[val_iZone]->GetOuterIter(); + CIntegration ****integration, + CGeometry ****geometry, + CSolver *****solver, + CNumerics ******numerics, + CConfig **config, + CSurfaceMovement **surface_movement, + CVolumetricMovement ***grid_movement, + CFreeFormDefBox*** FFDBox, + unsigned short val_iZone, + unsigned short val_iInst) { + + CSolver* feaSolver = solver[val_iZone][val_iInst][MESH_0][FEA_SOL]; /*-------------------- Aitken's relaxation ------------------------*/ /*------------------- Compute the coefficient ---------------------*/ - solver[val_iZone][INST_0][MESH_0][FEA_SOL]->ComputeAitken_Coefficient(geometry[val_iZone][INST_0], config[val_iZone], - solver[val_iZone][INST_0], OuterIter); + feaSolver->ComputeAitken_Coefficient(geometry[val_iZone][INST_0], config[val_iZone], + solver[val_iZone][INST_0], config[val_iZone]->GetOuterIter()); /*----------------- Set the relaxation parameter ------------------*/ - solver[val_iZone][INST_0][MESH_0][FEA_SOL]->SetAitken_Relaxation(geometry[val_iZone][INST_0], config[val_iZone], - solver[val_iZone][INST_0]); + feaSolver->SetAitken_Relaxation(geometry[val_iZone][INST_0], config[val_iZone], solver[val_iZone][INST_0]); /*----------------- Communicate the predicted solution and the old one ------------------*/ - solver[val_iZone][INST_0][MESH_0][FEA_SOL]->InitiateComms(geometry[val_iZone][INST_0][MESH_0], config[val_iZone], SOLUTION_PRED_OLD); - solver[val_iZone][INST_0][MESH_0][FEA_SOL]->CompleteComms(geometry[val_iZone][INST_0][MESH_0], config[val_iZone], SOLUTION_PRED_OLD); + feaSolver->InitiateComms(geometry[val_iZone][INST_0][MESH_0], config[val_iZone], SOLUTION_PRED_OLD); + feaSolver->CompleteComms(geometry[val_iZone][INST_0][MESH_0], config[val_iZone], SOLUTION_PRED_OLD); } bool CFEAIteration::Monitor(COutput *output, - CIntegration ****integration, - CGeometry ****geometry, - CSolver *****solver, - CNumerics ******numerics, - CConfig **config, - CSurfaceMovement **surface_movement, - CVolumetricMovement ***grid_movement, - CFreeFormDefBox*** FFDBox, - unsigned short val_iZone, - unsigned short val_iInst) { - - bool StopCalc = false; + CIntegration ****integration, + CGeometry ****geometry, + CSolver *****solver, + CNumerics ******numerics, + CConfig **config, + CSurfaceMovement **surface_movement, + CVolumetricMovement ***grid_movement, + CFreeFormDefBox*** FFDBox, + unsigned short val_iZone, + unsigned short val_iInst) { #ifndef HAVE_MPI StopTime = su2double(clock())/su2double(CLOCKS_PER_SEC); @@ -1711,32 +1663,20 @@ bool CFEAIteration::Monitor(COutput *output, UsedTime = StopTime - StartTime; solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->Compute_NodalStress(geometry[val_iZone][val_iInst][MESH_0], - numerics[val_iZone][val_iInst][MESH_0][FEA_SOL], config[val_iZone]); + numerics[val_iZone][val_iInst][MESH_0][FEA_SOL], + config[val_iZone]); if (config[val_iZone]->GetMultizone_Problem() || config[val_iZone]->GetSinglezone_Driver()){ - output->SetHistory_Output(geometry[val_iZone][INST_0][MESH_0], solver[val_iZone][INST_0][MESH_0], config[val_iZone], - config[val_iZone]->GetTimeIter(), config[val_iZone]->GetOuterIter(), config[val_iZone]->GetInnerIter()); + output->SetHistory_Output(geometry[val_iZone][INST_0][MESH_0], solver[val_iZone][INST_0][MESH_0], + config[val_iZone], config[val_iZone]->GetTimeIter(), + config[val_iZone]->GetOuterIter(), config[val_iZone]->GetInnerIter()); } - StopCalc = output->GetConvergence(); - - return StopCalc; + return output->GetConvergence(); } void CFEAIteration::Postprocess(COutput *output, - CIntegration ****integration, - CGeometry ****geometry, - CSolver *****solver, - CNumerics ******numerics, - CConfig **config, - CSurfaceMovement **surface_movement, - CVolumetricMovement ***grid_movement, - CFreeFormDefBox*** FFDBox, - unsigned short val_iZone, - unsigned short val_iInst) { } - -void CFEAIteration::Solve(COutput *output, CIntegration ****integration, CGeometry ****geometry, CSolver *****solver, @@ -1746,14 +1686,24 @@ void CFEAIteration::Solve(COutput *output, CVolumetricMovement ***grid_movement, CFreeFormDefBox*** FFDBox, unsigned short val_iZone, - unsigned short val_iInst - ) { + unsigned short val_iInst) { } - /*------------------ Structural subiteration ----------------------*/ - Iterate(output, integration, geometry, - solver, numerics, config, - surface_movement, grid_movement, FFDBox, val_iZone, INST_0); +void CFEAIteration::Solve(COutput *output, + CIntegration ****integration, + CGeometry ****geometry, + CSolver *****solver, + CNumerics ******numerics, + CConfig **config, + CSurfaceMovement **surface_movement, + CVolumetricMovement ***grid_movement, + CFreeFormDefBox*** FFDBox, + unsigned short val_iZone, + unsigned short val_iInst + ) { + /*------------------ Structural subiteration ----------------------*/ + Iterate(output, integration, geometry, solver, numerics, config, + surface_movement, grid_movement, FFDBox, val_iZone, INST_0); /*--- Write the convergence history for the structure (only screen output) ---*/ // if (multizone) output->SetConvHistory_Body(geometry, solver, config, integration, false, 0.0, val_iZone, INST_0); diff --git a/SU2_CFD/src/numerics/elasticity/CFEALinearElasticity.cpp b/SU2_CFD/src/numerics/elasticity/CFEALinearElasticity.cpp index c7e71511aaa9..7e09c1410b90 100644 --- a/SU2_CFD/src/numerics/elasticity/CFEALinearElasticity.cpp +++ b/SU2_CFD/src/numerics/elasticity/CFEALinearElasticity.cpp @@ -345,7 +345,7 @@ void CFEALinearElasticity::Compute_Averaged_NodalStress(CElement *element, const CFEAMeshElasticity::CFEAMeshElasticity(unsigned short val_nDim, unsigned short val_nVar, - unsigned long val_nElem, CConfig *config) : + unsigned long val_nElem, const CConfig *config) : CFEALinearElasticity() { DV_Val = NULL; FAux_Dead_Load = NULL; @@ -358,7 +358,7 @@ CFEAMeshElasticity::CFEAMeshElasticity(unsigned short val_nDim, unsigned short v unsigned long iVar; - E = config->GetDeform_ElasticityMod(); + E = 1.0; Nu = config->GetDeform_PoissonRatio(); Compute_Lame_Parameters(); diff --git a/SU2_CFD/src/solvers/CEulerSolver.cpp b/SU2_CFD/src/solvers/CEulerSolver.cpp index addb18101fe3..b35a3faa5f60 100644 --- a/SU2_CFD/src/solvers/CEulerSolver.cpp +++ b/SU2_CFD/src/solvers/CEulerSolver.cpp @@ -208,6 +208,57 @@ CEulerSolver::CEulerSolver(CGeometry *geometry, CConfig *config, LinSysSol.Initialize(nPoint, nPointDomain, nVar, 0.0); LinSysRes.Initialize(nPoint, nPointDomain, nVar, 0.0); +#ifdef HAVE_OMP + /*--- Get the edge coloring. If the expected parallel efficiency becomes too low setup the + * reducer strategy. Where one loop is performed over edges followed by a point loop to + * sum the fluxes for each cell and set the diagonal of the system matrix. ---*/ + + su2double parallelEff = 1.0; + const auto& coloring = geometry->GetEdgeColoring(¶llelEff); + + /*--- The decision to use the strategy is local to each rank. ---*/ + ReducerStrategy = parallelEff < COLORING_EFF_THRESH; + + /*--- When using the reducer force a single color to reduce the color loop overhead. ---*/ + if (ReducerStrategy && (coloring.getOuterSize()>1)) + geometry->SetNaturalEdgeColoring(); + + if (!coloring.empty()) { + /*--- If the reducer strategy is used we are not constrained by group + * size as we have no other edge loops in the Euler/NS solvers. ---*/ + auto groupSize = ReducerStrategy? 1ul : geometry->GetEdgeColorGroupSize(); + auto nColor = coloring.getOuterSize(); + EdgeColoring.reserve(nColor); + + for(auto iColor = 0ul; iColor < nColor; ++iColor) + EdgeColoring.emplace_back(coloring.innerIdx(iColor), coloring.getNumNonZeros(iColor), groupSize); + } + + /*--- If the reducer strategy is not being forced (by EDGE_COLORING_GROUP_SIZE=0) print some messages. ---*/ + if (config->GetEdgeColoringGroupSize() != 1<<30) { + + su2double minEff = 1.0; + SU2_MPI::Reduce(¶llelEff, &minEff, 1, MPI_DOUBLE, MPI_MIN, MASTER_NODE, MPI_COMM_WORLD); + + int tmp = ReducerStrategy, numRanksUsingReducer = 0; + SU2_MPI::Reduce(&tmp, &numRanksUsingReducer, 1, MPI_INT, MPI_SUM, MASTER_NODE, MPI_COMM_WORLD); + + if (minEff < COLORING_EFF_THRESH) { + cout << "WARNING: On " << numRanksUsingReducer << " MPI ranks the coloring efficiency was less than " + << COLORING_EFF_THRESH << " (min value was " << minEff << ").\n" + << " Those ranks will now use a fallback strategy, better performance may be possible\n" + << " with a different value of config option EDGE_COLORING_GROUP_SIZE (default 512)." << endl; + } + } + + if (ReducerStrategy) + EdgeFluxes.Initialize(geometry->GetnEdge(), geometry->GetnEdge(), nVar, nullptr); + + omp_chunk_size = computeStaticChunkSize(nPoint, omp_get_max_threads(), OMP_MAX_SIZE); +#else + EdgeColoring[0] = DummyGridColor<>(geometry->GetnEdge()); +#endif + /*--- Jacobians and vector structures for implicit computations ---*/ if (config->GetKind_TimeIntScheme_Flow() == EULER_IMPLICIT) { @@ -222,7 +273,7 @@ CEulerSolver::CEulerSolver(CGeometry *geometry, CConfig *config, if (rank == MASTER_NODE) cout << "Initialize Jacobian structure (" << description << "). MG level: " << iMesh <<"." << endl; - Jacobian.Initialize(nPoint, nPointDomain, nVar, nVar, true, geometry, config); + Jacobian.Initialize(nPoint, nPointDomain, nVar, nVar, true, geometry, config, ReducerStrategy); if (config->GetKind_Linear_Solver_Prec() == LINELET) { nLineLets = Jacobian.BuildLineletPreconditioner(geometry, config); @@ -420,32 +471,6 @@ CEulerSolver::CEulerSolver(CGeometry *geometry, CConfig *config, } SetBaseClassPointerToNodes(); -#ifdef HAVE_OMP - /*--- Get the edge coloring, on coarse grids (which are difficult to color) setup the reducer strategy, - * if required (i.e. in parallel). One loop is performed over edges followed by a point loop to sum - * the fluxes for each cell and set the diagonal of the system matrix. ---*/ - - const auto& coloring = geometry->GetEdgeColoring(); - - if (!coloring.empty()) { - auto nColor = coloring.getOuterSize(); - EdgeColoring.reserve(nColor); - - for(auto iColor = 0ul; iColor < nColor; ++iColor) { - EdgeColoring.emplace_back(coloring.innerIdx(iColor), coloring.getNumNonZeros(iColor)); - } - } - ColorGroupSize = geometry->GetEdgeColorGroupSize(); - - if ((MGLevel != MESH_0) && (omp_get_max_threads() > 1)) { - EdgeFluxes.Initialize(geometry->GetnEdge(), geometry->GetnEdge(), nVar, nullptr); - } - - omp_chunk_size = computeStaticChunkSize(nPoint, omp_get_max_threads(), OMP_MAX_SIZE); -#else - EdgeColoring[0] = DummyGridColor<>(geometry->GetnEdge()); -#endif - /*--- Check that the initial solution is physical, report any non-physical nodes ---*/ counter_local = 0; @@ -485,7 +510,7 @@ CEulerSolver::CEulerSolver(CGeometry *geometry, CConfig *config, SU2_MPI::Reduce(&counter_local, &counter_global, 1, MPI_UNSIGNED_LONG, MPI_SUM, MASTER_NODE, MPI_COMM_WORLD); if ((rank == MASTER_NODE) && (counter_global != 0)) - cout << "Warning. The original solution contains "<< counter_global << " points that are not physical." << endl; + cout << "Warning. The original solution contains " << counter_global << " points that are not physical." << endl; } /*--- Initialize the BGS residuals in FSI problems. ---*/ @@ -538,6 +563,10 @@ CEulerSolver::CEulerSolver(CGeometry *geometry, CConfig *config, /*--- Add the solver name (max 8 characters) ---*/ SolverName = "C.FLOW"; + /*--- Finally, check that the static arrays will be large enough (keep this + * check at the bottom to make sure we consider the "final" values). ---*/ + if((nDim > MAXNDIM) || (nPrimVar > MAXNVAR) || (nSecondaryVar > MAXNVAR)) + SU2_MPI::Error("Oops! The CEulerSolver static array sizes are not large enough.",CURRENT_FUNCTION); } CEulerSolver::~CEulerSolver(void) { @@ -2489,22 +2518,18 @@ void CEulerSolver::SetInitialCondition(CGeometry **geometry, CSolver ***solver_c } -void CEulerSolver::Preprocessing(CGeometry *geometry, CSolver **solver_container, CConfig *config, unsigned short iMesh, - unsigned short iRKStep, unsigned short RunTime_EqSystem, bool Output) { +void CEulerSolver::CommonPreprocessing(CGeometry *geometry, CSolver **solver_container, CConfig *config, unsigned short iMesh, + unsigned short iRKStep, unsigned short RunTime_EqSystem, bool Output) { - unsigned long InnerIter = config->GetInnerIter(); bool cont_adjoint = config->GetContinuous_Adjoint(); bool disc_adjoint = config->GetDiscrete_Adjoint(); bool implicit = (config->GetKind_TimeIntScheme_Flow() == EULER_IMPLICIT); - bool muscl = (config->GetMUSCL_Flow() || (cont_adjoint && config->GetKind_ConvNumScheme_AdjFlow() == ROE)); - bool limiter = (config->GetKind_SlopeLimit_Flow() != NO_LIMITER) && (InnerIter <= config->GetLimiterIter()); bool center = (config->GetKind_ConvNumScheme_Flow() == SPACE_CENTERED) || (cont_adjoint && config->GetKind_ConvNumScheme_AdjFlow() == SPACE_CENTERED); - bool center_jst = center && (config->GetKind_Centered_Flow() == JST); + bool center_jst = (config->GetKind_Centered_Flow() == JST) && (iMesh == MESH_0); bool engine = ((config->GetnMarker_EngineInflow() != 0) || (config->GetnMarker_EngineExhaust() != 0)); bool actuator_disk = ((config->GetnMarker_ActDiskInlet() != 0) || (config->GetnMarker_ActDiskOutlet() != 0)); bool nearfield = (config->GetnMarker_NearFieldBound() != 0); bool fixed_cl = config->GetFixed_CL_Mode(); - bool van_albada = config->GetKind_SlopeLimit_Flow() == VAN_ALBADA_EDGE; unsigned short kind_row_dissipation = config->GetKind_RoeLowDiss(); bool roe_low_dissipation = (kind_row_dissipation != NO_ROELOWDISS) && (config->GetKind_Upwind_Flow() == ROE || @@ -2567,33 +2592,12 @@ void CEulerSolver::Preprocessing(CGeometry *geometry, CSolver **solver_container SU2_OMP_BARRIER } - /*--- Upwind second order reconstruction ---*/ - - if ((muscl && !center) && (iMesh == MESH_0) && !Output) { - - /*--- Gradient computation for MUSCL reconstruction. ---*/ - - if (config->GetKind_Gradient_Method_Recon() == GREEN_GAUSS) - SetPrimitive_Gradient_GG(geometry, config, true); - if (config->GetKind_Gradient_Method_Recon() == LEAST_SQUARES) - SetPrimitive_Gradient_LS(geometry, config, true); - if (config->GetKind_Gradient_Method_Recon() == WEIGHTED_LEAST_SQUARES) - SetPrimitive_Gradient_LS(geometry, config, true); - - /*--- Limiter computation ---*/ - - if (limiter && (iMesh == MESH_0) && !Output && !van_albada) - SetPrimitive_Limiter(geometry, config); - } - /*--- Artificial dissipation ---*/ if (center && !Output) { SetMax_Eigenvalue(geometry, config); - if ((center_jst) && (iMesh == MESH_0)) { - SetCentered_Dissipation_Sensor(geometry, config); - SetUndivided_Laplacian(geometry, config); - } + if (center_jst) + SetUndivided_Laplacian_And_Centered_Dissipation_Sensor(geometry, config); } /*--- Roe Low Dissipation Sensor ---*/ @@ -2605,9 +2609,51 @@ void CEulerSolver::Preprocessing(CGeometry *geometry, CSolver **solver_container } } - /*--- Initialize the Jacobian matrices ---*/ + /*--- Initialize the Jacobian matrix and residual, not needed for the reducer strategy + * as we set blocks (including diagonal ones) and completely overwrite. ---*/ - if (implicit && !disc_adjoint) Jacobian.SetValZero(); + if(!ReducerStrategy && !Output) { + LinSysRes.SetValZero(); + if (implicit && !disc_adjoint) Jacobian.SetValZero(); + else {SU2_OMP_BARRIER} // because of "nowait" in LinSysRes + } + +} + +void CEulerSolver::Preprocessing(CGeometry *geometry, CSolver **solver_container, CConfig *config, unsigned short iMesh, + unsigned short iRKStep, unsigned short RunTime_EqSystem, bool Output) { + + unsigned long InnerIter = config->GetInnerIter(); + bool cont_adjoint = config->GetContinuous_Adjoint(); + bool muscl = (config->GetMUSCL_Flow() || (cont_adjoint && config->GetKind_ConvNumScheme_AdjFlow() == ROE)); + bool limiter = (config->GetKind_SlopeLimit_Flow() != NO_LIMITER) && (InnerIter <= config->GetLimiterIter()); + bool center = (config->GetKind_ConvNumScheme_Flow() == SPACE_CENTERED) || (cont_adjoint && config->GetKind_ConvNumScheme_AdjFlow() == SPACE_CENTERED); + bool van_albada = config->GetKind_SlopeLimit_Flow() == VAN_ALBADA_EDGE; + + /*--- Common preprocessing steps. ---*/ + + CommonPreprocessing(geometry, solver_container, config, iMesh, iRKStep, RunTime_EqSystem, Output); + + /*--- Upwind second order reconstruction ---*/ + + if ((muscl && !center) && (iMesh == MESH_0) && !Output) { + + /*--- Gradient computation for MUSCL reconstruction. ---*/ + + switch (config->GetKind_Gradient_Method_Recon()) { + case GREEN_GAUSS: + SetPrimitive_Gradient_GG(geometry, config, true); break; + case LEAST_SQUARES: + case WEIGHTED_LEAST_SQUARES: + SetPrimitive_Gradient_LS(geometry, config, true); break; + default: break; + } + + /*--- Limiter computation ---*/ + + if (limiter && (iMesh == MESH_0) && !Output && !van_albada) + SetPrimitive_Limiter(geometry, config); + } } @@ -2628,11 +2674,6 @@ unsigned long CEulerSolver::SetPrimitive_Variables(CSolver **solver_container, C /* Check for non-realizable states for reporting. */ if (!physical) nonPhysicalPoints++; - - /*--- Initialize the convective, source and viscous residual vector ---*/ - - if (!Output) LinSysRes.SetBlock_Zero(iPoint); - } return nonPhysicalPoints; @@ -2910,14 +2951,11 @@ void CEulerSolver::Centered_Residual(CGeometry *geometry, CSolver **solver_conta /*--- Pick one numerics object per thread. ---*/ CNumerics* numerics = numerics_container[CONV_TERM + omp_get_thread_num()*MAX_TERMS]; - /*--- Determine if using the reducer strategy is necessary, see CEulerSolver::SumEdgeFluxes(). ---*/ - const bool reducer_strategy = (MGLevel != MESH_0) && (omp_get_num_threads() > 1); - /*--- Loop over edge colors. ---*/ for (auto color : EdgeColoring) { /*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/ - SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize) + SU2_OMP_FOR_DYN(nextMultiple(OMP_MIN_SIZE, color.groupSize)) for(auto k = 0ul; k < color.size; ++k) { auto iEdge = color.indices[k]; @@ -2959,16 +2997,14 @@ void CEulerSolver::Centered_Residual(CGeometry *geometry, CSolver **solver_conta /*--- Update convective and artificial dissipation residuals. ---*/ - if (reducer_strategy) { + if (ReducerStrategy) { EdgeFluxes.SetBlock(iEdge, residual); if (implicit) - Jacobian.UpdateBlocks(iEdge, residual.jacobian_i, residual.jacobian_j); + Jacobian.SetBlocks(iEdge, residual.jacobian_i, residual.jacobian_j); } else { LinSysRes.AddBlock(iPoint, residual); LinSysRes.SubtractBlock(jPoint, residual); - - /*--- Set implicit computation ---*/ if (implicit) Jacobian.UpdateBlocks(iEdge, iPoint, jPoint, residual.jacobian_i, residual.jacobian_j); } @@ -2980,7 +3016,7 @@ void CEulerSolver::Centered_Residual(CGeometry *geometry, CSolver **solver_conta } } // end color loop - if (reducer_strategy) { + if (ReducerStrategy) { SumEdgeFluxes(geometry); if (implicit) Jacobian.SetDiagonalAsColumnSum(); @@ -2991,9 +3027,6 @@ void CEulerSolver::Centered_Residual(CGeometry *geometry, CSolver **solver_conta void CEulerSolver::Upwind_Residual(CGeometry *geometry, CSolver **solver_container, CNumerics **numerics_container, CConfig *config, unsigned short iMesh) { - assert(nDim <= MAXNDIM && nPrimVar <= MAXNVAR && nSecondaryVar <= MAXNVAR && - "Oops! The CEulerSolver static array sizes are not large enough."); - const auto InnerIter = config->GetInnerIter(); const bool implicit = (config->GetKind_TimeIntScheme_Flow() == EULER_IMPLICIT); const bool ideal_gas = (config->GetKind_FluidModel() == STANDARD_AIR) || @@ -3020,14 +3053,11 @@ void CEulerSolver::Upwind_Residual(CGeometry *geometry, CSolver **solver_contain su2double Primitive_i[MAXNVAR] = {0.0}, Primitive_j[MAXNVAR] = {0.0}; su2double Secondary_i[MAXNVAR] = {0.0}, Secondary_j[MAXNVAR] = {0.0}; - /*--- Determine if using the reducer strategy is necessary, see CEulerSolver::SumEdgeFluxes(). ---*/ - const bool reducer_strategy = (MGLevel != MESH_0) && (omp_get_num_threads() > 1); - /*--- Loop over edge colors. ---*/ for (auto color : EdgeColoring) { /*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/ - SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize) + SU2_OMP_FOR_DYN(nextMultiple(OMP_MIN_SIZE, color.groupSize)) for(auto k = 0ul; k < color.size; ++k) { auto iEdge = color.indices[k]; @@ -3193,10 +3223,10 @@ void CEulerSolver::Upwind_Residual(CGeometry *geometry, CSolver **solver_contain /*--- Update residual value ---*/ - if (reducer_strategy) { + if (ReducerStrategy) { EdgeFluxes.SetBlock(iEdge, residual); if (implicit) - Jacobian.UpdateBlocks(iEdge, residual.jacobian_i, residual.jacobian_j); + Jacobian.SetBlocks(iEdge, residual.jacobian_i, residual.jacobian_j); } else { LinSysRes.AddBlock(iPoint, residual); @@ -3214,7 +3244,7 @@ void CEulerSolver::Upwind_Residual(CGeometry *geometry, CSolver **solver_contain } } // end color loop - if (reducer_strategy) { + if (ReducerStrategy) { SumEdgeFluxes(geometry); if (implicit) Jacobian.SetDiagonalAsColumnSum(); @@ -3245,6 +3275,8 @@ void CEulerSolver::SumEdgeFluxes(CGeometry* geometry) { SU2_OMP_FOR_STAT(omp_chunk_size) for (unsigned long iPoint = 0; iPoint < nPoint; ++iPoint) { + LinSysRes.SetBlock_Zero(iPoint); + for (unsigned short iNeigh = 0; iNeigh < geometry->node[iPoint]->GetnPoint(); ++iNeigh) { auto iEdge = geometry->node[iPoint]->GetEdge(iNeigh); @@ -3632,142 +3664,82 @@ void CEulerSolver::SetMax_Eigenvalue(CGeometry *geometry, CConfig *config) { } -void CEulerSolver::SetUndivided_Laplacian(CGeometry *geometry, CConfig *config) { +void CEulerSolver::SetUndivided_Laplacian_And_Centered_Dissipation_Sensor(CGeometry *geometry, CConfig *config) { - nodes->SetUnd_LaplZero(); + /*--- We can access memory more efficiently if there are no periodic boundaries. ---*/ - /*--- Loop over edge colors. ---*/ - for (auto color : EdgeColoring) - { - /*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/ - SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize) - for(auto k = 0ul; k < color.size; ++k) { + const bool isPeriodic = (config->GetnMarker_Periodic() > 0); - auto iEdge = color.indices[k]; - - auto iPoint = geometry->edge[iEdge]->GetNode(0); - auto jPoint = geometry->edge[iEdge]->GetNode(1); + /*--- Loop domain points. ---*/ - /*--- Solution differences ---*/ + SU2_OMP_FOR_DYN(omp_chunk_size) + for (unsigned long iPoint = 0; iPoint < nPointDomain; ++iPoint) { - su2double Diff[MAXNVAR] = {0.0}; + const bool boundary_i = geometry->node[iPoint]->GetPhysicalBoundary(); + const su2double Pressure_i = nodes->GetPressure(iPoint); + /*--- Initialize. ---*/ for (unsigned short iVar = 0; iVar < nVar; iVar++) - Diff[iVar] = nodes->GetSolution(iPoint,iVar) - nodes->GetSolution(jPoint,iVar); - - /*--- Correction for compressible flows which use the enthalpy ---*/ - - Diff[nVar-1] += nodes->GetPressure(iPoint) - nodes->GetPressure(jPoint); + nodes->SetUnd_Lapl(iPoint, iVar, 0.0); - bool boundary_i = geometry->node[iPoint]->GetPhysicalBoundary(); - bool boundary_j = geometry->node[jPoint]->GetPhysicalBoundary(); - - /*--- Both points inside the domain, or both in the boundary ---*/ - - if ((!boundary_i && !boundary_j) || (boundary_i && boundary_j)) { - if (geometry->node[iPoint]->GetDomain()) nodes->SubtractUnd_Lapl(iPoint, Diff); - if (geometry->node[jPoint]->GetDomain()) nodes->AddUnd_Lapl(jPoint, Diff); - } - - /*--- iPoint inside the domain, jPoint on the boundary ---*/ + iPoint_UndLapl[iPoint] = 0.0; + jPoint_UndLapl[iPoint] = 0.0; - if (!boundary_i && boundary_j) - if (geometry->node[iPoint]->GetDomain()) nodes->SubtractUnd_Lapl(iPoint, Diff); + /*--- Loop over the neighbors of point i. ---*/ + for (unsigned short iNeigh = 0; iNeigh < geometry->node[iPoint]->GetnPoint(); ++iNeigh) + { + auto jPoint = geometry->node[iPoint]->GetPoint(iNeigh); + bool boundary_j = geometry->node[jPoint]->GetPhysicalBoundary(); - /*--- jPoint inside the domain, iPoint on the boundary ---*/ + /*--- If iPoint is boundary it only takes contributions from other boundary points. ---*/ + if (boundary_i && !boundary_j) continue; - if (boundary_i && !boundary_j) - if (geometry->node[jPoint]->GetDomain()) nodes->AddUnd_Lapl(jPoint, Diff); + /*--- Add solution differences, with correction for compressible flows which use the enthalpy. ---*/ - } - } // end color loop + for (unsigned short iVar = 0; iVar < nVar; iVar++) + nodes->AddUnd_Lapl(iPoint, iVar, nodes->GetSolution(jPoint,iVar)-nodes->GetSolution(iPoint,iVar)); - SU2_OMP_MASTER - { - /*--- Correct the Laplacian values across any periodic boundaries. ---*/ + su2double Pressure_j = nodes->GetPressure(jPoint); + nodes->AddUnd_Lapl(iPoint, nVar-1, Pressure_j-Pressure_i); - for (unsigned short iPeriodic = 1; iPeriodic <= config->GetnMarker_Periodic()/2; iPeriodic++) { - InitiatePeriodicComms(geometry, config, iPeriodic, PERIODIC_LAPLACIAN); - CompletePeriodicComms(geometry, config, iPeriodic, PERIODIC_LAPLACIAN); + /*--- Dissipation sensor, add pressure difference and pressure sum. ---*/ + iPoint_UndLapl[iPoint] += Pressure_j - Pressure_i; + jPoint_UndLapl[iPoint] += Pressure_j + Pressure_i; } - /*--- MPI parallelization ---*/ - - InitiateComms(geometry, config, UNDIVIDED_LAPLACIAN); - CompleteComms(geometry, config, UNDIVIDED_LAPLACIAN); - } - SU2_OMP_BARRIER - -} - -void CEulerSolver::SetCentered_Dissipation_Sensor(CGeometry *geometry, CConfig *config) { - - /*--- Reset variables to store the undivided pressure ---*/ - - SU2_OMP_FOR_STAT(omp_chunk_size) - for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++) { - iPoint_UndLapl[iPoint] = 0.0; - jPoint_UndLapl[iPoint] = 0.0; + if (!isPeriodic) + nodes->SetSensor(iPoint, fabs(iPoint_UndLapl[iPoint]) / jPoint_UndLapl[iPoint]); } - /*--- Loop over edge colors. ---*/ - for (auto color : EdgeColoring) - { - /*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/ - SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize) - for(auto k = 0ul; k < color.size; ++k) { - - auto iEdge = color.indices[k]; - - auto iPoint = geometry->edge[iEdge]->GetNode(0); - auto jPoint = geometry->edge[iEdge]->GetNode(1); - - su2double Pressure_i = nodes->GetPressure(iPoint); - su2double Pressure_j = nodes->GetPressure(jPoint); + if (isPeriodic) { + /*--- Correct the Laplacian and sensor values across any periodic boundaries. ---*/ - bool boundary_i = geometry->node[iPoint]->GetPhysicalBoundary(); - bool boundary_j = geometry->node[jPoint]->GetPhysicalBoundary(); - - /*--- Both points inside the domain, or both on the boundary ---*/ + SU2_OMP_MASTER + { + for (unsigned short iPeriodic = 1; iPeriodic <= config->GetnMarker_Periodic()/2; iPeriodic++) { + InitiatePeriodicComms(geometry, config, iPeriodic, PERIODIC_LAPLACIAN); + CompletePeriodicComms(geometry, config, iPeriodic, PERIODIC_LAPLACIAN); - if ((!boundary_i && !boundary_j) || (boundary_i && boundary_j)) { - if (geometry->node[iPoint]->GetDomain()) { iPoint_UndLapl[iPoint] += (Pressure_j - Pressure_i); jPoint_UndLapl[iPoint] += (Pressure_i + Pressure_j); } - if (geometry->node[jPoint]->GetDomain()) { iPoint_UndLapl[jPoint] += (Pressure_i - Pressure_j); jPoint_UndLapl[jPoint] += (Pressure_i + Pressure_j); } + InitiatePeriodicComms(geometry, config, iPeriodic, PERIODIC_SENSOR); + CompletePeriodicComms(geometry, config, iPeriodic, PERIODIC_SENSOR); + } } + SU2_OMP_BARRIER - /*--- iPoint inside the domain, jPoint on the boundary ---*/ - - if (!boundary_i && boundary_j) - if (geometry->node[iPoint]->GetDomain()) { iPoint_UndLapl[iPoint] += (Pressure_j - Pressure_i); jPoint_UndLapl[iPoint] += (Pressure_i + Pressure_j); } - - /*--- jPoint inside the domain, iPoint on the boundary ---*/ + /*--- Set final pressure switch for each point ---*/ - if (boundary_i && !boundary_j) - if (geometry->node[jPoint]->GetDomain()) { iPoint_UndLapl[jPoint] += (Pressure_i - Pressure_j); jPoint_UndLapl[jPoint] += (Pressure_i + Pressure_j); } + SU2_OMP_FOR_STAT(omp_chunk_size) + for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++) + nodes->SetSensor(iPoint, fabs(iPoint_UndLapl[iPoint]) / jPoint_UndLapl[iPoint]); } - } // end color loop - - /*--- Correct the sensor values across any periodic boundaries. ---*/ SU2_OMP_MASTER { - for (unsigned short iPeriodic = 1; iPeriodic <= config->GetnMarker_Periodic()/2; iPeriodic++) { - InitiatePeriodicComms(geometry, config, iPeriodic, PERIODIC_SENSOR); - CompletePeriodicComms(geometry, config, iPeriodic, PERIODIC_SENSOR); - } - } - SU2_OMP_BARRIER - - /*--- Set pressure switch for each point ---*/ - - SU2_OMP_FOR_STAT(omp_chunk_size) - for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++) - nodes->SetSensor(iPoint, fabs(iPoint_UndLapl[iPoint]) / jPoint_UndLapl[iPoint]); + /*--- MPI parallelization ---*/ - /*--- MPI parallelization ---*/ + InitiateComms(geometry, config, UNDIVIDED_LAPLACIAN); + CompleteComms(geometry, config, UNDIVIDED_LAPLACIAN); - SU2_OMP_MASTER - { InitiateComms(geometry, config, SENSOR); CompleteComms(geometry, config, SENSOR); } diff --git a/SU2_CFD/src/solvers/CFEASolver.cpp b/SU2_CFD/src/solvers/CFEASolver.cpp index 8f0066180225..28e338731015 100644 --- a/SU2_CFD/src/solvers/CFEASolver.cpp +++ b/SU2_CFD/src/solvers/CFEASolver.cpp @@ -96,15 +96,10 @@ CFEASolver::CFEASolver(bool mesh_deform_mode) : CSolver(mesh_deform_mode) { for (unsigned short iTerm = 0; iTerm < MAX_TERMS; iTerm++) element_container[iTerm] = new CElement* [MAX_FE_KINDS*omp_get_max_threads()](); - nodes = nullptr; - - element_properties = nullptr; - - iElem_iDe = nullptr; - topol_filter_applied = false; - element_based = false; + initial_calc = true; + } CFEASolver::CFEASolver(CGeometry *geometry, CConfig *config) : CSolver() { @@ -118,8 +113,8 @@ CFEASolver::CFEASolver(CGeometry *geometry, CConfig *config) : CSolver() { /*--- A priori we don't have an element-based input file (most of the applications will be like this) ---*/ element_based = false; - topol_filter_applied = false; + initial_calc = true; nElement = geometry->GetnElem(); nDim = geometry->GetnDim(); @@ -240,27 +235,8 @@ CFEASolver::CFEASolver(CGeometry *geometry, CConfig *config) : CSolver() { LinSysReact.Initialize(nPoint, nPointDomain, nVar, 0.0); -#ifdef HAVE_OMP - /*--- Get the element coloring. ---*/ - - const auto& coloring = geometry->GetElementColoring(); - - if (!coloring.empty()) { - auto nColor = coloring.getOuterSize(); - ElemColoring.reserve(nColor); - - for(auto iColor = 0ul; iColor < nColor; ++iColor) { - ElemColoring.emplace_back(coloring.innerIdx(iColor), coloring.getNumNonZeros(iColor)); - } - } - ColorGroupSize = geometry->GetElementColorGroupSize(); - - omp_chunk_size = computeStaticChunkSize(nPointDomain, omp_get_max_threads(), OMP_MAX_SIZE); -#else - ElemColoring[0] = DummyGridColor<>(nElement); -#endif - - iElem_iDe = nullptr; + /*--- Initialize structures for hybrid-parallel mode. ---*/ + HybridParallelInitialization(geometry); /*--- Initialize the value of the total objective function ---*/ Total_OFRefGeom = 0.0; @@ -329,15 +305,65 @@ CFEASolver::~CFEASolver(void) { delete [] element_container; } - if (element_properties != nullptr){ + if (element_properties != nullptr) { for (unsigned long iElem = 0; iElem < nElement; iElem++) - if (element_properties[iElem] != nullptr) delete element_properties[iElem]; + delete element_properties[iElem]; delete [] element_properties; } - if (iElem_iDe != nullptr) delete [] iElem_iDe; + delete [] iElem_iDe; + + delete nodes; + + if (LockStrategy) { + for (unsigned long iPoint = 0; iPoint < nPoint; iPoint++) + omp_destroy_lock(&UpdateLocks[iPoint]); + } +} + +void CFEASolver::HybridParallelInitialization(CGeometry* geometry) { +#ifdef HAVE_OMP + /*--- Get the element coloring. ---*/ + + su2double parallelEff = 1.0; + const auto& coloring = geometry->GetElementColoring(¶llelEff); + + /*--- If the coloring is too bad use lock-guarded accesses + * to CSysMatrix/Vector in element loops instead. ---*/ + LockStrategy = parallelEff < COLORING_EFF_THRESH; + + /*--- When using locks force a single color to reduce the color loop overhead. ---*/ + if (LockStrategy && (coloring.getOuterSize()>1)) + geometry->SetNaturalElementColoring(); + + if (!coloring.empty()) { + /*--- We are not constrained by the color group size when using locks. ---*/ + auto groupSize = LockStrategy? 1ul : geometry->GetElementColorGroupSize(); + auto nColor = coloring.getOuterSize(); + ElemColoring.reserve(nColor); + + for(auto iColor = 0ul; iColor < nColor; ++iColor) + ElemColoring.emplace_back(coloring.innerIdx(iColor), coloring.getNumNonZeros(iColor), groupSize); + } + + su2double minEff = 1.0; + SU2_MPI::Reduce(¶llelEff, &minEff, 1, MPI_DOUBLE, MPI_MIN, MASTER_NODE, MPI_COMM_WORLD); + + if (minEff < COLORING_EFF_THRESH) { + cout << "WARNING: The element coloring efficiency was " << minEff << ", a fallback strategy is in use.\n" + << " Better performance may be possible by reducing the number of threads per rank." << endl; + } + + if (LockStrategy) { + UpdateLocks.resize(nPoint); + for (unsigned long iPoint = 0; iPoint < nPoint; iPoint++) + omp_init_lock(&UpdateLocks[iPoint]); + } - if (nodes != nullptr) delete nodes; + omp_chunk_size = computeStaticChunkSize(nPointDomain, omp_get_max_threads(), OMP_MAX_SIZE); +#else + ElemColoring[0] = DummyGridColor<>(nElement); +#endif } void CFEASolver::Set_ElementProperties(CGeometry *geometry, CConfig *config) { @@ -726,25 +752,14 @@ void CFEASolver::Set_ReferenceGeometry(CGeometry *geometry, CConfig *config) { void CFEASolver::Preprocessing(CGeometry *geometry, CSolver **solver_container, CConfig *config, CNumerics **numerics, unsigned short iMesh, unsigned long Iteration, unsigned short RunTime_EqSystem, bool Output) { - bool dynamic = config->GetTime_Domain(); - bool first_iter = (config->GetInnerIter() == 0); - - /*--- Initial calculation, different logic for restarted simulations. ---*/ - bool initial_calc = false; - if (config->GetRestart()) - initial_calc = (config->GetTimeIter() == config->GetRestart_Iter()) && first_iter; - else - initial_calc = (config->GetTimeIter() == 0) && first_iter; - - bool disc_adj_fem = (config->GetKind_Solver() == DISC_ADJ_FEM); - - bool body_forces = config->GetDeadLoad(); - - bool fsi = config->GetFSI_Simulation(); - bool consistent_interpolation = (!config->GetConservativeInterpolation() || - (config->GetKindInterpolation() == WEIGHTED_AVERAGE)); - - bool topology_mode = config->GetTopology_Optimization(); + const bool dynamic = config->GetTime_Domain(); + const bool first_iter = (config->GetInnerIter() == 0); + const bool disc_adj_fem = (config->GetKind_Solver() == DISC_ADJ_FEM); + const bool body_forces = config->GetDeadLoad(); + const bool fsi = config->GetFSI_Simulation(); + const bool consistent_interpolation = (!config->GetConservativeInterpolation() || + (config->GetKindInterpolation() == WEIGHTED_AVERAGE)); + const bool topology_mode = config->GetTopology_Optimization(); /* * For topology optimization we apply a filter on the design density field to avoid @@ -777,7 +792,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) + if (body_forces && (initial_calc || disc_adj_fem)) Compute_DeadLoad(geometry, numerics, config); /*--- Clear the linear system solution. ---*/ @@ -794,6 +809,9 @@ void CFEASolver::Preprocessing(CGeometry *geometry, CSolver **solver_container, */ if (fsi && first_iter && consistent_interpolation) Integrate_FSI_Loads(geometry,config); + /*--- Next call to Preprocessing will not be "initial_calc" and linear operations will not be repeated. ---*/ + initial_calc = false; + } void CFEASolver::SetInitialCondition(CGeometry **geometry, CSolver ***solver_container, CConfig *config, unsigned long TimeIter) { @@ -829,7 +847,7 @@ void CFEASolver::Compute_StiffMatrix(CGeometry *geometry, CNumerics **numerics, for(auto color : ElemColoring) { /*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/ - SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize) + SU2_OMP_FOR_DYN(nextMultiple(OMP_MIN_SIZE, color.groupSize)) for(auto k = 0ul; k < color.size; ++k) { auto iElem = color.indices[k]; @@ -879,6 +897,8 @@ void CFEASolver::Compute_StiffMatrix(CGeometry *geometry, CNumerics **numerics, /*--- Update residual and stiffness matrix with contributions from the element. ---*/ for (iNode = 0; iNode < nNodes; iNode++) { + if (LockStrategy) omp_set_lock(&UpdateLocks[indexNode[iNode]]); + auto Ta = element->Get_Kt_a(iNode); for (iVar = 0; iVar < nVar; iVar++) LinSysRes(indexNode[iNode], iVar) -= simp_penalty*Ta[iVar]; @@ -887,6 +907,8 @@ void CFEASolver::Compute_StiffMatrix(CGeometry *geometry, CNumerics **numerics, auto Kab = element->Get_Kab(iNode, jNode); Jacobian.AddBlock(indexNode[iNode], indexNode[jNode], simp_penalty, Kab); } + + if (LockStrategy) omp_unset_lock(&UpdateLocks[indexNode[iNode]]); } } // end iElem loop @@ -917,7 +939,7 @@ void CFEASolver::Compute_StiffMatrix_NodalStressRes(CGeometry *geometry, CNumeri for(auto color : ElemColoring) { /*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/ - SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize) + SU2_OMP_FOR_DYN(nextMultiple(OMP_MIN_SIZE, color.groupSize)) for(auto k = 0ul; k < color.size; ++k) { auto iElem = color.indices[k]; @@ -986,6 +1008,8 @@ void CFEASolver::Compute_StiffMatrix_NodalStressRes(CGeometry *geometry, CNumeri /*--- Update residual and stiffness matrix with contributions from the element. ---*/ for (iNode = 0; iNode < nNodes; iNode++) { + if (LockStrategy) omp_set_lock(&UpdateLocks[indexNode[iNode]]); + auto Ta = fea_elem->Get_Kt_a(iNode); for (iVar = 0; iVar < nVar; iVar++) LinSysRes(indexNode[iNode], iVar) -= simp_penalty*Ta[iVar]; @@ -1023,6 +1047,8 @@ void CFEASolver::Compute_StiffMatrix_NodalStressRes(CGeometry *geometry, CNumeri Kij[iVar*(nVar+1)] += SU2_TYPE::GetValue(simp_penalty*Ks_ab_DE); } } + + if (LockStrategy) omp_unset_lock(&UpdateLocks[indexNode[iNode]]); } } // end iElem loop @@ -1048,7 +1074,7 @@ void CFEASolver::Compute_MassMatrix(CGeometry *geometry, CNumerics **numerics, C for(auto color : ElemColoring) { /*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/ - SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize) + SU2_OMP_FOR_DYN(nextMultiple(OMP_MIN_SIZE, color.groupSize)) for(auto k = 0ul; k < color.size; ++k) { auto iElem = color.indices[k]; @@ -1091,6 +1117,8 @@ void CFEASolver::Compute_MassMatrix(CGeometry *geometry, CNumerics **numerics, C /*--- Add contributions of this element to the mass matrix. ---*/ for (iNode = 0; iNode < nNodes; iNode++) { + if (LockStrategy) omp_set_lock(&UpdateLocks[indexNode[iNode]]); + for (jNode = 0; jNode < nNodes; jNode++) { auto Mij = MassMatrix.GetBlock(indexNode[iNode], indexNode[jNode]); @@ -1099,6 +1127,8 @@ void CFEASolver::Compute_MassMatrix(CGeometry *geometry, CNumerics **numerics, C for (iVar = 0; iVar < nVar; iVar++) Mij[iVar*(nVar+1)] += simp_penalty*Mab; } + + if (LockStrategy) omp_unset_lock(&UpdateLocks[indexNode[iNode]]); } } // end iElem loop @@ -1125,7 +1155,7 @@ void CFEASolver::Compute_MassRes(CGeometry *geometry, CNumerics **numerics, CCon for(auto color : ElemColoring) { /*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/ - SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize) + SU2_OMP_FOR_DYN(nextMultiple(OMP_MIN_SIZE, color.groupSize)) for(auto k = 0ul; k < color.size; ++k) { auto iElem = color.indices[k]; @@ -1165,7 +1195,10 @@ void CFEASolver::Compute_MassRes(CGeometry *geometry, CNumerics **numerics, CCon numerics[FEA_TERM + thread*MAX_TERMS]->Compute_Mass_Matrix(element, config); - /*--- Add contributions of this element to the mass matrix. ---*/ + /*--- Add contributions of this element to the mass matrix. + * In case we need to use locks we guard the entire update. ---*/ + if (LockStrategy) omp_set_lock(&UpdateLocks[0]); + for (iNode = 0; iNode < nNodes; iNode++) { for (jNode = 0; jNode < nNodes; jNode++) { @@ -1177,6 +1210,7 @@ void CFEASolver::Compute_MassRes(CGeometry *geometry, CNumerics **numerics, CCon } } } + if (LockStrategy) omp_unset_lock(&UpdateLocks[0]); } // end iElem loop @@ -1205,7 +1239,7 @@ void CFEASolver::Compute_NodalStressRes(CGeometry *geometry, CNumerics **numeric for(auto color : ElemColoring) { /*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/ - SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize) + SU2_OMP_FOR_DYN(nextMultiple(OMP_MIN_SIZE, color.groupSize)) for(auto k = 0ul; k < color.size; ++k) { auto iElem = color.indices[k]; @@ -1260,9 +1294,13 @@ void CFEASolver::Compute_NodalStressRes(CGeometry *geometry, CNumerics **numeric numerics[NUM_TERM]->Compute_NodalStress_Term(element, config); for (iNode = 0; iNode < nNodes; iNode++) { + if (LockStrategy) omp_set_lock(&UpdateLocks[indexNode[iNode]]); + auto Ta = element->Get_Kt_a(iNode); for (iVar = 0; iVar < nVar; iVar++) LinSysRes(indexNode[iNode], iVar) -= simp_penalty*Ta[iVar]; + + if (LockStrategy) omp_unset_lock(&UpdateLocks[indexNode[iNode]]); } } // end iElem loop @@ -1302,7 +1340,7 @@ void CFEASolver::Compute_NodalStress(CGeometry *geometry, CNumerics **numerics, for(auto color : ElemColoring) { /*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/ - SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize) + SU2_OMP_FOR_DYN(nextMultiple(OMP_MIN_SIZE, color.groupSize)) for(auto k = 0ul; k < color.size; ++k) { auto iElem = color.indices[k]; @@ -1359,6 +1397,8 @@ void CFEASolver::Compute_NodalStress(CGeometry *geometry, CNumerics **numerics, auto iPoint = indexNode[iNode]; + if (LockStrategy) omp_set_lock(&UpdateLocks[iPoint]); + auto Ta = element->Get_Kt_a(iNode); for (iVar = 0; iVar < nVar; iVar++) LinSysReact(iPoint,iVar) += simp_penalty*Ta[iVar]; @@ -1368,6 +1408,8 @@ void CFEASolver::Compute_NodalStress(CGeometry *geometry, CNumerics **numerics, for (iStress = 0; iStress < nStress; iStress++) nodes->AddStress_FEM(iPoint,iStress, weight*element->Get_NodalStress(iNode,iStress)); + + if (LockStrategy) omp_unset_lock(&UpdateLocks[iPoint]); } } // end iElem loop @@ -1564,7 +1606,7 @@ void CFEASolver::Compute_DeadLoad(CGeometry *geometry, CNumerics **numerics, CCo for(auto color : ElemColoring) { /*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/ - SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize) + SU2_OMP_FOR_DYN(nextMultiple(OMP_MIN_SIZE, color.groupSize)) for(auto k = 0ul; k < color.size; ++k) { auto iElem = color.indices[k]; @@ -1604,6 +1646,8 @@ void CFEASolver::Compute_DeadLoad(CGeometry *geometry, CNumerics **numerics, CCo /*--- Add contributions of this element to the mass matrix. ---*/ for (iNode = 0; iNode < nNodes; iNode++) { + if (LockStrategy) omp_set_lock(&UpdateLocks[indexNode[iNode]]); + auto Dead_Load = element->Get_FDL_a(iNode); su2double Aux_Dead_Load[MAXNVAR]; @@ -1611,6 +1655,8 @@ void CFEASolver::Compute_DeadLoad(CGeometry *geometry, CNumerics **numerics, CCo Aux_Dead_Load[iVar] = simp_penalty*Dead_Load[iVar]; nodes->Add_BodyForces_Res(indexNode[iNode], Aux_Dead_Load); + + if (LockStrategy) omp_unset_lock(&UpdateLocks[indexNode[iNode]]); } } // end iElem loop diff --git a/SU2_CFD/src/solvers/CMeshSolver.cpp b/SU2_CFD/src/solvers/CMeshSolver.cpp index c0ba9e1abd0b..25e4b711fc9f 100644 --- a/SU2_CFD/src/solvers/CMeshSolver.cpp +++ b/SU2_CFD/src/solvers/CMeshSolver.cpp @@ -96,39 +96,17 @@ CMeshSolver::CMeshSolver(CGeometry *geometry, CConfig *config) : CFEASolver(true /*--- Initialize matrix, solution, and r.h.s. structures for the linear solver. ---*/ + if (rank == MASTER_NODE) cout << "Initialize Jacobian structure (Mesh Deformation)." << endl; + LinSysSol.Initialize(nPoint, nPointDomain, nVar, 0.0); LinSysRes.Initialize(nPoint, nPointDomain, nVar, 0.0); Jacobian.Initialize(nPoint, nPointDomain, nVar, nVar, false, geometry, config); -#ifdef HAVE_OMP - /*--- Get the element coloring. ---*/ - - const auto& coloring = geometry->GetElementColoring(); - - if (!coloring.empty()) { - auto nColor = coloring.getOuterSize(); - ElemColoring.reserve(nColor); - - for(auto iColor = 0ul; iColor < nColor; ++iColor) { - ElemColoring.emplace_back(coloring.innerIdx(iColor), coloring.getNumNonZeros(iColor)); - } - } - ColorGroupSize = geometry->GetElementColorGroupSize(); + /*--- Initialize structures for hybrid-parallel mode. ---*/ - omp_chunk_size = computeStaticChunkSize(nPointDomain, omp_get_max_threads(), OMP_MAX_SIZE); -#else - ElemColoring[0] = DummyGridColor<>(nElement); -#endif + HybridParallelInitialization(geometry); - /*--- Structural parameters ---*/ - - E = config->GetDeform_ElasticityMod(); - Nu = config->GetDeform_PoissonRatio(); - - Mu = E / (2.0*(1.0 + Nu)); - Lambda = Nu*E/((1.0+Nu)*(1.0-2.0*Nu)); - - /*--- Element container structure ---*/ + /*--- Element container structure. ---*/ if (nDim == 2) { for(int thread = 0; thread < omp_get_max_threads(); ++thread) { @@ -410,23 +388,37 @@ void CMeshSolver::SetWallDistance(CGeometry *geometry, CConfig *config) { void CMeshSolver::SetMesh_Stiffness(CGeometry **geometry, CNumerics **numerics, CConfig *config){ - unsigned long iElem; + /*--- Use the config option as an upper bound on elasticity modulus. + * For RANS meshes the range of element volume or wall distance is + * very large and leads to an ill-conditioned stiffness matrix. + * Absolute values of elasticity modulus are not important for + * mesh deformation, since linear elasticity is used and all + * boundary conditions are essential (Dirichlet). ---*/ + const su2double maxE = config->GetDeform_ElasticityMod(); if (!stiffness_set) { - for (iElem = 0; iElem < nElement; iElem++) { + /*--- All threads must execute the entire loop (no worksharing), + * each sets the stiffnesses for its numerics instance. ---*/ + SU2_OMP_PARALLEL + { + CNumerics* myNumerics = numerics[FEA_TERM + omp_get_thread_num()*MAX_TERMS]; + + for (unsigned long iElem = 0; iElem < nElement; iElem++) { + + su2double E = 1.0; switch (config->GetDeform_Stiffness_Type()) { - /*--- Stiffness inverse of the volume of the element ---*/ - case INVERSE_VOLUME: E = 1.0 / element[iElem].GetRef_Volume(); break; - /*--- Stiffness inverse of the distance of the element to the closest wall ---*/ - case SOLID_WALL_DISTANCE: E = 1.0 / element[iElem].GetWallDistance(); break; + /*--- Stiffness inverse of the volume of the element ---*/ + case INVERSE_VOLUME: E = 1.0 / element[iElem].GetRef_Volume(); break; + + /*--- Stiffness inverse of the distance of the element to the closest wall ---*/ + case SOLID_WALL_DISTANCE: E = 1.0 / element[iElem].GetWallDistance(); break; } /*--- Set the element elastic properties in the numerics container ---*/ - numerics[FEA_TERM]->SetMeshElasticProperties(iElem, E); - + myNumerics->SetMeshElasticProperties(iElem, min(E,maxE)); + } } - stiffness_set = true; } @@ -436,15 +428,15 @@ void CMeshSolver::DeformMesh(CGeometry **geometry, CNumerics **numerics, CConfig if (multizone) nodes->Set_BGSSolution_k(); - /*--- Initialize sparse matrix ---*/ - Jacobian.SetValZero(); - /*--- Compute the stiffness matrix. ---*/ Compute_StiffMatrix(geometry[MESH_0], numerics, config); - /*--- Initialize vectors and clean residual ---*/ - LinSysSol.SetValZero(); - LinSysRes.SetValZero(); + /*--- Initialize vectors and clean residual. ---*/ + SU2_OMP_PARALLEL + { + LinSysSol.SetValZero(); + LinSysRes.SetValZero(); + } /*--- LinSysSol contains the non-transformed displacements in the periodic halo cells. Hence we still need a communication of the transformed coordinates, otherwise periodicity diff --git a/SU2_CFD/src/solvers/CNSSolver.cpp b/SU2_CFD/src/solvers/CNSSolver.cpp index 2484ce2ed0b7..3b22cc0c292a 100644 --- a/SU2_CFD/src/solvers/CNSSolver.cpp +++ b/SU2_CFD/src/solvers/CNSSolver.cpp @@ -171,108 +171,27 @@ void CNSSolver::Preprocessing(CGeometry *geometry, CSolver **solver_container, C unsigned long InnerIter = config->GetInnerIter(); bool cont_adjoint = config->GetContinuous_Adjoint(); - bool disc_adjoint = config->GetDiscrete_Adjoint(); - bool implicit = (config->GetKind_TimeIntScheme_Flow() == EULER_IMPLICIT); - bool center = (config->GetKind_ConvNumScheme_Flow() == SPACE_CENTERED) || (cont_adjoint && config->GetKind_ConvNumScheme_AdjFlow() == SPACE_CENTERED); - bool center_jst = center && config->GetKind_Centered_Flow() == JST; bool limiter_flow = (config->GetKind_SlopeLimit_Flow() != NO_LIMITER) && (InnerIter <= config->GetLimiterIter()); bool limiter_turb = (config->GetKind_SlopeLimit_Turb() != NO_LIMITER) && (InnerIter <= config->GetLimiterIter()); bool limiter_adjflow = (cont_adjoint && (config->GetKind_SlopeLimit_AdjFlow() != NO_LIMITER) && (InnerIter <= config->GetLimiterIter())); - bool fixed_cl = config->GetFixed_CL_Mode(); - bool engine = ((config->GetnMarker_EngineInflow() != 0) || (config->GetnMarker_EngineExhaust() != 0)); - bool actuator_disk = ((config->GetnMarker_ActDiskInlet() != 0) || (config->GetnMarker_ActDiskOutlet() != 0)); - bool nearfield = (config->GetnMarker_NearFieldBound() != 0); bool van_albada = config->GetKind_SlopeLimit_Flow() == VAN_ALBADA_EDGE; - unsigned short kind_row_dissipation = config->GetKind_RoeLowDiss(); - bool roe_low_dissipation = (kind_row_dissipation != NO_ROELOWDISS) && - (config->GetKind_Upwind_Flow() == ROE || - config->GetKind_Upwind_Flow() == SLAU || - config->GetKind_Upwind_Flow() == SLAU2); bool wall_functions = config->GetWall_Functions(); - /*--- Update the angle of attack at the far-field for fixed CL calculations (only direct problem). ---*/ + /*--- Common preprocessing steps (implemented by CEulerSolver) ---*/ - if (fixed_cl && !disc_adjoint && !cont_adjoint) { - SU2_OMP_MASTER - SetFarfield_AoA(geometry, solver_container, config, iMesh, Output); - SU2_OMP_BARRIER - } - - /*--- Set the primitive variables ---*/ - - SU2_OMP_MASTER - ErrorCounter = 0; - SU2_OMP_BARRIER - - SU2_OMP_ATOMIC - ErrorCounter += SetPrimitive_Variables(solver_container, config, Output); - - if ((iMesh == MESH_0) && (config->GetComm_Level() == COMM_FULL)) { - SU2_OMP_BARRIER - SU2_OMP_MASTER - { - unsigned long tmp = ErrorCounter; - SU2_MPI::Allreduce(&tmp, &ErrorCounter, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); - config->SetNonphysical_Points(ErrorCounter); - } - SU2_OMP_BARRIER - } - - /*--- Compute the engine properties ---*/ - - if (engine) { - SU2_OMP_MASTER - GetPower_Properties(geometry, config, iMesh, Output); - SU2_OMP_BARRIER - } - - /*--- Compute the actuator disk properties and distortion levels ---*/ - - if (actuator_disk) { - SU2_OMP_MASTER - { - Set_MPI_ActDisk(solver_container, geometry, config); - SetActDisk_BCThrust(geometry, solver_container, config, iMesh, Output); - } - SU2_OMP_BARRIER - } - - /*--- Compute NearField MPI ---*/ - - if (nearfield) { - SU2_OMP_MASTER - Set_MPI_Nearfield(geometry, config); - SU2_OMP_BARRIER - } - - /*--- Artificial dissipation ---*/ - - if (center && !Output) { - SetMax_Eigenvalue(geometry, config); - if ((center_jst) && (iMesh == MESH_0)) { - SetCentered_Dissipation_Sensor(geometry, config); - SetUndivided_Laplacian(geometry, config); - } - } - - /*--- Roe Low Dissipation Sensor ---*/ - - if (roe_low_dissipation){ - SetRoe_Dissipation(geometry, config); - if (kind_row_dissipation == FD_DUCROS || kind_row_dissipation == NTS_DUCROS){ - SetUpwind_Ducros_Sensor(geometry, config); - } - } + CommonPreprocessing(geometry, solver_container, config, iMesh, iRKStep, RunTime_EqSystem, Output); /*--- Compute gradient for MUSCL reconstruction. ---*/ if (config->GetReconstructionGradientRequired() && (iMesh == MESH_0)) { - if (config->GetKind_Gradient_Method_Recon() == GREEN_GAUSS) - SetPrimitive_Gradient_GG(geometry, config, true); - if (config->GetKind_Gradient_Method_Recon() == LEAST_SQUARES) - SetPrimitive_Gradient_LS(geometry, config, true); - if (config->GetKind_Gradient_Method_Recon() == WEIGHTED_LEAST_SQUARES) - SetPrimitive_Gradient_LS(geometry, config, true); + switch (config->GetKind_Gradient_Method_Recon()) { + case GREEN_GAUSS: + SetPrimitive_Gradient_GG(geometry, config, true); break; + case LEAST_SQUARES: + case WEIGHTED_LEAST_SQUARES: + SetPrimitive_Gradient_LS(geometry, config, true); break; + default: break; + } } /*--- Compute gradient of the primitive variables ---*/ @@ -280,12 +199,12 @@ void CNSSolver::Preprocessing(CGeometry *geometry, CSolver **solver_container, C if (config->GetKind_Gradient_Method() == GREEN_GAUSS) { SetPrimitive_Gradient_GG(geometry, config); } - if (config->GetKind_Gradient_Method() == WEIGHTED_LEAST_SQUARES) { + else if (config->GetKind_Gradient_Method() == WEIGHTED_LEAST_SQUARES) { SetPrimitive_Gradient_LS(geometry, config); } - /*--- Compute the limiter in case we need it in the turbulence model - or to limit the viscous terms (check this logic with JST and 2nd order turbulence model) ---*/ + /*--- Compute the limiter in case we need it in the turbulence model or to limit the + * viscous terms (check this logic with JST and 2nd order turbulence model) ---*/ if ((iMesh == MESH_0) && (limiter_flow || limiter_turb || limiter_adjflow) && !Output && !van_albada) { SetPrimitive_Limiter(geometry, config); @@ -342,10 +261,6 @@ void CNSSolver::Preprocessing(CGeometry *geometry, CSolver **solver_container, C SU2_OMP_BARRIER } - /*--- Initialize the Jacobian matrices ---*/ - - if (implicit && !config->GetDiscrete_Adjoint()) Jacobian.SetValZero(); - } unsigned long CNSSolver::SetPrimitive_Variables(CSolver **solver_container, CConfig *config, bool Output) { @@ -383,10 +298,6 @@ unsigned long CNSSolver::SetPrimitive_Variables(CSolver **solver_container, CCon nonPhysicalPoints += !physical; - /*--- Initialize the convective, source and viscous residual vector ---*/ - - if (!Output) LinSysRes.SetBlock_Zero(iPoint); - } return nonPhysicalPoints; @@ -440,7 +351,7 @@ void CNSSolver::Viscous_Residual(unsigned long iEdge, CGeometry *geometry, CSolv auto residual = numerics->ComputeResidual(config); - if ((MGLevel != MESH_0) && (omp_get_num_threads() > 1)) { + if (ReducerStrategy) { EdgeFluxes.SubtractBlock(iEdge, residual); if (implicit) Jacobian.UpdateBlocksSub(iEdge, residual.jacobian_i, residual.jacobian_j); diff --git a/SU2_CFD/src/solvers/CSolver.cpp b/SU2_CFD/src/solvers/CSolver.cpp index a4735cec3c17..112a912fddcb 100644 --- a/SU2_CFD/src/solvers/CSolver.cpp +++ b/SU2_CFD/src/solvers/CSolver.cpp @@ -2219,7 +2219,7 @@ void CSolver::CompleteComms(CGeometry *geometry, break; case UNDIVIDED_LAPLACIAN: for (iVar = 0; iVar < nVar; iVar++) - base_nodes->SetUndivided_Laplacian(iPoint, iVar, bufDRecv[buf_offset+iVar]); + base_nodes->SetUnd_Lapl(iPoint, iVar, bufDRecv[buf_offset+iVar]); break; case SOLUTION_LIMITER: for (iVar = 0; iVar < nVar; iVar++) diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index 284edf7ad6ee..3948a6e182b0 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -96,7 +96,7 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor /*--- Initialization of the structure of the whole Jacobian ---*/ if (rank == MASTER_NODE) cout << "Initialize Jacobian structure (SA model)." << endl; - Jacobian.Initialize(nPoint, nPointDomain, nVar, nVar, true, geometry, config); + Jacobian.Initialize(nPoint, nPointDomain, nVar, nVar, true, geometry, config, ReducerStrategy); if (config->GetKind_Linear_Solver_Prec() == LINELET) { nLineLets = Jacobian.BuildLineletPreconditioner(geometry, config); @@ -106,6 +106,9 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor LinSysSol.Initialize(nPoint, nPointDomain, nVar, 0.0); LinSysRes.Initialize(nPoint, nPointDomain, nVar, 0.0); + if (ReducerStrategy) + EdgeFluxes.Initialize(geometry->GetnEdge(), geometry->GetnEdge(), nVar, nullptr); + if (config->GetExtraOutput()) { if (nDim == 2) { nOutputVariables = 13; } else if (nDim == 3) { nOutputVariables = 19; } @@ -222,9 +225,9 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor SetImplicitPeriodic(true); - /* Store the initial CFL number for all grid points. */ + /*--- Store the initial CFL number for all grid points. ---*/ - const su2double CFL = config->GetCFL(MGLevel); + const su2double CFL = config->GetCFL(MGLevel)*config->GetCFLRedCoeff_Turb(); for (iPoint = 0; iPoint < nPoint; iPoint++) { nodes->SetLocalCFL(iPoint, CFL); } @@ -277,9 +280,12 @@ void CTurbSASolver::Preprocessing(CGeometry *geometry, CSolver **solver_containe const su2double* Vorticity = nullptr; su2double Laminar_Viscosity = 0.0; - /*--- Clear residual and system matrix. ---*/ - LinSysRes.SetValZero(); - Jacobian.SetValZero(); + /*--- Clear residual and system matrix, not needed for + * reducer strategy as we write over the entire matrix. ---*/ + if (!ReducerStrategy) { + LinSysRes.SetValZero(); + Jacobian.SetValZero(); + } /*--- Upwind second order reconstruction and gradients ---*/ diff --git a/SU2_CFD/src/solvers/CTurbSSTSolver.cpp b/SU2_CFD/src/solvers/CTurbSSTSolver.cpp index 148462ef1f77..96cab040c155 100644 --- a/SU2_CFD/src/solvers/CTurbSSTSolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSSTSolver.cpp @@ -98,7 +98,7 @@ CTurbSSTSolver::CTurbSSTSolver(CGeometry *geometry, CConfig *config, unsigned sh /*--- Initialization of the structure of the whole Jacobian ---*/ if (rank == MASTER_NODE) cout << "Initialize Jacobian structure (SST model)." << endl; - Jacobian.Initialize(nPoint, nPointDomain, nVar, nVar, true, geometry, config); + Jacobian.Initialize(nPoint, nPointDomain, nVar, nVar, true, geometry, config, ReducerStrategy); if (config->GetKind_Linear_Solver_Prec() == LINELET) { nLineLets = Jacobian.BuildLineletPreconditioner(geometry, config); @@ -108,6 +108,9 @@ CTurbSSTSolver::CTurbSSTSolver(CGeometry *geometry, CConfig *config, unsigned sh LinSysSol.Initialize(nPoint, nPointDomain, nVar, 0.0); LinSysRes.Initialize(nPoint, nPointDomain, nVar, 0.0); + if (ReducerStrategy) + EdgeFluxes.Initialize(geometry->GetnEdge(), geometry->GetnEdge(), nVar, nullptr); + /*--- Initialize the BGS residuals in multizone problems. ---*/ if (multizone){ Residual_BGS = new su2double[nVar](); @@ -223,9 +226,9 @@ CTurbSSTSolver::CTurbSSTSolver(CGeometry *geometry, CConfig *config, unsigned sh SetImplicitPeriodic(true); - /* Store the initial CFL number for all grid points. */ + /*--- Store the initial CFL number for all grid points. ---*/ - const su2double CFL = config->GetCFL(MGLevel); + const su2double CFL = config->GetCFL(MGLevel)*config->GetCFLRedCoeff_Turb(); for (iPoint = 0; iPoint < nPoint; iPoint++) { nodes->SetLocalCFL(iPoint, CFL); } @@ -274,9 +277,12 @@ void CTurbSSTSolver::Preprocessing(CGeometry *geometry, CSolver **solver_contain const bool limiter_turb = (config->GetKind_SlopeLimit_Turb() != NO_LIMITER) && (config->GetInnerIter() <= config->GetLimiterIter()); - /*--- Clear residual and system matrix. ---*/ - LinSysRes.SetValZero(); - Jacobian.SetValZero(); + /*--- Clear residual and system matrix, not needed for + * reducer strategy as we write over the entire matrix. ---*/ + if (!ReducerStrategy) { + LinSysRes.SetValZero(); + Jacobian.SetValZero(); + } /*--- Upwind second order reconstruction and gradients ---*/ diff --git a/SU2_CFD/src/solvers/CTurbSolver.cpp b/SU2_CFD/src/solvers/CTurbSolver.cpp index ca1024b209c3..661a7fa2b856 100644 --- a/SU2_CFD/src/solvers/CTurbSolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSolver.cpp @@ -48,19 +48,23 @@ CTurbSolver::CTurbSolver(CGeometry* geometry, CConfig *config) : CSolver() { dynamic_grid = config->GetDynamic_Grid(); #ifdef HAVE_OMP - /*--- Get the edge coloring. ---*/ + /*--- Get the edge coloring, see notes in CEulerSolver's constructor. ---*/ + su2double parallelEff = 1.0; + const auto& coloring = geometry->GetEdgeColoring(¶llelEff); - const auto& coloring = geometry->GetEdgeColoring(); + ReducerStrategy = parallelEff < COLORING_EFF_THRESH; + + if (ReducerStrategy && (coloring.getOuterSize()>1)) + geometry->SetNaturalEdgeColoring(); if (!coloring.empty()) { + auto groupSize = ReducerStrategy? 1ul : geometry->GetEdgeColorGroupSize(); auto nColor = coloring.getOuterSize(); - EdgeColoring.resize(nColor); + EdgeColoring.reserve(nColor); - for(auto iColor = 0ul; iColor < nColor; ++iColor) { - EdgeColoring.emplace_back(coloring.innerIdx(iColor), coloring.getNumNonZeros(iColor)); - } + for(auto iColor = 0ul; iColor < nColor; ++iColor) + EdgeColoring.emplace_back(coloring.innerIdx(iColor), coloring.getNumNonZeros(iColor), groupSize); } - ColorGroupSize = geometry->GetEdgeColorGroupSize(); nPoint = geometry->GetnPoint(); omp_chunk_size = computeStaticChunkSize(nPoint, omp_get_max_threads(), OMP_MAX_SIZE); @@ -92,6 +96,13 @@ void CTurbSolver::Upwind_Residual(CGeometry *geometry, CSolver **solver_containe const bool muscl = config->GetMUSCL_Turb(); const bool limiter = (config->GetKind_SlopeLimit_Turb() != NO_LIMITER); + /*--- Only reconstruct flow variables if MUSCL is on for flow (requires upwind) and turbulence. ---*/ + const bool musclFlow = config->GetMUSCL_Flow() && muscl && + (config->GetKind_ConvNumScheme_Flow() == SPACE_UPWIND); + /*--- Only consider flow limiters for cell-based limiters, edge-based would need to be recomputed. ---*/ + const bool limiterFlow = (config->GetKind_SlopeLimit_Flow() != NO_LIMITER) && + (config->GetKind_SlopeLimit_Flow() != VAN_ALBADA_EDGE); + CVariable* flowNodes = solver_container[FLOW_SOL]->GetNodes(); /*--- Pick one numerics object per thread. ---*/ @@ -105,7 +116,7 @@ void CTurbSolver::Upwind_Residual(CGeometry *geometry, CSolver **solver_containe for (auto color : EdgeColoring) { /*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/ - SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize) + SU2_OMP_FOR_DYN(nextMultiple(OMP_MIN_SIZE, color.groupSize)) for(auto k = 0ul; k < color.size; ++k) { auto iEdge = color.indices[k]; @@ -121,14 +132,14 @@ void CTurbSolver::Upwind_Residual(CGeometry *geometry, CSolver **solver_containe /*--- Primitive variables w/o reconstruction ---*/ - auto V_i = flowNodes->GetPrimitive(iPoint); - auto V_j = flowNodes->GetPrimitive(jPoint); + const auto V_i = flowNodes->GetPrimitive(iPoint); + const auto V_j = flowNodes->GetPrimitive(jPoint); numerics->SetPrimitive(V_i, V_j); /*--- Turbulent variables w/o reconstruction ---*/ - auto Turb_i = nodes->GetSolution(iPoint); - auto Turb_j = nodes->GetSolution(jPoint); + const auto Turb_i = nodes->GetSolution(iPoint); + const auto Turb_j = nodes->GetSolution(jPoint); numerics->SetTurbVar(Turb_i, Turb_j); /*--- Grid Movement ---*/ @@ -137,151 +148,166 @@ void CTurbSolver::Upwind_Residual(CGeometry *geometry, CSolver **solver_containe numerics->SetGridVel(geometry->node[iPoint]->GetGridVel(), geometry->node[jPoint]->GetGridVel()); - if (muscl) { - su2double *Limiter_i = nullptr, *Limiter_j = nullptr; + if (muscl || musclFlow) { + const su2double *Limiter_i = nullptr, *Limiter_j = nullptr; - auto Coord_i = geometry->node[iPoint]->GetCoord(); - auto Coord_j = geometry->node[jPoint]->GetCoord(); - - /*--- Reconstruct flow variables. ---*/ + const auto Coord_i = geometry->node[iPoint]->GetCoord(); + const auto Coord_j = geometry->node[jPoint]->GetCoord(); su2double Vector_ij[MAXNDIM] = {0.0}; for (iDim = 0; iDim < nDim; iDim++) { Vector_ij[iDim] = 0.5*(Coord_j[iDim] - Coord_i[iDim]); } - /*--- Mean flow primitive variables using gradient reconstruction and limiters ---*/ - - auto Gradient_i = flowNodes->GetGradient_Reconstruction(iPoint); - auto Gradient_j = flowNodes->GetGradient_Reconstruction(jPoint); + if (musclFlow) { + /*--- Reconstruct mean flow primitive variables. ---*/ - if (limiter) { - Limiter_i = flowNodes->GetLimiter_Primitive(iPoint); - Limiter_j = flowNodes->GetLimiter_Primitive(jPoint); - } + auto Gradient_i = flowNodes->GetGradient_Reconstruction(iPoint); + auto Gradient_j = flowNodes->GetGradient_Reconstruction(jPoint); - for (iVar = 0; iVar < solver_container[FLOW_SOL]->GetnPrimVarGrad(); iVar++) { - su2double Project_Grad_i = 0.0, Project_Grad_j = 0.0; - for (iDim = 0; iDim < nDim; iDim++) { - Project_Grad_i += Vector_ij[iDim]*Gradient_i[iVar][iDim]; - Project_Grad_j -= Vector_ij[iDim]*Gradient_j[iVar][iDim]; - } - if (limiter) { - flowPrimVar_i[iVar] = V_i[iVar] + Limiter_i[iVar]*Project_Grad_i; - flowPrimVar_j[iVar] = V_j[iVar] + Limiter_j[iVar]*Project_Grad_j; + if (limiterFlow) { + Limiter_i = flowNodes->GetLimiter_Primitive(iPoint); + Limiter_j = flowNodes->GetLimiter_Primitive(jPoint); } - else { + + for (iVar = 0; iVar < solver_container[FLOW_SOL]->GetnPrimVarGrad(); iVar++) { + su2double Project_Grad_i = 0.0, Project_Grad_j = 0.0; + for (iDim = 0; iDim < nDim; iDim++) { + Project_Grad_i += Vector_ij[iDim]*Gradient_i[iVar][iDim]; + Project_Grad_j -= Vector_ij[iDim]*Gradient_j[iVar][iDim]; + } + if (limiterFlow) { + Project_Grad_i *= Limiter_i[iVar]; + Project_Grad_j *= Limiter_j[iVar]; + } flowPrimVar_i[iVar] = V_i[iVar] + Project_Grad_i; flowPrimVar_j[iVar] = V_j[iVar] + Project_Grad_j; } - } - numerics->SetPrimitive(flowPrimVar_i, flowPrimVar_j); - - /*--- Reconstruct turbulence variables. ---*/ + numerics->SetPrimitive(flowPrimVar_i, flowPrimVar_j); + } - Gradient_i = nodes->GetGradient_Reconstruction(iPoint); - Gradient_j = nodes->GetGradient_Reconstruction(jPoint); + if (muscl) { + /*--- Reconstruct turbulence variables. ---*/ - if (limiter) { - Limiter_i = nodes->GetLimiter(iPoint); - Limiter_j = nodes->GetLimiter(jPoint); - } + auto Gradient_i = nodes->GetGradient_Reconstruction(iPoint); + auto Gradient_j = nodes->GetGradient_Reconstruction(jPoint); - for (iVar = 0; iVar < nVar; iVar++) { - su2double Project_Grad_i = 0.0, Project_Grad_j = 0.0; - for (iDim = 0; iDim < nDim; iDim++) { - Project_Grad_i += Vector_ij[iDim]*Gradient_i[iVar][iDim]; - Project_Grad_j -= Vector_ij[iDim]*Gradient_j[iVar][iDim]; - } if (limiter) { - solution_i[iVar] = Turb_i[iVar] + Limiter_i[iVar]*Project_Grad_i; - solution_j[iVar] = Turb_j[iVar] + Limiter_j[iVar]*Project_Grad_j; + Limiter_i = nodes->GetLimiter(iPoint); + Limiter_j = nodes->GetLimiter(jPoint); } - else { + + for (iVar = 0; iVar < nVar; iVar++) { + su2double Project_Grad_i = 0.0, Project_Grad_j = 0.0; + for (iDim = 0; iDim < nDim; iDim++) { + Project_Grad_i += Vector_ij[iDim]*Gradient_i[iVar][iDim]; + Project_Grad_j -= Vector_ij[iDim]*Gradient_j[iVar][iDim]; + } + if (limiter) { + Project_Grad_i *= Limiter_i[iVar]; + Project_Grad_j *= Limiter_j[iVar]; + } solution_i[iVar] = Turb_i[iVar] + Project_Grad_i; solution_j[iVar] = Turb_j[iVar] + Project_Grad_j; } - } - - numerics->SetTurbVar(solution_i, solution_j); + numerics->SetTurbVar(solution_i, solution_j); + } } - /*--- Add and subtract residual ---*/ + /*--- Update convective residual value ---*/ auto residual = numerics->ComputeResidual(config); - LinSysRes.AddBlock(iPoint, residual); - LinSysRes.SubtractBlock(jPoint, residual); - - /*--- Implicit part ---*/ + if (ReducerStrategy) { + EdgeFluxes.SetBlock(iEdge, residual); + Jacobian.SetBlocks(iEdge, residual.jacobian_i, residual.jacobian_j); + } + else { + LinSysRes.AddBlock(iPoint, residual); + LinSysRes.SubtractBlock(jPoint, residual); + Jacobian.UpdateBlocks(iEdge, iPoint, jPoint, residual.jacobian_i, residual.jacobian_j); + } - Jacobian.UpdateBlocks(iEdge, iPoint, jPoint, residual.jacobian_i, residual.jacobian_j); + /*--- Viscous contribution. ---*/ + Viscous_Residual(iEdge, geometry, solver_container, + numerics_container[VISC_TERM + omp_get_thread_num()*MAX_TERMS], config); } } // end color loop + if (ReducerStrategy) { + SumEdgeFluxes(geometry); + Jacobian.SetDiagonalAsColumnSum(); + } } -void CTurbSolver::Viscous_Residual(CGeometry *geometry, CSolver **solver_container, CNumerics **numerics_container, - CConfig *config, unsigned short iMesh, unsigned short iRKStep) { +void CTurbSolver::Viscous_Residual(unsigned long iEdge, CGeometry *geometry, CSolver **solver_container, + CNumerics *numerics, CConfig *config) { CVariable* flowNodes = solver_container[FLOW_SOL]->GetNodes(); - /*--- Pick one numerics object per thread. ---*/ - CNumerics* numerics = numerics_container[VISC_TERM + omp_get_thread_num()*MAX_TERMS]; + /*--- Points in edge ---*/ - /*--- Loop over edge colors. ---*/ - for (auto color : EdgeColoring) - { - /*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/ - SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize) - for(auto k = 0ul; k < color.size; ++k) { + auto iPoint = geometry->edge[iEdge]->GetNode(0); + auto jPoint = geometry->edge[iEdge]->GetNode(1); - auto iEdge = color.indices[k]; + /*--- Points coordinates, and normal vector ---*/ - /*--- Points in edge ---*/ + numerics->SetCoord(geometry->node[iPoint]->GetCoord(), + geometry->node[jPoint]->GetCoord()); + numerics->SetNormal(geometry->edge[iEdge]->GetNormal()); - auto iPoint = geometry->edge[iEdge]->GetNode(0); - auto jPoint = geometry->edge[iEdge]->GetNode(1); + /*--- Conservative variables w/o reconstruction ---*/ - /*--- Points coordinates, and normal vector ---*/ + numerics->SetPrimitive(flowNodes->GetPrimitive(iPoint), + flowNodes->GetPrimitive(jPoint)); - numerics->SetCoord(geometry->node[iPoint]->GetCoord(), - geometry->node[jPoint]->GetCoord()); - numerics->SetNormal(geometry->edge[iEdge]->GetNormal()); + /*--- Turbulent variables w/o reconstruction, and its gradients ---*/ - /*--- Conservative variables w/o reconstruction ---*/ + numerics->SetTurbVar(nodes->GetSolution(iPoint), + nodes->GetSolution(jPoint)); + numerics->SetTurbVarGradient(nodes->GetGradient(iPoint), + nodes->GetGradient(jPoint)); - numerics->SetPrimitive(flowNodes->GetPrimitive(iPoint), - flowNodes->GetPrimitive(jPoint)); + /*--- Menter's first blending function (only SST)---*/ + if ((config->GetKind_Turb_Model() == SST) || (config->GetKind_Turb_Model() == SST_SUST)) + numerics->SetF1blending(nodes->GetF1blending(iPoint), + nodes->GetF1blending(jPoint)); - /*--- Turbulent variables w/o reconstruction, and its gradients ---*/ + /*--- Compute residual, and Jacobians ---*/ - numerics->SetTurbVar(nodes->GetSolution(iPoint), - nodes->GetSolution(jPoint)); - numerics->SetTurbVarGradient(nodes->GetGradient(iPoint), - nodes->GetGradient(jPoint)); + auto residual = numerics->ComputeResidual(config); - /*--- Menter's first blending function (only SST)---*/ - if ((config->GetKind_Turb_Model() == SST) || (config->GetKind_Turb_Model() == SST_SUST)) - numerics->SetF1blending(nodes->GetF1blending(iPoint), - nodes->GetF1blending(jPoint)); + if (ReducerStrategy) { + EdgeFluxes.SubtractBlock(iEdge, residual); + Jacobian.UpdateBlocksSub(iEdge, residual.jacobian_i, residual.jacobian_j); + } + else { + LinSysRes.SubtractBlock(iPoint, residual); + LinSysRes.AddBlock(jPoint, residual); + Jacobian.UpdateBlocksSub(iEdge, iPoint, jPoint, residual.jacobian_i, residual.jacobian_j); + } +} - /*--- Compute residual, and Jacobians ---*/ +void CTurbSolver::SumEdgeFluxes(CGeometry* geometry) { - auto residual = numerics->ComputeResidual(config); + SU2_OMP_FOR_STAT(omp_chunk_size) + for (unsigned long iPoint = 0; iPoint < nPoint; ++iPoint) { - /*--- Add and subtract residual, and update Jacobians ---*/ + LinSysRes.SetBlock_Zero(iPoint); - LinSysRes.SubtractBlock(iPoint, residual); - LinSysRes.AddBlock(jPoint, residual); + for (unsigned short iNeigh = 0; iNeigh < geometry->node[iPoint]->GetnPoint(); ++iNeigh) { - Jacobian.UpdateBlocksSub(iEdge, iPoint, jPoint, residual.jacobian_i, residual.jacobian_j); + auto iEdge = geometry->node[iPoint]->GetEdge(iNeigh); + if (iPoint == geometry->edge[iEdge]->GetNode(0)) + LinSysRes.AddBlock(iPoint, EdgeFluxes.GetBlock(iEdge)); + else + LinSysRes.SubtractBlock(iPoint, EdgeFluxes.GetBlock(iEdge)); + } } - } // end color loop } @@ -585,14 +611,18 @@ void CTurbSolver::SetResidual_DualTime(CGeometry *geometry, CSolver **solver_con const bool second_order = (config->GetTime_Marching() == DT_STEPPING_2ND); const bool incompressible = (config->GetKind_Regime() == INCOMPRESSIBLE); + /*--- Flow solution, needed to get density. ---*/ + + CVariable* flowNodes = solver_container[FLOW_SOL]->GetNodes(); + /*--- Store the physical time step ---*/ const su2double TimeStep = config->GetDelta_UnstTimeND(); /*--- Local variables ---*/ - unsigned short iVar, iMarker, iDim; - unsigned long iPoint, jPoint, iVertex; + unsigned short iVar, iMarker, iDim, iNeigh; + unsigned long iPoint, jPoint, iVertex, iEdge; const su2double *U_time_nM1 = nullptr, *U_time_n = nullptr, *U_time_nP1 = nullptr; su2double Volume_nM1, Volume_nP1; @@ -634,14 +664,14 @@ void CTurbSolver::SetResidual_DualTime(CGeometry *geometry, CSolver **solver_con density could also be temperature dependent, but as it is not a part of the solution vector it's neither stored for previous time steps nor updated with the solution at the end of each iteration. */ - Density_nM1 = solver_container[FLOW_SOL]->GetNodes()->GetDensity(iPoint); - Density_n = solver_container[FLOW_SOL]->GetNodes()->GetDensity(iPoint); - Density_nP1 = solver_container[FLOW_SOL]->GetNodes()->GetDensity(iPoint); + Density_nM1 = flowNodes->GetDensity(iPoint); + Density_n = flowNodes->GetDensity(iPoint); + Density_nP1 = flowNodes->GetDensity(iPoint); } else{ - Density_nM1 = solver_container[FLOW_SOL]->GetNodes()->GetSolution_time_n1(iPoint)[0]; - Density_n = solver_container[FLOW_SOL]->GetNodes()->GetSolution_time_n(iPoint,0); - Density_nP1 = solver_container[FLOW_SOL]->GetNodes()->GetSolution(iPoint,0); + Density_nM1 = flowNodes->GetSolution_time_n1(iPoint)[0]; + Density_n = flowNodes->GetSolution_time_n(iPoint,0); + Density_nP1 = flowNodes->GetSolution(iPoint,0); } for (iVar = 0; iVar < nVar; iVar++) { @@ -681,77 +711,43 @@ void CTurbSolver::SetResidual_DualTime(CGeometry *geometry, CSolver **solver_con we will loop over the edges and boundaries to compute the GCL component of the dual time source term that depends on grid velocities. ---*/ - /*--- Loop over edge colors. ---*/ - for (auto color : EdgeColoring) - { - /*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/ - SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize) - for(auto k = 0ul; k < color.size; ++k) { - - auto iEdge = color.indices[k]; - - /*--- Get indices for nodes i & j plus the face normal ---*/ - - iPoint = geometry->edge[iEdge]->GetNode(0); - jPoint = geometry->edge[iEdge]->GetNode(1); - Normal = geometry->edge[iEdge]->GetNormal(); - - /*--- Grid velocities stored at nodes i & j ---*/ + SU2_OMP_FOR_STAT(omp_chunk_size) + for (iPoint = 0; iPoint < nPointDomain; ++iPoint) { GridVel_i = geometry->node[iPoint]->GetGridVel(); - GridVel_j = geometry->node[jPoint]->GetGridVel(); - - /*--- Compute the GCL term by averaging the grid velocities at the - edge mid-point and dotting with the face normal. ---*/ - - Residual_GCL = 0.0; - for (iDim = 0; iDim < nDim; iDim++) - Residual_GCL += 0.5*(GridVel_i[iDim]+GridVel_j[iDim])*Normal[iDim]; - - /*--- Compute the GCL component of the source term for node i ---*/ - - U_time_n = nodes->GetSolution_time_n(iPoint); - - /*--- Multiply by density at node i for the SST model ---*/ + U_time_n = nodes->GetSolution_time_n(iPoint); + Density_n = 1.0; if (sst_model) { if (incompressible) - Density_n = solver_container[FLOW_SOL]->GetNodes()->GetDensity(iPoint); // Temporary fix + Density_n = flowNodes->GetDensity(iPoint); // Temporary fix else - Density_n = solver_container[FLOW_SOL]->GetNodes()->GetSolution_time_n(iPoint,0); - - for (iVar = 0; iVar < nVar; iVar++) - LinSysRes(iPoint,iVar) += Density_n*U_time_n[iVar]*Residual_GCL; - } - else { - for (iVar = 0; iVar < nVar; iVar++) - LinSysRes(iPoint,iVar) += U_time_n[iVar]*Residual_GCL; + Density_n = flowNodes->GetSolution_time_n(iPoint,0); } - /*--- Compute the GCL component of the source term for node j ---*/ + for (iNeigh = 0; iNeigh < geometry->node[iPoint]->GetnNeighbor(); iNeigh++) { - U_time_n = nodes->GetSolution_time_n(jPoint); + iEdge = geometry->node[iPoint]->GetEdge(iNeigh); + Normal = geometry->edge[iEdge]->GetNormal(); - /*--- Multiply by density at node j for the SST model ---*/ + jPoint = geometry->node[iPoint]->GetPoint(iNeigh); + GridVel_j = geometry->node[jPoint]->GetGridVel(); - if (sst_model) { - if (incompressible) - Density_n = solver_container[FLOW_SOL]->GetNodes()->GetDensity(jPoint); // Temporary fix - else - Density_n = solver_container[FLOW_SOL]->GetNodes()->GetSolution_time_n(jPoint)[0]; + /*--- Determine whether to consider the normal outward or inward. ---*/ + su2double dir = (geometry->edge[iEdge]->GetNode(0) == iPoint)? 0.5 : -0.5; + + Residual_GCL = 0.0; + for (iDim = 0; iDim < nDim; iDim++) + Residual_GCL += dir*(GridVel_i[iDim]+GridVel_j[iDim])*Normal[iDim]; + + Residual_GCL *= Density_n; for (iVar = 0; iVar < nVar; iVar++) - LinSysRes(jPoint,iVar) -= Density_n*U_time_n[iVar]*Residual_GCL; - } - else { - for (iVar = 0; iVar < nVar; iVar++) - LinSysRes(jPoint,iVar) -= U_time_n[iVar]*Residual_GCL; + LinSysRes(iPoint,iVar) += U_time_n[iVar]*Residual_GCL; } - } - } // end color loop - /*--- Loop over the boundary edges ---*/ + /*--- Loop over the boundary edges ---*/ for (iMarker = 0; iMarker < geometry->GetnMarker(); iMarker++) { if ((config->GetMarker_All_KindBC(iMarker) != INTERNAL_BOUNDARY) && @@ -784,9 +780,9 @@ void CTurbSolver::SetResidual_DualTime(CGeometry *geometry, CSolver **solver_con if (sst_model) { if (incompressible) - Density_n = solver_container[FLOW_SOL]->GetNodes()->GetDensity(iPoint); // Temporary fix + Density_n = flowNodes->GetDensity(iPoint); // Temporary fix else - Density_n = solver_container[FLOW_SOL]->GetNodes()->GetSolution_time_n(iPoint,0); + Density_n = flowNodes->GetSolution_time_n(iPoint,0); for (iVar = 0; iVar < nVar; iVar++) LinSysRes(iPoint,iVar) += Density_n*U_time_n[iVar]*Residual_GCL; @@ -834,14 +830,14 @@ void CTurbSolver::SetResidual_DualTime(CGeometry *geometry, CSolver **solver_con density could also be temperature dependent, but as it is not a part of the solution vector it's neither stored for previous time steps nor updated with the solution at the end of each iteration. */ - Density_nM1 = solver_container[FLOW_SOL]->GetNodes()->GetDensity(iPoint); - Density_n = solver_container[FLOW_SOL]->GetNodes()->GetDensity(iPoint); - Density_nP1 = solver_container[FLOW_SOL]->GetNodes()->GetDensity(iPoint); + Density_nM1 = flowNodes->GetDensity(iPoint); + Density_n = flowNodes->GetDensity(iPoint); + Density_nP1 = flowNodes->GetDensity(iPoint); } else { - Density_nM1 = solver_container[FLOW_SOL]->GetNodes()->GetSolution_time_n1(iPoint)[0]; - Density_n = solver_container[FLOW_SOL]->GetNodes()->GetSolution_time_n(iPoint,0); - Density_nP1 = solver_container[FLOW_SOL]->GetNodes()->GetSolution(iPoint,0); + Density_nM1 = flowNodes->GetSolution_time_n1(iPoint)[0]; + Density_n = flowNodes->GetSolution_time_n(iPoint,0); + Density_nP1 = flowNodes->GetSolution(iPoint,0); } for (iVar = 0; iVar < nVar; iVar++) { diff --git a/TestCases/fea_fsi/Airfoil_RBF/config.cfg b/TestCases/fea_fsi/Airfoil_RBF/config.cfg index 437ca3394f55..ddcf42c9b8e4 100755 --- a/TestCases/fea_fsi/Airfoil_RBF/config.cfg +++ b/TestCases/fea_fsi/Airfoil_RBF/config.cfg @@ -3,7 +3,7 @@ % Case description: 2D airfoil FSI with radial basis function interp. % % Institution: Imperial College London % % Date: 2015.08.12 % -% File Version 6.2 "Falcon" % +% File Version 7.0.2 "Blackbird" % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% SOLVER = MULTIPHYSICS diff --git a/TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg b/TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg index 921a81ac1da0..eff2d6ed2b36 100644 --- a/TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg +++ b/TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg @@ -2,7 +2,7 @@ % SU2 configuration file % % Case description: 2D airfoil FSI with radial basis function interp. % % Institution: Imperial College London % -% File Version 7.0.2 "Blackbird" % +% File Version 7.0.2 "Blackbird" % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Interface options ---------------------------------------------------- % @@ -12,6 +12,7 @@ CONSERVATIVE_INTERPOLATION = YES KIND_RADIAL_BASIS_FUNCTION = WENDLAND_C2 RADIAL_BASIS_FUNCTION_PARAMETER = 0.015 RADIAL_BASIS_FUNCTION_POLYNOMIAL_TERM = YES +RADIAL_BASIS_FUNCTION_PRUNE_TOLERANCE = 0 % % Physics -------------------------------------------------------------- % SOLVER= ELASTICITY diff --git a/TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg b/TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg index 183cb402f3fc..9efc636b33d4 100644 --- a/TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg +++ b/TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg @@ -2,7 +2,7 @@ % SU2 configuration file % % Case description: 2D airfoil FSI with radial basis function interp. % % Institution: Imperial College London % -% File Version 7.0.2 "Blackbird" % +% File Version 7.0.2 "Blackbird" % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Interface options ---------------------------------------------------- % @@ -20,16 +20,20 @@ KIND_INTERPOLATION = RADIAL_BASIS_FUNCTION % orders) and you have issues, break up the interface or use "NO" for % consistent interpolation. CONSERVATIVE_INTERPOLATION = YES -% Wendland provides good results and produces a nice diagonally dominant -% interpolation kernel, other options: +% Wendland is usually the best option due to sparser and better +% conditioned interpolation kernel, other options: % INV_MULTI_QUADRIC; GAUSSIAN; THIN_PLATE_SPLINE; MULTI_QUADRIC KIND_RADIAL_BASIS_FUNCTION = WENDLAND_C2 % The radius in meters, 2 times the largest cell size on the interface is % a good compromise between accuracy and condition number of the kernel. RADIAL_BASIS_FUNCTION_PARAMETER = 0.015 -% Recommended as it recovers rigid body motion, only requires a few more -% matrix products... feel free to explore though! +% Polynomial terms to recover rigid body motion, this requires slightly +% more computation and reduces the sparsity of the interpolation matrix. RADIAL_BASIS_FUNCTION_POLYNOMIAL_TERM = YES +% The number of donor points per target point is reduced by prunning away +% small interpolation coefficients (making the interp. matrix sparse). +% This tolerance is relative to the maximum absolute coefficient. +RADIAL_BASIS_FUNCTION_PRUNE_TOLERANCE = 0 % % Physics -------------------------------------------------------------- % SOLVER= EULER diff --git a/TestCases/parallel_regression.py b/TestCases/parallel_regression.py index 6d15e0560ace..4475490dd1c6 100644 --- a/TestCases/parallel_regression.py +++ b/TestCases/parallel_regression.py @@ -91,7 +91,7 @@ def main(): fixedCL_naca0012.cfg_dir = "fixed_cl/naca0012" fixedCL_naca0012.cfg_file = "inv_NACA0012.cfg" fixedCL_naca0012.test_iter = 10 - fixedCL_naca0012.test_vals = [-12.137879, -6.705803, 0.300000, 0.019470] #last 4 columns + fixedCL_naca0012.test_vals = [-12.137437, -6.705109, 0.300000, 0.019470] #last 4 columns fixedCL_naca0012.su2_exec = "parallel_computation.py -f" fixedCL_naca0012.timeout = 1600 fixedCL_naca0012.tol = 0.00001 @@ -243,7 +243,7 @@ def main(): turb_naca0012_sa.cfg_dir = "rans/naca0012" turb_naca0012_sa.cfg_file = "turb_NACA0012_sa.cfg" turb_naca0012_sa.test_iter = 10 - turb_naca0012_sa.test_vals = [-12.078361, -16.147829, 1.064326, 0.019770] #last 4 columns + turb_naca0012_sa.test_vals = [-12.078401, -16.147829, 1.064326, 0.019770] #last 4 columns turb_naca0012_sa.su2_exec = "parallel_computation.py -f" turb_naca0012_sa.timeout = 3200 turb_naca0012_sa.tol = 0.00001 @@ -792,7 +792,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 = [-2.990702, -6.861616, 1.475736, 0.419771] #last 4 columns + unst_inc_turb_naca0015_sa.test_vals = [-2.990703, -6.865923, 1.475736, 0.419770] #last 4 columns unst_inc_turb_naca0015_sa.su2_exec = "parallel_computation.py -f" unst_inc_turb_naca0015_sa.timeout = 1600 unst_inc_turb_naca0015_sa.tol = 0.00001 @@ -976,7 +976,7 @@ def main(): bars_SST_2D.cfg_dir = "sliding_interface/bars_SST_2D" bars_SST_2D.cfg_file = "bars.cfg" bars_SST_2D.test_iter = 13 - bars_SST_2D.test_vals = [13.000000, -0.590195, -1.955395] #last 4 columns + bars_SST_2D.test_vals = [13.000000, -0.598555, -1.879082] #last 4 columns bars_SST_2D.su2_exec = "SU2_CFD" bars_SST_2D.timeout = 1600 bars_SST_2D.tol = 0.00001 diff --git a/TestCases/parallel_regression_AD.py b/TestCases/parallel_regression_AD.py index 69ceb1be21aa..a1c17b61e2c2 100644 --- a/TestCases/parallel_regression_AD.py +++ b/TestCases/parallel_regression_AD.py @@ -84,7 +84,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.230578, 0.678810, 0.181780, -0.000018] #last 4 columns + discadj_rans_naca0012_sa.test_vals = [-2.230573, 0.696562, 0.181780, -0.000018] #last 4 columns discadj_rans_naca0012_sa.su2_exec = "parallel_computation.py -f" discadj_rans_naca0012_sa.timeout = 1600 discadj_rans_naca0012_sa.tol = 0.00001 diff --git a/TestCases/serial_regression.py b/TestCases/serial_regression.py index 09657a95b35c..f57084dc5757 100644 --- a/TestCases/serial_regression.py +++ b/TestCases/serial_regression.py @@ -101,7 +101,7 @@ def main(): fixedCL_naca0012.cfg_dir = "fixed_cl/naca0012" fixedCL_naca0012.cfg_file = "inv_NACA0012.cfg" fixedCL_naca0012.test_iter = 10 - fixedCL_naca0012.test_vals = [-12.129044, -6.702294, 0.300000, 0.019470] #last 4 columns + fixedCL_naca0012.test_vals = [-12.128275, -6.700329, 0.300000, 0.019470] #last 4 columns fixedCL_naca0012.su2_exec = "SU2_CFD" fixedCL_naca0012.new_output = True fixedCL_naca0012.timeout = 1600 @@ -281,7 +281,7 @@ def main(): turb_naca0012_sa.cfg_dir = "rans/naca0012" turb_naca0012_sa.cfg_file = "turb_NACA0012_sa.cfg" turb_naca0012_sa.test_iter = 10 - turb_naca0012_sa.test_vals = [-12.075893, -16.146770, 1.064326, 0.019770] #last 4 columns + turb_naca0012_sa.test_vals = [-12.075861, -16.146770, 1.064326, 0.019770] #last 4 columns turb_naca0012_sa.su2_exec = "SU2_CFD" turb_naca0012_sa.new_output = True turb_naca0012_sa.timeout = 3200 @@ -790,7 +790,7 @@ def main(): turb_naca0012_p1c1.cfg_dir = "rans_uq/naca0012" turb_naca0012_p1c1.cfg_file = "turb_NACA0012_uq_p1c1.cfg" turb_naca0012_p1c1.test_iter = 10 - turb_naca0012_p1c1.test_vals = [-5.003327, 1.312032, 6.085201, 2.413460] #last 4 columns + turb_naca0012_p1c1.test_vals = [-5.003335, 1.312021, 6.085201, 2.413460] #last 4 columns turb_naca0012_p1c1.su2_exec = "SU2_CFD" turb_naca0012_p1c1.new_output = True turb_naca0012_p1c1.timeout = 1600 @@ -802,7 +802,7 @@ def main(): turb_naca0012_p1c2.cfg_dir = "rans_uq/naca0012" turb_naca0012_p1c2.cfg_file = "turb_NACA0012_uq_p1c2.cfg" turb_naca0012_p1c2.test_iter = 10 - turb_naca0012_p1c2.test_vals = [-5.263992, 1.251332, 6.085705, 2.413434] #last 4 columns + turb_naca0012_p1c2.test_vals = [-5.264009, 1.251324, 6.085705, 2.413434] #last 4 columns turb_naca0012_p1c2.su2_exec = "SU2_CFD" turb_naca0012_p1c2.new_output = True turb_naca0012_p1c2.timeout = 1600 @@ -926,7 +926,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 = [ -2.994996, -6.865786, 1.434864, 0.416627] #last 4 columns + unst_inc_turb_naca0015_sa.test_vals = [-2.994996, -6.869781, 1.434864, 0.416626] #last 4 columns unst_inc_turb_naca0015_sa.su2_exec = "SU2_CFD" unst_inc_turb_naca0015_sa.timeout = 1600 unst_inc_turb_naca0015_sa.tol = 0.00001 @@ -1134,7 +1134,7 @@ def main(): bars_SST_2D.cfg_dir = "sliding_interface/bars_SST_2D" bars_SST_2D.cfg_file = "bars.cfg" bars_SST_2D.test_iter = 13 - bars_SST_2D.test_vals = [13.000000, -0.590195, -1.955395] #last 3 columns + bars_SST_2D.test_vals = [13.000000, -0.598555, -1.879082] #last 3 columns bars_SST_2D.su2_exec = "SU2_CFD" bars_SST_2D.timeout = 1600 bars_SST_2D.tol = 0.00001 diff --git a/TestCases/serial_regression_AD.py b/TestCases/serial_regression_AD.py index e90b5d0e973a..70efb28b1f1d 100644 --- a/TestCases/serial_regression_AD.py +++ b/TestCases/serial_regression_AD.py @@ -84,7 +84,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.230561, 0.678834, 0.180740, -0.000018] #last 4 columns + discadj_rans_naca0012_sa.test_vals = [-2.230556, 0.696586, 0.180740, -0.000018] #last 4 columns discadj_rans_naca0012_sa.su2_exec = "SU2_CFD_AD" discadj_rans_naca0012_sa.timeout = 1600 discadj_rans_naca0012_sa.tol = 0.00001 diff --git a/config_template.cfg b/config_template.cfg index ec58a8a5c79d..352d37bed08b 100644 --- a/config_template.cfg +++ b/config_template.cfg @@ -1333,6 +1333,26 @@ UQ_URLX= 0.1 % Perturbation magnitude (float [0,1], default= 1.0) UQ_DELTA_B= 1.0 % +% --------------------- HYBRID PARALLEL (MPI+OpenMP) OPTIONS ---------------------% +% +% An advanced performance parameter for FVM solvers, a large-ish value should be best +% when relatively few threads per MPI rank are in use (~4). However, maximum parallelism +% is obtained with EDGE_COLORING_GROUP_SIZE=1, consider using this value only if SU2 +% warns about low coloring efficiency during preprocessing (performance is usually worse). +% Setting the option to 0 disables coloring and a different strategy is used instead, +% that strategy is automatically used when the coloring efficiency is less than 0.875. +% The optimum value/strategy is case-dependent. +EDGE_COLORING_GROUP_SIZE= 512 +% +% Independent "threads per MPI rank" setting for LU-SGS and ILU preconditioners. +% For problems where time is spend mostly in the solution of linear systems (e.g. elasticity, +% very high CFL central schemes), AND, if the memory bandwidth of the machine is saturated +% (4 or more cores per memory channel) better performance (via a reduction in linear iterations) +% may be possible by using a smaller value than that defined by the system or in the call to +% SU2_CFD (via the -t/--threads option). +% The default (0) means "same number of threads as for all else". +LINEAR_SOLVER_PREC_THREADS= 0 +% % ------------------------- SCREEN/HISTORY VOLUME OUTPUT --------------------------% % % Screen output fields (use 'SU2_CFD -d ' to view list of available fields)