diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp
index e316b4cb8d01..604f83e10f9b 100644
--- a/Common/include/CConfig.hpp
+++ b/Common/include/CConfig.hpp
@@ -988,9 +988,11 @@ class CConfig {
unsigned short Pred_Order; /*!< \brief Order of the predictor for FSI applications. */
unsigned short Kind_Interpolation; /*!< \brief type of interpolation to use for FSI applications. */
bool ConservativeInterpolation; /*!< \brief Conservative approach for non matching mesh interpolation. */
+ unsigned short NumNearestNeighbors; /*!< \brief Number of neighbors used for Nearest Neighbor interpolation. */
unsigned short Kind_RadialBasisFunction; /*!< \brief type of radial basis function to use for radial basis FSI. */
bool RadialBasisFunction_PolynomialOption; /*!< \brief Option of whether to include polynomial terms in Radial Basis Function Interpolation or not. */
- su2double RadialBasisFunction_Parameter; /*!< \brief Radial basis function parameter. */
+ su2double RadialBasisFunction_Parameter; /*!< \brief Radial basis function parameter (radius). */
+ su2double RadialBasisFunction_PruneTol; /*!< \brief Tolerance to prune the RBF interpolation matrix. */
bool Prestretch; /*!< \brief Read a reference geometry for optimization purposes. */
string Prestretch_FEMFileName; /*!< \brief File name for reference geometry. */
string FEA_FileName; /*!< \brief File name for element-based properties. */
@@ -3460,27 +3462,27 @@ class CConfig {
unsigned short GetMarker_All_ZoneInterface(unsigned short val_marker) const { return Marker_All_ZoneInterface[val_marker]; }
/*!
- * \brief Get the MixingPlane interface information for a marker val_marker.
- * \param[in] val_marker value of the marker on the grid.
- * \return 0 if is not part of the MixingPlane Interface and greater than 1 if it is part.
- */
+ * \brief Get the MixingPlane interface information for a marker val_marker.
+ * \param[in] val_marker value of the marker on the grid.
+ * \return 0 if is not part of the MixingPlane Interface and greater than 1 if it is part.
+ */
unsigned short GetMarker_All_MixingPlaneInterface(unsigned short val_marker) const { return Marker_All_MixingPlaneInterface[val_marker]; }
- /*!
- * \brief Get the Turbomachinery information for a marker val_marker.
- * \param[in] val_marker value of the marker on the grid.
- * \return 0 if is not part of the Turbomachinery and greater than 1 if it is part.
- */
+ /*!
+ * \brief Get the Turbomachinery information for a marker val_marker.
+ * \param[in] val_marker value of the marker on the grid.
+ * \return 0 if is not part of the Turbomachinery and greater than 1 if it is part.
+ */
unsigned short GetMarker_All_Turbomachinery(unsigned short val_marker) const { return Marker_All_Turbomachinery[val_marker]; }
- /*!
- * \brief Get the Turbomachinery flag information for a marker val_marker.
- * \param[in] val_marker value of the marker on the grid.
- * \return 0 if is not part of the Turbomachinery, flag INFLOW or OUTFLOW if it is part.
- */
+ /*!
+ * \brief Get the Turbomachinery flag information for a marker val_marker.
+ * \param[in] val_marker value of the marker on the grid.
+ * \return 0 if is not part of the Turbomachinery, flag INFLOW or OUTFLOW if it is part.
+ */
unsigned short GetMarker_All_TurbomachineryFlag(unsigned short val_marker) const { return Marker_All_TurbomachineryFlag[val_marker]; }
- /*!
+ /*!
* \brief Get the number of FSI interface markers val_marker.
* \param[in] void.
* \return Number of markers belonging to the FSI interface.
@@ -8787,6 +8789,16 @@ class CConfig {
*/
su2double GetRadialBasisFunctionParameter(void) const { return RadialBasisFunction_Parameter; }
+ /*!
+ * \brief Get the tolerance used to prune the interpolation matrix (making it sparser).
+ */
+ su2double GetRadialBasisFunctionPruneTol(void) const { return RadialBasisFunction_PruneTol; }
+
+ /*!
+ * \brief Get the number of donor points to use in Nearest Neighbor interpolation.
+ */
+ unsigned short GetNumNearestNeighbors(void) const { return NumNearestNeighbors; }
+
/*!
* \brief Get the kind of inlet face interpolation function to use.
*/
diff --git a/Common/include/geometry/CGeometry.hpp b/Common/include/geometry/CGeometry.hpp
index 3066dcd97fd6..4473ac72b72a 100644
--- a/Common/include/geometry/CGeometry.hpp
+++ b/Common/include/geometry/CGeometry.hpp
@@ -1621,7 +1621,7 @@ class CGeometry {
* \param[in] fillLvl - Level of fill of the pattern.
* \return Reference to the sparse pattern.
*/
- const CCompressedSparsePatternUL& GetSparsePattern(ConnectivityType type, unsigned long fillLvl);
+ const CCompressedSparsePatternUL& GetSparsePattern(ConnectivityType type, unsigned long fillLvl = 0);
/*!
* \brief Get the edge to sparse pattern map.
@@ -1630,12 +1630,25 @@ class CGeometry {
*/
const CEdgeToNonZeroMapUL& GetEdgeToSparsePatternMap(void);
+ /*!
+ * \brief Get the transpose of the (main, i.e 0 fill) sparse pattern (e.g. CSR becomes CSC).
+ * \param[in] type - Finite volume or finite element.
+ * \return Reference to the map.
+ */
+ const su2vector& GetTransposeSparsePatternMap(ConnectivityType type);
+
/*!
* \brief Get the edge coloring.
* \note This method computes the coloring if that has not been done yet.
+ * \param[out] efficiency - optional output of the coloring efficiency.
* \return Reference to the coloring.
*/
- const CCompressedSparsePatternUL& GetEdgeColoring(void);
+ const CCompressedSparsePatternUL& GetEdgeColoring(su2double* efficiency = nullptr);
+
+ /*!
+ * \brief Force the natural (sequential) edge coloring.
+ */
+ void SetNaturalEdgeColoring();
/*!
* \brief Get the group size used in edge coloring.
@@ -1646,9 +1659,15 @@ class CGeometry {
/*!
* \brief Get the element coloring.
* \note This method computes the coloring if that has not been done yet.
+ * \param[out] efficiency - optional output of the coloring efficiency.
* \return Reference to the coloring.
*/
- const CCompressedSparsePatternUL& GetElementColoring(void);
+ const CCompressedSparsePatternUL& GetElementColoring(su2double* efficiency = nullptr);
+
+ /*!
+ * \brief Force the natural (sequential) element coloring.
+ */
+ void SetNaturalElementColoring();
/*!
* \brief Get the group size used in element coloring.
diff --git a/Common/include/geometry/dual_grid/CVertex.hpp b/Common/include/geometry/dual_grid/CVertex.hpp
index 70bc0dd2492c..084e6e0137ca 100644
--- a/Common/include/geometry/dual_grid/CVertex.hpp
+++ b/Common/include/geometry/dual_grid/CVertex.hpp
@@ -37,26 +37,25 @@
*/
class CVertex : public CDualGrid {
protected:
- unsigned long *Nodes; /*!< \brief Vector to store the global nodes of an element. */
- su2double *Normal; /*!< \brief Normal coordinates of the element and its center of gravity. */
- su2double Aux_Var; /*!< \brief Auxiliar variable defined only on the surface. */
- su2double CartCoord[3]; /*!< \brief Vertex cartesians coordinates. */
- su2double VarCoord[3]; /*!< \brief Used for storing the coordinate variation due to a surface modification. */
- su2double *VarRot; /*!< \brief Used for storing the rotation variation due to a surface modification. */
- long PeriodicPoint[5]; /*!< \brief Store the periodic point of a boundary (iProcessor, iPoint) */
- bool ActDisk_Perimeter; /*!< \brief Identify nodes at the perimeter of the actuator disk */
- short Rotation_Type; /*!< \brief Type of rotation associated with the vertex (MPI and periodic) */
+ unsigned long Nodes[1]; /*!< \brief Vector to store the global nodes of an element. */
+ su2double Normal[3]; /*!< \brief Normal coordinates of the element and its center of gravity. */
+ su2double Aux_Var; /*!< \brief Auxiliar variable defined only on the surface. */
+ su2double CartCoord[3]; /*!< \brief Vertex cartesians coordinates. */
+ su2double VarCoord[3]; /*!< \brief Used for storing the coordinate variation due to a surface modification. */
+ su2double *VarRot; /*!< \brief Used for storing the rotation variation due to a surface modification. */
+ long PeriodicPoint[5]; /*!< \brief Store the periodic point of a boundary (iProcessor, iPoint) */
+ bool ActDisk_Perimeter; /*!< \brief Identify nodes at the perimeter of the actuator disk */
+ short Rotation_Type; /*!< \brief Type of rotation associated with the vertex (MPI and periodic) */
unsigned long Normal_Neighbor; /*!< \brief Index of the closest neighbor. */
- unsigned long *Donor_Points; /*!< \brief indices of donor points for interpolation across zones */
- unsigned long *Donor_Proc; /*!< \brief indices of donor processor for interpolation across zones in parallel */
- unsigned long Donor_Elem; /*!< \brief Store the donor element for interpolation across zones/ */
- unsigned short Donor_Face; /*!<\brief Store the donor face (w/in donor element) for interpolation across zones */
- su2double Basis_Function[3]; /*!< \brief Basis function values for interpolation across zones. */
- su2double *Donor_Coeff; /*!\brief Store a list of coefficients corresponding to the donor points. */
- unsigned short nDonor_Points; /*!\brief Number of points in Donor_Points; at least there will be one donor point (if the mesh is matching)*/
+ unsigned long *Donor_Points; /*!< \brief indices of donor points for interpolation across zones */
+ unsigned long *Donor_Proc; /*!< \brief indices of donor processor for interpolation across zones in parallel */
+ unsigned long Donor_Elem; /*!< \brief Store the donor element for interpolation across zones/ */
+ unsigned short Donor_Face; /*!< \brief Store the donor face (w/in donor element) for interpolation across zones */
+ su2double Basis_Function[3]; /*!< \brief Basis function values for interpolation across zones. */
+ su2double *Donor_Coeff; /*!< \brief Store a list of coefficients corresponding to the donor points. */
+ unsigned short nDonor_Points; /*!< \brief Number of points in Donor_Coeff. */
public:
-
/*!
* \brief Constructor of the class.
* \param[in] val_point - Node of the vertex.
@@ -358,17 +357,6 @@ class CVertex : public CDualGrid {
*/
inline unsigned long GetNormal_Neighbor(void) const { return Normal_Neighbor; }
- /*!
- * \brief Increment the number of donor points by 1.
- */
- inline void IncrementnDonor(void) {nDonor_Points++;}
-
- /*!
- * \brief Set the value of nDonor_Points
- * \param[in] nDonor - the number of donor points
- */
- inline void SetnDonorPoints(unsigned short nDonor) {nDonor_Points = nDonor;}
-
/*!
* \brief Return the value of nDonor_Points
* \return nDonor - the number of donor points
@@ -419,9 +407,9 @@ class CVertex : public CDualGrid {
/*!
* \brief Allocate memory based on how many donor points need to be stored.
- * Uses nDonor_Points
+ * \param[in] nDonor - the number of donor points
*/
- void Allocate_DonorInfo(void);
+ void Allocate_DonorInfo(unsigned short nDonor);
/*!
* \brief Get the rotation variation
diff --git a/Common/include/interface_interpolation/CInterpolator.hpp b/Common/include/interface_interpolation/CInterpolator.hpp
new file mode 100644
index 000000000000..af95221275f0
--- /dev/null
+++ b/Common/include/interface_interpolation/CInterpolator.hpp
@@ -0,0 +1,193 @@
+/*!
+ * \file CInterpolator.hpp
+ * \brief Base class for multiphysics interpolation.
+ * \author H. Kline
+ * \version 7.0.2 "Blackbird"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md)
+ *
+ * SU2 is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * SU2 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with SU2. If not, see .
+ */
+#pragma once
+
+#include "../../include/datatype_structure.hpp"
+
+class CConfig;
+class CGeometry;
+
+/*!
+ * \class CInterpolator
+ * \brief Main class for defining the interpolator, it requires
+ * a child class for each particular interpolation method
+ * \author H. Kline
+ */
+class CInterpolator {
+protected:
+ const int rank; /*!< \brief MPI Rank. */
+ const int size; /*!< \brief MPI Size. */
+ const unsigned donorZone; /*!< \brief Index of donor zone. */
+ const unsigned targetZone; /*!< \brief Index of target zone. */
+
+ unsigned long
+ MaxLocalVertex_Donor, /*!< \brief Maximum vertices per processor*/
+ nGlobalFace_Donor, /*!< \brief Number of global donor faces*/
+ nGlobalFaceNodes_Donor, /*!< \brief Number of global donor face nodes*/
+ MaxFace_Donor, /*!< \brief Maximum faces per processor*/
+ MaxFaceNodes_Donor; /*!< \brief Maximum nodes associated with faces per processor*/
+
+ unsigned long
+ *Buffer_Receive_nVertex_Donor, /*!< \brief Buffer to store the number of vertices per processor on the Donor domain */
+ *Buffer_Receive_nFace_Donor, /*!< \brief Buffer to store the number of faces per processor*/
+ *Buffer_Receive_nFaceNodes_Donor, /*!< \brief Buffer to store the number of nodes associated with faces per processor*/
+ Buffer_Send_nVertex_Donor[1], /*!< \brief Buffer to send number of vertices on the local processor*/
+ Buffer_Send_nFace_Donor[1], /*!< \brief Buffer to send number of faces on the local processor*/
+ Buffer_Send_nFaceNodes_Donor[1], /*!< \brief Buffer to send the number of nodes assocated with faces per processor*/
+ *Buffer_Send_FaceIndex, /*!< \brief Buffer to send indices pointing to the node indices that define the faces*/
+ *Buffer_Receive_FaceIndex, /*!< \brief Buffer to receive indices pointing to the node indices that define the faces*/
+ *Buffer_Send_FaceNodes, /*!< \brief Buffer to send indices pointing to the location of node information in other buffers, defining faces*/
+ *Buffer_Receive_FaceNodes, /*!< \brief Buffer to receive indices pointing to the location of node information in other buffers, defining faces*/
+ *Buffer_Send_FaceProc, /*!< \brief Buffer to send processor which stores the node indicated in Buffer_Receive_FaceNodes*/
+ *Buffer_Receive_FaceProc; /*!< \brief Buffer to receive processor which stores the node indicated in Buffer_Receive_FaceNodes*/
+
+ long *Buffer_Send_GlobalPoint, /*!< \brief Buffer to send global point indices*/
+ *Buffer_Receive_GlobalPoint; /*!< \brief Buffer to receive global point indices*/
+
+ su2double *Buffer_Send_Coord, /*!< \brief Buffer to send coordinate values*/
+ *Buffer_Send_Normal, /*!< \brief Buffer to send normal vector values */
+ *Buffer_Receive_Coord, /*!< \brief Buffer to receive coordinate values*/
+ *Buffer_Receive_Normal; /*!< \brief Buffer to receive normal vector values*/
+
+ unsigned long
+ *Receive_GlobalPoint, /*!< \brief Buffer to receive Global point indexes*/
+ *Buffer_Receive_nLinkedNodes, /*!< \brief Buffer to receive the number of edges connected to each node*/
+ *Buffer_Receive_LinkedNodes, /*!< \brief Buffer to receive the list of notes connected to the nodes through an edge*/
+ *Buffer_Receive_StartLinkedNodes, /*!< \brief Buffer to receive the index of the Receive_LinkedNodes buffer where corresponding list of linked nodes begins */
+ *Buffer_Receive_Proc; /*!< \brief Buffer to receive the thread that owns the node*/
+
+ unsigned long
+ nGlobalVertex_Target, /*!< \brief Global number of vertex of the target boundary*/
+ nLocalVertex_Target, /*!< \brief Number of vertex of the target boundary owned by the thread*/
+ nGlobalVertex_Donor, /*!< \brief Global number of vertex of the donor boundary*/
+ nLocalVertex_Donor, /*!< \brief Number of vertex of the donor boundary owned by the thread*/
+ nGlobalVertex, /*!< \brief Dummy variable to temporarily store the global number of vertex of a boundary*/
+ nLocalLinkedNodes; /*!< \brief Dummy variable to temporarily store the number of vertex of a boundary*/
+
+ CGeometry**** const Geometry; /*! \brief Vector which stores n zones of geometry. */
+ CGeometry* const donor_geometry; /*! \brief Donor geometry. */
+ CGeometry* const target_geometry; /*! \brief Target geometry. */
+
+public:
+ /*!
+ * \brief Constructor of the class.
+ * \param[in] geometry - Geometrical definition of the problem.
+ * \param[in] config - Definition of the particular problem.
+ * \param[in] iZone - index of the donor zone
+ * \param[in] jZone - index of the target zone
+ */
+ CInterpolator(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone);
+
+ /*!
+ * \brief No default construction allowed to force zones and geometry to always be set.
+ */
+ CInterpolator(void) = delete;
+
+ /*!
+ * \brief Destructor of the class, nothing is deleted, derived classes need to manage the MPI buffers.
+ */
+ virtual ~CInterpolator(void) = default;
+
+ /*!
+ * \brief Set up transfer matrix defining relation between two meshes
+ * \note Main method that derived classes must implement.
+ * \param[in] config - Definition of the particular problem.
+ */
+ virtual void Set_TransferCoeff(const CConfig* const* config) = 0;
+
+ /*!
+ * \brief Print information about the interpolation.
+ */
+ virtual void PrintStatistics(void) const { }
+
+ /*!
+ * \brief Find the index of the interface marker shared by that zone
+ * \param[in] config - Definition of the particular problem.
+ * \param[in] val_marker_interface - Interface tag.
+ */
+ static int Find_InterfaceMarker(const CConfig *config, unsigned short val_marker_interface);
+
+ /*!
+ * \brief Check whether an interface should be processed or not, i.e. if it is part of the zones.
+ * \param[in] val_markDonor - Marker tag from donor zone.
+ * \param[in] val_markTarget - Marker tag from target zone.
+ */
+ static bool CheckInterfaceBoundary(int val_markDonor, int val_markTarget);
+
+protected:
+ /*!
+ * \brief Recontstruct the boundary connectivity from parallel partitioning and broadcasts it to all threads
+ * \param[in] val_zone - index of the zone
+ * \param[in] val_marker - index of the marker
+ */
+ void ReconstructBoundary(unsigned long val_zone, int val_marker);
+
+ /*!
+ * \brief compute squared distance between 2 points
+ * \param[in] nDim - number of dimensions
+ * \param[in] point_i - coordinates of point i
+ * \param[in] point_j - coordinates of point j
+ */
+ static inline su2double PointsSquareDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) {
+ su2double d = 0.0;
+ for(unsigned short iDim = 0; iDim < nDim; iDim++)
+ d += pow(point_j[iDim] - point_i[iDim], 2);
+ return d;
+ }
+
+ /*!
+ * \brief compute distance between 2 points
+ * \param[in] nDim - number of dimensions
+ * \param[in] point_i - coordinates of point i
+ * \param[in] point_j - coordinates of point j
+ */
+ static inline su2double PointsDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) {
+ return sqrt(PointsSquareDistance(nDim, point_i, point_j));
+ }
+
+ /*!
+ * \brief Determine array sizes used to collect and send coordinate and global point
+ * information.
+ * \param[in] faces - boolean that determines whether or not to set face information as well
+ * \param[in] markDonor - Index of the boundary on the donor domain.
+ * \param[in] markTarget - Index of the boundary on the target domain.
+ * \param[in] nVertexDonor - Number of vertices on the donor boundary.
+ * \param[in] nDim - number of physical dimensions.
+ */
+ void Determine_ArraySize(bool faces, int markDonor, int markTarget, unsigned long nVertexDonor, unsigned short nDim);
+
+ /*!
+ * \brief Collect and communicate vertex info: coord, global point, and if faces=true the normal vector
+ * \param[in] faces - boolean that determines whether or not to set face information as well
+ * \param[in] markDonor - Index of the boundary on the donor domain.
+ * \param[in] markTarget - Index of the boundary on the target domain.
+ * \param[in] nVertexDonor - Number of vertices on the donor boundary.
+ * \param[in] nDim - number of physical dimensions.
+ */
+ void Collect_VertexInfo(bool faces, int markDonor, int markTarget, unsigned long nVertexDonor, unsigned short nDim);
+
+};
diff --git a/Common/include/interface_interpolation/CIsoparametric.hpp b/Common/include/interface_interpolation/CIsoparametric.hpp
new file mode 100644
index 000000000000..1175e3890f2e
--- /dev/null
+++ b/Common/include/interface_interpolation/CIsoparametric.hpp
@@ -0,0 +1,70 @@
+/*!
+ * \file CIsoparametric.hpp
+ * \brief Isoparametric interpolation using FE shape functions.
+ * \author H. Kline
+ * \version 7.0.2 "Blackbird"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md)
+ *
+ * SU2 is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * SU2 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with SU2. If not, see .
+ */
+#pragma once
+
+#include "CInterpolator.hpp"
+
+/*!
+ * \brief Isoparametric interpolation.
+ */
+class CIsoparametric final : public CInterpolator {
+public:
+ /*!
+ * \brief Constructor of the class.
+ * \param[in] geometry - Geometrical definition of the problem.
+ * \param[in] config - Definition of the particular problem.
+ * \param[in] iZone - index of the donor zone
+ * \param[in] jZone - index of the target zone
+ */
+ CIsoparametric(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone);
+
+ /*!
+ * \brief Set up transfer matrix defining relation between two meshes
+ * \param[in] config - Definition of the particular problem.
+ */
+ void Set_TransferCoeff(const CConfig* const* config) override;
+
+private:
+ /*!
+ * \brief Calculate the isoparametric representation of point iVertex in marker iZone_0 by
+ * nodes of element donor_elem in marker jMarker of zone iZone_1.
+ * \param[in] iVertex - vertex index of the point being interpolated.
+ * \param[in] nDim - the dimension of the coordinates.
+ * \param[in] iZone_1 - zone index of the element to use for interpolation (the DONOR zone)
+ * \param[in] donor_elem - element index of the element to use for interpolation (or global index of a point in 2D)
+ * \param[in] nDonorPoints - number of donor points in the element.
+ * \param[in] xj - point projected onto the plane of the donor element.
+ * \param[out] isoparams - isoparametric coefficients. Must be allocated to size nNodes ahead of time. (size> nDonors)
+ *
+ * \note If the problem is 2D, the 'face' projected onto is actually an edge; the local index
+ * of the edge is then stored in iFace, and the global index of the node (from which the edge
+ * is referenced)
+ */
+ void Isoparameters(unsigned short nDim, unsigned short nDonor, const su2double *X,
+ const su2double *xj, su2double* isoparams) const;
+
+};
diff --git a/Common/include/interface_interpolation/CMirror.hpp b/Common/include/interface_interpolation/CMirror.hpp
new file mode 100644
index 000000000000..311d54d96a60
--- /dev/null
+++ b/Common/include/interface_interpolation/CMirror.hpp
@@ -0,0 +1,53 @@
+/*!
+ * \file CMirror.hpp
+ * \brief Mirror interpolation for the conservative (work-wise) approach in FSI problems.
+ * \author H. Kline
+ * \version 7.0.2 "Blackbird"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md)
+ *
+ * SU2 is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * SU2 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with SU2. If not, see .
+ */
+#pragma once
+
+#include "CInterpolator.hpp"
+
+/*!
+ * \brief Mirror interpolation: copy point linking and coefficient values from the opposing mesh.
+ * \note Assumes that the oppoosing mesh has already run interpolation, otherwise will result in empty/trivial interpolation.
+ */
+class CMirror final : public CInterpolator {
+public:
+ /*!
+ * \brief Constructor of the class.
+ * \note Data is set in geometry[targetZone].
+ * \param[in] geometry_container
+ * \param[in] config - config container
+ * \param[in] iZone - First zone
+ * \param[in] jZone - Second zone
+ */
+ CMirror(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone);
+
+ /*!
+ * \brief Set up transfer matrix defining relation between two meshes
+ * \param[in] config - Definition of the particular problem.
+ */
+ void Set_TransferCoeff(const CConfig* const* config) override;
+
+};
diff --git a/Common/include/interface_interpolation/CNearestNeighbor.hpp b/Common/include/interface_interpolation/CNearestNeighbor.hpp
new file mode 100644
index 000000000000..c86d50add484
--- /dev/null
+++ b/Common/include/interface_interpolation/CNearestNeighbor.hpp
@@ -0,0 +1,62 @@
+/*!
+ * \file CNearestNeighbor.hpp
+ * \brief Nearest Neighbor interpolation class.
+ * \author H. Kline
+ * \version 7.0.2 "Blackbird"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md)
+ *
+ * SU2 is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * SU2 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with SU2. If not, see .
+ */
+#pragma once
+
+#include "CInterpolator.hpp"
+
+/*!
+ * \brief Nearest Neighbor(s) interpolation.
+ * \note The closest k neighbors are used for IDW interpolation, the computational
+ * cost of setting up the interpolation is O(N^2 log(k)), this can be improved
+ * by using a kd-tree.
+ */
+class CNearestNeighbor final : public CInterpolator {
+private:
+ su2double AvgDistance = 0.0, MaxDistance = 0.0;
+
+public:
+ /*!
+ * \brief Constructor of the class.
+ * \param[in] geometry - Geometrical definition of the problem.
+ * \param[in] config - Definition of the particular problem.
+ * \param[in] iZone - index of the donor zone
+ * \param[in] jZone - index of the target zone
+ */
+ CNearestNeighbor(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone);
+
+ /*!
+ * \brief Set up transfer matrix defining relation between two meshes.
+ * \param[in] config - Definition of the particular problem.
+ */
+ void Set_TransferCoeff(const CConfig* const* config) override;
+
+ /*!
+ * \brief Print interpolation statistics.
+ */
+ void PrintStatistics(void) const override;
+
+};
diff --git a/Common/include/interface_interpolation/CRadialBasisFunction.hpp b/Common/include/interface_interpolation/CRadialBasisFunction.hpp
new file mode 100644
index 000000000000..48416ae33053
--- /dev/null
+++ b/Common/include/interface_interpolation/CRadialBasisFunction.hpp
@@ -0,0 +1,139 @@
+/*!
+ * \file CRadialBasisFunction.hpp
+ * \brief Radial basis function interpolation.
+ * \author Joel Ho, P. Gomes
+ * \version 7.0.2 "Blackbird"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md)
+ *
+ * SU2 is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * SU2 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with SU2. If not, see .
+ */
+#pragma once
+
+#include "CInterpolator.hpp"
+#include "../option_structure.hpp"
+#include "../toolboxes/C2DContainer.hpp"
+
+/*!
+ * \brief Radial basis function interpolation.
+ */
+class CRadialBasisFunction final : public CInterpolator {
+ static_assert(su2passivematrix::Storage == StorageType::RowMajor,
+ "This class relies on row major storage throughout.");
+private:
+ unsigned long MinDonors = 0, AvgDonors = 0, MaxDonors = 0;
+ passivedouble Density = 0.0, AvgCorrection = 0.0, MaxCorrection = 0.0;
+
+public:
+ /*!
+ * \brief Constructor of the class.
+ * \param[in] geometry - Geometrical definition of the problem.
+ * \param[in] config - Definition of the particular problem.
+ * \param[in] iZone - index of the donor zone
+ * \param[in] jZone - index of the target zone
+ */
+ CRadialBasisFunction(CGeometry ****geometry_container, const CConfig* const* config,
+ unsigned int iZone, unsigned int jZone);
+
+ /*!
+ * \brief Set up transfer matrix defining relation between two meshes
+ * \param[in] config - Definition of the particular problem.
+ */
+ void Set_TransferCoeff(const CConfig* const* config) override;
+
+ /*!
+ * \brief Print information about the interpolation.
+ */
+ void PrintStatistics(void) const override;
+
+ /*!
+ * \brief Compute the value of a radial basis function, this is static so it can be re-used.
+ * \param[in] type - of radial basis function
+ * \param[in] radius - the characteristic dimension
+ * \param[in] dist - distance
+ * \return value of the RBF.
+ */
+ static su2double Get_RadialBasisValue(ENUM_RADIALBASIS type, const su2double radius, const su2double dist);
+
+ /*!
+ * \brief Compute the RBF "generator" matrix with or without polynomial terms.
+ * \note Multiplying C_inv_trunc by a column vector gives specific coefficients for given "known values",
+ * conversely, multiplying (on the left) by a row vector of polynomial and RBF values gives generic
+ * interpolation coefficients for a given target evaluation point.
+ * \param[in] type - Type of radial basis function.
+ * \param[in] usePolynomial - Whether to use polynomial terms.
+ * \param[in] radius - Normalizes point-to-point distance when computing RBF values.
+ * \param[in] coords - Coordinates of the donor points.
+ * \param[out] nPolynomial - Num of poly terms, -1 if !usePolynomial, nDim-1 if coords lie on plane, else nDim.
+ * \param[out] keepPolynomialRow - Size nDim, signals which (if any) iDim was removed from polynomial term.
+ * \param[out] C_inv_trunc - The generator matrix as described above.
+ */
+ static void ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, su2double radius,
+ const su2activematrix& coords, int& nPolynomial,
+ vector& keepPolynomialRow, su2passivematrix& C_inv_trunc);
+
+ /*!
+ * \brief If the polynomial term is included in the interpolation, and the points lie on a plane, the matrix
+ * becomes rank deficient and cannot be inverted. This method detects that condition and corrects it by
+ * removing a row from P (the polynomial part of the interpolation matrix).
+ * \param[in] max_diff_tol - Tolerance to detect whether points are on a plane.
+ * \param[out] keep_row - Marks the dimensions of P kept.
+ * \param[in,out] P - Polynomial part of the interpolation matrix, one row may be eliminated.
+ * \return n_polynomial - Size of the polynomial part on exit (in practice nDim or nDim-1).
+ */
+ static int CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, su2passivematrix &P);
+
+private:
+ /*!
+ * \brief Helper function, prunes (by setting to zero) small interpolation coefficients,
+ * i.e. <= tolerance*max(abs(coeffs)). The vector is re-scaled such that sum(coeffs)==1.
+ * \param[in] tolerance - Relative pruning tolerance.
+ * \param[in] size - Size of the coefficient vector.
+ * \param[in,out] coeffs - Iterator to start of vector of interpolation coefficients.
+ * \return Number of non-zero coefficients after pruning and correction factor.
+ */
+ template
+ static pair PruneSmallCoefficients(Float tolerance, Int size, ForwardIt coeffs) {
+
+ /*--- Determine the pruning threshold. ---*/
+ Float thresh = 0.0;
+ auto end = coeffs;
+ for (Int i = 0; i < size; ++i)
+ thresh = max(thresh, fabs(*(end++)));
+ thresh *= tolerance;
+
+ /*--- Prune and count non-zeros. ---*/
+ Int numNonZeros = 0;
+ Float coeffSum = 0.0;
+ for (auto it = coeffs; it != end; ++it) {
+ if (fabs(*it) > thresh) { // keep
+ coeffSum += *it;
+ ++numNonZeros;
+ }
+ else { *it = 0.0; } // prune
+ }
+
+ /*--- Correct remaining coefficients, sum must be 1 for conservation. ---*/
+ Float correction = 1.0 / coeffSum;
+ while (coeffs != end) *(coeffs++) *= correction;
+
+ return make_pair(numNonZeros, correction);
+ }
+
+};
diff --git a/Common/include/interface_interpolation/CSlidingMesh.hpp b/Common/include/interface_interpolation/CSlidingMesh.hpp
new file mode 100644
index 000000000000..74ae1d9ae18f
--- /dev/null
+++ b/Common/include/interface_interpolation/CSlidingMesh.hpp
@@ -0,0 +1,129 @@
+/*!
+ * \file CSlidingMesh.hpp
+ * \brief Sliding mesh interpolation.
+ * \author H. Kline
+ * \version 7.0.2 "Blackbird"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md)
+ *
+ * SU2 is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * SU2 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with SU2. If not, see .
+ */
+#pragma once
+
+#include "CInterpolator.hpp"
+
+/*!
+ * \brief Sliding mesh approach.
+ * \note The algorithm is based on Rinaldi et al. "Flux-conserving treatment of non-conformal interfaces
+ * for finite-volume discritization of conservation laws" 2015, Comp. Fluids, 120, pp 126-139
+ */
+class CSlidingMesh final : public CInterpolator {
+public:
+ /*!
+ * \brief Constructor of the class.
+ * \param[in] geometry - Geometrical definition of the problem.
+ * \param[in] config - Definition of the particular problem.
+ * \param[in] iZone - index of the donor zone
+ * \param[in] jZone - index of the target zone
+ */
+ CSlidingMesh(CGeometry ****geometry_container, const CConfig* const* config,
+ unsigned int iZone, unsigned int jZone);
+
+ /*!
+ * \brief Set up transfer matrix defining relation between two meshes
+ * \param[in] config - Definition of the particular problem.
+ */
+ void Set_TransferCoeff(const CConfig* const* config) override;
+
+private:
+ /*!
+ * \brief For 3-Dimensional grids, build the dual surface element
+ * \param[in] map - array containing the index of the boundary points connected to the node
+ * \param[in] startIndex - for each vertex specifies the corresponding index in the global
+ * array containing the indexes of all its neighbouring vertexes
+ * \param[in] nNeighbour - for each vertex specifies the number of its neighbouring vertexes (on the boundary)
+ * \param[in] coord - array containing the coordinates of all the boundary vertexes
+ * \param[in] centralNode - label of the vertex around which the dual surface element is built
+ * \param[out] element - double array where element node coordinates will be stored
+ * \return Number of points included in the element.
+ */
+ static int Build_3D_surface_element(const unsigned long *map, const unsigned long *startIndex,
+ const unsigned long* nNeighbor, const su2double *coord,
+ unsigned long centralNode, su2double** element);
+
+ /*!
+ * \brief For 2-Dimensional grids, compute intersection length of two segments projected along a given direction
+ * \param[in] nDim - Number of dimensions
+ * \param[in] A1 - first point of segment A
+ * \param[in] A2 - second point of segment A
+ * \param[in] B1 - first point of segment B
+ * \param[in] B2 - second point of segment B
+ * \param[in] Direction - along which segments are projected
+ */
+ static su2double ComputeLineIntersectionLength(unsigned short nDim, const su2double* A1, const su2double* A2,
+ const su2double* B1, const su2double* B2, const su2double* Direction);
+
+ /*!
+ * \brief For 3-Dimensional grids, compute intersection area between two triangle projected on a given plane
+ * \param[in] A1 - first point of triangle A
+ * \param[in] A2 - second point of triangle A
+ * \param[in] A3 - third point of triangle A
+ * \param[in] B1 - first point of triangle B
+ * \param[in] B2 - second point of triangle B
+ * \param[in] B3 - third point of triangle B
+ * \param[in] Direction - vector normal to projection plane
+ */
+ static su2double Compute_Triangle_Intersection(const su2double* A1, const su2double* A2, const su2double* A3,
+ const su2double* B1, const su2double* B2, const su2double* B3,
+ const su2double* Direction);
+
+ /*!
+ * \brief For 3-Dimensional grids, compute intersection area between two triangle projected on a given plane
+ * P1 from triangle P MUST be inside triangle Q, points order doesn't matter
+ * \param[in] P1 - first point of triangle A
+ * \param[in] P2 - second point of triangle A
+ * \param[in] P3 - third point of triangle A
+ * \param[in] Q1 - first point of triangle B
+ * \param[in] Q2 - second point of triangle B
+ * \param[in] Q3 - third point of triangle B
+ */
+ static su2double ComputeIntersectionArea(const su2double* P1, const su2double* P2, const su2double* P3,
+ const su2double* Q1, const su2double* Q2, const su2double* Q3);
+
+ /*!
+ * \brief For 2-Dimensional grids, check whether, and compute, two lines are intersecting
+ * \param[in] A1 - first defining first line
+ * \param[in] A2 - second defining first line
+ * \param[in] B1 - first defining second line
+ * \param[in] B2 - second defining second line
+ * \param[out] IntersectionPoint - Container for intersection coordinates
+ */
+ static void ComputeLineIntersectionPoint(const su2double* A1, const su2double* A2, const su2double* B1,
+ const su2double* B2, su2double* IntersectionPoint);
+
+ /*!
+ * \brief For N-Dimensional grids, check whether a point is inside a triangle specified by 3 T points
+ * \param[in] Point - query point
+ * \param[in] T1 - first point of triangle T
+ * \param[in] T2 - second point of triangle T
+ * \param[in] T3 - third point of triangle T
+ */
+ static bool CheckPointInsideTriangle(const su2double* Point, const su2double* T1,
+ const su2double* T2, const su2double* T3);
+};
diff --git a/Common/include/interpolation_structure.hpp b/Common/include/interpolation_structure.hpp
deleted file mode 100644
index 28bdf3af8677..000000000000
--- a/Common/include/interpolation_structure.hpp
+++ /dev/null
@@ -1,491 +0,0 @@
-/*!
- * \file interpolation_structure.hpp
- * \brief Headers of classes used for multiphysics interpolation.
- * The implementation is in the interpolation_structure.cpp file.
- * \author H. Kline
- * \version 7.0.2 "Blackbird"
- *
- * SU2 Project Website: https://su2code.github.io
- *
- * The SU2 Project is maintained by the SU2 Foundation
- * (http://su2foundation.org)
- *
- * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md)
- *
- * SU2 is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * SU2 is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with SU2. If not, see .
- */
-
-#pragma once
-
-#include "../../Common/include/mpi_structure.hpp"
-
-#include
-#include
-#include
-#include
-#include
-
-#include "CConfig.hpp"
-#include "geometry/CGeometry.hpp"
-
-using namespace std;
-
-
-/*!
- * \class CInterpolator
- * \brief Main class for defining the interpolator, it requires
- * a child class for each particular interpolation method
- * \author H. Kline
- */
-class CInterpolator {
-protected:
- int rank, /*!< \brief MPI Rank. */
- size; /*!< \brief MPI Size. */
- unsigned int nZone; /*!< \brief Number of zones*/
- unsigned int donorZone,
- targetZone; /*!< \brief Type of MPI zone */
-
- unsigned long
- MaxLocalVertex_Donor, /*!< \brief Maximum vertices per processor*/
- nGlobalFace_Donor, /*!< \brief Number of global donor faces*/
- nGlobalFaceNodes_Donor, /*!< \brief Number of global donor face nodes*/
- MaxFace_Donor, /*!< \brief Maximum faces per processor*/
- MaxFaceNodes_Donor; /*!< \brief Maximum nodes associated with faces per processor*/
-
- unsigned long
- *Buffer_Receive_nVertex_Donor, /*!< \brief Buffer to store the number of vertices per processor on the Donor domain */
- *Buffer_Receive_nFace_Donor, /*!< \brief Buffer to store the number of faces per processor*/
- *Buffer_Receive_nFaceNodes_Donor, /*!< \brief Buffer to store the number of nodes associated with faces per processor*/
- *Buffer_Send_nVertex_Donor, /*!< \brief Buffer to send number of vertices on the local processor*/
- *Buffer_Send_nFace_Donor, /*!< \brief Buffer to send number of faces on the local processor*/
- *Buffer_Send_nFaceNodes_Donor, /*!< \brief Buffer to send the number of nodes assocated with faces per processor*/
- *Buffer_Send_FaceIndex, /*!< \brief Buffer to send indices pointing to the node indices that define the faces*/
- *Buffer_Receive_FaceIndex, /*!< \brief Buffer to receive indices pointing to the node indices that define the faces*/
- *Buffer_Send_FaceNodes, /*!< \brief Buffer to send indices pointing to the location of node information in other buffers, defining faces*/
- *Buffer_Receive_FaceNodes, /*!< \brief Buffer to receive indices pointing to the location of node information in other buffers, defining faces*/
- *Buffer_Send_FaceProc, /*!< \brief Buffer to send processor which stores the node indicated in Buffer_Receive_FaceNodes*/
- *Buffer_Receive_FaceProc; /*!< \brief Buffer to receive processor which stores the node indicated in Buffer_Receive_FaceNodes*/
-
- long *Buffer_Send_GlobalPoint, /*!< \brief Buffer to send global point indices*/
- *Buffer_Receive_GlobalPoint; /*!< \brief Buffer to receive global point indices*/
-
- su2double *Buffer_Send_Coord, /*!< \brief Buffer to send coordinate values*/
- *Buffer_Send_Normal, /*!< \brief Buffer to send normal vector values */
- *Buffer_Receive_Coord, /*!< \brief Buffer to receive coordinate values*/
- *Buffer_Receive_Normal; /*!< \brief Buffer to receive normal vector values*/
-
- unsigned long *Receive_GlobalPoint, /*!< \brief Buffer to receive Global point indexes*/
- *Buffer_Receive_nLinkedNodes, /*!< \brief Buffer to receive the number of edges connected to each node*/
- *Buffer_Receive_LinkedNodes, /*!< \brief Buffer to receive the list of notes connected to the nodes through an edge*/
- *Buffer_Receive_StartLinkedNodes, /*!< \brief Buffer to receive the index of the Receive_LinkedNodes buffer where corresponding list of linked nodes begins */
- *Buffer_Receive_Proc; /*!< \brief Buffer to receive the thread that owns the node*/
-
- unsigned long nGlobalVertex_Target, /*!< \brief Global number of vertex of the target boundary*/
- nLocalVertex_Target, /*!< \brief Number of vertex of the target boundary owned by the thread*/
- nGlobalVertex_Donor, /*!< \brief Global number of vertex of the donor boundary*/
- nLocalVertex_Donor, /*!< \brief Number of vertex of the donor boundary owned by the thread*/
- nGlobalVertex, /*!< \brief Dummy variable to temporarily store the global number of vertex of a boundary*/
- nLocalLinkedNodes; /*!< \brief Dummy variable to temporarily store the number of vertex of a boundary*/
-
-public:
- CGeometry**** Geometry; /*! \brief Vector which stores n zones of geometry. */
- CGeometry* donor_geometry; /*! \brief Vector which stores the donor geometry. */
- CGeometry* target_geometry; /*! \brief Vector which stores the target geometry. */
-
- /*!
- * \brief Constructor of the class.
- */
- CInterpolator(void);
-
- /*!
- * \brief Constructor of the class.
- * \param[in] geometry - Geometrical definition of the problem.
- * \param[in] config - Definition of the particular problem.
- * \param[in] iZone - index of the donor zone
- * \param[in] jZone - index of the target zone
- */
- CInterpolator(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone);
-
- /*!
- * \brief Destructor of the class.
- */
- virtual ~CInterpolator(void);
-
- /*!
- * \brief Find the index of the interface marker shared by that zone
- * \param[in] config - Definition of the particular problem.
- * \param[in] val_marker_interface - Interface tag.
- */
- int Find_InterfaceMarker(CConfig *config, unsigned short val_marker_interface);
-
- /*!
- * \brief Check whether the interface should be processed or not
- * \param[in] val_markDonor - Marker tag from donor zone.
- * \param[in] val_markTarget - Marker tag from target zone.
- */
- bool CheckInterfaceBoundary(int val_markDonor, int val_markTarget);
-
- /*!
- * \brief Recontstruct the boundary connectivity from parallel partitioning and broadcasts it to all threads
- * \param[in] val_zone - index of the zone
- * \param[in] val_marker - index of the marker
- */
- void ReconstructBoundary(unsigned long val_zone, int val_marker);
-
- /*!
- * \brief compute distance between 2 points
- * \param[in] point_i
- * \param[in] point_i
- */
- su2double PointsDistance(su2double *point_i, su2double *point_j);
-
- /*!
- * \brief Set up transfer matrix defining relation between two meshes
- * \param[in] config - Definition of the particular problem.
- */
- virtual void Set_TransferCoeff(CConfig **config);
-
- /*!
- * \brief Determine array sizes used to collect and send coordinate and global point
- * information.
- * \param[in] faces - boolean that determines whether or not to set face information as well
- * \param[in] markDonor - Index of the boundary on the donor domain.
- * \param[in] markTarget - Index of the boundary on the target domain.
- * \param[in] nVertexDonor - Number of vertices on the donor boundary.
- * \param[in] nDim - number of physical dimensions.
- */
- void Determine_ArraySize(bool faces, int markDonor, int markTarget, unsigned long nVertexDonor, unsigned short nDim);
-
- /*!
- * \brief Collect and communicate vertex info: coord, global point, and if faces=true the normal vector
- * \param[in] faces - boolean that determines whether or not to set face information as well
- * \param[in] markDonor - Index of the boundary on the donor domain.
- * \param[in] markTarget - Index of the boundary on the target domain.
- * \param[in] nVertexDonor - Number of vertices on the donor boundary.
- * \param[in] nDim - number of physical dimensions.
- */
- void Collect_VertexInfo(bool faces, int markDonor, int markTarget, unsigned long nVertexDonor, unsigned short nDim);
-
-};
-
-/*!
- * \brief Nearest Neighbor interpolation
- */
-class CNearestNeighbor : public CInterpolator {
-public:
-
- /*!
- * \brief Constructor of the class.
- */
- CNearestNeighbor(void);
-
- /*!
- * \brief Constructor of the class.
- * \param[in] geometry - Geometrical definition of the problem.
- * \param[in] config - Definition of the particular problem.
- * \param[in] iZone - index of the donor zone
- * \param[in] jZone - index of the target zone
- */
- CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone);
-
- /*!
- * \brief Destructor of the class.
- */
- ~CNearestNeighbor(void);
-
- /*!
- * \brief Set up transfer matrix defining relation between two meshes
- * \param[in] config - Definition of the particular problem.
- */
- void Set_TransferCoeff(CConfig **config);
-
-};
-
-/*!
- * \brief Isoparametric interpolation
- */
-class CIsoparametric : public CInterpolator {
-public:
-
- /*!
- * \brief Constructor of the class.
- * \param[in] geometry - Geometrical definition of the problem.
- * \param[in] config - Definition of the particular problem.
- * \param[in] iZone - index of the donor zone
- * \param[in] jZone - index of the target zone
- */
- CIsoparametric(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone);
-
- /*!
- * \brief Destructor of the class.
- */
- ~CIsoparametric(void);
-
- /*!
- * \brief Set up transfer matrix defining relation between two meshes
- * \param[in] config - Definition of the particular problem.
- */
- void Set_TransferCoeff(CConfig **config);
-
- /*!
- * \brief Calculate the isoparametric representation of point iVertex in marker iZone_0 by nodes of element donor_elem in marker jMarker of zone iZone_1.
- * \param[in] iVertex - vertex index of the point being interpolated.
- * \param[in] nDim - the dimension of the coordinates.
- * \param[in] iZone_1 - zone index of the element to use for interpolation (the DONOR zone)
- * \param[in] donor_elem - element index of the element to use for interpolation (or global index of a point in 2D)
- * \param[in] nDonorPoints - number of donor points in the element.
- * \param[in] xj - point projected onto the plane of the donor element.
- * \param[out] isoparams - isoparametric coefficients. Must be allocated to size nNodes ahead of time. (size> nDonors)
- *
- * If the problem is 2D, the 'face' projected onto is actually an edge; the local index
- * of the edge is then stored in iFace, and the global index of the node (from which the edge
- * is referenced)
- */
- void Isoparameters(unsigned short nDim, unsigned short nDonor, su2double *X, su2double *xj,su2double* isoparams);
-
-};
-
-/*!
- * \brief Mirror interpolation: copy point linking and coefficient values from the opposing mesh
- * Assumes that the oppoosing mesh has already run interpolation. (otherwise this will result in empty/trivial interpolation)
- */
-class CMirror : public CInterpolator {
-public:
-
- /*!
- * \brief Constructor of the class.
- * \param[in] geometry_container
- * \param[in] config - config container
- * \param[in] iZone - First zone
- * \param[in] jZone - Second zone
- *
- * Data is set in geometry[targetZone]
- *
- */
- CMirror(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone);
-
- /*!
- * \brief Destructor of the class.
- */
- ~CMirror(void);
-
- /*!
- * \brief Set up transfer matrix defining relation between two meshes
- * \param[in] config - Definition of the particular problem.
- */
- void Set_TransferCoeff(CConfig **config);
-
-};
-
-/*!
- * \brief Sliding mesh approach
- */
-class CSlidingMesh : public CInterpolator {
-public:
-
- /*!
- * \brief Constructor of the class.
- * \param[in] geometry - Geometrical definition of the problem.
- * \param[in] config - Definition of the particular problem.
- * \param[in] iZone - index of the donor zone
- * \param[in] jZone - index of the target zone
- */
- CSlidingMesh(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone);
-
- /*!
- * \brief Destructor of the class.
- */
- ~CSlidingMesh(void);
-
- /*!
- * \brief Set up transfer matrix defining relation between two meshes
- * \param[in] config - Definition of the particular problem.
- */
- void Set_TransferCoeff(CConfig **config);
-
- /*!
- * \brief For 3-Dimensional grids, build the dual surface element
- * \param[in] map - array containing the index of the boundary points connected to the node
- * \param[in] startIndex - for each vertex specifies the corresponding index in the global array containing the indexes of all its neighbouring vertexes
- * \param[in] nNeighbour - for each vertex specifies the number of its neighbouring vertexes (on the boundary)
- * \param[in] coord - array containing the coordinates of all the boundary vertexes
- * \param[in] centralNode - label of the vertex around which the dual surface element is built
- * \param[in] element - double array where element node coordinates will be stored
- */
- int Build_3D_surface_element(unsigned long *map, unsigned long *startIndex, unsigned long* nNeighbor, su2double *coord, unsigned long centralNode, su2double** element);
-
- /*!
- * \brief For 2-Dimensional grids, compute intersection length of two segments projected along a given direction
- * \param[in] A1 - first point of segment A
- * \param[in] A2 - second point of segment A
- * \param[in] B1 - first point of segment B
- * \param[in] B2 - second point of segment B
- * \param[in] Direction - along which segments are projected
- */
- su2double ComputeLineIntersectionLength(su2double* A1, su2double* A2, su2double* B1, su2double* B2, su2double* Direction);
-
- /*!
- * \brief For 3-Dimensional grids, compute intersection area between two triangle projected on a given plane
- * \param[in] A1 - first point of triangle A
- * \param[in] A2 - second point of triangle A
- * \param[in] A3 - third point of triangle A
- * \param[in] B1 - first point of triangle B
- * \param[in] B2 - second point of triangle B
- * \param[in] B3 - third point of triangle B
- * \param[in] Direction - vector normal to projection plane
- */
- su2double Compute_Triangle_Intersection(su2double* A1, su2double* A2, su2double* A3, su2double* B1, su2double* B2, su2double* B3, su2double* Direction);
-
- /*!
- * \brief For 3-Dimensional grids, compute intersection area between two triangle projected on a given plane
- * P1 from triangle P MUST be inside triangle Q, points order doesn't matter
- * \param[in] P1 - first point of triangle A
- * \param[in] P2 - second point of triangle A
- * \param[in] P3 - third point of triangle A
- * \param[in] Q1 - first point of triangle B
- * \param[in] Q2 - second point of triangle B
- * \param[in] Q3 - third point of triangle B
- */
- su2double ComputeIntersectionArea( su2double* P1, su2double* P2, su2double* P3, su2double* Q1, su2double* Q2, su2double* Q3 );
-
- /*!
- * \brief For 2-Dimensional grids, check whether, and compute, two lines are intersecting
- * \param[in] A1 - first defining first line
- * \param[in] A2 - second defining first line
- * \param[in] B1 - first defining second line
- * \param[in] B2 - second defining second line
- * \param[in] IntersectionPoint - Container for intersection coordinates
- */
- void ComputeLineIntersectionPoint( su2double* A1, su2double* A2, su2double* B1, su2double* B2, su2double* IntersectionPoint );
-
- /*!
- * \brief For N-Dimensional grids, check whether a point is inside a triangle specified by 3 T points
- * \param[in] Point - query point
- * \param[in] T1 - first point of triangle T
- * \param[in] T2 - second point of triangle T
- * \param[in] T3 - third point of triangle T
- */
- bool CheckPointInsideTriangle(su2double* Point, su2double* T1, su2double* T2, su2double* T3);
-};
-
-/*!
- * \brief Radial basis function interpolation
- */
-class CRadialBasisFunction : public CInterpolator {
-public:
-
- /*!
- * \brief Constructor of the class.
- */
- CRadialBasisFunction(void);
-
- /*!
- * \brief Constructor of the class.
- * \param[in] geometry - Geometrical definition of the problem.
- * \param[in] config - Definition of the particular problem.
- * \param[in] iZone - index of the donor zone
- * \param[in] jZone - index of the target zone
- */
- CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone);
-
- /*!
- * \brief Destructor of the class.
- */
- ~CRadialBasisFunction(void);
-
- /*!
- * \brief Set up transfer matrix defining relation between two meshes
- * \param[in] config - Definition of the particular problem.
- */
- void Set_TransferCoeff(CConfig **config);
-
- /*!
- * \brief Compute the value of a radial basis function, this is static so it can be re-used.
- * \param[in] type - of radial basis function
- * \param[in] radius - the characteristic dimension
- * \param[in] dist - distance
- */
- static su2double Get_RadialBasisValue(const short unsigned int type, const su2double &radius, const su2double &dist);
-
-private:
- /*!
- * \brief If the polynomial term is included in the interpolation, and the points lie on a plane, the matrix becomes rank deficient
- * and cannot be inverted. This method detects that condition and corrects it by removing a row from P (the polynomial part of the matrix).
- * \param[in] m - number of rows of P
- * \param[in] n - number of columns of P
- * \param[in] skip_row - marks the row of P which is all ones (by construction)
- * \param[in] max_diff_tol_in - tolerance to detect points are on a plane
- * \param[out] keep_row - marks the rows of P kept
- * \param[out] n_polynomial - size of the polynomial part on exit (i.e. new number of rows)
- * \param[in,out] P - polynomial part of the matrix, may be changed or not!
- */
- void Check_PolynomialTerms(int m, unsigned long n, const int *skip_row, su2double max_diff_tol_in, int *keep_row, int &n_polynomial, su2double *P);
-
-};
-
-/*!
- * \brief Helper class used by CRadialBasisFunction to calculate the interpolation weights.
- * This does not inherit from CSysMatrix because: it is a dense format rather than block sparse;
- * as the interpolation is done on a single core there are no methods for communication.
- * The code can be compiled with LAPACK to use optimized matrix inversion and multiplication routines.
- * CPPFLAGS="-DHAVE_LAPACK" LDFLAGS=-L/path/to/lapack_lib LIBS="-llapack -lrefblas -lgfortran"
- */
-class CSymmetricMatrix{
-
- private:
-
- bool initialized, inversed;
- int sz, num_val;
- int *perm_vec;
- passivedouble *val_vec, *decompose_vec, *inv_val_vec;
-
- enum DecompositionType { none, cholesky, lu };
-
- DecompositionType decomposed;
-
- inline int CalcIdx(int i, int j);
- inline int CalcIdxFull(int i, int j);
- inline void CheckBounds(int i, int j);
-
- passivedouble ReadL(int i, int j);
- passivedouble ReadU(int i, int j);
- passivedouble ReadInv(int i,int j);
-
- // not optimized dense matrix factorization and inversion for portability
- void CholeskyDecompose(bool overwrite);
- void LUDecompose();
- void CalcInv(bool overwrite);
- // matrix inversion using LAPACK routines (LDLT factorization)
- void CalcInv_sptri();
- void CalcInv_potri() {}; // LLT not implemented yet
-
- public:
-
- /*--- Methods ---*/
- CSymmetricMatrix();
- ~CSymmetricMatrix();
-
- void Initialize(int N);
- void Initialize(int N, su2double *formed_val_vec);
-
- inline int GetSize();
-
- void Write(int i, int j, const su2double& val);
- passivedouble Read(int i, int j);
-
- void MatVecMult(passivedouble *v);
- void MatMatMult(bool left_mult, su2double *mat_vec, int N);
- void Invert(const bool is_spd);
-
-};
diff --git a/Common/include/linear_algebra/CSysMatrix.hpp b/Common/include/linear_algebra/CSysMatrix.hpp
index b01be62fe13f..73d803a4fbff 100644
--- a/Common/include/linear_algebra/CSysMatrix.hpp
+++ b/Common/include/linear_algebra/CSysMatrix.hpp
@@ -95,7 +95,7 @@ class CSysMatrix {
const unsigned long *row_ptr; /*!< \brief Pointers to the first element in each row. */
const unsigned long *dia_ptr; /*!< \brief Pointers to the diagonal element in each row. */
const unsigned long *col_ind; /*!< \brief Column index for each of the elements in val(). */
- vector col_ptr;/*!< \brief The transpose of col_ind, pointer to blocks with the same column index. */
+ const unsigned long *col_ptr; /*!< \brief The transpose of col_ind, pointer to blocks with the same column index. */
ScalarType *ILU_matrix; /*!< \brief Entries of the ILU sparse matrix. */
unsigned long nnz_ilu; /*!< \brief Number of possible nonzero entries in the matrix (ILU). */
@@ -349,10 +349,12 @@ class CSysMatrix {
* \param[in] neqn - Number of equations.
* \param[in] geometry - Geometrical definition of the problem.
* \param[in] config - Definition of the particular problem.
+ * \param[in] needTranspPtr - If "col_ptr" should be created.
*/
void Initialize(unsigned long npoint, unsigned long npointdomain,
unsigned short nvar, unsigned short neqn,
- bool EdgeConnect, CGeometry *geometry, CConfig *config);
+ bool EdgeConnect, CGeometry *geometry,
+ CConfig *config, bool needTranspPtr = false);
/*!
* \brief Sets to zero all the entries of the sparse matrix.
@@ -584,11 +586,13 @@ class CSysMatrix {
* \brief Update 2 blocks ij and ji (add to i* sub from j*).
* \note The template parameter Sign, can be used create a "subtractive"
* update i.e. subtract from row i and add to row j instead.
+ * The parameter Overwrite allows completely writing over the
+ * current values held by the matrix.
* \param[in] edge - Index of edge that connects iPoint and jPoint.
* \param[in] block_i - Subs from ji.
* \param[in] block_j - Adds to ij.
*/
- template
+ template
inline void UpdateBlocks(unsigned long iEdge, const OtherType* const* block_i, const OtherType* const* block_j) {
ScalarType *bij = &matrix[edge_ptr(iEdge,0)*nVar*nEqn];
@@ -598,8 +602,8 @@ class CSysMatrix {
for (iVar = 0; iVar < nVar; iVar++) {
for (jVar = 0; jVar < nEqn; jVar++) {
- bij[offset] += PassiveAssign(block_j[iVar][jVar]) * Sign;
- bji[offset] -= PassiveAssign(block_i[iVar][jVar]) * Sign;
+ bij[offset] = (Overwrite? ScalarType(0) : bij[offset]) + PassiveAssign(block_j[iVar][jVar]) * Sign;
+ bji[offset] = (Overwrite? ScalarType(0) : bji[offset]) - PassiveAssign(block_i[iVar][jVar]) * Sign;
++offset;
}
}
@@ -613,6 +617,14 @@ class CSysMatrix {
UpdateBlocks(iEdge, block_i, block_j);
}
+ /*!
+ * \brief Short-hand for the "additive overwrite" version of UpdateBlocks.
+ */
+ template
+ inline void SetBlocks(unsigned long iEdge, const OtherType* const* block_i, const OtherType* const* block_j) {
+ UpdateBlocks(iEdge, block_i, block_j);
+ }
+
/*!
* \brief Adds the specified block to the (i, i) subblock of the matrix-by-blocks structure.
* \param[in] block_i - Diagonal index.
diff --git a/Common/include/omp_structure.hpp b/Common/include/omp_structure.hpp
index 0a4ac692ae92..ad6e1c29d57b 100644
--- a/Common/include/omp_structure.hpp
+++ b/Common/include/omp_structure.hpp
@@ -80,6 +80,19 @@ inline void omp_set_num_threads(int) { }
*/
inline constexpr int omp_get_thread_num(void) {return 0;}
+/*!
+ * \brief Dummy lock type and associated functions.
+ */
+struct omp_lock_t {};
+struct DummyVectorOfLocks {
+ omp_lock_t l;
+ inline omp_lock_t& operator[](int) {return l;}
+};
+inline void omp_init_lock(omp_lock_t*){}
+inline void omp_set_lock(omp_lock_t*){}
+inline void omp_unset_lock(omp_lock_t*){}
+inline void omp_destroy_lock(omp_lock_t*){}
+
#endif
/*--- Convenience macros (do not use excessive nesting of macros). ---*/
@@ -108,6 +121,14 @@ inline constexpr size_t roundUpDiv(size_t numerator, size_t denominator)
return (numerator+denominator-1)/denominator;
}
+/*!
+ * \brief Round up to next multiple.
+ */
+inline constexpr size_t nextMultiple(size_t argument, size_t multiple)
+{
+ return roundUpDiv(argument, multiple) * multiple;
+}
+
/*!
* \brief Compute a chunk size based on totalWork and number of threads such that
* all threads get the same number of chunks (with limited size).
diff --git a/Common/include/option_structure.hpp b/Common/include/option_structure.hpp
index f2a115236b8c..1387b3227064 100644
--- a/Common/include/option_structure.hpp
+++ b/Common/include/option_structure.hpp
@@ -133,6 +133,8 @@ const int SU2_CONN_SIZE = 10; /*!< \brief Size of the connectivity array that
that we read from a mesh file in the format [[globalID vtkType n0 n1 n2 n3 n4 n5 n6 n7 n8]. */
const int SU2_CONN_SKIP = 2; /*!< \brief Offset to skip the globalID and VTK type at the start of the element connectivity list for each CGNS element. */
+const su2double COLORING_EFF_THRESH = 0.875; /*!< \brief Below this value fallback strategies are used instead. */
+
/*!
* \brief Boolean answers
*/
diff --git a/Common/include/toolboxes/C1DInterpolation.hpp b/Common/include/toolboxes/C1DInterpolation.hpp
index a336b33a2448..c5653de37f27 100644
--- a/Common/include/toolboxes/C1DInterpolation.hpp
+++ b/Common/include/toolboxes/C1DInterpolation.hpp
@@ -84,11 +84,6 @@ class CAkimaInterpolation final: public C1DInterpolation{
SetSpline(X,Data);
}
- /*!
- * \brief Destructor of the CAkimaInterpolation class.
- */
- ~CAkimaInterpolation(){}
-
/*!
* \brief for setting the cofficients for the Akima spline.
* \param[in] X - the x values.
@@ -119,11 +114,6 @@ class CLinearInterpolation final: public C1DInterpolation{
SetSpline(X,Data);
}
- /*!
- * \brief Destructor of the CInletInterpolation class.
- */
- ~CLinearInterpolation(){}
-
/*!
* \brief for setting the cofficients for Linear 'spline'.
* \param[in] X - the x values.
diff --git a/Common/include/toolboxes/C2DContainer.hpp b/Common/include/toolboxes/C2DContainer.hpp
index a5c978f2bff3..4b3dfcad14f1 100644
--- a/Common/include/toolboxes/C2DContainer.hpp
+++ b/Common/include/toolboxes/C2DContainer.hpp
@@ -113,15 +113,14 @@ class AccessorImpl
\
AccessorImpl& operator= (AccessorImpl&& other) noexcept \
{ \
- if(m_data!=nullptr) free(m_data); \
+ MemoryAllocation::aligned_free(m_data); \
MOVE; m_data=other.m_data; other.m_data=nullptr; \
return *this; \
} \
\
~AccessorImpl() \
{ \
- if(m_data!=nullptr) \
- MemoryAllocation::aligned_free(m_data); \
+ MemoryAllocation::aligned_free(m_data); \
}
/*!
* Shorthand for when specialization has only one more member than m_data.
@@ -371,6 +370,7 @@ class C2DContainer :
using Base::size;
using Index = Index_t;
using Scalar = Scalar_t;
+ static constexpr StorageType Storage = Store;
private:
/*!
@@ -380,7 +380,7 @@ class C2DContainer :
{
/*--- fully static, no allocation needed ---*/
if(StaticRows!=DynamicSize && StaticCols!=DynamicSize)
- return StaticRows*StaticCols;
+ return StaticRows*StaticCols;
/*--- dynamic row vector, swap size specification ---*/
if(StaticRows==1 && StaticCols==DynamicSize) {cols = rows; rows = 1;}
@@ -399,12 +399,10 @@ class C2DContainer :
/*--- compare with current dimensions to determine if deallocation
is needed, also makes the container safe against self assignment
no need to check for 0 size as the allocators handle that ---*/
- if(m_data!=nullptr)
- {
- if(rows==this->rows() && cols==this->cols())
- return reqSize;
- free(m_data);
- }
+ if(rows==this->rows() && cols==this->cols())
+ return reqSize;
+
+ MemoryAllocation::aligned_free(m_data);
/*--- request actual allocation to base class as it needs specialization ---*/
size_t bytes = reqSize*sizeof(Scalar_t);
diff --git a/Common/include/toolboxes/CSymmetricMatrix.hpp b/Common/include/toolboxes/CSymmetricMatrix.hpp
new file mode 100644
index 000000000000..bc15264c7049
--- /dev/null
+++ b/Common/include/toolboxes/CSymmetricMatrix.hpp
@@ -0,0 +1,87 @@
+/*!
+ * \file CSymmetricMatrix.hpp
+ * \brief Dense symmetric matrix, used for example in RBF interpolation.
+ * \author Joel Ho, P. Gomes
+ * \version 7.0.2 "Blackbird"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md)
+ *
+ * SU2 is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * SU2 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with SU2. If not, see .
+ */
+#pragma once
+
+#include
+#include "C2DContainer.hpp"
+
+using namespace std;
+
+/*!
+ * \brief The matrix is symmetric but full storage is used as that gives much better
+ * performance for some BLAS libraries (notably OpenBLAS). The code should be compiled
+ * with LAPACK to use optimized matrix inversion and multiplication routines.
+ */
+class CSymmetricMatrix {
+ static_assert(su2passivematrix::Storage == StorageType::RowMajor,
+ "Row major storage is assumed for LAPACK.");
+private:
+ su2passivematrix mat;
+
+ // Not optimized dense matrix factorization and inversion for portability.
+ void CalcInv(bool is_spd);
+ void CholeskyDecompose();
+ void LUDecompose(su2passivematrix& decomp, vector& perm) const;
+ // Matrix inversion using LAPACK routines (LDLT and LLT factorization).
+ void CalcInv_sytri();
+ void CalcInv_potri();
+
+public:
+ CSymmetricMatrix() = default;
+ CSymmetricMatrix(int N) {Initialize(N);}
+
+ void Initialize(int N);
+
+ inline int Size() const { return mat.rows(); }
+
+ inline passivedouble Get(int i, int j) const { return mat(min(i,j),max(i,j)); }
+
+ inline void Set(int i, int j, passivedouble val) { mat(min(i,j),max(i,j)) = val; }
+
+ inline passivedouble& operator() (int i, int j) { return mat(min(i,j),max(i,j)); }
+
+ inline const passivedouble& operator() (int i, int j) const { return mat(min(i,j),max(i,j)); }
+
+ template
+ void MatVecMult(ForwardIt vec_in, ForwardIt vec_out) const
+ {
+ for (int i = 0; i < Size(); ++i) {
+ *vec_out = 0.0;
+ auto vec = vec_in;
+ for (int k = 0; k < Size(); ++k)
+ *vec_out += *(vec++) * Get(i,k);
+ ++vec_out;
+ }
+ }
+
+ void MatMatMult(const char side, const su2passivematrix& mat_in, su2passivematrix& mat_out) const;
+
+ void Invert(bool is_spd = false);
+
+ su2passivematrix StealData();
+
+};
diff --git a/Common/include/toolboxes/graph_toolbox.hpp b/Common/include/toolboxes/graph_toolbox.hpp
index 5f5ea4cdf5bc..e87d48402f9a 100644
--- a/Common/include/toolboxes/graph_toolbox.hpp
+++ b/Common/include/toolboxes/graph_toolbox.hpp
@@ -28,6 +28,7 @@
#pragma once
#include "C2DContainer.hpp"
+#include "../omp_structure.hpp"
#include
#include
@@ -59,6 +60,7 @@ class CCompressedSparsePattern {
su2vector m_outerPtr; /*!< \brief Start positions of the inner indices for each outer index. */
su2vector m_innerIdx; /*!< \brief Inner indices of the non zero entries. */
su2vector m_diagPtr; /*!< \brief Position of the diagonal entry. */
+ su2vector m_innerIdxTransp; /*!< \brief Position of the transpose non zero entries, requires symmetry. */
public:
using IndexType = Index_t;
@@ -107,10 +109,30 @@ class CCompressedSparsePattern {
if(!m_diagPtr.empty()) return;
m_diagPtr.resize(getOuterSize());
+
+ SU2_OMP_PARALLEL_(for schedule(static,roundUpDiv(getOuterSize(),omp_get_max_threads())))
for(Index_t k = 0; k < getOuterSize(); ++k)
m_diagPtr(k) = findInnerIdx(k,k);
}
+ /*!
+ * \brief Build a list of pointers to the transpose entries of the pattern, requires symmetry.
+ */
+ void buildTransposePtr() {
+ if(!m_innerIdxTransp.empty()) return;
+
+ m_innerIdxTransp.resize(getNumNonZeros());
+
+ SU2_OMP_PARALLEL_(for schedule(static,roundUpDiv(getOuterSize(),omp_get_max_threads())))
+ for(Index_t i = 0; i < getOuterSize(); ++i) {
+ for(Index_t k = m_outerPtr(i); k < m_outerPtr(i+1); ++k) {
+ auto j = m_innerIdx(k);
+ m_innerIdxTransp(k) = findInnerIdx(j,i);
+ assert(m_innerIdxTransp(k) != m_innerIdx.size() && "The pattern is not symmetric.");
+ }
+ }
+ }
+
/*!
* \return True if the pattern is empty, i.e. has not been built yet.
*/
@@ -224,6 +246,14 @@ class CCompressedSparsePattern {
return m_diagPtr.data();
}
+ /*!
+ * \return Raw pointer to the transpose pointer vector.
+ */
+ inline const su2vector& transposePtr() const {
+ assert(!m_innerIdxTransp.empty() && "Transpose map has not been built.");
+ return m_innerIdxTransp;
+ }
+
/*!
* \return The minimum inner index.
*/
@@ -384,6 +414,30 @@ CEdgeToNonZeroMap mapEdgesToSparsePattern(Geometry_t& geometry,
}
+/*!
+ * \brief Create the natural coloring (equivalent to the normal sequential loop
+ * order) for a given number of inner indexes.
+ * \note This is to reduce overhead in "OpenMP-ready" code when only 1 thread is used.
+ * \param[in] numInnerIndexes - Number of indexes that are to be colored.
+ * \return Natural (sequential) coloring of the inner indices.
+ */
+template
+T createNaturalColoring(Index_t numInnerIndexes)
+{
+ /*--- One color. ---*/
+ su2vector outerPtr(2);
+ outerPtr(0) = 0;
+ outerPtr(1) = numInnerIndexes;
+
+ /*--- Containing all indexes in ascending order. ---*/
+ su2vector innerIdx(numInnerIndexes);
+ std::iota(innerIdx.data(), innerIdx.data()+numInnerIndexes, 0);
+
+ return T(std::move(outerPtr), std::move(innerIdx));
+}
+
+
/*!
* \brief Color contiguous groups of outer indices of a sparse pattern such that
* within each color, any two groups do not have inner indices in common.
@@ -404,7 +458,7 @@ CEdgeToNonZeroMap mapEdgesToSparsePattern(Geometry_t& geometry,
* \param[out] indexColor - Optional, vector with colors given to the outer indices.
* \return Coloring in the same type of the input pattern.
*/
-template
+template
T colorSparsePattern(const T& pattern, size_t groupSize = 1, bool balanceColors = false,
std::vector* indexColor = nullptr)
{
@@ -415,6 +469,10 @@ T colorSparsePattern(const T& pattern, size_t groupSize = 1, bool balanceColors
const Index_t grpSz = groupSize;
const Index_t nOuter = pattern.getOuterSize();
+
+ /*--- Trivial case. ---*/
+ if(groupSize >= nOuter) return createNaturalColoring(nOuter);
+
const Index_t minIdx = pattern.getMinInnerIdx();
const Index_t nInner = pattern.getMaxInnerIdx()+1-minIdx;
@@ -520,30 +578,6 @@ T colorSparsePattern(const T& pattern, size_t groupSize = 1, bool balanceColors
}
-/*!
- * \brief Create the natural coloring (equivalent to the normal sequential loop
- * order) for a given number of inner indexes.
- * \note This is to reduce overhead in "OpenMP-ready" code when only 1 thread is used.
- * \param[in] numInnerIndexes - Number of indexes that are to be colored.
- * \return Natural (sequential) coloring of the inner indices.
- */
-template
-T createNaturalColoring(Index_t numInnerIndexes)
-{
- /*--- One color. ---*/
- su2vector outerPtr(2);
- outerPtr(0) = 0;
- outerPtr(1) = numInnerIndexes;
-
- /*--- Containing all indexes in ascending order. ---*/
- su2vector innerIdx(numInnerIndexes);
- std::iota(innerIdx.data(), innerIdx.data()+numInnerIndexes, 0);
-
- return T(std::move(outerPtr), std::move(innerIdx));
-}
-
-
/*!
* \brief A way to represent one grid color that allows range-for syntax.
*/
@@ -553,9 +587,11 @@ struct GridColor
static_assert(std::is_integral::value,"");
const T size;
+ T groupSize;
const T* const indices;
- GridColor(const T* idx = nullptr, T sz = 0) : size(sz), indices(idx) { }
+ GridColor(const T* idx = nullptr, T sz = 0, T grp = 0) :
+ size(sz), groupSize(grp), indices(idx) { }
inline const T* begin() const {return indices;}
inline const T* end() const {return indices+size;}
@@ -592,3 +628,23 @@ struct DummyGridColor
inline IteratorLikeInt begin() const {return IteratorLikeInt(0);}
inline IteratorLikeInt end() const {return IteratorLikeInt(size);}
};
+
+
+/*!
+ * \brief Computes the efficiency of a grid coloring for given number of threads and chunk size.
+ */
+template
+su2double coloringEfficiency(const SparsePattern& coloring, int numThreads, int chunkSize)
+{
+ using Index_t = typename SparsePattern::IndexType;
+
+ /*--- Ideally compute time is proportional to total work over number of threads. ---*/
+ su2double ideal = coloring.getNumNonZeros() / su2double(numThreads);
+
+ /*--- In practice the total work is quantized first by colors and then by chunks. ---*/
+ Index_t real = 0;
+ for(Index_t color = 0; color < coloring.getOuterSize(); ++color)
+ real += chunkSize * roundUpDiv(roundUpDiv(coloring.getNumNonZeros(color), chunkSize), numThreads);
+
+ return ideal / real;
+}
diff --git a/Common/lib/Makefile.am b/Common/lib/Makefile.am
index 4ef40da28d37..f1a6f0d1b11f 100644
--- a/Common/lib/Makefile.am
+++ b/Common/lib/Makefile.am
@@ -93,12 +93,18 @@ lib_sources = \
../src/geometry/primal_grid/CTetrahedron.cpp \
../src/geometry/primal_grid/CQuadrilateral.cpp \
../src/geometry/primal_grid/CVertexMPI.cpp \
- ../src/interpolation_structure.cpp \
+ ../src/interface_interpolation/CInterpolator.cpp \
+ ../src/interface_interpolation/CMirror.cpp \
+ ../src/interface_interpolation/CSlidingMesh.cpp \
+ ../src/interface_interpolation/CIsoparametric.cpp \
+ ../src/interface_interpolation/CNearestNeighbor.cpp \
+ ../src/interface_interpolation/CRadialBasisFunction.cpp \
../src/adt_structure.cpp \
../src/wall_model.cpp \
../src/toolboxes/printing_toolbox.cpp \
../src/toolboxes/CLinearPartitioner.cpp \
../src/toolboxes/C1DInterpolation.cpp \
+ ../src/toolboxes/CSymmetricMatrix.cpp \
../src/toolboxes/MMS/CVerificationSolution.cpp \
../src/toolboxes/MMS/CIncTGVSolution.cpp \
../src/toolboxes/MMS/CInviscidVortexSolution.cpp \
diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp
index 283c0768abd5..cf65fc655c53 100644
--- a/Common/src/CConfig.cpp
+++ b/Common/src/CConfig.cpp
@@ -935,7 +935,7 @@ void CConfig::SetPointersNull(void) {
Kind_SurfaceMovement = NULL;
LocationStations = NULL;
Motion_Origin = NULL;
- Translation_Rate = NULL;
+ Translation_Rate = NULL;
Rotation_Rate = NULL;
Pitching_Omega = NULL;
Pitching_Ampl = NULL;
@@ -943,7 +943,7 @@ void CConfig::SetPointersNull(void) {
Plunging_Omega = NULL;
Plunging_Ampl = NULL;
MarkerMotion_Origin = NULL;
- MarkerTranslation_Rate = NULL;
+ MarkerTranslation_Rate = NULL;
MarkerRotation_Rate = NULL;
MarkerPitching_Omega = NULL;
MarkerPitching_Ampl = NULL;
@@ -2457,17 +2457,17 @@ void CConfig::SetConfig_Options() {
addBoolOption("WRT_FORCES_BREAKDOWN", Wrt_ForcesBreakdown, false);
-
- /* DESCRIPTION: Use conservative approach for interpolating between meshes.
- * Options: NO, YES \ingroup Config */
- addBoolOption("CONSERVATIVE_INTERPOLATION", ConservativeInterpolation, true);
-
/*!\par KIND_INTERPOLATION \n
* DESCRIPTION: Type of interpolation to use for multi-zone problems. \n OPTIONS: see \link Interpolator_Map \endlink
* Sets Kind_Interpolation \ingroup Config
*/
addEnumOption("KIND_INTERPOLATION", Kind_Interpolation, Interpolator_Map, NEAREST_NEIGHBOR);
+ /* DESCRIPTION: Use conservative approach for interpolating between meshes. */
+ addBoolOption("CONSERVATIVE_INTERPOLATION", ConservativeInterpolation, true);
+
+ addUnsignedShortOption("NUM_NEAREST_NEIGHBORS", NumNearestNeighbors, 1);
+
/*!\par KIND_INTERPOLATION \n
* DESCRIPTION: Type of radial basis function to use for radial basis function interpolation. \n OPTIONS: see \link RadialBasis_Map \endlink
* Sets Kind_RadialBasis \ingroup Config
@@ -2478,8 +2478,11 @@ void CConfig::SetConfig_Options() {
* Options: NO, YES \ingroup Config */
addBoolOption("RADIAL_BASIS_FUNCTION_POLYNOMIAL_TERM", RadialBasisFunction_PolynomialOption, true);
- /* DESCRIPTION: Radius for radial basis function */
- addDoubleOption("RADIAL_BASIS_FUNCTION_PARAMETER", RadialBasisFunction_Parameter, 1);
+ /* DESCRIPTION: Radius for radial basis function. */
+ addDoubleOption("RADIAL_BASIS_FUNCTION_PARAMETER", RadialBasisFunction_Parameter, 1.0);
+
+ /* DESCRIPTION: Tolerance to prune small coefficients from the RBF interpolation matrix. */
+ addDoubleOption("RADIAL_BASIS_FUNCTION_PRUNE_TOLERANCE", RadialBasisFunction_PruneTol, 1e-6);
/*!\par INLETINTERPOLATION \n
* DESCRIPTION: Type of spanwise interpolation to use for the inlet face. \n OPTIONS: see \link Inlet_SpanwiseInterpolation_Map \endlink
@@ -2807,7 +2810,7 @@ void CConfig::SetConfig_Options() {
/* DESCRIPTION: Level of fill for PaStiX incomplete LU factorization. */
addUnsignedShortOption("PASTIX_FILL_LEVEL", pastix_fill_lvl, 1);
- /* DESCRIPTION: Size of the edge groups colored for OpenMP parallelization of edge loops. */
+ /* DESCRIPTION: Size of the edge groups colored for thread parallel edge loops (0 forces the reducer strategy). */
addUnsignedLongOption("EDGE_COLORING_GROUP_SIZE", edgeColorGroupSize, 512);
/* END_CONFIG_OPTIONS */
@@ -3677,96 +3680,96 @@ void CConfig::SetPostprocessing(unsigned short val_software, unsigned short val_
for (iMarker = 0; iMarker < nMarker_Moving; iMarker++){
for (iDim = 0; iDim < 3; iDim++){
MarkerMotion_Origin[3*iMarker+iDim] = 0.0;
- }
- }
+ }
+ }
}
if (nMarkerMotion_Origin/3 != nMarker_Moving){
SU2_MPI::Error("Number of SURFACE_MOTION_ORIGIN must be three times the number of MARKER_MOVING, (x,y,z) per marker.", CURRENT_FUNCTION);
- }
+ }
if (nMarkerTranslation == 0){
nMarkerTranslation = 3*nMarker_Moving;
MarkerTranslation_Rate = new su2double[nMarkerTranslation];
for (iMarker = 0; iMarker < nMarker_Moving; iMarker++){
for (iDim = 0; iDim < 3; iDim++){
MarkerTranslation_Rate[3*iMarker+iDim] = 0.0;
- }
- }
+ }
+ }
}
if (nMarkerTranslation/3 != nMarker_Moving){
SU2_MPI::Error("Number of SURFACE_TRANSLATION_RATE must be three times the number of MARKER_MOVING, (x,y,z) per marker.", CURRENT_FUNCTION);
- }
+ }
if (nMarkerRotation_Rate == 0){
nMarkerRotation_Rate = 3*nMarker_Moving;
MarkerRotation_Rate = new su2double[nMarkerRotation_Rate];
for (iMarker = 0; iMarker < nMarker_Moving; iMarker++){
for (iDim = 0; iDim < 3; iDim++){
MarkerRotation_Rate[3*iMarker+iDim] = 0.0;
- }
- }
+ }
+ }
}
if (nMarkerRotation_Rate/3 != nMarker_Moving){
SU2_MPI::Error("Number of SURFACE_ROTATION_RATE must be three times the number of MARKER_MOVING, (x,y,z) per marker.", CURRENT_FUNCTION);
- }
+ }
if (nMarkerPlunging_Ampl == 0){
nMarkerPlunging_Ampl = 3*nMarker_Moving;
MarkerPlunging_Ampl = new su2double[nMarkerPlunging_Ampl];
for (iMarker = 0; iMarker < nMarker_Moving; iMarker++){
for (iDim = 0; iDim < 3; iDim++){
MarkerPlunging_Ampl[3*iMarker+iDim] = 0.0;
- }
- }
+ }
+ }
}
if (nMarkerPlunging_Ampl/3 != nMarker_Moving){
SU2_MPI::Error("Number of SURFACE_PLUNGING_AMPL must be three times the number of MARKER_MOVING, (x,y,z) per marker.", CURRENT_FUNCTION);
- }
+ }
if (nMarkerPlunging_Omega == 0){
nMarkerPlunging_Omega = 3*nMarker_Moving;
MarkerPlunging_Omega = new su2double[nMarkerPlunging_Omega];
for (iMarker = 0; iMarker < nMarker_Moving; iMarker++){
for (iDim = 0; iDim < 3; iDim++){
MarkerPlunging_Omega[3*iMarker+iDim] = 0.0;
- }
- }
+ }
+ }
}
if (nMarkerPlunging_Omega/3 != nMarker_Moving){
SU2_MPI::Error("Number of SURFACE_PLUNGING_OMEGA must be three times the number of MARKER_MOVING, (x,y,z) per marker.", CURRENT_FUNCTION);
- }
+ }
if (nMarkerPitching_Ampl == 0){
nMarkerPitching_Ampl = 3*nMarker_Moving;
MarkerPitching_Ampl = new su2double[nMarkerPitching_Ampl];
for (iMarker = 0; iMarker < nMarker_Moving; iMarker++){
for (iDim = 0; iDim < 3; iDim++){
MarkerPitching_Ampl[3*iMarker+iDim] = 0.0;
- }
- }
+ }
+ }
}
if (nMarkerPitching_Ampl/3 != nMarker_Moving){
SU2_MPI::Error("Number of SURFACE_PITCHING_AMPL must be three times the number of MARKER_MOVING, (x,y,z) per marker.", CURRENT_FUNCTION);
- }
+ }
if (nMarkerPitching_Omega == 0){
nMarkerPitching_Omega = 3*nMarker_Moving;
MarkerPitching_Omega = new su2double[nMarkerPitching_Omega];
for (iMarker = 0; iMarker < nMarker_Moving; iMarker++){
for (iDim = 0; iDim < 3; iDim++){
MarkerPitching_Omega[3*iMarker+iDim] = 0.0;
- }
- }
+ }
+ }
}
if (nMarkerPitching_Omega/3 != nMarker_Moving){
SU2_MPI::Error("Number of SURFACE_PITCHING_OMEGA must be three times the number of MARKER_MOVING, (x,y,z) per marker.", CURRENT_FUNCTION);
- }
+ }
if (nMarkerPitching_Phase == 0){
nMarkerPitching_Phase = 3*nMarker_Moving;
MarkerPitching_Phase = new su2double[nMarkerPitching_Phase];
for (iMarker = 0; iMarker < nMarker_Moving; iMarker++){
for (iDim = 0; iDim < 3; iDim++){
MarkerPitching_Phase[3*iMarker+iDim] = 0.0;
- }
- }
+ }
+ }
}
if (nMarkerPitching_Phase/3 != nMarker_Moving){
SU2_MPI::Error("Number of SURFACE_PITCHING_PHASE must be three times the number of MARKER_MOVING, (x,y,z) per marker.", CURRENT_FUNCTION);
- }
+ }
if (nMoveMotion_Origin == 0){
nMoveMotion_Origin = nMarker_Moving;
@@ -3824,11 +3827,10 @@ void CConfig::SetPostprocessing(unsigned short val_software, unsigned short val_
/*--- Set number of TurboPerformance markers ---*/
if(GetGrid_Movement() && RampRotatingFrame && !DiscreteAdjoint){
- FinalRotation_Rate_Z = Rotation_Rate[2];
- if(abs(FinalRotation_Rate_Z) > 0.0){
- Rotation_Rate[2] = RampRotatingFrame_Coeff[0];
- }
-
+ FinalRotation_Rate_Z = Rotation_Rate[2];
+ if(abs(FinalRotation_Rate_Z) > 0.0){
+ Rotation_Rate[2] = RampRotatingFrame_Coeff[0];
+ }
}
if(RampOutletPressure && !DiscreteAdjoint){
@@ -4289,39 +4291,39 @@ void CConfig::SetPostprocessing(unsigned short val_software, unsigned short val_
}
if (nElasticityMod == 0) {
- nElasticityMod = 1;
- ElasticityMod = new su2double[1]; ElasticityMod[0] = 2E11;
+ nElasticityMod = 1;
+ ElasticityMod = new su2double[1]; ElasticityMod[0] = 2E11;
}
if (nPoissonRatio == 0) {
- nPoissonRatio = 1;
- PoissonRatio = new su2double[1]; PoissonRatio[0] = 0.30;
+ nPoissonRatio = 1;
+ PoissonRatio = new su2double[1]; PoissonRatio[0] = 0.30;
}
if (nMaterialDensity == 0) {
- nMaterialDensity = 1;
- MaterialDensity = new su2double[1]; MaterialDensity[0] = 7854;
+ nMaterialDensity = 1;
+ MaterialDensity = new su2double[1]; MaterialDensity[0] = 7854;
}
if (nElectric_Constant == 0) {
- nElectric_Constant = 1;
- Electric_Constant = new su2double[1]; Electric_Constant[0] = 0.0;
+ nElectric_Constant = 1;
+ Electric_Constant = new su2double[1]; Electric_Constant[0] = 0.0;
}
if (nElectric_Field == 0) {
- nElectric_Field = 1;
- Electric_Field_Mod = new su2double[1]; Electric_Field_Mod[0] = 0.0;
+ nElectric_Field = 1;
+ Electric_Field_Mod = new su2double[1]; Electric_Field_Mod[0] = 0.0;
}
if (nDim_RefNode == 0) {
- nDim_RefNode = 3;
- RefNode_Displacement = new su2double[3];
- RefNode_Displacement[0] = 0.0; RefNode_Displacement[1] = 0.0; RefNode_Displacement[2] = 0.0;
+ nDim_RefNode = 3;
+ RefNode_Displacement = new su2double[3];
+ RefNode_Displacement[0] = 0.0; RefNode_Displacement[1] = 0.0; RefNode_Displacement[2] = 0.0;
}
if (nDim_Electric_Field == 0) {
- nDim_Electric_Field = 2;
- Electric_Field_Dir = new su2double[2]; Electric_Field_Dir[0] = 0.0; Electric_Field_Dir[1] = 1.0;
+ nDim_Electric_Field = 2;
+ Electric_Field_Dir = new su2double[2]; Electric_Field_Dir[0] = 0.0; Electric_Field_Dir[1] = 1.0;
}
if ((Kind_SU2 == SU2_CFD) && (Kind_Solver == NO_SOLVER)) {
@@ -4920,6 +4922,9 @@ void CConfig::SetPostprocessing(unsigned short val_software, unsigned short val_
CURRENT_FUNCTION);
}
+ /*--- 0 in the config file means "disable" which can be done using a very large group. ---*/
+ if (edgeColorGroupSize==0) edgeColorGroupSize = 1<<30;
+
}
void CConfig::SetMarkers(unsigned short val_software) {
diff --git a/Common/src/geometry/CGeometry.cpp b/Common/src/geometry/CGeometry.cpp
index 05af0f1c15c0..a7a502b30b63 100644
--- a/Common/src/geometry/CGeometry.cpp
+++ b/Common/src/geometry/CGeometry.cpp
@@ -4036,13 +4036,26 @@ const CEdgeToNonZeroMapUL& CGeometry::GetEdgeToSparsePatternMap(void)
return edgeToCSRMap;
}
-const CCompressedSparsePatternUL& CGeometry::GetEdgeColoring(void)
+const su2vector& CGeometry::GetTransposeSparsePatternMap(ConnectivityType type)
{
- if (edgeColoring.empty() && nEdge) {
+ /*--- Yes the const cast is weird but it is still better than repeating code. ---*/
+ auto& pattern = const_cast(GetSparsePattern(type));
+ pattern.buildTransposePtr();
+ return pattern.transposePtr();
+}
+
+const CCompressedSparsePatternUL& CGeometry::GetEdgeColoring(su2double* efficiency)
+{
+ /*--- Check for dry run mode with dummy geometry. ---*/
+ if (nEdge==0) return edgeColoring;
+
+ /*--- Build if required. ---*/
+ if (edgeColoring.empty()) {
- /*--- When not using threading, and for coarse grids, use the natural coloring. ---*/
- if ((omp_get_max_threads() == 1) || (MGLevel != MESH_0)) {
- edgeColoring = createNaturalColoring(nEdge);
+ /*--- When not using threading use the natural coloring to reduce overhead. ---*/
+ if (omp_get_max_threads() == 1) {
+ SetNaturalEdgeColoring();
+ if (efficiency != nullptr) *efficiency = 1.0; // by definition
return edgeColoring;
}
@@ -4051,7 +4064,7 @@ const CCompressedSparsePatternUL& CGeometry::GetEdgeColoring(void)
su2vector outerPtr(nEdge+1);
su2vector innerIdx(nEdge*2);
- for(unsigned long iEdge = 0; iEdge < nEdge; ++iEdge) {
+ for (unsigned long iEdge = 0; iEdge < nEdge; ++iEdge) {
outerPtr(iEdge) = 2*iEdge;
innerIdx(iEdge*2+0) = edge[iEdge]->GetNode(0);
innerIdx(iEdge*2+1) = edge[iEdge]->GetNode(1);
@@ -4060,23 +4073,42 @@ const CCompressedSparsePatternUL& CGeometry::GetEdgeColoring(void)
CCompressedSparsePatternUL pattern(move(outerPtr), move(innerIdx));
- /*--- Color the edges, only balance sizes on coarse levels. ---*/
- bool balanceColors = (MGLevel != MESH_0);
+ /*--- Color the edges. ---*/
+ constexpr bool balanceColors = true;
edgeColoring = colorSparsePattern(pattern, edgeColorGroupSize, balanceColors);
- if(edgeColoring.empty())
- SU2_MPI::Error("Edge coloring failed.", CURRENT_FUNCTION);
+ /*--- If the coloring fails use the natural coloring. This is a
+ * "soft" failure as this "bad" coloring should be detected
+ * downstream and a fallback strategy put in place. ---*/
+ if (edgeColoring.empty()) SetNaturalEdgeColoring();
+ }
+
+ if (efficiency != nullptr) {
+ *efficiency = coloringEfficiency(edgeColoring, omp_get_max_threads(), edgeColorGroupSize);
}
return edgeColoring;
}
-const CCompressedSparsePatternUL& CGeometry::GetElementColoring(void)
+void CGeometry::SetNaturalEdgeColoring()
+{
+ if (nEdge == 0) return;
+ edgeColoring = createNaturalColoring(nEdge);
+ /*--- In parallel, set the group size to nEdge to protect client code. ---*/
+ if (omp_get_max_threads() > 1) edgeColorGroupSize = nEdge;
+}
+
+const CCompressedSparsePatternUL& CGeometry::GetElementColoring(su2double* efficiency)
{
- if (elemColoring.empty() && nElem) {
+ /*--- Check for dry run mode with dummy geometry. ---*/
+ if (nElem==0) return elemColoring;
+
+ /*--- Build if required. ---*/
+ if (elemColoring.empty()) {
/*--- When not using threading use the natural coloring. ---*/
if (omp_get_max_threads() == 1) {
- elemColoring = createNaturalColoring(nElem);
+ SetNaturalElementColoring();
+ if (efficiency != nullptr) *efficiency = 1.0; // by definition
return elemColoring;
}
@@ -4085,10 +4117,10 @@ const CCompressedSparsePatternUL& CGeometry::GetElementColoring(void)
vector outerPtr(nElem+1);
vector innerIdx; innerIdx.reserve(nElem);
- for(unsigned long iElem = 0; iElem < nElem; ++iElem) {
+ for (unsigned long iElem = 0; iElem < nElem; ++iElem) {
outerPtr[iElem] = innerIdx.size();
- for(unsigned short iNode = 0; iNode < elem[iElem]->GetnNodes(); ++iNode) {
+ for (unsigned short iNode = 0; iNode < elem[iElem]->GetnNodes(); ++iNode) {
innerIdx.push_back(elem[iElem]->GetNode(iNode));
}
}
@@ -4097,10 +4129,23 @@ const CCompressedSparsePatternUL& CGeometry::GetElementColoring(void)
CCompressedSparsePatternUL pattern(outerPtr, innerIdx);
/*--- Color the elements. ---*/
- elemColoring = colorSparsePattern(pattern, elemColorGroupSize);
+ constexpr bool balanceColors = true;
+ elemColoring = colorSparsePattern(pattern, elemColorGroupSize, balanceColors);
+
+ /*--- Same as for the edge coloring. ---*/
+ if (elemColoring.empty()) SetNaturalElementColoring();
+ }
- if(elemColoring.empty())
- SU2_MPI::Error("Element coloring failed.", CURRENT_FUNCTION);
+ if (efficiency != nullptr) {
+ *efficiency = coloringEfficiency(elemColoring, omp_get_max_threads(), elemColorGroupSize);
}
return elemColoring;
}
+
+void CGeometry::SetNaturalElementColoring()
+{
+ if (nElem == 0) return;
+ elemColoring = createNaturalColoring(nElem);
+ /*--- In parallel, set the group size to nElem to protect client code. ---*/
+ if (omp_get_max_threads() > 1) elemColorGroupSize = nElem;
+}
diff --git a/Common/src/geometry/CMultiGridGeometry.cpp b/Common/src/geometry/CMultiGridGeometry.cpp
index 05746f165dc4..830d49b4fdcd 100644
--- a/Common/src/geometry/CMultiGridGeometry.cpp
+++ b/Common/src/geometry/CMultiGridGeometry.cpp
@@ -624,13 +624,8 @@ CMultiGridGeometry::CMultiGridGeometry(CGeometry **geometry, CConfig *config_con
Local_nPointCoarse = nPoint;
Local_nPointFine = fine_grid->GetnPoint();
-#ifdef HAVE_MPI
SU2_MPI::Allreduce(&Local_nPointCoarse, &Global_nPointCoarse, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD);
SU2_MPI::Allreduce(&Local_nPointFine, &Global_nPointFine, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD);
-#else
- Global_nPointCoarse = Local_nPointCoarse;
- Global_nPointFine = Local_nPointFine;
-#endif
su2double Coeff = 1.0, CFL = 0.0, factor = 1.5;
@@ -670,6 +665,8 @@ CMultiGridGeometry::CMultiGridGeometry(CGeometry **geometry, CConfig *config_con
}
}
+ edgeColorGroupSize = config->GetEdgeColoringGroupSize();
+
delete [] copy_marker;
}
diff --git a/Common/src/geometry/CPhysicalGeometry.cpp b/Common/src/geometry/CPhysicalGeometry.cpp
index cf4375463fb8..8f8ea74fe17a 100644
--- a/Common/src/geometry/CPhysicalGeometry.cpp
+++ b/Common/src/geometry/CPhysicalGeometry.cpp
@@ -468,15 +468,11 @@ CPhysicalGeometry::CPhysicalGeometry(CGeometry *geometry,
nLocal_Pris +
nLocal_Pyra);
nLocal_Bound_Elem = nLocal_Line + nLocal_BoundTria + nLocal_BoundQuad;
-#ifndef HAVE_MPI
- nGlobal_Elem = nLocal_Elem;
- nGlobal_Bound_Elem = nLocal_Bound_Elem;
-#else
+
SU2_MPI::Allreduce(&nLocal_Elem, &nGlobal_Elem, 1,
MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD);
SU2_MPI::Allreduce(&nLocal_Bound_Elem, &nGlobal_Bound_Elem, 1,
MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD);
-#endif
/*--- With the distribution of all points, elements, and markers based
on the ParMETIS coloring complete, as a final step, load this data into
diff --git a/Common/src/geometry/dual_grid/CVertex.cpp b/Common/src/geometry/dual_grid/CVertex.cpp
index 4cf14bf19f39..a65c22e63460 100644
--- a/Common/src/geometry/dual_grid/CVertex.cpp
+++ b/Common/src/geometry/dual_grid/CVertex.cpp
@@ -40,52 +40,35 @@ CVertex::CVertex(unsigned long val_point, unsigned short val_nDim) : CDualGrid(v
ActDisk_Perimeter = false;
- /*--- Pointers initialization ---*/
-
- Nodes = NULL;
- Normal = NULL;
-
- /*--- Allocate node, and face normal ---*/
-
- Nodes = new unsigned long[1];
- Normal = new su2double [nDim];
-
/*--- Initializate the structure ---*/
Nodes[0] = val_point;
- for (iDim = 0; iDim < nDim; iDim ++)
- Normal[iDim] = 0.0;
+
+ for (iDim = 0; iDim < 3; iDim ++) Normal[iDim] = 0.0;
/*--- Set to zero the variation of the coordinates ---*/
- VarCoord[0] = 0.0;
- VarCoord[1] = 0.0;
- VarCoord[2] = 0.0;
+ for (iDim = 0; iDim < 3; iDim ++) VarCoord[iDim] = 0.0;
- /*--- Set to NULL variation of the rotation ---*/
+ /*--- Set to nullptr variation of the rotation ---*/
- VarRot = NULL;
+ VarRot = nullptr;
- /*--- Set to NULL donor arrays for interpolation ---*/
+ /*--- Set to nullptr donor arrays for interpolation ---*/
- Donor_Points = NULL;
- Donor_Proc = NULL;
- Donor_Coeff = NULL;
+ Donor_Points = nullptr;
+ Donor_Proc = nullptr;
+ Donor_Coeff = nullptr;
nDonor_Points = 1;
}
CVertex::~CVertex() {
- if (Normal != NULL) delete[] Normal;
- if (Nodes != NULL) delete[] Nodes;
-
- /*--- donor arrays for interpolation ---*/
-
- if (VarRot != NULL) delete[] VarRot;
- if (Donor_Coeff != NULL) delete[] Donor_Coeff;
- if (Donor_Proc != NULL) delete[] Donor_Proc;
- if (Donor_Points != NULL) delete[] Donor_Points;
+ delete[] VarRot;
+ delete[] Donor_Coeff;
+ delete[] Donor_Proc;
+ delete[] Donor_Points;
}
@@ -131,13 +114,16 @@ void CVertex::SetNodes_Coord(su2double *val_coord_Edge_CG, su2double *val_coord_
}
-void CVertex::Allocate_DonorInfo(void){
+void CVertex::Allocate_DonorInfo(unsigned short nDonor) {
+
+ nDonor_Points = nDonor;
+
+ delete [] Donor_Points;
+ delete [] Donor_Proc;
+ delete [] Donor_Coeff;
- if( Donor_Points != NULL ) delete [] Donor_Points;
- if( Donor_Proc != NULL ) delete [] Donor_Proc;
- if( Donor_Coeff != NULL ) delete [] Donor_Coeff;
+ Donor_Points = new unsigned long [nDonor_Points];
+ Donor_Proc = new unsigned long [nDonor_Points];
+ Donor_Coeff = new su2double [nDonor_Points];
- Donor_Points = new unsigned long[nDonor_Points];
- Donor_Proc = new unsigned long[nDonor_Points];
- Donor_Coeff = new su2double[nDonor_Points];
}
diff --git a/Common/src/interface_interpolation/CInterpolator.cpp b/Common/src/interface_interpolation/CInterpolator.cpp
new file mode 100644
index 000000000000..2c2c0089bb9e
--- /dev/null
+++ b/Common/src/interface_interpolation/CInterpolator.cpp
@@ -0,0 +1,416 @@
+/*!
+ * \file CInterpolator.cpp
+ * \brief Definition of the base class for interface interpolation.
+ * \author H. Kline
+ * \version 7.0.2 "Blackbird"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md)
+ *
+ * SU2 is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * SU2 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with SU2. If not, see .
+ */
+
+#include "../../include/interface_interpolation/CInterpolator.hpp"
+#include "../../include/CConfig.hpp"
+#include "../../include/geometry/CGeometry.hpp"
+
+
+CInterpolator::CInterpolator(CGeometry ****geometry_container, const CConfig* const* config,
+ unsigned int iZone, unsigned int jZone) :
+ rank(SU2_MPI::GetRank()),
+ size(SU2_MPI::GetSize()),
+ donorZone(iZone),
+ targetZone(jZone),
+ Geometry(geometry_container),
+ donor_geometry(geometry_container[iZone][INST_0][MESH_0]),
+ target_geometry(geometry_container[jZone][INST_0][MESH_0]) {
+}
+
+int CInterpolator::Find_InterfaceMarker(const CConfig *config, unsigned short val_marker_interface) {
+
+ for (unsigned short iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) {
+ /*--- If the tag GetMarker_All_ZoneInterface(iMarker) equals the interface we are looking for. ---*/
+ if (config->GetMarker_All_ZoneInterface(iMarker) == val_marker_interface) return iMarker;
+ }
+ return -1;
+}
+
+bool CInterpolator::CheckInterfaceBoundary(int markDonor, int markTarget) {
+
+ /*--- Determine whether the boundary is not on the rank because of
+ * the partition or because it is not part of the zone. ---*/
+ int donorCheck = -1, targetCheck = -1;
+ SU2_MPI::Allreduce(&markDonor, &donorCheck, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD);
+ SU2_MPI::Allreduce(&markTarget, &targetCheck, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD);
+ return (donorCheck != -1) && (targetCheck != -1);
+}
+
+void CInterpolator::Determine_ArraySize(bool faces, int markDonor, int markTarget,
+ unsigned long nVertexDonor, unsigned short nDim) {
+
+ unsigned long nLocalVertex_Donor = 0, nLocalFaceNodes_Donor=0, nLocalFace_Donor=0;
+ unsigned long iVertex, iPointDonor = 0;
+ /* Only needed if face data is also collected */
+ unsigned long inode;
+ unsigned long donor_elem, jElem, jPoint;
+ unsigned short iDonor;
+ unsigned int nFaces=0, iFace, nNodes=0;
+ bool face_on_marker = true;
+
+ for (iVertex = 0; iVertex < nVertexDonor; iVertex++) {
+ iPointDonor = donor_geometry->vertex[markDonor][iVertex]->GetNode();
+
+ if (!donor_geometry->node[iPointDonor]->GetDomain()) continue;
+
+ nLocalVertex_Donor++;
+
+ if (!faces) continue;
+
+ /*--- On Donor geometry also communicate face info ---*/
+ if (nDim==3) {
+ for (jElem=0; jElemnode[iPointDonor]->GetnElem(); jElem++) {
+ donor_elem = donor_geometry->node[iPointDonor]->GetElem(jElem);
+ nFaces = donor_geometry->elem[donor_elem]->GetnFaces();
+ for (iFace=0; iFaceelem[donor_elem]->GetnNodesFace(iFace);
+ for (iDonor=0; iDonorelem[donor_elem]->GetFaces(iFace, iDonor);
+ jPoint = donor_geometry->elem[donor_elem]->GetNode(inode);
+ face_on_marker = (face_on_marker && (donor_geometry->node[jPoint]->GetVertex(markDonor) !=-1));
+ }
+ if (face_on_marker ) {
+ nLocalFace_Donor++;
+ nLocalFaceNodes_Donor+=nNodes;
+ }
+ }
+ }
+ }
+ else {
+ /*--- in 2D we use the edges ---*/
+ nNodes=2;
+ nFaces = donor_geometry->node[iPointDonor]->GetnPoint();
+ for (iFace=0; iFacenode[iPointDonor]->GetEdge(iFace);
+ jPoint = donor_geometry->edge[inode]->GetNode(iDonor);
+ face_on_marker = (face_on_marker && (donor_geometry->node[jPoint]->GetVertex(markDonor) !=-1));
+ }
+ if (face_on_marker ) {
+ nLocalFace_Donor++;
+ nLocalFaceNodes_Donor+=nNodes;
+ }
+ }
+ }
+ }
+
+ Buffer_Send_nVertex_Donor[0] = nLocalVertex_Donor;
+ if (faces) {
+ Buffer_Send_nFace_Donor[0] = nLocalFace_Donor;
+ Buffer_Send_nFaceNodes_Donor[0] = nLocalFaceNodes_Donor;
+ }
+
+ /*--- Send Interface vertex information --*/
+ SU2_MPI::Allreduce(&nLocalVertex_Donor, &MaxLocalVertex_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD);
+ SU2_MPI::Allgather(Buffer_Send_nVertex_Donor, 1, MPI_UNSIGNED_LONG,
+ Buffer_Receive_nVertex_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
+ if (faces) {
+ SU2_MPI::Allreduce(&nLocalFace_Donor, &nGlobalFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD);
+ SU2_MPI::Allreduce(&nLocalFace_Donor, &MaxFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD);
+ SU2_MPI::Allreduce(&nLocalFaceNodes_Donor, &nGlobalFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD);
+ SU2_MPI::Allreduce(&nLocalFaceNodes_Donor, &MaxFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD);
+ SU2_MPI::Allgather(Buffer_Send_nFace_Donor, 1, MPI_UNSIGNED_LONG,
+ Buffer_Receive_nFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
+ SU2_MPI::Allgather(Buffer_Send_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG,
+ Buffer_Receive_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
+ MaxFace_Donor++;
+ }
+}
+
+void CInterpolator::Collect_VertexInfo(bool faces, int markDonor, int markTarget,
+ unsigned long nVertexDonor, unsigned short nDim) {
+
+ unsigned long iVertex, iPointDonor = 0, iVertexDonor, nBuffer_Coord, nBuffer_Point, nLocalVertex_Donor;
+ unsigned short iDim;
+
+ for (iVertex = 0; iVertex < MaxLocalVertex_Donor; iVertex++) Buffer_Send_GlobalPoint[iVertex] = -1;
+
+ for (iVertex = 0; iVertex < MaxLocalVertex_Donor*nDim; iVertex++) Buffer_Send_Coord[iVertex] = 0.0;
+
+ if(faces)
+ for (iVertex = 0; iVertex < MaxLocalVertex_Donor*nDim; iVertex++) Buffer_Send_Normal[iVertex] = 0.0;
+
+ /*--- Copy coordinates and point to the auxiliar vector --*/
+ nLocalVertex_Donor = 0;
+
+ for (iVertexDonor = 0; iVertexDonor < nVertexDonor; iVertexDonor++) {
+ iPointDonor = donor_geometry->vertex[markDonor][iVertexDonor]->GetNode();
+ if (donor_geometry->node[iPointDonor]->GetDomain()) {
+ Buffer_Send_GlobalPoint[nLocalVertex_Donor] = donor_geometry->node[iPointDonor]->GetGlobalIndex();
+ for (iDim = 0; iDim < nDim; iDim++)
+ Buffer_Send_Coord[nLocalVertex_Donor*nDim+iDim] = donor_geometry->node[iPointDonor]->GetCoord(iDim);
+
+ if (faces) {
+ const su2double* Normal = donor_geometry->vertex[markDonor][iVertexDonor]->GetNormal();
+ for (iDim = 0; iDim < nDim; iDim++)
+ Buffer_Send_Normal[nLocalVertex_Donor*nDim+iDim] = Normal[iDim];
+ }
+ nLocalVertex_Donor++;
+ }
+ }
+ nBuffer_Coord = MaxLocalVertex_Donor*nDim;
+ nBuffer_Point = MaxLocalVertex_Donor;
+
+ SU2_MPI::Allgather(Buffer_Send_Coord, nBuffer_Coord, MPI_DOUBLE,
+ Buffer_Receive_Coord, nBuffer_Coord, MPI_DOUBLE, MPI_COMM_WORLD);
+ SU2_MPI::Allgather(Buffer_Send_GlobalPoint, nBuffer_Point, MPI_LONG,
+ Buffer_Receive_GlobalPoint, nBuffer_Point, MPI_LONG, MPI_COMM_WORLD);
+ if (faces) {
+ SU2_MPI::Allgather(Buffer_Send_Normal, nBuffer_Coord, MPI_DOUBLE,
+ Buffer_Receive_Normal, nBuffer_Coord, MPI_DOUBLE, MPI_COMM_WORLD);
+ }
+}
+
+void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){
+
+ CGeometry *geom = Geometry[val_zone][INST_0][MESH_0];
+
+ unsigned long iVertex, jVertex, kVertex;
+
+ unsigned long count, iTmp, *uptr, dPoint, EdgeIndex, jEdge, nEdges, nNodes, nVertex, iDim, nDim, iPoint;
+
+ unsigned long nGlobalLinkedNodes, nLocalVertex, nLocalLinkedNodes;
+
+ nDim = geom->GetnDim();
+
+ if( val_marker != -1 )
+ nVertex = geom->GetnVertex( val_marker );
+ else
+ nVertex = 0;
+
+
+ su2double *Buffer_Send_Coord = new su2double [ nVertex * nDim ];
+ unsigned long *Buffer_Send_GlobalPoint = new unsigned long [ nVertex ];
+
+ unsigned long *Buffer_Send_nLinkedNodes = new unsigned long [ nVertex ];
+ unsigned long *Buffer_Send_StartLinkedNodes = new unsigned long [ nVertex ];
+ unsigned long **Aux_Send_Map = new unsigned long*[ nVertex ];
+
+#ifdef HAVE_MPI
+ int nProcessor = size, iRank;
+ unsigned long iTmp2, tmp_index, tmp_index_2;
+#endif
+
+ /*--- Copy coordinates and point to the auxiliar vector ---*/
+
+ nGlobalVertex = 0;
+ nLocalVertex = 0;
+ nLocalLinkedNodes = 0;
+
+ for (iVertex = 0; iVertex < nVertex; iVertex++) {
+
+ Buffer_Send_nLinkedNodes[iVertex] = 0;
+ Aux_Send_Map[iVertex] = NULL;
+
+ iPoint = geom->vertex[val_marker][iVertex]->GetNode();
+
+ if (geom->node[iPoint]->GetDomain()) {
+ Buffer_Send_GlobalPoint[nLocalVertex] = geom->node[iPoint]->GetGlobalIndex();
+
+ for (iDim = 0; iDim < nDim; iDim++)
+ Buffer_Send_Coord[nLocalVertex*nDim+iDim] = geom->node[iPoint]->GetCoord(iDim);
+
+ nNodes = 0;
+ nEdges = geom->node[iPoint]->GetnPoint();
+
+ for (jEdge = 0; jEdge < nEdges; jEdge++){
+ EdgeIndex = geom->node[iPoint]->GetEdge(jEdge);
+
+ if( iPoint == geom->edge[EdgeIndex]->GetNode(0) )
+ dPoint = geom->edge[EdgeIndex]->GetNode(1);
+ else
+ dPoint = geom->edge[EdgeIndex]->GetNode(0);
+
+ if ( geom->node[dPoint]->GetVertex(val_marker) != -1 )
+ nNodes++;
+ }
+
+ Buffer_Send_StartLinkedNodes[nLocalVertex] = nLocalLinkedNodes;
+ Buffer_Send_nLinkedNodes[nLocalVertex] = nNodes;
+
+ nLocalLinkedNodes += nNodes;
+
+ Aux_Send_Map[nLocalVertex] = new unsigned long[ nNodes ];
+ nNodes = 0;
+
+ for (jEdge = 0; jEdge < nEdges; jEdge++){
+ EdgeIndex = geom->node[iPoint]->GetEdge(jEdge);
+
+ if( iPoint == geom->edge[EdgeIndex]->GetNode(0) )
+ dPoint = geom->edge[EdgeIndex]->GetNode(1);
+ else
+ dPoint = geom->edge[EdgeIndex]->GetNode(0);
+
+ if ( geom->node[dPoint]->GetVertex(val_marker) != -1 ){
+ Aux_Send_Map[nLocalVertex][nNodes] = geom->node[dPoint]->GetGlobalIndex();
+ nNodes++;
+ }
+ }
+ nLocalVertex++;
+ }
+ }
+
+ unsigned long *Buffer_Send_LinkedNodes = new unsigned long [ nLocalLinkedNodes ];
+
+ nLocalLinkedNodes = 0;
+
+ for (iVertex = 0; iVertex < nLocalVertex; iVertex++){
+ for (jEdge = 0; jEdge < Buffer_Send_nLinkedNodes[iVertex]; jEdge++){
+ Buffer_Send_LinkedNodes[nLocalLinkedNodes] = Aux_Send_Map[iVertex][jEdge];
+ nLocalLinkedNodes++;
+ }
+ }
+
+ for (iVertex = 0; iVertex < nVertex; iVertex++){
+ if( Aux_Send_Map[iVertex] != NULL )
+ delete [] Aux_Send_Map[iVertex];
+ }
+ delete [] Aux_Send_Map; Aux_Send_Map = NULL;
+
+ /*--- Reconstruct boundary by gathering data from all ranks ---*/
+
+ SU2_MPI::Allreduce( &nLocalVertex, &nGlobalVertex, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD);
+ SU2_MPI::Allreduce(&nLocalLinkedNodes, &nGlobalLinkedNodes, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD);
+
+ Buffer_Receive_Coord = new su2double [ nGlobalVertex * nDim ];
+ Buffer_Receive_GlobalPoint = new long[ nGlobalVertex ];
+ Buffer_Receive_Proc = new unsigned long[ nGlobalVertex ];
+
+ Buffer_Receive_nLinkedNodes = new unsigned long[ nGlobalVertex ];
+ Buffer_Receive_LinkedNodes = new unsigned long[ nGlobalLinkedNodes ];
+ Buffer_Receive_StartLinkedNodes = new unsigned long[ nGlobalVertex ];
+
+#ifdef HAVE_MPI
+ if (rank == MASTER_NODE){
+
+ for (iVertex = 0; iVertex < nDim*nLocalVertex; iVertex++)
+ Buffer_Receive_Coord[iVertex] = Buffer_Send_Coord[iVertex];
+
+ for (iVertex = 0; iVertex < nLocalVertex; iVertex++){
+ Buffer_Receive_GlobalPoint[iVertex] = Buffer_Send_GlobalPoint[iVertex];
+ Buffer_Receive_Proc[iVertex] = MASTER_NODE;
+ Buffer_Receive_nLinkedNodes[iVertex] = Buffer_Send_nLinkedNodes[iVertex];
+ Buffer_Receive_StartLinkedNodes[iVertex] = Buffer_Send_StartLinkedNodes[iVertex];
+ }
+
+ for (iVertex = 0; iVertex < nLocalLinkedNodes; iVertex++)
+ Buffer_Receive_LinkedNodes[iVertex] = Buffer_Send_LinkedNodes[iVertex];
+
+ tmp_index = nLocalVertex;
+ tmp_index_2 = nLocalLinkedNodes;
+
+ for(iRank = 1; iRank < nProcessor; iRank++){
+
+ SU2_MPI::Recv( &iTmp2, 1, MPI_UNSIGNED_LONG, iRank, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
+ SU2_MPI::Recv(&Buffer_Receive_LinkedNodes[tmp_index_2], iTmp2, MPI_UNSIGNED_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
+
+ SU2_MPI::Recv( &iTmp, 1, MPI_UNSIGNED_LONG, iRank, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
+ SU2_MPI::Recv(&Buffer_Receive_Coord[tmp_index*nDim], nDim*iTmp, MPI_DOUBLE, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
+
+ SU2_MPI::Recv( &Buffer_Receive_GlobalPoint[tmp_index], iTmp, MPI_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
+ SU2_MPI::Recv( &Buffer_Receive_nLinkedNodes[tmp_index], iTmp, MPI_UNSIGNED_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
+ SU2_MPI::Recv(&Buffer_Receive_StartLinkedNodes[tmp_index], iTmp, MPI_UNSIGNED_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
+
+ for (iVertex = 0; iVertex < iTmp; iVertex++){
+ Buffer_Receive_Proc[ tmp_index + iVertex ] = iRank;
+ Buffer_Receive_StartLinkedNodes[ tmp_index + iVertex ] += tmp_index_2;
+ }
+
+ tmp_index += iTmp;
+ tmp_index_2 += iTmp2;
+ }
+ }
+ else{
+ SU2_MPI::Send( &nLocalLinkedNodes, 1, MPI_UNSIGNED_LONG, 0, 0, MPI_COMM_WORLD);
+ SU2_MPI::Send(Buffer_Send_LinkedNodes, nLocalLinkedNodes, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD);
+
+ SU2_MPI::Send( &nLocalVertex, 1, MPI_UNSIGNED_LONG, 0, 0, MPI_COMM_WORLD);
+ SU2_MPI::Send(Buffer_Send_Coord, nDim * nLocalVertex, MPI_DOUBLE, 0, 1, MPI_COMM_WORLD);
+
+ SU2_MPI::Send( Buffer_Send_GlobalPoint, nLocalVertex, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD);
+ SU2_MPI::Send( Buffer_Send_nLinkedNodes, nLocalVertex, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD);
+ SU2_MPI::Send(Buffer_Send_StartLinkedNodes, nLocalVertex, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD);
+ }
+#else
+ for (iVertex = 0; iVertex < nDim * nGlobalVertex; iVertex++)
+ Buffer_Receive_Coord[iVertex] = Buffer_Send_Coord[iVertex];
+
+ for (iVertex = 0; iVertex < nGlobalVertex; iVertex++){
+ Buffer_Receive_GlobalPoint[iVertex] = Buffer_Send_GlobalPoint[iVertex];
+ Buffer_Receive_Proc[iVertex] = MASTER_NODE;
+ Buffer_Receive_nLinkedNodes[iVertex] = Buffer_Send_nLinkedNodes[iVertex];
+ Buffer_Receive_StartLinkedNodes[iVertex] = Buffer_Send_StartLinkedNodes[iVertex];
+ }
+
+ for (iVertex = 0; iVertex < nGlobalLinkedNodes; iVertex++)
+ Buffer_Receive_LinkedNodes[iVertex] = Buffer_Send_LinkedNodes[iVertex];
+#endif
+
+ if (rank == MASTER_NODE){
+ for (iVertex = 0; iVertex < nGlobalVertex; iVertex++){
+ count = 0;
+ uptr = &Buffer_Receive_LinkedNodes[ Buffer_Receive_StartLinkedNodes[iVertex] ];
+
+ for (jVertex = 0; jVertex < Buffer_Receive_nLinkedNodes[iVertex]; jVertex++){
+ iTmp = uptr[ jVertex ];
+ for (kVertex = 0; kVertex < nGlobalVertex; kVertex++){
+ if( Buffer_Receive_GlobalPoint[kVertex] == long(iTmp) ){
+ uptr[ jVertex ] = kVertex;
+ count++;
+ break;
+ }
+ }
+
+ if( count != (jVertex+1) ){
+ for (kVertex = jVertex; kVertex < Buffer_Receive_nLinkedNodes[iVertex]-1; kVertex++){
+ uptr[ kVertex ] = uptr[ kVertex + 1];
+ }
+ Buffer_Receive_nLinkedNodes[iVertex]--;
+ jVertex--;
+ }
+ }
+ }
+ }
+
+ SU2_MPI::Bcast(Buffer_Receive_GlobalPoint, nGlobalVertex, MPI_LONG, 0, MPI_COMM_WORLD);
+ SU2_MPI::Bcast(Buffer_Receive_Coord, nGlobalVertex*nDim, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+ SU2_MPI::Bcast(Buffer_Receive_Proc, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD);
+
+ SU2_MPI::Bcast(Buffer_Receive_nLinkedNodes, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD);
+ SU2_MPI::Bcast(Buffer_Receive_StartLinkedNodes, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD);
+ SU2_MPI::Bcast(Buffer_Receive_LinkedNodes, nGlobalLinkedNodes, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD);
+
+ delete [] Buffer_Send_Coord; Buffer_Send_Coord = NULL;
+ delete [] Buffer_Send_GlobalPoint; Buffer_Send_GlobalPoint = NULL;
+ delete [] Buffer_Send_LinkedNodes; Buffer_Send_LinkedNodes = NULL;
+ delete [] Buffer_Send_nLinkedNodes; Buffer_Send_nLinkedNodes = NULL;
+ delete [] Buffer_Send_StartLinkedNodes; Buffer_Send_StartLinkedNodes = NULL;
+
+}
diff --git a/Common/src/interface_interpolation/CIsoparametric.cpp b/Common/src/interface_interpolation/CIsoparametric.cpp
new file mode 100644
index 000000000000..d1032f26495b
--- /dev/null
+++ b/Common/src/interface_interpolation/CIsoparametric.cpp
@@ -0,0 +1,554 @@
+/*!
+ * \file CIsoparametric.cpp
+ * \brief Implementation isoparametric interpolation (using FE shape functions).
+ * \author H. Kline
+ * \version 7.0.2 "Blackbird"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md)
+ *
+ * SU2 is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * SU2 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with SU2. If not, see .
+ */
+
+#include "../../include/interface_interpolation/CIsoparametric.hpp"
+#include "../../include/CConfig.hpp"
+#include "../../include/geometry/CGeometry.hpp"
+
+
+CIsoparametric::CIsoparametric(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone,
+ unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) {
+ Set_TransferCoeff(config);
+}
+
+void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) {
+
+ unsigned long iVertex, jVertex;
+ unsigned long dPoint, inode, jElem, nElem;
+ unsigned short iDim, iDonor=0, iFace;
+
+ unsigned short nDim = donor_geometry->GetnDim();
+
+ unsigned short nMarkerInt;
+ unsigned short iMarkerInt;
+
+ int markDonor=0, markTarget=0;
+
+ long donor_elem=0, temp_donor=0;
+ unsigned int nNodes=0;
+ /*--- Restricted to 2-zone for now ---*/
+ unsigned int nFaces=1; //For 2D cases, we want to look at edges, not faces, as the 'interface'
+ bool face_on_marker=true;
+
+ unsigned long nVertexDonor = 0, nVertexTarget= 0;
+ unsigned long Point_Target = 0;
+
+ unsigned long iVertexDonor, iPointDonor = 0;
+ int iProcessor;
+
+ unsigned long nLocalFace_Donor = 0, nLocalFaceNodes_Donor=0;
+
+ unsigned long faceindex;
+
+ su2double dist = 0.0, mindist=1E6, *Coord, *Coord_i;
+ su2double myCoeff[10]; // Maximum # of donor points
+ su2double *Normal;
+ su2double *projected_point = new su2double[nDim];
+ su2double tmp, tmp2;
+ su2double storeCoeff[10];
+ unsigned long storeGlobal[10];
+ int storeProc[10];
+
+ int nProcessor = size;
+ Coord = new su2double[nDim];
+ Normal = new su2double[nDim];
+
+ Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor];
+ Buffer_Receive_nFace_Donor = new unsigned long [nProcessor];
+ Buffer_Receive_nFaceNodes_Donor = new unsigned long [nProcessor];
+
+ nMarkerInt = (config[donorZone]->GetMarker_n_ZoneInterface())/2;
+
+ /*--- For the number of markers on the interface... ---*/
+ for (iMarkerInt=1; iMarkerInt <= nMarkerInt; iMarkerInt++) {
+ /*--- Procedure:
+ * -Loop through vertices of the aero grid
+ * -Find nearest element and allocate enough space in the aero grid donor point info
+ * -set the transfer coefficient values
+ */
+
+ /*--- On the donor side: find the tag of the boundary sharing the interface ---*/
+ markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt);
+
+ /*--- On the target side: find the tag of the boundary sharing the interface ---*/
+ markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt);
+
+ /*--- Checks if the zone contains the interface, if not continue to the next step ---*/
+ if(!CheckInterfaceBoundary(markDonor, markTarget)) continue;
+
+ if(markDonor != -1)
+ nVertexDonor = donor_geometry->GetnVertex( markDonor );
+ else
+ nVertexDonor = 0;
+
+ if(markTarget != -1)
+ nVertexTarget = target_geometry->GetnVertex( markTarget );
+ else
+ nVertexTarget = 0;
+
+ /* Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor */
+ Determine_ArraySize(true, markDonor, markTarget, nVertexDonor, nDim);
+
+ Buffer_Send_Coord = new su2double [MaxLocalVertex_Donor*nDim];
+ Buffer_Send_Normal = new su2double [MaxLocalVertex_Donor*nDim];
+ Buffer_Send_GlobalPoint = new long [MaxLocalVertex_Donor];
+
+ Buffer_Receive_Coord = new su2double [nProcessor*MaxLocalVertex_Donor*nDim];
+ Buffer_Receive_Normal = new su2double [nProcessor*MaxLocalVertex_Donor*nDim];
+ Buffer_Receive_GlobalPoint = new long [nProcessor*MaxLocalVertex_Donor];
+
+ /*-- Collect coordinates, global points, and normal vectors ---*/
+ Collect_VertexInfo(true, markDonor,markTarget,nVertexDonor,nDim);
+
+ Buffer_Send_FaceIndex = new unsigned long[MaxFace_Donor];
+ Buffer_Send_FaceNodes = new unsigned long[MaxFaceNodes_Donor];
+ Buffer_Send_FaceProc = new unsigned long[MaxFaceNodes_Donor];
+
+ Buffer_Receive_FaceIndex = new unsigned long[MaxFace_Donor*nProcessor];
+ Buffer_Receive_FaceNodes = new unsigned long[MaxFaceNodes_Donor*nProcessor];
+ Buffer_Receive_FaceProc = new unsigned long[MaxFaceNodes_Donor*nProcessor];
+
+ nLocalFace_Donor=0;
+ nLocalFaceNodes_Donor=0;
+
+ /*--- Collect Face info ---*/
+
+ for (iVertex = 0; iVertex < MaxFace_Donor; iVertex++) {
+ Buffer_Send_FaceIndex[iVertex] = 0;
+ }
+ for (iVertex=0; iVertexvertex[markDonor][iVertexDonor]->GetNode();
+
+ if (donor_geometry->node[iPointDonor]->GetDomain()) {
+
+ if (nDim==3) nElem = donor_geometry->node[iPointDonor]->GetnElem();
+ else nElem =donor_geometry->node[iPointDonor]->GetnPoint();
+
+ for (jElem=0; jElem < nElem; jElem++) {
+ if (nDim==3) {
+ temp_donor = donor_geometry->node[iPointDonor]->GetElem(jElem);
+ nFaces = donor_geometry->elem[temp_donor]->GetnFaces();
+ for (iFace=0; iFaceelem[temp_donor]->GetnNodesFace(iFace);
+ for (iDonor=0; iDonorelem[temp_donor]->GetFaces(iFace, iDonor);
+ dPoint = donor_geometry->elem[temp_donor]->GetNode(inode);
+ face_on_marker = (face_on_marker && (donor_geometry->node[dPoint]->GetVertex(markDonor) !=-1));
+ }
+
+ if (face_on_marker ) {
+ for (iDonor=0; iDonorelem[temp_donor]->GetFaces(iFace, iDonor);
+ dPoint = donor_geometry->elem[temp_donor]->GetNode(inode);
+ // Match node on the face to the correct global index
+ long jGlobalPoint = donor_geometry->node[dPoint]->GetGlobalIndex();
+ for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) {
+ for (jVertex = 0; jVertex < Buffer_Receive_nVertex_Donor[iProcessor]; jVertex++) {
+ if (jGlobalPoint == Buffer_Receive_GlobalPoint[MaxLocalVertex_Donor*iProcessor+jVertex]) {
+ Buffer_Send_FaceNodes[nLocalFaceNodes_Donor]=MaxLocalVertex_Donor*iProcessor+jVertex;
+ Buffer_Send_FaceProc[nLocalFaceNodes_Donor]=iProcessor;
+ }
+ }
+ }
+ nLocalFaceNodes_Donor++; // Increment total number of face-nodes / processor
+ }
+ /* Store the indices */
+ Buffer_Send_FaceIndex[nLocalFace_Donor+1] = Buffer_Send_FaceIndex[nLocalFace_Donor]+nNodes;
+ nLocalFace_Donor++; // Increment number of faces / processor
+ }
+ }
+ }
+ else {
+ /*-- Determine whether this face/edge is on the marker --*/
+ face_on_marker=true;
+ for (iDonor=0; iDonornode[iPointDonor]->GetEdge(jElem);
+ dPoint = donor_geometry->edge[inode]->GetNode(iDonor);
+ face_on_marker = (face_on_marker && (donor_geometry->node[dPoint]->GetVertex(markDonor) !=-1));
+ }
+ if (face_on_marker ) {
+ for (iDonor=0; iDonornode[iPointDonor]->GetEdge(jElem);
+ dPoint = donor_geometry->edge[inode]->GetNode(iDonor);
+ // Match node on the face to the correct global index
+ long jGlobalPoint = donor_geometry->node[dPoint]->GetGlobalIndex();
+ for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) {
+ for (jVertex = 0; jVertex < Buffer_Receive_nVertex_Donor[iProcessor]; jVertex++) {
+ if (jGlobalPoint == Buffer_Receive_GlobalPoint[MaxLocalVertex_Donor*iProcessor+jVertex]) {
+ Buffer_Send_FaceNodes[nLocalFaceNodes_Donor]=MaxLocalVertex_Donor*iProcessor+jVertex;
+ Buffer_Send_FaceProc[nLocalFaceNodes_Donor]=iProcessor;
+ }
+ }
+ }
+ nLocalFaceNodes_Donor++; // Increment total number of face-nodes / processor
+ }
+ /* Store the indices */
+ Buffer_Send_FaceIndex[nLocalFace_Donor+1] = Buffer_Send_FaceIndex[nLocalFace_Donor]+nNodes;
+ nLocalFace_Donor++; // Increment number of faces / processor
+ }
+ }
+ }
+ }
+ }
+
+ //Buffer_Send_FaceIndex[nLocalFace_Donor+1] = MaxFaceNodes_Donor*rank+nLocalFaceNodes_Donor;
+
+ SU2_MPI::Allgather(Buffer_Send_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG,
+ Buffer_Receive_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
+ SU2_MPI::Allgather(Buffer_Send_FaceProc, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG,
+ Buffer_Receive_FaceProc, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
+ SU2_MPI::Allgather(Buffer_Send_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG,
+ Buffer_Receive_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
+
+ /*--- Loop over the vertices on the target Marker ---*/
+ for (iVertex = 0; iVertexvertex[markTarget][iVertex]->GetNode();
+
+ if (!target_geometry->node[Point_Target]->GetDomain()) continue;
+
+ Coord_i = target_geometry->node[Point_Target]->GetCoord();
+ /*---Loop over the faces previously communicated/stored ---*/
+ for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) {
+
+ nFaces = (unsigned int)Buffer_Receive_nFace_Donor[iProcessor];
+
+ for (iFace = 0; iFace< nFaces; iFace++) {
+ /*--- ---*/
+
+ nNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1] -
+ (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace];
+
+ su2double *X = new su2double[nNodes*(nDim+1)];
+ faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face
+ for (iDonor=0; iDonorvertex[markTarget][iVertex]->SetDonorElem(donor_elem); // in 2D is nearest neighbor
+ for (iDonor=0; iDonorvertex[markTarget][iVertex]->Allocate_DonorInfo(nNodes);
+
+ for (iDonor=0; iDonorvertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor,storeGlobal[iDonor]);
+ target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor,storeCoeff[iDonor]);
+ target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]);
+ }
+
+ }
+
+ delete[] Buffer_Send_Coord;
+ delete[] Buffer_Send_Normal;
+ delete[] Buffer_Send_GlobalPoint;
+
+ delete[] Buffer_Receive_Coord;
+ delete[] Buffer_Receive_Normal;
+ delete[] Buffer_Receive_GlobalPoint;
+
+ delete[] Buffer_Send_FaceIndex;
+ delete[] Buffer_Send_FaceNodes;
+ delete[] Buffer_Send_FaceProc;
+
+ delete[] Buffer_Receive_FaceIndex;
+ delete[] Buffer_Receive_FaceNodes;
+ delete[] Buffer_Receive_FaceProc;
+ }
+
+ delete[] Buffer_Receive_nVertex_Donor;
+ delete[] Buffer_Receive_nFace_Donor;
+ delete[] Buffer_Receive_nFaceNodes_Donor;
+
+ delete [] Coord;
+ delete [] Normal;
+
+ delete [] projected_point;
+}
+
+void CIsoparametric::Isoparameters(unsigned short nDim, unsigned short nDonor,
+ const su2double *X, const su2double *xj, su2double *isoparams) const {
+
+ short iDonor,iDim,k; // indices
+ su2double tmp, tmp2;
+
+ su2double *x = new su2double[nDim+1];
+ su2double *x_tmp = new su2double[nDim+1];
+ su2double *Q = new su2double[nDonor*nDonor];
+ su2double *R = new su2double[nDonor*nDonor];
+ su2double *A = new su2double[(nDim+2)*nDonor];
+ su2double *A2 = NULL;
+ su2double *x2 = new su2double[nDim+1];
+
+ bool *test = new bool[nDim+1];
+ bool *testi = new bool[nDim+1];
+
+ su2double eps = 1E-10;
+
+ short n = nDim+1;
+
+ if (nDonor>2) {
+ /*--- Create Matrix A: 1st row all 1's, 2nd row x coordinates, 3rd row y coordinates, etc ---*/
+ /*--- Right hand side is [1, \vec{x}']'---*/
+ for (iDonor=0; iDonoreps && iDonor=0; iDonor--) {
+ if (R[iDonor*nDonor+iDonor]>eps)
+ isoparams[iDonor]=x_tmp[iDonor]/R[iDonor*nDonor+iDonor];
+ else
+ isoparams[iDonor]=0;
+ for (k=0; k1.0) xi=1.0;
+ if (xi<-1.0) xi=-1.0;
+ if (eta>1.0) eta=1.0;
+ if (eta<-1.0) eta=-1.0;
+ isoparams[0]=0.25*(1-xi)*(1-eta);
+ isoparams[1]=0.25*(1+xi)*(1-eta);
+ isoparams[2]=0.25*(1+xi)*(1+eta);
+ isoparams[3]=0.25*(1-xi)*(1+eta);
+
+ }
+ if (nDonor<4) {
+ tmp = 0.0; // value for normalization
+ tmp2=0; // check for maximum value, to be used to id nearest neighbor if necessary
+ k=0; // index for maximum value
+ for (iDonor=0; iDonor< nDonor; iDonor++) {
+ if (isoparams[iDonor]>tmp2) {
+ k=iDonor;
+ tmp2=isoparams[iDonor];
+ }
+ // [0,1]
+ if (isoparams[iDonor]<0) isoparams[iDonor]=0;
+ if (isoparams[iDonor]>1) isoparams[iDonor] = 1;
+ tmp +=isoparams[iDonor];
+ }
+ if (tmp>0)
+ for (iDonor=0; iDonor< nDonor; iDonor++)
+ isoparams[iDonor]=isoparams[iDonor]/tmp;
+ else {
+ isoparams[k] = 1.0;
+ }
+ }
+
+ delete [] x;
+ delete [] x_tmp;
+ delete [] Q;
+ delete [] R;
+ delete [] A;
+ delete [] A2;
+ delete [] x2;
+
+ delete [] test;
+ delete [] testi;
+
+}
diff --git a/Common/src/interface_interpolation/CMirror.cpp b/Common/src/interface_interpolation/CMirror.cpp
new file mode 100644
index 000000000000..089af9eceec1
--- /dev/null
+++ b/Common/src/interface_interpolation/CMirror.cpp
@@ -0,0 +1,194 @@
+/*!
+ * \file CMirror.cpp
+ * \brief Implementation of mirror interpolation (conservative approach in FSI problems).
+ * \author H. Kline
+ * \version 7.0.2 "Blackbird"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md)
+ *
+ * SU2 is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * SU2 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with SU2. If not, see .
+ */
+
+#include "../../include/interface_interpolation/CMirror.hpp"
+#include "../../include/CConfig.hpp"
+#include "../../include/geometry/CGeometry.hpp"
+
+
+CMirror::CMirror(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone,
+ unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) {
+ Set_TransferCoeff(config);
+}
+
+void CMirror::Set_TransferCoeff(const CConfig* const* config) {
+
+ const int nProcessor = size;
+
+ Buffer_Receive_nFace_Donor = new unsigned long [nProcessor];
+ Buffer_Receive_nFaceNodes_Donor = new unsigned long [nProcessor];
+
+ /*--- Number of markers on the interface ---*/
+ const auto nMarkerInt = (config[targetZone]->GetMarker_n_ZoneInterface())/2;
+
+ /*--- For the number of markers on the interface... ---*/
+ for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) {
+ /*--- Procedure:
+ * - Loop through vertices of the aero grid
+ * - Find nearest element and allocate enough space in the aero grid donor point info
+ * - Set the transfer coefficient values
+ */
+
+ /*--- On the donor side: find the tag of the boundary sharing the interface ---*/
+ const auto markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt);
+
+ /*--- On the target side: find the tag of the boundary sharing the interface ---*/
+ const auto markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt);
+
+ /*--- Checks if the zone contains the interface, if not continue to the next step ---*/
+ if(!CheckInterfaceBoundary(markDonor, markTarget)) continue;
+
+ unsigned long nVertexDonor = 0, nVertexTarget = 0;
+ if (markDonor != -1) nVertexDonor = donor_geometry->GetnVertex( markDonor );
+ if (markTarget != -1) nVertexTarget = target_geometry->GetnVertex( markTarget );
+
+ /*-- Collect the number of donor nodes: re-use 'Face' containers --*/
+ auto nLocalFace_Donor = 0ul;
+ auto nLocalFaceNodes_Donor = 0ul;
+ for (auto jVertex = 0ul; jVertexvertex[markDonor][jVertex]->GetNode(); // Local index of jVertex
+
+ if (donor_geometry->node[Point_Donor]->GetDomain()) {
+ auto nNodes = donor_geometry->vertex[markDonor][jVertex]->GetnDonorPoints();
+ nLocalFaceNodes_Donor += nNodes;
+ nLocalFace_Donor++;
+ }
+ }
+
+ Buffer_Send_nFace_Donor[0] = nLocalFace_Donor;
+ Buffer_Send_nFaceNodes_Donor[0] = nLocalFaceNodes_Donor;
+
+ /*--- Send Interface vertex information --*/
+ SU2_MPI::Allreduce(&nLocalFaceNodes_Donor, &MaxFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD);
+ SU2_MPI::Allreduce(&nLocalFace_Donor, &MaxFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD);
+ SU2_MPI::Allgather(Buffer_Send_nFace_Donor, 1, MPI_UNSIGNED_LONG,
+ Buffer_Receive_nFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
+ SU2_MPI::Allgather(Buffer_Send_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG,
+ Buffer_Receive_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
+ MaxFace_Donor++;
+
+ /*-- Send donor info and init to 0 --*/
+ Buffer_Send_FaceIndex = new unsigned long[MaxFace_Donor] ();
+ Buffer_Send_FaceNodes = new unsigned long[MaxFaceNodes_Donor] ();
+ Buffer_Send_GlobalPoint = new long[MaxFaceNodes_Donor] ();
+ auto Buffer_Send_Coeff = new su2double[MaxFaceNodes_Donor] ();
+
+ Buffer_Receive_FaceIndex = new unsigned long[MaxFace_Donor*nProcessor];
+ Buffer_Receive_FaceNodes = new unsigned long[MaxFaceNodes_Donor*nProcessor];
+ Buffer_Receive_GlobalPoint = new long[MaxFaceNodes_Donor*nProcessor];
+ auto Buffer_Receive_Coeff = new su2double[MaxFaceNodes_Donor*nProcessor];
+
+ Buffer_Send_FaceIndex[0]=rank*MaxFaceNodes_Donor;
+ nLocalFace_Donor=0;
+ nLocalFaceNodes_Donor=0;
+
+ for (auto jVertex = 0ul; jVertex < nVertexDonor; jVertex++) {
+
+ auto Point_Donor = donor_geometry->vertex[markDonor][jVertex]->GetNode(); // Local index of jVertex
+
+ if (!donor_geometry->node[Point_Donor]->GetDomain()) continue;
+
+ auto nNodes = donor_geometry->vertex[markDonor][jVertex]->GetnDonorPoints();
+ for (auto iDonor = 0; iDonor < nNodes; iDonor++) {
+ Buffer_Send_FaceNodes[nLocalFaceNodes_Donor] = donor_geometry->node[Point_Donor]->GetGlobalIndex();
+ Buffer_Send_GlobalPoint[nLocalFaceNodes_Donor] =
+ donor_geometry->vertex[markDonor][jVertex]->GetInterpDonorPoint(iDonor);
+ Buffer_Send_Coeff[nLocalFaceNodes_Donor] =
+ donor_geometry->vertex[markDonor][jVertex]->GetDonorCoeff(iDonor);
+ nLocalFaceNodes_Donor++;
+ }
+ Buffer_Send_FaceIndex[nLocalFace_Donor+1] =Buffer_Send_FaceIndex[nLocalFace_Donor]+nNodes;
+ nLocalFace_Donor++;
+ }
+
+ SU2_MPI::Allgather(Buffer_Send_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG,
+ Buffer_Receive_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
+ SU2_MPI::Allgather(Buffer_Send_GlobalPoint, MaxFaceNodes_Donor, MPI_LONG,
+ Buffer_Receive_GlobalPoint, MaxFaceNodes_Donor, MPI_LONG, MPI_COMM_WORLD);
+ SU2_MPI::Allgather(Buffer_Send_Coeff, MaxFaceNodes_Donor, MPI_DOUBLE,
+ Buffer_Receive_Coeff, MaxFaceNodes_Donor, MPI_DOUBLE, MPI_COMM_WORLD);
+ SU2_MPI::Allgather(Buffer_Send_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG,
+ Buffer_Receive_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
+
+ /*--- Loop over the vertices on the target Marker ---*/
+ SU2_OMP_PARALLEL_(for schedule(dynamic,roundUpDiv(nVertexTarget,2*omp_get_max_threads())))
+ for (auto iVertex = 0ul; iVertex < nVertexTarget; iVertex++) {
+
+ auto target_vertex = target_geometry->vertex[markTarget][iVertex];
+ auto iPoint = target_vertex->GetNode();
+
+ if (!target_geometry->node[iPoint]->GetDomain()) continue;
+
+ const long Global_Point = target_geometry->node[iPoint]->GetGlobalIndex();
+
+ auto nNodes = 0;
+ for (int iProcessor = 0; iProcessor < nProcessor; iProcessor++) {
+ for (auto iFace = 0ul; iFace < Buffer_Receive_nFace_Donor[iProcessor]; iFace++) {
+ auto faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face
+ auto iNodes = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1] - faceindex;
+ for (auto iTarget = 0ul; iTargetAllocate_DonorInfo(nNodes);
+
+ for (int iProcessor = 0, iDonor = 0; iProcessor < nProcessor; iProcessor++) {
+ for (auto iFace = 0ul; iFace < Buffer_Receive_nFace_Donor[iProcessor]; iFace++) {
+
+ auto faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face
+ auto iNodes = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1] - faceindex;
+
+ for (auto iTarget = 0ul; iTarget < iNodes; iTarget++) {
+ if (Global_Point == long(Buffer_Receive_GlobalPoint[faceindex+iTarget])) {
+ auto coeff = Buffer_Receive_Coeff[faceindex+iTarget];
+ auto pGlobalPoint = Buffer_Receive_FaceNodes[faceindex+iTarget];
+ target_vertex->SetInterpDonorPoint(iDonor,pGlobalPoint);
+ target_vertex->SetDonorCoeff(iDonor,coeff);
+ target_vertex->SetInterpDonorProcessor(iDonor, iProcessor);
+ iDonor++;
+ }
+ }
+ }
+ }
+ }
+
+ delete[] Buffer_Send_FaceIndex;
+ delete[] Buffer_Send_FaceNodes;
+ delete[] Buffer_Send_GlobalPoint;
+ delete[] Buffer_Send_Coeff;
+
+ delete[] Buffer_Receive_FaceIndex;
+ delete[] Buffer_Receive_FaceNodes;
+ delete[] Buffer_Receive_GlobalPoint;
+ delete[] Buffer_Receive_Coeff;
+ }
+
+ delete[] Buffer_Receive_nFace_Donor;
+ delete[] Buffer_Receive_nFaceNodes_Donor;
+
+}
diff --git a/Common/src/interface_interpolation/CNearestNeighbor.cpp b/Common/src/interface_interpolation/CNearestNeighbor.cpp
new file mode 100644
index 000000000000..e1369f19aef0
--- /dev/null
+++ b/Common/src/interface_interpolation/CNearestNeighbor.cpp
@@ -0,0 +1,188 @@
+/*!
+ * \file CNearestNeighbor.cpp
+ * \brief Implementation of nearest neighbor interpolation.
+ * \author H. Kline
+ * \version 7.0.2 "Blackbird"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md)
+ *
+ * SU2 is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * SU2 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with SU2. If not, see .
+ */
+
+#include "../../include/interface_interpolation/CNearestNeighbor.hpp"
+#include "../../include/CConfig.hpp"
+#include "../../include/geometry/CGeometry.hpp"
+
+
+/*! \brief Helper struct to (partially) sort neighbours according to distance while
+ * keeping track of the origin of the point (i.e. index and processor). */
+struct DonorInfo {
+ su2double dist;
+ unsigned pidx;
+ int proc;
+ DonorInfo(su2double d = 0.0, unsigned i = 0, int p = 0) :
+ dist(d), pidx(i), proc(p) { }
+};
+
+CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone,
+ unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) {
+ Set_TransferCoeff(config);
+}
+
+void CNearestNeighbor::PrintStatistics() const {
+ if (rank != MASTER_NODE) return;
+ cout << " Avg/max distance to closest donor point: " << AvgDistance << "/" << MaxDistance << endl;
+}
+
+void CNearestNeighbor::Set_TransferCoeff(const CConfig* const* config) {
+
+ /*--- Desired number of donor points. ---*/
+ const auto nDonor = max(config[donorZone]->GetNumNearestNeighbors(), 1);
+
+ /*--- Epsilon used to avoid division by zero. ---*/
+ const su2double eps = numeric_limits::epsilon();
+
+ const int nProcessor = size;
+ const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2;
+ const auto nDim = donor_geometry->GetnDim();
+
+ Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor];
+
+ vector > DonorInfoVec(omp_get_max_threads());
+
+ /*--- Cycle over nMarkersInt interface to determine communication pattern. ---*/
+
+ AvgDistance = MaxDistance = 0.0;
+ unsigned long totalTargetPoints = 0;
+
+ for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) {
+
+ /*--- On the donor side: find the tag of the boundary sharing the interface. ---*/
+ const auto markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt);
+
+ /*--- On the target side: find the tag of the boundary sharing the interface. ---*/
+ const auto markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt);
+
+ /*--- Checks if the zone contains the interface, if not continue to the next step. ---*/
+ if (!CheckInterfaceBoundary(markDonor, markTarget)) continue;
+
+ unsigned long nVertexDonor = 0, nVertexTarget = 0;
+ if(markDonor != -1) nVertexDonor = donor_geometry->GetnVertex( markDonor );
+ if(markTarget != -1) nVertexTarget = target_geometry->GetnVertex( markTarget );
+
+ /* Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor. */
+ Determine_ArraySize(false, markDonor, markTarget, nVertexDonor, nDim);
+
+ const auto nPossibleDonor = accumulate(Buffer_Receive_nVertex_Donor,
+ Buffer_Receive_nVertex_Donor+nProcessor, 0ul);
+
+ Buffer_Send_Coord = new su2double [ MaxLocalVertex_Donor * nDim ];
+ Buffer_Send_GlobalPoint = new long [ MaxLocalVertex_Donor ];
+ Buffer_Receive_Coord = new su2double [ nProcessor * MaxLocalVertex_Donor * nDim ];
+ Buffer_Receive_GlobalPoint = new long [ nProcessor * MaxLocalVertex_Donor ];
+
+ /*-- Collect coordinates and global point indices. ---*/
+ Collect_VertexInfo( false, markDonor, markTarget, nVertexDonor, nDim );
+
+ /*--- Find the closest donor points to each target. ---*/
+ SU2_OMP_PARALLEL
+ {
+ /*--- Working array for this thread. ---*/
+ auto& donorInfo = DonorInfoVec[omp_get_thread_num()];
+ donorInfo.resize(nPossibleDonor);
+
+ su2double avgDist = 0.0, maxDist = 0.0;
+ unsigned long numTarget = 0;
+
+ SU2_OMP_FOR_DYN(roundUpDiv(nVertexTarget,2*omp_get_max_threads()))
+ for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget++) {
+
+ auto target_vertex = target_geometry->vertex[markTarget][iVertexTarget];
+ const auto Point_Target = target_vertex->GetNode();
+
+ if (!target_geometry->node[Point_Target]->GetDomain()) continue;
+
+ /*--- Coordinates of the target point. ---*/
+ const su2double* Coord_i = target_geometry->node[Point_Target]->GetCoord();
+
+ /*--- Compute all distances. ---*/
+ for (int iProcessor = 0, iDonor = 0; iProcessor < nProcessor; ++iProcessor) {
+ for (auto jVertex = 0ul; jVertex < Buffer_Receive_nVertex_Donor[iProcessor]; ++jVertex) {
+
+ const auto idx = iProcessor*MaxLocalVertex_Donor + jVertex;
+ const auto pGlobalPoint = Buffer_Receive_GlobalPoint[idx];
+ const su2double* Coord_j = &Buffer_Receive_Coord[idx*nDim];
+ const auto dist2 = PointsSquareDistance(nDim, Coord_i, Coord_j);
+
+ donorInfo[iDonor++] = DonorInfo(dist2, pGlobalPoint, iProcessor);
+ }
+ }
+
+ /*--- Find k closest points. ---*/
+ partial_sort(donorInfo.begin(), donorInfo.begin()+nDonor, donorInfo.end(),
+ [](const DonorInfo& a, const DonorInfo& b){return a.dist < b.dist;});
+
+ /*--- Update stats. ---*/
+ numTarget += 1;
+ su2double d = sqrt(donorInfo[0].dist);
+ avgDist += d;
+ maxDist = max(maxDist, d);
+
+ /*--- Compute interpolation numerators and denominator. ---*/
+ su2double denom = 0.0;
+ for (auto iDonor = 0ul; iDonor < nDonor; ++iDonor) {
+ donorInfo[iDonor].dist = 1.0 / (donorInfo[iDonor].dist + eps);
+ denom += donorInfo[iDonor].dist;
+ }
+
+ /*--- Set interpolation coefficients. ---*/
+ target_vertex->Allocate_DonorInfo(nDonor);
+
+ for (auto iDonor = 0ul; iDonor < nDonor; ++iDonor) {
+ target_vertex->SetInterpDonorPoint(iDonor, donorInfo[iDonor].pidx);
+ target_vertex->SetInterpDonorProcessor(iDonor, donorInfo[iDonor].proc);
+ target_vertex->SetDonorCoeff(iDonor, donorInfo[iDonor].dist/denom);
+ }
+ }
+ SU2_OMP_CRITICAL
+ {
+ totalTargetPoints += numTarget;
+ AvgDistance += avgDist;
+ MaxDistance = max(MaxDistance, maxDist);
+ }
+ } // end SU2_OMP_PARALLEL
+
+ delete[] Buffer_Send_Coord;
+ delete[] Buffer_Send_GlobalPoint;
+
+ delete[] Buffer_Receive_Coord;
+ delete[] Buffer_Receive_GlobalPoint;
+
+ }
+
+ delete[] Buffer_Receive_nVertex_Donor;
+
+ unsigned long tmp = totalTargetPoints;
+ SU2_MPI::Allreduce(&tmp, &totalTargetPoints, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD);
+ su2double tmp1 = AvgDistance, tmp2 = MaxDistance;
+ SU2_MPI::Allreduce(&tmp1, &AvgDistance, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
+ SU2_MPI::Allreduce(&tmp2, &MaxDistance, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
+ AvgDistance /= totalTargetPoints;
+
+}
diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp
new file mode 100644
index 000000000000..7938efb4a364
--- /dev/null
+++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp
@@ -0,0 +1,577 @@
+/*!
+ * \file CRadialBasisFunction.cpp
+ * \brief Implementation of RBF interpolation.
+ * \author Joel Ho, P. Gomes
+ * \version 7.0.2 "Blackbird"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md)
+ *
+ * SU2 is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * SU2 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with SU2. If not, see .
+ */
+
+#include "../../include/interface_interpolation/CRadialBasisFunction.hpp"
+#include "../../include/CConfig.hpp"
+#include "../../include/geometry/CGeometry.hpp"
+#include "../../include/toolboxes/CSymmetricMatrix.hpp"
+
+#if defined(HAVE_MKL)
+#include "mkl.h"
+#ifndef HAVE_LAPACK
+#define HAVE_LAPACK
+#endif
+#elif defined(HAVE_LAPACK)
+// dgemm(opA, opB, m, n, k, alpha, A, lda, B, ldb, beta, C, ldc)
+extern "C" void dgemm_(const char*, const char*, const int*, const int*, const int*,
+ const passivedouble*, const passivedouble*, const int*, const passivedouble*,
+ const int*, const passivedouble*, passivedouble*, const int*);
+#define DGEMM dgemm_
+#endif
+
+
+CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone,
+ unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) {
+ Set_TransferCoeff(config);
+}
+
+void CRadialBasisFunction::PrintStatistics() const {
+ if (rank != MASTER_NODE) return;
+ cout.precision(3);
+ cout << " Min/avg/max number of RBF donors per target point: "
+ << MinDonors << "/" << AvgDonors << "/" << MaxDonors << "\n"
+ << " Avg/max correction factor after pruning: " << AvgCorrection << "/" << MaxCorrection;
+ if (MaxCorrection < 1.1 || AvgCorrection < 1.02) cout << " (ok)\n";
+ else if (MaxCorrection < 2.0 && AvgCorrection < 1.05) cout << " (warning)\n";
+ else cout << " <<< WARNING >>>\n";
+ cout << " Interpolation matrix is " << Density << "% dense." << endl;
+ cout.unsetf(ios::floatfield);
+}
+
+su2double CRadialBasisFunction::Get_RadialBasisValue(ENUM_RADIALBASIS type, const su2double radius, const su2double dist)
+{
+ su2double rbf = dist/radius;
+
+ switch (type) {
+
+ case WENDLAND_C2:
+ if(rbf < 1) rbf = pow(pow((1-rbf),2),2)*(4*rbf+1); // double use of pow(x,2) for optimization
+ else rbf = 0.0;
+ break;
+
+ case GAUSSIAN:
+ rbf = exp(-rbf*rbf);
+ break;
+
+ case THIN_PLATE_SPLINE:
+ if(rbf < numeric_limits::min()) rbf = 0.0;
+ else rbf *= rbf*log(rbf);
+ break;
+
+ case MULTI_QUADRIC:
+ case INV_MULTI_QUADRIC:
+ rbf = sqrt(1.0+rbf*rbf);
+ if(type == INV_MULTI_QUADRIC) rbf = 1.0/rbf;
+ break;
+ }
+
+ return rbf;
+}
+
+void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) {
+
+ /*--- RBF options. ---*/
+ const auto kindRBF = static_cast(config[donorZone]->GetKindRadialBasisFunction());
+ const bool usePolynomial = config[donorZone]->GetRadialBasisFunctionPolynomialOption();
+ const su2double paramRBF = config[donorZone]->GetRadialBasisFunctionParameter();
+ const su2double pruneTol = config[donorZone]->GetRadialBasisFunctionPruneTol();
+
+ const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2;
+ const int nDim = donor_geometry->GetnDim();
+
+ const int nProcessor = size;
+ Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor];
+
+ /*--- Process interface patches in parallel, fetch all donor point coordinates,
+ * then distribute interpolation matrix computation over ranks and threads.
+ * To avoid repeating calls to Collect_VertexInfo we also save the global
+ * indices of the donor points and the mpi rank index that owns them. ---*/
+
+ vector donorCoordinates(nMarkerInt);
+ vector > donorGlobalPoint(nMarkerInt);
+ vector > donorProcessor(nMarkerInt);
+ vector assignedProcessor(nMarkerInt,-1);
+ vector totalWork(nProcessor,0);
+
+ for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; ++iMarkerInt) {
+
+ /*--- On the donor side: find the tag of the boundary sharing the interface. ---*/
+ const auto markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt+1);
+
+ /*--- On the target side: find the tag of the boundary sharing the interface. ---*/
+ const auto markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt+1);
+
+ /*--- If the zone does not contain the interface continue to the next pair of markers. ---*/
+ if(!CheckInterfaceBoundary(markDonor,markTarget)) continue;
+
+ unsigned long nVertexDonor = 0;
+ if(markDonor != -1) nVertexDonor = donor_geometry->GetnVertex(markDonor);
+
+ /*--- Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor. ---*/
+ Determine_ArraySize(false, markDonor, markTarget, nVertexDonor, nDim);
+
+ /*--- Compute total number of donor vertices. ---*/
+ const auto nGlobalVertexDonor = accumulate(Buffer_Receive_nVertex_Donor,
+ Buffer_Receive_nVertex_Donor+nProcessor, 0ul);
+
+ /*--- Gather coordinates and global point indices. ---*/
+ Buffer_Send_Coord = new su2double [ MaxLocalVertex_Donor * nDim ];
+ Buffer_Send_GlobalPoint = new long [ MaxLocalVertex_Donor ];
+ Buffer_Receive_Coord = new su2double [ nProcessor * MaxLocalVertex_Donor * nDim ];
+ Buffer_Receive_GlobalPoint = new long [ nProcessor * MaxLocalVertex_Donor ];
+
+ Collect_VertexInfo(false, markDonor, markTarget, nVertexDonor, nDim);
+
+ /*--- Compresses the gathered donor point information to simplify computations. ---*/
+ auto& donorCoord = donorCoordinates[iMarkerInt];
+ auto& donorPoint = donorGlobalPoint[iMarkerInt];
+ auto& donorProc = donorProcessor[iMarkerInt];
+ donorCoord.resize(nGlobalVertexDonor, nDim);
+ donorPoint.resize(nGlobalVertexDonor);
+ donorProc.resize(nGlobalVertexDonor);
+
+ auto iCount = 0ul;
+ for (int iProcessor = 0; iProcessor < nProcessor; ++iProcessor) {
+ auto offset = iProcessor * MaxLocalVertex_Donor;
+ for (auto iVertex = 0ul; iVertex < Buffer_Receive_nVertex_Donor[iProcessor]; ++iVertex) {
+ for (int iDim = 0; iDim < nDim; ++iDim)
+ donorCoord(iCount,iDim) = Buffer_Receive_Coord[(offset+iVertex)*nDim + iDim];
+ donorPoint[iCount] = Buffer_Receive_GlobalPoint[offset+iVertex];
+ donorProc[iCount] = iProcessor;
+ ++iCount;
+ }
+ }
+ assert((iCount == nGlobalVertexDonor) && "Global donor point count mismatch.");
+
+ delete[] Buffer_Send_Coord;
+ delete[] Buffer_Send_GlobalPoint;
+ delete[] Buffer_Receive_Coord;
+ delete[] Buffer_Receive_GlobalPoint;
+
+ /*--- Static work scheduling over ranks based on which one has less work currently. ---*/
+ int iProcessor = 0;
+ for (int i = 1; i < nProcessor; ++i)
+ if (totalWork[i] < totalWork[iProcessor]) iProcessor = i;
+
+ totalWork[iProcessor] += pow(nGlobalVertexDonor,3); // based on matrix inversion.
+
+ assignedProcessor[iMarkerInt] = iProcessor;
+
+ }
+ delete[] Buffer_Receive_nVertex_Donor;
+
+ /*--- Compute the interpolation matrices for each patch of coordinates
+ * assigned to the rank. Subdivide work further by threads. ---*/
+ vector nPolynomialVec(nMarkerInt,-1);
+ vector > keepPolynomialRowVec(nMarkerInt, vector(nDim,1));
+ vector CinvTrucVec(nMarkerInt);
+
+ SU2_OMP_PARALLEL_(for schedule(dynamic,1))
+ for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; ++iMarkerInt) {
+ if (rank == assignedProcessor[iMarkerInt]) {
+ ComputeGeneratorMatrix(kindRBF, usePolynomial, paramRBF,
+ donorCoordinates[iMarkerInt], nPolynomialVec[iMarkerInt],
+ keepPolynomialRowVec[iMarkerInt], CinvTrucVec[iMarkerInt]);
+ }
+ }
+
+ /*--- Final loop over interface markers to compute the interpolation coefficients. ---*/
+
+ /*--- Initialize variables for interpolation statistics. ---*/
+ unsigned long totalTargetPoints = 0, totalDonorPoints = 0, denseSize = 0;
+ MinDonors = 1<<30; MaxDonors = 0; MaxCorrection = 0.0; AvgCorrection = 0.0;
+
+ for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; iMarkerInt++) {
+
+ /*--- Identify the rank that computed the interpolation matrix for this marker. ---*/
+ const int iProcessor = assignedProcessor[iMarkerInt];
+ /*--- If no processor was assigned to work, the zone does not contain the interface. ---*/
+ if (iProcessor < 0) continue;
+
+ /*--- Setup target information. ---*/
+ const int markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt+1);
+ unsigned long nVertexTarget = 0;
+ if (markTarget != -1) nVertexTarget = target_geometry->GetnVertex(markTarget);
+
+ /*--- Set references to donor information. ---*/
+ auto& donorCoord = donorCoordinates[iMarkerInt];
+ auto& donorPoint = donorGlobalPoint[iMarkerInt];
+ auto& donorProc = donorProcessor[iMarkerInt];
+
+ auto& C_inv_trunc = CinvTrucVec[iMarkerInt];
+ auto& nPolynomial = nPolynomialVec[iMarkerInt];
+ auto& keepPolynomialRow = keepPolynomialRowVec[iMarkerInt];
+
+ const auto nGlobalVertexDonor = donorCoord.rows();
+
+#ifdef HAVE_MPI
+ /*--- For simplicity, broadcast small information about the interpolation matrix. ---*/
+ SU2_MPI::Bcast(&nPolynomial, 1, MPI_INT, iProcessor, MPI_COMM_WORLD);
+ SU2_MPI::Bcast(keepPolynomialRow.data(), nDim, MPI_INT, iProcessor, MPI_COMM_WORLD);
+
+ /*--- Send C_inv_trunc only to the ranks that need it (those with target points),
+ * partial broadcast. MPI wrapper not used due to passive double. ---*/
+ vector allNumVertex(nProcessor);
+ SU2_MPI::Allgather(&nVertexTarget, 1, MPI_UNSIGNED_LONG,
+ allNumVertex.data(), 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
+
+ if (rank == iProcessor) {
+ for (int jProcessor = 0; jProcessor < nProcessor; ++jProcessor)
+ if ((jProcessor != iProcessor) && (allNumVertex[jProcessor] != 0))
+ MPI_Send(C_inv_trunc.data(), C_inv_trunc.size(),
+ MPI_DOUBLE, jProcessor, 0, MPI_COMM_WORLD);
+ }
+ else if (nVertexTarget != 0) {
+ C_inv_trunc.resize(1+nPolynomial+nGlobalVertexDonor, nGlobalVertexDonor);
+ MPI_Recv(C_inv_trunc.data(), C_inv_trunc.size(), MPI_DOUBLE,
+ iProcessor, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
+ }
+#endif
+
+ /*--- Compute interpolation matrix (H). This is a large matrix-matrix product with
+ * the generator matrix (C_inv_trunc) on the right. We avoid instantiation
+ * of the entire function matrix (A) and of the result (H), but work
+ * on a slab (set of rows) of A/H to amortize accesses to C_inv_trunc. ---*/
+
+ /*--- Fetch domain target vertices. ---*/
+
+ vector targetVertices; targetVertices.reserve(nVertexTarget);
+ vector targetCoord; targetCoord.reserve(nVertexTarget);
+
+ for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; ++iVertexTarget) {
+
+ auto targetVertex = target_geometry->vertex[markTarget][iVertexTarget];
+ auto pointTarget = targetVertex->GetNode();
+
+ if (target_geometry->node[pointTarget]->GetDomain()) {
+ targetVertices.push_back(targetVertex);
+ targetCoord.push_back(target_geometry->node[pointTarget]->GetCoord());
+ }
+ }
+ nVertexTarget = targetVertices.size();
+ totalTargetPoints += nVertexTarget;
+ denseSize += nVertexTarget*nGlobalVertexDonor;
+
+ /*--- Distribute target slabs over the threads in the rank for processing. ---*/
+
+ SU2_OMP_PARALLEL
+ if (nVertexTarget > 0) {
+
+ constexpr unsigned long targetSlabSize = 32;
+
+ su2passivematrix funcMat(targetSlabSize, 1+nPolynomial+nGlobalVertexDonor);
+ su2passivematrix interpMat(targetSlabSize, nGlobalVertexDonor);
+
+ /*--- Thread-local variables for statistics. ---*/
+ unsigned long minDonors = 1<<30, maxDonors = 0, totalDonors = 0;
+ passivedouble sumCorr = 0.0, maxCorr = 0.0;
+
+ SU2_OMP_FOR_DYN(1)
+ for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget += targetSlabSize) {
+
+ const auto iLastVertex = min(nVertexTarget, iVertexTarget+targetSlabSize);
+ const auto slabSize = iLastVertex - iVertexTarget;
+
+ /*--- Prepare matrix of functions A (the targets to donors matrix). ---*/
+
+ /*--- Polynominal part: ---*/
+ if (usePolynomial) {
+ /*--- Constant term. ---*/
+ for (auto k = 0ul; k < slabSize; ++k) funcMat(k,0) = 1.0;
+
+ /*--- Linear terms. ---*/
+ for (int iDim = 0, idx = 1; iDim < nDim; ++iDim) {
+ /*--- Of which one may have been excluded. ---*/
+ if (!keepPolynomialRow[iDim]) continue;
+ for (auto k = 0ul; k < slabSize; ++k)
+ funcMat(k, idx) = SU2_TYPE::GetValue(targetCoord[iVertexTarget+k][iDim]);
+ idx += 1;
+ }
+ }
+ /*--- RBF terms: ---*/
+ for (auto iVertexDonor = 0ul; iVertexDonor < nGlobalVertexDonor; ++iVertexDonor) {
+ for (auto k = 0ul; k < slabSize; ++k) {
+ auto dist = PointsDistance(nDim, targetCoord[iVertexTarget+k], donorCoord[iVertexDonor]);
+ auto rbf = Get_RadialBasisValue(kindRBF, paramRBF, dist);
+ funcMat(k, 1+nPolynomial+iVertexDonor) = SU2_TYPE::GetValue(rbf);
+ }
+ }
+
+ /*--- Compute slab of the interpolation matrix. ---*/
+#ifdef HAVE_LAPACK
+ /*--- interpMat = funcMat * C_inv_trunc, but order of gemm arguments
+ * is swapped due to row-major storage of su2passivematrix. ---*/
+ const char op = 'N';
+ const int M = interpMat.cols(), N = slabSize, K = funcMat.cols();
+ // lda = C_inv_trunc.cols() = M; ldb = funcMat.cols() = K; ldc = interpMat.cols() = M;
+ const passivedouble alpha = 1.0, beta = 0.0;
+ DGEMM(&op, &op, &M, &N, &K, &alpha, C_inv_trunc[0], &M, funcMat[0], &K, &beta, interpMat[0], &M);
+#else
+ /*--- Naive product, loop order considers short-wide
+ * nature of funcMat and interpMat. ---*/
+ interpMat = 0.0;
+ for (auto k = 0ul; k < funcMat.cols(); ++k)
+ for (auto i = 0ul; i < slabSize; ++i)
+ for (auto j = 0ul; j < interpMat.cols(); ++j)
+ interpMat(i,j) += funcMat(i,k) * C_inv_trunc(k,j);
+#endif
+ /*--- Set interpolation coefficients. ---*/
+
+ for (auto k = 0ul; k < slabSize; ++k) {
+ auto targetVertex = targetVertices[iVertexTarget+k];
+
+ /*--- Prune small coefficients. ---*/
+ auto info = PruneSmallCoefficients(SU2_TYPE::GetValue(pruneTol), interpMat.cols(), interpMat[k]);
+ auto nnz = info.first;
+ totalDonors += nnz;
+ minDonors = min(minDonors, nnz);
+ maxDonors = max(maxDonors, nnz);
+ auto corr = fabs(info.second-1.0); // far from 1 either way is bad;
+ sumCorr += corr;
+ maxCorr = max(maxCorr, corr);
+
+ /*--- Allocate and set donor information for this target point. ---*/
+ targetVertex->Allocate_DonorInfo(nnz);
+
+ for (unsigned long iVertex = 0, iSet = 0; iVertex < nGlobalVertexDonor; ++iVertex) {
+ auto coeff = interpMat(k,iVertex);
+ if (fabs(coeff) > 0.0) {
+ targetVertex->SetInterpDonorProcessor(iSet, donorProc[iVertex]);
+ targetVertex->SetInterpDonorPoint(iSet, donorPoint[iVertex]);
+ targetVertex->SetDonorCoeff(iSet, coeff);
+ ++iSet;
+ }
+ }
+ }
+ } // end target vertex loop
+
+ SU2_OMP_CRITICAL
+ {
+ totalDonorPoints += totalDonors;
+ MinDonors = min(MinDonors, minDonors);
+ MaxDonors = max(MaxDonors, maxDonors);
+ AvgCorrection += sumCorr;
+ MaxCorrection = max(MaxCorrection, maxCorr);
+ }
+ } // end SU2_OMP_PARALLEL
+
+ /*--- Free global data that will no longer be used. ---*/
+ donorCoord.resize(0,0);
+ vector().swap(donorPoint);
+ vector().swap(donorProc);
+ C_inv_trunc.resize(0,0);
+
+ } // end loop over interface markers
+
+ /*--- Final reduction of interpolation statistics and basic sanity checks. ---*/
+ auto Reduce = [](SU2_MPI::Op op, unsigned long &val) {
+ auto tmp = val;
+ SU2_MPI::Allreduce(&tmp, &val, 1, MPI_UNSIGNED_LONG, op, MPI_COMM_WORLD);
+ };
+ Reduce(MPI_SUM, totalTargetPoints);
+ Reduce(MPI_SUM, totalDonorPoints);
+ Reduce(MPI_SUM, denseSize);
+ Reduce(MPI_MIN, MinDonors);
+ Reduce(MPI_MAX, MaxDonors);
+#ifdef HAVE_MPI
+ passivedouble tmp1 = AvgCorrection, tmp2 = MaxCorrection;
+ MPI_Allreduce(&tmp1, &AvgCorrection, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
+ MPI_Allreduce(&tmp2, &MaxCorrection, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
+#endif
+ if (totalTargetPoints == 0)
+ SU2_MPI::Error("Somehow there are no target interpolation points.", CURRENT_FUNCTION);
+
+ if (MinDonors == 0)
+ SU2_MPI::Error("One or more target points have no donors, either:\n"
+ " - The interface surfaces are not in contact.\n"
+ " - The RBF radius is too small.\n"
+ " - The pruning tolerance is too aggressive.", CURRENT_FUNCTION);
+
+ MaxCorrection += 1.0; // put back the reference "1"
+ AvgCorrection = AvgCorrection / totalTargetPoints + 1.0;
+ AvgDonors = totalDonorPoints / totalTargetPoints;
+ Density = totalDonorPoints / (0.01*denseSize);
+
+}
+
+void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial,
+ su2double radius, const su2activematrix& coords, int& nPolynomial,
+ vector& keepPolynomialRow, su2passivematrix& C_inv_trunc) {
+
+ const su2double interfaceCoordTol = 1e6 * numeric_limits::epsilon();
+
+ const int nVertexDonor = coords.rows();
+ const int nDim = coords.cols();
+
+ /*--- Populate interpolation kernel. ---*/
+ CSymmetricMatrix global_M(nVertexDonor);
+
+ for (int iVertex = 0; iVertex < nVertexDonor; ++iVertex)
+ for (int jVertex = iVertex; jVertex < nVertexDonor; ++jVertex)
+ global_M(iVertex, jVertex) = SU2_TYPE::GetValue(Get_RadialBasisValue(type, radius,
+ PointsDistance(nDim, coords[iVertex], coords[jVertex])));
+
+ /*--- Invert M matrix (operation is in-place). ---*/
+ const bool kernelIsSPD = (type==WENDLAND_C2) || (type==GAUSSIAN) || (type==INV_MULTI_QUADRIC);
+ global_M.Invert(kernelIsSPD);
+
+ /*--- Compute C_inv_trunc. ---*/
+ if (usePolynomial) {
+
+ /*--- Fill P matrix (P for points, with an extra top row of ones). ---*/
+ su2passivematrix P(1+nDim, nVertexDonor);
+
+ for (int iVertex = 0; iVertex < nVertexDonor; iVertex++) {
+ P(0, iVertex) = 1.0;
+ for (int iDim = 0; iDim < nDim; ++iDim)
+ P(1+iDim, iVertex) = SU2_TYPE::GetValue(coords(iVertex, iDim));
+ }
+
+ /*--- Check if points lie on a plane and remove one coordinate from P if so. ---*/
+ nPolynomial = CheckPolynomialTerms(interfaceCoordTol, keepPolynomialRow, P);
+
+ /*--- Compute Q = P * M^-1 ---*/
+ su2passivematrix Q;
+ global_M.MatMatMult('R', P, Q);
+
+ /*--- Compute Mp = (Q * P^T)^-1 ---*/
+ CSymmetricMatrix Mp(nPolynomial+1);
+
+ for (int i = 0; i <= nPolynomial; ++i)
+ for (int j = i; j <= nPolynomial; ++j) {
+ Mp(i,j) = 0.0;
+ for (int k = 0; k < nVertexDonor; ++k)
+ Mp(i,j) += Q(i,k) * P(j,k);
+ }
+ Mp.Invert(false);
+
+ /*--- Compute M_p * Q, the top part of C_inv_trunc. ---*/
+ su2passivematrix C_inv_top;
+ Mp.MatMatMult('L', Q, C_inv_top);
+
+ /*--- Compute M^-1 - Q^T * C_inv_top, the bottom (main) part of C_inv_trunc. ---*/
+ su2passivematrix C_inv_bot = global_M.StealData();
+#ifdef HAVE_LAPACK
+ /*--- Order of gemm arguments swapped due to row-major storage. ---*/
+ const char opa = 'N', opb = 'T';
+ const int M = nVertexDonor, N = nVertexDonor, K = nPolynomial+1;
+ // lda = C_inv_top.cols() = M; ldb = Q.cols() = M; ldc = C_inv_bot.cols() = M;
+ const passivedouble alpha = -1.0, beta = 1.0;
+ DGEMM(&opa, &opb, &M, &N, &K, &alpha, C_inv_top[0], &M, Q[0], &M, &beta, C_inv_bot[0], &M);
+#else // naive product
+ for (int i = 0; i < nVertexDonor; ++i)
+ for (int j = 0; j < nVertexDonor; ++j)
+ for (int k = 0; k <= nPolynomial; ++k)
+ C_inv_bot(i,j) -= Q(k,i) * C_inv_top(k,j);
+#endif
+
+ /*--- Merge top and bottom of C_inv_trunc. More intrusive memory
+ * management, or separate handling of top and bottom, would
+ * avoid these copies (and associated temporary vars). ---*/
+ C_inv_trunc.resize(1+nPolynomial+nVertexDonor, nVertexDonor);
+ memcpy(C_inv_trunc[0], C_inv_top.data(), C_inv_top.size()*sizeof(passivedouble));
+ memcpy(C_inv_trunc[1+nPolynomial], C_inv_bot.data(), C_inv_bot.size()*sizeof(passivedouble));
+ }
+ else {
+ /*--- No polynomial term used in the interpolation, C_inv_trunc = M^-1. ---*/
+
+ C_inv_trunc = global_M.StealData();
+
+ } // end usePolynomial
+
+}
+
+int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row,
+ su2passivematrix &P) {
+ const int m = P.rows();
+ const int n = P.cols();
+
+ /*--- The first row of P is all ones and we do not care about it for this analysis. ---*/
+ const int n_rows = m-1;
+ keep_row.resize(n_rows);
+
+ /*--- By default assume points are not on a plane (all rows kept). ---*/
+ int n_polynomial = n_rows;
+ keep_row.resize(n_rows);
+ for (int i = 0; i < n_rows; ++i) keep_row[i] = 1;
+
+ /*--- Fit a plane through the points in P. ---*/
+
+ /*--- Compute P times its transpose and invert. ---*/
+ CSymmetricMatrix PPT(n_rows);
+
+ for (int i = 0; i < n_rows; ++i)
+ for (int j = i; j < n_rows; ++j) {
+ PPT(i,j) = 0.0;
+ for (int k = 0; k < n; ++k) PPT(i,j) += P(i+1,k) * P(j+1,k);
+ }
+ PPT.Invert(true);
+
+ /*--- RHS for the least squares fit (vector of ones times P). ---*/
+ vector rhs(n_rows,0.0), coeff(n_rows);
+
+ for (int i = 0; i < n_rows; ++i)
+ for (int j = 0; j < n; ++j)
+ rhs[i] += P(i+1,j);
+
+ /*--- Multiply the RHS by the inverse thus obtaining the coefficients. ---*/
+ PPT.MatVecMult(rhs.begin(), coeff.begin());
+
+ /*--- Determine the maximum deviation of the points from the fitted plane. ---*/
+ passivedouble max_diff = 0.0;
+
+ for (int j = 0; j < n; ++j)
+ {
+ passivedouble sum = 0.0;
+ for (int i = 0; i < n_rows; ++i) sum += coeff[i] * P(i+1,j);
+
+ /*--- 1.0 is the arbitrary constant we are assuming when fitting
+ the plane, i.e. the vector of ones used to generate the RHS. ---*/
+ max_diff = max(abs(1.0-sum), max_diff);
+ }
+
+ /*--- If points lie on plane remove row associated with the maximum coefficient. ---*/
+ if (max_diff < max_diff_tol)
+ {
+ /*--- Find the max coeff and mark the corresponding row for removal. ---*/
+ int remove_row = 0;
+ for (int i = 1; i < n_rows; ++i)
+ if (abs(coeff[i]) > abs(coeff[remove_row]))
+ remove_row = i;
+
+ /*--- Mark row as removed and adjust number of polynomial terms. ---*/
+ n_polynomial = n_rows-1;
+ keep_row[remove_row] = 0;
+
+ /*--- Truncated P by shifting rows "up". ---*/
+ for (auto i = remove_row+1; i < m-1; ++i)
+ for (int j = 0; j < n; ++j)
+ P(i,j) = P(i+1,j);
+ }
+
+ return n_polynomial;
+}
diff --git a/Common/src/interface_interpolation/CSlidingMesh.cpp b/Common/src/interface_interpolation/CSlidingMesh.cpp
new file mode 100644
index 000000000000..3fe75ad5c033
--- /dev/null
+++ b/Common/src/interface_interpolation/CSlidingMesh.cpp
@@ -0,0 +1,1264 @@
+/*!
+ * \file CSlidingMesh.cpp
+ * \brief Implementation of sliding mesh interpolation.
+ * \author H. Kline
+ * \version 7.0.2 "Blackbird"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md)
+ *
+ * SU2 is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * SU2 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with SU2. If not, see .
+ */
+
+#include "../../include/interface_interpolation/CSlidingMesh.hpp"
+#include "../../include/CConfig.hpp"
+#include "../../include/geometry/CGeometry.hpp"
+
+
+CSlidingMesh::CSlidingMesh(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone,
+ unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) {
+ Set_TransferCoeff(config);
+}
+
+void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) {
+
+ /* 0 - Variable declaration */
+
+ /* --- General variables --- */
+
+ bool check;
+
+ unsigned short iDim;
+
+ unsigned long ii, jj, *uptr;
+ unsigned long vPoint;
+ unsigned long iEdgeVisited, nEdgeVisited, iNodeVisited;
+ unsigned long nAlreadyVisited, nToVisit, StartVisited;
+
+ unsigned long *alreadyVisitedDonor, *ToVisit, *tmpVect;
+ unsigned long *storeProc, *tmp_storeProc;
+
+ su2double dTMP;
+ su2double *Coeff_Vect, *tmp_Coeff_Vect;
+
+ /* --- Geometrical variables --- */
+
+ su2double *Coord_i, *Coord_j, dist, mindist, *Normal;
+ su2double Area, Area_old, tmp_Area;
+ su2double LineIntersectionLength, *Direction, length;
+
+
+ /* --- Markers Variables --- */
+
+ unsigned short iMarkerInt, nMarkerInt;
+
+ unsigned long iVertex, nVertexTarget;
+
+ int markDonor, markTarget;
+
+ /* --- Target variables --- */
+
+ unsigned long target_iPoint, jVertexTarget;
+ unsigned long nEdges_target, nNode_target;
+
+ unsigned long *Target_nLinkedNodes, *Target_LinkedNodes, *Target_StartLinkedNodes, *target_segment;
+ unsigned long *Target_Proc;
+ long *Target_GlobalPoint, *Donor_GlobalPoint;
+
+ su2double *TargetPoint_Coord, *target_iMidEdge_point, *target_jMidEdge_point, **target_element;
+
+ /* --- Donor variables --- */
+
+ unsigned long donor_StartIndex, donor_forward_point, donor_backward_point, donor_iPoint, donor_OldiPoint;
+ unsigned long nEdges_donor, nNode_donor, nGlobalVertex_Donor;
+
+ unsigned long nDonorPoints, iDonor;
+ unsigned long *Donor_Vect, *tmp_Donor_Vect;
+ unsigned long *Donor_nLinkedNodes, *Donor_LinkedNodes, *Donor_StartLinkedNodes;
+ unsigned long *Donor_Proc;
+
+ su2double *donor_iMidEdge_point, *donor_jMidEdge_point;
+ su2double **donor_element, *DonorPoint_Coord;
+
+ /* 1 - Variable pre-processing */
+
+ const unsigned short nDim = donor_geometry->GetnDim();
+
+ /*--- Setting up auxiliary vectors ---*/
+
+ Donor_Vect = nullptr;
+ Coeff_Vect = nullptr;
+ storeProc = nullptr;
+
+ tmp_Donor_Vect = nullptr;
+ tmp_Coeff_Vect = nullptr;
+ tmp_storeProc = nullptr;
+
+ Normal = new su2double[nDim];
+ Direction = new su2double[nDim];
+
+
+ /* 2 - Find boundary tag between touching grids */
+
+ /*--- Number of markers on the FSI interface ---*/
+ nMarkerInt = (int)( config[ donorZone ]->GetMarker_n_ZoneInterface() ) / 2;
+
+ /*--- For the number of markers on the interface... ---*/
+ for ( iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++ ){
+
+ /*--- On the donor side: find the tag of the boundary sharing the interface ---*/
+ markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt);
+
+ /*--- On the target side: find the tag of the boundary sharing the interface ---*/
+ markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt);
+
+ /*--- Checks if the zone contains the interface, if not continue to the next step ---*/
+ if(!CheckInterfaceBoundary(markDonor, markTarget)) continue;
+
+ if(markTarget != -1)
+ nVertexTarget = target_geometry->GetnVertex( markTarget );
+ else
+ nVertexTarget = 0;
+
+ /*
+ 3 -Reconstruct the boundaries from parallel partitioning
+ */
+
+ /*--- Target boundary ---*/
+ ReconstructBoundary(targetZone, markTarget);
+
+ nGlobalVertex_Target = nGlobalVertex;
+
+ TargetPoint_Coord = Buffer_Receive_Coord;
+ Target_GlobalPoint = Buffer_Receive_GlobalPoint;
+ Target_nLinkedNodes = Buffer_Receive_nLinkedNodes;
+ Target_StartLinkedNodes = Buffer_Receive_StartLinkedNodes;
+ Target_LinkedNodes = Buffer_Receive_LinkedNodes;
+ Target_Proc = Buffer_Receive_Proc;
+
+ /*--- Donor boundary ---*/
+ ReconstructBoundary(donorZone, markDonor);
+
+ nGlobalVertex_Donor = nGlobalVertex;
+
+ DonorPoint_Coord = Buffer_Receive_Coord;
+ Donor_GlobalPoint = Buffer_Receive_GlobalPoint;
+ Donor_nLinkedNodes = Buffer_Receive_nLinkedNodes;
+ Donor_StartLinkedNodes = Buffer_Receive_StartLinkedNodes;
+ Donor_LinkedNodes = Buffer_Receive_LinkedNodes;
+ Donor_Proc = Buffer_Receive_Proc;
+
+ /*--- Starts building the supermesh layer (2D or 3D) ---*/
+ /* - For each target node, it first finds the closest donor point
+ * - Then it creates the supermesh in the close proximity of the target point:
+ * - Starting from the closest donor node, it expands the supermesh by including
+ * donor elements neighboring the initial one, until the overall target area is fully covered.
+ */
+
+ if(nDim == 2){
+
+ target_iMidEdge_point = new su2double[nDim];
+ target_jMidEdge_point = new su2double[nDim];
+
+ donor_iMidEdge_point = new su2double[nDim];
+ donor_jMidEdge_point = new su2double[nDim];
+
+ /*--- Starts with supermesh reconstruction ---*/
+
+ target_segment = new unsigned long[2];
+
+ for (iVertex = 0; iVertex < nVertexTarget; iVertex++) {
+
+ nDonorPoints = 0;
+
+ /*--- Stores coordinates of the target node ---*/
+
+ target_iPoint = target_geometry->vertex[markTarget][iVertex]->GetNode();
+
+ if (target_geometry->node[target_iPoint]->GetDomain()){
+
+ Coord_i = target_geometry->node[target_iPoint]->GetCoord();
+
+ /*--- Brute force to find the closest donor_node ---*/
+
+ mindist = 1E6;
+ donor_StartIndex = 0;
+
+ for (donor_iPoint = 0; donor_iPoint < nGlobalVertex_Donor; donor_iPoint++) {
+
+ Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ];
+
+ dist = PointsDistance(nDim, Coord_i, Coord_j);
+
+ if (dist < mindist) {
+ mindist = dist;
+ donor_StartIndex = donor_iPoint;
+ }
+
+ if (dist == 0.0){
+ donor_StartIndex = donor_iPoint;
+ break;
+ }
+ }
+
+ donor_iPoint = donor_StartIndex;
+ donor_OldiPoint = donor_iPoint;
+
+ /*--- Contruct information regarding the target cell ---*/
+
+ long dPoint = target_geometry->node[target_iPoint]->GetGlobalIndex();
+ for (jVertexTarget = 0; jVertexTarget < nGlobalVertex_Target; jVertexTarget++)
+ if( dPoint == Target_GlobalPoint[jVertexTarget] )
+ break;
+
+ if ( Target_nLinkedNodes[jVertexTarget] == 1 ){
+ target_segment[0] = Target_LinkedNodes[ Target_StartLinkedNodes[jVertexTarget] ];
+ target_segment[1] = jVertexTarget;
+ }
+ else{
+ target_segment[0] = Target_LinkedNodes[ Target_StartLinkedNodes[jVertexTarget] ];
+ target_segment[1] = Target_LinkedNodes[ Target_StartLinkedNodes[jVertexTarget] + 1];
+ }
+
+ dTMP = 0;
+ for(iDim = 0; iDim < nDim; iDim++){
+ target_iMidEdge_point[iDim] = ( TargetPoint_Coord[ nDim * target_segment[0] + iDim ] +
+ target_geometry->node[ target_iPoint ]->GetCoord(iDim) ) / 2;
+ target_jMidEdge_point[iDim] = ( TargetPoint_Coord[ nDim * target_segment[1] + iDim ] +
+ target_geometry->node[ target_iPoint ]->GetCoord(iDim) ) / 2;
+
+ Direction[iDim] = target_jMidEdge_point[iDim] - target_iMidEdge_point[iDim];
+ dTMP += Direction[iDim] * Direction[iDim];
+ }
+
+ dTMP = sqrt(dTMP);
+ for(iDim = 0; iDim < nDim; iDim++)
+ Direction[iDim] /= dTMP;
+
+ length = PointsDistance(nDim, target_iMidEdge_point, target_jMidEdge_point);
+
+ check = false;
+
+ /*--- Proceeds along the forward direction (depending on which connected boundary node is found first) ---*/
+
+ while( !check ){
+
+ /*--- Proceeds until the value of the intersection area is null ---*/
+
+ if ( Donor_nLinkedNodes[donor_iPoint] == 1 ){
+ donor_forward_point = Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_iPoint] ];
+ donor_backward_point = donor_iPoint;
+ }
+ else{
+ uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_iPoint] ];
+
+ if( donor_OldiPoint != uptr[0] ){
+ donor_forward_point = uptr[0];
+ donor_backward_point = uptr[1];
+ }
+ else{
+ donor_forward_point = uptr[1];
+ donor_backward_point = uptr[0];
+ }
+ }
+
+ if(donor_iPoint >= nGlobalVertex_Donor){
+ check = true;
+ continue;
+ }
+
+ for(iDim = 0; iDim < nDim; iDim++){
+ donor_iMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_forward_point * nDim + iDim] +
+ DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2;
+ donor_jMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_backward_point * nDim + iDim] +
+ DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2;
+ }
+
+ LineIntersectionLength = ComputeLineIntersectionLength(nDim, target_iMidEdge_point, target_jMidEdge_point,
+ donor_iMidEdge_point, donor_jMidEdge_point, Direction);
+
+ if ( LineIntersectionLength == 0.0 ){
+ check = true;
+ continue;
+ }
+
+ /*--- In case the element intersects the target cell, update the auxiliary communication data structure ---*/
+
+ tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ];
+ tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ];
+ tmp_storeProc = new unsigned long[ nDonorPoints + 1 ];
+
+ for( iDonor = 0; iDonor < nDonorPoints; iDonor++){
+ tmp_Donor_Vect[iDonor] = Donor_Vect[iDonor];
+ tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor];
+ tmp_storeProc[iDonor] = storeProc[iDonor];
+ }
+
+ tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint;
+ tmp_Coeff_Vect[ nDonorPoints ] = LineIntersectionLength / length;
+ tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint];
+
+ if (Donor_Vect != nullptr) delete [] Donor_Vect;
+ if (Coeff_Vect != nullptr) delete [] Coeff_Vect;
+ if (storeProc != nullptr) delete [] storeProc;
+
+ Donor_Vect = tmp_Donor_Vect;
+ Coeff_Vect = tmp_Coeff_Vect;
+ storeProc = tmp_storeProc;
+
+ donor_OldiPoint = donor_iPoint;
+ donor_iPoint = donor_forward_point;
+
+ nDonorPoints++;
+ }
+
+ if ( Donor_nLinkedNodes[donor_StartIndex] == 2 ){
+ check = false;
+
+ uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_StartIndex] ];
+
+ donor_iPoint = uptr[1];
+ donor_OldiPoint = donor_StartIndex;
+ }
+ else
+ check = true;
+
+ /*--- Proceeds along the backward direction (depending on which connected boundary node is found first) ---*/
+
+ while( !check ){
+
+ /*--- Proceeds until the value of the intersection length is null ---*/
+ if ( Donor_nLinkedNodes[donor_iPoint] == 1 ){
+ donor_forward_point = donor_OldiPoint;
+ donor_backward_point = donor_iPoint;
+ }
+ else{
+ uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_iPoint] ];
+
+ if( donor_OldiPoint != uptr[0] ){
+ donor_forward_point = uptr[0];
+ donor_backward_point = uptr[1];
+ }
+ else{
+ donor_forward_point = uptr[1];
+ donor_backward_point = uptr[0];
+ }
+ }
+
+ if(donor_iPoint >= nGlobalVertex_Donor){
+ check = true;
+ continue;
+ }
+
+ for(iDim = 0; iDim < nDim; iDim++){
+ donor_iMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_forward_point * nDim + iDim] +
+ DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2;
+ donor_jMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_backward_point * nDim + iDim] +
+ DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2;
+ }
+
+ LineIntersectionLength = ComputeLineIntersectionLength(nDim, target_iMidEdge_point, target_jMidEdge_point,
+ donor_iMidEdge_point, donor_jMidEdge_point, Direction);
+
+ if ( LineIntersectionLength == 0.0 ){
+ check = true;
+ continue;
+ }
+
+ /*--- In case the element intersects the target cell, update the auxiliary communication data structure ---*/
+
+ tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ];
+ tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ];
+ tmp_storeProc = new unsigned long[ nDonorPoints + 1 ];
+
+ for( iDonor = 0; iDonor < nDonorPoints; iDonor++){
+ tmp_Donor_Vect[iDonor] = Donor_Vect[iDonor];
+ tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor];
+ tmp_storeProc[iDonor] = storeProc[iDonor];
+ }
+
+ tmp_Coeff_Vect[ nDonorPoints ] = LineIntersectionLength / length;
+ tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint;
+ tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint];
+
+ if (Donor_Vect != nullptr) delete [] Donor_Vect;
+ if (Coeff_Vect != nullptr) delete [] Coeff_Vect;
+ if (storeProc != nullptr) delete [] storeProc;
+
+ Donor_Vect = tmp_Donor_Vect;
+ Coeff_Vect = tmp_Coeff_Vect;
+ storeProc = tmp_storeProc;
+
+ donor_OldiPoint = donor_iPoint;
+ donor_iPoint = donor_forward_point;
+
+ nDonorPoints++;
+ }
+
+ /*--- Set the communication data structure and copy data from the auxiliary vectors ---*/
+
+ target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(nDonorPoints);
+
+ for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){
+ target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor, Coeff_Vect[iDonor]);
+ target_geometry->vertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor, Donor_GlobalPoint[Donor_Vect[iDonor]]);
+ target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]);
+ }
+ }
+ }
+
+ delete [] target_segment;
+
+ delete [] target_iMidEdge_point;
+ delete [] target_jMidEdge_point;
+
+ delete [] donor_iMidEdge_point;
+ delete [] donor_jMidEdge_point;
+ }
+ else{
+ /* --- 3D geometry, creates a superficial super-mesh --- */
+
+ for (iVertex = 0; iVertex < nVertexTarget; iVertex++) {
+
+ nDonorPoints = 0;
+
+ /*--- Stores coordinates of the target node ---*/
+
+ target_iPoint = target_geometry->vertex[markTarget][iVertex]->GetNode();
+
+ if (!target_geometry->node[target_iPoint]->GetDomain()) continue;
+
+ Coord_i = target_geometry->node[target_iPoint]->GetCoord();
+
+ target_geometry->vertex[markTarget][iVertex]->GetNormal(Normal);
+
+ /*--- The value of Area computed here includes also portion of boundary belonging to different marker ---*/
+ Area = 0.0;
+ for (iDim = 0; iDim < nDim; iDim++)
+ Area += Normal[iDim]*Normal[iDim];
+ Area = sqrt(Area);
+
+ for (iDim = 0; iDim < nDim; iDim++)
+ Normal[iDim] /= Area;
+
+ for (iDim = 0; iDim < nDim; iDim++)
+ Coord_i[iDim] = target_geometry->node[target_iPoint]->GetCoord(iDim);
+
+ long dPoint = target_geometry->node[target_iPoint]->GetGlobalIndex();
+ for (target_iPoint = 0; target_iPoint < nGlobalVertex_Target; target_iPoint++){
+ if( dPoint == Target_GlobalPoint[target_iPoint] )
+ break;
+ }
+
+ /*--- Build local surface dual mesh for target element ---*/
+
+ nEdges_target = Target_nLinkedNodes[target_iPoint];
+
+ nNode_target = 2*(nEdges_target + 1);
+
+ target_element = new su2double*[nNode_target];
+ for (ii = 0; ii < nNode_target; ii++)
+ target_element[ii] = new su2double[nDim];
+
+ nNode_target = Build_3D_surface_element(Target_LinkedNodes, Target_StartLinkedNodes, Target_nLinkedNodes,
+ TargetPoint_Coord, target_iPoint, target_element);
+
+ /*--- Brute force to find the closest donor_node ---*/
+
+ mindist = 1E6;
+ donor_StartIndex = 0;
+
+ for (donor_iPoint = 0; donor_iPoint < nGlobalVertex_Donor; donor_iPoint++) {
+
+ Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ];
+
+ dist = PointsDistance(nDim, Coord_i, Coord_j);
+
+ if (dist < mindist) {
+ mindist = dist;
+ donor_StartIndex = donor_iPoint;
+ }
+
+ if (dist == 0.0){
+ donor_StartIndex = donor_iPoint;
+ break;
+ }
+ }
+
+ donor_iPoint = donor_StartIndex;
+
+ nEdges_donor = Donor_nLinkedNodes[donor_iPoint];
+
+ donor_element = new su2double*[ 2*nEdges_donor + 2 ];
+ for (ii = 0; ii < 2*nEdges_donor + 2; ii++)
+ donor_element[ii] = new su2double[nDim];
+
+ nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes,
+ DonorPoint_Coord, donor_iPoint, donor_element);
+
+ Area = 0;
+ for (ii = 1; ii < nNode_target-1; ii++){
+ for (jj = 1; jj < nNode_donor-1; jj++){
+ Area += Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1],
+ donor_element[0], donor_element[jj], donor_element[jj+1], Normal);
+ }
+ }
+
+ for (ii = 0; ii < 2*nEdges_donor + 2; ii++)
+ delete [] donor_element[ii];
+ delete [] donor_element;
+
+ nDonorPoints = 1;
+
+ /*--- In case the element intersect the target cell update the auxiliary communication data structure ---*/
+
+ Coeff_Vect = new su2double[ nDonorPoints ];
+ Donor_Vect = new unsigned long[ nDonorPoints ];
+ storeProc = new unsigned long[ nDonorPoints ];
+
+ Coeff_Vect[0] = Area;
+ Donor_Vect[0] = donor_iPoint;
+ storeProc[0] = Donor_Proc[donor_iPoint];
+
+ alreadyVisitedDonor = new unsigned long[1];
+
+ alreadyVisitedDonor[0] = donor_iPoint;
+ nAlreadyVisited = 1;
+ StartVisited = 0;
+
+ Area_old = -1;
+
+ while( Area > Area_old ){
+
+ /*
+ * - Starting from the closest donor_point, it expands the supermesh by a countour search pattern.
+ * - The closest donor element becomes the core, at each iteration a new layer of elements around the core is taken into account
+ */
+
+ Area_old = Area;
+
+ ToVisit = nullptr;
+ nToVisit = 0;
+
+ for( iNodeVisited = StartVisited; iNodeVisited < nAlreadyVisited; iNodeVisited++ ){
+
+ vPoint = alreadyVisitedDonor[ iNodeVisited ];
+
+ nEdgeVisited = Donor_nLinkedNodes[vPoint];
+
+ for (iEdgeVisited = 0; iEdgeVisited < nEdgeVisited; iEdgeVisited++){
+
+ donor_iPoint = Donor_LinkedNodes[ Donor_StartLinkedNodes[vPoint] + iEdgeVisited];
+
+ /*--- Check if the node to visit is already listed in the data structure to avoid double visits ---*/
+
+ check = 0;
+
+ for( jj = 0; jj < nAlreadyVisited; jj++ ){
+ if( donor_iPoint == alreadyVisitedDonor[jj] ){
+ check = 1;
+ break;
+ }
+ }
+
+ if( check == 0 && ToVisit != nullptr){
+ for( jj = 0; jj < nToVisit; jj++ )
+ if( donor_iPoint == ToVisit[jj] ){
+ check = 1;
+ break;
+ }
+ }
+
+ if( check == 0 ){
+ /*--- If the node was not already visited, visit it and list it into data structure ---*/
+
+ tmpVect = new unsigned long[ nToVisit + 1 ];
+
+ for( jj = 0; jj < nToVisit; jj++ )
+ tmpVect[jj] = ToVisit[jj];
+ tmpVect[nToVisit] = donor_iPoint;
+
+ if( ToVisit != nullptr )
+ delete [] ToVisit;
+
+ ToVisit = tmpVect;
+ tmpVect = nullptr;
+
+ nToVisit++;
+
+ /*--- Find the value of the intersection area between the current donor element and the target element --- */
+
+ nEdges_donor = Donor_nLinkedNodes[donor_iPoint];
+
+ donor_element = new su2double*[ 2*nEdges_donor + 2 ];
+ for (ii = 0; ii < 2*nEdges_donor + 2; ii++)
+ donor_element[ii] = new su2double[nDim];
+
+ nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes,
+ DonorPoint_Coord, donor_iPoint, donor_element);
+
+ tmp_Area = 0;
+ for (ii = 1; ii < nNode_target-1; ii++)
+ for (jj = 1; jj < nNode_donor-1; jj++)
+ tmp_Area += Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1],
+ donor_element[0], donor_element[jj], donor_element[jj+1], Normal);
+
+ for (ii = 0; ii < 2*nEdges_donor + 2; ii++)
+ delete [] donor_element[ii];
+ delete [] donor_element;
+
+ /*--- In case the element intersect the target cell update the auxiliary communication data structure ---*/
+
+ tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ];
+ tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ];
+ tmp_storeProc = new unsigned long[ nDonorPoints + 1 ];
+
+ for( iDonor = 0; iDonor < nDonorPoints; iDonor++){
+ tmp_Donor_Vect[iDonor] = Donor_Vect[iDonor];
+ tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor];
+ tmp_storeProc[iDonor] = storeProc[iDonor];
+ }
+
+ tmp_Coeff_Vect[ nDonorPoints ] = tmp_Area;
+ tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint;
+ tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint];
+
+ if (Donor_Vect != nullptr) {delete [] Donor_Vect; }
+ if (Coeff_Vect != nullptr) {delete [] Coeff_Vect; }
+ if (storeProc != nullptr) {delete [] storeProc; }
+
+ Donor_Vect = tmp_Donor_Vect;
+ Coeff_Vect = tmp_Coeff_Vect;
+ storeProc = tmp_storeProc;
+
+ tmp_Coeff_Vect = nullptr;
+ tmp_Donor_Vect = nullptr;
+ tmp_storeProc = nullptr;
+
+ nDonorPoints++;
+
+ Area += tmp_Area;
+ }
+ }
+ }
+
+ /*--- Update auxiliary data structure ---*/
+
+ StartVisited = nAlreadyVisited;
+
+ tmpVect = new unsigned long[ nAlreadyVisited + nToVisit ];
+
+ for( jj = 0; jj < nAlreadyVisited; jj++ )
+ tmpVect[jj] = alreadyVisitedDonor[jj];
+
+ for( jj = 0; jj < nToVisit; jj++ )
+ tmpVect[ nAlreadyVisited + jj ] = ToVisit[jj];
+
+ if( alreadyVisitedDonor != nullptr )
+ delete [] alreadyVisitedDonor;
+
+ alreadyVisitedDonor = tmpVect;
+
+ nAlreadyVisited += nToVisit;
+
+ delete [] ToVisit;
+ }
+
+ delete [] alreadyVisitedDonor;
+
+ /*--- Set the communication data structure and copy data from the auxiliary vectors ---*/
+
+ target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(nDonorPoints);
+
+ for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){
+ target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor, Coeff_Vect[iDonor]/Area);
+ target_geometry->vertex[markTarget][iVertex]->SetInterpDonorPoint( iDonor, Donor_GlobalPoint[ Donor_Vect[iDonor] ] );
+ target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]);
+ }
+
+ for (ii = 0; ii < 2*nEdges_target + 2; ii++)
+ delete [] target_element[ii];
+ delete [] target_element;
+
+ delete [] Donor_Vect; Donor_Vect = nullptr;
+ delete [] Coeff_Vect; Coeff_Vect = nullptr;
+ delete [] storeProc; storeProc = nullptr;
+ }
+ }
+
+ delete [] TargetPoint_Coord;
+ delete [] Target_GlobalPoint;
+ delete [] Target_Proc;
+ delete [] Target_nLinkedNodes;
+ delete [] Target_LinkedNodes;
+ delete [] Target_StartLinkedNodes;
+
+ delete [] DonorPoint_Coord;
+ delete [] Donor_GlobalPoint;
+ delete [] Donor_Proc;
+ delete [] Donor_nLinkedNodes;
+ delete [] Donor_StartLinkedNodes;
+ delete [] Donor_LinkedNodes;
+
+ }
+
+ delete [] Normal;
+ delete [] Direction;
+
+ if (Donor_Vect != nullptr) delete [] Donor_Vect;
+ if (Coeff_Vect != nullptr) delete [] Coeff_Vect;
+ if (storeProc != nullptr) delete [] storeProc;
+}
+
+int CSlidingMesh::Build_3D_surface_element(const unsigned long *map, const unsigned long *startIndex,
+ const unsigned long* nNeighbor, const su2double *coord,
+ unsigned long centralNode, su2double** element) {
+
+ /*--- Given a node "centralNode", this routines reconstruct the vertex centered
+ * surface element around the node and store it into "element" ---*/
+
+ unsigned long iNode, jNode, kNode, iElementNode, iPoint, jPoint, nOuterNodes;
+
+ constexpr unsigned short nDim = 3;
+ unsigned short iDim, nTmp;
+
+ int NextNode, **OuterNodesNeighbour, CurrentNode, StartIndex, count;
+ const unsigned long *OuterNodes, *ptr;
+
+ /* --- Store central node as element first point --- */
+
+ for (iDim = 0; iDim < nDim; iDim++)
+ element[0][iDim] = coord[centralNode * nDim + iDim];
+
+ nOuterNodes = nNeighbor[centralNode];
+
+ OuterNodes = &map[ startIndex[centralNode] ];
+
+ /* --- Allocate auxiliary structure, vectors are longer than needed but this avoid further re-allocations due to length variation --- */
+
+ OuterNodesNeighbour = new int*[nOuterNodes];
+ for ( iNode = 0; iNode < nOuterNodes; iNode++ )
+ OuterNodesNeighbour[ iNode ] = new int[2];
+
+ /* --- Finds which and how many nodes belong to the specified marker, initialize some variables --- */
+
+ for ( iNode = 0; iNode < nOuterNodes; iNode++ ){
+ OuterNodesNeighbour[ iNode ][0] = -1;
+ OuterNodesNeighbour[ iNode ][1] = -1;
+ }
+
+ /* --- For each outer node, the program finds the two neighbouring outer nodes --- */
+
+ StartIndex = 0;
+ for( iNode = 0; iNode < nOuterNodes; iNode++ ){
+
+ count = 0;
+ iPoint = OuterNodes[ iNode ];
+ ptr = &map[ startIndex[iPoint] ];
+ nTmp = nNeighbor[iPoint];
+
+ for ( jNode = 0; jNode < nTmp; jNode++ ){
+ jPoint = ptr[jNode];
+ for( kNode = 0; kNode < nOuterNodes; kNode++ ){
+ if ( jPoint == OuterNodes[ kNode ] && jPoint != centralNode){
+ OuterNodesNeighbour[iNode][count] = (int)kNode;
+ count++;
+ break;
+ }
+ }
+ }
+
+ // If the central node belongs to two different markers, ie at corners, makes this outer node the starting point for reconstructing the element
+ if( count == 1 )
+ StartIndex = (int)iNode;
+ }
+
+ /* --- Build element, starts from one outer node and loops along the external edges until the element is reconstructed --- */
+
+ CurrentNode = StartIndex;
+ NextNode = OuterNodesNeighbour[ CurrentNode ][0];
+ iElementNode = 1;
+
+ while( NextNode != -1 ){
+
+ for (iDim = 0; iDim < nDim; iDim++)
+ element[ iElementNode ][iDim] = ( element[0][iDim] + coord[ OuterNodes[ CurrentNode ] * nDim + iDim ])/2;
+
+ iElementNode++;
+
+ for (iDim = 0; iDim < nDim; iDim++)
+ element[ iElementNode ][iDim] = ( element[0][iDim] + coord[ OuterNodes[ CurrentNode ] * nDim + iDim] +
+ coord[ OuterNodes[ NextNode ] * nDim + iDim] )/3;
+ iElementNode++;
+
+ if( OuterNodesNeighbour[ NextNode ][0] == CurrentNode){
+ CurrentNode = NextNode;
+ NextNode = OuterNodesNeighbour[ NextNode ][1];
+ }
+ else{
+ CurrentNode = NextNode;
+ NextNode = OuterNodesNeighbour[ NextNode ][0];
+ }
+
+ if (CurrentNode == StartIndex)
+ break;
+ }
+
+ if( CurrentNode == StartIndex ){ // This is a closed element, so add again element 1 to the end of the structure, useful later
+
+ for (iDim = 0; iDim < nDim; iDim++)
+ element[ iElementNode ][iDim] = element[1][iDim];
+ iElementNode++;
+ }
+ else{
+ for (iDim = 0; iDim < nDim; iDim++)
+ element[ iElementNode ][iDim] = ( element[0][iDim] + coord[ OuterNodes[ CurrentNode ] * nDim + iDim] )/2;
+ iElementNode++;
+ }
+
+ for ( iNode = 0; iNode < nOuterNodes; iNode++ )
+ delete [] OuterNodesNeighbour[ iNode ];
+ delete [] OuterNodesNeighbour;
+
+ return (int)iElementNode;
+
+}
+
+su2double CSlidingMesh::ComputeLineIntersectionLength(unsigned short nDim, const su2double* A1, const su2double* A2,
+ const su2double* B1, const su2double* B2, const su2double* Direction) {
+
+ /*--- Given 2 segments, each defined by 2 points, it projects them along a given direction
+ * and it computes the length of the segment resulting from their intersection ---*/
+ /*--- The algorithm works for both 2D and 3D problems ---*/
+
+ unsigned short iDim;
+
+ su2double dotA2, dotB1, dotB2;
+
+ dotA2 = 0;
+ for(iDim = 0; iDim < nDim; iDim++)
+ dotA2 += ( A2[iDim] - A1[iDim] ) * Direction[iDim];
+
+ if( dotA2 >= 0 ){
+ dotB1 = 0;
+ dotB2 = 0;
+ for(iDim = 0; iDim < nDim; iDim++){
+ dotB1 += ( B1[iDim] - A1[iDim] ) * Direction[iDim];
+ dotB2 += ( B2[iDim] - A1[iDim] ) * Direction[iDim];
+ }
+ }
+ else{
+ dotA2 *= -1;
+
+ dotB1 = 0;
+ dotB2 = 0;
+ for(iDim = 0; iDim < nDim; iDim++){
+ dotB1 -= ( B1[iDim] - A1[iDim] ) * Direction[iDim];
+ dotB2 -= ( B2[iDim] - A1[iDim] ) * Direction[iDim];
+ }
+ }
+
+ if( dotB1 >= 0 && dotB1 <= dotA2 ){
+ if ( dotB2 < 0 )
+ return fabs( dotB1 );
+ if ( dotB2 > dotA2 )
+ return fabs( dotA2 - dotB1 );
+
+ return fabs( dotB1 - dotB2 );
+ }
+
+ if( dotB2 >= 0 && dotB2 <= dotA2 ){
+ if ( dotB1 < 0 )
+ return fabs(dotB2);
+ if ( dotB1 > dotA2 )
+ return fabs( dotA2 - dotB2 );
+ }
+
+ if( ( dotB1 <= 0 && dotA2 <= dotB2 ) || ( dotB2 <= 0 && dotA2 <= dotB1 ) )
+ return fabs( dotA2 );
+
+ return 0.0;
+}
+
+su2double CSlidingMesh::Compute_Triangle_Intersection(const su2double* A1, const su2double* A2, const su2double* A3,
+ const su2double* B1, const su2double* B2, const su2double* B3,
+ const su2double* Direction) {
+
+ /* --- This routine is ONLY for 3D grids --- */
+ /* --- Projects triangle points onto a plane, specified by its normal "Direction", and calls the ComputeIntersectionArea routine --- */
+
+ unsigned short iDim;
+ constexpr unsigned short nDim = 3;
+
+ su2double I[3], J[3], K[3];
+ su2double a1[3], a2[3], a3[3];
+ su2double b1[3], b2[3], b3[3];
+ su2double m1, m2;
+
+ /* --- Reference frame is determined by: x = A1A2 y = x ^ ( -Direction ) --- */
+
+ for(iDim = 0; iDim < 3; iDim++){
+ a1[iDim] = 0;
+ a2[iDim] = 0;
+ a3[iDim] = 0;
+
+ b1[iDim] = 0;
+ b2[iDim] = 0;
+ b3[iDim] = 0;
+ }
+
+ m1 = 0;
+ for(iDim = 0; iDim < nDim; iDim++){
+ K[iDim] = Direction[iDim];
+
+ m1 += K[iDim] * K[iDim];
+ }
+
+ for(iDim = 0; iDim < nDim; iDim++)
+ K[iDim] /= sqrt(m1);
+
+ m2 = 0;
+ for(iDim = 0; iDim < nDim; iDim++)
+ m2 += (A2[iDim] - A1[iDim]) * K[iDim];
+
+ m1 = 0;
+ for(iDim = 0; iDim < nDim; iDim++){
+ I[iDim] = (A2[iDim] - A1[iDim]) - m2 * K[iDim];
+ m1 += I[iDim] * I[iDim];
+ }
+
+ for(iDim = 0; iDim < nDim; iDim++)
+ I[iDim] /= sqrt(m1);
+
+ // Cross product to find Y
+ J[0] = K[1]*I[2] - K[2]*I[1];
+ J[1] = -(K[0]*I[2] - K[2]*I[0]);
+ J[2] = K[0]*I[1] - K[1]*I[0];
+
+ /* --- Project all points on the plane specified by Direction and change their reference frame taking A1 as origin --- */
+
+ for(iDim = 0; iDim < nDim; iDim++){
+ a2[0] += (A2[iDim] - A1[iDim]) * I[iDim];
+ a2[1] += (A2[iDim] - A1[iDim]) * J[iDim];
+ a2[2] += (A2[iDim] - A1[iDim]) * K[iDim];
+
+ a3[0] += (A3[iDim] - A1[iDim]) * I[iDim];
+ a3[1] += (A3[iDim] - A1[iDim]) * J[iDim];
+ a3[2] += (A3[iDim] - A1[iDim]) * K[iDim];
+
+ b1[0] += (B1[iDim] - A1[iDim]) * I[iDim];
+ b1[1] += (B1[iDim] - A1[iDim]) * J[iDim];
+ b1[2] += (B1[iDim] - A1[iDim]) * K[iDim];
+
+ b2[0] += (B2[iDim] - A1[iDim]) * I[iDim];
+ b2[1] += (B2[iDim] - A1[iDim]) * J[iDim];
+ b2[2] += (B2[iDim] - A1[iDim]) * K[iDim];
+
+ b3[0] += (B3[iDim] - A1[iDim]) * I[iDim];
+ b3[1] += (B3[iDim] - A1[iDim]) * J[iDim];
+ b3[2] += (B3[iDim] - A1[iDim]) * K[iDim];
+ }
+
+ /*--- Compute intersection area ---*/
+
+ return ComputeIntersectionArea( a1, a2, a3, b1, b2, b3 );
+}
+
+su2double CSlidingMesh::ComputeIntersectionArea(const su2double* P1, const su2double* P2, const su2double* P3,
+ const su2double* Q1, const su2double* Q2, const su2double* Q3) {
+
+ /* --- This routines computes the area of the polygonal element generated by the superimposition of 2 planar triangle --- */
+ /* --- The 2 triangle must lie on the same plane --- */
+
+ unsigned short iDim, nPoints = 0, i, j, k;
+ unsigned short min_theta_index;
+
+ su2double points[16][2], IntersectionPoint[2], theta[6];
+ su2double TriangleP[4][2], TriangleQ[4][2];
+ su2double Area, det, dot1, dot2, dtmp, min_theta;
+
+ constexpr unsigned short nDim = 2;
+
+ for(iDim = 0; iDim < nDim; iDim++){
+ TriangleP[0][iDim] = 0;
+ TriangleP[1][iDim] = P2[iDim] - P1[iDim];
+ TriangleP[2][iDim] = P3[iDim] - P1[iDim];
+ TriangleP[3][iDim] = 0;
+
+ TriangleQ[0][iDim] = Q1[iDim] - P1[iDim];
+ TriangleQ[1][iDim] = Q2[iDim] - P1[iDim];
+ TriangleQ[2][iDim] = Q3[iDim] - P1[iDim];
+ TriangleQ[3][iDim] = Q1[iDim] - P1[iDim];
+ }
+
+
+ for( j = 0; j < 3; j++){
+ if( CheckPointInsideTriangle(TriangleP[j], TriangleQ[0], TriangleQ[1], TriangleQ[2]) ){
+
+ // Then P1 is also inside triangle Q, so store it
+ for(iDim = 0; iDim < nDim; iDim++)
+ points[nPoints][iDim] = TriangleP[j][iDim];
+
+ nPoints++;
+ }
+ }
+
+ for( j = 0; j < 3; j++){
+ if( CheckPointInsideTriangle(TriangleQ[j], TriangleP[0], TriangleP[1], TriangleP[2]) ){
+
+ // Then Q1 is also inside triangle P, so store it
+ for(iDim = 0; iDim < nDim; iDim++)
+ points[nPoints][iDim] = TriangleQ[j][iDim];
+
+ nPoints++;
+ }
+ }
+
+
+ // Compute all edge intersections
+
+ for( j = 0; j < 3; j++){
+ for( i = 0; i < 3; i++){
+
+ det = (TriangleP[j][0] - TriangleP[j+1][0]) * (TriangleQ[i][1] - TriangleQ[i+1][1]) -
+ (TriangleP[j][1] - TriangleP[j+1][1]) * (TriangleQ[i][0] - TriangleQ[i+1][0]);
+
+ if ( det != 0.0 ){
+ ComputeLineIntersectionPoint( TriangleP[j], TriangleP[j+1], TriangleQ[i], TriangleQ[i+1], IntersectionPoint );
+
+ dot1 = 0;
+ dot2 = 0;
+ for(iDim = 0; iDim < nDim; iDim++){
+ dot1 += ( TriangleP[j][iDim] - IntersectionPoint[iDim] ) * ( TriangleP[j+1][iDim] - IntersectionPoint[iDim] );
+ dot2 += ( TriangleQ[i][iDim] - IntersectionPoint[iDim] ) * ( TriangleQ[i+1][iDim] - IntersectionPoint[iDim] );
+ }
+
+ if( dot1 <= 0 && dot2 <= 0 ){ // It found one intersection
+
+ // Store temporarily the intersection point
+
+ for(iDim = 0; iDim < nDim; iDim++)
+ points[nPoints][iDim] = IntersectionPoint[iDim];
+
+ nPoints++;
+ }
+ }
+ }
+ }
+
+ // Remove double points, if any
+
+ for( i = 0; i < nPoints; i++){
+ for( j = i+1; j < nPoints; j++){
+ if(points[j][0] == points[i][0] && points[j][1] == points[i][1]){
+ for( k = j; k < nPoints-1; k++){
+ points[k][0] = points[k+1][0];
+ points[k][1] = points[k+1][1];
+ }
+ nPoints--;
+ j--;
+ }
+ }
+ }
+
+ // Re-order nodes
+
+ for( i = 1; i < nPoints; i++){ // Change again reference frame
+ for(iDim = 0; iDim < nDim; iDim++)
+ points[i][iDim] -= points[0][iDim];
+
+ // Compute polar azimuth for each node but the first
+ theta[i] = atan2(points[i][1], points[i][0]);
+ }
+
+ for(iDim = 0; iDim < nDim; iDim++)
+ points[0][iDim] = 0;
+
+ for( i = 1; i < nPoints; i++){
+
+ min_theta = theta[i];
+ min_theta_index = 0;
+
+ for( j = i + 1; j < nPoints; j++){
+
+ if( theta[j] < min_theta ){
+ min_theta = theta[j];
+ min_theta_index = j;
+ }
+ }
+
+ if( min_theta_index != 0 ){
+ dtmp = theta[i];
+ theta[i] = theta[min_theta_index];
+ theta[min_theta_index] = dtmp;
+
+ dtmp = points[i][0];
+ points[i][0] = points[min_theta_index][0];
+ points[min_theta_index][0] = dtmp;
+
+ dtmp = points[i][1];
+ points[i][1] = points[min_theta_index][1];
+ points[min_theta_index][1] = dtmp;
+ }
+ }
+
+ // compute area using cross product rule, points position are referred to the
+ // 2-dimensional, local, reference frame centered in points[0]
+
+ Area = 0;
+
+ if (nPoints > 2){
+ for( i = 1; i < nPoints-1; i++ ){
+
+ // Ax*By
+ Area += ( points[i][0] - points[0][0] ) * ( points[i+1][1] - points[0][1] );
+
+ // Ay*Bx
+ Area -= ( points[i][1] - points[0][1] ) * ( points[i+1][0] - points[0][0] );
+ }
+ }
+
+ return fabs(Area)/2;
+}
+
+void CSlidingMesh::ComputeLineIntersectionPoint(const su2double* A1, const su2double* A2, const su2double* B1,
+ const su2double* B2, su2double* IntersectionPoint ){
+
+ /* --- Uses determinant rule to compute the intersection point between 2 straight segments --- */
+ /* This works only for lines on a 2D plane, A1, A2 and B1, B2 are respectively the head and the tail points of each segment,
+ * since they're on a 2D plane they are defined by a 2-elements array containing their coordinates */
+
+ su2double det;
+
+ det = (A1[0] - A2[0]) * (B1[1] - B2[1]) - (A1[1] - A2[1]) * (B1[0] - B2[0]);
+
+ if ( det != 0.0 ){ // else there is no intersection point
+ IntersectionPoint[0] = ( ( A1[0]*A2[1] - A1[1]*A2[0] ) * ( B1[0] - B2[0] ) - ( B1[0]*B2[1] - B1[1]*B2[0] ) * ( A1[0] - A2[0] ) ) / det;
+ IntersectionPoint[1] = ( ( A1[0]*A2[1] - A1[1]*A2[0] ) * ( B1[1] - B2[1] ) - ( B1[0]*B2[1] - B1[1]*B2[0] ) * ( A1[1] - A2[1] ) ) / det;
+ }
+}
+
+bool CSlidingMesh::CheckPointInsideTriangle(const su2double* Point, const su2double* T1, const su2double* T2, const su2double* T3) {
+
+ /* --- Check whether a point "Point" lies inside or outside a triangle defined by 3 points "T1", "T2", "T3" --- */
+ /* For each edge it checks on which side the point lies:
+ * - Computes the unit vector pointing at the internal side of the edge
+ * - Comutes the vector that connects the point to a point along the edge
+ * - If the dot product is positive it means that the point is on the internal side of the edge
+ * - If the check is positive for all the 3 edges, then the point lies within the triangle
+ */
+
+ unsigned short iDim, check = 0;
+
+ su2double vect1[2], vect2[2], r[2];
+ su2double dot;
+
+ constexpr unsigned short nDim = 2;
+
+ /* --- Check first edge --- */
+
+ dot = 0;
+ for(iDim = 0; iDim < nDim; iDim++){
+ vect1[iDim] = T3[iDim] - T1[iDim]; // vec 1 is aligned to the edge
+ vect2[iDim] = T2[iDim] - T1[iDim]; // vect 2 is the vector connecting one edge point to the third triangle vertex
+
+ r[iDim] = Point[iDim] - T1[iDim]; // Connects point to vertex T1
+
+ dot += vect2[iDim] * vect2[iDim];
+ }
+ dot = sqrt(dot);
+
+ for(iDim = 0; iDim < nDim; iDim++)
+ vect2[iDim] /= dot;
+
+ dot = 0;
+ for(iDim = 0; iDim < nDim; iDim++)
+ dot += vect1[iDim] * vect2[iDim];
+
+ for(iDim = 0; iDim < nDim; iDim++)
+ vect1[iDim] = T3[iDim] - (T1[iDim] + dot * vect2[iDim]); // Computes the inward unit vector
+
+ dot = 0;
+ for(iDim = 0; iDim < nDim; iDim++) // Checs that the point lies on the internal plane
+ dot += vect1[iDim] * r[iDim];
+
+ if (dot >= 0)
+ check++;
+
+ /* --- Check second edge --- */
+
+ dot = 0;
+ for(iDim = 0; iDim < nDim; iDim++){
+ vect1[iDim] = T1[iDim] - T2[iDim];
+ vect2[iDim] = T3[iDim] - T2[iDim];
+
+ r[iDim] = Point[iDim] - T2[iDim];
+
+ dot += vect2[iDim] * vect2[iDim];
+ }
+ dot = sqrt(dot);
+
+ for(iDim = 0; iDim < nDim; iDim++)
+ vect2[iDim] /= dot;
+
+ dot = 0;
+ for(iDim = 0; iDim < nDim; iDim++)
+ dot += vect1[iDim] * vect2[iDim];
+
+ for(iDim = 0; iDim < nDim; iDim++)
+ vect1[iDim] = T1[iDim] - (T2[iDim] + dot * vect2[iDim]);
+
+ dot = 0;
+ for(iDim = 0; iDim < nDim; iDim++)
+ dot += vect1[iDim] * r[iDim];
+
+ if (dot >= 0)
+ check++;
+
+ /* --- Check third edge --- */
+
+ dot = 0;
+ for(iDim = 0; iDim < nDim; iDim++){
+ vect1[iDim] = T2[iDim] - T3[iDim];
+ vect2[iDim] = T1[iDim] - T3[iDim];
+
+ r[iDim] = Point[iDim] - T3[iDim];
+
+ dot += vect2[iDim] * vect2[iDim];
+ }
+ dot = sqrt(dot);
+
+ for(iDim = 0; iDim < nDim; iDim++)
+ vect2[iDim] /= dot;
+
+ dot = 0;
+ for(iDim = 0; iDim < nDim; iDim++)
+ dot += vect1[iDim] * vect2[iDim];
+
+ for(iDim = 0; iDim < nDim; iDim++)
+ vect1[iDim] = T2[iDim] - (T3[iDim] + dot * vect2[iDim]);
+
+ dot = 0;
+ for(iDim = 0; iDim < nDim; iDim++)
+ dot += vect1[iDim] * r[iDim];
+
+ if (dot >= 0)
+ check++;
+
+ return (check == 3);
+}
diff --git a/Common/src/interface_interpolation/meson.build b/Common/src/interface_interpolation/meson.build
new file mode 100644
index 000000000000..3e1d1a49b2e4
--- /dev/null
+++ b/Common/src/interface_interpolation/meson.build
@@ -0,0 +1,6 @@
+common_src += files(['CInterpolator.cpp',
+ 'CMirror.cpp',
+ 'CSlidingMesh.cpp',
+ 'CIsoparametric.cpp',
+ 'CNearestNeighbor.cpp',
+ 'CRadialBasisFunction.cpp'])
diff --git a/Common/src/interpolation_structure.cpp b/Common/src/interpolation_structure.cpp
deleted file mode 100644
index c0107c8b89ed..000000000000
--- a/Common/src/interpolation_structure.cpp
+++ /dev/null
@@ -1,3849 +0,0 @@
-/*!
- * \file interpolation_structure.cpp
- * \brief Main subroutines used by SU2_FSI
- * \author H. Kline
- * \version 7.0.2 "Blackbird"
- *
- * SU2 Project Website: https://su2code.github.io
- *
- * The SU2 Project is maintained by the SU2 Foundation
- * (http://su2foundation.org)
- *
- * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md)
- *
- * SU2 is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * SU2 is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with SU2. If not, see .
- */
-#include "../include/interpolation_structure.hpp"
-
-#if defined(HAVE_MKL)
-#include "mkl.h"
-#ifndef HAVE_LAPACK
-#define HAVE_LAPACK
-#endif
-#elif defined(HAVE_LAPACK)
-/*--- Lapack / Blas routines used in RBF interpolation. ---*/
-extern "C" void dsptrf_(char*, int*, passivedouble*, int*, int*);
-extern "C" void dsptri_(char*, int*, passivedouble*, int*, passivedouble*, int*);
-extern "C" void dsymm_(char*, char*, int*, int*, passivedouble*, passivedouble*, int*,
- passivedouble*, int*, passivedouble*, passivedouble*, int*);
-#endif
-
-CInterpolator::CInterpolator(void) {
-
- size = SU2_MPI::GetSize();
- rank = SU2_MPI::GetRank();
-
- nZone = 0;
- Geometry = NULL;
-
- donor_geometry = NULL;
- target_geometry = NULL;
-
- donorZone = 0;
- targetZone = 0;
-
- Buffer_Receive_nVertex_Donor = NULL;
- Buffer_Receive_nFace_Donor = NULL;
- Buffer_Receive_nFaceNodes_Donor = NULL;
- Buffer_Send_nVertex_Donor = NULL;
- Buffer_Send_nFace_Donor = NULL;
- Buffer_Send_nFaceNodes_Donor = NULL;
- Buffer_Receive_GlobalPoint = NULL;
- Buffer_Send_GlobalPoint = NULL;
- Buffer_Send_FaceIndex = NULL;
- Buffer_Receive_FaceIndex = NULL;
- Buffer_Send_FaceNodes = NULL;
- Buffer_Receive_FaceNodes = NULL;
- Buffer_Send_FaceProc = NULL;
- Buffer_Receive_FaceProc = NULL;
-
- Buffer_Send_Coord = NULL;
- Buffer_Send_Normal = NULL;
- Buffer_Receive_Coord = NULL;
- Buffer_Receive_Normal = NULL;
-
- Receive_GlobalPoint = NULL;
- Buffer_Receive_nLinkedNodes = NULL;
- Buffer_Receive_LinkedNodes = NULL;
- Buffer_Receive_StartLinkedNodes = NULL;
- Buffer_Receive_Proc = NULL;
-
-}
-
-CInterpolator::~CInterpolator(void) {
-
- //if (Buffer_Receive_nVertex_Donor!= NULL) delete[] Buffer_Receive_nVertex_Donor;
-}
-
-
-CInterpolator::CInterpolator(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) {
-
- size = SU2_MPI::GetSize();
- rank = SU2_MPI::GetRank();
-
- /* Store pointers*/
- Geometry = geometry_container;
-
- donorZone = iZone;
- targetZone = jZone;
-
- donor_geometry = geometry_container[donorZone][INST_0][MESH_0];
- target_geometry = geometry_container[targetZone][INST_0][MESH_0];
-
- /*--- Initialize transfer coefficients between the zones ---*/
- /* Since this is a virtual function, call it in the child class constructor */
- //Set_TransferCoeff(targetZone,donorZone,config);
- /*--- Initialize transfer coefficients between the zones ---*/
- //Set_TransferCoeff(Zones,config);
-
- //Buffer_Receive_nVertex_Donor = NULL;
-
-}
-
-inline void CInterpolator::Set_TransferCoeff(CConfig **config) { }
-
-void CInterpolator::Determine_ArraySize(bool faces, int markDonor, int markTarget, unsigned long nVertexDonor, unsigned short nDim) {
- unsigned long nLocalVertex_Donor = 0, nLocalFaceNodes_Donor=0, nLocalFace_Donor=0;
- unsigned long iVertex, iPointDonor = 0;
- /* Only needed if face data is also collected */
- unsigned long inode;
- unsigned long donor_elem, jElem, jPoint;
- unsigned short iDonor;
- unsigned int nFaces=0, iFace, nNodes=0;
- bool face_on_marker = true;
-
- for (iVertex = 0; iVertex < nVertexDonor; iVertex++) {
- iPointDonor = donor_geometry->vertex[markDonor][iVertex]->GetNode();
- if (donor_geometry->node[iPointDonor]->GetDomain()) {
- nLocalVertex_Donor++;
- if (faces) {
- /*--- On Donor geometry also communicate face info ---*/
- if (nDim==3) {
- for (jElem=0; jElemnode[iPointDonor]->GetnElem(); jElem++) {
- donor_elem = donor_geometry->node[iPointDonor]->GetElem(jElem);
- nFaces = donor_geometry->elem[donor_elem]->GetnFaces();
- for (iFace=0; iFaceelem[donor_elem]->GetnNodesFace(iFace);
- for (iDonor=0; iDonorelem[donor_elem]->GetFaces(iFace, iDonor);
- jPoint = donor_geometry->elem[donor_elem]->GetNode(inode);
- face_on_marker = (face_on_marker && (donor_geometry->node[jPoint]->GetVertex(markDonor) !=-1));
- }
- if (face_on_marker ) {
- nLocalFace_Donor++;
- nLocalFaceNodes_Donor+=nNodes;
- }
- }
- }
- }
- else {
- /*--- in 2D we use the edges ---*/
- nNodes=2;
- nFaces = donor_geometry->node[iPointDonor]->GetnPoint();
- for (iFace=0; iFacenode[iPointDonor]->GetEdge(iFace);
- jPoint = donor_geometry->edge[inode]->GetNode(iDonor);
- face_on_marker = (face_on_marker && (donor_geometry->node[jPoint]->GetVertex(markDonor) !=-1));
- }
- if (face_on_marker ) {
- nLocalFace_Donor++;
- nLocalFaceNodes_Donor+=nNodes;
- }
- }
- }
- }
- }
- }
-
- Buffer_Send_nVertex_Donor[0] = nLocalVertex_Donor;
- if (faces) {
- Buffer_Send_nFace_Donor[0] = nLocalFace_Donor;
- Buffer_Send_nFaceNodes_Donor[0] = nLocalFaceNodes_Donor;
- }
-
- /*--- Send Interface vertex information --*/
-#ifdef HAVE_MPI
- SU2_MPI::Allreduce(&nLocalVertex_Donor, &MaxLocalVertex_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD);
- SU2_MPI::Allgather(Buffer_Send_nVertex_Donor, 1, MPI_UNSIGNED_LONG, Buffer_Receive_nVertex_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
- if (faces) {
- SU2_MPI::Allreduce(&nLocalFace_Donor, &nGlobalFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD);
- SU2_MPI::Allreduce(&nLocalFace_Donor, &MaxFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD);
- SU2_MPI::Allreduce(&nLocalFaceNodes_Donor, &nGlobalFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD);
- SU2_MPI::Allreduce(&nLocalFaceNodes_Donor, &MaxFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD);
- SU2_MPI::Allgather(Buffer_Send_nFace_Donor, 1, MPI_UNSIGNED_LONG, Buffer_Receive_nFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
- SU2_MPI::Allgather(Buffer_Send_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, Buffer_Receive_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
- MaxFace_Donor++;
- }
-#else
- MaxLocalVertex_Donor = nLocalVertex_Donor;
- Buffer_Receive_nVertex_Donor[0] = Buffer_Send_nVertex_Donor[0];
- if (faces) {
- nGlobalFace_Donor = nLocalFace_Donor;
- nGlobalFaceNodes_Donor = nLocalFaceNodes_Donor;
- MaxFaceNodes_Donor = nLocalFaceNodes_Donor;
- MaxFace_Donor = nLocalFace_Donor+1;
- Buffer_Receive_nFace_Donor[0] = Buffer_Send_nFace_Donor[0];
- Buffer_Receive_nFaceNodes_Donor[0] = Buffer_Send_nFaceNodes_Donor[0];
- }
-#endif
-
-}
-
-void CInterpolator::Collect_VertexInfo(bool faces, int markDonor, int markTarget, unsigned long nVertexDonor, unsigned short nDim)
-{
- unsigned long iVertex, iPointDonor = 0, iVertexDonor, nBuffer_Coord, nBuffer_Point, nLocalVertex_Donor;
- unsigned short iDim;
-
- /* Only needed if face data is also collected */
- su2double *Normal;
-
- for (iVertex = 0; iVertex < MaxLocalVertex_Donor; iVertex++) {
- Buffer_Send_GlobalPoint[iVertex] = -1;
- for (iDim = 0; iDim < nDim; iDim++) {
- Buffer_Send_Coord[iVertex*nDim+iDim] = 0.0;
- if (faces)
- Buffer_Send_Normal[iVertex*nDim+iDim] = 0.0;
- }
- }
-
- /*--- Copy coordinates and point to the auxiliar vector --*/
- nLocalVertex_Donor = 0;
-
- for (iVertexDonor = 0; iVertexDonor < nVertexDonor; iVertexDonor++) {
- iPointDonor = donor_geometry->vertex[markDonor][iVertexDonor]->GetNode();
- if (donor_geometry->node[iPointDonor]->GetDomain()) {
- Buffer_Send_GlobalPoint[nLocalVertex_Donor] = donor_geometry->node[iPointDonor]->GetGlobalIndex();
- for (iDim = 0; iDim < nDim; iDim++)
- Buffer_Send_Coord[nLocalVertex_Donor*nDim+iDim] = donor_geometry->node[iPointDonor]->GetCoord(iDim);
-
- if (faces) {
- Normal = donor_geometry->vertex[markDonor][iVertexDonor]->GetNormal();
- for (iDim = 0; iDim < nDim; iDim++)
- Buffer_Send_Normal[nLocalVertex_Donor*nDim+iDim] = Normal[iDim];
- }
- nLocalVertex_Donor++;
- }
- }
- nBuffer_Coord = MaxLocalVertex_Donor*nDim;
- nBuffer_Point = MaxLocalVertex_Donor;
-
-#ifdef HAVE_MPI
- SU2_MPI::Allgather(Buffer_Send_Coord, nBuffer_Coord, MPI_DOUBLE, Buffer_Receive_Coord, nBuffer_Coord, MPI_DOUBLE, MPI_COMM_WORLD);
- SU2_MPI::Allgather(Buffer_Send_GlobalPoint, nBuffer_Point, MPI_LONG, Buffer_Receive_GlobalPoint, nBuffer_Point, MPI_LONG, MPI_COMM_WORLD);
- if (faces) {
- SU2_MPI::Allgather(Buffer_Send_Normal, nBuffer_Coord, MPI_DOUBLE, Buffer_Receive_Normal, nBuffer_Coord, MPI_DOUBLE, MPI_COMM_WORLD);
- }
-#else
- for (iVertex = 0; iVertex < nBuffer_Coord; iVertex++)
- Buffer_Receive_Coord[iVertex] = Buffer_Send_Coord[iVertex];
-
- for (iVertex = 0; iVertex < nBuffer_Point; iVertex++)
- Buffer_Receive_GlobalPoint[iVertex] = Buffer_Send_GlobalPoint[iVertex];
-
- if (faces) {
- for (iVertex = 0; iVertex < nBuffer_Coord; iVertex++)
- Buffer_Receive_Normal[iVertex] = Buffer_Send_Normal[iVertex];
- }
-#endif
-}
-
-int CInterpolator::Find_InterfaceMarker(CConfig *config, unsigned short val_marker_interface) {
-
- unsigned short nMarker = config->GetnMarker_All();
- unsigned short iMarker;
-
- for (iMarker = 0; iMarker < nMarker; iMarker++) {
-
- /*--- If the tag GetMarker_All_ZoneInterface(iMarker) equals the index we are looping at ---*/
- if (config->GetMarker_All_ZoneInterface(iMarker) == val_marker_interface ) {
-
- /*--- We have identified the identifier for the interface marker ---*/
- return iMarker;
- }
- }
-
- return -1;
-}
-
-
-void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){
-
- CGeometry *geom = Geometry[val_zone][INST_0][MESH_0];
-
- unsigned long iVertex, jVertex, kVertex;
-
- unsigned long count, iTmp, *uptr, dPoint, EdgeIndex, jEdge, nEdges, nNodes, nVertex, iDim, nDim, iPoint;
-
- unsigned long nGlobalLinkedNodes, nLocalVertex, nLocalLinkedNodes;
-
- nDim = geom->GetnDim();
-
- if( val_marker != -1 )
- nVertex = geom->GetnVertex( val_marker );
- else
- nVertex = 0;
-
-
- su2double *Buffer_Send_Coord = new su2double [ nVertex * nDim ];
- unsigned long *Buffer_Send_GlobalPoint = new unsigned long [ nVertex ];
-
- unsigned long *Buffer_Send_nLinkedNodes = new unsigned long [ nVertex ];
- unsigned long *Buffer_Send_StartLinkedNodes = new unsigned long [ nVertex ];
- unsigned long **Aux_Send_Map = new unsigned long*[ nVertex ];
-
-#ifdef HAVE_MPI
- int nProcessor = size, iRank;
- unsigned long iTmp2, tmp_index, tmp_index_2;
-#endif
-
- /*--- Copy coordinates and point to the auxiliar vector ---*/
-
- nGlobalVertex = 0;
- nLocalVertex = 0;
- nLocalLinkedNodes = 0;
-
- for (iVertex = 0; iVertex < nVertex; iVertex++) {
-
- Buffer_Send_nLinkedNodes[iVertex] = 0;
- Aux_Send_Map[iVertex] = NULL;
-
- iPoint = geom->vertex[val_marker][iVertex]->GetNode();
-
- if (geom->node[iPoint]->GetDomain()) {
- Buffer_Send_GlobalPoint[nLocalVertex] = geom->node[iPoint]->GetGlobalIndex();
-
- for (iDim = 0; iDim < nDim; iDim++)
- Buffer_Send_Coord[nLocalVertex*nDim+iDim] = geom->node[iPoint]->GetCoord(iDim);
-
- nNodes = 0;
- nEdges = geom->node[iPoint]->GetnPoint();
-
- for (jEdge = 0; jEdge < nEdges; jEdge++){
- EdgeIndex = geom->node[iPoint]->GetEdge(jEdge);
-
- if( iPoint == geom->edge[EdgeIndex]->GetNode(0) )
- dPoint = geom->edge[EdgeIndex]->GetNode(1);
- else
- dPoint = geom->edge[EdgeIndex]->GetNode(0);
-
- if ( geom->node[dPoint]->GetVertex(val_marker) != -1 )
- nNodes++;
- }
-
- Buffer_Send_StartLinkedNodes[nLocalVertex] = nLocalLinkedNodes;
- Buffer_Send_nLinkedNodes[nLocalVertex] = nNodes;
-
- nLocalLinkedNodes += nNodes;
-
- Aux_Send_Map[nLocalVertex] = new unsigned long[ nNodes ];
- nNodes = 0;
-
- for (jEdge = 0; jEdge < nEdges; jEdge++){
- EdgeIndex = geom->node[iPoint]->GetEdge(jEdge);
-
- if( iPoint == geom->edge[EdgeIndex]->GetNode(0) )
- dPoint = geom->edge[EdgeIndex]->GetNode(1);
- else
- dPoint = geom->edge[EdgeIndex]->GetNode(0);
-
- if ( geom->node[dPoint]->GetVertex(val_marker) != -1 ){
- Aux_Send_Map[nLocalVertex][nNodes] = geom->node[dPoint]->GetGlobalIndex();
- nNodes++;
- }
- }
- nLocalVertex++;
- }
- }
-
- unsigned long *Buffer_Send_LinkedNodes = new unsigned long [ nLocalLinkedNodes ];
-
- nLocalLinkedNodes = 0;
-
- for (iVertex = 0; iVertex < nLocalVertex; iVertex++){
- for (jEdge = 0; jEdge < Buffer_Send_nLinkedNodes[iVertex]; jEdge++){
- Buffer_Send_LinkedNodes[nLocalLinkedNodes] = Aux_Send_Map[iVertex][jEdge];
- nLocalLinkedNodes++;
- }
- }
-
- for (iVertex = 0; iVertex < nVertex; iVertex++){
- if( Aux_Send_Map[iVertex] != NULL )
- delete [] Aux_Send_Map[iVertex];
- }
- delete [] Aux_Send_Map; Aux_Send_Map = NULL;
-
- /*--- Reconstruct boundary by gathering data from all ranks ---*/
-
-#ifdef HAVE_MPI
- SU2_MPI::Allreduce( &nLocalVertex, &nGlobalVertex, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD);
- SU2_MPI::Allreduce(&nLocalLinkedNodes, &nGlobalLinkedNodes, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD);
-#else
- nGlobalVertex = nLocalVertex;
- nGlobalLinkedNodes = nLocalLinkedNodes;
-#endif
-
- Buffer_Receive_Coord = new su2double [ nGlobalVertex * nDim ];
- Buffer_Receive_GlobalPoint = new long[ nGlobalVertex ];
- Buffer_Receive_Proc = new unsigned long[ nGlobalVertex ];
-
- Buffer_Receive_nLinkedNodes = new unsigned long[ nGlobalVertex ];
- Buffer_Receive_LinkedNodes = new unsigned long[ nGlobalLinkedNodes ];
- Buffer_Receive_StartLinkedNodes = new unsigned long[ nGlobalVertex ];
-
-#ifdef HAVE_MPI
- if (rank == MASTER_NODE){
-
- for (iVertex = 0; iVertex < nDim*nLocalVertex; iVertex++)
- Buffer_Receive_Coord[iVertex] = Buffer_Send_Coord[iVertex];
-
- for (iVertex = 0; iVertex < nLocalVertex; iVertex++){
- Buffer_Receive_GlobalPoint[iVertex] = Buffer_Send_GlobalPoint[iVertex];
- Buffer_Receive_Proc[iVertex] = MASTER_NODE;
- Buffer_Receive_nLinkedNodes[iVertex] = Buffer_Send_nLinkedNodes[iVertex];
- Buffer_Receive_StartLinkedNodes[iVertex] = Buffer_Send_StartLinkedNodes[iVertex];
- }
-
- for (iVertex = 0; iVertex < nLocalLinkedNodes; iVertex++)
- Buffer_Receive_LinkedNodes[iVertex] = Buffer_Send_LinkedNodes[iVertex];
-
- tmp_index = nLocalVertex;
- tmp_index_2 = nLocalLinkedNodes;
-
- for(iRank = 1; iRank < nProcessor; iRank++){
-
- SU2_MPI::Recv( &iTmp2, 1, MPI_UNSIGNED_LONG, iRank, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
- SU2_MPI::Recv(&Buffer_Receive_LinkedNodes[tmp_index_2], iTmp2, MPI_UNSIGNED_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
-
- SU2_MPI::Recv( &iTmp, 1, MPI_UNSIGNED_LONG, iRank, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
- SU2_MPI::Recv(&Buffer_Receive_Coord[tmp_index*nDim], nDim*iTmp, MPI_DOUBLE, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
-
- SU2_MPI::Recv( &Buffer_Receive_GlobalPoint[tmp_index], iTmp, MPI_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
- SU2_MPI::Recv( &Buffer_Receive_nLinkedNodes[tmp_index], iTmp, MPI_UNSIGNED_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
- SU2_MPI::Recv(&Buffer_Receive_StartLinkedNodes[tmp_index], iTmp, MPI_UNSIGNED_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
-
- for (iVertex = 0; iVertex < iTmp; iVertex++){
- Buffer_Receive_Proc[ tmp_index + iVertex ] = iRank;
- Buffer_Receive_StartLinkedNodes[ tmp_index + iVertex ] += tmp_index_2;
- }
-
- tmp_index += iTmp;
- tmp_index_2 += iTmp2;
- }
- }
- else{
- SU2_MPI::Send( &nLocalLinkedNodes, 1, MPI_UNSIGNED_LONG, 0, 0, MPI_COMM_WORLD);
- SU2_MPI::Send(Buffer_Send_LinkedNodes, nLocalLinkedNodes, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD);
-
- SU2_MPI::Send( &nLocalVertex, 1, MPI_UNSIGNED_LONG, 0, 0, MPI_COMM_WORLD);
- SU2_MPI::Send(Buffer_Send_Coord, nDim * nLocalVertex, MPI_DOUBLE, 0, 1, MPI_COMM_WORLD);
-
- SU2_MPI::Send( Buffer_Send_GlobalPoint, nLocalVertex, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD);
- SU2_MPI::Send( Buffer_Send_nLinkedNodes, nLocalVertex, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD);
- SU2_MPI::Send(Buffer_Send_StartLinkedNodes, nLocalVertex, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD);
- }
-#else
- for (iVertex = 0; iVertex < nDim * nGlobalVertex; iVertex++)
- Buffer_Receive_Coord[iVertex] = Buffer_Send_Coord[iVertex];
-
- for (iVertex = 0; iVertex < nGlobalVertex; iVertex++){
- Buffer_Receive_GlobalPoint[iVertex] = Buffer_Send_GlobalPoint[iVertex];
- Buffer_Receive_Proc[iVertex] = MASTER_NODE;
- Buffer_Receive_nLinkedNodes[iVertex] = Buffer_Send_nLinkedNodes[iVertex];
- Buffer_Receive_StartLinkedNodes[iVertex] = Buffer_Send_StartLinkedNodes[iVertex];
- }
-
- for (iVertex = 0; iVertex < nGlobalLinkedNodes; iVertex++)
- Buffer_Receive_LinkedNodes[iVertex] = Buffer_Send_LinkedNodes[iVertex];
-#endif
-
- if (rank == MASTER_NODE){
- for (iVertex = 0; iVertex < nGlobalVertex; iVertex++){
- count = 0;
- uptr = &Buffer_Receive_LinkedNodes[ Buffer_Receive_StartLinkedNodes[iVertex] ];
-
- for (jVertex = 0; jVertex < Buffer_Receive_nLinkedNodes[iVertex]; jVertex++){
- iTmp = uptr[ jVertex ];
- for (kVertex = 0; kVertex < nGlobalVertex; kVertex++){
- if( Buffer_Receive_GlobalPoint[kVertex] == long(iTmp) ){
- uptr[ jVertex ] = kVertex;
- count++;
- break;
- }
- }
-
- if( count != (jVertex+1) ){
- for (kVertex = jVertex; kVertex < Buffer_Receive_nLinkedNodes[iVertex]-1; kVertex++){
- uptr[ kVertex ] = uptr[ kVertex + 1];
- }
- Buffer_Receive_nLinkedNodes[iVertex]--;
- jVertex--;
- }
- }
- }
- }
-
-#ifdef HAVE_MPI
- SU2_MPI::Bcast( Buffer_Receive_Coord, nGlobalVertex * nDim, MPI_DOUBLE, 0, MPI_COMM_WORLD);
- SU2_MPI::Bcast(Buffer_Receive_GlobalPoint, nGlobalVertex, MPI_LONG, 0, MPI_COMM_WORLD);
- SU2_MPI::Bcast( Buffer_Receive_Proc, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD );
-
- SU2_MPI::Bcast( Buffer_Receive_nLinkedNodes, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD);
- SU2_MPI::Bcast(Buffer_Receive_StartLinkedNodes, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD);
- SU2_MPI::Bcast( Buffer_Receive_LinkedNodes, nGlobalLinkedNodes, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD);
-#endif
-
- if( Buffer_Send_Coord != NULL) {delete [] Buffer_Send_Coord; Buffer_Send_Coord = NULL;}
- if( Buffer_Send_GlobalPoint != NULL) {delete [] Buffer_Send_GlobalPoint; Buffer_Send_GlobalPoint = NULL;}
- if( Buffer_Send_LinkedNodes != NULL) {delete [] Buffer_Send_LinkedNodes; Buffer_Send_LinkedNodes = NULL;}
- if( Buffer_Send_nLinkedNodes != NULL) {delete [] Buffer_Send_nLinkedNodes; Buffer_Send_nLinkedNodes = NULL;}
- if( Buffer_Send_StartLinkedNodes != NULL) {delete [] Buffer_Send_StartLinkedNodes; Buffer_Send_StartLinkedNodes = NULL;}
-}
-
-bool CInterpolator::CheckInterfaceBoundary(int markDonor, int markTarget){
-
- int Donor_check, Target_check;
-
- #ifdef HAVE_MPI
-
- int *Buffer_Recv_mark = NULL;
- int iRank, nProcessor = size;
-
- if (rank == MASTER_NODE)
- Buffer_Recv_mark = new int[nProcessor];
-
- Donor_check = -1;
- Target_check = -1;
-
- /*--- We gather a vector in MASTER_NODE to determine whether the boundary is not on the processor because of the partition or because the zone does not include it ---*/
-
- SU2_MPI::Gather(&markDonor , 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD);
-
- if (rank == MASTER_NODE)
- for (iRank = 0; iRank < nProcessor; iRank++)
- if( Buffer_Recv_mark[iRank] != -1 ){
- Donor_check = Buffer_Recv_mark[iRank];
- break;
- }
-
- SU2_MPI::Bcast(&Donor_check , 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD);
-
-
- SU2_MPI::Gather(&markTarget, 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD);
-
- if (rank == MASTER_NODE)
- for (iRank = 0; iRank < nProcessor; iRank++)
- if( Buffer_Recv_mark[iRank] != -1 ){
- Target_check = Buffer_Recv_mark[iRank];
- break;
- }
-
-
- SU2_MPI::Bcast(&Target_check, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD);
-
- if (rank == MASTER_NODE)
- delete [] Buffer_Recv_mark;
-
-#else
- Donor_check = markDonor;
- Target_check = markTarget;
-#endif
-
- if(Target_check == -1 || Donor_check == -1)
- return false;
- else
- return true;
-}
-
-su2double CInterpolator::PointsDistance(su2double *point_i, su2double *point_j){
-
- /*--- Compute distance between 2 points ---*/
-
- unsigned short iDim, nDim = donor_geometry->GetnDim();
- su2double m;
-
- m = 0 ;
- for(iDim = 0; iDim < nDim; iDim++)
- m += (point_j[iDim] - point_i[iDim])*(point_j[iDim] - point_i[iDim]);
-
- return sqrt(m);
-}
-
-/* Nearest Neighbor Interpolator */
-CNearestNeighbor::CNearestNeighbor(void): CInterpolator() { }
-
-CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) {
-
- /*--- Initialize transfer coefficients between the zones ---*/
- Set_TransferCoeff(config);
-}
-
-CNearestNeighbor::~CNearestNeighbor() {}
-
-void CNearestNeighbor::Set_TransferCoeff(CConfig **config) {
-
- int iProcessor, pProcessor, nProcessor = size;
- int markDonor, markTarget;
-
- unsigned short nDim, iMarkerInt, nMarkerInt, iDonor;
-
- unsigned long nVertexDonor, nVertexTarget, Point_Target, jVertex, iVertexTarget;
- unsigned long Global_Point_Donor;
- long pGlobalPoint = 0;
-
- su2double *Coord_i, *Coord_j, dist, mindist, maxdist;
-
- /*--- Initialize variables --- */
-
- nMarkerInt = (int) ( config[donorZone]->GetMarker_n_ZoneInterface() / 2 );
-
- nDim = donor_geometry->GetnDim();
-
- iDonor = 0;
-
- Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor];
-
-
- /*--- Cycle over nMarkersInt interface to determine communication pattern ---*/
-
- for (iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) {
-
-
- /*--- On the donor side: find the tag of the boundary sharing the interface ---*/
- markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt);
-
- /*--- On the target side: find the tag of the boundary sharing the interface ---*/
- markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt);
-
- /*--- Checks if the zone contains the interface, if not continue to the next step ---*/
- if( !CheckInterfaceBoundary(markDonor, markTarget) )
- continue;
-
- if(markDonor != -1)
- nVertexDonor = donor_geometry->GetnVertex( markDonor );
- else
- nVertexDonor = 0;
-
- if(markTarget != -1)
- nVertexTarget = target_geometry->GetnVertex( markTarget );
- else
- nVertexTarget = 0;
-
- Buffer_Send_nVertex_Donor = new unsigned long [ 1 ];
-
- /* Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor */
- Determine_ArraySize(false, markDonor, markTarget, nVertexDonor, nDim);
-
- Buffer_Send_Coord = new su2double [ MaxLocalVertex_Donor * nDim ];
- Buffer_Send_GlobalPoint = new long [ MaxLocalVertex_Donor ];
- Buffer_Receive_Coord = new su2double [ nProcessor * MaxLocalVertex_Donor * nDim ];
- Buffer_Receive_GlobalPoint = new long [ nProcessor * MaxLocalVertex_Donor ];
-
- /*-- Collect coordinates, global points, and normal vectors ---*/
- Collect_VertexInfo( false, markDonor, markTarget, nVertexDonor, nDim );
-
- /*--- Compute the closest point to a Near-Field boundary point ---*/
- maxdist = 0.0;
-
- for (iVertexTarget = 0; iVertexTarget < nVertexTarget; iVertexTarget++) {
-
- Point_Target = target_geometry->vertex[markTarget][iVertexTarget]->GetNode();
-
- if ( target_geometry->node[Point_Target]->GetDomain() ) {
-
- target_geometry->vertex[markTarget][iVertexTarget]->SetnDonorPoints(1);
- target_geometry->vertex[markTarget][iVertexTarget]->Allocate_DonorInfo(); // Possible meme leak?
-
- /*--- Coordinates of the boundary point ---*/
- Coord_i = target_geometry->node[Point_Target]->GetCoord();
-
- mindist = 1E6;
- pProcessor = 0;
-
- /*--- Loop over all the boundaries to find the pair ---*/
-
- for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) {
- for (jVertex = 0; jVertex < MaxLocalVertex_Donor; jVertex++) {
-
- Global_Point_Donor = iProcessor*MaxLocalVertex_Donor+jVertex;
-
- if (Buffer_Receive_GlobalPoint[Global_Point_Donor] != -1){
-
- Coord_j = &Buffer_Receive_Coord[ Global_Point_Donor*nDim];
-
- dist = PointsDistance(Coord_i, Coord_j);
-
- if (dist < mindist) {
- mindist = dist; pProcessor = iProcessor;
- pGlobalPoint = Buffer_Receive_GlobalPoint[Global_Point_Donor];
- }
-
- if (dist == 0.0) break;
- }
- }
- }
-
- /*--- Store the value of the pair ---*/
- maxdist = max(maxdist, mindist);
- target_geometry->vertex[markTarget][iVertexTarget]->SetInterpDonorPoint(iDonor, pGlobalPoint);
- target_geometry->vertex[markTarget][iVertexTarget]->SetInterpDonorProcessor(iDonor, pProcessor);
- target_geometry->vertex[markTarget][iVertexTarget]->SetDonorCoeff(iDonor, 1.0);
- }
- }
-
- delete[] Buffer_Send_Coord;
- delete[] Buffer_Send_GlobalPoint;
-
- delete[] Buffer_Receive_Coord;
- delete[] Buffer_Receive_GlobalPoint;
-
- delete[] Buffer_Send_nVertex_Donor;
-
- }
-
- delete[] Buffer_Receive_nVertex_Donor;
-}
-
-
-
-CIsoparametric::CIsoparametric(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) {
-
- /*--- Initialize transfer coefficients between the zones ---*/
- Set_TransferCoeff(config);
-
- /*--- For fluid-structure interaction data interpolated with have nDim dimensions ---*/
- // InitializeData(Zones,nDim);
-}
-
-CIsoparametric::~CIsoparametric() {}
-
-void CIsoparametric::Set_TransferCoeff(CConfig **config) {
- unsigned long iVertex, jVertex;
- unsigned long dPoint, inode, jElem, nElem;
- unsigned short iDim, iDonor=0, iFace;
-
- unsigned short nDim = donor_geometry->GetnDim();
-
- unsigned short nMarkerInt;
- unsigned short iMarkerInt;
-
- int markDonor=0, markTarget=0;
-
- long donor_elem=0, temp_donor=0;
- unsigned int nNodes=0;
- /*--- Restricted to 2-zone for now ---*/
- unsigned int nFaces=1; //For 2D cases, we want to look at edges, not faces, as the 'interface'
- bool face_on_marker=true;
-
- unsigned long nVertexDonor = 0, nVertexTarget= 0;
- unsigned long Point_Target = 0;
-
- unsigned long iVertexDonor, iPointDonor = 0;
- int iProcessor;
-
- unsigned long nLocalFace_Donor = 0, nLocalFaceNodes_Donor=0;
-
- unsigned long faceindex;
-
- su2double dist = 0.0, mindist=1E6, *Coord, *Coord_i;
- su2double myCoeff[10]; // Maximum # of donor points
- su2double *Normal;
- su2double *projected_point = new su2double[nDim];
- su2double tmp, tmp2;
- su2double storeCoeff[10];
- unsigned long storeGlobal[10];
- int storeProc[10];
-
- int nProcessor = size;
- Coord = new su2double[nDim];
- Normal = new su2double[nDim];
-
- nMarkerInt = (config[donorZone]->GetMarker_n_ZoneInterface())/2;
-
- /*--- For the number of markers on the interface... ---*/
- for (iMarkerInt=1; iMarkerInt <= nMarkerInt; iMarkerInt++) {
- /*--- Procedure:
- * -Loop through vertices of the aero grid
- * -Find nearest element and allocate enough space in the aero grid donor point info
- * -set the transfer coefficient values
- */
-
- /*--- On the donor side: find the tag of the boundary sharing the interface ---*/
- markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt);
-
- /*--- On the target side: find the tag of the boundary sharing the interface ---*/
- markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt);
-
- /*--- Checks if the zone contains the interface, if not continue to the next step ---*/
- if( !CheckInterfaceBoundary(markDonor, markTarget) )
- continue;
-
- if(markDonor != -1)
- nVertexDonor = donor_geometry->GetnVertex( markDonor );
- else
- nVertexDonor = 0;
-
- if(markTarget != -1)
- nVertexTarget = target_geometry->GetnVertex( markTarget );
- else
- nVertexTarget = 0;
-
- Buffer_Send_nVertex_Donor = new unsigned long [1];
- Buffer_Send_nFace_Donor = new unsigned long [1];
- Buffer_Send_nFaceNodes_Donor = new unsigned long [1];
-
- Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor];
- Buffer_Receive_nFace_Donor = new unsigned long [nProcessor];
- Buffer_Receive_nFaceNodes_Donor = new unsigned long [nProcessor];
-
- /* Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor */
- Determine_ArraySize(true, markDonor, markTarget, nVertexDonor, nDim);
-
- Buffer_Send_Coord = new su2double [MaxLocalVertex_Donor*nDim];
- Buffer_Send_Normal = new su2double [MaxLocalVertex_Donor*nDim];
- Buffer_Send_GlobalPoint = new long [MaxLocalVertex_Donor];
-
- Buffer_Receive_Coord = new su2double [nProcessor*MaxLocalVertex_Donor*nDim];
- Buffer_Receive_Normal = new su2double [nProcessor*MaxLocalVertex_Donor*nDim];
- Buffer_Receive_GlobalPoint = new long [nProcessor*MaxLocalVertex_Donor];
-
- /*-- Collect coordinates, global points, and normal vectors ---*/
- Collect_VertexInfo(true, markDonor,markTarget,nVertexDonor,nDim);
-
- Buffer_Send_FaceIndex = new unsigned long[MaxFace_Donor];
- Buffer_Send_FaceNodes = new unsigned long[MaxFaceNodes_Donor];
- Buffer_Send_FaceProc = new unsigned long[MaxFaceNodes_Donor];
-
- Buffer_Receive_FaceIndex = new unsigned long[MaxFace_Donor*nProcessor];
- Buffer_Receive_FaceNodes = new unsigned long[MaxFaceNodes_Donor*nProcessor];
- Buffer_Receive_FaceProc = new unsigned long[MaxFaceNodes_Donor*nProcessor];
-
- nLocalFace_Donor=0;
- nLocalFaceNodes_Donor=0;
-
- /*--- Collect Face info ---*/
-
- for (iVertex = 0; iVertex < MaxFace_Donor; iVertex++) {
- Buffer_Send_FaceIndex[iVertex] = 0;
- }
- for (iVertex=0; iVertexvertex[markDonor][iVertexDonor]->GetNode();
-
- if (donor_geometry->node[iPointDonor]->GetDomain()) {
-
- if (nDim==3) nElem = donor_geometry->node[iPointDonor]->GetnElem();
- else nElem =donor_geometry->node[iPointDonor]->GetnPoint();
-
- for (jElem=0; jElem < nElem; jElem++) {
- if (nDim==3) {
- temp_donor = donor_geometry->node[iPointDonor]->GetElem(jElem);
- nFaces = donor_geometry->elem[temp_donor]->GetnFaces();
- for (iFace=0; iFaceelem[temp_donor]->GetnNodesFace(iFace);
- for (iDonor=0; iDonorelem[temp_donor]->GetFaces(iFace, iDonor);
- dPoint = donor_geometry->elem[temp_donor]->GetNode(inode);
- face_on_marker = (face_on_marker && (donor_geometry->node[dPoint]->GetVertex(markDonor) !=-1));
- }
-
- if (face_on_marker ) {
- for (iDonor=0; iDonorelem[temp_donor]->GetFaces(iFace, iDonor);
- dPoint = donor_geometry->elem[temp_donor]->GetNode(inode);
- // Match node on the face to the correct global index
- long jGlobalPoint = donor_geometry->node[dPoint]->GetGlobalIndex();
- for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) {
- for (jVertex = 0; jVertex < Buffer_Receive_nVertex_Donor[iProcessor]; jVertex++) {
- if (jGlobalPoint == Buffer_Receive_GlobalPoint[MaxLocalVertex_Donor*iProcessor+jVertex]) {
- Buffer_Send_FaceNodes[nLocalFaceNodes_Donor]=MaxLocalVertex_Donor*iProcessor+jVertex;
- Buffer_Send_FaceProc[nLocalFaceNodes_Donor]=iProcessor;
- }
- }
- }
- nLocalFaceNodes_Donor++; // Increment total number of face-nodes / processor
- }
- /* Store the indices */
- Buffer_Send_FaceIndex[nLocalFace_Donor+1] = Buffer_Send_FaceIndex[nLocalFace_Donor]+nNodes;
- nLocalFace_Donor++; // Increment number of faces / processor
- }
- }
- }
- else {
- /*-- Determine whether this face/edge is on the marker --*/
- face_on_marker=true;
- for (iDonor=0; iDonornode[iPointDonor]->GetEdge(jElem);
- dPoint = donor_geometry->edge[inode]->GetNode(iDonor);
- face_on_marker = (face_on_marker && (donor_geometry->node[dPoint]->GetVertex(markDonor) !=-1));
- }
- if (face_on_marker ) {
- for (iDonor=0; iDonornode[iPointDonor]->GetEdge(jElem);
- dPoint = donor_geometry->edge[inode]->GetNode(iDonor);
- // Match node on the face to the correct global index
- long jGlobalPoint = donor_geometry->node[dPoint]->GetGlobalIndex();
- for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) {
- for (jVertex = 0; jVertex < Buffer_Receive_nVertex_Donor[iProcessor]; jVertex++) {
- if (jGlobalPoint == Buffer_Receive_GlobalPoint[MaxLocalVertex_Donor*iProcessor+jVertex]) {
- Buffer_Send_FaceNodes[nLocalFaceNodes_Donor]=MaxLocalVertex_Donor*iProcessor+jVertex;
- Buffer_Send_FaceProc[nLocalFaceNodes_Donor]=iProcessor;
- }
- }
- }
- nLocalFaceNodes_Donor++; // Increment total number of face-nodes / processor
- }
- /* Store the indices */
- Buffer_Send_FaceIndex[nLocalFace_Donor+1] = Buffer_Send_FaceIndex[nLocalFace_Donor]+nNodes;
- nLocalFace_Donor++; // Increment number of faces / processor
- }
- }
- }
- }
- }
-
- //Buffer_Send_FaceIndex[nLocalFace_Donor+1] = MaxFaceNodes_Donor*rank+nLocalFaceNodes_Donor;
-#ifdef HAVE_MPI
- SU2_MPI::Allgather(Buffer_Send_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, Buffer_Receive_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
- SU2_MPI::Allgather(Buffer_Send_FaceProc, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, Buffer_Receive_FaceProc, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
- SU2_MPI::Allgather(Buffer_Send_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, Buffer_Receive_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
-#else
- for (iFace=0; iFacevertex[markTarget][iVertex]->GetNode();
-
- if (target_geometry->node[Point_Target]->GetDomain()) {
-
- Coord_i = target_geometry->node[Point_Target]->GetCoord();
- /*---Loop over the faces previously communicated/stored ---*/
- for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) {
-
- nFaces = (unsigned int)Buffer_Receive_nFace_Donor[iProcessor];
-
- for (iFace = 0; iFace< nFaces; iFace++) {
- /*--- ---*/
-
- nNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1] -
- (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace];
-
- su2double *X = new su2double[nNodes*(nDim+1)];
- faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face
- for (iDonor=0; iDonorvertex[markTarget][iVertex]->SetDonorElem(donor_elem); // in 2D is nearest neighbor
- target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nNodes);
- for (iDonor=0; iDonorvertex[markTarget][iVertex]->GetnDonorPoints();
- target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo();
-
- for (iDonor=0; iDonorvertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor,storeGlobal[iDonor]);
- //cout <vertex[markTarget][iVertex]->SetDonorCoeff(iDonor,storeCoeff[iDonor]);
- target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]);
- }
- }
- }
-
- delete[] Buffer_Send_nVertex_Donor;
- delete[] Buffer_Send_nFace_Donor;
- delete[] Buffer_Send_nFaceNodes_Donor;
-
- delete[] Buffer_Receive_nVertex_Donor;
- delete[] Buffer_Receive_nFace_Donor;
- delete[] Buffer_Receive_nFaceNodes_Donor;
-
- delete[] Buffer_Send_Coord;
- delete[] Buffer_Send_Normal;
- delete[] Buffer_Send_GlobalPoint;
-
- delete[] Buffer_Receive_Coord;
- delete[] Buffer_Receive_Normal;
- delete[] Buffer_Receive_GlobalPoint;
-
- delete[] Buffer_Send_FaceIndex;
- delete[] Buffer_Send_FaceNodes;
- delete[] Buffer_Send_FaceProc;
-
- delete[] Buffer_Receive_FaceIndex;
- delete[] Buffer_Receive_FaceNodes;
- delete[] Buffer_Receive_FaceProc;
- }
- delete [] Coord;
- delete [] Normal;
-
- delete [] projected_point;
-}
-
-void CIsoparametric::Isoparameters(unsigned short nDim, unsigned short nDonor,
- su2double *X, su2double *xj, su2double *isoparams) {
- short iDonor,iDim,k; // indices
- su2double tmp, tmp2;
-
- su2double *x = new su2double[nDim+1];
- su2double *x_tmp = new su2double[nDim+1];
- su2double *Q = new su2double[nDonor*nDonor];
- su2double *R = new su2double[nDonor*nDonor];
- su2double *A = new su2double[(nDim+2)*nDonor];
- su2double *A2 = NULL;
- su2double *x2 = new su2double[nDim+1];
-
- bool *test = new bool[nDim+1];
- bool *testi = new bool[nDim+1];
-
- su2double eps = 1E-10;
-
- short n = nDim+1;
-
- if (nDonor>2) {
- /*--- Create Matrix A: 1st row all 1's, 2nd row x coordinates, 3rd row y coordinates, etc ---*/
- /*--- Right hand side is [1, \vec{x}']'---*/
- for (iDonor=0; iDonoreps && iDonor=0; iDonor--) {
- if (R[iDonor*nDonor+iDonor]>eps)
- isoparams[iDonor]=x_tmp[iDonor]/R[iDonor*nDonor+iDonor];
- else
- isoparams[iDonor]=0;
- for (k=0; k1.0) xi=1.0;
- if (xi<-1.0) xi=-1.0;
- if (eta>1.0) eta=1.0;
- if (eta<-1.0) eta=-1.0;
- isoparams[0]=0.25*(1-xi)*(1-eta);
- isoparams[1]=0.25*(1+xi)*(1-eta);
- isoparams[2]=0.25*(1+xi)*(1+eta);
- isoparams[3]=0.25*(1-xi)*(1+eta);
-
- }
- if (nDonor<4) {
- tmp = 0.0; // value for normalization
- tmp2=0; // check for maximum value, to be used to id nearest neighbor if necessary
- k=0; // index for maximum value
- for (iDonor=0; iDonor< nDonor; iDonor++) {
- if (isoparams[iDonor]>tmp2) {
- k=iDonor;
- tmp2=isoparams[iDonor];
- }
- // [0,1]
- if (isoparams[iDonor]<0) isoparams[iDonor]=0;
- if (isoparams[iDonor]>1) isoparams[iDonor] = 1;
- tmp +=isoparams[iDonor];
- }
- if (tmp>0)
- for (iDonor=0; iDonor< nDonor; iDonor++)
- isoparams[iDonor]=isoparams[iDonor]/tmp;
- else {
- isoparams[k] = 1.0;
- }
- }
-
- delete [] x;
- delete [] x_tmp;
- delete [] Q;
- delete [] R;
- delete [] A;
- if (A2 != NULL) delete [] A2;
- delete [] x2;
-
- delete [] test;
- delete [] testi;
-
-}
-
-
-/* Mirror Interpolator */
-CMirror::CMirror(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) {
-
- /*--- Initialize transfer coefficients between the zones ---*/
- Set_TransferCoeff(config);
-
-}
-
-CMirror::~CMirror() {}
-
-void CMirror::Set_TransferCoeff(CConfig **config) {
- unsigned long iVertex, jVertex;
- unsigned long iPoint;
- unsigned short iDonor=0, iFace=0, iTarget=0;
-
- unsigned short nMarkerInt;
- unsigned short iMarkerInt;
-
- int markDonor=0, markTarget=0;
-
- unsigned int nNodes=0, iNodes=0;
- unsigned long nVertexDonor = 0, nVertexTarget= 0;
- unsigned long Point_Donor = 0;
- unsigned long pGlobalPoint = 0;
- int iProcessor;
-
- unsigned long nLocalFace_Donor = 0, nLocalFaceNodes_Donor=0;
-
- unsigned long faceindex;
-
- int nProcessor = size;
-
- su2double *Buffer_Send_Coeff, *Buffer_Receive_Coeff;
- su2double coeff;
-
- /*--- Number of markers on the interface ---*/
- nMarkerInt = (config[targetZone]->GetMarker_n_ZoneInterface())/2;
-
- /*--- For the number of markers on the interface... ---*/
- for (iMarkerInt=1; iMarkerInt <= nMarkerInt; iMarkerInt++) {
- /*--- Procedure:
- * -Loop through vertices of the aero grid
- * -Find nearest element and allocate enough space in the aero grid donor point info
- * -set the transfer coefficient values
- */
-
- /*--- On the donor side: find the tag of the boundary sharing the interface ---*/
- markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt);
-
- /*--- On the target side: find the tag of the boundary sharing the interface ---*/
- markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt);
-
- /*--- Checks if the zone contains the interface, if not continue to the next step ---*/
- if( !CheckInterfaceBoundary(markDonor, markTarget) )
- continue;
-
- if(markDonor != -1)
- nVertexDonor = donor_geometry->GetnVertex( markDonor );
- else
- nVertexDonor = 0;
-
- if(markTarget != -1)
- nVertexTarget = target_geometry->GetnVertex( markTarget );
- else
- nVertexTarget = 0;
-
- /*-- Collect the number of donor nodes: re-use 'Face' containers --*/
- nLocalFace_Donor=0;
- nLocalFaceNodes_Donor=0;
- for (jVertex = 0; jVertexvertex[markDonor][jVertex]->GetNode(); // Local index of jVertex
-
- if (donor_geometry->node[Point_Donor]->GetDomain()) {
- nNodes = donor_geometry->vertex[markDonor][jVertex]->GetnDonorPoints();
- nLocalFaceNodes_Donor+=nNodes;
- nLocalFace_Donor++;
- }
- }
- Buffer_Send_nFace_Donor= new unsigned long [1];
- Buffer_Send_nFaceNodes_Donor= new unsigned long [1];
-
- Buffer_Receive_nFace_Donor = new unsigned long [nProcessor];
- Buffer_Receive_nFaceNodes_Donor = new unsigned long [nProcessor];
-
- Buffer_Send_nFace_Donor[0] = nLocalFace_Donor;
- Buffer_Send_nFaceNodes_Donor[0] = nLocalFaceNodes_Donor;
-
- /*--- Send Interface vertex information --*/
-#ifdef HAVE_MPI
- SU2_MPI::Allreduce(&nLocalFaceNodes_Donor, &MaxFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD);
- SU2_MPI::Allreduce(&nLocalFace_Donor, &MaxFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD);
- SU2_MPI::Allgather(Buffer_Send_nFace_Donor, 1, MPI_UNSIGNED_LONG, Buffer_Receive_nFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
- SU2_MPI::Allgather(Buffer_Send_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, Buffer_Receive_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
- MaxFace_Donor++;
-#else
- nGlobalFace_Donor = nLocalFace_Donor;
- nGlobalFaceNodes_Donor = nLocalFaceNodes_Donor;
- MaxFaceNodes_Donor = nLocalFaceNodes_Donor;
- MaxFace_Donor = nLocalFace_Donor+1;
- Buffer_Receive_nFace_Donor[0] = Buffer_Send_nFace_Donor[0];
- Buffer_Receive_nFaceNodes_Donor[0] = Buffer_Send_nFaceNodes_Donor[0];
-#endif
-
- /*-- Send donor info --*/
- Buffer_Send_FaceIndex = new unsigned long[MaxFace_Donor];
- Buffer_Send_FaceNodes = new unsigned long[MaxFaceNodes_Donor];
- Buffer_Send_GlobalPoint = new long[MaxFaceNodes_Donor];
- Buffer_Send_Coeff = new su2double[MaxFaceNodes_Donor];
-
- Buffer_Receive_FaceIndex= new unsigned long[MaxFace_Donor*nProcessor];
- Buffer_Receive_FaceNodes= new unsigned long[MaxFaceNodes_Donor*nProcessor];
- Buffer_Receive_GlobalPoint = new long[MaxFaceNodes_Donor*nProcessor];
- Buffer_Receive_Coeff = new su2double[MaxFaceNodes_Donor*nProcessor];
-
- for (iVertex=0; iVertexvertex[markDonor][jVertex]->GetNode(); // Local index of jVertex
- if (donor_geometry->node[Point_Donor]->GetDomain()) {
- nNodes = donor_geometry->vertex[markDonor][jVertex]->GetnDonorPoints();
- for (iDonor=0; iDonornode[Point_Donor]->GetGlobalIndex();
- Buffer_Send_GlobalPoint[nLocalFaceNodes_Donor] =
- donor_geometry->vertex[markDonor][jVertex]->GetInterpDonorPoint(iDonor);
- Buffer_Send_Coeff[nLocalFaceNodes_Donor] =
- donor_geometry->vertex[markDonor][jVertex]->GetDonorCoeff(iDonor);
- nLocalFaceNodes_Donor++;
- }
- Buffer_Send_FaceIndex[nLocalFace_Donor+1] =Buffer_Send_FaceIndex[nLocalFace_Donor]+nNodes;
- nLocalFace_Donor++;
- }
- }
-
-#ifdef HAVE_MPI
- SU2_MPI::Allgather(Buffer_Send_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, Buffer_Receive_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
- SU2_MPI::Allgather(Buffer_Send_GlobalPoint, MaxFaceNodes_Donor, MPI_LONG,Buffer_Receive_GlobalPoint, MaxFaceNodes_Donor, MPI_LONG, MPI_COMM_WORLD);
- SU2_MPI::Allgather(Buffer_Send_Coeff, MaxFaceNodes_Donor, MPI_DOUBLE,Buffer_Receive_Coeff, MaxFaceNodes_Donor, MPI_DOUBLE, MPI_COMM_WORLD);
- SU2_MPI::Allgather(Buffer_Send_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, Buffer_Receive_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
-#else
- for (iFace=0; iFacevertex[markTarget][iVertex]->GetNode();
- if (target_geometry->node[iPoint]->GetDomain()) {
- long Global_Point = target_geometry->node[iPoint]->GetGlobalIndex();
- nNodes = 0;
- for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) {
- for (iFace = 0; iFace < Buffer_Receive_nFace_Donor[iProcessor]; iFace++) {
- faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face
- iNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1]- (unsigned int)faceindex;
- for (iTarget=0; iTargetvertex[markTarget][iVertex]->SetnDonorPoints(nNodes);
- target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo();
-
- iDonor = 0;
- for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) {
- for (iFace = 0; iFace < Buffer_Receive_nFace_Donor[iProcessor]; iFace++) {
-
- faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face
- iNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1]- (unsigned int)faceindex;
- for (iTarget=0; iTargetvertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor,pGlobalPoint);
- target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor,coeff);
- target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, iProcessor);
- iDonor++;
- }
- }
- }
- }
- }
- }
- delete[] Buffer_Send_nFace_Donor;
- delete[] Buffer_Send_nFaceNodes_Donor;
-
- delete[] Buffer_Receive_nFace_Donor;
- delete[] Buffer_Receive_nFaceNodes_Donor;
-
- delete[] Buffer_Send_FaceIndex;
- delete[] Buffer_Send_FaceNodes;
- delete[] Buffer_Send_GlobalPoint;
- delete[] Buffer_Send_Coeff;
-
- delete[] Buffer_Receive_FaceIndex;
- delete[] Buffer_Receive_FaceNodes;
- delete[] Buffer_Receive_GlobalPoint;
- delete[] Buffer_Receive_Coeff;
-
- }
-}
-
-CSlidingMesh::CSlidingMesh(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone){
-
- /*--- Initialize transfer coefficients between the zones ---*/
- Set_TransferCoeff(config);
-
- /*--- For fluid-structure interaction data interpolated with have nDim dimensions ---*/
- // InitializeData(Zones,nDim);
-}
-
-CSlidingMesh::~CSlidingMesh(){}
-
-void CSlidingMesh::Set_TransferCoeff(CConfig **config){
-
- /* --- This routine sets the transfer coefficient for sliding mesh approach --- */
-
- /*
- * The algorithm is based on Rinaldi et al. "Flux-conserving treatment of non-conformal interfaces
- * for finite-volume discritization of conservaation laws" 2015, Comp. Fluids, 120, pp 126-139
- */
-
- /* 0 - Variable declaration - */
-
- /* --- General variables --- */
-
- bool check;
-
- unsigned short iDim, nDim;
-
- unsigned long ii, jj, *uptr;
- unsigned long vPoint;
- unsigned long iEdgeVisited, nEdgeVisited, iNodeVisited;
- unsigned long nAlreadyVisited, nToVisit, StartVisited;
-
- unsigned long *alreadyVisitedDonor, *ToVisit, *tmpVect;
- unsigned long *storeProc, *tmp_storeProc;
-
- su2double dTMP;
- su2double *Coeff_Vect, *tmp_Coeff_Vect;
-
- /* --- Geometrical variables --- */
-
- su2double *Coord_i, *Coord_j, dist, mindist, *Normal;
- su2double Area, Area_old, tmp_Area;
- su2double LineIntersectionLength, *Direction, length;
-
-
- /* --- Markers Variables --- */
-
- unsigned short iMarkerInt, nMarkerInt;
-
- unsigned long iVertex, nVertexTarget;
-
- int markDonor, markTarget;
-
- /* --- Target variables --- */
-
- unsigned long target_iPoint, jVertexTarget;
- unsigned long nEdges_target, nNode_target;
-
- unsigned long *Target_nLinkedNodes, *Target_LinkedNodes, *Target_StartLinkedNodes, *target_segment;
- unsigned long *Target_Proc;
- long *Target_GlobalPoint, *Donor_GlobalPoint;
-
- su2double *TargetPoint_Coord, *target_iMidEdge_point, *target_jMidEdge_point, **target_element;
-
- /* --- Donor variables --- */
-
- unsigned long donor_StartIndex, donor_forward_point, donor_backward_point, donor_iPoint, donor_OldiPoint;
- unsigned long nEdges_donor, nNode_donor, nGlobalVertex_Donor;
-
- unsigned long nDonorPoints, iDonor;
- unsigned long *Donor_Vect, *tmp_Donor_Vect;
- unsigned long *Donor_nLinkedNodes, *Donor_LinkedNodes, *Donor_StartLinkedNodes;
- unsigned long *Donor_Proc;
-
- su2double *donor_iMidEdge_point, *donor_jMidEdge_point;
- su2double **donor_element, *DonorPoint_Coord;
-
- /* 1 - Variable pre-processing - */
-
- nDim = donor_geometry->GetnDim();
-
- /*--- Setting up auxiliary vectors ---*/
-
- Donor_Vect = NULL;
- Coeff_Vect = NULL;
- storeProc = NULL;
-
- tmp_Donor_Vect = NULL;
- tmp_Coeff_Vect = NULL;
- tmp_storeProc = NULL;
-
- Normal = new su2double[nDim];
- Direction = new su2double[nDim];
-
-
- /* 2 - Find boundary tag between touching grids */
-
- /*--- Number of markers on the FSI interface ---*/
- nMarkerInt = (int)( config[ donorZone ]->GetMarker_n_ZoneInterface() ) / 2;
-
- /*--- For the number of markers on the interface... ---*/
- for ( iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++ ){
-
- /*--- On the donor side: find the tag of the boundary sharing the interface ---*/
- markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt);
-
- /*--- On the target side: find the tag of the boundary sharing the interface ---*/
- markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt);
-
- /*--- Checks if the zone contains the interface, if not continue to the next step ---*/
- if( !CheckInterfaceBoundary(markDonor, markTarget) )
- continue;
-
- if(markTarget != -1)
- nVertexTarget = target_geometry->GetnVertex( markTarget );
- else
- nVertexTarget = 0;
-
- /*
- 3 -Reconstruct the boundaries from parallel partitioning
- */
-
- /*--- Target boundary ---*/
- ReconstructBoundary(targetZone, markTarget);
-
- nGlobalVertex_Target = nGlobalVertex;
-
- TargetPoint_Coord = Buffer_Receive_Coord;
- Target_GlobalPoint = Buffer_Receive_GlobalPoint;
- Target_nLinkedNodes = Buffer_Receive_nLinkedNodes;
- Target_StartLinkedNodes = Buffer_Receive_StartLinkedNodes;
- Target_LinkedNodes = Buffer_Receive_LinkedNodes;
- Target_Proc = Buffer_Receive_Proc;
-
- /*--- Donor boundary ---*/
- ReconstructBoundary(donorZone, markDonor);
-
- nGlobalVertex_Donor = nGlobalVertex;
-
- DonorPoint_Coord = Buffer_Receive_Coord;
- Donor_GlobalPoint = Buffer_Receive_GlobalPoint;
- Donor_nLinkedNodes = Buffer_Receive_nLinkedNodes;
- Donor_StartLinkedNodes = Buffer_Receive_StartLinkedNodes;
- Donor_LinkedNodes = Buffer_Receive_LinkedNodes;
- Donor_Proc = Buffer_Receive_Proc;
-
- /*--- Starts building the supermesh layer (2D or 3D) ---*/
- /* - For each target node, it first finds the closest donor point
- * - Then it creates the supermesh in the close proximity of the target point:
- * - Starting from the closest donor node, it expands the supermesh by including
- * donor elements neighboring the initial one, until the overall target area is fully covered.
- */
-
- if(nDim == 2){
-
- target_iMidEdge_point = new su2double[nDim];
- target_jMidEdge_point = new su2double[nDim];
-
- donor_iMidEdge_point = new su2double[nDim];
- donor_jMidEdge_point = new su2double[nDim];
-
- /*--- Starts with supermesh reconstruction ---*/
-
- target_segment = new unsigned long[2];
-
- for (iVertex = 0; iVertex < nVertexTarget; iVertex++) {
-
- nDonorPoints = 0;
-
- /*--- Stores coordinates of the target node ---*/
-
- target_iPoint = target_geometry->vertex[markTarget][iVertex]->GetNode();
-
- if (target_geometry->node[target_iPoint]->GetDomain()){
-
- Coord_i = target_geometry->node[target_iPoint]->GetCoord();
-
- /*--- Brute force to find the closest donor_node ---*/
-
- mindist = 1E6;
- donor_StartIndex = 0;
-
- for (donor_iPoint = 0; donor_iPoint < nGlobalVertex_Donor; donor_iPoint++) {
-
- Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ];
-
- dist = PointsDistance(Coord_i, Coord_j);
-
- if (dist < mindist) {
- mindist = dist;
- donor_StartIndex = donor_iPoint;
- }
-
- if (dist == 0.0){
- donor_StartIndex = donor_iPoint;
- break;
- }
- }
-
- donor_iPoint = donor_StartIndex;
- donor_OldiPoint = donor_iPoint;
-
- /*--- Contruct information regarding the target cell ---*/
-
- long dPoint = target_geometry->node[target_iPoint]->GetGlobalIndex();
- for (jVertexTarget = 0; jVertexTarget < nGlobalVertex_Target; jVertexTarget++)
- if( dPoint == Target_GlobalPoint[jVertexTarget] )
- break;
-
- if ( Target_nLinkedNodes[jVertexTarget] == 1 ){
- target_segment[0] = Target_LinkedNodes[ Target_StartLinkedNodes[jVertexTarget] ];
- target_segment[1] = jVertexTarget;
- }
- else{
- target_segment[0] = Target_LinkedNodes[ Target_StartLinkedNodes[jVertexTarget] ];
- target_segment[1] = Target_LinkedNodes[ Target_StartLinkedNodes[jVertexTarget] + 1];
- }
-
- dTMP = 0;
- for(iDim = 0; iDim < nDim; iDim++){
- target_iMidEdge_point[iDim] = ( TargetPoint_Coord[ nDim * target_segment[0] + iDim ] + target_geometry->node[ target_iPoint ]->GetCoord(iDim) ) / 2;
- target_jMidEdge_point[iDim] = ( TargetPoint_Coord[ nDim * target_segment[1] + iDim ] + target_geometry->node[ target_iPoint ]->GetCoord(iDim) ) / 2;
-
- Direction[iDim] = target_jMidEdge_point[iDim] - target_iMidEdge_point[iDim];
- dTMP += Direction[iDim] * Direction[iDim];
- }
-
- dTMP = sqrt(dTMP);
- for(iDim = 0; iDim < nDim; iDim++)
- Direction[iDim] /= dTMP;
-
- length = PointsDistance(target_iMidEdge_point, target_jMidEdge_point);
-
- check = false;
-
- /*--- Proceeds along the forward direction (depending on which connected boundary node is found first) ---*/
-
- while( !check ){
-
- /*--- Proceeds until the value of the intersection area is null ---*/
-
- if ( Donor_nLinkedNodes[donor_iPoint] == 1 ){
- donor_forward_point = Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_iPoint] ];
- donor_backward_point = donor_iPoint;
- }
- else{
- uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_iPoint] ];
-
- if( donor_OldiPoint != uptr[0] ){
- donor_forward_point = uptr[0];
- donor_backward_point = uptr[1];
- }
- else{
- donor_forward_point = uptr[1];
- donor_backward_point = uptr[0];
- }
- }
-
- if(donor_iPoint >= nGlobalVertex_Donor){
- check = true;
- continue;
- }
-
- for(iDim = 0; iDim < nDim; iDim++){
- donor_iMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_forward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2;
- donor_jMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_backward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2;
- }
-
- LineIntersectionLength = ComputeLineIntersectionLength(target_iMidEdge_point, target_jMidEdge_point, donor_iMidEdge_point, donor_jMidEdge_point, Direction);
-
- if ( LineIntersectionLength == 0.0 ){
- check = true;
- continue;
- }
-
- /*--- In case the element intersects the target cell, update the auxiliary communication data structure ---*/
-
- tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ];
- tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ];
- tmp_storeProc = new unsigned long[ nDonorPoints + 1 ];
-
- for( iDonor = 0; iDonor < nDonorPoints; iDonor++){
- tmp_Donor_Vect[iDonor] = Donor_Vect[iDonor];
- tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor];
- tmp_storeProc[iDonor] = storeProc[iDonor];
- }
-
- tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint;
- tmp_Coeff_Vect[ nDonorPoints ] = LineIntersectionLength / length;
- tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint];
-
- if (Donor_Vect != NULL) delete [] Donor_Vect;
- if (Coeff_Vect != NULL) delete [] Coeff_Vect;
- if (storeProc != NULL) delete [] storeProc;
-
- Donor_Vect = tmp_Donor_Vect;
- Coeff_Vect = tmp_Coeff_Vect;
- storeProc = tmp_storeProc;
-
- donor_OldiPoint = donor_iPoint;
- donor_iPoint = donor_forward_point;
-
- nDonorPoints++;
- }
-
- if ( Donor_nLinkedNodes[donor_StartIndex] == 2 ){
- check = false;
-
- uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_StartIndex] ];
-
- donor_iPoint = uptr[1];
- donor_OldiPoint = donor_StartIndex;
- }
- else
- check = true;
-
- /*--- Proceeds along the backward direction (depending on which connected boundary node is found first) ---*/
-
- while( !check ){
-
- /*--- Proceeds until the value of the intersection length is null ---*/
- if ( Donor_nLinkedNodes[donor_iPoint] == 1 ){
- donor_forward_point = donor_OldiPoint;
- donor_backward_point = donor_iPoint;
- }
- else{
- uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_iPoint] ];
-
- if( donor_OldiPoint != uptr[0] ){
- donor_forward_point = uptr[0];
- donor_backward_point = uptr[1];
- }
- else{
- donor_forward_point = uptr[1];
- donor_backward_point = uptr[0];
- }
- }
-
- if(donor_iPoint >= nGlobalVertex_Donor){
- check = true;
- continue;
- }
-
- for(iDim = 0; iDim < nDim; iDim++){
- donor_iMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_forward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2;
- donor_jMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_backward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2;
- }
-
- LineIntersectionLength = ComputeLineIntersectionLength(target_iMidEdge_point, target_jMidEdge_point, donor_iMidEdge_point, donor_jMidEdge_point, Direction);
-
- if ( LineIntersectionLength == 0.0 ){
- check = true;
- continue;
- }
-
- /*--- In case the element intersects the target cell, update the auxiliary communication data structure ---*/
-
- tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ];
- tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ];
- tmp_storeProc = new unsigned long[ nDonorPoints + 1 ];
-
- for( iDonor = 0; iDonor < nDonorPoints; iDonor++){
- tmp_Donor_Vect[iDonor] = Donor_Vect[iDonor];
- tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor];
- tmp_storeProc[iDonor] = storeProc[iDonor];
- }
-
- tmp_Coeff_Vect[ nDonorPoints ] = LineIntersectionLength / length;
- tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint;
- tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint];
-
- if (Donor_Vect != NULL) delete [] Donor_Vect;
- if (Coeff_Vect != NULL) delete [] Coeff_Vect;
- if (storeProc != NULL) delete [] storeProc;
-
- Donor_Vect = tmp_Donor_Vect;
- Coeff_Vect = tmp_Coeff_Vect;
- storeProc = tmp_storeProc;
-
- donor_OldiPoint = donor_iPoint;
- donor_iPoint = donor_forward_point;
-
- nDonorPoints++;
- }
-
- /*--- Set the communication data structure and copy data from the auxiliary vectors ---*/
-
- target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nDonorPoints);
-
- target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo();
-
- for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){
- target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff( iDonor, Coeff_Vect[iDonor]);
- target_geometry->vertex[markTarget][iVertex]->SetInterpDonorPoint( iDonor, Donor_GlobalPoint[ Donor_Vect[iDonor] ]);
- target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]);
- }
- }
- }
-
- delete [] target_segment;
-
- delete [] target_iMidEdge_point;
- delete [] target_jMidEdge_point;
-
- delete [] donor_iMidEdge_point;
- delete [] donor_jMidEdge_point;
- }
- else{
- /* --- 3D geometry, creates a superficial super-mesh --- */
-
- for (iVertex = 0; iVertex < nVertexTarget; iVertex++) {
-
- nDonorPoints = 0;
-
- /*--- Stores coordinates of the target node ---*/
-
- target_iPoint = target_geometry->vertex[markTarget][iVertex]->GetNode();
-
- if (target_geometry->node[target_iPoint]->GetDomain()){
-
- Coord_i = target_geometry->node[target_iPoint]->GetCoord();
-
- target_geometry->vertex[markTarget][iVertex]->GetNormal(Normal);
-
- /*--- The value of Area computed here includes also portion of boundary belonging to different marker ---*/
- Area = 0.0;
- for (iDim = 0; iDim < nDim; iDim++)
- Area += Normal[iDim]*Normal[iDim];
- Area = sqrt(Area);
-
- for (iDim = 0; iDim < nDim; iDim++)
- Normal[iDim] /= Area;
-
- for (iDim = 0; iDim < nDim; iDim++)
- Coord_i[iDim] = target_geometry->node[target_iPoint]->GetCoord(iDim);
-
- long dPoint = target_geometry->node[target_iPoint]->GetGlobalIndex();
- for (target_iPoint = 0; target_iPoint < nGlobalVertex_Target; target_iPoint++){
- if( dPoint == Target_GlobalPoint[target_iPoint] )
- break;
- }
-
- /*--- Build local surface dual mesh for target element ---*/
-
- nEdges_target = Target_nLinkedNodes[target_iPoint];
-
- nNode_target = 2*(nEdges_target + 1);
-
- target_element = new su2double*[nNode_target];
- for (ii = 0; ii < nNode_target; ii++)
- target_element[ii] = new su2double[nDim];
-
- nNode_target = Build_3D_surface_element(Target_LinkedNodes, Target_StartLinkedNodes, Target_nLinkedNodes, TargetPoint_Coord, target_iPoint, target_element);
-
- /*--- Brute force to find the closest donor_node ---*/
-
- mindist = 1E6;
- donor_StartIndex = 0;
-
- for (donor_iPoint = 0; donor_iPoint < nGlobalVertex_Donor; donor_iPoint++) {
-
- Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ];
-
- dist = PointsDistance(Coord_i, Coord_j);
-
- if (dist < mindist) {
- mindist = dist;
- donor_StartIndex = donor_iPoint;
- }
-
- if (dist == 0.0){
- donor_StartIndex = donor_iPoint;
- break;
- }
- }
-
- donor_iPoint = donor_StartIndex;
-
- nEdges_donor = Donor_nLinkedNodes[donor_iPoint];
-
- donor_element = new su2double*[ 2*nEdges_donor + 2 ];
- for (ii = 0; ii < 2*nEdges_donor + 2; ii++)
- donor_element[ii] = new su2double[nDim];
-
- nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, DonorPoint_Coord, donor_iPoint, donor_element);
-
- Area = 0;
- for (ii = 1; ii < nNode_target-1; ii++){
- for (jj = 1; jj < nNode_donor-1; jj++){
- Area += Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], donor_element[0], donor_element[jj], donor_element[jj+1], Normal);
- //cout << Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], donor_element[0], donor_element[jj], donor_element[jj+1], Normal) << endl;
- }
- }
-
- for (ii = 0; ii < 2*nEdges_donor + 2; ii++)
- delete [] donor_element[ii];
- delete [] donor_element;
-
- nDonorPoints = 1;
-
- /*--- In case the element intersect the target cell update the auxiliary communication data structure ---*/
-
- Coeff_Vect = new su2double[ nDonorPoints ];
- Donor_Vect = new unsigned long[ nDonorPoints ];
- storeProc = new unsigned long[ nDonorPoints ];
-
- Coeff_Vect[0] = Area;
- Donor_Vect[0] = donor_iPoint;
- storeProc[0] = Donor_Proc[donor_iPoint];
-
- alreadyVisitedDonor = new unsigned long[1];
-
- alreadyVisitedDonor[0] = donor_iPoint;
- nAlreadyVisited = 1;
- StartVisited = 0;
-
- Area_old = -1;
-
- while( Area > Area_old ){
-
- /*
- * - Starting from the closest donor_point, it expands the supermesh by a countour search pattern.
- * - The closest donor element becomes the core, at each iteration a new layer of elements around the core is taken into account
- */
-
- Area_old = Area;
-
- ToVisit = NULL;
- nToVisit = 0;
-
- for( iNodeVisited = StartVisited; iNodeVisited < nAlreadyVisited; iNodeVisited++ ){
-
- vPoint = alreadyVisitedDonor[ iNodeVisited ];
-
- nEdgeVisited = Donor_nLinkedNodes[vPoint];
-
- for (iEdgeVisited = 0; iEdgeVisited < nEdgeVisited; iEdgeVisited++){
-
- donor_iPoint = Donor_LinkedNodes[ Donor_StartLinkedNodes[vPoint] + iEdgeVisited];
-
- /*--- Check if the node to visit is already listed in the data structure to avoid double visits ---*/
-
- check = 0;
-
- for( jj = 0; jj < nAlreadyVisited; jj++ ){
- if( donor_iPoint == alreadyVisitedDonor[jj] ){
- check = 1;
- break;
- }
- }
-
- if( check == 0 && ToVisit != NULL){
- for( jj = 0; jj < nToVisit; jj++ )
- if( donor_iPoint == ToVisit[jj] ){
- check = 1;
- break;
- }
- }
-
- if( check == 0 ){
- /*--- If the node was not already visited, visit it and list it into data structure ---*/
-
- tmpVect = new unsigned long[ nToVisit + 1 ];
-
- for( jj = 0; jj < nToVisit; jj++ )
- tmpVect[jj] = ToVisit[jj];
- tmpVect[nToVisit] = donor_iPoint;
-
- if( ToVisit != NULL )
- delete [] ToVisit;
-
- ToVisit = tmpVect;
- tmpVect = NULL;
-
- nToVisit++;
-
- /*--- Find the value of the intersection area between the current donor element and the target element --- */
-
- nEdges_donor = Donor_nLinkedNodes[donor_iPoint];
-
- donor_element = new su2double*[ 2*nEdges_donor + 2 ];
- for (ii = 0; ii < 2*nEdges_donor + 2; ii++)
- donor_element[ii] = new su2double[nDim];
-
- nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, DonorPoint_Coord, donor_iPoint, donor_element);
-
- tmp_Area = 0;
- for (ii = 1; ii < nNode_target-1; ii++)
- for (jj = 1; jj < nNode_donor-1; jj++)
- tmp_Area += Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], donor_element[0], donor_element[jj], donor_element[jj+1], Normal);
-
- for (ii = 0; ii < 2*nEdges_donor + 2; ii++)
- delete [] donor_element[ii];
- delete [] donor_element;
-
- /*--- In case the element intersect the target cell update the auxiliary communication data structure ---*/
-
- tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ];
- tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ];
- tmp_storeProc = new unsigned long[ nDonorPoints + 1 ];
-
- for( iDonor = 0; iDonor < nDonorPoints; iDonor++){
- tmp_Donor_Vect[iDonor] = Donor_Vect[iDonor];
- tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor];
- tmp_storeProc[iDonor] = storeProc[iDonor];
- }
-
- tmp_Coeff_Vect[ nDonorPoints ] = tmp_Area;
- tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint;
- tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint];
-
- if (Donor_Vect != NULL) {delete [] Donor_Vect; }
- if (Coeff_Vect != NULL) {delete [] Coeff_Vect; }
- if (storeProc != NULL) {delete [] storeProc; }
-
- Donor_Vect = tmp_Donor_Vect;
- Coeff_Vect = tmp_Coeff_Vect;
- storeProc = tmp_storeProc;
-
- tmp_Coeff_Vect = NULL;
- tmp_Donor_Vect = NULL;
- tmp_storeProc = NULL;
-
- nDonorPoints++;
-
- Area += tmp_Area;
- }
- }
- }
-
- /*--- Update auxiliary data structure ---*/
-
- StartVisited = nAlreadyVisited;
-
- tmpVect = new unsigned long[ nAlreadyVisited + nToVisit ];
-
- for( jj = 0; jj < nAlreadyVisited; jj++ )
- tmpVect[jj] = alreadyVisitedDonor[jj];
-
- for( jj = 0; jj < nToVisit; jj++ )
- tmpVect[ nAlreadyVisited + jj ] = ToVisit[jj];
-
- if( alreadyVisitedDonor != NULL )
- delete [] alreadyVisitedDonor;
-
- alreadyVisitedDonor = tmpVect;
-
- nAlreadyVisited += nToVisit;
-
- delete [] ToVisit;
- }
-
- delete [] alreadyVisitedDonor;
-
- /*--- Set the communication data structure and copy data from the auxiliary vectors ---*/
-
- target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nDonorPoints);
- target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo();
-
- for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){
- target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor, Coeff_Vect[iDonor]/Area);
- target_geometry->vertex[markTarget][iVertex]->SetInterpDonorPoint( iDonor, Donor_GlobalPoint[ Donor_Vect[iDonor] ] );
- target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]);
- //cout <GetnDim();
-
- su2double dotA2, dotB1, dotB2;
-
- dotA2 = 0;
- for(iDim = 0; iDim < nDim; iDim++)
- dotA2 += ( A2[iDim] - A1[iDim] ) * Direction[iDim];
-
- if( dotA2 >= 0 ){
- dotB1 = 0;
- dotB2 = 0;
- for(iDim = 0; iDim < nDim; iDim++){
- dotB1 += ( B1[iDim] - A1[iDim] ) * Direction[iDim];
- dotB2 += ( B2[iDim] - A1[iDim] ) * Direction[iDim];
- }
- }
- else{
- dotA2 *= -1;
-
- dotB1 = 0;
- dotB2 = 0;
- for(iDim = 0; iDim < nDim; iDim++){
- dotB1 -= ( B1[iDim] - A1[iDim] ) * Direction[iDim];
- dotB2 -= ( B2[iDim] - A1[iDim] ) * Direction[iDim];
- }
- }
-
- if( dotB1 >= 0 && dotB1 <= dotA2 ){
- if ( dotB2 < 0 )
- return fabs( dotB1 );
- if ( dotB2 > dotA2 )
- return fabs( dotA2 - dotB1 );
-
- return fabs( dotB1 - dotB2 );
- }
-
- if( dotB2 >= 0 && dotB2 <= dotA2 ){
- if ( dotB1 < 0 )
- return fabs(dotB2);
- if ( dotB1 > dotA2 )
- return fabs( dotA2 - dotB2 );
- }
-
- if( ( dotB1 <= 0 && dotA2 <= dotB2 ) || ( dotB2 <= 0 && dotA2 <= dotB1 ) )
- return fabs( dotA2 );
-
- return 0.0;
-}
-
-su2double CSlidingMesh::Compute_Triangle_Intersection(su2double* A1, su2double* A2, su2double* A3, su2double* B1, su2double* B2, su2double* B3, su2double* Direction){
-
- /* --- This routine is ONLY for 3D grids --- */
- /* --- Projects triangle points onto a plane, specified by its normal "Direction", and calls the ComputeIntersectionArea routine --- */
-
- unsigned short iDim;
- unsigned short nDim = 3;
-
- su2double I[3], J[3], K[3];
- su2double a1[3], a2[3], a3[3];
- su2double b1[3], b2[3], b3[3];
- su2double m1, m2;
-
- /* --- Reference frame is determined by: x = A1A2 y = x ^ ( -Direction ) --- */
-
- for(iDim = 0; iDim < 3; iDim++){
- a1[iDim] = 0;
- a2[iDim] = 0;
- a3[iDim] = 0;
-
- b1[iDim] = 0;
- b2[iDim] = 0;
- b3[iDim] = 0;
- }
-
- m1 = 0;
- for(iDim = 0; iDim < nDim; iDim++){
- K[iDim] = Direction[iDim];
-
- m1 += K[iDim] * K[iDim];
- }
-
- for(iDim = 0; iDim < nDim; iDim++)
- K[iDim] /= sqrt(m1);
-
- m2 = 0;
- for(iDim = 0; iDim < nDim; iDim++)
- m2 += (A2[iDim] - A1[iDim]) * K[iDim];
-
- m1 = 0;
- for(iDim = 0; iDim < nDim; iDim++){
- I[iDim] = (A2[iDim] - A1[iDim]) - m2 * K[iDim];
- m1 += I[iDim] * I[iDim];
- }
-
- for(iDim = 0; iDim < nDim; iDim++)
- I[iDim] /= sqrt(m1);
-
- // Cross product to find Y
- J[0] = K[1]*I[2] - K[2]*I[1];
- J[1] = -(K[0]*I[2] - K[2]*I[0]);
- J[2] = K[0]*I[1] - K[1]*I[0];
-
- /* --- Project all points on the plane specified by Direction and change their reference frame taking A1 as origin --- */
-
- for(iDim = 0; iDim < nDim; iDim++){
- a2[0] += (A2[iDim] - A1[iDim]) * I[iDim];
- a2[1] += (A2[iDim] - A1[iDim]) * J[iDim];
- a2[2] += (A2[iDim] - A1[iDim]) * K[iDim];
-
- a3[0] += (A3[iDim] - A1[iDim]) * I[iDim];
- a3[1] += (A3[iDim] - A1[iDim]) * J[iDim];
- a3[2] += (A3[iDim] - A1[iDim]) * K[iDim];
-
- b1[0] += (B1[iDim] - A1[iDim]) * I[iDim];
- b1[1] += (B1[iDim] - A1[iDim]) * J[iDim];
- b1[2] += (B1[iDim] - A1[iDim]) * K[iDim];
-
- b2[0] += (B2[iDim] - A1[iDim]) * I[iDim];
- b2[1] += (B2[iDim] - A1[iDim]) * J[iDim];
- b2[2] += (B2[iDim] - A1[iDim]) * K[iDim];
-
- b3[0] += (B3[iDim] - A1[iDim]) * I[iDim];
- b3[1] += (B3[iDim] - A1[iDim]) * J[iDim];
- b3[2] += (B3[iDim] - A1[iDim]) * K[iDim];
- }
-
- /* --- Compute intersection area --- */
-
- return ComputeIntersectionArea( a1, a2, a3, b1, b2, b3 );
-}
-
-su2double CSlidingMesh::ComputeIntersectionArea( su2double* P1, su2double* P2, su2double* P3, su2double* Q1, su2double* Q2, su2double* Q3 ){
-
- /* --- This routines computes the area of the polygonal element generated by the superimposition of 2 planar triangle --- */
- /* --- The 2 triangle must lie on the same plane --- */
-
- unsigned short iDim, nPoints, i, j, k;
- unsigned short nDim, min_theta_index;
-
- su2double points[16][2], IntersectionPoint[2], theta[6];
- su2double TriangleP[4][2], TriangleQ[4][2];
- su2double Area, det, dot1, dot2, dtmp, min_theta;
-
- nDim = 2;
- nPoints = 0;
-
- for(iDim = 0; iDim < nDim; iDim++){
- TriangleP[0][iDim] = 0;
- TriangleP[1][iDim] = P2[iDim] - P1[iDim];
- TriangleP[2][iDim] = P3[iDim] - P1[iDim];
- TriangleP[3][iDim] = 0;
-
- TriangleQ[0][iDim] = Q1[iDim] - P1[iDim];
- TriangleQ[1][iDim] = Q2[iDim] - P1[iDim];
- TriangleQ[2][iDim] = Q3[iDim] - P1[iDim];
- TriangleQ[3][iDim] = Q1[iDim] - P1[iDim];
- }
-
-
- for( j = 0; j < 3; j++){
- if( CheckPointInsideTriangle(TriangleP[j], TriangleQ[0], TriangleQ[1], TriangleQ[2]) ){
-
- // Then P1 is also inside triangle Q, so store it
- for(iDim = 0; iDim < nDim; iDim++)
- points[nPoints][iDim] = TriangleP[j][iDim];
-
- nPoints++;
- }
- }
-
- for( j = 0; j < 3; j++){
- if( CheckPointInsideTriangle(TriangleQ[j], TriangleP[0], TriangleP[1], TriangleP[2]) ){
-
- // Then Q1 is also inside triangle P, so store it
- for(iDim = 0; iDim < nDim; iDim++)
- points[nPoints][iDim] = TriangleQ[j][iDim];
-
- nPoints++;
- }
- }
-
-
- // Compute all edge intersections
-
- for( j = 0; j < 3; j++){
- for( i = 0; i < 3; i++){
-
- det = (TriangleP[j][0] - TriangleP[j+1][0]) * ( TriangleQ[i][1] - TriangleQ[i+1][1] ) - (TriangleP[j][1] - TriangleP[j+1][1]) * (TriangleQ[i][0] - TriangleQ[i+1][0]);
-
- if ( det != 0.0 ){
- ComputeLineIntersectionPoint( TriangleP[j], TriangleP[j+1], TriangleQ[i], TriangleQ[i+1], IntersectionPoint );
-
- dot1 = 0;
- dot2 = 0;
- for(iDim = 0; iDim < nDim; iDim++){
- dot1 += ( TriangleP[j][iDim] - IntersectionPoint[iDim] ) * ( TriangleP[j+1][iDim] - IntersectionPoint[iDim] );
- dot2 += ( TriangleQ[i][iDim] - IntersectionPoint[iDim] ) * ( TriangleQ[i+1][iDim] - IntersectionPoint[iDim] );
- }
-
- if( dot1 <= 0 && dot2 <= 0 ){ // It found one intersection
-
- // Store temporarily the intersection point
-
- for(iDim = 0; iDim < nDim; iDim++)
- points[nPoints][iDim] = IntersectionPoint[iDim];
-
- nPoints++;
- }
- }
- }
- }
-
- // Remove double points, if any
-
- for( i = 0; i < nPoints; i++){
- for( j = i+1; j < nPoints; j++){
- if(points[j][0] == points[i][0] && points[j][1] == points[i][1]){
- for( k = j; k < nPoints-1; k++){
- points[k][0] = points[k+1][0];
- points[k][1] = points[k+1][1];
- }
- nPoints--;
- j--;
- }
- }
- }
-
- // Re-order nodes
-
- for( i = 1; i < nPoints; i++){ // Change again reference frame
- for(iDim = 0; iDim < nDim; iDim++)
- points[i][iDim] -= points[0][iDim];
-
- // Compute polar azimuth for each node but the first
- theta[i] = atan2(points[i][1], points[i][0]);
- }
-
- for(iDim = 0; iDim < nDim; iDim++)
- points[0][iDim] = 0;
-
- for( i = 1; i < nPoints; i++){
-
- min_theta = theta[i];
- min_theta_index = 0;
-
- for( j = i + 1; j < nPoints; j++){
-
- if( theta[j] < min_theta ){
- min_theta = theta[j];
- min_theta_index = j;
- }
- }
-
- if( min_theta_index != 0 ){
- dtmp = theta[i];
- theta[i] = theta[min_theta_index];
- theta[min_theta_index] = dtmp;
-
- dtmp = points[i][0];
- points[i][0] = points[min_theta_index][0];
- points[min_theta_index][0] = dtmp;
-
- dtmp = points[i][1];
- points[i][1] = points[min_theta_index][1];
- points[min_theta_index][1] = dtmp;
- }
- }
-
- // compute area using cross product rule, points position are referred to the 2-dimensional, local, reference frame centered in points[0]
-
- Area = 0;
-
- if (nPoints > 2){
- for( i = 1; i < nPoints-1; i++ ){
-
- // Ax*By
- Area += ( points[i][0] - points[0][0] ) * ( points[i+1][1] - points[0][1] );
-
- // Ay*Bx
- Area -= ( points[i][1] - points[0][1] ) * ( points[i+1][0] - points[0][0] );
- }
- }
-
- return fabs(Area)/2;
-}
-
-void CSlidingMesh::ComputeLineIntersectionPoint( su2double* A1, su2double* A2, su2double* B1, su2double* B2, su2double* IntersectionPoint ){
-
- /* --- Uses determinant rule to compute the intersection point between 2 straight segments --- */
- /* This works only for lines on a 2D plane, A1, A2 and B1, B2 are respectively the head and the tail points of each segment,
- * since they're on a 2D plane they are defined by a 2-elements array containing their coordinates */
-
- su2double det;
-
- det = (A1[0] - A2[0]) * (B1[1] - B2[1]) - (A1[1] - A2[1]) * (B1[0] - B2[0]);
-
- if ( det != 0.0 ){ // else there is no intersection point
- IntersectionPoint[0] = ( ( A1[0]*A2[1] - A1[1]*A2[0] ) * ( B1[0] - B2[0] ) - ( B1[0]*B2[1] - B1[1]*B2[0] ) * ( A1[0] - A2[0] ) ) / det;
- IntersectionPoint[1] = ( ( A1[0]*A2[1] - A1[1]*A2[0] ) * ( B1[1] - B2[1] ) - ( B1[0]*B2[1] - B1[1]*B2[0] ) * ( A1[1] - A2[1] ) ) / det;
- }
-
- return;
-}
-
-bool CSlidingMesh::CheckPointInsideTriangle(su2double* Point, su2double* T1, su2double* T2, su2double* T3){
-
- /* --- Check whether a point "Point" lies inside or outside a triangle defined by 3 points "T1", "T2", "T3" --- */
- /* For each edge it checks on which side the point lies:
- * - Computes the unit vector pointing at the internal side of the edge
- * - Comutes the vector that connects the point to a point along the edge
- * - If the dot product is positive it means that the point is on the internal side of the edge
- * - If the check is positive for all the 3 edges, then the point lies within the triangle
- */
-
- unsigned short iDim, nDim, check;
-
- su2double vect1[2], vect2[2], r[2];
- su2double dot;
-
- check = 0;
- nDim = 2;
-
- /* --- Check first edge --- */
-
- dot = 0;
- for(iDim = 0; iDim < nDim; iDim++){
- vect1[iDim] = T3[iDim] - T1[iDim]; // vec 1 is aligned to the edge
- vect2[iDim] = T2[iDim] - T1[iDim]; // vect 2 is the vector connecting one edge point to the third triangle vertex
-
- r[iDim] = Point[iDim] - T1[iDim]; // Connects point to vertex T1
-
- dot += vect2[iDim] * vect2[iDim];
- }
- dot = sqrt(dot);
-
- for(iDim = 0; iDim < nDim; iDim++)
- vect2[iDim] /= dot;
-
- dot = 0;
- for(iDim = 0; iDim < nDim; iDim++)
- dot += vect1[iDim] * vect2[iDim];
-
- for(iDim = 0; iDim < nDim; iDim++)
- vect1[iDim] = T3[iDim] - (T1[iDim] + dot * vect2[iDim]); // Computes the inward unit vector
-
- dot = 0;
- for(iDim = 0; iDim < nDim; iDim++) // Checs that the point lies on the internal plane
- dot += vect1[iDim] * r[iDim];
-
- if (dot >= 0)
- check++;
-
- /* --- Check second edge --- */
-
- dot = 0;
- for(iDim = 0; iDim < nDim; iDim++){
- vect1[iDim] = T1[iDim] - T2[iDim];
- vect2[iDim] = T3[iDim] - T2[iDim];
-
- r[iDim] = Point[iDim] - T2[iDim];
-
- dot += vect2[iDim] * vect2[iDim];
- }
- dot = sqrt(dot);
-
- for(iDim = 0; iDim < nDim; iDim++)
- vect2[iDim] /= dot;
-
- dot = 0;
- for(iDim = 0; iDim < nDim; iDim++)
- dot += vect1[iDim] * vect2[iDim];
-
- for(iDim = 0; iDim < nDim; iDim++)
- vect1[iDim] = T1[iDim] - (T2[iDim] + dot * vect2[iDim]);
-
- dot = 0;
- for(iDim = 0; iDim < nDim; iDim++)
- dot += vect1[iDim] * r[iDim];
-
- if (dot >= 0)
- check++;
-
- /* --- Check third edge --- */
-
- dot = 0;
- for(iDim = 0; iDim < nDim; iDim++){
- vect1[iDim] = T2[iDim] - T3[iDim];
- vect2[iDim] = T1[iDim] - T3[iDim];
-
- r[iDim] = Point[iDim] - T3[iDim];
-
- dot += vect2[iDim] * vect2[iDim];
- }
- dot = sqrt(dot);
-
- for(iDim = 0; iDim < nDim; iDim++)
- vect2[iDim] /= dot;
-
- dot = 0;
- for(iDim = 0; iDim < nDim; iDim++)
- dot += vect1[iDim] * vect2[iDim];
-
- for(iDim = 0; iDim < nDim; iDim++)
- vect1[iDim] = T2[iDim] - (T3[iDim] + dot * vect2[iDim]);
-
- dot = 0;
- for(iDim = 0; iDim < nDim; iDim++)
- dot += vect1[iDim] * r[iDim];
-
- if (dot >= 0)
- check++;
-
- return (check == 3);
-}
-
-/*--- Radial Basis Function Interpolator ---*/
-CRadialBasisFunction::CRadialBasisFunction(void): CInterpolator() { }
-
-CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) {
-
- /*--- Initialize transfer coefficients between the zones ---*/
- Set_TransferCoeff(config);
-
-}
-
-CRadialBasisFunction::~CRadialBasisFunction() {}
-
-void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) {
-
- int iProcessor, nProcessor = size;
- int nPolynomial = 0;
- int mark_donor, mark_target, target_check, donor_check;
- int *skip_row = NULL, *calc_polynomial_check;
-
- unsigned short iDim, nDim, iMarkerInt, nMarkerInt;
-
- unsigned long iVertexDonor, jVertexDonor, iVertexTarget, iCount, jCount;
- unsigned long nVertexDonor, nVertexTarget, nVertexDonorInDomain;
- unsigned long nGlobalVertexDonor, iGlobalVertexDonor_end, nLocalM;
- unsigned long point_donor, point_target;
- unsigned long *nLocalM_arr;
-
- su2double val_i, val_j;
- su2double interface_coord_tol=1e6*numeric_limits::epsilon();
- su2double *Coord_i, *Coord_j;
- su2double *local_M;
- su2double *P = NULL;
- su2double *C_inv_trunc = NULL, *C_tmp = NULL;
- su2double *target_vec, *coeff_vec;
-
- CSymmetricMatrix *global_M = NULL, *Mp = NULL;
-
-#ifdef HAVE_MPI
- unsigned long iLocalM;
- su2double *global_M_val_arr = NULL, *Buffer_recv_local_M;
- int *Buffer_Recv_mark = new int[nProcessor], iRank;
-#endif
-
- /*--- Initialize variables --- */
-
- nMarkerInt = (int) ( config[donorZone]->GetMarker_n_ZoneInterface() / 2 );
-
- nDim = donor_geometry->GetnDim();
-
-
- Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor];
-
-
- /*--- Cycle over nMarkersInt interface to determine communication pattern ---*/
-
- for (iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) {
-
- /*--- On the donor side: find the tag of the boundary sharing the interface ---*/
- mark_donor = Find_InterfaceMarker(config[donorZone], iMarkerInt);
-
- /*--- On the target side: find the tag of the boundary sharing the interface ---*/
- mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt);
-
-#ifdef HAVE_MPI
-
- donor_check = -1;
- target_check = -1;
-
- /*--- We gather a vector in MASTER_NODE to determines whether the boundary is not on the processor because of the partition or because the zone does not include it ---*/
-
- SU2_MPI::Gather(&mark_donor , 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD);
-
- if (rank == MASTER_NODE)
- for (iRank = 0; iRank < nProcessor; iRank++)
- if( Buffer_Recv_mark[iRank] != -1 ) {
- donor_check = Buffer_Recv_mark[iRank];
- break;
- }
-
- SU2_MPI::Bcast(&donor_check , 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD);
-
-
- SU2_MPI::Gather(&mark_target, 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD);
-
- if (rank == MASTER_NODE)
- for (iRank = 0; iRank < nProcessor; iRank++)
- if( Buffer_Recv_mark[iRank] != -1 ) {
- target_check = Buffer_Recv_mark[iRank];
- break;
- }
-
- SU2_MPI::Bcast(&target_check, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD);
-
-#else
- donor_check = mark_donor;
- target_check = mark_target;
-#endif
-
- /*--- Checks if the zone contains the interface, if not continue to the next step ---*/
- if(target_check == -1 || donor_check == -1)
- continue;
-
- if(mark_donor != -1)
- nVertexDonor = donor_geometry->GetnVertex( mark_donor );
- else
- nVertexDonor = 0;
-
- if(mark_target != -1)
- nVertexTarget = target_geometry->GetnVertex( mark_target );
- else
- nVertexTarget = 0;
-
- Buffer_Send_nVertex_Donor = new unsigned long [ 1 ];
-
- /*--- Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor ---*/
- Determine_ArraySize(false, mark_donor, mark_target, nVertexDonor, nDim);
-
- /*--- Collect information about number of donor vertices in domain.
- Calculate total number of donor vertices across all ranks and
- number of vertices on boundary prior to current rank. ---*/
- nVertexDonorInDomain = Buffer_Send_nVertex_Donor[0];
- iGlobalVertexDonor_end = nGlobalVertexDonor = 0;
- for (iProcessor = 0; iProcessor < nProcessor; iProcessor++)
- {
- nGlobalVertexDonor += Buffer_Receive_nVertex_Donor[iProcessor];
- if (iProcessor<=rank) iGlobalVertexDonor_end += Buffer_Receive_nVertex_Donor[iProcessor];
- }
-
- /*-- Collect coordinates, global points, and normal vectors ---*/
- Buffer_Send_Coord = new su2double [ MaxLocalVertex_Donor * nDim ];
- Buffer_Send_GlobalPoint = new long [ MaxLocalVertex_Donor ];
- Buffer_Receive_Coord = new su2double [ nProcessor * MaxLocalVertex_Donor * nDim ];
- Buffer_Receive_GlobalPoint = new long [ nProcessor * MaxLocalVertex_Donor ];
-
- Collect_VertexInfo( false, mark_donor, mark_target, nVertexDonor, nDim);
-
- /*--- Send information about size of local_M array ---*/
- nLocalM = nVertexDonorInDomain*(nVertexDonorInDomain+1)/2 \
- + nVertexDonorInDomain*(nGlobalVertexDonor-iGlobalVertexDonor_end);
-
- nLocalM_arr = new unsigned long [nProcessor];
-#ifdef HAVE_MPI
- SU2_MPI::Allgather(&nLocalM, 1, MPI_UNSIGNED_LONG, nLocalM_arr, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
-#else
- nLocalM_arr[MASTER_NODE] = nLocalM;
-#endif
-
- /*--- Initialize local M array and calculate values ---*/
- local_M = new su2double [nLocalM];
- Coord_i = new su2double [nDim];
- Coord_j = new su2double [nDim];
- iCount=0;
- for (iVertexDonor=0; iVertexDonorGetKindRadialBasisFunction(),
- config[donorZone]->GetRadialBasisFunctionParameter(),
- PointsDistance(Coord_i, Coord_j));
- }
-
- for (iProcessor=rank+1; iProcessorGetKindRadialBasisFunction(),
- config[donorZone]->GetRadialBasisFunctionParameter(),
- PointsDistance(Coord_i, Coord_j));
- }
- }
- }
-
-#ifdef HAVE_MPI
- if (rank != MASTER_NODE) {
- SU2_MPI::Send(local_M, nLocalM, MPI_DOUBLE, MASTER_NODE, 0, MPI_COMM_WORLD);
- }
-
- /*--- Assemble global_M ---*/
- if (rank == MASTER_NODE) {
- global_M_val_arr = new su2double [nGlobalVertexDonor*(nGlobalVertexDonor+1)/2];
-
- /*--- Copy master node local_M to global_M ---*/
- iCount = 0;
- for (iLocalM=0; iLocalM SINGLE_NODE) {
- for (iProcessor=1; iProcessorInitialize(nGlobalVertexDonor, global_M_val_arr);
- }
-
-#else
- global_M = new CSymmetricMatrix;
- global_M->Initialize((int)nVertexDonorInDomain, local_M);
-#endif
-
- /*--- Invert M matrix ---*/
- if (rank == MASTER_NODE) {
- switch (config[donorZone]->GetKindRadialBasisFunction())
- {
- /*--- Basis functions that make M positive definite ---*/
- case WENDLAND_C2:
- case INV_MULTI_QUADRIC:
- case GAUSSIAN:
- global_M->Invert(true);
- break;
-
- case THIN_PLATE_SPLINE:
- case MULTI_QUADRIC:
- global_M->Invert(false);
- break;
- }
- }
-
- calc_polynomial_check = new int [nDim];
-
- /*--- Calculate C_inv_trunc ---*/
- if (rank == MASTER_NODE) {
-
- if ( config[donorZone]->GetRadialBasisFunctionPolynomialOption() ) {
-
- /*--- Fill P matrix and get minimum and maximum values ---*/
- P = new su2double [nGlobalVertexDonor*(nDim+1)];
- iCount = 0;
- for (iProcessor=MASTER_NODE; iProcessorInitialize(nPolynomial+1);
- for (int m=0; mRead((int)iVertexDonor, (int)jVertexDonor)*P[jVertexDonor*(nPolynomial+1)+n];
- }
- val_i += val_j*P[iVertexDonor*(nPolynomial+1)+m];
- }
- Mp->Write(m, n, val_i);
- }
- }
- Mp->Invert(false);
-
- /*--- Calculate M_p*P*M_inv ---*/
- C_inv_trunc = new su2double [(nGlobalVertexDonor+nPolynomial+1)*nGlobalVertexDonor];
- for (int m=0; mRead((int)jVertexDonor, (int)iVertexDonor);
- }
- val_i += val_j*Mp->Read(m, n);
- }
- /*--- Save in row major order ---*/
- C_inv_trunc[m*nGlobalVertexDonor+iVertexDonor] = val_i;
- }
- }
-
- /*--- Calculate (I - P'*M_p*P*M_inv) ---*/
- C_tmp = new su2double [nGlobalVertexDonor*nGlobalVertexDonor];
- for (iVertexDonor=0; iVertexDonorMatMatMult(true, C_tmp, (int)nGlobalVertexDonor);
-
- /*--- Write to C_inv_trunc matrix ---*/
- for (iVertexDonor=0; iVertexDonorRead((int)iVertexDonor, (int)jVertexDonor);
-
- } // endif GetRadialBasisFunctionPolynomialOption
- } // endif (rank == MASTER_NODE)
-
-#ifdef HAVE_MPI
- SU2_MPI::Bcast(&nPolynomial, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD);
- SU2_MPI::Bcast(calc_polynomial_check, nDim, MPI_INT, MASTER_NODE, MPI_COMM_WORLD);
-
- if (rank != MASTER_NODE) {
- C_inv_trunc = new su2double [(nGlobalVertexDonor+nPolynomial+1)*nGlobalVertexDonor];
- }
-
- SU2_MPI::Bcast(C_inv_trunc, (nGlobalVertexDonor+nPolynomial+1)*nGlobalVertexDonor, MPI_DOUBLE, MASTER_NODE, MPI_COMM_WORLD);
-#endif
-
- /*--- Calculate H matrix ---*/
- if (config[donorZone]->GetRadialBasisFunctionPolynomialOption())
- target_vec = new su2double [nGlobalVertexDonor+nPolynomial+1];
- else
- target_vec = new su2double [nGlobalVertexDonor];
-
- coeff_vec = new su2double [nGlobalVertexDonor];
-
- for (iVertexTarget = 0; iVertexTarget < nVertexTarget; iVertexTarget++) {
-
- point_target = target_geometry->vertex[mark_target][iVertexTarget]->GetNode();
-
- if ( target_geometry->node[point_target]->GetDomain() ) {
- iCount = 0;
- if (config[donorZone]->GetRadialBasisFunctionPolynomialOption()) {
- target_vec[iCount] = 1;
- iCount++;
- }
-
- for (iDim=0; iDimnode[point_target]->GetCoord(iDim);
- if (config[donorZone]->GetRadialBasisFunctionPolynomialOption()) {
- if (calc_polynomial_check[iDim] == 1) {
- target_vec[iCount] = Coord_i[iDim];
- iCount++;
- }
- }
- }
-
- for (iProcessor=0; iProcessorGetKindRadialBasisFunction(),
- config[donorZone]->GetRadialBasisFunctionParameter(),
- PointsDistance(Coord_i, Coord_j));
- }
- }
-
- for (iVertexDonor=0; iVertexDonorvertex[mark_target][iVertexTarget]->SetnDonorPoints(iCount);
- target_geometry->vertex[mark_target][iVertexTarget]->Allocate_DonorInfo();
-
- iCount = 0;
- jCount = 0;
- for (iProcessor=0; iProcessorvertex[mark_target][iVertexTarget]->SetInterpDonorPoint(jCount, point_donor);
- target_geometry->vertex[mark_target][iVertexTarget]->SetInterpDonorProcessor(jCount, iProcessor);
- target_geometry->vertex[mark_target][iVertexTarget]->SetDonorCoeff(jCount, coeff_vec[iCount]);
- jCount++;
- }
- iCount++;
- }
- }
- } // endif
- } // endfor
-
- /*--- Memory management ---*/
- delete [] nLocalM_arr;
- delete [] local_M;
- delete [] Coord_i;
- delete [] Coord_j;
- delete [] calc_polynomial_check;
- delete [] C_inv_trunc;
- delete [] target_vec;
- delete [] coeff_vec;
-
- if ( rank == MASTER_NODE ) {
- delete global_M;
-
- if ( config[donorZone]->GetRadialBasisFunctionPolynomialOption() ) {
- delete [] skip_row;
- delete [] P;
- delete Mp;
- delete [] C_tmp;
- }
- }
-
- delete[] Buffer_Send_Coord;
- delete[] Buffer_Send_GlobalPoint;
-
- delete[] Buffer_Receive_Coord;
- delete[] Buffer_Receive_GlobalPoint;
-
- delete[] Buffer_Send_nVertex_Donor;
-
-#ifdef HAVE_MPI
- if (rank == MASTER_NODE)
- delete [] global_M_val_arr;
-#endif
- } // end loop over markers
-
- delete[] Buffer_Receive_nVertex_Donor;
-
-#ifdef HAVE_MPI
- if (rank == MASTER_NODE)
- delete [] Buffer_Recv_mark;
-#endif
-}
-
-void CRadialBasisFunction::Check_PolynomialTerms(int m, unsigned long n, const int *skip_row, su2double max_diff_tol_in, int *keep_row, int &n_polynomial, su2double *P)
-{
- /*--- This routine keeps the AD information in P but the calculations are done in passivedouble as their purpose
- is to decide which (if any) row of P to remove, and that process is not differentiable anyway. ---*/
-
- int *write_row = NULL;
- unsigned long iCount, jCount, n_rows;
- passivedouble sum, max_diff, max_coeff, *coeff = NULL, max_diff_tol = SU2_TYPE::GetValue(max_diff_tol_in);
- CSymmetricMatrix *PPT;
- su2double *P_tmp = NULL;
-
- n_rows = 0;
- for (int i=0; iInitialize((int)n_rows);
-
- iCount = 0;
- for (int i = 0; i < m; i ++) {
- if (skip_row[i] == 0) {
-
- jCount = 0;
- for (int j = 0; j < m; j ++){
- if (skip_row[j] == 0) {
-
- sum = 0.0;
- for (unsigned long k = 0; k < n; k ++)
- {
- sum += SU2_TYPE::GetValue(P[k*m+i]*P[k*m+j]);
- }
- PPT->Write((int)iCount, (int)jCount, sum);
-
- jCount++;
- }
- }
-
- iCount++;
- }
- }
-
- PPT->Invert(true);
-
- /*--- RHS for the least squares fit (vector of ones times P) ---*/
- coeff = new passivedouble [n_rows];
- iCount = 0;
- for (int i = 0; i < m; i ++) {
- if (skip_row[i] == 0) {
- coeff[iCount] = 0;
- for (unsigned long j = 0; j < n; j += 1)
- {
- coeff[iCount] += SU2_TYPE::GetValue(P[j*m+i]);
- }
- iCount++;
- }
- }
-
- /*--- Multiply the RHS by the inverse thus obtaining the coefficients ---*/
- PPT->MatVecMult(coeff);
-
- /*--- Determine the maximum deviation of the points from the fitted plane ---*/
- max_diff = 0;
- for (unsigned long i = 0; i < n; i ++)
- {
- sum = 0;
- iCount = 0;
- for (int j = 0; j < m; j ++)
- {
- if (skip_row[j] == 0) {
- sum += coeff[iCount]*SU2_TYPE::GetValue(P[i*m+j]);
- iCount++;
- }
- }
- /*--- 1.0 is the arbitrary constant we are assuming when fitting the plane ---*/
- max_diff = max(abs(1.0-sum), max_diff);
- }
-
- for (unsigned long i=0; i max_coeff) iCount = i;
- }
-
- for (unsigned long i=0; i::min()) rbf = 0.0;
- else rbf *= rbf*log(rbf);
- break;
-
- case MULTI_QUADRIC:
- case INV_MULTI_QUADRIC:
- rbf = sqrt(1.0+rbf*rbf);
- if(type == INV_MULTI_QUADRIC) rbf = 1.0/rbf;
- break;
- }
-
- return rbf;
-}
-
-/*--- Symmetric matrix class definitions ---*/
-CSymmetricMatrix::CSymmetricMatrix()
-{
- initialized = false;
- inversed = false;
- decomposed = none;
-
- val_vec = NULL;
- decompose_vec = NULL;
- inv_val_vec = NULL;
- perm_vec = NULL;
-}
-
-CSymmetricMatrix::~CSymmetricMatrix()
-{
- if(val_vec) {delete [] val_vec;}
- if(decompose_vec) {delete [] decompose_vec;}
- if(inv_val_vec) {delete [] inv_val_vec;}
- if(perm_vec) {delete [] perm_vec;}
-}
-
-void CSymmetricMatrix::Initialize(int N)
-{
- int i;
-
- sz = N;
- num_val = sz*(sz+1)/2;
- val_vec = new passivedouble [num_val];
- for (i=0; i abs(pivot) ) {
- pivot = decompose_vec[CalcIdxFull(i, j)];
- pivot_idx = i;
- interchange_row = true;
- }
- }
-
- if ( interchange_row ) {
- for ( k=0; k=sz || j<0 || j>=sz) {
- throw out_of_range("Index to access matrix out of bounds.");
- }
-}
-
-void CSymmetricMatrix::Write(int i, int j, const su2double& val)
-{
- CheckBounds(i,j);
- val_vec[CalcIdx(i, j)] = SU2_TYPE::GetValue(val);
-}
-
-passivedouble CSymmetricMatrix::Read(int i, int j)
-{
- CheckBounds(i,j);
- return val_vec[CalcIdx(i, j)];
-}
-
-passivedouble CSymmetricMatrix::ReadL(int i, int j)
-{
- passivedouble *p = NULL;
-
- CheckBounds(i,j);
-
- if (decompose_vec) { p = decompose_vec; }
- else { p = val_vec; }
-
- switch (decomposed) {
- case cholesky:
- if (i>=j) return p[CalcIdx(i, j)];
- else return 0.0;
-
- case lu:
- if (i>j) return p[CalcIdxFull(i, j)];
- else return passivedouble(i==j);
-
- default:
- throw invalid_argument("Matrix not decomposed yet or results have been deleted.");
- }
-}
-
-passivedouble CSymmetricMatrix::ReadU(int i, int j)
-{
- passivedouble *p = NULL;
-
- CheckBounds(i,j);
-
- if (decompose_vec){ p = decompose_vec; }
- else {p = val_vec;}
-
- switch (decomposed) {
- case cholesky:
- return 0.0;
-
- case lu:
- if (j>=i) return p[CalcIdxFull(j, i)];
- else return 0.0;
-
- default:
- throw invalid_argument("Matrix not decomposed yet or results have been deleted.");
- }
-}
-
-double CSymmetricMatrix::ReadInv(int i, int j)
-{
- passivedouble *p = NULL;
-
- CheckBounds(i,j);
-
- if (inversed) {
- if (inv_val_vec) { p = inv_val_vec; }
- else { p = val_vec; }
-
- return p[CalcIdx(i, j)];
- }
- else {
- throw invalid_argument("Matrix inverse not calculated yet.");
- }
-}
diff --git a/Common/src/linear_algebra/CSysMatrix.cpp b/Common/src/linear_algebra/CSysMatrix.cpp
index 577caff65fc7..60a42b635428 100644
--- a/Common/src/linear_algebra/CSysMatrix.cpp
+++ b/Common/src/linear_algebra/CSysMatrix.cpp
@@ -89,8 +89,9 @@ CSysMatrix::~CSysMatrix(void) {
template
void CSysMatrix::Initialize(unsigned long npoint, unsigned long npointdomain,
- unsigned short nvar, unsigned short neqn,
- bool EdgeConnect, CGeometry *geometry, CConfig *config) {
+ unsigned short nvar, unsigned short neqn,
+ bool EdgeConnect, CGeometry *geometry,
+ CConfig *config, bool needTranspPtr) {
assert(omp_get_thread_num()==0 && "Only the master thread is allowed to initialize the matrix.");
@@ -126,10 +127,13 @@ void CSysMatrix::Initialize(unsigned long npoint, unsigned long npoi
const auto& csr = geometry->GetSparsePattern(type,0);
+ nnz = csr.getNumNonZeros();
row_ptr = csr.outerPtr();
col_ind = csr.innerIdx();
dia_ptr = csr.diagPtr();
- nnz = csr.getNumNonZeros();
+
+ if (needTranspPtr)
+ col_ptr = geometry->GetTransposeSparsePatternMap(type).data();
if (type == ConnectivityType::FiniteVolume)
edge_ptr.ptr = geometry->GetEdgeToSparsePatternMap().data();
@@ -189,24 +193,6 @@ void CSysMatrix::Initialize(unsigned long npoint, unsigned long npoi
omp_partitions[part] = part * pts_per_part;
omp_partitions[omp_num_parts] = nPointDomain;
- /*--- For coarse grid levels setup a structure that allows doing a column sum efficiently,
- * essentially the transpose of the col_ind, this allows populating the matrix by setting
- * the off-diagonal entries and then setting the diagonal ones as the sum of column
- * (excluding the diagonal itself). We use the fact that the pattern is symmetric. ---*/
-
- if ((geometry->GetMGLevel() != MESH_0) && (omp_get_max_threads() > 1)) {
-
- col_ptr.resize(nnz, nullptr);
-
- SU2_OMP_PARALLEL_(for schedule(static,omp_heavy_size))
- for (auto iPoint = 0ul; iPoint < nPoint; ++iPoint) {
- for (auto k = row_ptr[iPoint]; k < row_ptr[iPoint+1]; ++k) {
- auto jPoint = col_ind[k];
- col_ptr[k] = GetBlock(jPoint, iPoint);
- }
- }
- }
-
/*--- Generate MKL Kernels ---*/
#ifdef USE_MKL
@@ -1333,8 +1319,10 @@ void CSysMatrix::SetDiagonalAsColumnSum() {
auto block_ii = &matrix[dia_ptr[iPoint]*nVar*nEqn];
+ for (auto k = 0ul; k < nVar*nEqn; ++k) block_ii[k] = 0.0;
+
for (auto k = row_ptr[iPoint]; k < row_ptr[iPoint+1]; ++k) {
- auto block_ji = col_ptr[k];
+ auto block_ji = &matrix[col_ptr[k]*nVar*nEqn];
if (block_ji != block_ii) MatrixSubtraction(block_ii, block_ji, block_ii);
}
}
@@ -1395,7 +1383,7 @@ void CSysMatrix::ComputePastixPreconditioner(const CSysVector
void CSysMatrix::BuildPastixPreconditioner(CGeometry *geometry, CConfig *config,
unsigned short kind_fact, bool transposed) {
diff --git a/Common/src/linear_algebra/CSysSolve.cpp b/Common/src/linear_algebra/CSysSolve.cpp
index 9bafc45d8620..6f041e89f8bb 100644
--- a/Common/src/linear_algebra/CSysSolve.cpp
+++ b/Common/src/linear_algebra/CSysSolve.cpp
@@ -206,6 +206,7 @@ unsigned long CSysSolve::CG_LinSolver(const CSysVector &
* do this since the working vectors are shared. ---*/
if (!cg_ready) {
+ SU2_OMP_BARRIER
SU2_OMP_MASTER
{
auto nVar = b.GetNVar();
@@ -348,6 +349,7 @@ unsigned long CSysSolve::FGMRES_LinSolver(const CSysVector::BCGSTAB_LinSolver(const CSysVector::Smoother_LinSolver(const CSysVector::Solve(CSysMatrix & Jacobian, co
/*--- Enforce a hard limit on total number of iterations ---*/
unsigned long IterLimit = min(RestartIter, MaxIter-IterLinSol);
IterLinSol += FGMRES_LinSolver(*LinSysRes_ptr, *LinSysSol_ptr, mat_vec, *precond, SolverTol, IterLimit, residual, ScreenOutput, config);
- if ( residual < SolverTol*norm0 ) break;
+ if ( residual <= SolverTol*norm0 ) break;
}
break;
case SMOOTHER:
diff --git a/Common/src/meson.build b/Common/src/meson.build
index 37eca9d0d903..ca9c048e70d4 100644
--- a/Common/src/meson.build
+++ b/Common/src/meson.build
@@ -14,7 +14,6 @@ common_src =files(['geometry_structure_fem_part.cpp',
'fem_gauss_jacobi_quadrature.cpp',
'wall_model.cpp',
'adt_structure.cpp',
- 'interpolation_structure.cpp',
'mpi_structure.cpp',
'fem_cgns_elements.cpp',
'CMultiGridQueue.cpp'])
@@ -26,6 +25,7 @@ subdir('geometry/elements')
subdir('geometry/dual_grid')
subdir('geometry/primal_grid')
subdir('geometry/meshreader')
+subdir('interface_interpolation')
if get_option('enable-normal')
common = static_library('SU2Common',
diff --git a/Common/src/toolboxes/CSymmetricMatrix.cpp b/Common/src/toolboxes/CSymmetricMatrix.cpp
new file mode 100644
index 000000000000..5845daac5496
--- /dev/null
+++ b/Common/src/toolboxes/CSymmetricMatrix.cpp
@@ -0,0 +1,293 @@
+/*!
+ * \file CSymmetricMatrix.cpp
+ * \brief Implementation of dense symmetric matrix helper class (see hpp).
+ * \author Joel Ho, P. Gomes
+ * \version 7.0.2 "Blackbird"
+ *
+ * SU2 Project Website: https://su2code.github.io
+ *
+ * The SU2 Project is maintained by the SU2 Foundation
+ * (http://su2foundation.org)
+ *
+ * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md)
+ *
+ * SU2 is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * SU2 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with SU2. If not, see .
+ */
+
+#include "../../include/toolboxes/CSymmetricMatrix.hpp"
+#include "../../include/mpi_structure.hpp"
+
+#if defined(HAVE_MKL)
+#include "mkl.h"
+#ifndef HAVE_LAPACK
+#define HAVE_LAPACK
+#endif
+#elif defined(HAVE_LAPACK)
+/*--- Lapack / Blas routines used in CSymmetricMatrix. ---*/
+extern "C" void dsytrf_(const char*, const int*, passivedouble*, const int*, int*, passivedouble*, const int*, int*);
+extern "C" void dsytri_(const char*, const int*, passivedouble*, const int*, const int*, passivedouble*, int*);
+extern "C" void dpotrf_(const char*, const int*, passivedouble*, const int*, int*);
+extern "C" void dpotri_(const char*, const int*, passivedouble*, const int*, int*);
+extern "C" void dsymm_(const char*, const char*, const int*, const int*, const passivedouble*, const passivedouble*,
+ const int*, const passivedouble*, const int*, const passivedouble*, passivedouble*, const int*);
+#define DSYMM dsymm_
+#endif
+
+void CSymmetricMatrix::Initialize(int N) { mat.resize(N,N); }
+
+void CSymmetricMatrix::CholeskyDecompose()
+{
+#ifndef HAVE_LAPACK
+ int j;
+ for (j = 0; j < Size(); ++j) {
+ passivedouble sum = 0.0;
+ for (int k = 0; k < j; ++k) sum -= pow(Get(j,k), 2);
+ sum += Get(j,j);
+ if (sum < 0.0) break; // not SPD
+ Set(j, j, sqrt(sum));
+
+ for (int i = j+1; i < Size(); ++i) {
+ passivedouble sum = 0.0;
+ for (int k = 0; k < j; ++k) sum -= Get(i,k) * Get(j,k);
+ sum += Get(i,j);
+ Set(i, j, sum / Get(j,j));
+ }
+ }
+ if (j!=Size()) SU2_MPI::Error("LLT factorization failed.", CURRENT_FUNCTION);
+#endif
+}
+
+void CSymmetricMatrix::LUDecompose(su2passivematrix& decomp, vector& perm) const
+{
+#ifndef HAVE_LAPACK
+ const int sz = Size();
+
+ /*--- Copy matrix values to LU matrix, init permutation vec. ---*/
+ decomp.resize(sz,sz);
+ perm.resize(sz);
+
+ for (int i = 0; i < sz; ++i) {
+ perm[i] = i;
+ for (int j = i; j < sz; ++j) decomp(j,i) = decomp(i,j) = Get(i,j);
+ }
+
+ /*--- Decompose LU matrix. ---*/
+ for (int j = 0; j < sz-1; ++j) {
+ /*--- Search for maximum pivot and interchange rows. ---*/
+ passivedouble pivot = decomp(j,j);
+ int pivot_idx = j;
+ for (int i = j+1; i < sz; ++i)
+ if (abs(decomp(i,j)) > abs(pivot)) {
+ pivot = decomp(i,j);
+ pivot_idx = i;
+ }
+
+ if (pivot_idx != j) {
+ swap(perm[j], perm[pivot_idx]);
+ for (int k = 0; k < sz; ++k)
+ swap(decomp(j,k), decomp(pivot_idx,k));
+ }
+
+ /*--- Perform elimination. ---*/
+ for (int k = j+1; k < sz; ++k) decomp(k,j) /= pivot;
+
+ for (int k = j+1; k < sz; ++k)
+ for (int i = j+1; i < sz; ++i)
+ decomp(i,k) -= decomp(j,k)*decomp(i,j);
+ }
+#endif
+}
+
+void CSymmetricMatrix::CalcInv(bool is_spd)
+{
+#ifndef HAVE_LAPACK
+ const int sz = Size();
+
+ /*--- Compute inverse from decomposed matrices. ---*/
+ if (is_spd)
+ {
+ CholeskyDecompose();
+
+ /*--- Initialize inverse matrix. ---*/
+ CSymmetricMatrix inv(sz);
+
+ /*--- Compute L inverse. ---*/
+ /*--- Solve smaller and smaller systems. ---*/
+ for (int j = 0; j < sz; ++j) {
+ /*--- Forward substitution. ---*/
+ inv(j,j) = 1.0 / Get(j,j);
+
+ for (int i = j+1; i < sz; ++i) {
+ passivedouble sum = 0.0;
+ for (int k = j; k < i; ++k) sum -= Get(i,k) * inv(k,j);
+ inv(i,j) = sum / Get(i,i);
+ }
+ } // L inverse in inv
+
+ /*--- Multiply inversed matrices overwrite mat. ---*/
+ for (int j = 0; j < sz; ++j)
+ for (int i = j; i < sz; ++i) {
+ passivedouble sum = 0.0;
+ for (int k = i; k < sz; ++k) sum += inv(k,i) * inv(k,j);
+ Set(i, j, sum);
+ }
+ }
+ else
+ {
+ su2passivematrix decomp;
+ vector perm;
+ LUDecompose(decomp, perm);
+
+ /*--- Alias mat. ---*/
+ auto& inv = mat;
+
+ /*--- Invert L and U matrices in place. ---*/
+ for (int j = 0; j < sz; ++j) {
+ inv(j,j) = 1.0 / decomp(j,j);
+
+ for (int i = j+1; i < sz; ++i) {
+ inv(i,j) = -decomp(i,j);
+ inv(j,i) = -decomp(j,i) * inv(j,j);
+
+ for (int k = j+1; k < i; ++k) {
+ inv(i,j) -= decomp(i,k) * inv(k,j);
+ inv(j,i) -= decomp(k,i) * inv(j,k);
+ }
+ if (j+1 <= i) inv(j,i) /= decomp(i,i);
+ }
+ }
+ // inverses in mat
+
+ /*--- Multiply U_inv by L_inv, overwrite decomp_vec. ---*/
+ for (int i = 0; i < sz; ++i)
+ for (int j = 0; j < sz; ++j) {
+ decomp(i,j) = 0.0;
+ for (int k = max(i,j); k < sz; ++k)
+ decomp(i,j) += inv(i,k) * ((k==j)? 1.0 : inv(k,j));
+ }
+
+ /*--- Permute multiplied matrix to recover A_inv, overwrite mat. ---*/
+ for (int j = 0; j < sz; ++j) {
+ int k = perm[j];
+ for (int i = k; i < sz; ++i) Set(i, k, decomp(i,j));
+ }
+ }
+#endif
+}
+
+void CSymmetricMatrix::CalcInv_sytri()
+{
+#ifdef HAVE_LAPACK
+ const char uplo = 'L';
+ const int sz = Size();
+ int info;
+ vector ipiv(sz);
+
+ /*--- Query the optimum work size. ---*/
+ int query = -1; passivedouble tmp;
+ dsytrf_(&uplo, &sz, mat.data(), &sz, ipiv.data(), &tmp, &query, &info);
+ query = static_cast(tmp);
+ vector work(query);
+
+ /*--- Factorize and invert. ---*/
+ dsytrf_(&uplo, &sz, mat.data(), &sz, ipiv.data(), work.data(), &query, &info);
+ if (info!=0) SU2_MPI::Error("LDLT factorization failed.", CURRENT_FUNCTION);
+ dsytri_(&uplo, &sz, mat.data(), &sz, ipiv.data(), work.data(), &info);
+ if (info!=0) SU2_MPI::Error("Inversion with LDLT factorization failed.", CURRENT_FUNCTION);
+#endif
+}
+
+void CSymmetricMatrix::CalcInv_potri()
+{
+#ifdef HAVE_LAPACK
+ const char uplo = 'L';
+ const int sz = Size();
+ int info;
+
+ dpotrf_(&uplo, &sz, mat.data(), &sz, &info);
+ if (info!=0) SU2_MPI::Error("LLT factorization failed.", CURRENT_FUNCTION);
+ dpotri_(&uplo, &sz, mat.data(), &sz, &info);
+ if (info!=0) SU2_MPI::Error("Inversion with LLT factorization failed.", CURRENT_FUNCTION);
+#endif
+}
+
+void CSymmetricMatrix::Invert(const bool is_spd)
+{
+#ifdef HAVE_LAPACK
+ if(is_spd) CalcInv_potri();
+ else CalcInv_sytri();
+#else
+ CalcInv(is_spd);
+#endif
+}
+
+void CSymmetricMatrix::MatMatMult(const char side,
+ const su2passivematrix& mat_in,
+ su2passivematrix& mat_out) const
+{
+ /*--- Left side: mat_out = this * mat_in. ---*/
+ if (side == 'L' || side == 'l') {
+ const int M = Size(), N = mat_in.cols();
+ assert(M == mat_in.rows());
+
+ mat_out.resize(M,N);
+
+#ifdef HAVE_LAPACK
+ /*--- Right and lower because matrices are in row major order. ---*/
+ const char side = 'R', uplo = 'L';
+ const passivedouble alpha = 1.0, beta = 0.0;
+ DSYMM(&side, &uplo, &N, &M, &alpha, mat.data(), &M,
+ mat_in.data(), &N, &beta, mat_out.data(), &N);
+#else // Naive product
+ for (int i = 0; i < M; ++i)
+ for (int j = 0; j < N; ++j) {
+ mat_out(i,j) = 0.0;
+ for (int k = 0; k < M; ++k)
+ mat_out(i,j) += Get(i,k) * mat_in(k,j);
+ }
+#endif
+ }
+ /*--- Right_side: mat_out = mat_in * this. ---*/
+ else {
+ const int M = mat_in.rows(), N = Size();
+ assert(N == mat_in.cols());
+
+ mat_out.resize(M,N);
+
+#ifdef HAVE_LAPACK
+ /*--- Left and lower because matrices are in row major order. ---*/
+ const char side = 'L', uplo = 'L';
+ const passivedouble alpha = 1.0, beta = 0.0;
+ DSYMM(&side, &uplo, &N, &M, &alpha, mat.data(), &N,
+ mat_in.data(), &N, &beta, mat_out.data(), &N);
+#else // Naive product
+ for (int i = 0; i < M; ++i)
+ for (int j = 0; j < N; ++j) {
+ mat_out(i,j) = 0.0;
+ for (int k = 0; k < N; ++k)
+ mat_out(i,j) += mat_in(i,k) * Get(k,j);
+ }
+#endif
+ }
+}
+
+su2passivematrix CSymmetricMatrix::StealData()
+{
+ /*--- Fill lower triangular part. ---*/
+ for (int i = 1; i < Size(); ++i)
+ for (int j = 0; j < i; ++j)
+ mat(i,j) = mat(j,i);
+
+ return move(mat);
+}
diff --git a/Common/src/toolboxes/meson.build b/Common/src/toolboxes/meson.build
index c753d5142564..123713157639 100644
--- a/Common/src/toolboxes/meson.build
+++ b/Common/src/toolboxes/meson.build
@@ -1,5 +1,6 @@
common_src += files(['CLinearPartitioner.cpp',
'printing_toolbox.cpp',
- 'C1DInterpolation.cpp'])
+ 'C1DInterpolation.cpp',
+ 'CSymmetricMatrix.cpp'])
subdir('MMS')
diff --git a/SU2_CFD/include/SU2_CFD.hpp b/SU2_CFD/include/SU2_CFD.hpp
index e16bc95833b7..5ab8379ddd60 100644
--- a/SU2_CFD/include/SU2_CFD.hpp
+++ b/SU2_CFD/include/SU2_CFD.hpp
@@ -44,7 +44,6 @@
#include "../../Common/include/geometry/CGeometry.hpp"
#include "../../Common/include/grid_movement_structure.hpp"
#include "../../Common/include/CConfig.hpp"
-#include "../../Common/include/interpolation_structure.hpp"
#include "../include/definition_structure.hpp"
#include "../include/iteration_structure.hpp"
#include "../include/interfaces/CInterface.hpp"
diff --git a/SU2_CFD/include/drivers/CDriver.hpp b/SU2_CFD/include/drivers/CDriver.hpp
index 2c5a05905a67..5889aa6cee53 100644
--- a/SU2_CFD/include/drivers/CDriver.hpp
+++ b/SU2_CFD/include/drivers/CDriver.hpp
@@ -37,11 +37,11 @@
#include "../../../Common/include/geometry/CGeometry.hpp"
#include "../../../Common/include/grid_movement_structure.hpp"
-#include "../../../Common/include/interpolation_structure.hpp"
using namespace std;
class COutputLegacy;
+class CInterpolator;
/*!
* \class CDriver
diff --git a/SU2_CFD/include/interfaces/CInterface.hpp b/SU2_CFD/include/interfaces/CInterface.hpp
index 6c3a2c8cab84..ecd5b540a4bd 100644
--- a/SU2_CFD/include/interfaces/CInterface.hpp
+++ b/SU2_CFD/include/interfaces/CInterface.hpp
@@ -7,7 +7,7 @@
*
* SU2 Project Website: https://su2code.github.io
*
- * The SU2 Project is maintained by the SU2 Foundation
+ * The SU2 Project is maintained by the SU2 Foundation
* (http://su2foundation.org)
*
* Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md)
@@ -56,20 +56,20 @@ using namespace std;
class CInterface {
protected:
- int rank, /*!< \brief MPI Rank. */
- size; /*!< \brief MPI Size. */
+ const int rank; /*!< \brief MPI Rank. */
+ const int size; /*!< \brief MPI Size. */
- su2double *Physical_Constants;
- su2double *Donor_Variable;
- su2double *Target_Variable;
- bool valAggregated;
+ su2double *Physical_Constants = nullptr;
+ su2double *Donor_Variable = nullptr;
+ su2double *Target_Variable = nullptr;
+ bool valAggregated = false;
/*--- Mixing Plane interface variable ---*/
- su2double *SpanValueCoeffTarget;
- unsigned short *SpanLevelDonor;
- unsigned short nSpanMaxAllZones;
+ su2double *SpanValueCoeffTarget = nullptr;
+ unsigned short *SpanLevelDonor = nullptr;
+ unsigned short nSpanMaxAllZones = 0;
- unsigned short nVar;
+ unsigned short nVar = 0;
public:
/*!
diff --git a/SU2_CFD/include/numerics/elasticity/CFEALinearElasticity.hpp b/SU2_CFD/include/numerics/elasticity/CFEALinearElasticity.hpp
index acda3944b9b2..dd564505bdc4 100644
--- a/SU2_CFD/include/numerics/elasticity/CFEALinearElasticity.hpp
+++ b/SU2_CFD/include/numerics/elasticity/CFEALinearElasticity.hpp
@@ -109,7 +109,7 @@ class CFEAMeshElasticity final : public CFEALinearElasticity {
* \param[in] val_nVar - Number of variables of the problem.
* \param[in] config - Definition of the particular problem.
*/
- CFEAMeshElasticity(unsigned short val_nDim, unsigned short val_nVar, unsigned long val_nElem, CConfig *config);
+ CFEAMeshElasticity(unsigned short val_nDim, unsigned short val_nVar, unsigned long val_nElem, const CConfig *config);
/*!
* \brief Destructor of the class.
diff --git a/SU2_CFD/include/solvers/CEulerSolver.hpp b/SU2_CFD/include/solvers/CEulerSolver.hpp
index df1944c6129a..8b335d7b3384 100644
--- a/SU2_CFD/include/solvers/CEulerSolver.hpp
+++ b/SU2_CFD/include/solvers/CEulerSolver.hpp
@@ -264,16 +264,18 @@ class CEulerSolver : public CSolver {
#ifdef HAVE_OMP
vector > EdgeColoring; /*!< \brief Edge colors. */
+ bool ReducerStrategy = false; /*!< \brief If the reducer strategy is in use. */
#else
array,1> EdgeColoring;
+ /*--- Never use the reducer strategy if compiling for MPI-only. ---*/
+ static constexpr bool ReducerStrategy = false;
#endif
- unsigned long ColorGroupSize; /*!< \brief Group size used for coloring, chunk size in edge loops must be a multiple of this. */
- /*--- Edge fluxes, for OpenMP parallelization on coarse grids. As it is difficult to
- * color them, we first store the fluxes and then compute the sum for each cell.
- * This strategy is thread-safe but lower performance than writting to both end
- * points of each edge, so we only use it when necessary, i.e. coarse grids and
- * with more than one thread per MPI rank. ---*/
+ /*--- Edge fluxes, for OpenMP parallelization off difficult-to-color grids.
+ * We first store the fluxes and then compute the sum for each cell.
+ * This strategy is thread-safe but lower performance than writting to both
+ * end points of each edge, so we only use it when necessary, i.e. when the
+ * coloring does not allow "enough" parallelism. ---*/
CSysVector EdgeFluxes; /*!< \brief Flux across each edge. */
@@ -300,23 +302,27 @@ class CEulerSolver : public CSolver {
void SumEdgeFluxes(CGeometry* geometry);
/*!
- * \brief Update the AoA and freestream velocity at the farfield.
+ * \brief Preprocessing actions common to the Euler and NS solvers.
* \param[in] geometry - Geometrical definition of the problem.
* \param[in] solver_container - Container vector with all the solutions.
* \param[in] config - Definition of the particular problem.
- * \param[in] iMesh - current mesh level for the multigrid.
+ * \param[in] iRKStep - Current step of the Runge-Kutta iteration.
+ * \param[in] RunTime_EqSystem - System of equations which is going to be solved.
* \param[in] Output - boolean to determine whether to print output.
*/
- void SetFarfield_AoA(CGeometry *geometry, CSolver **solver_container,
- CConfig *config, unsigned short iMesh, bool Output);
+ void CommonPreprocessing(CGeometry *geometry, CSolver **solver_container, CConfig *config, unsigned short iMesh,
+ unsigned short iRKStep, unsigned short RunTime_EqSystem, bool Output);
/*!
- * \brief Compute a pressure sensor switch.
+ * \brief Update the AoA and freestream velocity at the farfield.
* \param[in] geometry - Geometrical definition of the problem.
* \param[in] solver_container - Container vector with all the solutions.
* \param[in] config - Definition of the particular problem.
+ * \param[in] iMesh - current mesh level for the multigrid.
+ * \param[in] Output - boolean to determine whether to print output.
*/
- void SetCentered_Dissipation_Sensor(CGeometry *geometry, CConfig *config);
+ void SetFarfield_AoA(CGeometry *geometry, CSolver **solver_container,
+ CConfig *config, unsigned short iMesh, bool Output);
/*!
* \brief Compute Ducros Sensor for Roe Dissipation.
@@ -360,11 +366,13 @@ class CEulerSolver : public CSolver {
void SetMax_Eigenvalue(CGeometry *geometry, CConfig *config);
/*!
- * \brief Compute the undivided laplacian for the solution, except the energy equation.
+ * \brief Compute the undivided laplacian for the solution and the
+ * dissipation sensor for centered schemes.
* \param[in] geometry - Geometrical definition of the problem.
* \param[in] config - Definition of the particular problem.
*/
- void SetUndivided_Laplacian(CGeometry *geometry, CConfig *config);
+ void SetUndivided_Laplacian_And_Centered_Dissipation_Sensor(CGeometry *geometry,
+ CConfig *config);
/*!
* \brief A virtual member.
@@ -373,7 +381,6 @@ class CEulerSolver : public CSolver {
*/
inline virtual void SetRoe_Dissipation(CGeometry *geometry, CConfig *config) { }
-private:
/*!
* \brief Compute the velocity^2, SoundSpeed, Pressure, Enthalpy, Viscosity.
* \param[in] solver_container - Container vector with all the solutions.
@@ -381,9 +388,8 @@ class CEulerSolver : public CSolver {
* \param[in] Output - boolean to determine whether to print output.
* \return - The number of non-physical points.
*/
- unsigned long SetPrimitive_Variables(CSolver **solver_container,
- CConfig *config,
- bool Output);
+ virtual unsigned long SetPrimitive_Variables(CSolver **solver_container,
+ CConfig *config, bool Output);
protected:
@@ -438,7 +444,6 @@ class CEulerSolver : public CSolver {
return sqrt(Vel2);
}
-
/*!
* \brief Compute the density multiply by energy at the infinity.
* \return Value of the density multiply by energy at the infinity.
diff --git a/SU2_CFD/include/solvers/CFEASolver.hpp b/SU2_CFD/include/solvers/CFEASolver.hpp
index eac3ff18a3bb..eae891063a05 100644
--- a/SU2_CFD/include/solvers/CFEASolver.hpp
+++ b/SU2_CFD/include/solvers/CFEASolver.hpp
@@ -46,7 +46,7 @@ class CFEASolver : public CSolver {
su2double Total_CFEA; /*!< \brief Total FEA coefficient for all the boundaries. */
- unsigned short *iElem_iDe; /*!< \brief For DE cases, ID of the region considered for each iElem. */
+ unsigned short *iElem_iDe = nullptr; /*!< \brief For DE cases, ID of the region considered for each iElem. */
su2double a_dt[9]; /*!< \brief Integration constants. */
@@ -77,15 +77,19 @@ class CFEASolver : public CSolver {
#ifdef HAVE_OMP
vector > ElemColoring; /*!< \brief Element colors. */
+ bool LockStrategy = false; /*!< \brief Whether to use an OpenMP lock to guard updates of the Jacobian. */
+ vector UpdateLocks; /*!< \brief Locks that may be used to protect accesses to CSysMatrix/Vector in element loops. */
#else
- array,1> ElemColoring;
+ array,1> ElemColoring; /*--- Behaves like a normal integer type. ---*/
+ static constexpr bool LockStrategy = false; /*--- Lock strategy is never needed for MPI-only. ---*/
+ DummyVectorOfLocks UpdateLocks;
#endif
- unsigned long ColorGroupSize; /*!< \brief Group size used for coloring, chunk size must be a multiple of this. */
- bool element_based; /*!< \brief Bool to determine if an element-based file is used. */
- bool topol_filter_applied; /*!< \brief True if density filtering has been performed. */
+ bool element_based; /*!< \brief Bool to determine if an element-based file is used. */
+ bool topol_filter_applied; /*!< \brief True if density filtering has been performed. */
+ bool initial_calc = true; /*!< \brief Becomes false after first call to Preprocessing. */
- unsigned long nElement; /*!< \brief Number of elements. */
+ unsigned long nElement; /*!< \brief Number of elements. */
/*!
* \brief The highest level in the variable hierarchy this solver can safely use,
@@ -116,6 +120,12 @@ class CFEASolver : public CSolver {
}
}
+ /*!
+ * \brief Actions required to initialize the supporting variables for hybrid parallel execution.
+ * \param[in] geometry - Geometrical definition of the problem.
+ */
+ void HybridParallelInitialization(CGeometry* geometry);
+
/*!
* \brief Set container of element properties.
* \param[in] geometry - Geometrical definition of the problem.
@@ -170,8 +180,8 @@ class CFEASolver : public CSolver {
CSysMatrix MassMatrix; /*!< \brief Sparse structure for storing the mass matrix. */
- CElement*** element_container; /*!< \brief Vector which the define the finite element structure for each problem. */
- CProperty** element_properties; /*!< \brief Vector which stores the properties of each element */
+ CElement*** element_container = nullptr; /*!< \brief Vector which the define the finite element structure for each problem. */
+ CProperty** element_properties = nullptr; /*!< \brief Vector which stores the properties of each element */
/*!
* \brief Constructor of the class.
diff --git a/SU2_CFD/include/solvers/CMeshSolver.hpp b/SU2_CFD/include/solvers/CMeshSolver.hpp
index a39c571bd3e1..0c33951be053 100644
--- a/SU2_CFD/include/solvers/CMeshSolver.hpp
+++ b/SU2_CFD/include/solvers/CMeshSolver.hpp
@@ -48,12 +48,6 @@ class CMeshSolver final : public CFEASolver {
su2double MinDistance;
su2double MaxDistance;
- su2double E; /*!< \brief Young's modulus of elasticity. */
- su2double Nu; /*!< \brief Poisson's ratio. */
-
- su2double Mu; /*!< \brief Lame's coeficient. */
- su2double Lambda; /*!< \brief Lame's coeficient. */
-
vector element; /*!< \brief Vector which stores element information for each problem. */
/*!
diff --git a/SU2_CFD/include/solvers/CNSSolver.hpp b/SU2_CFD/include/solvers/CNSSolver.hpp
index 0566e637c363..aa0bcfe59b6d 100644
--- a/SU2_CFD/include/solvers/CNSSolver.hpp
+++ b/SU2_CFD/include/solvers/CNSSolver.hpp
@@ -68,7 +68,6 @@ class CNSSolver final : public CEulerSolver {
*/
void SetRoe_Dissipation(CGeometry *geometry, CConfig *config) override;
-private:
/*!
* \brief Compute the velocity^2, SoundSpeed, Pressure, Enthalpy, Viscosity.
* \param[in] solver_container - Container vector with all the solutions.
@@ -77,8 +76,7 @@ class CNSSolver final : public CEulerSolver {
* \return - The number of non-physical points.
*/
unsigned long SetPrimitive_Variables(CSolver **solver_container,
- CConfig *config,
- bool Output);
+ CConfig *config, bool Output) override;
protected:
diff --git a/SU2_CFD/include/solvers/CTurbSolver.hpp b/SU2_CFD/include/solvers/CTurbSolver.hpp
index cfbc0aa44bae..ce4adf446c8f 100644
--- a/SU2_CFD/include/solvers/CTurbSolver.hpp
+++ b/SU2_CFD/include/solvers/CTurbSolver.hpp
@@ -55,7 +55,7 @@ class CTurbSolver : public CSolver {
Gamma_Minus_One, /*!< \brief Fluids's Gamma - 1.0 . */
***Inlet_TurbVars = nullptr; /*!< \brief Turbulence variables at inlet profiles */
- /* Sliding meshes variables */
+ /*--- Sliding meshes variables. ---*/
su2double ****SlidingState = nullptr;
int **SlidingStateNodes = nullptr;
@@ -64,10 +64,15 @@ class CTurbSolver : public CSolver {
#ifdef HAVE_OMP
vector > EdgeColoring; /*!< \brief Edge colors. */
+ bool ReducerStrategy = false; /*!< \brief If the reducer strategy is in use. */
#else
array,1> EdgeColoring;
+ /*--- Never use the reducer strategy if compiling for MPI-only. ---*/
+ static constexpr bool ReducerStrategy = false;
#endif
- unsigned long ColorGroupSize; /*!< \brief Group size used for coloring, chunk size in edge loops must be a multiple of this. */
+
+ /*--- Edge fluxes for reducer strategy (see the notes in CEulerSolver.hpp). ---*/
+ CSysVector EdgeFluxes; /*!< \brief Flux across each edge. */
/*!
* \brief The highest level in the variable hierarchy this solver can safely use.
@@ -79,6 +84,28 @@ class CTurbSolver : public CSolver {
*/
inline CVariable* GetBaseClassPointerToNodes() final { return nodes; }
+private:
+
+ /*!
+ * \brief Compute the viscous flux for the turbulent equation at a particular edge.
+ * \param[in] iEdge - Edge for which we want to compute the flux
+ * \param[in] geometry - Geometrical definition of the problem.
+ * \param[in] solver_container - Container vector with all the solutions.
+ * \param[in] numerics - Description of the numerical method.
+ * \param[in] config - Definition of the particular problem.
+ */
+ void Viscous_Residual(unsigned long iEdge,
+ CGeometry *geometry,
+ CSolver **solver_container,
+ CNumerics *numerics,
+ CConfig *config);
+
+ /*!
+ * \brief Sum the edge fluxes for each cell to populate the residual vector, only used on coarse grids.
+ * \param[in] geometry - Geometrical definition of the problem.
+ */
+ void SumEdgeFluxes(CGeometry* geometry);
+
public:
/*!
@@ -106,29 +133,12 @@ class CTurbSolver : public CSolver {
* \param[in] config - Definition of the particular problem.
* \param[in] iMesh - Index of the mesh in multigrid computations.
*/
-
void Upwind_Residual(CGeometry *geometry,
CSolver **solver_container,
CNumerics **numerics_container,
CConfig *config,
unsigned short iMesh) override;
- /*!
- * \brief Compute the viscous residuals for the turbulent equation.
- * \param[in] geometry - Geometrical definition of the problem.
- * \param[in] solver_container - Container vector with all the solutions.
- * \param[in] numerics_container - Description of the numerical method.
- * \param[in] config - Definition of the particular problem.
- * \param[in] iMesh - Index of the mesh in multigrid computations.
- * \param[in] iRKStep - Current step of the Runge-Kutta iteration.
- */
- void Viscous_Residual(CGeometry *geometry,
- CSolver **solver_container,
- CNumerics **numerics_container,
- CConfig *config,
- unsigned short iMesh,
- unsigned short iRKStep) override;
-
/*!
* \brief Impose the Symmetry Plane boundary condition.
* \param[in] geometry - Geometrical definition of the problem.
diff --git a/SU2_CFD/include/variables/CVariable.hpp b/SU2_CFD/include/variables/CVariable.hpp
index c9da45da7762..5e28e972abc8 100644
--- a/SU2_CFD/include/variables/CVariable.hpp
+++ b/SU2_CFD/include/variables/CVariable.hpp
@@ -1076,16 +1076,6 @@ class CVariable {
*/
inline virtual su2double GetSensor(unsigned long iPoint, unsigned long iSpecies) const { return 0.0; }
- /*!
- * \brief Set the value of the undivided laplacian of the solution.
- * \param[in] iPoint - Point index.
- * \param[in] iVar - Index of the variable.
- * \param[in] val_undivided_laplacian - Value of the undivided solution for the index iVar.
- */
- inline void SetUndivided_Laplacian(unsigned long iPoint, unsigned long iVar, su2double val_undivided_laplacian) {
- Undivided_Laplacian(iPoint,iVar) = val_undivided_laplacian;
- }
-
/*!
* \brief Add the value of the undivided laplacian of the solution.
* \param[in] iPoint - Point index.
@@ -1107,13 +1097,13 @@ class CVariable {
}
/*!
- * \brief Subtract the value of the undivided laplacian of the solution.
+ * \brief Increment the value of the undivided laplacian of the solution.
* \param[in] iPoint - Point index.
* \param[in] iVar - Variable of the undivided laplacian.
* \param[in] val_und_lapl - Value of the undivided solution.
*/
- inline void SubtractUnd_Lapl(unsigned long iPoint, unsigned long iVar, su2double val_und_lapl) {
- Undivided_Laplacian(iPoint, iVar) -= val_und_lapl;
+ inline void AddUnd_Lapl(unsigned long iPoint, unsigned long iVar, su2double val_und_lapl) {
+ Undivided_Laplacian(iPoint, iVar) += val_und_lapl;
}
/*!
diff --git a/SU2_CFD/obj/Makefile.am b/SU2_CFD/obj/Makefile.am
index 039a27cf3fca..a46d07ef0b79 100644
--- a/SU2_CFD/obj/Makefile.am
+++ b/SU2_CFD/obj/Makefile.am
@@ -124,8 +124,8 @@ libSU2Core_sources = ../src/definition_structure.cpp \
../src/output/CAdjFlowCompOutput.cpp \
../src/output/CAdjFlowIncOutput.cpp \
../src/output/CMultizoneOutput.cpp \
- ../src/output/output_structure_legacy.cpp \
../src/output/COutputFactory.cpp \
+ ../src/output/output_structure_legacy.cpp \
../src/python_wrapper_structure.cpp \
../src/solvers/CAdjEulerSolver.cpp \
../src/solvers/CAdjNSSolver.cpp \
diff --git a/SU2_CFD/src/SU2_CFD.cpp b/SU2_CFD/src/SU2_CFD.cpp
index 9fac0ec50e99..229aaab1f7e1 100644
--- a/SU2_CFD/src/SU2_CFD.cpp
+++ b/SU2_CFD/src/SU2_CFD.cpp
@@ -25,7 +25,6 @@
* License along with SU2. If not, see .
*/
-
#include "../include/SU2_CFD.hpp"
/* LIBXSMM include files, if supported. */
diff --git a/SU2_CFD/src/drivers/CDriver.cpp b/SU2_CFD/src/drivers/CDriver.cpp
index 85205cffa674..aec0ab14f9d2 100644
--- a/SU2_CFD/src/drivers/CDriver.cpp
+++ b/SU2_CFD/src/drivers/CDriver.cpp
@@ -38,6 +38,12 @@
#include "../../include/output/COutputFactory.hpp"
#include "../../include/output/COutputLegacy.hpp"
+#include "../../../Common/include/interface_interpolation/CMirror.hpp"
+#include "../../../Common/include/interface_interpolation/CSlidingMesh.hpp"
+#include "../../../Common/include/interface_interpolation/CIsoparametric.hpp"
+#include "../../../Common/include/interface_interpolation/CNearestNeighbor.hpp"
+#include "../../../Common/include/interface_interpolation/CRadialBasisFunction.hpp"
+
#include "../../include/interfaces/cfd/CConservativeVarsInterface.hpp"
#include "../../include/interfaces/cfd/CMixingPlaneInterface.hpp"
#include "../../include/interfaces/cfd/CSlidingInterface.hpp"
@@ -83,10 +89,9 @@
#endif
#include
-CDriver::CDriver(char* confFile,
- unsigned short val_nZone,
- SU2_Comm MPICommunicator, bool dummy_geo):config_file_name(confFile), StartTime(0.0), StopTime(0.0), UsedTime(0.0),
- TimeIter(0), nZone(val_nZone), StopCalc(false), fsi(false), fem_solver(false), dry_run(dummy_geo) {
+CDriver::CDriver(char* confFile, unsigned short val_nZone, SU2_Comm MPICommunicator, bool dummy_geo) :
+ config_file_name(confFile), StartTime(0.0), StopTime(0.0), UsedTime(0.0),
+ TimeIter(0), nZone(val_nZone), StopCalc(false), fsi(false), fem_solver(false), dry_run(dummy_geo) {
/*--- Initialize Medipack (must also be here so it is initialized from python) ---*/
#ifdef HAVE_MPI
@@ -95,8 +100,6 @@ CDriver::CDriver(char* confFile,
#endif
#endif
- unsigned short jZone;
-
SU2_MPI::SetComm(MPICommunicator);
rank = SU2_MPI::GetRank();
@@ -144,13 +147,8 @@ CDriver::CDriver(char* confFile,
/*--- Allocate transfer and interpolation container --- */
- interface_container[iZone] = new CInterface*[nZone];
- interpolator_container[iZone] = new CInterpolator*[nZone];
-
- for (jZone = 0; jZone < nZone; jZone++){
- interface_container[iZone][jZone] = NULL;
- interpolator_container[iZone][jZone] = NULL;
- }
+ interface_container[iZone] = new CInterface*[nZone] ();
+ interpolator_container[iZone] = new CInterpolator*[nZone] ();
for (iInst = 0; iInst < nInst[iZone]; iInst++){
@@ -207,7 +205,7 @@ CDriver::CDriver(char* confFile,
/*--- Dynamic mesh processing. ---*/
DynamicMesh_Preprocessing(config_container[iZone], geometry_container[iZone][iInst], solver_container[iZone][iInst],
- iteration_container[iZone][iInst], grid_movement[iZone][iInst], surface_movement[iZone]);
+ iteration_container[iZone][iInst], grid_movement[iZone][iInst], surface_movement[iZone]);
/*--- Static mesh processing. ---*/
StaticMesh_Preprocessing(config_container[iZone], geometry_container[iZone][iInst], surface_movement[iZone]);
@@ -2526,22 +2524,11 @@ void CDriver::Interface_Preprocessing(CConfig **config, CSolver***** solver, CGe
unsigned short donorZone, targetZone;
unsigned short nVar, nVarTransfer;
- unsigned short nMarkerTarget, iMarkerTarget, nMarkerDonor, iMarkerDonor;
-
/*--- Initialize some useful booleans ---*/
bool fluid_donor, structural_donor, heat_donor;
bool fluid_target, structural_target, heat_target;
- bool discrete_adjoint = config[ZONE_0]->GetDiscrete_Adjoint();
-
- int markDonor, markTarget, Donor_check, Target_check, iMarkerInt, nMarkerInt;
-
-#ifdef HAVE_MPI
- int *Buffer_Recv_mark = NULL, iRank, nProcessor = size;
-
- if (rank == MASTER_NODE)
- Buffer_Recv_mark = new int[nProcessor];
-#endif
+ const bool discrete_adjoint = config[ZONE_0]->GetDiscrete_Adjoint();
/*--- Coupling between zones ---*/
// There's a limit here, the interface boundary must connect only 2 zones
@@ -2560,157 +2547,86 @@ void CDriver::Interface_Preprocessing(CConfig **config, CSolver***** solver, CGe
continue;
}
- nMarkerInt = (int) ( config[donorZone]->GetMarker_n_ZoneInterface() / 2 );
+ const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface() / 2;
/*--- Loops on Interface markers to find if the 2 zones are sharing the boundary and to
*--- determine donor and target marker tag ---*/
- for (iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) {
-
- markDonor = -1;
- markTarget = -1;
-
- /*--- On the donor side ---*/
- nMarkerDonor = config[donorZone]->GetnMarker_All();
-
- for (iMarkerDonor = 0; iMarkerDonor < nMarkerDonor; iMarkerDonor++) {
+ for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) {
- /*--- If the tag GetMarker_All_ZoneInterface(iMarker) equals the index we are looping at ---*/
- if ( config[donorZone]->GetMarker_All_ZoneInterface(iMarkerDonor) == iMarkerInt ) {
- /*--- We have identified the identifier for the interface marker ---*/
- markDonor = iMarkerDonor;
-
- break;
- }
+ /* --- Check if zones are actually sharing the interface boundary, if not skip. ---*/
+ if(!CInterpolator::CheckInterfaceBoundary(CInterpolator::Find_InterfaceMarker(config[donorZone],iMarkerInt),
+ CInterpolator::Find_InterfaceMarker(config[targetZone],iMarkerInt))) {
+ interface_types[donorZone][targetZone] = NO_COMMON_INTERFACE;
+ continue;
}
- /*--- On the target side ---*/
- nMarkerTarget = config[targetZone]->GetnMarker_All();
-
- for (iMarkerTarget = 0; iMarkerTarget < nMarkerTarget; iMarkerTarget++) {
-
- /*--- If the tag GetMarker_All_ZoneInterface(iMarker) equals the index we are looping at ---*/
- if ( config[targetZone]->GetMarker_All_ZoneInterface(iMarkerTarget) == iMarkerInt ) {
- /*--- We have identified the identifier for the interface marker ---*/
- markTarget = iMarkerTarget;
-
- break;
- }
- }
+ /*--- Set some boolean to properly allocate data structure later ---*/
+ fluid_target = false;
+ structural_target = false;
-#ifdef HAVE_MPI
+ fluid_donor = false;
+ structural_donor = false;
- Donor_check = -1;
- Target_check = -1;
+ heat_donor = false;
+ heat_target = false;
- /*--- We gather a vector in MASTER_NODE that determines if the boundary is not on the processor because
- * of the partition or because the zone does not include it ---*/
+ switch ( config[targetZone]->GetKind_Solver() ) {
- SU2_MPI::Gather(&markDonor , 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD);
+ case EULER : case NAVIER_STOKES: case RANS:
+ case INC_EULER : case INC_NAVIER_STOKES: case INC_RANS:
+ case DISC_ADJ_INC_EULER: case DISC_ADJ_INC_NAVIER_STOKES: case DISC_ADJ_INC_RANS:
+ case DISC_ADJ_EULER: case DISC_ADJ_NAVIER_STOKES: case DISC_ADJ_RANS:
+ fluid_target = true;
+ break;
- if (rank == MASTER_NODE) {
- for (iRank = 0; iRank < nProcessor; iRank++) {
- if( Buffer_Recv_mark[iRank] != -1 ) {
- Donor_check = Buffer_Recv_mark[iRank];
+ case FEM_ELASTICITY: case DISC_ADJ_FEM:
+ structural_target = true;
+ break;
- break;
- }
- }
+ case HEAT_EQUATION: case DISC_ADJ_HEAT:
+ heat_target = true;
+ break;
}
- SU2_MPI::Bcast(&Donor_check , 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD);
+ switch ( config[donorZone]->GetKind_Solver() ) {
- SU2_MPI::Gather(&markTarget, 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD);
+ case EULER : case NAVIER_STOKES: case RANS:
+ case INC_EULER : case INC_NAVIER_STOKES: case INC_RANS:
+ case DISC_ADJ_INC_EULER: case DISC_ADJ_INC_NAVIER_STOKES: case DISC_ADJ_INC_RANS:
+ case DISC_ADJ_EULER: case DISC_ADJ_NAVIER_STOKES: case DISC_ADJ_RANS:
+ fluid_donor = true;
+ break;
- if (rank == MASTER_NODE){
- for (iRank = 0; iRank < nProcessor; iRank++){
- if( Buffer_Recv_mark[iRank] != -1 ){
- Target_check = Buffer_Recv_mark[iRank];
+ case FEM_ELASTICITY: case DISC_ADJ_FEM:
+ structural_donor = true;
+ break;
- break;
- }
- }
+ case HEAT_EQUATION : case DISC_ADJ_HEAT:
+ heat_donor = true;
+ break;
}
- SU2_MPI::Bcast(&Target_check, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD);
-
-#else
- Donor_check = markDonor;
- Target_check = markTarget;
-#endif
-
- /* --- Check ifzones are actually sharing the interface boundary, if not skip ---*/
- if(Target_check == -1 || Donor_check == -1) {
- interface_types[donorZone][targetZone] = NO_COMMON_INTERFACE;
- continue;
- }
-
- /*--- Set some boolean to properly allocate data structure later ---*/
- fluid_target = false;
- structural_target = false;
-
- fluid_donor = false;
- structural_donor = false;
-
- heat_donor = false;
- heat_target = false;
-
- switch ( config[targetZone]->GetKind_Solver() ) {
-
- case EULER : case NAVIER_STOKES: case RANS:
- case INC_EULER : case INC_NAVIER_STOKES: case INC_RANS:
- case DISC_ADJ_INC_EULER: case DISC_ADJ_INC_NAVIER_STOKES: case DISC_ADJ_INC_RANS:
- case DISC_ADJ_EULER: case DISC_ADJ_NAVIER_STOKES: case DISC_ADJ_RANS:
- fluid_target = true;
- break;
-
- case FEM_ELASTICITY: case DISC_ADJ_FEM:
- structural_target = true;
- break;
-
- case HEAT_EQUATION: case DISC_ADJ_HEAT:
- heat_target = true;
- break;
- }
-
- switch ( config[donorZone]->GetKind_Solver() ) {
-
- case EULER : case NAVIER_STOKES: case RANS:
- case INC_EULER : case INC_NAVIER_STOKES: case INC_RANS:
- case DISC_ADJ_INC_EULER: case DISC_ADJ_INC_NAVIER_STOKES: case DISC_ADJ_INC_RANS:
- case DISC_ADJ_EULER: case DISC_ADJ_NAVIER_STOKES: case DISC_ADJ_RANS:
- fluid_donor = true;
- break;
-
- case FEM_ELASTICITY: case DISC_ADJ_FEM:
- structural_donor = true;
- break;
-
- case HEAT_EQUATION : case DISC_ADJ_HEAT:
- heat_donor = true;
- break;
- }
-
- /*--- Begin the creation of the communication pattern among zones ---*/
+ /*--- Begin the creation of the communication pattern among zones ---*/
- /*--- Retrieve the number of conservative variables (for problems not involving structural analysis ---*/
- if (fluid_donor && fluid_target)
- nVar = solver[donorZone][INST_0][MESH_0][FLOW_SOL]->GetnVar();
- else
- /*--- If at least one of the components is structural ---*/
- nVar = nDim;
+ /*--- Retrieve the number of conservative variables (for problems not involving structural analysis ---*/
+ if (fluid_donor && fluid_target)
+ nVar = solver[donorZone][INST_0][MESH_0][FLOW_SOL]->GetnVar();
+ else
+ /*--- If at least one of the components is structural ---*/
+ nVar = nDim;
- if (rank == MASTER_NODE) cout << "From zone " << donorZone << " to zone " << targetZone << ": ";
+ if (rank == MASTER_NODE) cout << "From zone " << donorZone << " to zone " << targetZone << ": ";
/*--- Match Zones ---*/
- if (rank == MASTER_NODE) cout << "Setting coupling ";
+ if (rank == MASTER_NODE) cout << "Setting coupling ";
- bool conservative_interp = config[donorZone]->GetConservativeInterpolation();
+ bool conservative_interp = config[donorZone]->GetConservativeInterpolation();
- /*--- Conditions for conservative interpolation are not met, we cannot fallback on the consistent approach
- because CFlowTractionInterface relies on the information in config to be correct. ---*/
- if ( conservative_interp && targetZone == 0 && structural_target )
- SU2_MPI::Error("Conservative interpolation assumes the structural model mesh is evaluated second, "
- "somehow this has not happened.",CURRENT_FUNCTION);
+ /*--- Conditions for conservative interpolation are not met, we cannot fallback on the consistent approach
+ because CFlowTractionInterface relies on the information in config to be correct. ---*/
+ if ( conservative_interp && targetZone == 0 && structural_target )
+ SU2_MPI::Error("Conservative interpolation assumes the structural model mesh is evaluated second, "
+ "somehow this has not happened.",CURRENT_FUNCTION);
switch (config[donorZone]->GetKindInterpolation()) {
@@ -2721,8 +2637,8 @@ void CDriver::Interface_Preprocessing(CConfig **config, CSolver***** solver, CGe
"from opposite mesh." << endl;
}
else {
- interpolation[donorZone][targetZone] = new CNearestNeighbor(geometry, config, donorZone, targetZone);
- if (rank == MASTER_NODE) cout << "using a nearest-neighbor approach." << endl;
+ interpolation[donorZone][targetZone] = new CNearestNeighbor(geometry, config, donorZone, targetZone);
+ if (rank == MASTER_NODE) cout << "using a nearest-neighbor approach." << endl;
}
break;
@@ -2733,107 +2649,110 @@ void CDriver::Interface_Preprocessing(CConfig **config, CSolver***** solver, CGe
"from opposite mesh." << endl;
}
else {
- interpolation[donorZone][targetZone] = new CIsoparametric(geometry, config, donorZone, targetZone);
- if (rank == MASTER_NODE) cout << "using an isoparametric approach." << endl;
+ interpolation[donorZone][targetZone] = new CIsoparametric(geometry, config, donorZone, targetZone);
+ if (rank == MASTER_NODE) cout << "using an isoparametric approach." << endl;
}
break;
- case WEIGHTED_AVERAGE:
- interpolation[donorZone][targetZone] = new CSlidingMesh(geometry, config, donorZone, targetZone);
- if (rank == MASTER_NODE) cout << "using an sliding mesh approach." << endl;
+ case WEIGHTED_AVERAGE:
+ interpolation[donorZone][targetZone] = new CSlidingMesh(geometry, config, donorZone, targetZone);
+ if (rank == MASTER_NODE) cout << "using an sliding mesh approach." << endl;
- break;
+ break;
case RADIAL_BASIS_FUNCTION:
if ( conservative_interp && targetZone > 0 && structural_target ) {
interpolation[donorZone][targetZone] = new CMirror(geometry, config, donorZone, targetZone);
if (rank == MASTER_NODE) cout << "using a mirror approach: matching coefficients "
"from opposite mesh." << endl;
- }
- else {
- interpolation[donorZone][targetZone] = new CRadialBasisFunction(geometry, config,
- donorZone, targetZone);
- if (rank == MASTER_NODE) cout << "using a radial basis function approach." << endl;
- }
- break;
}
+ else {
+ interpolation[donorZone][targetZone] = new CRadialBasisFunction(geometry, config,
+ donorZone, targetZone);
+ if (rank == MASTER_NODE) cout << "using a radial basis function approach." << endl;
+ }
+ break;
+ }
+
+ if (interpolation[donorZone][targetZone])
+ interpolation[donorZone][targetZone]->PrintStatistics();
/*--- Initialize the appropriate transfer strategy ---*/
- if (rank == MASTER_NODE) cout << "Transferring ";
-
- if (fluid_donor && structural_target) {
- interface_types[donorZone][targetZone] = FLOW_TRACTION;
- nVarTransfer = 2;
- if(!discrete_adjoint) {
- interface[donorZone][targetZone] = new CFlowTractionInterface(nVar, nVarTransfer, config[donorZone]);
- } else {
- interface[donorZone][targetZone] = new CDiscAdjFlowTractionInterface(nVar, nVarTransfer, config[donorZone]);
+ if (rank == MASTER_NODE) cout << "Transferring ";
+
+ if (fluid_donor && structural_target) {
+ interface_types[donorZone][targetZone] = FLOW_TRACTION;
+ nVarTransfer = 2;
+ if(!discrete_adjoint) {
+ interface[donorZone][targetZone] = new CFlowTractionInterface(nVar, nVarTransfer, config[donorZone]);
+ } else {
+ interface[donorZone][targetZone] = new CDiscAdjFlowTractionInterface(nVar, nVarTransfer, config[donorZone]);
+ }
+ if (rank == MASTER_NODE) cout << "flow tractions. " << endl;
}
- if (rank == MASTER_NODE) cout << "flow tractions. " << endl;
- }
- else if (structural_donor && fluid_target) {
- /*--- If we are using the new mesh solver, we transfer the total boundary displacements (not incremental) --*/
- if (solver_container[targetZone][INST_0][MESH_0][MESH_SOL] != NULL) {
- interface_types[donorZone][targetZone] = BOUNDARY_DISPLACEMENTS;
+ else if (structural_donor && fluid_target) {
+ /*--- If we are using the new mesh solver, we transfer the total boundary displacements (not incremental) --*/
+ if (solver_container[targetZone][INST_0][MESH_0][MESH_SOL] != NULL) {
+ interface_types[donorZone][targetZone] = BOUNDARY_DISPLACEMENTS;
+ nVarTransfer = 0;
+ interface[donorZone][targetZone] = new CDisplacementsInterface(nVar, nVarTransfer, config[donorZone]);
+ if (rank == MASTER_NODE) cout << "boundary displacements from the structural solver. " << endl;
+ }
+ /*--- We keep the legacy method temporarily until FSI-adjoint has been adapted ---*/
+ /// TODO: LEGACY CLEANUP remove the "else" part and every class and enum referenced there,
+ /// add a check above to make sure MESH_SOL has been instantiated.
+ else {
+ nVarTransfer = 0;
+ if(!discrete_adjoint) {
+ interface_types[donorZone][targetZone] = STRUCTURAL_DISPLACEMENTS_LEGACY;
+ interface[donorZone][targetZone] = new CDisplacementsInterfaceLegacy(nVar, nVarTransfer, config[donorZone]);
+ } else {
+ interface_types[donorZone][targetZone] = STRUCTURAL_DISPLACEMENTS_DISC_ADJ;
+ interface[donorZone][targetZone] = new CDiscAdjDisplacementsInterfaceLegacy(nVar, nVarTransfer, config[donorZone]);
+ }
+ if (rank == MASTER_NODE) cout << "structural displacements (legacy). " << endl;
+ }
+ }
+ else if (fluid_donor && fluid_target) {
+ interface_types[donorZone][targetZone] = SLIDING_INTERFACE;
+ nVarTransfer = 0;
+ nVar = solver[donorZone][INST_0][MESH_0][FLOW_SOL]->GetnPrimVar();
+ interface[donorZone][targetZone] = new CSlidingInterface(nVar, nVarTransfer, config[donorZone]);
+ if (rank == MASTER_NODE) cout << "sliding interface. " << endl;
+ }
+ else if (fluid_donor && heat_target) {
+ nVarTransfer = 0;
+ nVar = 4;
+ if(config[donorZone]->GetEnergy_Equation() || (config[donorZone]->GetKind_Regime() == COMPRESSIBLE))
+ interface_types[donorZone][targetZone] = CONJUGATE_HEAT_FS;
+ else if (config[donorZone]->GetWeakly_Coupled_Heat())
+ interface_types[donorZone][targetZone] = CONJUGATE_HEAT_WEAKLY_FS;
+ else { }
+ interface[donorZone][targetZone] = new CConjugateHeatInterface(nVar, nVarTransfer, config[donorZone]);
+ if (rank == MASTER_NODE) cout << "conjugate heat variables. " << endl;
+ }
+ else if (heat_donor && fluid_target) {
nVarTransfer = 0;
- interface[donorZone][targetZone] = new CDisplacementsInterface(nVar, nVarTransfer, config[donorZone]);
- if (rank == MASTER_NODE) cout << "boundary displacements from the structural solver. " << endl;
+ nVar = 4;
+ if(config[targetZone]->GetEnergy_Equation() || (config[targetZone]->GetKind_Regime() == COMPRESSIBLE))
+ interface_types[donorZone][targetZone] = CONJUGATE_HEAT_SF;
+ else if (config[targetZone]->GetWeakly_Coupled_Heat())
+ interface_types[donorZone][targetZone] = CONJUGATE_HEAT_WEAKLY_SF;
+ else { }
+ interface[donorZone][targetZone] = new CConjugateHeatInterface(nVar, nVarTransfer, config[donorZone]);
+ if (rank == MASTER_NODE) cout << "conjugate heat variables. " << endl;
+ }
+ else if (heat_donor && heat_target) {
+ SU2_MPI::Error("Conjugate heat transfer between solids not implemented yet.", CURRENT_FUNCTION);
}
- /*--- We keep the legacy method temporarily until FSI-adjoint has been adapted ---*/
- /// TODO: LEGACY CLEANUP remove the "else" part and every class and enum referenced there,
- /// add a check above to make sure MESH_SOL has been instantiated.
else {
+ interface_types[donorZone][targetZone] = CONSERVATIVE_VARIABLES;
nVarTransfer = 0;
- if(!discrete_adjoint) {
- interface_types[donorZone][targetZone] = STRUCTURAL_DISPLACEMENTS_LEGACY;
- interface[donorZone][targetZone] = new CDisplacementsInterfaceLegacy(nVar, nVarTransfer, config[donorZone]);
- } else {
- interface_types[donorZone][targetZone] = STRUCTURAL_DISPLACEMENTS_DISC_ADJ;
- interface[donorZone][targetZone] = new CDiscAdjDisplacementsInterfaceLegacy(nVar, nVarTransfer, config[donorZone]);
- }
- if (rank == MASTER_NODE) cout << "structural displacements (legacy). " << endl;
+ interface[donorZone][targetZone] = new CConservativeVarsInterface(nVar, nVarTransfer, config[donorZone]);
+ if (rank == MASTER_NODE) cout << "generic conservative variables. " << endl;
}
- }
- else if (fluid_donor && fluid_target) {
- interface_types[donorZone][targetZone] = SLIDING_INTERFACE;
- nVarTransfer = 0;
- nVar = solver[donorZone][INST_0][MESH_0][FLOW_SOL]->GetnPrimVar();
- interface[donorZone][targetZone] = new CSlidingInterface(nVar, nVarTransfer, config[donorZone]);
- if (rank == MASTER_NODE) cout << "sliding interface. " << endl;
- }
- else if (fluid_donor && heat_target) {
- nVarTransfer = 0;
- nVar = 4;
- if(config[donorZone]->GetEnergy_Equation() || (config[donorZone]->GetKind_Regime() == COMPRESSIBLE))
- interface_types[donorZone][targetZone] = CONJUGATE_HEAT_FS;
- else if (config[donorZone]->GetWeakly_Coupled_Heat())
- interface_types[donorZone][targetZone] = CONJUGATE_HEAT_WEAKLY_FS;
- else { }
- interface[donorZone][targetZone] = new CConjugateHeatInterface(nVar, nVarTransfer, config[donorZone]);
- if (rank == MASTER_NODE) cout << "conjugate heat variables. " << endl;
- }
- else if (heat_donor && fluid_target) {
- nVarTransfer = 0;
- nVar = 4;
- if(config[targetZone]->GetEnergy_Equation() || (config[targetZone]->GetKind_Regime() == COMPRESSIBLE))
- interface_types[donorZone][targetZone] = CONJUGATE_HEAT_SF;
- else if (config[targetZone]->GetWeakly_Coupled_Heat())
- interface_types[donorZone][targetZone] = CONJUGATE_HEAT_WEAKLY_SF;
- else { }
- interface[donorZone][targetZone] = new CConjugateHeatInterface(nVar, nVarTransfer, config[donorZone]);
- if (rank == MASTER_NODE) cout << "conjugate heat variables. " << endl;
- }
- else if (heat_donor && heat_target) {
- SU2_MPI::Error("Conjugate heat transfer between solids not implemented yet.", CURRENT_FUNCTION);
- }
- else {
- interface_types[donorZone][targetZone] = CONSERVATIVE_VARIABLES;
- nVarTransfer = 0;
- interface[donorZone][targetZone] = new CConservativeVarsInterface(nVar, nVarTransfer, config[donorZone]);
- if (rank == MASTER_NODE) cout << "generic conservative variables. " << endl;
- }
- break;
+ break;
}
@@ -2851,10 +2770,6 @@ void CDriver::Interface_Preprocessing(CConfig **config, CSolver***** solver, CGe
}
-#ifdef HAVE_MPI
- if (rank == MASTER_NODE)
- delete [] Buffer_Recv_mark;
-#endif
}
void CDriver::StaticMesh_Preprocessing(CConfig *config, CGeometry** geometry, CSurfaceMovement* surface_movement){
@@ -3124,11 +3039,9 @@ void CDriver::Turbomachinery_Preprocessing(CConfig** config, CGeometry**** geome
}
-
-
-
CDriver::~CDriver(void) {}
+
CFluidDriver::CFluidDriver(char* confFile, unsigned short val_nZone, SU2_Comm MPICommunicator) : CDriver(confFile, val_nZone, MPICommunicator, false) {
Max_Iter = config_container[ZONE_0]->GetnInner_Iter();
}
diff --git a/SU2_CFD/src/drivers/CMultizoneDriver.cpp b/SU2_CFD/src/drivers/CMultizoneDriver.cpp
index 99984bbce32e..66db6a2544cc 100644
--- a/SU2_CFD/src/drivers/CMultizoneDriver.cpp
+++ b/SU2_CFD/src/drivers/CMultizoneDriver.cpp
@@ -6,7 +6,7 @@
*
* SU2 Project Website: https://su2code.github.io
*
- * The SU2 Project is maintained by the SU2 Foundation
+ * The SU2 Project is maintained by the SU2 Foundation
* (http://su2foundation.org)
*
* Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md)
@@ -27,14 +27,10 @@
#include "../../include/drivers/CMultizoneDriver.hpp"
#include "../../include/definition_structure.hpp"
+#include "../../../Common/include/interface_interpolation/CInterpolator.hpp"
-
-CMultizoneDriver::CMultizoneDriver(char* confFile,
- unsigned short val_nZone,
- SU2_Comm MPICommunicator) : CDriver(confFile,
- val_nZone,
- MPICommunicator,
- false) {
+CMultizoneDriver::CMultizoneDriver(char* confFile, unsigned short val_nZone, SU2_Comm MPICommunicator) :
+ CDriver(confFile, val_nZone, MPICommunicator, false) {
/*--- Initialize the counter for TimeIter ---*/
TimeIter = 0;
diff --git a/SU2_CFD/src/integration/CStructuralIntegration.cpp b/SU2_CFD/src/integration/CStructuralIntegration.cpp
index 1c6ac815f5ef..18bc767675fc 100644
--- a/SU2_CFD/src/integration/CStructuralIntegration.cpp
+++ b/SU2_CFD/src/integration/CStructuralIntegration.cpp
@@ -71,32 +71,20 @@ void CStructuralIntegration::Structural_Iteration(CGeometry ****geometry, CSolve
void CStructuralIntegration::Space_Integration_FEM(CGeometry *geometry, CSolver **solver_container,
CNumerics **numerics, CConfig *config,
unsigned short RunTime_EqSystem) {
- bool dynamic = config->GetTime_Domain();
bool first_iter = (config->GetInnerIter() == 0);
bool linear_analysis = (config->GetGeometricConditions() == SMALL_DEFORMATIONS);
- bool nonlinear_analysis = (config->GetGeometricConditions() == LARGE_DEFORMATIONS);
unsigned short IterativeScheme = config->GetKind_SpaceIteScheme_FEA();
unsigned short MainSolver = config->GetContainerPosition(RunTime_EqSystem);
CSolver* solver = solver_container[MainSolver];
- /*--- Initial calculation, different logic for restarted simulations. ---*/
- bool initial_calc = false;
- if (config->GetRestart())
- initial_calc = (config->GetTimeIter() == config->GetRestart_Iter()) && first_iter;
- else
- initial_calc = (config->GetTimeIter() == 0) && first_iter;
+ /*--- Mass Matrix was computed during preprocessing, see notes therein. ---*/
- /*--- Mass Matrix computed during preprocessing, see notes therein. ---*/
-
- /*--- If the analysis is linear, only a the constitutive term of the stiffness matrix has to be computed. ---*/
- /*--- This is done only once, at the beginning of the calculation. From then on, K is constant. ---*/
- /*--- For correct differentiation of dynamic cases the matrix needs to be computed every time. ---*/
- if (linear_analysis && (dynamic || initial_calc))
+ if (linear_analysis) {
+ /*--- If the analysis is linear, only a the constitutive term of the stiffness matrix has to be computed. ---*/
solver->Compute_StiffMatrix(geometry, numerics, config);
-
- if (nonlinear_analysis) {
-
+ }
+ else {
/*--- If the analysis is nonlinear the stress terms also need to be computed. ---*/
/*--- For full Newton-Raphson the stiffness matrix and the nodal term are updated every time. ---*/
if (IterativeScheme == NEWTON_RAPHSON) {
@@ -111,7 +99,6 @@ void CStructuralIntegration::Space_Integration_FEM(CGeometry *geometry, CSolver
else
solver->Compute_NodalStressRes(geometry, numerics, config);
}
-
}
/*--- Apply the NATURAL BOUNDARY CONDITIONS (loads). ---*/
diff --git a/SU2_CFD/src/interfaces/CInterface.cpp b/SU2_CFD/src/interfaces/CInterface.cpp
index 33b5ef6ad62a..df0d1ab46490 100644
--- a/SU2_CFD/src/interfaces/CInterface.cpp
+++ b/SU2_CFD/src/interfaces/CInterface.cpp
@@ -6,7 +6,7 @@
*
* SU2 Project Website: https://su2code.github.io
*
- * The SU2 Project is maintained by the SU2 Foundation
+ * The SU2 Project is maintained by the SU2 Foundation
* (http://su2foundation.org)
*
* Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md)
@@ -26,79 +26,42 @@
*/
#include "../../include/interfaces/CInterface.hpp"
+#include "../../../Common/include/interface_interpolation/CInterpolator.hpp"
-CInterface::CInterface(void) {
-
- rank = SU2_MPI::GetRank();
- size = SU2_MPI::GetSize();
-
- Physical_Constants = NULL;
- Donor_Variable = NULL;
- Target_Variable = NULL;
- SpanLevelDonor = NULL;
- SpanValueCoeffTarget = NULL;
-
- nVar = 0;
-
+CInterface::CInterface(void) :
+ rank(SU2_MPI::GetRank()),
+ size(SU2_MPI::GetSize()) {
}
-CInterface::CInterface(unsigned short val_nVar, unsigned short val_nConst, CConfig *config) {
-
- rank = SU2_MPI::GetRank();
- size = SU2_MPI::GetSize();
+CInterface::CInterface(unsigned short val_nVar, unsigned short val_nConst, CConfig *config) :
+ rank(SU2_MPI::GetRank()),
+ size(SU2_MPI::GetSize()),
+ nVar(val_nVar) {
- Physical_Constants = NULL;
- Donor_Variable = NULL;
- Target_Variable = NULL;
-
- unsigned short iVar;
-
- Physical_Constants = new su2double[val_nConst];
- Donor_Variable = new su2double[val_nVar];
- Target_Variable = new su2double[val_nVar];
+ Physical_Constants = new su2double[val_nConst] ();
+ Donor_Variable = new su2double[val_nVar] ();
+ Target_Variable = new su2double[val_nVar] ();
/*--- By default, the value is aggregated in the transfer routine ---*/
valAggregated = true;
-
- nVar = val_nVar;
-
- for (iVar = 0; iVar < nVar; iVar++) {
- Donor_Variable[iVar] = 0.0;
- Target_Variable[iVar] = 0.0;
- }
-
- for (iVar = 0; iVar < val_nConst; iVar++) {
- Physical_Constants[iVar] = 0.0;
- }
-
- SpanLevelDonor = NULL;
- SpanValueCoeffTarget = NULL;
-
}
CInterface::~CInterface(void) {
- if (Physical_Constants != NULL) delete [] Physical_Constants;
- if (Donor_Variable != NULL) delete [] Donor_Variable;
- if (Target_Variable != NULL) delete [] Target_Variable;
-
- if (SpanValueCoeffTarget != NULL) delete[] SpanValueCoeffTarget;
- if (SpanLevelDonor != NULL) delete[] SpanLevelDonor;
+ delete [] Physical_Constants;
+ delete [] Donor_Variable;
+ delete [] Target_Variable;
+ delete[] SpanValueCoeffTarget;
+ delete[] SpanLevelDonor;
}
void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution,
CGeometry *donor_geometry, CGeometry *target_geometry,
CConfig *donor_config, CConfig *target_config) {
-
- unsigned short nMarkerInt, nMarkerDonor, nMarkerTarget; // Number of markers on the interface, donor and target side
- unsigned short iMarkerInt, iMarkerDonor, iMarkerTarget; // Variables for iteration over markers
int Marker_Donor, Marker_Target;
- int Target_check, Donor_check;
-
- unsigned long iVertex; // Variables for iteration over vertices and nodes
-
+ unsigned long iVertex;
unsigned short iVar;
GetPhysical_Constants(donor_solution, target_solution, donor_geometry, target_geometry,
@@ -107,14 +70,8 @@ void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution
unsigned long Point_Donor_Global, Donor_Global_Index;
unsigned long Point_Donor, Point_Target;
-#ifdef HAVE_MPI
- int *Buffer_Recv_mark = NULL, iRank;
-
- if (rank == MASTER_NODE)
- Buffer_Recv_mark = new int[size];
-#endif
-
- unsigned long Buffer_Send_nVertexDonor[1], *Buffer_Recv_nVertexDonor;
+ unsigned long Buffer_Send_nVertexDonor[1];
+ unsigned long *Buffer_Recv_nVertexDonor = new unsigned long[size];
unsigned long iLocalVertex = 0;
unsigned long nLocalVertexDonor = 0, nLocalVertexDonorOwned = 0;
@@ -125,98 +82,23 @@ void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution
unsigned long nBuffer_BcastVariables = 0, nBuffer_BcastIndices = 0;
- int nProcessor = 0;
+ const int nProcessor = size;
/*--- Number of markers on the FSI interface ---*/
- nMarkerInt = (donor_config->GetMarker_n_ZoneInterface())/2;
- nMarkerTarget = target_config->GetnMarker_All();
- nMarkerDonor = donor_config->GetnMarker_All();
-
- nProcessor = size;
+ const auto nMarkerInt = donor_config->GetMarker_n_ZoneInterface()/2;
/*--- Outer loop over the markers on the FSI interface: compute one by one ---*/
/*--- The tags are always an integer greater than 1: loop from 1 to nMarkerFSI ---*/
- for (iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) {
+ for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) {
- Buffer_Recv_nVertexDonor = NULL;
+ /*--- Check if this interface connects the two zones, if not continue. ---*/
- Marker_Donor = -1;
- Marker_Target = -1;
+ Marker_Donor = CInterpolator::Find_InterfaceMarker(donor_config, iMarkerInt);
+ Marker_Target = CInterpolator::Find_InterfaceMarker(target_config, iMarkerInt);
- /*--- The donor and target markers are tagged with the same index.
- *--- This is independent of the MPI domain decomposition.
- *--- We need to loop over all markers on both sides and get the number of nodes
- *--- that belong to each FSI marker for each processor ---*/
-
- /*--- On the donor side ---*/
-
- for (iMarkerDonor = 0; iMarkerDonor < nMarkerDonor; iMarkerDonor++) {
- /*--- If the tag GetMarker_All_ZoneInterface(iMarkerDonor) equals the index we are looping at ---*/
- if ( donor_config->GetMarker_All_ZoneInterface(iMarkerDonor) == iMarkerInt ) {
- /*--- Store the identifier for the structural marker ---*/
- Marker_Donor = iMarkerDonor;
- /*--- Exit the for loop: we have found the local index for iMarkerFSI on the FEA side ---*/
- break;
- }
- }
-
- /*--- On the target side we only have to identify the marker;
- * then we'll loop over it and retrieve from the donor points ---*/
-
- for (iMarkerTarget = 0; iMarkerTarget < nMarkerTarget; iMarkerTarget++) {
- /*--- If the tag GetMarker_All_ZoneInterface(iMarkerFlow) equals the index we are looping at ---*/
- if ( target_config->GetMarker_All_ZoneInterface(iMarkerTarget) == iMarkerInt ) {
- /*--- Store the identifier for the fluid marker ---*/
- Marker_Target = iMarkerTarget;
- /*--- Exit the for loop: we have found the local index for iMarkerFSI on the FEA side ---*/
- break;
- }
- }
-
-#ifdef HAVE_MPI
-
- Donor_check = -1;
- Target_check = -1;
-
- /*--- We gather a vector in MASTER_NODE that determines if the boundary is not on the processor
- * because of the partition or because the zone does not include it ---*/
-
- SU2_MPI::Gather(&Marker_Donor , 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD);
-
- if (rank == MASTER_NODE) {
- for (iRank = 0; iRank < nProcessor; iRank++) {
- if( Buffer_Recv_mark[iRank] != -1 ) {
- Donor_check = Buffer_Recv_mark[iRank];
- break;
- }
- }
- }
-
- SU2_MPI::Bcast(&Donor_check , 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD);
-
- SU2_MPI::Gather(&Marker_Target, 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD);
-
- if (rank == MASTER_NODE) {
- for (iRank = 0; iRank < nProcessor; iRank++) {
- if( Buffer_Recv_mark[iRank] != -1 ) {
- Target_check = Buffer_Recv_mark[iRank];
- break;
- }
- }
- }
-
- SU2_MPI::Bcast(&Target_check, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD);
-
-#else
- Donor_check = Marker_Donor;
- Target_check = Marker_Target;
-#endif
-
- if(Target_check == -1 || Donor_check == -1) {
- continue;
- }
+ if(!CInterpolator::CheckInterfaceBoundary(Marker_Donor, Marker_Target)) continue;
nLocalVertexDonorOwned = 0;
nLocalVertexDonor = 0;
@@ -233,10 +115,6 @@ void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution
Buffer_Send_nVertexDonor[0] = nLocalVertexDonor; // Retrieve total number of vertices on Donor marker
- // Allocate memory to receive how many vertices are on each rank on the structural side
- if (rank == MASTER_NODE) Buffer_Recv_nVertexDonor = new unsigned long[size];
-
-#ifdef HAVE_MPI
/*--- We receive MaxLocalVertexDonor as the maximum number of vertices
* in one single processor on the donor side---*/
SU2_MPI::Allreduce(&nLocalVertexDonor, &MaxLocalVertexDonor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD);
@@ -245,13 +123,8 @@ void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution
SU2_MPI::Allreduce(&nLocalVertexDonorOwned, &TotalVertexDonor, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD);
/*--- We gather a vector in MASTER_NODE that determines how many elements are there
* on each processor on the structural side ---*/
- SU2_MPI::Gather(&Buffer_Send_nVertexDonor, 1, MPI_UNSIGNED_LONG, Buffer_Recv_nVertexDonor, 1,
- MPI_UNSIGNED_LONG, MASTER_NODE, MPI_COMM_WORLD);
-#else
- MaxLocalVertexDonor = nLocalVertexDonor;
- TotalVertexDonor = nLocalVertexDonorOwned;
- Buffer_Recv_nVertexDonor[0] = Buffer_Send_nVertexDonor[0];
-#endif
+ SU2_MPI::Gather(&Buffer_Send_nVertexDonor, 1, MPI_UNSIGNED_LONG,
+ Buffer_Recv_nVertexDonor, 1, MPI_UNSIGNED_LONG, MASTER_NODE, MPI_COMM_WORLD);
/*--- We will be gathering the donor information into the master node ---*/
nBuffer_DonorVariables = MaxLocalVertexDonor * nVar;
@@ -308,20 +181,12 @@ void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution
}
-#ifdef HAVE_MPI
/*--- Once all the messages have been prepared, we gather them all into the MASTER_NODE ---*/
SU2_MPI::Gather(Buffer_Send_DonorVariables, nBuffer_DonorVariables, MPI_DOUBLE, Buffer_Recv_DonorVariables,
nBuffer_DonorVariables, MPI_DOUBLE, MASTER_NODE, MPI_COMM_WORLD);
SU2_MPI::Gather(Buffer_Send_DonorIndices, nBuffer_DonorIndices, MPI_LONG, Buffer_Recv_DonorIndices,
nBuffer_DonorIndices, MPI_LONG, MASTER_NODE, MPI_COMM_WORLD);
-#else
- for (unsigned long iVariable = 0; iVariable < nBuffer_DonorVariables; iVariable++)
- Buffer_Recv_DonorVariables[iVariable] = Buffer_Send_DonorVariables[iVariable];
- for (unsigned long iVariable = 0; iVariable < nBuffer_DonorIndices; iVariable++)
- Buffer_Recv_DonorIndices[iVariable] = Buffer_Send_DonorIndices[iVariable];
-#endif
-
/*--- Now we pack the information to send it over to the different processors ---*/
if (rank == MASTER_NODE) {
@@ -352,10 +217,8 @@ void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution
}
-#ifdef HAVE_MPI
SU2_MPI::Bcast(Buffer_Bcast_Variables, nBuffer_BcastVariables, MPI_DOUBLE, MASTER_NODE, MPI_COMM_WORLD);
SU2_MPI::Bcast(Buffer_Bcast_Indices, nBuffer_BcastIndices, MPI_LONG, MASTER_NODE, MPI_COMM_WORLD);
-#endif
long indexPoint_iVertex;
unsigned short iDonorPoint, nDonorPoints;
@@ -418,18 +281,11 @@ void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution
delete [] Buffer_Bcast_Variables;
delete [] Buffer_Bcast_Indices;
- if (rank == MASTER_NODE) {
- delete [] Buffer_Recv_nVertexDonor;
- delete [] Buffer_Recv_DonorVariables;
- delete [] Buffer_Recv_DonorIndices;
- }
-
+ delete [] Buffer_Recv_DonorVariables;
+ delete [] Buffer_Recv_DonorIndices;
}
-#ifdef HAVE_MPI
- if (rank == MASTER_NODE && Buffer_Recv_mark != NULL)
- delete [] Buffer_Recv_mark;
-#endif
+ delete [] Buffer_Recv_nVertexDonor;
}
void CInterface::PreprocessAverage(CGeometry *donor_geometry, CGeometry *target_geometry,
@@ -444,12 +300,6 @@ void CInterface::PreprocessAverage(CGeometry *donor_geometry, CGeometry *target_
const su2double *SpanValuesDonor, *SpanValuesTarget;
su2double dist, test, dist2, test2;
-#ifdef HAVE_MPI
- int iSize;
- int *BuffMarkerDonor, *BuffDonorFlag;
-#endif
-
-
nMarkerDonor = donor_geometry->GetnMarker();
nMarkerTarget = target_geometry->GetnMarker();
//TODO turbo this approach only works if all the turboamchinery marker
@@ -478,25 +328,23 @@ void CInterface::PreprocessAverage(CGeometry *donor_geometry, CGeometry *target_
}
#ifdef HAVE_MPI
- BuffMarkerDonor = new int[size];
- BuffDonorFlag = new int[size];
- for (iSize=0; iSize 0.0){
Marker_Donor = BuffMarkerDonor[iSize];
- Donor_Flag = BuffDonorFlag[iSize];
+ Donor_Flag = BuffDonorFlag[iSize];
break;
}
}
@@ -574,6 +422,7 @@ void CInterface::PreprocessAverage(CGeometry *donor_geometry, CGeometry *target_
void CInterface::AllgatherAverage(CSolver *donor_solution, CSolver *target_solution,
CGeometry *donor_geometry, CGeometry *target_geometry,
CConfig *donor_config, CConfig *target_config, unsigned short iMarkerInt){
+
unsigned short nMarkerDonor, nMarkerTarget; // Number of markers on the interface, donor and target side
unsigned short iMarkerDonor, iMarkerTarget; // Variables for iteration over markers
unsigned short iSpan, nSpanDonor, nSpanTarget;
@@ -767,7 +616,6 @@ void CInterface::AllgatherAverage(CSolver *donor_solution, CSolver *target_solut
delete [] BuffAvgKineDonor;
delete [] BuffAvgOmegaDonor;
delete [] BuffMarkerDonor;
-
#endif
/*--- On the target side we have to identify the marker as well ---*/
@@ -904,4 +752,3 @@ void CInterface::GatherAverageTurboGeoValues(CGeometry *donor_geometry, CGeometr
SetAverageTurboGeoValues(donor_geometry, target_geometry, donorZone);
}
-
diff --git a/SU2_CFD/src/interfaces/cfd/CSlidingInterface.cpp b/SU2_CFD/src/interfaces/cfd/CSlidingInterface.cpp
index 242a5d6bddc7..a8cf13a71dd5 100644
--- a/SU2_CFD/src/interfaces/cfd/CSlidingInterface.cpp
+++ b/SU2_CFD/src/interfaces/cfd/CSlidingInterface.cpp
@@ -7,7 +7,7 @@
*
* SU2 Project Website: https://su2code.github.io
*
- * The SU2 Project is maintained by the SU2 Foundation
+ * The SU2 Project is maintained by the SU2 Foundation
* (http://su2foundation.org)
*
* Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md)
@@ -29,52 +29,25 @@
#include "../../../include/interfaces/cfd/CSlidingInterface.hpp"
-CSlidingInterface::CSlidingInterface(void) : CInterface() {
-
-}
+CSlidingInterface::CSlidingInterface(void) : CInterface() { }
CSlidingInterface::CSlidingInterface(unsigned short val_nVar, unsigned short val_nConst,
CConfig *config) : CInterface() {
- rank = SU2_MPI::GetRank();
- size = SU2_MPI::GetSize();
-
- Physical_Constants = NULL;
- Donor_Variable = NULL;
- Target_Variable = NULL;
-
- unsigned short iVar;
-
- Physical_Constants = new su2double[val_nConst];
- Donor_Variable = new su2double[val_nVar];
-
- Target_Variable = new su2double[val_nVar+1];
+ Physical_Constants = new su2double[val_nConst] ();
+ Donor_Variable = new su2double[val_nVar] ();
+ Target_Variable = new su2double[val_nVar+1] ();
valAggregated = false;
nVar = val_nVar;
-
- for (iVar = 0; iVar < nVar; iVar++) {
- Donor_Variable[iVar] = 0.0;
- Target_Variable[iVar] = 0.0;
- }
-
- for (iVar = 0; iVar < val_nConst; iVar++) {
- Physical_Constants[iVar] = 0.0;
- }
-
-}
-
-CSlidingInterface::~CSlidingInterface(void) {
-
}
+CSlidingInterface::~CSlidingInterface(void) { }
void CSlidingInterface::GetPhysical_Constants(CSolver *donor_solution, CSolver *target_solution,
CGeometry *donor_geometry, CGeometry *target_geometry,
- CConfig *donor_config, CConfig *target_config) {
-
-}
+ CConfig *donor_config, CConfig *target_config) { }
void CSlidingInterface::GetDonor_Variable(CSolver *donor_solution, CGeometry *donor_geometry,
CConfig *donor_config, unsigned long Marker_Donor,
diff --git a/SU2_CFD/src/iteration_structure.cpp b/SU2_CFD/src/iteration_structure.cpp
index 353816cac7d2..b4f1e6615d67 100644
--- a/SU2_CFD/src/iteration_structure.cpp
+++ b/SU2_CFD/src/iteration_structure.cpp
@@ -1308,17 +1308,16 @@ CFEAIteration::CFEAIteration(CConfig *config) : CIteration(config) { }
CFEAIteration::~CFEAIteration(void) { }
void CFEAIteration::Preprocess() { }
void CFEAIteration::Iterate(COutput *output,
- CIntegration ****integration,
- CGeometry ****geometry,
- CSolver *****solver,
- CNumerics ******numerics,
- CConfig **config,
- CSurfaceMovement **surface_movement,
- CVolumetricMovement ***grid_movement,
- CFreeFormDefBox*** FFDBox,
- unsigned short val_iZone,
- unsigned short val_iInst
- ) {
+ CIntegration ****integration,
+ CGeometry ****geometry,
+ CSolver *****solver,
+ CNumerics ******numerics,
+ CConfig **config,
+ CSurfaceMovement **surface_movement,
+ CVolumetricMovement ***grid_movement,
+ CFreeFormDefBox*** FFDBox,
+ unsigned short val_iZone,
+ unsigned short val_iInst) {
su2double loadIncrement;
unsigned long IntIter = 0;
@@ -1327,227 +1326,176 @@ void CFEAIteration::Iterate(COutput *output,
unsigned long iIncrement;
unsigned long nIncrements = config[val_iZone]->GetNumberIncrements();
- bool nonlinear = (config[val_iZone]->GetGeometricConditions() == LARGE_DEFORMATIONS); // Geometrically non-linear problems
- bool linear = (config[val_iZone]->GetGeometricConditions() == SMALL_DEFORMATIONS); // Geometrically non-linear problems
-
+ bool nonlinear = (config[val_iZone]->GetGeometricConditions() == LARGE_DEFORMATIONS);
+ bool linear = (config[val_iZone]->GetGeometricConditions() == SMALL_DEFORMATIONS);
bool disc_adj_fem = (config[val_iZone]->GetKind_Solver() == DISC_ADJ_FEM);
+ bool incremental_load = config[val_iZone]->GetIncrementalLoad(); // Loads applied in steps
- bool incremental_load = config[val_iZone]->GetIncrementalLoad(); // If an incremental load is applied
-
- /*--- This is to prevent problems when running a linear solver ---*/
- if (!nonlinear) incremental_load = false;
+ CIntegration* feaIntegration = integration[val_iZone][val_iInst][FEA_SOL];
+ CSolver* feaSolver = solver[val_iZone][val_iInst][MESH_0][FEA_SOL];
/*--- Set the convergence monitor to false, to prevent the solver to stop in intermediate FSI subiterations ---*/
- integration[val_iZone][val_iInst][FEA_SOL]->SetConvergence(false);
+ feaIntegration->SetConvergence(false);
+
+ /*--- FEA equations ---*/
+ config[val_iZone]->SetGlobalParam(FEM_ELASTICITY, RUNTIME_FEA_SYS);
if (linear) {
config[val_iZone]->SetInnerIter(0);
- /*--- FEA equations ---*/
-
- config[val_iZone]->SetGlobalParam(FEM_ELASTICITY, RUNTIME_FEA_SYS);
-
/*--- Run the iteration ---*/
- integration[val_iZone][val_iInst][FEA_SOL]->Structural_Iteration(geometry, solver, numerics,
- config, RUNTIME_FEA_SYS, val_iZone, val_iInst);
+ feaIntegration->Structural_Iteration(geometry, solver, numerics, config,
+ RUNTIME_FEA_SYS, val_iZone, val_iInst);
- if (!disc_adj_fem){
- Monitor(output, integration, geometry, solver, numerics, config, surface_movement, grid_movement, FFDBox, val_iZone, INST_0);
+ if (!disc_adj_fem) {
+ Monitor(output, integration, geometry, solver, numerics, config,
+ surface_movement, grid_movement, FFDBox, val_iZone, INST_0);
/*--- Set the convergence monitor to true, to prevent the solver to stop in intermediate FSI subiterations ---*/
output->SetConvergence(true);
}
}
- /*--- If the structure is held static and the solver is nonlinear, we don't need to solve for static time, but we need to compute Mass Matrix and Integration constants ---*/
- else if (nonlinear) {
+ else if (nonlinear && !incremental_load) {
/*--- THIS IS THE DIRECT APPROACH (NO INCREMENTAL LOAD APPLIED) ---*/
- if (!incremental_load) {
-
- /*--- Keep the current inner iter, we need to restore it in discrete adjoint cases as file output depends on it ---*/
- unsigned long CurIter = config[val_iZone]->GetInnerIter();
-
- IntIter = 0;
- config[val_iZone]->SetInnerIter(IntIter);
-
- /*--- FEA equations ---*/
+ /*--- Keep the current inner iter, we need to restore it in discrete adjoint cases as file output depends on it ---*/
+ const auto CurIter = config[val_iZone]->GetInnerIter();
- config[val_iZone]->SetGlobalParam(FEM_ELASTICITY, RUNTIME_FEA_SYS);
-
- /*--- Run the iteration ---*/
-
- integration[val_iZone][val_iInst][FEA_SOL]->Structural_Iteration(geometry, solver, numerics,
- config, RUNTIME_FEA_SYS, val_iZone, val_iInst);
-
- if (!disc_adj_fem)
- Monitor(output, integration, geometry, solver, numerics, config, surface_movement, grid_movement, FFDBox, val_iZone, INST_0);
-
- /*----------------- If the solver is non-linear, we need to subiterate using a Newton-Raphson approach ----------------------*/
-
- for (IntIter = 1; IntIter < config[val_iZone]->GetnInner_Iter(); IntIter++) {
-
- /*--- Limit to only one iteration for the discrete adjoint recording, restore inner iter (see above) ---*/
- if (disc_adj_fem) {
- config[val_iZone]->SetInnerIter(CurIter);
- break;
- }
-
- config[val_iZone]->SetInnerIter(IntIter);
+ /*--- Newton-Raphson subiterations ---*/
- integration[val_iZone][val_iInst][FEA_SOL]->Structural_Iteration(geometry, solver, numerics,
- config, RUNTIME_FEA_SYS, val_iZone, val_iInst);
-
- StopCalc = Monitor(output, integration, geometry, solver, numerics, config, surface_movement, grid_movement, FFDBox, val_iZone, INST_0);
+ for (IntIter = 0; IntIter < config[val_iZone]->GetnInner_Iter(); IntIter++) {
+ config[val_iZone]->SetInnerIter(IntIter);
- if (StopCalc) break;
+ feaIntegration->Structural_Iteration(geometry, solver, numerics, config,
+ RUNTIME_FEA_SYS, val_iZone, val_iInst);
+ /*--- Limit to only one iteration for the discrete adjoint recording, restore inner iter (see above) ---*/
+ if (disc_adj_fem) {
+ config[val_iZone]->SetInnerIter(CurIter);
+ break;
}
+ else {
+ StopCalc = Monitor(output, integration, geometry, solver, numerics, config,
+ surface_movement, grid_movement, FFDBox, val_iZone, INST_0);
+ if (StopCalc && (IntIter > 0)) break;
+ }
}
- /*--- The incremental load is only used in nonlinear cases ---*/
- else if (incremental_load) {
- /*--- Set the initial condition: store the current solution as Solution_Old ---*/
+ }
+ else {
+
+ /*--- THIS IS THE INCREMENTAL LOAD APPROACH (only makes sense for nonlinear) ---*/
- solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->SetInitialCondition(geometry[val_iZone][val_iInst], solver[val_iZone][val_iInst], config[val_iZone], TimeIter);
+ /*--- Set the initial condition: store the current solution as Solution_Old ---*/
- /*--- The load increment is 1.0 ---*/
+ feaSolver->SetInitialCondition(geometry[val_iZone][val_iInst],
+ solver[val_iZone][val_iInst], config[val_iZone], TimeIter);
- loadIncrement = 1.0;
- solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->SetLoad_Increment(loadIncrement);
- solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->SetForceCoeff(loadIncrement);
+ /*--- Assume the initial load increment as 1.0 ---*/
- /*--- Set the value of the internal iteration ---*/
+ loadIncrement = 1.0;
+ feaSolver->SetLoad_Increment(loadIncrement);
+ feaSolver->SetForceCoeff(loadIncrement);
+
+ /*--- Run two nonlinear iterations to check if incremental loading can be skipped ---*/
+
+ for (IntIter = 0; IntIter < 2; ++IntIter) {
- IntIter = 0;
config[val_iZone]->SetInnerIter(IntIter);
- /*--- FEA equations ---*/
-
- config[val_iZone]->SetGlobalParam(FEM_ELASTICITY, RUNTIME_FEA_SYS);
-
/*--- Run the first iteration ---*/
- integration[val_iZone][val_iInst][FEA_SOL]->Structural_Iteration(geometry, solver, numerics,
- config, RUNTIME_FEA_SYS, val_iZone, val_iInst);
-
- /*--- Write the convergence history (first, compute Von Mises stress) ---*/
-
- Monitor(output, integration, geometry, solver, numerics, config, surface_movement, grid_movement, FFDBox, val_iZone, INST_0);
+ feaIntegration->Structural_Iteration(geometry, solver, numerics, config,
+ RUNTIME_FEA_SYS, val_iZone, val_iInst);
- /*--- Run the second iteration ---*/
-
- IntIter = 1;
- config[val_iZone]->SetInnerIter(IntIter);
+ /*--- Write the convergence history (first computes Von Mises stress) ---*/
- integration[val_iZone][val_iInst][FEA_SOL]->Structural_Iteration(geometry, solver, numerics,
- config, RUNTIME_FEA_SYS, val_iZone, val_iInst);
+ Monitor(output, integration, geometry, solver, numerics, config,
+ surface_movement, grid_movement, FFDBox, val_iZone, INST_0);
+ }
- /*--- Write the convergence history (first, compute Von Mises stress) ---*/
- Monitor(output, integration, geometry, solver, numerics, config, surface_movement, grid_movement, FFDBox, val_iZone, INST_0);
+ bool meetCriteria;
+ su2double Residual_UTOL, Residual_RTOL, Residual_ETOL;
+ su2double Criteria_UTOL, Criteria_RTOL, Criteria_ETOL;
- bool meetCriteria;
- su2double Residual_UTOL, Residual_RTOL, Residual_ETOL;
- su2double Criteria_UTOL, Criteria_RTOL, Criteria_ETOL;
+ Criteria_UTOL = config[val_iZone]->GetIncLoad_Criteria(0);
+ Criteria_RTOL = config[val_iZone]->GetIncLoad_Criteria(1);
+ Criteria_ETOL = config[val_iZone]->GetIncLoad_Criteria(2);
- Criteria_UTOL = config[val_iZone]->GetIncLoad_Criteria(0);
- Criteria_RTOL = config[val_iZone]->GetIncLoad_Criteria(1);
- Criteria_ETOL = config[val_iZone]->GetIncLoad_Criteria(2);
+ Residual_UTOL = log10(feaSolver->LinSysSol.norm());
+ Residual_RTOL = log10(feaSolver->LinSysRes.norm());
+ Residual_ETOL = log10(feaSolver->LinSysSol.dot(feaSolver->LinSysRes));
- Residual_UTOL = log10(solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->LinSysSol.norm());
- Residual_RTOL = log10(solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->LinSysRes.norm());
- Residual_ETOL = log10(solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->LinSysSol.dot(
- solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->LinSysRes));
+ meetCriteria = ( ( Residual_UTOL < Criteria_UTOL ) &&
+ ( Residual_RTOL < Criteria_RTOL ) &&
+ ( Residual_ETOL < Criteria_ETOL ) );
- meetCriteria = ( ( Residual_UTOL < Criteria_UTOL ) &&
- ( Residual_RTOL < Criteria_RTOL ) &&
- ( Residual_ETOL < Criteria_ETOL ) );
+ /*--- If the criteria is met, i.e. the load is not "too big", continue the regular calculation ---*/
- /*--- If the criteria is met and the load is not "too big", do the regular calculation ---*/
- if (meetCriteria) {
+ if (meetCriteria) {
- for (IntIter = 2; IntIter < config[val_iZone]->GetnInner_Iter(); IntIter++) {
+ /*--- Newton-Raphson subiterations ---*/
- integration[val_iZone][val_iInst][FEA_SOL]->Structural_Iteration(geometry, solver, numerics,
- config, RUNTIME_FEA_SYS, val_iZone, val_iInst);
+ for (IntIter = 2; IntIter < config[val_iZone]->GetnInner_Iter(); IntIter++) {
- /*--- Write the convergence history (first, compute Von Mises stress) ---*/
- StopCalc = Monitor(output, integration, geometry, solver, numerics, config,
- surface_movement, grid_movement, FFDBox, val_iZone, INST_0);
+ config[val_iZone]->SetInnerIter(IntIter);
- if (StopCalc) break;
+ feaIntegration->Structural_Iteration(geometry, solver, numerics, config,
+ RUNTIME_FEA_SYS, val_iZone, val_iInst);
- }
+ StopCalc = Monitor(output, integration, geometry, solver, numerics, config,
+ surface_movement, grid_movement, FFDBox, val_iZone, INST_0);
+ if (StopCalc) break;
}
- /*--- If the criteria is not met, a whole set of subiterations for the different loads must be done ---*/
+ }
- else {
+ /*--- If the criteria is not met, a whole set of subiterations for the different loads must be done ---*/
- /*--- Here we have to restart the solution to the original one of the iteration ---*/
- /*--- Retrieve the Solution_Old as the current solution before subiterating ---*/
+ else {
- solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->ResetInitialCondition(geometry[val_iZone][val_iInst],
- solver[val_iZone][val_iInst], config[val_iZone], TimeIter);
+ /*--- Here we have to restore the solution to the one before testing the criteria ---*/
+ /*--- Retrieve the Solution_Old as the current solution before subiterating ---*/
- /*--- For the number of increments ---*/
- for (iIncrement = 0; iIncrement < nIncrements; iIncrement++) {
+ feaSolver->ResetInitialCondition(geometry[val_iZone][val_iInst],
+ solver[val_iZone][val_iInst], config[val_iZone], TimeIter);
- loadIncrement = (iIncrement + 1.0) * (1.0 / nIncrements);
+ /*--- For the number of increments ---*/
+ for (iIncrement = 0; iIncrement < nIncrements; iIncrement++) {
- /*--- Set the load increment and the initial condition, and output the parameters of UTOL, RTOL, ETOL for the previous iteration ---*/
+ /*--- Set the load increment and the initial condition, and output the
+ * parameters of UTOL, RTOL, ETOL for the previous iteration ---*/
- /*--- Set the convergence monitor to false, to force se solver to converge every subiteration ---*/
- output->SetConvergence(false);
+ loadIncrement = (iIncrement + 1.0) * (1.0 / nIncrements);
+ feaSolver->SetLoad_Increment(loadIncrement);
- /*--- FEA equations ---*/
+ /*--- Set the convergence monitor to false, to force the solver to converge every subiteration ---*/
+ output->SetConvergence(false);
- config[val_iZone]->SetGlobalParam(FEM_ELASTICITY, RUNTIME_FEA_SYS);
+ if (rank == MASTER_NODE)
+ cout << "\nIncremental load: increment " << iIncrement + 1 << endl;
- solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->SetLoad_Increment(loadIncrement);
+ /*--- Newton-Raphson subiterations ---*/
- if (rank == MASTER_NODE) {
- cout << endl;
- cout << "Incremental load: increment " << iIncrement + 1 << endl;
- }
+ for (IntIter = 0; IntIter < config[val_iZone]->GetnInner_Iter(); IntIter++) {
- /*--- Set the value of the internal iteration ---*/
- IntIter = 0;
config[val_iZone]->SetInnerIter(IntIter);
- /*--- FEA equations ---*/
-
- config[val_iZone]->SetGlobalParam(FEM_ELASTICITY, RUNTIME_FEA_SYS);
-
- /*--- Run the iteration ---*/
-
- integration[val_iZone][val_iInst][FEA_SOL]->Structural_Iteration(geometry, solver, numerics,
- config, RUNTIME_FEA_SYS, val_iZone, val_iInst);
-
- Monitor(output, integration, geometry, solver, numerics, config, surface_movement, grid_movement, FFDBox, val_iZone, INST_0);
-
-
- /*----------------- If the solver is non-linear, we need to subiterate using a Newton-Raphson approach ----------------------*/
-
- for (IntIter = 1; IntIter < config[val_iZone]->GetnInner_Iter(); IntIter++) {
-
- config[val_iZone]->SetInnerIter(IntIter);
-
- integration[val_iZone][val_iInst][FEA_SOL]->Structural_Iteration(geometry, solver, numerics,
- config, RUNTIME_FEA_SYS, val_iZone, val_iInst);
+ feaIntegration->Structural_Iteration(geometry, solver, numerics, config,
+ RUNTIME_FEA_SYS, val_iZone, val_iInst);
- /*--- Write the convergence history (first, compute Von Mises stress) ---*/
- StopCalc = Monitor(output, integration, geometry, solver, numerics, config, surface_movement, grid_movement, FFDBox, val_iZone, INST_0);
-
- if (StopCalc) break;
+ /*--- Write the convergence history (first, compute Von Mises stress) ---*/
+ StopCalc = Monitor(output, integration, geometry, solver, numerics, config,
+ surface_movement, grid_movement, FFDBox, val_iZone, INST_0);
- }
+ if (StopCalc && (IntIter > 0)) break;
}
@@ -1559,59 +1507,66 @@ void CFEAIteration::Iterate(COutput *output,
/*--- Finally, we need to compute the objective function, in case that we are running a discrete adjoint solver... ---*/
- switch (config[val_iZone]->GetKind_ObjFunc()){
+ switch (config[val_iZone]->GetKind_ObjFunc()) {
case REFERENCE_GEOMETRY:
- if ((config[val_iZone]->GetDV_FEA() == YOUNG_MODULUS) || (config[val_iZone]->GetDV_FEA() == DENSITY_VAL)){
- solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->Stiffness_Penalty(geometry[val_iZone][val_iInst][MESH_0],solver[val_iZone][val_iInst][MESH_0],
- numerics[val_iZone][val_iInst][MESH_0][FEA_SOL], config[val_iZone]);
+ if ((config[val_iZone]->GetDV_FEA() == YOUNG_MODULUS) || (config[val_iZone]->GetDV_FEA() == DENSITY_VAL)) {
+ feaSolver->Stiffness_Penalty(geometry[val_iZone][val_iInst][MESH_0], solver[val_iZone][val_iInst][MESH_0],
+ numerics[val_iZone][val_iInst][MESH_0][FEA_SOL], config[val_iZone]);
}
- solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->Compute_OFRefGeom(geometry[val_iZone][val_iInst][MESH_0],solver[val_iZone][val_iInst][MESH_0], config[val_iZone]);
+ feaSolver->Compute_OFRefGeom(geometry[val_iZone][val_iInst][MESH_0],
+ solver[val_iZone][val_iInst][MESH_0], config[val_iZone]);
break;
case REFERENCE_NODE:
- if ((config[val_iZone]->GetDV_FEA() == YOUNG_MODULUS) || (config[val_iZone]->GetDV_FEA() == DENSITY_VAL)){
- solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->Stiffness_Penalty(geometry[val_iZone][val_iInst][MESH_0],solver[val_iZone][val_iInst][MESH_0],
- numerics[val_iZone][val_iInst][MESH_0][FEA_SOL], config[val_iZone]);
+ if ((config[val_iZone]->GetDV_FEA() == YOUNG_MODULUS) || (config[val_iZone]->GetDV_FEA() == DENSITY_VAL)) {
+ feaSolver->Stiffness_Penalty(geometry[val_iZone][val_iInst][MESH_0], solver[val_iZone][val_iInst][MESH_0],
+ numerics[val_iZone][val_iInst][MESH_0][FEA_SOL], config[val_iZone]);
}
- solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->Compute_OFRefNode(geometry[val_iZone][val_iInst][MESH_0],solver[val_iZone][val_iInst][MESH_0], config[val_iZone]);
+ feaSolver->Compute_OFRefNode(geometry[val_iZone][val_iInst][MESH_0],
+ solver[val_iZone][val_iInst][MESH_0], config[val_iZone]);
break;
case VOLUME_FRACTION:
case TOPOL_DISCRETENESS:
- solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->Compute_OFVolFrac(geometry[val_iZone][val_iInst][MESH_0],solver[val_iZone][val_iInst][MESH_0], config[val_iZone]);
+ feaSolver->Compute_OFVolFrac(geometry[val_iZone][val_iInst][MESH_0],
+ solver[val_iZone][val_iInst][MESH_0], config[val_iZone]);
break;
case TOPOL_COMPLIANCE:
- solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->Compute_OFCompliance(geometry[val_iZone][val_iInst][MESH_0], solver[val_iZone][val_iInst][MESH_0], config[val_iZone]);
+ feaSolver->Compute_OFCompliance(geometry[val_iZone][val_iInst][MESH_0],
+ solver[val_iZone][val_iInst][MESH_0], config[val_iZone]);
break;
}
}
void CFEAIteration::Update(COutput *output,
- CIntegration ****integration,
- CGeometry ****geometry,
- CSolver *****solver,
- CNumerics ******numerics,
- CConfig **config,
- CSurfaceMovement **surface_movement,
- CVolumetricMovement ***grid_movement,
- CFreeFormDefBox*** FFDBox,
- unsigned short val_iZone,
- unsigned short val_iInst) {
+ CIntegration ****integration,
+ CGeometry ****geometry,
+ CSolver *****solver,
+ CNumerics ******numerics,
+ CConfig **config,
+ CSurfaceMovement **surface_movement,
+ CVolumetricMovement ***grid_movement,
+ CFreeFormDefBox*** FFDBox,
+ unsigned short val_iZone,
+ unsigned short val_iInst) {
su2double Physical_dt, Physical_t;
- unsigned long TimeIter = config[val_iZone]->GetTimeIter();
- bool dynamic = (config[val_iZone]->GetTime_Domain()); // Dynamic problems
- bool static_fem = (!config[val_iZone]->GetTime_Domain()); // Static problems
- bool fsi = config[val_iZone]->GetFSI_Simulation(); // Fluid-Structure Interaction problems
+ unsigned long TimeIter = config[val_iZone]->GetTimeIter();
+ bool dynamic = (config[val_iZone]->GetTime_Domain()); // Dynamic problems
+ bool fsi = config[val_iZone]->GetFSI_Simulation(); // Fluid-Structure Interaction problems
+
+ CSolver* feaSolver = solver[val_iZone][val_iInst][MESH_0][FEA_SOL];
/*----------------- Compute averaged nodal stress and reactions ------------------------*/
- solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->Compute_NodalStress(geometry[val_iZone][val_iInst][MESH_0], numerics[val_iZone][val_iInst][MESH_0][FEA_SOL], config[val_iZone]);
+ feaSolver->Compute_NodalStress(geometry[val_iZone][val_iInst][MESH_0],
+ numerics[val_iZone][val_iInst][MESH_0][FEA_SOL], config[val_iZone]);
/*----------------- Update structural solver ----------------------*/
if (dynamic) {
- integration[val_iZone][val_iInst][FEA_SOL]->SetStructural_Solver(geometry[val_iZone][val_iInst][MESH_0], solver[val_iZone][val_iInst][MESH_0], config[val_iZone], MESH_0);
+ integration[val_iZone][val_iInst][FEA_SOL]->SetStructural_Solver(geometry[val_iZone][val_iInst][MESH_0],
+ solver[val_iZone][val_iInst][MESH_0], config[val_iZone], MESH_0);
integration[val_iZone][val_iInst][FEA_SOL]->SetConvergence(false);
/*--- Verify convergence criteria (based on total time) ---*/
@@ -1621,87 +1576,84 @@ void CFEAIteration::Update(COutput *output,
if (Physical_t >= config[val_iZone]->GetTotal_DynTime())
integration[val_iZone][val_iInst][FEA_SOL]->SetConvergence(true);
- } else if ( static_fem && fsi) {
+ } else if (fsi) {
/*--- For FSI problems, output the relaxed result, which is the one transferred into the fluid domain (for restart purposes) ---*/
- switch (config[val_iZone]->GetKind_TimeIntScheme_FEA()) {
- case (NEWMARK_IMPLICIT):
- solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->ImplicitNewmark_Relaxation(geometry[val_iZone][val_iInst][MESH_0], solver[val_iZone][val_iInst][MESH_0], config[val_iZone]);
- break;
+ if (config[val_iZone]->GetKind_TimeIntScheme_FEA() == NEWMARK_IMPLICIT) {
+ feaSolver->ImplicitNewmark_Relaxation(geometry[val_iZone][val_iInst][MESH_0],
+ solver[val_iZone][val_iInst][MESH_0], config[val_iZone]);
}
}
}
void CFEAIteration::Predictor(COutput *output,
- CIntegration ****integration,
- CGeometry ****geometry,
- CSolver *****solver,
- CNumerics ******numerics,
- CConfig **config,
- CSurfaceMovement **surface_movement,
- CVolumetricMovement ***grid_movement,
- CFreeFormDefBox*** FFDBox,
- unsigned short val_iZone,
- unsigned short val_iInst) {
-
+ CIntegration ****integration,
+ CGeometry ****geometry,
+ CSolver *****solver,
+ CNumerics ******numerics,
+ CConfig **config,
+ CSurfaceMovement **surface_movement,
+ CVolumetricMovement ***grid_movement,
+ CFreeFormDefBox*** FFDBox,
+ unsigned short val_iZone,
+ unsigned short val_iInst) {
+
+ CSolver* feaSolver = solver[val_iZone][val_iInst][MESH_0][FEA_SOL];
+
/*--- Predict displacements ---*/
- solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->PredictStruct_Displacement(geometry[val_iZone][val_iInst], config[val_iZone],
- solver[val_iZone][val_iInst]);
+ feaSolver->PredictStruct_Displacement(geometry[val_iZone][val_iInst], config[val_iZone], solver[val_iZone][val_iInst]);
/*--- For parallel simulations we need to communicate the predicted solution before updating the fluid mesh ---*/
- solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->InitiateComms(geometry[val_iZone][val_iInst][MESH_0], config[val_iZone], SOLUTION_PRED);
- solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->CompleteComms(geometry[val_iZone][val_iInst][MESH_0], config[val_iZone], SOLUTION_PRED);
+ feaSolver->InitiateComms(geometry[val_iZone][val_iInst][MESH_0], config[val_iZone], SOLUTION_PRED);
+ feaSolver->CompleteComms(geometry[val_iZone][val_iInst][MESH_0], config[val_iZone], SOLUTION_PRED);
}
void CFEAIteration::Relaxation(COutput *output,
- CIntegration ****integration,
- CGeometry ****geometry,
- CSolver *****solver,
- CNumerics ******numerics,
- CConfig **config,
- CSurfaceMovement **surface_movement,
- CVolumetricMovement ***grid_movement,
- CFreeFormDefBox*** FFDBox,
- unsigned short val_iZone,
- unsigned short val_iInst) {
-
- unsigned long OuterIter = config[val_iZone]->GetOuterIter();
+ CIntegration ****integration,
+ CGeometry ****geometry,
+ CSolver *****solver,
+ CNumerics ******numerics,
+ CConfig **config,
+ CSurfaceMovement **surface_movement,
+ CVolumetricMovement ***grid_movement,
+ CFreeFormDefBox*** FFDBox,
+ unsigned short val_iZone,
+ unsigned short val_iInst) {
+
+ CSolver* feaSolver = solver[val_iZone][val_iInst][MESH_0][FEA_SOL];
/*-------------------- Aitken's relaxation ------------------------*/
/*------------------- Compute the coefficient ---------------------*/
- solver[val_iZone][INST_0][MESH_0][FEA_SOL]->ComputeAitken_Coefficient(geometry[val_iZone][INST_0], config[val_iZone],
- solver[val_iZone][INST_0], OuterIter);
+ feaSolver->ComputeAitken_Coefficient(geometry[val_iZone][INST_0], config[val_iZone],
+ solver[val_iZone][INST_0], config[val_iZone]->GetOuterIter());
/*----------------- Set the relaxation parameter ------------------*/
- solver[val_iZone][INST_0][MESH_0][FEA_SOL]->SetAitken_Relaxation(geometry[val_iZone][INST_0], config[val_iZone],
- solver[val_iZone][INST_0]);
+ feaSolver->SetAitken_Relaxation(geometry[val_iZone][INST_0], config[val_iZone], solver[val_iZone][INST_0]);
/*----------------- Communicate the predicted solution and the old one ------------------*/
- solver[val_iZone][INST_0][MESH_0][FEA_SOL]->InitiateComms(geometry[val_iZone][INST_0][MESH_0], config[val_iZone], SOLUTION_PRED_OLD);
- solver[val_iZone][INST_0][MESH_0][FEA_SOL]->CompleteComms(geometry[val_iZone][INST_0][MESH_0], config[val_iZone], SOLUTION_PRED_OLD);
+ feaSolver->InitiateComms(geometry[val_iZone][INST_0][MESH_0], config[val_iZone], SOLUTION_PRED_OLD);
+ feaSolver->CompleteComms(geometry[val_iZone][INST_0][MESH_0], config[val_iZone], SOLUTION_PRED_OLD);
}
bool CFEAIteration::Monitor(COutput *output,
- CIntegration ****integration,
- CGeometry ****geometry,
- CSolver *****solver,
- CNumerics ******numerics,
- CConfig **config,
- CSurfaceMovement **surface_movement,
- CVolumetricMovement ***grid_movement,
- CFreeFormDefBox*** FFDBox,
- unsigned short val_iZone,
- unsigned short val_iInst) {
-
- bool StopCalc = false;
+ CIntegration ****integration,
+ CGeometry ****geometry,
+ CSolver *****solver,
+ CNumerics ******numerics,
+ CConfig **config,
+ CSurfaceMovement **surface_movement,
+ CVolumetricMovement ***grid_movement,
+ CFreeFormDefBox*** FFDBox,
+ unsigned short val_iZone,
+ unsigned short val_iInst) {
#ifndef HAVE_MPI
StopTime = su2double(clock())/su2double(CLOCKS_PER_SEC);
@@ -1711,32 +1663,20 @@ bool CFEAIteration::Monitor(COutput *output,
UsedTime = StopTime - StartTime;
solver[val_iZone][val_iInst][MESH_0][FEA_SOL]->Compute_NodalStress(geometry[val_iZone][val_iInst][MESH_0],
- numerics[val_iZone][val_iInst][MESH_0][FEA_SOL], config[val_iZone]);
+ numerics[val_iZone][val_iInst][MESH_0][FEA_SOL],
+ config[val_iZone]);
if (config[val_iZone]->GetMultizone_Problem() || config[val_iZone]->GetSinglezone_Driver()){
- output->SetHistory_Output(geometry[val_iZone][INST_0][MESH_0], solver[val_iZone][INST_0][MESH_0], config[val_iZone],
- config[val_iZone]->GetTimeIter(), config[val_iZone]->GetOuterIter(), config[val_iZone]->GetInnerIter());
+ output->SetHistory_Output(geometry[val_iZone][INST_0][MESH_0], solver[val_iZone][INST_0][MESH_0],
+ config[val_iZone], config[val_iZone]->GetTimeIter(),
+ config[val_iZone]->GetOuterIter(), config[val_iZone]->GetInnerIter());
}
- StopCalc = output->GetConvergence();
-
- return StopCalc;
+ return output->GetConvergence();
}
void CFEAIteration::Postprocess(COutput *output,
- CIntegration ****integration,
- CGeometry ****geometry,
- CSolver *****solver,
- CNumerics ******numerics,
- CConfig **config,
- CSurfaceMovement **surface_movement,
- CVolumetricMovement ***grid_movement,
- CFreeFormDefBox*** FFDBox,
- unsigned short val_iZone,
- unsigned short val_iInst) { }
-
-void CFEAIteration::Solve(COutput *output,
CIntegration ****integration,
CGeometry ****geometry,
CSolver *****solver,
@@ -1746,14 +1686,24 @@ void CFEAIteration::Solve(COutput *output,
CVolumetricMovement ***grid_movement,
CFreeFormDefBox*** FFDBox,
unsigned short val_iZone,
- unsigned short val_iInst
- ) {
+ unsigned short val_iInst) { }
- /*------------------ Structural subiteration ----------------------*/
- Iterate(output, integration, geometry,
- solver, numerics, config,
- surface_movement, grid_movement, FFDBox, val_iZone, INST_0);
+void CFEAIteration::Solve(COutput *output,
+ CIntegration ****integration,
+ CGeometry ****geometry,
+ CSolver *****solver,
+ CNumerics ******numerics,
+ CConfig **config,
+ CSurfaceMovement **surface_movement,
+ CVolumetricMovement ***grid_movement,
+ CFreeFormDefBox*** FFDBox,
+ unsigned short val_iZone,
+ unsigned short val_iInst
+ ) {
+ /*------------------ Structural subiteration ----------------------*/
+ Iterate(output, integration, geometry, solver, numerics, config,
+ surface_movement, grid_movement, FFDBox, val_iZone, INST_0);
/*--- Write the convergence history for the structure (only screen output) ---*/
// if (multizone) output->SetConvHistory_Body(geometry, solver, config, integration, false, 0.0, val_iZone, INST_0);
diff --git a/SU2_CFD/src/numerics/elasticity/CFEALinearElasticity.cpp b/SU2_CFD/src/numerics/elasticity/CFEALinearElasticity.cpp
index c7e71511aaa9..7e09c1410b90 100644
--- a/SU2_CFD/src/numerics/elasticity/CFEALinearElasticity.cpp
+++ b/SU2_CFD/src/numerics/elasticity/CFEALinearElasticity.cpp
@@ -345,7 +345,7 @@ void CFEALinearElasticity::Compute_Averaged_NodalStress(CElement *element, const
CFEAMeshElasticity::CFEAMeshElasticity(unsigned short val_nDim, unsigned short val_nVar,
- unsigned long val_nElem, CConfig *config) :
+ unsigned long val_nElem, const CConfig *config) :
CFEALinearElasticity() {
DV_Val = NULL;
FAux_Dead_Load = NULL;
@@ -358,7 +358,7 @@ CFEAMeshElasticity::CFEAMeshElasticity(unsigned short val_nDim, unsigned short v
unsigned long iVar;
- E = config->GetDeform_ElasticityMod();
+ E = 1.0;
Nu = config->GetDeform_PoissonRatio();
Compute_Lame_Parameters();
diff --git a/SU2_CFD/src/solvers/CEulerSolver.cpp b/SU2_CFD/src/solvers/CEulerSolver.cpp
index addb18101fe3..b35a3faa5f60 100644
--- a/SU2_CFD/src/solvers/CEulerSolver.cpp
+++ b/SU2_CFD/src/solvers/CEulerSolver.cpp
@@ -208,6 +208,57 @@ CEulerSolver::CEulerSolver(CGeometry *geometry, CConfig *config,
LinSysSol.Initialize(nPoint, nPointDomain, nVar, 0.0);
LinSysRes.Initialize(nPoint, nPointDomain, nVar, 0.0);
+#ifdef HAVE_OMP
+ /*--- Get the edge coloring. If the expected parallel efficiency becomes too low setup the
+ * reducer strategy. Where one loop is performed over edges followed by a point loop to
+ * sum the fluxes for each cell and set the diagonal of the system matrix. ---*/
+
+ su2double parallelEff = 1.0;
+ const auto& coloring = geometry->GetEdgeColoring(¶llelEff);
+
+ /*--- The decision to use the strategy is local to each rank. ---*/
+ ReducerStrategy = parallelEff < COLORING_EFF_THRESH;
+
+ /*--- When using the reducer force a single color to reduce the color loop overhead. ---*/
+ if (ReducerStrategy && (coloring.getOuterSize()>1))
+ geometry->SetNaturalEdgeColoring();
+
+ if (!coloring.empty()) {
+ /*--- If the reducer strategy is used we are not constrained by group
+ * size as we have no other edge loops in the Euler/NS solvers. ---*/
+ auto groupSize = ReducerStrategy? 1ul : geometry->GetEdgeColorGroupSize();
+ auto nColor = coloring.getOuterSize();
+ EdgeColoring.reserve(nColor);
+
+ for(auto iColor = 0ul; iColor < nColor; ++iColor)
+ EdgeColoring.emplace_back(coloring.innerIdx(iColor), coloring.getNumNonZeros(iColor), groupSize);
+ }
+
+ /*--- If the reducer strategy is not being forced (by EDGE_COLORING_GROUP_SIZE=0) print some messages. ---*/
+ if (config->GetEdgeColoringGroupSize() != 1<<30) {
+
+ su2double minEff = 1.0;
+ SU2_MPI::Reduce(¶llelEff, &minEff, 1, MPI_DOUBLE, MPI_MIN, MASTER_NODE, MPI_COMM_WORLD);
+
+ int tmp = ReducerStrategy, numRanksUsingReducer = 0;
+ SU2_MPI::Reduce(&tmp, &numRanksUsingReducer, 1, MPI_INT, MPI_SUM, MASTER_NODE, MPI_COMM_WORLD);
+
+ if (minEff < COLORING_EFF_THRESH) {
+ cout << "WARNING: On " << numRanksUsingReducer << " MPI ranks the coloring efficiency was less than "
+ << COLORING_EFF_THRESH << " (min value was " << minEff << ").\n"
+ << " Those ranks will now use a fallback strategy, better performance may be possible\n"
+ << " with a different value of config option EDGE_COLORING_GROUP_SIZE (default 512)." << endl;
+ }
+ }
+
+ if (ReducerStrategy)
+ EdgeFluxes.Initialize(geometry->GetnEdge(), geometry->GetnEdge(), nVar, nullptr);
+
+ omp_chunk_size = computeStaticChunkSize(nPoint, omp_get_max_threads(), OMP_MAX_SIZE);
+#else
+ EdgeColoring[0] = DummyGridColor<>(geometry->GetnEdge());
+#endif
+
/*--- Jacobians and vector structures for implicit computations ---*/
if (config->GetKind_TimeIntScheme_Flow() == EULER_IMPLICIT) {
@@ -222,7 +273,7 @@ CEulerSolver::CEulerSolver(CGeometry *geometry, CConfig *config,
if (rank == MASTER_NODE)
cout << "Initialize Jacobian structure (" << description << "). MG level: " << iMesh <<"." << endl;
- Jacobian.Initialize(nPoint, nPointDomain, nVar, nVar, true, geometry, config);
+ Jacobian.Initialize(nPoint, nPointDomain, nVar, nVar, true, geometry, config, ReducerStrategy);
if (config->GetKind_Linear_Solver_Prec() == LINELET) {
nLineLets = Jacobian.BuildLineletPreconditioner(geometry, config);
@@ -420,32 +471,6 @@ CEulerSolver::CEulerSolver(CGeometry *geometry, CConfig *config,
}
SetBaseClassPointerToNodes();
-#ifdef HAVE_OMP
- /*--- Get the edge coloring, on coarse grids (which are difficult to color) setup the reducer strategy,
- * if required (i.e. in parallel). One loop is performed over edges followed by a point loop to sum
- * the fluxes for each cell and set the diagonal of the system matrix. ---*/
-
- const auto& coloring = geometry->GetEdgeColoring();
-
- if (!coloring.empty()) {
- auto nColor = coloring.getOuterSize();
- EdgeColoring.reserve(nColor);
-
- for(auto iColor = 0ul; iColor < nColor; ++iColor) {
- EdgeColoring.emplace_back(coloring.innerIdx(iColor), coloring.getNumNonZeros(iColor));
- }
- }
- ColorGroupSize = geometry->GetEdgeColorGroupSize();
-
- if ((MGLevel != MESH_0) && (omp_get_max_threads() > 1)) {
- EdgeFluxes.Initialize(geometry->GetnEdge(), geometry->GetnEdge(), nVar, nullptr);
- }
-
- omp_chunk_size = computeStaticChunkSize(nPoint, omp_get_max_threads(), OMP_MAX_SIZE);
-#else
- EdgeColoring[0] = DummyGridColor<>(geometry->GetnEdge());
-#endif
-
/*--- Check that the initial solution is physical, report any non-physical nodes ---*/
counter_local = 0;
@@ -485,7 +510,7 @@ CEulerSolver::CEulerSolver(CGeometry *geometry, CConfig *config,
SU2_MPI::Reduce(&counter_local, &counter_global, 1, MPI_UNSIGNED_LONG, MPI_SUM, MASTER_NODE, MPI_COMM_WORLD);
if ((rank == MASTER_NODE) && (counter_global != 0))
- cout << "Warning. The original solution contains "<< counter_global << " points that are not physical." << endl;
+ cout << "Warning. The original solution contains " << counter_global << " points that are not physical." << endl;
}
/*--- Initialize the BGS residuals in FSI problems. ---*/
@@ -538,6 +563,10 @@ CEulerSolver::CEulerSolver(CGeometry *geometry, CConfig *config,
/*--- Add the solver name (max 8 characters) ---*/
SolverName = "C.FLOW";
+ /*--- Finally, check that the static arrays will be large enough (keep this
+ * check at the bottom to make sure we consider the "final" values). ---*/
+ if((nDim > MAXNDIM) || (nPrimVar > MAXNVAR) || (nSecondaryVar > MAXNVAR))
+ SU2_MPI::Error("Oops! The CEulerSolver static array sizes are not large enough.",CURRENT_FUNCTION);
}
CEulerSolver::~CEulerSolver(void) {
@@ -2489,22 +2518,18 @@ void CEulerSolver::SetInitialCondition(CGeometry **geometry, CSolver ***solver_c
}
-void CEulerSolver::Preprocessing(CGeometry *geometry, CSolver **solver_container, CConfig *config, unsigned short iMesh,
- unsigned short iRKStep, unsigned short RunTime_EqSystem, bool Output) {
+void CEulerSolver::CommonPreprocessing(CGeometry *geometry, CSolver **solver_container, CConfig *config, unsigned short iMesh,
+ unsigned short iRKStep, unsigned short RunTime_EqSystem, bool Output) {
- unsigned long InnerIter = config->GetInnerIter();
bool cont_adjoint = config->GetContinuous_Adjoint();
bool disc_adjoint = config->GetDiscrete_Adjoint();
bool implicit = (config->GetKind_TimeIntScheme_Flow() == EULER_IMPLICIT);
- bool muscl = (config->GetMUSCL_Flow() || (cont_adjoint && config->GetKind_ConvNumScheme_AdjFlow() == ROE));
- bool limiter = (config->GetKind_SlopeLimit_Flow() != NO_LIMITER) && (InnerIter <= config->GetLimiterIter());
bool center = (config->GetKind_ConvNumScheme_Flow() == SPACE_CENTERED) || (cont_adjoint && config->GetKind_ConvNumScheme_AdjFlow() == SPACE_CENTERED);
- bool center_jst = center && (config->GetKind_Centered_Flow() == JST);
+ bool center_jst = (config->GetKind_Centered_Flow() == JST) && (iMesh == MESH_0);
bool engine = ((config->GetnMarker_EngineInflow() != 0) || (config->GetnMarker_EngineExhaust() != 0));
bool actuator_disk = ((config->GetnMarker_ActDiskInlet() != 0) || (config->GetnMarker_ActDiskOutlet() != 0));
bool nearfield = (config->GetnMarker_NearFieldBound() != 0);
bool fixed_cl = config->GetFixed_CL_Mode();
- bool van_albada = config->GetKind_SlopeLimit_Flow() == VAN_ALBADA_EDGE;
unsigned short kind_row_dissipation = config->GetKind_RoeLowDiss();
bool roe_low_dissipation = (kind_row_dissipation != NO_ROELOWDISS) &&
(config->GetKind_Upwind_Flow() == ROE ||
@@ -2567,33 +2592,12 @@ void CEulerSolver::Preprocessing(CGeometry *geometry, CSolver **solver_container
SU2_OMP_BARRIER
}
- /*--- Upwind second order reconstruction ---*/
-
- if ((muscl && !center) && (iMesh == MESH_0) && !Output) {
-
- /*--- Gradient computation for MUSCL reconstruction. ---*/
-
- if (config->GetKind_Gradient_Method_Recon() == GREEN_GAUSS)
- SetPrimitive_Gradient_GG(geometry, config, true);
- if (config->GetKind_Gradient_Method_Recon() == LEAST_SQUARES)
- SetPrimitive_Gradient_LS(geometry, config, true);
- if (config->GetKind_Gradient_Method_Recon() == WEIGHTED_LEAST_SQUARES)
- SetPrimitive_Gradient_LS(geometry, config, true);
-
- /*--- Limiter computation ---*/
-
- if (limiter && (iMesh == MESH_0) && !Output && !van_albada)
- SetPrimitive_Limiter(geometry, config);
- }
-
/*--- Artificial dissipation ---*/
if (center && !Output) {
SetMax_Eigenvalue(geometry, config);
- if ((center_jst) && (iMesh == MESH_0)) {
- SetCentered_Dissipation_Sensor(geometry, config);
- SetUndivided_Laplacian(geometry, config);
- }
+ if (center_jst)
+ SetUndivided_Laplacian_And_Centered_Dissipation_Sensor(geometry, config);
}
/*--- Roe Low Dissipation Sensor ---*/
@@ -2605,9 +2609,51 @@ void CEulerSolver::Preprocessing(CGeometry *geometry, CSolver **solver_container
}
}
- /*--- Initialize the Jacobian matrices ---*/
+ /*--- Initialize the Jacobian matrix and residual, not needed for the reducer strategy
+ * as we set blocks (including diagonal ones) and completely overwrite. ---*/
- if (implicit && !disc_adjoint) Jacobian.SetValZero();
+ if(!ReducerStrategy && !Output) {
+ LinSysRes.SetValZero();
+ if (implicit && !disc_adjoint) Jacobian.SetValZero();
+ else {SU2_OMP_BARRIER} // because of "nowait" in LinSysRes
+ }
+
+}
+
+void CEulerSolver::Preprocessing(CGeometry *geometry, CSolver **solver_container, CConfig *config, unsigned short iMesh,
+ unsigned short iRKStep, unsigned short RunTime_EqSystem, bool Output) {
+
+ unsigned long InnerIter = config->GetInnerIter();
+ bool cont_adjoint = config->GetContinuous_Adjoint();
+ bool muscl = (config->GetMUSCL_Flow() || (cont_adjoint && config->GetKind_ConvNumScheme_AdjFlow() == ROE));
+ bool limiter = (config->GetKind_SlopeLimit_Flow() != NO_LIMITER) && (InnerIter <= config->GetLimiterIter());
+ bool center = (config->GetKind_ConvNumScheme_Flow() == SPACE_CENTERED) || (cont_adjoint && config->GetKind_ConvNumScheme_AdjFlow() == SPACE_CENTERED);
+ bool van_albada = config->GetKind_SlopeLimit_Flow() == VAN_ALBADA_EDGE;
+
+ /*--- Common preprocessing steps. ---*/
+
+ CommonPreprocessing(geometry, solver_container, config, iMesh, iRKStep, RunTime_EqSystem, Output);
+
+ /*--- Upwind second order reconstruction ---*/
+
+ if ((muscl && !center) && (iMesh == MESH_0) && !Output) {
+
+ /*--- Gradient computation for MUSCL reconstruction. ---*/
+
+ switch (config->GetKind_Gradient_Method_Recon()) {
+ case GREEN_GAUSS:
+ SetPrimitive_Gradient_GG(geometry, config, true); break;
+ case LEAST_SQUARES:
+ case WEIGHTED_LEAST_SQUARES:
+ SetPrimitive_Gradient_LS(geometry, config, true); break;
+ default: break;
+ }
+
+ /*--- Limiter computation ---*/
+
+ if (limiter && (iMesh == MESH_0) && !Output && !van_albada)
+ SetPrimitive_Limiter(geometry, config);
+ }
}
@@ -2628,11 +2674,6 @@ unsigned long CEulerSolver::SetPrimitive_Variables(CSolver **solver_container, C
/* Check for non-realizable states for reporting. */
if (!physical) nonPhysicalPoints++;
-
- /*--- Initialize the convective, source and viscous residual vector ---*/
-
- if (!Output) LinSysRes.SetBlock_Zero(iPoint);
-
}
return nonPhysicalPoints;
@@ -2910,14 +2951,11 @@ void CEulerSolver::Centered_Residual(CGeometry *geometry, CSolver **solver_conta
/*--- Pick one numerics object per thread. ---*/
CNumerics* numerics = numerics_container[CONV_TERM + omp_get_thread_num()*MAX_TERMS];
- /*--- Determine if using the reducer strategy is necessary, see CEulerSolver::SumEdgeFluxes(). ---*/
- const bool reducer_strategy = (MGLevel != MESH_0) && (omp_get_num_threads() > 1);
-
/*--- Loop over edge colors. ---*/
for (auto color : EdgeColoring)
{
/*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/
- SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize)
+ SU2_OMP_FOR_DYN(nextMultiple(OMP_MIN_SIZE, color.groupSize))
for(auto k = 0ul; k < color.size; ++k) {
auto iEdge = color.indices[k];
@@ -2959,16 +2997,14 @@ void CEulerSolver::Centered_Residual(CGeometry *geometry, CSolver **solver_conta
/*--- Update convective and artificial dissipation residuals. ---*/
- if (reducer_strategy) {
+ if (ReducerStrategy) {
EdgeFluxes.SetBlock(iEdge, residual);
if (implicit)
- Jacobian.UpdateBlocks(iEdge, residual.jacobian_i, residual.jacobian_j);
+ Jacobian.SetBlocks(iEdge, residual.jacobian_i, residual.jacobian_j);
}
else {
LinSysRes.AddBlock(iPoint, residual);
LinSysRes.SubtractBlock(jPoint, residual);
-
- /*--- Set implicit computation ---*/
if (implicit)
Jacobian.UpdateBlocks(iEdge, iPoint, jPoint, residual.jacobian_i, residual.jacobian_j);
}
@@ -2980,7 +3016,7 @@ void CEulerSolver::Centered_Residual(CGeometry *geometry, CSolver **solver_conta
}
} // end color loop
- if (reducer_strategy) {
+ if (ReducerStrategy) {
SumEdgeFluxes(geometry);
if (implicit)
Jacobian.SetDiagonalAsColumnSum();
@@ -2991,9 +3027,6 @@ void CEulerSolver::Centered_Residual(CGeometry *geometry, CSolver **solver_conta
void CEulerSolver::Upwind_Residual(CGeometry *geometry, CSolver **solver_container,
CNumerics **numerics_container, CConfig *config, unsigned short iMesh) {
- assert(nDim <= MAXNDIM && nPrimVar <= MAXNVAR && nSecondaryVar <= MAXNVAR &&
- "Oops! The CEulerSolver static array sizes are not large enough.");
-
const auto InnerIter = config->GetInnerIter();
const bool implicit = (config->GetKind_TimeIntScheme_Flow() == EULER_IMPLICIT);
const bool ideal_gas = (config->GetKind_FluidModel() == STANDARD_AIR) ||
@@ -3020,14 +3053,11 @@ void CEulerSolver::Upwind_Residual(CGeometry *geometry, CSolver **solver_contain
su2double Primitive_i[MAXNVAR] = {0.0}, Primitive_j[MAXNVAR] = {0.0};
su2double Secondary_i[MAXNVAR] = {0.0}, Secondary_j[MAXNVAR] = {0.0};
- /*--- Determine if using the reducer strategy is necessary, see CEulerSolver::SumEdgeFluxes(). ---*/
- const bool reducer_strategy = (MGLevel != MESH_0) && (omp_get_num_threads() > 1);
-
/*--- Loop over edge colors. ---*/
for (auto color : EdgeColoring)
{
/*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/
- SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize)
+ SU2_OMP_FOR_DYN(nextMultiple(OMP_MIN_SIZE, color.groupSize))
for(auto k = 0ul; k < color.size; ++k) {
auto iEdge = color.indices[k];
@@ -3193,10 +3223,10 @@ void CEulerSolver::Upwind_Residual(CGeometry *geometry, CSolver **solver_contain
/*--- Update residual value ---*/
- if (reducer_strategy) {
+ if (ReducerStrategy) {
EdgeFluxes.SetBlock(iEdge, residual);
if (implicit)
- Jacobian.UpdateBlocks(iEdge, residual.jacobian_i, residual.jacobian_j);
+ Jacobian.SetBlocks(iEdge, residual.jacobian_i, residual.jacobian_j);
}
else {
LinSysRes.AddBlock(iPoint, residual);
@@ -3214,7 +3244,7 @@ void CEulerSolver::Upwind_Residual(CGeometry *geometry, CSolver **solver_contain
}
} // end color loop
- if (reducer_strategy) {
+ if (ReducerStrategy) {
SumEdgeFluxes(geometry);
if (implicit)
Jacobian.SetDiagonalAsColumnSum();
@@ -3245,6 +3275,8 @@ void CEulerSolver::SumEdgeFluxes(CGeometry* geometry) {
SU2_OMP_FOR_STAT(omp_chunk_size)
for (unsigned long iPoint = 0; iPoint < nPoint; ++iPoint) {
+ LinSysRes.SetBlock_Zero(iPoint);
+
for (unsigned short iNeigh = 0; iNeigh < geometry->node[iPoint]->GetnPoint(); ++iNeigh) {
auto iEdge = geometry->node[iPoint]->GetEdge(iNeigh);
@@ -3632,142 +3664,82 @@ void CEulerSolver::SetMax_Eigenvalue(CGeometry *geometry, CConfig *config) {
}
-void CEulerSolver::SetUndivided_Laplacian(CGeometry *geometry, CConfig *config) {
+void CEulerSolver::SetUndivided_Laplacian_And_Centered_Dissipation_Sensor(CGeometry *geometry, CConfig *config) {
- nodes->SetUnd_LaplZero();
+ /*--- We can access memory more efficiently if there are no periodic boundaries. ---*/
- /*--- Loop over edge colors. ---*/
- for (auto color : EdgeColoring)
- {
- /*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/
- SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize)
- for(auto k = 0ul; k < color.size; ++k) {
+ const bool isPeriodic = (config->GetnMarker_Periodic() > 0);
- auto iEdge = color.indices[k];
-
- auto iPoint = geometry->edge[iEdge]->GetNode(0);
- auto jPoint = geometry->edge[iEdge]->GetNode(1);
+ /*--- Loop domain points. ---*/
- /*--- Solution differences ---*/
+ SU2_OMP_FOR_DYN(omp_chunk_size)
+ for (unsigned long iPoint = 0; iPoint < nPointDomain; ++iPoint) {
- su2double Diff[MAXNVAR] = {0.0};
+ const bool boundary_i = geometry->node[iPoint]->GetPhysicalBoundary();
+ const su2double Pressure_i = nodes->GetPressure(iPoint);
+ /*--- Initialize. ---*/
for (unsigned short iVar = 0; iVar < nVar; iVar++)
- Diff[iVar] = nodes->GetSolution(iPoint,iVar) - nodes->GetSolution(jPoint,iVar);
-
- /*--- Correction for compressible flows which use the enthalpy ---*/
-
- Diff[nVar-1] += nodes->GetPressure(iPoint) - nodes->GetPressure(jPoint);
+ nodes->SetUnd_Lapl(iPoint, iVar, 0.0);
- bool boundary_i = geometry->node[iPoint]->GetPhysicalBoundary();
- bool boundary_j = geometry->node[jPoint]->GetPhysicalBoundary();
-
- /*--- Both points inside the domain, or both in the boundary ---*/
-
- if ((!boundary_i && !boundary_j) || (boundary_i && boundary_j)) {
- if (geometry->node[iPoint]->GetDomain()) nodes->SubtractUnd_Lapl(iPoint, Diff);
- if (geometry->node[jPoint]->GetDomain()) nodes->AddUnd_Lapl(jPoint, Diff);
- }
-
- /*--- iPoint inside the domain, jPoint on the boundary ---*/
+ iPoint_UndLapl[iPoint] = 0.0;
+ jPoint_UndLapl[iPoint] = 0.0;
- if (!boundary_i && boundary_j)
- if (geometry->node[iPoint]->GetDomain()) nodes->SubtractUnd_Lapl(iPoint, Diff);
+ /*--- Loop over the neighbors of point i. ---*/
+ for (unsigned short iNeigh = 0; iNeigh < geometry->node[iPoint]->GetnPoint(); ++iNeigh)
+ {
+ auto jPoint = geometry->node[iPoint]->GetPoint(iNeigh);
+ bool boundary_j = geometry->node[jPoint]->GetPhysicalBoundary();
- /*--- jPoint inside the domain, iPoint on the boundary ---*/
+ /*--- If iPoint is boundary it only takes contributions from other boundary points. ---*/
+ if (boundary_i && !boundary_j) continue;
- if (boundary_i && !boundary_j)
- if (geometry->node[jPoint]->GetDomain()) nodes->AddUnd_Lapl(jPoint, Diff);
+ /*--- Add solution differences, with correction for compressible flows which use the enthalpy. ---*/
- }
- } // end color loop
+ for (unsigned short iVar = 0; iVar < nVar; iVar++)
+ nodes->AddUnd_Lapl(iPoint, iVar, nodes->GetSolution(jPoint,iVar)-nodes->GetSolution(iPoint,iVar));
- SU2_OMP_MASTER
- {
- /*--- Correct the Laplacian values across any periodic boundaries. ---*/
+ su2double Pressure_j = nodes->GetPressure(jPoint);
+ nodes->AddUnd_Lapl(iPoint, nVar-1, Pressure_j-Pressure_i);
- for (unsigned short iPeriodic = 1; iPeriodic <= config->GetnMarker_Periodic()/2; iPeriodic++) {
- InitiatePeriodicComms(geometry, config, iPeriodic, PERIODIC_LAPLACIAN);
- CompletePeriodicComms(geometry, config, iPeriodic, PERIODIC_LAPLACIAN);
+ /*--- Dissipation sensor, add pressure difference and pressure sum. ---*/
+ iPoint_UndLapl[iPoint] += Pressure_j - Pressure_i;
+ jPoint_UndLapl[iPoint] += Pressure_j + Pressure_i;
}
- /*--- MPI parallelization ---*/
-
- InitiateComms(geometry, config, UNDIVIDED_LAPLACIAN);
- CompleteComms(geometry, config, UNDIVIDED_LAPLACIAN);
- }
- SU2_OMP_BARRIER
-
-}
-
-void CEulerSolver::SetCentered_Dissipation_Sensor(CGeometry *geometry, CConfig *config) {
-
- /*--- Reset variables to store the undivided pressure ---*/
-
- SU2_OMP_FOR_STAT(omp_chunk_size)
- for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++) {
- iPoint_UndLapl[iPoint] = 0.0;
- jPoint_UndLapl[iPoint] = 0.0;
+ if (!isPeriodic)
+ nodes->SetSensor(iPoint, fabs(iPoint_UndLapl[iPoint]) / jPoint_UndLapl[iPoint]);
}
- /*--- Loop over edge colors. ---*/
- for (auto color : EdgeColoring)
- {
- /*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/
- SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize)
- for(auto k = 0ul; k < color.size; ++k) {
-
- auto iEdge = color.indices[k];
-
- auto iPoint = geometry->edge[iEdge]->GetNode(0);
- auto jPoint = geometry->edge[iEdge]->GetNode(1);
-
- su2double Pressure_i = nodes->GetPressure(iPoint);
- su2double Pressure_j = nodes->GetPressure(jPoint);
+ if (isPeriodic) {
+ /*--- Correct the Laplacian and sensor values across any periodic boundaries. ---*/
- bool boundary_i = geometry->node[iPoint]->GetPhysicalBoundary();
- bool boundary_j = geometry->node[jPoint]->GetPhysicalBoundary();
-
- /*--- Both points inside the domain, or both on the boundary ---*/
+ SU2_OMP_MASTER
+ {
+ for (unsigned short iPeriodic = 1; iPeriodic <= config->GetnMarker_Periodic()/2; iPeriodic++) {
+ InitiatePeriodicComms(geometry, config, iPeriodic, PERIODIC_LAPLACIAN);
+ CompletePeriodicComms(geometry, config, iPeriodic, PERIODIC_LAPLACIAN);
- if ((!boundary_i && !boundary_j) || (boundary_i && boundary_j)) {
- if (geometry->node[iPoint]->GetDomain()) { iPoint_UndLapl[iPoint] += (Pressure_j - Pressure_i); jPoint_UndLapl[iPoint] += (Pressure_i + Pressure_j); }
- if (geometry->node[jPoint]->GetDomain()) { iPoint_UndLapl[jPoint] += (Pressure_i - Pressure_j); jPoint_UndLapl[jPoint] += (Pressure_i + Pressure_j); }
+ InitiatePeriodicComms(geometry, config, iPeriodic, PERIODIC_SENSOR);
+ CompletePeriodicComms(geometry, config, iPeriodic, PERIODIC_SENSOR);
+ }
}
+ SU2_OMP_BARRIER
- /*--- iPoint inside the domain, jPoint on the boundary ---*/
-
- if (!boundary_i && boundary_j)
- if (geometry->node[iPoint]->GetDomain()) { iPoint_UndLapl[iPoint] += (Pressure_j - Pressure_i); jPoint_UndLapl[iPoint] += (Pressure_i + Pressure_j); }
-
- /*--- jPoint inside the domain, iPoint on the boundary ---*/
+ /*--- Set final pressure switch for each point ---*/
- if (boundary_i && !boundary_j)
- if (geometry->node[jPoint]->GetDomain()) { iPoint_UndLapl[jPoint] += (Pressure_i - Pressure_j); jPoint_UndLapl[jPoint] += (Pressure_i + Pressure_j); }
+ SU2_OMP_FOR_STAT(omp_chunk_size)
+ for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++)
+ nodes->SetSensor(iPoint, fabs(iPoint_UndLapl[iPoint]) / jPoint_UndLapl[iPoint]);
}
- } // end color loop
-
- /*--- Correct the sensor values across any periodic boundaries. ---*/
SU2_OMP_MASTER
{
- for (unsigned short iPeriodic = 1; iPeriodic <= config->GetnMarker_Periodic()/2; iPeriodic++) {
- InitiatePeriodicComms(geometry, config, iPeriodic, PERIODIC_SENSOR);
- CompletePeriodicComms(geometry, config, iPeriodic, PERIODIC_SENSOR);
- }
- }
- SU2_OMP_BARRIER
-
- /*--- Set pressure switch for each point ---*/
-
- SU2_OMP_FOR_STAT(omp_chunk_size)
- for (unsigned long iPoint = 0; iPoint < nPointDomain; iPoint++)
- nodes->SetSensor(iPoint, fabs(iPoint_UndLapl[iPoint]) / jPoint_UndLapl[iPoint]);
+ /*--- MPI parallelization ---*/
- /*--- MPI parallelization ---*/
+ InitiateComms(geometry, config, UNDIVIDED_LAPLACIAN);
+ CompleteComms(geometry, config, UNDIVIDED_LAPLACIAN);
- SU2_OMP_MASTER
- {
InitiateComms(geometry, config, SENSOR);
CompleteComms(geometry, config, SENSOR);
}
diff --git a/SU2_CFD/src/solvers/CFEASolver.cpp b/SU2_CFD/src/solvers/CFEASolver.cpp
index 8f0066180225..28e338731015 100644
--- a/SU2_CFD/src/solvers/CFEASolver.cpp
+++ b/SU2_CFD/src/solvers/CFEASolver.cpp
@@ -96,15 +96,10 @@ CFEASolver::CFEASolver(bool mesh_deform_mode) : CSolver(mesh_deform_mode) {
for (unsigned short iTerm = 0; iTerm < MAX_TERMS; iTerm++)
element_container[iTerm] = new CElement* [MAX_FE_KINDS*omp_get_max_threads()]();
- nodes = nullptr;
-
- element_properties = nullptr;
-
- iElem_iDe = nullptr;
-
topol_filter_applied = false;
-
element_based = false;
+ initial_calc = true;
+
}
CFEASolver::CFEASolver(CGeometry *geometry, CConfig *config) : CSolver() {
@@ -118,8 +113,8 @@ CFEASolver::CFEASolver(CGeometry *geometry, CConfig *config) : CSolver() {
/*--- A priori we don't have an element-based input file (most of the applications will be like this) ---*/
element_based = false;
-
topol_filter_applied = false;
+ initial_calc = true;
nElement = geometry->GetnElem();
nDim = geometry->GetnDim();
@@ -240,27 +235,8 @@ CFEASolver::CFEASolver(CGeometry *geometry, CConfig *config) : CSolver() {
LinSysReact.Initialize(nPoint, nPointDomain, nVar, 0.0);
-#ifdef HAVE_OMP
- /*--- Get the element coloring. ---*/
-
- const auto& coloring = geometry->GetElementColoring();
-
- if (!coloring.empty()) {
- auto nColor = coloring.getOuterSize();
- ElemColoring.reserve(nColor);
-
- for(auto iColor = 0ul; iColor < nColor; ++iColor) {
- ElemColoring.emplace_back(coloring.innerIdx(iColor), coloring.getNumNonZeros(iColor));
- }
- }
- ColorGroupSize = geometry->GetElementColorGroupSize();
-
- omp_chunk_size = computeStaticChunkSize(nPointDomain, omp_get_max_threads(), OMP_MAX_SIZE);
-#else
- ElemColoring[0] = DummyGridColor<>(nElement);
-#endif
-
- iElem_iDe = nullptr;
+ /*--- Initialize structures for hybrid-parallel mode. ---*/
+ HybridParallelInitialization(geometry);
/*--- Initialize the value of the total objective function ---*/
Total_OFRefGeom = 0.0;
@@ -329,15 +305,65 @@ CFEASolver::~CFEASolver(void) {
delete [] element_container;
}
- if (element_properties != nullptr){
+ if (element_properties != nullptr) {
for (unsigned long iElem = 0; iElem < nElement; iElem++)
- if (element_properties[iElem] != nullptr) delete element_properties[iElem];
+ delete element_properties[iElem];
delete [] element_properties;
}
- if (iElem_iDe != nullptr) delete [] iElem_iDe;
+ delete [] iElem_iDe;
+
+ delete nodes;
+
+ if (LockStrategy) {
+ for (unsigned long iPoint = 0; iPoint < nPoint; iPoint++)
+ omp_destroy_lock(&UpdateLocks[iPoint]);
+ }
+}
+
+void CFEASolver::HybridParallelInitialization(CGeometry* geometry) {
+#ifdef HAVE_OMP
+ /*--- Get the element coloring. ---*/
+
+ su2double parallelEff = 1.0;
+ const auto& coloring = geometry->GetElementColoring(¶llelEff);
+
+ /*--- If the coloring is too bad use lock-guarded accesses
+ * to CSysMatrix/Vector in element loops instead. ---*/
+ LockStrategy = parallelEff < COLORING_EFF_THRESH;
+
+ /*--- When using locks force a single color to reduce the color loop overhead. ---*/
+ if (LockStrategy && (coloring.getOuterSize()>1))
+ geometry->SetNaturalElementColoring();
+
+ if (!coloring.empty()) {
+ /*--- We are not constrained by the color group size when using locks. ---*/
+ auto groupSize = LockStrategy? 1ul : geometry->GetElementColorGroupSize();
+ auto nColor = coloring.getOuterSize();
+ ElemColoring.reserve(nColor);
+
+ for(auto iColor = 0ul; iColor < nColor; ++iColor)
+ ElemColoring.emplace_back(coloring.innerIdx(iColor), coloring.getNumNonZeros(iColor), groupSize);
+ }
+
+ su2double minEff = 1.0;
+ SU2_MPI::Reduce(¶llelEff, &minEff, 1, MPI_DOUBLE, MPI_MIN, MASTER_NODE, MPI_COMM_WORLD);
+
+ if (minEff < COLORING_EFF_THRESH) {
+ cout << "WARNING: The element coloring efficiency was " << minEff << ", a fallback strategy is in use.\n"
+ << " Better performance may be possible by reducing the number of threads per rank." << endl;
+ }
+
+ if (LockStrategy) {
+ UpdateLocks.resize(nPoint);
+ for (unsigned long iPoint = 0; iPoint < nPoint; iPoint++)
+ omp_init_lock(&UpdateLocks[iPoint]);
+ }
- if (nodes != nullptr) delete nodes;
+ omp_chunk_size = computeStaticChunkSize(nPointDomain, omp_get_max_threads(), OMP_MAX_SIZE);
+#else
+ ElemColoring[0] = DummyGridColor<>(nElement);
+#endif
}
void CFEASolver::Set_ElementProperties(CGeometry *geometry, CConfig *config) {
@@ -726,25 +752,14 @@ void CFEASolver::Set_ReferenceGeometry(CGeometry *geometry, CConfig *config) {
void CFEASolver::Preprocessing(CGeometry *geometry, CSolver **solver_container, CConfig *config, CNumerics **numerics,
unsigned short iMesh, unsigned long Iteration, unsigned short RunTime_EqSystem, bool Output) {
- bool dynamic = config->GetTime_Domain();
- bool first_iter = (config->GetInnerIter() == 0);
-
- /*--- Initial calculation, different logic for restarted simulations. ---*/
- bool initial_calc = false;
- if (config->GetRestart())
- initial_calc = (config->GetTimeIter() == config->GetRestart_Iter()) && first_iter;
- else
- initial_calc = (config->GetTimeIter() == 0) && first_iter;
-
- bool disc_adj_fem = (config->GetKind_Solver() == DISC_ADJ_FEM);
-
- bool body_forces = config->GetDeadLoad();
-
- bool fsi = config->GetFSI_Simulation();
- bool consistent_interpolation = (!config->GetConservativeInterpolation() ||
- (config->GetKindInterpolation() == WEIGHTED_AVERAGE));
-
- bool topology_mode = config->GetTopology_Optimization();
+ const bool dynamic = config->GetTime_Domain();
+ const bool first_iter = (config->GetInnerIter() == 0);
+ const bool disc_adj_fem = (config->GetKind_Solver() == DISC_ADJ_FEM);
+ const bool body_forces = config->GetDeadLoad();
+ const bool fsi = config->GetFSI_Simulation();
+ const bool consistent_interpolation = (!config->GetConservativeInterpolation() ||
+ (config->GetKindInterpolation() == WEIGHTED_AVERAGE));
+ const bool topology_mode = config->GetTopology_Optimization();
/*
* For topology optimization we apply a filter on the design density field to avoid
@@ -777,7 +792,7 @@ void CFEASolver::Preprocessing(CGeometry *geometry, CSolver **solver_container,
*
* Only initialized once, at the first iteration of the first time step.
*/
- if (body_forces && initial_calc)
+ if (body_forces && (initial_calc || disc_adj_fem))
Compute_DeadLoad(geometry, numerics, config);
/*--- Clear the linear system solution. ---*/
@@ -794,6 +809,9 @@ void CFEASolver::Preprocessing(CGeometry *geometry, CSolver **solver_container,
*/
if (fsi && first_iter && consistent_interpolation) Integrate_FSI_Loads(geometry,config);
+ /*--- Next call to Preprocessing will not be "initial_calc" and linear operations will not be repeated. ---*/
+ initial_calc = false;
+
}
void CFEASolver::SetInitialCondition(CGeometry **geometry, CSolver ***solver_container, CConfig *config, unsigned long TimeIter) {
@@ -829,7 +847,7 @@ void CFEASolver::Compute_StiffMatrix(CGeometry *geometry, CNumerics **numerics,
for(auto color : ElemColoring) {
/*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/
- SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize)
+ SU2_OMP_FOR_DYN(nextMultiple(OMP_MIN_SIZE, color.groupSize))
for(auto k = 0ul; k < color.size; ++k) {
auto iElem = color.indices[k];
@@ -879,6 +897,8 @@ void CFEASolver::Compute_StiffMatrix(CGeometry *geometry, CNumerics **numerics,
/*--- Update residual and stiffness matrix with contributions from the element. ---*/
for (iNode = 0; iNode < nNodes; iNode++) {
+ if (LockStrategy) omp_set_lock(&UpdateLocks[indexNode[iNode]]);
+
auto Ta = element->Get_Kt_a(iNode);
for (iVar = 0; iVar < nVar; iVar++)
LinSysRes(indexNode[iNode], iVar) -= simp_penalty*Ta[iVar];
@@ -887,6 +907,8 @@ void CFEASolver::Compute_StiffMatrix(CGeometry *geometry, CNumerics **numerics,
auto Kab = element->Get_Kab(iNode, jNode);
Jacobian.AddBlock(indexNode[iNode], indexNode[jNode], simp_penalty, Kab);
}
+
+ if (LockStrategy) omp_unset_lock(&UpdateLocks[indexNode[iNode]]);
}
} // end iElem loop
@@ -917,7 +939,7 @@ void CFEASolver::Compute_StiffMatrix_NodalStressRes(CGeometry *geometry, CNumeri
for(auto color : ElemColoring) {
/*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/
- SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize)
+ SU2_OMP_FOR_DYN(nextMultiple(OMP_MIN_SIZE, color.groupSize))
for(auto k = 0ul; k < color.size; ++k) {
auto iElem = color.indices[k];
@@ -986,6 +1008,8 @@ void CFEASolver::Compute_StiffMatrix_NodalStressRes(CGeometry *geometry, CNumeri
/*--- Update residual and stiffness matrix with contributions from the element. ---*/
for (iNode = 0; iNode < nNodes; iNode++) {
+ if (LockStrategy) omp_set_lock(&UpdateLocks[indexNode[iNode]]);
+
auto Ta = fea_elem->Get_Kt_a(iNode);
for (iVar = 0; iVar < nVar; iVar++)
LinSysRes(indexNode[iNode], iVar) -= simp_penalty*Ta[iVar];
@@ -1023,6 +1047,8 @@ void CFEASolver::Compute_StiffMatrix_NodalStressRes(CGeometry *geometry, CNumeri
Kij[iVar*(nVar+1)] += SU2_TYPE::GetValue(simp_penalty*Ks_ab_DE);
}
}
+
+ if (LockStrategy) omp_unset_lock(&UpdateLocks[indexNode[iNode]]);
}
} // end iElem loop
@@ -1048,7 +1074,7 @@ void CFEASolver::Compute_MassMatrix(CGeometry *geometry, CNumerics **numerics, C
for(auto color : ElemColoring) {
/*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/
- SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize)
+ SU2_OMP_FOR_DYN(nextMultiple(OMP_MIN_SIZE, color.groupSize))
for(auto k = 0ul; k < color.size; ++k) {
auto iElem = color.indices[k];
@@ -1091,6 +1117,8 @@ void CFEASolver::Compute_MassMatrix(CGeometry *geometry, CNumerics **numerics, C
/*--- Add contributions of this element to the mass matrix. ---*/
for (iNode = 0; iNode < nNodes; iNode++) {
+ if (LockStrategy) omp_set_lock(&UpdateLocks[indexNode[iNode]]);
+
for (jNode = 0; jNode < nNodes; jNode++) {
auto Mij = MassMatrix.GetBlock(indexNode[iNode], indexNode[jNode]);
@@ -1099,6 +1127,8 @@ void CFEASolver::Compute_MassMatrix(CGeometry *geometry, CNumerics **numerics, C
for (iVar = 0; iVar < nVar; iVar++)
Mij[iVar*(nVar+1)] += simp_penalty*Mab;
}
+
+ if (LockStrategy) omp_unset_lock(&UpdateLocks[indexNode[iNode]]);
}
} // end iElem loop
@@ -1125,7 +1155,7 @@ void CFEASolver::Compute_MassRes(CGeometry *geometry, CNumerics **numerics, CCon
for(auto color : ElemColoring) {
/*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/
- SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize)
+ SU2_OMP_FOR_DYN(nextMultiple(OMP_MIN_SIZE, color.groupSize))
for(auto k = 0ul; k < color.size; ++k) {
auto iElem = color.indices[k];
@@ -1165,7 +1195,10 @@ void CFEASolver::Compute_MassRes(CGeometry *geometry, CNumerics **numerics, CCon
numerics[FEA_TERM + thread*MAX_TERMS]->Compute_Mass_Matrix(element, config);
- /*--- Add contributions of this element to the mass matrix. ---*/
+ /*--- Add contributions of this element to the mass matrix.
+ * In case we need to use locks we guard the entire update. ---*/
+ if (LockStrategy) omp_set_lock(&UpdateLocks[0]);
+
for (iNode = 0; iNode < nNodes; iNode++) {
for (jNode = 0; jNode < nNodes; jNode++) {
@@ -1177,6 +1210,7 @@ void CFEASolver::Compute_MassRes(CGeometry *geometry, CNumerics **numerics, CCon
}
}
}
+ if (LockStrategy) omp_unset_lock(&UpdateLocks[0]);
} // end iElem loop
@@ -1205,7 +1239,7 @@ void CFEASolver::Compute_NodalStressRes(CGeometry *geometry, CNumerics **numeric
for(auto color : ElemColoring) {
/*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/
- SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize)
+ SU2_OMP_FOR_DYN(nextMultiple(OMP_MIN_SIZE, color.groupSize))
for(auto k = 0ul; k < color.size; ++k) {
auto iElem = color.indices[k];
@@ -1260,9 +1294,13 @@ void CFEASolver::Compute_NodalStressRes(CGeometry *geometry, CNumerics **numeric
numerics[NUM_TERM]->Compute_NodalStress_Term(element, config);
for (iNode = 0; iNode < nNodes; iNode++) {
+ if (LockStrategy) omp_set_lock(&UpdateLocks[indexNode[iNode]]);
+
auto Ta = element->Get_Kt_a(iNode);
for (iVar = 0; iVar < nVar; iVar++)
LinSysRes(indexNode[iNode], iVar) -= simp_penalty*Ta[iVar];
+
+ if (LockStrategy) omp_unset_lock(&UpdateLocks[indexNode[iNode]]);
}
} // end iElem loop
@@ -1302,7 +1340,7 @@ void CFEASolver::Compute_NodalStress(CGeometry *geometry, CNumerics **numerics,
for(auto color : ElemColoring) {
/*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/
- SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize)
+ SU2_OMP_FOR_DYN(nextMultiple(OMP_MIN_SIZE, color.groupSize))
for(auto k = 0ul; k < color.size; ++k) {
auto iElem = color.indices[k];
@@ -1359,6 +1397,8 @@ void CFEASolver::Compute_NodalStress(CGeometry *geometry, CNumerics **numerics,
auto iPoint = indexNode[iNode];
+ if (LockStrategy) omp_set_lock(&UpdateLocks[iPoint]);
+
auto Ta = element->Get_Kt_a(iNode);
for (iVar = 0; iVar < nVar; iVar++)
LinSysReact(iPoint,iVar) += simp_penalty*Ta[iVar];
@@ -1368,6 +1408,8 @@ void CFEASolver::Compute_NodalStress(CGeometry *geometry, CNumerics **numerics,
for (iStress = 0; iStress < nStress; iStress++)
nodes->AddStress_FEM(iPoint,iStress, weight*element->Get_NodalStress(iNode,iStress));
+
+ if (LockStrategy) omp_unset_lock(&UpdateLocks[iPoint]);
}
} // end iElem loop
@@ -1564,7 +1606,7 @@ void CFEASolver::Compute_DeadLoad(CGeometry *geometry, CNumerics **numerics, CCo
for(auto color : ElemColoring) {
/*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/
- SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize)
+ SU2_OMP_FOR_DYN(nextMultiple(OMP_MIN_SIZE, color.groupSize))
for(auto k = 0ul; k < color.size; ++k) {
auto iElem = color.indices[k];
@@ -1604,6 +1646,8 @@ void CFEASolver::Compute_DeadLoad(CGeometry *geometry, CNumerics **numerics, CCo
/*--- Add contributions of this element to the mass matrix. ---*/
for (iNode = 0; iNode < nNodes; iNode++) {
+ if (LockStrategy) omp_set_lock(&UpdateLocks[indexNode[iNode]]);
+
auto Dead_Load = element->Get_FDL_a(iNode);
su2double Aux_Dead_Load[MAXNVAR];
@@ -1611,6 +1655,8 @@ void CFEASolver::Compute_DeadLoad(CGeometry *geometry, CNumerics **numerics, CCo
Aux_Dead_Load[iVar] = simp_penalty*Dead_Load[iVar];
nodes->Add_BodyForces_Res(indexNode[iNode], Aux_Dead_Load);
+
+ if (LockStrategy) omp_unset_lock(&UpdateLocks[indexNode[iNode]]);
}
} // end iElem loop
diff --git a/SU2_CFD/src/solvers/CMeshSolver.cpp b/SU2_CFD/src/solvers/CMeshSolver.cpp
index c0ba9e1abd0b..25e4b711fc9f 100644
--- a/SU2_CFD/src/solvers/CMeshSolver.cpp
+++ b/SU2_CFD/src/solvers/CMeshSolver.cpp
@@ -96,39 +96,17 @@ CMeshSolver::CMeshSolver(CGeometry *geometry, CConfig *config) : CFEASolver(true
/*--- Initialize matrix, solution, and r.h.s. structures for the linear solver. ---*/
+ if (rank == MASTER_NODE) cout << "Initialize Jacobian structure (Mesh Deformation)." << endl;
+
LinSysSol.Initialize(nPoint, nPointDomain, nVar, 0.0);
LinSysRes.Initialize(nPoint, nPointDomain, nVar, 0.0);
Jacobian.Initialize(nPoint, nPointDomain, nVar, nVar, false, geometry, config);
-#ifdef HAVE_OMP
- /*--- Get the element coloring. ---*/
-
- const auto& coloring = geometry->GetElementColoring();
-
- if (!coloring.empty()) {
- auto nColor = coloring.getOuterSize();
- ElemColoring.reserve(nColor);
-
- for(auto iColor = 0ul; iColor < nColor; ++iColor) {
- ElemColoring.emplace_back(coloring.innerIdx(iColor), coloring.getNumNonZeros(iColor));
- }
- }
- ColorGroupSize = geometry->GetElementColorGroupSize();
+ /*--- Initialize structures for hybrid-parallel mode. ---*/
- omp_chunk_size = computeStaticChunkSize(nPointDomain, omp_get_max_threads(), OMP_MAX_SIZE);
-#else
- ElemColoring[0] = DummyGridColor<>(nElement);
-#endif
+ HybridParallelInitialization(geometry);
- /*--- Structural parameters ---*/
-
- E = config->GetDeform_ElasticityMod();
- Nu = config->GetDeform_PoissonRatio();
-
- Mu = E / (2.0*(1.0 + Nu));
- Lambda = Nu*E/((1.0+Nu)*(1.0-2.0*Nu));
-
- /*--- Element container structure ---*/
+ /*--- Element container structure. ---*/
if (nDim == 2) {
for(int thread = 0; thread < omp_get_max_threads(); ++thread) {
@@ -410,23 +388,37 @@ void CMeshSolver::SetWallDistance(CGeometry *geometry, CConfig *config) {
void CMeshSolver::SetMesh_Stiffness(CGeometry **geometry, CNumerics **numerics, CConfig *config){
- unsigned long iElem;
+ /*--- Use the config option as an upper bound on elasticity modulus.
+ * For RANS meshes the range of element volume or wall distance is
+ * very large and leads to an ill-conditioned stiffness matrix.
+ * Absolute values of elasticity modulus are not important for
+ * mesh deformation, since linear elasticity is used and all
+ * boundary conditions are essential (Dirichlet). ---*/
+ const su2double maxE = config->GetDeform_ElasticityMod();
if (!stiffness_set) {
- for (iElem = 0; iElem < nElement; iElem++) {
+ /*--- All threads must execute the entire loop (no worksharing),
+ * each sets the stiffnesses for its numerics instance. ---*/
+ SU2_OMP_PARALLEL
+ {
+ CNumerics* myNumerics = numerics[FEA_TERM + omp_get_thread_num()*MAX_TERMS];
+
+ for (unsigned long iElem = 0; iElem < nElement; iElem++) {
+
+ su2double E = 1.0;
switch (config->GetDeform_Stiffness_Type()) {
- /*--- Stiffness inverse of the volume of the element ---*/
- case INVERSE_VOLUME: E = 1.0 / element[iElem].GetRef_Volume(); break;
- /*--- Stiffness inverse of the distance of the element to the closest wall ---*/
- case SOLID_WALL_DISTANCE: E = 1.0 / element[iElem].GetWallDistance(); break;
+ /*--- Stiffness inverse of the volume of the element ---*/
+ case INVERSE_VOLUME: E = 1.0 / element[iElem].GetRef_Volume(); break;
+
+ /*--- Stiffness inverse of the distance of the element to the closest wall ---*/
+ case SOLID_WALL_DISTANCE: E = 1.0 / element[iElem].GetWallDistance(); break;
}
/*--- Set the element elastic properties in the numerics container ---*/
- numerics[FEA_TERM]->SetMeshElasticProperties(iElem, E);
-
+ myNumerics->SetMeshElasticProperties(iElem, min(E,maxE));
+ }
}
-
stiffness_set = true;
}
@@ -436,15 +428,15 @@ void CMeshSolver::DeformMesh(CGeometry **geometry, CNumerics **numerics, CConfig
if (multizone) nodes->Set_BGSSolution_k();
- /*--- Initialize sparse matrix ---*/
- Jacobian.SetValZero();
-
/*--- Compute the stiffness matrix. ---*/
Compute_StiffMatrix(geometry[MESH_0], numerics, config);
- /*--- Initialize vectors and clean residual ---*/
- LinSysSol.SetValZero();
- LinSysRes.SetValZero();
+ /*--- Initialize vectors and clean residual. ---*/
+ SU2_OMP_PARALLEL
+ {
+ LinSysSol.SetValZero();
+ LinSysRes.SetValZero();
+ }
/*--- LinSysSol contains the non-transformed displacements in the periodic halo cells.
Hence we still need a communication of the transformed coordinates, otherwise periodicity
diff --git a/SU2_CFD/src/solvers/CNSSolver.cpp b/SU2_CFD/src/solvers/CNSSolver.cpp
index 2484ce2ed0b7..3b22cc0c292a 100644
--- a/SU2_CFD/src/solvers/CNSSolver.cpp
+++ b/SU2_CFD/src/solvers/CNSSolver.cpp
@@ -171,108 +171,27 @@ void CNSSolver::Preprocessing(CGeometry *geometry, CSolver **solver_container, C
unsigned long InnerIter = config->GetInnerIter();
bool cont_adjoint = config->GetContinuous_Adjoint();
- bool disc_adjoint = config->GetDiscrete_Adjoint();
- bool implicit = (config->GetKind_TimeIntScheme_Flow() == EULER_IMPLICIT);
- bool center = (config->GetKind_ConvNumScheme_Flow() == SPACE_CENTERED) || (cont_adjoint && config->GetKind_ConvNumScheme_AdjFlow() == SPACE_CENTERED);
- bool center_jst = center && config->GetKind_Centered_Flow() == JST;
bool limiter_flow = (config->GetKind_SlopeLimit_Flow() != NO_LIMITER) && (InnerIter <= config->GetLimiterIter());
bool limiter_turb = (config->GetKind_SlopeLimit_Turb() != NO_LIMITER) && (InnerIter <= config->GetLimiterIter());
bool limiter_adjflow = (cont_adjoint && (config->GetKind_SlopeLimit_AdjFlow() != NO_LIMITER) && (InnerIter <= config->GetLimiterIter()));
- bool fixed_cl = config->GetFixed_CL_Mode();
- bool engine = ((config->GetnMarker_EngineInflow() != 0) || (config->GetnMarker_EngineExhaust() != 0));
- bool actuator_disk = ((config->GetnMarker_ActDiskInlet() != 0) || (config->GetnMarker_ActDiskOutlet() != 0));
- bool nearfield = (config->GetnMarker_NearFieldBound() != 0);
bool van_albada = config->GetKind_SlopeLimit_Flow() == VAN_ALBADA_EDGE;
- unsigned short kind_row_dissipation = config->GetKind_RoeLowDiss();
- bool roe_low_dissipation = (kind_row_dissipation != NO_ROELOWDISS) &&
- (config->GetKind_Upwind_Flow() == ROE ||
- config->GetKind_Upwind_Flow() == SLAU ||
- config->GetKind_Upwind_Flow() == SLAU2);
bool wall_functions = config->GetWall_Functions();
- /*--- Update the angle of attack at the far-field for fixed CL calculations (only direct problem). ---*/
+ /*--- Common preprocessing steps (implemented by CEulerSolver) ---*/
- if (fixed_cl && !disc_adjoint && !cont_adjoint) {
- SU2_OMP_MASTER
- SetFarfield_AoA(geometry, solver_container, config, iMesh, Output);
- SU2_OMP_BARRIER
- }
-
- /*--- Set the primitive variables ---*/
-
- SU2_OMP_MASTER
- ErrorCounter = 0;
- SU2_OMP_BARRIER
-
- SU2_OMP_ATOMIC
- ErrorCounter += SetPrimitive_Variables(solver_container, config, Output);
-
- if ((iMesh == MESH_0) && (config->GetComm_Level() == COMM_FULL)) {
- SU2_OMP_BARRIER
- SU2_OMP_MASTER
- {
- unsigned long tmp = ErrorCounter;
- SU2_MPI::Allreduce(&tmp, &ErrorCounter, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD);
- config->SetNonphysical_Points(ErrorCounter);
- }
- SU2_OMP_BARRIER
- }
-
- /*--- Compute the engine properties ---*/
-
- if (engine) {
- SU2_OMP_MASTER
- GetPower_Properties(geometry, config, iMesh, Output);
- SU2_OMP_BARRIER
- }
-
- /*--- Compute the actuator disk properties and distortion levels ---*/
-
- if (actuator_disk) {
- SU2_OMP_MASTER
- {
- Set_MPI_ActDisk(solver_container, geometry, config);
- SetActDisk_BCThrust(geometry, solver_container, config, iMesh, Output);
- }
- SU2_OMP_BARRIER
- }
-
- /*--- Compute NearField MPI ---*/
-
- if (nearfield) {
- SU2_OMP_MASTER
- Set_MPI_Nearfield(geometry, config);
- SU2_OMP_BARRIER
- }
-
- /*--- Artificial dissipation ---*/
-
- if (center && !Output) {
- SetMax_Eigenvalue(geometry, config);
- if ((center_jst) && (iMesh == MESH_0)) {
- SetCentered_Dissipation_Sensor(geometry, config);
- SetUndivided_Laplacian(geometry, config);
- }
- }
-
- /*--- Roe Low Dissipation Sensor ---*/
-
- if (roe_low_dissipation){
- SetRoe_Dissipation(geometry, config);
- if (kind_row_dissipation == FD_DUCROS || kind_row_dissipation == NTS_DUCROS){
- SetUpwind_Ducros_Sensor(geometry, config);
- }
- }
+ CommonPreprocessing(geometry, solver_container, config, iMesh, iRKStep, RunTime_EqSystem, Output);
/*--- Compute gradient for MUSCL reconstruction. ---*/
if (config->GetReconstructionGradientRequired() && (iMesh == MESH_0)) {
- if (config->GetKind_Gradient_Method_Recon() == GREEN_GAUSS)
- SetPrimitive_Gradient_GG(geometry, config, true);
- if (config->GetKind_Gradient_Method_Recon() == LEAST_SQUARES)
- SetPrimitive_Gradient_LS(geometry, config, true);
- if (config->GetKind_Gradient_Method_Recon() == WEIGHTED_LEAST_SQUARES)
- SetPrimitive_Gradient_LS(geometry, config, true);
+ switch (config->GetKind_Gradient_Method_Recon()) {
+ case GREEN_GAUSS:
+ SetPrimitive_Gradient_GG(geometry, config, true); break;
+ case LEAST_SQUARES:
+ case WEIGHTED_LEAST_SQUARES:
+ SetPrimitive_Gradient_LS(geometry, config, true); break;
+ default: break;
+ }
}
/*--- Compute gradient of the primitive variables ---*/
@@ -280,12 +199,12 @@ void CNSSolver::Preprocessing(CGeometry *geometry, CSolver **solver_container, C
if (config->GetKind_Gradient_Method() == GREEN_GAUSS) {
SetPrimitive_Gradient_GG(geometry, config);
}
- if (config->GetKind_Gradient_Method() == WEIGHTED_LEAST_SQUARES) {
+ else if (config->GetKind_Gradient_Method() == WEIGHTED_LEAST_SQUARES) {
SetPrimitive_Gradient_LS(geometry, config);
}
- /*--- Compute the limiter in case we need it in the turbulence model
- or to limit the viscous terms (check this logic with JST and 2nd order turbulence model) ---*/
+ /*--- Compute the limiter in case we need it in the turbulence model or to limit the
+ * viscous terms (check this logic with JST and 2nd order turbulence model) ---*/
if ((iMesh == MESH_0) && (limiter_flow || limiter_turb || limiter_adjflow) && !Output && !van_albada) {
SetPrimitive_Limiter(geometry, config);
@@ -342,10 +261,6 @@ void CNSSolver::Preprocessing(CGeometry *geometry, CSolver **solver_container, C
SU2_OMP_BARRIER
}
- /*--- Initialize the Jacobian matrices ---*/
-
- if (implicit && !config->GetDiscrete_Adjoint()) Jacobian.SetValZero();
-
}
unsigned long CNSSolver::SetPrimitive_Variables(CSolver **solver_container, CConfig *config, bool Output) {
@@ -383,10 +298,6 @@ unsigned long CNSSolver::SetPrimitive_Variables(CSolver **solver_container, CCon
nonPhysicalPoints += !physical;
- /*--- Initialize the convective, source and viscous residual vector ---*/
-
- if (!Output) LinSysRes.SetBlock_Zero(iPoint);
-
}
return nonPhysicalPoints;
@@ -440,7 +351,7 @@ void CNSSolver::Viscous_Residual(unsigned long iEdge, CGeometry *geometry, CSolv
auto residual = numerics->ComputeResidual(config);
- if ((MGLevel != MESH_0) && (omp_get_num_threads() > 1)) {
+ if (ReducerStrategy) {
EdgeFluxes.SubtractBlock(iEdge, residual);
if (implicit)
Jacobian.UpdateBlocksSub(iEdge, residual.jacobian_i, residual.jacobian_j);
diff --git a/SU2_CFD/src/solvers/CSolver.cpp b/SU2_CFD/src/solvers/CSolver.cpp
index a4735cec3c17..112a912fddcb 100644
--- a/SU2_CFD/src/solvers/CSolver.cpp
+++ b/SU2_CFD/src/solvers/CSolver.cpp
@@ -2219,7 +2219,7 @@ void CSolver::CompleteComms(CGeometry *geometry,
break;
case UNDIVIDED_LAPLACIAN:
for (iVar = 0; iVar < nVar; iVar++)
- base_nodes->SetUndivided_Laplacian(iPoint, iVar, bufDRecv[buf_offset+iVar]);
+ base_nodes->SetUnd_Lapl(iPoint, iVar, bufDRecv[buf_offset+iVar]);
break;
case SOLUTION_LIMITER:
for (iVar = 0; iVar < nVar; iVar++)
diff --git a/SU2_CFD/src/solvers/CTurbSASolver.cpp b/SU2_CFD/src/solvers/CTurbSASolver.cpp
index 284edf7ad6ee..3948a6e182b0 100644
--- a/SU2_CFD/src/solvers/CTurbSASolver.cpp
+++ b/SU2_CFD/src/solvers/CTurbSASolver.cpp
@@ -96,7 +96,7 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor
/*--- Initialization of the structure of the whole Jacobian ---*/
if (rank == MASTER_NODE) cout << "Initialize Jacobian structure (SA model)." << endl;
- Jacobian.Initialize(nPoint, nPointDomain, nVar, nVar, true, geometry, config);
+ Jacobian.Initialize(nPoint, nPointDomain, nVar, nVar, true, geometry, config, ReducerStrategy);
if (config->GetKind_Linear_Solver_Prec() == LINELET) {
nLineLets = Jacobian.BuildLineletPreconditioner(geometry, config);
@@ -106,6 +106,9 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor
LinSysSol.Initialize(nPoint, nPointDomain, nVar, 0.0);
LinSysRes.Initialize(nPoint, nPointDomain, nVar, 0.0);
+ if (ReducerStrategy)
+ EdgeFluxes.Initialize(geometry->GetnEdge(), geometry->GetnEdge(), nVar, nullptr);
+
if (config->GetExtraOutput()) {
if (nDim == 2) { nOutputVariables = 13; }
else if (nDim == 3) { nOutputVariables = 19; }
@@ -222,9 +225,9 @@ CTurbSASolver::CTurbSASolver(CGeometry *geometry, CConfig *config, unsigned shor
SetImplicitPeriodic(true);
- /* Store the initial CFL number for all grid points. */
+ /*--- Store the initial CFL number for all grid points. ---*/
- const su2double CFL = config->GetCFL(MGLevel);
+ const su2double CFL = config->GetCFL(MGLevel)*config->GetCFLRedCoeff_Turb();
for (iPoint = 0; iPoint < nPoint; iPoint++) {
nodes->SetLocalCFL(iPoint, CFL);
}
@@ -277,9 +280,12 @@ void CTurbSASolver::Preprocessing(CGeometry *geometry, CSolver **solver_containe
const su2double* Vorticity = nullptr;
su2double Laminar_Viscosity = 0.0;
- /*--- Clear residual and system matrix. ---*/
- LinSysRes.SetValZero();
- Jacobian.SetValZero();
+ /*--- Clear residual and system matrix, not needed for
+ * reducer strategy as we write over the entire matrix. ---*/
+ if (!ReducerStrategy) {
+ LinSysRes.SetValZero();
+ Jacobian.SetValZero();
+ }
/*--- Upwind second order reconstruction and gradients ---*/
diff --git a/SU2_CFD/src/solvers/CTurbSSTSolver.cpp b/SU2_CFD/src/solvers/CTurbSSTSolver.cpp
index 148462ef1f77..96cab040c155 100644
--- a/SU2_CFD/src/solvers/CTurbSSTSolver.cpp
+++ b/SU2_CFD/src/solvers/CTurbSSTSolver.cpp
@@ -98,7 +98,7 @@ CTurbSSTSolver::CTurbSSTSolver(CGeometry *geometry, CConfig *config, unsigned sh
/*--- Initialization of the structure of the whole Jacobian ---*/
if (rank == MASTER_NODE) cout << "Initialize Jacobian structure (SST model)." << endl;
- Jacobian.Initialize(nPoint, nPointDomain, nVar, nVar, true, geometry, config);
+ Jacobian.Initialize(nPoint, nPointDomain, nVar, nVar, true, geometry, config, ReducerStrategy);
if (config->GetKind_Linear_Solver_Prec() == LINELET) {
nLineLets = Jacobian.BuildLineletPreconditioner(geometry, config);
@@ -108,6 +108,9 @@ CTurbSSTSolver::CTurbSSTSolver(CGeometry *geometry, CConfig *config, unsigned sh
LinSysSol.Initialize(nPoint, nPointDomain, nVar, 0.0);
LinSysRes.Initialize(nPoint, nPointDomain, nVar, 0.0);
+ if (ReducerStrategy)
+ EdgeFluxes.Initialize(geometry->GetnEdge(), geometry->GetnEdge(), nVar, nullptr);
+
/*--- Initialize the BGS residuals in multizone problems. ---*/
if (multizone){
Residual_BGS = new su2double[nVar]();
@@ -223,9 +226,9 @@ CTurbSSTSolver::CTurbSSTSolver(CGeometry *geometry, CConfig *config, unsigned sh
SetImplicitPeriodic(true);
- /* Store the initial CFL number for all grid points. */
+ /*--- Store the initial CFL number for all grid points. ---*/
- const su2double CFL = config->GetCFL(MGLevel);
+ const su2double CFL = config->GetCFL(MGLevel)*config->GetCFLRedCoeff_Turb();
for (iPoint = 0; iPoint < nPoint; iPoint++) {
nodes->SetLocalCFL(iPoint, CFL);
}
@@ -274,9 +277,12 @@ void CTurbSSTSolver::Preprocessing(CGeometry *geometry, CSolver **solver_contain
const bool limiter_turb = (config->GetKind_SlopeLimit_Turb() != NO_LIMITER) &&
(config->GetInnerIter() <= config->GetLimiterIter());
- /*--- Clear residual and system matrix. ---*/
- LinSysRes.SetValZero();
- Jacobian.SetValZero();
+ /*--- Clear residual and system matrix, not needed for
+ * reducer strategy as we write over the entire matrix. ---*/
+ if (!ReducerStrategy) {
+ LinSysRes.SetValZero();
+ Jacobian.SetValZero();
+ }
/*--- Upwind second order reconstruction and gradients ---*/
diff --git a/SU2_CFD/src/solvers/CTurbSolver.cpp b/SU2_CFD/src/solvers/CTurbSolver.cpp
index ca1024b209c3..661a7fa2b856 100644
--- a/SU2_CFD/src/solvers/CTurbSolver.cpp
+++ b/SU2_CFD/src/solvers/CTurbSolver.cpp
@@ -48,19 +48,23 @@ CTurbSolver::CTurbSolver(CGeometry* geometry, CConfig *config) : CSolver() {
dynamic_grid = config->GetDynamic_Grid();
#ifdef HAVE_OMP
- /*--- Get the edge coloring. ---*/
+ /*--- Get the edge coloring, see notes in CEulerSolver's constructor. ---*/
+ su2double parallelEff = 1.0;
+ const auto& coloring = geometry->GetEdgeColoring(¶llelEff);
- const auto& coloring = geometry->GetEdgeColoring();
+ ReducerStrategy = parallelEff < COLORING_EFF_THRESH;
+
+ if (ReducerStrategy && (coloring.getOuterSize()>1))
+ geometry->SetNaturalEdgeColoring();
if (!coloring.empty()) {
+ auto groupSize = ReducerStrategy? 1ul : geometry->GetEdgeColorGroupSize();
auto nColor = coloring.getOuterSize();
- EdgeColoring.resize(nColor);
+ EdgeColoring.reserve(nColor);
- for(auto iColor = 0ul; iColor < nColor; ++iColor) {
- EdgeColoring.emplace_back(coloring.innerIdx(iColor), coloring.getNumNonZeros(iColor));
- }
+ for(auto iColor = 0ul; iColor < nColor; ++iColor)
+ EdgeColoring.emplace_back(coloring.innerIdx(iColor), coloring.getNumNonZeros(iColor), groupSize);
}
- ColorGroupSize = geometry->GetEdgeColorGroupSize();
nPoint = geometry->GetnPoint();
omp_chunk_size = computeStaticChunkSize(nPoint, omp_get_max_threads(), OMP_MAX_SIZE);
@@ -92,6 +96,13 @@ void CTurbSolver::Upwind_Residual(CGeometry *geometry, CSolver **solver_containe
const bool muscl = config->GetMUSCL_Turb();
const bool limiter = (config->GetKind_SlopeLimit_Turb() != NO_LIMITER);
+ /*--- Only reconstruct flow variables if MUSCL is on for flow (requires upwind) and turbulence. ---*/
+ const bool musclFlow = config->GetMUSCL_Flow() && muscl &&
+ (config->GetKind_ConvNumScheme_Flow() == SPACE_UPWIND);
+ /*--- Only consider flow limiters for cell-based limiters, edge-based would need to be recomputed. ---*/
+ const bool limiterFlow = (config->GetKind_SlopeLimit_Flow() != NO_LIMITER) &&
+ (config->GetKind_SlopeLimit_Flow() != VAN_ALBADA_EDGE);
+
CVariable* flowNodes = solver_container[FLOW_SOL]->GetNodes();
/*--- Pick one numerics object per thread. ---*/
@@ -105,7 +116,7 @@ void CTurbSolver::Upwind_Residual(CGeometry *geometry, CSolver **solver_containe
for (auto color : EdgeColoring)
{
/*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/
- SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize)
+ SU2_OMP_FOR_DYN(nextMultiple(OMP_MIN_SIZE, color.groupSize))
for(auto k = 0ul; k < color.size; ++k) {
auto iEdge = color.indices[k];
@@ -121,14 +132,14 @@ void CTurbSolver::Upwind_Residual(CGeometry *geometry, CSolver **solver_containe
/*--- Primitive variables w/o reconstruction ---*/
- auto V_i = flowNodes->GetPrimitive(iPoint);
- auto V_j = flowNodes->GetPrimitive(jPoint);
+ const auto V_i = flowNodes->GetPrimitive(iPoint);
+ const auto V_j = flowNodes->GetPrimitive(jPoint);
numerics->SetPrimitive(V_i, V_j);
/*--- Turbulent variables w/o reconstruction ---*/
- auto Turb_i = nodes->GetSolution(iPoint);
- auto Turb_j = nodes->GetSolution(jPoint);
+ const auto Turb_i = nodes->GetSolution(iPoint);
+ const auto Turb_j = nodes->GetSolution(jPoint);
numerics->SetTurbVar(Turb_i, Turb_j);
/*--- Grid Movement ---*/
@@ -137,151 +148,166 @@ void CTurbSolver::Upwind_Residual(CGeometry *geometry, CSolver **solver_containe
numerics->SetGridVel(geometry->node[iPoint]->GetGridVel(),
geometry->node[jPoint]->GetGridVel());
- if (muscl) {
- su2double *Limiter_i = nullptr, *Limiter_j = nullptr;
+ if (muscl || musclFlow) {
+ const su2double *Limiter_i = nullptr, *Limiter_j = nullptr;
- auto Coord_i = geometry->node[iPoint]->GetCoord();
- auto Coord_j = geometry->node[jPoint]->GetCoord();
-
- /*--- Reconstruct flow variables. ---*/
+ const auto Coord_i = geometry->node[iPoint]->GetCoord();
+ const auto Coord_j = geometry->node[jPoint]->GetCoord();
su2double Vector_ij[MAXNDIM] = {0.0};
for (iDim = 0; iDim < nDim; iDim++) {
Vector_ij[iDim] = 0.5*(Coord_j[iDim] - Coord_i[iDim]);
}
- /*--- Mean flow primitive variables using gradient reconstruction and limiters ---*/
-
- auto Gradient_i = flowNodes->GetGradient_Reconstruction(iPoint);
- auto Gradient_j = flowNodes->GetGradient_Reconstruction(jPoint);
+ if (musclFlow) {
+ /*--- Reconstruct mean flow primitive variables. ---*/
- if (limiter) {
- Limiter_i = flowNodes->GetLimiter_Primitive(iPoint);
- Limiter_j = flowNodes->GetLimiter_Primitive(jPoint);
- }
+ auto Gradient_i = flowNodes->GetGradient_Reconstruction(iPoint);
+ auto Gradient_j = flowNodes->GetGradient_Reconstruction(jPoint);
- for (iVar = 0; iVar < solver_container[FLOW_SOL]->GetnPrimVarGrad(); iVar++) {
- su2double Project_Grad_i = 0.0, Project_Grad_j = 0.0;
- for (iDim = 0; iDim < nDim; iDim++) {
- Project_Grad_i += Vector_ij[iDim]*Gradient_i[iVar][iDim];
- Project_Grad_j -= Vector_ij[iDim]*Gradient_j[iVar][iDim];
- }
- if (limiter) {
- flowPrimVar_i[iVar] = V_i[iVar] + Limiter_i[iVar]*Project_Grad_i;
- flowPrimVar_j[iVar] = V_j[iVar] + Limiter_j[iVar]*Project_Grad_j;
+ if (limiterFlow) {
+ Limiter_i = flowNodes->GetLimiter_Primitive(iPoint);
+ Limiter_j = flowNodes->GetLimiter_Primitive(jPoint);
}
- else {
+
+ for (iVar = 0; iVar < solver_container[FLOW_SOL]->GetnPrimVarGrad(); iVar++) {
+ su2double Project_Grad_i = 0.0, Project_Grad_j = 0.0;
+ for (iDim = 0; iDim < nDim; iDim++) {
+ Project_Grad_i += Vector_ij[iDim]*Gradient_i[iVar][iDim];
+ Project_Grad_j -= Vector_ij[iDim]*Gradient_j[iVar][iDim];
+ }
+ if (limiterFlow) {
+ Project_Grad_i *= Limiter_i[iVar];
+ Project_Grad_j *= Limiter_j[iVar];
+ }
flowPrimVar_i[iVar] = V_i[iVar] + Project_Grad_i;
flowPrimVar_j[iVar] = V_j[iVar] + Project_Grad_j;
}
- }
- numerics->SetPrimitive(flowPrimVar_i, flowPrimVar_j);
-
- /*--- Reconstruct turbulence variables. ---*/
+ numerics->SetPrimitive(flowPrimVar_i, flowPrimVar_j);
+ }
- Gradient_i = nodes->GetGradient_Reconstruction(iPoint);
- Gradient_j = nodes->GetGradient_Reconstruction(jPoint);
+ if (muscl) {
+ /*--- Reconstruct turbulence variables. ---*/
- if (limiter) {
- Limiter_i = nodes->GetLimiter(iPoint);
- Limiter_j = nodes->GetLimiter(jPoint);
- }
+ auto Gradient_i = nodes->GetGradient_Reconstruction(iPoint);
+ auto Gradient_j = nodes->GetGradient_Reconstruction(jPoint);
- for (iVar = 0; iVar < nVar; iVar++) {
- su2double Project_Grad_i = 0.0, Project_Grad_j = 0.0;
- for (iDim = 0; iDim < nDim; iDim++) {
- Project_Grad_i += Vector_ij[iDim]*Gradient_i[iVar][iDim];
- Project_Grad_j -= Vector_ij[iDim]*Gradient_j[iVar][iDim];
- }
if (limiter) {
- solution_i[iVar] = Turb_i[iVar] + Limiter_i[iVar]*Project_Grad_i;
- solution_j[iVar] = Turb_j[iVar] + Limiter_j[iVar]*Project_Grad_j;
+ Limiter_i = nodes->GetLimiter(iPoint);
+ Limiter_j = nodes->GetLimiter(jPoint);
}
- else {
+
+ for (iVar = 0; iVar < nVar; iVar++) {
+ su2double Project_Grad_i = 0.0, Project_Grad_j = 0.0;
+ for (iDim = 0; iDim < nDim; iDim++) {
+ Project_Grad_i += Vector_ij[iDim]*Gradient_i[iVar][iDim];
+ Project_Grad_j -= Vector_ij[iDim]*Gradient_j[iVar][iDim];
+ }
+ if (limiter) {
+ Project_Grad_i *= Limiter_i[iVar];
+ Project_Grad_j *= Limiter_j[iVar];
+ }
solution_i[iVar] = Turb_i[iVar] + Project_Grad_i;
solution_j[iVar] = Turb_j[iVar] + Project_Grad_j;
}
- }
-
- numerics->SetTurbVar(solution_i, solution_j);
+ numerics->SetTurbVar(solution_i, solution_j);
+ }
}
- /*--- Add and subtract residual ---*/
+ /*--- Update convective residual value ---*/
auto residual = numerics->ComputeResidual(config);
- LinSysRes.AddBlock(iPoint, residual);
- LinSysRes.SubtractBlock(jPoint, residual);
-
- /*--- Implicit part ---*/
+ if (ReducerStrategy) {
+ EdgeFluxes.SetBlock(iEdge, residual);
+ Jacobian.SetBlocks(iEdge, residual.jacobian_i, residual.jacobian_j);
+ }
+ else {
+ LinSysRes.AddBlock(iPoint, residual);
+ LinSysRes.SubtractBlock(jPoint, residual);
+ Jacobian.UpdateBlocks(iEdge, iPoint, jPoint, residual.jacobian_i, residual.jacobian_j);
+ }
- Jacobian.UpdateBlocks(iEdge, iPoint, jPoint, residual.jacobian_i, residual.jacobian_j);
+ /*--- Viscous contribution. ---*/
+ Viscous_Residual(iEdge, geometry, solver_container,
+ numerics_container[VISC_TERM + omp_get_thread_num()*MAX_TERMS], config);
}
} // end color loop
+ if (ReducerStrategy) {
+ SumEdgeFluxes(geometry);
+ Jacobian.SetDiagonalAsColumnSum();
+ }
}
-void CTurbSolver::Viscous_Residual(CGeometry *geometry, CSolver **solver_container, CNumerics **numerics_container,
- CConfig *config, unsigned short iMesh, unsigned short iRKStep) {
+void CTurbSolver::Viscous_Residual(unsigned long iEdge, CGeometry *geometry, CSolver **solver_container,
+ CNumerics *numerics, CConfig *config) {
CVariable* flowNodes = solver_container[FLOW_SOL]->GetNodes();
- /*--- Pick one numerics object per thread. ---*/
- CNumerics* numerics = numerics_container[VISC_TERM + omp_get_thread_num()*MAX_TERMS];
+ /*--- Points in edge ---*/
- /*--- Loop over edge colors. ---*/
- for (auto color : EdgeColoring)
- {
- /*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/
- SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize)
- for(auto k = 0ul; k < color.size; ++k) {
+ auto iPoint = geometry->edge[iEdge]->GetNode(0);
+ auto jPoint = geometry->edge[iEdge]->GetNode(1);
- auto iEdge = color.indices[k];
+ /*--- Points coordinates, and normal vector ---*/
- /*--- Points in edge ---*/
+ numerics->SetCoord(geometry->node[iPoint]->GetCoord(),
+ geometry->node[jPoint]->GetCoord());
+ numerics->SetNormal(geometry->edge[iEdge]->GetNormal());
- auto iPoint = geometry->edge[iEdge]->GetNode(0);
- auto jPoint = geometry->edge[iEdge]->GetNode(1);
+ /*--- Conservative variables w/o reconstruction ---*/
- /*--- Points coordinates, and normal vector ---*/
+ numerics->SetPrimitive(flowNodes->GetPrimitive(iPoint),
+ flowNodes->GetPrimitive(jPoint));
- numerics->SetCoord(geometry->node[iPoint]->GetCoord(),
- geometry->node[jPoint]->GetCoord());
- numerics->SetNormal(geometry->edge[iEdge]->GetNormal());
+ /*--- Turbulent variables w/o reconstruction, and its gradients ---*/
- /*--- Conservative variables w/o reconstruction ---*/
+ numerics->SetTurbVar(nodes->GetSolution(iPoint),
+ nodes->GetSolution(jPoint));
+ numerics->SetTurbVarGradient(nodes->GetGradient(iPoint),
+ nodes->GetGradient(jPoint));
- numerics->SetPrimitive(flowNodes->GetPrimitive(iPoint),
- flowNodes->GetPrimitive(jPoint));
+ /*--- Menter's first blending function (only SST)---*/
+ if ((config->GetKind_Turb_Model() == SST) || (config->GetKind_Turb_Model() == SST_SUST))
+ numerics->SetF1blending(nodes->GetF1blending(iPoint),
+ nodes->GetF1blending(jPoint));
- /*--- Turbulent variables w/o reconstruction, and its gradients ---*/
+ /*--- Compute residual, and Jacobians ---*/
- numerics->SetTurbVar(nodes->GetSolution(iPoint),
- nodes->GetSolution(jPoint));
- numerics->SetTurbVarGradient(nodes->GetGradient(iPoint),
- nodes->GetGradient(jPoint));
+ auto residual = numerics->ComputeResidual(config);
- /*--- Menter's first blending function (only SST)---*/
- if ((config->GetKind_Turb_Model() == SST) || (config->GetKind_Turb_Model() == SST_SUST))
- numerics->SetF1blending(nodes->GetF1blending(iPoint),
- nodes->GetF1blending(jPoint));
+ if (ReducerStrategy) {
+ EdgeFluxes.SubtractBlock(iEdge, residual);
+ Jacobian.UpdateBlocksSub(iEdge, residual.jacobian_i, residual.jacobian_j);
+ }
+ else {
+ LinSysRes.SubtractBlock(iPoint, residual);
+ LinSysRes.AddBlock(jPoint, residual);
+ Jacobian.UpdateBlocksSub(iEdge, iPoint, jPoint, residual.jacobian_i, residual.jacobian_j);
+ }
+}
- /*--- Compute residual, and Jacobians ---*/
+void CTurbSolver::SumEdgeFluxes(CGeometry* geometry) {
- auto residual = numerics->ComputeResidual(config);
+ SU2_OMP_FOR_STAT(omp_chunk_size)
+ for (unsigned long iPoint = 0; iPoint < nPoint; ++iPoint) {
- /*--- Add and subtract residual, and update Jacobians ---*/
+ LinSysRes.SetBlock_Zero(iPoint);
- LinSysRes.SubtractBlock(iPoint, residual);
- LinSysRes.AddBlock(jPoint, residual);
+ for (unsigned short iNeigh = 0; iNeigh < geometry->node[iPoint]->GetnPoint(); ++iNeigh) {
- Jacobian.UpdateBlocksSub(iEdge, iPoint, jPoint, residual.jacobian_i, residual.jacobian_j);
+ auto iEdge = geometry->node[iPoint]->GetEdge(iNeigh);
+ if (iPoint == geometry->edge[iEdge]->GetNode(0))
+ LinSysRes.AddBlock(iPoint, EdgeFluxes.GetBlock(iEdge));
+ else
+ LinSysRes.SubtractBlock(iPoint, EdgeFluxes.GetBlock(iEdge));
+ }
}
- } // end color loop
}
@@ -585,14 +611,18 @@ void CTurbSolver::SetResidual_DualTime(CGeometry *geometry, CSolver **solver_con
const bool second_order = (config->GetTime_Marching() == DT_STEPPING_2ND);
const bool incompressible = (config->GetKind_Regime() == INCOMPRESSIBLE);
+ /*--- Flow solution, needed to get density. ---*/
+
+ CVariable* flowNodes = solver_container[FLOW_SOL]->GetNodes();
+
/*--- Store the physical time step ---*/
const su2double TimeStep = config->GetDelta_UnstTimeND();
/*--- Local variables ---*/
- unsigned short iVar, iMarker, iDim;
- unsigned long iPoint, jPoint, iVertex;
+ unsigned short iVar, iMarker, iDim, iNeigh;
+ unsigned long iPoint, jPoint, iVertex, iEdge;
const su2double *U_time_nM1 = nullptr, *U_time_n = nullptr, *U_time_nP1 = nullptr;
su2double Volume_nM1, Volume_nP1;
@@ -634,14 +664,14 @@ void CTurbSolver::SetResidual_DualTime(CGeometry *geometry, CSolver **solver_con
density could also be temperature dependent, but as it is not a part
of the solution vector it's neither stored for previous time steps
nor updated with the solution at the end of each iteration. */
- Density_nM1 = solver_container[FLOW_SOL]->GetNodes()->GetDensity(iPoint);
- Density_n = solver_container[FLOW_SOL]->GetNodes()->GetDensity(iPoint);
- Density_nP1 = solver_container[FLOW_SOL]->GetNodes()->GetDensity(iPoint);
+ Density_nM1 = flowNodes->GetDensity(iPoint);
+ Density_n = flowNodes->GetDensity(iPoint);
+ Density_nP1 = flowNodes->GetDensity(iPoint);
}
else{
- Density_nM1 = solver_container[FLOW_SOL]->GetNodes()->GetSolution_time_n1(iPoint)[0];
- Density_n = solver_container[FLOW_SOL]->GetNodes()->GetSolution_time_n(iPoint,0);
- Density_nP1 = solver_container[FLOW_SOL]->GetNodes()->GetSolution(iPoint,0);
+ Density_nM1 = flowNodes->GetSolution_time_n1(iPoint)[0];
+ Density_n = flowNodes->GetSolution_time_n(iPoint,0);
+ Density_nP1 = flowNodes->GetSolution(iPoint,0);
}
for (iVar = 0; iVar < nVar; iVar++) {
@@ -681,77 +711,43 @@ void CTurbSolver::SetResidual_DualTime(CGeometry *geometry, CSolver **solver_con
we will loop over the edges and boundaries to compute the GCL component
of the dual time source term that depends on grid velocities. ---*/
- /*--- Loop over edge colors. ---*/
- for (auto color : EdgeColoring)
- {
- /*--- Chunk size is at least OMP_MIN_SIZE and a multiple of the color group size. ---*/
- SU2_OMP_FOR_DYN(roundUpDiv(OMP_MIN_SIZE, ColorGroupSize)*ColorGroupSize)
- for(auto k = 0ul; k < color.size; ++k) {
-
- auto iEdge = color.indices[k];
-
- /*--- Get indices for nodes i & j plus the face normal ---*/
-
- iPoint = geometry->edge[iEdge]->GetNode(0);
- jPoint = geometry->edge[iEdge]->GetNode(1);
- Normal = geometry->edge[iEdge]->GetNormal();
-
- /*--- Grid velocities stored at nodes i & j ---*/
+ SU2_OMP_FOR_STAT(omp_chunk_size)
+ for (iPoint = 0; iPoint < nPointDomain; ++iPoint) {
GridVel_i = geometry->node[iPoint]->GetGridVel();
- GridVel_j = geometry->node[jPoint]->GetGridVel();
-
- /*--- Compute the GCL term by averaging the grid velocities at the
- edge mid-point and dotting with the face normal. ---*/
-
- Residual_GCL = 0.0;
- for (iDim = 0; iDim < nDim; iDim++)
- Residual_GCL += 0.5*(GridVel_i[iDim]+GridVel_j[iDim])*Normal[iDim];
-
- /*--- Compute the GCL component of the source term for node i ---*/
-
- U_time_n = nodes->GetSolution_time_n(iPoint);
-
- /*--- Multiply by density at node i for the SST model ---*/
+ U_time_n = nodes->GetSolution_time_n(iPoint);
+ Density_n = 1.0;
if (sst_model) {
if (incompressible)
- Density_n = solver_container[FLOW_SOL]->GetNodes()->GetDensity(iPoint); // Temporary fix
+ Density_n = flowNodes->GetDensity(iPoint); // Temporary fix
else
- Density_n = solver_container[FLOW_SOL]->GetNodes()->GetSolution_time_n(iPoint,0);
-
- for (iVar = 0; iVar < nVar; iVar++)
- LinSysRes(iPoint,iVar) += Density_n*U_time_n[iVar]*Residual_GCL;
- }
- else {
- for (iVar = 0; iVar < nVar; iVar++)
- LinSysRes(iPoint,iVar) += U_time_n[iVar]*Residual_GCL;
+ Density_n = flowNodes->GetSolution_time_n(iPoint,0);
}
- /*--- Compute the GCL component of the source term for node j ---*/
+ for (iNeigh = 0; iNeigh < geometry->node[iPoint]->GetnNeighbor(); iNeigh++) {
- U_time_n = nodes->GetSolution_time_n(jPoint);
+ iEdge = geometry->node[iPoint]->GetEdge(iNeigh);
+ Normal = geometry->edge[iEdge]->GetNormal();
- /*--- Multiply by density at node j for the SST model ---*/
+ jPoint = geometry->node[iPoint]->GetPoint(iNeigh);
+ GridVel_j = geometry->node[jPoint]->GetGridVel();
- if (sst_model) {
- if (incompressible)
- Density_n = solver_container[FLOW_SOL]->GetNodes()->GetDensity(jPoint); // Temporary fix
- else
- Density_n = solver_container[FLOW_SOL]->GetNodes()->GetSolution_time_n(jPoint)[0];
+ /*--- Determine whether to consider the normal outward or inward. ---*/
+ su2double dir = (geometry->edge[iEdge]->GetNode(0) == iPoint)? 0.5 : -0.5;
+
+ Residual_GCL = 0.0;
+ for (iDim = 0; iDim < nDim; iDim++)
+ Residual_GCL += dir*(GridVel_i[iDim]+GridVel_j[iDim])*Normal[iDim];
+
+ Residual_GCL *= Density_n;
for (iVar = 0; iVar < nVar; iVar++)
- LinSysRes(jPoint,iVar) -= Density_n*U_time_n[iVar]*Residual_GCL;
- }
- else {
- for (iVar = 0; iVar < nVar; iVar++)
- LinSysRes(jPoint,iVar) -= U_time_n[iVar]*Residual_GCL;
+ LinSysRes(iPoint,iVar) += U_time_n[iVar]*Residual_GCL;
}
-
}
- } // end color loop
- /*--- Loop over the boundary edges ---*/
+ /*--- Loop over the boundary edges ---*/
for (iMarker = 0; iMarker < geometry->GetnMarker(); iMarker++) {
if ((config->GetMarker_All_KindBC(iMarker) != INTERNAL_BOUNDARY) &&
@@ -784,9 +780,9 @@ void CTurbSolver::SetResidual_DualTime(CGeometry *geometry, CSolver **solver_con
if (sst_model) {
if (incompressible)
- Density_n = solver_container[FLOW_SOL]->GetNodes()->GetDensity(iPoint); // Temporary fix
+ Density_n = flowNodes->GetDensity(iPoint); // Temporary fix
else
- Density_n = solver_container[FLOW_SOL]->GetNodes()->GetSolution_time_n(iPoint,0);
+ Density_n = flowNodes->GetSolution_time_n(iPoint,0);
for (iVar = 0; iVar < nVar; iVar++)
LinSysRes(iPoint,iVar) += Density_n*U_time_n[iVar]*Residual_GCL;
@@ -834,14 +830,14 @@ void CTurbSolver::SetResidual_DualTime(CGeometry *geometry, CSolver **solver_con
density could also be temperature dependent, but as it is not a part
of the solution vector it's neither stored for previous time steps
nor updated with the solution at the end of each iteration. */
- Density_nM1 = solver_container[FLOW_SOL]->GetNodes()->GetDensity(iPoint);
- Density_n = solver_container[FLOW_SOL]->GetNodes()->GetDensity(iPoint);
- Density_nP1 = solver_container[FLOW_SOL]->GetNodes()->GetDensity(iPoint);
+ Density_nM1 = flowNodes->GetDensity(iPoint);
+ Density_n = flowNodes->GetDensity(iPoint);
+ Density_nP1 = flowNodes->GetDensity(iPoint);
}
else {
- Density_nM1 = solver_container[FLOW_SOL]->GetNodes()->GetSolution_time_n1(iPoint)[0];
- Density_n = solver_container[FLOW_SOL]->GetNodes()->GetSolution_time_n(iPoint,0);
- Density_nP1 = solver_container[FLOW_SOL]->GetNodes()->GetSolution(iPoint,0);
+ Density_nM1 = flowNodes->GetSolution_time_n1(iPoint)[0];
+ Density_n = flowNodes->GetSolution_time_n(iPoint,0);
+ Density_nP1 = flowNodes->GetSolution(iPoint,0);
}
for (iVar = 0; iVar < nVar; iVar++) {
diff --git a/TestCases/fea_fsi/Airfoil_RBF/config.cfg b/TestCases/fea_fsi/Airfoil_RBF/config.cfg
index 437ca3394f55..ddcf42c9b8e4 100755
--- a/TestCases/fea_fsi/Airfoil_RBF/config.cfg
+++ b/TestCases/fea_fsi/Airfoil_RBF/config.cfg
@@ -3,7 +3,7 @@
% Case description: 2D airfoil FSI with radial basis function interp. %
% Institution: Imperial College London %
% Date: 2015.08.12 %
-% File Version 6.2 "Falcon" %
+% File Version 7.0.2 "Blackbird" %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SOLVER = MULTIPHYSICS
diff --git a/TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg b/TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg
index 921a81ac1da0..eff2d6ed2b36 100644
--- a/TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg
+++ b/TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg
@@ -2,7 +2,7 @@
% SU2 configuration file %
% Case description: 2D airfoil FSI with radial basis function interp. %
% Institution: Imperial College London %
-% File Version 7.0.2 "Blackbird" %
+% File Version 7.0.2 "Blackbird" %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Interface options ---------------------------------------------------- %
@@ -12,6 +12,7 @@ CONSERVATIVE_INTERPOLATION = YES
KIND_RADIAL_BASIS_FUNCTION = WENDLAND_C2
RADIAL_BASIS_FUNCTION_PARAMETER = 0.015
RADIAL_BASIS_FUNCTION_POLYNOMIAL_TERM = YES
+RADIAL_BASIS_FUNCTION_PRUNE_TOLERANCE = 0
%
% Physics -------------------------------------------------------------- %
SOLVER= ELASTICITY
diff --git a/TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg b/TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg
index 183cb402f3fc..9efc636b33d4 100644
--- a/TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg
+++ b/TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg
@@ -2,7 +2,7 @@
% SU2 configuration file %
% Case description: 2D airfoil FSI with radial basis function interp. %
% Institution: Imperial College London %
-% File Version 7.0.2 "Blackbird" %
+% File Version 7.0.2 "Blackbird" %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Interface options ---------------------------------------------------- %
@@ -20,16 +20,20 @@ KIND_INTERPOLATION = RADIAL_BASIS_FUNCTION
% orders) and you have issues, break up the interface or use "NO" for
% consistent interpolation.
CONSERVATIVE_INTERPOLATION = YES
-% Wendland provides good results and produces a nice diagonally dominant
-% interpolation kernel, other options:
+% Wendland is usually the best option due to sparser and better
+% conditioned interpolation kernel, other options:
% INV_MULTI_QUADRIC; GAUSSIAN; THIN_PLATE_SPLINE; MULTI_QUADRIC
KIND_RADIAL_BASIS_FUNCTION = WENDLAND_C2
% The radius in meters, 2 times the largest cell size on the interface is
% a good compromise between accuracy and condition number of the kernel.
RADIAL_BASIS_FUNCTION_PARAMETER = 0.015
-% Recommended as it recovers rigid body motion, only requires a few more
-% matrix products... feel free to explore though!
+% Polynomial terms to recover rigid body motion, this requires slightly
+% more computation and reduces the sparsity of the interpolation matrix.
RADIAL_BASIS_FUNCTION_POLYNOMIAL_TERM = YES
+% The number of donor points per target point is reduced by prunning away
+% small interpolation coefficients (making the interp. matrix sparse).
+% This tolerance is relative to the maximum absolute coefficient.
+RADIAL_BASIS_FUNCTION_PRUNE_TOLERANCE = 0
%
% Physics -------------------------------------------------------------- %
SOLVER= EULER
diff --git a/TestCases/parallel_regression.py b/TestCases/parallel_regression.py
index 6d15e0560ace..4475490dd1c6 100644
--- a/TestCases/parallel_regression.py
+++ b/TestCases/parallel_regression.py
@@ -91,7 +91,7 @@ def main():
fixedCL_naca0012.cfg_dir = "fixed_cl/naca0012"
fixedCL_naca0012.cfg_file = "inv_NACA0012.cfg"
fixedCL_naca0012.test_iter = 10
- fixedCL_naca0012.test_vals = [-12.137879, -6.705803, 0.300000, 0.019470] #last 4 columns
+ fixedCL_naca0012.test_vals = [-12.137437, -6.705109, 0.300000, 0.019470] #last 4 columns
fixedCL_naca0012.su2_exec = "parallel_computation.py -f"
fixedCL_naca0012.timeout = 1600
fixedCL_naca0012.tol = 0.00001
@@ -243,7 +243,7 @@ def main():
turb_naca0012_sa.cfg_dir = "rans/naca0012"
turb_naca0012_sa.cfg_file = "turb_NACA0012_sa.cfg"
turb_naca0012_sa.test_iter = 10
- turb_naca0012_sa.test_vals = [-12.078361, -16.147829, 1.064326, 0.019770] #last 4 columns
+ turb_naca0012_sa.test_vals = [-12.078401, -16.147829, 1.064326, 0.019770] #last 4 columns
turb_naca0012_sa.su2_exec = "parallel_computation.py -f"
turb_naca0012_sa.timeout = 3200
turb_naca0012_sa.tol = 0.00001
@@ -792,7 +792,7 @@ def main():
unst_inc_turb_naca0015_sa.cfg_dir = "unsteady/pitching_naca0015_rans_inc"
unst_inc_turb_naca0015_sa.cfg_file = "config_incomp_turb_sa.cfg"
unst_inc_turb_naca0015_sa.test_iter = 1
- unst_inc_turb_naca0015_sa.test_vals = [-2.990702, -6.861616, 1.475736, 0.419771] #last 4 columns
+ unst_inc_turb_naca0015_sa.test_vals = [-2.990703, -6.865923, 1.475736, 0.419770] #last 4 columns
unst_inc_turb_naca0015_sa.su2_exec = "parallel_computation.py -f"
unst_inc_turb_naca0015_sa.timeout = 1600
unst_inc_turb_naca0015_sa.tol = 0.00001
@@ -976,7 +976,7 @@ def main():
bars_SST_2D.cfg_dir = "sliding_interface/bars_SST_2D"
bars_SST_2D.cfg_file = "bars.cfg"
bars_SST_2D.test_iter = 13
- bars_SST_2D.test_vals = [13.000000, -0.590195, -1.955395] #last 4 columns
+ bars_SST_2D.test_vals = [13.000000, -0.598555, -1.879082] #last 4 columns
bars_SST_2D.su2_exec = "SU2_CFD"
bars_SST_2D.timeout = 1600
bars_SST_2D.tol = 0.00001
diff --git a/TestCases/parallel_regression_AD.py b/TestCases/parallel_regression_AD.py
index 69ceb1be21aa..a1c17b61e2c2 100644
--- a/TestCases/parallel_regression_AD.py
+++ b/TestCases/parallel_regression_AD.py
@@ -84,7 +84,7 @@ def main():
discadj_rans_naca0012_sa.cfg_dir = "disc_adj_rans/naca0012"
discadj_rans_naca0012_sa.cfg_file = "turb_NACA0012_sa.cfg"
discadj_rans_naca0012_sa.test_iter = 10
- discadj_rans_naca0012_sa.test_vals = [-2.230578, 0.678810, 0.181780, -0.000018] #last 4 columns
+ discadj_rans_naca0012_sa.test_vals = [-2.230573, 0.696562, 0.181780, -0.000018] #last 4 columns
discadj_rans_naca0012_sa.su2_exec = "parallel_computation.py -f"
discadj_rans_naca0012_sa.timeout = 1600
discadj_rans_naca0012_sa.tol = 0.00001
diff --git a/TestCases/serial_regression.py b/TestCases/serial_regression.py
index 09657a95b35c..f57084dc5757 100644
--- a/TestCases/serial_regression.py
+++ b/TestCases/serial_regression.py
@@ -101,7 +101,7 @@ def main():
fixedCL_naca0012.cfg_dir = "fixed_cl/naca0012"
fixedCL_naca0012.cfg_file = "inv_NACA0012.cfg"
fixedCL_naca0012.test_iter = 10
- fixedCL_naca0012.test_vals = [-12.129044, -6.702294, 0.300000, 0.019470] #last 4 columns
+ fixedCL_naca0012.test_vals = [-12.128275, -6.700329, 0.300000, 0.019470] #last 4 columns
fixedCL_naca0012.su2_exec = "SU2_CFD"
fixedCL_naca0012.new_output = True
fixedCL_naca0012.timeout = 1600
@@ -281,7 +281,7 @@ def main():
turb_naca0012_sa.cfg_dir = "rans/naca0012"
turb_naca0012_sa.cfg_file = "turb_NACA0012_sa.cfg"
turb_naca0012_sa.test_iter = 10
- turb_naca0012_sa.test_vals = [-12.075893, -16.146770, 1.064326, 0.019770] #last 4 columns
+ turb_naca0012_sa.test_vals = [-12.075861, -16.146770, 1.064326, 0.019770] #last 4 columns
turb_naca0012_sa.su2_exec = "SU2_CFD"
turb_naca0012_sa.new_output = True
turb_naca0012_sa.timeout = 3200
@@ -790,7 +790,7 @@ def main():
turb_naca0012_p1c1.cfg_dir = "rans_uq/naca0012"
turb_naca0012_p1c1.cfg_file = "turb_NACA0012_uq_p1c1.cfg"
turb_naca0012_p1c1.test_iter = 10
- turb_naca0012_p1c1.test_vals = [-5.003327, 1.312032, 6.085201, 2.413460] #last 4 columns
+ turb_naca0012_p1c1.test_vals = [-5.003335, 1.312021, 6.085201, 2.413460] #last 4 columns
turb_naca0012_p1c1.su2_exec = "SU2_CFD"
turb_naca0012_p1c1.new_output = True
turb_naca0012_p1c1.timeout = 1600
@@ -802,7 +802,7 @@ def main():
turb_naca0012_p1c2.cfg_dir = "rans_uq/naca0012"
turb_naca0012_p1c2.cfg_file = "turb_NACA0012_uq_p1c2.cfg"
turb_naca0012_p1c2.test_iter = 10
- turb_naca0012_p1c2.test_vals = [-5.263992, 1.251332, 6.085705, 2.413434] #last 4 columns
+ turb_naca0012_p1c2.test_vals = [-5.264009, 1.251324, 6.085705, 2.413434] #last 4 columns
turb_naca0012_p1c2.su2_exec = "SU2_CFD"
turb_naca0012_p1c2.new_output = True
turb_naca0012_p1c2.timeout = 1600
@@ -926,7 +926,7 @@ def main():
unst_inc_turb_naca0015_sa.cfg_dir = "unsteady/pitching_naca0015_rans_inc"
unst_inc_turb_naca0015_sa.cfg_file = "config_incomp_turb_sa.cfg"
unst_inc_turb_naca0015_sa.test_iter = 1
- unst_inc_turb_naca0015_sa.test_vals = [ -2.994996, -6.865786, 1.434864, 0.416627] #last 4 columns
+ unst_inc_turb_naca0015_sa.test_vals = [-2.994996, -6.869781, 1.434864, 0.416626] #last 4 columns
unst_inc_turb_naca0015_sa.su2_exec = "SU2_CFD"
unst_inc_turb_naca0015_sa.timeout = 1600
unst_inc_turb_naca0015_sa.tol = 0.00001
@@ -1134,7 +1134,7 @@ def main():
bars_SST_2D.cfg_dir = "sliding_interface/bars_SST_2D"
bars_SST_2D.cfg_file = "bars.cfg"
bars_SST_2D.test_iter = 13
- bars_SST_2D.test_vals = [13.000000, -0.590195, -1.955395] #last 3 columns
+ bars_SST_2D.test_vals = [13.000000, -0.598555, -1.879082] #last 3 columns
bars_SST_2D.su2_exec = "SU2_CFD"
bars_SST_2D.timeout = 1600
bars_SST_2D.tol = 0.00001
diff --git a/TestCases/serial_regression_AD.py b/TestCases/serial_regression_AD.py
index e90b5d0e973a..70efb28b1f1d 100644
--- a/TestCases/serial_regression_AD.py
+++ b/TestCases/serial_regression_AD.py
@@ -84,7 +84,7 @@ def main():
discadj_rans_naca0012_sa.cfg_dir = "disc_adj_rans/naca0012"
discadj_rans_naca0012_sa.cfg_file = "turb_NACA0012_sa.cfg"
discadj_rans_naca0012_sa.test_iter = 10
- discadj_rans_naca0012_sa.test_vals = [-2.230561, 0.678834, 0.180740, -0.000018] #last 4 columns
+ discadj_rans_naca0012_sa.test_vals = [-2.230556, 0.696586, 0.180740, -0.000018] #last 4 columns
discadj_rans_naca0012_sa.su2_exec = "SU2_CFD_AD"
discadj_rans_naca0012_sa.timeout = 1600
discadj_rans_naca0012_sa.tol = 0.00001
diff --git a/config_template.cfg b/config_template.cfg
index ec58a8a5c79d..352d37bed08b 100644
--- a/config_template.cfg
+++ b/config_template.cfg
@@ -1333,6 +1333,26 @@ UQ_URLX= 0.1
% Perturbation magnitude (float [0,1], default= 1.0)
UQ_DELTA_B= 1.0
%
+% --------------------- HYBRID PARALLEL (MPI+OpenMP) OPTIONS ---------------------%
+%
+% An advanced performance parameter for FVM solvers, a large-ish value should be best
+% when relatively few threads per MPI rank are in use (~4). However, maximum parallelism
+% is obtained with EDGE_COLORING_GROUP_SIZE=1, consider using this value only if SU2
+% warns about low coloring efficiency during preprocessing (performance is usually worse).
+% Setting the option to 0 disables coloring and a different strategy is used instead,
+% that strategy is automatically used when the coloring efficiency is less than 0.875.
+% The optimum value/strategy is case-dependent.
+EDGE_COLORING_GROUP_SIZE= 512
+%
+% Independent "threads per MPI rank" setting for LU-SGS and ILU preconditioners.
+% For problems where time is spend mostly in the solution of linear systems (e.g. elasticity,
+% very high CFL central schemes), AND, if the memory bandwidth of the machine is saturated
+% (4 or more cores per memory channel) better performance (via a reduction in linear iterations)
+% may be possible by using a smaller value than that defined by the system or in the call to
+% SU2_CFD (via the -t/--threads option).
+% The default (0) means "same number of threads as for all else".
+LINEAR_SOLVER_PREC_THREADS= 0
+%
% ------------------------- SCREEN/HISTORY VOLUME OUTPUT --------------------------%
%
% Screen output fields (use 'SU2_CFD -d ' to view list of available fields)