From f50785a5eee5a9445679a882f27099f68e703e09 Mon Sep 17 00:00:00 2001 From: pcarruscag Date: Wed, 11 Mar 2020 15:20:29 +0000 Subject: [PATCH 01/54] general cleanup --- SU2_CFD/include/solvers/CFEASolver.hpp | 6 +++--- SU2_CFD/src/solvers/CFEASolver.cpp | 16 ++++------------ 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/SU2_CFD/include/solvers/CFEASolver.hpp b/SU2_CFD/include/solvers/CFEASolver.hpp index b4a20c377d97..948ef934ffef 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. */ @@ -179,8 +179,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/src/solvers/CFEASolver.cpp b/SU2_CFD/src/solvers/CFEASolver.cpp index f1a7104a235f..3819e0b713a1 100644 --- a/SU2_CFD/src/solvers/CFEASolver.cpp +++ b/SU2_CFD/src/solvers/CFEASolver.cpp @@ -96,12 +96,6 @@ 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; @@ -243,8 +237,6 @@ CFEASolver::CFEASolver(CGeometry *geometry, CConfig *config) : CSolver() { /*--- Initialize structures for hybrid-parallel mode. ---*/ HybridParallelInitialization(geometry); - iElem_iDe = nullptr; - /*--- Initialize the value of the total objective function ---*/ Total_OFRefGeom = 0.0; Total_OFRefNode = 0.0; @@ -312,15 +304,15 @@ 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; - if (nodes != nullptr) delete nodes; + delete nodes; if (LockStrategy) { for (unsigned long iPoint = 0; iPoint < nPoint; iPoint++) From 49f7d625845a2d85fbaf169ec6d0b6a399ce42f1 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 17 Mar 2020 11:54:04 +0000 Subject: [PATCH 02/54] cleanup RBF interpolation --- Common/include/interpolation_structure.hpp | 141 +- Common/src/interpolation_structure.cpp | 1423 +++++++------------- 2 files changed, 523 insertions(+), 1041 deletions(-) diff --git a/Common/include/interpolation_structure.hpp b/Common/include/interpolation_structure.hpp index 28bdf3af8677..a1349ecd4df7 100644 --- a/Common/include/interpolation_structure.hpp +++ b/Common/include/interpolation_structure.hpp @@ -145,10 +145,16 @@ class CInterpolator { /*! * \brief compute distance between 2 points + * \param[in] nDim - number of dimensions * \param[in] point_i * \param[in] point_i */ - su2double PointsDistance(su2double *point_i, su2double *point_j); + inline su2double PointsDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) const { + su2double m = 0; + for(unsigned short iDim = 0; iDim < nDim; iDim++) + m += pow(point_j[iDim] - point_i[iDim], 2); + return sqrt(m); + } /*! * \brief Set up transfer matrix defining relation between two meshes @@ -382,9 +388,8 @@ class CSlidingMesh : public CInterpolator { /*! * \brief Radial basis function interpolation */ -class CRadialBasisFunction : public CInterpolator { +class CRadialBasisFunction final : public CInterpolator { public: - /*! * \brief Constructor of the class. */ @@ -399,16 +404,11 @@ class CRadialBasisFunction : public CInterpolator { */ 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); + void Set_TransferCoeff(CConfig **config) override; /*! * \brief Compute the value of a radial basis function, this is static so it can be re-used. @@ -416,76 +416,77 @@ class CRadialBasisFunction : public CInterpolator { * \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); - + static su2double Get_RadialBasisValue(const unsigned short 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! + * \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). */ - 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); + int CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, su2passivematrix &P) const; }; /*! - * \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" + * \brief Helper class used by CRadialBasisFunction to compute the interpolation weights. + * 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{ +private: + enum DecompositionType { NONE, CHOLESKY, LU }; + + vector val_vec, decomp_vec; + vector perm_vec; + int sz = 0; + bool initialized = false; + DecompositionType decomposed = NONE; + + inline void CheckBounds(int i, int j) const { + assert(initialized && "Matrix not initialized."); + assert(i>=0 && i=0 && jGetnDim(); - 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() { } @@ -685,7 +646,7 @@ void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { Coord_j = &Buffer_Receive_Coord[ Global_Point_Donor*nDim]; - dist = PointsDistance(Coord_i, Coord_j); + dist = PointsDistance(nDim, Coord_i, Coord_j); if (dist < mindist) { mindist = dist; pProcessor = iProcessor; @@ -1674,7 +1635,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config){ Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ]; - dist = PointsDistance(Coord_i, Coord_j); + dist = PointsDistance(nDim, Coord_i, Coord_j); if (dist < mindist) { mindist = dist; @@ -1719,7 +1680,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config){ for(iDim = 0; iDim < nDim; iDim++) Direction[iDim] /= dTMP; - length = PointsDistance(target_iMidEdge_point, target_jMidEdge_point); + length = PointsDistance(nDim, target_iMidEdge_point, target_jMidEdge_point); check = false; @@ -1951,7 +1912,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config){ Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ]; - dist = PointsDistance(Coord_i, Coord_j); + dist = PointsDistance(nDim, Coord_i, Coord_j); if (dist < mindist) { mindist = dist; @@ -2723,620 +2684,344 @@ bool CSlidingMesh::CheckPointInsideTriangle(su2double* Point, su2double* T1, su2 } /*--- Radial Basis Function Interpolator ---*/ -CRadialBasisFunction::CRadialBasisFunction(void): CInterpolator() { } +CRadialBasisFunction::CRadialBasisFunction(void): CInterpolator() { } -CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { +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 + /*--- RBF options. ---*/ + const unsigned short kindRBF = config[donorZone]->GetKindRadialBasisFunction(); + const su2double paramRBF = config[donorZone]->GetRadialBasisFunctionParameter(); + const bool usePolynomial = config[donorZone]->GetRadialBasisFunctionPolynomialOption(); + const passivedouble eps = numeric_limits::epsilon(); + const su2double interfaceCoordTol = 1e6 * eps; - /*--- Initialize variables --- */ - - nMarkerInt = (int) ( config[donorZone]->GetMarker_n_ZoneInterface() / 2 ); - - nDim = donor_geometry->GetnDim(); + const int nDim = donor_geometry->GetnDim(); - + const int nProcessor = size; + Buffer_Send_nVertex_Donor = new unsigned long [1]; Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; + /*--- Cycle over nMarkersInt interface to determine communication pattern. ---*/ - /*--- Cycle over nMarkersInt interface to determine communication pattern ---*/ + const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2; - for (iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { + for (unsigned short 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); + /*--- On the donor side: find the tag of the boundary sharing the interface. ---*/ + int mark_donor = Find_InterfaceMarker(config[donorZone], iMarkerInt); -#ifdef HAVE_MPI + /*--- On the target side: find the tag of the boundary sharing the interface. ---*/ + int mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt); - 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; - } + /*--- 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. ---*/ + int donor_check, target_check; + SU2_MPI::Allreduce(&mark_donor, &donor_check, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); + SU2_MPI::Allreduce(&mark_target, &target_check, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); - 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 the zone does not contain the interface continue to the next pair of markers. ---*/ + 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 ]; + unsigned long nVertexDonor = 0, nVertexTarget = 0; + if(mark_donor != -1) nVertexDonor = donor_geometry->GetnVertex( mark_donor ); + if(mark_target != -1) nVertexTarget = target_geometry->GetnVertex( mark_target ); - /*--- Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor ---*/ + /*--- 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 ]; + /*--- Compute total number of donor vertices. ---*/ + auto nGlobalVertexDonor = accumulate(Buffer_Receive_nVertex_Donor, + Buffer_Receive_nVertex_Donor+nProcessor, 0ul); - Collect_VertexInfo( false, mark_donor, mark_target, nVertexDonor, nDim); + /*--- 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 ]; - /*--- 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)); + auto iCount = 0ul; + for (int iProcessor = 0; iProcessor < nProcessor; ++iProcessor) { + auto offset = iProcessor * MaxLocalVertex_Donor * nDim; + 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]; + ++iCount; } + } + assert((iCount == nGlobalVertexDonor) && "Global donor point count mismatch."); - for (iProcessor=rank+1; iProcessor keepPolynomialRow(nDim,1); - local_M[iCount++] = Get_RadialBasisValue(config[donorZone]->GetKindRadialBasisFunction(), - 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 ---*/ + su2passivematrix C_inv_trunc; + + if (rank==MASTER_NODE) { + + /*--- Populate interpolation kernel. ---*/ + CSymmetricMatrix global_M(nGlobalVertexDonor); + + for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) + for (auto jVertex = iVertex; jVertex < nGlobalVertexDonor; ++jVertex) + global_M(iVertex, jVertex) = SU2_TYPE::GetValue(Get_RadialBasisValue(kindRBF, paramRBF, + PointsDistance(nDim, DonorCoord[iVertex], DonorCoord[jVertex]))); + + /*--- Invert M matrix (operation is in-place). ---*/ + switch (kindRBF) { + /*--- Basis functions that make M positive definite. ---*/ case WENDLAND_C2: case INV_MULTI_QUADRIC: case GAUSSIAN: - global_M->Invert(true); - break; + global_M.Invert(true); break; + /*--- Basis functions that make M semi-positive definite. ---*/ case THIN_PLATE_SPLINE: case MULTI_QUADRIC: - global_M->Invert(false); - break; + 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); + /*--- Check if points lie on a plane and remove one coordinate from P if so. ---*/ + nPolynomial = CheckPolynomialTerms(interfaceCoordTol, keepPolynomialRow, P); + + /*--- Calculate Mp = (P * M^-1 * P^T)^-1 ---*/ + CSymmetricMatrix Mp(nPolynomial+1); + + su2passivematrix tmp; + global_M.MatMatMult('R', P, tmp); // tmp = P * M^-1 + + for (int i = 0; i <= nPolynomial; ++i) // Mp = tmp * P + for (int j = i; j <= nPolynomial; ++j) { + Mp(i,j) = 0.0; + for (auto k = 0ul; k < nGlobalVertexDonor; ++k) Mp(i,j) += tmp(i,k) * P(j,k); } - } - 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); + /*--- Calculate M^-1 * (I - P^T * M_p * P * M^-1), finalize bottom of C_inv_trunc. ---*/ + global_M.MatMatMult('L', tmp, C_inv_trunc); - /*--- 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(keepPolynomialRow.data(), nDim, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - SU2_MPI::Bcast(C_inv_trunc, (nGlobalVertexDonor+nPolynomial+1)*nGlobalVertexDonor, MPI_DOUBLE, MASTER_NODE, MPI_COMM_WORLD); + if (rank != MASTER_NODE) + C_inv_trunc.resize(nGlobalVertexDonor+nPolynomial+1, nGlobalVertexDonor); + +#ifdef HAVE_MPI + /*--- MPI wrapper not used due to passive double. ---*/ + MPI_Bcast(C_inv_trunc.data(), C_inv_trunc.size(), 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++; + for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget++) { + + auto target_vertex = target_geometry->vertex[mark_target][iVertexTarget]; + const auto point_target = target_vertex->GetNode(); + + /*--- If not domain point move to next. ---*/ + if (!target_geometry->node[point_target]->GetDomain()) continue; + + su2passivevector target_vec(nGlobalVertexDonor+nPolynomial+1); + su2passivevector coeff_vec(nGlobalVertexDonor); + + const su2double* coord_i = target_geometry->node[point_target]->GetCoord(); + + /*--- Prepare target vector (one row of the target matrix). ---*/ + /*--- polynominal part ---*/ + int idx = 0; + if (usePolynomial) { + target_vec(idx++) = 1.0; // constant term + for (int iDim = 0; iDim < nDim; ++iDim) // linear terms + if (keepPolynomialRow[iDim]) // of which one may have been excluded + target_vec(idx++) = SU2_TYPE::GetValue(coord_i[iDim]); + } + /*--- RBF part ---*/ + for (auto iVertexDonor = 0ul; iVertexDonor < nGlobalVertexDonor; ++iVertexDonor) + target_vec(iVertexDonor+idx) = SU2_TYPE::GetValue(Get_RadialBasisValue(kindRBF, paramRBF, + PointsDistance(nDim, coord_i, DonorCoord[iVertexDonor]))); + + /*--- Multiply target vector by C_inv_trunc to obtain the interpolation coefficients. ---*/ + for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) { + coeff_vec(iVertex) = 0.0; + for (auto jVertex = 0ul; jVertex < target_vec.size(); ++jVertex) + coeff_vec(iVertex) += target_vec(jVertex) * C_inv_trunc(jVertex, iVertex); + } + + /*--- Count the number of donor points (non zero coefficients) for this target point. ---*/ + idx = 0; + for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) + idx += (fabs(coeff_vec(iVertex)) > eps); + + target_vertex->SetnDonorPoints(idx); + target_vertex->Allocate_DonorInfo(); + + int iSet = 0; + for (int iProcessor = 0, iTest = -1; iProcessor < nProcessor; ++iProcessor) { + const auto offset = iProcessor * MaxLocalVertex_Donor; + for (auto iVertex = 0ul; iVertex < Buffer_Receive_nVertex_Donor[iProcessor]; ++iVertex) + if (fabs(coeff_vec(++iTest)) > eps) { + auto point_donor = Buffer_Receive_GlobalPoint[offset + iVertex]; + target_vertex->SetInterpDonorProcessor(iSet, iProcessor); + target_vertex->SetInterpDonorPoint(iSet, point_donor); + target_vertex->SetDonorCoeff(iSet, coeff_vec(iTest)); + ++iSet; } - } - } // 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; } - } - + assert(idx==iSet && "Error while setting donor point coefficients."); + + } // end target vertex loop + 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 + } // end loop over interface markers + delete[] Buffer_Send_nVertex_Donor; 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); +int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, + su2passivematrix &P) const { + const int m = P.rows(); + const int n = P.cols(); - 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++; + /*--- 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; + 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); } - } - - /*--- 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 ++) + PPT.Invert(true); + + /*--- RHS for the least squares fit (vector of ones times P). ---*/ + vector coeff(n_rows,0.0); + + for (int i = 0; i < n_rows; ++i) + for (int j = 0; j < n; ++j) + coeff[i] += P(i+1,j); + + /*--- Multiply the RHS by the inverse thus obtaining the coefficients. ---*/ + PPT.MatVecMult(coeff.data()); + + /*--- Determine the maximum deviation of the points from the fitted plane. ---*/ + passivedouble max_diff = 0.0; + + for (int j = 0; j < n; ++j) { - 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 ---*/ + 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); } - - for (unsigned long i=0; i max_coeff) iCount = i; - } + /*--- 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; - for (unsigned long i=0; i 0.0 && "LLT failed, matrix is not SPD."); + Set(j, j, sqrt(sum)); - /*--- Decompose matrix ---*/ - for (j=0; j abs(pivot) ) { - pivot = decompose_vec[CalcIdxFull(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; - interchange_row = true; - } - } - - if ( interchange_row ) { - for ( k=0; k inv(sz*sz, 0.0); + + /*--- Compute L inverse. ---*/ + /*--- Solve smaller and smaller systems. ---*/ + for (int j = 0; j < sz; ++j) { + /*--- Forward substitution. ---*/ + inv[IdxSym(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[IdxSym(k,j)]; + inv[IdxSym(i,j)] = sum / Get(i,i); + } + } // L inverse in inv + + /*--- Multiply inversed matrices overwrite val_vec. ---*/ + 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[IdxSym(k,i)] * inv[IdxSym(k,j)]; + Set(i, j, sum); + } - inv_val_vec[CalcIdx(j, j)] = 1.0; + break; + } - /*--- Forward substitution ---*/ - for (i=j; i& inv = val_vec; - if (i==j) { - inv_val_vec[CalcIdx(i, i)] = 1/ReadL(i, i); - } - else { - sum = 0.0; - for (k=j; k().swap(decomp_vec); break; - - default: - throw invalid_argument("Default (LU) decomposition failed."); + } + default: assert(false && "Default (LU) decomposition failed."); } - decompose_vec = NULL; - decomposed = none; - inversed = true; + decomposed = NONE; #endif } -void CSymmetricMatrix::CalcInv_sptri() +void CSymmetricMatrix::CalcInv_sytri() { #ifdef HAVE_LAPACK char uplo = 'L'; - int info, *ipiv = new int [sz]; - passivedouble *work = new passivedouble [sz]; + int info; + perm_vec.resize(sz); // ipiv array + + /*--- Query the optimum work size. ---*/ + int query = -1; passivedouble tmp; + dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), &tmp, &query, &info); + query = static_cast(tmp); + decomp_vec.resize(query); // work array + + /*--- Factorize and invert. ---*/ + dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &query, &info); + if (info!=0) SU2_MPI::Error("LDLT factorization failed.", CURRENT_FUNCTION); + dsytri_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &info); + if (info!=0) SU2_MPI::Error("Inversion with LDLT factorization failed.", CURRENT_FUNCTION); + + decomposed = NONE; +#endif +} - dsptrf_(&uplo, &sz, val_vec, ipiv, &info); - dsptri_(&uplo, &sz, val_vec, ipiv, work, &info); +void CSymmetricMatrix::CalcInv_potri() +{ +#ifdef HAVE_LAPACK + char uplo = 'L'; + int info; - delete [] ipiv; - delete [] work; + dpotrf_(&uplo, &sz, val_vec.data(), &sz, &info); + if (info!=0) SU2_MPI::Error("LLT factorization failed.", CURRENT_FUNCTION); + dpotri_(&uplo, &sz, val_vec.data(), &sz, &info); + if (info!=0) SU2_MPI::Error("Inversion with LLT factorization failed.", CURRENT_FUNCTION); - if(decompose_vec) delete[] decompose_vec; - decompose_vec = NULL; - decomposed = none; - inversed = true; + decomposed = NONE; #endif } void CSymmetricMatrix::Invert(const bool is_spd) { #ifdef HAVE_LAPACK - CalcInv_sptri(); + if(is_spd) CalcInv_potri(); + else CalcInv_sytri(); #else if(!is_spd) LUDecompose(); - else CholeskyDecompose(true); - CalcInv(true); + else CholeskyDecompose(); + CalcInv(); #endif } -void CSymmetricMatrix::MatVecMult(passivedouble *v) +void CSymmetricMatrix::MatVecMult(passivedouble *v) const { passivedouble *tmp_res = new passivedouble [sz]; for (int i=0; i=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."); - } } From ff8c57f96f8313e9815d108003711b0396b75576 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 17 Mar 2020 14:35:56 +0000 Subject: [PATCH 03/54] prune tolerance for sparse RBF interpolation matrix --- Common/include/CConfig.hpp | 8 ++++- Common/src/CConfig.cpp | 7 ++-- Common/src/interpolation_structure.cpp | 37 ++++++++++++++------ TestCases/fea_fsi/Airfoil_RBF/config.cfg | 2 +- TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg | 3 +- TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg | 14 +++++--- 6 files changed, 50 insertions(+), 21 deletions(-) diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp index e316b4cb8d01..877f073cc08e 100644 --- a/Common/include/CConfig.hpp +++ b/Common/include/CConfig.hpp @@ -990,7 +990,8 @@ class CConfig { bool ConservativeInterpolation; /*!< \brief Conservative approach for non matching mesh 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. */ @@ -8787,6 +8788,11 @@ 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 kind of inlet face interpolation function to use. */ diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index 84dd8cef7eae..2899aab3328f 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -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 diff --git a/Common/src/interpolation_structure.cpp b/Common/src/interpolation_structure.cpp index 000a55cf9e06..421d3d94e0e0 100644 --- a/Common/src/interpolation_structure.cpp +++ b/Common/src/interpolation_structure.cpp @@ -2697,8 +2697,9 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { /*--- RBF options. ---*/ const unsigned short kindRBF = config[donorZone]->GetKindRadialBasisFunction(); - const su2double paramRBF = config[donorZone]->GetRadialBasisFunctionParameter(); const bool usePolynomial = config[donorZone]->GetRadialBasisFunctionPolynomialOption(); + const su2double paramRBF = config[donorZone]->GetRadialBasisFunctionParameter(); + const su2double pruneTol = config[donorZone]->GetRadialBasisFunctionPruneTol(); const passivedouble eps = numeric_limits::epsilon(); const su2double interfaceCoordTol = 1e6 * eps; @@ -2792,7 +2793,7 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { global_M.Invert(false); break; } - /*--- Calculate C_inv_trunc. ---*/ + /*--- Compute C_inv_trunc. ---*/ if (usePolynomial) { /*--- Fill P matrix (P for points, with an extra top row of ones). ---*/ @@ -2807,7 +2808,7 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { /*--- Check if points lie on a plane and remove one coordinate from P if so. ---*/ nPolynomial = CheckPolynomialTerms(interfaceCoordTol, keepPolynomialRow, P); - /*--- Calculate Mp = (P * M^-1 * P^T)^-1 ---*/ + /*--- Compute Mp = (P * M^-1 * P^T)^-1 ---*/ CSymmetricMatrix Mp(nPolynomial+1); su2passivematrix tmp; @@ -2820,12 +2821,12 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { } Mp.Invert(false); // Mp = Mp^-1 - /*--- Calculate M_p * P * M^-1, the top part of C_inv_trunc. ---*/ + /*--- Compute M_p * P * M^-1, the top part of C_inv_trunc. ---*/ Mp.MatMatMult('L', P, tmp); su2passivematrix C_inv_top; global_M.MatMatMult('R', tmp, C_inv_top); - /*--- Calculate tmp = (I - P^T * M_p * P * M^-1), part of the bottom part of + /*--- Compute tmp = (I - P^T * M_p * P * M^-1), part of the bottom part of C_inv_trunc. Note that most of the product is known from the top part. ---*/ tmp.resize(nGlobalVertexDonor, nGlobalVertexDonor); @@ -2837,7 +2838,7 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { tmp(i,i) += 1.0; // identity part } - /*--- Calculate M^-1 * (I - P^T * M_p * P * M^-1), finalize bottom of C_inv_trunc. ---*/ + /*--- Compute M^-1 * (I - P^T * M_p * P * M^-1), finalize bottom of C_inv_trunc. ---*/ global_M.MatMatMult('L', tmp, C_inv_trunc); /*--- Merge top and bottom of C_inv_trunc. ---*/ @@ -2901,18 +2902,32 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { target_vec(iVertexDonor+idx) = SU2_TYPE::GetValue(Get_RadialBasisValue(kindRBF, paramRBF, PointsDistance(nDim, coord_i, DonorCoord[iVertexDonor]))); - /*--- Multiply target vector by C_inv_trunc to obtain the interpolation coefficients. ---*/ + /*--- Multiply target vector by C_inv_trunc to obtain the interpolation coefficients. + Simultaneously determine a reference for dropping small coefficients. ---*/ + passivedouble coeffRef = 0.0; for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) { coeff_vec(iVertex) = 0.0; for (auto jVertex = 0ul; jVertex < target_vec.size(); ++jVertex) coeff_vec(iVertex) += target_vec(jVertex) * C_inv_trunc(jVertex, iVertex); + coeffRef = max(coeffRef, fabs(coeff_vec(iVertex))); } + coeffRef *= SU2_TYPE::GetValue(pruneTol); - /*--- Count the number of donor points (non zero coefficients) for this target point. ---*/ + /*--- Prune and count the number of donor points for this target point. ---*/ idx = 0; - for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) - idx += (fabs(coeff_vec(iVertex)) > eps); + passivedouble coeffSum = 0.0; + for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) { + if (fabs(coeff_vec(iVertex)) > coeffRef) { + coeffSum += coeff_vec(iVertex); + ++idx; + } + else coeff_vec(iVertex) = 0.0; + } + /*--- Correct remaining coefficients, sum must be 1 for conservation. ---*/ + passivedouble correction = 1.0 / coeffSum; + for (auto i = 0ul; i < nGlobalVertexDonor; ++i) coeff_vec(i) *= correction; + /*--- Allocate and set donor information for this target point. ---*/ target_vertex->SetnDonorPoints(idx); target_vertex->Allocate_DonorInfo(); @@ -2920,7 +2935,7 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { for (int iProcessor = 0, iTest = -1; iProcessor < nProcessor; ++iProcessor) { const auto offset = iProcessor * MaxLocalVertex_Donor; for (auto iVertex = 0ul; iVertex < Buffer_Receive_nVertex_Donor[iProcessor]; ++iVertex) - if (fabs(coeff_vec(++iTest)) > eps) { + if (fabs(coeff_vec(++iTest)) > 0.0) { auto point_donor = Buffer_Receive_GlobalPoint[offset + iVertex]; target_vertex->SetInterpDonorProcessor(iSet, iProcessor); target_vertex->SetInterpDonorPoint(iSet, point_donor); 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..99e3530ccdd3 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 = 1e-6 % % Physics -------------------------------------------------------------- % SOLVER= ELASTICITY diff --git a/TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg b/TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg index 183cb402f3fc..6d293bd5506e 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 = 1e-6 % % Physics -------------------------------------------------------------- % SOLVER= EULER From 88961123db636096e963162e2c43a0391dfa03a9 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 17 Mar 2020 19:06:33 +0000 Subject: [PATCH 04/54] RBF interpolation in parallel --- Common/src/interpolation_structure.cpp | 301 +++++++++++++++---------- 1 file changed, 187 insertions(+), 114 deletions(-) diff --git a/Common/src/interpolation_structure.cpp b/Common/src/interpolation_structure.cpp index 421d3d94e0e0..414729f9ff55 100644 --- a/Common/src/interpolation_structure.cpp +++ b/Common/src/interpolation_structure.cpp @@ -198,7 +198,7 @@ void CInterpolator::Collect_VertexInfo(bool faces, int markDonor, int markTarget unsigned short iDim; /* Only needed if face data is also collected */ - su2double *Normal; + const su2double *Normal = nullptr; for (iVertex = 0; iVertex < MaxLocalVertex_Donor; iVertex++) { Buffer_Send_GlobalPoint[iVertex] = -1; @@ -220,7 +220,7 @@ void CInterpolator::Collect_VertexInfo(bool faces, int markDonor, int markTarget Buffer_Send_Coord[nLocalVertex_Donor*nDim+iDim] = donor_geometry->node[iPointDonor]->GetCoord(iDim); if (faces) { - Normal = donor_geometry->vertex[markDonor][iVertexDonor]->GetNormal(); + Normal = donor_geometry->vertex[markDonor][iVertexDonor]->GetNormal(); for (iDim = 0; iDim < nDim; iDim++) Buffer_Send_Normal[nLocalVertex_Donor*nDim+iDim] = Normal[iDim]; } @@ -2703,17 +2703,24 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { const passivedouble eps = numeric_limits::epsilon(); const su2double interfaceCoordTol = 1e6 * eps; + const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2; const int nDim = donor_geometry->GetnDim(); const int nProcessor = size; Buffer_Send_nVertex_Donor = new unsigned long [1]; Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; - /*--- Cycle over nMarkersInt interface to determine communication pattern. ---*/ - - const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2; + /*--- 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 = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { + for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; ++iMarkerInt) { /*--- On the donor side: find the tag of the boundary sharing the interface. ---*/ int mark_donor = Find_InterfaceMarker(config[donorZone], iMarkerInt); @@ -2730,9 +2737,8 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { /*--- If the zone does not contain the interface continue to the next pair of markers. ---*/ if(target_check == -1 || donor_check == -1) continue; - unsigned long nVertexDonor = 0, nVertexTarget = 0; - if(mark_donor != -1) nVertexDonor = donor_geometry->GetnVertex( mark_donor ); - if(mark_target != -1) nVertexTarget = target_geometry->GetnVertex( mark_target ); + unsigned long nVertexDonor = 0; + if(mark_donor != -1) nVertexDonor = donor_geometry->GetnVertex(mark_donor); /*--- Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor. ---*/ Determine_ArraySize(false, mark_donor, mark_target, nVertexDonor, nDim); @@ -2749,132 +2755,205 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { Collect_VertexInfo(false, mark_donor, mark_target, nVertexDonor, nDim); - /*--- Compress the gathered donor point coordinates to simplify calculations. ---*/ - su2activematrix DonorCoord(nGlobalVertexDonor, nDim); + /*--- Compresses the gathered donor point information to simplify computation. ---*/ + auto& DonorCoord = DonorCoordinates[iMarkerInt-1]; + auto& DonorPoint = DonorGlobalPoint[iMarkerInt-1]; + auto& DonorProc = DonorProcessor[iMarkerInt-1]; + 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 * nDim; + 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]; + 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."); - /*--- The master node prepares the global interpolation kernel. ---*/ - int nPolynomial = -1; - vector keepPolynomialRow(nDim,1); + delete[] Buffer_Send_Coord; + delete[] Buffer_Send_GlobalPoint; + delete[] Buffer_Receive_Coord; + delete[] Buffer_Receive_GlobalPoint; - su2passivematrix C_inv_trunc; + /*--- 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; - if (rank==MASTER_NODE) { + TotalWork[iProcessor] += pow(nGlobalVertexDonor,3); // based on matrix inversion. - /*--- Populate interpolation kernel. ---*/ - CSymmetricMatrix global_M(nGlobalVertexDonor); + AssignedProcessor[iMarkerInt-1] = iProcessor; - for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) - for (auto jVertex = iVertex; jVertex < nGlobalVertexDonor; ++jVertex) - global_M(iVertex, jVertex) = SU2_TYPE::GetValue(Get_RadialBasisValue(kindRBF, paramRBF, - PointsDistance(nDim, DonorCoord[iVertex], DonorCoord[jVertex]))); + } - /*--- Invert M matrix (operation is in-place). ---*/ - switch (kindRBF) { - /*--- Basis functions that make M positive definite. ---*/ - case WENDLAND_C2: - case INV_MULTI_QUADRIC: - case GAUSSIAN: - global_M.Invert(true); break; + /*--- 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); - /*--- Basis functions that make M semi-positive definite. ---*/ - case THIN_PLATE_SPLINE: - case MULTI_QUADRIC: - global_M.Invert(false); break; - } + SU2_OMP_PARALLEL_(for schedule(dynamic,1)) + for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; ++iMarkerInt) { - /*--- Compute C_inv_trunc. ---*/ - if (usePolynomial) { + if (rank != AssignedProcessor[iMarkerInt]) continue; // #notmyjob - /*--- Fill P matrix (P for points, with an extra top row of ones). ---*/ - su2passivematrix P(1+nDim, nGlobalVertexDonor); + const auto& DonorCoord = DonorCoordinates[iMarkerInt]; + const auto nGlobalVertexDonor = DonorCoord.rows(); - for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; iVertex++) { - P(0, iVertex) = 1.0; - for (int iDim = 0; iDim < nDim; ++iDim) - P(1+iDim, iVertex) = SU2_TYPE::GetValue(DonorCoord(iVertex, iDim)); - } + auto& C_inv_trunc = CinvTrucVec[iMarkerInt]; + auto& nPolynomial = nPolynomialVec[iMarkerInt]; + auto& keepPolynomialRow = keepPolynomialRowVec[iMarkerInt]; - /*--- Check if points lie on a plane and remove one coordinate from P if so. ---*/ - nPolynomial = CheckPolynomialTerms(interfaceCoordTol, keepPolynomialRow, P); + /*--- Populate interpolation kernel. ---*/ + CSymmetricMatrix global_M(nGlobalVertexDonor); - /*--- Compute Mp = (P * M^-1 * P^T)^-1 ---*/ - CSymmetricMatrix Mp(nPolynomial+1); + for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) + for (auto jVertex = iVertex; jVertex < nGlobalVertexDonor; ++jVertex) + global_M(iVertex, jVertex) = SU2_TYPE::GetValue(Get_RadialBasisValue(kindRBF, paramRBF, + PointsDistance(nDim, DonorCoord[iVertex], DonorCoord[jVertex]))); - su2passivematrix tmp; - global_M.MatMatMult('R', P, tmp); // tmp = P * M^-1 + /*--- Invert M matrix (operation is in-place). ---*/ + switch (kindRBF) { + /*--- Basis functions that make M positive definite. ---*/ + case WENDLAND_C2: + case INV_MULTI_QUADRIC: + case GAUSSIAN: + global_M.Invert(true); break; - for (int i = 0; i <= nPolynomial; ++i) // Mp = tmp * P - for (int j = i; j <= nPolynomial; ++j) { - Mp(i,j) = 0.0; - for (auto k = 0ul; k < nGlobalVertexDonor; ++k) Mp(i,j) += tmp(i,k) * P(j,k); - } - Mp.Invert(false); // Mp = Mp^-1 + /*--- Basis functions that make M semi-positive definite. ---*/ + case THIN_PLATE_SPLINE: + case MULTI_QUADRIC: + global_M.Invert(false); break; + } - /*--- Compute M_p * P * M^-1, the top part of C_inv_trunc. ---*/ - Mp.MatMatMult('L', P, tmp); - su2passivematrix C_inv_top; - global_M.MatMatMult('R', tmp, C_inv_top); + /*--- Compute C_inv_trunc. ---*/ + if (usePolynomial) { - /*--- Compute tmp = (I - P^T * M_p * P * M^-1), part of the bottom part of - C_inv_trunc. Note that most of the product is known from the top part. ---*/ - tmp.resize(nGlobalVertexDonor, nGlobalVertexDonor); + /*--- Fill P matrix (P for points, with an extra top row of ones). ---*/ + su2passivematrix P(1+nDim, nGlobalVertexDonor); - for (auto i = 0ul; i < nGlobalVertexDonor; ++i) { - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) { - tmp(i,j) = 0.0; - for (int k = 0; k <= nPolynomial; ++k) tmp(i,j) -= P(k,i) * C_inv_top(k,j); - } - tmp(i,i) += 1.0; // identity part + for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; iVertex++) { + P(0, iVertex) = 1.0; + for (int iDim = 0; iDim < nDim; ++iDim) + P(1+iDim, iVertex) = SU2_TYPE::GetValue(DonorCoord(iVertex, iDim)); + } + + /*--- Check if points lie on a plane and remove one coordinate from P if so. ---*/ + nPolynomial = CheckPolynomialTerms(interfaceCoordTol, keepPolynomialRow, P); + + /*--- Compute Mp = (P * M^-1 * P^T)^-1 ---*/ + CSymmetricMatrix Mp(nPolynomial+1); + + su2passivematrix tmp; + global_M.MatMatMult('R', P, tmp); // tmp = P * M^-1 + + for (int i = 0; i <= nPolynomial; ++i) // Mp = tmp * P + for (int j = i; j <= nPolynomial; ++j) { + Mp(i,j) = 0.0; + for (auto k = 0ul; k < nGlobalVertexDonor; ++k) Mp(i,j) += tmp(i,k) * P(j,k); } + Mp.Invert(false); // Mp = Mp^-1 - /*--- Compute M^-1 * (I - P^T * M_p * P * M^-1), finalize bottom of C_inv_trunc. ---*/ - global_M.MatMatMult('L', tmp, C_inv_trunc); + /*--- Compute M_p * P * M^-1, the top part of C_inv_trunc. ---*/ + Mp.MatMatMult('L', P, tmp); + su2passivematrix C_inv_top; + global_M.MatMatMult('R', tmp, C_inv_top); - /*--- Merge top and bottom of C_inv_trunc. ---*/ - tmp = move(C_inv_trunc); - C_inv_trunc.resize(1+nPolynomial+nGlobalVertexDonor, nGlobalVertexDonor); - memcpy(C_inv_trunc[0], C_inv_top.data(), C_inv_top.size()*sizeof(passivedouble)); - memcpy(C_inv_trunc[1+nPolynomial], tmp.data(), tmp.size()*sizeof(passivedouble)); + /*--- Compute tmp = (I - P^T * M_p * P * M^-1), part of the bottom part of + C_inv_trunc. Note that most of the product is known from the top part. ---*/ + tmp.resize(nGlobalVertexDonor, nGlobalVertexDonor); + + for (auto i = 0ul; i < nGlobalVertexDonor; ++i) { + for (auto j = 0ul; j < nGlobalVertexDonor; ++j) { + tmp(i,j) = 0.0; + for (int k = 0; k <= nPolynomial; ++k) tmp(i,j) -= P(k,i) * C_inv_top(k,j); + } + tmp(i,i) += 1.0; // identity part } - else { - /*--- No polynomial term used in the interpolation, C_inv_trunc = M^-1. ---*/ - C_inv_trunc.resize(nGlobalVertexDonor, nGlobalVertexDonor); - for (auto i = 0ul; i < nGlobalVertexDonor; ++i) - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) - C_inv_trunc(i,j) = global_M(i,j); + /*--- Compute M^-1 * (I - P^T * M_p * P * M^-1), finalize bottom of C_inv_trunc. ---*/ + global_M.MatMatMult('L', tmp, C_inv_trunc); - } // end usePolynomial + /*--- Merge top and bottom of C_inv_trunc. ---*/ + tmp = move(C_inv_trunc); + C_inv_trunc.resize(1+nPolynomial+nGlobalVertexDonor, nGlobalVertexDonor); + memcpy(C_inv_trunc[0], C_inv_top.data(), C_inv_top.size()*sizeof(passivedouble)); + memcpy(C_inv_trunc[1+nPolynomial], tmp.data(), tmp.size()*sizeof(passivedouble)); + } + else { + /*--- No polynomial term used in the interpolation, C_inv_trunc = M^-1. ---*/ + + C_inv_trunc.resize(nGlobalVertexDonor, nGlobalVertexDonor); + for (auto i = 0ul; i < nGlobalVertexDonor; ++i) + for (auto j = 0ul; j < nGlobalVertexDonor; ++j) + C_inv_trunc(i,j) = global_M(i,j); + + } // end usePolynomial + + } - } // end (rank == MASTER_NODE) + /*--- Final loop over interface markers to compute the interpolation coefficients. ---*/ - /*--- Broadcast C_inv_trunc to all nodes, each node then - computes its part of the interpolation matrix. ---*/ + for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; iMarkerInt++) { - SU2_MPI::Bcast(&nPolynomial, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - SU2_MPI::Bcast(keepPolynomialRow.data(), nDim, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); + /*--- 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; - if (rank != MASTER_NODE) - C_inv_trunc.resize(nGlobalVertexDonor+nPolynomial+1, nGlobalVertexDonor); + /*--- Setup target information. ---*/ + const int mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt+1); + unsigned long nVertexTarget = 0; + if(mark_target != -1) nVertexTarget = target_geometry->GetnVertex(mark_target); + + /*--- 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 - /*--- MPI wrapper not used due to passive double. ---*/ - MPI_Bcast(C_inv_trunc.data(), C_inv_trunc.size(), MPI_DOUBLE, MASTER_NODE, MPI_COMM_WORLD); + /*--- 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 H matrix. ---*/ + /*--- Compute H matrix, distributing target points over the threads in the rank. ---*/ + SU2_OMP_PARALLEL + { + su2passivevector target_vec(nGlobalVertexDonor+nPolynomial+1); + su2passivevector coeff_vec(nGlobalVertexDonor); + SU2_OMP_FOR_DYN(roundUpDiv(nVertexTarget, 2*omp_get_max_threads())) for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget++) { auto target_vertex = target_geometry->vertex[mark_target][iVertexTarget]; @@ -2883,9 +2962,6 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { /*--- If not domain point move to next. ---*/ if (!target_geometry->node[point_target]->GetDomain()) continue; - su2passivevector target_vec(nGlobalVertexDonor+nPolynomial+1); - su2passivevector coeff_vec(nGlobalVertexDonor); - const su2double* coord_i = target_geometry->node[point_target]->GetCoord(); /*--- Prepare target vector (one row of the target matrix). ---*/ @@ -2930,27 +3006,24 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { /*--- Allocate and set donor information for this target point. ---*/ target_vertex->SetnDonorPoints(idx); target_vertex->Allocate_DonorInfo(); - - int iSet = 0; - for (int iProcessor = 0, iTest = -1; iProcessor < nProcessor; ++iProcessor) { - const auto offset = iProcessor * MaxLocalVertex_Donor; - for (auto iVertex = 0ul; iVertex < Buffer_Receive_nVertex_Donor[iProcessor]; ++iVertex) - if (fabs(coeff_vec(++iTest)) > 0.0) { - auto point_donor = Buffer_Receive_GlobalPoint[offset + iVertex]; - target_vertex->SetInterpDonorProcessor(iSet, iProcessor); - target_vertex->SetInterpDonorPoint(iSet, point_donor); - target_vertex->SetDonorCoeff(iSet, coeff_vec(iTest)); - ++iSet; - } + idx = 0; + for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) { + if (fabs(coeff_vec(iVertex)) > 0.0) { + target_vertex->SetInterpDonorProcessor(idx, DonorProc[iVertex]); + target_vertex->SetInterpDonorPoint(idx, DonorPoint[iVertex]); + target_vertex->SetDonorCoeff(idx, coeff_vec(iVertex)); + ++idx; + } } - assert(idx==iSet && "Error while setting donor point coefficients."); } // end target vertex loop + } // end SU2_OMP_PARALLEL - delete[] Buffer_Send_Coord; - delete[] Buffer_Send_GlobalPoint; - delete[] Buffer_Receive_Coord; - delete[] Buffer_Receive_GlobalPoint; + /*--- Delete 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 From 47ad5d13ebc810a295b77cb02646c166f25f8ba7 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 17 Mar 2020 19:07:52 +0000 Subject: [PATCH 05/54] adjust some settings in regression case to make it closer to reference --- TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg | 2 +- TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg b/TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg index 99e3530ccdd3..eff2d6ed2b36 100644 --- a/TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg +++ b/TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg @@ -12,7 +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 = 1e-6 +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 6d293bd5506e..9efc636b33d4 100644 --- a/TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg +++ b/TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg @@ -33,7 +33,7 @@ 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 = 1e-6 +RADIAL_BASIS_FUNCTION_PRUNE_TOLERANCE = 0 % % Physics -------------------------------------------------------------- % SOLVER= EULER From 04fff4b39e06d48971902a83089749ed4748c1e7 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 17 Mar 2020 21:10:49 +0000 Subject: [PATCH 06/54] move pruning to separate function --- Common/include/interpolation_structure.hpp | 9 ++ Common/src/interpolation_structure.cpp | 121 ++++++++++++--------- 2 files changed, 81 insertions(+), 49 deletions(-) diff --git a/Common/include/interpolation_structure.hpp b/Common/include/interpolation_structure.hpp index a1349ecd4df7..448178a7a4e7 100644 --- a/Common/include/interpolation_structure.hpp +++ b/Common/include/interpolation_structure.hpp @@ -430,6 +430,15 @@ class CRadialBasisFunction final : public CInterpolator { */ int CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, su2passivematrix &P) const; + /*! + * \brief 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,out] coeffs - The vector of interpolation coefficients. + * \return Number of non-zero coefficients after pruning. + */ + int PruneSmallCoefficients(passivedouble tolerance, su2passivevector& coeffs) const; + }; /*! diff --git a/Common/src/interpolation_structure.cpp b/Common/src/interpolation_structure.cpp index 414729f9ff55..95d1a9b9aa5f 100644 --- a/Common/src/interpolation_structure.cpp +++ b/Common/src/interpolation_structure.cpp @@ -2720,13 +2720,13 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { vector AssignedProcessor(nMarkerInt,-1); vector TotalWork(nProcessor,0); - for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; ++iMarkerInt) { + for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; ++iMarkerInt) { /*--- On the donor side: find the tag of the boundary sharing the interface. ---*/ - int mark_donor = Find_InterfaceMarker(config[donorZone], iMarkerInt); + int mark_donor = Find_InterfaceMarker(config[donorZone], iMarkerInt+1); /*--- On the target side: find the tag of the boundary sharing the interface. ---*/ - int mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt); + int mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt+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. ---*/ @@ -2756,9 +2756,9 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { Collect_VertexInfo(false, mark_donor, mark_target, nVertexDonor, nDim); /*--- Compresses the gathered donor point information to simplify computation. ---*/ - auto& DonorCoord = DonorCoordinates[iMarkerInt-1]; - auto& DonorPoint = DonorGlobalPoint[iMarkerInt-1]; - auto& DonorProc = DonorProcessor[iMarkerInt-1]; + auto& DonorCoord = DonorCoordinates[iMarkerInt]; + auto& DonorPoint = DonorGlobalPoint[iMarkerInt]; + auto& DonorProc = DonorProcessor[iMarkerInt]; DonorCoord.resize(nGlobalVertexDonor, nDim); DonorPoint.resize(nGlobalVertexDonor); DonorProc.resize(nGlobalVertexDonor); @@ -2788,7 +2788,7 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { TotalWork[iProcessor] += pow(nGlobalVertexDonor,3); // based on matrix inversion. - AssignedProcessor[iMarkerInt-1] = iProcessor; + AssignedProcessor[iMarkerInt] = iProcessor; } @@ -2950,7 +2950,6 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { /*--- Compute H matrix, distributing target points over the threads in the rank. ---*/ SU2_OMP_PARALLEL { - su2passivevector target_vec(nGlobalVertexDonor+nPolynomial+1); su2passivevector coeff_vec(nGlobalVertexDonor); SU2_OMP_FOR_DYN(roundUpDiv(nVertexTarget, 2*omp_get_max_threads())) @@ -2964,55 +2963,52 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { const su2double* coord_i = target_geometry->node[point_target]->GetCoord(); - /*--- Prepare target vector (one row of the target matrix). ---*/ - /*--- polynominal part ---*/ - int idx = 0; + /*--- Multiply target vector by C_inv_trunc to obtain the interpolation coefficients. + * The target vector is not stored, we consume its entries immediately to avoid + * strided access to C_inv_trunc (as it is row-major). ---*/ + + /*--- Polynominal part: ---*/ if (usePolynomial) { - target_vec(idx++) = 1.0; // constant term - for (int iDim = 0; iDim < nDim; ++iDim) // linear terms - if (keepPolynomialRow[iDim]) // of which one may have been excluded - target_vec(idx++) = SU2_TYPE::GetValue(coord_i[iDim]); + /*--- Constant term. ---*/ + for (auto j = 0ul; j < nGlobalVertexDonor; ++j) + coeff_vec(j) = C_inv_trunc(0,j); + + /*--- Linear terms. ---*/ + for (int iDim = 0, idx = 1; iDim < nDim; ++iDim) { + /*--- Of which one may have been excluded. ---*/ + if (!keepPolynomialRow[iDim]) continue; + for (auto j = 0ul; j < nGlobalVertexDonor; ++j) + coeff_vec(j) += SU2_TYPE::GetValue(coord_i[iDim]) * C_inv_trunc(idx,j); + idx += 1; + } } - /*--- RBF part ---*/ - for (auto iVertexDonor = 0ul; iVertexDonor < nGlobalVertexDonor; ++iVertexDonor) - target_vec(iVertexDonor+idx) = SU2_TYPE::GetValue(Get_RadialBasisValue(kindRBF, paramRBF, - PointsDistance(nDim, coord_i, DonorCoord[iVertexDonor]))); - - /*--- Multiply target vector by C_inv_trunc to obtain the interpolation coefficients. - Simultaneously determine a reference for dropping small coefficients. ---*/ - passivedouble coeffRef = 0.0; - for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) { - coeff_vec(iVertex) = 0.0; - for (auto jVertex = 0ul; jVertex < target_vec.size(); ++jVertex) - coeff_vec(iVertex) += target_vec(jVertex) * C_inv_trunc(jVertex, iVertex); - coeffRef = max(coeffRef, fabs(coeff_vec(iVertex))); + else { + /*--- Initialize vector to zero. ---*/ + for (auto j = 0ul; j < nGlobalVertexDonor; ++j) coeff_vec(j) = 0.0; } - coeffRef *= SU2_TYPE::GetValue(pruneTol); - - /*--- Prune and count the number of donor points for this target point. ---*/ - idx = 0; - passivedouble coeffSum = 0.0; - for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) { - if (fabs(coeff_vec(iVertex)) > coeffRef) { - coeffSum += coeff_vec(iVertex); - ++idx; - } - else coeff_vec(iVertex) = 0.0; + + /*--- RBF terms: ---*/ + for (auto iVertexDonor = 0ul; iVertexDonor < nGlobalVertexDonor; ++iVertexDonor) { + auto w_ij = SU2_TYPE::GetValue(Get_RadialBasisValue(kindRBF, paramRBF, + PointsDistance(nDim, coord_i, DonorCoord[iVertexDonor]))); + + for (auto j = 0ul; j < nGlobalVertexDonor; ++j) + coeff_vec(j) += w_ij * C_inv_trunc(1+nPolynomial+iVertexDonor, j); } - /*--- Correct remaining coefficients, sum must be 1 for conservation. ---*/ - passivedouble correction = 1.0 / coeffSum; - for (auto i = 0ul; i < nGlobalVertexDonor; ++i) coeff_vec(i) *= correction; + + /*--- Prune small coefficients. ---*/ + auto nnz = PruneSmallCoefficients(SU2_TYPE::GetValue(pruneTol), coeff_vec); /*--- Allocate and set donor information for this target point. ---*/ - target_vertex->SetnDonorPoints(idx); + target_vertex->SetnDonorPoints(nnz); target_vertex->Allocate_DonorInfo(); - idx = 0; - for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) { + + for (unsigned long iVertex = 0, iSet = 0; iVertex < nGlobalVertexDonor; ++iVertex) { if (fabs(coeff_vec(iVertex)) > 0.0) { - target_vertex->SetInterpDonorProcessor(idx, DonorProc[iVertex]); - target_vertex->SetInterpDonorPoint(idx, DonorPoint[iVertex]); - target_vertex->SetDonorCoeff(idx, coeff_vec(iVertex)); - ++idx; + target_vertex->SetInterpDonorProcessor(iSet, DonorProc[iVertex]); + target_vertex->SetInterpDonorPoint(iSet, DonorPoint[iVertex]); + target_vertex->SetDonorCoeff(iSet, coeff_vec(iVertex)); + ++iSet; } } @@ -3032,6 +3028,33 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { } +int CRadialBasisFunction::PruneSmallCoefficients(passivedouble tolerance, + su2passivevector& coeffs) const { + + /*--- Determine the pruning threshold. ---*/ + passivedouble thresh = 0.0; + for (auto i = 0ul; i < coeffs.size(); ++i) + thresh = max(thresh, fabs(coeffs(i))); + thresh *= tolerance; + + /*--- Prune and count non-zeros. ---*/ + int numNonZeros = 0; + passivedouble coeffSum = 0.0; + for (auto i = 0ul; i < coeffs.size(); ++i) { + if (fabs(coeffs(i)) > thresh) { + coeffSum += coeffs(i); + ++numNonZeros; + } + else coeffs(i) = 0.0; + } + + /*--- Correct remaining coefficients, sum must be 1 for conservation. ---*/ + passivedouble correction = 1.0 / coeffSum; + for (auto i = 0ul; i < coeffs.size(); ++i) coeffs(i) *= correction; + + return numNonZeros; +} + int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, su2passivematrix &P) const { const int m = P.rows(); From d077e55a16813db87485182d070a41721ed66a67 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 17 Mar 2020 23:22:06 +0000 Subject: [PATCH 07/54] move one more chunk of code to separate function --- Common/include/interpolation_structure.hpp | 20 +- Common/src/interpolation_structure.cpp | 279 ++++++++++----------- 2 files changed, 155 insertions(+), 144 deletions(-) diff --git a/Common/include/interpolation_structure.hpp b/Common/include/interpolation_structure.hpp index 448178a7a4e7..bffcc34bf78d 100644 --- a/Common/include/interpolation_structure.hpp +++ b/Common/include/interpolation_structure.hpp @@ -415,10 +415,28 @@ class CRadialBasisFunction final : public CInterpolator { * \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(const unsigned short type, const su2double radius, const su2double dist); + static su2double Get_RadialBasisValue(ENUM_RADIALBASIS type, const su2double radius, const su2double dist); private: + /*! + * \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. + */ + void ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, su2double radius, + const su2activematrix& coords, int& nPolynomial, + vector& keepPolynomialRow, su2passivematrix& C_inv_trunc) const; + /*! * \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 diff --git a/Common/src/interpolation_structure.cpp b/Common/src/interpolation_structure.cpp index 95d1a9b9aa5f..f8dab7563711 100644 --- a/Common/src/interpolation_structure.cpp +++ b/Common/src/interpolation_structure.cpp @@ -2683,7 +2683,6 @@ bool CSlidingMesh::CheckPointInsideTriangle(su2double* Point, su2double* T1, su2 return (check == 3); } -/*--- Radial Basis Function Interpolator ---*/ CRadialBasisFunction::CRadialBasisFunction(void): CInterpolator() { } CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, @@ -2693,15 +2692,43 @@ CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, CCo Set_TransferCoeff(config); } +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(CConfig **config) { /*--- RBF options. ---*/ - const unsigned short kindRBF = config[donorZone]->GetKindRadialBasisFunction(); + 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 passivedouble eps = numeric_limits::epsilon(); - const su2double interfaceCoordTol = 1e6 * eps; const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2; const int nDim = donor_geometry->GetnDim(); @@ -2755,7 +2782,7 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { Collect_VertexInfo(false, mark_donor, mark_target, nVertexDonor, nDim); - /*--- Compresses the gathered donor point information to simplify computation. ---*/ + /*--- Compresses the gathered donor point information to simplify computations. ---*/ auto& DonorCoord = DonorCoordinates[iMarkerInt]; auto& DonorPoint = DonorGlobalPoint[iMarkerInt]; auto& DonorProc = DonorProcessor[iMarkerInt]; @@ -2800,102 +2827,11 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { SU2_OMP_PARALLEL_(for schedule(dynamic,1)) for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; ++iMarkerInt) { - - if (rank != AssignedProcessor[iMarkerInt]) continue; // #notmyjob - - const auto& DonorCoord = DonorCoordinates[iMarkerInt]; - const auto nGlobalVertexDonor = DonorCoord.rows(); - - auto& C_inv_trunc = CinvTrucVec[iMarkerInt]; - auto& nPolynomial = nPolynomialVec[iMarkerInt]; - auto& keepPolynomialRow = keepPolynomialRowVec[iMarkerInt]; - - /*--- Populate interpolation kernel. ---*/ - CSymmetricMatrix global_M(nGlobalVertexDonor); - - for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) - for (auto jVertex = iVertex; jVertex < nGlobalVertexDonor; ++jVertex) - global_M(iVertex, jVertex) = SU2_TYPE::GetValue(Get_RadialBasisValue(kindRBF, paramRBF, - PointsDistance(nDim, DonorCoord[iVertex], DonorCoord[jVertex]))); - - /*--- Invert M matrix (operation is in-place). ---*/ - switch (kindRBF) { - /*--- Basis functions that make M positive definite. ---*/ - case WENDLAND_C2: - case INV_MULTI_QUADRIC: - case GAUSSIAN: - global_M.Invert(true); break; - - /*--- Basis functions that make M semi-positive definite. ---*/ - case THIN_PLATE_SPLINE: - case MULTI_QUADRIC: - global_M.Invert(false); break; - } - - /*--- Compute C_inv_trunc. ---*/ - if (usePolynomial) { - - /*--- Fill P matrix (P for points, with an extra top row of ones). ---*/ - su2passivematrix P(1+nDim, nGlobalVertexDonor); - - for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; iVertex++) { - P(0, iVertex) = 1.0; - for (int iDim = 0; iDim < nDim; ++iDim) - P(1+iDim, iVertex) = SU2_TYPE::GetValue(DonorCoord(iVertex, iDim)); - } - - /*--- Check if points lie on a plane and remove one coordinate from P if so. ---*/ - nPolynomial = CheckPolynomialTerms(interfaceCoordTol, keepPolynomialRow, P); - - /*--- Compute Mp = (P * M^-1 * P^T)^-1 ---*/ - CSymmetricMatrix Mp(nPolynomial+1); - - su2passivematrix tmp; - global_M.MatMatMult('R', P, tmp); // tmp = P * M^-1 - - for (int i = 0; i <= nPolynomial; ++i) // Mp = tmp * P - for (int j = i; j <= nPolynomial; ++j) { - Mp(i,j) = 0.0; - for (auto k = 0ul; k < nGlobalVertexDonor; ++k) Mp(i,j) += tmp(i,k) * P(j,k); - } - Mp.Invert(false); // Mp = Mp^-1 - - /*--- Compute M_p * P * M^-1, the top part of C_inv_trunc. ---*/ - Mp.MatMatMult('L', P, tmp); - su2passivematrix C_inv_top; - global_M.MatMatMult('R', tmp, C_inv_top); - - /*--- Compute tmp = (I - P^T * M_p * P * M^-1), part of the bottom part of - C_inv_trunc. Note that most of the product is known from the top part. ---*/ - tmp.resize(nGlobalVertexDonor, nGlobalVertexDonor); - - for (auto i = 0ul; i < nGlobalVertexDonor; ++i) { - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) { - tmp(i,j) = 0.0; - for (int k = 0; k <= nPolynomial; ++k) tmp(i,j) -= P(k,i) * C_inv_top(k,j); - } - tmp(i,i) += 1.0; // identity part - } - - /*--- Compute M^-1 * (I - P^T * M_p * P * M^-1), finalize bottom of C_inv_trunc. ---*/ - global_M.MatMatMult('L', tmp, C_inv_trunc); - - /*--- Merge top and bottom of C_inv_trunc. ---*/ - tmp = move(C_inv_trunc); - C_inv_trunc.resize(1+nPolynomial+nGlobalVertexDonor, nGlobalVertexDonor); - memcpy(C_inv_trunc[0], C_inv_top.data(), C_inv_top.size()*sizeof(passivedouble)); - memcpy(C_inv_trunc[1+nPolynomial], tmp.data(), tmp.size()*sizeof(passivedouble)); + if (rank == AssignedProcessor[iMarkerInt]) { + ComputeGeneratorMatrix(kindRBF, usePolynomial, paramRBF, + DonorCoordinates[iMarkerInt], nPolynomialVec[iMarkerInt], + keepPolynomialRowVec[iMarkerInt], CinvTrucVec[iMarkerInt]); } - else { - /*--- No polynomial term used in the interpolation, C_inv_trunc = M^-1. ---*/ - - C_inv_trunc.resize(nGlobalVertexDonor, nGlobalVertexDonor); - for (auto i = 0ul; i < nGlobalVertexDonor; ++i) - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) - C_inv_trunc(i,j) = global_M(i,j); - - } // end usePolynomial - } /*--- Final loop over interface markers to compute the interpolation coefficients. ---*/ @@ -3028,31 +2964,91 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { } -int CRadialBasisFunction::PruneSmallCoefficients(passivedouble tolerance, - su2passivevector& coeffs) const { +void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, + su2double radius, const su2activematrix& coords, int& nPolynomial, + vector& keepPolynomialRow, su2passivematrix& C_inv_trunc) const { - /*--- Determine the pruning threshold. ---*/ - passivedouble thresh = 0.0; - for (auto i = 0ul; i < coeffs.size(); ++i) - thresh = max(thresh, fabs(coeffs(i))); - thresh *= tolerance; + const su2double interfaceCoordTol = 1e6 * numeric_limits::epsilon(); - /*--- Prune and count non-zeros. ---*/ - int numNonZeros = 0; - passivedouble coeffSum = 0.0; - for (auto i = 0ul; i < coeffs.size(); ++i) { - if (fabs(coeffs(i)) > thresh) { - coeffSum += coeffs(i); - ++numNonZeros; + const auto nVertexDonor = coords.rows(); + const int nDim = coords.cols(); + + /*--- Populate interpolation kernel. ---*/ + CSymmetricMatrix global_M(nVertexDonor); + + for (auto iVertex = 0ul; iVertex < nVertexDonor; ++iVertex) + for (auto 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 (auto iVertex = 0ul; 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)); } - else coeffs(i) = 0.0; + + /*--- Check if points lie on a plane and remove one coordinate from P if so. ---*/ + nPolynomial = CheckPolynomialTerms(interfaceCoordTol, keepPolynomialRow, P); + + /*--- Compute Mp = (P * M^-1 * P^T)^-1 ---*/ + CSymmetricMatrix Mp(nPolynomial+1); + + su2passivematrix tmp; + global_M.MatMatMult('R', P, tmp); // tmp = P * M^-1 + + for (int i = 0; i <= nPolynomial; ++i) // Mp = tmp * P + for (int j = i; j <= nPolynomial; ++j) { + Mp(i,j) = 0.0; + for (auto k = 0ul; k < nVertexDonor; ++k) Mp(i,j) += tmp(i,k) * P(j,k); + } + Mp.Invert(false); // Mp = Mp^-1 + + /*--- Compute M_p * P * M^-1, the top part of C_inv_trunc. ---*/ + Mp.MatMatMult('L', P, tmp); + su2passivematrix C_inv_top; + global_M.MatMatMult('R', tmp, C_inv_top); + + /*--- Compute tmp = (I - P^T * M_p * P * M^-1), part of the bottom part of + C_inv_trunc. Note that most of the product is known from the top part. ---*/ + tmp.resize(nVertexDonor, nVertexDonor); + + for (auto i = 0ul; i < nVertexDonor; ++i) { + for (auto j = 0ul; j < nVertexDonor; ++j) { + tmp(i,j) = 0.0; + for (int k = 0; k <= nPolynomial; ++k) tmp(i,j) -= P(k,i) * C_inv_top(k,j); + } + tmp(i,i) += 1.0; // identity part + } + + /*--- Compute M^-1 * (I - P^T * M_p * P * M^-1), finalize bottom of C_inv_trunc. ---*/ + global_M.MatMatMult('L', tmp, C_inv_trunc); + + /*--- Merge top and bottom of C_inv_trunc. ---*/ + tmp = move(C_inv_trunc); + 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], tmp.data(), tmp.size()*sizeof(passivedouble)); } + else { + /*--- No polynomial term used in the interpolation, C_inv_trunc = M^-1. ---*/ - /*--- Correct remaining coefficients, sum must be 1 for conservation. ---*/ - passivedouble correction = 1.0 / coeffSum; - for (auto i = 0ul; i < coeffs.size(); ++i) coeffs(i) *= correction; + C_inv_trunc.resize(nVertexDonor, nVertexDonor); + for (auto i = 0ul; i < nVertexDonor; ++i) + for (auto j = 0ul; j < nVertexDonor; ++j) + C_inv_trunc(i,j) = global_M(i,j); + + } // end usePolynomial - return numNonZeros; } int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, @@ -3125,34 +3121,31 @@ int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector::min()) rbf = 0.0; - else rbf *= rbf*log(rbf); - break; + /*--- Determine the pruning threshold. ---*/ + passivedouble thresh = 0.0; + for (auto i = 0ul; i < coeffs.size(); ++i) + thresh = max(thresh, fabs(coeffs(i))); + thresh *= tolerance; - case MULTI_QUADRIC: - case INV_MULTI_QUADRIC: - rbf = sqrt(1.0+rbf*rbf); - if(type == INV_MULTI_QUADRIC) rbf = 1.0/rbf; - break; + /*--- Prune and count non-zeros. ---*/ + int numNonZeros = 0; + passivedouble coeffSum = 0.0; + for (auto i = 0ul; i < coeffs.size(); ++i) { + if (fabs(coeffs(i)) > thresh) { + coeffSum += coeffs(i); + ++numNonZeros; + } + else coeffs(i) = 0.0; } - return rbf; + /*--- Correct remaining coefficients, sum must be 1 for conservation. ---*/ + passivedouble correction = 1.0 / coeffSum; + for (auto i = 0ul; i < coeffs.size(); ++i) coeffs(i) *= correction; + + return numNonZeros; } void CSymmetricMatrix::Initialize(int N) From 9185ca91546b277162dbe5e8bdf9f43d41d76ec4 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 18 Mar 2020 10:32:18 +0000 Subject: [PATCH 08/54] hybrid parallel nearest neighbor search --- Common/include/interpolation_structure.hpp | 34 +++-- Common/src/interpolation_structure.cpp | 154 +++++++++------------ 2 files changed, 84 insertions(+), 104 deletions(-) diff --git a/Common/include/interpolation_structure.hpp b/Common/include/interpolation_structure.hpp index bffcc34bf78d..7728dcc8180c 100644 --- a/Common/include/interpolation_structure.hpp +++ b/Common/include/interpolation_structure.hpp @@ -142,18 +142,28 @@ class CInterpolator { * \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 + */ + inline su2double PointsSquareDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) const { + 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 - * \param[in] point_i + * \param[in] point_i - coordinates of point i + * \param[in] point_j - coordinates of point j */ inline su2double PointsDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) const { - su2double m = 0; - for(unsigned short iDim = 0; iDim < nDim; iDim++) - m += pow(point_j[iDim] - point_i[iDim], 2); - return sqrt(m); + return sqrt(PointsSquareDistance(nDim, point_i, point_j)); } /*! @@ -188,9 +198,8 @@ class CInterpolator { /*! * \brief Nearest Neighbor interpolation */ -class CNearestNeighbor : public CInterpolator { +class CNearestNeighbor final : public CInterpolator { public: - /*! * \brief Constructor of the class. */ @@ -205,16 +214,11 @@ class CNearestNeighbor : public CInterpolator { */ 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); + void Set_TransferCoeff(CConfig **config) override; }; diff --git a/Common/src/interpolation_structure.cpp b/Common/src/interpolation_structure.cpp index f8dab7563711..762bcb38644c 100644 --- a/Common/src/interpolation_structure.cpp +++ b/Common/src/interpolation_structure.cpp @@ -544,139 +544,114 @@ bool CInterpolator::CheckInterfaceBoundary(int markDonor, int markTarget){ return true; } -/* Nearest Neighbor Interpolator */ -CNearestNeighbor::CNearestNeighbor(void): CInterpolator() { } +CNearestNeighbor::CNearestNeighbor(void): CInterpolator() { } -CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { +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 ---*/ + /*--- 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; + /*--- By definition, one donor point per target point. ---*/ + constexpr auto numDonor = 1; + constexpr auto idxDonor = 0; - unsigned long nVertexDonor, nVertexTarget, Point_Target, jVertex, iVertexTarget; - unsigned long Global_Point_Donor; - long pGlobalPoint = 0; + const su2double eps = numeric_limits::epsilon(); - su2double *Coord_i, *Coord_j, dist, mindist, maxdist; - - /*--- Initialize variables --- */ - - nMarkerInt = (int) ( config[donorZone]->GetMarker_n_ZoneInterface() / 2 ); - - nDim = donor_geometry->GetnDim(); + const int nProcessor = size; + const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2; + const auto nDim = donor_geometry->GetnDim(); - iDonor = 0; - + Buffer_Send_nVertex_Donor = new unsigned long [1]; Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; + /*--- Cycle over nMarkersInt interface to determine communication pattern. ---*/ - /*--- Cycle over nMarkersInt interface to determine communication pattern ---*/ + for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { - 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 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 ---*/ - markTarget = Find_InterfaceMarker(config[targetZone], 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; + /*--- 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 ]; + 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 */ + /* 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_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 coordinates and global point indices. ---*/ Collect_VertexInfo( false, markDonor, markTarget, nVertexDonor, nDim ); - /*--- Compute the closest point to a Near-Field boundary point ---*/ - maxdist = 0.0; + /*--- Compute the closest donor point to each target. ---*/ + SU2_OMP_PARALLEL_(for schedule(dynamic,roundUpDiv(nVertexTarget,2*omp_get_max_threads()))) + for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget++) { - for (iVertexTarget = 0; iVertexTarget < nVertexTarget; iVertexTarget++) { + auto target_vertex = target_geometry->vertex[markTarget][iVertexTarget]; + const auto Point_Target = target_vertex->GetNode(); - Point_Target = target_geometry->vertex[markTarget][iVertexTarget]->GetNode(); + if (!target_geometry->node[Point_Target]->GetDomain()) continue; - if ( target_geometry->node[Point_Target]->GetDomain() ) { + /*--- Coordinates of the target point. ---*/ + const su2double* Coord_i = target_geometry->node[Point_Target]->GetCoord(); - target_geometry->vertex[markTarget][iVertexTarget]->SetnDonorPoints(1); - target_geometry->vertex[markTarget][iVertexTarget]->Allocate_DonorInfo(); // Possible meme leak? + su2double mindist = 1e20; + long pGlobalPoint = 0; + int pProcessor = 0; - /*--- Coordinates of the boundary point ---*/ - Coord_i = target_geometry->node[Point_Target]->GetCoord(); + for (int iProcessor = 0; iProcessor < nProcessor; ++iProcessor) { + for (auto jVertex = 0ul; jVertex < Buffer_Receive_nVertex_Donor[iProcessor]; ++jVertex) { - mindist = 1E6; - pProcessor = 0; + const auto idx = iProcessor*MaxLocalVertex_Donor + jVertex; - /*--- Loop over all the boundaries to find the pair ---*/ + const su2double* Coord_j = &Buffer_Receive_Coord[idx*nDim]; - 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(nDim, Coord_i, Coord_j); - - if (dist < mindist) { - mindist = dist; pProcessor = iProcessor; - pGlobalPoint = Buffer_Receive_GlobalPoint[Global_Point_Donor]; - } - - if (dist == 0.0) break; - } + const auto dist = PointsSquareDistance(nDim, Coord_i, Coord_j); + + if (dist < mindist) { + mindist = dist; + pProcessor = iProcessor; + pGlobalPoint = Buffer_Receive_GlobalPoint[idx]; } - } - /*--- 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); + /*--- Test for "exact" match. ---*/ + if (dist < eps) break; + } } + + /*--- Store matching pair. ---*/ + target_vertex->SetnDonorPoints(numDonor); + target_vertex->Allocate_DonorInfo(); + target_vertex->SetInterpDonorPoint(idxDonor, pGlobalPoint); + target_vertex->SetInterpDonorProcessor(idxDonor, pProcessor); + target_vertex->SetDonorCoeff(idxDonor, 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_Send_nVertex_Donor; delete[] Buffer_Receive_nVertex_Donor; + } @@ -2883,7 +2858,8 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { } #endif - /*--- Compute H matrix, distributing target points over the threads in the rank. ---*/ + /*--- Compute H (interpolation) matrix, distributing + * target points over the threads in the rank. ---*/ SU2_OMP_PARALLEL { su2passivevector coeff_vec(nGlobalVertexDonor); From b7223b2c9efe964059c6bd6606f5a493494ff8da Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 18 Mar 2020 11:49:35 +0000 Subject: [PATCH 09/54] general cleanup of all interpolation classes --- Common/include/interpolation_structure.hpp | 113 +-- Common/src/interpolation_structure.cpp | 985 +++++++++------------ 2 files changed, 449 insertions(+), 649 deletions(-) diff --git a/Common/include/interpolation_structure.hpp b/Common/include/interpolation_structure.hpp index 7728dcc8180c..3f8a01cba646 100644 --- a/Common/include/interpolation_structure.hpp +++ b/Common/include/interpolation_structure.hpp @@ -50,11 +50,10 @@ using namespace std; */ 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 */ + 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*/ @@ -77,7 +76,7 @@ class CInterpolator { *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*/ + 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*/ @@ -98,18 +97,12 @@ class CInterpolator { 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. */ + CGeometry**** const Geometry; /*! \brief Vector which stores n zones of geometry. */ + CGeometry* const donor_geometry; /*! \brief Vector which stores the donor geometry. */ + CGeometry* const target_geometry; /*! \brief Vector which stores the target geometry. */ /*! - * \brief Constructor of the class. - */ - CInterpolator(void); - - /*! - * \brief Constructor of the class. + * \brief Constructor of the class, protected as it does not make sense to instantiate base 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 @@ -117,25 +110,39 @@ class CInterpolator { */ CInterpolator(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); +public: + /*! + * \brief No default construction allowed. + */ + CInterpolator(void) = delete; + /*! - * \brief Destructor of the class. + * \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 should implement. + * \param[in] config - Definition of the particular problem. */ - virtual ~CInterpolator(void); + inline virtual void Set_TransferCoeff(CConfig **config) {} +protected: /*! * \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); + int Find_InterfaceMarker(const CConfig *config, unsigned short val_marker_interface) const; /*! - * \brief Check whether the interface should be processed or not + * \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. */ - bool CheckInterfaceBoundary(int val_markDonor, int val_markTarget); - + bool CheckInterfaceBoundary(int val_markDonor, int val_markTarget) const; + /*! * \brief Recontstruct the boundary connectivity from parallel partitioning and broadcasts it to all threads * \param[in] val_zone - index of the zone @@ -166,12 +173,6 @@ class CInterpolator { return sqrt(PointsSquareDistance(nDim, point_i, 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. @@ -200,11 +201,6 @@ class CInterpolator { */ class CNearestNeighbor final : public CInterpolator { public: - /*! - * \brief Constructor of the class. - */ - CNearestNeighbor(void); - /*! * \brief Constructor of the class. * \param[in] geometry - Geometrical definition of the problem. @@ -225,9 +221,8 @@ class CNearestNeighbor final : public CInterpolator { /*! * \brief Isoparametric interpolation */ -class CIsoparametric : public CInterpolator { +class CIsoparametric final : public CInterpolator { public: - /*! * \brief Constructor of the class. * \param[in] geometry - Geometrical definition of the problem. @@ -237,17 +232,13 @@ class CIsoparametric : public CInterpolator { */ 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); + void Set_TransferCoeff(CConfig **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. @@ -258,7 +249,7 @@ class CIsoparametric : public CInterpolator { * \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 + * \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) */ @@ -270,40 +261,31 @@ class CIsoparametric : public CInterpolator { * \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 { +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 - * - * 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); - + void Set_TransferCoeff(CConfig **config) override; + }; /*! * \brief Sliding mesh approach */ -class CSlidingMesh : public CInterpolator { +class CSlidingMesh final : public CInterpolator { public: - /*! * \brief Constructor of the class. * \param[in] geometry - Geometrical definition of the problem. @@ -313,17 +295,13 @@ class CSlidingMesh : public CInterpolator { */ 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); - + void Set_TransferCoeff(CConfig **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 @@ -333,7 +311,8 @@ class CSlidingMesh : public CInterpolator { * \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); + 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 @@ -387,6 +366,7 @@ class CSlidingMesh : public CInterpolator { * \param[in] T3 - third point of triangle T */ bool CheckPointInsideTriangle(su2double* Point, su2double* T1, su2double* T2, su2double* T3); + }; /*! @@ -394,11 +374,6 @@ class CSlidingMesh : public CInterpolator { */ class CRadialBasisFunction final : public CInterpolator { public: - /*! - * \brief Constructor of the class. - */ - CRadialBasisFunction(void); - /*! * \brief Constructor of the class. * \param[in] geometry - Geometrical definition of the problem. diff --git a/Common/src/interpolation_structure.cpp b/Common/src/interpolation_structure.cpp index 762bcb38644c..04f210167e2f 100644 --- a/Common/src/interpolation_structure.cpp +++ b/Common/src/interpolation_structure.cpp @@ -41,81 +41,23 @@ extern "C" void dsymm_(char*, char*, int*, int*, passivedouble*, passivedouble*, 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) : + 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]) { -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; + /*--- Initialize transfer coefficients between the zones. ---*/ + Set_TransferCoeff(config); } -inline void CInterpolator::Set_TransferCoeff(CConfig **config) { } +void CInterpolator::Determine_ArraySize(bool faces, int markDonor, int markTarget, + unsigned long nVertexDonor, unsigned short nDim) { -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 */ @@ -127,49 +69,51 @@ void CInterpolator::Determine_ArraySize(bool faces, int markDonor, int markTarge 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; - } - } + + 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)); } - } - 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; - } + 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; @@ -180,34 +124,33 @@ void CInterpolator::Determine_ArraySize(bool faces, int markDonor, int markTarge /*--- 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); + 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); + 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) -{ +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 */ - const su2double *Normal = nullptr; - - 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; - } - } + 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; @@ -220,7 +163,7 @@ void CInterpolator::Collect_VertexInfo(bool faces, int markDonor, int markTarget Buffer_Send_Coord[nLocalVertex_Donor*nDim+iDim] = donor_geometry->node[iPointDonor]->GetCoord(iDim); if (faces) { - Normal = donor_geometry->vertex[markDonor][iVertexDonor]->GetNormal(); + const su2double* Normal = donor_geometry->vertex[markDonor][iVertexDonor]->GetNormal(); for (iDim = 0; iDim < nDim; iDim++) Buffer_Send_Normal[nLocalVertex_Donor*nDim+iDim] = Normal[iDim]; } @@ -230,32 +173,25 @@ void CInterpolator::Collect_VertexInfo(bool faces, int markDonor, int markTarget 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); + 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); + SU2_MPI::Allgather(Buffer_Send_Normal, nBuffer_Coord, MPI_DOUBLE, + Buffer_Receive_Normal, nBuffer_Coord, MPI_DOUBLE, MPI_COMM_WORLD); } } -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 ) { +int CInterpolator::Find_InterfaceMarker(const CConfig *config, unsigned short val_marker_interface) const { - /*--- We have identified the identifier for the interface marker ---*/ - return iMarker; - } + 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; } - void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ CGeometry *geom = Geometry[val_zone][INST_0][MESH_0]; @@ -364,13 +300,8 @@ void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ /*--- 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 ]; @@ -472,86 +403,34 @@ void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ } } -#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::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); - SU2_MPI::Gather(&markTarget, 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, 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; - 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 +bool CInterpolator::CheckInterfaceBoundary(int markDonor, int markTarget) const { - if(Target_check == -1 || Donor_check == -1) - return false; - else - return true; + /*--- 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); } -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); -} + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { } void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { @@ -656,18 +535,11 @@ void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { -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() {} +CIsoparametric::CIsoparametric(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { } void CIsoparametric::Set_TransferCoeff(CConfig **config) { + unsigned long iVertex, jVertex; unsigned long dPoint, inode, jElem, nElem; unsigned short iDim, iDonor=0, iFace; @@ -719,14 +591,13 @@ void CIsoparametric::Set_TransferCoeff(CConfig **config) { */ /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ - markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); + 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(!CheckInterfaceBoundary(markDonor, markTarget)) continue; if(markDonor != -1) nVertexDonor = donor_geometry->GetnVertex( markDonor ); @@ -863,118 +734,112 @@ void CIsoparametric::Set_TransferCoeff(CConfig **config) { } //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()) { + 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++) { + 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]; + nFaces = (unsigned int)Buffer_Receive_nFace_Donor[iProcessor]; - for (iFace = 0; iFace< nFaces; iFace++) { - /*--- ---*/ + 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]; + 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]->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]); - } + /*--- Set the appropriate amount of memory and fill ---*/ + nNodes =target_geometry->vertex[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; @@ -1008,7 +873,7 @@ void CIsoparametric::Set_TransferCoeff(CConfig **config) { } void CIsoparametric::Isoparameters(unsigned short nDim, unsigned short nDonor, - su2double *X, su2double *xj, su2double *isoparams) { + su2double *X, su2double *xj, su2double *isoparams) { short iDonor,iDim,k; // indices su2double tmp, tmp2; @@ -1191,7 +1056,7 @@ void CIsoparametric::Isoparameters(unsigned short nDim, unsigned short nDonor, delete [] Q; delete [] R; delete [] A; - if (A2 != NULL) delete [] A2; + delete [] A2; delete [] x2; delete [] test; @@ -1199,16 +1064,8 @@ void CIsoparametric::Isoparameters(unsigned short nDim, unsigned short nDonor, } - -/* 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() {} +CMirror::CMirror(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { } void CMirror::Set_TransferCoeff(CConfig **config) { unsigned long iVertex, jVertex; @@ -1241,9 +1098,9 @@ void CMirror::Set_TransferCoeff(CConfig **config) { /*--- 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 + * - 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 ---*/ @@ -1253,8 +1110,7 @@ void CMirror::Set_TransferCoeff(CConfig **config) { 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(!CheckInterfaceBoundary(markDonor, markTarget)) continue; if(markDonor != -1) nVertexDonor = donor_geometry->GetnVertex( markDonor ); @@ -1348,21 +1204,15 @@ void CMirror::Set_TransferCoeff(CConfig **config) { } } -#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; iFaceGetnVertex( markTarget ); @@ -1816,8 +1657,8 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config){ 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]->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]); } } @@ -1842,266 +1683,264 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config){ target_iPoint = target_geometry->vertex[markTarget][iVertex]->GetNode(); - if (target_geometry->node[target_iPoint]->GetDomain()){ + if (!target_geometry->node[target_iPoint]->GetDomain()) continue; - Coord_i = target_geometry->node[target_iPoint]->GetCoord(); + 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; + target_geometry->vertex[markTarget][iVertex]->GetNormal(Normal); - 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 ---*/ + /*--- 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); - nEdges_target = Target_nLinkedNodes[target_iPoint]; + 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); + 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]; - 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); + 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 ---*/ + /*--- 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 ]; + mindist = 1E6; + donor_StartIndex = 0; - dist = PointsDistance(nDim, Coord_i, Coord_j); + for (donor_iPoint = 0; donor_iPoint < nGlobalVertex_Donor; donor_iPoint++) { + + Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ]; - if (dist < mindist) { - mindist = dist; - donor_StartIndex = donor_iPoint; - } + dist = PointsDistance(nDim, Coord_i, Coord_j); - if (dist == 0.0){ - donor_StartIndex = donor_iPoint; - break; - } + if (dist < mindist) { + mindist = dist; + donor_StartIndex = donor_iPoint; } - - donor_iPoint = donor_StartIndex; - nEdges_donor = Donor_nLinkedNodes[donor_iPoint]; + if (dist == 0.0){ + donor_StartIndex = donor_iPoint; + break; + } + } + + donor_iPoint = donor_StartIndex; - donor_element = new su2double*[ 2*nEdges_donor + 2 ]; - for (ii = 0; ii < 2*nEdges_donor + 2; ii++) - donor_element[ii] = new su2double[nDim]; + nEdges_donor = Donor_nLinkedNodes[donor_iPoint]; - nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, DonorPoint_Coord, donor_iPoint, donor_element); + donor_element = new su2double*[ 2*nEdges_donor + 2 ]; + for (ii = 0; ii < 2*nEdges_donor + 2; ii++) + donor_element[ii] = new su2double[nDim]; - 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; - } + 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; + for (ii = 0; ii < 2*nEdges_donor + 2; ii++) + delete [] donor_element[ii]; + delete [] donor_element; - nDonorPoints = 1; + nDonorPoints = 1; - /*--- In case the element intersect the target cell update the auxiliary communication data structure ---*/ + /*--- 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 = 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]; + Coeff_Vect[0] = Area; + Donor_Vect[0] = donor_iPoint; + storeProc[0] = Donor_Proc[donor_iPoint]; - alreadyVisitedDonor = new unsigned long[1]; + alreadyVisitedDonor = new unsigned long[1]; - alreadyVisitedDonor[0] = donor_iPoint; - nAlreadyVisited = 1; - StartVisited = 0; + alreadyVisitedDonor[0] = donor_iPoint; + nAlreadyVisited = 1; + StartVisited = 0; - Area_old = -1; - - while( Area > Area_old ){ + 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 - */ + /* + * - 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; + Area_old = Area; - ToVisit = NULL; - nToVisit = 0; + ToVisit = NULL; + nToVisit = 0; - for( iNodeVisited = StartVisited; iNodeVisited < nAlreadyVisited; iNodeVisited++ ){ + for( iNodeVisited = StartVisited; iNodeVisited < nAlreadyVisited; iNodeVisited++ ){ - vPoint = alreadyVisitedDonor[ iNodeVisited ]; - - nEdgeVisited = Donor_nLinkedNodes[vPoint]; - - for (iEdgeVisited = 0; iEdgeVisited < nEdgeVisited; iEdgeVisited++){ + vPoint = alreadyVisitedDonor[ iNodeVisited ]; + + nEdgeVisited = Donor_nLinkedNodes[vPoint]; - donor_iPoint = Donor_LinkedNodes[ Donor_StartLinkedNodes[vPoint] + iEdgeVisited]; + for (iEdgeVisited = 0; iEdgeVisited < nEdgeVisited; iEdgeVisited++){ - /*--- Check if the node to visit is already listed in the data structure to avoid double visits ---*/ + donor_iPoint = Donor_LinkedNodes[ Donor_StartLinkedNodes[vPoint] + iEdgeVisited]; - check = 0; + /*--- Check if the node to visit is already listed in the data structure to avoid double visits ---*/ - for( jj = 0; jj < nAlreadyVisited; jj++ ){ - if( donor_iPoint == alreadyVisitedDonor[jj] ){ - check = 1; - break; - } - } + check = 0; - if( check == 0 && ToVisit != NULL){ - for( jj = 0; jj < nToVisit; jj++ ) - if( donor_iPoint == ToVisit[jj] ){ - check = 1; - break; - } + for( jj = 0; jj < nAlreadyVisited; jj++ ){ + if( donor_iPoint == alreadyVisitedDonor[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 ]; + if( check == 0 && ToVisit != NULL){ + for( jj = 0; jj < nToVisit; jj++ ) + if( donor_iPoint == ToVisit[jj] ){ + check = 1; + break; + } + } - for( jj = 0; jj < nToVisit; jj++ ) - tmpVect[jj] = ToVisit[jj]; - tmpVect[nToVisit] = donor_iPoint; + if( check == 0 ){ + /*--- If the node was not already visited, visit it and list it into data structure ---*/ - if( ToVisit != NULL ) - delete [] ToVisit; - - ToVisit = tmpVect; - tmpVect = NULL; + tmpVect = new unsigned long[ nToVisit + 1 ]; - nToVisit++; + for( jj = 0; jj < nToVisit; jj++ ) + tmpVect[jj] = ToVisit[jj]; + tmpVect[nToVisit] = donor_iPoint; - /*--- Find the value of the intersection area between the current donor element and the target element --- */ + if( ToVisit != NULL ) + delete [] ToVisit; + + ToVisit = tmpVect; + tmpVect = NULL; - nEdges_donor = Donor_nLinkedNodes[donor_iPoint]; + nToVisit++; - donor_element = new su2double*[ 2*nEdges_donor + 2 ]; - for (ii = 0; ii < 2*nEdges_donor + 2; ii++) - donor_element[ii] = new su2double[nDim]; + /*--- Find the value of the intersection area between the current donor element and the target element --- */ - nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, DonorPoint_Coord, donor_iPoint, donor_element); + nEdges_donor = Donor_nLinkedNodes[donor_iPoint]; - 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); + donor_element = new su2double*[ 2*nEdges_donor + 2 ]; + for (ii = 0; ii < 2*nEdges_donor + 2; ii++) + donor_element[ii] = new su2double[nDim]; - 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 ---*/ + nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, DonorPoint_Coord, donor_iPoint, donor_element); - 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]; + 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); - if (Donor_Vect != NULL) {delete [] Donor_Vect; } - if (Coeff_Vect != NULL) {delete [] Coeff_Vect; } - if (storeProc != NULL) {delete [] storeProc; } + for (ii = 0; ii < 2*nEdges_donor + 2; ii++) + delete [] donor_element[ii]; + delete [] donor_element; - Donor_Vect = tmp_Donor_Vect; - Coeff_Vect = tmp_Coeff_Vect; - storeProc = tmp_storeProc; + /*--- In case the element intersect the target cell update the auxiliary communication data structure ---*/ - tmp_Coeff_Vect = NULL; - tmp_Donor_Vect = NULL; - tmp_storeProc = NULL; + tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ]; + tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ]; + tmp_storeProc = new unsigned long[ nDonorPoints + 1 ]; - nDonorPoints++; - - Area += tmp_Area; + 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; - /*--- Update auxiliary data structure ---*/ + nDonorPoints++; - StartVisited = nAlreadyVisited; + Area += tmp_Area; + } + } + } - tmpVect = new unsigned long[ nAlreadyVisited + nToVisit ]; + /*--- Update auxiliary data structure ---*/ - for( jj = 0; jj < nAlreadyVisited; jj++ ) - tmpVect[jj] = alreadyVisitedDonor[jj]; - - for( jj = 0; jj < nToVisit; jj++ ) - tmpVect[ nAlreadyVisited + jj ] = ToVisit[jj]; + StartVisited = nAlreadyVisited; - if( alreadyVisitedDonor != NULL ) - delete [] alreadyVisitedDonor; + tmpVect = new unsigned long[ nAlreadyVisited + nToVisit ]; - alreadyVisitedDonor = tmpVect; + for( jj = 0; jj < nAlreadyVisited; jj++ ) + tmpVect[jj] = alreadyVisitedDonor[jj]; + + for( jj = 0; jj < nToVisit; jj++ ) + tmpVect[ nAlreadyVisited + jj ] = ToVisit[jj]; - nAlreadyVisited += nToVisit; + if( alreadyVisitedDonor != NULL ) + delete [] alreadyVisitedDonor; - delete [] ToVisit; - } + alreadyVisitedDonor = tmpVect; - delete [] alreadyVisitedDonor; - - /*--- Set the communication data structure and copy data from the auxiliary vectors ---*/ + nAlreadyVisited += nToVisit; - target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nDonorPoints); - target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); + delete [] ToVisit; + } - 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 <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 <GetnVertex(mark_donor); From b6da6e930b8bc4cd8c5dd29df89c58379322371e Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 18 Mar 2020 11:50:30 +0000 Subject: [PATCH 10/54] strip trailing spaces --- Common/include/interpolation_structure.hpp | 24 +- Common/src/interpolation_structure.cpp | 376 ++++++++++----------- 2 files changed, 200 insertions(+), 200 deletions(-) diff --git a/Common/include/interpolation_structure.hpp b/Common/include/interpolation_structure.hpp index 3f8a01cba646..115e477ef46b 100644 --- a/Common/include/interpolation_structure.hpp +++ b/Common/include/interpolation_structure.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) @@ -83,13 +83,13 @@ class CInterpolator { *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*/ @@ -140,7 +140,7 @@ class CInterpolator { * \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. - */ + */ bool CheckInterfaceBoundary(int val_markDonor, int val_markTarget) const; /*! @@ -305,15 +305,15 @@ class CSlidingMesh final : public CInterpolator { /*! * \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] 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 @@ -323,7 +323,7 @@ class CSlidingMesh final : public CInterpolator { * \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 @@ -335,7 +335,7 @@ class CSlidingMesh final : public CInterpolator { * \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 @@ -347,7 +347,7 @@ class CSlidingMesh final : public CInterpolator { * \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 @@ -357,7 +357,7 @@ class CSlidingMesh final : public CInterpolator { * \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 @@ -482,7 +482,7 @@ class CSymmetricMatrix{ inline int GetSize() const { return sz; } inline passivedouble Get(int i, int j) const { return val_vec[IdxSym(i,j)]; } - + inline void Set(int i, int j, passivedouble val) { val_vec[IdxSym(i,j)] = val; } inline passivedouble& operator() (int i, int j) { return val_vec[IdxSym(i,j)]; } diff --git a/Common/src/interpolation_structure.cpp b/Common/src/interpolation_structure.cpp index 04f210167e2f..e88bcef4e1f4 100644 --- a/Common/src/interpolation_structure.cpp +++ b/Common/src/interpolation_structure.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) @@ -193,26 +193,26 @@ int CInterpolator::Find_InterfaceMarker(const CConfig *config, unsigned short va } 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 ]; @@ -221,29 +221,29 @@ void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ 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); @@ -264,23 +264,23 @@ void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ Aux_Send_Map[nLocalVertex] = new unsigned long[ nNodes ]; nNodes = 0; - for (jEdge = 0; jEdge < nEdges; jEdge++){ + 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); + dPoint = geom->edge[EdgeIndex]->GetNode(0); - if ( geom->node[dPoint]->GetVertex(val_marker) != -1 ){ + 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; @@ -291,7 +291,7 @@ void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ nLocalLinkedNodes++; } } - + for (iVertex = 0; iVertex < nVertex; iVertex++){ if( Aux_Send_Map[iVertex] != NULL ) delete [] Aux_Send_Map[iVertex]; @@ -306,7 +306,7 @@ void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ 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 ]; @@ -323,21 +323,21 @@ void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ 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); @@ -346,7 +346,7 @@ void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ Buffer_Receive_Proc[ tmp_index + iVertex ] = iRank; Buffer_Receive_StartLinkedNodes[ tmp_index + iVertex ] += tmp_index_2; } - + tmp_index += iTmp; tmp_index_2 += iTmp2; } @@ -354,34 +354,34 @@ void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ 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 +#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++){ @@ -391,13 +391,13 @@ void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ 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--; + jVertex--; } } } @@ -453,7 +453,7 @@ void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { /*--- 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); @@ -487,7 +487,7 @@ void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { /*--- Coordinates of the target point. ---*/ const su2double* Coord_i = target_geometry->node[Point_Target]->GetCoord(); - su2double mindist = 1e20; + su2double mindist = 1e20; long pGlobalPoint = 0; int pProcessor = 0; @@ -502,8 +502,8 @@ void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { if (dist < mindist) { mindist = dist; - pProcessor = iProcessor; - pGlobalPoint = Buffer_Receive_GlobalPoint[idx]; + pProcessor = iProcessor; + pGlobalPoint = Buffer_Receive_GlobalPoint[idx]; } /*--- Test for "exact" match. ---*/ @@ -592,7 +592,7 @@ void CIsoparametric::Set_TransferCoeff(CConfig **config) { /*--- 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); @@ -608,7 +608,7 @@ void CIsoparametric::Set_TransferCoeff(CConfig **config) { 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]; @@ -825,7 +825,7 @@ void CIsoparametric::Set_TransferCoeff(CConfig **config) { storeProc[iDonor] = (int)Buffer_Receive_FaceProc[faceindex+iDonor]; } } - + delete [] X; } } @@ -868,7 +868,7 @@ void CIsoparametric::Set_TransferCoeff(CConfig **config) { } delete [] Coord; delete [] Normal; - + delete [] projected_point; } @@ -876,7 +876,7 @@ 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]; @@ -884,12 +884,12 @@ void CIsoparametric::Isoparameters(unsigned short nDim, unsigned short 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) { @@ -1050,7 +1050,7 @@ void CIsoparametric::Isoparameters(unsigned short nDim, unsigned short nDonor, isoparams[k] = 1.0; } } - + delete [] x; delete [] x_tmp; delete [] Q; @@ -1058,7 +1058,7 @@ void CIsoparametric::Isoparameters(unsigned short nDim, unsigned short nDonor, delete [] A; delete [] A2; delete [] x2; - + delete [] test; delete [] testi; @@ -1279,9 +1279,9 @@ CSlidingMesh::CSlidingMesh(CGeometry ****geometry_container, CConfig **config, u 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 + * 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 */ @@ -1290,19 +1290,19 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { /* --- 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; + su2double *Coeff_Vect, *tmp_Coeff_Vect; /* --- Geometrical variables --- */ @@ -1313,7 +1313,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { /* --- Markers Variables --- */ - unsigned short iMarkerInt, nMarkerInt; + unsigned short iMarkerInt, nMarkerInt; unsigned long iVertex, nVertexTarget; @@ -1325,24 +1325,24 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { unsigned long nEdges_target, nNode_target; unsigned long *Target_nLinkedNodes, *Target_LinkedNodes, *Target_StartLinkedNodes, *target_segment; - unsigned long *Target_Proc; + 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 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(); @@ -1356,11 +1356,11 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { 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 ---*/ @@ -1389,7 +1389,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { /*--- Target boundary ---*/ ReconstructBoundary(targetZone, markTarget); - + nGlobalVertex_Target = nGlobalVertex; TargetPoint_Coord = Buffer_Receive_Coord; @@ -1398,10 +1398,10 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { 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; @@ -1414,22 +1414,22 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { /*--- 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 + * - 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; @@ -1446,34 +1446,34 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { 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; + 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; @@ -1482,7 +1482,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { 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; @@ -1503,7 +1503,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { /*--- 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 ){ @@ -1512,7 +1512,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { } else{ uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_iPoint] ]; - + if( donor_OldiPoint != uptr[0] ){ donor_forward_point = uptr[0]; donor_backward_point = uptr[1]; @@ -1522,12 +1522,12 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { 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; @@ -1539,13 +1539,13 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { 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]; @@ -1555,9 +1555,9 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { 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 (Donor_Vect != NULL) delete [] Donor_Vect; + if (Coeff_Vect != NULL) delete [] Coeff_Vect; if (storeProc != NULL) delete [] storeProc; Donor_Vect = tmp_Donor_Vect; @@ -1569,10 +1569,10 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { nDonorPoints++; } - + if ( Donor_nLinkedNodes[donor_StartIndex] == 2 ){ check = false; - + uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_StartIndex] ]; donor_iPoint = uptr[1]; @@ -1592,7 +1592,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { } else{ uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_iPoint] ]; - + if( donor_OldiPoint != uptr[0] ){ donor_forward_point = uptr[0]; donor_backward_point = uptr[1]; @@ -1607,11 +1607,11 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { 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); @@ -1625,14 +1625,14 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { 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_Coeff_Vect[ nDonorPoints ] = LineIntersectionLength / length; tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint; tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint]; @@ -1646,52 +1646,52 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { 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++ ){ + + 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{ + 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++) + for (iDim = 0; iDim < nDim; iDim++) Area += Normal[iDim]*Normal[iDim]; Area = sqrt(Area); @@ -1700,23 +1700,23 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { 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 ---*/ @@ -1725,29 +1725,29 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { 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; + 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]; + donor_element[ii] = new su2double[nDim]; nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, DonorPoint_Coord, donor_iPoint, donor_element); @@ -1782,10 +1782,10 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { StartVisited = 0; Area_old = -1; - - while( Area > Area_old ){ - /* + 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 */ @@ -1798,7 +1798,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { for( iNodeVisited = StartVisited; iNodeVisited < nAlreadyVisited; iNodeVisited++ ){ vPoint = alreadyVisitedDonor[ iNodeVisited ]; - + nEdgeVisited = Donor_nLinkedNodes[vPoint]; for (iEdgeVisited = 0; iEdgeVisited < nEdgeVisited; iEdgeVisited++){ @@ -1811,7 +1811,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { for( jj = 0; jj < nAlreadyVisited; jj++ ){ if( donor_iPoint == alreadyVisitedDonor[jj] ){ - check = 1; + check = 1; break; } } @@ -1819,12 +1819,12 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { if( check == 0 && ToVisit != NULL){ for( jj = 0; jj < nToVisit; jj++ ) if( donor_iPoint == ToVisit[jj] ){ - check = 1; + check = 1; break; - } + } } - if( check == 0 ){ + if( check == 0 ){ /*--- If the node was not already visited, visit it and list it into data structure ---*/ tmpVect = new unsigned long[ nToVisit + 1 ]; @@ -1835,19 +1835,19 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { if( ToVisit != NULL ) delete [] ToVisit; - + ToVisit = tmpVect; tmpVect = NULL; - nToVisit++; + 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 ]; + donor_element = new su2double*[ 2*nEdges_donor + 2 ]; for (ii = 0; ii < 2*nEdges_donor + 2; ii++) - donor_element[ii] = new su2double[nDim]; + donor_element[ii] = new su2double[nDim]; nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, DonorPoint_Coord, donor_iPoint, donor_element); @@ -1871,8 +1871,8 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor]; tmp_storeProc[iDonor] = storeProc[iDonor]; } - - tmp_Coeff_Vect[ nDonorPoints ] = tmp_Area; + + tmp_Coeff_Vect[ nDonorPoints ] = tmp_Area; tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint; tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint]; @@ -1884,15 +1884,15 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { Coeff_Vect = tmp_Coeff_Vect; storeProc = tmp_storeProc; - tmp_Coeff_Vect = NULL; + tmp_Coeff_Vect = NULL; tmp_Donor_Vect = NULL; tmp_storeProc = NULL; nDonorPoints++; - + Area += tmp_Area; } - } + } } /*--- Update auxiliary data structure ---*/ @@ -1903,18 +1903,18 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { 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; + alreadyVisitedDonor = tmpVect; - nAlreadyVisited += nToVisit; + nAlreadyVisited += nToVisit; - delete [] ToVisit; + delete [] ToVisit; } delete [] alreadyVisitedDonor; @@ -1924,17 +1924,17 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nDonorPoints); target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); - for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){ + 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 <= 0) check++; - return (check == 3); + return (check == 3); } CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, @@ -2914,7 +2914,7 @@ int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector Date: Wed, 18 Mar 2020 14:55:31 +0000 Subject: [PATCH 11/54] split interpolation_structure --- .../interface_interpolation/CInterpolator.hpp | 186 + .../CIsoparametric.hpp | 70 + .../interface_interpolation/CMirror.hpp | 53 + .../CNearestNeighbor.hpp | 51 + .../CRadialBasisFunction.hpp | 159 + .../interface_interpolation/CSlidingMesh.hpp | 117 + Common/include/interpolation_structure.hpp | 498 --- Common/lib/Makefile.am | 7 +- .../interface_interpolation/CInterpolator.cpp | 415 +++ .../CIsoparametric.cpp | 563 +++ .../src/interface_interpolation/CMirror.cpp | 243 ++ .../CNearestNeighbor.cpp | 137 + .../CRadialBasisFunction.cpp | 772 ++++ .../interface_interpolation/CSlidingMesh.cpp | 1255 +++++++ .../src/interface_interpolation/meson.build | 6 + Common/src/interpolation_structure.cpp | 3235 ----------------- Common/src/meson.build | 2 +- SU2_CFD/include/SU2_CFD.hpp | 1 - SU2_CFD/include/drivers/CDriver.hpp | 2 +- SU2_CFD/src/drivers/CDriver.cpp | 8 +- SU2_CFD/src/drivers/CMultizoneDriver.cpp | 4 +- 21 files changed, 4044 insertions(+), 3740 deletions(-) create mode 100644 Common/include/interface_interpolation/CInterpolator.hpp create mode 100644 Common/include/interface_interpolation/CIsoparametric.hpp create mode 100644 Common/include/interface_interpolation/CMirror.hpp create mode 100644 Common/include/interface_interpolation/CNearestNeighbor.hpp create mode 100644 Common/include/interface_interpolation/CRadialBasisFunction.hpp create mode 100644 Common/include/interface_interpolation/CSlidingMesh.hpp delete mode 100644 Common/include/interpolation_structure.hpp create mode 100644 Common/src/interface_interpolation/CInterpolator.cpp create mode 100644 Common/src/interface_interpolation/CIsoparametric.cpp create mode 100644 Common/src/interface_interpolation/CMirror.cpp create mode 100644 Common/src/interface_interpolation/CNearestNeighbor.cpp create mode 100644 Common/src/interface_interpolation/CRadialBasisFunction.cpp create mode 100644 Common/src/interface_interpolation/CSlidingMesh.cpp create mode 100644 Common/src/interface_interpolation/meson.build delete mode 100644 Common/src/interpolation_structure.cpp diff --git a/Common/include/interface_interpolation/CInterpolator.hpp b/Common/include/interface_interpolation/CInterpolator.hpp new file mode 100644 index 000000000000..5144e37ce2bc --- /dev/null +++ b/Common/include/interface_interpolation/CInterpolator.hpp @@ -0,0 +1,186 @@ +/*! + * \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, /*!< \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*/ + + 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, CConfig **config, unsigned int iZone, unsigned int jZone); + + /*! + * \brief No default construction allowed. + */ + 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(CConfig **config) = 0; + +protected: + /*! + * \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(const CConfig *config, unsigned short val_marker_interface) const; + + /*! + * \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. + */ + bool CheckInterfaceBoundary(int val_markDonor, int val_markTarget) const; + + /*! + * \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 + */ + inline su2double PointsSquareDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) const { + 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 + */ + inline su2double PointsDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) const { + 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..250a7814f15d --- /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, CConfig **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(CConfig **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..c53ebc4bd889 --- /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, CConfig **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(CConfig **config) override; + +}; diff --git a/Common/include/interface_interpolation/CNearestNeighbor.hpp b/Common/include/interface_interpolation/CNearestNeighbor.hpp new file mode 100644 index 000000000000..95dcdfdd3e2f --- /dev/null +++ b/Common/include/interface_interpolation/CNearestNeighbor.hpp @@ -0,0 +1,51 @@ +/*! + * \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 interpolation. + */ +class CNearestNeighbor 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 + */ + CNearestNeighbor(CGeometry ****geometry_container, CConfig **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(CConfig **config) override; + +}; diff --git a/Common/include/interface_interpolation/CRadialBasisFunction.hpp b/Common/include/interface_interpolation/CRadialBasisFunction.hpp new file mode 100644 index 000000000000..18b1b2083644 --- /dev/null +++ b/Common/include/interface_interpolation/CRadialBasisFunction.hpp @@ -0,0 +1,159 @@ +/*! + * \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 { +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, CConfig **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(CConfig **config) 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); + +private: + /*! + * \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. + */ + void ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, su2double radius, + const su2activematrix& coords, int& nPolynomial, + vector& keepPolynomialRow, su2passivematrix& C_inv_trunc) const; + + /*! + * \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). + */ + int CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, su2passivematrix &P) const; + + /*! + * \brief 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,out] coeffs - The vector of interpolation coefficients. + * \return Number of non-zero coefficients after pruning. + */ + int PruneSmallCoefficients(passivedouble tolerance, su2passivevector& coeffs) const; + +}; + +/*! + * \brief Helper class used by CRadialBasisFunction to compute the interpolation weights. + * 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 { +private: + enum DecompositionType { NONE, CHOLESKY, LU }; + + vector val_vec, decomp_vec; + vector perm_vec; + int sz = 0; + bool initialized = false; + DecompositionType decomposed = NONE; + + inline void CheckBounds(int i, int j) const { + assert(initialized && "Matrix not initialized."); + assert(i>=0 && i=0 && j. + */ +#pragma once + +#include "CInterpolator.hpp" + +/*! + * \brief Sliding mesh approach. + */ +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, CConfig **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(CConfig **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[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); + +}; diff --git a/Common/include/interpolation_structure.hpp b/Common/include/interpolation_structure.hpp deleted file mode 100644 index 115e477ef46b..000000000000 --- a/Common/include/interpolation_structure.hpp +++ /dev/null @@ -1,498 +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: - 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, /*!< \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*/ - - CGeometry**** const Geometry; /*! \brief Vector which stores n zones of geometry. */ - CGeometry* const donor_geometry; /*! \brief Vector which stores the donor geometry. */ - CGeometry* const target_geometry; /*! \brief Vector which stores the target geometry. */ - - /*! - * \brief Constructor of the class, protected as it does not make sense to instantiate base 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); - -public: - /*! - * \brief No default construction allowed. - */ - 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 should implement. - * \param[in] config - Definition of the particular problem. - */ - inline virtual void Set_TransferCoeff(CConfig **config) {} - -protected: - /*! - * \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(const CConfig *config, unsigned short val_marker_interface) const; - - /*! - * \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. - */ - bool CheckInterfaceBoundary(int val_markDonor, int val_markTarget) const; - - /*! - * \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 - */ - inline su2double PointsSquareDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) const { - 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 - */ - inline su2double PointsDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) const { - 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); - -}; - -/*! - * \brief Nearest Neighbor interpolation - */ -class CNearestNeighbor 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 - */ - CNearestNeighbor(CGeometry ****geometry_container, CConfig **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(CConfig **config) override; - -}; - -/*! - * \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, CConfig **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(CConfig **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, 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 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, CConfig **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(CConfig **config) override; - -}; - -/*! - * \brief Sliding mesh approach - */ -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, CConfig **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(CConfig **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[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 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 - */ - CRadialBasisFunction(CGeometry ****geometry_container, CConfig **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(CConfig **config) 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); - -private: - /*! - * \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. - */ - void ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, su2double radius, - const su2activematrix& coords, int& nPolynomial, - vector& keepPolynomialRow, su2passivematrix& C_inv_trunc) const; - - /*! - * \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). - */ - int CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, su2passivematrix &P) const; - - /*! - * \brief 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,out] coeffs - The vector of interpolation coefficients. - * \return Number of non-zero coefficients after pruning. - */ - int PruneSmallCoefficients(passivedouble tolerance, su2passivevector& coeffs) const; - -}; - -/*! - * \brief Helper class used by CRadialBasisFunction to compute the interpolation weights. - * 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{ -private: - enum DecompositionType { NONE, CHOLESKY, LU }; - - vector val_vec, decomp_vec; - vector perm_vec; - int sz = 0; - bool initialized = false; - DecompositionType decomposed = NONE; - - inline void CheckBounds(int i, int j) const { - assert(initialized && "Matrix not initialized."); - assert(i>=0 && i=0 && j. + */ + +#include "../../include/interface_interpolation/CInterpolator.hpp" +#include "../../include/CConfig.hpp" +#include "../../include/geometry/CGeometry.hpp" + + +CInterpolator::CInterpolator(CGeometry ****geometry_container, CConfig **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) const { + + 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) const { + + /*--- 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..0d42834b6a29 --- /dev/null +++ b/Common/src/interface_interpolation/CIsoparametric.cpp @@ -0,0 +1,563 @@ +/*! + * \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, CConfig **config, unsigned int iZone, + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { + Set_TransferCoeff(config); +} + +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; + + 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 + 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, + 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..67ed3246c628 --- /dev/null +++ b/Common/src/interface_interpolation/CMirror.cpp @@ -0,0 +1,243 @@ +/*! + * \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, CConfig **config, unsigned int iZone, + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { + Set_TransferCoeff(config); +} + +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++; + } + } + + 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 ---*/ + for (iVertex = 0; iVertexvertex[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; + + } +} diff --git a/Common/src/interface_interpolation/CNearestNeighbor.cpp b/Common/src/interface_interpolation/CNearestNeighbor.cpp new file mode 100644 index 000000000000..04843729aa97 --- /dev/null +++ b/Common/src/interface_interpolation/CNearestNeighbor.cpp @@ -0,0 +1,137 @@ +/*! + * \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" + + +CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { + Set_TransferCoeff(config); +} + +void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { + + /*--- By definition, one donor point per target point. ---*/ + constexpr auto numDonor = 1; + constexpr auto idxDonor = 0; + + 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_Send_nVertex_Donor = new unsigned long [1]; + Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; + + /*--- Cycle over nMarkersInt interface to determine communication pattern. ---*/ + + 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); + + 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 ); + + /*--- Compute the closest donor point to each target. ---*/ + SU2_OMP_PARALLEL_(for schedule(dynamic,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(); + + su2double mindist = 1e20; + long pGlobalPoint = 0; + int pProcessor = 0; + + for (int iProcessor = 0; iProcessor < nProcessor; ++iProcessor) { + for (auto jVertex = 0ul; jVertex < Buffer_Receive_nVertex_Donor[iProcessor]; ++jVertex) { + + const auto idx = iProcessor*MaxLocalVertex_Donor + jVertex; + + const su2double* Coord_j = &Buffer_Receive_Coord[idx*nDim]; + + const auto dist = PointsSquareDistance(nDim, Coord_i, Coord_j); + + if (dist < mindist) { + mindist = dist; + pProcessor = iProcessor; + pGlobalPoint = Buffer_Receive_GlobalPoint[idx]; + } + + /*--- Test for "exact" match. ---*/ + if (dist < eps) break; + } + } + + /*--- Store matching pair. ---*/ + target_vertex->SetnDonorPoints(numDonor); + target_vertex->Allocate_DonorInfo(); + target_vertex->SetInterpDonorPoint(idxDonor, pGlobalPoint); + target_vertex->SetInterpDonorProcessor(idxDonor, pProcessor); + target_vertex->SetDonorCoeff(idxDonor, 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; + +} diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp new file mode 100644 index 000000000000..c9ba5b5e9294 --- /dev/null +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -0,0 +1,772 @@ +/*! + * \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" + + +CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { + Set_TransferCoeff(config); +} + +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(CConfig **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_Send_nVertex_Donor = new unsigned long [1]; + 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 mark_donor = Find_InterfaceMarker(config[donorZone], iMarkerInt+1); + + /*--- On the target side: find the tag of the boundary sharing the interface. ---*/ + const auto mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt+1); + + /*--- If the zone does not contain the interface continue to the next pair of markers. ---*/ + if(!CheckInterfaceBoundary(mark_donor,mark_target)) continue; + + unsigned long nVertexDonor = 0; + if(mark_donor != -1) nVertexDonor = donor_geometry->GetnVertex(mark_donor); + + /*--- Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor. ---*/ + Determine_ArraySize(false, mark_donor, mark_target, nVertexDonor, nDim); + + /*--- Compute total number of donor vertices. ---*/ + 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, mark_donor, mark_target, 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; + + } + + /*--- 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. ---*/ + + 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 mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt+1); + unsigned long nVertexTarget = 0; + if(mark_target != -1) nVertexTarget = target_geometry->GetnVertex(mark_target); + + /*--- 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 H (interpolation) matrix, distributing + * target points over the threads in the rank. ---*/ + SU2_OMP_PARALLEL + { + su2passivevector coeff_vec(nGlobalVertexDonor); + + SU2_OMP_FOR_DYN(roundUpDiv(nVertexTarget, 2*omp_get_max_threads())) + for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget++) { + + auto target_vertex = target_geometry->vertex[mark_target][iVertexTarget]; + const auto point_target = target_vertex->GetNode(); + + /*--- If not domain point move to next. ---*/ + if (!target_geometry->node[point_target]->GetDomain()) continue; + + const su2double* coord_i = target_geometry->node[point_target]->GetCoord(); + + /*--- Multiply target vector by C_inv_trunc to obtain the interpolation coefficients. + * The target vector is not stored, we consume its entries immediately to avoid + * strided access to C_inv_trunc (as it is row-major). ---*/ + + /*--- Polynominal part: ---*/ + if (usePolynomial) { + /*--- Constant term. ---*/ + for (auto j = 0ul; j < nGlobalVertexDonor; ++j) + coeff_vec(j) = C_inv_trunc(0,j); + + /*--- Linear terms. ---*/ + for (int iDim = 0, idx = 1; iDim < nDim; ++iDim) { + /*--- Of which one may have been excluded. ---*/ + if (!keepPolynomialRow[iDim]) continue; + for (auto j = 0ul; j < nGlobalVertexDonor; ++j) + coeff_vec(j) += SU2_TYPE::GetValue(coord_i[iDim]) * C_inv_trunc(idx,j); + idx += 1; + } + } + else { + /*--- Initialize vector to zero. ---*/ + for (auto j = 0ul; j < nGlobalVertexDonor; ++j) coeff_vec(j) = 0.0; + } + + /*--- RBF terms: ---*/ + for (auto iVertexDonor = 0ul; iVertexDonor < nGlobalVertexDonor; ++iVertexDonor) { + auto w_ij = SU2_TYPE::GetValue(Get_RadialBasisValue(kindRBF, paramRBF, + PointsDistance(nDim, coord_i, DonorCoord[iVertexDonor]))); + + for (auto j = 0ul; j < nGlobalVertexDonor; ++j) + coeff_vec(j) += w_ij * C_inv_trunc(1+nPolynomial+iVertexDonor, j); + } + + /*--- Prune small coefficients. ---*/ + auto nnz = PruneSmallCoefficients(SU2_TYPE::GetValue(pruneTol), coeff_vec); + + /*--- Allocate and set donor information for this target point. ---*/ + target_vertex->SetnDonorPoints(nnz); + target_vertex->Allocate_DonorInfo(); + + for (unsigned long iVertex = 0, iSet = 0; iVertex < nGlobalVertexDonor; ++iVertex) { + if (fabs(coeff_vec(iVertex)) > 0.0) { + target_vertex->SetInterpDonorProcessor(iSet, DonorProc[iVertex]); + target_vertex->SetInterpDonorPoint(iSet, DonorPoint[iVertex]); + target_vertex->SetDonorCoeff(iSet, coeff_vec(iVertex)); + ++iSet; + } + } + + } // end target vertex loop + } // end SU2_OMP_PARALLEL + + /*--- Delete 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 + + delete[] Buffer_Send_nVertex_Donor; + delete[] Buffer_Receive_nVertex_Donor; + +} + +void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, + su2double radius, const su2activematrix& coords, int& nPolynomial, + vector& keepPolynomialRow, su2passivematrix& C_inv_trunc) const { + + const su2double interfaceCoordTol = 1e6 * numeric_limits::epsilon(); + + const auto nVertexDonor = coords.rows(); + const int nDim = coords.cols(); + + /*--- Populate interpolation kernel. ---*/ + CSymmetricMatrix global_M(nVertexDonor); + + for (auto iVertex = 0ul; iVertex < nVertexDonor; ++iVertex) + for (auto 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 (auto iVertex = 0ul; 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 Mp = (P * M^-1 * P^T)^-1 ---*/ + CSymmetricMatrix Mp(nPolynomial+1); + + su2passivematrix tmp; + global_M.MatMatMult('R', P, tmp); // tmp = P * M^-1 + + for (int i = 0; i <= nPolynomial; ++i) // Mp = tmp * P + for (int j = i; j <= nPolynomial; ++j) { + Mp(i,j) = 0.0; + for (auto k = 0ul; k < nVertexDonor; ++k) Mp(i,j) += tmp(i,k) * P(j,k); + } + Mp.Invert(false); // Mp = Mp^-1 + + /*--- Compute M_p * P * M^-1, the top part of C_inv_trunc. ---*/ + Mp.MatMatMult('L', P, tmp); + su2passivematrix C_inv_top; + global_M.MatMatMult('R', tmp, C_inv_top); + + /*--- Compute tmp = (I - P^T * M_p * P * M^-1), part of the bottom part of + C_inv_trunc. Note that most of the product is known from the top part. ---*/ + tmp.resize(nVertexDonor, nVertexDonor); + + for (auto i = 0ul; i < nVertexDonor; ++i) { + for (auto j = 0ul; j < nVertexDonor; ++j) { + tmp(i,j) = 0.0; + for (int k = 0; k <= nPolynomial; ++k) tmp(i,j) -= P(k,i) * C_inv_top(k,j); + } + tmp(i,i) += 1.0; // identity part + } + + /*--- Compute M^-1 * (I - P^T * M_p * P * M^-1), finalize bottom of C_inv_trunc. ---*/ + global_M.MatMatMult('L', tmp, C_inv_trunc); + + /*--- Merge top and bottom of C_inv_trunc. ---*/ + tmp = move(C_inv_trunc); + 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], tmp.data(), tmp.size()*sizeof(passivedouble)); + } + else { + /*--- No polynomial term used in the interpolation, C_inv_trunc = M^-1. ---*/ + + C_inv_trunc.resize(nVertexDonor, nVertexDonor); + for (auto i = 0ul; i < nVertexDonor; ++i) + for (auto j = 0ul; j < nVertexDonor; ++j) + C_inv_trunc(i,j) = global_M(i,j); + + } // end usePolynomial + +} + +int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, + su2passivematrix &P) const { + 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; + 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 coeff(n_rows,0.0); + + for (int i = 0; i < n_rows; ++i) + for (int j = 0; j < n; ++j) + coeff[i] += P(i+1,j); + + /*--- Multiply the RHS by the inverse thus obtaining the coefficients. ---*/ + PPT.MatVecMult(coeff.data()); + + /*--- 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; +} + +int CRadialBasisFunction::PruneSmallCoefficients(passivedouble tolerance, + su2passivevector& coeffs) const { + + /*--- Determine the pruning threshold. ---*/ + passivedouble thresh = 0.0; + for (auto i = 0ul; i < coeffs.size(); ++i) + thresh = max(thresh, fabs(coeffs(i))); + thresh *= tolerance; + + /*--- Prune and count non-zeros. ---*/ + int numNonZeros = 0; + passivedouble coeffSum = 0.0; + for (auto i = 0ul; i < coeffs.size(); ++i) { + if (fabs(coeffs(i)) > thresh) { + coeffSum += coeffs(i); + ++numNonZeros; + } + else coeffs(i) = 0.0; + } + + /*--- Correct remaining coefficients, sum must be 1 for conservation. ---*/ + passivedouble correction = 1.0 / coeffSum; + for (auto i = 0ul; i < coeffs.size(); ++i) coeffs(i) *= correction; + + return numNonZeros; +} + +void CSymmetricMatrix::Initialize(int N) +{ + sz = N; + val_vec.resize(sz*sz); + initialized = true; +} + +void CSymmetricMatrix::CholeskyDecompose() +{ +#ifndef HAVE_LAPACK + assert(initialized && "Matrix not initialized."); + + for (int j = 0; j < sz; ++j) { + passivedouble sum = 0.0; + for (int k = 0; k < j; ++k) sum -= pow(Get(j,k), 2); + sum += Get(j,j); + assert(sum > 0.0 && "LLT failed, matrix is not SPD."); + Set(j, j, sqrt(sum)); + + for (int i = j+1; i < sz; ++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)); + } + } + decomposed = CHOLESKY; +#endif +} + +void CSymmetricMatrix::LUDecompose() +{ +#ifndef HAVE_LAPACK + assert(initialized && "Matrix not initialized."); + + /*--- Copy matrix values to LU matrix, init permutation vec. ---*/ + decomp_vec.resize(sz*sz); + perm_vec.resize(sz); + for (int i = 0; i < sz; ++i) { + perm_vec[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_vec[j], perm_vec[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); + } + + decomposed = LU; +#endif +} + +void CSymmetricMatrix::CalcInv() +{ +#ifndef HAVE_LAPACK + assert(initialized && "Matrix not initialized."); + + /*--- Decompose matrix if not done yet. ---*/ + if (decomposed == NONE) { LUDecompose(); } + + /*--- Compute inverse from decomposed matrices. ---*/ + switch (decomposed) { + + case CHOLESKY: + { + /*--- Initialize inverse matrix. ---*/ + vector inv(sz*sz, 0.0); + + /*--- Compute L inverse. ---*/ + /*--- Solve smaller and smaller systems. ---*/ + for (int j = 0; j < sz; ++j) { + /*--- Forward substitution. ---*/ + inv[IdxSym(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[IdxSym(k,j)]; + inv[IdxSym(i,j)] = sum / Get(i,i); + } + } // L inverse in inv + + /*--- Multiply inversed matrices overwrite val_vec. ---*/ + 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[IdxSym(k,i)] * inv[IdxSym(k,j)]; + Set(i, j, sum); + } + + break; + } + + case LU: + { + /*--- Alias val_vec. ---*/ + vector& inv = val_vec; + + /*--- Invert L and U matrices in place. ---*/ + for (int j = 0; j < sz; ++j) { + inv[IdxFull(j,j)] = 1.0 / decomp(j,j); + + for (int i = j+1; i < sz; ++i) { + inv[IdxFull(i,j)] = -decomp(i,j); + inv[IdxFull(j,i)] = -decomp(j,i) * inv[IdxFull(j,j)]; + + for (int k = j+1; k < i; ++k) { + inv[IdxFull(i,j)] -= decomp(i,k) * inv[IdxFull(k,j)]; + inv[IdxFull(j,i)] -= decomp(k,i) * inv[IdxFull(j,k)]; + } + if (j+1 <= i) inv[IdxFull(j,i)] /= decomp(i,i); + } + } + // inverses in val_vec + + /*--- 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[IdxFull(i,k)] * ((k==j)? 1.0 : inv[IdxFull(k,j)]); + } + + /*--- Permute multiplied matrix to recover A_inv, overwrite val_vec. ---*/ + for (int j = 0; j < sz; ++j) { + int k = perm_vec[j]; + for (int i = k; i < sz; ++i) Set(i, k, decomp(i,j)); + } + + /*--- Decomposition no longer needed. ---*/ + vector().swap(decomp_vec); + + break; + } + default: assert(false && "Default (LU) decomposition failed."); + } + + decomposed = NONE; +#endif +} + +void CSymmetricMatrix::CalcInv_sytri() +{ +#ifdef HAVE_LAPACK + char uplo = 'L'; + int info; + perm_vec.resize(sz); // ipiv array + + /*--- Query the optimum work size. ---*/ + int query = -1; passivedouble tmp; + dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), &tmp, &query, &info); + query = static_cast(tmp); + decomp_vec.resize(query); // work array + + /*--- Factorize and invert. ---*/ + dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &query, &info); + if (info!=0) SU2_MPI::Error("LDLT factorization failed.", CURRENT_FUNCTION); + dsytri_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &info); + if (info!=0) SU2_MPI::Error("Inversion with LDLT factorization failed.", CURRENT_FUNCTION); + + decomposed = NONE; +#endif +} + +void CSymmetricMatrix::CalcInv_potri() +{ +#ifdef HAVE_LAPACK + char uplo = 'L'; + int info; + + dpotrf_(&uplo, &sz, val_vec.data(), &sz, &info); + if (info!=0) SU2_MPI::Error("LLT factorization failed.", CURRENT_FUNCTION); + dpotri_(&uplo, &sz, val_vec.data(), &sz, &info); + if (info!=0) SU2_MPI::Error("Inversion with LLT factorization failed.", CURRENT_FUNCTION); + + decomposed = NONE; +#endif +} + +void CSymmetricMatrix::Invert(const bool is_spd) +{ +#ifdef HAVE_LAPACK + if(is_spd) CalcInv_potri(); + else CalcInv_sytri(); +#else + if(!is_spd) LUDecompose(); + else CholeskyDecompose(); + CalcInv(); +#endif +} + +void CSymmetricMatrix::MatVecMult(passivedouble *v) const +{ + passivedouble *tmp_res = new passivedouble [sz]; + + for (int i=0; i. + */ + +#include "../../include/interface_interpolation/CSlidingMesh.hpp" +#include "../../include/CConfig.hpp" +#include "../../include/geometry/CGeometry.hpp" + + +CSlidingMesh::CSlidingMesh(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { + Set_TransferCoeff(config); +} + +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 conservation 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(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(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()) 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); + //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; + } +} + +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); +} 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 e88bcef4e1f4..000000000000 --- a/Common/src/interpolation_structure.cpp +++ /dev/null @@ -1,3235 +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 dsytrf_(char*, int*, passivedouble*, int*, int*, passivedouble*, int*, int*); -extern "C" void dsytri_(char*, int*, passivedouble*, int*, int*, passivedouble*, int*); -extern "C" void dpotrf_(char*, int*, passivedouble*, int*, int*); -extern "C" void dpotri_(char*, int*, passivedouble*, int*, int*); -extern "C" void dsymm_(char*, char*, int*, int*, passivedouble*, passivedouble*, int*, - passivedouble*, int*, passivedouble*, passivedouble*, int*); -#endif - -CInterpolator::CInterpolator(CGeometry ****geometry_container, CConfig **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]) { - - /*--- Initialize transfer coefficients between the zones. ---*/ - Set_TransferCoeff(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()) 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); - } -} - -int CInterpolator::Find_InterfaceMarker(const CConfig *config, unsigned short val_marker_interface) const { - - 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; -} - -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; - -} - -bool CInterpolator::CheckInterfaceBoundary(int markDonor, int markTarget) const { - - /*--- 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); -} - -CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, - unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { } - -void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { - - /*--- By definition, one donor point per target point. ---*/ - constexpr auto numDonor = 1; - constexpr auto idxDonor = 0; - - 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_Send_nVertex_Donor = new unsigned long [1]; - Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; - - /*--- Cycle over nMarkersInt interface to determine communication pattern. ---*/ - - 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); - - 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 ); - - /*--- Compute the closest donor point to each target. ---*/ - SU2_OMP_PARALLEL_(for schedule(dynamic,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(); - - su2double mindist = 1e20; - long pGlobalPoint = 0; - int pProcessor = 0; - - for (int iProcessor = 0; iProcessor < nProcessor; ++iProcessor) { - for (auto jVertex = 0ul; jVertex < Buffer_Receive_nVertex_Donor[iProcessor]; ++jVertex) { - - const auto idx = iProcessor*MaxLocalVertex_Donor + jVertex; - - const su2double* Coord_j = &Buffer_Receive_Coord[idx*nDim]; - - const auto dist = PointsSquareDistance(nDim, Coord_i, Coord_j); - - if (dist < mindist) { - mindist = dist; - pProcessor = iProcessor; - pGlobalPoint = Buffer_Receive_GlobalPoint[idx]; - } - - /*--- Test for "exact" match. ---*/ - if (dist < eps) break; - } - } - - /*--- Store matching pair. ---*/ - target_vertex->SetnDonorPoints(numDonor); - target_vertex->Allocate_DonorInfo(); - target_vertex->SetInterpDonorPoint(idxDonor, pGlobalPoint); - target_vertex->SetInterpDonorProcessor(idxDonor, pProcessor); - target_vertex->SetDonorCoeff(idxDonor, 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) { } - -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; - - 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 - 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; - delete [] A2; - delete [] x2; - - delete [] test; - delete [] testi; - -} - -CMirror::CMirror(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, - unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { } - -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++; - } - } - - 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 ---*/ - for (iVertex = 0; iVertexvertex[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) { } - -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(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(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()) 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); - //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; - } -} - -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); -} - -CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, - unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { } - -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(CConfig **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_Send_nVertex_Donor = new unsigned long [1]; - 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 mark_donor = Find_InterfaceMarker(config[donorZone], iMarkerInt+1); - - /*--- On the target side: find the tag of the boundary sharing the interface. ---*/ - const auto mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt+1); - - /*--- If the zone does not contain the interface continue to the next pair of markers. ---*/ - if(!CheckInterfaceBoundary(mark_donor,mark_target)) continue; - - unsigned long nVertexDonor = 0; - if(mark_donor != -1) nVertexDonor = donor_geometry->GetnVertex(mark_donor); - - /*--- Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor. ---*/ - Determine_ArraySize(false, mark_donor, mark_target, nVertexDonor, nDim); - - /*--- Compute total number of donor vertices. ---*/ - 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, mark_donor, mark_target, 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; - - } - - /*--- 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. ---*/ - - 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 mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt+1); - unsigned long nVertexTarget = 0; - if(mark_target != -1) nVertexTarget = target_geometry->GetnVertex(mark_target); - - /*--- 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 H (interpolation) matrix, distributing - * target points over the threads in the rank. ---*/ - SU2_OMP_PARALLEL - { - su2passivevector coeff_vec(nGlobalVertexDonor); - - SU2_OMP_FOR_DYN(roundUpDiv(nVertexTarget, 2*omp_get_max_threads())) - for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget++) { - - auto target_vertex = target_geometry->vertex[mark_target][iVertexTarget]; - const auto point_target = target_vertex->GetNode(); - - /*--- If not domain point move to next. ---*/ - if (!target_geometry->node[point_target]->GetDomain()) continue; - - const su2double* coord_i = target_geometry->node[point_target]->GetCoord(); - - /*--- Multiply target vector by C_inv_trunc to obtain the interpolation coefficients. - * The target vector is not stored, we consume its entries immediately to avoid - * strided access to C_inv_trunc (as it is row-major). ---*/ - - /*--- Polynominal part: ---*/ - if (usePolynomial) { - /*--- Constant term. ---*/ - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) - coeff_vec(j) = C_inv_trunc(0,j); - - /*--- Linear terms. ---*/ - for (int iDim = 0, idx = 1; iDim < nDim; ++iDim) { - /*--- Of which one may have been excluded. ---*/ - if (!keepPolynomialRow[iDim]) continue; - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) - coeff_vec(j) += SU2_TYPE::GetValue(coord_i[iDim]) * C_inv_trunc(idx,j); - idx += 1; - } - } - else { - /*--- Initialize vector to zero. ---*/ - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) coeff_vec(j) = 0.0; - } - - /*--- RBF terms: ---*/ - for (auto iVertexDonor = 0ul; iVertexDonor < nGlobalVertexDonor; ++iVertexDonor) { - auto w_ij = SU2_TYPE::GetValue(Get_RadialBasisValue(kindRBF, paramRBF, - PointsDistance(nDim, coord_i, DonorCoord[iVertexDonor]))); - - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) - coeff_vec(j) += w_ij * C_inv_trunc(1+nPolynomial+iVertexDonor, j); - } - - /*--- Prune small coefficients. ---*/ - auto nnz = PruneSmallCoefficients(SU2_TYPE::GetValue(pruneTol), coeff_vec); - - /*--- Allocate and set donor information for this target point. ---*/ - target_vertex->SetnDonorPoints(nnz); - target_vertex->Allocate_DonorInfo(); - - for (unsigned long iVertex = 0, iSet = 0; iVertex < nGlobalVertexDonor; ++iVertex) { - if (fabs(coeff_vec(iVertex)) > 0.0) { - target_vertex->SetInterpDonorProcessor(iSet, DonorProc[iVertex]); - target_vertex->SetInterpDonorPoint(iSet, DonorPoint[iVertex]); - target_vertex->SetDonorCoeff(iSet, coeff_vec(iVertex)); - ++iSet; - } - } - - } // end target vertex loop - } // end SU2_OMP_PARALLEL - - /*--- Delete 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 - - delete[] Buffer_Send_nVertex_Donor; - delete[] Buffer_Receive_nVertex_Donor; - -} - -void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, - su2double radius, const su2activematrix& coords, int& nPolynomial, - vector& keepPolynomialRow, su2passivematrix& C_inv_trunc) const { - - const su2double interfaceCoordTol = 1e6 * numeric_limits::epsilon(); - - const auto nVertexDonor = coords.rows(); - const int nDim = coords.cols(); - - /*--- Populate interpolation kernel. ---*/ - CSymmetricMatrix global_M(nVertexDonor); - - for (auto iVertex = 0ul; iVertex < nVertexDonor; ++iVertex) - for (auto 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 (auto iVertex = 0ul; 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 Mp = (P * M^-1 * P^T)^-1 ---*/ - CSymmetricMatrix Mp(nPolynomial+1); - - su2passivematrix tmp; - global_M.MatMatMult('R', P, tmp); // tmp = P * M^-1 - - for (int i = 0; i <= nPolynomial; ++i) // Mp = tmp * P - for (int j = i; j <= nPolynomial; ++j) { - Mp(i,j) = 0.0; - for (auto k = 0ul; k < nVertexDonor; ++k) Mp(i,j) += tmp(i,k) * P(j,k); - } - Mp.Invert(false); // Mp = Mp^-1 - - /*--- Compute M_p * P * M^-1, the top part of C_inv_trunc. ---*/ - Mp.MatMatMult('L', P, tmp); - su2passivematrix C_inv_top; - global_M.MatMatMult('R', tmp, C_inv_top); - - /*--- Compute tmp = (I - P^T * M_p * P * M^-1), part of the bottom part of - C_inv_trunc. Note that most of the product is known from the top part. ---*/ - tmp.resize(nVertexDonor, nVertexDonor); - - for (auto i = 0ul; i < nVertexDonor; ++i) { - for (auto j = 0ul; j < nVertexDonor; ++j) { - tmp(i,j) = 0.0; - for (int k = 0; k <= nPolynomial; ++k) tmp(i,j) -= P(k,i) * C_inv_top(k,j); - } - tmp(i,i) += 1.0; // identity part - } - - /*--- Compute M^-1 * (I - P^T * M_p * P * M^-1), finalize bottom of C_inv_trunc. ---*/ - global_M.MatMatMult('L', tmp, C_inv_trunc); - - /*--- Merge top and bottom of C_inv_trunc. ---*/ - tmp = move(C_inv_trunc); - 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], tmp.data(), tmp.size()*sizeof(passivedouble)); - } - else { - /*--- No polynomial term used in the interpolation, C_inv_trunc = M^-1. ---*/ - - C_inv_trunc.resize(nVertexDonor, nVertexDonor); - for (auto i = 0ul; i < nVertexDonor; ++i) - for (auto j = 0ul; j < nVertexDonor; ++j) - C_inv_trunc(i,j) = global_M(i,j); - - } // end usePolynomial - -} - -int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, - su2passivematrix &P) const { - 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; - 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 coeff(n_rows,0.0); - - for (int i = 0; i < n_rows; ++i) - for (int j = 0; j < n; ++j) - coeff[i] += P(i+1,j); - - /*--- Multiply the RHS by the inverse thus obtaining the coefficients. ---*/ - PPT.MatVecMult(coeff.data()); - - /*--- 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; -} - -int CRadialBasisFunction::PruneSmallCoefficients(passivedouble tolerance, - su2passivevector& coeffs) const { - - /*--- Determine the pruning threshold. ---*/ - passivedouble thresh = 0.0; - for (auto i = 0ul; i < coeffs.size(); ++i) - thresh = max(thresh, fabs(coeffs(i))); - thresh *= tolerance; - - /*--- Prune and count non-zeros. ---*/ - int numNonZeros = 0; - passivedouble coeffSum = 0.0; - for (auto i = 0ul; i < coeffs.size(); ++i) { - if (fabs(coeffs(i)) > thresh) { - coeffSum += coeffs(i); - ++numNonZeros; - } - else coeffs(i) = 0.0; - } - - /*--- Correct remaining coefficients, sum must be 1 for conservation. ---*/ - passivedouble correction = 1.0 / coeffSum; - for (auto i = 0ul; i < coeffs.size(); ++i) coeffs(i) *= correction; - - return numNonZeros; -} - -void CSymmetricMatrix::Initialize(int N) -{ - sz = N; - val_vec.resize(sz*sz); - initialized = true; -} - -void CSymmetricMatrix::CholeskyDecompose() -{ -#ifndef HAVE_LAPACK - assert(initialized && "Matrix not initialized."); - - for (int j = 0; j < sz; ++j) { - passivedouble sum = 0.0; - for (int k = 0; k < j; ++k) sum -= pow(Get(j,k), 2); - sum += Get(j,j); - assert(sum > 0.0 && "LLT failed, matrix is not SPD."); - Set(j, j, sqrt(sum)); - - for (int i = j+1; i < sz; ++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)); - } - } - decomposed = CHOLESKY; -#endif -} - -void CSymmetricMatrix::LUDecompose() -{ -#ifndef HAVE_LAPACK - assert(initialized && "Matrix not initialized."); - - /*--- Copy matrix values to LU matrix, init permutation vec. ---*/ - decomp_vec.resize(sz*sz); - perm_vec.resize(sz); - for (int i = 0; i < sz; ++i) { - perm_vec[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_vec[j], perm_vec[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); - } - - decomposed = LU; -#endif -} - -void CSymmetricMatrix::CalcInv() -{ -#ifndef HAVE_LAPACK - assert(initialized && "Matrix not initialized."); - - /*--- Decompose matrix if not done yet. ---*/ - if (decomposed == NONE) { LUDecompose(); } - - /*--- Compute inverse from decomposed matrices. ---*/ - switch (decomposed) { - - case CHOLESKY: - { - /*--- Initialize inverse matrix. ---*/ - vector inv(sz*sz, 0.0); - - /*--- Compute L inverse. ---*/ - /*--- Solve smaller and smaller systems. ---*/ - for (int j = 0; j < sz; ++j) { - /*--- Forward substitution. ---*/ - inv[IdxSym(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[IdxSym(k,j)]; - inv[IdxSym(i,j)] = sum / Get(i,i); - } - } // L inverse in inv - - /*--- Multiply inversed matrices overwrite val_vec. ---*/ - 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[IdxSym(k,i)] * inv[IdxSym(k,j)]; - Set(i, j, sum); - } - - break; - } - - case LU: - { - /*--- Alias val_vec. ---*/ - vector& inv = val_vec; - - /*--- Invert L and U matrices in place. ---*/ - for (int j = 0; j < sz; ++j) { - inv[IdxFull(j,j)] = 1.0 / decomp(j,j); - - for (int i = j+1; i < sz; ++i) { - inv[IdxFull(i,j)] = -decomp(i,j); - inv[IdxFull(j,i)] = -decomp(j,i) * inv[IdxFull(j,j)]; - - for (int k = j+1; k < i; ++k) { - inv[IdxFull(i,j)] -= decomp(i,k) * inv[IdxFull(k,j)]; - inv[IdxFull(j,i)] -= decomp(k,i) * inv[IdxFull(j,k)]; - } - if (j+1 <= i) inv[IdxFull(j,i)] /= decomp(i,i); - } - } - // inverses in val_vec - - /*--- 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[IdxFull(i,k)] * ((k==j)? 1.0 : inv[IdxFull(k,j)]); - } - - /*--- Permute multiplied matrix to recover A_inv, overwrite val_vec. ---*/ - for (int j = 0; j < sz; ++j) { - int k = perm_vec[j]; - for (int i = k; i < sz; ++i) Set(i, k, decomp(i,j)); - } - - /*--- Decomposition no longer needed. ---*/ - vector().swap(decomp_vec); - - break; - } - default: assert(false && "Default (LU) decomposition failed."); - } - - decomposed = NONE; -#endif -} - -void CSymmetricMatrix::CalcInv_sytri() -{ -#ifdef HAVE_LAPACK - char uplo = 'L'; - int info; - perm_vec.resize(sz); // ipiv array - - /*--- Query the optimum work size. ---*/ - int query = -1; passivedouble tmp; - dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), &tmp, &query, &info); - query = static_cast(tmp); - decomp_vec.resize(query); // work array - - /*--- Factorize and invert. ---*/ - dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &query, &info); - if (info!=0) SU2_MPI::Error("LDLT factorization failed.", CURRENT_FUNCTION); - dsytri_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &info); - if (info!=0) SU2_MPI::Error("Inversion with LDLT factorization failed.", CURRENT_FUNCTION); - - decomposed = NONE; -#endif -} - -void CSymmetricMatrix::CalcInv_potri() -{ -#ifdef HAVE_LAPACK - char uplo = 'L'; - int info; - - dpotrf_(&uplo, &sz, val_vec.data(), &sz, &info); - if (info!=0) SU2_MPI::Error("LLT factorization failed.", CURRENT_FUNCTION); - dpotri_(&uplo, &sz, val_vec.data(), &sz, &info); - if (info!=0) SU2_MPI::Error("Inversion with LLT factorization failed.", CURRENT_FUNCTION); - - decomposed = NONE; -#endif -} - -void CSymmetricMatrix::Invert(const bool is_spd) -{ -#ifdef HAVE_LAPACK - if(is_spd) CalcInv_potri(); - else CalcInv_sytri(); -#else - if(!is_spd) LUDecompose(); - else CholeskyDecompose(); - CalcInv(); -#endif -} - -void CSymmetricMatrix::MatVecMult(passivedouble *v) const -{ - passivedouble *tmp_res = new passivedouble [sz]; - - for (int i=0; i Date: Wed, 18 Mar 2020 15:34:26 +0000 Subject: [PATCH 12/54] cleanup interface preprocessing --- .../interface_interpolation/CInterpolator.hpp | 48 +- .../interface_interpolation/CInterpolator.cpp | 4 +- .../CIsoparametric.cpp | 34 +- .../CRadialBasisFunction.cpp | 16 + .../interface_interpolation/CSlidingMesh.cpp | 72 +-- SU2_CFD/src/drivers/CDriver.cpp | 411 +++++++----------- SU2_CFD/src/drivers/CMultizoneDriver.cpp | 8 +- 7 files changed, 271 insertions(+), 322 deletions(-) diff --git a/Common/include/interface_interpolation/CInterpolator.hpp b/Common/include/interface_interpolation/CInterpolator.hpp index 5144e37ce2bc..74e4840d5d02 100644 --- a/Common/include/interface_interpolation/CInterpolator.hpp +++ b/Common/include/interface_interpolation/CInterpolator.hpp @@ -45,11 +45,11 @@ class CInterpolator { 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*/ + 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 */ @@ -68,23 +68,25 @@ class CInterpolator { 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*/ + 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 + *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*/ + 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. */ @@ -117,21 +119,21 @@ class CInterpolator { */ virtual void Set_TransferCoeff(CConfig **config) = 0; -protected: /*! * \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(const CConfig *config, unsigned short val_marker_interface) const; + 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. */ - bool CheckInterfaceBoundary(int val_markDonor, int val_markTarget) const; + 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 diff --git a/Common/src/interface_interpolation/CInterpolator.cpp b/Common/src/interface_interpolation/CInterpolator.cpp index 354336c35ade..05d54a74fbdf 100644 --- a/Common/src/interface_interpolation/CInterpolator.cpp +++ b/Common/src/interface_interpolation/CInterpolator.cpp @@ -40,7 +40,7 @@ CInterpolator::CInterpolator(CGeometry ****geometry_container, CConfig **config, target_geometry(geometry_container[jZone][INST_0][MESH_0]) { } -int CInterpolator::Find_InterfaceMarker(const CConfig *config, unsigned short val_marker_interface) const { +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. ---*/ @@ -49,7 +49,7 @@ int CInterpolator::Find_InterfaceMarker(const CConfig *config, unsigned short va return -1; } -bool CInterpolator::CheckInterfaceBoundary(int markDonor, int markTarget) const { +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. ---*/ diff --git a/Common/src/interface_interpolation/CIsoparametric.cpp b/Common/src/interface_interpolation/CIsoparametric.cpp index 0d42834b6a29..ef3468b86fb3 100644 --- a/Common/src/interface_interpolation/CIsoparametric.cpp +++ b/Common/src/interface_interpolation/CIsoparametric.cpp @@ -77,6 +77,14 @@ void CIsoparametric::Set_TransferCoeff(CConfig **config) { Coord = new su2double[nDim]; Normal = new su2double[nDim]; + 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]; + nMarkerInt = (config[donorZone]->GetMarker_n_ZoneInterface())/2; /*--- For the number of markers on the interface... ---*/ @@ -106,14 +114,6 @@ void CIsoparametric::Set_TransferCoeff(CConfig **config) { 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); @@ -332,21 +332,12 @@ void CIsoparametric::Set_TransferCoeff(CConfig **config) { 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; @@ -363,6 +354,15 @@ void CIsoparametric::Set_TransferCoeff(CConfig **config) { delete[] Buffer_Receive_FaceNodes; delete[] Buffer_Receive_FaceProc; } + + 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 [] Coord; delete [] Normal; diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp index c9ba5b5e9294..fd5504ed8c8b 100644 --- a/Common/src/interface_interpolation/CRadialBasisFunction.cpp +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -29,6 +29,22 @@ #include "../../include/CConfig.hpp" #include "../../include/geometry/CGeometry.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 dsytrf_(char*, int*, passivedouble*, int*, int*, passivedouble*, int*, int*); +extern "C" void dsytri_(char*, int*, passivedouble*, int*, int*, passivedouble*, int*); +extern "C" void dpotrf_(char*, int*, passivedouble*, int*, int*); +extern "C" void dpotri_(char*, int*, passivedouble*, int*, int*); +extern "C" void dsymm_(char*, char*, int*, int*, passivedouble*, passivedouble*, int*, + passivedouble*, int*, passivedouble*, passivedouble*, int*); +#endif CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { diff --git a/Common/src/interface_interpolation/CSlidingMesh.cpp b/Common/src/interface_interpolation/CSlidingMesh.cpp index 41018ddda0be..62084eb55afd 100644 --- a/Common/src/interface_interpolation/CSlidingMesh.cpp +++ b/Common/src/interface_interpolation/CSlidingMesh.cpp @@ -244,8 +244,10 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { 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; + 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]; @@ -288,11 +290,14 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { } 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; + 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); + LineIntersectionLength = ComputeLineIntersectionLength(target_iMidEdge_point, target_jMidEdge_point, + donor_iMidEdge_point, donor_jMidEdge_point, Direction); if ( LineIntersectionLength == 0.0 ){ check = true; @@ -368,11 +373,14 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { } 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; + 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); + LineIntersectionLength = ComputeLineIntersectionLength(target_iMidEdge_point, target_jMidEdge_point, + donor_iMidEdge_point, donor_jMidEdge_point, Direction); if ( LineIntersectionLength == 0.0 ){ check = true; @@ -476,7 +484,8 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { 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); + 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 ---*/ @@ -508,13 +517,14 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { 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); + 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; + 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); } } @@ -608,12 +618,14 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { 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); + 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); + 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]; @@ -687,7 +699,6 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { 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 < -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 @@ -2532,22 +2531,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 @@ -2566,278 +2554,209 @@ 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++) { + for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { - markDonor = -1; - markTarget = -1; - - /*--- On the donor side ---*/ - nMarkerDonor = config[donorZone]->GetnMarker_All(); - - for (iMarkerDonor = 0; iMarkerDonor < nMarkerDonor; iMarkerDonor++) { - - /*--- 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; - } - } - -#ifdef HAVE_MPI + /*--- Set some boolean to properly allocate data structure later ---*/ + fluid_target = false; + structural_target = false; - Donor_check = -1; - Target_check = -1; + fluid_donor = false; + structural_donor = 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 ---*/ + heat_donor = false; + heat_target = false; - SU2_MPI::Gather(&markDonor , 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); + switch ( config[targetZone]->GetKind_Solver() ) { - if (rank == MASTER_NODE) { - for (iRank = 0; iRank < nProcessor; iRank++) { - if( Buffer_Recv_mark[iRank] != -1 ) { - Donor_check = Buffer_Recv_mark[iRank]; + 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; - } - } - } - 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); + case FEM_ELASTICITY: case DISC_ADJ_FEM: + structural_target = 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 HEAT_EQUATION: case DISC_ADJ_HEAT: + heat_target = true; break; - } } - } - - SU2_MPI::Bcast(&Target_check, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); -#else - Donor_check = markDonor; - Target_check = markTarget; -#endif - - /* --- Check if zones 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; + switch ( config[donorZone]->GetKind_Solver() ) { - heat_donor = false; - heat_target = false; + 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; - switch ( config[targetZone]->GetKind_Solver() ) { + case FEM_ELASTICITY: case DISC_ADJ_FEM: + structural_donor = true; + break; - 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 HEAT_EQUATION : case DISC_ADJ_HEAT: + heat_donor = true; + break; + } - case FEM_ELASTICITY: case DISC_ADJ_FEM: - structural_target = true; - break; + /*--- Begin the creation of the communication pattern among zones ---*/ - case HEAT_EQUATION: case DISC_ADJ_HEAT: - heat_target = true; - break; - } + /*--- 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; - switch ( config[donorZone]->GetKind_Solver() ) { + if (rank == MASTER_NODE) cout << "From zone " << donorZone << " to zone " << targetZone << ": "; - 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; + /*--- Match Zones ---*/ + if (rank == MASTER_NODE) cout << "Setting coupling "; - case FEM_ELASTICITY: case DISC_ADJ_FEM: - structural_donor = true; - break; + bool conservative_interp = config[donorZone]->GetConservativeInterpolation(); - case HEAT_EQUATION : case DISC_ADJ_HEAT: - heat_donor = true; - break; - } + /*--- 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); - /*--- Begin the creation of the communication pattern among zones ---*/ + switch (config[donorZone]->GetKindInterpolation()) { - /*--- 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 << ": "; + case NEAREST_NEIGHBOR: + 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 CNearestNeighbor(geometry, config, donorZone, targetZone); + if (rank == MASTER_NODE) cout << "using a nearest-neighbor approach." << endl; + } + break; - /*--- Match Zones ---*/ - if (rank == MASTER_NODE) cout << "Setting coupling "; + case ISOPARAMETRIC: + 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 CIsoparametric(geometry, config, donorZone, targetZone); + if (rank == MASTER_NODE) cout << "using an isoparametric approach." << endl; + } + break; - bool conservative_interp = config[donorZone]->GetConservativeInterpolation(); + case WEIGHTED_AVERAGE: + interpolation[donorZone][targetZone] = new CSlidingMesh(geometry, config, donorZone, targetZone); + if (rank == MASTER_NODE) cout << "using an sliding mesh approach." << endl; - /*--- 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); + break; - switch (config[donorZone]->GetKindInterpolation()) { + 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; + } - case NEAREST_NEIGHBOR: - 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 CNearestNeighbor(geometry, config, donorZone, targetZone); - if (rank == MASTER_NODE) cout << "using a nearest-neighbor approach." << endl; - } - break; + /*--- Initialize the appropriate transfer strategy ---*/ + if (rank == MASTER_NODE) cout << "Transferring "; - case ISOPARAMETRIC: - 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 CIsoparametric(geometry, config, donorZone, targetZone); - if (rank == MASTER_NODE) cout << "using an isoparametric approach." << endl; + 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]); } - break; - - case WEIGHTED_AVERAGE: - interpolation[donorZone][targetZone] = new CSlidingMesh(geometry, config, donorZone, targetZone); - if (rank == MASTER_NODE) cout << "using an sliding mesh approach." << endl; - - 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; + 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; + 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 { - interpolation[donorZone][targetZone] = new CRadialBasisFunction(geometry, config, - donorZone, targetZone); - if (rank == MASTER_NODE) cout << "using a radial basis function approach." << endl; + 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; } - break; - } - - /*--- 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 << "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 (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; - 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[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); } - /*--- 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; } @@ -2855,10 +2774,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){ diff --git a/SU2_CFD/src/drivers/CMultizoneDriver.cpp b/SU2_CFD/src/drivers/CMultizoneDriver.cpp index 2179cb084a11..66db6a2544cc 100644 --- a/SU2_CFD/src/drivers/CMultizoneDriver.cpp +++ b/SU2_CFD/src/drivers/CMultizoneDriver.cpp @@ -29,12 +29,8 @@ #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; From e1fa6dfbac3298a9f9b3c630de738090e980d139 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 18 Mar 2020 15:56:59 +0000 Subject: [PATCH 13/54] move CSymmetricMatrix to toolboxes --- .../interface_interpolation/CInterpolator.hpp | 4 +- .../CRadialBasisFunction.hpp | 70 +--- Common/include/toolboxes/CSymmetricMatrix.hpp | 90 +++++ Common/lib/Makefile.am | 1 + .../src/interface_interpolation/CMirror.cpp | 102 +++--- .../CRadialBasisFunction.cpp | 308 +--------------- Common/src/toolboxes/CSymmetricMatrix.cpp | 332 ++++++++++++++++++ Common/src/toolboxes/meson.build | 3 +- 8 files changed, 489 insertions(+), 421 deletions(-) create mode 100644 Common/include/toolboxes/CSymmetricMatrix.hpp create mode 100644 Common/src/toolboxes/CSymmetricMatrix.cpp diff --git a/Common/include/interface_interpolation/CInterpolator.hpp b/Common/include/interface_interpolation/CInterpolator.hpp index 74e4840d5d02..487e5131f345 100644 --- a/Common/include/interface_interpolation/CInterpolator.hpp +++ b/Common/include/interface_interpolation/CInterpolator.hpp @@ -147,7 +147,7 @@ class CInterpolator { * \param[in] point_i - coordinates of point i * \param[in] point_j - coordinates of point j */ - inline su2double PointsSquareDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) const { + 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); @@ -160,7 +160,7 @@ class CInterpolator { * \param[in] point_i - coordinates of point i * \param[in] point_j - coordinates of point j */ - inline su2double PointsDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) const { + static inline su2double PointsDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) { return sqrt(PointsSquareDistance(nDim, point_i, point_j)); } diff --git a/Common/include/interface_interpolation/CRadialBasisFunction.hpp b/Common/include/interface_interpolation/CRadialBasisFunction.hpp index 18b1b2083644..37b75d784940 100644 --- a/Common/include/interface_interpolation/CRadialBasisFunction.hpp +++ b/Common/include/interface_interpolation/CRadialBasisFunction.hpp @@ -59,7 +59,6 @@ class CRadialBasisFunction final : public CInterpolator { */ static su2double Get_RadialBasisValue(ENUM_RADIALBASIS type, const su2double radius, const su2double dist); -private: /*! * \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", @@ -73,9 +72,9 @@ class CRadialBasisFunction final : public CInterpolator { * \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. */ - void ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, su2double radius, - const su2activematrix& coords, int& nPolynomial, - vector& keepPolynomialRow, su2passivematrix& C_inv_trunc) const; + 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 @@ -86,7 +85,7 @@ class CRadialBasisFunction final : public CInterpolator { * \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). */ - int CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, su2passivematrix &P) const; + static int CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, su2passivematrix &P); /*! * \brief Prunes (by setting to zero) small interpolation coefficients, i.e. @@ -95,65 +94,6 @@ class CRadialBasisFunction final : public CInterpolator { * \param[in,out] coeffs - The vector of interpolation coefficients. * \return Number of non-zero coefficients after pruning. */ - int PruneSmallCoefficients(passivedouble tolerance, su2passivevector& coeffs) const; - -}; - -/*! - * \brief Helper class used by CRadialBasisFunction to compute the interpolation weights. - * 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 { -private: - enum DecompositionType { NONE, CHOLESKY, LU }; - - vector val_vec, decomp_vec; - vector perm_vec; - int sz = 0; - bool initialized = false; - DecompositionType decomposed = NONE; - - inline void CheckBounds(int i, int j) const { - assert(initialized && "Matrix not initialized."); - assert(i>=0 && i=0 && j. + */ +#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 { +private: + enum DecompositionType { NONE, CHOLESKY, LU }; + + vector val_vec, decomp_vec; + vector perm_vec; + int sz = 0; + bool initialized = false; + DecompositionType decomposed = NONE; + + inline void CheckBounds(int i, int j) const { + assert(initialized && "Matrix not initialized."); + assert(i>=0 && i=0 && jGetMarker_n_ZoneInterface())/2; @@ -73,7 +79,7 @@ void CMirror::Set_TransferCoeff(CConfig **config) { */ /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ - markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); + 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); @@ -103,11 +109,6 @@ void CMirror::Set_TransferCoeff(CConfig **config) { 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; @@ -116,8 +117,10 @@ void CMirror::Set_TransferCoeff(CConfig **config) { #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); + 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; @@ -129,15 +132,15 @@ void CMirror::Set_TransferCoeff(CConfig **config) { #endif /*-- Send donor info --*/ - Buffer_Send_FaceIndex = new unsigned long[MaxFace_Donor]; - Buffer_Send_FaceNodes = new unsigned long[MaxFaceNodes_Donor]; + 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_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_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]; + Buffer_Receive_Coeff = new su2double[MaxFaceNodes_Donor*nProcessor]; for (iVertex=0; iVertexvertex[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; iTargetnode[iPoint]->GetDomain()) continue; + + 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++; - } + target_geometry->vertex[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; @@ -238,6 +236,12 @@ void CMirror::Set_TransferCoeff(CConfig **config) { delete[] Buffer_Receive_FaceNodes; delete[] Buffer_Receive_GlobalPoint; delete[] Buffer_Receive_Coeff; - } + + delete[] Buffer_Send_nFace_Donor; + delete[] Buffer_Send_nFaceNodes_Donor; + + delete[] Buffer_Receive_nFace_Donor; + delete[] Buffer_Receive_nFaceNodes_Donor; + } diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp index fd5504ed8c8b..eabd34671bab 100644 --- a/Common/src/interface_interpolation/CRadialBasisFunction.cpp +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -28,23 +28,8 @@ #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) -/*--- 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 dsytrf_(char*, int*, passivedouble*, int*, int*, passivedouble*, int*, int*); -extern "C" void dsytri_(char*, int*, passivedouble*, int*, int*, passivedouble*, int*); -extern "C" void dpotrf_(char*, int*, passivedouble*, int*, int*); -extern "C" void dpotri_(char*, int*, passivedouble*, int*, int*); -extern "C" void dsymm_(char*, char*, int*, int*, passivedouble*, passivedouble*, int*, - passivedouble*, int*, passivedouble*, passivedouble*, int*); -#endif CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { @@ -320,7 +305,7 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, su2double radius, const su2activematrix& coords, int& nPolynomial, - vector& keepPolynomialRow, su2passivematrix& C_inv_trunc) const { + vector& keepPolynomialRow, su2passivematrix& C_inv_trunc) { const su2double interfaceCoordTol = 1e6 * numeric_limits::epsilon(); @@ -406,7 +391,7 @@ void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool us } int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, - su2passivematrix &P) const { + su2passivematrix &P) { const int m = P.rows(); const int n = P.cols(); @@ -476,7 +461,7 @@ int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector 0.0 && "LLT failed, matrix is not SPD."); - Set(j, j, sqrt(sum)); - - for (int i = j+1; i < sz; ++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)); - } - } - decomposed = CHOLESKY; -#endif -} - -void CSymmetricMatrix::LUDecompose() -{ -#ifndef HAVE_LAPACK - assert(initialized && "Matrix not initialized."); - - /*--- Copy matrix values to LU matrix, init permutation vec. ---*/ - decomp_vec.resize(sz*sz); - perm_vec.resize(sz); - for (int i = 0; i < sz; ++i) { - perm_vec[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_vec[j], perm_vec[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); - } - - decomposed = LU; -#endif -} - -void CSymmetricMatrix::CalcInv() -{ -#ifndef HAVE_LAPACK - assert(initialized && "Matrix not initialized."); - - /*--- Decompose matrix if not done yet. ---*/ - if (decomposed == NONE) { LUDecompose(); } - - /*--- Compute inverse from decomposed matrices. ---*/ - switch (decomposed) { - - case CHOLESKY: - { - /*--- Initialize inverse matrix. ---*/ - vector inv(sz*sz, 0.0); - - /*--- Compute L inverse. ---*/ - /*--- Solve smaller and smaller systems. ---*/ - for (int j = 0; j < sz; ++j) { - /*--- Forward substitution. ---*/ - inv[IdxSym(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[IdxSym(k,j)]; - inv[IdxSym(i,j)] = sum / Get(i,i); - } - } // L inverse in inv - - /*--- Multiply inversed matrices overwrite val_vec. ---*/ - 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[IdxSym(k,i)] * inv[IdxSym(k,j)]; - Set(i, j, sum); - } - - break; - } - - case LU: - { - /*--- Alias val_vec. ---*/ - vector& inv = val_vec; - - /*--- Invert L and U matrices in place. ---*/ - for (int j = 0; j < sz; ++j) { - inv[IdxFull(j,j)] = 1.0 / decomp(j,j); - - for (int i = j+1; i < sz; ++i) { - inv[IdxFull(i,j)] = -decomp(i,j); - inv[IdxFull(j,i)] = -decomp(j,i) * inv[IdxFull(j,j)]; - - for (int k = j+1; k < i; ++k) { - inv[IdxFull(i,j)] -= decomp(i,k) * inv[IdxFull(k,j)]; - inv[IdxFull(j,i)] -= decomp(k,i) * inv[IdxFull(j,k)]; - } - if (j+1 <= i) inv[IdxFull(j,i)] /= decomp(i,i); - } - } - // inverses in val_vec - - /*--- 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[IdxFull(i,k)] * ((k==j)? 1.0 : inv[IdxFull(k,j)]); - } - - /*--- Permute multiplied matrix to recover A_inv, overwrite val_vec. ---*/ - for (int j = 0; j < sz; ++j) { - int k = perm_vec[j]; - for (int i = k; i < sz; ++i) Set(i, k, decomp(i,j)); - } - - /*--- Decomposition no longer needed. ---*/ - vector().swap(decomp_vec); - - break; - } - default: assert(false && "Default (LU) decomposition failed."); - } - - decomposed = NONE; -#endif -} - -void CSymmetricMatrix::CalcInv_sytri() -{ -#ifdef HAVE_LAPACK - char uplo = 'L'; - int info; - perm_vec.resize(sz); // ipiv array - - /*--- Query the optimum work size. ---*/ - int query = -1; passivedouble tmp; - dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), &tmp, &query, &info); - query = static_cast(tmp); - decomp_vec.resize(query); // work array - - /*--- Factorize and invert. ---*/ - dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &query, &info); - if (info!=0) SU2_MPI::Error("LDLT factorization failed.", CURRENT_FUNCTION); - dsytri_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &info); - if (info!=0) SU2_MPI::Error("Inversion with LDLT factorization failed.", CURRENT_FUNCTION); - - decomposed = NONE; -#endif -} - -void CSymmetricMatrix::CalcInv_potri() -{ -#ifdef HAVE_LAPACK - char uplo = 'L'; - int info; - - dpotrf_(&uplo, &sz, val_vec.data(), &sz, &info); - if (info!=0) SU2_MPI::Error("LLT factorization failed.", CURRENT_FUNCTION); - dpotri_(&uplo, &sz, val_vec.data(), &sz, &info); - if (info!=0) SU2_MPI::Error("Inversion with LLT factorization failed.", CURRENT_FUNCTION); - - decomposed = NONE; -#endif -} - -void CSymmetricMatrix::Invert(const bool is_spd) -{ -#ifdef HAVE_LAPACK - if(is_spd) CalcInv_potri(); - else CalcInv_sytri(); -#else - if(!is_spd) LUDecompose(); - else CholeskyDecompose(); - CalcInv(); -#endif -} - -void CSymmetricMatrix::MatVecMult(passivedouble *v) const -{ - passivedouble *tmp_res = new passivedouble [sz]; - - for (int i=0; i. + */ + +#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 RBF interpolation. ---*/ +extern "C" void dsptrf_(char*, int*, passivedouble*, int*, int*); +extern "C" void dsptri_(char*, int*, passivedouble*, int*, passivedouble*, int*); +extern "C" void dsytrf_(char*, int*, passivedouble*, int*, int*, passivedouble*, int*, int*); +extern "C" void dsytri_(char*, int*, passivedouble*, int*, int*, passivedouble*, int*); +extern "C" void dpotrf_(char*, int*, passivedouble*, int*, int*); +extern "C" void dpotri_(char*, int*, passivedouble*, int*, int*); +extern "C" void dsymm_(char*, char*, int*, int*, passivedouble*, passivedouble*, int*, + passivedouble*, int*, passivedouble*, passivedouble*, int*); +#endif + + +void CSymmetricMatrix::Initialize(int N) +{ + sz = N; + val_vec.resize(sz*sz); + initialized = true; +} + +void CSymmetricMatrix::CholeskyDecompose() +{ +#ifndef HAVE_LAPACK + assert(initialized && "Matrix not initialized."); + + for (int j = 0; j < sz; ++j) { + passivedouble sum = 0.0; + for (int k = 0; k < j; ++k) sum -= pow(Get(j,k), 2); + sum += Get(j,j); + assert(sum > 0.0 && "LLT failed, matrix is not SPD."); + Set(j, j, sqrt(sum)); + + for (int i = j+1; i < sz; ++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)); + } + } + decomposed = CHOLESKY; +#endif +} + +void CSymmetricMatrix::LUDecompose() +{ +#ifndef HAVE_LAPACK + assert(initialized && "Matrix not initialized."); + + /*--- Copy matrix values to LU matrix, init permutation vec. ---*/ + decomp_vec.resize(sz*sz); + perm_vec.resize(sz); + for (int i = 0; i < sz; ++i) { + perm_vec[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_vec[j], perm_vec[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); + } + + decomposed = LU; +#endif +} + +void CSymmetricMatrix::CalcInv() +{ +#ifndef HAVE_LAPACK + assert(initialized && "Matrix not initialized."); + + /*--- Decompose matrix if not done yet. ---*/ + if (decomposed == NONE) { LUDecompose(); } + + /*--- Compute inverse from decomposed matrices. ---*/ + switch (decomposed) { + + case CHOLESKY: + { + /*--- Initialize inverse matrix. ---*/ + vector inv(sz*sz, 0.0); + + /*--- Compute L inverse. ---*/ + /*--- Solve smaller and smaller systems. ---*/ + for (int j = 0; j < sz; ++j) { + /*--- Forward substitution. ---*/ + inv[IdxSym(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[IdxSym(k,j)]; + inv[IdxSym(i,j)] = sum / Get(i,i); + } + } // L inverse in inv + + /*--- Multiply inversed matrices overwrite val_vec. ---*/ + 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[IdxSym(k,i)] * inv[IdxSym(k,j)]; + Set(i, j, sum); + } + + break; + } + + case LU: + { + /*--- Alias val_vec. ---*/ + vector& inv = val_vec; + + /*--- Invert L and U matrices in place. ---*/ + for (int j = 0; j < sz; ++j) { + inv[IdxFull(j,j)] = 1.0 / decomp(j,j); + + for (int i = j+1; i < sz; ++i) { + inv[IdxFull(i,j)] = -decomp(i,j); + inv[IdxFull(j,i)] = -decomp(j,i) * inv[IdxFull(j,j)]; + + for (int k = j+1; k < i; ++k) { + inv[IdxFull(i,j)] -= decomp(i,k) * inv[IdxFull(k,j)]; + inv[IdxFull(j,i)] -= decomp(k,i) * inv[IdxFull(j,k)]; + } + if (j+1 <= i) inv[IdxFull(j,i)] /= decomp(i,i); + } + } + // inverses in val_vec + + /*--- 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[IdxFull(i,k)] * ((k==j)? 1.0 : inv[IdxFull(k,j)]); + } + + /*--- Permute multiplied matrix to recover A_inv, overwrite val_vec. ---*/ + for (int j = 0; j < sz; ++j) { + int k = perm_vec[j]; + for (int i = k; i < sz; ++i) Set(i, k, decomp(i,j)); + } + + /*--- Decomposition no longer needed. ---*/ + vector().swap(decomp_vec); + + break; + } + default: assert(false && "Default (LU) decomposition failed."); + } + + decomposed = NONE; +#endif +} + +void CSymmetricMatrix::CalcInv_sytri() +{ +#ifdef HAVE_LAPACK + char uplo = 'L'; + int info; + perm_vec.resize(sz); // ipiv array + + /*--- Query the optimum work size. ---*/ + int query = -1; passivedouble tmp; + dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), &tmp, &query, &info); + query = static_cast(tmp); + decomp_vec.resize(query); // work array + + /*--- Factorize and invert. ---*/ + dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &query, &info); + if (info!=0) SU2_MPI::Error("LDLT factorization failed.", CURRENT_FUNCTION); + dsytri_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &info); + if (info!=0) SU2_MPI::Error("Inversion with LDLT factorization failed.", CURRENT_FUNCTION); + + decomposed = NONE; +#endif +} + +void CSymmetricMatrix::CalcInv_potri() +{ +#ifdef HAVE_LAPACK + char uplo = 'L'; + int info; + + dpotrf_(&uplo, &sz, val_vec.data(), &sz, &info); + if (info!=0) SU2_MPI::Error("LLT factorization failed.", CURRENT_FUNCTION); + dpotri_(&uplo, &sz, val_vec.data(), &sz, &info); + if (info!=0) SU2_MPI::Error("Inversion with LLT factorization failed.", CURRENT_FUNCTION); + + decomposed = NONE; +#endif +} + +void CSymmetricMatrix::Invert(const bool is_spd) +{ +#ifdef HAVE_LAPACK + if(is_spd) CalcInv_potri(); + else CalcInv_sytri(); +#else + if(!is_spd) LUDecompose(); + else CholeskyDecompose(); + CalcInv(); +#endif +} + +void CSymmetricMatrix::MatVecMult(passivedouble *v) const +{ + passivedouble *tmp_res = new passivedouble [sz]; + + for (int i=0; i Date: Wed, 18 Mar 2020 16:34:31 +0000 Subject: [PATCH 14/54] cleanup CMirror, make search hybrid parallel --- .../src/interface_interpolation/CMirror.cpp | 178 +++++++----------- SU2_CFD/src/SU2_CFD.cpp | 1 - SU2_CFD/src/drivers/CDriver.cpp | 4 +- 3 files changed, 67 insertions(+), 116 deletions(-) diff --git a/Common/src/interface_interpolation/CMirror.cpp b/Common/src/interface_interpolation/CMirror.cpp index 0106de67f214..49bcc91b93c0 100644 --- a/Common/src/interface_interpolation/CMirror.cpp +++ b/Common/src/interface_interpolation/CMirror.cpp @@ -37,29 +37,7 @@ CMirror::CMirror(CGeometry ****geometry_container, CConfig **config, unsigned i 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; + const int nProcessor = size; Buffer_Send_nFace_Donor= new unsigned long [1]; Buffer_Send_nFaceNodes_Donor= new unsigned long [1]; @@ -68,10 +46,10 @@ void CMirror::Set_TransferCoeff(CConfig **config) { Buffer_Receive_nFaceNodes_Donor = new unsigned long [nProcessor]; /*--- Number of markers on the interface ---*/ - nMarkerInt = (config[targetZone]->GetMarker_n_ZoneInterface())/2; + const auto nMarkerInt = (config[targetZone]->GetMarker_n_ZoneInterface())/2; /*--- For the number of markers on the interface... ---*/ - for (iMarkerInt=1; iMarkerInt <= nMarkerInt; iMarkerInt++) { + 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 @@ -79,33 +57,27 @@ void CMirror::Set_TransferCoeff(CConfig **config) { */ /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ - markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); + const auto 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); + 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; - if(markDonor != -1) - nVertexDonor = donor_geometry->GetnVertex( markDonor ); - else - nVertexDonor = 0; - - if(markTarget != -1) - nVertexTarget = target_geometry->GetnVertex( markTarget ); - else - nVertexTarget = 0; + 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 --*/ - nLocalFace_Donor=0; - nLocalFaceNodes_Donor=0; - for (jVertex = 0; jVertexvertex[markDonor][jVertex]->GetNode(); // Local index of jVertex + 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()) { - nNodes = donor_geometry->vertex[markDonor][jVertex]->GetnDonorPoints(); - nLocalFaceNodes_Donor+=nNodes; + auto nNodes = donor_geometry->vertex[markDonor][jVertex]->GetnDonorPoints(); + nLocalFaceNodes_Donor += nNodes; nLocalFace_Donor++; } } @@ -114,7 +86,6 @@ void CMirror::Set_TransferCoeff(CConfig **config) { 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, @@ -122,58 +93,39 @@ void CMirror::Set_TransferCoeff(CConfig **config) { 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]; + + /*-- 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]; - 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++; + 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, @@ -186,40 +138,42 @@ void CMirror::Set_TransferCoeff(CConfig **config) { 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]; + auto iPoint = target_vertex->GetNode(); - iPoint = target_geometry->vertex[markTarget][iVertex]->GetNode(); if (!target_geometry->node[iPoint]->GetDomain()) continue; - 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; iTargetnode[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; 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); + target_vertex->SetnDonorPoints(nNodes); + target_vertex->Allocate_DonorInfo(); + + 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++; } } 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 b1b741c731b6..0d17063686a1 100644 --- a/SU2_CFD/src/drivers/CDriver.cpp +++ b/SU2_CFD/src/drivers/CDriver.cpp @@ -3043,11 +3043,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(); } From fe8fcc9e6fb4b0a858ab3f490b3555504306d2b1 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 18 Mar 2020 18:11:19 +0000 Subject: [PATCH 15/54] generalize CNearestNeighbor for KNN --- Common/include/CConfig.hpp | 6 ++ .../CNearestNeighbor.hpp | 7 +- .../interface_interpolation/CSlidingMesh.hpp | 9 ++- Common/src/CConfig.cpp | 10 +-- .../CNearestNeighbor.cpp | 72 ++++++++++++------- .../interface_interpolation/CSlidingMesh.cpp | 11 +-- 6 files changed, 71 insertions(+), 44 deletions(-) diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp index 877f073cc08e..d3f028bcf4b0 100644 --- a/Common/include/CConfig.hpp +++ b/Common/include/CConfig.hpp @@ -988,6 +988,7 @@ 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 (radius). */ @@ -8793,6 +8794,11 @@ class CConfig { */ 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/interface_interpolation/CNearestNeighbor.hpp b/Common/include/interface_interpolation/CNearestNeighbor.hpp index 95dcdfdd3e2f..5c16ed2cdb29 100644 --- a/Common/include/interface_interpolation/CNearestNeighbor.hpp +++ b/Common/include/interface_interpolation/CNearestNeighbor.hpp @@ -29,7 +29,10 @@ #include "CInterpolator.hpp" /*! - * \brief Nearest Neighbor interpolation. + * \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 { public: @@ -43,7 +46,7 @@ class CNearestNeighbor final : public CInterpolator { CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); /*! - * \brief Set up transfer matrix defining relation between two meshes + * \brief Set up transfer matrix defining relation between two meshes. * \param[in] config - Definition of the particular problem. */ void Set_TransferCoeff(CConfig **config) override; diff --git a/Common/include/interface_interpolation/CSlidingMesh.hpp b/Common/include/interface_interpolation/CSlidingMesh.hpp index 43abee84af98..3cbb419d15db 100644 --- a/Common/include/interface_interpolation/CSlidingMesh.hpp +++ b/Common/include/interface_interpolation/CSlidingMesh.hpp @@ -30,6 +30,8 @@ /*! * \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: @@ -81,7 +83,8 @@ class CSlidingMesh final : public CInterpolator { * \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); + 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 @@ -93,7 +96,7 @@ class CSlidingMesh final : public CInterpolator { * \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 ); + 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 @@ -103,7 +106,7 @@ class CSlidingMesh final : public CInterpolator { * \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 ); + 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 diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index 2899aab3328f..4f0328015ace 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -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 diff --git a/Common/src/interface_interpolation/CNearestNeighbor.cpp b/Common/src/interface_interpolation/CNearestNeighbor.cpp index 04843729aa97..7d74a8fb93dc 100644 --- a/Common/src/interface_interpolation/CNearestNeighbor.cpp +++ b/Common/src/interface_interpolation/CNearestNeighbor.cpp @@ -30,6 +30,15 @@ #include "../../include/geometry/CGeometry.hpp" +/*! \brief Helper struct to search sort neighbours according to distance. */ +struct DonorInfo { + su2double dist; + unsigned long pidx; + int proc; + DonorInfo(su2double d = 0.0, unsigned long i = 0, int p = 0) : + dist(d), pidx(i), proc(p) {} +}; + CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { Set_TransferCoeff(config); @@ -37,9 +46,8 @@ CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, CConfig **c void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { - /*--- By definition, one donor point per target point. ---*/ - constexpr auto numDonor = 1; - constexpr auto idxDonor = 0; + /*--- Desired number of donor points. ---*/ + const auto nDonor = config[donorZone]->GetNumNearestNeighbors(); const su2double eps = numeric_limits::epsilon(); @@ -50,6 +58,8 @@ void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { Buffer_Send_nVertex_Donor = new unsigned long [1]; Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; + vector > DonorInfoVec(omp_get_max_threads()); + /*--- Cycle over nMarkersInt interface to determine communication pattern. ---*/ for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { @@ -70,6 +80,9 @@ void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { /* 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 ]; @@ -78,8 +91,14 @@ void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { /*-- Collect coordinates and global point indices. ---*/ Collect_VertexInfo( false, markDonor, markTarget, nVertexDonor, nDim ); - /*--- Compute the closest donor point to each target. ---*/ - SU2_OMP_PARALLEL_(for schedule(dynamic,roundUpDiv(nVertexTarget,2*omp_get_max_threads()))) + /*--- 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); + + 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]; @@ -90,38 +109,41 @@ void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { /*--- Coordinates of the target point. ---*/ const su2double* Coord_i = target_geometry->node[Point_Target]->GetCoord(); - su2double mindist = 1e20; - long pGlobalPoint = 0; - int pProcessor = 0; - - for (int iProcessor = 0; iProcessor < nProcessor; ++iProcessor) { + /*--- 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); - const auto dist = PointsSquareDistance(nDim, Coord_i, Coord_j); + donorInfo[iDonor++] = DonorInfo(dist2, pGlobalPoint, iProcessor); + } + } - if (dist < mindist) { - mindist = dist; - pProcessor = iProcessor; - pGlobalPoint = Buffer_Receive_GlobalPoint[idx]; - } + /*--- Find k closest points. ---*/ + partial_sort(donorInfo.begin(), donorInfo.begin()+nDonor, donorInfo.end(), + [](const DonorInfo& a, const DonorInfo& b){return a.dist < b.dist;}); - /*--- Test for "exact" match. ---*/ - if (dist < eps) break; - } + /*--- 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; } - /*--- Store matching pair. ---*/ - target_vertex->SetnDonorPoints(numDonor); + /*--- Set interpolation coefficients. ---*/ + target_vertex->SetnDonorPoints(nDonor); target_vertex->Allocate_DonorInfo(); - target_vertex->SetInterpDonorPoint(idxDonor, pGlobalPoint); - target_vertex->SetInterpDonorProcessor(idxDonor, pProcessor); - target_vertex->SetDonorCoeff(idxDonor, 1.0); + 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); + } } + } // end SU2_OMP_PARALLEL delete[] Buffer_Send_Coord; delete[] Buffer_Send_GlobalPoint; diff --git a/Common/src/interface_interpolation/CSlidingMesh.cpp b/Common/src/interface_interpolation/CSlidingMesh.cpp index 62084eb55afd..b76804529b90 100644 --- a/Common/src/interface_interpolation/CSlidingMesh.cpp +++ b/Common/src/interface_interpolation/CSlidingMesh.cpp @@ -37,14 +37,7 @@ CSlidingMesh::CSlidingMesh(CGeometry ****geometry_container, CConfig **config, u 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 conservation laws" 2015, Comp. Fluids, 120, pp 126-139 - */ - - /* 0 - Variable declaration - */ + /* 0 - Variable declaration */ /* --- General variables --- */ @@ -102,7 +95,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { su2double *donor_iMidEdge_point, *donor_jMidEdge_point; su2double **donor_element, *DonorPoint_Coord; - /* 1 - Variable pre-processing - */ + /* 1 - Variable pre-processing */ nDim = donor_geometry->GetnDim(); From dd1cdcedaf10a294d40a9655d29dec566260c958 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 18 Mar 2020 19:16:03 +0000 Subject: [PATCH 16/54] more cleanup, try to recover behavior of testcase --- .../CNearestNeighbor.cpp | 31 ++- SU2_CFD/include/interfaces/CInterface.hpp | 22 +- SU2_CFD/src/interfaces/CInterface.cpp | 210 +++--------------- .../src/interfaces/cfd/CSlidingInterface.cpp | 41 +--- 4 files changed, 70 insertions(+), 234 deletions(-) diff --git a/Common/src/interface_interpolation/CNearestNeighbor.cpp b/Common/src/interface_interpolation/CNearestNeighbor.cpp index 7d74a8fb93dc..aa40c4ea433c 100644 --- a/Common/src/interface_interpolation/CNearestNeighbor.cpp +++ b/Common/src/interface_interpolation/CNearestNeighbor.cpp @@ -47,7 +47,7 @@ CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, CConfig **c void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { /*--- Desired number of donor points. ---*/ - const auto nDonor = config[donorZone]->GetNumNearestNeighbors(); + const auto nDonor = max(config[donorZone]->GetNumNearestNeighbors(), 1); const su2double eps = numeric_limits::epsilon(); @@ -126,21 +126,28 @@ void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { partial_sort(donorInfo.begin(), donorInfo.begin()+nDonor, donorInfo.end(), [](const DonorInfo& a, const DonorInfo& b){return a.dist < b.dist;}); - /*--- 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->SetnDonorPoints(nDonor); target_vertex->Allocate_DonorInfo(); - 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); + if (nDonor > 1) { + /*--- 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; + } + + 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); + } + } + else { + target_vertex->SetInterpDonorPoint(0, donorInfo[0].pidx); + target_vertex->SetInterpDonorProcessor(0, donorInfo[0].proc); + target_vertex->SetDonorCoeff(0, 1.0); } } } // end SU2_OMP_PARALLEL 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/src/interfaces/CInterface.cpp b/SU2_CFD/src/interfaces/CInterface.cpp index 33b5ef6ad62a..455c8b8d96b9 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++) { - - Buffer_Recv_nVertexDonor = NULL; - - Marker_Donor = -1; - Marker_Target = -1; - - /*--- 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; - } - } - } + for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { - SU2_MPI::Bcast(&Target_check, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); + /*--- Check if this interface connects the two zones, if not continue. ---*/ -#else - Donor_check = Marker_Donor; - Target_check = Marker_Target; -#endif + Marker_Donor = CInterpolator::Find_InterfaceMarker(donor_config, iMarkerInt); + Marker_Target = CInterpolator::Find_InterfaceMarker(target_config, iMarkerInt); - 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, @@ -449,7 +305,6 @@ void CInterface::PreprocessAverage(CGeometry *donor_geometry, CGeometry *target_ int *BuffMarkerDonor, *BuffDonorFlag; #endif - nMarkerDonor = donor_geometry->GetnMarker(); nMarkerTarget = target_geometry->GetnMarker(); //TODO turbo this approach only works if all the turboamchinery marker @@ -574,6 +429,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; 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, From c0504ff15275d77db3ffe28d797515ba9de9f162 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 18 Mar 2020 19:56:21 +0000 Subject: [PATCH 17/54] fix build issues --- Common/src/interface_interpolation/CNearestNeighbor.cpp | 9 +++++---- Common/src/linear_algebra/CSysMatrix.cpp | 2 +- SU2_CFD/obj/Makefile.am | 1 - 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Common/src/interface_interpolation/CNearestNeighbor.cpp b/Common/src/interface_interpolation/CNearestNeighbor.cpp index aa40c4ea433c..8322cc0cf50c 100644 --- a/Common/src/interface_interpolation/CNearestNeighbor.cpp +++ b/Common/src/interface_interpolation/CNearestNeighbor.cpp @@ -30,13 +30,14 @@ #include "../../include/geometry/CGeometry.hpp" -/*! \brief Helper struct to search sort neighbours according to distance. */ +/*! \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 long pidx; + unsigned pidx; int proc; - DonorInfo(su2double d = 0.0, unsigned long i = 0, int p = 0) : - dist(d), pidx(i), proc(p) {} + DonorInfo(su2double d = 0.0, unsigned i = 0, int p = 0) : + dist(d), pidx(i), proc(p) { } }; CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, diff --git a/Common/src/linear_algebra/CSysMatrix.cpp b/Common/src/linear_algebra/CSysMatrix.cpp index 2a47d4785a60..60a42b635428 100644 --- a/Common/src/linear_algebra/CSysMatrix.cpp +++ b/Common/src/linear_algebra/CSysMatrix.cpp @@ -1383,7 +1383,7 @@ void CSysMatrix::ComputePastixPreconditioner(const CSysVector void CSysMatrix::BuildPastixPreconditioner(CGeometry *geometry, CConfig *config, unsigned short kind_fact, bool transposed) { diff --git a/SU2_CFD/obj/Makefile.am b/SU2_CFD/obj/Makefile.am index 2ed339136e4e..a46d07ef0b79 100644 --- a/SU2_CFD/obj/Makefile.am +++ b/SU2_CFD/obj/Makefile.am @@ -126,7 +126,6 @@ libSU2Core_sources = ../src/definition_structure.cpp \ ../src/output/CMultizoneOutput.cpp \ ../src/output/COutputFactory.cpp \ ../src/output/output_structure_legacy.cpp \ - ../src/output/COutputFactory.cpp \ ../src/python_wrapper_structure.cpp \ ../src/solvers/CAdjEulerSolver.cpp \ ../src/solvers/CAdjNSSolver.cpp \ From a61f3b808da800fc2bd733b13bb6de554c399bc4 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 19 Mar 2020 10:55:54 +0000 Subject: [PATCH 18/54] const use of CConfig in CInterpolator --- Common/include/CConfig.hpp | 30 ++++++++--------- .../interface_interpolation/CInterpolator.hpp | 4 +-- .../CIsoparametric.hpp | 4 +-- .../interface_interpolation/CMirror.hpp | 4 +-- .../CNearestNeighbor.hpp | 4 +-- .../CRadialBasisFunction.hpp | 5 +-- .../interface_interpolation/CSlidingMesh.hpp | 5 +-- .../interface_interpolation/CInterpolator.cpp | 3 +- .../CIsoparametric.cpp | 4 +-- .../src/interface_interpolation/CMirror.cpp | 4 +-- .../CNearestNeighbor.cpp | 33 ++++++++----------- .../CRadialBasisFunction.cpp | 4 +-- .../interface_interpolation/CSlidingMesh.cpp | 4 +-- SU2_CFD/src/interfaces/CInterface.cpp | 23 ++++--------- 14 files changed, 59 insertions(+), 72 deletions(-) diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp index d3f028bcf4b0..604f83e10f9b 100644 --- a/Common/include/CConfig.hpp +++ b/Common/include/CConfig.hpp @@ -3462,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. diff --git a/Common/include/interface_interpolation/CInterpolator.hpp b/Common/include/interface_interpolation/CInterpolator.hpp index 487e5131f345..1bc27bcb3cd5 100644 --- a/Common/include/interface_interpolation/CInterpolator.hpp +++ b/Common/include/interface_interpolation/CInterpolator.hpp @@ -100,7 +100,7 @@ class CInterpolator { * \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); + CInterpolator(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone); /*! * \brief No default construction allowed. @@ -117,7 +117,7 @@ class CInterpolator { * \note Main method that derived classes must implement. * \param[in] config - Definition of the particular problem. */ - virtual void Set_TransferCoeff(CConfig **config) = 0; + virtual void Set_TransferCoeff(const CConfig* const* config) = 0; /*! * \brief Find the index of the interface marker shared by that zone diff --git a/Common/include/interface_interpolation/CIsoparametric.hpp b/Common/include/interface_interpolation/CIsoparametric.hpp index 250a7814f15d..1175e3890f2e 100644 --- a/Common/include/interface_interpolation/CIsoparametric.hpp +++ b/Common/include/interface_interpolation/CIsoparametric.hpp @@ -40,13 +40,13 @@ class CIsoparametric final : public CInterpolator { * \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); + 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(CConfig **config) override; + void Set_TransferCoeff(const CConfig* const* config) override; private: /*! diff --git a/Common/include/interface_interpolation/CMirror.hpp b/Common/include/interface_interpolation/CMirror.hpp index c53ebc4bd889..311d54d96a60 100644 --- a/Common/include/interface_interpolation/CMirror.hpp +++ b/Common/include/interface_interpolation/CMirror.hpp @@ -42,12 +42,12 @@ class CMirror final : public CInterpolator { * \param[in] iZone - First zone * \param[in] jZone - Second zone */ - CMirror(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); + 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(CConfig **config) override; + void Set_TransferCoeff(const CConfig* const* config) override; }; diff --git a/Common/include/interface_interpolation/CNearestNeighbor.hpp b/Common/include/interface_interpolation/CNearestNeighbor.hpp index 5c16ed2cdb29..c8efe3998e40 100644 --- a/Common/include/interface_interpolation/CNearestNeighbor.hpp +++ b/Common/include/interface_interpolation/CNearestNeighbor.hpp @@ -43,12 +43,12 @@ class CNearestNeighbor final : public CInterpolator { * \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); + 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(CConfig **config) override; + void Set_TransferCoeff(const CConfig* const* config) override; }; diff --git a/Common/include/interface_interpolation/CRadialBasisFunction.hpp b/Common/include/interface_interpolation/CRadialBasisFunction.hpp index 37b75d784940..784ba1b321fd 100644 --- a/Common/include/interface_interpolation/CRadialBasisFunction.hpp +++ b/Common/include/interface_interpolation/CRadialBasisFunction.hpp @@ -42,13 +42,14 @@ class CRadialBasisFunction final : public CInterpolator { * \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); + 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(CConfig **config) override; + void Set_TransferCoeff(const CConfig* const* config) override; /*! * \brief Compute the value of a radial basis function, this is static so it can be re-used. diff --git a/Common/include/interface_interpolation/CSlidingMesh.hpp b/Common/include/interface_interpolation/CSlidingMesh.hpp index 3cbb419d15db..e84c4c908a74 100644 --- a/Common/include/interface_interpolation/CSlidingMesh.hpp +++ b/Common/include/interface_interpolation/CSlidingMesh.hpp @@ -42,13 +42,14 @@ class CSlidingMesh final : public CInterpolator { * \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); + 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(CConfig **config) override; + void Set_TransferCoeff(const CConfig* const* config) override; private: /*! diff --git a/Common/src/interface_interpolation/CInterpolator.cpp b/Common/src/interface_interpolation/CInterpolator.cpp index 05d54a74fbdf..2c2c0089bb9e 100644 --- a/Common/src/interface_interpolation/CInterpolator.cpp +++ b/Common/src/interface_interpolation/CInterpolator.cpp @@ -30,7 +30,8 @@ #include "../../include/geometry/CGeometry.hpp" -CInterpolator::CInterpolator(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : +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), diff --git a/Common/src/interface_interpolation/CIsoparametric.cpp b/Common/src/interface_interpolation/CIsoparametric.cpp index ef3468b86fb3..4e46e5f94ee1 100644 --- a/Common/src/interface_interpolation/CIsoparametric.cpp +++ b/Common/src/interface_interpolation/CIsoparametric.cpp @@ -30,12 +30,12 @@ #include "../../include/geometry/CGeometry.hpp" -CIsoparametric::CIsoparametric(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, +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(CConfig **config) { +void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { unsigned long iVertex, jVertex; unsigned long dPoint, inode, jElem, nElem; diff --git a/Common/src/interface_interpolation/CMirror.cpp b/Common/src/interface_interpolation/CMirror.cpp index 49bcc91b93c0..d64d0451811d 100644 --- a/Common/src/interface_interpolation/CMirror.cpp +++ b/Common/src/interface_interpolation/CMirror.cpp @@ -30,12 +30,12 @@ #include "../../include/geometry/CGeometry.hpp" -CMirror::CMirror(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, +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(CConfig **config) { +void CMirror::Set_TransferCoeff(const CConfig* const* config) { const int nProcessor = size; diff --git a/Common/src/interface_interpolation/CNearestNeighbor.cpp b/Common/src/interface_interpolation/CNearestNeighbor.cpp index 8322cc0cf50c..9d5536e88fe7 100644 --- a/Common/src/interface_interpolation/CNearestNeighbor.cpp +++ b/Common/src/interface_interpolation/CNearestNeighbor.cpp @@ -40,12 +40,12 @@ struct DonorInfo { dist(d), pidx(i), proc(p) { } }; -CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, +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::Set_TransferCoeff(CConfig **config) { +void CNearestNeighbor::Set_TransferCoeff(const CConfig* const* config) { /*--- Desired number of donor points. ---*/ const auto nDonor = max(config[donorZone]->GetNumNearestNeighbors(), 1); @@ -127,28 +127,21 @@ void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { partial_sort(donorInfo.begin(), donorInfo.begin()+nDonor, donorInfo.end(), [](const DonorInfo& a, const DonorInfo& b){return a.dist < b.dist;}); + /*--- 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->SetnDonorPoints(nDonor); target_vertex->Allocate_DonorInfo(); - if (nDonor > 1) { - /*--- 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; - } - - 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); - } - } - else { - target_vertex->SetInterpDonorPoint(0, donorInfo[0].pidx); - target_vertex->SetInterpDonorProcessor(0, donorInfo[0].proc); - target_vertex->SetDonorCoeff(0, 1.0); + 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); } } } // end SU2_OMP_PARALLEL diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp index eabd34671bab..3edf9bace1b2 100644 --- a/Common/src/interface_interpolation/CRadialBasisFunction.cpp +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -31,7 +31,7 @@ #include "../../include/toolboxes/CSymmetricMatrix.hpp" -CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, +CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { Set_TransferCoeff(config); } @@ -66,7 +66,7 @@ su2double CRadialBasisFunction::Get_RadialBasisValue(ENUM_RADIALBASIS type, cons return rbf; } -void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { +void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { /*--- RBF options. ---*/ const auto kindRBF = static_cast(config[donorZone]->GetKindRadialBasisFunction()); diff --git a/Common/src/interface_interpolation/CSlidingMesh.cpp b/Common/src/interface_interpolation/CSlidingMesh.cpp index b76804529b90..4468b31157e2 100644 --- a/Common/src/interface_interpolation/CSlidingMesh.cpp +++ b/Common/src/interface_interpolation/CSlidingMesh.cpp @@ -30,12 +30,12 @@ #include "../../include/geometry/CGeometry.hpp" -CSlidingMesh::CSlidingMesh(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, +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(CConfig **config) { +void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { /* 0 - Variable declaration */ diff --git a/SU2_CFD/src/interfaces/CInterface.cpp b/SU2_CFD/src/interfaces/CInterface.cpp index 455c8b8d96b9..df0d1ab46490 100644 --- a/SU2_CFD/src/interfaces/CInterface.cpp +++ b/SU2_CFD/src/interfaces/CInterface.cpp @@ -300,11 +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 @@ -333,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; } } @@ -623,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 ---*/ @@ -760,4 +752,3 @@ void CInterface::GatherAverageTurboGeoValues(CGeometry *donor_geometry, CGeometr SetAverageTurboGeoValues(donor_geometry, target_geometry, donorZone); } - From 02df871bf41d942aa3d1ca0a448b36d97a9626f9 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 19 Mar 2020 12:12:50 +0000 Subject: [PATCH 19/54] more const correctness --- .../interface_interpolation/CSlidingMesh.hpp | 32 +++-- Common/include/toolboxes/C2DContainer.hpp | 1 + Common/include/toolboxes/CSymmetricMatrix.hpp | 14 ++- .../CRadialBasisFunction.cpp | 7 +- .../interface_interpolation/CSlidingMesh.cpp | 111 +++++++++--------- Common/src/toolboxes/CSymmetricMatrix.cpp | 29 ++--- 6 files changed, 100 insertions(+), 94 deletions(-) diff --git a/Common/include/interface_interpolation/CSlidingMesh.hpp b/Common/include/interface_interpolation/CSlidingMesh.hpp index e84c4c908a74..74ae1d9ae18f 100644 --- a/Common/include/interface_interpolation/CSlidingMesh.hpp +++ b/Common/include/interface_interpolation/CSlidingMesh.hpp @@ -55,24 +55,29 @@ class CSlidingMesh final : public CInterpolator { /*! * \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] 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 + * \param[out] element - double array where element node coordinates will be stored + * \return Number of points included in the element. */ - int Build_3D_surface_element(unsigned long *map, unsigned long *startIndex, unsigned long* nNeighbor, - su2double *coord, unsigned long centralNode, su2double** 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 */ - su2double ComputeLineIntersectionLength(su2double* A1, su2double* A2, su2double* B1, su2double* B2, su2double* Direction); + 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 @@ -84,8 +89,9 @@ class CSlidingMesh final : public CInterpolator { * \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); + 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 @@ -97,7 +103,8 @@ class CSlidingMesh final : public CInterpolator { * \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); + 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 @@ -105,9 +112,10 @@ class CSlidingMesh final : public CInterpolator { * \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 + * \param[out] IntersectionPoint - Container for intersection coordinates */ - void ComputeLineIntersectionPoint(su2double* A1, su2double* A2, su2double* B1, su2double* B2, su2double* IntersectionPoint); + 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 @@ -116,6 +124,6 @@ class CSlidingMesh final : public CInterpolator { * \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); - + static bool CheckPointInsideTriangle(const su2double* Point, const su2double* T1, + const su2double* T2, const su2double* T3); }; diff --git a/Common/include/toolboxes/C2DContainer.hpp b/Common/include/toolboxes/C2DContainer.hpp index a5c978f2bff3..93f1314766f6 100644 --- a/Common/include/toolboxes/C2DContainer.hpp +++ b/Common/include/toolboxes/C2DContainer.hpp @@ -371,6 +371,7 @@ class C2DContainer : using Base::size; using Index = Index_t; using Scalar = Scalar_t; + static constexpr StorageType Storage = Store; private: /*! diff --git a/Common/include/toolboxes/CSymmetricMatrix.hpp b/Common/include/toolboxes/CSymmetricMatrix.hpp index 24f057cdcd65..b32613a864ae 100644 --- a/Common/include/toolboxes/CSymmetricMatrix.hpp +++ b/Common/include/toolboxes/CSymmetricMatrix.hpp @@ -81,9 +81,19 @@ class CSymmetricMatrix { inline const passivedouble& operator() (int i, int j) const { return val_vec[IdxSym(i,j)]; } - void MatVecMult(passivedouble *v) const; + template + void MatVecMult(ForwardIt vec_in, ForwardIt vec_out) const + { + for (int i = 0; i < sz; ++i) { + *vec_out = 0.0; + auto vec = vec_in; + for (int k = 0; k < sz; ++k) + *vec_out += *(vec++) * Get(i,k); + ++vec_out; + } + } - void MatMatMult(const char side, su2passivematrix& mat_in, su2passivematrix& mat_out); + void MatMatMult(const char side, const su2passivematrix& mat_in, su2passivematrix& mat_out) const; void Invert(const bool is_spd); diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp index 3edf9bace1b2..e25bdbb688d4 100644 --- a/Common/src/interface_interpolation/CRadialBasisFunction.cpp +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -401,6 +401,7 @@ int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector coeff(n_rows,0.0); + vector rhs(n_rows,0.0), coeff(n_rows); for (int i = 0; i < n_rows; ++i) for (int j = 0; j < n; ++j) - coeff[i] += P(i+1,j); + rhs[i] += P(i+1,j); /*--- Multiply the RHS by the inverse thus obtaining the coefficients. ---*/ - PPT.MatVecMult(coeff.data()); + PPT.MatVecMult(rhs.begin(), coeff.begin()); /*--- Determine the maximum deviation of the points from the fitted plane. ---*/ passivedouble max_diff = 0.0; diff --git a/Common/src/interface_interpolation/CSlidingMesh.cpp b/Common/src/interface_interpolation/CSlidingMesh.cpp index 4468b31157e2..2ac685049af6 100644 --- a/Common/src/interface_interpolation/CSlidingMesh.cpp +++ b/Common/src/interface_interpolation/CSlidingMesh.cpp @@ -43,7 +43,7 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { bool check; - unsigned short iDim, nDim; + unsigned short iDim; unsigned long ii, jj, *uptr; unsigned long vPoint; @@ -97,17 +97,17 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { /* 1 - Variable pre-processing */ - nDim = donor_geometry->GetnDim(); + const unsigned short nDim = donor_geometry->GetnDim(); /*--- Setting up auxiliary vectors ---*/ - Donor_Vect = NULL; - Coeff_Vect = NULL; - storeProc = NULL; + Donor_Vect = nullptr; + Coeff_Vect = nullptr; + storeProc = nullptr; - tmp_Donor_Vect = NULL; - tmp_Coeff_Vect = NULL; - tmp_storeProc = NULL; + tmp_Donor_Vect = nullptr; + tmp_Coeff_Vect = nullptr; + tmp_storeProc = nullptr; Normal = new su2double[nDim]; Direction = new su2double[nDim]; @@ -289,7 +289,7 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; } - LineIntersectionLength = ComputeLineIntersectionLength(target_iMidEdge_point, target_jMidEdge_point, + LineIntersectionLength = ComputeLineIntersectionLength(nDim, target_iMidEdge_point, target_jMidEdge_point, donor_iMidEdge_point, donor_jMidEdge_point, Direction); if ( LineIntersectionLength == 0.0 ){ @@ -313,9 +313,9 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { 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; + 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; @@ -372,7 +372,7 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; } - LineIntersectionLength = ComputeLineIntersectionLength(target_iMidEdge_point, target_jMidEdge_point, + LineIntersectionLength = ComputeLineIntersectionLength(nDim, target_iMidEdge_point, target_jMidEdge_point, donor_iMidEdge_point, donor_jMidEdge_point, Direction); if ( LineIntersectionLength == 0.0 ){ @@ -396,9 +396,9 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { 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; + 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; @@ -554,7 +554,7 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { Area_old = Area; - ToVisit = NULL; + ToVisit = nullptr; nToVisit = 0; for( iNodeVisited = StartVisited; iNodeVisited < nAlreadyVisited; iNodeVisited++ ){ @@ -578,7 +578,7 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { } } - if( check == 0 && ToVisit != NULL){ + if( check == 0 && ToVisit != nullptr){ for( jj = 0; jj < nToVisit; jj++ ) if( donor_iPoint == ToVisit[jj] ){ check = 1; @@ -595,11 +595,11 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { tmpVect[jj] = ToVisit[jj]; tmpVect[nToVisit] = donor_iPoint; - if( ToVisit != NULL ) + if( ToVisit != nullptr ) delete [] ToVisit; ToVisit = tmpVect; - tmpVect = NULL; + tmpVect = nullptr; nToVisit++; @@ -640,17 +640,17 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { 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; } + 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 = NULL; - tmp_Donor_Vect = NULL; - tmp_storeProc = NULL; + tmp_Coeff_Vect = nullptr; + tmp_Donor_Vect = nullptr; + tmp_storeProc = nullptr; nDonorPoints++; @@ -671,7 +671,7 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { for( jj = 0; jj < nToVisit; jj++ ) tmpVect[ nAlreadyVisited + jj ] = ToVisit[jj]; - if( alreadyVisitedDonor != NULL ) + if( alreadyVisitedDonor != nullptr ) delete [] alreadyVisitedDonor; alreadyVisitedDonor = tmpVect; @@ -698,9 +698,9 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { delete [] target_element[ii]; delete [] target_element; - delete [] Donor_Vect; Donor_Vect = NULL; - delete [] Coeff_Vect; Coeff_Vect = NULL; - delete [] storeProc; storeProc = NULL; + delete [] Donor_Vect; Donor_Vect = nullptr; + delete [] Coeff_Vect; Coeff_Vect = nullptr; + delete [] storeProc; storeProc = nullptr; } } @@ -723,24 +723,25 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { delete [] Normal; delete [] Direction; - if (Donor_Vect != NULL) delete [] Donor_Vect; - if (Coeff_Vect != NULL) delete [] Coeff_Vect; - if (storeProc != NULL) delete [] storeProc; + 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(unsigned long *map, unsigned long *startIndex, unsigned long* nNeighbor, - su2double *coord, unsigned long centralNode, su2double** element){ +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" ---*/ - /*--- Returns the number of points included in the element ---*/ unsigned long iNode, jNode, kNode, iElementNode, iPoint, jPoint, nOuterNodes; - unsigned short nDim = 3, iDim, nTmp; + constexpr unsigned short nDim = 3; + unsigned short iDim, nTmp; int NextNode, **OuterNodesNeighbour, CurrentNode, StartIndex, count; - unsigned long *OuterNodes, *ptr; + const unsigned long *OuterNodes, *ptr; /* --- Store central node as element first point --- */ @@ -841,15 +842,14 @@ int CSlidingMesh::Build_3D_surface_element(unsigned long *map, unsigned long *st } -su2double CSlidingMesh::ComputeLineIntersectionLength(su2double* A1, su2double* A2, su2double* B1, - su2double* B2, su2double* Direction){ +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; - unsigned short nDim = donor_geometry->GetnDim(); su2double dotA2, dotB1, dotB2; @@ -898,14 +898,15 @@ su2double CSlidingMesh::ComputeLineIntersectionLength(su2double* A1, su2double* return 0.0; } -su2double CSlidingMesh::Compute_Triangle_Intersection(su2double* A1, su2double* A2, su2double* A3, - su2double* B1, su2double* B2, su2double* B3, su2double* Direction){ +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; - unsigned short nDim = 3; + constexpr unsigned short nDim = 3; su2double I[3], J[3], K[3]; su2double a1[3], a2[3], a3[3]; @@ -981,21 +982,20 @@ su2double CSlidingMesh::Compute_Triangle_Intersection(su2double* A1, su2double* return ComputeIntersectionArea( a1, a2, a3, b1, b2, b3 ); } -su2double CSlidingMesh::ComputeIntersectionArea(su2double* P1, su2double* P2, su2double* P3, - su2double* Q1, su2double* Q2, su2double* Q3 ){ +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, i, j, k; - unsigned short nDim, min_theta_index; + 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; - nDim = 2; - nPoints = 0; + constexpr unsigned short nDim = 2; for(iDim = 0; iDim < nDim; iDim++){ TriangleP[0][iDim] = 0; @@ -1139,8 +1139,8 @@ su2double CSlidingMesh::ComputeIntersectionArea(su2double* P1, su2double* P2, su return fabs(Area)/2; } -void CSlidingMesh::ComputeLineIntersectionPoint(su2double* A1, su2double* A2, su2double* B1, su2double* B2, - su2double* IntersectionPoint ){ +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, @@ -1156,7 +1156,7 @@ void CSlidingMesh::ComputeLineIntersectionPoint(su2double* A1, su2double* A2, su } } -bool CSlidingMesh::CheckPointInsideTriangle(su2double* Point, su2double* T1, su2double* T2, su2double* T3){ +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: @@ -1166,13 +1166,12 @@ bool CSlidingMesh::CheckPointInsideTriangle(su2double* Point, su2double* T1, su2 * - If the check is positive for all the 3 edges, then the point lies within the triangle */ - unsigned short iDim, nDim, check; + unsigned short iDim, check = 0; su2double vect1[2], vect2[2], r[2]; su2double dot; - check = 0; - nDim = 2; + constexpr unsigned short nDim = 2; /* --- Check first edge --- */ diff --git a/Common/src/toolboxes/CSymmetricMatrix.cpp b/Common/src/toolboxes/CSymmetricMatrix.cpp index 8e884d4131ea..8ec0a020ebf7 100644 --- a/Common/src/toolboxes/CSymmetricMatrix.cpp +++ b/Common/src/toolboxes/CSymmetricMatrix.cpp @@ -256,25 +256,12 @@ void CSymmetricMatrix::Invert(const bool is_spd) #endif } -void CSymmetricMatrix::MatVecMult(passivedouble *v) const -{ - passivedouble *tmp_res = new passivedouble [sz]; - - for (int i=0; i(val_vec.data()), + &M, const_cast(mat_in.data()), &N, &beta, mat_out.data(), &N); #endif } /*--- Right_side: mat_out = mat_in * this. ---*/ @@ -324,8 +311,8 @@ void CSymmetricMatrix::MatMatMult(const char side, /*--- Left and lower because matrices are in row major order. ---*/ char side = 'L', uplo = 'L'; passivedouble alpha = 1.0, beta = 0.0; - dsymm_(&side, &uplo, &N, &M, &alpha, val_vec.data(), &N, - mat_in.data(), &N, &beta, mat_out.data(), &N); + dsymm_(&side, &uplo, &N, &M, &alpha, const_cast(val_vec.data()), + &N, const_cast(mat_in.data()), &N, &beta, mat_out.data(), &N); #endif } From 759e8a7e03424995ef1bee2c06abcef1b927de7a Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 19 Mar 2020 19:46:35 +0000 Subject: [PATCH 20/54] more blas 3 optimization of RBF interpolation, some cleanup of CVertex --- Common/include/geometry/dual_grid/CVertex.hpp | 48 ++-- .../interface_interpolation/CInterpolator.hpp | 6 +- .../CRadialBasisFunction.hpp | 37 ++- Common/src/geometry/dual_grid/CVertex.cpp | 60 ++--- .../CIsoparametric.cpp | 15 +- .../src/interface_interpolation/CMirror.cpp | 9 +- .../CNearestNeighbor.cpp | 5 +- .../CRadialBasisFunction.cpp | 233 ++++++++++-------- .../interface_interpolation/CSlidingMesh.cpp | 7 +- Common/src/toolboxes/CSymmetricMatrix.cpp | 32 ++- 10 files changed, 225 insertions(+), 227 deletions(-) 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 index 1bc27bcb3cd5..6da4e028c4f2 100644 --- a/Common/include/interface_interpolation/CInterpolator.hpp +++ b/Common/include/interface_interpolation/CInterpolator.hpp @@ -55,9 +55,9 @@ class CInterpolator { *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_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*/ diff --git a/Common/include/interface_interpolation/CRadialBasisFunction.hpp b/Common/include/interface_interpolation/CRadialBasisFunction.hpp index 784ba1b321fd..c5f9cb19e856 100644 --- a/Common/include/interface_interpolation/CRadialBasisFunction.hpp +++ b/Common/include/interface_interpolation/CRadialBasisFunction.hpp @@ -35,6 +35,8 @@ */ class CRadialBasisFunction final : public CInterpolator { public: + static_assert(su2passivematrix::Storage == StorageType::RowMajor, + "This class does not work otherwise."); /*! * \brief Constructor of the class. * \param[in] geometry - Geometrical definition of the problem. @@ -89,12 +91,39 @@ class CRadialBasisFunction final : public CInterpolator { static int CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, su2passivematrix &P); /*! - * \brief Prunes (by setting to zero) small interpolation coefficients, i.e. - * <= tolerance*max(abs(coeffs)). The vector is re-scaled such that sum(coeffs)==1. + * \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,out] coeffs - The vector of interpolation coefficients. + * \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. */ - static int PruneSmallCoefficients(passivedouble tolerance, su2passivevector& coeffs); + template + static Int 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; + for (auto it = coeffs; it != end; ++it) *it *= correction; + + return numNonZeros; + } }; 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/CIsoparametric.cpp b/Common/src/interface_interpolation/CIsoparametric.cpp index 4e46e5f94ee1..d1032f26495b 100644 --- a/Common/src/interface_interpolation/CIsoparametric.cpp +++ b/Common/src/interface_interpolation/CIsoparametric.cpp @@ -77,10 +77,6 @@ void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { Coord = new su2double[nDim]; Normal = new su2double[nDim]; - 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]; @@ -259,7 +255,7 @@ void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { /*--- ---*/ nNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1] - - (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; + (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 @@ -314,7 +310,6 @@ void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { /*--- Store info ---*/ donor_elem = temp_donor; target_geometry->vertex[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(); + target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(nNodes); for (iDonor=0; iDonorvertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor,storeGlobal[iDonor]); @@ -355,10 +350,6 @@ void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { delete[] Buffer_Receive_FaceProc; } - 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; diff --git a/Common/src/interface_interpolation/CMirror.cpp b/Common/src/interface_interpolation/CMirror.cpp index d64d0451811d..089af9eceec1 100644 --- a/Common/src/interface_interpolation/CMirror.cpp +++ b/Common/src/interface_interpolation/CMirror.cpp @@ -39,9 +39,6 @@ void CMirror::Set_TransferCoeff(const CConfig* const* config) { const int nProcessor = size; - 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]; @@ -158,8 +155,7 @@ void CMirror::Set_TransferCoeff(const CConfig* const* config) { } } - target_vertex->SetnDonorPoints(nNodes); - target_vertex->Allocate_DonorInfo(); + target_vertex->Allocate_DonorInfo(nNodes); for (int iProcessor = 0, iDonor = 0; iProcessor < nProcessor; iProcessor++) { for (auto iFace = 0ul; iFace < Buffer_Receive_nFace_Donor[iProcessor]; iFace++) { @@ -192,9 +188,6 @@ void CMirror::Set_TransferCoeff(const CConfig* const* config) { delete[] Buffer_Receive_Coeff; } - delete[] Buffer_Send_nFace_Donor; - delete[] Buffer_Send_nFaceNodes_Donor; - 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 index 9d5536e88fe7..3e0a7ee24170 100644 --- a/Common/src/interface_interpolation/CNearestNeighbor.cpp +++ b/Common/src/interface_interpolation/CNearestNeighbor.cpp @@ -56,7 +56,6 @@ void CNearestNeighbor::Set_TransferCoeff(const CConfig* const* config) { const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2; const auto nDim = donor_geometry->GetnDim(); - Buffer_Send_nVertex_Donor = new unsigned long [1]; Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; vector > DonorInfoVec(omp_get_max_threads()); @@ -135,8 +134,7 @@ void CNearestNeighbor::Set_TransferCoeff(const CConfig* const* config) { } /*--- Set interpolation coefficients. ---*/ - target_vertex->SetnDonorPoints(nDonor); - target_vertex->Allocate_DonorInfo(); + target_vertex->Allocate_DonorInfo(nDonor); for (auto iDonor = 0ul; iDonor < nDonor; ++iDonor) { target_vertex->SetInterpDonorPoint(iDonor, donorInfo[iDonor].pidx); @@ -154,7 +152,6 @@ void CNearestNeighbor::Set_TransferCoeff(const CConfig* const* config) { } - delete[] Buffer_Send_nVertex_Donor; delete[] Buffer_Receive_nVertex_Donor; } diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp index e25bdbb688d4..f87df1a47460 100644 --- a/Common/src/interface_interpolation/CRadialBasisFunction.cpp +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -30,6 +30,18 @@ #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*); +#endif + CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { @@ -78,35 +90,35 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { const int nDim = donor_geometry->GetnDim(); const int nProcessor = size; - Buffer_Send_nVertex_Donor = new unsigned long [1]; 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); + + 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 mark_donor = Find_InterfaceMarker(config[donorZone], iMarkerInt+1); + const auto markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt+1); /*--- On the target side: find the tag of the boundary sharing the interface. ---*/ - const auto mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt+1); + 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(mark_donor,mark_target)) continue; + if(!CheckInterfaceBoundary(markDonor,markTarget)) continue; unsigned long nVertexDonor = 0; - if(mark_donor != -1) nVertexDonor = donor_geometry->GetnVertex(mark_donor); + if(markDonor != -1) nVertexDonor = donor_geometry->GetnVertex(markDonor); /*--- Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor. ---*/ - Determine_ArraySize(false, mark_donor, mark_target, nVertexDonor, nDim); + Determine_ArraySize(false, markDonor, markTarget, nVertexDonor, nDim); /*--- Compute total number of donor vertices. ---*/ auto nGlobalVertexDonor = accumulate(Buffer_Receive_nVertex_Donor, @@ -118,24 +130,24 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { 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); + 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& 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; + donorCoord(iCount,iDim) = Buffer_Receive_Coord[(offset+iVertex)*nDim + iDim]; + donorPoint[iCount] = Buffer_Receive_GlobalPoint[offset+iVertex]; + donorProc[iCount] = iProcessor; ++iCount; } } @@ -149,13 +161,14 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { /*--- 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; + if (totalWork[i] < totalWork[iProcessor]) iProcessor = i; - TotalWork[iProcessor] += pow(nGlobalVertexDonor,3); // based on matrix inversion. + totalWork[iProcessor] += pow(nGlobalVertexDonor,3); // based on matrix inversion. - AssignedProcessor[iMarkerInt] = iProcessor; + 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. ---*/ @@ -165,9 +178,9 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { SU2_OMP_PARALLEL_(for schedule(dynamic,1)) for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; ++iMarkerInt) { - if (rank == AssignedProcessor[iMarkerInt]) { + if (rank == assignedProcessor[iMarkerInt]) { ComputeGeneratorMatrix(kindRBF, usePolynomial, paramRBF, - DonorCoordinates[iMarkerInt], nPolynomialVec[iMarkerInt], + donorCoordinates[iMarkerInt], nPolynomialVec[iMarkerInt], keepPolynomialRowVec[iMarkerInt], CinvTrucVec[iMarkerInt]); } } @@ -177,25 +190,25 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; iMarkerInt++) { /*--- Identify the rank that computed the interpolation matrix for this marker. ---*/ - const int iProcessor = AssignedProcessor[iMarkerInt]; + 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 mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt+1); + const int markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt+1); unsigned long nVertexTarget = 0; - if(mark_target != -1) nVertexTarget = target_geometry->GetnVertex(mark_target); + 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& 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(); + const auto nGlobalVertexDonor = donorCoord.rows(); #ifdef HAVE_MPI /*--- For simplicity, broadcast small information about the interpolation matrix. ---*/ @@ -221,86 +234,119 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { } #endif - /*--- Compute H (interpolation) matrix, distributing - * target points over the threads in the rank. ---*/ + /*--- 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(); + + /*--- Distribute target slabs over the threads in the rank for processing. ---*/ + SU2_OMP_PARALLEL - { - su2passivevector coeff_vec(nGlobalVertexDonor); + if (nVertexTarget > 0) { - SU2_OMP_FOR_DYN(roundUpDiv(nVertexTarget, 2*omp_get_max_threads())) - for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget++) { + constexpr unsigned long targetSlabSize = 32; - auto target_vertex = target_geometry->vertex[mark_target][iVertexTarget]; - const auto point_target = target_vertex->GetNode(); + su2passivematrix funcMat(targetSlabSize, 1+nPolynomial+nGlobalVertexDonor); + su2passivematrix interpMat(targetSlabSize, nGlobalVertexDonor); - /*--- If not domain point move to next. ---*/ - if (!target_geometry->node[point_target]->GetDomain()) continue; + SU2_OMP_FOR_DYN(1) + for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget += targetSlabSize) { - const su2double* coord_i = target_geometry->node[point_target]->GetCoord(); + const auto iLastVertex = min(nVertexTarget, iVertexTarget+targetSlabSize); + const auto slabSize = iLastVertex - iVertexTarget; - /*--- Multiply target vector by C_inv_trunc to obtain the interpolation coefficients. - * The target vector is not stored, we consume its entries immediately to avoid - * strided access to C_inv_trunc (as it is row-major). ---*/ + /*--- Prepare matrix of functions A (the targets to donors matrix). ---*/ /*--- Polynominal part: ---*/ if (usePolynomial) { /*--- Constant term. ---*/ - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) - coeff_vec(j) = C_inv_trunc(0,j); + 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 j = 0ul; j < nGlobalVertexDonor; ++j) - coeff_vec(j) += SU2_TYPE::GetValue(coord_i[iDim]) * C_inv_trunc(idx,j); + for (auto k = 0ul; k < slabSize; ++k) + funcMat(k, idx) = SU2_TYPE::GetValue(targetCoord[iVertexTarget+k][iDim]); idx += 1; } } - else { - /*--- Initialize vector to zero. ---*/ - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) coeff_vec(j) = 0.0; - } - /*--- RBF terms: ---*/ for (auto iVertexDonor = 0ul; iVertexDonor < nGlobalVertexDonor; ++iVertexDonor) { - auto w_ij = SU2_TYPE::GetValue(Get_RadialBasisValue(kindRBF, paramRBF, - PointsDistance(nDim, coord_i, DonorCoord[iVertexDonor]))); - - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) - coeff_vec(j) += w_ij * C_inv_trunc(1+nPolynomial+iVertexDonor, j); + 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); + } } - /*--- Prune small coefficients. ---*/ - auto nnz = PruneSmallCoefficients(SU2_TYPE::GetValue(pruneTol), coeff_vec); + /*--- 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 nnz = PruneSmallCoefficients(SU2_TYPE::GetValue(pruneTol), interpMat.cols(), interpMat[k]); - /*--- Allocate and set donor information for this target point. ---*/ - target_vertex->SetnDonorPoints(nnz); - target_vertex->Allocate_DonorInfo(); + /*--- Allocate and set donor information for this target point. ---*/ + targetVertex->Allocate_DonorInfo(nnz); - for (unsigned long iVertex = 0, iSet = 0; iVertex < nGlobalVertexDonor; ++iVertex) { - if (fabs(coeff_vec(iVertex)) > 0.0) { - target_vertex->SetInterpDonorProcessor(iSet, DonorProc[iVertex]); - target_vertex->SetInterpDonorPoint(iSet, DonorPoint[iVertex]); - target_vertex->SetDonorCoeff(iSet, coeff_vec(iVertex)); - ++iSet; + 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 } // end SU2_OMP_PARALLEL - /*--- Delete global data that will no longer be used. ---*/ - DonorCoord.resize(0,0); - vector().swap(DonorPoint); - vector().swap(DonorProc); + /*--- 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 - delete[] Buffer_Send_nVertex_Donor; - delete[] Buffer_Receive_nVertex_Donor; - } void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, @@ -460,30 +506,3 @@ int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector thresh) { - coeffSum += coeffs(i); - ++numNonZeros; - } - else coeffs(i) = 0.0; - } - - /*--- Correct remaining coefficients, sum must be 1 for conservation. ---*/ - passivedouble correction = 1.0 / coeffSum; - for (auto i = 0ul; i < coeffs.size(); ++i) coeffs(i) *= correction; - - return numNonZeros; -} diff --git a/Common/src/interface_interpolation/CSlidingMesh.cpp b/Common/src/interface_interpolation/CSlidingMesh.cpp index 2ac685049af6..3fe75ad5c033 100644 --- a/Common/src/interface_interpolation/CSlidingMesh.cpp +++ b/Common/src/interface_interpolation/CSlidingMesh.cpp @@ -412,9 +412,7 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { /*--- 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(); + target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(nDonorPoints); for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){ target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor, Coeff_Vect[iDonor]); @@ -685,8 +683,7 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { /*--- 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(); + 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); diff --git a/Common/src/toolboxes/CSymmetricMatrix.cpp b/Common/src/toolboxes/CSymmetricMatrix.cpp index 8ec0a020ebf7..b8eec72c2111 100644 --- a/Common/src/toolboxes/CSymmetricMatrix.cpp +++ b/Common/src/toolboxes/CSymmetricMatrix.cpp @@ -34,15 +34,13 @@ #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 dsytrf_(char*, int*, passivedouble*, int*, int*, passivedouble*, int*, int*); -extern "C" void dsytri_(char*, int*, passivedouble*, int*, int*, passivedouble*, int*); -extern "C" void dpotrf_(char*, int*, passivedouble*, int*, int*); -extern "C" void dpotri_(char*, int*, passivedouble*, int*, int*); -extern "C" void dsymm_(char*, char*, int*, int*, passivedouble*, passivedouble*, int*, - passivedouble*, int*, passivedouble*, passivedouble*, int*); +/*--- 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*); #endif @@ -283,10 +281,10 @@ void CSymmetricMatrix::MatMatMult(const char side, val_vec.data(), M, mat_in.data(), N, beta, mat_out.data(), N); #else // BLAS/LAPACK /*--- Right and lower because matrices are in row major order. ---*/ - char side = 'R', uplo = 'L'; - passivedouble alpha = 1.0, beta = 0.0; - dsymm_(&side, &uplo, &N, &M, &alpha, const_cast(val_vec.data()), - &M, const_cast(mat_in.data()), &N, &beta, mat_out.data(), &N); + const char side = 'R', uplo = 'L'; + const passivedouble alpha = 1.0, beta = 0.0; + dsymm_(&side, &uplo, &N, &M, &alpha, val_vec.data(), &M, + mat_in.data(), &N, &beta, mat_out.data(), &N); #endif } /*--- Right_side: mat_out = mat_in * this. ---*/ @@ -309,10 +307,10 @@ void CSymmetricMatrix::MatMatMult(const char side, val_vec.data(), N, mat_in.data(), N, beta, mat_out.data(), N); #else // BLAS/LAPACK /*--- Left and lower because matrices are in row major order. ---*/ - char side = 'L', uplo = 'L'; - passivedouble alpha = 1.0, beta = 0.0; - dsymm_(&side, &uplo, &N, &M, &alpha, const_cast(val_vec.data()), - &N, const_cast(mat_in.data()), &N, &beta, mat_out.data(), &N); + const char side = 'L', uplo = 'L'; + const passivedouble alpha = 1.0, beta = 0.0; + dsymm_(&side, &uplo, &N, &M, &alpha, val_vec.data(), &N, + mat_in.data(), &N, &beta, mat_out.data(), &N); #endif } From 9ef625a042087cfabfc1ca6dd697bb6683c75bca Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 19 Mar 2020 21:28:18 +0000 Subject: [PATCH 21/54] fix deallocation bug in C2DContainer for systems with weird aligned_alloc --- Common/include/toolboxes/C2DContainer.hpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/Common/include/toolboxes/C2DContainer.hpp b/Common/include/toolboxes/C2DContainer.hpp index 93f1314766f6..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. @@ -381,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;} @@ -400,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); From faafa22c194f2037ee0276d8339ae6e46bc3f776 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 19 Mar 2020 21:29:11 +0000 Subject: [PATCH 22/54] more CSymmetricMatrix optimization --- Common/include/toolboxes/CSymmetricMatrix.hpp | 43 ++- .../CRadialBasisFunction.cpp | 8 +- Common/src/toolboxes/CSymmetricMatrix.cpp | 246 ++++++++---------- 3 files changed, 131 insertions(+), 166 deletions(-) diff --git a/Common/include/toolboxes/CSymmetricMatrix.hpp b/Common/include/toolboxes/CSymmetricMatrix.hpp index b32613a864ae..853257dbdf54 100644 --- a/Common/include/toolboxes/CSymmetricMatrix.hpp +++ b/Common/include/toolboxes/CSymmetricMatrix.hpp @@ -37,30 +37,15 @@ using namespace std; * 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: - enum DecompositionType { NONE, CHOLESKY, LU }; - - vector val_vec, decomp_vec; - vector perm_vec; - int sz = 0; - bool initialized = false; - DecompositionType decomposed = NONE; - - inline void CheckBounds(int i, int j) const { - assert(initialized && "Matrix not initialized."); - assert(i>=0 && i=0 && j& perm) const; // Matrix inversion using LAPACK routines (LDLT and LLT factorization). void CalcInv_sytri(); void CalcInv_potri(); @@ -71,23 +56,23 @@ class CSymmetricMatrix { void Initialize(int N); - inline int GetSize() const { return sz; } + inline int Size() const { return mat.rows(); } - inline passivedouble Get(int i, int j) const { return val_vec[IdxSym(i,j)]; } + 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) { val_vec[IdxSym(i,j)] = val; } + 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 val_vec[IdxSym(i,j)]; } + 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 val_vec[IdxSym(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 < sz; ++i) { + for (int i = 0; i < Size(); ++i) { *vec_out = 0.0; auto vec = vec_in; - for (int k = 0; k < sz; ++k) + for (int k = 0; k < Size(); ++k) *vec_out += *(vec++) * Get(i,k); ++vec_out; } @@ -95,6 +80,8 @@ class CSymmetricMatrix { void MatMatMult(const char side, const su2passivematrix& mat_in, su2passivematrix& mat_out) const; - void Invert(const bool is_spd); + void Invert(bool is_spd = false); + + su2passivematrix StealData(); }; diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp index f87df1a47460..ff8d7697ed65 100644 --- a/Common/src/interface_interpolation/CRadialBasisFunction.cpp +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -40,6 +40,7 @@ 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 @@ -305,7 +306,7 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { 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); + 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. ---*/ @@ -427,10 +428,7 @@ void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool us else { /*--- No polynomial term used in the interpolation, C_inv_trunc = M^-1. ---*/ - C_inv_trunc.resize(nVertexDonor, nVertexDonor); - for (auto i = 0ul; i < nVertexDonor; ++i) - for (auto j = 0ul; j < nVertexDonor; ++j) - C_inv_trunc(i,j) = global_M(i,j); + C_inv_trunc = global_M.StealData(); } // end usePolynomial diff --git a/Common/src/toolboxes/CSymmetricMatrix.cpp b/Common/src/toolboxes/CSymmetricMatrix.cpp index b8eec72c2111..f5190a8672e0 100644 --- a/Common/src/toolboxes/CSymmetricMatrix.cpp +++ b/Common/src/toolboxes/CSymmetricMatrix.cpp @@ -41,49 +41,44 @@ 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) -{ - sz = N; - val_vec.resize(sz*sz); - initialized = true; -} +void CSymmetricMatrix::Initialize(int N) { mat.resize(N,N); } void CSymmetricMatrix::CholeskyDecompose() { #ifndef HAVE_LAPACK - assert(initialized && "Matrix not initialized."); - - for (int j = 0; j < sz; ++j) { + 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); - assert(sum > 0.0 && "LLT failed, matrix is not SPD."); + if (sum < 0.0) break; // not SPD Set(j, j, sqrt(sum)); - for (int i = j+1; i < sz; ++i) { + 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)); } } - decomposed = CHOLESKY; + if (j!=Size()) SU2_MPI::Error("LLT factorization failed.", CURRENT_FUNCTION); #endif } -void CSymmetricMatrix::LUDecompose() +void CSymmetricMatrix::LUDecompose(su2activematrix& decomp, vector& perm) const { #ifndef HAVE_LAPACK - assert(initialized && "Matrix not initialized."); + const int sz = Size(); /*--- Copy matrix values to LU matrix, init permutation vec. ---*/ - decomp_vec.resize(sz*sz); - perm_vec.resize(sz); + decomp.resize(sz,sz); + perm.resize(sz); + for (int i = 0; i < sz; ++i) { - perm_vec[i] = i; + perm[i] = i; for (int j = i; j < sz; ++j) decomp(j,i) = decomp(i,j) = Get(i,j); } @@ -99,7 +94,7 @@ void CSymmetricMatrix::LUDecompose() } if (pivot_idx != j) { - swap(perm_vec[j], perm_vec[pivot_idx]); + swap(perm[j], perm[pivot_idx]); for (int k = 0; k < sz; ++k) swap(decomp(j,k), decomp(pivot_idx,k)); } @@ -111,116 +106,104 @@ void CSymmetricMatrix::LUDecompose() for (int i = j+1; i < sz; ++i) decomp(i,k) -= decomp(j,k)*decomp(i,j); } - - decomposed = LU; #endif } -void CSymmetricMatrix::CalcInv() +void CSymmetricMatrix::CalcInv(bool is_spd) { #ifndef HAVE_LAPACK - assert(initialized && "Matrix not initialized."); - - /*--- Decompose matrix if not done yet. ---*/ - if (decomposed == NONE) { LUDecompose(); } + const int sz = Size(); /*--- Compute inverse from decomposed matrices. ---*/ - switch (decomposed) { - - case CHOLESKY: - { - /*--- Initialize inverse matrix. ---*/ - vector inv(sz*sz, 0.0); - - /*--- Compute L inverse. ---*/ - /*--- Solve smaller and smaller systems. ---*/ - for (int j = 0; j < sz; ++j) { - /*--- Forward substitution. ---*/ - inv[IdxSym(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[IdxSym(k,j)]; - inv[IdxSym(i,j)] = sum / Get(i,i); - } - } // L inverse in inv - - /*--- Multiply inversed matrices overwrite val_vec. ---*/ - 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[IdxSym(k,i)] * inv[IdxSym(k,j)]; - Set(i, j, sum); - } - - break; - } - - case LU: - { - /*--- Alias val_vec. ---*/ - vector& inv = val_vec; - - /*--- Invert L and U matrices in place. ---*/ - for (int j = 0; j < sz; ++j) { - inv[IdxFull(j,j)] = 1.0 / decomp(j,j); - - for (int i = j+1; i < sz; ++i) { - inv[IdxFull(i,j)] = -decomp(i,j); - inv[IdxFull(j,i)] = -decomp(j,i) * inv[IdxFull(j,j)]; - - for (int k = j+1; k < i; ++k) { - inv[IdxFull(i,j)] -= decomp(i,k) * inv[IdxFull(k,j)]; - inv[IdxFull(j,i)] -= decomp(k,i) * inv[IdxFull(j,k)]; - } - if (j+1 <= i) inv[IdxFull(j,i)] /= decomp(i,i); - } + 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); } - // inverses in val_vec - - /*--- 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[IdxFull(i,k)] * ((k==j)? 1.0 : inv[IdxFull(k,j)]); + } // 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 - /*--- Permute multiplied matrix to recover A_inv, overwrite val_vec. ---*/ + /*--- Multiply U_inv by L_inv, overwrite decomp_vec. ---*/ + for (int i = 0; i < sz; ++i) for (int j = 0; j < sz; ++j) { - int k = perm_vec[j]; - for (int i = k; i < sz; ++i) Set(i, k, decomp(i,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)); } - /*--- Decomposition no longer needed. ---*/ - vector().swap(decomp_vec); - - break; + /*--- 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)); } - default: assert(false && "Default (LU) decomposition failed."); } - - decomposed = NONE; #endif } void CSymmetricMatrix::CalcInv_sytri() { #ifdef HAVE_LAPACK - char uplo = 'L'; + const char uplo = 'L'; + const int sz = Size(); int info; - perm_vec.resize(sz); // ipiv array + vector ipiv(sz); /*--- Query the optimum work size. ---*/ int query = -1; passivedouble tmp; - dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), &tmp, &query, &info); + dsytrf_(&uplo, &sz, mat.data(), &sz, ipiv.data(), &tmp, &query, &info); query = static_cast(tmp); - decomp_vec.resize(query); // work array + vector work(query); /*--- Factorize and invert. ---*/ - dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &query, &info); + 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, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &info); + dsytri_(&uplo, &sz, mat.data(), &sz, ipiv.data(), work.data(), &info); if (info!=0) SU2_MPI::Error("Inversion with LDLT factorization failed.", CURRENT_FUNCTION); decomposed = NONE; @@ -230,12 +213,13 @@ void CSymmetricMatrix::CalcInv_sytri() void CSymmetricMatrix::CalcInv_potri() { #ifdef HAVE_LAPACK - char uplo = 'L'; + const char uplo = 'L'; + const int sz = Size(); int info; - dpotrf_(&uplo, &sz, val_vec.data(), &sz, &info); + dpotrf_(&uplo, &sz, mat.data(), &sz, &info); if (info!=0) SU2_MPI::Error("LLT factorization failed.", CURRENT_FUNCTION); - dpotri_(&uplo, &sz, val_vec.data(), &sz, &info); + dpotri_(&uplo, &sz, mat.data(), &sz, &info); if (info!=0) SU2_MPI::Error("Inversion with LLT factorization failed.", CURRENT_FUNCTION); decomposed = NONE; @@ -248,9 +232,7 @@ void CSymmetricMatrix::Invert(const bool is_spd) if(is_spd) CalcInv_potri(); else CalcInv_sytri(); #else - if(!is_spd) LUDecompose(); - else CholeskyDecompose(); - CalcInv(); + CalcInv(is_spd); #endif } @@ -258,60 +240,58 @@ void CSymmetricMatrix::MatMatMult(const char side, const su2passivematrix& mat_in, su2passivematrix& mat_out) const { - /*--- Assumes row major storage of in/out matrices for LAPACK. ---*/ - static_assert(su2passivematrix::Storage == StorageType::RowMajor,""); - /*--- Left side: mat_out = this * mat_in. ---*/ if (side == 'L' || side == 'l') { - int M = sz, N = mat_in.cols(); + const int M = Size(), N = mat_in.cols(); assert(M == mat_in.rows()); mat_out.resize(M,N); -#if !defined(HAVE_LAPACK) // Naive product +#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); } -#elif defined(HAVE_MKL) - passivedouble alpha = 1.0, beta = 0.0; - cblas_dsymm(CblasRowMajor, CblasLeft, CblasUpper, M, N, alpha, - val_vec.data(), M, mat_in.data(), N, beta, mat_out.data(), N); -#else // BLAS/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, val_vec.data(), &M, - mat_in.data(), &N, &beta, mat_out.data(), &N); #endif } /*--- Right_side: mat_out = mat_in * this. ---*/ else { - int M = mat_in.rows(), N = sz; + const int M = mat_in.rows(), N = Size(); assert(N == mat_in.cols()); mat_out.resize(M,N); -#if !defined(HAVE_LAPACK) // Naive product +#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); } -#elif defined(HAVE_MKL) - passivedouble alpha = 1.0, beta = 0.0; - cblas_dsymm(CblasRowMajor, CblasRight, CblasUpper, M, N, alpha, - val_vec.data(), N, mat_in.data(), N, beta, mat_out.data(), N); -#else // BLAS/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, val_vec.data(), &N, - mat_in.data(), &N, &beta, mat_out.data(), &N); #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); } From 72da53230a6dffa15b3ecf4b95d2d1e893324f27 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 19 Mar 2020 22:33:22 +0000 Subject: [PATCH 23/54] fix AD build --- Common/include/toolboxes/CSymmetricMatrix.hpp | 2 +- Common/src/toolboxes/CSymmetricMatrix.cpp | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Common/include/toolboxes/CSymmetricMatrix.hpp b/Common/include/toolboxes/CSymmetricMatrix.hpp index 853257dbdf54..bc15264c7049 100644 --- a/Common/include/toolboxes/CSymmetricMatrix.hpp +++ b/Common/include/toolboxes/CSymmetricMatrix.hpp @@ -45,7 +45,7 @@ class CSymmetricMatrix { // Not optimized dense matrix factorization and inversion for portability. void CalcInv(bool is_spd); void CholeskyDecompose(); - void LUDecompose(su2activematrix& decomp, vector& perm) const; + void LUDecompose(su2passivematrix& decomp, vector& perm) const; // Matrix inversion using LAPACK routines (LDLT and LLT factorization). void CalcInv_sytri(); void CalcInv_potri(); diff --git a/Common/src/toolboxes/CSymmetricMatrix.cpp b/Common/src/toolboxes/CSymmetricMatrix.cpp index f5190a8672e0..5845daac5496 100644 --- a/Common/src/toolboxes/CSymmetricMatrix.cpp +++ b/Common/src/toolboxes/CSymmetricMatrix.cpp @@ -68,7 +68,7 @@ void CSymmetricMatrix::CholeskyDecompose() #endif } -void CSymmetricMatrix::LUDecompose(su2activematrix& decomp, vector& perm) const +void CSymmetricMatrix::LUDecompose(su2passivematrix& decomp, vector& perm) const { #ifndef HAVE_LAPACK const int sz = Size(); @@ -205,8 +205,6 @@ void CSymmetricMatrix::CalcInv_sytri() 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); - - decomposed = NONE; #endif } @@ -221,8 +219,6 @@ void CSymmetricMatrix::CalcInv_potri() 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); - - decomposed = NONE; #endif } From 6864175cea2601be8cdfccbcbf6e2dbed003be09 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 19 Mar 2020 23:29:26 +0000 Subject: [PATCH 24/54] basic interpolation statistics and error checking --- .../interface_interpolation/CInterpolator.hpp | 7 +++- .../CRadialBasisFunction.hpp | 8 +++++ .../CRadialBasisFunction.cpp | 36 +++++++++++++++++++ SU2_CFD/src/drivers/CDriver.cpp | 16 ++++----- 4 files changed, 56 insertions(+), 11 deletions(-) diff --git a/Common/include/interface_interpolation/CInterpolator.hpp b/Common/include/interface_interpolation/CInterpolator.hpp index 6da4e028c4f2..af95221275f0 100644 --- a/Common/include/interface_interpolation/CInterpolator.hpp +++ b/Common/include/interface_interpolation/CInterpolator.hpp @@ -103,7 +103,7 @@ class CInterpolator { CInterpolator(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone); /*! - * \brief No default construction allowed. + * \brief No default construction allowed to force zones and geometry to always be set. */ CInterpolator(void) = delete; @@ -119,6 +119,11 @@ class CInterpolator { */ 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. diff --git a/Common/include/interface_interpolation/CRadialBasisFunction.hpp b/Common/include/interface_interpolation/CRadialBasisFunction.hpp index c5f9cb19e856..01c056ac9b84 100644 --- a/Common/include/interface_interpolation/CRadialBasisFunction.hpp +++ b/Common/include/interface_interpolation/CRadialBasisFunction.hpp @@ -34,6 +34,9 @@ * \brief Radial basis function interpolation. */ class CRadialBasisFunction final : public CInterpolator { +private: + unsigned long MinDonors = 0, AvgDonors = 0, MaxDonors = 0; + public: static_assert(su2passivematrix::Storage == StorageType::RowMajor, "This class does not work otherwise."); @@ -53,6 +56,11 @@ class CRadialBasisFunction final : public CInterpolator { */ 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 diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp index ff8d7697ed65..8d503ecaca3c 100644 --- a/Common/src/interface_interpolation/CRadialBasisFunction.cpp +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -49,6 +49,12 @@ CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, con Set_TransferCoeff(config); } +void CRadialBasisFunction::PrintStatistics() const { + if (rank == MASTER_NODE) + cout << "Min / avg / max number of RBF donors per target point: " + << MinDonors << " / " << AvgDonors << " / " << MaxDonors << endl; +} + su2double CRadialBasisFunction::Get_RadialBasisValue(ENUM_RADIALBASIS type, const su2double radius, const su2double dist) { su2double rbf = dist/radius; @@ -188,6 +194,9 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { /*--- Final loop over interface markers to compute the interpolation coefficients. ---*/ + unsigned long totalTargetPoints = 0, totalDonorPoints = 0; + MinDonors = 1<<30; MaxDonors = 0; + for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; iMarkerInt++) { /*--- Identify the rank that computed the interpolation matrix for this marker. ---*/ @@ -256,6 +265,7 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { } } nVertexTarget = targetVertices.size(); + totalTargetPoints += nVertexTarget; /*--- Distribute target slabs over the threads in the rank for processing. ---*/ @@ -267,6 +277,8 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { su2passivematrix funcMat(targetSlabSize, 1+nPolynomial+nGlobalVertexDonor); su2passivematrix interpMat(targetSlabSize, nGlobalVertexDonor); + unsigned long minDonors = 1<<30, maxDonors = 0, totalDonors = 0; + SU2_OMP_FOR_DYN(1) for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget += targetSlabSize) { @@ -323,6 +335,9 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { /*--- Prune small coefficients. ---*/ auto nnz = PruneSmallCoefficients(SU2_TYPE::GetValue(pruneTol), interpMat.cols(), interpMat[k]); + totalDonors += nnz; + minDonors = min(minDonors, nnz); + maxDonors = max(maxDonors, nnz); /*--- Allocate and set donor information for this target point. ---*/ targetVertex->Allocate_DonorInfo(nnz); @@ -338,6 +353,13 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { } } } // end target vertex loop + + SU2_OMP_CRITICAL + { + totalDonorPoints += totalDonors; + MinDonors = min(MinDonors, minDonors); + MaxDonors = max(MaxDonors, maxDonors); + } } // end SU2_OMP_PARALLEL /*--- Free global data that will no longer be used. ---*/ @@ -348,6 +370,20 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { } // end loop over interface markers + /*--- Final reduction of interpolation statistics. ---*/ + auto Reduce = [](SU2_MPI::Op op, unsigned long &val) { + auto tmp = val; + SU2_MPI::Reduce(&tmp, &val, 1, MPI_UNSIGNED_LONG, op, MASTER_NODE, MPI_COMM_WORLD); + }; + Reduce(MPI_SUM, totalTargetPoints); + Reduce(MPI_SUM, totalDonorPoints); + Reduce(MPI_MIN, MinDonors); + Reduce(MPI_MAX, MaxDonors); + AvgDonors = passivedouble(totalDonorPoints) / totalTargetPoints; + if (MinDonors == 0) + SU2_MPI::Error("One or more target points have no donors, either:\n" + " - The RBF radius is too small.\n" + " - The pruning tolerance is too aggressive.", CURRENT_FUNCTION); } void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, diff --git a/SU2_CFD/src/drivers/CDriver.cpp b/SU2_CFD/src/drivers/CDriver.cpp index 0d17063686a1..aec0ab14f9d2 100644 --- a/SU2_CFD/src/drivers/CDriver.cpp +++ b/SU2_CFD/src/drivers/CDriver.cpp @@ -100,8 +100,6 @@ CDriver::CDriver(char* confFile, unsigned short val_nZone, SU2_Comm MPICommunica #endif #endif - unsigned short jZone; - SU2_MPI::SetComm(MPICommunicator); rank = SU2_MPI::GetRank(); @@ -149,13 +147,8 @@ CDriver::CDriver(char* confFile, unsigned short val_nZone, SU2_Comm MPICommunica /*--- 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++){ @@ -212,7 +205,7 @@ CDriver::CDriver(char* confFile, unsigned short val_nZone, SU2_Comm MPICommunica /*--- 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]); @@ -2681,6 +2674,9 @@ void CDriver::Interface_Preprocessing(CConfig **config, CSolver***** solver, CGe break; } + if (interpolation[donorZone][targetZone]) + interpolation[donorZone][targetZone]->PrintStatistics(); + /*--- Initialize the appropriate transfer strategy ---*/ if (rank == MASTER_NODE) cout << "Transferring "; From f48e235a612b90b273cb331e62578bbca0ba5de5 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Fri, 20 Mar 2020 10:57:32 +0000 Subject: [PATCH 25/54] cleaner computation of RBF polynomial terms --- .../CRadialBasisFunction.hpp | 5 +- .../CRadialBasisFunction.cpp | 99 +++++++++++-------- 2 files changed, 62 insertions(+), 42 deletions(-) diff --git a/Common/include/interface_interpolation/CRadialBasisFunction.hpp b/Common/include/interface_interpolation/CRadialBasisFunction.hpp index 01c056ac9b84..7d75460ea392 100644 --- a/Common/include/interface_interpolation/CRadialBasisFunction.hpp +++ b/Common/include/interface_interpolation/CRadialBasisFunction.hpp @@ -34,12 +34,13 @@ * \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; public: - static_assert(su2passivematrix::Storage == StorageType::RowMajor, - "This class does not work otherwise."); /*! * \brief Constructor of the class. * \param[in] geometry - Geometrical definition of the problem. diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp index 8d503ecaca3c..9bbce09ee271 100644 --- a/Common/src/interface_interpolation/CRadialBasisFunction.cpp +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -50,9 +50,13 @@ CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, con } void CRadialBasisFunction::PrintStatistics() const { - if (rank == MASTER_NODE) - cout << "Min / avg / max number of RBF donors per target point: " - << MinDonors << " / " << AvgDonors << " / " << MaxDonors << endl; + if (rank == MASTER_NODE) { + cout.precision(3); + cout << " Min/avg/max number of RBF donors per target point: " + << MinDonors << "/" << AvgDonors << "/" << MaxDonors + << "\n Interpolation matrix is " << Density << "% dense." << endl; + cout.unsetf(ios::floatfield); + } } su2double CRadialBasisFunction::Get_RadialBasisValue(ENUM_RADIALBASIS type, const su2double radius, const su2double dist) @@ -128,8 +132,8 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { Determine_ArraySize(false, markDonor, markTarget, nVertexDonor, nDim); /*--- Compute total number of donor vertices. ---*/ - auto nGlobalVertexDonor = accumulate(Buffer_Receive_nVertex_Donor, - Buffer_Receive_nVertex_Donor+nProcessor, 0ul); + 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 ]; @@ -194,7 +198,8 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { /*--- Final loop over interface markers to compute the interpolation coefficients. ---*/ - unsigned long totalTargetPoints = 0, totalDonorPoints = 0; + /*--- Initialize variables for interpolation statistics. ---*/ + unsigned long totalTargetPoints = 0, totalDonorPoints = 0, denseSize = 0; MinDonors = 1<<30; MaxDonors = 0; for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; iMarkerInt++) { @@ -266,6 +271,7 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { } nVertexTarget = targetVertices.size(); totalTargetPoints += nVertexTarget; + denseSize += nVertexTarget*nGlobalVertexDonor; /*--- Distribute target slabs over the threads in the rank for processing. ---*/ @@ -277,6 +283,7 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { su2passivematrix funcMat(targetSlabSize, 1+nPolynomial+nGlobalVertexDonor); su2passivematrix interpMat(targetSlabSize, nGlobalVertexDonor); + /*--- Thread-local variables for statistics. ---*/ unsigned long minDonors = 1<<30, maxDonors = 0, totalDonors = 0; SU2_OMP_FOR_DYN(1) @@ -370,20 +377,29 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { } // end loop over interface markers - /*--- Final reduction of interpolation statistics. ---*/ + /*--- Final reduction of interpolation statistics and basic sanity checks. ---*/ auto Reduce = [](SU2_MPI::Op op, unsigned long &val) { auto tmp = val; - SU2_MPI::Reduce(&tmp, &val, 1, MPI_UNSIGNED_LONG, op, MASTER_NODE, MPI_COMM_WORLD); + 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); - AvgDonors = passivedouble(totalDonorPoints) / totalTargetPoints; + + 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); + + AvgDonors = totalDonorPoints / totalTargetPoints; + Density = totalDonorPoints / (0.01*denseSize); + } void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, @@ -392,14 +408,14 @@ void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool us const su2double interfaceCoordTol = 1e6 * numeric_limits::epsilon(); - const auto nVertexDonor = coords.rows(); + const int nVertexDonor = coords.rows(); const int nDim = coords.cols(); /*--- Populate interpolation kernel. ---*/ CSymmetricMatrix global_M(nVertexDonor); - for (auto iVertex = 0ul; iVertex < nVertexDonor; ++iVertex) - for (auto jVertex = iVertex; jVertex < nVertexDonor; ++jVertex) + 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]))); @@ -413,7 +429,7 @@ void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool us /*--- Fill P matrix (P for points, with an extra top row of ones). ---*/ su2passivematrix P(1+nDim, nVertexDonor); - for (auto iVertex = 0ul; iVertex < nVertexDonor; iVertex++) { + 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)); @@ -422,44 +438,47 @@ void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool us /*--- Check if points lie on a plane and remove one coordinate from P if so. ---*/ nPolynomial = CheckPolynomialTerms(interfaceCoordTol, keepPolynomialRow, P); - /*--- Compute Mp = (P * M^-1 * P^T)^-1 ---*/ - CSymmetricMatrix Mp(nPolynomial+1); + /*--- Compute Q = P * M^-1 ---*/ + su2passivematrix Q; + global_M.MatMatMult('R', P, Q); - su2passivematrix tmp; - global_M.MatMatMult('R', P, tmp); // tmp = P * M^-1 + /*--- Compute Mp = (Q * P^T)^-1 ---*/ + CSymmetricMatrix Mp(nPolynomial+1); - for (int i = 0; i <= nPolynomial; ++i) // Mp = tmp * P + for (int i = 0; i <= nPolynomial; ++i) for (int j = i; j <= nPolynomial; ++j) { Mp(i,j) = 0.0; - for (auto k = 0ul; k < nVertexDonor; ++k) Mp(i,j) += tmp(i,k) * P(j,k); + for (int k = 0; k < nVertexDonor; ++k) + Mp(i,j) += Q(i,k) * P(j,k); } - Mp.Invert(false); // Mp = Mp^-1 + Mp.Invert(false); - /*--- Compute M_p * P * M^-1, the top part of C_inv_trunc. ---*/ - Mp.MatMatMult('L', P, tmp); + /*--- Compute M_p * Q, the top part of C_inv_trunc. ---*/ su2passivematrix C_inv_top; - global_M.MatMatMult('R', tmp, C_inv_top); + Mp.MatMatMult('L', Q, C_inv_top); - /*--- Compute tmp = (I - P^T * M_p * P * M^-1), part of the bottom part of - C_inv_trunc. Note that most of the product is known from the top part. ---*/ - tmp.resize(nVertexDonor, nVertexDonor); - - for (auto i = 0ul; i < nVertexDonor; ++i) { - for (auto j = 0ul; j < nVertexDonor; ++j) { - tmp(i,j) = 0.0; - for (int k = 0; k <= nPolynomial; ++k) tmp(i,j) -= P(k,i) * C_inv_top(k,j); - } - tmp(i,i) += 1.0; // identity part - } - - /*--- Compute M^-1 * (I - P^T * M_p * P * M^-1), finalize bottom of C_inv_trunc. ---*/ - global_M.MatMatMult('L', tmp, C_inv_trunc); + /*--- 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. ---*/ - tmp = move(C_inv_trunc); + /*--- 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], tmp.data(), tmp.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. ---*/ From 2af4b88f841da8e9e904bc8dc0147725b2161bdb Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Fri, 20 Mar 2020 15:59:54 +0000 Subject: [PATCH 26/54] fix bug in pruning of coefficients, more interp stats --- .../CRadialBasisFunction.hpp | 15 ++++---- .../CRadialBasisFunction.cpp | 38 +++++++++++++------ 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/Common/include/interface_interpolation/CRadialBasisFunction.hpp b/Common/include/interface_interpolation/CRadialBasisFunction.hpp index 7d75460ea392..48416ae33053 100644 --- a/Common/include/interface_interpolation/CRadialBasisFunction.hpp +++ b/Common/include/interface_interpolation/CRadialBasisFunction.hpp @@ -38,7 +38,7 @@ class CRadialBasisFunction final : public CInterpolator { "This class relies on row major storage throughout."); private: unsigned long MinDonors = 0, AvgDonors = 0, MaxDonors = 0; - passivedouble Density = 0.0; + passivedouble Density = 0.0, AvgCorrection = 0.0, MaxCorrection = 0.0; public: /*! @@ -99,22 +99,23 @@ class CRadialBasisFunction final : public CInterpolator { */ 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. + * \return Number of non-zero coefficients after pruning and correction factor. */ - template - static Int PruneSmallCoefficients(Float tolerance, Int size, ForwardIt coeffs) { + 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 = max(thresh, fabs(*(end++))); thresh *= tolerance; /*--- Prune and count non-zeros. ---*/ @@ -130,9 +131,9 @@ class CRadialBasisFunction final : public CInterpolator { /*--- Correct remaining coefficients, sum must be 1 for conservation. ---*/ Float correction = 1.0 / coeffSum; - for (auto it = coeffs; it != end; ++it) *it *= correction; + while (coeffs != end) *(coeffs++) *= correction; - return numNonZeros; + return make_pair(numNonZeros, correction); } }; diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp index 9bbce09ee271..7938efb4a364 100644 --- a/Common/src/interface_interpolation/CRadialBasisFunction.cpp +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -50,13 +50,16 @@ CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, con } void CRadialBasisFunction::PrintStatistics() const { - if (rank == MASTER_NODE) { - cout.precision(3); - cout << " Min/avg/max number of RBF donors per target point: " - << MinDonors << "/" << AvgDonors << "/" << MaxDonors - << "\n Interpolation matrix is " << Density << "% dense." << endl; - cout.unsetf(ios::floatfield); - } + 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) @@ -200,7 +203,7 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { /*--- Initialize variables for interpolation statistics. ---*/ unsigned long totalTargetPoints = 0, totalDonorPoints = 0, denseSize = 0; - MinDonors = 1<<30; MaxDonors = 0; + MinDonors = 1<<30; MaxDonors = 0; MaxCorrection = 0.0; AvgCorrection = 0.0; for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; iMarkerInt++) { @@ -212,7 +215,7 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { /*--- Setup target information. ---*/ const int markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt+1); unsigned long nVertexTarget = 0; - if(markTarget != -1) nVertexTarget = target_geometry->GetnVertex(markTarget); + if (markTarget != -1) nVertexTarget = target_geometry->GetnVertex(markTarget); /*--- Set references to donor information. ---*/ auto& donorCoord = donorCoordinates[iMarkerInt]; @@ -285,6 +288,7 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { /*--- 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) { @@ -341,10 +345,14 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { auto targetVertex = targetVertices[iVertexTarget+k]; /*--- Prune small coefficients. ---*/ - auto nnz = PruneSmallCoefficients(SU2_TYPE::GetValue(pruneTol), interpMat.cols(), interpMat[k]); + 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); @@ -366,6 +374,8 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { totalDonorPoints += totalDonors; MinDonors = min(MinDonors, minDonors); MaxDonors = max(MaxDonors, maxDonors); + AvgCorrection += sumCorr; + MaxCorrection = max(MaxCorrection, maxCorr); } } // end SU2_OMP_PARALLEL @@ -387,7 +397,11 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { 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); @@ -397,6 +411,8 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { " - 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); From 41598d88a57af0272516176557d376e751ba2df1 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Mon, 23 Mar 2020 12:05:39 +0000 Subject: [PATCH 27/54] some indentation in CConfig --- Common/src/CConfig.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index 4f0328015ace..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; @@ -3827,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){ From d67df2980e54f873e4d135b86881a9780548b3dd Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Mon, 23 Mar 2020 19:50:48 +0000 Subject: [PATCH 28/54] cleanup CFEAIteration --- SU2_CFD/src/iteration_structure.cpp | 474 +++++++++++++--------------- 1 file changed, 212 insertions(+), 262 deletions(-) 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); From 429ff42aeaedb7a69082f27760acd8bdbc9e2478 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 24 Mar 2020 09:34:47 +0000 Subject: [PATCH 29/54] nearest neighbor stats --- .../CNearestNeighbor.hpp | 8 +++++ .../CNearestNeighbor.cpp | 31 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/Common/include/interface_interpolation/CNearestNeighbor.hpp b/Common/include/interface_interpolation/CNearestNeighbor.hpp index c8efe3998e40..c86d50add484 100644 --- a/Common/include/interface_interpolation/CNearestNeighbor.hpp +++ b/Common/include/interface_interpolation/CNearestNeighbor.hpp @@ -35,6 +35,9 @@ * by using a kd-tree. */ class CNearestNeighbor final : public CInterpolator { +private: + su2double AvgDistance = 0.0, MaxDistance = 0.0; + public: /*! * \brief Constructor of the class. @@ -51,4 +54,9 @@ class CNearestNeighbor final : public CInterpolator { */ void Set_TransferCoeff(const CConfig* const* config) override; + /*! + * \brief Print interpolation statistics. + */ + void PrintStatistics(void) const override; + }; diff --git a/Common/src/interface_interpolation/CNearestNeighbor.cpp b/Common/src/interface_interpolation/CNearestNeighbor.cpp index 3e0a7ee24170..e1369f19aef0 100644 --- a/Common/src/interface_interpolation/CNearestNeighbor.cpp +++ b/Common/src/interface_interpolation/CNearestNeighbor.cpp @@ -45,11 +45,17 @@ CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, const CConf 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; @@ -62,6 +68,9 @@ void CNearestNeighbor::Set_TransferCoeff(const CConfig* const* config) { /*--- 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. ---*/ @@ -98,6 +107,9 @@ void CNearestNeighbor::Set_TransferCoeff(const CConfig* const* config) { 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++) { @@ -126,6 +138,12 @@ void CNearestNeighbor::Set_TransferCoeff(const CConfig* const* config) { 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) { @@ -142,6 +160,12 @@ void CNearestNeighbor::Set_TransferCoeff(const CConfig* const* config) { 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; @@ -154,4 +178,11 @@ void CNearestNeighbor::Set_TransferCoeff(const CConfig* const* config) { 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; + } From ae7fae5b95126287bdaa831e4108588f022074ce Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 24 Mar 2020 10:20:43 +0000 Subject: [PATCH 30/54] simplify "initial_calc" logic of CFEASolver to avoid confusing user errors --- SU2_CFD/include/solvers/CFEASolver.hpp | 1 + .../integration/CStructuralIntegration.cpp | 23 +++--------- SU2_CFD/src/solvers/CFEASolver.cpp | 37 ++++++++----------- 3 files changed, 21 insertions(+), 40 deletions(-) diff --git a/SU2_CFD/include/solvers/CFEASolver.hpp b/SU2_CFD/include/solvers/CFEASolver.hpp index 948ef934ffef..eae891063a05 100644 --- a/SU2_CFD/include/solvers/CFEASolver.hpp +++ b/SU2_CFD/include/solvers/CFEASolver.hpp @@ -87,6 +87,7 @@ class CFEASolver : public CSolver { 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. */ 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/solvers/CFEASolver.cpp b/SU2_CFD/src/solvers/CFEASolver.cpp index 3819e0b713a1..28e338731015 100644 --- a/SU2_CFD/src/solvers/CFEASolver.cpp +++ b/SU2_CFD/src/solvers/CFEASolver.cpp @@ -97,8 +97,9 @@ CFEASolver::CFEASolver(bool mesh_deform_mode) : CSolver(mesh_deform_mode) { element_container[iTerm] = new CElement* [MAX_FE_KINDS*omp_get_max_threads()](); topol_filter_applied = false; - element_based = false; + initial_calc = true; + } CFEASolver::CFEASolver(CGeometry *geometry, CConfig *config) : CSolver() { @@ -112,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(); @@ -751,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 @@ -802,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. ---*/ @@ -819,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) { From 01d8531fd848828236c41ba5a04c9484b330e777 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 25 Mar 2020 07:57:56 +0000 Subject: [PATCH 31/54] fix, CFL_REDUCTION_TURB had no effect --- SU2_CFD/src/solvers/CTurbSASolver.cpp | 4 ++-- SU2_CFD/src/solvers/CTurbSSTSolver.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp index 9d78a28e2b13..3948a6e182b0 100644 --- a/SU2_CFD/src/solvers/CTurbSASolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp @@ -225,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); } diff --git a/SU2_CFD/src/solvers/CTurbSSTSolver.cpp b/SU2_CFD/src/solvers/CTurbSSTSolver.cpp index 41fc01aa4890..96cab040c155 100644 --- a/SU2_CFD/src/solvers/CTurbSSTSolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSSTSolver.cpp @@ -226,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); } From 3553116797ac147a7bee493dd96c0e3d0635d671 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 26 Mar 2020 08:23:58 +0000 Subject: [PATCH 32/54] update testcases after re-enabling option CFL_REDUCTION_TURB --- .../transonic_stator_2D/transonic_stator.cfg | 4 +--- .../harmonic_balance/hb_rans_preconditioning/davis.cfg | 2 +- TestCases/parallel_regression.py | 4 ++-- TestCases/serial_regression.py | 4 ++-- TestCases/sliding_interface/bars_SST_2D/bars.cfg | 2 +- .../sliding_interface/single_stage/single_stage_NN.cfg | 7 +------ .../sliding_interface/single_stage/single_stage_WA.cfg | 8 +------- TestCases/turbomachinery/axial_stage_2D/Axial_stage2D.cfg | 4 +--- .../centrifugal_blade/centrifugal_blade.cfg | 5 +---- .../centrifugal_stage/centrifugal_stage.cfg | 5 +---- .../transonic_stator_2D/transonic_stator.cfg | 4 +--- .../transonic_stator_2D/transonic_stator_rst.cfg | 4 +--- 12 files changed, 14 insertions(+), 39 deletions(-) diff --git a/TestCases/disc_adj_turbomachinery/transonic_stator_2D/transonic_stator.cfg b/TestCases/disc_adj_turbomachinery/transonic_stator_2D/transonic_stator.cfg index b78895338f5f..8c2c470dcb61 100644 --- a/TestCases/disc_adj_turbomachinery/transonic_stator_2D/transonic_stator.cfg +++ b/TestCases/disc_adj_turbomachinery/transonic_stator_2D/transonic_stator.cfg @@ -277,9 +277,7 @@ SLOPE_LIMITER_TURB= VENKATAKRISHNAN TIME_DISCRE_TURB= EULER_IMPLICIT % % Reduction factor of the CFL coefficient in the turbulence problem -CFL_REDUCTION_TURB= 0.1 -% -% Relaxation coefficient +CFL_REDUCTION_TURB= 1.0 % % % ----------------------- DESIGN VARIABLE PARAMETERS --------------------------% diff --git a/TestCases/harmonic_balance/hb_rans_preconditioning/davis.cfg b/TestCases/harmonic_balance/hb_rans_preconditioning/davis.cfg index 6a41168df502..3463d40b379d 100644 --- a/TestCases/harmonic_balance/hb_rans_preconditioning/davis.cfg +++ b/TestCases/harmonic_balance/hb_rans_preconditioning/davis.cfg @@ -193,7 +193,7 @@ MUSCL_TURB= NO TIME_DISCRE_TURB= EULER_IMPLICIT % % Reduction factor of the CFL coefficient in the turbulence problem -CFL_REDUCTION_TURB= 0.4 +CFL_REDUCTION_TURB= 1.0 % --------------------------- CONVERGENCE PARAMETERS --------------------------% % Convergence criteria (CAUCHY, RESIDUAL) diff --git a/TestCases/parallel_regression.py b/TestCases/parallel_regression.py index 4475490dd1c6..691f5502810e 100644 --- a/TestCases/parallel_regression.py +++ b/TestCases/parallel_regression.py @@ -834,7 +834,7 @@ def main(): Jones_tc.cfg_dir = "turbomachinery/APU_turbocharger" Jones_tc.cfg_file = "Jones.cfg" Jones_tc.test_iter = 5 - Jones_tc.test_vals = [-5.316335, 0.355081, 44.772280, 2.269966] #last 4 columns + Jones_tc.test_vals = [-5.290553, 0.372874, 44.765930, 2.270197] #last 4 columns Jones_tc.su2_exec = "parallel_computation.py -f" Jones_tc.timeout = 1600 Jones_tc.new_output = False @@ -846,7 +846,7 @@ def main(): Jones_tc_rst.cfg_dir = "turbomachinery/APU_turbocharger" Jones_tc_rst.cfg_file = "Jones_rst.cfg" Jones_tc_rst.test_iter = 5 - Jones_tc_rst.test_vals = [-3.034157, 0.013763, 82.263700, 2.792251] #last 4 columns + Jones_tc_rst.test_vals = [-3.030374, 0.017344, 82.263660, 2.792178] #last 4 columns Jones_tc_rst.su2_exec = "parallel_computation.py -f" Jones_tc_rst.timeout = 1600 Jones_tc_rst.new_output = False diff --git a/TestCases/serial_regression.py b/TestCases/serial_regression.py index f57084dc5757..e3f0ceca73d4 100644 --- a/TestCases/serial_regression.py +++ b/TestCases/serial_regression.py @@ -983,7 +983,7 @@ def main(): Jones_tc.cfg_dir = "turbomachinery/APU_turbocharger" Jones_tc.cfg_file = "Jones.cfg" Jones_tc.test_iter = 5 - Jones_tc.test_vals = [-5.316334, 0.355080, 44.772250, 2.269935] #last 4 columns + Jones_tc.test_vals = [-5.290552, 0.372873, 44.765920, 2.270165] #last 4 columns Jones_tc.su2_exec = "SU2_CFD" Jones_tc.new_output = False Jones_tc.timeout = 1600 @@ -995,7 +995,7 @@ def main(): Jones_tc_rst.cfg_dir = "turbomachinery/APU_turbocharger" Jones_tc_rst.cfg_file = "Jones_rst.cfg" Jones_tc_rst.test_iter = 5 - Jones_tc_rst.test_vals = [-3.034158, 0.013762, 82.263710, 2.792251] #last 4 columns + Jones_tc_rst.test_vals = [-3.030374, 0.017342, 82.263670, 2.792178] #last 4 columns Jones_tc_rst.su2_exec = "SU2_CFD" Jones_tc_rst.new_output = False Jones_tc_rst.timeout = 1600 diff --git a/TestCases/sliding_interface/bars_SST_2D/bars.cfg b/TestCases/sliding_interface/bars_SST_2D/bars.cfg index 6778a99dec54..ee4c56a856ba 100644 --- a/TestCases/sliding_interface/bars_SST_2D/bars.cfg +++ b/TestCases/sliding_interface/bars_SST_2D/bars.cfg @@ -252,7 +252,7 @@ SLOPE_LIMITER_TURB= NONE TIME_DISCRE_TURB= EULER_IMPLICIT % % Reduction factor of the CFL coefficient in the turbulence problem -CFL_REDUCTION_TURB= 0.10 +CFL_REDUCTION_TURB= 1.0 % --------------------------- CONVERGENCE PARAMETERS --------------------------% % diff --git a/TestCases/sliding_interface/single_stage/single_stage_NN.cfg b/TestCases/sliding_interface/single_stage/single_stage_NN.cfg index e8bc15224ebd..1abaa6406bdf 100644 --- a/TestCases/sliding_interface/single_stage/single_stage_NN.cfg +++ b/TestCases/sliding_interface/single_stage/single_stage_NN.cfg @@ -217,12 +217,7 @@ SLOPE_LIMITER_TURB= VENKATAKRISHNAN TIME_DISCRE_TURB= EULER_IMPLICIT % % Reduction factor of the CFL coefficient in the turbulence problem -CFL_REDUCTION_TURB= 0.01 -% -% Relaxation coefficient -% -% -% +CFL_REDUCTION_TURB= 1.0 % % --------------------------- CONVERGENCE PARAMETERS --------------------------% % Convergence criteria (CAUCHY, RESIDUAL) diff --git a/TestCases/sliding_interface/single_stage/single_stage_WA.cfg b/TestCases/sliding_interface/single_stage/single_stage_WA.cfg index 69d11c02dfdf..97bb4f23ef55 100644 --- a/TestCases/sliding_interface/single_stage/single_stage_WA.cfg +++ b/TestCases/sliding_interface/single_stage/single_stage_WA.cfg @@ -194,9 +194,6 @@ SLOPE_LIMITER_FLOW= NONE % Time discretization (RUNGE-KUTTA_EXPLICIT, EULER_IMPLICIT, EULER_EXPLICIT) TIME_DISCRE_FLOW= EULER_IMPLICIT % -% Relaxation coefficient -% -% % % % -------------------- TURBULENT NUMERICAL METHOD DEFINITION ------------------% @@ -215,10 +212,7 @@ SLOPE_LIMITER_TURB= VENKATAKRISHNAN TIME_DISCRE_TURB= EULER_IMPLICIT % % Reduction factor of the CFL coefficient in the turbulence problem -CFL_REDUCTION_TURB= 0.01 -% -% Relaxation coefficient -% +CFL_REDUCTION_TURB= 1.0 % % % diff --git a/TestCases/turbomachinery/axial_stage_2D/Axial_stage2D.cfg b/TestCases/turbomachinery/axial_stage_2D/Axial_stage2D.cfg index dc86b6875204..4cedf5e074f3 100755 --- a/TestCases/turbomachinery/axial_stage_2D/Axial_stage2D.cfg +++ b/TestCases/turbomachinery/axial_stage_2D/Axial_stage2D.cfg @@ -260,9 +260,7 @@ SLOPE_LIMITER_TURB= VENKATAKRISHNAN TIME_DISCRE_TURB= EULER_IMPLICIT % % Reduction factor of the CFL coefficient in the turbulence problem -CFL_REDUCTION_TURB= 0.5 -% -% Relaxation coefficient +CFL_REDUCTION_TURB= 1.0 % % % --------------------------- CONVERGENCE PARAMETERS --------------------------% diff --git a/TestCases/turbomachinery/centrifugal_blade/centrifugal_blade.cfg b/TestCases/turbomachinery/centrifugal_blade/centrifugal_blade.cfg index ce87e1e5f692..c853f7b86215 100755 --- a/TestCases/turbomachinery/centrifugal_blade/centrifugal_blade.cfg +++ b/TestCases/turbomachinery/centrifugal_blade/centrifugal_blade.cfg @@ -295,10 +295,7 @@ SLOPE_LIMITER_TURB= VENKATAKRISHNAN TIME_DISCRE_TURB= EULER_IMPLICIT % % Reduction factor of the CFL coefficient in the turbulence problem -CFL_REDUCTION_TURB= 0.01 -% -% Relaxation coefficient -% +CFL_REDUCTION_TURB= 1.0 % % % diff --git a/TestCases/turbomachinery/centrifugal_stage/centrifugal_stage.cfg b/TestCases/turbomachinery/centrifugal_stage/centrifugal_stage.cfg index e650e733a9f1..459b20d83c31 100755 --- a/TestCases/turbomachinery/centrifugal_stage/centrifugal_stage.cfg +++ b/TestCases/turbomachinery/centrifugal_stage/centrifugal_stage.cfg @@ -303,10 +303,7 @@ SLOPE_LIMITER_TURB= VENKATAKRISHNAN TIME_DISCRE_TURB= EULER_IMPLICIT % % Reduction factor of the CFL coefficient in the turbulence problem -CFL_REDUCTION_TURB= 0.01 -% -% Relaxation coefficient -% +CFL_REDUCTION_TURB= 1.0 % % % diff --git a/TestCases/turbomachinery/transonic_stator_2D/transonic_stator.cfg b/TestCases/turbomachinery/transonic_stator_2D/transonic_stator.cfg index dc3a5cbd334b..bb6a6c7c7896 100644 --- a/TestCases/turbomachinery/transonic_stator_2D/transonic_stator.cfg +++ b/TestCases/turbomachinery/transonic_stator_2D/transonic_stator.cfg @@ -268,9 +268,7 @@ SLOPE_LIMITER_TURB= VENKATAKRISHNAN TIME_DISCRE_TURB= EULER_IMPLICIT % % Reduction factor of the CFL coefficient in the turbulence problem -CFL_REDUCTION_TURB= 0.1 -% -% Relaxation coefficient +CFL_REDUCTION_TURB= 1.0 % % % --------------------------- CONVERGENCE PARAMETERS --------------------------% diff --git a/TestCases/turbomachinery/transonic_stator_2D/transonic_stator_rst.cfg b/TestCases/turbomachinery/transonic_stator_2D/transonic_stator_rst.cfg index 404834b6cf9e..323644a7d903 100644 --- a/TestCases/turbomachinery/transonic_stator_2D/transonic_stator_rst.cfg +++ b/TestCases/turbomachinery/transonic_stator_2D/transonic_stator_rst.cfg @@ -273,9 +273,7 @@ SLOPE_LIMITER_TURB= VENKATAKRISHNAN TIME_DISCRE_TURB= EULER_IMPLICIT % % Reduction factor of the CFL coefficient in the turbulence problem -CFL_REDUCTION_TURB= 0.1 -% -% Relaxation coefficient +CFL_REDUCTION_TURB= 1.0 % % % --------------------------- CONVERGENCE PARAMETERS --------------------------% From 56b7c55b363692b2a38209180e17f02fa3b07c0a Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Sat, 28 Mar 2020 15:30:16 +0000 Subject: [PATCH 33/54] option to define a region of maximum mesh stiffness around deforming boundaries --- Common/include/CConfig.hpp | 14 +++-- .../interface_interpolation/CInterpolator.hpp | 4 +- Common/src/CConfig.cpp | 8 ++- .../elasticity/CFEALinearElasticity.cpp | 1 - SU2_CFD/src/solvers/CMeshSolver.cpp | 55 ++++++++++++------- 5 files changed, 53 insertions(+), 29 deletions(-) diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp index 604f83e10f9b..eec6a12d5b94 100644 --- a/Common/include/CConfig.hpp +++ b/Common/include/CConfig.hpp @@ -605,7 +605,7 @@ class CConfig { su2double Min_Beta_RoeTurkel, /*!< \brief Minimum value of Beta for the Roe-Turkel low Mach preconditioner. */ Max_Beta_RoeTurkel; /*!< \brief Maximum value of Beta for the Roe-Turkel low Mach preconditioner. */ unsigned long GridDef_Nonlinear_Iter; /*!< \brief Number of nonlinear increments for grid deformation. */ - unsigned short Deform_Stiffness_Type; /*!< \brief Type of element stiffness imposed for FEA mesh deformation. */ + unsigned short Deform_StiffnessType; /*!< \brief Type of element stiffness imposed for FEA mesh deformation. */ bool Deform_Mesh; /*!< \brief Determines whether the mesh will be deformed. */ bool Deform_Output; /*!< \brief Print the residuals during mesh deformation to the console. */ su2double Deform_Tol_Factor; /*!< \brief Factor to multiply smallest volume for deform tolerance (0.001 default) */ @@ -613,8 +613,9 @@ class CConfig { su2double Deform_Limit; /*!< \brief Deform limit */ unsigned short FFD_Continuity; /*!< \brief Surface continuity at the intersection with the FFD */ unsigned short FFD_CoordSystem; /*!< \brief Define the coordinates system */ - su2double Deform_ElasticityMod, - Deform_PoissonRatio; /*!< \brief Young's Modulus and poisson ratio for volume deformation stiffness model */ + su2double Deform_ElasticityMod, /*!< \brief Young's modulus for volume deformation stiffness model */ + Deform_PoissonRatio, /*!< \brief Poisson's ratio for volume deformation stiffness model */ + Deform_StiffLayerSize; /*!< \brief Size of the layer of highest stiffness for wall distance-based mesh stiffness */ bool Visualize_Surface_Def; /*!< \brief Flag to visualize the surface deformacion in SU2_DEF. */ bool Visualize_Volume_Def; /*!< \brief Flag to visualize the volume deformation in SU2_DEF. */ bool FFD_Symmetry_Plane; /*!< \brief FFD symmetry plane. */ @@ -4105,7 +4106,12 @@ class CConfig { * \brief Get the type of stiffness to impose for FEA mesh deformation. * \return type of stiffness to impose for FEA mesh deformation. */ - unsigned short GetDeform_Stiffness_Type(void) const { return Deform_Stiffness_Type; } + unsigned short GetDeform_Stiffness_Type(void) const { return Deform_StiffnessType; } + + /*! + * \brief Get the size of the layer of highest stiffness for wall distance-based mesh stiffness. + */ + su2double GetDeform_StiffLayerSize(void) const { return Deform_StiffLayerSize; } /*! * \brief Creates a tecplot file to visualize the volume deformation deformation made by the DEF software. diff --git a/Common/include/interface_interpolation/CInterpolator.hpp b/Common/include/interface_interpolation/CInterpolator.hpp index af95221275f0..f8254b17ea09 100644 --- a/Common/include/interface_interpolation/CInterpolator.hpp +++ b/Common/include/interface_interpolation/CInterpolator.hpp @@ -39,8 +39,8 @@ class CGeometry; */ class CInterpolator { protected: - const int rank; /*!< \brief MPI Rank. */ - const int size; /*!< \brief MPI Size. */ + 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. */ diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index cf65fc655c53..7434dbac56df 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -2221,11 +2221,13 @@ void CConfig::SetConfig_Options() { /* DESCRIPTION: Deform limit in m or inches */ addDoubleOption("DEFORM_LIMIT", Deform_Limit, 1E6); /* DESCRIPTION: Type of element stiffness imposed for FEA mesh deformation (INVERSE_VOLUME, WALL_DISTANCE, CONSTANT_STIFFNESS) */ - addEnumOption("DEFORM_STIFFNESS_TYPE", Deform_Stiffness_Type, Deform_Stiffness_Map, SOLID_WALL_DISTANCE); - /* DESCRIPTION: Poisson's ratio for constant stiffness FEA method of grid deformation*/ + addEnumOption("DEFORM_STIFFNESS_TYPE", Deform_StiffnessType, Deform_Stiffness_Map, SOLID_WALL_DISTANCE); + /* DESCRIPTION: Poisson's ratio for constant stiffness FEA method of grid deformation */ addDoubleOption("DEFORM_ELASTICITY_MODULUS", Deform_ElasticityMod, 2E11); - /* DESCRIPTION: Young's modulus and Poisson's ratio for constant stiffness FEA method of grid deformation*/ + /* DESCRIPTION: Young's modulus and Poisson's ratio for constant stiffness FEA method of grid deformation */ addDoubleOption("DEFORM_POISSONS_RATIO", Deform_PoissonRatio, 0.3); + /* DESCRIPTION: Size of the layer of highest stiffness for wall distance-based mesh stiffness */ + addDoubleOption("DEFORM_STIFF_LAYER_SIZE", Deform_StiffLayerSize, 0.0); /* DESCRIPTION: Linear solver for the mesh deformation\n OPTIONS: see \link Linear_Solver_Map \endlink \n DEFAULT: FGMRES \ingroup Config*/ addEnumOption("DEFORM_LINEAR_SOLVER", Kind_Deform_Linear_Solver, Linear_Solver_Map, FGMRES); /* \n DESCRIPTION: Preconditioner for the Krylov linear solvers \n OPTIONS: see \link Linear_Solver_Prec_Map \endlink \n DEFAULT: LU_SGS \ingroup Config*/ diff --git a/SU2_CFD/src/numerics/elasticity/CFEALinearElasticity.cpp b/SU2_CFD/src/numerics/elasticity/CFEALinearElasticity.cpp index 7e09c1410b90..25d0d0b3b360 100644 --- a/SU2_CFD/src/numerics/elasticity/CFEALinearElasticity.cpp +++ b/SU2_CFD/src/numerics/elasticity/CFEALinearElasticity.cpp @@ -366,7 +366,6 @@ CFEAMeshElasticity::CFEAMeshElasticity(unsigned short val_nDim, unsigned short v case INVERSE_VOLUME: case SOLID_WALL_DISTANCE: element_based = true; - Nu = config->GetDeform_Coeff(); break; case CONSTANT_STIFFNESS: element_based = false; diff --git a/SU2_CFD/src/solvers/CMeshSolver.cpp b/SU2_CFD/src/solvers/CMeshSolver.cpp index 25e4b711fc9f..0af54d960a2e 100644 --- a/SU2_CFD/src/solvers/CMeshSolver.cpp +++ b/SU2_CFD/src/solvers/CMeshSolver.cpp @@ -388,6 +388,8 @@ void CMeshSolver::SetWallDistance(CGeometry *geometry, CConfig *config) { void CMeshSolver::SetMesh_Stiffness(CGeometry **geometry, CNumerics **numerics, CConfig *config){ + if (stiffness_set) return; + /*--- 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. @@ -396,31 +398,46 @@ void CMeshSolver::SetMesh_Stiffness(CGeometry **geometry, CNumerics **numerics, * boundary conditions are essential (Dirichlet). ---*/ const su2double maxE = config->GetDeform_ElasticityMod(); - if (!stiffness_set) { - /*--- 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; + /*--- 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]; - switch (config->GetDeform_Stiffness_Type()) { - /*--- Stiffness inverse of the volume of the element ---*/ - case INVERSE_VOLUME: E = 1.0 / element[iElem].GetRef_Volume(); break; + switch (config->GetDeform_Stiffness_Type()) { - /*--- 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: + for (unsigned long iElem = 0; iElem < nElement; iElem++) { + su2double E = 1.0 / element[iElem].GetRef_Volume(); + myNumerics->SetMeshElasticProperties(iElem, min(E,maxE)); } + break; - /*--- Set the element elastic properties in the numerics container ---*/ - myNumerics->SetMeshElasticProperties(iElem, min(E,maxE)); - } + /*--- Stiffness inverse of the distance of the element to the closest wall. ---*/ + case SOLID_WALL_DISTANCE: { + const su2double offset = config->GetDeform_StiffLayerSize(); + if (fabs(offset) > 0.0) { + /*--- With prescribed layer of maximum stiffness (reaches max and holds). ---*/ + su2double d0 = offset / MaxDistance; + su2double dmin = 1.0 / maxE; + su2double scale = 1.0 / (1.0 - d0); + for (unsigned long iElem = 0; iElem < nElement; iElem++) { + su2double E = 1.0 / max(dmin, (element[iElem].GetWallDistance() - d0)*scale); + myNumerics->SetMeshElasticProperties(iElem, E); + } + } else { + /*--- Without prescribed layer of maximum stiffness (may not reach max). ---*/ + for (unsigned long iElem = 0; iElem < nElement; iElem++) { + su2double E = 1.0 / element[iElem].GetWallDistance(); + myNumerics->SetMeshElasticProperties(iElem, min(E,maxE)); + } + } } - stiffness_set = true; + break; + } } + stiffness_set = true; } From 1fa58dafce81f11385b4fa79f0224b1cc183291b Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Sat, 28 Mar 2020 19:40:39 +0000 Subject: [PATCH 34/54] initialize the linear system solution of CMeshSolver --- Common/src/linear_algebra/CSysSolve.cpp | 18 +----------------- SU2_CFD/src/solvers/CFEASolver.cpp | 15 +-------------- SU2_CFD/src/solvers/CMeshSolver.cpp | 10 ++++++++-- 3 files changed, 10 insertions(+), 33 deletions(-) diff --git a/Common/src/linear_algebra/CSysSolve.cpp b/Common/src/linear_algebra/CSysSolve.cpp index 6f041e89f8bb..0e97709bd330 100644 --- a/Common/src/linear_algebra/CSysSolve.cpp +++ b/Common/src/linear_algebra/CSysSolve.cpp @@ -239,10 +239,6 @@ unsigned long CSysSolve::CG_LinSolver(const CSysVector & return 0; } - /*--- Set the norm to the initial initial residual value ---*/ - - norm0 = norm_r; - /*--- Output header information including initial residual ---*/ if ((monitoring) && (master)) { @@ -398,10 +394,6 @@ unsigned long CSysSolve::FGMRES_LinSolver(const CSysVector::BCGSTAB_LinSolver(const CSysVector::Smoother_LinSolver(const CSysVector::Solve_b(CSysMatrix & Jacobian, /*--- 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 PASTIX_LDLT : case PASTIX_LU: diff --git a/SU2_CFD/src/solvers/CFEASolver.cpp b/SU2_CFD/src/solvers/CFEASolver.cpp index 28e338731015..2db9d13fb60a 100644 --- a/SU2_CFD/src/solvers/CFEASolver.cpp +++ b/SU2_CFD/src/solvers/CFEASolver.cpp @@ -2768,19 +2768,6 @@ void CFEASolver::Solve_System(CGeometry *geometry, CConfig *config) { SU2_OMP_PARALLEL { - /*--- Initialize residual and solution at the ghost points ---*/ - - SU2_OMP(sections) - { - SU2_OMP(section) - for (auto iPoint = nPointDomain; iPoint < nPoint; iPoint++) - LinSysRes.SetBlock_Zero(iPoint); - - SU2_OMP(section) - for (auto iPoint = nPointDomain; iPoint < nPoint; iPoint++) - LinSysSol.SetBlock_Zero(iPoint); - } - /*--- Solve or smooth the linear system. ---*/ auto iter = System.Solve(Jacobian, LinSysRes, LinSysSol, geometry, config); @@ -2789,7 +2776,7 @@ void CFEASolver::Solve_System(CGeometry *geometry, CConfig *config) { SetIterLinSolver(iter); SetResLinSolver(System.GetResidual()); } - SU2_OMP_BARRIER + //SU2_OMP_BARRIER } // end SU2_OMP_PARALLEL } diff --git a/SU2_CFD/src/solvers/CMeshSolver.cpp b/SU2_CFD/src/solvers/CMeshSolver.cpp index 0af54d960a2e..f2c0df295c60 100644 --- a/SU2_CFD/src/solvers/CMeshSolver.cpp +++ b/SU2_CFD/src/solvers/CMeshSolver.cpp @@ -448,10 +448,9 @@ void CMeshSolver::DeformMesh(CGeometry **geometry, CNumerics **numerics, CConfig /*--- Compute the stiffness matrix. ---*/ Compute_StiffMatrix(geometry[MESH_0], numerics, config); - /*--- Initialize vectors and clean residual. ---*/ + /*--- Clean residual, we do not want an incremental solution. ---*/ SU2_OMP_PARALLEL { - LinSysSol.SetValZero(); LinSysRes.SetValZero(); } @@ -706,6 +705,13 @@ void CMeshSolver::LoadRestart(CGeometry **geometry, CSolver ***solver, CConfig * geometry[MESH_0]->InitiateComms(geometry[MESH_0], config, COORDINATES); geometry[MESH_0]->CompleteComms(geometry[MESH_0], config, COORDINATES); + /*--- Init the linear system solution. ---*/ + for (unsigned long iPoint = 0; iPoint < nPoint; ++iPoint) { + for (unsigned short iDim = 0; iDim < nDim; ++iDim) { + LinSysSol(iPoint, iDim) = nodes->GetSolution(iPoint, iDim); + } + } + /*--- Recompute the edges and dual mesh control volumes in the domain and on the boundaries. ---*/ UpdateDualGrid(geometry[MESH_0], config); From afa41549bbca126a3071d5991b9581f6abd50c30 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Mon, 30 Mar 2020 15:16:05 +0100 Subject: [PATCH 35/54] mitigate bug affecting displacement boundary conditions caused by partitioning of markers --- Common/src/geometry/CPhysicalGeometry.cpp | 73 +++++++--------------- SU2_CFD/include/solvers/CFEASolver.hpp | 12 ++++ SU2_CFD/src/solvers/CFEASolver.cpp | 76 ++++++++++++++++++++++- SU2_CFD/src/solvers/CMeshSolver.cpp | 12 ++++ 4 files changed, 121 insertions(+), 52 deletions(-) diff --git a/Common/src/geometry/CPhysicalGeometry.cpp b/Common/src/geometry/CPhysicalGeometry.cpp index 8f8ea74fe17a..a57784a49431 100644 --- a/Common/src/geometry/CPhysicalGeometry.cpp +++ b/Common/src/geometry/CPhysicalGeometry.cpp @@ -3070,61 +3070,32 @@ void CPhysicalGeometry::LoadSurfaceElements(CConfig *config, CGeometry *geometry /*--- Initialize pointers for turbomachinery computations ---*/ - nSpanWiseSections = new unsigned short[2]; - nSpanSectionsByMarker = new unsigned short[nMarker]; - SpanWiseValue = new su2double*[2]; - for (unsigned short iMarker = 0; iMarker < 2; iMarker++){ - nSpanWiseSections[iMarker] = 0; - SpanWiseValue[iMarker] = NULL; - } - - nVertexSpan = new long* [nMarker]; - nTotVertexSpan = new unsigned long* [nMarker]; - turbovertex = new CTurboVertex***[nMarker]; - AverageTurboNormal = new su2double**[nMarker]; - AverageNormal = new su2double**[nMarker]; - AverageGridVel = new su2double**[nMarker]; - AverageTangGridVel = new su2double*[nMarker]; - SpanArea = new su2double*[nMarker]; - TurboRadius = new su2double*[nMarker]; - MaxAngularCoord = new su2double*[nMarker]; - MinAngularCoord = new su2double*[nMarker]; - MinRelAngularCoord = new su2double*[nMarker]; - - for (unsigned short iMarker = 0; iMarker < nMarker; iMarker++){ - nSpanSectionsByMarker[iMarker] = 0; - nVertexSpan[iMarker] = NULL; - nTotVertexSpan[iMarker] = NULL; - turbovertex[iMarker] = NULL; - AverageTurboNormal[iMarker] = NULL; - AverageNormal[iMarker] = NULL; - AverageGridVel[iMarker] = NULL; - AverageTangGridVel[iMarker] = NULL; - SpanArea[iMarker] = NULL; - TurboRadius[iMarker] = NULL; - MaxAngularCoord[iMarker] = NULL; - MinAngularCoord[iMarker] = NULL; - MinRelAngularCoord[iMarker] = NULL; - } + nSpanWiseSections = new unsigned short[2] (); + SpanWiseValue = new su2double*[2] (); + + nSpanSectionsByMarker = new unsigned short[nMarker] (); + nVertexSpan = new long* [nMarker] (); + nTotVertexSpan = new unsigned long* [nMarker] (); + turbovertex = new CTurboVertex***[nMarker] (); + AverageTurboNormal = new su2double**[nMarker] (); + AverageNormal = new su2double**[nMarker] (); + AverageGridVel = new su2double**[nMarker] (); + AverageTangGridVel = new su2double*[nMarker] (); + SpanArea = new su2double*[nMarker] (); + TurboRadius = new su2double*[nMarker] (); + MaxAngularCoord = new su2double*[nMarker] (); + MinAngularCoord = new su2double*[nMarker] (); + MinRelAngularCoord = new su2double*[nMarker] (); /*--- Initialize pointers for turbomachinery performance computation ---*/ nTurboPerf = config->GetnMarker_TurboPerformance(); - TangGridVelIn = new su2double*[config->GetnMarker_TurboPerformance()]; - SpanAreaIn = new su2double*[config->GetnMarker_TurboPerformance()]; - TurboRadiusIn = new su2double*[config->GetnMarker_TurboPerformance()]; - TangGridVelOut = new su2double*[config->GetnMarker_TurboPerformance()]; - SpanAreaOut = new su2double*[config->GetnMarker_TurboPerformance()]; - TurboRadiusOut = new su2double*[config->GetnMarker_TurboPerformance()]; - - for (unsigned short iMarker = 0; iMarker < config->GetnMarker_TurboPerformance(); iMarker++){ - TangGridVelIn[iMarker] = NULL; - SpanAreaIn[iMarker] = NULL; - TurboRadiusIn[iMarker] = NULL; - TangGridVelOut[iMarker] = NULL; - SpanAreaOut[iMarker] = NULL; - TurboRadiusOut[iMarker] = NULL; - } + TangGridVelIn = new su2double*[nTurboPerf] (); + SpanAreaIn = new su2double*[nTurboPerf] (); + TurboRadiusIn = new su2double*[nTurboPerf] (); + TangGridVelOut = new su2double*[nTurboPerf] (); + SpanAreaOut = new su2double*[nTurboPerf] (); + TurboRadiusOut = new su2double*[nTurboPerf] (); } diff --git a/SU2_CFD/include/solvers/CFEASolver.hpp b/SU2_CFD/include/solvers/CFEASolver.hpp index eae891063a05..04d012b14b13 100644 --- a/SU2_CFD/include/solvers/CFEASolver.hpp +++ b/SU2_CFD/include/solvers/CFEASolver.hpp @@ -91,6 +91,9 @@ class CFEASolver : public CSolver { unsigned long nElement; /*!< \brief Number of elements. */ + /*--- Extra vertices for row/column elimination, see Set_VertexEliminationSchedule. ---*/ + vector ExtraVerticesToEliminate; + /*! * \brief The highest level in the variable hierarchy this solver can safely use, * CVariable is the common denominator between the FEA and Mesh deformation variables. @@ -147,6 +150,15 @@ class CFEASolver : public CSolver { */ void Set_Prestretch(CGeometry *geometry, CConfig *config); + /*! + * \brief Mitigation for an issue with Dirichlet boundary conditions and MPI, + * some ranks do not get enough of the markers to cover their halo points. + * This breaks the symmetry of the global matrix as columns are not fully eliminated. + * \param[in] geometry - Geometrical definition of the problem. + * \param[in] markers - List of essential BC markers. + */ + void Set_VertexEliminationSchedule(CGeometry *geometry, const vector& markers); + /*! * \brief Compute constants for time integration. * \param[in] config - Definition of the particular problem. diff --git a/SU2_CFD/src/solvers/CFEASolver.cpp b/SU2_CFD/src/solvers/CFEASolver.cpp index 2db9d13fb60a..030950aefcdc 100644 --- a/SU2_CFD/src/solvers/CFEASolver.cpp +++ b/SU2_CFD/src/solvers/CFEASolver.cpp @@ -29,6 +29,8 @@ #include "../../include/variables/CFEABoundVariable.hpp" #include "../../../Common/include/toolboxes/printing_toolbox.hpp" #include +#include +#include /*! * \brief Anonymous namespace with helper functions of the FEA solver. @@ -284,11 +286,24 @@ CFEASolver::CFEASolver(CGeometry *geometry, CConfig *config) : CSolver() { /*--- If dynamic, we also need to communicate the old solution ---*/ - if(dynamic) { + if (dynamic) { InitiateComms(geometry, config, SOLUTION_FEA_OLD); CompleteComms(geometry, config, SOLUTION_FEA_OLD); } + if (size != SINGLE_NODE) { + vector essentialMarkers; + for (unsigned short iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { + const auto kindBnd = config->GetMarker_All_KindBC(iMarker); + if ((kindBnd == CLAMPED_BOUNDARY) || + (kindBnd == DISP_DIR_BOUNDARY) || + (kindBnd == DISPLACEMENT_BOUNDARY)) { + essentialMarkers.push_back(iMarker); + } + } + Set_VertexEliminationSchedule(geometry, essentialMarkers); + } + /*--- Add the solver name (max 8 characters) ---*/ SolverName = "FEA"; } @@ -748,6 +763,56 @@ void CFEASolver::Set_ReferenceGeometry(CGeometry *geometry, CConfig *config) { } +void CFEASolver::Set_VertexEliminationSchedule(CGeometry *geometry, const vector& markers) { + + /*--- Store global point indices of essential BC markers. ---*/ + vector myPoints; + + for (auto iMarker : markers) { + for (auto iVertex = 0ul; iVertex < geometry->nVertex[iMarker]; iVertex++) { + auto iPoint = geometry->vertex[iMarker][iVertex]->GetNode(); + myPoints.push_back(geometry->node[iPoint]->GetGlobalIndex()); + } + } + + const unordered_set markerPoints(myPoints.begin(), myPoints.end()); + + vector numPoints(size); + unsigned long num = myPoints.size(); + SU2_MPI::Allgather(&num, 1, MPI_UNSIGNED_LONG, numPoints.data(), 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + + /*--- Global to local map for the halo points of the rank (not covered by the CGeometry map). ---*/ + unordered_map Global2Local; + for (auto iPoint = nPointDomain; iPoint < nPoint; ++iPoint) { + Global2Local[geometry->node[iPoint]->GetGlobalIndex()] = iPoint; + } + + /*--- Populate elimination list. ---*/ + ExtraVerticesToEliminate.clear(); + + for (int i = 0; i < size; ++i) { + /*--- Send our point list. ---*/ + if (rank == i) { + SU2_MPI::Bcast(myPoints.data(), numPoints[i], MPI_UNSIGNED_LONG, rank, MPI_COMM_WORLD); + continue; + } + + /*--- Receive point list. ---*/ + vector theirPoints(numPoints[i]); + SU2_MPI::Bcast(theirPoints.data(), numPoints[i], MPI_UNSIGNED_LONG, i, MPI_COMM_WORLD); + + for (auto iPointGlobal : theirPoints) { + /*--- Check if the rank has the point. ---*/ + auto it = Global2Local.find(iPointGlobal); + if (it == Global2Local.end()) continue; + + /*--- If the point is not covered by this rank's markers, mark it for elimination. ---*/ + if (markerPoints.count(iPointGlobal) == 0) + ExtraVerticesToEliminate.push_back(it->second); + } + } + +} void CFEASolver::Preprocessing(CGeometry *geometry, CSolver **solver_container, CConfig *config, CNumerics **numerics, unsigned short iMesh, unsigned long Iteration, unsigned short RunTime_EqSystem, bool Output) { @@ -2768,6 +2833,15 @@ void CFEASolver::Solve_System(CGeometry *geometry, CConfig *config) { SU2_OMP_PARALLEL { + /*--- Enforce solution at some halo points possibly not covered by essential BC markers. ---*/ + + Jacobian.InitiateComms(LinSysSol, geometry, config, SOLUTION_MATRIX); + Jacobian.CompleteComms(LinSysSol, geometry, config, SOLUTION_MATRIX); + + for (auto iPoint : ExtraVerticesToEliminate) { + Jacobian.EnforceSolutionAtNode(iPoint, LinSysSol.GetBlock(iPoint), LinSysRes); + } + /*--- Solve or smooth the linear system. ---*/ auto iter = System.Solve(Jacobian, LinSysRes, LinSysSol, geometry, config); diff --git a/SU2_CFD/src/solvers/CMeshSolver.cpp b/SU2_CFD/src/solvers/CMeshSolver.cpp index f2c0df295c60..f1db24ec395a 100644 --- a/SU2_CFD/src/solvers/CMeshSolver.cpp +++ b/SU2_CFD/src/solvers/CMeshSolver.cpp @@ -157,6 +157,18 @@ CMeshSolver::CMeshSolver(CGeometry *geometry, CConfig *config) : CFEASolver(true /*--- Compute the wall distance using the reference coordinates ---*/ SetWallDistance(geometry, config); + if (size != SINGLE_NODE) { + vector essentialMarkers; + /*--- Markers types covered in SetBoundaryDisplacements. ---*/ + for (unsigned short iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { + if (((config->GetMarker_All_KindBC(iMarker) != SEND_RECEIVE) && + (config->GetMarker_All_KindBC(iMarker) != PERIODIC_BOUNDARY)) || + (config->GetMarker_All_Deform_Mesh(iMarker) == YES)) { + essentialMarkers.push_back(iMarker); + } + } + Set_VertexEliminationSchedule(geometry, essentialMarkers); + } } void CMeshSolver::SetMinMaxVolume(CGeometry *geometry, CConfig *config, bool updated) { From f9aa579337d481c8786f39eef9d51f587d94c6fb Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Mon, 30 Mar 2020 15:50:05 +0100 Subject: [PATCH 36/54] small fix --- SU2_CFD/src/solvers/CFEASolver.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SU2_CFD/src/solvers/CFEASolver.cpp b/SU2_CFD/src/solvers/CFEASolver.cpp index 030950aefcdc..ce4a66a00350 100644 --- a/SU2_CFD/src/solvers/CFEASolver.cpp +++ b/SU2_CFD/src/solvers/CFEASolver.cpp @@ -2125,7 +2125,7 @@ void CFEASolver::BC_Damper(CGeometry *geometry, CNumerics *numerics, CConfig *co unsigned short iNode, iDim; unsigned long indexNode[4] = {0}; - su2double nodeCoord[4][3] = {0.0}; + su2double nodeCoord[4][3] = {{0.0}}; bool quad = (geometry->bound[val_marker][iElem]->GetVTK_Type() == QUADRILATERAL); unsigned short nNodes = quad? 4 : nDim; @@ -2831,8 +2831,6 @@ void CFEASolver::GeneralizedAlpha_UpdateLoads(CGeometry *geometry, CSolver **sol void CFEASolver::Solve_System(CGeometry *geometry, CConfig *config) { - SU2_OMP_PARALLEL - { /*--- Enforce solution at some halo points possibly not covered by essential BC markers. ---*/ Jacobian.InitiateComms(LinSysSol, geometry, config, SOLUTION_MATRIX); @@ -2842,6 +2840,8 @@ void CFEASolver::Solve_System(CGeometry *geometry, CConfig *config) { Jacobian.EnforceSolutionAtNode(iPoint, LinSysSol.GetBlock(iPoint), LinSysRes); } + SU2_OMP_PARALLEL + { /*--- Solve or smooth the linear system. ---*/ auto iter = System.Solve(Jacobian, LinSysRes, LinSysSol, geometry, config); @@ -2851,8 +2851,8 @@ void CFEASolver::Solve_System(CGeometry *geometry, CConfig *config) { SetResLinSolver(System.GetResidual()); } //SU2_OMP_BARRIER - } // end SU2_OMP_PARALLEL + } From bf4366b4d31a9852314a90471425e544801fce33 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Mon, 30 Mar 2020 23:22:50 +0100 Subject: [PATCH 37/54] ensure MPI-independence (consistent ordering of donor points) of RBF interpolation --- .../CRadialBasisFunction.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp index 7938efb4a364..80647309fdaf 100644 --- a/Common/src/interface_interpolation/CRadialBasisFunction.cpp +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -172,6 +172,22 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { delete[] Buffer_Receive_Coord; delete[] Buffer_Receive_GlobalPoint; + /*--- Give an MPI-independent order to the points (required due to high condition + * number of the RBF matrix, avoids diff results with diff number of ranks. ---*/ + vector order(nGlobalVertexDonor); + iota(order.begin(), order.end(), 0); + sort(order.begin(), order.end(), [&donorPoint](int i, int j){return donorPoint[i] < donorPoint[j];}); + + for (int i = 0; i < int(nGlobalVertexDonor); ++i) { + int j = order[i]; + while (j < i) j = order[j]; + if (i == j) continue; + swap(donorProc[i], donorProc[j]); + swap(donorPoint[i], donorPoint[j]); + for (int iDim = 0; iDim < nDim; ++iDim) + swap(donorCoord(i,iDim), donorCoord(j,iDim)); + } + /*--- Static work scheduling over ranks based on which one has less work currently. ---*/ int iProcessor = 0; for (int i = 1; i < nProcessor; ++i) From c120270be3239468986de06f048a267667191168 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 31 Mar 2020 08:56:40 +0100 Subject: [PATCH 38/54] MPI-independent nearest neighbor search --- Common/src/interface_interpolation/CNearestNeighbor.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Common/src/interface_interpolation/CNearestNeighbor.cpp b/Common/src/interface_interpolation/CNearestNeighbor.cpp index e1369f19aef0..2d6f4289d54a 100644 --- a/Common/src/interface_interpolation/CNearestNeighbor.cpp +++ b/Common/src/interface_interpolation/CNearestNeighbor.cpp @@ -38,6 +38,10 @@ struct DonorInfo { int proc; DonorInfo(su2double d = 0.0, unsigned i = 0, int p = 0) : dist(d), pidx(i), proc(p) { } + bool operator< (const DonorInfo& other) const { + /*--- Global index is used as tie-breaker to make sorted order independent of initial. ---*/ + return (dist != other.dist)? (dist < other.dist) : (pidx < other.pidx); + } }; CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, @@ -135,8 +139,7 @@ void CNearestNeighbor::Set_TransferCoeff(const CConfig* const* config) { } /*--- Find k closest points. ---*/ - partial_sort(donorInfo.begin(), donorInfo.begin()+nDonor, donorInfo.end(), - [](const DonorInfo& a, const DonorInfo& b){return a.dist < b.dist;}); + partial_sort(donorInfo.begin(), donorInfo.begin()+nDonor, donorInfo.end()); /*--- Update stats. ---*/ numTarget += 1; From 259a77093d71a3695e6569ec4564437659e4c438 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 31 Mar 2020 10:47:46 +0100 Subject: [PATCH 39/54] simplify (and fix) FSI load integration --- SU2_CFD/src/solvers/CFEASolver.cpp | 167 ++++++----------------------- 1 file changed, 33 insertions(+), 134 deletions(-) diff --git a/SU2_CFD/src/solvers/CFEASolver.cpp b/SU2_CFD/src/solvers/CFEASolver.cpp index ce4a66a00350..da0e84e5f078 100644 --- a/SU2_CFD/src/solvers/CFEASolver.cpp +++ b/SU2_CFD/src/solvers/CFEASolver.cpp @@ -2195,44 +2195,38 @@ void CFEASolver::BC_Deforming(CGeometry *geometry, CNumerics *numerics, CConfig void CFEASolver::Integrate_FSI_Loads(CGeometry *geometry, CConfig *config) { - unsigned short iDim, iNode, nNode; - unsigned long iPoint, iElem, nElem; + const auto nMarker = config->GetnMarker_All(); + const auto nMarkerInt = config->GetMarker_n_ZoneInterface()/2u; - unsigned short iMarkerInt, nMarkerInt = config->GetMarker_n_ZoneInterface()/2, - iMarker, nMarker = config->GetnMarker_All(); + unordered_map vertexArea; - /*--- Temporary storage to store the forces on the element faces ---*/ - vector forces; + /*--- Compute current area associated with each vertex. ---*/ - /*--- Loop through the FSI interface pairs ---*/ - /*--- 1st pass to compute forces ---*/ - for (iMarkerInt = 1; iMarkerInt <= nMarkerInt; ++iMarkerInt) { - /*--- Find the marker index associated with the pair ---*/ + for (auto iMarkerInt = 1u; iMarkerInt <= nMarkerInt; ++iMarkerInt) { + /*--- Find the marker index associated with the pair. ---*/ + unsigned short iMarker; for (iMarker = 0; iMarker < nMarker; ++iMarker) if (config->GetMarker_All_ZoneInterface(iMarker) == iMarkerInt) break; - /*--- The current mpi rank may not have this marker ---*/ + /*--- The current mpi rank may not have this marker. ---*/ if (iMarker == nMarker) continue; - nElem = geometry->GetnElem_Bound(iMarker); - - for (iElem = 0; iElem < nElem; ++iElem) { - /*--- Define the boundary element ---*/ - unsigned long nodeList[4]; - su2double coords[4][3]; + for (auto iElem = 0u; iElem < geometry->GetnElem_Bound(iMarker); ++iElem) { + /*--- Define the boundary element. ---*/ + unsigned long nodeList[4] = {0}; + su2double coords[4][3] = {{0.0}}; bool quad = geometry->bound[iMarker][iElem]->GetVTK_Type() == QUADRILATERAL; - nNode = quad? 4 : nDim; + auto nNode = quad? 4u : nDim; - for (iNode = 0; iNode < nNode; ++iNode) { + for (auto iNode = 0u; iNode < nNode; ++iNode) { nodeList[iNode] = geometry->bound[iMarker][iElem]->GetNode(iNode); - for (iDim = 0; iDim < nDim; ++iDim) + for (auto iDim = 0u; iDim < nDim; ++iDim) coords[iNode][iDim] = geometry->node[nodeList[iNode]]->GetCoord(iDim)+ nodes->GetSolution(nodeList[iNode],iDim); } - /*--- Compute the area ---*/ - - su2double normal[3] = {0.0, 0.0, 0.0}; + /*--- Compute the area contribution to each node. ---*/ + su2double normal[3] = {0.0}; switch (nNode) { case 2: LineNormal(coords, normal); break; @@ -2240,125 +2234,30 @@ void CFEASolver::Integrate_FSI_Loads(CGeometry *geometry, CConfig *config) { case 4: QuadrilateralNormal(coords, normal); break; } - su2double area = sqrt(pow(normal[0],2) + pow(normal[1],2) + pow(normal[2],2)); - - /*--- Integrate ---*/ - passivedouble weight = 1.0/nNode; - su2double force[3] = {0.0, 0.0, 0.0}; - - for (iNode = 0; iNode < nNode; ++iNode) - for (iDim = 0; iDim < nDim; ++iDim) - force[iDim] += weight*area*nodes->Get_FlowTraction(nodeList[iNode],iDim); - - for (iDim = 0; iDim < nDim; ++iDim) forces.push_back(force[iDim]); - } - } - - /*--- 2nd pass to set values. This is to account for overlap in the markers. ---*/ - /*--- By putting the integrated values back into the nodes no changes have to be made elsewhere. ---*/ - nodes->Clear_FlowTraction(); - - vector::iterator force_it = forces.begin(); - - for (iMarkerInt = 1; iMarkerInt <= nMarkerInt; ++iMarkerInt) { - /*--- Find the marker index associated with the pair ---*/ - for (iMarker = 0; iMarker < nMarker; ++iMarker) - if (config->GetMarker_All_ZoneInterface(iMarker) == iMarkerInt) - break; - /*--- The current mpi rank may not have this marker ---*/ - if (iMarker == nMarker) continue; - - nElem = geometry->GetnElem_Bound(iMarker); - - for (iElem = 0; iElem < nElem; ++iElem) { - bool quad = geometry->bound[iMarker][iElem]->GetVTK_Type() == QUADRILATERAL; - nNode = quad? 4 : nDim; - passivedouble weight = 1.0/nNode; - - su2double force[3]; - for (iDim = 0; iDim < nDim; ++iDim) force[iDim] = *(force_it++)*weight; + su2double area = sqrt(pow(normal[0],2) + pow(normal[1],2) + pow(normal[2],2)) / nNode; - for (iNode = 0; iNode < nNode; ++iNode) { - iPoint = geometry->bound[iMarker][iElem]->GetNode(iNode); - nodes->Add_FlowTraction(iPoint,force); + /*--- Update area of nodes. ---*/ + for (auto iNode = 0u; iNode < nNode; ++iNode) { + auto iPoint = nodeList[iNode]; + if (vertexArea.count(iPoint) == 0) + vertexArea[iPoint] = area; + else + vertexArea[iPoint] += area; } } } -#ifdef HAVE_MPI - /*--- Perform a global reduction, every rank will get the nodal values of all halo elements ---*/ - /*--- This should be cheaper than the "normal" way, since very few points are both halo and interface ---*/ - vector halo_point_loc, halo_point_glb; - vector halo_force; - - for (iMarkerInt = 1; iMarkerInt <= nMarkerInt; ++iMarkerInt) { - /*--- Find the marker index associated with the pair ---*/ - for (iMarker = 0; iMarker < nMarker; ++iMarker) - if (config->GetMarker_All_ZoneInterface(iMarker) == iMarkerInt) - break; - /*--- The current mpi rank may not have this marker ---*/ - if (iMarker == nMarker) continue; - - nElem = geometry->GetnElem_Bound(iMarker); - - for (iElem = 0; iElem < nElem; ++iElem) { - bool quad = geometry->bound[iMarker][iElem]->GetVTK_Type() == QUADRILATERAL; - nNode = quad? 4 : nDim; + /*--- Integrate tractions. ---*/ - /*--- If this is an halo element we share the nodal forces ---*/ - for (iNode = 0; iNode < nNode; ++iNode) - if (!geometry->node[geometry->bound[iMarker][iElem]->GetNode(iNode)]->GetDomain()) - break; - - if (iNode < nNode) { - for (iNode = 0; iNode < nNode; ++iNode) { - iPoint = geometry->bound[iMarker][iElem]->GetNode(iNode); - /*--- local is for when later we update the values in this rank ---*/ - halo_point_loc.push_back(iPoint); - halo_point_glb.push_back(geometry->node[iPoint]->GetGlobalIndex()); - for (iDim = 0; iDim < nDim; ++iDim) - halo_force.push_back(nodes->Get_FlowTraction(iPoint,iDim)); - } - } - } - } - /*--- Determine the size of the arrays we need ---*/ - unsigned long nHaloLoc = halo_point_loc.size(); - unsigned long nHaloMax; - MPI_Allreduce(&nHaloLoc,&nHaloMax,1,MPI_UNSIGNED_LONG,MPI_MAX,MPI_COMM_WORLD); - - /*--- Shared arrays, all the: number of halo points; halo point global indices; respective forces ---*/ - unsigned long *halo_point_num = new unsigned long[size]; - unsigned long *halo_point_all = new unsigned long[size*nHaloMax]; - su2double *halo_force_all = new su2double[size*nHaloMax*nDim]; - - /*--- Make "allgathers" extra safe by resizing all vectors to the same size (some - issues observed when nHaloLoc = 0, especially with the discrete adjoint. ---*/ - halo_point_glb.resize(nHaloMax,0); - halo_force.resize(nHaloMax*nDim,0.0); - - MPI_Allgather(&nHaloLoc,1,MPI_UNSIGNED_LONG,halo_point_num,1,MPI_UNSIGNED_LONG,MPI_COMM_WORLD); - MPI_Allgather(halo_point_glb.data(),nHaloMax,MPI_UNSIGNED_LONG,halo_point_all,nHaloMax,MPI_UNSIGNED_LONG,MPI_COMM_WORLD); - SU2_MPI::Allgather(halo_force.data(),nHaloMax*nDim,MPI_DOUBLE,halo_force_all,nHaloMax*nDim,MPI_DOUBLE,MPI_COMM_WORLD); - - /*--- Find shared points with other ranks and update our values ---*/ - for (int proc = 0; proc < size; ++proc) - if (proc != rank) { - unsigned long offset = proc*nHaloMax; - for (iPoint = 0; iPoint < halo_point_num[proc]; ++iPoint) { - unsigned long iPoint_glb = halo_point_all[offset+iPoint]; - ptrdiff_t pos = find(halo_point_glb.begin(),halo_point_glb.end(),iPoint_glb)-halo_point_glb.begin(); - if (pos < long(halo_point_glb.size())) { - unsigned long iPoint_loc = halo_point_loc[pos]; - nodes->Add_FlowTraction(iPoint_loc,&halo_force_all[(offset+iPoint)*nDim]); - } - } + for (auto it = vertexArea.begin(); it != vertexArea.end(); ++it) { + auto iPoint = it->first; + su2double area = it->second; + su2double force[3] = {0.0}; + for (auto iDim = 0u; iDim < nDim; ++iDim) + force[iDim] = nodes->Get_FlowTraction(iPoint,iDim)*area; + nodes->Set_FlowTraction(iPoint, force); } - delete [] halo_point_num; - delete [] halo_point_all; - delete [] halo_force_all; -#endif } su2double CFEASolver::Compute_LoadCoefficient(su2double CurrentTime, su2double RampTime, CConfig *config){ From cf259f3e4359671166d72231f0ee66b993a0164a Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 31 Mar 2020 10:57:41 +0100 Subject: [PATCH 40/54] update jones turbo case from merge with develop --- TestCases/parallel_regression.py | 4 ++-- TestCases/serial_regression.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/TestCases/parallel_regression.py b/TestCases/parallel_regression.py index 1cfcf1f85b62..cdb016b222fa 100644 --- a/TestCases/parallel_regression.py +++ b/TestCases/parallel_regression.py @@ -834,7 +834,7 @@ def main(): Jones_tc.cfg_dir = "turbomachinery/APU_turbocharger" Jones_tc.cfg_file = "Jones.cfg" Jones_tc.test_iter = 5 - Jones_tc.test_vals = [-5.300315, 0.365966, 44.731500, 2.271371] #last 4 columns + Jones_tc.test_vals = [-5.280323, 0.379654, 44.725390, 2.271597] #last 4 columns Jones_tc.su2_exec = "parallel_computation.py -f" Jones_tc.timeout = 1600 Jones_tc.new_output = False @@ -846,7 +846,7 @@ def main(): Jones_tc_rst.cfg_dir = "turbomachinery/APU_turbocharger" Jones_tc_rst.cfg_file = "Jones_rst.cfg" Jones_tc_rst.test_iter = 5 - Jones_tc_rst.test_vals = [-4.626438, -1.570818, 34.014660, 10.187090] #last 4 columns + Jones_tc_rst.test_vals = [-4.625216, -1.569511, 34.013520, 10.187670] #last 4 columns Jones_tc_rst.su2_exec = "parallel_computation.py -f" Jones_tc_rst.timeout = 1600 Jones_tc_rst.new_output = False diff --git a/TestCases/serial_regression.py b/TestCases/serial_regression.py index 69cd1536926e..065017913e88 100644 --- a/TestCases/serial_regression.py +++ b/TestCases/serial_regression.py @@ -983,7 +983,7 @@ def main(): Jones_tc.cfg_dir = "turbomachinery/APU_turbocharger" Jones_tc.cfg_file = "Jones.cfg" Jones_tc.test_iter = 5 - Jones_tc.test_vals = [-5.300315, 0.365965, 44.731520, 2.271338] #last 4 columns + Jones_tc.test_vals = [-5.280323, 0.379653, 44.725410, 2.271564] #last 4 columns Jones_tc.su2_exec = "SU2_CFD" Jones_tc.new_output = False Jones_tc.timeout = 1600 @@ -995,7 +995,7 @@ def main(): Jones_tc_rst.cfg_dir = "turbomachinery/APU_turbocharger" Jones_tc_rst.cfg_file = "Jones_rst.cfg" Jones_tc_rst.test_iter = 5 - Jones_tc_rst.test_vals = [-4.626481, -1.570875, 34.015260, 10.187090] #last 4 columns + Jones_tc_rst.test_vals = [-4.625262, -1.569571, 34.014130, 10.187660] #last 4 columns Jones_tc_rst.su2_exec = "SU2_CFD" Jones_tc_rst.new_output = False Jones_tc_rst.timeout = 1600 From 96802c6d584e2111f89cfd428d863ef133401ef5 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 31 Mar 2020 12:05:16 +0100 Subject: [PATCH 41/54] avoid residual changes for mesh deformation cases --- Common/include/linear_algebra/CSysSolve.hpp | 11 +++++++++++ Common/src/linear_algebra/CSysSolve.cpp | 20 ++++++++++++++++++++ SU2_CFD/src/solvers/CMeshSolver.cpp | 1 + 3 files changed, 32 insertions(+) diff --git a/Common/include/linear_algebra/CSysSolve.hpp b/Common/include/linear_algebra/CSysSolve.hpp index df2848de37da..8f514a1c5bc3 100644 --- a/Common/include/linear_algebra/CSysSolve.hpp +++ b/Common/include/linear_algebra/CSysSolve.hpp @@ -48,6 +48,10 @@ template class CPreconditioner; using namespace std; +/*--- Relative tolerance, target residual is tol*||b-Ax||, + * Absolute tolerance, target residual is tol*||b||. ---*/ +enum class LinearToleranceType {RELATIVE, ABSOLUTE}; + /*! * \class CSysSolve * \brief Class for solving linear systems using classical and Krylov-subspace iterative methods @@ -98,6 +102,8 @@ class CSysSolve { VectorType* LinSysSol_ptr; /*!< \brief Pointer to appropriate LinSysSol (set to original or temporary in call to Solve). */ const VectorType* LinSysRes_ptr; /*!< \brief Pointer to appropriate LinSysRes (set to original or temporary in call to Solve). */ + LinearToleranceType tol_type = LinearToleranceType::RELATIVE; /*!< \brief How the linear solvers interpret the tolerance. */ + /*! * \brief sign transfer function * \param[in] x - value having sign prescribed @@ -313,4 +319,9 @@ class CSysSolve { */ inline ScalarType GetResidual(void) const { return Residual; } + /*! + * \brief Set the type of the tolerance for stoping the linear solvers (RELATIVE or ABSOLUTE). + */ + inline void SetToleranceType(LinearToleranceType type) {tol_type = type;} + }; diff --git a/Common/src/linear_algebra/CSysSolve.cpp b/Common/src/linear_algebra/CSysSolve.cpp index 334654f157bb..8e3118832f3c 100644 --- a/Common/src/linear_algebra/CSysSolve.cpp +++ b/Common/src/linear_algebra/CSysSolve.cpp @@ -239,6 +239,11 @@ unsigned long CSysSolve::CG_LinSolver(const CSysVector & return 0; } + /*--- Set the norm to the initial initial residual value ---*/ + + if (tol_type == LinearToleranceType::RELATIVE) + norm0 = norm_r; + /*--- Output header information including initial residual ---*/ if ((monitoring) && (master)) { @@ -394,6 +399,11 @@ unsigned long CSysSolve::FGMRES_LinSolver(const CSysVector::BCGSTAB_LinSolver(const CSysVector::Smoother_LinSolver(const CSysVector Date: Tue, 31 Mar 2020 12:09:53 +0100 Subject: [PATCH 42/54] re-delete files after merge with develop --- Common/include/interpolation_structure.hpp | 491 --- Common/src/interpolation_structure.cpp | 3849 -------------------- 2 files changed, 4340 deletions(-) delete mode 100644 Common/include/interpolation_structure.hpp delete mode 100644 Common/src/interpolation_structure.cpp diff --git a/Common/include/interpolation_structure.hpp b/Common/include/interpolation_structure.hpp deleted file mode 100644 index 3ea27b746b9d..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.3 "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/src/interpolation_structure.cpp b/Common/src/interpolation_structure.cpp deleted file mode 100644 index 2cf20d8ec7e5..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.3 "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."); - } -} From 123f008a84fe7b9d5d4648604beba96a1ef483cb Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 31 Mar 2020 16:47:54 +0100 Subject: [PATCH 43/54] update primal FSI cases due to initialized mesh deformation --- TestCases/parallel_regression.py | 6 +++--- TestCases/serial_regression.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/TestCases/parallel_regression.py b/TestCases/parallel_regression.py index 36b5cc768bc2..a21b75b97ce5 100644 --- a/TestCases/parallel_regression.py +++ b/TestCases/parallel_regression.py @@ -1052,7 +1052,7 @@ def main(): stat_fsi.cfg_dir = "fea_fsi/stat_fsi" stat_fsi.cfg_file = "config.cfg" stat_fsi.test_iter = 7 - stat_fsi.test_vals = [-3.313322, -4.963786, 0.000000, 46.000000] #last 5 columns + stat_fsi.test_vals = [-3.313612, -4.957573, 0.000000, 7.000000] #last 4 columns stat_fsi.su2_exec = "mpirun -n 2 SU2_CFD" stat_fsi.multizone = True stat_fsi.timeout = 1600 @@ -1064,7 +1064,7 @@ def main(): dyn_fsi.cfg_dir = "fea_fsi/dyn_fsi" dyn_fsi.cfg_file = "config.cfg" dyn_fsi.test_iter = 4 - dyn_fsi.test_vals = [-4.389734, -4.060117, 0.000000, 64.000000] #last 5 columns + dyn_fsi.test_vals = [-4.379832, -4.005999, 0.000000, 0.000000] #last 4 columns dyn_fsi.multizone = True dyn_fsi.unsteady = True dyn_fsi.su2_exec = "mpirun -n 2 SU2_CFD" @@ -1077,7 +1077,7 @@ def main(): stat_fsi_restart.cfg_dir = "fea_fsi/stat_fsi" stat_fsi_restart.cfg_file = "config_restart.cfg" stat_fsi_restart.test_iter = 1 - stat_fsi_restart.test_vals = [-3.422307, -4.212725, 0.000000, 46.000000] #last 5 columns + stat_fsi_restart.test_vals = [-3.422425, -4.289201, 0.000000, 27.000000] #last 4 columns stat_fsi_restart.su2_exec = "mpirun -n 2 SU2_CFD" stat_fsi_restart.multizone = True stat_fsi_restart.timeout = 1600 diff --git a/TestCases/serial_regression.py b/TestCases/serial_regression.py index c322903f1ece..06ba6e448457 100644 --- a/TestCases/serial_regression.py +++ b/TestCases/serial_regression.py @@ -1231,7 +1231,7 @@ def main(): stat_fsi.cfg_dir = "fea_fsi/stat_fsi" stat_fsi.cfg_file = "config.cfg" stat_fsi.test_iter = 7 - stat_fsi.test_vals = [-3.323551, -4.982863, 0.000000, 47.000000] #last 5 columns + stat_fsi.test_vals = [-3.326934, -4.981505, 0.000000, 7.000000] #last 5 columns stat_fsi.su2_exec = "SU2_CFD" stat_fsi.timeout = 1600 stat_fsi.multizone = True @@ -1243,7 +1243,7 @@ def main(): stat_fsi_restart.cfg_dir = "fea_fsi/stat_fsi" stat_fsi_restart.cfg_file = "config_restart.cfg" stat_fsi_restart.test_iter = 1 - stat_fsi_restart.test_vals = [-3.407098, -4.248366, 0.000000, 47.000000] #last 5 columns + stat_fsi_restart.test_vals = [-3.407486, -4.339837, 0.000000, 27.000000] #last 5 columns stat_fsi_restart.multizone = True stat_fsi_restart.su2_exec = "SU2_CFD" stat_fsi_restart.timeout = 1600 @@ -1255,7 +1255,7 @@ def main(): dyn_fsi.cfg_dir = "fea_fsi/dyn_fsi" dyn_fsi.cfg_file = "config.cfg" dyn_fsi.test_iter = 4 - dyn_fsi.test_vals = [-4.389732, -4.060110, 0.000000, 59.000000] #last 5 columns + dyn_fsi.test_vals = [-4.379829, -4.005994, 0.000000, 0.000000] #last 5 columns dyn_fsi.multizone = True dyn_fsi.unsteady = True dyn_fsi.su2_exec = "SU2_CFD" From 83bb15d55f9fe06d662c397d62f90104d47ae834 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 31 Mar 2020 16:48:26 +0100 Subject: [PATCH 44/54] fix for disc adj FEA problems --- SU2_CFD/include/limiters/computeLimiters_impl.hpp | 6 +----- SU2_CFD/src/solvers/CFEASolver.cpp | 4 ++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/SU2_CFD/include/limiters/computeLimiters_impl.hpp b/SU2_CFD/include/limiters/computeLimiters_impl.hpp index 9b4cd2fd3888..47c257349989 100644 --- a/SU2_CFD/include/limiters/computeLimiters_impl.hpp +++ b/SU2_CFD/include/limiters/computeLimiters_impl.hpp @@ -98,15 +98,13 @@ void computeLimiters_impl(CSolver* solver, omp_get_max_threads(), OMP_MAX_CHUNK); #endif -#ifdef CODI_REVERSE_TYPE bool tapeActive = false; if (config.GetDiscrete_Adjoint() && config.GetFrozen_Limiter_Disc()) { /*--- If limiters are frozen do not record the computation ---*/ - tapeActive = AD::globalTape.isActive(); + tapeActive = AD::TapeActive(); AD::StopRecording(); } -#endif CLimiterDetails limiterDetails; @@ -248,8 +246,6 @@ void computeLimiters_impl(CSolver* solver, } SU2_OMP_BARRIER -#ifdef CODI_REVERSE_TYPE if (tapeActive) AD::StartRecording(); -#endif } diff --git a/SU2_CFD/src/solvers/CFEASolver.cpp b/SU2_CFD/src/solvers/CFEASolver.cpp index 8dc172501e92..e68759d4e69c 100644 --- a/SU2_CFD/src/solvers/CFEASolver.cpp +++ b/SU2_CFD/src/solvers/CFEASolver.cpp @@ -2741,6 +2741,10 @@ void CFEASolver::Solve_System(CGeometry *geometry, CConfig *config) { SU2_OMP_PARALLEL { + /*--- This is required for the discrete adjoint. ---*/ + SU2_OMP_FOR_STAT(OMP_MIN_SIZE) + for (auto i = nPointDomain*nVar; i < nPoint*nVar; ++i) LinSysRes[i] = 0.0; + /*--- Solve or smooth the linear system. ---*/ auto iter = System.Solve(Jacobian, LinSysRes, LinSysSol, geometry, config); From 5e5237cf468b7774dc85670a02a0fa12021b53cd Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 31 Mar 2020 18:19:54 +0100 Subject: [PATCH 45/54] updates and fixes for testcases --- TestCases/parallel_regression_AD.py | 2 +- .../py_wrapper/disc_adj_flow/mesh_disp_sens/configAD_flow.cfg | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/TestCases/parallel_regression_AD.py b/TestCases/parallel_regression_AD.py index e8e1d10790eb..d1d23b143a49 100644 --- a/TestCases/parallel_regression_AD.py +++ b/TestCases/parallel_regression_AD.py @@ -303,7 +303,7 @@ def main(): discadj_fsi2.cfg_dir = "disc_adj_fsi/Airfoil_2d" discadj_fsi2.cfg_file = "config.cfg" discadj_fsi2.test_iter = 8 - discadj_fsi2.test_vals = [-5.070991, -2.5239e-13] #last 2 columns + discadj_fsi2.test_vals = [-5.071003, -2.5250e-13] #last 2 columns discadj_fsi2.su2_exec = "mpirun -n 2 SU2_CFD_AD" discadj_fsi2.timeout = 1600 discadj_fsi2.tol = 1e-16 diff --git a/TestCases/py_wrapper/disc_adj_flow/mesh_disp_sens/configAD_flow.cfg b/TestCases/py_wrapper/disc_adj_flow/mesh_disp_sens/configAD_flow.cfg index 6f8f5a69fecb..4d48d3472619 100644 --- a/TestCases/py_wrapper/disc_adj_flow/mesh_disp_sens/configAD_flow.cfg +++ b/TestCases/py_wrapper/disc_adj_flow/mesh_disp_sens/configAD_flow.cfg @@ -155,6 +155,7 @@ WRT_CON_FREQ_DUALTIME= 1 DEFORM_MESH = YES DEFORM_STIFFNESS_TYPE = WALL_DISTANCE +DEFORM_POISSONS_RATIO = 1E6 DEFORM_LINEAR_SOLVER = CONJUGATE_GRADIENT DEFORM_LINEAR_SOLVER_PREC = ILU From f73eec5ba876ea2ba8213e24ef962c9fb90a453a Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 31 Mar 2020 18:23:01 +0100 Subject: [PATCH 46/54] version update in new file --- TestCases/disc_adj_rans/naca0012/naca0012.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TestCases/disc_adj_rans/naca0012/naca0012.cfg b/TestCases/disc_adj_rans/naca0012/naca0012.cfg index 121f6adc3a08..dd94734aa705 100644 --- a/TestCases/disc_adj_rans/naca0012/naca0012.cfg +++ b/TestCases/disc_adj_rans/naca0012/naca0012.cfg @@ -5,7 +5,7 @@ % Author: Steffen Schotthöfer % % Institution: TU Kaiserslautern % % Date: Mar 16, 2017 % -% File Version 7.0.2 "Blackbird" % +% File Version 7.0.3 "Blackbird" % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% From 4944e2e83c842508f38abdff552830df98a6a92e Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 1 Apr 2020 13:12:18 +0100 Subject: [PATCH 47/54] fix #915 --- SU2_CFD/src/output/CFlowCompOutput.cpp | 1 + SU2_CFD/src/output/CFlowIncOutput.cpp | 1 + SU2_CFD/src/solvers/CEulerSolver.cpp | 27 +++++++++----------- SU2_CFD/src/solvers/CIncEulerSolver.cpp | 33 ++++++------------------- 4 files changed, 21 insertions(+), 41 deletions(-) diff --git a/SU2_CFD/src/output/CFlowCompOutput.cpp b/SU2_CFD/src/output/CFlowCompOutput.cpp index d52ade6b9124..1d64d91625b1 100644 --- a/SU2_CFD/src/output/CFlowCompOutput.cpp +++ b/SU2_CFD/src/output/CFlowCompOutput.cpp @@ -58,6 +58,7 @@ CFlowCompOutput::CFlowCompOutput(CConfig *config, unsigned short nDim) : CFlowOu requestedVolumeFields.emplace_back("COORDINATES"); requestedVolumeFields.emplace_back("SOLUTION"); requestedVolumeFields.emplace_back("PRIMITIVE"); + if (config->GetGrid_Movement()) requestedVolumeFields.emplace_back("GRID_VELOCITY"); nRequestedVolumeFields = requestedVolumeFields.size(); } diff --git a/SU2_CFD/src/output/CFlowIncOutput.cpp b/SU2_CFD/src/output/CFlowIncOutput.cpp index 64dd61c090d8..b631a6a20b65 100644 --- a/SU2_CFD/src/output/CFlowIncOutput.cpp +++ b/SU2_CFD/src/output/CFlowIncOutput.cpp @@ -60,6 +60,7 @@ CFlowIncOutput::CFlowIncOutput(CConfig *config, unsigned short nDim) : CFlowOutp requestedVolumeFields.emplace_back("COORDINATES"); requestedVolumeFields.emplace_back("SOLUTION"); requestedVolumeFields.emplace_back("PRIMITIVE"); + if (config->GetGrid_Movement()) requestedVolumeFields.emplace_back("GRID_VELOCITY"); nRequestedVolumeFields = requestedVolumeFields.size(); } diff --git a/SU2_CFD/src/solvers/CEulerSolver.cpp b/SU2_CFD/src/solvers/CEulerSolver.cpp index 327a50b5c92e..06d623de796f 100644 --- a/SU2_CFD/src/solvers/CEulerSolver.cpp +++ b/SU2_CFD/src/solvers/CEulerSolver.cpp @@ -11483,19 +11483,14 @@ void CEulerSolver::LoadRestart(CGeometry **geometry, CSolver ***solver, CConfig unsigned short iDim, iVar, iMesh, iMeshFine; unsigned long iPoint, index, iChildren, Point_Fine; unsigned short turb_model = config->GetKind_Turb_Model(); - su2double Area_Children, Area_Parent, *Coord, *Solution_Fine; + su2double Area_Children, Area_Parent, Coord[MAXNDIM] = {0.0}, *Solution_Fine; bool dual_time = ((config->GetTime_Marching() == DT_STEPPING_1ST) || (config->GetTime_Marching() == DT_STEPPING_2ND)); - bool static_fsi = ((config->GetTime_Marching() == STEADY) && - config->GetFSI_Simulation()); + bool static_fsi = ((config->GetTime_Marching() == STEADY) && config->GetFSI_Simulation()); bool steady_restart = config->GetSteadyRestart(); - bool turbulent = (config->GetKind_Turb_Model() != NONE); - - string restart_filename = config->GetFilename(config->GetSolution_FileName(), "", val_iter); + bool turbulent = (config->GetKind_Turb_Model() != NONE); - Coord = new su2double [nDim]; - for (iDim = 0; iDim < nDim; iDim++) - Coord[iDim] = 0.0; + string restart_filename = config->GetFilename(config->GetSolution_FileName(), "", val_iter); int counter = 0; long iPoint_Local = 0; unsigned long iPoint_Global = 0; @@ -11550,13 +11545,12 @@ void CEulerSolver::LoadRestart(CGeometry **geometry, CSolver ***solver, CConfig /*--- If we are restarting the solution from a previously computed static calculation (no grid movement) ---*/ /*--- the grid velocities are set to 0. This is useful for FSI computations ---*/ + /*--- Rewind the index to retrieve the Coords. ---*/ + index = counter*Restart_Vars[1]; + for (iDim = 0; iDim < nDim; iDim++) { Coord[iDim] = Restart_Data[index+iDim]; } + su2double GridVel[3] = {0.0,0.0,0.0}; if (!steady_restart) { - - /*--- Rewind the index to retrieve the Coords. ---*/ - index = counter*Restart_Vars[1]; - for (iDim = 0; iDim < nDim; iDim++) { Coord[iDim] = Restart_Data[index+iDim]; } - /*--- Move the index forward to get the grid velocities. ---*/ index = counter*Restart_Vars[1] + skipVars + nVar + turbVars; for (iDim = 0; iDim < nDim; iDim++) { GridVel[iDim] = Restart_Data[index+iDim]; } @@ -11568,6 +11562,9 @@ void CEulerSolver::LoadRestart(CGeometry **geometry, CSolver ***solver, CConfig } } + /*--- For static FSI problems, grid_movement is 0 but we need to read in and store the + grid coordinates for each node (but not the grid velocities, as there are none). ---*/ + if (static_fsi && val_update_geo) { /*--- Rewind the index to retrieve the Coords. ---*/ index = counter*Restart_Vars[1]; @@ -11691,8 +11688,6 @@ void CEulerSolver::LoadRestart(CGeometry **geometry, CSolver ***solver, CConfig if (dual_time && config->GetGrid_Movement() && (config->GetKind_GridMovement() != RIGID_MOTION)) Restart_OldGeometry(geometry[MESH_0], config); - delete [] Coord; - /*--- Delete the class memory that is used to load the restart. ---*/ delete [] Restart_Vars; Restart_Vars = nullptr; diff --git a/SU2_CFD/src/solvers/CIncEulerSolver.cpp b/SU2_CFD/src/solvers/CIncEulerSolver.cpp index 2a7a0d8427be..0e20070e169f 100644 --- a/SU2_CFD/src/solvers/CIncEulerSolver.cpp +++ b/SU2_CFD/src/solvers/CIncEulerSolver.cpp @@ -5830,24 +5830,18 @@ void CIncEulerSolver::LoadRestart(CGeometry **geometry, CSolver ***solver, CConf unsigned short iDim, iVar, iMesh, iMeshFine; unsigned long iPoint, index, iChildren, Point_Fine; unsigned short turb_model = config->GetKind_Turb_Model(); - su2double Area_Children, Area_Parent, *Coord, *Solution_Fine; - bool static_fsi = ((config->GetTime_Marching() == STEADY) && - (config->GetFSI_Simulation())); + su2double Area_Children, Area_Parent, Coord[3] = {0.0}, *Solution_Fine; + bool static_fsi = ((config->GetTime_Marching() == STEADY) && config->GetFSI_Simulation()); bool dual_time = ((config->GetTime_Marching() == DT_STEPPING_1ST) || (config->GetTime_Marching() == DT_STEPPING_2ND)); bool steady_restart = config->GetSteadyRestart(); - bool turbulent = (config->GetKind_Solver() == INC_RANS) || (config->GetKind_Solver() == DISC_ADJ_INC_RANS); + bool turbulent = (config->GetKind_Solver() == INC_RANS) || (config->GetKind_Solver() == DISC_ADJ_INC_RANS); string restart_filename = config->GetFilename(config->GetSolution_FileName(), "", val_iter); - Coord = new su2double [nDim]; - for (iDim = 0; iDim < nDim; iDim++) - Coord[iDim] = 0.0; - int counter = 0; long iPoint_Local = 0; unsigned long iPoint_Global = 0; unsigned long iPoint_Global_Local = 0; - unsigned short rbuf_NotMatching = 0, sbuf_NotMatching = 0; /*--- Skip coordinates ---*/ @@ -5910,13 +5904,12 @@ void CIncEulerSolver::LoadRestart(CGeometry **geometry, CSolver ***solver, CConf /*--- If we are restarting the solution from a previously computed static calculation (no grid movement) ---*/ /*--- the grid velocities are set to 0. This is useful for FSI computations ---*/ + /*--- Rewind the index to retrieve the Coords. ---*/ + index = counter*Restart_Vars[1]; + for (iDim = 0; iDim < nDim; iDim++) { Coord[iDim] = Restart_Data[index+iDim]; } + su2double GridVel[3] = {0.0,0.0,0.0}; if (!steady_restart) { - - /*--- Rewind the index to retrieve the Coords. ---*/ - index = counter*Restart_Vars[1]; - for (iDim = 0; iDim < nDim; iDim++) { Coord[iDim] = Restart_Data[index+iDim]; } - /*--- Move the index forward to get the grid velocities. ---*/ index = counter*Restart_Vars[1] + skipVars + nVar_Restart + turbVars; for (iDim = 0; iDim < nDim; iDim++) { GridVel[iDim] = Restart_Data[index+iDim]; } @@ -5928,7 +5921,6 @@ void CIncEulerSolver::LoadRestart(CGeometry **geometry, CSolver ***solver, CConf } } - /*--- For static FSI problems, grid_movement is 0 but we need to read in and store the grid coordinates for each node (but not the grid velocities, as there are none). ---*/ @@ -5950,14 +5942,7 @@ void CIncEulerSolver::LoadRestart(CGeometry **geometry, CSolver ***solver, CConf /*--- Detect a wrong solution file ---*/ - if (iPoint_Global_Local < nPointDomain) { sbuf_NotMatching = 1; } - -#ifndef HAVE_MPI - rbuf_NotMatching = sbuf_NotMatching; -#else - SU2_MPI::Allreduce(&sbuf_NotMatching, &rbuf_NotMatching, 1, MPI_UNSIGNED_SHORT, MPI_SUM, MPI_COMM_WORLD); -#endif - if (rbuf_NotMatching != 0) { + if (iPoint_Global_Local < nPointDomain) { SU2_MPI::Error(string("The solution file ") + restart_filename + string(" doesn't match with the mesh file!\n") + string("It could be empty lines at the end of the file."), CURRENT_FUNCTION); } @@ -6057,8 +6042,6 @@ void CIncEulerSolver::LoadRestart(CGeometry **geometry, CSolver ***solver, CConf if (dual_time && config->GetGrid_Movement() && (config->GetKind_GridMovement() != RIGID_MOTION)) Restart_OldGeometry(geometry[MESH_0], config); - delete [] Coord; - /*--- Delete the class memory that is used to load the restart. ---*/ if (Restart_Vars != NULL) delete [] Restart_Vars; From 0015fb551a462f6de4f83549f84b340f28a41f13 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 2 Apr 2020 16:20:10 +0100 Subject: [PATCH 48/54] refactor isoparametric interpolation --- Common/include/geometry/dual_grid/CVertex.hpp | 26 - Common/include/geometry/elements/CElement.hpp | 22 +- .../interface_interpolation/CInterpolator.hpp | 52 +- .../CIsoparametric.hpp | 53 +- Common/include/toolboxes/geometry_toolbox.hpp | 116 +++ Common/src/geometry/elements/CQUAD4.cpp | 20 +- .../interface_interpolation/CInterpolator.cpp | 172 ++-- .../CIsoparametric.cpp | 762 ++++++++---------- .../CNearestNeighbor.cpp | 15 +- .../CRadialBasisFunction.cpp | 13 +- .../interface_interpolation/CSlidingMesh.cpp | 7 +- SU2_CFD/src/solvers/CFEASolver.cpp | 48 +- 12 files changed, 617 insertions(+), 689 deletions(-) create mode 100644 Common/include/toolboxes/geometry_toolbox.hpp diff --git a/Common/include/geometry/dual_grid/CVertex.hpp b/Common/include/geometry/dual_grid/CVertex.hpp index 6d5915dd9ed6..41c2fc4da20d 100644 --- a/Common/include/geometry/dual_grid/CVertex.hpp +++ b/Common/include/geometry/dual_grid/CVertex.hpp @@ -49,8 +49,6 @@ class CVertex : public CDualGrid { 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_Coeff. */ @@ -307,30 +305,6 @@ class CVertex : public CDualGrid { */ inline bool GetActDisk_Perimeter(void) const { return ActDisk_Perimeter; } - /*! - * \brief Set the donor element of a vertex for interpolation across zones. - * \param[in] val_donorelem - donor element index. - */ - inline void SetDonorElem(long val_donorelem) { Donor_Elem = val_donorelem; } - - /*! - * \brief Get the donor element of a vertex for interpolation across zones. - * \return Value of the donor element of a vertex. - */ - inline long GetDonorElem(void) const { return Donor_Elem; } - - /*! - * \brief Set the donor face of a vertex for interpolation across zones. - * \param[in] val_donorface- donor face index (w/in donor elem). - */ - inline void SetDonorFace(unsigned short val_donorface) { Donor_Face = val_donorface; } - - /*! - * \brief Get the donor face of a vertex for interpolation across zones. - * \return Value of the donor face index (w/in donor elem). - */ - inline unsigned short GetDonorFace(void) const { return Donor_Face; } - /*! * \brief Set the finite element basis functions needed for interpolation. * \param[in] val_node - a node index of the owner element. diff --git a/Common/include/geometry/elements/CElement.hpp b/Common/include/geometry/elements/CElement.hpp index 20975dd2b9fc..c3aae53ef255 100644 --- a/Common/include/geometry/elements/CElement.hpp +++ b/Common/include/geometry/elements/CElement.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) @@ -627,6 +627,26 @@ class CQUAD4 final : public CElementWithKnownSizes<4,4,2> { */ CQUAD4(); + /*! + * \brief Shape functions (Ni) evaluated at point Xi,Eta. + */ + inline static void ShapeFunctions(su2double Xi, su2double Eta, su2double* Ni) { + Ni[0] = 0.25*(1.0-Xi)*(1.0-Eta); + Ni[1] = 0.25*(1.0+Xi)*(1.0-Eta); + Ni[2] = 0.25*(1.0+Xi)*(1.0+Eta); + Ni[3] = 0.25*(1.0-Xi)*(1.0+Eta); + } + + /*! + * \brief Shape function Jacobian (dNi) evaluated at point Xi,Eta. + */ + inline static void ShapeFunctionJacobian(su2double Xi, su2double Eta, su2double dNi[][2]) { + dNi[0][0] = -0.25*(1.0-Eta); dNi[0][1] = -0.25*(1.0-Xi); + dNi[1][0] = 0.25*(1.0-Eta); dNi[1][1] = -0.25*(1.0+Xi); + dNi[2][0] = 0.25*(1.0+Eta); dNi[2][1] = 0.25*(1.0+Xi); + dNi[3][0] = -0.25*(1.0+Eta); dNi[3][1] = 0.25*(1.0-Xi); + } + /*! * \brief Compute the value of the area of the element. * \param[in] mode - Type of coordinates to consider in the computation. diff --git a/Common/include/interface_interpolation/CInterpolator.hpp b/Common/include/interface_interpolation/CInterpolator.hpp index ec15bd712db1..de0754b94d0d 100644 --- a/Common/include/interface_interpolation/CInterpolator.hpp +++ b/Common/include/interface_interpolation/CInterpolator.hpp @@ -27,10 +27,14 @@ #pragma once #include "../../include/datatype_structure.hpp" +#include "../../include/toolboxes/C2DContainer.hpp" +#include class CConfig; class CGeometry; +using namespace std; + /*! * \class CInterpolator * \brief Main class for defining the interpolator, it requires @@ -147,47 +151,35 @@ class CInterpolator { 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 + * \brief Determine array sizes used to collect and send coordinate and global point information. * \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); + void Determine_ArraySize(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 + * \brief Collect and communicate vertex info: coord, global point. * \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); + void Collect_VertexInfo(int markDonor, int markTarget, unsigned long nVertexDonor, unsigned short nDim); + /*! + * \brief Collect all donor elements in an interface pair. + * \param[in] markDonor - Index of the boundary on the donor domain. + * \param[in] nDim - number of physical dimensions. + * \param[in] compress - Squeeze the information (Allgatherv instead of Allgather). + * \param[out] allNumElem - Number of donor element per rank. + * \param[out] numNodes - Number of nodes for each element. + * \param[out] idxNodes - Index (global) of those nodes. + * \return Number of collected donor elements. + * \note The last two outputs are always sized for Allgather. + */ + unsigned long Collect_ElementInfo(int markDonor, unsigned short nDim, bool compress, + vector& allNumElem, vector& numNodes, + su2matrix& idxNodes) const; }; diff --git a/Common/include/interface_interpolation/CIsoparametric.hpp b/Common/include/interface_interpolation/CIsoparametric.hpp index 7b81bc765270..2990026e40d3 100644 --- a/Common/include/interface_interpolation/CIsoparametric.hpp +++ b/Common/include/interface_interpolation/CIsoparametric.hpp @@ -1,7 +1,7 @@ /*! * \file CIsoparametric.hpp * \brief Isoparametric interpolation using FE shape functions. - * \author H. Kline + * \author H. Kline, P. Gomes * \version 7.0.3 "Blackbird" * * SU2 Project Website: https://su2code.github.io @@ -32,6 +32,10 @@ * \brief Isoparametric interpolation. */ class CIsoparametric final : public CInterpolator { +private: + su2double MaxDistance = 0.0, ErrorRate = 0.0; + unsigned long ErrorCounter = 0; + public: /*! * \brief Constructor of the class. @@ -40,7 +44,8 @@ class CIsoparametric final : public CInterpolator { * \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); + CIsoparametric(CGeometry ****geometry_container, const CConfig* const* config, + unsigned int iZone, unsigned int jZone); /*! * \brief Set up transfer matrix defining relation between two meshes @@ -48,23 +53,37 @@ class CIsoparametric final : public CInterpolator { */ void Set_TransferCoeff(const CConfig* const* config) override; + /*! + * \brief Print information about the interpolation. + */ + void PrintStatistics(void) const 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) + * \brief Compute the isoparametric interpolation coefficients for a 2D line element. + * \param[in] X - Coordinate matrix defining the line. + * \param[in] xj - Coordinates of the target point projected onto the plane of the element. + * \param[out] isoparams - Isoparametric coefficients. + * \return 0 on success, 1 if xj is outside element bounds. + */ + static int LineIsoparameters(const su2double X[][3], const su2double *xj, su2double* isoparams); + + /*! + * \brief Compute the isoparametric interpolation coefficients for a 3D triangle element. + * \param[in] X - Coordinate matrix defining the triangle. + * \param[in] xj - Coordinates of the target point projected onto the plane of the element. + * \param[out] isoparams - Isoparametric coefficients. + * \return 0 on success, 1 if xj is outside element bounds. + */ + static int TriangleIsoparameters(const su2double X[][3], const su2double *xj, su2double* isoparams); + + /*! + * \brief Compute the isoparametric interpolation coefficients for a 3D quadrilateral element. + * \param[in] X - Coordinate matrix defining the quadrilateral. + * \param[in] xj - Coordinates of the target point projected onto the plane of the element. + * \param[out] isoparams - Isoparametric coefficients. + * \return 0 on success, 1 if xj is outside element bounds. */ - void Isoparameters(unsigned short nDim, unsigned short nDonor, const su2double *X, - const su2double *xj, su2double* isoparams) const; + static int QuadrilateralIsoparameters(const su2double X[][3], const su2double *xj, su2double* isoparams); }; diff --git a/Common/include/toolboxes/geometry_toolbox.hpp b/Common/include/toolboxes/geometry_toolbox.hpp new file mode 100644 index 000000000000..7891ae89f1b4 --- /dev/null +++ b/Common/include/toolboxes/geometry_toolbox.hpp @@ -0,0 +1,116 @@ +/*! + * \file geometry_toolbox.hpp + * \brief Collection of common lightweight geometry-oriented methods. + * \version 7.0.3 "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 + +namespace GeometryToolbox { + +/*! \return ||a-b||^2 */ +template +inline T SquaredDistance(Int nDim, const T* a, const T* b) { + T d(0); + for(Int i = 0; i < nDim; i++) d += pow(a[i]-b[i], 2); + return d; +} + +/*! \return ||a-b|| */ +template +inline T Distance(Int nDim, const T* a, const T* b) { + return sqrt(SquaredDistance(nDim, a, b)); +} + +/*! \brief d = a-b */ +template +inline void Distance(Int nDim, const T* a, const T* b, T* d) { + for(Int i = 0; i < nDim; i++) d[i] = a[i] - b[i]; +} + +/*! \return a.b */ +template +inline T DotProduct(Int nDim, const T* a, const T* b) { + T d(0); + for (Int i = 0; i < nDim; ++i) d += a[i]*b[i]; + return d; +} + +/*! \return ||a||^2 */ +template +inline T SquaredNorm(Int nDim, const T* a) { + return DotProduct(nDim, a, a); +} + +/*! \return ||a|| */ +template +inline T Norm(Int nDim, const T* a) { + return sqrt(SquaredNorm(nDim, a)); +} + +/*! \brief c = a x b */ +template +inline void CrossProduct(const T* a, const T* b, T* c) { + c[0] = a[1]*b[2] - a[2]*b[1]; + c[1] = a[2]*b[0] - a[0]*b[2]; + c[2] = a[0]*b[1] - a[1]*b[0]; +} + +/*! \brief Set U as the normal to a 2D line defined by coords[iPoint][iDim]. */ +template +inline void LineNormal(const T& coords, U* normal) { + normal[0] = coords[0][1] - coords[1][1]; + normal[1] = coords[1][0] - coords[0][0]; +} + +/*! \brief Normal vector of a triangle, cross product of two sides. */ +template +inline void TriangleNormal(const T& coords, U* normal) { + + U a[3], b[3]; + + for (int iDim = 0; iDim < 3; iDim++) { + a[iDim] = coords[1][iDim] - coords[0][iDim]; + b[iDim] = coords[2][iDim] - coords[0][iDim]; + } + + CrossProduct(a, b, normal); + normal[0] *= 0.5; normal[1] *= 0.5; normal[2] *= 0.5; +} + +/*! \brief Normal vector of a quadrilateral, cross product of the two diagonals. */ +template +inline void QuadrilateralNormal(const T& coords, U* normal) { + + U a[3], b[3]; + + for (int iDim = 0; iDim < 3; iDim++) { + a[iDim] = coords[2][iDim] - coords[0][iDim]; + b[iDim] = coords[3][iDim] - coords[1][iDim]; + } + + CrossProduct(a, b, normal); + normal[0] *= 0.5; normal[1] *= 0.5; normal[2] *= 0.5; +} + +} diff --git a/Common/src/geometry/elements/CQUAD4.cpp b/Common/src/geometry/elements/CQUAD4.cpp index a47051f36e0e..7661a2da251b 100644 --- a/Common/src/geometry/elements/CQUAD4.cpp +++ b/Common/src/geometry/elements/CQUAD4.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) @@ -41,25 +41,23 @@ CQUAD4::CQUAD4() : CElementWithKnownSizes() { /*--- Store the values of the shape functions and their derivatives ---*/ - unsigned short iNode, iGauss; - su2double Xi, Eta, val_Ni; + unsigned short iNode, iGauss, jGauss; + su2double Xi, Eta; for (iGauss = 0; iGauss < NGAUSS; iGauss++) { Xi = GaussCoord[iGauss][0]; Eta = GaussCoord[iGauss][1]; - val_Ni = 0.25*(1.0-Xi)*(1.0-Eta); GaussPoint[iGauss].SetNi(val_Ni,0); - val_Ni = 0.25*(1.0+Xi)*(1.0-Eta); GaussPoint[iGauss].SetNi(val_Ni,1); - val_Ni = 0.25*(1.0+Xi)*(1.0+Eta); GaussPoint[iGauss].SetNi(val_Ni,2); - val_Ni = 0.25*(1.0-Xi)*(1.0+Eta); GaussPoint[iGauss].SetNi(val_Ni,3); + su2double Ni[4] = {0.0}; + ShapeFunctions(Xi, Eta, Ni); + + for (jGauss = 0; jGauss < NGAUSS; jGauss++) + GaussPoint[iGauss].SetNi(Ni[jGauss], jGauss); /*--- dN/d xi, dN/d eta ---*/ - dNiXj[iGauss][0][0] = -0.25*(1.0-Eta); dNiXj[iGauss][0][1] = -0.25*(1.0-Xi); - dNiXj[iGauss][1][0] = 0.25*(1.0-Eta); dNiXj[iGauss][1][1] = -0.25*(1.0+Xi); - dNiXj[iGauss][2][0] = 0.25*(1.0+Eta); dNiXj[iGauss][2][1] = 0.25*(1.0+Xi); - dNiXj[iGauss][3][0] = -0.25*(1.0+Eta); dNiXj[iGauss][3][1] = 0.25*(1.0-Xi); + ShapeFunctionJacobian(Xi, Eta, dNiXj[iGauss]); } diff --git a/Common/src/interface_interpolation/CInterpolator.cpp b/Common/src/interface_interpolation/CInterpolator.cpp index cef2fa431b9b..d8e3f931350d 100644 --- a/Common/src/interface_interpolation/CInterpolator.cpp +++ b/Common/src/interface_interpolation/CInterpolator.cpp @@ -60,132 +60,110 @@ bool CInterpolator::CheckInterfaceBoundary(int markDonor, int markTarget) { 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; - } - } - } +void CInterpolator::Determine_ArraySize(int markDonor, int markTarget, + unsigned long nVertexDonor, unsigned short nDim) { + + /*--- Count donor vertices. ---*/ + auto nLocalVertex_Donor = 0ul; + for (auto iVertex = 0ul; iVertex < nVertexDonor; iVertex++) { + auto iPointDonor = donor_geometry->vertex[markDonor][iVertex]->GetNode(); + nLocalVertex_Donor += donor_geometry->node[iPointDonor]->GetDomain(); } 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) { +void CInterpolator::Collect_VertexInfo(int markDonor, int markTarget, + unsigned long nVertexDonor, unsigned short nDim) { - unsigned long iVertex, iPointDonor = 0, iVertexDonor, nBuffer_Coord, nBuffer_Point, nLocalVertex_Donor; + unsigned long iVertex; 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; + auto iLocalVertexDonor = 0ul; - for (iVertexDonor = 0; iVertexDonor < nVertexDonor; iVertexDonor++) { - iPointDonor = donor_geometry->vertex[markDonor][iVertexDonor]->GetNode(); + for (iVertex = 0; iVertex < nVertexDonor; iVertex++) { + auto iPointDonor = donor_geometry->vertex[markDonor][iVertex]->GetNode(); if (donor_geometry->node[iPointDonor]->GetDomain()) { - Buffer_Send_GlobalPoint[nLocalVertex_Donor] = donor_geometry->node[iPointDonor]->GetGlobalIndex(); + Buffer_Send_GlobalPoint[iLocalVertexDonor] = 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++; + Buffer_Send_Coord[iLocalVertexDonor*nDim+iDim] = donor_geometry->node[iPointDonor]->GetCoord(iDim); + iLocalVertexDonor++; } } - nBuffer_Coord = MaxLocalVertex_Donor*nDim; - nBuffer_Point = MaxLocalVertex_Donor; + auto nBuffer_Coord = MaxLocalVertex_Donor*nDim; + auto 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); +} + +unsigned long CInterpolator::Collect_ElementInfo(int markDonor, unsigned short nDim, bool compress, + vector& allNumElem, vector& numNodes, + su2matrix& idxNodes) const { + + const auto maxElemNodes = (nDim == 2u)? 2u : 4u; // line and quad respectively + + unsigned long nElemDonor = 0; + if (markDonor != -1) nElemDonor = donor_geometry->GetnElem_Bound(markDonor); + + allNumElem.resize(size); + SU2_MPI::Allgather(&nElemDonor, 1, MPI_UNSIGNED_LONG, allNumElem.data(), 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + + auto nMaxElemDonor = *max_element(allNumElem.begin(), allNumElem.end()); + + vector bufferSendNum(nMaxElemDonor); + su2matrix bufferSendIdx(nMaxElemDonor, maxElemNodes); + + numNodes.resize(nMaxElemDonor*size); + idxNodes.resize(nMaxElemDonor*size, maxElemNodes); + + for (auto iElem = 0ul; iElem < nElemDonor; ++iElem) { + + const auto nNode = donor_geometry->bound[markDonor][iElem]->GetnNodes(); + bufferSendNum[iElem] = nNode; + assert(nNode < maxElemNodes && "Donor element has too many nodes."); + + for (auto iNode = 0u; iNode < nNode; ++iNode) { + auto iPoint = donor_geometry->bound[markDonor][iElem]->GetNode(iNode); + auto iPointGlobal = donor_geometry->node[iPoint]->GetGlobalIndex(); + bufferSendIdx(iElem, iNode) = iPointGlobal; + } } + + SU2_MPI::Allgather(bufferSendNum.data(), bufferSendNum.size(), MPI_UNSIGNED_SHORT, + numNodes.data(), bufferSendNum.size(), MPI_UNSIGNED_SHORT, MPI_COMM_WORLD); + SU2_MPI::Allgather(bufferSendIdx.data(), bufferSendIdx.size(), MPI_LONG, + idxNodes.data(), bufferSendIdx.size(), MPI_LONG, MPI_COMM_WORLD); + + if (!compress) + return accumulate(allNumElem.begin(), allNumElem.end(), 0ul); + + /*--- Compress the information (overlapping copy do not use memcpy). ---*/ + + unsigned long dstIdx = 0; + for (int iProcessor = 0; iProcessor < size; ++iProcessor) { + auto srcOffset = iProcessor * nMaxElemDonor; + for (auto idx = 0u; idx < allNumElem[iProcessor]; ++idx) { + numNodes[dstIdx] = numNodes[srcOffset+idx]; + for (auto iNode = 0u; iNode < maxElemNodes; ++iNode) + idxNodes(dstIdx, iNode) = idxNodes(srcOffset+idx, iNode); + ++dstIdx; + } + } + + return dstIdx; } void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ diff --git a/Common/src/interface_interpolation/CIsoparametric.cpp b/Common/src/interface_interpolation/CIsoparametric.cpp index e83a4902d17c..3312a39f878d 100644 --- a/Common/src/interface_interpolation/CIsoparametric.cpp +++ b/Common/src/interface_interpolation/CIsoparametric.cpp @@ -1,7 +1,7 @@ /*! * \file CIsoparametric.cpp * \brief Implementation isoparametric interpolation (using FE shape functions). - * \author H. Kline + * \author H. Kline, P. Gomes * \version 7.0.3 "Blackbird" * * SU2 Project Website: https://su2code.github.io @@ -28,6 +28,11 @@ #include "../../include/interface_interpolation/CIsoparametric.hpp" #include "../../include/CConfig.hpp" #include "../../include/geometry/CGeometry.hpp" +#include "../../include/geometry/elements/CElement.hpp" +#include "../../include/toolboxes/geometry_toolbox.hpp" +#include + +using namespace GeometryToolbox; CIsoparametric::CIsoparametric(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, @@ -35,520 +40,387 @@ CIsoparametric::CIsoparametric(CGeometry ****geometry_container, const CConfig* Set_TransferCoeff(config); } +void CIsoparametric::PrintStatistics(void) const { + if (rank != MASTER_NODE) return; + cout << " Maximum distance to closest donor element: " << MaxDistance << ".\n" + << " Interpolation mitigated on " << ErrorCounter << " (" << ErrorRate << "%) target vertices." << endl; +} + void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { - unsigned long iVertex, jVertex; - unsigned long dPoint, inode, jElem, nElem; - unsigned short iDim, iDonor=0, iFace; + /*--- Angle between target and donor below which we trigger fallback measures. ---*/ + const su2double thetaMin = 80.0/180.0*PI_NUMBER; - unsigned short nDim = donor_geometry->GetnDim(); + const int nProcessor = size; + const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2; + const auto nDim = donor_geometry->GetnDim(); - unsigned short nMarkerInt; - unsigned short iMarkerInt; + Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; - int markDonor=0, markTarget=0; + /*--- Init stats. ---*/ + MaxDistance = 0.0; ErrorCounter = 0; + unsigned long nGlobalVertexTarget = 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; + /*--- Cycle over nMarkersInt interface to determine communication pattern. ---*/ - unsigned long nVertexDonor = 0, nVertexTarget= 0; - unsigned long Point_Target = 0; + for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { - unsigned long iVertexDonor, iPointDonor = 0; - int iProcessor; + /* High level procedure: + * - Loop through vertices of the target grid; + * - Find nearest element; + * - Compute and set the transfer coefficients. + */ - unsigned long nLocalFace_Donor = 0, nLocalFaceNodes_Donor=0; + /*--- On the donor side: find the tag of the boundary sharing the interface. ---*/ + const auto markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); - unsigned long faceindex; + /*--- On the target side: find the tag of the boundary sharing the interface. ---*/ + const auto markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); - 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]; + /*--- Checks if the zone contains the interface, if not continue to the next step. ---*/ + if (!CheckInterfaceBoundary(markDonor, markTarget)) continue; - int nProcessor = size; - Coord = new su2double[nDim]; - Normal = new su2double[nDim]; + unsigned long nVertexDonor = 0, nVertexTarget = 0; + if (markDonor != -1) nVertexDonor = donor_geometry->GetnVertex(markDonor); + if (markTarget != -1) nVertexTarget = target_geometry->GetnVertex(markTarget); - 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(markDonor, markTarget, nVertexDonor, nDim); - nMarkerInt = (config[donorZone]->GetMarker_n_ZoneInterface())/2; + const auto nGlobalVertexDonor = accumulate(Buffer_Receive_nVertex_Donor, + Buffer_Receive_nVertex_Donor+nProcessor, 0ul); - /*--- 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 - */ + 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 ]; - /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ - markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); + /*--- Collect coordinates and global point indices. ---*/ + Collect_VertexInfo(markDonor, markTarget, nVertexDonor, nDim); - /*--- On the target side: find the tag of the boundary sharing the interface ---*/ - markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); + /*--- Compress the vertex information, and build a map of global point to "compressed + * index" to then reconstruct the donor elements in local index space. ---*/ - /*--- Checks if the zone contains the interface, if not continue to the next step ---*/ - if(!CheckInterfaceBoundary(markDonor, markTarget)) continue; + su2activematrix donorCoord(nGlobalVertexDonor, nDim); + vector donorPoint(nGlobalVertexDonor); + vector donorProc(nGlobalVertexDonor); + unordered_map globalToLocalMap; - if(markDonor != -1) - nVertexDonor = donor_geometry->GetnVertex( markDonor ); - else - nVertexDonor = 0; + 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; + assert((globalToLocalMap.count(donorPoint[iCount]) == 0) && "Duplicate donor point found."); + globalToLocalMap[donorPoint[iCount]] = iCount; + ++iCount; + } + } + assert((iCount == nGlobalVertexDonor) && "Global donor point count mismatch."); - if(markTarget != -1) - nVertexTarget = target_geometry->GetnVertex( markTarget ); - else - nVertexTarget = 0; + delete[] Buffer_Send_Coord; + delete[] Buffer_Send_GlobalPoint; + delete[] Buffer_Receive_Coord; + delete[] Buffer_Receive_GlobalPoint; - /* Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor */ - Determine_ArraySize(true, markDonor, markTarget, nVertexDonor, nDim); + /*--- Collect donor element (face) information. ---*/ - Buffer_Send_Coord = new su2double [MaxLocalVertex_Donor*nDim]; - Buffer_Send_Normal = new su2double [MaxLocalVertex_Donor*nDim]; - Buffer_Send_GlobalPoint = new long [MaxLocalVertex_Donor]; + vector allNumElem; + vector elemNumNodes; + su2matrix elemIdxNodes; - 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]; + auto nGlobalElemDonor = Collect_ElementInfo(markDonor, nDim, true, + allNumElem, elemNumNodes, elemIdxNodes); - /*-- Collect coordinates, global points, and normal vectors ---*/ - Collect_VertexInfo(true, markDonor,markTarget,nVertexDonor,nDim); + su2activematrix elemCentroid(nGlobalElemDonor, 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]; + SU2_OMP_PARALLEL + { + /*--- Compute element centroids to then find the closest one to a given target point. ---*/ - 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]; + SU2_OMP_FOR_STAT(roundUpDiv(nGlobalElemDonor,omp_get_max_threads())) + for (auto iElem = 0u; iElem < nGlobalElemDonor; ++iElem) { - nLocalFace_Donor=0; - nLocalFaceNodes_Donor=0; + const auto nNode = elemNumNodes[iElem]; - /*--- Collect Face info ---*/ + for (auto iDim = 0u; iDim < nDim; ++iDim) + elemCentroid(iElem, iDim) = 0.0; - 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 - } + /*--- Compute transfer coefficients for each target point. ---*/ + su2double maxDist = 0.0; + unsigned long errorCount = 0, totalCount = 0; + + SU2_OMP_FOR_DYN(roundUpDiv(nVertexTarget,2*omp_get_max_threads())) + for (auto iVertex = 0u; iVertex < nVertexTarget; ++iVertex) { + + auto target_vertex = target_geometry->vertex[markTarget][iVertex]; + const auto iPoint = target_vertex->GetNode(); + + if (!target_geometry->node[iPoint]->GetDomain()) continue; + totalCount += 1; + + /*--- Coordinates and normal of the target point. ---*/ + const su2double* coord_i = target_geometry->node[iPoint]->GetCoord(); + const su2double* normal_i = target_vertex->GetNormal(); + + /*--- Find closest element (the naive way). ---*/ + su2double minDist = 1e20; + auto iElemDonor = 0u; + for (auto iElem = 0u; iElem < nGlobalElemDonor; ++iElem) { + su2double dist = SquaredDistance(nDim, coord_i, elemCentroid[iElem]); + if (dist < minDist) { + minDist = dist; + iElemDonor = iElem; } } - 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 - } + + /*--- Fetch donor element info. ---*/ + int procList[4] = {0}; + long nodeList[4] = {0}; + su2double coords[4][3] = {{0.0}}; + + const auto nNode = elemNumNodes[iElemDonor]; + + for (auto iNode = 0u; iNode < nNode; ++iNode) { + const auto iVertex = elemIdxNodes(iElemDonor, iNode); + procList[iNode] = donorProc[iVertex]; + nodeList[iNode] = donorPoint[iVertex]; + for (auto iDim = 0u; iDim < nDim; ++iDim) + coords[iNode][iDim] = donorCoord(iVertex,iDim); } - } + + const su2double* coord_j = elemCentroid[iElemDonor]; + su2double normal_j[3] = {0.0}; + + switch (nNode) { + case 2: LineNormal(coords, normal_j); break; + case 3: TriangleNormal(coords, normal_j); break; + case 4: QuadrilateralNormal(coords, normal_j); break; } - } - //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; iDonor= thetaMin) { + su2double dist_ij[3] = {0.0}; + Distance(nDim, coord_j, coord_i, dist_ij); + proj = DotProduct(nDim, dist_ij, normal_j) / proj; + for (auto iDim = 0u; iDim < nDim; ++iDim) + projCoord[iDim] = coord_i[iDim] + proj*dist_ij[iDim]; + + maxDist = max(maxDist, proj*Norm(nDim, dist_ij)); } + else { + /*--- Target and donor are too out of alignement, as fallback + * use the element centroid as the projected coordinate. ---*/ + for (auto iDim = 0u; iDim < nDim; ++iDim) + projCoord[iDim] = coord_j[iDim]; - /*--- Set the appropriate amount of memory and fill ---*/ - target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(nNodes); + errorCount += 1; + maxDist = max(maxDist, sqrt(minDist)); + } - 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]); + /*--- Compute and set interpolation coefficients. ---*/ + + su2double isoparams[4] = {0.0}; + switch (nNode) { + case 2: errorCount += LineIsoparameters(coords, projCoord, isoparams); break; + case 3: errorCount += TriangleIsoparameters(coords, projCoord, isoparams); break; + case 4: errorCount += QuadrilateralIsoparameters(coords, projCoord, isoparams); break; } + target_vertex->Allocate_DonorInfo(nNode); + + for (auto iDonor = 0u; iDonor < nNode; ++iDonor) { + target_vertex->SetDonorCoeff(iDonor, isoparams[iDonor]); + target_vertex->SetInterpDonorPoint(iDonor, nodeList[iDonor]); + target_vertex->SetInterpDonorProcessor(iDonor, procList[iDonor]); + } + + } + SU2_OMP_CRITICAL + { + MaxDistance = max(MaxDistance,maxDist); + ErrorCounter += errorCount; + nGlobalVertexTarget += totalCount; } + } // end SU2_OMP_PARALLEL - delete[] Buffer_Send_Coord; - delete[] Buffer_Send_Normal; - delete[] Buffer_Send_GlobalPoint; + } // end nMarkerInt loop - delete[] Buffer_Receive_Coord; - delete[] Buffer_Receive_Normal; - delete[] Buffer_Receive_GlobalPoint; + /*--- Final reduction of statistics. ---*/ + su2double tmp = MaxDistance; + unsigned long tmp1 = ErrorCounter, tmp2 = nGlobalVertexTarget; + SU2_MPI::Allreduce(&tmp, &MaxDistance, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); + SU2_MPI::Allreduce(&tmp1, &ErrorCounter, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); + SU2_MPI::Allreduce(&tmp2, &nGlobalVertexTarget, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); - delete[] Buffer_Send_FaceIndex; - delete[] Buffer_Send_FaceNodes; - delete[] Buffer_Send_FaceProc; + ErrorRate = 100*su2double(ErrorCounter) / nGlobalVertexTarget; - delete[] Buffer_Receive_FaceIndex; - delete[] Buffer_Receive_FaceNodes; - delete[] Buffer_Receive_FaceProc; +} + +int CIsoparametric::LineIsoparameters(const su2double X[][3], const su2double *xj, su2double *isoparams) { + + su2double l01 = Distance(2, X[0], X[1]); + su2double l0j = Distance(2, X[0], xj); + su2double lj1 = Distance(2, xj, X[1]); + + /*--- Detect out of bounds point. ---*/ + + const int outOfBounds = (l0j+lj1) > l01; + + if (outOfBounds) { + l0j = (l0j > lj1)? l01 : 0.0; // which ever is closest becomes the donor + lj1 = l01 - l0j; } - delete[] Buffer_Receive_nVertex_Donor; - delete[] Buffer_Receive_nFace_Donor; - delete[] Buffer_Receive_nFaceNodes_Donor; + isoparams[0] = lj1 / l01; + isoparams[1] = 1.0 - isoparams[0]; - delete [] Coord; - delete [] Normal; + return outOfBounds; +} + +int CIsoparametric::TriangleIsoparameters(const su2double X[][3], const su2double *xj, su2double *isoparams) { + + /*--- The isoparameters are the solution to the determined system X^T * isoparams = xj. + * This is consistent with the shape functions of the linear triangular element. ---*/ + + su2double A[3][3] = {{0.0}}; // = X^T + + for (int i = 0; i < 3; ++i) { + isoparams[i] = xj[i]; // use isoparams as rhs + for (int j = 0; j < 3; ++j) + A[i][j] = X[j][i]; + } - delete [] projected_point; + /*--- Solve normal system by in-place Gaussian elimination without pivoting. ---*/ + + /*--- Transform system in Upper Matrix. ---*/ + for (int i = 1; i < 3; ++i) { + for (int j = 0; j < i; ++j) { + su2double w = A[i][j] / A[j][j]; + for (int k = j; k < 3; ++k) + A[i][k] -= w * A[j][k]; + isoparams[i] -= w * isoparams[j]; + } + } + + /*--- Backwards substitution. ---*/ + for (int i = 2; i >= 0; --i) { + for (int j = i+1; j < 3; ++j) + isoparams[i] -= A[i][j] * isoparams[j]; + isoparams[i] /= A[i][i]; + } + + /*--- Detect out of bounds point. ---*/ + const int outOfBounds = (isoparams[0] < 0.0) || (isoparams[1] < 0.0) || (isoparams[2] < 0.0); + + /*--- Simple mitigation. ---*/ + if (outOfBounds) + isoparams[0] = isoparams[1] = isoparams[2] = 1.0/3.0; + + return outOfBounds; } -void CIsoparametric::Isoparameters(unsigned short nDim, unsigned short nDonor, - const su2double *X, const su2double *xj, su2double *isoparams) const { +int CIsoparametric::QuadrilateralIsoparameters(const su2double X[][3], const su2double *xj, su2double *isoparams) { - short iDonor,iDim,k; // indices - su2double tmp, tmp2; + /*--- The isoparameters are the shape functions (Ni) evaluated at xj, for that we need + * the corresponding Xi and Eta, which are obtained by solving the overdetermined + * nonlinear system xj - X^T * Ni(Xi,Eta) = 0 via the Gauss-Newton method. ---*/ - 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]; + constexpr int NITER = 10; + const su2double tol = 1e-12; - bool *test = new bool[nDim+1]; - bool *testi = new bool[nDim+1]; + su2double Xi = 0.0, Eta = 0.0, eps; - su2double eps = 1E-10; + /*--- Finding Xi and Eta is a "third order" effect that we do not + * differentiate (also because we need to iterate). ---*/ + const bool tapeActive = AD::TapeActive(); + AD::StopRecording(); - short n = nDim+1; + for (int iter = 0; iter < NITER; ++iter) { - 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 (tapeActive) AD::StartRecording(); + + int outOfBounds = 0; + if (eps > 0.01) { + /*--- Iteration did not converge. ---*/ + Xi = Eta = 0.0; + outOfBounds = 1; } - 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; + else { + /*--- Check bounds. ---*/ + outOfBounds = (fabs(Xi) > 1.0) || (fabs(Eta) > 1.0); + + /*--- Mitigate by clamping coordinates. ---*/ + if (outOfBounds) { + Xi = max(-1.0, min(Xi, 1.0)); + Eta = max(-1.0, min(Eta, 1.0)); } } - delete [] x; - delete [] x_tmp; - delete [] Q; - delete [] R; - delete [] A; - delete [] A2; - delete [] x2; - - delete [] test; - delete [] testi; + /*--- Evaluate isoparameters. ---*/ + CQUAD4::ShapeFunctions(Xi, Eta, isoparams); + return outOfBounds; } diff --git a/Common/src/interface_interpolation/CNearestNeighbor.cpp b/Common/src/interface_interpolation/CNearestNeighbor.cpp index b94457b16c62..580dcb7e460b 100644 --- a/Common/src/interface_interpolation/CNearestNeighbor.cpp +++ b/Common/src/interface_interpolation/CNearestNeighbor.cpp @@ -28,6 +28,7 @@ #include "../../include/interface_interpolation/CNearestNeighbor.hpp" #include "../../include/CConfig.hpp" #include "../../include/geometry/CGeometry.hpp" +#include "../../include/toolboxes/geometry_toolbox.hpp" /*! \brief Helper struct to (partially) sort neighbours according to distance while @@ -87,11 +88,11 @@ void CNearestNeighbor::Set_TransferCoeff(const CConfig* const* config) { 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 ); + 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); + /*--- Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor. ---*/ + Determine_ArraySize(markDonor, markTarget, nVertexDonor, nDim); const auto nPossibleDonor = accumulate(Buffer_Receive_nVertex_Donor, Buffer_Receive_nVertex_Donor+nProcessor, 0ul); @@ -101,8 +102,8 @@ void CNearestNeighbor::Set_TransferCoeff(const CConfig* const* config) { 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 ); + /*--- Collect coordinates and global point indices. ---*/ + Collect_VertexInfo(markDonor, markTarget, nVertexDonor, nDim); /*--- Find the closest donor points to each target. ---*/ SU2_OMP_PARALLEL @@ -132,7 +133,7 @@ void CNearestNeighbor::Set_TransferCoeff(const CConfig* const* config) { 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); + const auto dist2 = GeometryToolbox::SquaredDistance(nDim, Coord_i, Coord_j); donorInfo[iDonor++] = DonorInfo(dist2, pGlobalPoint, iProcessor); } diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp index 1ab414476f9a..4eb5d574be45 100644 --- a/Common/src/interface_interpolation/CRadialBasisFunction.cpp +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -29,6 +29,7 @@ #include "../../include/CConfig.hpp" #include "../../include/geometry/CGeometry.hpp" #include "../../include/toolboxes/CSymmetricMatrix.hpp" +#include "../../include/toolboxes/geometry_toolbox.hpp" #if defined(HAVE_MKL) #include "mkl.h" @@ -126,13 +127,13 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { 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; + if (!CheckInterfaceBoundary(markDonor,markTarget)) continue; unsigned long nVertexDonor = 0; - if(markDonor != -1) nVertexDonor = donor_geometry->GetnVertex(markDonor); + if (markDonor != -1) nVertexDonor = donor_geometry->GetnVertex(markDonor); /*--- Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor. ---*/ - Determine_ArraySize(false, markDonor, markTarget, nVertexDonor, nDim); + Determine_ArraySize(markDonor, markTarget, nVertexDonor, nDim); /*--- Compute total number of donor vertices. ---*/ const auto nGlobalVertexDonor = accumulate(Buffer_Receive_nVertex_Donor, @@ -144,7 +145,7 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { Buffer_Receive_Coord = new su2double [ nProcessor * MaxLocalVertex_Donor * nDim ]; Buffer_Receive_GlobalPoint = new long [ nProcessor * MaxLocalVertex_Donor ]; - Collect_VertexInfo(false, markDonor, markTarget, nVertexDonor, nDim); + Collect_VertexInfo(markDonor, markTarget, nVertexDonor, nDim); /*--- Compresses the gathered donor point information to simplify computations. ---*/ auto& donorCoord = donorCoordinates[iMarkerInt]; @@ -331,7 +332,7 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { /*--- 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 dist = GeometryToolbox::Distance(nDim, targetCoord[iVertexTarget+k], donorCoord[iVertexDonor]); auto rbf = Get_RadialBasisValue(kindRBF, paramRBF, dist); funcMat(k, 1+nPolynomial+iVertexDonor) = SU2_TYPE::GetValue(rbf); } @@ -449,7 +450,7 @@ void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool us 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]))); + GeometryToolbox::Distance(nDim, coords[iVertex], coords[jVertex]))); /*--- Invert M matrix (operation is in-place). ---*/ const bool kernelIsSPD = (type==WENDLAND_C2) || (type==GAUSSIAN) || (type==INV_MULTI_QUADRIC); diff --git a/Common/src/interface_interpolation/CSlidingMesh.cpp b/Common/src/interface_interpolation/CSlidingMesh.cpp index b4791cff96b7..a79b96dbd593 100644 --- a/Common/src/interface_interpolation/CSlidingMesh.cpp +++ b/Common/src/interface_interpolation/CSlidingMesh.cpp @@ -28,6 +28,7 @@ #include "../../include/interface_interpolation/CSlidingMesh.hpp" #include "../../include/CConfig.hpp" #include "../../include/geometry/CGeometry.hpp" +#include "../../include/toolboxes/geometry_toolbox.hpp" CSlidingMesh::CSlidingMesh(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, @@ -203,7 +204,7 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ]; - dist = PointsDistance(nDim, Coord_i, Coord_j); + dist = GeometryToolbox::Distance(nDim, Coord_i, Coord_j); if (dist < mindist) { mindist = dist; @@ -250,7 +251,7 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { for(iDim = 0; iDim < nDim; iDim++) Direction[iDim] /= dTMP; - length = PointsDistance(nDim, target_iMidEdge_point, target_jMidEdge_point); + length = GeometryToolbox::Distance(nDim, target_iMidEdge_point, target_jMidEdge_point); check = false; @@ -487,7 +488,7 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ]; - dist = PointsDistance(nDim, Coord_i, Coord_j); + dist = GeometryToolbox::Distance(nDim, Coord_i, Coord_j); if (dist < mindist) { mindist = dist; diff --git a/SU2_CFD/src/solvers/CFEASolver.cpp b/SU2_CFD/src/solvers/CFEASolver.cpp index e68759d4e69c..e95c1e4697ab 100644 --- a/SU2_CFD/src/solvers/CFEASolver.cpp +++ b/SU2_CFD/src/solvers/CFEASolver.cpp @@ -28,57 +28,13 @@ #include "../../include/solvers/CFEASolver.hpp" #include "../../include/variables/CFEABoundVariable.hpp" #include "../../../Common/include/toolboxes/printing_toolbox.hpp" +#include "../../../Common/include/toolboxes/geometry_toolbox.hpp" #include #include #include -/*! - * \brief Anonymous namespace with helper functions of the FEA solver. - */ -namespace { - - template - void CrossProduct(const T* a, const T* b, T* c) { - c[0] = a[1]*b[2] - a[2]*b[1]; - c[1] = a[2]*b[0] - a[0]*b[2]; - c[2] = a[0]*b[1] - a[1]*b[0]; - } - - template - void LineNormal(const T& coords, U* normal) { - normal[0] = coords[0][1] - coords[1][1]; - normal[1] = coords[1][0] - coords[0][0]; - } - - template - void TriangleNormal(const T& coords, U* normal) { - /*--- Cross product of two sides. ---*/ - U a[3], b[3]; - - for (int iDim = 0; iDim < 3; iDim++) { - a[iDim] = coords[1][iDim] - coords[0][iDim]; - b[iDim] = coords[2][iDim] - coords[0][iDim]; - } +using namespace GeometryToolbox; - CrossProduct(a, b, normal); - normal[0] *= 0.5; normal[1] *= 0.5; normal[2] *= 0.5; - } - - template - void QuadrilateralNormal(const T& coords, U* normal) { - /*--- Cross product of the two diagonals. ---*/ - U a[3], b[3]; - - for (int iDim = 0; iDim < 3; iDim++) { - a[iDim] = coords[2][iDim] - coords[0][iDim]; - b[iDim] = coords[3][iDim] - coords[1][iDim]; - } - - CrossProduct(a, b, normal); - normal[0] *= 0.5; normal[1] *= 0.5; normal[2] *= 0.5; - } - -} CFEASolver::CFEASolver(bool mesh_deform_mode) : CSolver(mesh_deform_mode) { From 8fc82a5b6f83928c2325d19a613bca363afd2d2a Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 2 Apr 2020 17:46:26 +0100 Subject: [PATCH 49/54] small fixes --- Common/include/geometry/dual_grid/CVertex.hpp | 5 +++ .../interface_interpolation/CInterpolator.hpp | 6 +-- .../CIsoparametric.hpp | 2 +- .../interface_interpolation/CInterpolator.cpp | 2 +- .../CIsoparametric.cpp | 40 ++++++++++++------- 5 files changed, 34 insertions(+), 21 deletions(-) diff --git a/Common/include/geometry/dual_grid/CVertex.hpp b/Common/include/geometry/dual_grid/CVertex.hpp index 41c2fc4da20d..a63f5a51be52 100644 --- a/Common/include/geometry/dual_grid/CVertex.hpp +++ b/Common/include/geometry/dual_grid/CVertex.hpp @@ -110,6 +110,11 @@ class CVertex : public CDualGrid { */ inline su2double *GetNormal(void) override { return Normal; } + /*! + * \brief Get the ith component of the normal. + */ + inline su2double GetNormal(unsigned short iDim) const { return Normal[iDim]; } + /*! * \brief Initialize normal vector. */ diff --git a/Common/include/interface_interpolation/CInterpolator.hpp b/Common/include/interface_interpolation/CInterpolator.hpp index de0754b94d0d..3d7a0b3f9f19 100644 --- a/Common/include/interface_interpolation/CInterpolator.hpp +++ b/Common/include/interface_interpolation/CInterpolator.hpp @@ -38,7 +38,7 @@ using namespace std; /*! * \class CInterpolator * \brief Main class for defining the interpolator, it requires - * a child class for each particular interpolation method + * a child class for each particular interpolation method. * \author H. Kline */ class CInterpolator { @@ -65,9 +65,7 @@ class CInterpolator { *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*/ + *Buffer_Receive_FaceNodes; /*!< \brief Buffer to receive indices pointing to the location of node information in other buffers, defining faces*/ long *Buffer_Send_GlobalPoint, /*!< \brief Buffer to send global point indices*/ *Buffer_Receive_GlobalPoint; /*!< \brief Buffer to receive global point indices*/ diff --git a/Common/include/interface_interpolation/CIsoparametric.hpp b/Common/include/interface_interpolation/CIsoparametric.hpp index 2990026e40d3..d55c98a68b81 100644 --- a/Common/include/interface_interpolation/CIsoparametric.hpp +++ b/Common/include/interface_interpolation/CIsoparametric.hpp @@ -1,7 +1,7 @@ /*! * \file CIsoparametric.hpp * \brief Isoparametric interpolation using FE shape functions. - * \author H. Kline, P. Gomes + * \author P. Gomes * \version 7.0.3 "Blackbird" * * SU2 Project Website: https://su2code.github.io diff --git a/Common/src/interface_interpolation/CInterpolator.cpp b/Common/src/interface_interpolation/CInterpolator.cpp index d8e3f931350d..5386304e94a7 100644 --- a/Common/src/interface_interpolation/CInterpolator.cpp +++ b/Common/src/interface_interpolation/CInterpolator.cpp @@ -133,7 +133,7 @@ unsigned long CInterpolator::Collect_ElementInfo(int markDonor, unsigned short n const auto nNode = donor_geometry->bound[markDonor][iElem]->GetnNodes(); bufferSendNum[iElem] = nNode; - assert(nNode < maxElemNodes && "Donor element has too many nodes."); + assert(nNode <= maxElemNodes && "Donor element has too many nodes."); for (auto iNode = 0u; iNode < nNode; ++iNode) { auto iPoint = donor_geometry->bound[markDonor][iElem]->GetNode(iNode); diff --git a/Common/src/interface_interpolation/CIsoparametric.cpp b/Common/src/interface_interpolation/CIsoparametric.cpp index 3312a39f878d..c41c92c2c463 100644 --- a/Common/src/interface_interpolation/CIsoparametric.cpp +++ b/Common/src/interface_interpolation/CIsoparametric.cpp @@ -1,7 +1,7 @@ /*! * \file CIsoparametric.cpp * \brief Implementation isoparametric interpolation (using FE shape functions). - * \author H. Kline, P. Gomes + * \author P. Gomes * \version 7.0.3 "Blackbird" * * SU2 Project Website: https://su2code.github.io @@ -48,8 +48,8 @@ void CIsoparametric::PrintStatistics(void) const { void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { - /*--- Angle between target and donor below which we trigger fallback measures. ---*/ - const su2double thetaMin = 80.0/180.0*PI_NUMBER; + /*--- Angle between target and donor above which we trigger fallback measures. ---*/ + const su2double thetaMax = 20.0/180.0*PI_NUMBER; const int nProcessor = size; const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2; @@ -174,9 +174,12 @@ void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { if (!target_geometry->node[iPoint]->GetDomain()) continue; totalCount += 1; - /*--- Coordinates and normal of the target point. ---*/ + /*--- Coordinates and unit normal of the target point. ---*/ const su2double* coord_i = target_geometry->node[iPoint]->GetCoord(); - const su2double* normal_i = target_vertex->GetNormal(); + const su2double area_i = Norm(nDim, target_vertex->GetNormal()); + su2double unitNormal_i[3] = {0.0}; + for (auto iDim = 0u; iDim < nDim; ++iDim) + unitNormal_i[iDim] = target_vertex->GetNormal(iDim) / area_i; /*--- Find closest element (the naive way). ---*/ su2double minDist = 1e20; @@ -215,19 +218,19 @@ void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { /*--- Project the target onto the donor plane, as grids may not be exactly conformal. * For quadrilaterals this is approximate as the element may be warped. ---*/ - su2double proj = DotProduct(nDim, normal_i, normal_j); - su2double theta = acos(fabs(proj) / (Norm(nDim, normal_i)*Norm(nDim, normal_j))); + su2double proj = DotProduct(nDim, unitNormal_i, normal_j); + su2double theta = acos(fabs(proj) / Norm(nDim, normal_j)); su2double projCoord[3] = {0.0}; - if (theta >= thetaMin) { + if (theta < thetaMax) { su2double dist_ij[3] = {0.0}; Distance(nDim, coord_j, coord_i, dist_ij); proj = DotProduct(nDim, dist_ij, normal_j) / proj; for (auto iDim = 0u; iDim < nDim; ++iDim) - projCoord[iDim] = coord_i[iDim] + proj*dist_ij[iDim]; + projCoord[iDim] = coord_i[iDim] + proj*unitNormal_i[iDim]; - maxDist = max(maxDist, proj*Norm(nDim, dist_ij)); + maxDist = max(maxDist, fabs(proj)); } else { /*--- Target and donor are too out of alignement, as fallback @@ -280,13 +283,15 @@ void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { int CIsoparametric::LineIsoparameters(const su2double X[][3], const su2double *xj, su2double *isoparams) { + const su2double extrapTol = 1.01; + su2double l01 = Distance(2, X[0], X[1]); su2double l0j = Distance(2, X[0], xj); su2double lj1 = Distance(2, xj, X[1]); /*--- Detect out of bounds point. ---*/ - const int outOfBounds = (l0j+lj1) > l01; + const int outOfBounds = (l0j+lj1) > (extrapTol*l01); if (outOfBounds) { l0j = (l0j > lj1)? l01 : 0.0; // which ever is closest becomes the donor @@ -304,6 +309,8 @@ int CIsoparametric::TriangleIsoparameters(const su2double X[][3], const su2doubl /*--- The isoparameters are the solution to the determined system X^T * isoparams = xj. * This is consistent with the shape functions of the linear triangular element. ---*/ + const su2double extrapTol = -0.01; + su2double A[3][3] = {{0.0}}; // = X^T for (int i = 0; i < 3; ++i) { @@ -312,7 +319,7 @@ int CIsoparametric::TriangleIsoparameters(const su2double X[][3], const su2doubl A[i][j] = X[j][i]; } - /*--- Solve normal system by in-place Gaussian elimination without pivoting. ---*/ + /*--- Solve system by in-place Gaussian elimination without pivoting. ---*/ /*--- Transform system in Upper Matrix. ---*/ for (int i = 1; i < 3; ++i) { @@ -323,7 +330,6 @@ int CIsoparametric::TriangleIsoparameters(const su2double X[][3], const su2doubl isoparams[i] -= w * isoparams[j]; } } - /*--- Backwards substitution. ---*/ for (int i = 2; i >= 0; --i) { for (int j = i+1; j < 3; ++j) @@ -332,7 +338,9 @@ int CIsoparametric::TriangleIsoparameters(const su2double X[][3], const su2doubl } /*--- Detect out of bounds point. ---*/ - const int outOfBounds = (isoparams[0] < 0.0) || (isoparams[1] < 0.0) || (isoparams[2] < 0.0); + const int outOfBounds = (isoparams[0] < extrapTol) || + (isoparams[1] < extrapTol) || + (isoparams[2] < extrapTol); /*--- Simple mitigation. ---*/ if (outOfBounds) @@ -347,6 +355,8 @@ int CIsoparametric::QuadrilateralIsoparameters(const su2double X[][3], const su2 * the corresponding Xi and Eta, which are obtained by solving the overdetermined * nonlinear system xj - X^T * Ni(Xi,Eta) = 0 via the Gauss-Newton method. ---*/ + const su2double extrapTol = 1.01; + constexpr int NITER = 10; const su2double tol = 1e-12; @@ -410,7 +420,7 @@ int CIsoparametric::QuadrilateralIsoparameters(const su2double X[][3], const su2 } else { /*--- Check bounds. ---*/ - outOfBounds = (fabs(Xi) > 1.0) || (fabs(Eta) > 1.0); + outOfBounds = (fabs(Xi) > extrapTol) || (fabs(Eta) > extrapTol); /*--- Mitigate by clamping coordinates. ---*/ if (outOfBounds) { From 66cef8928cdc29ca2cd982d31cb83fbf29e4c839 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 2 Apr 2020 19:01:14 +0100 Subject: [PATCH 50/54] tuning parameters --- .../src/interface_interpolation/CIsoparametric.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Common/src/interface_interpolation/CIsoparametric.cpp b/Common/src/interface_interpolation/CIsoparametric.cpp index c41c92c2c463..880d01de0a39 100644 --- a/Common/src/interface_interpolation/CIsoparametric.cpp +++ b/Common/src/interface_interpolation/CIsoparametric.cpp @@ -49,7 +49,7 @@ void CIsoparametric::PrintStatistics(void) const { void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { /*--- Angle between target and donor above which we trigger fallback measures. ---*/ - const su2double thetaMax = 20.0/180.0*PI_NUMBER; + const su2double thetaMax = 30.0/180.0*PI_NUMBER; const int nProcessor = size; const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2; @@ -262,7 +262,7 @@ void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { } SU2_OMP_CRITICAL { - MaxDistance = max(MaxDistance,maxDist); + MaxDistance = max(MaxDistance, maxDist); ErrorCounter += errorCount; nGlobalVertexTarget += totalCount; } @@ -357,8 +357,8 @@ int CIsoparametric::QuadrilateralIsoparameters(const su2double X[][3], const su2 const su2double extrapTol = 1.01; - constexpr int NITER = 10; - const su2double tol = 1e-12; + constexpr int NITER = 200; + const su2double tol = 1e-5, relax = 0.8; su2double Xi = 0.0, Eta = 0.0, eps; @@ -402,8 +402,8 @@ int CIsoparametric::QuadrilateralIsoparameters(const su2double X[][3], const su2 su2double detA = 1.0 / (A[0][0]*A[1][1] - A[0][1]*A[1][0]); su2double dXi = (b[0]*A[1][1] - b[1]*A[0][1]) * detA; su2double dEta = (A[0][0]*b[1] - A[1][0]*b[0]) * detA; - Xi -= dXi; - Eta -= dEta; + Xi -= relax * dXi; + Eta -= relax * dEta; eps = fabs(dXi)+fabs(dEta); if (eps < tol) break; @@ -414,7 +414,7 @@ int CIsoparametric::QuadrilateralIsoparameters(const su2double X[][3], const su2 int outOfBounds = 0; if (eps > 0.01) { - /*--- Iteration did not converge. ---*/ + /*--- Iteration diverged. ---*/ Xi = Eta = 0.0; outOfBounds = 1; } From 209c7f30f657cd784b9e57f3d111fbe6a8efd701 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 2 Apr 2020 19:51:49 +0100 Subject: [PATCH 51/54] fix uninit variable --- Common/src/interface_interpolation/CIsoparametric.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/src/interface_interpolation/CIsoparametric.cpp b/Common/src/interface_interpolation/CIsoparametric.cpp index 880d01de0a39..d81892d53611 100644 --- a/Common/src/interface_interpolation/CIsoparametric.cpp +++ b/Common/src/interface_interpolation/CIsoparametric.cpp @@ -381,7 +381,7 @@ int CIsoparametric::QuadrilateralIsoparameters(const su2double X[][3], const su2 su2double dNi[4][2] = {{0.0}}; CQUAD4::ShapeFunctionJacobian(Xi, Eta, dNi); - su2double jac[3][2]; + su2double jac[3][2] = {{0.0}}; for (int i = 0; i < 3; ++i) for (int j = 0; j < 2; ++j) for (int k = 0; k < 4; ++k) From 74ffc2e21484ee485cdaced59f2623bdbdc19e6e Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 2 Apr 2020 20:40:57 +0100 Subject: [PATCH 52/54] small tweaks --- .../CIsoparametric.cpp | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/Common/src/interface_interpolation/CIsoparametric.cpp b/Common/src/interface_interpolation/CIsoparametric.cpp index d81892d53611..bb46dbe2991f 100644 --- a/Common/src/interface_interpolation/CIsoparametric.cpp +++ b/Common/src/interface_interpolation/CIsoparametric.cpp @@ -43,7 +43,7 @@ CIsoparametric::CIsoparametric(CGeometry ****geometry_container, const CConfig* void CIsoparametric::PrintStatistics(void) const { if (rank != MASTER_NODE) return; cout << " Maximum distance to closest donor element: " << MaxDistance << ".\n" - << " Interpolation mitigated on " << ErrorCounter << " (" << ErrorRate << "%) target vertices." << endl; + << " Interpolation mitigated for " << ErrorCounter << " (" << ErrorRate << "%) target vertices." << endl; } void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { @@ -283,7 +283,7 @@ void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { int CIsoparametric::LineIsoparameters(const su2double X[][3], const su2double *xj, su2double *isoparams) { - const su2double extrapTol = 1.01; + const su2double extrapTol = 1.05; su2double l01 = Distance(2, X[0], X[1]); su2double l0j = Distance(2, X[0], xj); @@ -293,12 +293,7 @@ int CIsoparametric::LineIsoparameters(const su2double X[][3], const su2double *x const int outOfBounds = (l0j+lj1) > (extrapTol*l01); - if (outOfBounds) { - l0j = (l0j > lj1)? l01 : 0.0; // which ever is closest becomes the donor - lj1 = l01 - l0j; - } - - isoparams[0] = lj1 / l01; + isoparams[0] = max(1.0-extrapTol, min(lj1/l01, extrapTol)); isoparams[1] = 1.0 - isoparams[0]; return outOfBounds; @@ -309,7 +304,7 @@ int CIsoparametric::TriangleIsoparameters(const su2double X[][3], const su2doubl /*--- The isoparameters are the solution to the determined system X^T * isoparams = xj. * This is consistent with the shape functions of the linear triangular element. ---*/ - const su2double extrapTol = -0.01; + const su2double extrapTol = -0.05; su2double A[3][3] = {{0.0}}; // = X^T @@ -342,9 +337,18 @@ int CIsoparametric::TriangleIsoparameters(const su2double X[][3], const su2doubl (isoparams[1] < extrapTol) || (isoparams[2] < extrapTol); - /*--- Simple mitigation. ---*/ - if (outOfBounds) - isoparams[0] = isoparams[1] = isoparams[2] = 1.0/3.0; + /*--- Mitigation. ---*/ + if (outOfBounds) { + /*--- Clamp. ---*/ + su2double sum = 0.0; + for (int i = 0; i < 3; ++i) { + isoparams[i] = max(isoparams[i], extrapTol); + sum += isoparams[i]; + } + /*--- Enforce unit sum. ---*/ + for (int i = 0; i < 3; ++i) + isoparams[i] /= sum; + } return outOfBounds; } @@ -353,12 +357,12 @@ int CIsoparametric::QuadrilateralIsoparameters(const su2double X[][3], const su2 /*--- The isoparameters are the shape functions (Ni) evaluated at xj, for that we need * the corresponding Xi and Eta, which are obtained by solving the overdetermined - * nonlinear system xj - X^T * Ni(Xi,Eta) = 0 via the Gauss-Newton method. ---*/ + * nonlinear system xj - X^T * Ni(Xi,Eta) = 0 via the modified Marquardt method. ---*/ - const su2double extrapTol = 1.01; + const su2double extrapTol = 1.05; - constexpr int NITER = 200; - const su2double tol = 1e-5, relax = 0.8; + constexpr int NITER = 20; + const su2double tol = 1e-10, lambda = 0.05; su2double Xi = 0.0, Eta = 0.0, eps; @@ -393,17 +397,19 @@ int CIsoparametric::QuadrilateralIsoparameters(const su2double X[][3], const su2 for (int j = i; j < 2; ++j) for (int k = 0; k < 3; ++k) A[i][j] += jac[k][i] * jac[k][j]; - A[1][0] = A[0][1]; + + A[i][i] *= (1.0+lambda); for (int k = 0; k < 3; ++k) b[i] += jac[k][i] * r[k]; } + A[1][0] = A[0][1]; su2double detA = 1.0 / (A[0][0]*A[1][1] - A[0][1]*A[1][0]); su2double dXi = (b[0]*A[1][1] - b[1]*A[0][1]) * detA; su2double dEta = (A[0][0]*b[1] - A[1][0]*b[0]) * detA; - Xi -= relax * dXi; - Eta -= relax * dEta; + Xi -= dXi; + Eta -= dEta; eps = fabs(dXi)+fabs(dEta); if (eps < tol) break; @@ -414,7 +420,7 @@ int CIsoparametric::QuadrilateralIsoparameters(const su2double X[][3], const su2 int outOfBounds = 0; if (eps > 0.01) { - /*--- Iteration diverged. ---*/ + /*--- Iteration diverged, hard fallback. ---*/ Xi = Eta = 0.0; outOfBounds = 1; } @@ -424,8 +430,8 @@ int CIsoparametric::QuadrilateralIsoparameters(const su2double X[][3], const su2 /*--- Mitigate by clamping coordinates. ---*/ if (outOfBounds) { - Xi = max(-1.0, min(Xi, 1.0)); - Eta = max(-1.0, min(Eta, 1.0)); + Xi = max(-extrapTol, min(Xi, extrapTol)); + Eta = max(-extrapTol, min(Eta, extrapTol)); } } From abf5b0f4a55390fddf744754a638de52cc1ced80 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Fri, 3 Apr 2020 15:50:04 +0100 Subject: [PATCH 53/54] robustness improvements --- .../CIsoparametric.hpp | 14 +- Common/include/toolboxes/geometry_toolbox.hpp | 25 +++ .../CIsoparametric.cpp | 171 +++++++++--------- 3 files changed, 122 insertions(+), 88 deletions(-) diff --git a/Common/include/interface_interpolation/CIsoparametric.hpp b/Common/include/interface_interpolation/CIsoparametric.hpp index d55c98a68b81..600414b77c11 100644 --- a/Common/include/interface_interpolation/CIsoparametric.hpp +++ b/Common/include/interface_interpolation/CIsoparametric.hpp @@ -33,6 +33,8 @@ */ class CIsoparametric final : public CInterpolator { private: + static constexpr auto NUM_CANDIDATE_DONORS = 8ul; /*!< \brief Test this many nearby donor elements for "best fit". */ + /*--- Statistics. ---*/ su2double MaxDistance = 0.0, ErrorRate = 0.0; unsigned long ErrorCounter = 0; @@ -62,27 +64,27 @@ class CIsoparametric final : public CInterpolator { /*! * \brief Compute the isoparametric interpolation coefficients for a 2D line element. * \param[in] X - Coordinate matrix defining the line. - * \param[in] xj - Coordinates of the target point projected onto the plane of the element. + * \param[in] xj - Coordinates of the target point. * \param[out] isoparams - Isoparametric coefficients. - * \return 0 on success, 1 if xj is outside element bounds. + * \return 0 on success, 1 if xj is too far outside element bounds. */ static int LineIsoparameters(const su2double X[][3], const su2double *xj, su2double* isoparams); /*! * \brief Compute the isoparametric interpolation coefficients for a 3D triangle element. * \param[in] X - Coordinate matrix defining the triangle. - * \param[in] xj - Coordinates of the target point projected onto the plane of the element. + * \param[in] xj - Coordinates of the target point. * \param[out] isoparams - Isoparametric coefficients. - * \return 0 on success, 1 if xj is outside element bounds. + * \return 0 on success, 1 if xj is too far outside element bounds. */ static int TriangleIsoparameters(const su2double X[][3], const su2double *xj, su2double* isoparams); /*! * \brief Compute the isoparametric interpolation coefficients for a 3D quadrilateral element. * \param[in] X - Coordinate matrix defining the quadrilateral. - * \param[in] xj - Coordinates of the target point projected onto the plane of the element. + * \param[in] xj - Coordinates of the target point. * \param[out] isoparams - Isoparametric coefficients. - * \return 0 on success, 1 if xj is outside element bounds. + * \return 0 on success, 1 if xj is too far outside element bounds. */ static int QuadrilateralIsoparameters(const su2double X[][3], const su2double *xj, su2double* isoparams); diff --git a/Common/include/toolboxes/geometry_toolbox.hpp b/Common/include/toolboxes/geometry_toolbox.hpp index 7891ae89f1b4..76e9339e72d8 100644 --- a/Common/include/toolboxes/geometry_toolbox.hpp +++ b/Common/include/toolboxes/geometry_toolbox.hpp @@ -76,6 +76,31 @@ inline void CrossProduct(const T* a, const T* b, T* c) { c[2] = a[0]*b[1] - a[1]*b[0]; } +/*! + * \brief Compute the coordinate (c) where the line defined by coordinate l0 and + * direction d intersects the plane defined by point p0 and normal n. + * \return The intersection distance. + */ +template +inline T LinePlaneIntersection(const T* l0, const T* d, const T* p0, const T* n, T* c) { + T dist[nDim] = {0.0}; + Distance(nDim, p0, l0, dist); + T alpha = DotProduct(nDim, dist, n) / DotProduct(nDim, d, n); + for (int iDim = 0; iDim < nDim; ++iDim) + c[iDim] = l0[iDim] + alpha * d[iDim]; + return fabs(alpha) * Norm(nDim,d); +} + +/*! + * \brief Compute the coordinate (c) where point p1 intersects the plane defined + * by point p0 and normal n if projected perpendicular to it. + * \return The normal distance. + */ +template +inline T PointPlaneProjection(const T* p1, const T* p0, const T* n, T* c) { + return LinePlaneIntersection(p1, n, p0, n, c); +} + /*! \brief Set U as the normal to a 2D line defined by coords[iPoint][iDim]. */ template inline void LineNormal(const T& coords, U* normal) { diff --git a/Common/src/interface_interpolation/CIsoparametric.cpp b/Common/src/interface_interpolation/CIsoparametric.cpp index bb46dbe2991f..9745b5b9d70b 100644 --- a/Common/src/interface_interpolation/CIsoparametric.cpp +++ b/Common/src/interface_interpolation/CIsoparametric.cpp @@ -34,6 +34,19 @@ using namespace GeometryToolbox; +/*! \brief Helper struct to store information about candidate donor elements. */ +struct DonorInfo { + su2double isoparams[4] = {0.0}; /*!< \brief Interpolation coefficients. */ + su2double distance = 0.0; /*!< \brief Distance from target to final mapped point on donor plane. */ + unsigned iElem = 0; /*!< \brief Identification of the element. */ + int error = 2; /*!< \brief If the mapped point is "outside" of the donor. */ + + bool operator< (const DonorInfo& other) const { + /*--- Best donor is one for which the mapped point is within bounds and closest to target. ---*/ + return (error == other.error)? (distance < other.distance) : (error < other.error); + } +}; + CIsoparametric::CIsoparametric(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { @@ -48,9 +61,6 @@ void CIsoparametric::PrintStatistics(void) const { void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { - /*--- Angle between target and donor above which we trigger fallback measures. ---*/ - const su2double thetaMax = 30.0/180.0*PI_NUMBER; - const int nProcessor = size; const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2; const auto nDim = donor_geometry->GetnDim(); @@ -132,8 +142,8 @@ void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { vector elemNumNodes; su2matrix elemIdxNodes; - auto nGlobalElemDonor = Collect_ElementInfo(markDonor, nDim, true, - allNumElem, elemNumNodes, elemIdxNodes); + const auto nGlobalElemDonor = Collect_ElementInfo(markDonor, nDim, true, + allNumElem, elemNumNodes, elemIdxNodes); su2activematrix elemCentroid(nGlobalElemDonor, nDim); @@ -164,6 +174,7 @@ void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { /*--- Compute transfer coefficients for each target point. ---*/ su2double maxDist = 0.0; unsigned long errorCount = 0, totalCount = 0; + vector nearElems(nGlobalElemDonor); SU2_OMP_FOR_DYN(roundUpDiv(nVertexTarget,2*omp_get_max_threads())) for (auto iVertex = 0u; iVertex < nVertexTarget; ++iVertex) { @@ -174,89 +185,74 @@ void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { if (!target_geometry->node[iPoint]->GetDomain()) continue; totalCount += 1; - /*--- Coordinates and unit normal of the target point. ---*/ + /*--- Coordinates of the target point. ---*/ const su2double* coord_i = target_geometry->node[iPoint]->GetCoord(); - const su2double area_i = Norm(nDim, target_vertex->GetNormal()); - su2double unitNormal_i[3] = {0.0}; - for (auto iDim = 0u; iDim < nDim; ++iDim) - unitNormal_i[iDim] = target_vertex->GetNormal(iDim) / area_i; - - /*--- Find closest element (the naive way). ---*/ - su2double minDist = 1e20; - auto iElemDonor = 0u; - for (auto iElem = 0u; iElem < nGlobalElemDonor; ++iElem) { - su2double dist = SquaredDistance(nDim, coord_i, elemCentroid[iElem]); - if (dist < minDist) { - minDist = dist; - iElemDonor = iElem; - } - } - /*--- Fetch donor element info. ---*/ - int procList[4] = {0}; - long nodeList[4] = {0}; - su2double coords[4][3] = {{0.0}}; - - const auto nNode = elemNumNodes[iElemDonor]; + /*--- Find "n" closest candidate donor elements (the naive way). ---*/ + iota(nearElems.begin(), nearElems.end(), 0); + auto last = nearElems.begin() + min(NUM_CANDIDATE_DONORS, nGlobalElemDonor); + partial_sort(nearElems.begin(), last, nearElems.end(), + [&](unsigned iElem, unsigned jElem) { + return SquaredDistance(nDim, coord_i, elemCentroid[iElem]) < + SquaredDistance(nDim, coord_i, elemCentroid[jElem]); + } + ); - for (auto iNode = 0u; iNode < nNode; ++iNode) { - const auto iVertex = elemIdxNodes(iElemDonor, iNode); - procList[iNode] = donorProc[iVertex]; - nodeList[iNode] = donorPoint[iVertex]; - for (auto iDim = 0u; iDim < nDim; ++iDim) - coords[iNode][iDim] = donorCoord(iVertex,iDim); - } + /*--- Evaluate interpolation for the candidates. ---*/ + array candidateElems; - const su2double* coord_j = elemCentroid[iElemDonor]; - su2double normal_j[3] = {0.0}; + for (auto i = 0u; i < min(NUM_CANDIDATE_DONORS, nGlobalElemDonor); ++i) { + const auto iElem = nearElems[i]; - switch (nNode) { - case 2: LineNormal(coords, normal_j); break; - case 3: TriangleNormal(coords, normal_j); break; - case 4: QuadrilateralNormal(coords, normal_j); break; - } + /*--- Fetch element info. ---*/ + auto& candidate = candidateElems[i]; + candidate.iElem = iElem; + const auto nNode = elemNumNodes[iElem]; + su2double coords[4][3] = {{0.0}}; - /*--- Project the target onto the donor plane, as grids may not be exactly conformal. - * For quadrilaterals this is approximate as the element may be warped. ---*/ - su2double proj = DotProduct(nDim, unitNormal_i, normal_j); - su2double theta = acos(fabs(proj) / Norm(nDim, normal_j)); + for (auto iNode = 0u; iNode < nNode; ++iNode) { + const auto iVertex = elemIdxNodes(iElem, iNode); + for (auto iDim = 0u; iDim < nDim; ++iDim) + coords[iNode][iDim] = donorCoord(iVertex,iDim); + } - su2double projCoord[3] = {0.0}; + /*--- Compute the interpolation coefficients. ---*/ + switch (nNode) { + case 2: candidate.error = LineIsoparameters(coords, coord_i, candidate.isoparams); break; + case 3: candidate.error = TriangleIsoparameters(coords, coord_i, candidate.isoparams); break; + case 4: candidate.error = QuadrilateralIsoparameters(coords, coord_i, candidate.isoparams); break; + } - if (theta < thetaMax) { - su2double dist_ij[3] = {0.0}; - Distance(nDim, coord_j, coord_i, dist_ij); - proj = DotProduct(nDim, dist_ij, normal_j) / proj; + /*--- Evaluate distance from target to final mapped point. ---*/ + su2double finalCoord[3] = {0.0}; for (auto iDim = 0u; iDim < nDim; ++iDim) - projCoord[iDim] = coord_i[iDim] + proj*unitNormal_i[iDim]; + for (auto iNode = 0u; iNode < nNode; ++iNode) + finalCoord[iDim] += coords[iNode][iDim] * candidate.isoparams[iNode]; - maxDist = max(maxDist, fabs(proj)); + candidate.distance = Distance(nDim, coord_i, finalCoord); + if (candidate.distance != candidate.distance) // NaN check + candidate.error = 2; } - else { - /*--- Target and donor are too out of alignement, as fallback - * use the element centroid as the projected coordinate. ---*/ - for (auto iDim = 0u; iDim < nDim; ++iDim) - projCoord[iDim] = coord_j[iDim]; - errorCount += 1; - maxDist = max(maxDist, sqrt(minDist)); - } + /*--- Find best donor. ---*/ + partial_sort(candidateElems.begin(), candidateElems.begin()+1, candidateElems.end()); + const auto& donor = candidateElems[0]; - /*--- Compute and set interpolation coefficients. ---*/ + if (donor.error > 1) + SU2_MPI::Error("Isoparametric interpolation failed, NaN detected.", CURRENT_FUNCTION); - su2double isoparams[4] = {0.0}; - switch (nNode) { - case 2: errorCount += LineIsoparameters(coords, projCoord, isoparams); break; - case 3: errorCount += TriangleIsoparameters(coords, projCoord, isoparams); break; - case 4: errorCount += QuadrilateralIsoparameters(coords, projCoord, isoparams); break; - } + errorCount += donor.error; + maxDist = max(maxDist, donor.distance); + + const auto nNode = elemNumNodes[donor.iElem]; target_vertex->Allocate_DonorInfo(nNode); - for (auto iDonor = 0u; iDonor < nNode; ++iDonor) { - target_vertex->SetDonorCoeff(iDonor, isoparams[iDonor]); - target_vertex->SetInterpDonorPoint(iDonor, nodeList[iDonor]); - target_vertex->SetInterpDonorProcessor(iDonor, procList[iDonor]); + for (auto iNode = 0u; iNode < nNode; ++iNode) { + const auto iVertex = elemIdxNodes(donor.iElem, iNode); + target_vertex->SetDonorCoeff(iNode, donor.isoparams[iNode]); + target_vertex->SetInterpDonorPoint(iNode, donorPoint[iVertex]); + target_vertex->SetInterpDonorProcessor(iNode, donorProc[iVertex]); } } @@ -283,11 +279,18 @@ void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { int CIsoparametric::LineIsoparameters(const su2double X[][3], const su2double *xj, su2double *isoparams) { - const su2double extrapTol = 1.05; + const su2double extrapTol = 1.5; + + /*--- Project the target point onto the line. ---*/ + + su2double normal[2] = {0.0}; + LineNormal(X, normal); + su2double xprj[2] = {0.0}; + PointPlaneProjection(xj, X[0], normal, xprj); su2double l01 = Distance(2, X[0], X[1]); - su2double l0j = Distance(2, X[0], xj); - su2double lj1 = Distance(2, xj, X[1]); + su2double l0j = Distance(2, X[0], xprj); + su2double lj1 = Distance(2, xprj, X[1]); /*--- Detect out of bounds point. ---*/ @@ -304,15 +307,18 @@ int CIsoparametric::TriangleIsoparameters(const su2double X[][3], const su2doubl /*--- The isoparameters are the solution to the determined system X^T * isoparams = xj. * This is consistent with the shape functions of the linear triangular element. ---*/ - const su2double extrapTol = -0.05; + const su2double extrapTol = -0.5; - su2double A[3][3] = {{0.0}}; // = X^T + /*--- Project the target point onto the triangle. ---*/ - for (int i = 0; i < 3; ++i) { - isoparams[i] = xj[i]; // use isoparams as rhs + su2double normal[3] = {0.0}; + TriangleNormal(X, normal); + PointPlaneProjection(xj, X[0], normal, isoparams); // use isoparams as rhs + + su2double A[3][3]; // = X^T + for (int i = 0; i < 3; ++i) for (int j = 0; j < 3; ++j) A[i][j] = X[j][i]; - } /*--- Solve system by in-place Gaussian elimination without pivoting. ---*/ @@ -357,9 +363,10 @@ int CIsoparametric::QuadrilateralIsoparameters(const su2double X[][3], const su2 /*--- The isoparameters are the shape functions (Ni) evaluated at xj, for that we need * the corresponding Xi and Eta, which are obtained by solving the overdetermined - * nonlinear system xj - X^T * Ni(Xi,Eta) = 0 via the modified Marquardt method. ---*/ + * nonlinear system r = xj - X^T * Ni(Xi,Eta) = 0 via the modified Marquardt method. + * It is not necessary to project the point as minimizing ||r|| is equivalent. ---*/ - const su2double extrapTol = 1.05; + const su2double extrapTol = 3.0; constexpr int NITER = 20; const su2double tol = 1e-10, lambda = 0.05; @@ -435,7 +442,7 @@ int CIsoparametric::QuadrilateralIsoparameters(const su2double X[][3], const su2 } } - /*--- Evaluate isoparameters. ---*/ + /*--- Evaluate isoparameters at final Xi and Eta. ---*/ CQUAD4::ShapeFunctions(Xi, Eta, isoparams); return outOfBounds; From 79dcfaef0a4d596831496e6c321702b641fe84b8 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Fri, 3 Apr 2020 15:55:10 +0100 Subject: [PATCH 54/54] old compiler fixes --- Common/include/interface_interpolation/CIsoparametric.hpp | 2 +- Common/src/interface_interpolation/CIsoparametric.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Common/include/interface_interpolation/CIsoparametric.hpp b/Common/include/interface_interpolation/CIsoparametric.hpp index 600414b77c11..cd2d1562aa0f 100644 --- a/Common/include/interface_interpolation/CIsoparametric.hpp +++ b/Common/include/interface_interpolation/CIsoparametric.hpp @@ -33,7 +33,7 @@ */ class CIsoparametric final : public CInterpolator { private: - static constexpr auto NUM_CANDIDATE_DONORS = 8ul; /*!< \brief Test this many nearby donor elements for "best fit". */ + enum: unsigned long { NUM_CANDIDATE_DONORS = 8 }; /*!< \brief Test this many nearby donor elements for "best fit". */ /*--- Statistics. ---*/ su2double MaxDistance = 0.0, ErrorRate = 0.0; unsigned long ErrorCounter = 0; diff --git a/Common/src/interface_interpolation/CIsoparametric.cpp b/Common/src/interface_interpolation/CIsoparametric.cpp index 9745b5b9d70b..6c2cc7ca9a55 100644 --- a/Common/src/interface_interpolation/CIsoparametric.cpp +++ b/Common/src/interface_interpolation/CIsoparametric.cpp @@ -190,7 +190,7 @@ void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { /*--- Find "n" closest candidate donor elements (the naive way). ---*/ iota(nearElems.begin(), nearElems.end(), 0); - auto last = nearElems.begin() + min(NUM_CANDIDATE_DONORS, nGlobalElemDonor); + auto last = nearElems.begin() + min(NUM_CANDIDATE_DONORS, nGlobalElemDonor); partial_sort(nearElems.begin(), last, nearElems.end(), [&](unsigned iElem, unsigned jElem) { return SquaredDistance(nDim, coord_i, elemCentroid[iElem]) < @@ -201,7 +201,7 @@ void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { /*--- Evaluate interpolation for the candidates. ---*/ array candidateElems; - for (auto i = 0u; i < min(NUM_CANDIDATE_DONORS, nGlobalElemDonor); ++i) { + for (auto i = 0u; i < min(NUM_CANDIDATE_DONORS, nGlobalElemDonor); ++i) { const auto iElem = nearElems[i]; /*--- Fetch element info. ---*/