From e825d0539a625e831a85447a5674fbf3db3937b4 Mon Sep 17 00:00:00 2001 From: cvencro Date: Sun, 13 Oct 2019 20:02:37 +0100 Subject: [PATCH 001/112] Initial implementation of surface plunging with new mesh solver --- Common/include/grid_movement_structure.hpp | 4 +- Common/src/grid_movement_structure.cpp | 16 ++- SU2_CFD/include/iteration_structure.hpp | 2 +- SU2_CFD/src/drivers/CDriver.cpp | 10 +- SU2_CFD/src/drivers/CMultizoneDriver.cpp | 4 +- SU2_CFD/src/drivers/CSinglezoneDriver.cpp | 2 +- SU2_CFD/src/iteration_structure.cpp | 134 ++++++++++++++++++++- SU2_CFD/src/solver_direct_mean.cpp | 2 +- SU2_CFD/src/solver_structure.cpp | 6 +- 9 files changed, 159 insertions(+), 21 deletions(-) diff --git a/Common/include/grid_movement_structure.hpp b/Common/include/grid_movement_structure.hpp index 907a44632adb..fe7c0abc822e 100644 --- a/Common/include/grid_movement_structure.hpp +++ b/Common/include/grid_movement_structure.hpp @@ -50,6 +50,8 @@ #include "geometry_structure.hpp" #include "config_structure.hpp" +//#include "../../SU2_CFD/include/solver_structure.hpp" +//#include "../../SU2_CFD/include/solvers/CMeshSolver.hpp" #include "linear_algebra/CSysMatrix.hpp" #include "linear_algebra/CSysVector.hpp" #include "linear_algebra/CSysSolve.hpp" @@ -1610,7 +1612,7 @@ class CSurfaceMovement : public CGridMovement { * \param[in] iter - Current physical time iteration. * \param[in] iZone - Zone number in the mesh. */ - void Surface_Plunging(CGeometry *geometry, CConfig *config, + void Surface_Plunging(CGeometry *geometry, CConfig *config,// CSolver **solver, unsigned long iter, unsigned short iZone); /*! diff --git a/Common/src/grid_movement_structure.cpp b/Common/src/grid_movement_structure.cpp index 2adb86203694..054e4b2d410e 100644 --- a/Common/src/grid_movement_structure.cpp +++ b/Common/src/grid_movement_structure.cpp @@ -5790,14 +5790,14 @@ void CSurfaceMovement::Surface_Translating(CGeometry *geometry, CConfig *config, } } -void CSurfaceMovement::Surface_Plunging(CGeometry *geometry, CConfig *config, +void CSurfaceMovement::Surface_Plunging(CGeometry *geometry, CConfig *config, //CSolver **solver, unsigned long iter, unsigned short iZone) { su2double deltaT, time_new, time_old, Lref; su2double Center[3] = {0.0, 0.0, 0.0}, VarCoord[3], Omega[3], Ampl[3]; su2double DEG2RAD = PI_NUMBER/180.0; unsigned short iMarker, jMarker, Moving; - unsigned long iVertex; + unsigned long iPoint, iVertex; string Marker_Tag, Moving_Tag; unsigned short iDim; @@ -5861,8 +5861,16 @@ void CSurfaceMovement::Surface_Plunging(CGeometry *geometry, CConfig *config, for (iVertex = 0; iVertex < geometry->nVertex[iMarker]; iVertex++) { - /*--- Set node displacement for volume deformation ---*/ - geometry->vertex[iMarker][iVertex]->SetVarCoord(VarCoord); + // if (!config->GetDeform_Mesh()) { + /*--- Set node displacement for volume deformation ---*/ + geometry->vertex[iMarker][iVertex]->SetVarCoord(VarCoord); + // } + // else { + // /*--- Get node index ---*/ + // iPoint = geometry->vertex[iMarker][iVertex]->GetNode(); + + // solver[MESH_SOL]->node[iPoint]->SetBound_Disp(VarCoord); + // } } } diff --git a/SU2_CFD/include/iteration_structure.hpp b/SU2_CFD/include/iteration_structure.hpp index 6d8f7a76a46f..df33a326d0c7 100644 --- a/SU2_CFD/include/iteration_structure.hpp +++ b/SU2_CFD/include/iteration_structure.hpp @@ -97,7 +97,7 @@ class CIteration { * \param[in] ExtIter - Current physical time iteration number. */ virtual void SetGrid_Movement(CGeometry **geometry, CSurfaceMovement *surface_movement, - CVolumetricMovement *grid_movement, + CVolumetricMovement *grid_movement, CNumerics ****numerics, CSolver ***solver, CConfig *config, unsigned long IntIter, unsigned long ExtIter); /*! * \brief Run the mesh deformation algorithms. diff --git a/SU2_CFD/src/drivers/CDriver.cpp b/SU2_CFD/src/drivers/CDriver.cpp index 33b1482108bb..ae1db6307149 100644 --- a/SU2_CFD/src/drivers/CDriver.cpp +++ b/SU2_CFD/src/drivers/CDriver.cpp @@ -3187,7 +3187,7 @@ void CDriver::DynamicMesh_Preprocessing(CConfig *config, CGeometry **geometry, C surface_movement->CopyBoundary(geometry[MESH_0], config); if (config->GetUnsteady_Simulation() == HARMONIC_BALANCE){ if (rank == MASTER_NODE) cout << endl << "Instance "<< iInst + 1 <<":" << endl; - iteration->SetGrid_Movement(geometry, surface_movement, grid_movement, solver, config, 0, iInst); + iteration->SetGrid_Movement(geometry, surface_movement, grid_movement, numerics_container[iZone][iInst], solver, config, 0, iInst); } } @@ -4209,7 +4209,7 @@ void CFluidDriver::DynamicMeshUpdate(unsigned long ExtIter) { harmonic_balance = (config_container[iZone]->GetUnsteady_Simulation() == HARMONIC_BALANCE); /*--- Dynamic mesh update ---*/ if ((config_container[iZone]->GetGrid_Movement()) && (!harmonic_balance)) { - iteration_container[iZone][INST_0]->SetGrid_Movement(geometry_container[iZone][INST_0], surface_movement[iZone], grid_movement[iZone][INST_0], solver_container[iZone][INST_0], config_container[iZone], 0, ExtIter ); + iteration_container[iZone][INST_0]->SetGrid_Movement(geometry_container[iZone][INST_0], surface_movement[iZone], grid_movement[iZone][INST_0], numerics_container[iZone][INST_0], solver_container[iZone][INST_0], config_container[iZone], 0, ExtIter ); } } @@ -5092,7 +5092,7 @@ void CFSIDriver::Run() { /*-----------------------------------------------------------------*/ iteration_container[ZONE_FLOW][INST_0]->SetGrid_Movement(geometry_container[ZONE_FLOW][INST_0], - surface_movement[ZONE_FLOW], grid_movement[ZONE_FLOW][INST_0], + surface_movement[ZONE_FLOW], grid_movement[ZONE_FLOW][INST_0], numerics_container[ZONE_FLOW][INST_0], solver_container[ZONE_FLOW][INST_0], config_container[ZONE_FLOW], 0, ExtIter ); /*-----------------------------------------------------------------*/ @@ -5411,7 +5411,7 @@ void CFSIDriver::Update() { iteration_container[ZONE_FLOW][INST_0]->SetGrid_Movement(geometry_container[ZONE_FLOW][INST_0], - surface_movement[ZONE_FLOW], grid_movement[ZONE_FLOW][INST_0], + surface_movement[ZONE_FLOW], grid_movement[ZONE_FLOW][INST_0], numerics_container[ZONE_FLOW][INST_0], solver_container[ZONE_FLOW][INST_0], config_container[ZONE_FLOW], 0, ExtIter ); /*--- TODO: Temporary output of objective function for Flow OFs. Needs to be integrated into the refurbished output ---*/ @@ -6262,7 +6262,7 @@ void CDiscAdjFSIDriver::Mesh_Deformation_Direct(unsigned short ZONE_FLOW, unsign /*-----------------------------------------------------------------*/ direct_iteration[ZONE_FLOW]->SetGrid_Movement(geometry_container[ZONE_FLOW][INST_0], - surface_movement[ZONE_FLOW], grid_movement[ZONE_FLOW][INST_0], + surface_movement[ZONE_FLOW], grid_movement[ZONE_FLOW][INST_0], numerics_container[ZONE_FLOW][INST_0], solver_container[ZONE_FLOW][INST_0], config_container[ZONE_FLOW], 0, ExtIter ); geometry_container[ZONE_FLOW][INST_0][MESH_0]->UpdateGeometry(geometry_container[ZONE_FLOW][INST_0], config_container[ZONE_FLOW]); diff --git a/SU2_CFD/src/drivers/CMultizoneDriver.cpp b/SU2_CFD/src/drivers/CMultizoneDriver.cpp index d2973ce87814..bd7d8d571c10 100644 --- a/SU2_CFD/src/drivers/CMultizoneDriver.cpp +++ b/SU2_CFD/src/drivers/CMultizoneDriver.cpp @@ -637,7 +637,7 @@ void CMultizoneDriver::DynamicMeshUpdate(unsigned long ExtIter) { /*--- Dynamic mesh update ---*/ if ((config_container[iZone]->GetGrid_Movement()) && (!harmonic_balance) && (!fsi)) { iteration_container[iZone][INST_0]->SetGrid_Movement(geometry_container[iZone][INST_0],surface_movement[iZone], - grid_movement[iZone][INST_0], solver_container[iZone][INST_0], + grid_movement[iZone][INST_0], numerics_container[iZone][INST_0], solver_container[iZone][INST_0], config_container[iZone], 0, ExtIter); } } @@ -648,7 +648,7 @@ void CMultizoneDriver::DynamicMeshUpdate(unsigned short val_iZone, unsigned long /*--- Legacy dynamic mesh update - Only if GRID_MOVEMENT = YES ---*/ if (config_container[ZONE_0]->GetGrid_Movement()) { iteration_container[val_iZone][INST_0]->SetGrid_Movement(geometry_container[val_iZone][INST_0],surface_movement[val_iZone], - grid_movement[val_iZone][INST_0], solver_container[val_iZone][INST_0], + grid_movement[val_iZone][INST_0], numerics_container[iZone][INST_0], solver_container[val_iZone][INST_0], config_container[val_iZone], 0, ExtIter); } diff --git a/SU2_CFD/src/drivers/CSinglezoneDriver.cpp b/SU2_CFD/src/drivers/CSinglezoneDriver.cpp index 1ae8e86bcc02..408baf779fb0 100644 --- a/SU2_CFD/src/drivers/CSinglezoneDriver.cpp +++ b/SU2_CFD/src/drivers/CSinglezoneDriver.cpp @@ -296,7 +296,7 @@ void CSinglezoneDriver::DynamicMeshUpdate(unsigned long ExtIter) { /*--- Legacy dynamic mesh update - Only if GRID_MOVEMENT = YES ---*/ if (config_container[ZONE_0]->GetGrid_Movement()) { iteration_container[ZONE_0][INST_0]->SetGrid_Movement(geometry_container[ZONE_0][INST_0],surface_movement[ZONE_0], - grid_movement[ZONE_0][INST_0], solver_container[ZONE_0][INST_0], + grid_movement[ZONE_0][INST_0], numerics_container[ZONE_0][INST_0], solver_container[ZONE_0][INST_0], config_container[ZONE_0], 0, ExtIter); } diff --git a/SU2_CFD/src/iteration_structure.cpp b/SU2_CFD/src/iteration_structure.cpp index bde0f7cee0ee..c71d8b66a40f 100644 --- a/SU2_CFD/src/iteration_structure.cpp +++ b/SU2_CFD/src/iteration_structure.cpp @@ -54,6 +54,7 @@ CIteration::~CIteration(void) { } void CIteration::SetGrid_Movement(CGeometry **geometry, CSurfaceMovement *surface_movement, CVolumetricMovement *grid_movement, + CNumerics ****numerics, CSolver ***solver, CConfig *config, unsigned long IntIter, @@ -127,14 +128,139 @@ void CIteration::SetGrid_Movement(CGeometry **geometry, /*--- Compute the new node locations for moving markers ---*/ - surface_movement->Surface_Plunging(geometry[MESH_0], - config, ExtIter, val_iZone); + // surface_movement->Surface_Plunging(geometry[MESH_0], + // config, + // solver[MESH_0], TimeIter, val_iZone); + + su2double deltaT, time_new, time_old, Lref; + su2double Center[3] = {0.0, 0.0, 0.0}, VarCoord[3], Omega[3], Ampl[3]; + su2double DEG2RAD = PI_NUMBER/180.0; + unsigned short iMarker, jMarker, Moving; + unsigned long iPoint, iVertex; + string Marker_Tag, Moving_Tag; + unsigned short iDim; + + /*--- Initialize the delta variation in coordinates ---*/ + VarCoord[0] = 0.0; VarCoord[1] = 0.0; VarCoord[2] = 0.0; + + /*--- Retrieve values from the config file ---*/ + + deltaT = config->GetDelta_UnstTimeND(); + Lref = config->GetLength_Ref(); + + /*--- Compute delta time based on physical time step ---*/ + time_new = static_cast(ExtIter)*deltaT; + if (ExtIter == 0) { + time_old = time_new; + } else { + time_old = static_cast(ExtIter-1)*deltaT; + } + + /*--- Store displacement of each node on the plunging surface ---*/ + /*--- Loop over markers and find the particular marker(s) (surface) to plunge ---*/ + + for (iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { + Moving = config->GetMarker_All_Moving(iMarker); + if (Moving == YES) { + for (jMarker = 0; jMarkerGetnMarker_Moving(); jMarker++) { + + Moving_Tag = config->GetMarker_Moving_TagBound(jMarker); + Marker_Tag = config->GetMarker_All_TagBound(iMarker); + + if (Marker_Tag == Moving_Tag && (config->GetKind_SurfaceMovement(jMarker) == DEFORMING)) { + + /*--- Plunging frequency and amplitude from config. ---*/ + + for (iDim = 0; iDim < 3; iDim++){ + Ampl[iDim] = config->GetMarkerPlunging_Ampl(jMarker, iDim)/Lref; + Omega[iDim] = config->GetMarkerPlunging_Omega(jMarker, iDim)/config->GetOmega_Ref(); + Center[iDim] = config->GetMarkerMotion_Origin(jMarker, iDim); + } + /*--- Print some information to the console. Be verbose at the first + iteration only (mostly for debugging purposes). ---*/ + // Note that the MASTER_NODE might not contain all the markers being moved. + + if (rank == MASTER_NODE) { + cout << " Storing plunging displacement for marker: "; + cout << Marker_Tag << "." << endl; + if (ExtIter == 0) { + cout << " Plunging frequency: (" << Omega[0] << ", " << Omega[1]; + cout << ", " << Omega[2] << ") rad/s." << endl; + cout << " Plunging amplitude: (" << Ampl[0]/DEG2RAD; + cout << ", " << Ampl[1]/DEG2RAD << ", " << Ampl[2]/DEG2RAD; + cout << ") degrees."<< endl; + } + } + + /*--- Compute delta change in the position in the x, y, & z directions. ---*/ + + VarCoord[0] = -Ampl[0]*(sin(Omega[0]*time_new) - sin(Omega[0]*time_old)); + VarCoord[1] = -Ampl[1]*(sin(Omega[1]*time_new) - sin(Omega[1]*time_old)); + VarCoord[2] = -Ampl[2]*(sin(Omega[2]*time_new) - sin(Omega[2]*time_old)); + + for (iVertex = 0; iVertex < geometry[MESH_0]->nVertex[iMarker]; iVertex++) { + + if (!config->GetDeform_Mesh()) { + + /*--- Set node displacement for volume deformation ---*/ + geometry[MESH_0]->vertex[iMarker][iVertex]->SetVarCoord(VarCoord); + } + else { + + /*--- Get node index ---*/ + iPoint = geometry[MESH_0]->vertex[iMarker][iVertex]->GetNode(); + + solver[MESH_0][MESH_SOL]->node[iPoint]->SetBound_Disp(VarCoord); + } + + } + } + } + } + } + + /*--- When updating the origins it is assumed that all markers have the + same plunging movement, because we use the last VarCoord set ---*/ + + /*--- Set the mesh motion center to the new location after + incrementing the position with the translation. This new + location will be used for subsequent mesh motion for the given marker.---*/ + + for (jMarker=0; jMarkerGetnMarker_Moving(); jMarker++) { + + /*-- Check if we want to update the motion origin for the given marker ---*/ + + if (config->GetMoveMotion_Origin(jMarker) == YES) { + for (iDim = 0; iDim < 3; iDim++){ + Center[iDim] += VarCoord[iDim]; + } + config->SetMarkerMotion_Origin(Center, jMarker); + } + } + + /*--- Set the moment computation center to the new location after + incrementing the position with the plunging. ---*/ + + for (jMarker=0; jMarkerGetnMarker_Monitoring(); jMarker++) { + Center[0] = config->GetRefOriginMoment_X(jMarker) + VarCoord[0]; + Center[1] = config->GetRefOriginMoment_Y(jMarker) + VarCoord[1]; + Center[2] = config->GetRefOriginMoment_Z(jMarker) + VarCoord[2]; + config->SetRefOriginMoment_X(jMarker, Center[0]); + config->SetRefOriginMoment_Y(jMarker, Center[1]); + config->SetRefOriginMoment_Z(jMarker, Center[2]); + } + /*--- Deform the volume grid around the new boundary locations ---*/ if (rank == MASTER_NODE) cout << " Deforming the volume grid." << endl; - grid_movement->SetVolume_Deformation(geometry[MESH_0], + if (!config->GetDeform_Mesh()) { + grid_movement->SetVolume_Deformation(geometry[MESH_0], config, true); + } + else { + SetMesh_Deformation(geometry, solver[MESH_0], numerics[MESH_0], config, NONE); + } /*--- Pitching ---*/ @@ -651,7 +777,7 @@ void CFluidIteration::Iterate(COutput *output, if ((config[val_iZone]->GetGrid_Movement()) && (config[val_iZone]->GetAeroelastic_Simulation()) && unsteady) { - SetGrid_Movement(geometry[val_iZone][val_iInst], surface_movement[val_iZone], grid_movement[val_iZone][val_iInst], + SetGrid_Movement(geometry[val_iZone][val_iInst], surface_movement[val_iZone], grid_movement[val_iZone][val_iInst], numerics[val_iZone][val_iInst], solver[val_iZone][val_iInst], config[val_iZone], IntIter, ExtIter); /*--- Apply a Wind Gust ---*/ diff --git a/SU2_CFD/src/solver_direct_mean.cpp b/SU2_CFD/src/solver_direct_mean.cpp index 5ac515b4373b..ab8e6fd9a15d 100644 --- a/SU2_CFD/src/solver_direct_mean.cpp +++ b/SU2_CFD/src/solver_direct_mean.cpp @@ -12869,7 +12869,7 @@ void CEulerSolver::LoadRestart(CGeometry **geometry, CSolver ***solver, CConfig /*--- Update the old geometry (coordinates n and n-1) in dual time-stepping strategy. ---*/ if (dual_time && config->GetGrid_Movement() && (config->GetKind_GridMovement() != RIGID_MOTION)) - Restart_OldGeometry(geometry[MESH_0], config); + Restart_OldGeometry(geometry[MESH_0], config);//solver[iMesh][MESH_SOL]->Restart_OldGeometry(geometry[MESH_0], config); delete [] Coord; diff --git a/SU2_CFD/src/solver_structure.cpp b/SU2_CFD/src/solver_structure.cpp index 1d96521d5b34..21f1c7c23b9f 100644 --- a/SU2_CFD/src/solver_structure.cpp +++ b/SU2_CFD/src/solver_structure.cpp @@ -3995,7 +3995,8 @@ void CSolver::Restart_OldGeometry(CGeometry *geometry, CConfig *config) { /*-------------------------------------------------------------------------------------------*/ /*--- Modify file name for an unsteady restart ---*/ - Unst_RestartIter = SU2_TYPE::Int(config->GetUnst_RestartIter())-1; + if (config->GetRestart()) Unst_RestartIter = SU2_TYPE::Int(config->GetRestart_Iter())-1; + else Unst_RestartIter = SU2_TYPE::Int(config->GetUnst_AdjointIter())-1; filename_n = config->GetUnsteady_FileName(filename, Unst_RestartIter); /*--- Open the restart file, throw an error if this fails. ---*/ @@ -4066,7 +4067,8 @@ void CSolver::Restart_OldGeometry(CGeometry *geometry, CConfig *config) { string filename_n1; /*--- Modify file name for an unsteady restart ---*/ - Unst_RestartIter = SU2_TYPE::Int(config->GetUnst_RestartIter())-2; + if (config->GetRestart()) Unst_RestartIter = SU2_TYPE::Int(config->GetRestart_Iter())-2; + else Unst_RestartIter = SU2_TYPE::Int(config->GetUnst_AdjointIter())-2; filename_n1 = config->GetUnsteady_FileName(filename, Unst_RestartIter); /*--- Open the restart file, throw an error if this fails. ---*/ From 21abcba32c690c3b62025718d29466a5cd46396f Mon Sep 17 00:00:00 2001 From: cvencro Date: Mon, 14 Oct 2019 07:19:49 +0100 Subject: [PATCH 002/112] Attempt Surface Plunging inside CMeshSolver --- Common/src/grid_movement_structure.cpp | 2 +- SU2_CFD/include/solver_structure.hpp | 2 + SU2_CFD/include/solver_structure.inl | 2 + SU2_CFD/include/solvers/CMeshSolver.hpp | 1 + SU2_CFD/src/iteration_structure.cpp | 9 +- SU2_CFD/src/solvers/CMeshSolver.cpp | 125 ++++++++++++++++++++++++ 6 files changed, 138 insertions(+), 3 deletions(-) diff --git a/Common/src/grid_movement_structure.cpp b/Common/src/grid_movement_structure.cpp index 054e4b2d410e..3f7103d9f0bd 100644 --- a/Common/src/grid_movement_structure.cpp +++ b/Common/src/grid_movement_structure.cpp @@ -5790,7 +5790,7 @@ void CSurfaceMovement::Surface_Translating(CGeometry *geometry, CConfig *config, } } -void CSurfaceMovement::Surface_Plunging(CGeometry *geometry, CConfig *config, //CSolver **solver, +void CSurfaceMovement::Surface_Plunging(CGeometry *geometry, CConfig *config, unsigned long iter, unsigned short iZone) { su2double deltaT, time_new, time_old, Lref; diff --git a/SU2_CFD/include/solver_structure.hpp b/SU2_CFD/include/solver_structure.hpp index e9ba63fa2743..c32e27c22769 100644 --- a/SU2_CFD/include/solver_structure.hpp +++ b/SU2_CFD/include/solver_structure.hpp @@ -4333,6 +4333,8 @@ class CSolver { */ virtual void DeformMesh(CGeometry **geometry, CNumerics **numerics, CConfig *config); + // virtual void Surface_Plunging(CGeometry *geometry, CConfig *config, CSolver **solver, unsigned long iter, unsigned short iZone); + /*! * \brief A virtual member. * \param[in] geometry - Geometrical definition. diff --git a/SU2_CFD/include/solver_structure.inl b/SU2_CFD/include/solver_structure.inl index 28e9c0921f43..b974dfc05529 100644 --- a/SU2_CFD/include/solver_structure.inl +++ b/SU2_CFD/include/solver_structure.inl @@ -1150,6 +1150,8 @@ inline void CSolver::SetDES_LengthScale(CSolver** solver, CGeometry *geometry, C inline void CSolver::DeformMesh(CGeometry **geometry, CNumerics **numerics, CConfig *config) { } +// inline void CSolver::Surface_Plunging(CGeometry *geometry, CConfig *config, CSolver **solver, unsigned long iter, unsigned short iZone) { } + inline void CSolver::SetMesh_Stiffness(CGeometry **geometry, CNumerics **numerics, CConfig *config) { } inline void CSolver::SetConjugateHeatVariable(unsigned short val_marker, unsigned long val_vertex, unsigned short pos_var, su2double relaxation_factor, su2double val_var) { } diff --git a/SU2_CFD/include/solvers/CMeshSolver.hpp b/SU2_CFD/include/solvers/CMeshSolver.hpp index fbc32516efce..ed0deae5166d 100644 --- a/SU2_CFD/include/solvers/CMeshSolver.hpp +++ b/SU2_CFD/include/solvers/CMeshSolver.hpp @@ -189,4 +189,5 @@ class CMeshSolver : public CFEASolver { node[iPoint]->Set_OldSolution(); } + // void Surface_Plunging(CGeometry *geometry, CConfig *config, CSolver **solver, unsigned long iter, unsigned short iZone); }; diff --git a/SU2_CFD/src/iteration_structure.cpp b/SU2_CFD/src/iteration_structure.cpp index c71d8b66a40f..bb26b0f1f98c 100644 --- a/SU2_CFD/src/iteration_structure.cpp +++ b/SU2_CFD/src/iteration_structure.cpp @@ -128,9 +128,14 @@ void CIteration::SetGrid_Movement(CGeometry **geometry, /*--- Compute the new node locations for moving markers ---*/ - // surface_movement->Surface_Plunging(geometry[MESH_0], + // if (!config->GetDeform_Mesh()) { + // surface_movement->Surface_Plunging(geometry[MESH_0], // config, - // solver[MESH_0], TimeIter, val_iZone); + // ExtIter, val_iZone); + // } + // else { + // //solver[MESH_0][MESH_SOL]->Surface_Plunging(geometry[MESH_0], config, solver[MESH_0], ExtIter, val_iZone); + // } su2double deltaT, time_new, time_old, Lref; su2double Center[3] = {0.0, 0.0, 0.0}, VarCoord[3], Omega[3], Ampl[3]; diff --git a/SU2_CFD/src/solvers/CMeshSolver.cpp b/SU2_CFD/src/solvers/CMeshSolver.cpp index 80c64b9666fa..e5dcc0319af8 100644 --- a/SU2_CFD/src/solvers/CMeshSolver.cpp +++ b/SU2_CFD/src/solvers/CMeshSolver.cpp @@ -1082,3 +1082,128 @@ void CMeshSolver::Restart_OldGeometry(CGeometry *geometry, CConfig *config) { } } + +// void Surface_Plunging(CGeometry *geometry, CConfig *config, CSolver **solver, unsigned long iter, unsigned short iZone) { + +// // surface_movement->Surface_Plunging(geometry[MESH_0], +// // config, +// // solver[MESH_0], TimeIter, val_iZone); + +// su2double deltaT, time_new, time_old, Lref; +// su2double Center[3] = {0.0, 0.0, 0.0}, VarCoord[3], Omega[3], Ampl[3]; +// su2double DEG2RAD = PI_NUMBER/180.0; +// unsigned short iMarker, jMarker, Moving; +// unsigned long iPoint, iVertex; +// string Marker_Tag, Moving_Tag; +// unsigned short iDim; + +// /*--- Initialize the delta variation in coordinates ---*/ +// VarCoord[0] = 0.0; VarCoord[1] = 0.0; VarCoord[2] = 0.0; + +// /*--- Retrieve values from the config file ---*/ + +// deltaT = config->GetDelta_UnstTimeND(); +// Lref = config->GetLength_Ref(); + +// /*--- Compute delta time based on physical time step ---*/ +// time_new = static_cast(iter)*deltaT; +// if (iter == 0) { +// time_old = time_new; +// } else { +// time_old = static_cast(iter-1)*deltaT; +// } + +// /*--- Store displacement of each node on the plunging surface ---*/ +// /*--- Loop over markers and find the particular marker(s) (surface) to plunge ---*/ + +// for (iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { +// Moving = config->GetMarker_All_Moving(iMarker); +// if (Moving == YES) { +// for (jMarker = 0; jMarkerGetnMarker_Moving(); jMarker++) { + +// Moving_Tag = config->GetMarker_Moving_TagBound(jMarker); +// Marker_Tag = config->GetMarker_All_TagBound(iMarker); + +// if (Marker_Tag == Moving_Tag && (config->GetKind_SurfaceMovement(jMarker) == DEFORMING)) { + +// /*--- Plunging frequency and amplitude from config. ---*/ + +// for (iDim = 0; iDim < 3; iDim++){ +// Ampl[iDim] = config->GetMarkerPlunging_Ampl(jMarker, iDim)/Lref; +// Omega[iDim] = config->GetMarkerPlunging_Omega(jMarker, iDim)/config->GetOmega_Ref(); +// Center[iDim] = config->GetMarkerMotion_Origin(jMarker, iDim); +// } +// /*--- Print some information to the console. Be verbose at the first +// iteration only (mostly for debugging purposes). ---*/ +// // Note that the MASTER_NODE might not contain all the markers being moved. + +// if (SU2_MPI::GetRank() == MASTER_NODE) { +// cout << " Storing plunging displacement for marker: "; +// cout << Marker_Tag << "." << endl; +// if (iter == 0) { +// cout << " Plunging frequency: (" << Omega[0] << ", " << Omega[1]; +// cout << ", " << Omega[2] << ") rad/s." << endl; +// cout << " Plunging amplitude: (" << Ampl[0]/DEG2RAD; +// cout << ", " << Ampl[1]/DEG2RAD << ", " << Ampl[2]/DEG2RAD; +// cout << ") degrees."<< endl; +// } +// } + +// /*--- Compute delta change in the position in the x, y, & z directions. ---*/ + +// VarCoord[0] = -Ampl[0]*(sin(Omega[0]*time_new) - sin(Omega[0]*time_old)); +// VarCoord[1] = -Ampl[1]*(sin(Omega[1]*time_new) - sin(Omega[1]*time_old)); +// VarCoord[2] = -Ampl[2]*(sin(Omega[2]*time_new) - sin(Omega[2]*time_old)); + +// for (iVertex = 0; iVertex < geometry->nVertex[iMarker]; iVertex++) { + +// if (!config->GetDeform_Mesh()) { + +// /*--- Set node displacement for volume deformation ---*/ +// geometry->vertex[iMarker][iVertex]->SetVarCoord(VarCoord); +// } +// else { + +// /*--- Get node index ---*/ +// iPoint = geometry->vertex[iMarker][iVertex]->GetNode(); + +// solver[MESH_SOL]->node[iPoint]->SetBound_Disp(VarCoord); +// } + +// } +// } +// } +// } +// } + +// /*--- When updating the origins it is assumed that all markers have the +// same plunging movement, because we use the last VarCoord set ---*/ + +// /*--- Set the mesh motion center to the new location after +// incrementing the position with the translation. This new +// location will be used for subsequent mesh motion for the given marker.---*/ + +// for (jMarker=0; jMarkerGetnMarker_Moving(); jMarker++) { + +// /*-- Check if we want to update the motion origin for the given marker ---*/ + +// if (config->GetMoveMotion_Origin(jMarker) == YES) { +// for (iDim = 0; iDim < 3; iDim++){ +// Center[iDim] += VarCoord[iDim]; +// } +// config->SetMarkerMotion_Origin(Center, jMarker); +// } +// } + +// /*--- Set the moment computation center to the new location after +// incrementing the position with the plunging. ---*/ + +// for (jMarker=0; jMarkerGetnMarker_Monitoring(); jMarker++) { +// Center[0] = config->GetRefOriginMoment_X(jMarker) + VarCoord[0]; +// Center[1] = config->GetRefOriginMoment_Y(jMarker) + VarCoord[1]; +// Center[2] = config->GetRefOriginMoment_Z(jMarker) + VarCoord[2]; +// config->SetRefOriginMoment_X(jMarker, Center[0]); +// config->SetRefOriginMoment_Y(jMarker, Center[1]); +// config->SetRefOriginMoment_Z(jMarker, Center[2]); +// } +// } \ No newline at end of file From ddc36495ef5ea824632aece0126ba8ef7bf84189 Mon Sep 17 00:00:00 2001 From: cvencro Date: Tue, 15 Oct 2019 12:41:11 +0100 Subject: [PATCH 003/112] Initial implementation of surface pitching, rotating, translating with new mesh solver --- SU2_CFD/src/iteration_structure.cpp | 615 ++++++++++++++++++++++++++-- 1 file changed, 582 insertions(+), 33 deletions(-) diff --git a/SU2_CFD/src/iteration_structure.cpp b/SU2_CFD/src/iteration_structure.cpp index bb26b0f1f98c..0a1d2b49cade 100644 --- a/SU2_CFD/src/iteration_structure.cpp +++ b/SU2_CFD/src/iteration_structure.cpp @@ -115,19 +115,153 @@ void CIteration::SetGrid_Movement(CGeometry **geometry, /*--- Compute the new node locations for moving markers ---*/ - surface_movement->Surface_Translating(geometry[MESH_0], - config, ExtIter, val_iZone); - /*--- Deform the volume grid around the new boundary locations ---*/ + /*----------------------------------------------------------------------------------------------------------*/ + /*----------------------------------------------------------------------------------------------------------*/ + /*----------------------------------------------------------------------------------------------------------*/ + // CVC: To do: For deformation with new mesh solver, need to access solver[MESH_0][MESH_SOL] for SetBound_Disp + // inside Surface_Movement functions, rest of code is identical to grid_movement_structure.cpp + // Either enable solver[MESH_0][MESH_SOL] inside grid_movement_structure.cpp + // or move Surface_Movement functions to inside CMeshSolver + + // surface_movement->Surface_Translating(geometry[MESH_0], + // config, ExtIter, val_iZone); + + su2double deltaT, time_new, time_old, Lref, *Coord; + su2double Center[3] = {0.0,0.0,0.0}, VarCoord[3] = {0.0,0.0,0.0}, VarCoordAbs[3] = {0.0, 0.0, 0.0}; + su2double xDot[3] = {0.0,0.0,0.0}; + unsigned short iMarker, jMarker, Moving; + unsigned long iPoint, iVertex; + string Marker_Tag, Moving_Tag; + unsigned short iDim, nDim = geometry[MESH_0]->GetnDim(); + + /*--- Initialize the delta variation in coordinates ---*/ + VarCoord[0] = 0.0; VarCoord[1] = 0.0; VarCoord[2] = 0.0; + + /*--- Retrieve values from the config file ---*/ + + deltaT = config->GetDelta_UnstTimeND(); + + /*--- Compute delta time based on physical time step ---*/ + time_new = static_cast(ExtIter)*deltaT; + if (ExtIter == 0) { + time_old = time_new; + } else { + time_old = static_cast(ExtIter-1)*deltaT; + } + + /*--- Store displacement of each node on the translating surface ---*/ + /*--- Loop over markers and find the particular marker(s) (surface) to translate ---*/ + + for (iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { + Moving = config->GetMarker_All_Moving(iMarker); + if (Moving == YES) { + for (jMarker = 0; jMarkerGetnMarker_Moving(); jMarker++) { + + Moving_Tag = config->GetMarker_Moving_TagBound(jMarker); + Marker_Tag = config->GetMarker_All_TagBound(iMarker); + + if (Marker_Tag == Moving_Tag && (config->GetKind_SurfaceMovement(jMarker) == DEFORMING)) { - if (rank == MASTER_NODE) + for (iDim = 0; iDim < 3; iDim++){ + xDot[iDim] = config->GetMarkerTranslationRate(jMarker, iDim); + Center[iDim] = config->GetMarkerMotion_Origin(jMarker, iDim); + } + + /*--- Print some information to the console. Be verbose at the first + iteration only (mostly for debugging purposes). ---*/ + // Note that the MASTER_NODE might not contain all the markers being moved. + + if (rank == MASTER_NODE) { + cout << " Storing translating displacement for marker: "; + cout << Marker_Tag << "." << endl; + if (ExtIter == 0) { + cout << " Translational velocity: (" << xDot[0]*config->GetVelocity_Ref() << ", " << xDot[1]*config->GetVelocity_Ref(); + cout << ", " << xDot[2]*config->GetVelocity_Ref(); + if (config->GetSystemMeasurements() == SI) cout << ") m/s." << endl; + else cout << ") ft/s." << endl; + } + } + + /*--- Compute delta change in the position in the x, y, & z directions. ---*/ + + VarCoord[0] = xDot[0]*(time_new-time_old); + VarCoord[1] = xDot[1]*(time_new-time_old); + VarCoord[2] = xDot[2]*(time_new-time_old); + + for (iVertex = 0; iVertex < geometry[MESH_0]->nVertex[iMarker]; iVertex++) { + + if (!config->GetDeform_Mesh()) { + + /*--- Set node displacement for volume deformation ---*/ + geometry[MESH_0]->vertex[iMarker][iVertex]->SetVarCoord(VarCoord); + } + else { + iPoint = geometry[MESH_0]->vertex[iMarker][iVertex]->GetNode(); + + for (iDim = 0; iDim < 3; iDim++){ + VarCoordAbs[iDim] = solver[MESH_0][MESH_SOL]->node[iPoint]->GetBound_Disp(iDim) + VarCoord[iDim]; + } + + solver[MESH_0][MESH_SOL]->node[iPoint]->SetBound_Disp(VarCoordAbs); + } + } + } + } + } + } + + /*--- When updating the origins it is assumed that all markers have the + same translational velocity, because we use the last VarCoord set ---*/ + + /*--- Set the mesh motion center to the new location after + incrementing the position with the translation. This new + location will be used for subsequent mesh motion for the given marker.---*/ + + for (jMarker=0; jMarkerGetnMarker_Moving(); jMarker++) { + + /*-- Check if we want to update the motion origin for the given marker ---*/ + + if (config->GetMoveMotion_Origin(jMarker) == YES) { + for (iDim = 0; iDim < 3; iDim++){ + Center[iDim] += VarCoord[iDim]; + } + config->SetMarkerMotion_Origin(Center, jMarker); + } + } + + /*--- Set the moment computation center to the new location after + incrementing the position with the translation. ---*/ + + for (jMarker=0; jMarkerGetnMarker_Monitoring(); jMarker++) { + Center[0] = config->GetRefOriginMoment_X(jMarker) + VarCoord[0]; + Center[1] = config->GetRefOriginMoment_Y(jMarker) + VarCoord[1]; + Center[2] = config->GetRefOriginMoment_Z(jMarker) + VarCoord[2]; + config->SetRefOriginMoment_X(jMarker, Center[0]); + config->SetRefOriginMoment_Y(jMarker, Center[1]); + config->SetRefOriginMoment_Z(jMarker, Center[2]); + } + + /*----------------------------------------------------------------------------------------------------------*/ + /*----------------------------------------------------------------------------------------------------------*/ + /*----------------------------------------------------------------------------------------------------------*/ + + /*--- Deform the volume grid around the new boundary locations ---*/ + /*--- Set volume deformation if new elastic mesh solver is not used ---*/ + /*--- If Deform_Mesh true, the mesh deformation is handled independently ---*/ + if (rank == MASTER_NODE && !config->GetDeform_Mesh()) { cout << " Deforming the volume grid." << endl; - grid_movement->SetVolume_Deformation(geometry[MESH_0], + grid_movement->SetVolume_Deformation(geometry[MESH_0], config, true); + } /*--- Plunging ---*/ /*--- Compute the new node locations for moving markers ---*/ + /*----------------------------------------------------------------------------------------------------------*/ + /*----------------------------------------------------------------------------------------------------------*/ + /*----------------------------------------------------------------------------------------------------------*/ + // if (!config->GetDeform_Mesh()) { // surface_movement->Surface_Plunging(geometry[MESH_0], // config, @@ -137,13 +271,14 @@ void CIteration::SetGrid_Movement(CGeometry **geometry, // //solver[MESH_0][MESH_SOL]->Surface_Plunging(geometry[MESH_0], config, solver[MESH_0], ExtIter, val_iZone); // } - su2double deltaT, time_new, time_old, Lref; - su2double Center[3] = {0.0, 0.0, 0.0}, VarCoord[3], Omega[3], Ampl[3]; + // su2double deltaT, time_new, time_old, Lref; + // su2double Center[3] = {0.0, 0.0, 0.0}, VarCoord[3], VarCoordAbs[3], VarCoordAbs[3] = {0.0, 0.0, 0.0}; + su2double Omega[3], Ampl[3]; su2double DEG2RAD = PI_NUMBER/180.0; - unsigned short iMarker, jMarker, Moving; - unsigned long iPoint, iVertex; - string Marker_Tag, Moving_Tag; - unsigned short iDim; + // unsigned short iMarker, jMarker, Moving; + // unsigned long iPoint, iVertex; + // string Marker_Tag, Moving_Tag; + // unsigned short iDim, nDim = geometry[MESH_0]->GetnDim(); /*--- Initialize the delta variation in coordinates ---*/ VarCoord[0] = 0.0; VarCoord[1] = 0.0; VarCoord[2] = 0.0; @@ -202,22 +337,24 @@ void CIteration::SetGrid_Movement(CGeometry **geometry, VarCoord[0] = -Ampl[0]*(sin(Omega[0]*time_new) - sin(Omega[0]*time_old)); VarCoord[1] = -Ampl[1]*(sin(Omega[1]*time_new) - sin(Omega[1]*time_old)); VarCoord[2] = -Ampl[2]*(sin(Omega[2]*time_new) - sin(Omega[2]*time_old)); - + for (iVertex = 0; iVertex < geometry[MESH_0]->nVertex[iMarker]; iVertex++) { - + if (!config->GetDeform_Mesh()) { /*--- Set node displacement for volume deformation ---*/ geometry[MESH_0]->vertex[iMarker][iVertex]->SetVarCoord(VarCoord); } else { - - /*--- Get node index ---*/ + /* --- Get node index ---*/ iPoint = geometry[MESH_0]->vertex[iMarker][iVertex]->GetNode(); - solver[MESH_0][MESH_SOL]->node[iPoint]->SetBound_Disp(VarCoord); + for (iDim = 0; iDim < 3; iDim++){ + VarCoordAbs[iDim] = solver[MESH_0][MESH_SOL]->node[iPoint]->GetBound_Disp(iDim) + VarCoord[iDim]; + } + + solver[MESH_0][MESH_SOL]->node[iPoint]->SetBound_Disp(VarCoordAbs); } - } } } @@ -255,43 +392,455 @@ void CIteration::SetGrid_Movement(CGeometry **geometry, config->SetRefOriginMoment_Z(jMarker, Center[2]); } - /*--- Deform the volume grid around the new boundary locations ---*/ + /*----------------------------------------------------------------------------------------------------------*/ + /*----------------------------------------------------------------------------------------------------------*/ + /*----------------------------------------------------------------------------------------------------------*/ - if (rank == MASTER_NODE) + /*--- Deform the volume grid around the new boundary locations ---*/ + /*--- Set volume deformation if new elastic mesh solver is not used ---*/ + /*--- If Deform_Mesh true, the mesh deformation is handled independently ---*/ + if (rank == MASTER_NODE && !config->GetDeform_Mesh()) { cout << " Deforming the volume grid." << endl; - if (!config->GetDeform_Mesh()) { grid_movement->SetVolume_Deformation(geometry[MESH_0], config, true); } - else { - SetMesh_Deformation(geometry, solver[MESH_0], numerics[MESH_0], config, NONE); - } /*--- Pitching ---*/ /*--- Compute the new node locations for moving markers ---*/ - surface_movement->Surface_Pitching(geometry[MESH_0], - config, ExtIter, val_iZone); - /*--- Deform the volume grid around the new boundary locations ---*/ + /*----------------------------------------------------------------------------------------------------------*/ + /*----------------------------------------------------------------------------------------------------------*/ + /*----------------------------------------------------------------------------------------------------------*/ + + // surface_movement->Surface_Pitching(geometry[MESH_0], + // config, ExtIter, val_iZone); + + //su2double deltaT, time_new, time_old, Lref, *Coord; + //su2double Center[3], VarCoord[3], Omega[3], Ampl[3], VarCoordAbs[3] = {0.0, 0.0, 0.0}; + su2double Phase[3]; + su2double rotCoord[3], r[3] = {0.0,0.0,0.0}; + su2double rotMatrix[3][3] = {{0.0,0.0,0.0}, {0.0,0.0,0.0}, {0.0,0.0,0.0}}; + su2double dtheta, dphi, dpsi, cosTheta, sinTheta; + su2double cosPhi, sinPhi, cosPsi, sinPsi; + //su2double DEG2RAD = PI_NUMBER/180.0; + //unsigned short iMarker, jMarker, Moving, iDim, nDim = geometry[MESH_0]->GetnDim(); + //unsigned long iPoint, iVertex; + //string Marker_Tag, Moving_Tag; + + /*--- Initialize the delta variation in coordinates ---*/ + VarCoord[0] = 0.0; VarCoord[1] = 0.0; VarCoord[2] = 0.0; + + /*--- Retrieve values from the config file ---*/ + + deltaT = config->GetDelta_UnstTimeND(); + Lref = config->GetLength_Ref(); + + /*--- Compute delta time based on physical time step ---*/ + time_new = static_cast(ExtIter)*deltaT; + if (ExtIter == 0) { + time_old = time_new; + } else { + time_old = static_cast(ExtIter-1)*deltaT; + } - if (rank == MASTER_NODE) + /*--- Store displacement of each node on the pitching surface ---*/ + /*--- Loop over markers and find the particular marker(s) (surface) to pitch ---*/ + + for (iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { + Moving = config->GetMarker_All_Moving(iMarker); + if (Moving == YES) { + for (jMarker = 0; jMarkerGetnMarker_Moving(); jMarker++) { + Moving_Tag = config->GetMarker_Moving_TagBound(jMarker); + Marker_Tag = config->GetMarker_All_TagBound(iMarker); + + if (Marker_Tag == Moving_Tag && (config->GetKind_SurfaceMovement(jMarker) == DEFORMING)) { + /*--- Pitching origin, frequency, and amplitude from config. ---*/ + + for (iDim = 0; iDim < 3; iDim++){ + Ampl[iDim] = config->GetMarkerPitching_Ampl(jMarker, iDim)*DEG2RAD; + Omega[iDim] = config->GetMarkerPitching_Omega(jMarker, iDim)/config->GetOmega_Ref(); + Phase[iDim] = config->GetMarkerPitching_Phase(jMarker, iDim)*DEG2RAD; + Center[iDim] = config->GetMarkerMotion_Origin(jMarker, iDim); + } + /*--- Print some information to the console. Be verbose at the first + iteration only (mostly for debugging purposes). ---*/ + // Note that the MASTER_NODE might not contain all the markers being moved. + + if (rank == MASTER_NODE) { + cout << " Storing pitching displacement for marker: "; + cout << Marker_Tag << "." << endl; + if (ExtIter == 0) { + cout << " Pitching frequency: (" << Omega[0] << ", " << Omega[1]; + cout << ", " << Omega[2] << ") rad/s about origin: (" << Center[0]; + cout << ", " << Center[1] << ", " << Center[2] << ")." << endl; + cout << " Pitching amplitude about origin: (" << Ampl[0]/DEG2RAD; + cout << ", " << Ampl[1]/DEG2RAD << ", " << Ampl[2]/DEG2RAD; + cout << ") degrees."<< endl; + cout << " Pitching phase lag about origin: (" << Phase[0]/DEG2RAD; + cout << ", " << Phase[1]/DEG2RAD <<", "<< Phase[2]/DEG2RAD; + cout << ") degrees."<< endl; + } + } + + /*--- Compute delta change in the angle about the x, y, & z axes. ---*/ + + dtheta = -Ampl[0]*(sin(Omega[0]*time_new + Phase[0]) + - sin(Omega[0]*time_old + Phase[0])); + dphi = -Ampl[1]*(sin(Omega[1]*time_new + Phase[1]) + - sin(Omega[1]*time_old + Phase[1])); + dpsi = -Ampl[2]*(sin(Omega[2]*time_new + Phase[2]) + - sin(Omega[2]*time_old + Phase[2])); + + /*--- Store angles separately for clarity. Compute sines/cosines. ---*/ + + cosTheta = cos(dtheta); cosPhi = cos(dphi); cosPsi = cos(dpsi); + sinTheta = sin(dtheta); sinPhi = sin(dphi); sinPsi = sin(dpsi); + + /*--- Compute the rotation matrix. Note that the implicit + ordering is rotation about the x-axis, y-axis, then z-axis. ---*/ + + rotMatrix[0][0] = cosPhi*cosPsi; + rotMatrix[1][0] = cosPhi*sinPsi; + rotMatrix[2][0] = -sinPhi; + + rotMatrix[0][1] = sinTheta*sinPhi*cosPsi - cosTheta*sinPsi; + rotMatrix[1][1] = sinTheta*sinPhi*sinPsi + cosTheta*cosPsi; + rotMatrix[2][1] = sinTheta*cosPhi; + + rotMatrix[0][2] = cosTheta*sinPhi*cosPsi + sinTheta*sinPsi; + rotMatrix[1][2] = cosTheta*sinPhi*sinPsi - sinTheta*cosPsi; + rotMatrix[2][2] = cosTheta*cosPhi; + + for (iVertex = 0; iVertex < geometry[MESH_0]->nVertex[iMarker]; iVertex++) { + + /*--- Index and coordinates of the current point ---*/ + + iPoint = geometry[MESH_0]->vertex[iMarker][iVertex]->GetNode(); + if (!config->GetDeform_Mesh()) { + Coord = geometry[MESH_0]->node[iPoint]->GetCoord(); + } + else { + Coord = solver[MESH_0][MESH_SOL]->node[iPoint]->GetMesh_Coord(); + } + + /*--- Calculate non-dim. position from rotation center ---*/ + + for (iDim = 0; iDim < nDim; iDim++) + r[iDim] = (Coord[iDim]-Center[iDim])/Lref; + if (nDim == 2) r[nDim] = 0.0; + + /*--- Compute transformed point coordinates ---*/ + + rotCoord[0] = rotMatrix[0][0]*r[0] + + rotMatrix[0][1]*r[1] + + rotMatrix[0][2]*r[2] + Center[0]; + + rotCoord[1] = rotMatrix[1][0]*r[0] + + rotMatrix[1][1]*r[1] + + rotMatrix[1][2]*r[2] + Center[1]; + + rotCoord[2] = rotMatrix[2][0]*r[0] + + rotMatrix[2][1]*r[1] + + rotMatrix[2][2]*r[2] + Center[2]; + + /*--- Calculate delta change in the x, y, & z directions ---*/ + for (iDim = 0; iDim < nDim; iDim++) + VarCoord[iDim] = (rotCoord[iDim]-Coord[iDim])/Lref; + if (nDim == 2) VarCoord[nDim] = 0.0; + + if (!config->GetDeform_Mesh()) { + + /*--- Set node displacement for volume deformation ---*/ + geometry[MESH_0]->vertex[iMarker][iVertex]->SetVarCoord(VarCoord); + } + else { + + for (iDim = 0; iDim < 3; iDim++){ + VarCoordAbs[iDim] = solver[MESH_0][MESH_SOL]->node[iPoint]->GetBound_Disp(iDim) + VarCoord[iDim]; + } + + solver[MESH_0][MESH_SOL]->node[iPoint]->SetBound_Disp(VarCoordAbs); + } + } + } + } + } + } + /*--- For pitching we don't update the motion origin and moment reference origin. ---*/ + + /*----------------------------------------------------------------------------------------------------------*/ + /*----------------------------------------------------------------------------------------------------------*/ + /*----------------------------------------------------------------------------------------------------------*/ + + /*--- Deform the volume grid around the new boundary locations ---*/ + /*--- Set volume deformation if new elastic mesh solver is not used ---*/ + /*--- If Deform_Mesh true, the mesh deformation is handled independently ---*/ + if (rank == MASTER_NODE && !config->GetDeform_Mesh()) { cout << " Deforming the volume grid." << endl; - grid_movement->SetVolume_Deformation(geometry[MESH_0], + grid_movement->SetVolume_Deformation(geometry[MESH_0], config, true); + } /*--- Rotating ---*/ /*--- Compute the new node locations for moving markers ---*/ - surface_movement->Surface_Rotating(geometry[MESH_0], - config, ExtIter, val_iZone); - /*--- Deform the volume grid around the new boundary locations ---*/ + /*----------------------------------------------------------------------------------------------------------*/ + /*----------------------------------------------------------------------------------------------------------*/ + /*----------------------------------------------------------------------------------------------------------*/ + + // surface_movement->Surface_Rotating(geometry[MESH_0], + // config, ExtIter, val_iZone); + + //su2double deltaT, time_new, time_old, Lref, *Coord; + //su2double Center[3] = {0.0,0.0,0.0}, VarCoord[3] = {0.0,0.0,0.0}, Omega[3] = {0.0,0.0,0.0}, VarCoordAbs[3] = {0.0, 0.0, 0.0}; + //rotCoord[3] = {0.0,0.0,0.0}, r[3] = {0.0,0.0,0.0}, + su2double Center_Aux[3] = {0.0,0.0,0.0}; + //su2double rotMatrix[3][3] = {{0.0,0.0,0.0}, {0.0,0.0,0.0}, {0.0,0.0,0.0}}; + //su2double dtheta, dphi, dpsi, cosTheta, sinTheta; + //su2double cosPhi, sinPhi, cosPsi, sinPsi; + // unsigned short iMarker, jMarker, Moving, iDim, nDim = geometry[MESH_0]->GetnDim(); + // unsigned long iPoint, iVertex; + // string Marker_Tag, Moving_Tag; + + /*--- Initialize the delta variation in coordinates ---*/ + VarCoord[0] = 0.0; VarCoord[1] = 0.0; VarCoord[2] = 0.0; + + /*--- Retrieve values from the config file ---*/ + + deltaT = config->GetDelta_UnstTimeND(); + Lref = config->GetLength_Ref(); + + /*--- Compute delta time based on physical time step ---*/ + time_new = static_cast(ExtIter)*deltaT; + if (ExtIter == 0) { + time_old = time_new; + } else { + time_old = static_cast(ExtIter-1)*deltaT; + } + + /*--- Store displacement of each node on the rotating surface ---*/ + /*--- Loop over markers and find the particular marker(s) (surface) to rotate ---*/ - if (rank == MASTER_NODE) + for (iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { + Moving = config->GetMarker_All_Moving(iMarker); + if (Moving == YES) { + for (jMarker = 0; jMarkerGetnMarker_Moving(); jMarker++) { + + Moving_Tag = config->GetMarker_Moving_TagBound(jMarker); + Marker_Tag = config->GetMarker_All_TagBound(iMarker); + + if (Marker_Tag == Moving_Tag && (config->GetKind_SurfaceMovement(jMarker) == DEFORMING)) { + + /*--- Rotation origin and angular velocity from config. ---*/ + + for (iDim = 0; iDim < 3; iDim++){ + Omega[iDim] = config->GetMarkerRotationRate(jMarker, iDim)/config->GetOmega_Ref(); + Center[iDim] = config->GetMarkerMotion_Origin(jMarker, iDim); + } + /*--- Print some information to the console. Be verbose at the first + iteration only (mostly for debugging purposes). ---*/ + // Note that the MASTER_NODE might not contain all the markers being moved. + + if (rank == MASTER_NODE) { + cout << " Storing rotating displacement for marker: "; + cout << Marker_Tag << "." << endl; + if (ExtIter == 0) { + cout << " Angular velocity: (" << Omega[0] << ", " << Omega[1]; + cout << ", " << Omega[2] << ") rad/s about origin: (" << Center[0]; + cout << ", " << Center[1] << ", " << Center[2] << ")." << endl; + } + } + + /*--- Compute delta change in the angle about the x, y, & z axes. ---*/ + + dtheta = Omega[0]*(time_new-time_old); + dphi = Omega[1]*(time_new-time_old); + dpsi = Omega[2]*(time_new-time_old); + + /*--- Store angles separately for clarity. Compute sines/cosines. ---*/ + + cosTheta = cos(dtheta); cosPhi = cos(dphi); cosPsi = cos(dpsi); + sinTheta = sin(dtheta); sinPhi = sin(dphi); sinPsi = sin(dpsi); + + /*--- Compute the rotation matrix. Note that the implicit + ordering is rotation about the x-axis, y-axis, then z-axis. ---*/ + + rotMatrix[0][0] = cosPhi*cosPsi; + rotMatrix[1][0] = cosPhi*sinPsi; + rotMatrix[2][0] = -sinPhi; + + rotMatrix[0][1] = sinTheta*sinPhi*cosPsi - cosTheta*sinPsi; + rotMatrix[1][1] = sinTheta*sinPhi*sinPsi + cosTheta*cosPsi; + rotMatrix[2][1] = sinTheta*cosPhi; + + rotMatrix[0][2] = cosTheta*sinPhi*cosPsi + sinTheta*sinPsi; + rotMatrix[1][2] = cosTheta*sinPhi*sinPsi - sinTheta*cosPsi; + rotMatrix[2][2] = cosTheta*cosPhi; + + for (iVertex = 0; iVertex < geometry[MESH_0]->nVertex[iMarker]; iVertex++) { + + /*--- Index and coordinates of the CURRENT point ---*/ + + iPoint = geometry[MESH_0]->vertex[iMarker][iVertex]->GetNode(); + // if (!config->GetDeform_Mesh()) { + Coord = geometry[MESH_0]->node[iPoint]->GetCoord(); + // } + // else { + // for (iDim = 0; iDim < nDim; iDim++) { + // Coord[iDim] = solver[MESH_0][MESH_SOL]->node[iPoint]->GetMesh_Coord(iDim) + + // solver[MESH_0][MESH_SOL]->node[iPoint]->GetSolution(iDim); + // } + // } + + /*--- Calculate non-dim. position from rotation center ---*/ + + for (iDim = 0; iDim < nDim; iDim++) + r[iDim] = (Coord[iDim]-Center[iDim])/Lref; + if (nDim == 2) r[nDim] = 0.0; + + /*--- Compute transformed point coordinates ---*/ + + rotCoord[0] = rotMatrix[0][0]*r[0] + + rotMatrix[0][1]*r[1] + + rotMatrix[0][2]*r[2] + Center[0]; + + rotCoord[1] = rotMatrix[1][0]*r[0] + + rotMatrix[1][1]*r[1] + + rotMatrix[1][2]*r[2] + Center[1]; + + rotCoord[2] = rotMatrix[2][0]*r[0] + + rotMatrix[2][1]*r[1] + + rotMatrix[2][2]*r[2] + Center[2]; + + /*--- Calculate delta change in the x, y, & z directions ---*/ + for (iDim = 0; iDim < nDim; iDim++) + VarCoord[iDim] = (rotCoord[iDim]-Coord[iDim])/Lref; + if (nDim == 2) VarCoord[nDim] = 0.0; + + if (!config->GetDeform_Mesh()) { + + /*--- Set node displacement for volume deformation ---*/ + geometry[MESH_0]->vertex[iMarker][iVertex]->SetVarCoord(VarCoord); + } + if (config->GetDeform_Mesh()) { + + /*--- Update boundary displacement from all movements if using new elastic mesh solver ---*/ + iPoint = geometry[MESH_0]->vertex[iMarker][iVertex]->GetNode(); + + for (iDim = 0; iDim < 3; iDim++){ + VarCoordAbs[iDim] = solver[MESH_0][MESH_SOL]->node[iPoint]->GetBound_Disp(iDim) + VarCoord[iDim]; + } + + solver[MESH_0][MESH_SOL]->node[iPoint]->SetBound_Disp(VarCoordAbs); + } + } + } + } + } + } + + /*--- When updating the origins it is assumed that all markers have the + same rotation movement, because we use the last markers rotation matrix and center ---*/ + + /*--- Set the mesh motion center to the new location after + incrementing the position with the rotation. This new + location will be used for subsequent mesh motion for the given marker.---*/ + + for (jMarker=0; jMarkerGetnMarker_Moving(); jMarker++) { + + /*-- Check if we want to update the motion origin for the given marker ---*/ + + if (config->GetMoveMotion_Origin(jMarker) == YES) { + + for (iDim = 0; iDim < 3; iDim++){ + Center_Aux[iDim] = config->GetMarkerMotion_Origin(jMarker, iDim); + } + + /*--- Calculate non-dim. position from rotation center ---*/ + + for (iDim = 0; iDim < nDim; iDim++) + r[iDim] = (Center_Aux[iDim]-Center[iDim])/Lref; + if (nDim == 2) r[nDim] = 0.0; + + /*--- Compute transformed point coordinates ---*/ + + rotCoord[0] = rotMatrix[0][0]*r[0] + + rotMatrix[0][1]*r[1] + + rotMatrix[0][2]*r[2] + Center[0]; + + rotCoord[1] = rotMatrix[1][0]*r[0] + + rotMatrix[1][1]*r[1] + + rotMatrix[1][2]*r[2] + Center[1]; + + rotCoord[2] = rotMatrix[2][0]*r[0] + + rotMatrix[2][1]*r[1] + + rotMatrix[2][2]*r[2] + Center[2]; + + /*--- Calculate delta change in the x, y, & z directions ---*/ + for (iDim = 0; iDim < nDim; iDim++) + VarCoord[iDim] = (rotCoord[iDim]-Center_Aux[iDim])/Lref; + if (nDim == 2) VarCoord[nDim] = 0.0; + + for (iDim = 0; iDim < 3; iDim++){ + Center_Aux[iDim] += VarCoord[iDim]; + } + config->SetMarkerMotion_Origin(Center_Aux, jMarker); + } + } + + /*--- Set the moment computation center to the new location after + incrementing the position with the rotation. ---*/ + + for (jMarker=0; jMarkerGetnMarker_Monitoring(); jMarker++) { + + Center_Aux[0] = config->GetRefOriginMoment_X(jMarker); + Center_Aux[1] = config->GetRefOriginMoment_Y(jMarker); + Center_Aux[2] = config->GetRefOriginMoment_Z(jMarker); + + /*--- Calculate non-dim. position from rotation center ---*/ + + for (iDim = 0; iDim < nDim; iDim++) + r[iDim] = (Center_Aux[iDim]-Center[iDim])/Lref; + if (nDim == 2) r[nDim] = 0.0; + + /*--- Compute transformed point coordinates ---*/ + + rotCoord[0] = rotMatrix[0][0]*r[0] + + rotMatrix[0][1]*r[1] + + rotMatrix[0][2]*r[2] + Center[0]; + + rotCoord[1] = rotMatrix[1][0]*r[0] + + rotMatrix[1][1]*r[1] + + rotMatrix[1][2]*r[2] + Center[1]; + + rotCoord[2] = rotMatrix[2][0]*r[0] + + rotMatrix[2][1]*r[1] + + rotMatrix[2][2]*r[2] + Center[2]; + + /*--- Calculate delta change in the x, y, & z directions ---*/ + for (iDim = 0; iDim < nDim; iDim++) + VarCoord[iDim] = (rotCoord[iDim]-Center_Aux[iDim])/Lref; + if (nDim == 2) VarCoord[nDim] = 0.0; + + config->SetRefOriginMoment_X(jMarker, Center_Aux[0]+VarCoord[0]); + config->SetRefOriginMoment_Y(jMarker, Center_Aux[1]+VarCoord[1]); + config->SetRefOriginMoment_Z(jMarker, Center_Aux[2]+VarCoord[2]); + } + + /*----------------------------------------------------------------------------------------------------------*/ + /*----------------------------------------------------------------------------------------------------------*/ + /*----------------------------------------------------------------------------------------------------------*/ + + /*--- Deform the volume grid around the new boundary locations ---*/ + /*--- Set volume deformation if new elastic mesh solver is not used ---*/ + /*--- If Deform_Mesh true, the mesh deformation is handled independently ---*/ + if (rank == MASTER_NODE && !config->GetDeform_Mesh()) { cout << " Deforming the volume grid." << endl; - grid_movement->SetVolume_Deformation(geometry[MESH_0], + grid_movement->SetVolume_Deformation(geometry[MESH_0], config, true); + } /*--- Update the grid velocities on the fine mesh using finite differencing based on node coordinates at previous times. ---*/ From 3fcaeafb91a97ac9f55eb9f4414707e46d4a56fc Mon Sep 17 00:00:00 2001 From: cr109 Date: Sat, 9 Nov 2019 23:49:12 +0000 Subject: [PATCH 004/112] Add surface deformation routines to CMeshSolver --- Common/include/grid_movement_structure.hpp | 4 +- Common/src/grid_movement_structure.cpp | 16 +- SU2_CFD/include/iteration_structure.hpp | 2 +- SU2_CFD/include/solver_structure.hpp | 12 +- SU2_CFD/include/solver_structure.inl | 8 +- SU2_CFD/include/solvers/CMeshSolver.hpp | 13 +- SU2_CFD/src/drivers/CDriver.cpp | 6 +- SU2_CFD/src/drivers/CMultizoneDriver.cpp | 4 +- SU2_CFD/src/drivers/CSinglezoneDriver.cpp | 2 +- SU2_CFD/src/iteration_structure.cpp | 705 ++------------------- SU2_CFD/src/solver_direct_mean.cpp | 2 +- SU2_CFD/src/solvers/CMeshSolver.cpp | 612 +++++++++++++++++- 12 files changed, 690 insertions(+), 696 deletions(-) diff --git a/Common/include/grid_movement_structure.hpp b/Common/include/grid_movement_structure.hpp index 101150feca30..fbe90a13d007 100644 --- a/Common/include/grid_movement_structure.hpp +++ b/Common/include/grid_movement_structure.hpp @@ -50,8 +50,6 @@ #include "geometry_structure.hpp" #include "config_structure.hpp" -//#include "../../SU2_CFD/include/solver_structure.hpp" -//#include "../../SU2_CFD/include/solvers/CMeshSolver.hpp" #include "linear_algebra/CSysMatrix.hpp" #include "linear_algebra/CSysVector.hpp" #include "linear_algebra/CSysSolve.hpp" @@ -1612,7 +1610,7 @@ class CSurfaceMovement : public CGridMovement { * \param[in] iter - Current physical time iteration. * \param[in] iZone - Zone number in the mesh. */ - void Surface_Plunging(CGeometry *geometry, CConfig *config,// CSolver **solver, + void Surface_Plunging(CGeometry *geometry, CConfig *config, unsigned long iter, unsigned short iZone); /*! diff --git a/Common/src/grid_movement_structure.cpp b/Common/src/grid_movement_structure.cpp index de64a8e11d76..0f46ea936ef5 100644 --- a/Common/src/grid_movement_structure.cpp +++ b/Common/src/grid_movement_structure.cpp @@ -5796,14 +5796,14 @@ void CSurfaceMovement::Surface_Translating(CGeometry *geometry, CConfig *config, } } -void CSurfaceMovement::Surface_Plunging(CGeometry *geometry, CConfig *config, +void CSurfaceMovement::Surface_Plunging(CGeometry *geometry, CConfig *config, unsigned long iter, unsigned short iZone) { su2double deltaT, time_new, time_old, Lref; su2double Center[3] = {0.0, 0.0, 0.0}, VarCoord[3], Omega[3], Ampl[3]; su2double DEG2RAD = PI_NUMBER/180.0; unsigned short iMarker, jMarker, Moving; - unsigned long iPoint, iVertex; + unsigned long iVertex; string Marker_Tag, Moving_Tag; unsigned short iDim; @@ -5867,16 +5867,8 @@ void CSurfaceMovement::Surface_Plunging(CGeometry *geometry, CConfig *config, for (iVertex = 0; iVertex < geometry->nVertex[iMarker]; iVertex++) { - // if (!config->GetDeform_Mesh()) { - /*--- Set node displacement for volume deformation ---*/ - geometry->vertex[iMarker][iVertex]->SetVarCoord(VarCoord); - // } - // else { - // /*--- Get node index ---*/ - // iPoint = geometry->vertex[iMarker][iVertex]->GetNode(); - - // solver[MESH_SOL]->node[iPoint]->SetBound_Disp(VarCoord); - // } + /*--- Set node displacement for volume deformation ---*/ + geometry->vertex[iMarker][iVertex]->SetVarCoord(VarCoord); } } diff --git a/SU2_CFD/include/iteration_structure.hpp b/SU2_CFD/include/iteration_structure.hpp index 061da7424551..894ab0423133 100644 --- a/SU2_CFD/include/iteration_structure.hpp +++ b/SU2_CFD/include/iteration_structure.hpp @@ -97,7 +97,7 @@ class CIteration { * \param[in] ExtIter - Current physical time iteration number. */ virtual void SetGrid_Movement(CGeometry **geometry, CSurfaceMovement *surface_movement, - CVolumetricMovement *grid_movement, CNumerics ****numerics, + CVolumetricMovement *grid_movement, CSolver ***solver, CConfig *config, unsigned long IntIter, unsigned long TimeIter); /*! * \brief Run the mesh deformation algorithms. diff --git a/SU2_CFD/include/solver_structure.hpp b/SU2_CFD/include/solver_structure.hpp index 365d3cfba871..e707a4f524e5 100644 --- a/SU2_CFD/include/solver_structure.hpp +++ b/SU2_CFD/include/solver_structure.hpp @@ -4444,7 +4444,17 @@ class CSolver { */ virtual void DeformMesh(CGeometry **geometry, CNumerics **numerics, CConfig *config); - // virtual void Surface_Plunging(CGeometry *geometry, CConfig *config, CSolver **solver, unsigned long iter, unsigned short iZone); + virtual void Surface_Pitching(CGeometry *geometry, CConfig *config, + unsigned long iter, unsigned short iZone); + + virtual void Surface_Rotating(CGeometry *geometry, CConfig *config, + unsigned long iter, unsigned short iZone); + + virtual void Surface_Plunging(CGeometry *geometry, CConfig *config, + unsigned long iter, unsigned short iZone); + + virtual void Surface_Translating(CGeometry *geometry, CConfig *config, + unsigned long iter, unsigned short iZone); /*! * \brief A virtual member. diff --git a/SU2_CFD/include/solver_structure.inl b/SU2_CFD/include/solver_structure.inl index 486c41ca7a64..9e68cdf74ae9 100644 --- a/SU2_CFD/include/solver_structure.inl +++ b/SU2_CFD/include/solver_structure.inl @@ -1152,7 +1152,13 @@ inline void CSolver::SetDES_LengthScale(CSolver** solver, CGeometry *geometry, C inline void CSolver::DeformMesh(CGeometry **geometry, CNumerics **numerics, CConfig *config) { } -// inline void CSolver::Surface_Plunging(CGeometry *geometry, CConfig *config, CSolver **solver, unsigned long iter, unsigned short iZone) { } +inline void CSolver::Surface_Pitching(CGeometry *geometry, CConfig *config, unsigned long iter, unsigned short iZone) { } + +inline void CSolver::Surface_Rotating(CGeometry *geometry, CConfig *config, unsigned long iter, unsigned short iZone) { } + +inline void CSolver::Surface_Plunging(CGeometry *geometry, CConfig *config, unsigned long iter, unsigned short iZone) { } + +inline void CSolver::Surface_Translating(CGeometry *geometry, CConfig *config, unsigned long iter, unsigned short iZone) { } inline void CSolver::SetMesh_Stiffness(CGeometry **geometry, CNumerics **numerics, CConfig *config) { } diff --git a/SU2_CFD/include/solvers/CMeshSolver.hpp b/SU2_CFD/include/solvers/CMeshSolver.hpp index 1e9edc73c6bb..d1da99f716bc 100644 --- a/SU2_CFD/include/solvers/CMeshSolver.hpp +++ b/SU2_CFD/include/solvers/CMeshSolver.hpp @@ -179,7 +179,6 @@ class CMeshSolver : public CFEASolver { * \brief Get minimun volume in the mesh * \return */ - // void Surface_Plunging(CGeometry *geometry, CConfig *config, CSolver **solver, unsigned long iter, unsigned short iZone); su2double GetMinimum_Volume(){return MinVolume_Curr;} /*! @@ -188,4 +187,16 @@ class CMeshSolver : public CFEASolver { */ su2double GetMaximum_Volume(){return MaxVolume_Curr;} + void Surface_Pitching(CGeometry *geometry, CConfig *config, + unsigned long iter, unsigned short iZone); + + void Surface_Rotating(CGeometry *geometry, CConfig *config, + unsigned long iter, unsigned short iZone); + + void Surface_Plunging(CGeometry *geometry, CConfig *config, + unsigned long iter, unsigned short iZone); + + void Surface_Translating(CGeometry *geometry, CConfig *config, + unsigned long iter, unsigned short iZone); + }; diff --git a/SU2_CFD/src/drivers/CDriver.cpp b/SU2_CFD/src/drivers/CDriver.cpp index c8fc2a2c1275..bb4d6b371754 100644 --- a/SU2_CFD/src/drivers/CDriver.cpp +++ b/SU2_CFD/src/drivers/CDriver.cpp @@ -3215,7 +3215,7 @@ void CDriver::DynamicMesh_Preprocessing(CConfig *config, CGeometry **geometry, C surface_movement->CopyBoundary(geometry[MESH_0], config); if (config->GetTime_Marching() == HARMONIC_BALANCE){ if (rank == MASTER_NODE) cout << endl << "Instance "<< iInst + 1 <<":" << endl; - iteration->SetGrid_Movement(geometry, surface_movement, grid_movement, numerics_container[iZone][iInst], solver, config, 0, iInst); + iteration->SetGrid_Movement(geometry, surface_movement, grid_movement, solver, config, 0, iInst); } } @@ -4124,7 +4124,7 @@ void CFluidDriver::DynamicMeshUpdate(unsigned long TimeIter) { harmonic_balance = (config_container[iZone]->GetTime_Marching() == HARMONIC_BALANCE); /*--- Dynamic mesh update ---*/ if ((config_container[iZone]->GetGrid_Movement()) && (!harmonic_balance)) { - iteration_container[iZone][INST_0]->SetGrid_Movement(geometry_container[iZone][INST_0], surface_movement[iZone], grid_movement[iZone][INST_0], numerics_container[iZone][INST_0], solver_container[iZone][INST_0], config_container[iZone], 0, TimeIter ); + iteration_container[iZone][INST_0]->SetGrid_Movement(geometry_container[iZone][INST_0], surface_movement[iZone], grid_movement[iZone][INST_0], solver_container[iZone][INST_0], config_container[iZone], 0, TimeIter ); } } @@ -5805,7 +5805,7 @@ void CDiscAdjFSIDriver::Mesh_Deformation_Direct(unsigned short ZONE_FLOW, unsign /*-----------------------------------------------------------------*/ direct_iteration[ZONE_FLOW]->SetGrid_Movement(geometry_container[ZONE_FLOW][INST_0], - surface_movement[ZONE_FLOW], grid_movement[ZONE_FLOW][INST_0], numerics_container[ZONE_FLOW][INST_0], + surface_movement[ZONE_FLOW], grid_movement[ZONE_FLOW][INST_0], solver_container[ZONE_FLOW][INST_0], config_container[ZONE_FLOW], 0, ExtIter ); geometry_container[ZONE_FLOW][INST_0][MESH_0]->UpdateGeometry(geometry_container[ZONE_FLOW][INST_0], config_container[ZONE_FLOW]); diff --git a/SU2_CFD/src/drivers/CMultizoneDriver.cpp b/SU2_CFD/src/drivers/CMultizoneDriver.cpp index 827c7da0c567..4b14df45f0fe 100644 --- a/SU2_CFD/src/drivers/CMultizoneDriver.cpp +++ b/SU2_CFD/src/drivers/CMultizoneDriver.cpp @@ -529,7 +529,7 @@ void CMultizoneDriver::DynamicMeshUpdate(unsigned long TimeIter) { /*--- Dynamic mesh update ---*/ if ((config_container[iZone]->GetGrid_Movement()) && (!harmonic_balance) && (!fsi)) { iteration_container[iZone][INST_0]->SetGrid_Movement(geometry_container[iZone][INST_0],surface_movement[iZone], - grid_movement[iZone][INST_0], numerics_container[iZone][INST_0], solver_container[iZone][INST_0], + grid_movement[iZone][INST_0], solver_container[iZone][INST_0], config_container[iZone], 0, TimeIter); } } @@ -540,7 +540,7 @@ void CMultizoneDriver::DynamicMeshUpdate(unsigned short val_iZone, unsigned long /*--- Legacy dynamic mesh update - Only if GRID_MOVEMENT = YES ---*/ if (config_container[ZONE_0]->GetGrid_Movement() || config_container[ZONE_0]->GetSurface_Movement(FLUID_STRUCTURE_STATIC)) { iteration_container[val_iZone][INST_0]->SetGrid_Movement(geometry_container[val_iZone][INST_0],surface_movement[val_iZone], - grid_movement[val_iZone][INST_0], numerics_container[iZone][INST_0], solver_container[val_iZone][INST_0], + grid_movement[val_iZone][INST_0], solver_container[val_iZone][INST_0], config_container[val_iZone], 0, TimeIter); } diff --git a/SU2_CFD/src/drivers/CSinglezoneDriver.cpp b/SU2_CFD/src/drivers/CSinglezoneDriver.cpp index 5d5c08b4d880..4e9e8a066faf 100644 --- a/SU2_CFD/src/drivers/CSinglezoneDriver.cpp +++ b/SU2_CFD/src/drivers/CSinglezoneDriver.cpp @@ -238,7 +238,7 @@ void CSinglezoneDriver::DynamicMeshUpdate(unsigned long TimeIter) { /*--- Legacy dynamic mesh update - Only if GRID_MOVEMENT = YES ---*/ if (config_container[ZONE_0]->GetGrid_Movement()) { iteration_container[ZONE_0][INST_0]->SetGrid_Movement(geometry_container[ZONE_0][INST_0],surface_movement[ZONE_0], - grid_movement[ZONE_0][INST_0], numerics_container[ZONE_0][INST_0], solver_container[ZONE_0][INST_0], + grid_movement[ZONE_0][INST_0], solver_container[ZONE_0][INST_0], config_container[ZONE_0], 0, TimeIter); } diff --git a/SU2_CFD/src/iteration_structure.cpp b/SU2_CFD/src/iteration_structure.cpp index 497e05dbd43f..9709fd0f21d6 100644 --- a/SU2_CFD/src/iteration_structure.cpp +++ b/SU2_CFD/src/iteration_structure.cpp @@ -54,7 +54,6 @@ CIteration::~CIteration(void) { } void CIteration::SetGrid_Movement(CGeometry **geometry, CSurfaceMovement *surface_movement, CVolumetricMovement *grid_movement, - CNumerics ****numerics, CSolver ***solver, CConfig *config, unsigned long IntIter, @@ -114,139 +113,25 @@ void CIteration::SetGrid_Movement(CGeometry **geometry, /*--- Compute the new node locations for moving markers ---*/ - /*----------------------------------------------------------------------------------------------------------*/ - /*----------------------------------------------------------------------------------------------------------*/ - /*----------------------------------------------------------------------------------------------------------*/ // CVC: To do: For deformation with new mesh solver, need to access solver[MESH_0][MESH_SOL] for SetBound_Disp // inside Surface_Movement functions, rest of code is identical to grid_movement_structure.cpp // Either enable solver[MESH_0][MESH_SOL] inside grid_movement_structure.cpp // or move Surface_Movement functions to inside CMeshSolver - // surface_movement->Surface_Translating(geometry[MESH_0], - // config, TimeIter, val_iZone); - - su2double deltaT, time_new, time_old, Lref, *Coord; - su2double Center[3] = {0.0,0.0,0.0}, VarCoord[3] = {0.0,0.0,0.0}, VarCoordAbs[3] = {0.0, 0.0, 0.0}; - su2double xDot[3] = {0.0,0.0,0.0}; - unsigned short iMarker, jMarker, Moving; - unsigned long iPoint, iVertex; - string Marker_Tag, Moving_Tag; - unsigned short iDim, nDim = geometry[MESH_0]->GetnDim(); - - /*--- Initialize the delta variation in coordinates ---*/ - VarCoord[0] = 0.0; VarCoord[1] = 0.0; VarCoord[2] = 0.0; - - /*--- Retrieve values from the config file ---*/ - - deltaT = config->GetDelta_UnstTimeND(); - - /*--- Compute delta time based on physical time step ---*/ - time_new = static_cast(TimeIter)*deltaT; - if (TimeIter == 0) { - time_old = time_new; - } else { - time_old = static_cast(TimeIter-1)*deltaT; + if (!config->GetDeform_Mesh()) { + surface_movement->Surface_Translating(geometry[MESH_0], + config, TimeIter, val_iZone); } - - /*--- Store displacement of each node on the translating surface ---*/ - /*--- Loop over markers and find the particular marker(s) (surface) to translate ---*/ - - for (iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { - Moving = config->GetMarker_All_Moving(iMarker); - if (Moving == YES) { - for (jMarker = 0; jMarkerGetnMarker_Moving(); jMarker++) { - - Moving_Tag = config->GetMarker_Moving_TagBound(jMarker); - Marker_Tag = config->GetMarker_All_TagBound(iMarker); - - if (Marker_Tag == Moving_Tag && (config->GetKind_SurfaceMovement(jMarker) == DEFORMING)) { - - for (iDim = 0; iDim < 3; iDim++){ - xDot[iDim] = config->GetMarkerTranslationRate(jMarker, iDim); - Center[iDim] = config->GetMarkerMotion_Origin(jMarker, iDim); - } - - /*--- Print some information to the console. Be verbose at the first - iteration only (mostly for debugging purposes). ---*/ - // Note that the MASTER_NODE might not contain all the markers being moved. - - if (rank == MASTER_NODE) { - cout << " Storing translating displacement for marker: "; - cout << Marker_Tag << "." << endl; - if (TimeIter == 0) { - cout << " Translational velocity: (" << xDot[0]*config->GetVelocity_Ref() << ", " << xDot[1]*config->GetVelocity_Ref(); - cout << ", " << xDot[2]*config->GetVelocity_Ref(); - if (config->GetSystemMeasurements() == SI) cout << ") m/s." << endl; - else cout << ") ft/s." << endl; - } - } - - /*--- Compute delta change in the position in the x, y, & z directions. ---*/ - - VarCoord[0] = xDot[0]*(time_new-time_old); - VarCoord[1] = xDot[1]*(time_new-time_old); - VarCoord[2] = xDot[2]*(time_new-time_old); - - for (iVertex = 0; iVertex < geometry[MESH_0]->nVertex[iMarker]; iVertex++) { - - if (!config->GetDeform_Mesh()) { - - /*--- Set node displacement for volume deformation ---*/ - geometry[MESH_0]->vertex[iMarker][iVertex]->SetVarCoord(VarCoord); - } - else { - iPoint = geometry[MESH_0]->vertex[iMarker][iVertex]->GetNode(); - - for (iDim = 0; iDim < 3; iDim++){ - VarCoordAbs[iDim] = solver[MESH_0][MESH_SOL]->GetNodes()->GetBound_Disp(iPoint, iDim) + VarCoord[iDim]; - } - - solver[MESH_0][MESH_SOL]->GetNodes()->SetBound_Disp(iPoint, VarCoordAbs); - } - } - } - } - } - } - - /*--- When updating the origins it is assumed that all markers have the - same translational velocity, because we use the last VarCoord set ---*/ - - /*--- Set the mesh motion center to the new location after - incrementing the position with the translation. This new - location will be used for subsequent mesh motion for the given marker.---*/ - - for (jMarker=0; jMarkerGetnMarker_Moving(); jMarker++) { - - /*-- Check if we want to update the motion origin for the given marker ---*/ - - if (config->GetMoveMotion_Origin(jMarker) == YES) { - for (iDim = 0; iDim < 3; iDim++){ - Center[iDim] += VarCoord[iDim]; - } - config->SetMarkerMotion_Origin(Center, jMarker); - } - } - - /*--- Set the moment computation center to the new location after - incrementing the position with the translation. ---*/ - - for (jMarker=0; jMarkerGetnMarker_Monitoring(); jMarker++) { - Center[0] = config->GetRefOriginMoment_X(jMarker) + VarCoord[0]; - Center[1] = config->GetRefOriginMoment_Y(jMarker) + VarCoord[1]; - Center[2] = config->GetRefOriginMoment_Z(jMarker) + VarCoord[2]; - config->SetRefOriginMoment_X(jMarker, Center[0]); - config->SetRefOriginMoment_Y(jMarker, Center[1]); - config->SetRefOriginMoment_Z(jMarker, Center[2]); + else { + solver[MESH_0][MESH_SOL]->Surface_Translating(geometry[MESH_0], + config, TimeIter, val_iZone); } - /*----------------------------------------------------------------------------------------------------------*/ - /*----------------------------------------------------------------------------------------------------------*/ - /*----------------------------------------------------------------------------------------------------------*/ - /*--- Deform the volume grid around the new boundary locations ---*/ /*--- Set volume deformation if new elastic mesh solver is not used ---*/ - /*--- If Deform_Mesh true, the mesh deformation is handled independently ---*/ + /*--- If Deform_Mesh true, the mesh deformation is handled by SetMesh_Deformation ---*/ + // CVC: Debug: To Do: What if multiple prescribed movements? E.g., Pitching + Plunging? + if (rank == MASTER_NODE && !config->GetDeform_Mesh()) { cout << " Deforming the volume grid." << endl; grid_movement->SetVolume_Deformation(geometry[MESH_0], @@ -257,147 +142,20 @@ void CIteration::SetGrid_Movement(CGeometry **geometry, /*--- Compute the new node locations for moving markers ---*/ - /*----------------------------------------------------------------------------------------------------------*/ - /*----------------------------------------------------------------------------------------------------------*/ - /*----------------------------------------------------------------------------------------------------------*/ - - // if (!config->GetDeform_Mesh()) { - // surface_movement->Surface_Plunging(geometry[MESH_0], - // config, - // TimeIter, val_iZone); - // } - // else { - // //solver[MESH_0][MESH_SOL]->Surface_Plunging(geometry[MESH_0], config, solver[MESH_0], TimeIter, val_iZone); - // } - - // su2double deltaT, time_new, time_old, Lref; - // su2double Center[3] = {0.0, 0.0, 0.0}, VarCoord[3], VarCoordAbs[3], VarCoordAbs[3] = {0.0, 0.0, 0.0}; - su2double Omega[3], Ampl[3]; - su2double DEG2RAD = PI_NUMBER/180.0; - // unsigned short iMarker, jMarker, Moving; - // unsigned long iPoint, iVertex; - // string Marker_Tag, Moving_Tag; - // unsigned short iDim, nDim = geometry[MESH_0]->GetnDim(); - - /*--- Initialize the delta variation in coordinates ---*/ - VarCoord[0] = 0.0; VarCoord[1] = 0.0; VarCoord[2] = 0.0; - - /*--- Retrieve values from the config file ---*/ - - deltaT = config->GetDelta_UnstTimeND(); - Lref = config->GetLength_Ref(); - - /*--- Compute delta time based on physical time step ---*/ - time_new = static_cast(TimeIter)*deltaT; - if (TimeIter == 0) { - time_old = time_new; - } else { - time_old = static_cast(TimeIter-1)*deltaT; - } - - /*--- Store displacement of each node on the plunging surface ---*/ - /*--- Loop over markers and find the particular marker(s) (surface) to plunge ---*/ - - for (iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { - Moving = config->GetMarker_All_Moving(iMarker); - if (Moving == YES) { - for (jMarker = 0; jMarkerGetnMarker_Moving(); jMarker++) { - - Moving_Tag = config->GetMarker_Moving_TagBound(jMarker); - Marker_Tag = config->GetMarker_All_TagBound(iMarker); - - if (Marker_Tag == Moving_Tag && (config->GetKind_SurfaceMovement(jMarker) == DEFORMING)) { - - /*--- Plunging frequency and amplitude from config. ---*/ - - for (iDim = 0; iDim < 3; iDim++){ - Ampl[iDim] = config->GetMarkerPlunging_Ampl(jMarker, iDim)/Lref; - Omega[iDim] = config->GetMarkerPlunging_Omega(jMarker, iDim)/config->GetOmega_Ref(); - Center[iDim] = config->GetMarkerMotion_Origin(jMarker, iDim); - } - /*--- Print some information to the console. Be verbose at the first - iteration only (mostly for debugging purposes). ---*/ - // Note that the MASTER_NODE might not contain all the markers being moved. - - if (rank == MASTER_NODE) { - cout << " Storing plunging displacement for marker: "; - cout << Marker_Tag << "." << endl; - if (TimeIter == 0) { - cout << " Plunging frequency: (" << Omega[0] << ", " << Omega[1]; - cout << ", " << Omega[2] << ") rad/s." << endl; - cout << " Plunging amplitude: (" << Ampl[0]/DEG2RAD; - cout << ", " << Ampl[1]/DEG2RAD << ", " << Ampl[2]/DEG2RAD; - cout << ") degrees."<< endl; - } - } - - /*--- Compute delta change in the position in the x, y, & z directions. ---*/ - - VarCoord[0] = -Ampl[0]*(sin(Omega[0]*time_new) - sin(Omega[0]*time_old)); - VarCoord[1] = -Ampl[1]*(sin(Omega[1]*time_new) - sin(Omega[1]*time_old)); - VarCoord[2] = -Ampl[2]*(sin(Omega[2]*time_new) - sin(Omega[2]*time_old)); - - for (iVertex = 0; iVertex < geometry[MESH_0]->nVertex[iMarker]; iVertex++) { - - if (!config->GetDeform_Mesh()) { - - /*--- Set node displacement for volume deformation ---*/ - geometry[MESH_0]->vertex[iMarker][iVertex]->SetVarCoord(VarCoord); - } - else { - /* --- Get node index ---*/ - iPoint = geometry[MESH_0]->vertex[iMarker][iVertex]->GetNode(); - - for (iDim = 0; iDim < 3; iDim++){ - VarCoordAbs[iDim] = solver[MESH_0][MESH_SOL]->GetNodes()->GetBound_Disp(iPoint, iDim) + VarCoord[iDim]; - } - - solver[MESH_0][MESH_SOL]->GetNodes()->SetBound_Disp(iPoint, VarCoordAbs); - } - } - } - } - } - } - - /*--- When updating the origins it is assumed that all markers have the - same plunging movement, because we use the last VarCoord set ---*/ - - /*--- Set the mesh motion center to the new location after - incrementing the position with the translation. This new - location will be used for subsequent mesh motion for the given marker.---*/ - - for (jMarker=0; jMarkerGetnMarker_Moving(); jMarker++) { - - /*-- Check if we want to update the motion origin for the given marker ---*/ - - if (config->GetMoveMotion_Origin(jMarker) == YES) { - for (iDim = 0; iDim < 3; iDim++){ - Center[iDim] += VarCoord[iDim]; - } - config->SetMarkerMotion_Origin(Center, jMarker); - } + if (!config->GetDeform_Mesh()) { + surface_movement->Surface_Plunging(geometry[MESH_0], + config, TimeIter, val_iZone); } - - /*--- Set the moment computation center to the new location after - incrementing the position with the plunging. ---*/ - - for (jMarker=0; jMarkerGetnMarker_Monitoring(); jMarker++) { - Center[0] = config->GetRefOriginMoment_X(jMarker) + VarCoord[0]; - Center[1] = config->GetRefOriginMoment_Y(jMarker) + VarCoord[1]; - Center[2] = config->GetRefOriginMoment_Z(jMarker) + VarCoord[2]; - config->SetRefOriginMoment_X(jMarker, Center[0]); - config->SetRefOriginMoment_Y(jMarker, Center[1]); - config->SetRefOriginMoment_Z(jMarker, Center[2]); + else { + solver[MESH_0][MESH_SOL]->Surface_Plunging(geometry[MESH_0], + config, TimeIter, val_iZone); } - /*----------------------------------------------------------------------------------------------------------*/ - /*----------------------------------------------------------------------------------------------------------*/ - /*----------------------------------------------------------------------------------------------------------*/ - /*--- Deform the volume grid around the new boundary locations ---*/ /*--- Set volume deformation if new elastic mesh solver is not used ---*/ - /*--- If Deform_Mesh true, the mesh deformation is handled independently ---*/ + /*--- If Deform_Mesh true, the mesh deformation is handled by SetMesh_Deformation ---*/ + // CVC: Debug: To Do: What if multiple prescribed movements? E.g., Pitching + Plunging? + if (rank == MASTER_NODE && !config->GetDeform_Mesh()) { cout << " Deforming the volume grid." << endl; grid_movement->SetVolume_Deformation(geometry[MESH_0], @@ -408,174 +166,20 @@ void CIteration::SetGrid_Movement(CGeometry **geometry, /*--- Compute the new node locations for moving markers ---*/ - /*----------------------------------------------------------------------------------------------------------*/ - /*----------------------------------------------------------------------------------------------------------*/ - /*----------------------------------------------------------------------------------------------------------*/ - - // surface_movement->Surface_Pitching(geometry[MESH_0], - // config, TimeIter, val_iZone); - - //su2double deltaT, time_new, time_old, Lref, *Coord; - //su2double Center[3], VarCoord[3], Omega[3], Ampl[3], VarCoordAbs[3] = {0.0, 0.0, 0.0}; - su2double Phase[3]; - su2double rotCoord[3], r[3] = {0.0,0.0,0.0}; - su2double rotMatrix[3][3] = {{0.0,0.0,0.0}, {0.0,0.0,0.0}, {0.0,0.0,0.0}}; - su2double dtheta, dphi, dpsi, cosTheta, sinTheta; - su2double cosPhi, sinPhi, cosPsi, sinPsi; - //su2double DEG2RAD = PI_NUMBER/180.0; - //unsigned short iMarker, jMarker, Moving, iDim, nDim = geometry[MESH_0]->GetnDim(); - //unsigned long iPoint, iVertex; - //string Marker_Tag, Moving_Tag; - - /*--- Initialize the delta variation in coordinates ---*/ - VarCoord[0] = 0.0; VarCoord[1] = 0.0; VarCoord[2] = 0.0; - - /*--- Retrieve values from the config file ---*/ - - deltaT = config->GetDelta_UnstTimeND(); - Lref = config->GetLength_Ref(); - - /*--- Compute delta time based on physical time step ---*/ - time_new = static_cast(TimeIter)*deltaT; - if (TimeIter == 0) { - time_old = time_new; - } else { - time_old = static_cast(TimeIter-1)*deltaT; + if (!config->GetDeform_Mesh()) { + surface_movement->Surface_Pitching(geometry[MESH_0], + config, TimeIter, val_iZone); } - - /*--- Store displacement of each node on the pitching surface ---*/ - /*--- Loop over markers and find the particular marker(s) (surface) to pitch ---*/ - - for (iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { - Moving = config->GetMarker_All_Moving(iMarker); - if (Moving == YES) { - for (jMarker = 0; jMarkerGetnMarker_Moving(); jMarker++) { - Moving_Tag = config->GetMarker_Moving_TagBound(jMarker); - Marker_Tag = config->GetMarker_All_TagBound(iMarker); - - if (Marker_Tag == Moving_Tag && (config->GetKind_SurfaceMovement(jMarker) == DEFORMING)) { - /*--- Pitching origin, frequency, and amplitude from config. ---*/ - - for (iDim = 0; iDim < 3; iDim++){ - Ampl[iDim] = config->GetMarkerPitching_Ampl(jMarker, iDim)*DEG2RAD; - Omega[iDim] = config->GetMarkerPitching_Omega(jMarker, iDim)/config->GetOmega_Ref(); - Phase[iDim] = config->GetMarkerPitching_Phase(jMarker, iDim)*DEG2RAD; - Center[iDim] = config->GetMarkerMotion_Origin(jMarker, iDim); - } - /*--- Print some information to the console. Be verbose at the first - iteration only (mostly for debugging purposes). ---*/ - // Note that the MASTER_NODE might not contain all the markers being moved. - - if (rank == MASTER_NODE) { - cout << " Storing pitching displacement for marker: "; - cout << Marker_Tag << "." << endl; - if (TimeIter == 0) { - cout << " Pitching frequency: (" << Omega[0] << ", " << Omega[1]; - cout << ", " << Omega[2] << ") rad/s about origin: (" << Center[0]; - cout << ", " << Center[1] << ", " << Center[2] << ")." << endl; - cout << " Pitching amplitude about origin: (" << Ampl[0]/DEG2RAD; - cout << ", " << Ampl[1]/DEG2RAD << ", " << Ampl[2]/DEG2RAD; - cout << ") degrees."<< endl; - cout << " Pitching phase lag about origin: (" << Phase[0]/DEG2RAD; - cout << ", " << Phase[1]/DEG2RAD <<", "<< Phase[2]/DEG2RAD; - cout << ") degrees."<< endl; - } - } - - /*--- Compute delta change in the angle about the x, y, & z axes. ---*/ - - dtheta = -Ampl[0]*(sin(Omega[0]*time_new + Phase[0]) - - sin(Omega[0]*time_old + Phase[0])); - dphi = -Ampl[1]*(sin(Omega[1]*time_new + Phase[1]) - - sin(Omega[1]*time_old + Phase[1])); - dpsi = -Ampl[2]*(sin(Omega[2]*time_new + Phase[2]) - - sin(Omega[2]*time_old + Phase[2])); - - /*--- Store angles separately for clarity. Compute sines/cosines. ---*/ - - cosTheta = cos(dtheta); cosPhi = cos(dphi); cosPsi = cos(dpsi); - sinTheta = sin(dtheta); sinPhi = sin(dphi); sinPsi = sin(dpsi); - - /*--- Compute the rotation matrix. Note that the implicit - ordering is rotation about the x-axis, y-axis, then z-axis. ---*/ - - rotMatrix[0][0] = cosPhi*cosPsi; - rotMatrix[1][0] = cosPhi*sinPsi; - rotMatrix[2][0] = -sinPhi; - - rotMatrix[0][1] = sinTheta*sinPhi*cosPsi - cosTheta*sinPsi; - rotMatrix[1][1] = sinTheta*sinPhi*sinPsi + cosTheta*cosPsi; - rotMatrix[2][1] = sinTheta*cosPhi; - - rotMatrix[0][2] = cosTheta*sinPhi*cosPsi + sinTheta*sinPsi; - rotMatrix[1][2] = cosTheta*sinPhi*sinPsi - sinTheta*cosPsi; - rotMatrix[2][2] = cosTheta*cosPhi; - - for (iVertex = 0; iVertex < geometry[MESH_0]->nVertex[iMarker]; iVertex++) { - - /*--- Index and coordinates of the current point ---*/ - - iPoint = geometry[MESH_0]->vertex[iMarker][iVertex]->GetNode(); - // if (!config->GetDeform_Mesh()) { - Coord = geometry[MESH_0]->node[iPoint]->GetCoord(); - // } - // else { - // //CVC: Debug: Double Check - // Coord = solver[MESH_0][MESH_SOL]->GetNodes()->GetMesh_Coord(iPoint); - // } - - /*--- Calculate non-dim. position from rotation center ---*/ - - for (iDim = 0; iDim < nDim; iDim++) - r[iDim] = (Coord[iDim]-Center[iDim])/Lref; - if (nDim == 2) r[nDim] = 0.0; - - /*--- Compute transformed point coordinates ---*/ - - rotCoord[0] = rotMatrix[0][0]*r[0] - + rotMatrix[0][1]*r[1] - + rotMatrix[0][2]*r[2] + Center[0]; - - rotCoord[1] = rotMatrix[1][0]*r[0] - + rotMatrix[1][1]*r[1] - + rotMatrix[1][2]*r[2] + Center[1]; - - rotCoord[2] = rotMatrix[2][0]*r[0] - + rotMatrix[2][1]*r[1] - + rotMatrix[2][2]*r[2] + Center[2]; - - /*--- Calculate delta change in the x, y, & z directions ---*/ - for (iDim = 0; iDim < nDim; iDim++) - VarCoord[iDim] = (rotCoord[iDim]-Coord[iDim])/Lref; - if (nDim == 2) VarCoord[nDim] = 0.0; - - if (!config->GetDeform_Mesh()) { - - /*--- Set node displacement for volume deformation ---*/ - geometry[MESH_0]->vertex[iMarker][iVertex]->SetVarCoord(VarCoord); - } - else { - - for (iDim = 0; iDim < 3; iDim++){ - VarCoordAbs[iDim] = solver[MESH_0][MESH_SOL]->GetNodes()->GetBound_Disp(iPoint, iDim) + VarCoord[iDim]; - } - - solver[MESH_0][MESH_SOL]->GetNodes()->SetBound_Disp(iPoint, VarCoordAbs); - } - } - } - } - } + else { + solver[MESH_0][MESH_SOL]->Surface_Pitching(geometry[MESH_0], + config, TimeIter, val_iZone); } - /*--- For pitching we don't update the motion origin and moment reference origin. ---*/ - - /*----------------------------------------------------------------------------------------------------------*/ - /*----------------------------------------------------------------------------------------------------------*/ - /*----------------------------------------------------------------------------------------------------------*/ /*--- Deform the volume grid around the new boundary locations ---*/ /*--- Set volume deformation if new elastic mesh solver is not used ---*/ - /*--- If Deform_Mesh true, the mesh deformation is handled independently ---*/ + /*--- If Deform_Mesh true, the mesh deformation is handled by SetMesh_Deformation ---*/ + // CVC: Debug: To Do: What if multiple prescribed movements? E.g., Pitching + Plunging? + if (rank == MASTER_NODE && !config->GetDeform_Mesh()) { cout << " Deforming the volume grid." << endl; grid_movement->SetVolume_Deformation(geometry[MESH_0], @@ -586,256 +190,19 @@ void CIteration::SetGrid_Movement(CGeometry **geometry, /*--- Compute the new node locations for moving markers ---*/ - /*----------------------------------------------------------------------------------------------------------*/ - /*----------------------------------------------------------------------------------------------------------*/ - /*----------------------------------------------------------------------------------------------------------*/ - - // surface_movement->Surface_Rotating(geometry[MESH_0], - // config, TimeIter, val_iZone); - - //su2double deltaT, time_new, time_old, Lref, *Coord; - //su2double Center[3] = {0.0,0.0,0.0}, VarCoord[3] = {0.0,0.0,0.0}, Omega[3] = {0.0,0.0,0.0}, VarCoordAbs[3] = {0.0, 0.0, 0.0}; - //rotCoord[3] = {0.0,0.0,0.0}, r[3] = {0.0,0.0,0.0}, - su2double Center_Aux[3] = {0.0,0.0,0.0}; - //su2double rotMatrix[3][3] = {{0.0,0.0,0.0}, {0.0,0.0,0.0}, {0.0,0.0,0.0}}; - //su2double dtheta, dphi, dpsi, cosTheta, sinTheta; - //su2double cosPhi, sinPhi, cosPsi, sinPsi; - // unsigned short iMarker, jMarker, Moving, iDim, nDim = geometry[MESH_0]->GetnDim(); - // unsigned long iPoint, iVertex; - // string Marker_Tag, Moving_Tag; - - /*--- Initialize the delta variation in coordinates ---*/ - VarCoord[0] = 0.0; VarCoord[1] = 0.0; VarCoord[2] = 0.0; - - /*--- Retrieve values from the config file ---*/ - - deltaT = config->GetDelta_UnstTimeND(); - Lref = config->GetLength_Ref(); - - /*--- Compute delta time based on physical time step ---*/ - time_new = static_cast(TimeIter)*deltaT; - if (TimeIter == 0) { - time_old = time_new; - } else { - time_old = static_cast(TimeIter-1)*deltaT; + if (!config->GetDeform_Mesh()) { + surface_movement->Surface_Rotating(geometry[MESH_0], + config, TimeIter, val_iZone); } - - /*--- Store displacement of each node on the rotating surface ---*/ - /*--- Loop over markers and find the particular marker(s) (surface) to rotate ---*/ - - for (iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { - Moving = config->GetMarker_All_Moving(iMarker); - if (Moving == YES) { - for (jMarker = 0; jMarkerGetnMarker_Moving(); jMarker++) { - - Moving_Tag = config->GetMarker_Moving_TagBound(jMarker); - Marker_Tag = config->GetMarker_All_TagBound(iMarker); - - if (Marker_Tag == Moving_Tag && (config->GetKind_SurfaceMovement(jMarker) == DEFORMING)) { - - /*--- Rotation origin and angular velocity from config. ---*/ - - for (iDim = 0; iDim < 3; iDim++){ - Omega[iDim] = config->GetMarkerRotationRate(jMarker, iDim)/config->GetOmega_Ref(); - Center[iDim] = config->GetMarkerMotion_Origin(jMarker, iDim); - } - /*--- Print some information to the console. Be verbose at the first - iteration only (mostly for debugging purposes). ---*/ - // Note that the MASTER_NODE might not contain all the markers being moved. - - if (rank == MASTER_NODE) { - cout << " Storing rotating displacement for marker: "; - cout << Marker_Tag << "." << endl; - if (TimeIter == 0) { - cout << " Angular velocity: (" << Omega[0] << ", " << Omega[1]; - cout << ", " << Omega[2] << ") rad/s about origin: (" << Center[0]; - cout << ", " << Center[1] << ", " << Center[2] << ")." << endl; - } - } - - /*--- Compute delta change in the angle about the x, y, & z axes. ---*/ - - dtheta = Omega[0]*(time_new-time_old); - dphi = Omega[1]*(time_new-time_old); - dpsi = Omega[2]*(time_new-time_old); - - /*--- Store angles separately for clarity. Compute sines/cosines. ---*/ - - cosTheta = cos(dtheta); cosPhi = cos(dphi); cosPsi = cos(dpsi); - sinTheta = sin(dtheta); sinPhi = sin(dphi); sinPsi = sin(dpsi); - - /*--- Compute the rotation matrix. Note that the implicit - ordering is rotation about the x-axis, y-axis, then z-axis. ---*/ - - rotMatrix[0][0] = cosPhi*cosPsi; - rotMatrix[1][0] = cosPhi*sinPsi; - rotMatrix[2][0] = -sinPhi; - - rotMatrix[0][1] = sinTheta*sinPhi*cosPsi - cosTheta*sinPsi; - rotMatrix[1][1] = sinTheta*sinPhi*sinPsi + cosTheta*cosPsi; - rotMatrix[2][1] = sinTheta*cosPhi; - - rotMatrix[0][2] = cosTheta*sinPhi*cosPsi + sinTheta*sinPsi; - rotMatrix[1][2] = cosTheta*sinPhi*sinPsi - sinTheta*cosPsi; - rotMatrix[2][2] = cosTheta*cosPhi; - - for (iVertex = 0; iVertex < geometry[MESH_0]->nVertex[iMarker]; iVertex++) { - - /*--- Index and coordinates of the CURRENT point ---*/ - - iPoint = geometry[MESH_0]->vertex[iMarker][iVertex]->GetNode(); - // if (!config->GetDeform_Mesh()) { - Coord = geometry[MESH_0]->node[iPoint]->GetCoord(); - // } - // else { - // for (iDim = 0; iDim < nDim; iDim++) { - // Coord[iDim] = solver[MESH_0][MESH_SOL]->node[iPoint]->GetMesh_Coord(iDim) + - // solver[MESH_0][MESH_SOL]->node[iPoint]->GetSolution(iDim); - // } - // } - - /*--- Calculate non-dim. position from rotation center ---*/ - - for (iDim = 0; iDim < nDim; iDim++) - r[iDim] = (Coord[iDim]-Center[iDim])/Lref; - if (nDim == 2) r[nDim] = 0.0; - - /*--- Compute transformed point coordinates ---*/ - - rotCoord[0] = rotMatrix[0][0]*r[0] - + rotMatrix[0][1]*r[1] - + rotMatrix[0][2]*r[2] + Center[0]; - - rotCoord[1] = rotMatrix[1][0]*r[0] - + rotMatrix[1][1]*r[1] - + rotMatrix[1][2]*r[2] + Center[1]; - - rotCoord[2] = rotMatrix[2][0]*r[0] - + rotMatrix[2][1]*r[1] - + rotMatrix[2][2]*r[2] + Center[2]; - - /*--- Calculate delta change in the x, y, & z directions ---*/ - for (iDim = 0; iDim < nDim; iDim++) - VarCoord[iDim] = (rotCoord[iDim]-Coord[iDim])/Lref; - if (nDim == 2) VarCoord[nDim] = 0.0; - - if (!config->GetDeform_Mesh()) { - - /*--- Set node displacement for volume deformation ---*/ - geometry[MESH_0]->vertex[iMarker][iVertex]->SetVarCoord(VarCoord); - } - if (config->GetDeform_Mesh()) { - - /*--- Update boundary displacement from all movements if using new elastic mesh solver ---*/ - iPoint = geometry[MESH_0]->vertex[iMarker][iVertex]->GetNode(); - - for (iDim = 0; iDim < 3; iDim++){ - VarCoordAbs[iDim] = solver[MESH_0][MESH_SOL]->GetNodes()->GetBound_Disp(iPoint, iDim) + VarCoord[iDim]; - } - - solver[MESH_0][MESH_SOL]->GetNodes()->SetBound_Disp(iPoint, VarCoordAbs); - } - } - } - } - } - } - - /*--- When updating the origins it is assumed that all markers have the - same rotation movement, because we use the last markers rotation matrix and center ---*/ - - /*--- Set the mesh motion center to the new location after - incrementing the position with the rotation. This new - location will be used for subsequent mesh motion for the given marker.---*/ - - for (jMarker=0; jMarkerGetnMarker_Moving(); jMarker++) { - - /*-- Check if we want to update the motion origin for the given marker ---*/ - - if (config->GetMoveMotion_Origin(jMarker) == YES) { - - for (iDim = 0; iDim < 3; iDim++){ - Center_Aux[iDim] = config->GetMarkerMotion_Origin(jMarker, iDim); - } - - /*--- Calculate non-dim. position from rotation center ---*/ - - for (iDim = 0; iDim < nDim; iDim++) - r[iDim] = (Center_Aux[iDim]-Center[iDim])/Lref; - if (nDim == 2) r[nDim] = 0.0; - - /*--- Compute transformed point coordinates ---*/ - - rotCoord[0] = rotMatrix[0][0]*r[0] - + rotMatrix[0][1]*r[1] - + rotMatrix[0][2]*r[2] + Center[0]; - - rotCoord[1] = rotMatrix[1][0]*r[0] - + rotMatrix[1][1]*r[1] - + rotMatrix[1][2]*r[2] + Center[1]; - - rotCoord[2] = rotMatrix[2][0]*r[0] - + rotMatrix[2][1]*r[1] - + rotMatrix[2][2]*r[2] + Center[2]; - - /*--- Calculate delta change in the x, y, & z directions ---*/ - for (iDim = 0; iDim < nDim; iDim++) - VarCoord[iDim] = (rotCoord[iDim]-Center_Aux[iDim])/Lref; - if (nDim == 2) VarCoord[nDim] = 0.0; - - for (iDim = 0; iDim < 3; iDim++){ - Center_Aux[iDim] += VarCoord[iDim]; - } - config->SetMarkerMotion_Origin(Center_Aux, jMarker); - } - } - - /*--- Set the moment computation center to the new location after - incrementing the position with the rotation. ---*/ - - for (jMarker=0; jMarkerGetnMarker_Monitoring(); jMarker++) { - - Center_Aux[0] = config->GetRefOriginMoment_X(jMarker); - Center_Aux[1] = config->GetRefOriginMoment_Y(jMarker); - Center_Aux[2] = config->GetRefOriginMoment_Z(jMarker); - - /*--- Calculate non-dim. position from rotation center ---*/ - - for (iDim = 0; iDim < nDim; iDim++) - r[iDim] = (Center_Aux[iDim]-Center[iDim])/Lref; - if (nDim == 2) r[nDim] = 0.0; - - /*--- Compute transformed point coordinates ---*/ - - rotCoord[0] = rotMatrix[0][0]*r[0] - + rotMatrix[0][1]*r[1] - + rotMatrix[0][2]*r[2] + Center[0]; - - rotCoord[1] = rotMatrix[1][0]*r[0] - + rotMatrix[1][1]*r[1] - + rotMatrix[1][2]*r[2] + Center[1]; - - rotCoord[2] = rotMatrix[2][0]*r[0] - + rotMatrix[2][1]*r[1] - + rotMatrix[2][2]*r[2] + Center[2]; - - /*--- Calculate delta change in the x, y, & z directions ---*/ - for (iDim = 0; iDim < nDim; iDim++) - VarCoord[iDim] = (rotCoord[iDim]-Center_Aux[iDim])/Lref; - if (nDim == 2) VarCoord[nDim] = 0.0; - - config->SetRefOriginMoment_X(jMarker, Center_Aux[0]+VarCoord[0]); - config->SetRefOriginMoment_Y(jMarker, Center_Aux[1]+VarCoord[1]); - config->SetRefOriginMoment_Z(jMarker, Center_Aux[2]+VarCoord[2]); + else { + solver[MESH_0][MESH_SOL]->Surface_Rotating(geometry[MESH_0], + config, TimeIter, val_iZone); } - /*----------------------------------------------------------------------------------------------------------*/ - /*----------------------------------------------------------------------------------------------------------*/ - /*----------------------------------------------------------------------------------------------------------*/ - /*--- Deform the volume grid around the new boundary locations ---*/ /*--- Set volume deformation if new elastic mesh solver is not used ---*/ - /*--- If Deform_Mesh true, the mesh deformation is handled independently ---*/ + /*--- If Deform_Mesh true, the mesh deformation is handled by SetMesh_Deformation ---*/ + // CVC: Debug: To Do: What if multiple prescribed movements? E.g., Pitching + Plunging? if (rank == MASTER_NODE && !config->GetDeform_Mesh()) { cout << " Deforming the volume grid." << endl; grid_movement->SetVolume_Deformation(geometry[MESH_0], @@ -1271,7 +638,7 @@ void CFluidIteration::Iterate(COutput *output, if ((config[val_iZone]->GetGrid_Movement()) && (config[val_iZone]->GetAeroelastic_Simulation()) && unsteady) { - SetGrid_Movement(geometry[val_iZone][val_iInst], surface_movement[val_iZone], grid_movement[val_iZone][val_iInst], numerics[val_iZone][val_iInst], + SetGrid_Movement(geometry[val_iZone][val_iInst], surface_movement[val_iZone], grid_movement[val_iZone][val_iInst], solver[val_iZone][val_iInst], config[val_iZone], InnerIter, TimeIter); /*--- Apply a Wind Gust ---*/ diff --git a/SU2_CFD/src/solver_direct_mean.cpp b/SU2_CFD/src/solver_direct_mean.cpp index ac8a41c93f43..ed28f3a2bf2e 100644 --- a/SU2_CFD/src/solver_direct_mean.cpp +++ b/SU2_CFD/src/solver_direct_mean.cpp @@ -12757,7 +12757,7 @@ void CEulerSolver::LoadRestart(CGeometry **geometry, CSolver ***solver, CConfig /*--- Update the old geometry (coordinates n and n-1) in dual time-stepping strategy. ---*/ if (dual_time && config->GetGrid_Movement() && (config->GetKind_GridMovement() != RIGID_MOTION)) - Restart_OldGeometry(geometry[MESH_0], config);//solver[iMesh][MESH_SOL]->Restart_OldGeometry(geometry[MESH_0], config); + Restart_OldGeometry(geometry[MESH_0], config); delete [] Coord; diff --git a/SU2_CFD/src/solvers/CMeshSolver.cpp b/SU2_CFD/src/solvers/CMeshSolver.cpp index 0d0d37c5fcbf..0c6f69f50de2 100644 --- a/SU2_CFD/src/solvers/CMeshSolver.cpp +++ b/SU2_CFD/src/solvers/CMeshSolver.cpp @@ -1102,4 +1102,614 @@ void CMeshSolver::Restart_OldGeometry(CGeometry *geometry, CConfig *config) { // config->SetRefOriginMoment_Y(jMarker, Center[1]); // config->SetRefOriginMoment_Z(jMarker, Center[2]); // } -// } \ No newline at end of file +// } + +void CMeshSolver::Surface_Pitching(CGeometry *geometry, CConfig *config, + unsigned long iter, unsigned short iZone) { + + su2double deltaT, time_new, time_old, Lref, *Coord; + su2double Center[3], VarCoord[3], Omega[3], Ampl[3], Phase[3]; + su2double VarCoordAbs[3] = {0.0, 0.0, 0.0}; + su2double rotCoord[3], r[3] = {0.0,0.0,0.0}; + su2double rotMatrix[3][3] = {{0.0,0.0,0.0}, {0.0,0.0,0.0}, {0.0,0.0,0.0}}; + su2double dtheta, dphi, dpsi, cosTheta, sinTheta; + su2double cosPhi, sinPhi, cosPsi, sinPsi; + su2double DEG2RAD = PI_NUMBER/180.0; + unsigned short iMarker, jMarker, Moving, iDim, nDim = geometry->GetnDim(); + unsigned long iPoint, iVertex; + string Marker_Tag, Moving_Tag; + + CSolver *solver = NULL; + + /*--- Initialize the delta variation in coordinates ---*/ + VarCoord[0] = 0.0; VarCoord[1] = 0.0; VarCoord[2] = 0.0; + + /*--- Retrieve values from the config file ---*/ + + deltaT = config->GetDelta_UnstTimeND(); + Lref = config->GetLength_Ref(); + + /*--- Compute delta time based on physical time step ---*/ + time_new = static_cast(iter)*deltaT; + if (iter == 0) { + time_old = time_new; + } else { + time_old = static_cast(iter-1)*deltaT; + } + + /*--- Store displacement of each node on the pitching surface ---*/ + /*--- Loop over markers and find the particular marker(s) (surface) to pitch ---*/ + + for (iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { + Moving = config->GetMarker_All_Moving(iMarker); + if (Moving == YES) { + for (jMarker = 0; jMarkerGetnMarker_Moving(); jMarker++) { + + Moving_Tag = config->GetMarker_Moving_TagBound(jMarker); + Marker_Tag = config->GetMarker_All_TagBound(iMarker); + + if (Marker_Tag == Moving_Tag && (config->GetKind_SurfaceMovement(jMarker) == DEFORMING)) { + + /*--- Pitching origin, frequency, and amplitude from config. ---*/ + + for (iDim = 0; iDim < 3; iDim++){ + Ampl[iDim] = config->GetMarkerPitching_Ampl(jMarker, iDim)*DEG2RAD; + Omega[iDim] = config->GetMarkerPitching_Omega(jMarker, iDim)/config->GetOmega_Ref(); + Phase[iDim] = config->GetMarkerPitching_Phase(jMarker, iDim)*DEG2RAD; + Center[iDim] = config->GetMarkerMotion_Origin(jMarker, iDim); + } + /*--- Print some information to the console. Be verbose at the first + iteration only (mostly for debugging purposes). ---*/ + // Note that the MASTER_NODE might not contain all the markers being moved. + + if (rank == MASTER_NODE) { + cout << " Storing pitching displacement for marker: "; + cout << Marker_Tag << "." << endl; + if (iter == 0) { + cout << " Pitching frequency: (" << Omega[0] << ", " << Omega[1]; + cout << ", " << Omega[2] << ") rad/s about origin: (" << Center[0]; + cout << ", " << Center[1] << ", " << Center[2] << ")." << endl; + cout << " Pitching amplitude about origin: (" << Ampl[0]/DEG2RAD; + cout << ", " << Ampl[1]/DEG2RAD << ", " << Ampl[2]/DEG2RAD; + cout << ") degrees."<< endl; + cout << " Pitching phase lag about origin: (" << Phase[0]/DEG2RAD; + cout << ", " << Phase[1]/DEG2RAD <<", "<< Phase[2]/DEG2RAD; + cout << ") degrees."<< endl; + } + } + + /*--- Compute delta change in the angle about the x, y, & z axes. ---*/ + + dtheta = -Ampl[0]*(sin(Omega[0]*time_new + Phase[0]) + - sin(Omega[0]*time_old + Phase[0])); + dphi = -Ampl[1]*(sin(Omega[1]*time_new + Phase[1]) + - sin(Omega[1]*time_old + Phase[1])); + dpsi = -Ampl[2]*(sin(Omega[2]*time_new + Phase[2]) + - sin(Omega[2]*time_old + Phase[2])); + + /*--- Store angles separately for clarity. Compute sines/cosines. ---*/ + + cosTheta = cos(dtheta); cosPhi = cos(dphi); cosPsi = cos(dpsi); + sinTheta = sin(dtheta); sinPhi = sin(dphi); sinPsi = sin(dpsi); + + /*--- Compute the rotation matrix. Note that the implicit + ordering is rotation about the x-axis, y-axis, then z-axis. ---*/ + + rotMatrix[0][0] = cosPhi*cosPsi; + rotMatrix[1][0] = cosPhi*sinPsi; + rotMatrix[2][0] = -sinPhi; + + rotMatrix[0][1] = sinTheta*sinPhi*cosPsi - cosTheta*sinPsi; + rotMatrix[1][1] = sinTheta*sinPhi*sinPsi + cosTheta*cosPsi; + rotMatrix[2][1] = sinTheta*cosPhi; + + rotMatrix[0][2] = cosTheta*sinPhi*cosPsi + sinTheta*sinPsi; + rotMatrix[1][2] = cosTheta*sinPhi*sinPsi - sinTheta*cosPsi; + rotMatrix[2][2] = cosTheta*cosPhi; + + for (iVertex = 0; iVertex < geometry->nVertex[iMarker]; iVertex++) { + + /*--- Index and coordinates of the current point ---*/ + + iPoint = geometry->vertex[iMarker][iVertex]->GetNode(); + Coord = geometry->node[iPoint]->GetCoord(); + + /*--- Calculate non-dim. position from rotation center ---*/ + + for (iDim = 0; iDim < nDim; iDim++) + r[iDim] = (Coord[iDim]-Center[iDim])/Lref; + if (nDim == 2) r[nDim] = 0.0; + + /*--- Compute transformed point coordinates ---*/ + + rotCoord[0] = rotMatrix[0][0]*r[0] + + rotMatrix[0][1]*r[1] + + rotMatrix[0][2]*r[2] + Center[0]; + + rotCoord[1] = rotMatrix[1][0]*r[0] + + rotMatrix[1][1]*r[1] + + rotMatrix[1][2]*r[2] + Center[1]; + + rotCoord[2] = rotMatrix[2][0]*r[0] + + rotMatrix[2][1]*r[1] + + rotMatrix[2][2]*r[2] + Center[2]; + + /*--- Calculate delta change in the x, y, & z directions ---*/ + for (iDim = 0; iDim < nDim; iDim++) + VarCoord[iDim] = (rotCoord[iDim]-Coord[iDim])/Lref; + if (nDim == 2) VarCoord[nDim] = 0.0; + + // /*--- Set node displacement for volume deformation ---*/ + // geometry->vertex[iMarker][iVertex]->SetVarCoord(VarCoord); + + for (iDim = 0; iDim < 3; iDim++){ + VarCoordAbs[iDim] = nodes->GetBound_Disp(iPoint, iDim) + VarCoord[iDim]; + } + + nodes->SetBound_Disp(iPoint, VarCoordAbs); + } + } + } + } + } + /*--- For pitching we don't update the motion origin and moment reference origin. ---*/ + +} + +void CMeshSolver::Surface_Rotating(CGeometry *geometry, CConfig *config, + unsigned long iter, unsigned short iZone) { + su2double deltaT, time_new, time_old, Lref, *Coord; + su2double VarCoordAbs[3] = {0.0, 0.0, 0.0}; + su2double Center[3] = {0.0,0.0,0.0}, VarCoord[3] = {0.0,0.0,0.0}, Omega[3] = {0.0,0.0,0.0}, + rotCoord[3] = {0.0,0.0,0.0}, r[3] = {0.0,0.0,0.0}, Center_Aux[3] = {0.0,0.0,0.0}; + su2double rotMatrix[3][3] = {{0.0,0.0,0.0}, {0.0,0.0,0.0}, {0.0,0.0,0.0}}; + su2double dtheta, dphi, dpsi, cosTheta, sinTheta; + su2double cosPhi, sinPhi, cosPsi, sinPsi; + unsigned short iMarker, jMarker, Moving, iDim, nDim = geometry->GetnDim(); + unsigned long iPoint, iVertex; + string Marker_Tag, Moving_Tag; + + /*--- Initialize the delta variation in coordinates ---*/ + VarCoord[0] = 0.0; VarCoord[1] = 0.0; VarCoord[2] = 0.0; + + /*--- Retrieve values from the config file ---*/ + + deltaT = config->GetDelta_UnstTimeND(); + Lref = config->GetLength_Ref(); + + /*--- Compute delta time based on physical time step ---*/ + time_new = static_cast(iter)*deltaT; + if (iter == 0) { + time_old = time_new; + } else { + time_old = static_cast(iter-1)*deltaT; + } + + /*--- Store displacement of each node on the rotating surface ---*/ + /*--- Loop over markers and find the particular marker(s) (surface) to rotate ---*/ + + for (iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { + Moving = config->GetMarker_All_Moving(iMarker); + if (Moving == YES) { + for (jMarker = 0; jMarkerGetnMarker_Moving(); jMarker++) { + + Moving_Tag = config->GetMarker_Moving_TagBound(jMarker); + Marker_Tag = config->GetMarker_All_TagBound(iMarker); + + if (Marker_Tag == Moving_Tag && (config->GetKind_SurfaceMovement(jMarker) == DEFORMING)) { + + /*--- Rotation origin and angular velocity from config. ---*/ + + for (iDim = 0; iDim < 3; iDim++){ + Omega[iDim] = config->GetMarkerRotationRate(jMarker, iDim)/config->GetOmega_Ref(); + Center[iDim] = config->GetMarkerMotion_Origin(jMarker, iDim); + } + /*--- Print some information to the console. Be verbose at the first + iteration only (mostly for debugging purposes). ---*/ + // Note that the MASTER_NODE might not contain all the markers being moved. + + if (rank == MASTER_NODE) { + cout << " Storing rotating displacement for marker: "; + cout << Marker_Tag << "." << endl; + if (iter == 0) { + cout << " Angular velocity: (" << Omega[0] << ", " << Omega[1]; + cout << ", " << Omega[2] << ") rad/s about origin: (" << Center[0]; + cout << ", " << Center[1] << ", " << Center[2] << ")." << endl; + } + } + + /*--- Compute delta change in the angle about the x, y, & z axes. ---*/ + + dtheta = Omega[0]*(time_new-time_old); + dphi = Omega[1]*(time_new-time_old); + dpsi = Omega[2]*(time_new-time_old); + + /*--- Store angles separately for clarity. Compute sines/cosines. ---*/ + + cosTheta = cos(dtheta); cosPhi = cos(dphi); cosPsi = cos(dpsi); + sinTheta = sin(dtheta); sinPhi = sin(dphi); sinPsi = sin(dpsi); + + /*--- Compute the rotation matrix. Note that the implicit + ordering is rotation about the x-axis, y-axis, then z-axis. ---*/ + + rotMatrix[0][0] = cosPhi*cosPsi; + rotMatrix[1][0] = cosPhi*sinPsi; + rotMatrix[2][0] = -sinPhi; + + rotMatrix[0][1] = sinTheta*sinPhi*cosPsi - cosTheta*sinPsi; + rotMatrix[1][1] = sinTheta*sinPhi*sinPsi + cosTheta*cosPsi; + rotMatrix[2][1] = sinTheta*cosPhi; + + rotMatrix[0][2] = cosTheta*sinPhi*cosPsi + sinTheta*sinPsi; + rotMatrix[1][2] = cosTheta*sinPhi*sinPsi - sinTheta*cosPsi; + rotMatrix[2][2] = cosTheta*cosPhi; + + for (iVertex = 0; iVertex < geometry->nVertex[iMarker]; iVertex++) { + + /*--- Index and coordinates of the current point ---*/ + + iPoint = geometry->vertex[iMarker][iVertex]->GetNode(); + Coord = geometry->node[iPoint]->GetCoord(); + + /*--- Calculate non-dim. position from rotation center ---*/ + + for (iDim = 0; iDim < nDim; iDim++) + r[iDim] = (Coord[iDim]-Center[iDim])/Lref; + if (nDim == 2) r[nDim] = 0.0; + + /*--- Compute transformed point coordinates ---*/ + + rotCoord[0] = rotMatrix[0][0]*r[0] + + rotMatrix[0][1]*r[1] + + rotMatrix[0][2]*r[2] + Center[0]; + + rotCoord[1] = rotMatrix[1][0]*r[0] + + rotMatrix[1][1]*r[1] + + rotMatrix[1][2]*r[2] + Center[1]; + + rotCoord[2] = rotMatrix[2][0]*r[0] + + rotMatrix[2][1]*r[1] + + rotMatrix[2][2]*r[2] + Center[2]; + + /*--- Calculate delta change in the x, y, & z directions ---*/ + for (iDim = 0; iDim < nDim; iDim++) + VarCoord[iDim] = (rotCoord[iDim]-Coord[iDim])/Lref; + if (nDim == 2) VarCoord[nDim] = 0.0; + + // /*--- Set node displacement for volume deformation ---*/ + // geometry->vertex[iMarker][iVertex]->SetVarCoord(VarCoord); + + for (iDim = 0; iDim < 3; iDim++){ + VarCoordAbs[iDim] = nodes->GetBound_Disp(iPoint, iDim) + VarCoord[iDim]; + } + + nodes->SetBound_Disp(iPoint, VarCoordAbs); + } + } + } + } + } + + /*--- When updating the origins it is assumed that all markers have the + same rotation movement, because we use the last markers rotation matrix and center ---*/ + + /*--- Set the mesh motion center to the new location after + incrementing the position with the rotation. This new + location will be used for subsequent mesh motion for the given marker.---*/ + + for (jMarker=0; jMarkerGetnMarker_Moving(); jMarker++) { + + /*-- Check if we want to update the motion origin for the given marker ---*/ + + if (config->GetMoveMotion_Origin(jMarker) == YES) { + + for (iDim = 0; iDim < 3; iDim++){ + Center_Aux[iDim] = config->GetMarkerMotion_Origin(jMarker, iDim); + } + + /*--- Calculate non-dim. position from rotation center ---*/ + + for (iDim = 0; iDim < nDim; iDim++) + r[iDim] = (Center_Aux[iDim]-Center[iDim])/Lref; + if (nDim == 2) r[nDim] = 0.0; + + /*--- Compute transformed point coordinates ---*/ + + rotCoord[0] = rotMatrix[0][0]*r[0] + + rotMatrix[0][1]*r[1] + + rotMatrix[0][2]*r[2] + Center[0]; + + rotCoord[1] = rotMatrix[1][0]*r[0] + + rotMatrix[1][1]*r[1] + + rotMatrix[1][2]*r[2] + Center[1]; + + rotCoord[2] = rotMatrix[2][0]*r[0] + + rotMatrix[2][1]*r[1] + + rotMatrix[2][2]*r[2] + Center[2]; + + /*--- Calculate delta change in the x, y, & z directions ---*/ + for (iDim = 0; iDim < nDim; iDim++) + VarCoord[iDim] = (rotCoord[iDim]-Center_Aux[iDim])/Lref; + if (nDim == 2) VarCoord[nDim] = 0.0; + + for (iDim = 0; iDim < 3; iDim++){ + Center_Aux[iDim] += VarCoord[iDim]; + } + config->SetMarkerMotion_Origin(Center_Aux, jMarker); + } + } + + /*--- Set the moment computation center to the new location after + incrementing the position with the rotation. ---*/ + + for (jMarker=0; jMarkerGetnMarker_Monitoring(); jMarker++) { + + Center_Aux[0] = config->GetRefOriginMoment_X(jMarker); + Center_Aux[1] = config->GetRefOriginMoment_Y(jMarker); + Center_Aux[2] = config->GetRefOriginMoment_Z(jMarker); + + /*--- Calculate non-dim. position from rotation center ---*/ + + for (iDim = 0; iDim < nDim; iDim++) + r[iDim] = (Center_Aux[iDim]-Center[iDim])/Lref; + if (nDim == 2) r[nDim] = 0.0; + + /*--- Compute transformed point coordinates ---*/ + + rotCoord[0] = rotMatrix[0][0]*r[0] + + rotMatrix[0][1]*r[1] + + rotMatrix[0][2]*r[2] + Center[0]; + + rotCoord[1] = rotMatrix[1][0]*r[0] + + rotMatrix[1][1]*r[1] + + rotMatrix[1][2]*r[2] + Center[1]; + + rotCoord[2] = rotMatrix[2][0]*r[0] + + rotMatrix[2][1]*r[1] + + rotMatrix[2][2]*r[2] + Center[2]; + + /*--- Calculate delta change in the x, y, & z directions ---*/ + for (iDim = 0; iDim < nDim; iDim++) + VarCoord[iDim] = (rotCoord[iDim]-Center_Aux[iDim])/Lref; + if (nDim == 2) VarCoord[nDim] = 0.0; + + config->SetRefOriginMoment_X(jMarker, Center_Aux[0]+VarCoord[0]); + config->SetRefOriginMoment_Y(jMarker, Center_Aux[1]+VarCoord[1]); + config->SetRefOriginMoment_Z(jMarker, Center_Aux[2]+VarCoord[2]); + } +} + +void CMeshSolver::Surface_Plunging(CGeometry *geometry, CConfig *config, + unsigned long iter, unsigned short iZone) { + su2double deltaT, time_new, time_old, Lref; + su2double Center[3] = {0.0, 0.0, 0.0}, VarCoord[3], Omega[3], Ampl[3]; + su2double VarCoordAbs[3] = {0.0, 0.0, 0.0}; + su2double DEG2RAD = PI_NUMBER/180.0; + unsigned short iMarker, jMarker, Moving; + unsigned long iPoint, iVertex; + string Marker_Tag, Moving_Tag; + unsigned short iDim; + + /*--- Initialize the delta variation in coordinates ---*/ + VarCoord[0] = 0.0; VarCoord[1] = 0.0; VarCoord[2] = 0.0; + + /*--- Retrieve values from the config file ---*/ + + deltaT = config->GetDelta_UnstTimeND(); + Lref = config->GetLength_Ref(); + + /*--- Compute delta time based on physical time step ---*/ + time_new = static_cast(iter)*deltaT; + if (iter == 0) { + time_old = time_new; + } else { + time_old = static_cast(iter-1)*deltaT; + } + + /*--- Store displacement of each node on the plunging surface ---*/ + /*--- Loop over markers and find the particular marker(s) (surface) to plunge ---*/ + + for (iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { + Moving = config->GetMarker_All_Moving(iMarker); + if (Moving == YES) { + for (jMarker = 0; jMarkerGetnMarker_Moving(); jMarker++) { + + Moving_Tag = config->GetMarker_Moving_TagBound(jMarker); + Marker_Tag = config->GetMarker_All_TagBound(iMarker); + + if (Marker_Tag == Moving_Tag && (config->GetKind_SurfaceMovement(jMarker) == DEFORMING)) { + + /*--- Plunging frequency and amplitude from config. ---*/ + + for (iDim = 0; iDim < 3; iDim++){ + Ampl[iDim] = config->GetMarkerPlunging_Ampl(jMarker, iDim)/Lref; + Omega[iDim] = config->GetMarkerPlunging_Omega(jMarker, iDim)/config->GetOmega_Ref(); + Center[iDim] = config->GetMarkerMotion_Origin(jMarker, iDim); + } + /*--- Print some information to the console. Be verbose at the first + iteration only (mostly for debugging purposes). ---*/ + // Note that the MASTER_NODE might not contain all the markers being moved. + + if (rank == MASTER_NODE) { + cout << " Storing plunging displacement for marker: "; + cout << Marker_Tag << "." << endl; + if (iter == 0) { + cout << " Plunging frequency: (" << Omega[0] << ", " << Omega[1]; + cout << ", " << Omega[2] << ") rad/s." << endl; + cout << " Plunging amplitude: (" << Ampl[0]/DEG2RAD; + cout << ", " << Ampl[1]/DEG2RAD << ", " << Ampl[2]/DEG2RAD; + cout << ") degrees."<< endl; + } + } + + /*--- Compute delta change in the position in the x, y, & z directions. ---*/ + + VarCoord[0] = -Ampl[0]*(sin(Omega[0]*time_new) - sin(Omega[0]*time_old)); + VarCoord[1] = -Ampl[1]*(sin(Omega[1]*time_new) - sin(Omega[1]*time_old)); + VarCoord[2] = -Ampl[2]*(sin(Omega[2]*time_new) - sin(Omega[2]*time_old)); + + for (iVertex = 0; iVertex < geometry->nVertex[iMarker]; iVertex++) { + + // /*--- Set node displacement for volume deformation ---*/ + // geometry->vertex[iMarker][iVertex]->SetVarCoord(VarCoord); + + iPoint = geometry->vertex[iMarker][iVertex]->GetNode(); + + for (iDim = 0; iDim < 3; iDim++){ + VarCoordAbs[iDim] = nodes->GetBound_Disp(iPoint, iDim) + VarCoord[iDim]; + } + + nodes->SetBound_Disp(iPoint, VarCoordAbs); + + } + } + } + } + } + + /*--- When updating the origins it is assumed that all markers have the + same plunging movement, because we use the last VarCoord set ---*/ + + /*--- Set the mesh motion center to the new location after + incrementing the position with the translation. This new + location will be used for subsequent mesh motion for the given marker.---*/ + + for (jMarker=0; jMarkerGetnMarker_Moving(); jMarker++) { + + /*-- Check if we want to update the motion origin for the given marker ---*/ + + if (config->GetMoveMotion_Origin(jMarker) == YES) { + for (iDim = 0; iDim < 3; iDim++){ + Center[iDim] += VarCoord[iDim]; + } + config->SetMarkerMotion_Origin(Center, jMarker); + } + } + + /*--- Set the moment computation center to the new location after + incrementing the position with the plunging. ---*/ + + for (jMarker=0; jMarkerGetnMarker_Monitoring(); jMarker++) { + Center[0] = config->GetRefOriginMoment_X(jMarker) + VarCoord[0]; + Center[1] = config->GetRefOriginMoment_Y(jMarker) + VarCoord[1]; + Center[2] = config->GetRefOriginMoment_Z(jMarker) + VarCoord[2]; + config->SetRefOriginMoment_X(jMarker, Center[0]); + config->SetRefOriginMoment_Y(jMarker, Center[1]); + config->SetRefOriginMoment_Z(jMarker, Center[2]); + } +} + +void CMeshSolver::Surface_Translating(CGeometry *geometry, CConfig *config, + unsigned long iter, unsigned short iZone) { + su2double deltaT, time_new, time_old; + su2double Center[3] = {0.0,0.0,0.0}, VarCoord[3] = {0.0,0.0,0.0}; + su2double VarCoordAbs[3] = {0.0, 0.0, 0.0}; + su2double xDot[3] = {0.0,0.0,0.0}; + unsigned short iMarker, jMarker, Moving; + unsigned long iPoint, iVertex; + string Marker_Tag, Moving_Tag; + unsigned short iDim; + + /*--- Initialize the delta variation in coordinates ---*/ + VarCoord[0] = 0.0; VarCoord[1] = 0.0; VarCoord[2] = 0.0; + + /*--- Retrieve values from the config file ---*/ + + deltaT = config->GetDelta_UnstTimeND(); + + /*--- Compute delta time based on physical time step ---*/ + time_new = static_cast(iter)*deltaT; + if (iter == 0) { + time_old = time_new; + } else { + time_old = static_cast(iter-1)*deltaT; + } + + /*--- Store displacement of each node on the translating surface ---*/ + /*--- Loop over markers and find the particular marker(s) (surface) to translate ---*/ + + for (iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { + Moving = config->GetMarker_All_Moving(iMarker); + if (Moving == YES) { + for (jMarker = 0; jMarkerGetnMarker_Moving(); jMarker++) { + + Moving_Tag = config->GetMarker_Moving_TagBound(jMarker); + Marker_Tag = config->GetMarker_All_TagBound(iMarker); + + if (Marker_Tag == Moving_Tag && (config->GetKind_SurfaceMovement(jMarker) == DEFORMING)) { + + for (iDim = 0; iDim < 3; iDim++){ + xDot[iDim] = config->GetMarkerTranslationRate(jMarker, iDim); + Center[iDim] = config->GetMarkerMotion_Origin(jMarker, iDim); + } + + /*--- Print some information to the console. Be verbose at the first + iteration only (mostly for debugging purposes). ---*/ + // Note that the MASTER_NODE might not contain all the markers being moved. + + if (rank == MASTER_NODE) { + cout << " Storing translating displacement for marker: "; + cout << Marker_Tag << "." << endl; + if (iter == 0) { + cout << " Translational velocity: (" << xDot[0]*config->GetVelocity_Ref() << ", " << xDot[1]*config->GetVelocity_Ref(); + cout << ", " << xDot[2]*config->GetVelocity_Ref(); + if (config->GetSystemMeasurements() == SI) cout << ") m/s." << endl; + else cout << ") ft/s." << endl; + } + } + + /*--- Compute delta change in the position in the x, y, & z directions. ---*/ + + VarCoord[0] = xDot[0]*(time_new-time_old); + VarCoord[1] = xDot[1]*(time_new-time_old); + VarCoord[2] = xDot[2]*(time_new-time_old); + + for (iVertex = 0; iVertex < geometry->nVertex[iMarker]; iVertex++) { + + // /*--- Set node displacement for volume deformation ---*/ + // geometry->vertex[iMarker][iVertex]->SetVarCoord(VarCoord); + + iPoint = geometry->vertex[iMarker][iVertex]->GetNode(); + + for (iDim = 0; iDim < 3; iDim++){ + VarCoordAbs[iDim] = nodes->GetBound_Disp(iPoint, iDim) + VarCoord[iDim]; + } + + nodes->SetBound_Disp(iPoint, VarCoordAbs); + } + } + } + } + } + + /*--- When updating the origins it is assumed that all markers have the + same translational velocity, because we use the last VarCoord set ---*/ + + /*--- Set the mesh motion center to the new location after + incrementing the position with the translation. This new + location will be used for subsequent mesh motion for the given marker.---*/ + + for (jMarker=0; jMarkerGetnMarker_Moving(); jMarker++) { + + /*-- Check if we want to update the motion origin for the given marker ---*/ + + if (config->GetMoveMotion_Origin(jMarker) == YES) { + for (iDim = 0; iDim < 3; iDim++){ + Center[iDim] += VarCoord[iDim]; + } + config->SetMarkerMotion_Origin(Center, jMarker); + } + } + + /*--- Set the moment computation center to the new location after + incrementing the position with the translation. ---*/ + + for (jMarker=0; jMarkerGetnMarker_Monitoring(); jMarker++) { + Center[0] = config->GetRefOriginMoment_X(jMarker) + VarCoord[0]; + Center[1] = config->GetRefOriginMoment_Y(jMarker) + VarCoord[1]; + Center[2] = config->GetRefOriginMoment_Z(jMarker) + VarCoord[2]; + config->SetRefOriginMoment_X(jMarker, Center[0]); + config->SetRefOriginMoment_Y(jMarker, Center[1]); + config->SetRefOriginMoment_Z(jMarker, Center[2]); + } +} \ No newline at end of file From 17f4158b6c9500a44a6656d5f168c1fb9b5e0a79 Mon Sep 17 00:00:00 2001 From: cvencro Date: Wed, 27 Nov 2019 02:18:32 +0000 Subject: [PATCH 005/112] Load restarts with CMeshSolver if deforming mesh --- Common/src/geometry_structure.cpp | 2 +- SU2_CFD/src/iteration_structure.cpp | 8 ++++++++ SU2_CFD/src/solver_direct_mean.cpp | 7 +++++-- SU2_CFD/src/solvers/CMeshSolver.cpp | 6 ++++-- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/Common/src/geometry_structure.cpp b/Common/src/geometry_structure.cpp index acdcfda7f207..77c98ef90ed4 100644 --- a/Common/src/geometry_structure.cpp +++ b/Common/src/geometry_structure.cpp @@ -2727,7 +2727,7 @@ void CGeometry::UpdateGeometry(CGeometry **geometry_container, CConfig *config) geometry_container[MESH_0]->InitiateComms(geometry_container[MESH_0], config, COORDINATES); geometry_container[MESH_0]->CompleteComms(geometry_container[MESH_0], config, COORDINATES); - if (config->GetGrid_Movement()){ + if (config->GetGrid_Movement() || config->GetDynamic_Grid()){ geometry_container[MESH_0]->InitiateComms(geometry_container[MESH_0], config, GRID_VELOCITY); geometry_container[MESH_0]->CompleteComms(geometry_container[MESH_0], config, GRID_VELOCITY); } diff --git a/SU2_CFD/src/iteration_structure.cpp b/SU2_CFD/src/iteration_structure.cpp index ea7edaf7e62f..77556b415b7e 100644 --- a/SU2_CFD/src/iteration_structure.cpp +++ b/SU2_CFD/src/iteration_structure.cpp @@ -2151,6 +2151,10 @@ void CDiscAdjFluidIteration::Preprocess(COutput *output, LoadUnsteady_Solution(geometry, solver,config, val_iInst, val_iZone, Direct_Iter); + if (config[val_iZone]->GetDeform_Mesh()) { + solver[val_iZone][val_iInst][MESH_0][MESH_SOL]->LoadRestart(geometry[val_iZone][val_iInst], solver[val_iZone][val_iInst], config[val_iZone], Direct_Iter, true); + } + } else if ((TimeIter > 0) && dual_time) { /*--- @@ -2160,6 +2164,10 @@ void CDiscAdjFluidIteration::Preprocess(COutput *output, Afterwards the GridVelocity is computed based on the Coordinates. ---*/ + if (config[val_iZone]->GetDeform_Mesh()) { + solver[val_iZone][val_iInst][MESH_0][MESH_SOL]->LoadRestart(geometry[val_iZone][val_iInst], solver[val_iZone][val_iInst], config[val_iZone], Direct_Iter, true); + } + /*--- Load solution timestep n-1 | n-2 for DualTimestepping 1st | 2nd order ---*/ if (dual_time_1st){ LoadUnsteady_Solution(geometry, solver,config, val_iInst, val_iZone, Direct_Iter - 1); diff --git a/SU2_CFD/src/solver_direct_mean.cpp b/SU2_CFD/src/solver_direct_mean.cpp index f34e36fcbfc5..7384b85adb64 100644 --- a/SU2_CFD/src/solver_direct_mean.cpp +++ b/SU2_CFD/src/solver_direct_mean.cpp @@ -12756,8 +12756,11 @@ void CEulerSolver::LoadRestart(CGeometry **geometry, CSolver ***solver, CConfig /*--- Update the old geometry (coordinates n and n-1) in dual time-stepping strategy. ---*/ - if (dual_time && config->GetGrid_Movement() && (config->GetKind_GridMovement() != RIGID_MOTION)) - Restart_OldGeometry(geometry[MESH_0], config); + if (dual_time && config->GetGrid_Movement() && (config->GetKind_GridMovement() != RIGID_MOTION)) { + if (!config->GetDeform_Mesh()) { + Restart_OldGeometry(geometry[MESH_0], config); + } + } delete [] Coord; diff --git a/SU2_CFD/src/solvers/CMeshSolver.cpp b/SU2_CFD/src/solvers/CMeshSolver.cpp index b11d212a1e00..a3cc8270ab03 100644 --- a/SU2_CFD/src/solvers/CMeshSolver.cpp +++ b/SU2_CFD/src/solvers/CMeshSolver.cpp @@ -815,7 +815,9 @@ void CMeshSolver::Restart_OldGeometry(CGeometry *geometry, CConfig *config) { unsigned short CommType = (iStep == 1) ? SOLUTION_TIME_N : SOLUTION_TIME_N1; /*--- Modify file name for an unsteady restart ---*/ - int Unst_RestartIter = SU2_TYPE::Int(config->GetRestart_Iter())-iStep; + int Unst_RestartIter; + if (config->GetRestart()) Unst_RestartIter = SU2_TYPE::Int(config->GetRestart_Iter()) - SU2_TYPE::Int(config->GetTimeIter())-iStep-1; + else Unst_RestartIter = SU2_TYPE::Int(config->GetUnst_AdjointIter()) - SU2_TYPE::Int(config->GetTimeIter())-iStep-1; string filename_n = config->GetUnsteady_FileName(filename, Unst_RestartIter, ""); /*--- Read the restart data from either an ASCII or binary SU2 file. ---*/ @@ -1613,4 +1615,4 @@ void CMeshSolver::Surface_Translating(CGeometry *geometry, CConfig *config, config->SetRefOriginMoment_Y(jMarker, Center[1]); config->SetRefOriginMoment_Z(jMarker, Center[2]); } -} \ No newline at end of file +} From a29dc5784be354e943aee8f0e27ed31f4cbdb81a Mon Sep 17 00:00:00 2001 From: cvencro Date: Wed, 27 Nov 2019 13:06:37 +0000 Subject: [PATCH 006/112] Use initial mesh to set up negative restart request in dual-time --- SU2_CFD/src/solvers/CMeshSolver.cpp | 96 +++++++++++++++++------------ 1 file changed, 56 insertions(+), 40 deletions(-) diff --git a/SU2_CFD/src/solvers/CMeshSolver.cpp b/SU2_CFD/src/solvers/CMeshSolver.cpp index a3cc8270ab03..be4313f15450 100644 --- a/SU2_CFD/src/solvers/CMeshSolver.cpp +++ b/SU2_CFD/src/solvers/CMeshSolver.cpp @@ -818,64 +818,80 @@ void CMeshSolver::Restart_OldGeometry(CGeometry *geometry, CConfig *config) { int Unst_RestartIter; if (config->GetRestart()) Unst_RestartIter = SU2_TYPE::Int(config->GetRestart_Iter()) - SU2_TYPE::Int(config->GetTimeIter())-iStep-1; else Unst_RestartIter = SU2_TYPE::Int(config->GetUnst_AdjointIter()) - SU2_TYPE::Int(config->GetTimeIter())-iStep-1; - string filename_n = config->GetUnsteady_FileName(filename, Unst_RestartIter, ""); - /*--- Read the restart data from either an ASCII or binary SU2 file. ---*/ + if (Unst_RestartIter < 0) { - if (config->GetRead_Binary_Restart()) { - Read_SU2_Restart_Binary(geometry, config, filename_n); - } else { - Read_SU2_Restart_ASCII(geometry, config, filename_n); + if (rank == MASTER_NODE) cout << "Requested mesh restart filename is negative. Setting initial mesh" << endl; + + /*--- Set loaded solution into correct previous time containers. ---*/ + unsigned long iPoint; + for (iPoint = 0; iPoint < nPoint; iPoint++ ) { + for (unsigned short iDim = 0; iDim < nDim; iDim++){ + if(iStep==1) + nodes->Set_Solution_time_n(iPoint, iDim, nodes->GetSolution(iPoint, iDim)); + else + nodes->Set_Solution_time_n1(iPoint, iDim, nodes->GetSolution(iPoint, iDim)); + } + } } + else { + string filename_n = config->GetUnsteady_FileName(filename, Unst_RestartIter, ""); - /*--- Load data from the restart into correct containers. ---*/ - int counter = 0; - for (iPoint_Global = 0; iPoint_Global < geometry->GetGlobal_nPointDomain(); iPoint_Global++ ) { + /*--- Read the restart data from either an ASCII or binary SU2 file. ---*/ - /*--- Retrieve local index. If this node from the restart file lives - on the current processor, we will load and instantiate the vars. ---*/ + if (config->GetRead_Binary_Restart()) { + Read_SU2_Restart_Binary(geometry, config, filename_n); + } else { + Read_SU2_Restart_ASCII(geometry, config, filename_n); + } - long iPoint_Local = geometry->GetGlobal_to_Local_Point(iPoint_Global); + /*--- Load data from the restart into correct containers. ---*/ + int counter = 0; + for (iPoint_Global = 0; iPoint_Global < geometry->GetGlobal_nPointDomain(); iPoint_Global++ ) { - if (iPoint_Local > -1) { + /*--- Retrieve local index. If this node from the restart file lives + on the current processor, we will load and instantiate the vars. ---*/ - /*--- We need to store this point's data, so jump to the correct - offset in the buffer of data from the restart file and load it. ---*/ + long iPoint_Local = geometry->GetGlobal_to_Local_Point(iPoint_Global); - unsigned long index = counter*Restart_Vars[1]; - for (unsigned short iDim = 0; iDim < nDim; iDim++){ - curr_coord = Restart_Data[index+iDim]; - displ = curr_coord - nodes->GetMesh_Coord(iPoint_Local,iDim); + if (iPoint_Local > -1) { - if(iStep==1) - nodes->Set_Solution_time_n(iPoint_Local, iDim, displ); - else - nodes->Set_Solution_time_n1(iPoint_Local, iDim, displ); - } - iPoint_Global_Local++; + /*--- We need to store this point's data, so jump to the correct + offset in the buffer of data from the restart file and load it. ---*/ - /*--- Increment the overall counter for how many points have been loaded. ---*/ - counter++; - } + unsigned long index = counter*Restart_Vars[1]; + for (unsigned short iDim = 0; iDim < nDim; iDim++){ + curr_coord = Restart_Data[index+iDim]; + displ = curr_coord - nodes->GetMesh_Coord(iPoint_Local,iDim); - } + if(iStep==1) + nodes->Set_Solution_time_n(iPoint_Local, iDim, displ); + else + nodes->Set_Solution_time_n1(iPoint_Local, iDim, displ); + } + iPoint_Global_Local++; - /*--- Detect a wrong solution file ---*/ + /*--- Increment the overall counter for how many points have been loaded. ---*/ + counter++; + } + } - if (iPoint_Global_Local < nPointDomain) { sbuf_NotMatching = 1; } + /*--- Detect a wrong solution file ---*/ - SU2_MPI::Allreduce(&sbuf_NotMatching, &rbuf_NotMatching, 1, MPI_UNSIGNED_SHORT, MPI_SUM, MPI_COMM_WORLD); + if (iPoint_Global_Local < nPointDomain) { sbuf_NotMatching = 1; } - if (rbuf_NotMatching != 0) { - SU2_MPI::Error(string("The solution file ") + filename_n + string(" doesn't match with the mesh file!\n") + - string("It could be empty lines at the end of the file."), CURRENT_FUNCTION); - } + SU2_MPI::Allreduce(&sbuf_NotMatching, &rbuf_NotMatching, 1, MPI_UNSIGNED_SHORT, MPI_SUM, MPI_COMM_WORLD); - /*--- Delete the class memory that is used to load the restart. ---*/ + if (rbuf_NotMatching != 0) { + SU2_MPI::Error(string("The solution file ") + filename_n + string(" doesn't match with the mesh file!\n") + + string("It could be empty lines at the end of the file."), CURRENT_FUNCTION); + } - if (Restart_Vars != NULL) { delete [] Restart_Vars; Restart_Vars = NULL; } - if (Restart_Data != NULL) { delete [] Restart_Data; Restart_Data = NULL; } + /*--- Delete the class memory that is used to load the restart. ---*/ + if (Restart_Vars != NULL) { delete [] Restart_Vars; Restart_Vars = NULL; } + if (Restart_Data != NULL) { delete [] Restart_Data; Restart_Data = NULL; } + } InitiateComms(geometry, config, CommType); CompleteComms(geometry, config, CommType); } @@ -1615,4 +1631,4 @@ void CMeshSolver::Surface_Translating(CGeometry *geometry, CConfig *config, config->SetRefOriginMoment_Y(jMarker, Center[1]); config->SetRefOriginMoment_Z(jMarker, Center[2]); } -} +} \ No newline at end of file From b6a6bed73cdbd19f456c8f87a1eca6154f23d387 Mon Sep 17 00:00:00 2001 From: cvencro Date: Tue, 3 Dec 2019 15:00:52 +0000 Subject: [PATCH 007/112] Revert Euler wall boundary condition --- SU2_CFD/src/solver_direct_mean.cpp | 172 ++++++++++++++++++++++++++++- 1 file changed, 170 insertions(+), 2 deletions(-) diff --git a/SU2_CFD/src/solver_direct_mean.cpp b/SU2_CFD/src/solver_direct_mean.cpp index 7384b85adb64..68f4479eb783 100644 --- a/SU2_CFD/src/solver_direct_mean.cpp +++ b/SU2_CFD/src/solver_direct_mean.cpp @@ -7804,9 +7804,177 @@ void CEulerSolver::BC_Euler_Wall(CGeometry *geometry, CConfig *config, unsigned short val_marker) { - /*--- Call the equivalent symmetry plane boundary condition. ---*/ - BC_Sym_Plane(geometry, solver_container, conv_numerics, visc_numerics, config, val_marker); + unsigned short iDim, iVar, jVar, kVar, jDim; + unsigned long iPoint, iVertex; + su2double *Normal = NULL, *GridVel = NULL, Area, UnitNormal[3], *NormalArea, + ProjGridVel = 0.0, turb_ke; + su2double Density_b, StaticEnergy_b, Enthalpy_b, *Velocity_b, Kappa_b, Chi_b, Energy_b, VelMagnitude2_b, Pressure_b; + su2double Density_i, *Velocity_i, ProjVelocity_i = 0.0, Energy_i, VelMagnitude2_i; + su2double **Jacobian_b, **DubDu; + + bool implicit = (config->GetKind_TimeIntScheme_Flow() == EULER_IMPLICIT); + bool tkeNeeded = (config->GetKind_Turb_Model() == SST) || (config->GetKind_Turb_Model() == SST_SUST); + + Normal = new su2double[nDim]; + NormalArea = new su2double[nDim]; + Velocity_b = new su2double[nDim]; + Velocity_i = new su2double[nDim]; + Jacobian_b = new su2double*[nVar]; + DubDu = new su2double*[nVar]; + for (iVar = 0; iVar < nVar; iVar++) { + Jacobian_b[iVar] = new su2double[nVar]; + DubDu[iVar] = new su2double[nVar]; + } + + /*--- Loop over all the vertices on this boundary marker ---*/ + + for (iVertex = 0; iVertex < geometry->nVertex[val_marker]; iVertex++) { + iPoint = geometry->vertex[val_marker][iVertex]->GetNode(); + + /*--- Check if the node belongs to the domain (i.e, not a halo node) ---*/ + + if (geometry->node[iPoint]->GetDomain()) { + + /*--- Normal vector for this vertex (negative for outward convention) ---*/ + + geometry->vertex[val_marker][iVertex]->GetNormal(Normal); + + Area = 0.0; + for (iDim = 0; iDim < nDim; iDim++) Area += Normal[iDim]*Normal[iDim]; + Area = sqrt (Area); + + for (iDim = 0; iDim < nDim; iDim++) { + NormalArea[iDim] = -Normal[iDim]; + UnitNormal[iDim] = -Normal[iDim]/Area; + } + + /*--- Get the state i ---*/ + + VelMagnitude2_i = 0.0; ProjVelocity_i = 0.0; + for (iDim = 0; iDim < nDim; iDim++) { + Velocity_i[iDim] = nodes->GetVelocity(iPoint, iDim); + ProjVelocity_i += Velocity_i[iDim]*UnitNormal[iDim]; + VelMagnitude2_i += Velocity_i[iDim]*Velocity_i[iDim]; + } + Density_i = nodes->GetDensity(iPoint); + Energy_i = nodes->GetEnergy(iPoint); + + /*--- Compute the boundary state b ---*/ + + for (iDim = 0; iDim < nDim; iDim++) + Velocity_b[iDim] = Velocity_i[iDim] - ProjVelocity_i * UnitNormal[iDim]; //Force the velocity to be tangential to the surface. + + if (dynamic_grid) { + GridVel = geometry->node[iPoint]->GetGridVel(); + ProjGridVel = 0.0; + for (iDim = 0; iDim < nDim; iDim++) ProjGridVel += GridVel[iDim]*UnitNormal[iDim]; + for (iDim = 0; iDim < nDim; iDim++) Velocity_b[iDim] += GridVel[iDim] - ProjGridVel * UnitNormal[iDim]; + } + + VelMagnitude2_b = 0.0; + for (iDim = 0; iDim < nDim; iDim++) + VelMagnitude2_b += Velocity_b[iDim] * Velocity_b[iDim]; + + /*--- Compute the residual ---*/ + turb_ke = 0.0; + if (tkeNeeded) turb_ke = solver_container[TURB_SOL]->GetNodes()->GetSolution(iPoint, 0); + + Density_b = Density_i; + StaticEnergy_b = Energy_i - 0.5 * VelMagnitude2_i - turb_ke; + Energy_b = StaticEnergy_b + 0.5 * VelMagnitude2_b + turb_ke; + + FluidModel->SetTDState_rhoe(Density_b, StaticEnergy_b); + Kappa_b = FluidModel->GetdPde_rho() / Density_b; + Chi_b = FluidModel->GetdPdrho_e() - Kappa_b * StaticEnergy_b; + Pressure_b = FluidModel->GetPressure(); + Enthalpy_b = Energy_b + Pressure_b/Density_b; + + conv_numerics->GetInviscidProjFlux(&Density_b, Velocity_b, &Pressure_b, &Enthalpy_b, NormalArea, Residual); + + /*--- Grid velocity correction to the energy term ---*/ + if (dynamic_grid) { + GridVel = geometry->node[iPoint]->GetGridVel(); + ProjGridVel = 0.0; + for (iDim = 0; iDim < nDim; iDim++) + ProjGridVel += GridVel[iDim]*UnitNormal[iDim]; + Residual[nVar-1] += Pressure_b*ProjGridVel*Area; + } + + /*--- Add the Reynolds stress tensor contribution ---*/ + + if (tkeNeeded) { + for (iDim = 0; iDim < nDim; iDim++) + Residual[iDim+1] += (2.0/3.0)*Density_b*turb_ke*NormalArea[iDim]; + } + + /*--- Add value to the residual ---*/ + + LinSysRes.AddBlock(iPoint, Residual); + + /*--- Form Jacobians for implicit computations ---*/ + + if (implicit) { + + /*--- Initialize Jacobian ---*/ + + for (iVar = 0; iVar < nVar; iVar++) { + for (jVar = 0; jVar < nVar; jVar++) + Jacobian_i[iVar][jVar] = 0.0; + } + + /*--- Compute DubDu ---*/ + + for (iVar = 0; iVar < nVar; iVar++) { + for (jVar = 0; jVar < nVar; jVar++) + DubDu[iVar][jVar]= 0.0; + DubDu[iVar][iVar]= 1.0; + } + + for (iDim = 0; iDim < nDim; iDim++) + for (jDim = 0; jDimGetInviscidProjJac(Velocity_b, &Enthalpy_b, &Chi_b, &Kappa_b, NormalArea, 1, Jacobian_b); + + // Check for grid movement, should be already considered since Jacobian b is computed from u_b + // if (grid_movement) { + // Jacobian_b[nVar-1][0] += 0.5*ProjGridVel*ProjGridVel; + // for (iDim = 0; iDim < nDim; iDim++) + // Jacobian_b[nVar-1][iDim+1] -= ProjGridVel * UnitNormal[iDim]; + // } + + /*--- Compute numerical flux Jacobian at node i ---*/ + + for (iVar = 0; iVar < nVar; iVar++) + for (jVar = 0; jVar < nVar; jVar++) + for (kVar = 0; kVar < nVar; kVar++) + Jacobian_i[iVar][jVar] += Jacobian_b[iVar][kVar] * DubDu[kVar][jVar]; + + /*--- Add the Jacobian to the sparse matrix ---*/ + + Jacobian.AddBlock(iPoint, iPoint, Jacobian_i); + + } + } + } + + delete [] Normal; + delete [] NormalArea; + delete [] Velocity_b; + delete [] Velocity_i; + for (iVar = 0; iVar < nVar; iVar++) { + delete [] Jacobian_b[iVar]; + delete [] DubDu[iVar]; + } + delete [] Jacobian_b; + delete [] DubDu; } From 27ec14cff0bc8120f1a9827d59a435d9e31dc844 Mon Sep 17 00:00:00 2001 From: cvencro Date: Sun, 8 Dec 2019 11:25:28 +0000 Subject: [PATCH 008/112] tidy up --- SU2_CFD/src/iteration_structure.cpp | 13 +-- SU2_CFD/src/solver_structure.cpp | 8 -- SU2_CFD/src/solvers/CMeshSolver.cpp | 125 ---------------------------- 3 files changed, 4 insertions(+), 142 deletions(-) diff --git a/SU2_CFD/src/iteration_structure.cpp b/SU2_CFD/src/iteration_structure.cpp index 637747cb5e0c..464445045676 100644 --- a/SU2_CFD/src/iteration_structure.cpp +++ b/SU2_CFD/src/iteration_structure.cpp @@ -104,11 +104,6 @@ void CIteration::SetGrid_Movement(CGeometry **geometry, /*--- Compute the new node locations for moving markers ---*/ - // CVC: To do: For deformation with new mesh solver, need to access solver[MESH_0][MESH_SOL] for SetBound_Disp - // inside Surface_Movement functions, rest of code is identical to grid_movement_structure.cpp - // Either enable solver[MESH_0][MESH_SOL] inside grid_movement_structure.cpp - // or move Surface_Movement functions to inside CMeshSolver - if (!config->GetDeform_Mesh()) { surface_movement->Surface_Translating(geometry[MESH_0], config, TimeIter, val_iZone); @@ -121,7 +116,7 @@ void CIteration::SetGrid_Movement(CGeometry **geometry, /*--- Deform the volume grid around the new boundary locations ---*/ /*--- Set volume deformation if new elastic mesh solver is not used ---*/ /*--- If Deform_Mesh true, the mesh deformation is handled by SetMesh_Deformation ---*/ - // CVC: Debug: To Do: What if multiple prescribed movements? E.g., Pitching + Plunging? + // To Do: What if multiple prescribed movements? E.g., Pitching + Plunging? if (rank == MASTER_NODE && !config->GetDeform_Mesh()) { cout << " Deforming the volume grid." << endl; @@ -145,7 +140,7 @@ void CIteration::SetGrid_Movement(CGeometry **geometry, /*--- Deform the volume grid around the new boundary locations ---*/ /*--- Set volume deformation if new elastic mesh solver is not used ---*/ /*--- If Deform_Mesh true, the mesh deformation is handled by SetMesh_Deformation ---*/ - // CVC: Debug: To Do: What if multiple prescribed movements? E.g., Pitching + Plunging? + // To Do: What if multiple prescribed movements? E.g., Pitching + Plunging? if (rank == MASTER_NODE && !config->GetDeform_Mesh()) { cout << " Deforming the volume grid." << endl; @@ -169,7 +164,7 @@ void CIteration::SetGrid_Movement(CGeometry **geometry, /*--- Deform the volume grid around the new boundary locations ---*/ /*--- Set volume deformation if new elastic mesh solver is not used ---*/ /*--- If Deform_Mesh true, the mesh deformation is handled by SetMesh_Deformation ---*/ - // CVC: Debug: To Do: What if multiple prescribed movements? E.g., Pitching + Plunging? + // To Do: What if multiple prescribed movements? E.g., Pitching + Plunging? if (rank == MASTER_NODE && !config->GetDeform_Mesh()) { cout << " Deforming the volume grid." << endl; @@ -193,7 +188,7 @@ void CIteration::SetGrid_Movement(CGeometry **geometry, /*--- Deform the volume grid around the new boundary locations ---*/ /*--- Set volume deformation if new elastic mesh solver is not used ---*/ /*--- If Deform_Mesh true, the mesh deformation is handled by SetMesh_Deformation ---*/ - // CVC: Debug: To Do: What if multiple prescribed movements? E.g., Pitching + Plunging? + // To Do: What if multiple prescribed movements? E.g., Pitching + Plunging? if (rank == MASTER_NODE && !config->GetDeform_Mesh()) { cout << " Deforming the volume grid." << endl; grid_movement->SetVolume_Deformation(geometry[MESH_0], diff --git a/SU2_CFD/src/solver_structure.cpp b/SU2_CFD/src/solver_structure.cpp index 46e524aeca68..5e0bd7c12fbd 100644 --- a/SU2_CFD/src/solver_structure.cpp +++ b/SU2_CFD/src/solver_structure.cpp @@ -4268,10 +4268,6 @@ void CSolver::Restart_OldGeometry(CGeometry *geometry, CConfig *config) { if (config->GetRestart()) Unst_RestartIter = SU2_TYPE::Int(config->GetRestart_Iter())-1; else Unst_RestartIter = SU2_TYPE::Int(config->GetUnst_AdjointIter())-1; filename_n = config->GetUnsteady_FileName(filename, Unst_RestartIter, ".csv"); -//======= -// Unst_RestartIter = SU2_TYPE::Int(config->GetRestart_Iter())-1; -// filename_n = config->GetFilename(filename, ".csv", Unst_RestartIter); -//>>>>>>> develop /*--- Open the restart file, throw an error if this fails. ---*/ @@ -4343,10 +4339,6 @@ void CSolver::Restart_OldGeometry(CGeometry *geometry, CConfig *config) { if (config->GetRestart()) Unst_RestartIter = SU2_TYPE::Int(config->GetRestart_Iter())-2; else Unst_RestartIter = SU2_TYPE::Int(config->GetUnst_AdjointIter())-2; filename_n1 = config->GetUnsteady_FileName(filename, Unst_RestartIter, ".csv"); -//======= -// Unst_RestartIter = SU2_TYPE::Int(config->GetRestart_Iter())-2; -// filename_n1 = config->GetFilename(filename, ".csv", Unst_RestartIter); -//>>>>>>> develop /*--- Open the restart file, throw an error if this fails. ---*/ diff --git a/SU2_CFD/src/solvers/CMeshSolver.cpp b/SU2_CFD/src/solvers/CMeshSolver.cpp index a51590506471..171e4d3533cc 100644 --- a/SU2_CFD/src/solvers/CMeshSolver.cpp +++ b/SU2_CFD/src/solvers/CMeshSolver.cpp @@ -889,131 +889,6 @@ void CMeshSolver::Restart_OldGeometry(CGeometry *geometry, CConfig *config) { } -// void Surface_Plunging(CGeometry *geometry, CConfig *config, CSolver **solver, unsigned long iter, unsigned short iZone) { - -// // surface_movement->Surface_Plunging(geometry[MESH_0], -// // config, -// // solver[MESH_0], TimeIter, val_iZone); - -// su2double deltaT, time_new, time_old, Lref; -// su2double Center[3] = {0.0, 0.0, 0.0}, VarCoord[3], Omega[3], Ampl[3]; -// su2double DEG2RAD = PI_NUMBER/180.0; -// unsigned short iMarker, jMarker, Moving; -// unsigned long iPoint, iVertex; -// string Marker_Tag, Moving_Tag; -// unsigned short iDim; - -// /*--- Initialize the delta variation in coordinates ---*/ -// VarCoord[0] = 0.0; VarCoord[1] = 0.0; VarCoord[2] = 0.0; - -// /*--- Retrieve values from the config file ---*/ - -// deltaT = config->GetDelta_UnstTimeND(); -// Lref = config->GetLength_Ref(); - -// /*--- Compute delta time based on physical time step ---*/ -// time_new = static_cast(iter)*deltaT; -// if (iter == 0) { -// time_old = time_new; -// } else { -// time_old = static_cast(iter-1)*deltaT; -// } - -// /*--- Store displacement of each node on the plunging surface ---*/ -// /*--- Loop over markers and find the particular marker(s) (surface) to plunge ---*/ - -// for (iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { -// Moving = config->GetMarker_All_Moving(iMarker); -// if (Moving == YES) { -// for (jMarker = 0; jMarkerGetnMarker_Moving(); jMarker++) { - -// Moving_Tag = config->GetMarker_Moving_TagBound(jMarker); -// Marker_Tag = config->GetMarker_All_TagBound(iMarker); - -// if (Marker_Tag == Moving_Tag && (config->GetKind_SurfaceMovement(jMarker) == DEFORMING)) { - -// /*--- Plunging frequency and amplitude from config. ---*/ - -// for (iDim = 0; iDim < 3; iDim++){ -// Ampl[iDim] = config->GetMarkerPlunging_Ampl(jMarker, iDim)/Lref; -// Omega[iDim] = config->GetMarkerPlunging_Omega(jMarker, iDim)/config->GetOmega_Ref(); -// Center[iDim] = config->GetMarkerMotion_Origin(jMarker, iDim); -// } -// /*--- Print some information to the console. Be verbose at the first -// iteration only (mostly for debugging purposes). ---*/ -// // Note that the MASTER_NODE might not contain all the markers being moved. - -// if (SU2_MPI::GetRank() == MASTER_NODE) { -// cout << " Storing plunging displacement for marker: "; -// cout << Marker_Tag << "." << endl; -// if (iter == 0) { -// cout << " Plunging frequency: (" << Omega[0] << ", " << Omega[1]; -// cout << ", " << Omega[2] << ") rad/s." << endl; -// cout << " Plunging amplitude: (" << Ampl[0]/DEG2RAD; -// cout << ", " << Ampl[1]/DEG2RAD << ", " << Ampl[2]/DEG2RAD; -// cout << ") degrees."<< endl; -// } -// } - -// /*--- Compute delta change in the position in the x, y, & z directions. ---*/ - -// VarCoord[0] = -Ampl[0]*(sin(Omega[0]*time_new) - sin(Omega[0]*time_old)); -// VarCoord[1] = -Ampl[1]*(sin(Omega[1]*time_new) - sin(Omega[1]*time_old)); -// VarCoord[2] = -Ampl[2]*(sin(Omega[2]*time_new) - sin(Omega[2]*time_old)); - -// for (iVertex = 0; iVertex < geometry->nVertex[iMarker]; iVertex++) { - -// if (!config->GetDeform_Mesh()) { - -// /*--- Set node displacement for volume deformation ---*/ -// geometry->vertex[iMarker][iVertex]->SetVarCoord(VarCoord); -// } -// else { - -// /*--- Get node index ---*/ -// iPoint = geometry->vertex[iMarker][iVertex]->GetNode(); - -// solver[MESH_SOL]->node[iPoint]->SetBound_Disp(VarCoord); -// } - -// } -// } -// } -// } -// } - -// /*--- When updating the origins it is assumed that all markers have the -// same plunging movement, because we use the last VarCoord set ---*/ - -// /*--- Set the mesh motion center to the new location after -// incrementing the position with the translation. This new -// location will be used for subsequent mesh motion for the given marker.---*/ - -// for (jMarker=0; jMarkerGetnMarker_Moving(); jMarker++) { - -// /*-- Check if we want to update the motion origin for the given marker ---*/ - -// if (config->GetMoveMotion_Origin(jMarker) == YES) { -// for (iDim = 0; iDim < 3; iDim++){ -// Center[iDim] += VarCoord[iDim]; -// } -// config->SetMarkerMotion_Origin(Center, jMarker); -// } -// } - -// /*--- Set the moment computation center to the new location after -// incrementing the position with the plunging. ---*/ - -// for (jMarker=0; jMarkerGetnMarker_Monitoring(); jMarker++) { -// Center[0] = config->GetRefOriginMoment_X(jMarker) + VarCoord[0]; -// Center[1] = config->GetRefOriginMoment_Y(jMarker) + VarCoord[1]; -// Center[2] = config->GetRefOriginMoment_Z(jMarker) + VarCoord[2]; -// config->SetRefOriginMoment_X(jMarker, Center[0]); -// config->SetRefOriginMoment_Y(jMarker, Center[1]); -// config->SetRefOriginMoment_Z(jMarker, Center[2]); -// } -// } - void CMeshSolver::Surface_Pitching(CGeometry *geometry, CConfig *config, unsigned long iter, unsigned short iZone) { From 00536f02a19a45181c652b965a7e6f16e052d24c Mon Sep 17 00:00:00 2001 From: cvencro Date: Mon, 9 Dec 2019 14:57:32 +0000 Subject: [PATCH 009/112] if statement to swap between BCs to help debug --- SU2_CFD/src/solver_direct_mean.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/SU2_CFD/src/solver_direct_mean.cpp b/SU2_CFD/src/solver_direct_mean.cpp index 5ce7e8a8507c..b38d8b65e087 100644 --- a/SU2_CFD/src/solver_direct_mean.cpp +++ b/SU2_CFD/src/solver_direct_mean.cpp @@ -7903,6 +7903,11 @@ void CEulerSolver::BC_Euler_Wall(CGeometry *geometry, CConfig *config, unsigned short val_marker) { + if (false) { + /*--- Call the equivalent symmetry plane boundary condition. ---*/ + BC_Sym_Plane(geometry, solver_container, conv_numerics, visc_numerics, config, val_marker); + } + else { unsigned short iDim, iVar, jVar, kVar, jDim; unsigned long iPoint, iVertex; su2double *Normal = NULL, *GridVel = NULL, Area, UnitNormal[3], *NormalArea, @@ -8074,6 +8079,7 @@ void CEulerSolver::BC_Euler_Wall(CGeometry *geometry, } delete [] Jacobian_b; delete [] DubDu; + } } From b957cdfe31d56dc42b03142794dba7ca5a464b8b Mon Sep 17 00:00:00 2001 From: cvencro Date: Wed, 11 Dec 2019 17:09:12 +0000 Subject: [PATCH 010/112] Fix to use z rotational rate in 2D problems --- Common/src/geometry/CGeometry.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Common/src/geometry/CGeometry.cpp b/Common/src/geometry/CGeometry.cpp index 05619e53e3ce..79588402399e 100644 --- a/Common/src/geometry/CGeometry.cpp +++ b/Common/src/geometry/CGeometry.cpp @@ -3841,7 +3841,7 @@ void CGeometry::SetRotationalVelocity(CConfig *config, bool print) { /*--- Center of rotation & angular velocity vector from config ---*/ - for (iDim = 0; iDim < nDim; iDim++) { + for (iDim = 0; iDim < 3; iDim++) { Center[iDim] = config->GetMotion_Origin(iDim); Omega[iDim] = config->GetRotation_Rate(iDim)/config->GetOmega_Ref(); } From f46eb5e198d846192781a681768f8d947e30320e Mon Sep 17 00:00:00 2001 From: cvencro Date: Fri, 31 Jan 2020 06:45:44 +0000 Subject: [PATCH 011/112] roll forward on euler wall bc for this PR --- SU2_CFD/src/solvers/CEulerSolver.cpp | 178 +-------------------------- 1 file changed, 2 insertions(+), 176 deletions(-) diff --git a/SU2_CFD/src/solvers/CEulerSolver.cpp b/SU2_CFD/src/solvers/CEulerSolver.cpp index 4680661df4d8..9cc1fa6ba131 100644 --- a/SU2_CFD/src/solvers/CEulerSolver.cpp +++ b/SU2_CFD/src/solvers/CEulerSolver.cpp @@ -7323,183 +7323,9 @@ void CEulerSolver::BC_Euler_Wall(CGeometry *geometry, CConfig *config, unsigned short val_marker) { - if (false) { - /*--- Call the equivalent symmetry plane boundary condition. ---*/ - BC_Sym_Plane(geometry, solver_container, conv_numerics, visc_numerics, config, val_marker); - } - else { - unsigned short iDim, iVar, jVar, kVar, jDim; - unsigned long iPoint, iVertex; - su2double *Normal = NULL, *GridVel = NULL, Area, UnitNormal[3], *NormalArea, - ProjGridVel = 0.0, turb_ke; - su2double Density_b, StaticEnergy_b, Enthalpy_b, *Velocity_b, Kappa_b, Chi_b, Energy_b, VelMagnitude2_b, Pressure_b; - su2double Density_i, *Velocity_i, ProjVelocity_i = 0.0, Energy_i, VelMagnitude2_i; - su2double **Jacobian_b, **DubDu; - - bool implicit = (config->GetKind_TimeIntScheme_Flow() == EULER_IMPLICIT); - bool tkeNeeded = (config->GetKind_Turb_Model() == SST) || (config->GetKind_Turb_Model() == SST_SUST); - - Normal = new su2double[nDim]; - NormalArea = new su2double[nDim]; - Velocity_b = new su2double[nDim]; - Velocity_i = new su2double[nDim]; - Jacobian_b = new su2double*[nVar]; - DubDu = new su2double*[nVar]; - for (iVar = 0; iVar < nVar; iVar++) { - Jacobian_b[iVar] = new su2double[nVar]; - DubDu[iVar] = new su2double[nVar]; - } - - /*--- Loop over all the vertices on this boundary marker ---*/ - - for (iVertex = 0; iVertex < geometry->nVertex[val_marker]; iVertex++) { - iPoint = geometry->vertex[val_marker][iVertex]->GetNode(); - - /*--- Check if the node belongs to the domain (i.e, not a halo node) ---*/ - - if (geometry->node[iPoint]->GetDomain()) { - - /*--- Normal vector for this vertex (negative for outward convention) ---*/ - - geometry->vertex[val_marker][iVertex]->GetNormal(Normal); - - Area = 0.0; - for (iDim = 0; iDim < nDim; iDim++) Area += Normal[iDim]*Normal[iDim]; - Area = sqrt (Area); - - for (iDim = 0; iDim < nDim; iDim++) { - NormalArea[iDim] = -Normal[iDim]; - UnitNormal[iDim] = -Normal[iDim]/Area; - } - - /*--- Get the state i ---*/ - - VelMagnitude2_i = 0.0; ProjVelocity_i = 0.0; - for (iDim = 0; iDim < nDim; iDim++) { - Velocity_i[iDim] = nodes->GetVelocity(iPoint, iDim); - ProjVelocity_i += Velocity_i[iDim]*UnitNormal[iDim]; - VelMagnitude2_i += Velocity_i[iDim]*Velocity_i[iDim]; - } - Density_i = nodes->GetDensity(iPoint); - Energy_i = nodes->GetEnergy(iPoint); - - /*--- Compute the boundary state b ---*/ - - for (iDim = 0; iDim < nDim; iDim++) - Velocity_b[iDim] = Velocity_i[iDim] - ProjVelocity_i * UnitNormal[iDim]; //Force the velocity to be tangential to the surface. - - if (dynamic_grid) { - GridVel = geometry->node[iPoint]->GetGridVel(); - ProjGridVel = 0.0; - for (iDim = 0; iDim < nDim; iDim++) ProjGridVel += GridVel[iDim]*UnitNormal[iDim]; - for (iDim = 0; iDim < nDim; iDim++) Velocity_b[iDim] += GridVel[iDim] - ProjGridVel * UnitNormal[iDim]; - } - - VelMagnitude2_b = 0.0; - for (iDim = 0; iDim < nDim; iDim++) - VelMagnitude2_b += Velocity_b[iDim] * Velocity_b[iDim]; - - /*--- Compute the residual ---*/ - - turb_ke = 0.0; - if (tkeNeeded) turb_ke = solver_container[TURB_SOL]->GetNodes()->GetSolution(iPoint, 0); - - Density_b = Density_i; - StaticEnergy_b = Energy_i - 0.5 * VelMagnitude2_i - turb_ke; - Energy_b = StaticEnergy_b + 0.5 * VelMagnitude2_b + turb_ke; - - FluidModel->SetTDState_rhoe(Density_b, StaticEnergy_b); - Kappa_b = FluidModel->GetdPde_rho() / Density_b; - Chi_b = FluidModel->GetdPdrho_e() - Kappa_b * StaticEnergy_b; - Pressure_b = FluidModel->GetPressure(); - Enthalpy_b = Energy_b + Pressure_b/Density_b; + /*--- Call the equivalent symmetry plane boundary condition. ---*/ + BC_Sym_Plane(geometry, solver_container, conv_numerics, visc_numerics, config, val_marker); - conv_numerics->GetInviscidProjFlux(&Density_b, Velocity_b, &Pressure_b, &Enthalpy_b, NormalArea, Residual); - - /*--- Grid velocity correction to the energy term ---*/ - if (dynamic_grid) { - GridVel = geometry->node[iPoint]->GetGridVel(); - ProjGridVel = 0.0; - for (iDim = 0; iDim < nDim; iDim++) - ProjGridVel += GridVel[iDim]*UnitNormal[iDim]; - Residual[nVar-1] += Pressure_b*ProjGridVel*Area; - } - - /*--- Add the Reynolds stress tensor contribution ---*/ - - if (tkeNeeded) { - for (iDim = 0; iDim < nDim; iDim++) - Residual[iDim+1] += (2.0/3.0)*Density_b*turb_ke*NormalArea[iDim]; - } - - /*--- Add value to the residual ---*/ - - LinSysRes.AddBlock(iPoint, Residual); - - /*--- Form Jacobians for implicit computations ---*/ - - if (implicit) { - - /*--- Initialize Jacobian ---*/ - - for (iVar = 0; iVar < nVar; iVar++) { - for (jVar = 0; jVar < nVar; jVar++) - Jacobian_i[iVar][jVar] = 0.0; - } - - /*--- Compute DubDu ---*/ - - for (iVar = 0; iVar < nVar; iVar++) { - for (jVar = 0; jVar < nVar; jVar++) - DubDu[iVar][jVar]= 0.0; - DubDu[iVar][iVar]= 1.0; - } - - for (iDim = 0; iDim < nDim; iDim++) - for (jDim = 0; jDimGetInviscidProjJac(Velocity_b, &Enthalpy_b, &Chi_b, &Kappa_b, NormalArea, 1, Jacobian_b); - - // Check for grid movement, should be already considered since Jacobian b is computed from u_b - // if (grid_movement) { - // Jacobian_b[nVar-1][0] += 0.5*ProjGridVel*ProjGridVel; - // for (iDim = 0; iDim < nDim; iDim++) - // Jacobian_b[nVar-1][iDim+1] -= ProjGridVel * UnitNormal[iDim]; - // } - - /*--- Compute numerical flux Jacobian at node i ---*/ - - for (iVar = 0; iVar < nVar; iVar++) - for (jVar = 0; jVar < nVar; jVar++) - for (kVar = 0; kVar < nVar; kVar++) - Jacobian_i[iVar][jVar] += Jacobian_b[iVar][kVar] * DubDu[kVar][jVar]; - - /*--- Add the Jacobian to the sparse matrix ---*/ - - Jacobian.AddBlock(iPoint, iPoint, Jacobian_i); - - } - } - } - - delete [] Normal; - delete [] NormalArea; - delete [] Velocity_b; - delete [] Velocity_i; - for (iVar = 0; iVar < nVar; iVar++) { - delete [] Jacobian_b[iVar]; - delete [] DubDu[iVar]; - } - delete [] Jacobian_b; - delete [] DubDu; - } } From 73785638cd862d9d5fc2b292ae2db9e51d4bc3d4 Mon Sep 17 00:00:00 2001 From: cvencro Date: Wed, 5 Feb 2020 16:24:55 +0000 Subject: [PATCH 012/112] Add missing comments --- SU2_CFD/include/solvers/CMeshSolver.hpp | 29 ++++++++++++++++++++++++- SU2_CFD/include/solvers/CSolver.hpp | 28 ++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/SU2_CFD/include/solvers/CMeshSolver.hpp b/SU2_CFD/include/solvers/CMeshSolver.hpp index c42c2eef968a..bf292a16bc0f 100644 --- a/SU2_CFD/include/solvers/CMeshSolver.hpp +++ b/SU2_CFD/include/solvers/CMeshSolver.hpp @@ -177,18 +177,45 @@ class CMeshSolver final : public CFEASolver { * \brief Get maximum volume in the mesh * \return */ - inline su2double GetMaximum_Volume() const override {return MaxVolume_Curr;} + /*! + * \brief Pitching definition for deforming mesh + * \param[in] geometry - Geometrical definition of the problem. + * \param[in] config - Definition of the particular problem. + * \param[in] iter - Current time iteration number + * \param[in] iZone - Current zone + */ void Surface_Pitching(CGeometry *geometry, CConfig *config, unsigned long iter, unsigned short iZone); + /*! + * \brief Rotating definition for deforming mesh + * \param[in] geometry - Geometrical definition of the problem. + * \param[in] config - Definition of the particular problem. + * \param[in] iter - Current time iteration number + * \param[in] iZone - Current zone + */ void Surface_Rotating(CGeometry *geometry, CConfig *config, unsigned long iter, unsigned short iZone); + /*! + * \brief Plunging definition for deforming mesh + * \param[in] geometry - Geometrical definition of the problem. + * \param[in] config - Definition of the particular problem. + * \param[in] iter - Current time iteration number + * \param[in] iZone - Current zone + */ void Surface_Plunging(CGeometry *geometry, CConfig *config, unsigned long iter, unsigned short iZone); + /*! + * \brief Translating definition for deforming mesh + * \param[in] geometry - Geometrical definition of the problem. + * \param[in] config - Definition of the particular problem. + * \param[in] iter - Current time iteration number + * \param[in] iZone - Current zone + */ void Surface_Translating(CGeometry *geometry, CConfig *config, unsigned long iter, unsigned short iZone); diff --git a/SU2_CFD/include/solvers/CSolver.hpp b/SU2_CFD/include/solvers/CSolver.hpp index ca0bace9c0a5..56934f492544 100644 --- a/SU2_CFD/include/solvers/CSolver.hpp +++ b/SU2_CFD/include/solvers/CSolver.hpp @@ -4688,12 +4688,40 @@ class CSolver { CNumerics **numerics, CConfig *config) { } + /*! + * \brief Pitching definition for deforming mesh + * \param[in] geometry - Geometrical definition of the problem. + * \param[in] config - Definition of the particular problem. + * \param[in] iter - Current time iteration number + * \param[in] iZone - Current zone + */ inline virtual void Surface_Pitching(CGeometry *geometry, CConfig *config, unsigned long iter, unsigned short iZone) { } + /*! + * \brief Rotating definition for deforming mesh + * \param[in] geometry - Geometrical definition of the problem. + * \param[in] config - Definition of the particular problem. + * \param[in] iter - Current time iteration number + * \param[in] iZone - Current zone + */ inline virtual void Surface_Rotating(CGeometry *geometry, CConfig *config, unsigned long iter, unsigned short iZone) { } + /*! + * \brief Plunging definition for deforming mesh + * \param[in] geometry - Geometrical definition of the problem. + * \param[in] config - Definition of the particular problem. + * \param[in] iter - Current time iteration number + * \param[in] iZone - Current zone + */ inline virtual void Surface_Plunging(CGeometry *geometry, CConfig *config, unsigned long iter, unsigned short iZone) { } + /*! + * \brief Translating definition for deforming mesh + * \param[in] geometry - Geometrical definition of the problem. + * \param[in] config - Definition of the particular problem. + * \param[in] iter - Current time iteration number + * \param[in] iZone - Current zone + */ inline virtual void Surface_Translating(CGeometry *geometry, CConfig *config, unsigned long iter, unsigned short iZone) { } /*! From 649f7fa33c60172f4a77daf86f44f97aa4fc4292 Mon Sep 17 00:00:00 2001 From: cvencro Date: Wed, 5 Feb 2020 17:56:18 +0000 Subject: [PATCH 013/112] fix for fsi2d regression test --- SU2_CFD/src/solvers/CSolver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SU2_CFD/src/solvers/CSolver.cpp b/SU2_CFD/src/solvers/CSolver.cpp index e13f01459253..56a320fdae62 100644 --- a/SU2_CFD/src/solvers/CSolver.cpp +++ b/SU2_CFD/src/solvers/CSolver.cpp @@ -3461,7 +3461,7 @@ void CSolver::Restart_OldGeometry(CGeometry *geometry, CConfig *config) { /*--- Modify file name for an unsteady restart ---*/ if (config->GetRestart()) Unst_RestartIter = SU2_TYPE::Int(config->GetRestart_Iter())-1; else Unst_RestartIter = SU2_TYPE::Int(config->GetUnst_AdjointIter())-1; - filename_n = config->GetUnsteady_FileName(filename, Unst_RestartIter, ".csv"); + filename_n = config->GetFilename(filename, ".csv", Unst_RestartIter); /*--- Open the restart file, throw an error if this fails. ---*/ @@ -3532,7 +3532,7 @@ void CSolver::Restart_OldGeometry(CGeometry *geometry, CConfig *config) { /*--- Modify file name for an unsteady restart ---*/ if (config->GetRestart()) Unst_RestartIter = SU2_TYPE::Int(config->GetRestart_Iter())-2; else Unst_RestartIter = SU2_TYPE::Int(config->GetUnst_AdjointIter())-2; - filename_n1 = config->GetUnsteady_FileName(filename, Unst_RestartIter, ".csv"); + filename_n1 = config->GetFilename(filename, ".csv", Unst_RestartIter); /*--- Open the restart file, throw an error if this fails. ---*/ From 8e01376aff0c2d4253f983e80ba5270d3da39366 Mon Sep 17 00:00:00 2001 From: cvencro Date: Wed, 5 Feb 2020 19:31:25 +0000 Subject: [PATCH 014/112] fix for unsteady restart reg test --- SU2_CFD/src/solvers/CMeshSolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SU2_CFD/src/solvers/CMeshSolver.cpp b/SU2_CFD/src/solvers/CMeshSolver.cpp index f4356b6435fd..8d147833fab4 100644 --- a/SU2_CFD/src/solvers/CMeshSolver.cpp +++ b/SU2_CFD/src/solvers/CMeshSolver.cpp @@ -755,7 +755,7 @@ void CMeshSolver::Restart_OldGeometry(CGeometry *geometry, CConfig *config) { /*--- Modify file name for an unsteady restart ---*/ int Unst_RestartIter; - if (config->GetRestart()) Unst_RestartIter = SU2_TYPE::Int(config->GetRestart_Iter()) - SU2_TYPE::Int(config->GetTimeIter())-iStep-1; + if (config->GetRestart()) Unst_RestartIter = SU2_TYPE::Int(config->GetRestart_Iter()) - iStep; else Unst_RestartIter = SU2_TYPE::Int(config->GetUnst_AdjointIter()) - SU2_TYPE::Int(config->GetTimeIter())-iStep-1; if (Unst_RestartIter < 0) { From 745b26e98588e31883517b6d7a0fdb64f3f23e2a Mon Sep 17 00:00:00 2001 From: cvencro Date: Wed, 5 Feb 2020 19:55:24 +0000 Subject: [PATCH 015/112] update mesh restart solution --- SU2_CFD/src/solvers/CMeshSolver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SU2_CFD/src/solvers/CMeshSolver.cpp b/SU2_CFD/src/solvers/CMeshSolver.cpp index 8d147833fab4..9de813a4a925 100644 --- a/SU2_CFD/src/solvers/CMeshSolver.cpp +++ b/SU2_CFD/src/solvers/CMeshSolver.cpp @@ -760,7 +760,7 @@ void CMeshSolver::Restart_OldGeometry(CGeometry *geometry, CConfig *config) { if (Unst_RestartIter < 0) { - if (rank == MASTER_NODE) cout << "Requested mesh restart filename is negative. Setting initial mesh" << endl; + if (rank == MASTER_NODE) cout << "Requested mesh restart filename is negative. Setting known solution" << endl; /*--- Set loaded solution into correct previous time containers. ---*/ unsigned long iPoint; @@ -769,7 +769,7 @@ void CMeshSolver::Restart_OldGeometry(CGeometry *geometry, CConfig *config) { if(iStep==1) nodes->Set_Solution_time_n(iPoint, iDim, nodes->GetSolution(iPoint, iDim)); else - nodes->Set_Solution_time_n1(iPoint, iDim, nodes->GetSolution(iPoint, iDim)); + nodes->Set_Solution_time_n1(iPoint, iDim, nodes->GetSolution_time_n(iPoint, iDim)); } } } From 01e43ed9a745714549e4e317a46db5668b197992 Mon Sep 17 00:00:00 2001 From: cvencro Date: Mon, 9 Mar 2020 06:40:11 +0000 Subject: [PATCH 016/112] update deform mesh check for incompressible flow --- SU2_CFD/src/solvers/CIncEulerSolver.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/SU2_CFD/src/solvers/CIncEulerSolver.cpp b/SU2_CFD/src/solvers/CIncEulerSolver.cpp index da61bb50b5e7..eb1d0626b456 100644 --- a/SU2_CFD/src/solvers/CIncEulerSolver.cpp +++ b/SU2_CFD/src/solvers/CIncEulerSolver.cpp @@ -6054,8 +6054,11 @@ void CIncEulerSolver::LoadRestart(CGeometry **geometry, CSolver ***solver, CConf } /*--- Update the old geometry (coordinates n and n-1) in dual time-stepping strategy ---*/ - if (dual_time && config->GetGrid_Movement() && (config->GetKind_GridMovement() != RIGID_MOTION)) - Restart_OldGeometry(geometry[MESH_0], config); + if (dual_time && config->GetGrid_Movement() && (config->GetKind_GridMovement() != RIGID_MOTION)) { + if (!config->GetDeform_Mesh()) { + Restart_OldGeometry(geometry[MESH_0], config); + } + } delete [] Coord; From 0ef39c4a7a0e026e6ece7237cc8462d1d6372f4f Mon Sep 17 00:00:00 2001 From: cvencro Date: Mon, 9 Mar 2020 06:46:27 +0000 Subject: [PATCH 017/112] added deforming pitching airfoil testcase --- .../inv_NACA0012_pitching_deform.cfg | 133 ++++++++++++++++++ .../inv_NACA0012_pitching_deform_ad.cfg | 133 ++++++++++++++++++ 2 files changed, 266 insertions(+) create mode 100644 TestCases/disc_adj_euler/naca0012_pitching/inv_NACA0012_pitching_deform.cfg create mode 100644 TestCases/disc_adj_euler/naca0012_pitching/inv_NACA0012_pitching_deform_ad.cfg diff --git a/TestCases/disc_adj_euler/naca0012_pitching/inv_NACA0012_pitching_deform.cfg b/TestCases/disc_adj_euler/naca0012_pitching/inv_NACA0012_pitching_deform.cfg new file mode 100644 index 000000000000..2387eaa95368 --- /dev/null +++ b/TestCases/disc_adj_euler/naca0012_pitching/inv_NACA0012_pitching_deform.cfg @@ -0,0 +1,133 @@ +% ------------- DIRECT, ADJOINT, AND LINEARIZED PROBLEM DEFINITION ------------% +SOLVER= EULER +MATH_PROBLEM= DIRECT +RESTART_SOL= NO +TIME_ITER= 5 +INNER_ITER= 50 + +% ------------------------- UNSTEADY SIMULATION -------------------------------% +TIME_DOMAIN= YES +TIME_MARCHING= DUAL_TIME_STEPPING-2ND_ORDER +TIME_STEP= 0.0023555025613149587 +MAX_TIME= 0.01 +UNST_RESTART_ITER= 5 +UNST_ADJOINT_ITER= 5 + +% ----------------------- DYNAMIC MESH DEFINITION -----------------------------% +DEFORM_MESH= YES +MARKER_DEFORM_MESH= (airfoil) + +SURFACE_MOVEMENT= DEFORMING +MARKER_MOVING= ( airfoil ) +SURFACE_MOTION_ORIGIN= (0.248 0.0 0.0) +SURFACE_PITCHING_OMEGA= (0.0 0.0 106.69842) +SURFACE_PITCHING_AMPL= (0.0 0.0 1.01) + +% -------------------- COMPRESSIBLE FREE-STREAM DEFINITION --------------------% +MACH_NUMBER= 0.8 +AOA= 1.25 +FREESTREAM_PRESSURE= 101325.0 +FREESTREAM_TEMPERATURE= 288.15 + +% ---------------------- REFERENCE VALUE DEFINITION ---------------------------% +REF_ORIGIN_MOMENT_X= 0.25 +REF_ORIGIN_MOMENT_Y= 0.00 +REF_ORIGIN_MOMENT_Z= 0.00 +REF_LENGTH= 1.0 +REF_AREA= 1.0 +REF_DIMENSIONALIZATION= FREESTREAM_PRESS_EQ_ONE + +% ----------------------- BOUNDARY CONDITION DEFINITION -----------------------% +MARKER_EULER= ( airfoil ) +MARKER_FAR= ( farfield ) + +% ------------------------ SURFACES IDENTIFICATION ----------------------------% +MARKER_PLOTTING= ( airfoil ) +MARKER_MONITORING= ( airfoil ) + +% ------------- COMMON PARAMETERS DEFINING THE NUMERICAL METHOD ---------------% +NUM_METHOD_GRAD= GREEN_GAUSS +CFL_NUMBER= 10.0 +CFL_ADAPT= NO +CFL_ADAPT_PARAM= ( 1.5, 0.5, 1.0, 100.0 ) +RK_ALPHA_COEFF= ( 0.66667, 0.66667, 1.000000 ) + +% ------------------------ LINEAR SOLVER DEFINITION ---------------------------% +LINEAR_SOLVER= FGMRES +LINEAR_SOLVER_PREC= ILU +LINEAR_SOLVER_ERROR= 1E-4 +LINEAR_SOLVER_ITER= 2 + +% -------------------------- MULTIGRID PARAMETERS -----------------------------% +MGLEVEL= 2 +MGCYCLE= V_CYCLE +MG_PRE_SMOOTH= ( 1, 2, 3, 3 ) +MG_POST_SMOOTH= ( 0, 0, 0, 0 ) +MG_CORRECTION_SMOOTH= ( 0, 0, 0, 0 ) +MG_DAMP_RESTRICTION= 1.0 +MG_DAMP_PROLONGATION= 1.0 + +% --------------------- FLOW NUMERICAL METHOD DEFINITION ----------------------% +CONV_NUM_METHOD_FLOW= JST +SLOPE_LIMITER_FLOW= VENKATAKRISHNAN +JST_SENSOR_COEFF= ( 0.5, 0.02 ) +TIME_DISCRE_FLOW= EULER_IMPLICIT + +% ---------------- ADJOINT-FLOW NUMERICAL METHOD DEFINITION -------------------% +OBJECTIVE_FUNCTION= EFFICIENCY +OPT_OBJECTIVE= EFFICIENCY * 1.0 +CONV_NUM_METHOD_ADJFLOW= JST +SLOPE_LIMITER_ADJFLOW= VENKATAKRISHNAN +ADJ_JST_SENSOR_COEFF= ( 0.0, 0.02 ) +TIME_DISCRE_ADJFLOW= EULER_IMPLICIT +CFL_REDUCTION_ADJFLOW= 0.8 +LIMIT_ADJFLOW= 1E6 + +% -------------------- FREE-FORM DEFORMATION PARAMETERS -----------------------% +FFD_TOLERANCE= 1E-10 +FFD_ITERATIONS= 500 +FFD_DEFINITION= (airfoil_box, -0.1, -0.25, 0, 1.1, -0.25, 0, 1.1, 0.25, 0, -0.1, 0.25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) +FFD_DEGREE= (10, 1, 0) +FFD_CONTINUITY= 2ND_DERIVATIVE + +% ----------------------- DESIGN VARIABLE PARAMETERS --------------------------% +DV_KIND= FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT +DV_MARKER= ( airfoil ) +DV_PARAM= ( airfoil_box, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 2.0, 0.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 3.0, 0.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 4.0, 0.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 5.0, 0.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 6.0, 0.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 7.0, 0.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 8.0, 0.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 9.0, 0.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 10.0, 0.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 2.0, 1.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 3.0, 1.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 4.0, 1.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 5.0, 1.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 6.0, 1.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 7.0, 1.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 8.0, 1.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 9.0, 1.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 10.0, 1.0, 0.0, 0.0, 1.0, 0.0) +DV_VALUE= 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001 + +% ------------------------ GRID DEFORMATION PARAMETERS ------------------------% +DEFORM_NONLINEAR_ITER= 1 +DEFORM_CONSOLE_OUTPUT= YES +DEFORM_LINEAR_SOLVER_ERROR= 0.000000001 +DEFORM_STIFFNESS_TYPE= INVERSE_VOLUME + +% --------------------------- CONVERGENCE PARAMETERS --------------------------% +CONV_CRITERIA= RESIDUAL +CONV_RESIDUAL_MINVAL= -20 +CONV_STARTITER= 10 +CONV_CAUCHY_ELEMS= 100 +CONV_CAUCHY_EPS= 1E-6 + +% ------------------------- INPUT/OUTPUT INFORMATION --------------------------% +MESH_FILENAME= mesh_NACA0012_inv_FFD.su2 +MESH_FORMAT= SU2 +MESH_OUT_FILENAME= mesh_out.su2 +SOLUTION_FILENAME= solution_flow.dat +SOLUTION_ADJ_FILENAME= solution_adj.dat +OUTPUT_FILES= (RESTART_ASCII, RESTART, PARAVIEW) +HISTORY_OUTPUT= ITER, RMS_RES, AERO_COEFF +TABULAR_FORMAT= CSV +CONV_FILENAME= history +RESTART_FILENAME= solution_flow.dat +RESTART_ADJ_FILENAME= solution_adj.dat +VOLUME_FILENAME= flow +VOLUME_ADJ_FILENAME= adjoint +GRAD_OBJFUNC_FILENAME= of_grad.dat +SURFACE_FILENAME= surface_flow +SURFACE_ADJ_FILENAME= surface_adjoint +WRT_SOL_FREQ= 250.0 +WRT_SOL_FREQ_DUALTIME= 1 +WRT_CON_FREQ= 1 +WRT_CON_FREQ_DUALTIME= 10 + diff --git a/TestCases/disc_adj_euler/naca0012_pitching/inv_NACA0012_pitching_deform_ad.cfg b/TestCases/disc_adj_euler/naca0012_pitching/inv_NACA0012_pitching_deform_ad.cfg new file mode 100644 index 000000000000..c07290c332fa --- /dev/null +++ b/TestCases/disc_adj_euler/naca0012_pitching/inv_NACA0012_pitching_deform_ad.cfg @@ -0,0 +1,133 @@ +% ------------- DIRECT, ADJOINT, AND LINEARIZED PROBLEM DEFINITION ------------% +SOLVER= EULER +MATH_PROBLEM= DISCRETE_ADJOINT +RESTART_SOL= NO +TIME_ITER= 5 +INNER_ITER= 50 + +% ------------------------- UNSTEADY SIMULATION -------------------------------% +TIME_DOMAIN= YES +TIME_MARCHING= DUAL_TIME_STEPPING-2ND_ORDER +TIME_STEP= 0.0023555025613149587 +MAX_TIME= 0.01 +UNST_RESTART_ITER= 5 +UNST_ADJOINT_ITER= 5 + +% ----------------------- DYNAMIC MESH DEFINITION -----------------------------% +DEFORM_MESH= YES +MARKER_DEFORM_MESH= (airfoil) + +SURFACE_MOVEMENT= DEFORMING +MARKER_MOVING= ( airfoil ) +SURFACE_MOTION_ORIGIN= (0.248 0.0 0.0) +SURFACE_PITCHING_OMEGA= (0.0 0.0 106.69842) +SURFACE_PITCHING_AMPL= (0.0 0.0 1.01) + +% -------------------- COMPRESSIBLE FREE-STREAM DEFINITION --------------------% +MACH_NUMBER= 0.8 +AOA= 1.25 +FREESTREAM_PRESSURE= 101325.0 +FREESTREAM_TEMPERATURE= 288.15 + +% ---------------------- REFERENCE VALUE DEFINITION ---------------------------% +REF_ORIGIN_MOMENT_X= 0.25 +REF_ORIGIN_MOMENT_Y= 0.00 +REF_ORIGIN_MOMENT_Z= 0.00 +REF_LENGTH= 1.0 +REF_AREA= 1.0 +REF_DIMENSIONALIZATION= FREESTREAM_PRESS_EQ_ONE + +% ----------------------- BOUNDARY CONDITION DEFINITION -----------------------% +MARKER_EULER= ( airfoil ) +MARKER_FAR= ( farfield ) + +% ------------------------ SURFACES IDENTIFICATION ----------------------------% +MARKER_PLOTTING= ( airfoil ) +MARKER_MONITORING= ( airfoil ) + +% ------------- COMMON PARAMETERS DEFINING THE NUMERICAL METHOD ---------------% +NUM_METHOD_GRAD= GREEN_GAUSS +CFL_NUMBER= 10.0 +CFL_ADAPT= NO +CFL_ADAPT_PARAM= ( 1.5, 0.5, 1.0, 100.0 ) +RK_ALPHA_COEFF= ( 0.66667, 0.66667, 1.000000 ) + +% ------------------------ LINEAR SOLVER DEFINITION ---------------------------% +LINEAR_SOLVER= FGMRES +LINEAR_SOLVER_PREC= ILU +LINEAR_SOLVER_ERROR= 1E-4 +LINEAR_SOLVER_ITER= 2 + +% -------------------------- MULTIGRID PARAMETERS -----------------------------% +MGLEVEL= 2 +MGCYCLE= V_CYCLE +MG_PRE_SMOOTH= ( 1, 2, 3, 3 ) +MG_POST_SMOOTH= ( 0, 0, 0, 0 ) +MG_CORRECTION_SMOOTH= ( 0, 0, 0, 0 ) +MG_DAMP_RESTRICTION= 1.0 +MG_DAMP_PROLONGATION= 1.0 + +% --------------------- FLOW NUMERICAL METHOD DEFINITION ----------------------% +CONV_NUM_METHOD_FLOW= JST +SLOPE_LIMITER_FLOW= VENKATAKRISHNAN +JST_SENSOR_COEFF= ( 0.5, 0.02 ) +TIME_DISCRE_FLOW= EULER_IMPLICIT + +% ---------------- ADJOINT-FLOW NUMERICAL METHOD DEFINITION -------------------% +OBJECTIVE_FUNCTION= EFFICIENCY +OPT_OBJECTIVE= EFFICIENCY * 1.0 +CONV_NUM_METHOD_ADJFLOW= JST +SLOPE_LIMITER_ADJFLOW= VENKATAKRISHNAN +ADJ_JST_SENSOR_COEFF= ( 0.0, 0.02 ) +TIME_DISCRE_ADJFLOW= EULER_IMPLICIT +CFL_REDUCTION_ADJFLOW= 0.8 +LIMIT_ADJFLOW= 1E6 + +% -------------------- FREE-FORM DEFORMATION PARAMETERS -----------------------% +FFD_TOLERANCE= 1E-10 +FFD_ITERATIONS= 500 +FFD_DEFINITION= (airfoil_box, -0.1, -0.25, 0, 1.1, -0.25, 0, 1.1, 0.25, 0, -0.1, 0.25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) +FFD_DEGREE= (10, 1, 0) +FFD_CONTINUITY= 2ND_DERIVATIVE + +% ----------------------- DESIGN VARIABLE PARAMETERS --------------------------% +DV_KIND= FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT, FFD_CONTROL_POINT +DV_MARKER= ( airfoil ) +DV_PARAM= ( airfoil_box, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 2.0, 0.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 3.0, 0.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 4.0, 0.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 5.0, 0.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 6.0, 0.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 7.0, 0.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 8.0, 0.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 9.0, 0.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 10.0, 0.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 2.0, 1.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 3.0, 1.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 4.0, 1.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 5.0, 1.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 6.0, 1.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 7.0, 1.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 8.0, 1.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 9.0, 1.0, 0.0, 0.0, 1.0, 0.0) ; ( airfoil_box, 10.0, 1.0, 0.0, 0.0, 1.0, 0.0) +DV_VALUE= 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001 + +% ------------------------ GRID DEFORMATION PARAMETERS ------------------------% +DEFORM_NONLINEAR_ITER= 1 +DEFORM_CONSOLE_OUTPUT= YES +DEFORM_LINEAR_SOLVER_ERROR= 0.000000001 +DEFORM_STIFFNESS_TYPE= INVERSE_VOLUME + +% --------------------------- CONVERGENCE PARAMETERS --------------------------% +CONV_CRITERIA= RESIDUAL +CONV_RESIDUAL_MINVAL= -20 +CONV_STARTITER= 10 +CONV_CAUCHY_ELEMS= 100 +CONV_CAUCHY_EPS= 1E-6 + +% ------------------------- INPUT/OUTPUT INFORMATION --------------------------% +MESH_FILENAME= mesh_NACA0012_inv_FFD.su2 +MESH_FORMAT= SU2 +MESH_OUT_FILENAME= mesh_out.su2 +SOLUTION_FILENAME= solution_flow.dat +SOLUTION_ADJ_FILENAME= solution_adj.dat +OUTPUT_FILES= (RESTART_ASCII, RESTART, PARAVIEW) +HISTORY_OUTPUT= ITER, RMS_RES, AERO_COEFF +TABULAR_FORMAT= CSV +CONV_FILENAME= history +RESTART_FILENAME= solution_flow.dat +RESTART_ADJ_FILENAME= solution_adj.dat +VOLUME_FILENAME= flow +VOLUME_ADJ_FILENAME= adjoint +GRAD_OBJFUNC_FILENAME= of_grad.dat +SURFACE_FILENAME= surface_flow +SURFACE_ADJ_FILENAME= surface_adjoint +WRT_SOL_FREQ= 250.0 +WRT_SOL_FREQ_DUALTIME= 1 +WRT_CON_FREQ= 1 +WRT_CON_FREQ_DUALTIME= 10 + From d77f73c68694da40bd660375cc3568137fbadb03 Mon Sep 17 00:00:00 2001 From: cvencro Date: Mon, 9 Mar 2020 07:19:50 +0000 Subject: [PATCH 018/112] add dual time derivative --- .../variables/CDiscAdjMeshBoundVariable.hpp | 11 ++ SU2_CFD/include/variables/CMeshVariable.hpp | 15 +++ SU2_CFD/src/iteration_structure.cpp | 4 +- SU2_CFD/src/solvers/CDiscAdjMeshSolver.cpp | 110 +++++++++++++++++- .../variables/CDiscAdjMeshBoundVariable.cpp | 8 ++ SU2_CFD/src/variables/CMeshVariable.cpp | 37 ++++++ 6 files changed, 183 insertions(+), 2 deletions(-) diff --git a/SU2_CFD/include/variables/CDiscAdjMeshBoundVariable.hpp b/SU2_CFD/include/variables/CDiscAdjMeshBoundVariable.hpp index 6ed2d4ef8017..565458a40f5b 100644 --- a/SU2_CFD/include/variables/CDiscAdjMeshBoundVariable.hpp +++ b/SU2_CFD/include/variables/CDiscAdjMeshBoundVariable.hpp @@ -37,6 +37,9 @@ class CDiscAdjMeshBoundVariable final : public CVariable { MatrixType Bound_Disp_Sens; /*!< \brief Store the reference coordinates of the mesh. */ MatrixType Bound_Disp_Direct; /*!< \brief Store the reference boundary displacements of the mesh. */ + MatrixType DualTime_Derivative; + MatrixType DualTime_Derivative_n; + MatrixType Solution_BGS_k; /*!< \brief BGS solution to compute overall convergence. */ CVertexMap VertexMap; /*!< \brief Object that controls accesses to the variables of this class. */ @@ -62,6 +65,14 @@ class CDiscAdjMeshBoundVariable final : public CVariable { */ void AllocateBoundaryVariables(CConfig *config); + inline void SetDual_Time_Derivative(unsigned long iPoint, unsigned long iVar, su2double der) override { DualTime_Derivative(iPoint,iVar) = der; } + + inline void SetDual_Time_Derivative_n(unsigned long iPoint, unsigned long iVar, su2double der) override { DualTime_Derivative_n(iPoint,iVar) = der; } + + inline su2double GetDual_Time_Derivative(unsigned long iPoint, unsigned long iVar) const override { return DualTime_Derivative(iPoint,iVar); } + + inline su2double GetDual_Time_Derivative_n(unsigned long iPoint, unsigned long iVar) const override { return DualTime_Derivative_n(iPoint,iVar); } + /*! * \brief Get the value of the displacement imposed at the boundary. * \return Value of the boundary displacement. diff --git a/SU2_CFD/include/variables/CMeshVariable.hpp b/SU2_CFD/include/variables/CMeshVariable.hpp index b434b82197e1..025f9a0c841e 100644 --- a/SU2_CFD/include/variables/CMeshVariable.hpp +++ b/SU2_CFD/include/variables/CMeshVariable.hpp @@ -99,4 +99,19 @@ class CMeshVariable : public CVariable { adj_mesh[iDim] = SU2_TYPE::GetDerivative(Mesh_Coord(iPoint,iDim)); } + /*! + * \brief Register the variables in the solution_time_n array as input/output variable. + */ + void RegisterSolution(bool input, bool push_index = true); + + /*! + * \brief Register the variables in the solution_time_n array as input/output variable. + */ + void RegisterSolution_time_n(); + + /*! + * \brief Register the variables in the solution_time_n1 array as input/output variable. + */ + void RegisterSolution_time_n1(); + }; diff --git a/SU2_CFD/src/iteration_structure.cpp b/SU2_CFD/src/iteration_structure.cpp index b7a118b19a46..539cc57f94c6 100644 --- a/SU2_CFD/src/iteration_structure.cpp +++ b/SU2_CFD/src/iteration_structure.cpp @@ -2262,7 +2262,9 @@ void CDiscAdjFluidIteration::Preprocess(COutput *output, if (config[val_iZone]->AddRadiation()){ solver[val_iZone][val_iInst][MESH_0][ADJRAD_SOL]->Preprocessing(geometry[val_iZone][val_iInst][MESH_0], solver[val_iZone][val_iInst][MESH_0], config[val_iZone] , MESH_0, 0, RUNTIME_ADJRAD_SYS, false); } - + if (config[val_iZone]->GetDeform_Mesh()) { + solver[val_iZone][val_iInst][MESH_0][ADJMESH_SOL]->Preprocessing(geometry[val_iZone][val_iInst][MESH_0], solver[val_iZone][val_iInst][MESH_0], config[val_iZone] , MESH_0, 0, RUNTIME_ADJFLOW_SYS, false); + } } diff --git a/SU2_CFD/src/solvers/CDiscAdjMeshSolver.cpp b/SU2_CFD/src/solvers/CDiscAdjMeshSolver.cpp index ce3108d51699..711970ebabeb 100644 --- a/SU2_CFD/src/solvers/CDiscAdjMeshSolver.cpp +++ b/SU2_CFD/src/solvers/CDiscAdjMeshSolver.cpp @@ -123,19 +123,61 @@ CDiscAdjMeshSolver::~CDiscAdjMeshSolver(void){ void CDiscAdjMeshSolver::Preprocessing(CGeometry *geometry, CSolver **solver_container, CConfig *config_container, unsigned short iMesh, unsigned short iRKStep, unsigned short RunTime_EqSystem, bool Output){ + bool dual_time = (config_container->GetTime_Marching() == DT_STEPPING_1ST || + config_container->GetTime_Marching() == DT_STEPPING_2ND); + su2double *solution_n, *solution_n1; + unsigned long iPoint; + unsigned short iVar; + if (dual_time) { + for (iPoint = 0; iPointGetnPoint(); iPoint++) { + solution_n = nodes->GetSolution_time_n(iPoint); + solution_n1 = nodes->GetSolution_time_n1(iPoint); + for (iVar = 0; iVar < nVar; iVar++) { + nodes->SetDual_Time_Derivative(iPoint, iVar, solution_n[iVar]+nodes->GetDual_Time_Derivative_n(iPoint, iVar)); + nodes->SetDual_Time_Derivative_n(iPoint,iVar, solution_n1[iVar]); + } + } + } } void CDiscAdjMeshSolver::SetRecording(CGeometry* geometry, CConfig *config){ + bool time_n1_needed = config->GetTime_Marching() == DT_STEPPING_2ND; + bool time_n_needed = (config->GetTime_Marching() == DT_STEPPING_1ST) || time_n1_needed; + bool time_domain = config->GetTime_Domain(); unsigned long iPoint; + unsigned short iDim; + /*--- Reset the solution to the initial (converged) solution ---*/ for (iPoint = 0; iPoint < nPoint; iPoint++) { direct_solver->GetNodes()->SetBound_Disp(iPoint,nodes->GetBoundDisp_Direct(iPoint)); } + if (time_domain) { + // for (iPoint = 0; iPoint < nPoint; iPoint++) { + // for (iDim = 0; iDim < nVar; iDim++) { + // AD::ResetInput(direct_solver->GetNodes()->GetSolution(iPoint)[iDim]); + // } + // } + if (time_n_needed) { + for (iPoint = 0; iPoint < nPoint; iPoint++) { + for (iDim = 0; iDim < nVar; iDim++) { + AD::ResetInput(direct_solver->GetNodes()->GetSolution_time_n(iPoint)[iDim]); + } + } + } + if (time_n1_needed) { + for (iPoint = 0; iPoint < nPoint; iPoint++) { + for (iDim = 0; iDim < nVar; iDim++) { + AD::ResetInput(direct_solver->GetNodes()->GetSolution_time_n1(iPoint)[iDim]); + } + } + } + } + /*--- Set indices to zero ---*/ RegisterVariables(geometry, config, true); @@ -144,10 +186,20 @@ void CDiscAdjMeshSolver::SetRecording(CGeometry* geometry, CConfig *config){ void CDiscAdjMeshSolver::RegisterSolution(CGeometry *geometry, CConfig *config){ + bool time_n1_needed = (config->GetTime_Marching() == DT_STEPPING_2ND); + bool time_n_needed = (config->GetTime_Marching() == DT_STEPPING_1ST) || time_n1_needed; + bool time_domain = config->GetTime_Domain(); + /*--- Register reference mesh coordinates ---*/ bool input = true; direct_solver->GetNodes()->Register_MeshCoord(input); - + if (time_domain) { + // direct_solver->GetNodes()->RegisterSolution(input); + if (time_n_needed) + direct_solver->GetNodes()->RegisterSolution_time_n(); + if (time_n1_needed) + direct_solver->GetNodes()->RegisterSolution_time_n1(); + } } void CDiscAdjMeshSolver::RegisterVariables(CGeometry *geometry, CConfig *config, bool reset){ @@ -160,7 +212,13 @@ void CDiscAdjMeshSolver::RegisterVariables(CGeometry *geometry, CConfig *config, void CDiscAdjMeshSolver::ExtractAdjoint_Solution(CGeometry *geometry, CConfig *config){ + bool time_n1_needed = config->GetTime_Marching() == DT_STEPPING_2ND; + bool time_domain = config->GetTime_Domain(); + bool dual_time = (config->GetTime_Marching() == DT_STEPPING_1ST || + config->GetTime_Marching() == DT_STEPPING_2ND); + unsigned long iPoint; + unsigned short iDim; /*--- Extract the sensitivities of the mesh coordinates ---*/ @@ -176,6 +234,56 @@ void CDiscAdjMeshSolver::ExtractAdjoint_Solution(CGeometry *geometry, CConfig *c } + if (time_domain) { + // for (iPoint = 0; iPoint < nPoint; iPoint++) { + + // /*--- Extract the adjoint solution at time n ---*/ + + // direct_solver->GetNodes()->GetAdjointSolution(iPoint,Solution); + + // /*--- Store the adjoint solution at time n ---*/ + + // nodes->SetSolution(iPoint,Solution); + // } + + if (dual_time) { + for (iPoint = 0; iPoint < nPoint; iPoint++) { + + /*--- Extract the adjoint solution at time n ---*/ + + direct_solver->GetNodes()->GetAdjointSolution_time_n(iPoint,Solution); + + /*--- Store the adjoint solution at time n ---*/ + + nodes->Set_Solution_time_n(iPoint,Solution); + } + } + if (time_n1_needed) { + + for (iPoint = 0; iPoint < nPoint; iPoint++) { + + /*--- Extract the adjoint solution at time n-1 ---*/ + + direct_solver->GetNodes()->GetAdjointSolution_time_n1(iPoint,Solution); + + /*--- Store the adjoint solution at time n-1 ---*/ + + nodes->Set_Solution_time_n1(iPoint,Solution); + } + } + + for (iPoint = 0; iPoint < nPoint; iPoint++) { + for (iDim = 0; iDim < nVar; iDim++) { + Solution[iDim] = nodes->GetSolution(iPoint,iDim); + } + if (dual_time) { + for (iDim = 0; iDim < nVar; iDim++) { + Solution[iDim] += nodes->GetDual_Time_Derivative(iPoint,iDim); + } + } + nodes->SetSolution(iPoint,Solution); + } + } } void CDiscAdjMeshSolver::ExtractAdjoint_Variables(CGeometry *geometry, CConfig *config){ diff --git a/SU2_CFD/src/variables/CDiscAdjMeshBoundVariable.cpp b/SU2_CFD/src/variables/CDiscAdjMeshBoundVariable.cpp index 6680eb7ec018..616af24eb1a3 100644 --- a/SU2_CFD/src/variables/CDiscAdjMeshBoundVariable.cpp +++ b/SU2_CFD/src/variables/CDiscAdjMeshBoundVariable.cpp @@ -35,6 +35,14 @@ CDiscAdjMeshBoundVariable::CDiscAdjMeshBoundVariable(unsigned long npoint, unsig nDim = ndim; VertexMap.Reset(nPoint); + + if (config->GetTime_Domain()) { + DualTime_Derivative.resize(nPoint,nDim) = su2double(0.0); + DualTime_Derivative_n.resize(nPoint,nDim) = su2double(0.0); + + Solution_time_n.resize(nPoint,nDim) = su2double(0.0); + Solution_time_n1.resize(nPoint,nDim) = su2double(0.0); + } } void CDiscAdjMeshBoundVariable::AllocateBoundaryVariables(CConfig *config) { diff --git a/SU2_CFD/src/variables/CMeshVariable.cpp b/SU2_CFD/src/variables/CMeshVariable.cpp index 9c1af06f2db1..17e73d3ee584 100644 --- a/SU2_CFD/src/variables/CMeshVariable.cpp +++ b/SU2_CFD/src/variables/CMeshVariable.cpp @@ -60,3 +60,40 @@ void CMeshVariable::Register_MeshCoord(bool input) { AD::RegisterOutput(Mesh_Coord(iPoint,iDim)); } } + +void CMeshVariable::RegisterSolution(bool input, bool push_index) { + for (unsigned long iPoint = 0; iPoint < nPoint; ++iPoint) { + for (unsigned long iDim = 0; iDim < nDim; iDim++) { + if(input) { + if(push_index) { + AD::RegisterInput(Solution(iPoint,iDim)); + } + else { + AD::RegisterInput(Solution(iPoint,iDim), false); + AD::SetIndex(AD_InputIndex(iPoint,iDim), Solution(iPoint,iDim)); + } + } + else { + AD::RegisterOutput(Solution(iPoint,iDim)); + if(!push_index) + AD::SetIndex(AD_OutputIndex(iPoint,iDim), Solution(iPoint,iDim)); + } + } + } +} + +void CMeshVariable::RegisterSolution_time_n() { + for (unsigned long iPoint = 0; iPoint < nPoint; ++iPoint) { + for(unsigned long iDim=0; iDim Date: Wed, 11 Mar 2020 15:20:29 +0000 Subject: [PATCH 019/112] general cleanup --- SU2_CFD/include/solvers/CFEASolver.hpp | 6 +++--- SU2_CFD/src/solvers/CFEASolver.cpp | 16 ++++------------ 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/SU2_CFD/include/solvers/CFEASolver.hpp b/SU2_CFD/include/solvers/CFEASolver.hpp index b4a20c377d97..948ef934ffef 100644 --- a/SU2_CFD/include/solvers/CFEASolver.hpp +++ b/SU2_CFD/include/solvers/CFEASolver.hpp @@ -46,7 +46,7 @@ class CFEASolver : public CSolver { su2double Total_CFEA; /*!< \brief Total FEA coefficient for all the boundaries. */ - unsigned short *iElem_iDe; /*!< \brief For DE cases, ID of the region considered for each iElem. */ + unsigned short *iElem_iDe = nullptr; /*!< \brief For DE cases, ID of the region considered for each iElem. */ su2double a_dt[9]; /*!< \brief Integration constants. */ @@ -179,8 +179,8 @@ class CFEASolver : public CSolver { CSysMatrix MassMatrix; /*!< \brief Sparse structure for storing the mass matrix. */ - CElement*** element_container; /*!< \brief Vector which the define the finite element structure for each problem. */ - CProperty** element_properties; /*!< \brief Vector which stores the properties of each element */ + CElement*** element_container = nullptr; /*!< \brief Vector which the define the finite element structure for each problem. */ + CProperty** element_properties = nullptr; /*!< \brief Vector which stores the properties of each element */ /*! * \brief Constructor of the class. diff --git a/SU2_CFD/src/solvers/CFEASolver.cpp b/SU2_CFD/src/solvers/CFEASolver.cpp index f1a7104a235f..3819e0b713a1 100644 --- a/SU2_CFD/src/solvers/CFEASolver.cpp +++ b/SU2_CFD/src/solvers/CFEASolver.cpp @@ -96,12 +96,6 @@ CFEASolver::CFEASolver(bool mesh_deform_mode) : CSolver(mesh_deform_mode) { for (unsigned short iTerm = 0; iTerm < MAX_TERMS; iTerm++) element_container[iTerm] = new CElement* [MAX_FE_KINDS*omp_get_max_threads()](); - nodes = nullptr; - - element_properties = nullptr; - - iElem_iDe = nullptr; - topol_filter_applied = false; element_based = false; @@ -243,8 +237,6 @@ CFEASolver::CFEASolver(CGeometry *geometry, CConfig *config) : CSolver() { /*--- Initialize structures for hybrid-parallel mode. ---*/ HybridParallelInitialization(geometry); - iElem_iDe = nullptr; - /*--- Initialize the value of the total objective function ---*/ Total_OFRefGeom = 0.0; Total_OFRefNode = 0.0; @@ -312,15 +304,15 @@ CFEASolver::~CFEASolver(void) { delete [] element_container; } - if (element_properties != nullptr){ + if (element_properties != nullptr) { for (unsigned long iElem = 0; iElem < nElement; iElem++) - if (element_properties[iElem] != nullptr) delete element_properties[iElem]; + delete element_properties[iElem]; delete [] element_properties; } - if (iElem_iDe != nullptr) delete [] iElem_iDe; + delete [] iElem_iDe; - if (nodes != nullptr) delete nodes; + delete nodes; if (LockStrategy) { for (unsigned long iPoint = 0; iPoint < nPoint; iPoint++) From 49f7d625845a2d85fbaf169ec6d0b6a399ce42f1 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 17 Mar 2020 11:54:04 +0000 Subject: [PATCH 020/112] cleanup RBF interpolation --- Common/include/interpolation_structure.hpp | 141 +- Common/src/interpolation_structure.cpp | 1423 +++++++------------- 2 files changed, 523 insertions(+), 1041 deletions(-) diff --git a/Common/include/interpolation_structure.hpp b/Common/include/interpolation_structure.hpp index 28bdf3af8677..a1349ecd4df7 100644 --- a/Common/include/interpolation_structure.hpp +++ b/Common/include/interpolation_structure.hpp @@ -145,10 +145,16 @@ class CInterpolator { /*! * \brief compute distance between 2 points + * \param[in] nDim - number of dimensions * \param[in] point_i * \param[in] point_i */ - su2double PointsDistance(su2double *point_i, su2double *point_j); + inline su2double PointsDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) const { + su2double m = 0; + for(unsigned short iDim = 0; iDim < nDim; iDim++) + m += pow(point_j[iDim] - point_i[iDim], 2); + return sqrt(m); + } /*! * \brief Set up transfer matrix defining relation between two meshes @@ -382,9 +388,8 @@ class CSlidingMesh : public CInterpolator { /*! * \brief Radial basis function interpolation */ -class CRadialBasisFunction : public CInterpolator { +class CRadialBasisFunction final : public CInterpolator { public: - /*! * \brief Constructor of the class. */ @@ -399,16 +404,11 @@ class CRadialBasisFunction : public CInterpolator { */ CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); - /*! - * \brief Destructor of the class. - */ - ~CRadialBasisFunction(void); - /*! * \brief Set up transfer matrix defining relation between two meshes * \param[in] config - Definition of the particular problem. */ - void Set_TransferCoeff(CConfig **config); + void Set_TransferCoeff(CConfig **config) override; /*! * \brief Compute the value of a radial basis function, this is static so it can be re-used. @@ -416,76 +416,77 @@ class CRadialBasisFunction : public CInterpolator { * \param[in] radius - the characteristic dimension * \param[in] dist - distance */ - static su2double Get_RadialBasisValue(const short unsigned int type, const su2double &radius, const su2double &dist); - + static su2double Get_RadialBasisValue(const unsigned short type, const su2double radius, const su2double dist); + private: /*! - * \brief If the polynomial term is included in the interpolation, and the points lie on a plane, the matrix becomes rank deficient - * and cannot be inverted. This method detects that condition and corrects it by removing a row from P (the polynomial part of the matrix). - * \param[in] m - number of rows of P - * \param[in] n - number of columns of P - * \param[in] skip_row - marks the row of P which is all ones (by construction) - * \param[in] max_diff_tol_in - tolerance to detect points are on a plane - * \param[out] keep_row - marks the rows of P kept - * \param[out] n_polynomial - size of the polynomial part on exit (i.e. new number of rows) - * \param[in,out] P - polynomial part of the matrix, may be changed or not! + * \brief If the polynomial term is included in the interpolation, and the points lie on a plane, the matrix + * becomes rank deficient and cannot be inverted. This method detects that condition and corrects it by + * removing a row from P (the polynomial part of the interpolation matrix). + * \param[in] max_diff_tol - Tolerance to detect whether points are on a plane. + * \param[out] keep_row - Marks the dimensions of P kept. + * \param[in,out] P - Polynomial part of the interpolation matrix, one row may be eliminated. + * \return n_polynomial - Size of the polynomial part on exit (in practice nDim or nDim-1). */ - void Check_PolynomialTerms(int m, unsigned long n, const int *skip_row, su2double max_diff_tol_in, int *keep_row, int &n_polynomial, su2double *P); + int CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, su2passivematrix &P) const; }; /*! - * \brief Helper class used by CRadialBasisFunction to calculate the interpolation weights. - * This does not inherit from CSysMatrix because: it is a dense format rather than block sparse; - * as the interpolation is done on a single core there are no methods for communication. - * The code can be compiled with LAPACK to use optimized matrix inversion and multiplication routines. - * CPPFLAGS="-DHAVE_LAPACK" LDFLAGS=-L/path/to/lapack_lib LIBS="-llapack -lrefblas -lgfortran" + * \brief Helper class used by CRadialBasisFunction to compute the interpolation weights. + * The matrix is symmetric but full storage is used as that gives much better performance + * for some BLAS libraries (notably OpenBLAS). The code should be compiled with LAPACK + * to use optimized matrix inversion and multiplication routines. */ class CSymmetricMatrix{ +private: + enum DecompositionType { NONE, CHOLESKY, LU }; + + vector val_vec, decomp_vec; + vector perm_vec; + int sz = 0; + bool initialized = false; + DecompositionType decomposed = NONE; + + inline void CheckBounds(int i, int j) const { + assert(initialized && "Matrix not initialized."); + assert(i>=0 && i=0 && jGetnDim(); - su2double m; - - m = 0 ; - for(iDim = 0; iDim < nDim; iDim++) - m += (point_j[iDim] - point_i[iDim])*(point_j[iDim] - point_i[iDim]); - - return sqrt(m); -} - /* Nearest Neighbor Interpolator */ CNearestNeighbor::CNearestNeighbor(void): CInterpolator() { } @@ -685,7 +646,7 @@ void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { Coord_j = &Buffer_Receive_Coord[ Global_Point_Donor*nDim]; - dist = PointsDistance(Coord_i, Coord_j); + dist = PointsDistance(nDim, Coord_i, Coord_j); if (dist < mindist) { mindist = dist; pProcessor = iProcessor; @@ -1674,7 +1635,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config){ Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ]; - dist = PointsDistance(Coord_i, Coord_j); + dist = PointsDistance(nDim, Coord_i, Coord_j); if (dist < mindist) { mindist = dist; @@ -1719,7 +1680,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config){ for(iDim = 0; iDim < nDim; iDim++) Direction[iDim] /= dTMP; - length = PointsDistance(target_iMidEdge_point, target_jMidEdge_point); + length = PointsDistance(nDim, target_iMidEdge_point, target_jMidEdge_point); check = false; @@ -1951,7 +1912,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config){ Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ]; - dist = PointsDistance(Coord_i, Coord_j); + dist = PointsDistance(nDim, Coord_i, Coord_j); if (dist < mindist) { mindist = dist; @@ -2723,620 +2684,344 @@ bool CSlidingMesh::CheckPointInsideTriangle(su2double* Point, su2double* T1, su2 } /*--- Radial Basis Function Interpolator ---*/ -CRadialBasisFunction::CRadialBasisFunction(void): CInterpolator() { } +CRadialBasisFunction::CRadialBasisFunction(void): CInterpolator() { } -CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { +CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { /*--- Initialize transfer coefficients between the zones ---*/ Set_TransferCoeff(config); - } -CRadialBasisFunction::~CRadialBasisFunction() {} - void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { - int iProcessor, nProcessor = size; - int nPolynomial = 0; - int mark_donor, mark_target, target_check, donor_check; - int *skip_row = NULL, *calc_polynomial_check; - - unsigned short iDim, nDim, iMarkerInt, nMarkerInt; - - unsigned long iVertexDonor, jVertexDonor, iVertexTarget, iCount, jCount; - unsigned long nVertexDonor, nVertexTarget, nVertexDonorInDomain; - unsigned long nGlobalVertexDonor, iGlobalVertexDonor_end, nLocalM; - unsigned long point_donor, point_target; - unsigned long *nLocalM_arr; - - su2double val_i, val_j; - su2double interface_coord_tol=1e6*numeric_limits::epsilon(); - su2double *Coord_i, *Coord_j; - su2double *local_M; - su2double *P = NULL; - su2double *C_inv_trunc = NULL, *C_tmp = NULL; - su2double *target_vec, *coeff_vec; - - CSymmetricMatrix *global_M = NULL, *Mp = NULL; - -#ifdef HAVE_MPI - unsigned long iLocalM; - su2double *global_M_val_arr = NULL, *Buffer_recv_local_M; - int *Buffer_Recv_mark = new int[nProcessor], iRank; -#endif + /*--- RBF options. ---*/ + const unsigned short kindRBF = config[donorZone]->GetKindRadialBasisFunction(); + const su2double paramRBF = config[donorZone]->GetRadialBasisFunctionParameter(); + const bool usePolynomial = config[donorZone]->GetRadialBasisFunctionPolynomialOption(); + const passivedouble eps = numeric_limits::epsilon(); + const su2double interfaceCoordTol = 1e6 * eps; - /*--- Initialize variables --- */ - - nMarkerInt = (int) ( config[donorZone]->GetMarker_n_ZoneInterface() / 2 ); - - nDim = donor_geometry->GetnDim(); + const int nDim = donor_geometry->GetnDim(); - + const int nProcessor = size; + Buffer_Send_nVertex_Donor = new unsigned long [1]; Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; + /*--- Cycle over nMarkersInt interface to determine communication pattern. ---*/ - /*--- Cycle over nMarkersInt interface to determine communication pattern ---*/ + const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2; - for (iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { + for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { - /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ - mark_donor = Find_InterfaceMarker(config[donorZone], iMarkerInt); - - /*--- On the target side: find the tag of the boundary sharing the interface ---*/ - mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt); + /*--- On the donor side: find the tag of the boundary sharing the interface. ---*/ + int mark_donor = Find_InterfaceMarker(config[donorZone], iMarkerInt); -#ifdef HAVE_MPI + /*--- On the target side: find the tag of the boundary sharing the interface. ---*/ + int mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt); - donor_check = -1; - target_check = -1; - - /*--- We gather a vector in MASTER_NODE to determines whether the boundary is not on the processor because of the partition or because the zone does not include it ---*/ - - SU2_MPI::Gather(&mark_donor , 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - - if (rank == MASTER_NODE) - for (iRank = 0; iRank < nProcessor; iRank++) - if( Buffer_Recv_mark[iRank] != -1 ) { - donor_check = Buffer_Recv_mark[iRank]; - break; - } - - SU2_MPI::Bcast(&donor_check , 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - - - SU2_MPI::Gather(&mark_target, 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - - if (rank == MASTER_NODE) - for (iRank = 0; iRank < nProcessor; iRank++) - if( Buffer_Recv_mark[iRank] != -1 ) { - target_check = Buffer_Recv_mark[iRank]; - break; - } + /*--- We gather a vector in MASTER_NODE to determines whether the boundary is not on + the processor because of the partition or because the zone does not include it. ---*/ + int donor_check, target_check; + SU2_MPI::Allreduce(&mark_donor, &donor_check, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); + SU2_MPI::Allreduce(&mark_target, &target_check, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); - SU2_MPI::Bcast(&target_check, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - -#else - donor_check = mark_donor; - target_check = mark_target; -#endif - - /*--- Checks if the zone contains the interface, if not continue to the next step ---*/ - if(target_check == -1 || donor_check == -1) - continue; + /*--- If the zone does not contain the interface continue to the next pair of markers. ---*/ + if(target_check == -1 || donor_check == -1) continue; - if(mark_donor != -1) - nVertexDonor = donor_geometry->GetnVertex( mark_donor ); - else - nVertexDonor = 0; - - if(mark_target != -1) - nVertexTarget = target_geometry->GetnVertex( mark_target ); - else - nVertexTarget = 0; - - Buffer_Send_nVertex_Donor = new unsigned long [ 1 ]; + unsigned long nVertexDonor = 0, nVertexTarget = 0; + if(mark_donor != -1) nVertexDonor = donor_geometry->GetnVertex( mark_donor ); + if(mark_target != -1) nVertexTarget = target_geometry->GetnVertex( mark_target ); - /*--- Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor ---*/ + /*--- Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor. ---*/ Determine_ArraySize(false, mark_donor, mark_target, nVertexDonor, nDim); - - /*--- Collect information about number of donor vertices in domain. - Calculate total number of donor vertices across all ranks and - number of vertices on boundary prior to current rank. ---*/ - nVertexDonorInDomain = Buffer_Send_nVertex_Donor[0]; - iGlobalVertexDonor_end = nGlobalVertexDonor = 0; - for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) - { - nGlobalVertexDonor += Buffer_Receive_nVertex_Donor[iProcessor]; - if (iProcessor<=rank) iGlobalVertexDonor_end += Buffer_Receive_nVertex_Donor[iProcessor]; - } - /*-- Collect coordinates, global points, and normal vectors ---*/ - Buffer_Send_Coord = new su2double [ MaxLocalVertex_Donor * nDim ]; - Buffer_Send_GlobalPoint = new long [ MaxLocalVertex_Donor ]; - Buffer_Receive_Coord = new su2double [ nProcessor * MaxLocalVertex_Donor * nDim ]; - Buffer_Receive_GlobalPoint = new long [ nProcessor * MaxLocalVertex_Donor ]; + /*--- Compute total number of donor vertices. ---*/ + auto nGlobalVertexDonor = accumulate(Buffer_Receive_nVertex_Donor, + Buffer_Receive_nVertex_Donor+nProcessor, 0ul); - Collect_VertexInfo( false, mark_donor, mark_target, nVertexDonor, nDim); + /*--- Gather coordinates and global point indices. ---*/ + Buffer_Send_Coord = new su2double [ MaxLocalVertex_Donor * nDim ]; + Buffer_Send_GlobalPoint = new long [ MaxLocalVertex_Donor ]; + Buffer_Receive_Coord = new su2double [ nProcessor * MaxLocalVertex_Donor * nDim ]; + Buffer_Receive_GlobalPoint = new long [ nProcessor * MaxLocalVertex_Donor ]; - /*--- Send information about size of local_M array ---*/ - nLocalM = nVertexDonorInDomain*(nVertexDonorInDomain+1)/2 \ - + nVertexDonorInDomain*(nGlobalVertexDonor-iGlobalVertexDonor_end); - - nLocalM_arr = new unsigned long [nProcessor]; -#ifdef HAVE_MPI - SU2_MPI::Allgather(&nLocalM, 1, MPI_UNSIGNED_LONG, nLocalM_arr, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); -#else - nLocalM_arr[MASTER_NODE] = nLocalM; -#endif - - /*--- Initialize local M array and calculate values ---*/ - local_M = new su2double [nLocalM]; - Coord_i = new su2double [nDim]; - Coord_j = new su2double [nDim]; - iCount=0; - for (iVertexDonor=0; iVertexDonorGetKindRadialBasisFunction(), - config[donorZone]->GetRadialBasisFunctionParameter(), - PointsDistance(Coord_i, Coord_j)); + auto iCount = 0ul; + for (int iProcessor = 0; iProcessor < nProcessor; ++iProcessor) { + auto offset = iProcessor * MaxLocalVertex_Donor * nDim; + for (auto iVertex = 0ul; iVertex < Buffer_Receive_nVertex_Donor[iProcessor]; ++iVertex) { + for (int iDim = 0; iDim < nDim; ++iDim) + DonorCoord(iCount,iDim) = Buffer_Receive_Coord[offset + iVertex*nDim + iDim]; + ++iCount; } + } + assert((iCount == nGlobalVertexDonor) && "Global donor point count mismatch."); - for (iProcessor=rank+1; iProcessor keepPolynomialRow(nDim,1); - local_M[iCount++] = Get_RadialBasisValue(config[donorZone]->GetKindRadialBasisFunction(), - config[donorZone]->GetRadialBasisFunctionParameter(), - PointsDistance(Coord_i, Coord_j)); - } - } - } - -#ifdef HAVE_MPI - if (rank != MASTER_NODE) { - SU2_MPI::Send(local_M, nLocalM, MPI_DOUBLE, MASTER_NODE, 0, MPI_COMM_WORLD); - } - - /*--- Assemble global_M ---*/ - if (rank == MASTER_NODE) { - global_M_val_arr = new su2double [nGlobalVertexDonor*(nGlobalVertexDonor+1)/2]; - - /*--- Copy master node local_M to global_M ---*/ - iCount = 0; - for (iLocalM=0; iLocalM SINGLE_NODE) { - for (iProcessor=1; iProcessorInitialize(nGlobalVertexDonor, global_M_val_arr); - } - -#else - global_M = new CSymmetricMatrix; - global_M->Initialize((int)nVertexDonorInDomain, local_M); -#endif - - /*--- Invert M matrix ---*/ - if (rank == MASTER_NODE) { - switch (config[donorZone]->GetKindRadialBasisFunction()) - { - /*--- Basis functions that make M positive definite ---*/ + su2passivematrix C_inv_trunc; + + if (rank==MASTER_NODE) { + + /*--- Populate interpolation kernel. ---*/ + CSymmetricMatrix global_M(nGlobalVertexDonor); + + for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) + for (auto jVertex = iVertex; jVertex < nGlobalVertexDonor; ++jVertex) + global_M(iVertex, jVertex) = SU2_TYPE::GetValue(Get_RadialBasisValue(kindRBF, paramRBF, + PointsDistance(nDim, DonorCoord[iVertex], DonorCoord[jVertex]))); + + /*--- Invert M matrix (operation is in-place). ---*/ + switch (kindRBF) { + /*--- Basis functions that make M positive definite. ---*/ case WENDLAND_C2: case INV_MULTI_QUADRIC: case GAUSSIAN: - global_M->Invert(true); - break; + global_M.Invert(true); break; + /*--- Basis functions that make M semi-positive definite. ---*/ case THIN_PLATE_SPLINE: case MULTI_QUADRIC: - global_M->Invert(false); - break; + global_M.Invert(false); break; } - } - - calc_polynomial_check = new int [nDim]; - - /*--- Calculate C_inv_trunc ---*/ - if (rank == MASTER_NODE) { - - if ( config[donorZone]->GetRadialBasisFunctionPolynomialOption() ) { - - /*--- Fill P matrix and get minimum and maximum values ---*/ - P = new su2double [nGlobalVertexDonor*(nDim+1)]; - iCount = 0; - for (iProcessor=MASTER_NODE; iProcessorInitialize(nPolynomial+1); - for (int m=0; mRead((int)iVertexDonor, (int)jVertexDonor)*P[jVertexDonor*(nPolynomial+1)+n]; - } - val_i += val_j*P[iVertexDonor*(nPolynomial+1)+m]; - } - Mp->Write(m, n, val_i); + /*--- Check if points lie on a plane and remove one coordinate from P if so. ---*/ + nPolynomial = CheckPolynomialTerms(interfaceCoordTol, keepPolynomialRow, P); + + /*--- Calculate Mp = (P * M^-1 * P^T)^-1 ---*/ + CSymmetricMatrix Mp(nPolynomial+1); + + su2passivematrix tmp; + global_M.MatMatMult('R', P, tmp); // tmp = P * M^-1 + + for (int i = 0; i <= nPolynomial; ++i) // Mp = tmp * P + for (int j = i; j <= nPolynomial; ++j) { + Mp(i,j) = 0.0; + for (auto k = 0ul; k < nGlobalVertexDonor; ++k) Mp(i,j) += tmp(i,k) * P(j,k); } - } - Mp->Invert(false); - - /*--- Calculate M_p*P*M_inv ---*/ - C_inv_trunc = new su2double [(nGlobalVertexDonor+nPolynomial+1)*nGlobalVertexDonor]; - for (int m=0; mRead((int)jVertexDonor, (int)iVertexDonor); - } - val_i += val_j*Mp->Read(m, n); - } - /*--- Save in row major order ---*/ - C_inv_trunc[m*nGlobalVertexDonor+iVertexDonor] = val_i; - } - } - - /*--- Calculate (I - P'*M_p*P*M_inv) ---*/ - C_tmp = new su2double [nGlobalVertexDonor*nGlobalVertexDonor]; - for (iVertexDonor=0; iVertexDonorMatMatMult(true, C_tmp, (int)nGlobalVertexDonor); + /*--- Calculate M^-1 * (I - P^T * M_p * P * M^-1), finalize bottom of C_inv_trunc. ---*/ + global_M.MatMatMult('L', tmp, C_inv_trunc); - /*--- Write to C_inv_trunc matrix ---*/ - for (iVertexDonor=0; iVertexDonorRead((int)iVertexDonor, (int)jVertexDonor); - - } // endif GetRadialBasisFunctionPolynomialOption - } // endif (rank == MASTER_NODE) - -#ifdef HAVE_MPI SU2_MPI::Bcast(&nPolynomial, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - SU2_MPI::Bcast(calc_polynomial_check, nDim, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - - if (rank != MASTER_NODE) { - C_inv_trunc = new su2double [(nGlobalVertexDonor+nPolynomial+1)*nGlobalVertexDonor]; - } + SU2_MPI::Bcast(keepPolynomialRow.data(), nDim, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - SU2_MPI::Bcast(C_inv_trunc, (nGlobalVertexDonor+nPolynomial+1)*nGlobalVertexDonor, MPI_DOUBLE, MASTER_NODE, MPI_COMM_WORLD); + if (rank != MASTER_NODE) + C_inv_trunc.resize(nGlobalVertexDonor+nPolynomial+1, nGlobalVertexDonor); + +#ifdef HAVE_MPI + /*--- MPI wrapper not used due to passive double. ---*/ + MPI_Bcast(C_inv_trunc.data(), C_inv_trunc.size(), MPI_DOUBLE, MASTER_NODE, MPI_COMM_WORLD); #endif - - /*--- Calculate H matrix ---*/ - if (config[donorZone]->GetRadialBasisFunctionPolynomialOption()) - target_vec = new su2double [nGlobalVertexDonor+nPolynomial+1]; - else - target_vec = new su2double [nGlobalVertexDonor]; - - coeff_vec = new su2double [nGlobalVertexDonor]; - - for (iVertexTarget = 0; iVertexTarget < nVertexTarget; iVertexTarget++) { - point_target = target_geometry->vertex[mark_target][iVertexTarget]->GetNode(); - - if ( target_geometry->node[point_target]->GetDomain() ) { - iCount = 0; - if (config[donorZone]->GetRadialBasisFunctionPolynomialOption()) { - target_vec[iCount] = 1; - iCount++; - } - - for (iDim=0; iDimnode[point_target]->GetCoord(iDim); - if (config[donorZone]->GetRadialBasisFunctionPolynomialOption()) { - if (calc_polynomial_check[iDim] == 1) { - target_vec[iCount] = Coord_i[iDim]; - iCount++; - } - } - } - - for (iProcessor=0; iProcessorGetKindRadialBasisFunction(), - config[donorZone]->GetRadialBasisFunctionParameter(), - PointsDistance(Coord_i, Coord_j)); - } - } - - for (iVertexDonor=0; iVertexDonorvertex[mark_target][iVertexTarget]->SetnDonorPoints(iCount); - target_geometry->vertex[mark_target][iVertexTarget]->Allocate_DonorInfo(); - - iCount = 0; - jCount = 0; - for (iProcessor=0; iProcessorvertex[mark_target][iVertexTarget]->SetInterpDonorPoint(jCount, point_donor); - target_geometry->vertex[mark_target][iVertexTarget]->SetInterpDonorProcessor(jCount, iProcessor); - target_geometry->vertex[mark_target][iVertexTarget]->SetDonorCoeff(jCount, coeff_vec[iCount]); - jCount++; - } - iCount++; + for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget++) { + + auto target_vertex = target_geometry->vertex[mark_target][iVertexTarget]; + const auto point_target = target_vertex->GetNode(); + + /*--- If not domain point move to next. ---*/ + if (!target_geometry->node[point_target]->GetDomain()) continue; + + su2passivevector target_vec(nGlobalVertexDonor+nPolynomial+1); + su2passivevector coeff_vec(nGlobalVertexDonor); + + const su2double* coord_i = target_geometry->node[point_target]->GetCoord(); + + /*--- Prepare target vector (one row of the target matrix). ---*/ + /*--- polynominal part ---*/ + int idx = 0; + if (usePolynomial) { + target_vec(idx++) = 1.0; // constant term + for (int iDim = 0; iDim < nDim; ++iDim) // linear terms + if (keepPolynomialRow[iDim]) // of which one may have been excluded + target_vec(idx++) = SU2_TYPE::GetValue(coord_i[iDim]); + } + /*--- RBF part ---*/ + for (auto iVertexDonor = 0ul; iVertexDonor < nGlobalVertexDonor; ++iVertexDonor) + target_vec(iVertexDonor+idx) = SU2_TYPE::GetValue(Get_RadialBasisValue(kindRBF, paramRBF, + PointsDistance(nDim, coord_i, DonorCoord[iVertexDonor]))); + + /*--- Multiply target vector by C_inv_trunc to obtain the interpolation coefficients. ---*/ + for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) { + coeff_vec(iVertex) = 0.0; + for (auto jVertex = 0ul; jVertex < target_vec.size(); ++jVertex) + coeff_vec(iVertex) += target_vec(jVertex) * C_inv_trunc(jVertex, iVertex); + } + + /*--- Count the number of donor points (non zero coefficients) for this target point. ---*/ + idx = 0; + for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) + idx += (fabs(coeff_vec(iVertex)) > eps); + + target_vertex->SetnDonorPoints(idx); + target_vertex->Allocate_DonorInfo(); + + int iSet = 0; + for (int iProcessor = 0, iTest = -1; iProcessor < nProcessor; ++iProcessor) { + const auto offset = iProcessor * MaxLocalVertex_Donor; + for (auto iVertex = 0ul; iVertex < Buffer_Receive_nVertex_Donor[iProcessor]; ++iVertex) + if (fabs(coeff_vec(++iTest)) > eps) { + auto point_donor = Buffer_Receive_GlobalPoint[offset + iVertex]; + target_vertex->SetInterpDonorProcessor(iSet, iProcessor); + target_vertex->SetInterpDonorPoint(iSet, point_donor); + target_vertex->SetDonorCoeff(iSet, coeff_vec(iTest)); + ++iSet; } - } - } // endif - } // endfor - - /*--- Memory management ---*/ - delete [] nLocalM_arr; - delete [] local_M; - delete [] Coord_i; - delete [] Coord_j; - delete [] calc_polynomial_check; - delete [] C_inv_trunc; - delete [] target_vec; - delete [] coeff_vec; - - if ( rank == MASTER_NODE ) { - delete global_M; - - if ( config[donorZone]->GetRadialBasisFunctionPolynomialOption() ) { - delete [] skip_row; - delete [] P; - delete Mp; - delete [] C_tmp; } - } - + assert(idx==iSet && "Error while setting donor point coefficients."); + + } // end target vertex loop + delete[] Buffer_Send_Coord; delete[] Buffer_Send_GlobalPoint; - delete[] Buffer_Receive_Coord; delete[] Buffer_Receive_GlobalPoint; - delete[] Buffer_Send_nVertex_Donor; - -#ifdef HAVE_MPI - if (rank == MASTER_NODE) - delete [] global_M_val_arr; -#endif - } // end loop over markers + } // end loop over interface markers + delete[] Buffer_Send_nVertex_Donor; delete[] Buffer_Receive_nVertex_Donor; -#ifdef HAVE_MPI - if (rank == MASTER_NODE) - delete [] Buffer_Recv_mark; -#endif } -void CRadialBasisFunction::Check_PolynomialTerms(int m, unsigned long n, const int *skip_row, su2double max_diff_tol_in, int *keep_row, int &n_polynomial, su2double *P) -{ - /*--- This routine keeps the AD information in P but the calculations are done in passivedouble as their purpose - is to decide which (if any) row of P to remove, and that process is not differentiable anyway. ---*/ - - int *write_row = NULL; - unsigned long iCount, jCount, n_rows; - passivedouble sum, max_diff, max_coeff, *coeff = NULL, max_diff_tol = SU2_TYPE::GetValue(max_diff_tol_in); - CSymmetricMatrix *PPT; - su2double *P_tmp = NULL; - - n_rows = 0; - for (int i=0; iInitialize((int)n_rows); +int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, + su2passivematrix &P) const { + const int m = P.rows(); + const int n = P.cols(); - iCount = 0; - for (int i = 0; i < m; i ++) { - if (skip_row[i] == 0) { - - jCount = 0; - for (int j = 0; j < m; j ++){ - if (skip_row[j] == 0) { - - sum = 0.0; - for (unsigned long k = 0; k < n; k ++) - { - sum += SU2_TYPE::GetValue(P[k*m+i]*P[k*m+j]); - } - PPT->Write((int)iCount, (int)jCount, sum); - - jCount++; - } - } - - iCount++; - } - } - - PPT->Invert(true); - - /*--- RHS for the least squares fit (vector of ones times P) ---*/ - coeff = new passivedouble [n_rows]; - iCount = 0; - for (int i = 0; i < m; i ++) { - if (skip_row[i] == 0) { - coeff[iCount] = 0; - for (unsigned long j = 0; j < n; j += 1) - { - coeff[iCount] += SU2_TYPE::GetValue(P[j*m+i]); - } - iCount++; + /*--- The first row of P is all ones and we do not care about it for this analysis. ---*/ + const int n_rows = m-1; + keep_row.resize(n_rows); + + /*--- By default assume points are not on a plane (all rows kept). ---*/ + int n_polynomial = n_rows; + for (int i = 0; i < n_rows; ++i) keep_row[i] = 1; + + /*--- Fit a plane through the points in P. ---*/ + + /*--- Compute P times its transpose and invert. ---*/ + CSymmetricMatrix PPT(n_rows); + + for (int i = 0; i < n_rows; ++i) + for (int j = i; j < n_rows; ++j) { + PPT(i,j) = 0.0; + for (int k = 0; k < n; ++k) PPT(i,j) += P(i+1,k) * P(j+1,k); } - } - - /*--- Multiply the RHS by the inverse thus obtaining the coefficients ---*/ - PPT->MatVecMult(coeff); - - /*--- Determine the maximum deviation of the points from the fitted plane ---*/ - max_diff = 0; - for (unsigned long i = 0; i < n; i ++) + PPT.Invert(true); + + /*--- RHS for the least squares fit (vector of ones times P). ---*/ + vector coeff(n_rows,0.0); + + for (int i = 0; i < n_rows; ++i) + for (int j = 0; j < n; ++j) + coeff[i] += P(i+1,j); + + /*--- Multiply the RHS by the inverse thus obtaining the coefficients. ---*/ + PPT.MatVecMult(coeff.data()); + + /*--- Determine the maximum deviation of the points from the fitted plane. ---*/ + passivedouble max_diff = 0.0; + + for (int j = 0; j < n; ++j) { - sum = 0; - iCount = 0; - for (int j = 0; j < m; j ++) - { - if (skip_row[j] == 0) { - sum += coeff[iCount]*SU2_TYPE::GetValue(P[i*m+j]); - iCount++; - } - } - /*--- 1.0 is the arbitrary constant we are assuming when fitting the plane ---*/ + passivedouble sum = 0.0; + for (int i = 0; i < n_rows; ++i) sum += coeff[i] * P(i+1,j); + + /*--- 1.0 is the arbitrary constant we are assuming when fitting + the plane, i.e. the vector of ones used to generate the RHS. ---*/ max_diff = max(abs(1.0-sum), max_diff); } - - for (unsigned long i=0; i max_coeff) iCount = i; - } + /*--- Find the max coeff and mark the corresponding row for removal. ---*/ + int remove_row = 0; + for (int i = 1; i < n_rows; ++i) + if (abs(coeff[i]) > abs(coeff[remove_row])) + remove_row = i; - for (unsigned long i=0; i 0.0 && "LLT failed, matrix is not SPD."); + Set(j, j, sqrt(sum)); - /*--- Decompose matrix ---*/ - for (j=0; j abs(pivot) ) { - pivot = decompose_vec[CalcIdxFull(i, j)]; + /*--- Decompose LU matrix. ---*/ + for (int j = 0; j < sz-1; ++j) { + /*--- Search for maximum pivot and interchange rows. ---*/ + passivedouble pivot = decomp(j,j); + int pivot_idx = j; + for (int i = j+1; i < sz; ++i) + if (abs(decomp(i,j)) > abs(pivot)) { + pivot = decomp(i,j); pivot_idx = i; - interchange_row = true; - } - } - - if ( interchange_row ) { - for ( k=0; k inv(sz*sz, 0.0); + + /*--- Compute L inverse. ---*/ + /*--- Solve smaller and smaller systems. ---*/ + for (int j = 0; j < sz; ++j) { + /*--- Forward substitution. ---*/ + inv[IdxSym(j,j)] = 1.0 / Get(j,j); + + for (int i = j+1; i < sz; ++i) { + passivedouble sum = 0.0; + for (int k = j; k < i; ++k) sum -= Get(i,k) * inv[IdxSym(k,j)]; + inv[IdxSym(i,j)] = sum / Get(i,i); + } + } // L inverse in inv + + /*--- Multiply inversed matrices overwrite val_vec. ---*/ + for (int j = 0; j < sz; ++j) + for (int i = j; i < sz; ++i) { + passivedouble sum = 0.0; + for (int k = i; k < sz; ++k) sum += inv[IdxSym(k,i)] * inv[IdxSym(k,j)]; + Set(i, j, sum); + } - inv_val_vec[CalcIdx(j, j)] = 1.0; + break; + } - /*--- Forward substitution ---*/ - for (i=j; i& inv = val_vec; - if (i==j) { - inv_val_vec[CalcIdx(i, i)] = 1/ReadL(i, i); - } - else { - sum = 0.0; - for (k=j; k().swap(decomp_vec); break; - - default: - throw invalid_argument("Default (LU) decomposition failed."); + } + default: assert(false && "Default (LU) decomposition failed."); } - decompose_vec = NULL; - decomposed = none; - inversed = true; + decomposed = NONE; #endif } -void CSymmetricMatrix::CalcInv_sptri() +void CSymmetricMatrix::CalcInv_sytri() { #ifdef HAVE_LAPACK char uplo = 'L'; - int info, *ipiv = new int [sz]; - passivedouble *work = new passivedouble [sz]; + int info; + perm_vec.resize(sz); // ipiv array + + /*--- Query the optimum work size. ---*/ + int query = -1; passivedouble tmp; + dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), &tmp, &query, &info); + query = static_cast(tmp); + decomp_vec.resize(query); // work array + + /*--- Factorize and invert. ---*/ + dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &query, &info); + if (info!=0) SU2_MPI::Error("LDLT factorization failed.", CURRENT_FUNCTION); + dsytri_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &info); + if (info!=0) SU2_MPI::Error("Inversion with LDLT factorization failed.", CURRENT_FUNCTION); + + decomposed = NONE; +#endif +} - dsptrf_(&uplo, &sz, val_vec, ipiv, &info); - dsptri_(&uplo, &sz, val_vec, ipiv, work, &info); +void CSymmetricMatrix::CalcInv_potri() +{ +#ifdef HAVE_LAPACK + char uplo = 'L'; + int info; - delete [] ipiv; - delete [] work; + dpotrf_(&uplo, &sz, val_vec.data(), &sz, &info); + if (info!=0) SU2_MPI::Error("LLT factorization failed.", CURRENT_FUNCTION); + dpotri_(&uplo, &sz, val_vec.data(), &sz, &info); + if (info!=0) SU2_MPI::Error("Inversion with LLT factorization failed.", CURRENT_FUNCTION); - if(decompose_vec) delete[] decompose_vec; - decompose_vec = NULL; - decomposed = none; - inversed = true; + decomposed = NONE; #endif } void CSymmetricMatrix::Invert(const bool is_spd) { #ifdef HAVE_LAPACK - CalcInv_sptri(); + if(is_spd) CalcInv_potri(); + else CalcInv_sytri(); #else if(!is_spd) LUDecompose(); - else CholeskyDecompose(true); - CalcInv(true); + else CholeskyDecompose(); + CalcInv(); #endif } -void CSymmetricMatrix::MatVecMult(passivedouble *v) +void CSymmetricMatrix::MatVecMult(passivedouble *v) const { passivedouble *tmp_res = new passivedouble [sz]; for (int i=0; i=sz || j<0 || j>=sz) { - throw out_of_range("Index to access matrix out of bounds."); } -} -void CSymmetricMatrix::Write(int i, int j, const su2double& val) -{ - CheckBounds(i,j); - val_vec[CalcIdx(i, j)] = SU2_TYPE::GetValue(val); -} - -passivedouble CSymmetricMatrix::Read(int i, int j) -{ - CheckBounds(i,j); - return val_vec[CalcIdx(i, j)]; -} - -passivedouble CSymmetricMatrix::ReadL(int i, int j) -{ - passivedouble *p = NULL; - - CheckBounds(i,j); - - if (decompose_vec) { p = decompose_vec; } - else { p = val_vec; } - - switch (decomposed) { - case cholesky: - if (i>=j) return p[CalcIdx(i, j)]; - else return 0.0; - - case lu: - if (i>j) return p[CalcIdxFull(i, j)]; - else return passivedouble(i==j); - - default: - throw invalid_argument("Matrix not decomposed yet or results have been deleted."); - } -} - -passivedouble CSymmetricMatrix::ReadU(int i, int j) -{ - passivedouble *p = NULL; - - CheckBounds(i,j); - - if (decompose_vec){ p = decompose_vec; } - else {p = val_vec;} - - switch (decomposed) { - case cholesky: - return 0.0; - - case lu: - if (j>=i) return p[CalcIdxFull(j, i)]; - else return 0.0; - - default: - throw invalid_argument("Matrix not decomposed yet or results have been deleted."); - } -} - -double CSymmetricMatrix::ReadInv(int i, int j) -{ - passivedouble *p = NULL; - - CheckBounds(i,j); - - if (inversed) { - if (inv_val_vec) { p = inv_val_vec; } - else { p = val_vec; } - - return p[CalcIdx(i, j)]; - } - else { - throw invalid_argument("Matrix inverse not calculated yet."); - } } From ff8c57f96f8313e9815d108003711b0396b75576 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 17 Mar 2020 14:35:56 +0000 Subject: [PATCH 021/112] prune tolerance for sparse RBF interpolation matrix --- Common/include/CConfig.hpp | 8 ++++- Common/src/CConfig.cpp | 7 ++-- Common/src/interpolation_structure.cpp | 37 ++++++++++++++------ TestCases/fea_fsi/Airfoil_RBF/config.cfg | 2 +- TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg | 3 +- TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg | 14 +++++--- 6 files changed, 50 insertions(+), 21 deletions(-) diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp index e316b4cb8d01..877f073cc08e 100644 --- a/Common/include/CConfig.hpp +++ b/Common/include/CConfig.hpp @@ -990,7 +990,8 @@ class CConfig { bool ConservativeInterpolation; /*!< \brief Conservative approach for non matching mesh interpolation. */ unsigned short Kind_RadialBasisFunction; /*!< \brief type of radial basis function to use for radial basis FSI. */ bool RadialBasisFunction_PolynomialOption; /*!< \brief Option of whether to include polynomial terms in Radial Basis Function Interpolation or not. */ - su2double RadialBasisFunction_Parameter; /*!< \brief Radial basis function parameter. */ + su2double RadialBasisFunction_Parameter; /*!< \brief Radial basis function parameter (radius). */ + su2double RadialBasisFunction_PruneTol; /*!< \brief Tolerance to prune the RBF interpolation matrix. */ bool Prestretch; /*!< \brief Read a reference geometry for optimization purposes. */ string Prestretch_FEMFileName; /*!< \brief File name for reference geometry. */ string FEA_FileName; /*!< \brief File name for element-based properties. */ @@ -8787,6 +8788,11 @@ class CConfig { */ su2double GetRadialBasisFunctionParameter(void) const { return RadialBasisFunction_Parameter; } + /*! + * \brief Get the tolerance used to prune the interpolation matrix (making it sparser). + */ + su2double GetRadialBasisFunctionPruneTol(void) const { return RadialBasisFunction_PruneTol; } + /*! * \brief Get the kind of inlet face interpolation function to use. */ diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index 84dd8cef7eae..2899aab3328f 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -2478,8 +2478,11 @@ void CConfig::SetConfig_Options() { * Options: NO, YES \ingroup Config */ addBoolOption("RADIAL_BASIS_FUNCTION_POLYNOMIAL_TERM", RadialBasisFunction_PolynomialOption, true); - /* DESCRIPTION: Radius for radial basis function */ - addDoubleOption("RADIAL_BASIS_FUNCTION_PARAMETER", RadialBasisFunction_Parameter, 1); + /* DESCRIPTION: Radius for radial basis function. */ + addDoubleOption("RADIAL_BASIS_FUNCTION_PARAMETER", RadialBasisFunction_Parameter, 1.0); + + /* DESCRIPTION: Tolerance to prune small coefficients from the RBF interpolation matrix. */ + addDoubleOption("RADIAL_BASIS_FUNCTION_PRUNE_TOLERANCE", RadialBasisFunction_PruneTol, 1e-6); /*!\par INLETINTERPOLATION \n * DESCRIPTION: Type of spanwise interpolation to use for the inlet face. \n OPTIONS: see \link Inlet_SpanwiseInterpolation_Map \endlink diff --git a/Common/src/interpolation_structure.cpp b/Common/src/interpolation_structure.cpp index 000a55cf9e06..421d3d94e0e0 100644 --- a/Common/src/interpolation_structure.cpp +++ b/Common/src/interpolation_structure.cpp @@ -2697,8 +2697,9 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { /*--- RBF options. ---*/ const unsigned short kindRBF = config[donorZone]->GetKindRadialBasisFunction(); - const su2double paramRBF = config[donorZone]->GetRadialBasisFunctionParameter(); const bool usePolynomial = config[donorZone]->GetRadialBasisFunctionPolynomialOption(); + const su2double paramRBF = config[donorZone]->GetRadialBasisFunctionParameter(); + const su2double pruneTol = config[donorZone]->GetRadialBasisFunctionPruneTol(); const passivedouble eps = numeric_limits::epsilon(); const su2double interfaceCoordTol = 1e6 * eps; @@ -2792,7 +2793,7 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { global_M.Invert(false); break; } - /*--- Calculate C_inv_trunc. ---*/ + /*--- Compute C_inv_trunc. ---*/ if (usePolynomial) { /*--- Fill P matrix (P for points, with an extra top row of ones). ---*/ @@ -2807,7 +2808,7 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { /*--- Check if points lie on a plane and remove one coordinate from P if so. ---*/ nPolynomial = CheckPolynomialTerms(interfaceCoordTol, keepPolynomialRow, P); - /*--- Calculate Mp = (P * M^-1 * P^T)^-1 ---*/ + /*--- Compute Mp = (P * M^-1 * P^T)^-1 ---*/ CSymmetricMatrix Mp(nPolynomial+1); su2passivematrix tmp; @@ -2820,12 +2821,12 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { } Mp.Invert(false); // Mp = Mp^-1 - /*--- Calculate M_p * P * M^-1, the top part of C_inv_trunc. ---*/ + /*--- Compute M_p * P * M^-1, the top part of C_inv_trunc. ---*/ Mp.MatMatMult('L', P, tmp); su2passivematrix C_inv_top; global_M.MatMatMult('R', tmp, C_inv_top); - /*--- Calculate tmp = (I - P^T * M_p * P * M^-1), part of the bottom part of + /*--- Compute tmp = (I - P^T * M_p * P * M^-1), part of the bottom part of C_inv_trunc. Note that most of the product is known from the top part. ---*/ tmp.resize(nGlobalVertexDonor, nGlobalVertexDonor); @@ -2837,7 +2838,7 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { tmp(i,i) += 1.0; // identity part } - /*--- Calculate M^-1 * (I - P^T * M_p * P * M^-1), finalize bottom of C_inv_trunc. ---*/ + /*--- Compute M^-1 * (I - P^T * M_p * P * M^-1), finalize bottom of C_inv_trunc. ---*/ global_M.MatMatMult('L', tmp, C_inv_trunc); /*--- Merge top and bottom of C_inv_trunc. ---*/ @@ -2901,18 +2902,32 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { target_vec(iVertexDonor+idx) = SU2_TYPE::GetValue(Get_RadialBasisValue(kindRBF, paramRBF, PointsDistance(nDim, coord_i, DonorCoord[iVertexDonor]))); - /*--- Multiply target vector by C_inv_trunc to obtain the interpolation coefficients. ---*/ + /*--- Multiply target vector by C_inv_trunc to obtain the interpolation coefficients. + Simultaneously determine a reference for dropping small coefficients. ---*/ + passivedouble coeffRef = 0.0; for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) { coeff_vec(iVertex) = 0.0; for (auto jVertex = 0ul; jVertex < target_vec.size(); ++jVertex) coeff_vec(iVertex) += target_vec(jVertex) * C_inv_trunc(jVertex, iVertex); + coeffRef = max(coeffRef, fabs(coeff_vec(iVertex))); } + coeffRef *= SU2_TYPE::GetValue(pruneTol); - /*--- Count the number of donor points (non zero coefficients) for this target point. ---*/ + /*--- Prune and count the number of donor points for this target point. ---*/ idx = 0; - for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) - idx += (fabs(coeff_vec(iVertex)) > eps); + passivedouble coeffSum = 0.0; + for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) { + if (fabs(coeff_vec(iVertex)) > coeffRef) { + coeffSum += coeff_vec(iVertex); + ++idx; + } + else coeff_vec(iVertex) = 0.0; + } + /*--- Correct remaining coefficients, sum must be 1 for conservation. ---*/ + passivedouble correction = 1.0 / coeffSum; + for (auto i = 0ul; i < nGlobalVertexDonor; ++i) coeff_vec(i) *= correction; + /*--- Allocate and set donor information for this target point. ---*/ target_vertex->SetnDonorPoints(idx); target_vertex->Allocate_DonorInfo(); @@ -2920,7 +2935,7 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { for (int iProcessor = 0, iTest = -1; iProcessor < nProcessor; ++iProcessor) { const auto offset = iProcessor * MaxLocalVertex_Donor; for (auto iVertex = 0ul; iVertex < Buffer_Receive_nVertex_Donor[iProcessor]; ++iVertex) - if (fabs(coeff_vec(++iTest)) > eps) { + if (fabs(coeff_vec(++iTest)) > 0.0) { auto point_donor = Buffer_Receive_GlobalPoint[offset + iVertex]; target_vertex->SetInterpDonorProcessor(iSet, iProcessor); target_vertex->SetInterpDonorPoint(iSet, point_donor); diff --git a/TestCases/fea_fsi/Airfoil_RBF/config.cfg b/TestCases/fea_fsi/Airfoil_RBF/config.cfg index 437ca3394f55..ddcf42c9b8e4 100755 --- a/TestCases/fea_fsi/Airfoil_RBF/config.cfg +++ b/TestCases/fea_fsi/Airfoil_RBF/config.cfg @@ -3,7 +3,7 @@ % Case description: 2D airfoil FSI with radial basis function interp. % % Institution: Imperial College London % % Date: 2015.08.12 % -% File Version 6.2 "Falcon" % +% File Version 7.0.2 "Blackbird" % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% SOLVER = MULTIPHYSICS diff --git a/TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg b/TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg index 921a81ac1da0..99e3530ccdd3 100644 --- a/TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg +++ b/TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg @@ -2,7 +2,7 @@ % SU2 configuration file % % Case description: 2D airfoil FSI with radial basis function interp. % % Institution: Imperial College London % -% File Version 7.0.2 "Blackbird" % +% File Version 7.0.2 "Blackbird" % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Interface options ---------------------------------------------------- % @@ -12,6 +12,7 @@ CONSERVATIVE_INTERPOLATION = YES KIND_RADIAL_BASIS_FUNCTION = WENDLAND_C2 RADIAL_BASIS_FUNCTION_PARAMETER = 0.015 RADIAL_BASIS_FUNCTION_POLYNOMIAL_TERM = YES +RADIAL_BASIS_FUNCTION_PRUNE_TOLERANCE = 1e-6 % % Physics -------------------------------------------------------------- % SOLVER= ELASTICITY diff --git a/TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg b/TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg index 183cb402f3fc..6d293bd5506e 100644 --- a/TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg +++ b/TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg @@ -2,7 +2,7 @@ % SU2 configuration file % % Case description: 2D airfoil FSI with radial basis function interp. % % Institution: Imperial College London % -% File Version 7.0.2 "Blackbird" % +% File Version 7.0.2 "Blackbird" % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Interface options ---------------------------------------------------- % @@ -20,16 +20,20 @@ KIND_INTERPOLATION = RADIAL_BASIS_FUNCTION % orders) and you have issues, break up the interface or use "NO" for % consistent interpolation. CONSERVATIVE_INTERPOLATION = YES -% Wendland provides good results and produces a nice diagonally dominant -% interpolation kernel, other options: +% Wendland is usually the best option due to sparser and better +% conditioned interpolation kernel, other options: % INV_MULTI_QUADRIC; GAUSSIAN; THIN_PLATE_SPLINE; MULTI_QUADRIC KIND_RADIAL_BASIS_FUNCTION = WENDLAND_C2 % The radius in meters, 2 times the largest cell size on the interface is % a good compromise between accuracy and condition number of the kernel. RADIAL_BASIS_FUNCTION_PARAMETER = 0.015 -% Recommended as it recovers rigid body motion, only requires a few more -% matrix products... feel free to explore though! +% Polynomial terms to recover rigid body motion, this requires slightly +% more computation and reduces the sparsity of the interpolation matrix. RADIAL_BASIS_FUNCTION_POLYNOMIAL_TERM = YES +% The number of donor points per target point is reduced by prunning away +% small interpolation coefficients (making the interp. matrix sparse). +% This tolerance is relative to the maximum absolute coefficient. +RADIAL_BASIS_FUNCTION_PRUNE_TOLERANCE = 1e-6 % % Physics -------------------------------------------------------------- % SOLVER= EULER From 88961123db636096e963162e2c43a0391dfa03a9 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 17 Mar 2020 19:06:33 +0000 Subject: [PATCH 022/112] RBF interpolation in parallel --- Common/src/interpolation_structure.cpp | 301 +++++++++++++++---------- 1 file changed, 187 insertions(+), 114 deletions(-) diff --git a/Common/src/interpolation_structure.cpp b/Common/src/interpolation_structure.cpp index 421d3d94e0e0..414729f9ff55 100644 --- a/Common/src/interpolation_structure.cpp +++ b/Common/src/interpolation_structure.cpp @@ -198,7 +198,7 @@ void CInterpolator::Collect_VertexInfo(bool faces, int markDonor, int markTarget unsigned short iDim; /* Only needed if face data is also collected */ - su2double *Normal; + const su2double *Normal = nullptr; for (iVertex = 0; iVertex < MaxLocalVertex_Donor; iVertex++) { Buffer_Send_GlobalPoint[iVertex] = -1; @@ -220,7 +220,7 @@ void CInterpolator::Collect_VertexInfo(bool faces, int markDonor, int markTarget Buffer_Send_Coord[nLocalVertex_Donor*nDim+iDim] = donor_geometry->node[iPointDonor]->GetCoord(iDim); if (faces) { - Normal = donor_geometry->vertex[markDonor][iVertexDonor]->GetNormal(); + Normal = donor_geometry->vertex[markDonor][iVertexDonor]->GetNormal(); for (iDim = 0; iDim < nDim; iDim++) Buffer_Send_Normal[nLocalVertex_Donor*nDim+iDim] = Normal[iDim]; } @@ -2703,17 +2703,24 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { const passivedouble eps = numeric_limits::epsilon(); const su2double interfaceCoordTol = 1e6 * eps; + const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2; const int nDim = donor_geometry->GetnDim(); const int nProcessor = size; Buffer_Send_nVertex_Donor = new unsigned long [1]; Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; - /*--- Cycle over nMarkersInt interface to determine communication pattern. ---*/ - - const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2; + /*--- Process interface patches in parallel, fetch all donor point coordinates, + * then distribute interpolation matrix computation over ranks and threads. + * To avoid repeating calls to Collect_VertexInfo we also save the global + * indices of the donor points and the mpi rank index that owns them. ---*/ + vector DonorCoordinates(nMarkerInt); + vector > DonorGlobalPoint(nMarkerInt); + vector > DonorProcessor(nMarkerInt); + vector AssignedProcessor(nMarkerInt,-1); + vector TotalWork(nProcessor,0); - for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { + for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; ++iMarkerInt) { /*--- On the donor side: find the tag of the boundary sharing the interface. ---*/ int mark_donor = Find_InterfaceMarker(config[donorZone], iMarkerInt); @@ -2730,9 +2737,8 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { /*--- If the zone does not contain the interface continue to the next pair of markers. ---*/ if(target_check == -1 || donor_check == -1) continue; - unsigned long nVertexDonor = 0, nVertexTarget = 0; - if(mark_donor != -1) nVertexDonor = donor_geometry->GetnVertex( mark_donor ); - if(mark_target != -1) nVertexTarget = target_geometry->GetnVertex( mark_target ); + unsigned long nVertexDonor = 0; + if(mark_donor != -1) nVertexDonor = donor_geometry->GetnVertex(mark_donor); /*--- Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor. ---*/ Determine_ArraySize(false, mark_donor, mark_target, nVertexDonor, nDim); @@ -2749,132 +2755,205 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { Collect_VertexInfo(false, mark_donor, mark_target, nVertexDonor, nDim); - /*--- Compress the gathered donor point coordinates to simplify calculations. ---*/ - su2activematrix DonorCoord(nGlobalVertexDonor, nDim); + /*--- Compresses the gathered donor point information to simplify computation. ---*/ + auto& DonorCoord = DonorCoordinates[iMarkerInt-1]; + auto& DonorPoint = DonorGlobalPoint[iMarkerInt-1]; + auto& DonorProc = DonorProcessor[iMarkerInt-1]; + DonorCoord.resize(nGlobalVertexDonor, nDim); + DonorPoint.resize(nGlobalVertexDonor); + DonorProc.resize(nGlobalVertexDonor); auto iCount = 0ul; for (int iProcessor = 0; iProcessor < nProcessor; ++iProcessor) { - auto offset = iProcessor * MaxLocalVertex_Donor * nDim; + auto offset = iProcessor * MaxLocalVertex_Donor; for (auto iVertex = 0ul; iVertex < Buffer_Receive_nVertex_Donor[iProcessor]; ++iVertex) { for (int iDim = 0; iDim < nDim; ++iDim) - DonorCoord(iCount,iDim) = Buffer_Receive_Coord[offset + iVertex*nDim + iDim]; + DonorCoord(iCount,iDim) = Buffer_Receive_Coord[(offset+iVertex)*nDim + iDim]; + DonorPoint[iCount] = Buffer_Receive_GlobalPoint[offset+iVertex]; + DonorProc[iCount] = iProcessor; ++iCount; } } assert((iCount == nGlobalVertexDonor) && "Global donor point count mismatch."); - /*--- The master node prepares the global interpolation kernel. ---*/ - int nPolynomial = -1; - vector keepPolynomialRow(nDim,1); + delete[] Buffer_Send_Coord; + delete[] Buffer_Send_GlobalPoint; + delete[] Buffer_Receive_Coord; + delete[] Buffer_Receive_GlobalPoint; - su2passivematrix C_inv_trunc; + /*--- Static work scheduling over ranks based on which one has less work currently. ---*/ + int iProcessor = 0; + for (int i = 1; i < nProcessor; ++i) + if (TotalWork[i] < TotalWork[iProcessor]) iProcessor = i; - if (rank==MASTER_NODE) { + TotalWork[iProcessor] += pow(nGlobalVertexDonor,3); // based on matrix inversion. - /*--- Populate interpolation kernel. ---*/ - CSymmetricMatrix global_M(nGlobalVertexDonor); + AssignedProcessor[iMarkerInt-1] = iProcessor; - for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) - for (auto jVertex = iVertex; jVertex < nGlobalVertexDonor; ++jVertex) - global_M(iVertex, jVertex) = SU2_TYPE::GetValue(Get_RadialBasisValue(kindRBF, paramRBF, - PointsDistance(nDim, DonorCoord[iVertex], DonorCoord[jVertex]))); + } - /*--- Invert M matrix (operation is in-place). ---*/ - switch (kindRBF) { - /*--- Basis functions that make M positive definite. ---*/ - case WENDLAND_C2: - case INV_MULTI_QUADRIC: - case GAUSSIAN: - global_M.Invert(true); break; + /*--- Compute the interpolation matrices for each patch of coordinates + * assigned to the rank. Subdivide work further by threads. ---*/ + vector nPolynomialVec(nMarkerInt,-1); + vector > keepPolynomialRowVec(nMarkerInt, vector(nDim,1)); + vector CinvTrucVec(nMarkerInt); - /*--- Basis functions that make M semi-positive definite. ---*/ - case THIN_PLATE_SPLINE: - case MULTI_QUADRIC: - global_M.Invert(false); break; - } + SU2_OMP_PARALLEL_(for schedule(dynamic,1)) + for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; ++iMarkerInt) { - /*--- Compute C_inv_trunc. ---*/ - if (usePolynomial) { + if (rank != AssignedProcessor[iMarkerInt]) continue; // #notmyjob - /*--- Fill P matrix (P for points, with an extra top row of ones). ---*/ - su2passivematrix P(1+nDim, nGlobalVertexDonor); + const auto& DonorCoord = DonorCoordinates[iMarkerInt]; + const auto nGlobalVertexDonor = DonorCoord.rows(); - for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; iVertex++) { - P(0, iVertex) = 1.0; - for (int iDim = 0; iDim < nDim; ++iDim) - P(1+iDim, iVertex) = SU2_TYPE::GetValue(DonorCoord(iVertex, iDim)); - } + auto& C_inv_trunc = CinvTrucVec[iMarkerInt]; + auto& nPolynomial = nPolynomialVec[iMarkerInt]; + auto& keepPolynomialRow = keepPolynomialRowVec[iMarkerInt]; - /*--- Check if points lie on a plane and remove one coordinate from P if so. ---*/ - nPolynomial = CheckPolynomialTerms(interfaceCoordTol, keepPolynomialRow, P); + /*--- Populate interpolation kernel. ---*/ + CSymmetricMatrix global_M(nGlobalVertexDonor); - /*--- Compute Mp = (P * M^-1 * P^T)^-1 ---*/ - CSymmetricMatrix Mp(nPolynomial+1); + for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) + for (auto jVertex = iVertex; jVertex < nGlobalVertexDonor; ++jVertex) + global_M(iVertex, jVertex) = SU2_TYPE::GetValue(Get_RadialBasisValue(kindRBF, paramRBF, + PointsDistance(nDim, DonorCoord[iVertex], DonorCoord[jVertex]))); - su2passivematrix tmp; - global_M.MatMatMult('R', P, tmp); // tmp = P * M^-1 + /*--- Invert M matrix (operation is in-place). ---*/ + switch (kindRBF) { + /*--- Basis functions that make M positive definite. ---*/ + case WENDLAND_C2: + case INV_MULTI_QUADRIC: + case GAUSSIAN: + global_M.Invert(true); break; - for (int i = 0; i <= nPolynomial; ++i) // Mp = tmp * P - for (int j = i; j <= nPolynomial; ++j) { - Mp(i,j) = 0.0; - for (auto k = 0ul; k < nGlobalVertexDonor; ++k) Mp(i,j) += tmp(i,k) * P(j,k); - } - Mp.Invert(false); // Mp = Mp^-1 + /*--- Basis functions that make M semi-positive definite. ---*/ + case THIN_PLATE_SPLINE: + case MULTI_QUADRIC: + global_M.Invert(false); break; + } - /*--- Compute M_p * P * M^-1, the top part of C_inv_trunc. ---*/ - Mp.MatMatMult('L', P, tmp); - su2passivematrix C_inv_top; - global_M.MatMatMult('R', tmp, C_inv_top); + /*--- Compute C_inv_trunc. ---*/ + if (usePolynomial) { - /*--- Compute tmp = (I - P^T * M_p * P * M^-1), part of the bottom part of - C_inv_trunc. Note that most of the product is known from the top part. ---*/ - tmp.resize(nGlobalVertexDonor, nGlobalVertexDonor); + /*--- Fill P matrix (P for points, with an extra top row of ones). ---*/ + su2passivematrix P(1+nDim, nGlobalVertexDonor); - for (auto i = 0ul; i < nGlobalVertexDonor; ++i) { - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) { - tmp(i,j) = 0.0; - for (int k = 0; k <= nPolynomial; ++k) tmp(i,j) -= P(k,i) * C_inv_top(k,j); - } - tmp(i,i) += 1.0; // identity part + for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; iVertex++) { + P(0, iVertex) = 1.0; + for (int iDim = 0; iDim < nDim; ++iDim) + P(1+iDim, iVertex) = SU2_TYPE::GetValue(DonorCoord(iVertex, iDim)); + } + + /*--- Check if points lie on a plane and remove one coordinate from P if so. ---*/ + nPolynomial = CheckPolynomialTerms(interfaceCoordTol, keepPolynomialRow, P); + + /*--- Compute Mp = (P * M^-1 * P^T)^-1 ---*/ + CSymmetricMatrix Mp(nPolynomial+1); + + su2passivematrix tmp; + global_M.MatMatMult('R', P, tmp); // tmp = P * M^-1 + + for (int i = 0; i <= nPolynomial; ++i) // Mp = tmp * P + for (int j = i; j <= nPolynomial; ++j) { + Mp(i,j) = 0.0; + for (auto k = 0ul; k < nGlobalVertexDonor; ++k) Mp(i,j) += tmp(i,k) * P(j,k); } + Mp.Invert(false); // Mp = Mp^-1 - /*--- Compute M^-1 * (I - P^T * M_p * P * M^-1), finalize bottom of C_inv_trunc. ---*/ - global_M.MatMatMult('L', tmp, C_inv_trunc); + /*--- Compute M_p * P * M^-1, the top part of C_inv_trunc. ---*/ + Mp.MatMatMult('L', P, tmp); + su2passivematrix C_inv_top; + global_M.MatMatMult('R', tmp, C_inv_top); - /*--- Merge top and bottom of C_inv_trunc. ---*/ - tmp = move(C_inv_trunc); - C_inv_trunc.resize(1+nPolynomial+nGlobalVertexDonor, nGlobalVertexDonor); - memcpy(C_inv_trunc[0], C_inv_top.data(), C_inv_top.size()*sizeof(passivedouble)); - memcpy(C_inv_trunc[1+nPolynomial], tmp.data(), tmp.size()*sizeof(passivedouble)); + /*--- Compute tmp = (I - P^T * M_p * P * M^-1), part of the bottom part of + C_inv_trunc. Note that most of the product is known from the top part. ---*/ + tmp.resize(nGlobalVertexDonor, nGlobalVertexDonor); + + for (auto i = 0ul; i < nGlobalVertexDonor; ++i) { + for (auto j = 0ul; j < nGlobalVertexDonor; ++j) { + tmp(i,j) = 0.0; + for (int k = 0; k <= nPolynomial; ++k) tmp(i,j) -= P(k,i) * C_inv_top(k,j); + } + tmp(i,i) += 1.0; // identity part } - else { - /*--- No polynomial term used in the interpolation, C_inv_trunc = M^-1. ---*/ - C_inv_trunc.resize(nGlobalVertexDonor, nGlobalVertexDonor); - for (auto i = 0ul; i < nGlobalVertexDonor; ++i) - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) - C_inv_trunc(i,j) = global_M(i,j); + /*--- Compute M^-1 * (I - P^T * M_p * P * M^-1), finalize bottom of C_inv_trunc. ---*/ + global_M.MatMatMult('L', tmp, C_inv_trunc); - } // end usePolynomial + /*--- Merge top and bottom of C_inv_trunc. ---*/ + tmp = move(C_inv_trunc); + C_inv_trunc.resize(1+nPolynomial+nGlobalVertexDonor, nGlobalVertexDonor); + memcpy(C_inv_trunc[0], C_inv_top.data(), C_inv_top.size()*sizeof(passivedouble)); + memcpy(C_inv_trunc[1+nPolynomial], tmp.data(), tmp.size()*sizeof(passivedouble)); + } + else { + /*--- No polynomial term used in the interpolation, C_inv_trunc = M^-1. ---*/ + + C_inv_trunc.resize(nGlobalVertexDonor, nGlobalVertexDonor); + for (auto i = 0ul; i < nGlobalVertexDonor; ++i) + for (auto j = 0ul; j < nGlobalVertexDonor; ++j) + C_inv_trunc(i,j) = global_M(i,j); + + } // end usePolynomial + + } - } // end (rank == MASTER_NODE) + /*--- Final loop over interface markers to compute the interpolation coefficients. ---*/ - /*--- Broadcast C_inv_trunc to all nodes, each node then - computes its part of the interpolation matrix. ---*/ + for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; iMarkerInt++) { - SU2_MPI::Bcast(&nPolynomial, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - SU2_MPI::Bcast(keepPolynomialRow.data(), nDim, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); + /*--- Identify the rank that computed the interpolation matrix for this marker. ---*/ + const int iProcessor = AssignedProcessor[iMarkerInt]; + /*--- If no processor was assigned to work, the zone does not contain the interface. ---*/ + if (iProcessor < 0) continue; - if (rank != MASTER_NODE) - C_inv_trunc.resize(nGlobalVertexDonor+nPolynomial+1, nGlobalVertexDonor); + /*--- Setup target information. ---*/ + const int mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt+1); + unsigned long nVertexTarget = 0; + if(mark_target != -1) nVertexTarget = target_geometry->GetnVertex(mark_target); + + /*--- Set references to donor information. ---*/ + auto& DonorCoord = DonorCoordinates[iMarkerInt]; + auto& DonorPoint = DonorGlobalPoint[iMarkerInt]; + auto& DonorProc = DonorProcessor[iMarkerInt]; + + auto& C_inv_trunc = CinvTrucVec[iMarkerInt]; + auto& nPolynomial = nPolynomialVec[iMarkerInt]; + auto& keepPolynomialRow = keepPolynomialRowVec[iMarkerInt]; + + const auto nGlobalVertexDonor = DonorCoord.rows(); #ifdef HAVE_MPI - /*--- MPI wrapper not used due to passive double. ---*/ - MPI_Bcast(C_inv_trunc.data(), C_inv_trunc.size(), MPI_DOUBLE, MASTER_NODE, MPI_COMM_WORLD); + /*--- For simplicity, broadcast small information about the interpolation matrix. ---*/ + SU2_MPI::Bcast(&nPolynomial, 1, MPI_INT, iProcessor, MPI_COMM_WORLD); + SU2_MPI::Bcast(keepPolynomialRow.data(), nDim, MPI_INT, iProcessor, MPI_COMM_WORLD); + + /*--- Send C_inv_trunc only to the ranks that need it (those with target points), + * partial broadcast. MPI wrapper not used due to passive double. ---*/ + vector allNumVertex(nProcessor); + SU2_MPI::Allgather(&nVertexTarget, 1, MPI_UNSIGNED_LONG, + allNumVertex.data(), 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + + if (rank == iProcessor) { + for (int jProcessor = 0; jProcessor < nProcessor; ++jProcessor) + if ((jProcessor != iProcessor) && (allNumVertex[jProcessor] != 0)) + MPI_Send(C_inv_trunc.data(), C_inv_trunc.size(), + MPI_DOUBLE, jProcessor, 0, MPI_COMM_WORLD); + } + else if (nVertexTarget != 0) { + C_inv_trunc.resize(1+nPolynomial+nGlobalVertexDonor, nGlobalVertexDonor); + MPI_Recv(C_inv_trunc.data(), C_inv_trunc.size(), MPI_DOUBLE, + iProcessor, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + } #endif - /*--- Compute H matrix. ---*/ + /*--- Compute H matrix, distributing target points over the threads in the rank. ---*/ + SU2_OMP_PARALLEL + { + su2passivevector target_vec(nGlobalVertexDonor+nPolynomial+1); + su2passivevector coeff_vec(nGlobalVertexDonor); + SU2_OMP_FOR_DYN(roundUpDiv(nVertexTarget, 2*omp_get_max_threads())) for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget++) { auto target_vertex = target_geometry->vertex[mark_target][iVertexTarget]; @@ -2883,9 +2962,6 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { /*--- If not domain point move to next. ---*/ if (!target_geometry->node[point_target]->GetDomain()) continue; - su2passivevector target_vec(nGlobalVertexDonor+nPolynomial+1); - su2passivevector coeff_vec(nGlobalVertexDonor); - const su2double* coord_i = target_geometry->node[point_target]->GetCoord(); /*--- Prepare target vector (one row of the target matrix). ---*/ @@ -2930,27 +3006,24 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { /*--- Allocate and set donor information for this target point. ---*/ target_vertex->SetnDonorPoints(idx); target_vertex->Allocate_DonorInfo(); - - int iSet = 0; - for (int iProcessor = 0, iTest = -1; iProcessor < nProcessor; ++iProcessor) { - const auto offset = iProcessor * MaxLocalVertex_Donor; - for (auto iVertex = 0ul; iVertex < Buffer_Receive_nVertex_Donor[iProcessor]; ++iVertex) - if (fabs(coeff_vec(++iTest)) > 0.0) { - auto point_donor = Buffer_Receive_GlobalPoint[offset + iVertex]; - target_vertex->SetInterpDonorProcessor(iSet, iProcessor); - target_vertex->SetInterpDonorPoint(iSet, point_donor); - target_vertex->SetDonorCoeff(iSet, coeff_vec(iTest)); - ++iSet; - } + idx = 0; + for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) { + if (fabs(coeff_vec(iVertex)) > 0.0) { + target_vertex->SetInterpDonorProcessor(idx, DonorProc[iVertex]); + target_vertex->SetInterpDonorPoint(idx, DonorPoint[iVertex]); + target_vertex->SetDonorCoeff(idx, coeff_vec(iVertex)); + ++idx; + } } - assert(idx==iSet && "Error while setting donor point coefficients."); } // end target vertex loop + } // end SU2_OMP_PARALLEL - delete[] Buffer_Send_Coord; - delete[] Buffer_Send_GlobalPoint; - delete[] Buffer_Receive_Coord; - delete[] Buffer_Receive_GlobalPoint; + /*--- Delete global data that will no longer be used. ---*/ + DonorCoord.resize(0,0); + vector().swap(DonorPoint); + vector().swap(DonorProc); + C_inv_trunc.resize(0,0); } // end loop over interface markers From 47ad5d13ebc810a295b77cb02646c166f25f8ba7 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 17 Mar 2020 19:07:52 +0000 Subject: [PATCH 023/112] adjust some settings in regression case to make it closer to reference --- TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg | 2 +- TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg b/TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg index 99e3530ccdd3..eff2d6ed2b36 100644 --- a/TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg +++ b/TestCases/fea_fsi/Airfoil_RBF/configFEA.cfg @@ -12,7 +12,7 @@ CONSERVATIVE_INTERPOLATION = YES KIND_RADIAL_BASIS_FUNCTION = WENDLAND_C2 RADIAL_BASIS_FUNCTION_PARAMETER = 0.015 RADIAL_BASIS_FUNCTION_POLYNOMIAL_TERM = YES -RADIAL_BASIS_FUNCTION_PRUNE_TOLERANCE = 1e-6 +RADIAL_BASIS_FUNCTION_PRUNE_TOLERANCE = 0 % % Physics -------------------------------------------------------------- % SOLVER= ELASTICITY diff --git a/TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg b/TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg index 6d293bd5506e..9efc636b33d4 100644 --- a/TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg +++ b/TestCases/fea_fsi/Airfoil_RBF/configFlow.cfg @@ -33,7 +33,7 @@ RADIAL_BASIS_FUNCTION_POLYNOMIAL_TERM = YES % The number of donor points per target point is reduced by prunning away % small interpolation coefficients (making the interp. matrix sparse). % This tolerance is relative to the maximum absolute coefficient. -RADIAL_BASIS_FUNCTION_PRUNE_TOLERANCE = 1e-6 +RADIAL_BASIS_FUNCTION_PRUNE_TOLERANCE = 0 % % Physics -------------------------------------------------------------- % SOLVER= EULER From 04fff4b39e06d48971902a83089749ed4748c1e7 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 17 Mar 2020 21:10:49 +0000 Subject: [PATCH 024/112] move pruning to separate function --- Common/include/interpolation_structure.hpp | 9 ++ Common/src/interpolation_structure.cpp | 121 ++++++++++++--------- 2 files changed, 81 insertions(+), 49 deletions(-) diff --git a/Common/include/interpolation_structure.hpp b/Common/include/interpolation_structure.hpp index a1349ecd4df7..448178a7a4e7 100644 --- a/Common/include/interpolation_structure.hpp +++ b/Common/include/interpolation_structure.hpp @@ -430,6 +430,15 @@ class CRadialBasisFunction final : public CInterpolator { */ int CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, su2passivematrix &P) const; + /*! + * \brief Prunes (by setting to zero) small interpolation coefficients, i.e. + * <= tolerance*max(abs(coeffs)). The vector is re-scaled such that sum(coeffs)==1. + * \param[in] tolerance - Relative pruning tolerance. + * \param[in,out] coeffs - The vector of interpolation coefficients. + * \return Number of non-zero coefficients after pruning. + */ + int PruneSmallCoefficients(passivedouble tolerance, su2passivevector& coeffs) const; + }; /*! diff --git a/Common/src/interpolation_structure.cpp b/Common/src/interpolation_structure.cpp index 414729f9ff55..95d1a9b9aa5f 100644 --- a/Common/src/interpolation_structure.cpp +++ b/Common/src/interpolation_structure.cpp @@ -2720,13 +2720,13 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { vector AssignedProcessor(nMarkerInt,-1); vector TotalWork(nProcessor,0); - for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; ++iMarkerInt) { + for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; ++iMarkerInt) { /*--- On the donor side: find the tag of the boundary sharing the interface. ---*/ - int mark_donor = Find_InterfaceMarker(config[donorZone], iMarkerInt); + int mark_donor = Find_InterfaceMarker(config[donorZone], iMarkerInt+1); /*--- On the target side: find the tag of the boundary sharing the interface. ---*/ - int mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt); + int mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt+1); /*--- We gather a vector in MASTER_NODE to determines whether the boundary is not on the processor because of the partition or because the zone does not include it. ---*/ @@ -2756,9 +2756,9 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { Collect_VertexInfo(false, mark_donor, mark_target, nVertexDonor, nDim); /*--- Compresses the gathered donor point information to simplify computation. ---*/ - auto& DonorCoord = DonorCoordinates[iMarkerInt-1]; - auto& DonorPoint = DonorGlobalPoint[iMarkerInt-1]; - auto& DonorProc = DonorProcessor[iMarkerInt-1]; + auto& DonorCoord = DonorCoordinates[iMarkerInt]; + auto& DonorPoint = DonorGlobalPoint[iMarkerInt]; + auto& DonorProc = DonorProcessor[iMarkerInt]; DonorCoord.resize(nGlobalVertexDonor, nDim); DonorPoint.resize(nGlobalVertexDonor); DonorProc.resize(nGlobalVertexDonor); @@ -2788,7 +2788,7 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { TotalWork[iProcessor] += pow(nGlobalVertexDonor,3); // based on matrix inversion. - AssignedProcessor[iMarkerInt-1] = iProcessor; + AssignedProcessor[iMarkerInt] = iProcessor; } @@ -2950,7 +2950,6 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { /*--- Compute H matrix, distributing target points over the threads in the rank. ---*/ SU2_OMP_PARALLEL { - su2passivevector target_vec(nGlobalVertexDonor+nPolynomial+1); su2passivevector coeff_vec(nGlobalVertexDonor); SU2_OMP_FOR_DYN(roundUpDiv(nVertexTarget, 2*omp_get_max_threads())) @@ -2964,55 +2963,52 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { const su2double* coord_i = target_geometry->node[point_target]->GetCoord(); - /*--- Prepare target vector (one row of the target matrix). ---*/ - /*--- polynominal part ---*/ - int idx = 0; + /*--- Multiply target vector by C_inv_trunc to obtain the interpolation coefficients. + * The target vector is not stored, we consume its entries immediately to avoid + * strided access to C_inv_trunc (as it is row-major). ---*/ + + /*--- Polynominal part: ---*/ if (usePolynomial) { - target_vec(idx++) = 1.0; // constant term - for (int iDim = 0; iDim < nDim; ++iDim) // linear terms - if (keepPolynomialRow[iDim]) // of which one may have been excluded - target_vec(idx++) = SU2_TYPE::GetValue(coord_i[iDim]); + /*--- Constant term. ---*/ + for (auto j = 0ul; j < nGlobalVertexDonor; ++j) + coeff_vec(j) = C_inv_trunc(0,j); + + /*--- Linear terms. ---*/ + for (int iDim = 0, idx = 1; iDim < nDim; ++iDim) { + /*--- Of which one may have been excluded. ---*/ + if (!keepPolynomialRow[iDim]) continue; + for (auto j = 0ul; j < nGlobalVertexDonor; ++j) + coeff_vec(j) += SU2_TYPE::GetValue(coord_i[iDim]) * C_inv_trunc(idx,j); + idx += 1; + } } - /*--- RBF part ---*/ - for (auto iVertexDonor = 0ul; iVertexDonor < nGlobalVertexDonor; ++iVertexDonor) - target_vec(iVertexDonor+idx) = SU2_TYPE::GetValue(Get_RadialBasisValue(kindRBF, paramRBF, - PointsDistance(nDim, coord_i, DonorCoord[iVertexDonor]))); - - /*--- Multiply target vector by C_inv_trunc to obtain the interpolation coefficients. - Simultaneously determine a reference for dropping small coefficients. ---*/ - passivedouble coeffRef = 0.0; - for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) { - coeff_vec(iVertex) = 0.0; - for (auto jVertex = 0ul; jVertex < target_vec.size(); ++jVertex) - coeff_vec(iVertex) += target_vec(jVertex) * C_inv_trunc(jVertex, iVertex); - coeffRef = max(coeffRef, fabs(coeff_vec(iVertex))); + else { + /*--- Initialize vector to zero. ---*/ + for (auto j = 0ul; j < nGlobalVertexDonor; ++j) coeff_vec(j) = 0.0; } - coeffRef *= SU2_TYPE::GetValue(pruneTol); - - /*--- Prune and count the number of donor points for this target point. ---*/ - idx = 0; - passivedouble coeffSum = 0.0; - for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) { - if (fabs(coeff_vec(iVertex)) > coeffRef) { - coeffSum += coeff_vec(iVertex); - ++idx; - } - else coeff_vec(iVertex) = 0.0; + + /*--- RBF terms: ---*/ + for (auto iVertexDonor = 0ul; iVertexDonor < nGlobalVertexDonor; ++iVertexDonor) { + auto w_ij = SU2_TYPE::GetValue(Get_RadialBasisValue(kindRBF, paramRBF, + PointsDistance(nDim, coord_i, DonorCoord[iVertexDonor]))); + + for (auto j = 0ul; j < nGlobalVertexDonor; ++j) + coeff_vec(j) += w_ij * C_inv_trunc(1+nPolynomial+iVertexDonor, j); } - /*--- Correct remaining coefficients, sum must be 1 for conservation. ---*/ - passivedouble correction = 1.0 / coeffSum; - for (auto i = 0ul; i < nGlobalVertexDonor; ++i) coeff_vec(i) *= correction; + + /*--- Prune small coefficients. ---*/ + auto nnz = PruneSmallCoefficients(SU2_TYPE::GetValue(pruneTol), coeff_vec); /*--- Allocate and set donor information for this target point. ---*/ - target_vertex->SetnDonorPoints(idx); + target_vertex->SetnDonorPoints(nnz); target_vertex->Allocate_DonorInfo(); - idx = 0; - for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) { + + for (unsigned long iVertex = 0, iSet = 0; iVertex < nGlobalVertexDonor; ++iVertex) { if (fabs(coeff_vec(iVertex)) > 0.0) { - target_vertex->SetInterpDonorProcessor(idx, DonorProc[iVertex]); - target_vertex->SetInterpDonorPoint(idx, DonorPoint[iVertex]); - target_vertex->SetDonorCoeff(idx, coeff_vec(iVertex)); - ++idx; + target_vertex->SetInterpDonorProcessor(iSet, DonorProc[iVertex]); + target_vertex->SetInterpDonorPoint(iSet, DonorPoint[iVertex]); + target_vertex->SetDonorCoeff(iSet, coeff_vec(iVertex)); + ++iSet; } } @@ -3032,6 +3028,33 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { } +int CRadialBasisFunction::PruneSmallCoefficients(passivedouble tolerance, + su2passivevector& coeffs) const { + + /*--- Determine the pruning threshold. ---*/ + passivedouble thresh = 0.0; + for (auto i = 0ul; i < coeffs.size(); ++i) + thresh = max(thresh, fabs(coeffs(i))); + thresh *= tolerance; + + /*--- Prune and count non-zeros. ---*/ + int numNonZeros = 0; + passivedouble coeffSum = 0.0; + for (auto i = 0ul; i < coeffs.size(); ++i) { + if (fabs(coeffs(i)) > thresh) { + coeffSum += coeffs(i); + ++numNonZeros; + } + else coeffs(i) = 0.0; + } + + /*--- Correct remaining coefficients, sum must be 1 for conservation. ---*/ + passivedouble correction = 1.0 / coeffSum; + for (auto i = 0ul; i < coeffs.size(); ++i) coeffs(i) *= correction; + + return numNonZeros; +} + int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, su2passivematrix &P) const { const int m = P.rows(); From d077e55a16813db87485182d070a41721ed66a67 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Tue, 17 Mar 2020 23:22:06 +0000 Subject: [PATCH 025/112] move one more chunk of code to separate function --- Common/include/interpolation_structure.hpp | 20 +- Common/src/interpolation_structure.cpp | 279 ++++++++++----------- 2 files changed, 155 insertions(+), 144 deletions(-) diff --git a/Common/include/interpolation_structure.hpp b/Common/include/interpolation_structure.hpp index 448178a7a4e7..bffcc34bf78d 100644 --- a/Common/include/interpolation_structure.hpp +++ b/Common/include/interpolation_structure.hpp @@ -415,10 +415,28 @@ class CRadialBasisFunction final : public CInterpolator { * \param[in] type - of radial basis function * \param[in] radius - the characteristic dimension * \param[in] dist - distance + * \return value of the RBF. */ - static su2double Get_RadialBasisValue(const unsigned short type, const su2double radius, const su2double dist); + static su2double Get_RadialBasisValue(ENUM_RADIALBASIS type, const su2double radius, const su2double dist); private: + /*! + * \brief Compute the RBF "generator" matrix with or without polynomial terms. + * \note Multiplying C_inv_trunc by a column vector gives specific coefficients for given "known values", + * conversely, multiplying (on the left) by a row vector of polynomial and RBF values gives generic + * interpolation coefficients for a given target evaluation point. + * \param[in] type - Type of radial basis function. + * \param[in] usePolynomial - Whether to use polynomial terms. + * \param[in] radius - Normalizes point-to-point distance when computing RBF values. + * \param[in] coords - Coordinates of the donor points. + * \param[out] nPolynomial - Num of poly terms, -1 if !usePolynomial, nDim-1 if coords lie on plane, else nDim. + * \param[out] keepPolynomialRow - Size nDim, signals which (if any) iDim was removed from polynomial term. + * \param[out] C_inv_trunc - The generator matrix as described above. + */ + void ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, su2double radius, + const su2activematrix& coords, int& nPolynomial, + vector& keepPolynomialRow, su2passivematrix& C_inv_trunc) const; + /*! * \brief If the polynomial term is included in the interpolation, and the points lie on a plane, the matrix * becomes rank deficient and cannot be inverted. This method detects that condition and corrects it by diff --git a/Common/src/interpolation_structure.cpp b/Common/src/interpolation_structure.cpp index 95d1a9b9aa5f..f8dab7563711 100644 --- a/Common/src/interpolation_structure.cpp +++ b/Common/src/interpolation_structure.cpp @@ -2683,7 +2683,6 @@ bool CSlidingMesh::CheckPointInsideTriangle(su2double* Point, su2double* T1, su2 return (check == 3); } -/*--- Radial Basis Function Interpolator ---*/ CRadialBasisFunction::CRadialBasisFunction(void): CInterpolator() { } CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, @@ -2693,15 +2692,43 @@ CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, CCo Set_TransferCoeff(config); } +su2double CRadialBasisFunction::Get_RadialBasisValue(ENUM_RADIALBASIS type, const su2double radius, const su2double dist) +{ + su2double rbf = dist/radius; + + switch (type) { + + case WENDLAND_C2: + if(rbf < 1) rbf = pow(pow((1-rbf),2),2)*(4*rbf+1); // double use of pow(x,2) for optimization + else rbf = 0.0; + break; + + case GAUSSIAN: + rbf = exp(-rbf*rbf); + break; + + case THIN_PLATE_SPLINE: + if(rbf < numeric_limits::min()) rbf = 0.0; + else rbf *= rbf*log(rbf); + break; + + case MULTI_QUADRIC: + case INV_MULTI_QUADRIC: + rbf = sqrt(1.0+rbf*rbf); + if(type == INV_MULTI_QUADRIC) rbf = 1.0/rbf; + break; + } + + return rbf; +} + void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { /*--- RBF options. ---*/ - const unsigned short kindRBF = config[donorZone]->GetKindRadialBasisFunction(); + const auto kindRBF = static_cast(config[donorZone]->GetKindRadialBasisFunction()); const bool usePolynomial = config[donorZone]->GetRadialBasisFunctionPolynomialOption(); const su2double paramRBF = config[donorZone]->GetRadialBasisFunctionParameter(); const su2double pruneTol = config[donorZone]->GetRadialBasisFunctionPruneTol(); - const passivedouble eps = numeric_limits::epsilon(); - const su2double interfaceCoordTol = 1e6 * eps; const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2; const int nDim = donor_geometry->GetnDim(); @@ -2755,7 +2782,7 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { Collect_VertexInfo(false, mark_donor, mark_target, nVertexDonor, nDim); - /*--- Compresses the gathered donor point information to simplify computation. ---*/ + /*--- Compresses the gathered donor point information to simplify computations. ---*/ auto& DonorCoord = DonorCoordinates[iMarkerInt]; auto& DonorPoint = DonorGlobalPoint[iMarkerInt]; auto& DonorProc = DonorProcessor[iMarkerInt]; @@ -2800,102 +2827,11 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { SU2_OMP_PARALLEL_(for schedule(dynamic,1)) for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; ++iMarkerInt) { - - if (rank != AssignedProcessor[iMarkerInt]) continue; // #notmyjob - - const auto& DonorCoord = DonorCoordinates[iMarkerInt]; - const auto nGlobalVertexDonor = DonorCoord.rows(); - - auto& C_inv_trunc = CinvTrucVec[iMarkerInt]; - auto& nPolynomial = nPolynomialVec[iMarkerInt]; - auto& keepPolynomialRow = keepPolynomialRowVec[iMarkerInt]; - - /*--- Populate interpolation kernel. ---*/ - CSymmetricMatrix global_M(nGlobalVertexDonor); - - for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; ++iVertex) - for (auto jVertex = iVertex; jVertex < nGlobalVertexDonor; ++jVertex) - global_M(iVertex, jVertex) = SU2_TYPE::GetValue(Get_RadialBasisValue(kindRBF, paramRBF, - PointsDistance(nDim, DonorCoord[iVertex], DonorCoord[jVertex]))); - - /*--- Invert M matrix (operation is in-place). ---*/ - switch (kindRBF) { - /*--- Basis functions that make M positive definite. ---*/ - case WENDLAND_C2: - case INV_MULTI_QUADRIC: - case GAUSSIAN: - global_M.Invert(true); break; - - /*--- Basis functions that make M semi-positive definite. ---*/ - case THIN_PLATE_SPLINE: - case MULTI_QUADRIC: - global_M.Invert(false); break; - } - - /*--- Compute C_inv_trunc. ---*/ - if (usePolynomial) { - - /*--- Fill P matrix (P for points, with an extra top row of ones). ---*/ - su2passivematrix P(1+nDim, nGlobalVertexDonor); - - for (auto iVertex = 0ul; iVertex < nGlobalVertexDonor; iVertex++) { - P(0, iVertex) = 1.0; - for (int iDim = 0; iDim < nDim; ++iDim) - P(1+iDim, iVertex) = SU2_TYPE::GetValue(DonorCoord(iVertex, iDim)); - } - - /*--- Check if points lie on a plane and remove one coordinate from P if so. ---*/ - nPolynomial = CheckPolynomialTerms(interfaceCoordTol, keepPolynomialRow, P); - - /*--- Compute Mp = (P * M^-1 * P^T)^-1 ---*/ - CSymmetricMatrix Mp(nPolynomial+1); - - su2passivematrix tmp; - global_M.MatMatMult('R', P, tmp); // tmp = P * M^-1 - - for (int i = 0; i <= nPolynomial; ++i) // Mp = tmp * P - for (int j = i; j <= nPolynomial; ++j) { - Mp(i,j) = 0.0; - for (auto k = 0ul; k < nGlobalVertexDonor; ++k) Mp(i,j) += tmp(i,k) * P(j,k); - } - Mp.Invert(false); // Mp = Mp^-1 - - /*--- Compute M_p * P * M^-1, the top part of C_inv_trunc. ---*/ - Mp.MatMatMult('L', P, tmp); - su2passivematrix C_inv_top; - global_M.MatMatMult('R', tmp, C_inv_top); - - /*--- Compute tmp = (I - P^T * M_p * P * M^-1), part of the bottom part of - C_inv_trunc. Note that most of the product is known from the top part. ---*/ - tmp.resize(nGlobalVertexDonor, nGlobalVertexDonor); - - for (auto i = 0ul; i < nGlobalVertexDonor; ++i) { - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) { - tmp(i,j) = 0.0; - for (int k = 0; k <= nPolynomial; ++k) tmp(i,j) -= P(k,i) * C_inv_top(k,j); - } - tmp(i,i) += 1.0; // identity part - } - - /*--- Compute M^-1 * (I - P^T * M_p * P * M^-1), finalize bottom of C_inv_trunc. ---*/ - global_M.MatMatMult('L', tmp, C_inv_trunc); - - /*--- Merge top and bottom of C_inv_trunc. ---*/ - tmp = move(C_inv_trunc); - C_inv_trunc.resize(1+nPolynomial+nGlobalVertexDonor, nGlobalVertexDonor); - memcpy(C_inv_trunc[0], C_inv_top.data(), C_inv_top.size()*sizeof(passivedouble)); - memcpy(C_inv_trunc[1+nPolynomial], tmp.data(), tmp.size()*sizeof(passivedouble)); + if (rank == AssignedProcessor[iMarkerInt]) { + ComputeGeneratorMatrix(kindRBF, usePolynomial, paramRBF, + DonorCoordinates[iMarkerInt], nPolynomialVec[iMarkerInt], + keepPolynomialRowVec[iMarkerInt], CinvTrucVec[iMarkerInt]); } - else { - /*--- No polynomial term used in the interpolation, C_inv_trunc = M^-1. ---*/ - - C_inv_trunc.resize(nGlobalVertexDonor, nGlobalVertexDonor); - for (auto i = 0ul; i < nGlobalVertexDonor; ++i) - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) - C_inv_trunc(i,j) = global_M(i,j); - - } // end usePolynomial - } /*--- Final loop over interface markers to compute the interpolation coefficients. ---*/ @@ -3028,31 +2964,91 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { } -int CRadialBasisFunction::PruneSmallCoefficients(passivedouble tolerance, - su2passivevector& coeffs) const { +void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, + su2double radius, const su2activematrix& coords, int& nPolynomial, + vector& keepPolynomialRow, su2passivematrix& C_inv_trunc) const { - /*--- Determine the pruning threshold. ---*/ - passivedouble thresh = 0.0; - for (auto i = 0ul; i < coeffs.size(); ++i) - thresh = max(thresh, fabs(coeffs(i))); - thresh *= tolerance; + const su2double interfaceCoordTol = 1e6 * numeric_limits::epsilon(); - /*--- Prune and count non-zeros. ---*/ - int numNonZeros = 0; - passivedouble coeffSum = 0.0; - for (auto i = 0ul; i < coeffs.size(); ++i) { - if (fabs(coeffs(i)) > thresh) { - coeffSum += coeffs(i); - ++numNonZeros; + const auto nVertexDonor = coords.rows(); + const int nDim = coords.cols(); + + /*--- Populate interpolation kernel. ---*/ + CSymmetricMatrix global_M(nVertexDonor); + + for (auto iVertex = 0ul; iVertex < nVertexDonor; ++iVertex) + for (auto jVertex = iVertex; jVertex < nVertexDonor; ++jVertex) + global_M(iVertex, jVertex) = SU2_TYPE::GetValue(Get_RadialBasisValue(type, radius, + PointsDistance(nDim, coords[iVertex], coords[jVertex]))); + + /*--- Invert M matrix (operation is in-place). ---*/ + const bool kernelIsSPD = (type==WENDLAND_C2) || (type==GAUSSIAN) || (type==INV_MULTI_QUADRIC); + global_M.Invert(kernelIsSPD); + + /*--- Compute C_inv_trunc. ---*/ + if (usePolynomial) { + + /*--- Fill P matrix (P for points, with an extra top row of ones). ---*/ + su2passivematrix P(1+nDim, nVertexDonor); + + for (auto iVertex = 0ul; iVertex < nVertexDonor; iVertex++) { + P(0, iVertex) = 1.0; + for (int iDim = 0; iDim < nDim; ++iDim) + P(1+iDim, iVertex) = SU2_TYPE::GetValue(coords(iVertex, iDim)); } - else coeffs(i) = 0.0; + + /*--- Check if points lie on a plane and remove one coordinate from P if so. ---*/ + nPolynomial = CheckPolynomialTerms(interfaceCoordTol, keepPolynomialRow, P); + + /*--- Compute Mp = (P * M^-1 * P^T)^-1 ---*/ + CSymmetricMatrix Mp(nPolynomial+1); + + su2passivematrix tmp; + global_M.MatMatMult('R', P, tmp); // tmp = P * M^-1 + + for (int i = 0; i <= nPolynomial; ++i) // Mp = tmp * P + for (int j = i; j <= nPolynomial; ++j) { + Mp(i,j) = 0.0; + for (auto k = 0ul; k < nVertexDonor; ++k) Mp(i,j) += tmp(i,k) * P(j,k); + } + Mp.Invert(false); // Mp = Mp^-1 + + /*--- Compute M_p * P * M^-1, the top part of C_inv_trunc. ---*/ + Mp.MatMatMult('L', P, tmp); + su2passivematrix C_inv_top; + global_M.MatMatMult('R', tmp, C_inv_top); + + /*--- Compute tmp = (I - P^T * M_p * P * M^-1), part of the bottom part of + C_inv_trunc. Note that most of the product is known from the top part. ---*/ + tmp.resize(nVertexDonor, nVertexDonor); + + for (auto i = 0ul; i < nVertexDonor; ++i) { + for (auto j = 0ul; j < nVertexDonor; ++j) { + tmp(i,j) = 0.0; + for (int k = 0; k <= nPolynomial; ++k) tmp(i,j) -= P(k,i) * C_inv_top(k,j); + } + tmp(i,i) += 1.0; // identity part + } + + /*--- Compute M^-1 * (I - P^T * M_p * P * M^-1), finalize bottom of C_inv_trunc. ---*/ + global_M.MatMatMult('L', tmp, C_inv_trunc); + + /*--- Merge top and bottom of C_inv_trunc. ---*/ + tmp = move(C_inv_trunc); + C_inv_trunc.resize(1+nPolynomial+nVertexDonor, nVertexDonor); + memcpy(C_inv_trunc[0], C_inv_top.data(), C_inv_top.size()*sizeof(passivedouble)); + memcpy(C_inv_trunc[1+nPolynomial], tmp.data(), tmp.size()*sizeof(passivedouble)); } + else { + /*--- No polynomial term used in the interpolation, C_inv_trunc = M^-1. ---*/ - /*--- Correct remaining coefficients, sum must be 1 for conservation. ---*/ - passivedouble correction = 1.0 / coeffSum; - for (auto i = 0ul; i < coeffs.size(); ++i) coeffs(i) *= correction; + C_inv_trunc.resize(nVertexDonor, nVertexDonor); + for (auto i = 0ul; i < nVertexDonor; ++i) + for (auto j = 0ul; j < nVertexDonor; ++j) + C_inv_trunc(i,j) = global_M(i,j); + + } // end usePolynomial - return numNonZeros; } int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, @@ -3125,34 +3121,31 @@ int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector::min()) rbf = 0.0; - else rbf *= rbf*log(rbf); - break; + /*--- Determine the pruning threshold. ---*/ + passivedouble thresh = 0.0; + for (auto i = 0ul; i < coeffs.size(); ++i) + thresh = max(thresh, fabs(coeffs(i))); + thresh *= tolerance; - case MULTI_QUADRIC: - case INV_MULTI_QUADRIC: - rbf = sqrt(1.0+rbf*rbf); - if(type == INV_MULTI_QUADRIC) rbf = 1.0/rbf; - break; + /*--- Prune and count non-zeros. ---*/ + int numNonZeros = 0; + passivedouble coeffSum = 0.0; + for (auto i = 0ul; i < coeffs.size(); ++i) { + if (fabs(coeffs(i)) > thresh) { + coeffSum += coeffs(i); + ++numNonZeros; + } + else coeffs(i) = 0.0; } - return rbf; + /*--- Correct remaining coefficients, sum must be 1 for conservation. ---*/ + passivedouble correction = 1.0 / coeffSum; + for (auto i = 0ul; i < coeffs.size(); ++i) coeffs(i) *= correction; + + return numNonZeros; } void CSymmetricMatrix::Initialize(int N) From 9185ca91546b277162dbe5e8bdf9f43d41d76ec4 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 18 Mar 2020 10:32:18 +0000 Subject: [PATCH 026/112] hybrid parallel nearest neighbor search --- Common/include/interpolation_structure.hpp | 34 +++-- Common/src/interpolation_structure.cpp | 154 +++++++++------------ 2 files changed, 84 insertions(+), 104 deletions(-) diff --git a/Common/include/interpolation_structure.hpp b/Common/include/interpolation_structure.hpp index bffcc34bf78d..7728dcc8180c 100644 --- a/Common/include/interpolation_structure.hpp +++ b/Common/include/interpolation_structure.hpp @@ -142,18 +142,28 @@ class CInterpolator { * \param[in] val_marker - index of the marker */ void ReconstructBoundary(unsigned long val_zone, int val_marker); - + + /*! + * \brief compute squared distance between 2 points + * \param[in] nDim - number of dimensions + * \param[in] point_i - coordinates of point i + * \param[in] point_j - coordinates of point j + */ + inline su2double PointsSquareDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) const { + su2double d = 0.0; + for(unsigned short iDim = 0; iDim < nDim; iDim++) + d += pow(point_j[iDim] - point_i[iDim], 2); + return d; + } + /*! * \brief compute distance between 2 points * \param[in] nDim - number of dimensions - * \param[in] point_i - * \param[in] point_i + * \param[in] point_i - coordinates of point i + * \param[in] point_j - coordinates of point j */ inline su2double PointsDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) const { - su2double m = 0; - for(unsigned short iDim = 0; iDim < nDim; iDim++) - m += pow(point_j[iDim] - point_i[iDim], 2); - return sqrt(m); + return sqrt(PointsSquareDistance(nDim, point_i, point_j)); } /*! @@ -188,9 +198,8 @@ class CInterpolator { /*! * \brief Nearest Neighbor interpolation */ -class CNearestNeighbor : public CInterpolator { +class CNearestNeighbor final : public CInterpolator { public: - /*! * \brief Constructor of the class. */ @@ -205,16 +214,11 @@ class CNearestNeighbor : public CInterpolator { */ CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); - /*! - * \brief Destructor of the class. - */ - ~CNearestNeighbor(void); - /*! * \brief Set up transfer matrix defining relation between two meshes * \param[in] config - Definition of the particular problem. */ - void Set_TransferCoeff(CConfig **config); + void Set_TransferCoeff(CConfig **config) override; }; diff --git a/Common/src/interpolation_structure.cpp b/Common/src/interpolation_structure.cpp index f8dab7563711..762bcb38644c 100644 --- a/Common/src/interpolation_structure.cpp +++ b/Common/src/interpolation_structure.cpp @@ -544,139 +544,114 @@ bool CInterpolator::CheckInterfaceBoundary(int markDonor, int markTarget){ return true; } -/* Nearest Neighbor Interpolator */ -CNearestNeighbor::CNearestNeighbor(void): CInterpolator() { } +CNearestNeighbor::CNearestNeighbor(void): CInterpolator() { } -CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { +CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { - /*--- Initialize transfer coefficients between the zones ---*/ + /*--- Initialize transfer coefficients between the zones. ---*/ Set_TransferCoeff(config); } -CNearestNeighbor::~CNearestNeighbor() {} - void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { - int iProcessor, pProcessor, nProcessor = size; - int markDonor, markTarget; - - unsigned short nDim, iMarkerInt, nMarkerInt, iDonor; + /*--- By definition, one donor point per target point. ---*/ + constexpr auto numDonor = 1; + constexpr auto idxDonor = 0; - unsigned long nVertexDonor, nVertexTarget, Point_Target, jVertex, iVertexTarget; - unsigned long Global_Point_Donor; - long pGlobalPoint = 0; + const su2double eps = numeric_limits::epsilon(); - su2double *Coord_i, *Coord_j, dist, mindist, maxdist; - - /*--- Initialize variables --- */ - - nMarkerInt = (int) ( config[donorZone]->GetMarker_n_ZoneInterface() / 2 ); - - nDim = donor_geometry->GetnDim(); + const int nProcessor = size; + const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2; + const auto nDim = donor_geometry->GetnDim(); - iDonor = 0; - + Buffer_Send_nVertex_Donor = new unsigned long [1]; Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; + /*--- Cycle over nMarkersInt interface to determine communication pattern. ---*/ - /*--- Cycle over nMarkersInt interface to determine communication pattern ---*/ + for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { - for (iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { - - - /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ - markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); + /*--- On the donor side: find the tag of the boundary sharing the interface. ---*/ + const auto markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); - /*--- On the target side: find the tag of the boundary sharing the interface ---*/ - markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); + /*--- On the target side: find the tag of the boundary sharing the interface. ---*/ + const auto markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); - /*--- Checks if the zone contains the interface, if not continue to the next step ---*/ - if( !CheckInterfaceBoundary(markDonor, markTarget) ) - continue; + /*--- Checks if the zone contains the interface, if not continue to the next step. ---*/ + if (!CheckInterfaceBoundary(markDonor, markTarget)) continue; - if(markDonor != -1) - nVertexDonor = donor_geometry->GetnVertex( markDonor ); - else - nVertexDonor = 0; - - if(markTarget != -1) - nVertexTarget = target_geometry->GetnVertex( markTarget ); - else - nVertexTarget = 0; - - Buffer_Send_nVertex_Donor = new unsigned long [ 1 ]; + unsigned long nVertexDonor = 0, nVertexTarget = 0; + if(markDonor != -1) nVertexDonor = donor_geometry->GetnVertex( markDonor ); + if(markTarget != -1) nVertexTarget = target_geometry->GetnVertex( markTarget ); - /* Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor */ + /* Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor. */ Determine_ArraySize(false, markDonor, markTarget, nVertexDonor, nDim); - Buffer_Send_Coord = new su2double [ MaxLocalVertex_Donor * nDim ]; - Buffer_Send_GlobalPoint = new long [ MaxLocalVertex_Donor ]; - Buffer_Receive_Coord = new su2double [ nProcessor * MaxLocalVertex_Donor * nDim ]; + Buffer_Send_Coord = new su2double [ MaxLocalVertex_Donor * nDim ]; + Buffer_Send_GlobalPoint = new long [ MaxLocalVertex_Donor ]; + Buffer_Receive_Coord = new su2double [ nProcessor * MaxLocalVertex_Donor * nDim ]; Buffer_Receive_GlobalPoint = new long [ nProcessor * MaxLocalVertex_Donor ]; - /*-- Collect coordinates, global points, and normal vectors ---*/ + /*-- Collect coordinates and global point indices. ---*/ Collect_VertexInfo( false, markDonor, markTarget, nVertexDonor, nDim ); - /*--- Compute the closest point to a Near-Field boundary point ---*/ - maxdist = 0.0; + /*--- Compute the closest donor point to each target. ---*/ + SU2_OMP_PARALLEL_(for schedule(dynamic,roundUpDiv(nVertexTarget,2*omp_get_max_threads()))) + for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget++) { - for (iVertexTarget = 0; iVertexTarget < nVertexTarget; iVertexTarget++) { + auto target_vertex = target_geometry->vertex[markTarget][iVertexTarget]; + const auto Point_Target = target_vertex->GetNode(); - Point_Target = target_geometry->vertex[markTarget][iVertexTarget]->GetNode(); + if (!target_geometry->node[Point_Target]->GetDomain()) continue; - if ( target_geometry->node[Point_Target]->GetDomain() ) { + /*--- Coordinates of the target point. ---*/ + const su2double* Coord_i = target_geometry->node[Point_Target]->GetCoord(); - target_geometry->vertex[markTarget][iVertexTarget]->SetnDonorPoints(1); - target_geometry->vertex[markTarget][iVertexTarget]->Allocate_DonorInfo(); // Possible meme leak? + su2double mindist = 1e20; + long pGlobalPoint = 0; + int pProcessor = 0; - /*--- Coordinates of the boundary point ---*/ - Coord_i = target_geometry->node[Point_Target]->GetCoord(); + for (int iProcessor = 0; iProcessor < nProcessor; ++iProcessor) { + for (auto jVertex = 0ul; jVertex < Buffer_Receive_nVertex_Donor[iProcessor]; ++jVertex) { - mindist = 1E6; - pProcessor = 0; + const auto idx = iProcessor*MaxLocalVertex_Donor + jVertex; - /*--- Loop over all the boundaries to find the pair ---*/ + const su2double* Coord_j = &Buffer_Receive_Coord[idx*nDim]; - for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { - for (jVertex = 0; jVertex < MaxLocalVertex_Donor; jVertex++) { - - Global_Point_Donor = iProcessor*MaxLocalVertex_Donor+jVertex; - - if (Buffer_Receive_GlobalPoint[Global_Point_Donor] != -1){ - - Coord_j = &Buffer_Receive_Coord[ Global_Point_Donor*nDim]; - - dist = PointsDistance(nDim, Coord_i, Coord_j); - - if (dist < mindist) { - mindist = dist; pProcessor = iProcessor; - pGlobalPoint = Buffer_Receive_GlobalPoint[Global_Point_Donor]; - } - - if (dist == 0.0) break; - } + const auto dist = PointsSquareDistance(nDim, Coord_i, Coord_j); + + if (dist < mindist) { + mindist = dist; + pProcessor = iProcessor; + pGlobalPoint = Buffer_Receive_GlobalPoint[idx]; } - } - /*--- Store the value of the pair ---*/ - maxdist = max(maxdist, mindist); - target_geometry->vertex[markTarget][iVertexTarget]->SetInterpDonorPoint(iDonor, pGlobalPoint); - target_geometry->vertex[markTarget][iVertexTarget]->SetInterpDonorProcessor(iDonor, pProcessor); - target_geometry->vertex[markTarget][iVertexTarget]->SetDonorCoeff(iDonor, 1.0); + /*--- Test for "exact" match. ---*/ + if (dist < eps) break; + } } + + /*--- Store matching pair. ---*/ + target_vertex->SetnDonorPoints(numDonor); + target_vertex->Allocate_DonorInfo(); + target_vertex->SetInterpDonorPoint(idxDonor, pGlobalPoint); + target_vertex->SetInterpDonorProcessor(idxDonor, pProcessor); + target_vertex->SetDonorCoeff(idxDonor, 1.0); + } delete[] Buffer_Send_Coord; delete[] Buffer_Send_GlobalPoint; - + delete[] Buffer_Receive_Coord; delete[] Buffer_Receive_GlobalPoint; - delete[] Buffer_Send_nVertex_Donor; - } + delete[] Buffer_Send_nVertex_Donor; delete[] Buffer_Receive_nVertex_Donor; + } @@ -2883,7 +2858,8 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { } #endif - /*--- Compute H matrix, distributing target points over the threads in the rank. ---*/ + /*--- Compute H (interpolation) matrix, distributing + * target points over the threads in the rank. ---*/ SU2_OMP_PARALLEL { su2passivevector coeff_vec(nGlobalVertexDonor); From b7223b2c9efe964059c6bd6606f5a493494ff8da Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 18 Mar 2020 11:49:35 +0000 Subject: [PATCH 027/112] general cleanup of all interpolation classes --- Common/include/interpolation_structure.hpp | 113 +-- Common/src/interpolation_structure.cpp | 985 +++++++++------------ 2 files changed, 449 insertions(+), 649 deletions(-) diff --git a/Common/include/interpolation_structure.hpp b/Common/include/interpolation_structure.hpp index 7728dcc8180c..3f8a01cba646 100644 --- a/Common/include/interpolation_structure.hpp +++ b/Common/include/interpolation_structure.hpp @@ -50,11 +50,10 @@ using namespace std; */ class CInterpolator { protected: - int rank, /*!< \brief MPI Rank. */ - size; /*!< \brief MPI Size. */ - unsigned int nZone; /*!< \brief Number of zones*/ - unsigned int donorZone, - targetZone; /*!< \brief Type of MPI zone */ + const int rank; /*!< \brief MPI Rank. */ + const int size; /*!< \brief MPI Size. */ + const unsigned donorZone; /*!< \brief Index of donor zone. */ + const unsigned targetZone; /*!< \brief Index of target zone. */ unsigned long MaxLocalVertex_Donor, /*!< \brief Maximum vertices per processor*/ @@ -77,7 +76,7 @@ class CInterpolator { *Buffer_Send_FaceProc, /*!< \brief Buffer to send processor which stores the node indicated in Buffer_Receive_FaceNodes*/ *Buffer_Receive_FaceProc; /*!< \brief Buffer to receive processor which stores the node indicated in Buffer_Receive_FaceNodes*/ - long *Buffer_Send_GlobalPoint, /*!< \brief Buffer to send global point indices*/ + long *Buffer_Send_GlobalPoint, /*!< \brief Buffer to send global point indices*/ *Buffer_Receive_GlobalPoint; /*!< \brief Buffer to receive global point indices*/ su2double *Buffer_Send_Coord, /*!< \brief Buffer to send coordinate values*/ @@ -98,18 +97,12 @@ class CInterpolator { nGlobalVertex, /*!< \brief Dummy variable to temporarily store the global number of vertex of a boundary*/ nLocalLinkedNodes; /*!< \brief Dummy variable to temporarily store the number of vertex of a boundary*/ -public: - CGeometry**** Geometry; /*! \brief Vector which stores n zones of geometry. */ - CGeometry* donor_geometry; /*! \brief Vector which stores the donor geometry. */ - CGeometry* target_geometry; /*! \brief Vector which stores the target geometry. */ + CGeometry**** const Geometry; /*! \brief Vector which stores n zones of geometry. */ + CGeometry* const donor_geometry; /*! \brief Vector which stores the donor geometry. */ + CGeometry* const target_geometry; /*! \brief Vector which stores the target geometry. */ /*! - * \brief Constructor of the class. - */ - CInterpolator(void); - - /*! - * \brief Constructor of the class. + * \brief Constructor of the class, protected as it does not make sense to instantiate base class. * \param[in] geometry - Geometrical definition of the problem. * \param[in] config - Definition of the particular problem. * \param[in] iZone - index of the donor zone @@ -117,25 +110,39 @@ class CInterpolator { */ CInterpolator(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); +public: + /*! + * \brief No default construction allowed. + */ + CInterpolator(void) = delete; + /*! - * \brief Destructor of the class. + * \brief Destructor of the class, nothing is deleted, derived classes need to manage the MPI buffers. + */ + virtual ~CInterpolator(void) = default; + + /*! + * \brief Set up transfer matrix defining relation between two meshes + * \note Main method that derived classes should implement. + * \param[in] config - Definition of the particular problem. */ - virtual ~CInterpolator(void); + inline virtual void Set_TransferCoeff(CConfig **config) {} +protected: /*! * \brief Find the index of the interface marker shared by that zone * \param[in] config - Definition of the particular problem. * \param[in] val_marker_interface - Interface tag. */ - int Find_InterfaceMarker(CConfig *config, unsigned short val_marker_interface); + int Find_InterfaceMarker(const CConfig *config, unsigned short val_marker_interface) const; /*! - * \brief Check whether the interface should be processed or not + * \brief Check whether an interface should be processed or not, i.e. if it is part of the zones. * \param[in] val_markDonor - Marker tag from donor zone. * \param[in] val_markTarget - Marker tag from target zone. */ - bool CheckInterfaceBoundary(int val_markDonor, int val_markTarget); - + bool CheckInterfaceBoundary(int val_markDonor, int val_markTarget) const; + /*! * \brief Recontstruct the boundary connectivity from parallel partitioning and broadcasts it to all threads * \param[in] val_zone - index of the zone @@ -166,12 +173,6 @@ class CInterpolator { return sqrt(PointsSquareDistance(nDim, point_i, point_j)); } - /*! - * \brief Set up transfer matrix defining relation between two meshes - * \param[in] config - Definition of the particular problem. - */ - virtual void Set_TransferCoeff(CConfig **config); - /*! * \brief Determine array sizes used to collect and send coordinate and global point * information. @@ -200,11 +201,6 @@ class CInterpolator { */ class CNearestNeighbor final : public CInterpolator { public: - /*! - * \brief Constructor of the class. - */ - CNearestNeighbor(void); - /*! * \brief Constructor of the class. * \param[in] geometry - Geometrical definition of the problem. @@ -225,9 +221,8 @@ class CNearestNeighbor final : public CInterpolator { /*! * \brief Isoparametric interpolation */ -class CIsoparametric : public CInterpolator { +class CIsoparametric final : public CInterpolator { public: - /*! * \brief Constructor of the class. * \param[in] geometry - Geometrical definition of the problem. @@ -237,17 +232,13 @@ class CIsoparametric : public CInterpolator { */ CIsoparametric(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); - /*! - * \brief Destructor of the class. - */ - ~CIsoparametric(void); - /*! * \brief Set up transfer matrix defining relation between two meshes * \param[in] config - Definition of the particular problem. */ - void Set_TransferCoeff(CConfig **config); + void Set_TransferCoeff(CConfig **config) override; +private: /*! * \brief Calculate the isoparametric representation of point iVertex in marker iZone_0 by nodes of element donor_elem in marker jMarker of zone iZone_1. * \param[in] iVertex - vertex index of the point being interpolated. @@ -258,7 +249,7 @@ class CIsoparametric : public CInterpolator { * \param[in] xj - point projected onto the plane of the donor element. * \param[out] isoparams - isoparametric coefficients. Must be allocated to size nNodes ahead of time. (size> nDonors) * - * If the problem is 2D, the 'face' projected onto is actually an edge; the local index + * \note If the problem is 2D, the 'face' projected onto is actually an edge; the local index * of the edge is then stored in iFace, and the global index of the node (from which the edge * is referenced) */ @@ -270,40 +261,31 @@ class CIsoparametric : public CInterpolator { * \brief Mirror interpolation: copy point linking and coefficient values from the opposing mesh * Assumes that the oppoosing mesh has already run interpolation. (otherwise this will result in empty/trivial interpolation) */ -class CMirror : public CInterpolator { +class CMirror final : public CInterpolator { public: - /*! * \brief Constructor of the class. + * \note Data is set in geometry[targetZone]. * \param[in] geometry_container * \param[in] config - config container * \param[in] iZone - First zone * \param[in] jZone - Second zone - * - * Data is set in geometry[targetZone] - * */ CMirror(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); - /*! - * \brief Destructor of the class. - */ - ~CMirror(void); - /*! * \brief Set up transfer matrix defining relation between two meshes * \param[in] config - Definition of the particular problem. */ - void Set_TransferCoeff(CConfig **config); - + void Set_TransferCoeff(CConfig **config) override; + }; /*! * \brief Sliding mesh approach */ -class CSlidingMesh : public CInterpolator { +class CSlidingMesh final : public CInterpolator { public: - /*! * \brief Constructor of the class. * \param[in] geometry - Geometrical definition of the problem. @@ -313,17 +295,13 @@ class CSlidingMesh : public CInterpolator { */ CSlidingMesh(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); - /*! - * \brief Destructor of the class. - */ - ~CSlidingMesh(void); - /*! * \brief Set up transfer matrix defining relation between two meshes * \param[in] config - Definition of the particular problem. */ - void Set_TransferCoeff(CConfig **config); - + void Set_TransferCoeff(CConfig **config) override; + +private: /*! * \brief For 3-Dimensional grids, build the dual surface element * \param[in] map - array containing the index of the boundary points connected to the node @@ -333,7 +311,8 @@ class CSlidingMesh : public CInterpolator { * \param[in] centralNode - label of the vertex around which the dual surface element is built * \param[in] element - double array where element node coordinates will be stored */ - int Build_3D_surface_element(unsigned long *map, unsigned long *startIndex, unsigned long* nNeighbor, su2double *coord, unsigned long centralNode, su2double** element); + int Build_3D_surface_element(unsigned long *map, unsigned long *startIndex, unsigned long* nNeighbor, + su2double *coord, unsigned long centralNode, su2double** element); /*! * \brief For 2-Dimensional grids, compute intersection length of two segments projected along a given direction @@ -387,6 +366,7 @@ class CSlidingMesh : public CInterpolator { * \param[in] T3 - third point of triangle T */ bool CheckPointInsideTriangle(su2double* Point, su2double* T1, su2double* T2, su2double* T3); + }; /*! @@ -394,11 +374,6 @@ class CSlidingMesh : public CInterpolator { */ class CRadialBasisFunction final : public CInterpolator { public: - /*! - * \brief Constructor of the class. - */ - CRadialBasisFunction(void); - /*! * \brief Constructor of the class. * \param[in] geometry - Geometrical definition of the problem. diff --git a/Common/src/interpolation_structure.cpp b/Common/src/interpolation_structure.cpp index 762bcb38644c..04f210167e2f 100644 --- a/Common/src/interpolation_structure.cpp +++ b/Common/src/interpolation_structure.cpp @@ -41,81 +41,23 @@ extern "C" void dsymm_(char*, char*, int*, int*, passivedouble*, passivedouble*, passivedouble*, int*, passivedouble*, passivedouble*, int*); #endif -CInterpolator::CInterpolator(void) { - - size = SU2_MPI::GetSize(); - rank = SU2_MPI::GetRank(); - - nZone = 0; - Geometry = NULL; - - donor_geometry = NULL; - target_geometry = NULL; - - donorZone = 0; - targetZone = 0; - - Buffer_Receive_nVertex_Donor = NULL; - Buffer_Receive_nFace_Donor = NULL; - Buffer_Receive_nFaceNodes_Donor = NULL; - Buffer_Send_nVertex_Donor = NULL; - Buffer_Send_nFace_Donor = NULL; - Buffer_Send_nFaceNodes_Donor = NULL; - Buffer_Receive_GlobalPoint = NULL; - Buffer_Send_GlobalPoint = NULL; - Buffer_Send_FaceIndex = NULL; - Buffer_Receive_FaceIndex = NULL; - Buffer_Send_FaceNodes = NULL; - Buffer_Receive_FaceNodes = NULL; - Buffer_Send_FaceProc = NULL; - Buffer_Receive_FaceProc = NULL; - - Buffer_Send_Coord = NULL; - Buffer_Send_Normal = NULL; - Buffer_Receive_Coord = NULL; - Buffer_Receive_Normal = NULL; - - Receive_GlobalPoint = NULL; - Buffer_Receive_nLinkedNodes = NULL; - Buffer_Receive_LinkedNodes = NULL; - Buffer_Receive_StartLinkedNodes = NULL; - Buffer_Receive_Proc = NULL; - -} - -CInterpolator::~CInterpolator(void) { - - //if (Buffer_Receive_nVertex_Donor!= NULL) delete[] Buffer_Receive_nVertex_Donor; -} - +CInterpolator::CInterpolator(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : + rank(SU2_MPI::GetRank()), + size(SU2_MPI::GetSize()), + donorZone(iZone), + targetZone(jZone), + Geometry(geometry_container), + donor_geometry(geometry_container[iZone][INST_0][MESH_0]), + target_geometry(geometry_container[jZone][INST_0][MESH_0]) { -CInterpolator::CInterpolator(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) { - - size = SU2_MPI::GetSize(); - rank = SU2_MPI::GetRank(); - - /* Store pointers*/ - Geometry = geometry_container; - - donorZone = iZone; - targetZone = jZone; - - donor_geometry = geometry_container[donorZone][INST_0][MESH_0]; - target_geometry = geometry_container[targetZone][INST_0][MESH_0]; - - /*--- Initialize transfer coefficients between the zones ---*/ - /* Since this is a virtual function, call it in the child class constructor */ - //Set_TransferCoeff(targetZone,donorZone,config); - /*--- Initialize transfer coefficients between the zones ---*/ - //Set_TransferCoeff(Zones,config); - - //Buffer_Receive_nVertex_Donor = NULL; + /*--- Initialize transfer coefficients between the zones. ---*/ + Set_TransferCoeff(config); } -inline void CInterpolator::Set_TransferCoeff(CConfig **config) { } +void CInterpolator::Determine_ArraySize(bool faces, int markDonor, int markTarget, + unsigned long nVertexDonor, unsigned short nDim) { -void CInterpolator::Determine_ArraySize(bool faces, int markDonor, int markTarget, unsigned long nVertexDonor, unsigned short nDim) { unsigned long nLocalVertex_Donor = 0, nLocalFaceNodes_Donor=0, nLocalFace_Donor=0; unsigned long iVertex, iPointDonor = 0; /* Only needed if face data is also collected */ @@ -127,49 +69,51 @@ void CInterpolator::Determine_ArraySize(bool faces, int markDonor, int markTarge for (iVertex = 0; iVertex < nVertexDonor; iVertex++) { iPointDonor = donor_geometry->vertex[markDonor][iVertex]->GetNode(); - if (donor_geometry->node[iPointDonor]->GetDomain()) { - nLocalVertex_Donor++; - if (faces) { - /*--- On Donor geometry also communicate face info ---*/ - if (nDim==3) { - for (jElem=0; jElemnode[iPointDonor]->GetnElem(); jElem++) { - donor_elem = donor_geometry->node[iPointDonor]->GetElem(jElem); - nFaces = donor_geometry->elem[donor_elem]->GetnFaces(); - for (iFace=0; iFaceelem[donor_elem]->GetnNodesFace(iFace); - for (iDonor=0; iDonorelem[donor_elem]->GetFaces(iFace, iDonor); - jPoint = donor_geometry->elem[donor_elem]->GetNode(inode); - face_on_marker = (face_on_marker && (donor_geometry->node[jPoint]->GetVertex(markDonor) !=-1)); - } - if (face_on_marker ) { - nLocalFace_Donor++; - nLocalFaceNodes_Donor+=nNodes; - } - } + + if (!donor_geometry->node[iPointDonor]->GetDomain()) continue; + + nLocalVertex_Donor++; + + if (!faces) continue; + + /*--- On Donor geometry also communicate face info ---*/ + if (nDim==3) { + for (jElem=0; jElemnode[iPointDonor]->GetnElem(); jElem++) { + donor_elem = donor_geometry->node[iPointDonor]->GetElem(jElem); + nFaces = donor_geometry->elem[donor_elem]->GetnFaces(); + for (iFace=0; iFaceelem[donor_elem]->GetnNodesFace(iFace); + for (iDonor=0; iDonorelem[donor_elem]->GetFaces(iFace, iDonor); + jPoint = donor_geometry->elem[donor_elem]->GetNode(inode); + face_on_marker = (face_on_marker && (donor_geometry->node[jPoint]->GetVertex(markDonor) !=-1)); } - } - else { - /*--- in 2D we use the edges ---*/ - nNodes=2; - nFaces = donor_geometry->node[iPointDonor]->GetnPoint(); - for (iFace=0; iFacenode[iPointDonor]->GetEdge(iFace); - jPoint = donor_geometry->edge[inode]->GetNode(iDonor); - face_on_marker = (face_on_marker && (donor_geometry->node[jPoint]->GetVertex(markDonor) !=-1)); - } - if (face_on_marker ) { - nLocalFace_Donor++; - nLocalFaceNodes_Donor+=nNodes; - } + if (face_on_marker ) { + nLocalFace_Donor++; + nLocalFaceNodes_Donor+=nNodes; } } } } + else { + /*--- in 2D we use the edges ---*/ + nNodes=2; + nFaces = donor_geometry->node[iPointDonor]->GetnPoint(); + for (iFace=0; iFacenode[iPointDonor]->GetEdge(iFace); + jPoint = donor_geometry->edge[inode]->GetNode(iDonor); + face_on_marker = (face_on_marker && (donor_geometry->node[jPoint]->GetVertex(markDonor) !=-1)); + } + if (face_on_marker ) { + nLocalFace_Donor++; + nLocalFaceNodes_Donor+=nNodes; + } + } + } } Buffer_Send_nVertex_Donor[0] = nLocalVertex_Donor; @@ -180,34 +124,33 @@ void CInterpolator::Determine_ArraySize(bool faces, int markDonor, int markTarge /*--- Send Interface vertex information --*/ SU2_MPI::Allreduce(&nLocalVertex_Donor, &MaxLocalVertex_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_nVertex_Donor, 1, MPI_UNSIGNED_LONG, Buffer_Receive_nVertex_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_nVertex_Donor, 1, MPI_UNSIGNED_LONG, + Buffer_Receive_nVertex_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); if (faces) { SU2_MPI::Allreduce(&nLocalFace_Donor, &nGlobalFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); SU2_MPI::Allreduce(&nLocalFace_Donor, &MaxFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); SU2_MPI::Allreduce(&nLocalFaceNodes_Donor, &nGlobalFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); SU2_MPI::Allreduce(&nLocalFaceNodes_Donor, &MaxFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_nFace_Donor, 1, MPI_UNSIGNED_LONG, Buffer_Receive_nFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, Buffer_Receive_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_nFace_Donor, 1, MPI_UNSIGNED_LONG, + Buffer_Receive_nFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, + Buffer_Receive_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); MaxFace_Donor++; } } -void CInterpolator::Collect_VertexInfo(bool faces, int markDonor, int markTarget, unsigned long nVertexDonor, unsigned short nDim) -{ +void CInterpolator::Collect_VertexInfo(bool faces, int markDonor, int markTarget, + unsigned long nVertexDonor, unsigned short nDim) { + unsigned long iVertex, iPointDonor = 0, iVertexDonor, nBuffer_Coord, nBuffer_Point, nLocalVertex_Donor; unsigned short iDim; - /* Only needed if face data is also collected */ - const su2double *Normal = nullptr; - - for (iVertex = 0; iVertex < MaxLocalVertex_Donor; iVertex++) { - Buffer_Send_GlobalPoint[iVertex] = -1; - for (iDim = 0; iDim < nDim; iDim++) { - Buffer_Send_Coord[iVertex*nDim+iDim] = 0.0; - if (faces) - Buffer_Send_Normal[iVertex*nDim+iDim] = 0.0; - } - } + for (iVertex = 0; iVertex < MaxLocalVertex_Donor; iVertex++) Buffer_Send_GlobalPoint[iVertex] = -1; + + for (iVertex = 0; iVertex < MaxLocalVertex_Donor*nDim; iVertex++) Buffer_Send_Coord[iVertex] = 0.0; + + if(faces) + for (iVertex = 0; iVertex < MaxLocalVertex_Donor*nDim; iVertex++) Buffer_Send_Normal[iVertex] = 0.0; /*--- Copy coordinates and point to the auxiliar vector --*/ nLocalVertex_Donor = 0; @@ -220,7 +163,7 @@ void CInterpolator::Collect_VertexInfo(bool faces, int markDonor, int markTarget Buffer_Send_Coord[nLocalVertex_Donor*nDim+iDim] = donor_geometry->node[iPointDonor]->GetCoord(iDim); if (faces) { - Normal = donor_geometry->vertex[markDonor][iVertexDonor]->GetNormal(); + const su2double* Normal = donor_geometry->vertex[markDonor][iVertexDonor]->GetNormal(); for (iDim = 0; iDim < nDim; iDim++) Buffer_Send_Normal[nLocalVertex_Donor*nDim+iDim] = Normal[iDim]; } @@ -230,32 +173,25 @@ void CInterpolator::Collect_VertexInfo(bool faces, int markDonor, int markTarget nBuffer_Coord = MaxLocalVertex_Donor*nDim; nBuffer_Point = MaxLocalVertex_Donor; - SU2_MPI::Allgather(Buffer_Send_Coord, nBuffer_Coord, MPI_DOUBLE, Buffer_Receive_Coord, nBuffer_Coord, MPI_DOUBLE, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_GlobalPoint, nBuffer_Point, MPI_LONG, Buffer_Receive_GlobalPoint, nBuffer_Point, MPI_LONG, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_Coord, nBuffer_Coord, MPI_DOUBLE, + Buffer_Receive_Coord, nBuffer_Coord, MPI_DOUBLE, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_GlobalPoint, nBuffer_Point, MPI_LONG, + Buffer_Receive_GlobalPoint, nBuffer_Point, MPI_LONG, MPI_COMM_WORLD); if (faces) { - SU2_MPI::Allgather(Buffer_Send_Normal, nBuffer_Coord, MPI_DOUBLE, Buffer_Receive_Normal, nBuffer_Coord, MPI_DOUBLE, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_Normal, nBuffer_Coord, MPI_DOUBLE, + Buffer_Receive_Normal, nBuffer_Coord, MPI_DOUBLE, MPI_COMM_WORLD); } } -int CInterpolator::Find_InterfaceMarker(CConfig *config, unsigned short val_marker_interface) { - - unsigned short nMarker = config->GetnMarker_All(); - unsigned short iMarker; - - for (iMarker = 0; iMarker < nMarker; iMarker++) { - - /*--- If the tag GetMarker_All_ZoneInterface(iMarker) equals the index we are looping at ---*/ - if (config->GetMarker_All_ZoneInterface(iMarker) == val_marker_interface ) { +int CInterpolator::Find_InterfaceMarker(const CConfig *config, unsigned short val_marker_interface) const { - /*--- We have identified the identifier for the interface marker ---*/ - return iMarker; - } + for (unsigned short iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { + /*--- If the tag GetMarker_All_ZoneInterface(iMarker) equals the interface we are looking for. ---*/ + if (config->GetMarker_All_ZoneInterface(iMarker) == val_marker_interface) return iMarker; } - return -1; } - void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ CGeometry *geom = Geometry[val_zone][INST_0][MESH_0]; @@ -364,13 +300,8 @@ void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ /*--- Reconstruct boundary by gathering data from all ranks ---*/ -#ifdef HAVE_MPI SU2_MPI::Allreduce( &nLocalVertex, &nGlobalVertex, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); SU2_MPI::Allreduce(&nLocalLinkedNodes, &nGlobalLinkedNodes, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); -#else - nGlobalVertex = nLocalVertex; - nGlobalLinkedNodes = nLocalLinkedNodes; -#endif Buffer_Receive_Coord = new su2double [ nGlobalVertex * nDim ]; Buffer_Receive_GlobalPoint = new long[ nGlobalVertex ]; @@ -472,86 +403,34 @@ void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ } } -#ifdef HAVE_MPI - SU2_MPI::Bcast( Buffer_Receive_Coord, nGlobalVertex * nDim, MPI_DOUBLE, 0, MPI_COMM_WORLD); - SU2_MPI::Bcast(Buffer_Receive_GlobalPoint, nGlobalVertex, MPI_LONG, 0, MPI_COMM_WORLD); - SU2_MPI::Bcast( Buffer_Receive_Proc, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD ); - - SU2_MPI::Bcast( Buffer_Receive_nLinkedNodes, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); - SU2_MPI::Bcast(Buffer_Receive_StartLinkedNodes, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); - SU2_MPI::Bcast( Buffer_Receive_LinkedNodes, nGlobalLinkedNodes, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); -#endif - - if( Buffer_Send_Coord != NULL) {delete [] Buffer_Send_Coord; Buffer_Send_Coord = NULL;} - if( Buffer_Send_GlobalPoint != NULL) {delete [] Buffer_Send_GlobalPoint; Buffer_Send_GlobalPoint = NULL;} - if( Buffer_Send_LinkedNodes != NULL) {delete [] Buffer_Send_LinkedNodes; Buffer_Send_LinkedNodes = NULL;} - if( Buffer_Send_nLinkedNodes != NULL) {delete [] Buffer_Send_nLinkedNodes; Buffer_Send_nLinkedNodes = NULL;} - if( Buffer_Send_StartLinkedNodes != NULL) {delete [] Buffer_Send_StartLinkedNodes; Buffer_Send_StartLinkedNodes = NULL;} -} - -bool CInterpolator::CheckInterfaceBoundary(int markDonor, int markTarget){ - - int Donor_check, Target_check; - - #ifdef HAVE_MPI - - int *Buffer_Recv_mark = NULL; - int iRank, nProcessor = size; - - if (rank == MASTER_NODE) - Buffer_Recv_mark = new int[nProcessor]; - - Donor_check = -1; - Target_check = -1; - - /*--- We gather a vector in MASTER_NODE to determine whether the boundary is not on the processor because of the partition or because the zone does not include it ---*/ - - SU2_MPI::Gather(&markDonor , 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - - if (rank == MASTER_NODE) - for (iRank = 0; iRank < nProcessor; iRank++) - if( Buffer_Recv_mark[iRank] != -1 ){ - Donor_check = Buffer_Recv_mark[iRank]; - break; - } - - SU2_MPI::Bcast(&Donor_check , 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); + SU2_MPI::Bcast(Buffer_Receive_GlobalPoint, nGlobalVertex, MPI_LONG, 0, MPI_COMM_WORLD); + SU2_MPI::Bcast(Buffer_Receive_Coord, nGlobalVertex*nDim, MPI_DOUBLE, 0, MPI_COMM_WORLD); + SU2_MPI::Bcast(Buffer_Receive_Proc, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + SU2_MPI::Bcast(Buffer_Receive_nLinkedNodes, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + SU2_MPI::Bcast(Buffer_Receive_StartLinkedNodes, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + SU2_MPI::Bcast(Buffer_Receive_LinkedNodes, nGlobalLinkedNodes, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); - SU2_MPI::Gather(&markTarget, 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); + delete [] Buffer_Send_Coord; Buffer_Send_Coord = NULL; + delete [] Buffer_Send_GlobalPoint; Buffer_Send_GlobalPoint = NULL; + delete [] Buffer_Send_LinkedNodes; Buffer_Send_LinkedNodes = NULL; + delete [] Buffer_Send_nLinkedNodes; Buffer_Send_nLinkedNodes = NULL; + delete [] Buffer_Send_StartLinkedNodes; Buffer_Send_StartLinkedNodes = NULL; - if (rank == MASTER_NODE) - for (iRank = 0; iRank < nProcessor; iRank++) - if( Buffer_Recv_mark[iRank] != -1 ){ - Target_check = Buffer_Recv_mark[iRank]; - break; - } - - - SU2_MPI::Bcast(&Target_check, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - - if (rank == MASTER_NODE) - delete [] Buffer_Recv_mark; +} -#else - Donor_check = markDonor; - Target_check = markTarget; -#endif +bool CInterpolator::CheckInterfaceBoundary(int markDonor, int markTarget) const { - if(Target_check == -1 || Donor_check == -1) - return false; - else - return true; + /*--- Determine whether the boundary is not on the rank because of + * the partition or because it is not part of the zone. ---*/ + int donorCheck = -1, targetCheck = -1; + SU2_MPI::Allreduce(&markDonor, &donorCheck, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); + SU2_MPI::Allreduce(&markTarget, &targetCheck, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); + return (donorCheck != -1) && (targetCheck != -1); } -CNearestNeighbor::CNearestNeighbor(void): CInterpolator() { } - CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, - unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { - - /*--- Initialize transfer coefficients between the zones. ---*/ - Set_TransferCoeff(config); -} + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { } void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { @@ -656,18 +535,11 @@ void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { -CIsoparametric::CIsoparametric(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { - - /*--- Initialize transfer coefficients between the zones ---*/ - Set_TransferCoeff(config); - - /*--- For fluid-structure interaction data interpolated with have nDim dimensions ---*/ - // InitializeData(Zones,nDim); -} - -CIsoparametric::~CIsoparametric() {} +CIsoparametric::CIsoparametric(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { } void CIsoparametric::Set_TransferCoeff(CConfig **config) { + unsigned long iVertex, jVertex; unsigned long dPoint, inode, jElem, nElem; unsigned short iDim, iDonor=0, iFace; @@ -719,14 +591,13 @@ void CIsoparametric::Set_TransferCoeff(CConfig **config) { */ /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ - markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); + markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); /*--- On the target side: find the tag of the boundary sharing the interface ---*/ markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); /*--- Checks if the zone contains the interface, if not continue to the next step ---*/ - if( !CheckInterfaceBoundary(markDonor, markTarget) ) - continue; + if(!CheckInterfaceBoundary(markDonor, markTarget)) continue; if(markDonor != -1) nVertexDonor = donor_geometry->GetnVertex( markDonor ); @@ -863,118 +734,112 @@ void CIsoparametric::Set_TransferCoeff(CConfig **config) { } //Buffer_Send_FaceIndex[nLocalFace_Donor+1] = MaxFaceNodes_Donor*rank+nLocalFaceNodes_Donor; -#ifdef HAVE_MPI - SU2_MPI::Allgather(Buffer_Send_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, Buffer_Receive_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_FaceProc, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, Buffer_Receive_FaceProc, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, Buffer_Receive_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); -#else - for (iFace=0; iFacevertex[markTarget][iVertex]->GetNode(); - if (target_geometry->node[Point_Target]->GetDomain()) { + if (!target_geometry->node[Point_Target]->GetDomain()) continue; - Coord_i = target_geometry->node[Point_Target]->GetCoord(); - /*---Loop over the faces previously communicated/stored ---*/ - for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { + Coord_i = target_geometry->node[Point_Target]->GetCoord(); + /*---Loop over the faces previously communicated/stored ---*/ + for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { - nFaces = (unsigned int)Buffer_Receive_nFace_Donor[iProcessor]; + nFaces = (unsigned int)Buffer_Receive_nFace_Donor[iProcessor]; - for (iFace = 0; iFace< nFaces; iFace++) { - /*--- ---*/ + for (iFace = 0; iFace< nFaces; iFace++) { + /*--- ---*/ - nNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1] - - (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; + nNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1] - + (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; - su2double *X = new su2double[nNodes*(nDim+1)]; - faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face - for (iDonor=0; iDonorvertex[markTarget][iVertex]->SetDonorElem(donor_elem); // in 2D is nearest neighbor - target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nNodes); - for (iDonor=0; iDonorvertex[markTarget][iVertex]->SetDonorElem(donor_elem); // in 2D is nearest neighbor + target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nNodes); + for (iDonor=0; iDonorvertex[markTarget][iVertex]->GetnDonorPoints(); - target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); - - for (iDonor=0; iDonorvertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor,storeGlobal[iDonor]); - //cout <vertex[markTarget][iVertex]->SetDonorCoeff(iDonor,storeCoeff[iDonor]); - target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]); - } + /*--- Set the appropriate amount of memory and fill ---*/ + nNodes =target_geometry->vertex[markTarget][iVertex]->GetnDonorPoints(); + target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); + + for (iDonor=0; iDonorvertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor,storeGlobal[iDonor]); + //cout <vertex[markTarget][iVertex]->SetDonorCoeff(iDonor,storeCoeff[iDonor]); + target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]); } + } delete[] Buffer_Send_nVertex_Donor; @@ -1008,7 +873,7 @@ void CIsoparametric::Set_TransferCoeff(CConfig **config) { } void CIsoparametric::Isoparameters(unsigned short nDim, unsigned short nDonor, - su2double *X, su2double *xj, su2double *isoparams) { + su2double *X, su2double *xj, su2double *isoparams) { short iDonor,iDim,k; // indices su2double tmp, tmp2; @@ -1191,7 +1056,7 @@ void CIsoparametric::Isoparameters(unsigned short nDim, unsigned short nDonor, delete [] Q; delete [] R; delete [] A; - if (A2 != NULL) delete [] A2; + delete [] A2; delete [] x2; delete [] test; @@ -1199,16 +1064,8 @@ void CIsoparametric::Isoparameters(unsigned short nDim, unsigned short nDonor, } - -/* Mirror Interpolator */ -CMirror::CMirror(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { - - /*--- Initialize transfer coefficients between the zones ---*/ - Set_TransferCoeff(config); - -} - -CMirror::~CMirror() {} +CMirror::CMirror(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { } void CMirror::Set_TransferCoeff(CConfig **config) { unsigned long iVertex, jVertex; @@ -1241,9 +1098,9 @@ void CMirror::Set_TransferCoeff(CConfig **config) { /*--- For the number of markers on the interface... ---*/ for (iMarkerInt=1; iMarkerInt <= nMarkerInt; iMarkerInt++) { /*--- Procedure: - * -Loop through vertices of the aero grid - * -Find nearest element and allocate enough space in the aero grid donor point info - * -set the transfer coefficient values + * - Loop through vertices of the aero grid + * - Find nearest element and allocate enough space in the aero grid donor point info + * - Set the transfer coefficient values */ /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ @@ -1253,8 +1110,7 @@ void CMirror::Set_TransferCoeff(CConfig **config) { markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); /*--- Checks if the zone contains the interface, if not continue to the next step ---*/ - if( !CheckInterfaceBoundary(markDonor, markTarget) ) - continue; + if(!CheckInterfaceBoundary(markDonor, markTarget)) continue; if(markDonor != -1) nVertexDonor = donor_geometry->GetnVertex( markDonor ); @@ -1348,21 +1204,15 @@ void CMirror::Set_TransferCoeff(CConfig **config) { } } -#ifdef HAVE_MPI - SU2_MPI::Allgather(Buffer_Send_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, Buffer_Receive_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_GlobalPoint, MaxFaceNodes_Donor, MPI_LONG,Buffer_Receive_GlobalPoint, MaxFaceNodes_Donor, MPI_LONG, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_Coeff, MaxFaceNodes_Donor, MPI_DOUBLE,Buffer_Receive_Coeff, MaxFaceNodes_Donor, MPI_DOUBLE, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, Buffer_Receive_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); -#else - for (iFace=0; iFaceGetnVertex( markTarget ); @@ -1816,8 +1657,8 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config){ target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){ - target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff( iDonor, Coeff_Vect[iDonor]); - target_geometry->vertex[markTarget][iVertex]->SetInterpDonorPoint( iDonor, Donor_GlobalPoint[ Donor_Vect[iDonor] ]); + target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor, Coeff_Vect[iDonor]); + target_geometry->vertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor, Donor_GlobalPoint[Donor_Vect[iDonor]]); target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]); } } @@ -1842,266 +1683,264 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config){ target_iPoint = target_geometry->vertex[markTarget][iVertex]->GetNode(); - if (target_geometry->node[target_iPoint]->GetDomain()){ + if (!target_geometry->node[target_iPoint]->GetDomain()) continue; - Coord_i = target_geometry->node[target_iPoint]->GetCoord(); + Coord_i = target_geometry->node[target_iPoint]->GetCoord(); - target_geometry->vertex[markTarget][iVertex]->GetNormal(Normal); - - /*--- The value of Area computed here includes also portion of boundary belonging to different marker ---*/ - Area = 0.0; - for (iDim = 0; iDim < nDim; iDim++) - Area += Normal[iDim]*Normal[iDim]; - Area = sqrt(Area); - - for (iDim = 0; iDim < nDim; iDim++) - Normal[iDim] /= Area; + target_geometry->vertex[markTarget][iVertex]->GetNormal(Normal); - for (iDim = 0; iDim < nDim; iDim++) - Coord_i[iDim] = target_geometry->node[target_iPoint]->GetCoord(iDim); - - long dPoint = target_geometry->node[target_iPoint]->GetGlobalIndex(); - for (target_iPoint = 0; target_iPoint < nGlobalVertex_Target; target_iPoint++){ - if( dPoint == Target_GlobalPoint[target_iPoint] ) - break; - } - - /*--- Build local surface dual mesh for target element ---*/ + /*--- The value of Area computed here includes also portion of boundary belonging to different marker ---*/ + Area = 0.0; + for (iDim = 0; iDim < nDim; iDim++) + Area += Normal[iDim]*Normal[iDim]; + Area = sqrt(Area); + + for (iDim = 0; iDim < nDim; iDim++) + Normal[iDim] /= Area; + + for (iDim = 0; iDim < nDim; iDim++) + Coord_i[iDim] = target_geometry->node[target_iPoint]->GetCoord(iDim); - nEdges_target = Target_nLinkedNodes[target_iPoint]; + long dPoint = target_geometry->node[target_iPoint]->GetGlobalIndex(); + for (target_iPoint = 0; target_iPoint < nGlobalVertex_Target; target_iPoint++){ + if( dPoint == Target_GlobalPoint[target_iPoint] ) + break; + } + + /*--- Build local surface dual mesh for target element ---*/ + + nEdges_target = Target_nLinkedNodes[target_iPoint]; - nNode_target = 2*(nEdges_target + 1); + nNode_target = 2*(nEdges_target + 1); + + target_element = new su2double*[nNode_target]; + for (ii = 0; ii < nNode_target; ii++) + target_element[ii] = new su2double[nDim]; - target_element = new su2double*[nNode_target]; - for (ii = 0; ii < nNode_target; ii++) - target_element[ii] = new su2double[nDim]; - - nNode_target = Build_3D_surface_element(Target_LinkedNodes, Target_StartLinkedNodes, Target_nLinkedNodes, TargetPoint_Coord, target_iPoint, target_element); + nNode_target = Build_3D_surface_element(Target_LinkedNodes, Target_StartLinkedNodes, Target_nLinkedNodes, TargetPoint_Coord, target_iPoint, target_element); - /*--- Brute force to find the closest donor_node ---*/ + /*--- Brute force to find the closest donor_node ---*/ - mindist = 1E6; - donor_StartIndex = 0; - - for (donor_iPoint = 0; donor_iPoint < nGlobalVertex_Donor; donor_iPoint++) { - - Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ]; + mindist = 1E6; + donor_StartIndex = 0; - dist = PointsDistance(nDim, Coord_i, Coord_j); + for (donor_iPoint = 0; donor_iPoint < nGlobalVertex_Donor; donor_iPoint++) { + + Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ]; - if (dist < mindist) { - mindist = dist; - donor_StartIndex = donor_iPoint; - } + dist = PointsDistance(nDim, Coord_i, Coord_j); - if (dist == 0.0){ - donor_StartIndex = donor_iPoint; - break; - } + if (dist < mindist) { + mindist = dist; + donor_StartIndex = donor_iPoint; } - - donor_iPoint = donor_StartIndex; - nEdges_donor = Donor_nLinkedNodes[donor_iPoint]; + if (dist == 0.0){ + donor_StartIndex = donor_iPoint; + break; + } + } + + donor_iPoint = donor_StartIndex; - donor_element = new su2double*[ 2*nEdges_donor + 2 ]; - for (ii = 0; ii < 2*nEdges_donor + 2; ii++) - donor_element[ii] = new su2double[nDim]; + nEdges_donor = Donor_nLinkedNodes[donor_iPoint]; - nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, DonorPoint_Coord, donor_iPoint, donor_element); + donor_element = new su2double*[ 2*nEdges_donor + 2 ]; + for (ii = 0; ii < 2*nEdges_donor + 2; ii++) + donor_element[ii] = new su2double[nDim]; - Area = 0; - for (ii = 1; ii < nNode_target-1; ii++){ - for (jj = 1; jj < nNode_donor-1; jj++){ - Area += Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], donor_element[0], donor_element[jj], donor_element[jj+1], Normal); - //cout << Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], donor_element[0], donor_element[jj], donor_element[jj+1], Normal) << endl; - } + nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, DonorPoint_Coord, donor_iPoint, donor_element); + + Area = 0; + for (ii = 1; ii < nNode_target-1; ii++){ + for (jj = 1; jj < nNode_donor-1; jj++){ + Area += Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], donor_element[0], donor_element[jj], donor_element[jj+1], Normal); + //cout << Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], donor_element[0], donor_element[jj], donor_element[jj+1], Normal) << endl; } + } - for (ii = 0; ii < 2*nEdges_donor + 2; ii++) - delete [] donor_element[ii]; - delete [] donor_element; + for (ii = 0; ii < 2*nEdges_donor + 2; ii++) + delete [] donor_element[ii]; + delete [] donor_element; - nDonorPoints = 1; + nDonorPoints = 1; - /*--- In case the element intersect the target cell update the auxiliary communication data structure ---*/ + /*--- In case the element intersect the target cell update the auxiliary communication data structure ---*/ - Coeff_Vect = new su2double[ nDonorPoints ]; - Donor_Vect = new unsigned long[ nDonorPoints ]; - storeProc = new unsigned long[ nDonorPoints ]; + Coeff_Vect = new su2double[ nDonorPoints ]; + Donor_Vect = new unsigned long[ nDonorPoints ]; + storeProc = new unsigned long[ nDonorPoints ]; - Coeff_Vect[0] = Area; - Donor_Vect[0] = donor_iPoint; - storeProc[0] = Donor_Proc[donor_iPoint]; + Coeff_Vect[0] = Area; + Donor_Vect[0] = donor_iPoint; + storeProc[0] = Donor_Proc[donor_iPoint]; - alreadyVisitedDonor = new unsigned long[1]; + alreadyVisitedDonor = new unsigned long[1]; - alreadyVisitedDonor[0] = donor_iPoint; - nAlreadyVisited = 1; - StartVisited = 0; + alreadyVisitedDonor[0] = donor_iPoint; + nAlreadyVisited = 1; + StartVisited = 0; - Area_old = -1; - - while( Area > Area_old ){ + Area_old = -1; + + while( Area > Area_old ){ - /* - * - Starting from the closest donor_point, it expands the supermesh by a countour search pattern. - * - The closest donor element becomes the core, at each iteration a new layer of elements around the core is taken into account - */ + /* + * - Starting from the closest donor_point, it expands the supermesh by a countour search pattern. + * - The closest donor element becomes the core, at each iteration a new layer of elements around the core is taken into account + */ - Area_old = Area; + Area_old = Area; - ToVisit = NULL; - nToVisit = 0; + ToVisit = NULL; + nToVisit = 0; - for( iNodeVisited = StartVisited; iNodeVisited < nAlreadyVisited; iNodeVisited++ ){ + for( iNodeVisited = StartVisited; iNodeVisited < nAlreadyVisited; iNodeVisited++ ){ - vPoint = alreadyVisitedDonor[ iNodeVisited ]; - - nEdgeVisited = Donor_nLinkedNodes[vPoint]; - - for (iEdgeVisited = 0; iEdgeVisited < nEdgeVisited; iEdgeVisited++){ + vPoint = alreadyVisitedDonor[ iNodeVisited ]; + + nEdgeVisited = Donor_nLinkedNodes[vPoint]; - donor_iPoint = Donor_LinkedNodes[ Donor_StartLinkedNodes[vPoint] + iEdgeVisited]; + for (iEdgeVisited = 0; iEdgeVisited < nEdgeVisited; iEdgeVisited++){ - /*--- Check if the node to visit is already listed in the data structure to avoid double visits ---*/ + donor_iPoint = Donor_LinkedNodes[ Donor_StartLinkedNodes[vPoint] + iEdgeVisited]; - check = 0; + /*--- Check if the node to visit is already listed in the data structure to avoid double visits ---*/ - for( jj = 0; jj < nAlreadyVisited; jj++ ){ - if( donor_iPoint == alreadyVisitedDonor[jj] ){ - check = 1; - break; - } - } + check = 0; - if( check == 0 && ToVisit != NULL){ - for( jj = 0; jj < nToVisit; jj++ ) - if( donor_iPoint == ToVisit[jj] ){ - check = 1; - break; - } + for( jj = 0; jj < nAlreadyVisited; jj++ ){ + if( donor_iPoint == alreadyVisitedDonor[jj] ){ + check = 1; + break; } + } - if( check == 0 ){ - /*--- If the node was not already visited, visit it and list it into data structure ---*/ - - tmpVect = new unsigned long[ nToVisit + 1 ]; + if( check == 0 && ToVisit != NULL){ + for( jj = 0; jj < nToVisit; jj++ ) + if( donor_iPoint == ToVisit[jj] ){ + check = 1; + break; + } + } - for( jj = 0; jj < nToVisit; jj++ ) - tmpVect[jj] = ToVisit[jj]; - tmpVect[nToVisit] = donor_iPoint; + if( check == 0 ){ + /*--- If the node was not already visited, visit it and list it into data structure ---*/ - if( ToVisit != NULL ) - delete [] ToVisit; - - ToVisit = tmpVect; - tmpVect = NULL; + tmpVect = new unsigned long[ nToVisit + 1 ]; - nToVisit++; + for( jj = 0; jj < nToVisit; jj++ ) + tmpVect[jj] = ToVisit[jj]; + tmpVect[nToVisit] = donor_iPoint; - /*--- Find the value of the intersection area between the current donor element and the target element --- */ + if( ToVisit != NULL ) + delete [] ToVisit; + + ToVisit = tmpVect; + tmpVect = NULL; - nEdges_donor = Donor_nLinkedNodes[donor_iPoint]; + nToVisit++; - donor_element = new su2double*[ 2*nEdges_donor + 2 ]; - for (ii = 0; ii < 2*nEdges_donor + 2; ii++) - donor_element[ii] = new su2double[nDim]; + /*--- Find the value of the intersection area between the current donor element and the target element --- */ - nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, DonorPoint_Coord, donor_iPoint, donor_element); + nEdges_donor = Donor_nLinkedNodes[donor_iPoint]; - tmp_Area = 0; - for (ii = 1; ii < nNode_target-1; ii++) - for (jj = 1; jj < nNode_donor-1; jj++) - tmp_Area += Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], donor_element[0], donor_element[jj], donor_element[jj+1], Normal); + donor_element = new su2double*[ 2*nEdges_donor + 2 ]; + for (ii = 0; ii < 2*nEdges_donor + 2; ii++) + donor_element[ii] = new su2double[nDim]; - for (ii = 0; ii < 2*nEdges_donor + 2; ii++) - delete [] donor_element[ii]; - delete [] donor_element; - - /*--- In case the element intersect the target cell update the auxiliary communication data structure ---*/ + nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, DonorPoint_Coord, donor_iPoint, donor_element); - tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ]; - tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ]; - tmp_storeProc = new unsigned long[ nDonorPoints + 1 ]; - - for( iDonor = 0; iDonor < nDonorPoints; iDonor++){ - tmp_Donor_Vect[iDonor] = Donor_Vect[iDonor]; - tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor]; - tmp_storeProc[iDonor] = storeProc[iDonor]; - } - - tmp_Coeff_Vect[ nDonorPoints ] = tmp_Area; - tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint; - tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint]; + tmp_Area = 0; + for (ii = 1; ii < nNode_target-1; ii++) + for (jj = 1; jj < nNode_donor-1; jj++) + tmp_Area += Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], donor_element[0], donor_element[jj], donor_element[jj+1], Normal); - if (Donor_Vect != NULL) {delete [] Donor_Vect; } - if (Coeff_Vect != NULL) {delete [] Coeff_Vect; } - if (storeProc != NULL) {delete [] storeProc; } + for (ii = 0; ii < 2*nEdges_donor + 2; ii++) + delete [] donor_element[ii]; + delete [] donor_element; - Donor_Vect = tmp_Donor_Vect; - Coeff_Vect = tmp_Coeff_Vect; - storeProc = tmp_storeProc; + /*--- In case the element intersect the target cell update the auxiliary communication data structure ---*/ - tmp_Coeff_Vect = NULL; - tmp_Donor_Vect = NULL; - tmp_storeProc = NULL; + tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ]; + tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ]; + tmp_storeProc = new unsigned long[ nDonorPoints + 1 ]; - nDonorPoints++; - - Area += tmp_Area; + for( iDonor = 0; iDonor < nDonorPoints; iDonor++){ + tmp_Donor_Vect[iDonor] = Donor_Vect[iDonor]; + tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor]; + tmp_storeProc[iDonor] = storeProc[iDonor]; } - } - } + + tmp_Coeff_Vect[ nDonorPoints ] = tmp_Area; + tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint; + tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint]; + + if (Donor_Vect != NULL) {delete [] Donor_Vect; } + if (Coeff_Vect != NULL) {delete [] Coeff_Vect; } + if (storeProc != NULL) {delete [] storeProc; } + + Donor_Vect = tmp_Donor_Vect; + Coeff_Vect = tmp_Coeff_Vect; + storeProc = tmp_storeProc; + + tmp_Coeff_Vect = NULL; + tmp_Donor_Vect = NULL; + tmp_storeProc = NULL; - /*--- Update auxiliary data structure ---*/ + nDonorPoints++; - StartVisited = nAlreadyVisited; + Area += tmp_Area; + } + } + } - tmpVect = new unsigned long[ nAlreadyVisited + nToVisit ]; + /*--- Update auxiliary data structure ---*/ - for( jj = 0; jj < nAlreadyVisited; jj++ ) - tmpVect[jj] = alreadyVisitedDonor[jj]; - - for( jj = 0; jj < nToVisit; jj++ ) - tmpVect[ nAlreadyVisited + jj ] = ToVisit[jj]; + StartVisited = nAlreadyVisited; - if( alreadyVisitedDonor != NULL ) - delete [] alreadyVisitedDonor; + tmpVect = new unsigned long[ nAlreadyVisited + nToVisit ]; - alreadyVisitedDonor = tmpVect; + for( jj = 0; jj < nAlreadyVisited; jj++ ) + tmpVect[jj] = alreadyVisitedDonor[jj]; + + for( jj = 0; jj < nToVisit; jj++ ) + tmpVect[ nAlreadyVisited + jj ] = ToVisit[jj]; - nAlreadyVisited += nToVisit; + if( alreadyVisitedDonor != NULL ) + delete [] alreadyVisitedDonor; - delete [] ToVisit; - } + alreadyVisitedDonor = tmpVect; - delete [] alreadyVisitedDonor; - - /*--- Set the communication data structure and copy data from the auxiliary vectors ---*/ + nAlreadyVisited += nToVisit; - target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nDonorPoints); - target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); + delete [] ToVisit; + } - for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){ - target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor, Coeff_Vect[iDonor]/Area); - target_geometry->vertex[markTarget][iVertex]->SetInterpDonorPoint( iDonor, Donor_GlobalPoint[ Donor_Vect[iDonor] ] ); - target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]); - //cout <vertex[markTarget][iVertex]->SetnDonorPoints(nDonorPoints); + target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); + + for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){ + target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor, Coeff_Vect[iDonor]/Area); + target_geometry->vertex[markTarget][iVertex]->SetInterpDonorPoint( iDonor, Donor_GlobalPoint[ Donor_Vect[iDonor] ] ); + target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]); + //cout <GetnVertex(mark_donor); From b6da6e930b8bc4cd8c5dd29df89c58379322371e Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 18 Mar 2020 11:50:30 +0000 Subject: [PATCH 028/112] strip trailing spaces --- Common/include/interpolation_structure.hpp | 24 +- Common/src/interpolation_structure.cpp | 376 ++++++++++----------- 2 files changed, 200 insertions(+), 200 deletions(-) diff --git a/Common/include/interpolation_structure.hpp b/Common/include/interpolation_structure.hpp index 3f8a01cba646..115e477ef46b 100644 --- a/Common/include/interpolation_structure.hpp +++ b/Common/include/interpolation_structure.hpp @@ -7,7 +7,7 @@ * * SU2 Project Website: https://su2code.github.io * - * The SU2 Project is maintained by the SU2 Foundation + * The SU2 Project is maintained by the SU2 Foundation * (http://su2foundation.org) * * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md) @@ -83,13 +83,13 @@ class CInterpolator { *Buffer_Send_Normal, /*!< \brief Buffer to send normal vector values */ *Buffer_Receive_Coord, /*!< \brief Buffer to receive coordinate values*/ *Buffer_Receive_Normal; /*!< \brief Buffer to receive normal vector values*/ - + unsigned long *Receive_GlobalPoint, /*!< \brief Buffer to receive Global point indexes*/ *Buffer_Receive_nLinkedNodes, /*!< \brief Buffer to receive the number of edges connected to each node*/ *Buffer_Receive_LinkedNodes, /*!< \brief Buffer to receive the list of notes connected to the nodes through an edge*/ *Buffer_Receive_StartLinkedNodes, /*!< \brief Buffer to receive the index of the Receive_LinkedNodes buffer where corresponding list of linked nodes begins */ *Buffer_Receive_Proc; /*!< \brief Buffer to receive the thread that owns the node*/ - + unsigned long nGlobalVertex_Target, /*!< \brief Global number of vertex of the target boundary*/ nLocalVertex_Target, /*!< \brief Number of vertex of the target boundary owned by the thread*/ nGlobalVertex_Donor, /*!< \brief Global number of vertex of the donor boundary*/ @@ -140,7 +140,7 @@ class CInterpolator { * \brief Check whether an interface should be processed or not, i.e. if it is part of the zones. * \param[in] val_markDonor - Marker tag from donor zone. * \param[in] val_markTarget - Marker tag from target zone. - */ + */ bool CheckInterfaceBoundary(int val_markDonor, int val_markTarget) const; /*! @@ -305,15 +305,15 @@ class CSlidingMesh final : public CInterpolator { /*! * \brief For 3-Dimensional grids, build the dual surface element * \param[in] map - array containing the index of the boundary points connected to the node - * \param[in] startIndex - for each vertex specifies the corresponding index in the global array containing the indexes of all its neighbouring vertexes + * \param[in] startIndex - for each vertex specifies the corresponding index in the global array containing the indexes of all its neighbouring vertexes * \param[in] nNeighbour - for each vertex specifies the number of its neighbouring vertexes (on the boundary) * \param[in] coord - array containing the coordinates of all the boundary vertexes * \param[in] centralNode - label of the vertex around which the dual surface element is built * \param[in] element - double array where element node coordinates will be stored - */ + */ int Build_3D_surface_element(unsigned long *map, unsigned long *startIndex, unsigned long* nNeighbor, su2double *coord, unsigned long centralNode, su2double** element); - + /*! * \brief For 2-Dimensional grids, compute intersection length of two segments projected along a given direction * \param[in] A1 - first point of segment A @@ -323,7 +323,7 @@ class CSlidingMesh final : public CInterpolator { * \param[in] Direction - along which segments are projected */ su2double ComputeLineIntersectionLength(su2double* A1, su2double* A2, su2double* B1, su2double* B2, su2double* Direction); - + /*! * \brief For 3-Dimensional grids, compute intersection area between two triangle projected on a given plane * \param[in] A1 - first point of triangle A @@ -335,7 +335,7 @@ class CSlidingMesh final : public CInterpolator { * \param[in] Direction - vector normal to projection plane */ su2double Compute_Triangle_Intersection(su2double* A1, su2double* A2, su2double* A3, su2double* B1, su2double* B2, su2double* B3, su2double* Direction); - + /*! * \brief For 3-Dimensional grids, compute intersection area between two triangle projected on a given plane * P1 from triangle P MUST be inside triangle Q, points order doesn't matter @@ -347,7 +347,7 @@ class CSlidingMesh final : public CInterpolator { * \param[in] Q3 - third point of triangle B */ su2double ComputeIntersectionArea( su2double* P1, su2double* P2, su2double* P3, su2double* Q1, su2double* Q2, su2double* Q3 ); - + /*! * \brief For 2-Dimensional grids, check whether, and compute, two lines are intersecting * \param[in] A1 - first defining first line @@ -357,7 +357,7 @@ class CSlidingMesh final : public CInterpolator { * \param[in] IntersectionPoint - Container for intersection coordinates */ void ComputeLineIntersectionPoint( su2double* A1, su2double* A2, su2double* B1, su2double* B2, su2double* IntersectionPoint ); - + /*! * \brief For N-Dimensional grids, check whether a point is inside a triangle specified by 3 T points * \param[in] Point - query point @@ -482,7 +482,7 @@ class CSymmetricMatrix{ inline int GetSize() const { return sz; } inline passivedouble Get(int i, int j) const { return val_vec[IdxSym(i,j)]; } - + inline void Set(int i, int j, passivedouble val) { val_vec[IdxSym(i,j)] = val; } inline passivedouble& operator() (int i, int j) { return val_vec[IdxSym(i,j)]; } diff --git a/Common/src/interpolation_structure.cpp b/Common/src/interpolation_structure.cpp index 04f210167e2f..e88bcef4e1f4 100644 --- a/Common/src/interpolation_structure.cpp +++ b/Common/src/interpolation_structure.cpp @@ -6,7 +6,7 @@ * * SU2 Project Website: https://su2code.github.io * - * The SU2 Project is maintained by the SU2 Foundation + * The SU2 Project is maintained by the SU2 Foundation * (http://su2foundation.org) * * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md) @@ -193,26 +193,26 @@ int CInterpolator::Find_InterfaceMarker(const CConfig *config, unsigned short va } void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ - + CGeometry *geom = Geometry[val_zone][INST_0][MESH_0]; - + unsigned long iVertex, jVertex, kVertex; - + unsigned long count, iTmp, *uptr, dPoint, EdgeIndex, jEdge, nEdges, nNodes, nVertex, iDim, nDim, iPoint; - + unsigned long nGlobalLinkedNodes, nLocalVertex, nLocalLinkedNodes; - + nDim = geom->GetnDim(); - + if( val_marker != -1 ) nVertex = geom->GetnVertex( val_marker ); else nVertex = 0; - - + + su2double *Buffer_Send_Coord = new su2double [ nVertex * nDim ]; unsigned long *Buffer_Send_GlobalPoint = new unsigned long [ nVertex ]; - + unsigned long *Buffer_Send_nLinkedNodes = new unsigned long [ nVertex ]; unsigned long *Buffer_Send_StartLinkedNodes = new unsigned long [ nVertex ]; unsigned long **Aux_Send_Map = new unsigned long*[ nVertex ]; @@ -221,29 +221,29 @@ void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ int nProcessor = size, iRank; unsigned long iTmp2, tmp_index, tmp_index_2; #endif - + /*--- Copy coordinates and point to the auxiliar vector ---*/ - + nGlobalVertex = 0; nLocalVertex = 0; nLocalLinkedNodes = 0; - + for (iVertex = 0; iVertex < nVertex; iVertex++) { - + Buffer_Send_nLinkedNodes[iVertex] = 0; Aux_Send_Map[iVertex] = NULL; - + iPoint = geom->vertex[val_marker][iVertex]->GetNode(); - + if (geom->node[iPoint]->GetDomain()) { Buffer_Send_GlobalPoint[nLocalVertex] = geom->node[iPoint]->GetGlobalIndex(); - + for (iDim = 0; iDim < nDim; iDim++) Buffer_Send_Coord[nLocalVertex*nDim+iDim] = geom->node[iPoint]->GetCoord(iDim); - + nNodes = 0; nEdges = geom->node[iPoint]->GetnPoint(); - + for (jEdge = 0; jEdge < nEdges; jEdge++){ EdgeIndex = geom->node[iPoint]->GetEdge(jEdge); @@ -264,23 +264,23 @@ void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ Aux_Send_Map[nLocalVertex] = new unsigned long[ nNodes ]; nNodes = 0; - for (jEdge = 0; jEdge < nEdges; jEdge++){ + for (jEdge = 0; jEdge < nEdges; jEdge++){ EdgeIndex = geom->node[iPoint]->GetEdge(jEdge); if( iPoint == geom->edge[EdgeIndex]->GetNode(0) ) dPoint = geom->edge[EdgeIndex]->GetNode(1); else - dPoint = geom->edge[EdgeIndex]->GetNode(0); + dPoint = geom->edge[EdgeIndex]->GetNode(0); - if ( geom->node[dPoint]->GetVertex(val_marker) != -1 ){ + if ( geom->node[dPoint]->GetVertex(val_marker) != -1 ){ Aux_Send_Map[nLocalVertex][nNodes] = geom->node[dPoint]->GetGlobalIndex(); nNodes++; } - } + } nLocalVertex++; } } - + unsigned long *Buffer_Send_LinkedNodes = new unsigned long [ nLocalLinkedNodes ]; nLocalLinkedNodes = 0; @@ -291,7 +291,7 @@ void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ nLocalLinkedNodes++; } } - + for (iVertex = 0; iVertex < nVertex; iVertex++){ if( Aux_Send_Map[iVertex] != NULL ) delete [] Aux_Send_Map[iVertex]; @@ -306,7 +306,7 @@ void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ Buffer_Receive_Coord = new su2double [ nGlobalVertex * nDim ]; Buffer_Receive_GlobalPoint = new long[ nGlobalVertex ]; Buffer_Receive_Proc = new unsigned long[ nGlobalVertex ]; - + Buffer_Receive_nLinkedNodes = new unsigned long[ nGlobalVertex ]; Buffer_Receive_LinkedNodes = new unsigned long[ nGlobalLinkedNodes ]; Buffer_Receive_StartLinkedNodes = new unsigned long[ nGlobalVertex ]; @@ -323,21 +323,21 @@ void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ Buffer_Receive_nLinkedNodes[iVertex] = Buffer_Send_nLinkedNodes[iVertex]; Buffer_Receive_StartLinkedNodes[iVertex] = Buffer_Send_StartLinkedNodes[iVertex]; } - + for (iVertex = 0; iVertex < nLocalLinkedNodes; iVertex++) Buffer_Receive_LinkedNodes[iVertex] = Buffer_Send_LinkedNodes[iVertex]; - + tmp_index = nLocalVertex; tmp_index_2 = nLocalLinkedNodes; for(iRank = 1; iRank < nProcessor; iRank++){ - + SU2_MPI::Recv( &iTmp2, 1, MPI_UNSIGNED_LONG, iRank, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); SU2_MPI::Recv(&Buffer_Receive_LinkedNodes[tmp_index_2], iTmp2, MPI_UNSIGNED_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); SU2_MPI::Recv( &iTmp, 1, MPI_UNSIGNED_LONG, iRank, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); SU2_MPI::Recv(&Buffer_Receive_Coord[tmp_index*nDim], nDim*iTmp, MPI_DOUBLE, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - + SU2_MPI::Recv( &Buffer_Receive_GlobalPoint[tmp_index], iTmp, MPI_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); SU2_MPI::Recv( &Buffer_Receive_nLinkedNodes[tmp_index], iTmp, MPI_UNSIGNED_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); SU2_MPI::Recv(&Buffer_Receive_StartLinkedNodes[tmp_index], iTmp, MPI_UNSIGNED_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); @@ -346,7 +346,7 @@ void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ Buffer_Receive_Proc[ tmp_index + iVertex ] = iRank; Buffer_Receive_StartLinkedNodes[ tmp_index + iVertex ] += tmp_index_2; } - + tmp_index += iTmp; tmp_index_2 += iTmp2; } @@ -354,34 +354,34 @@ void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ else{ SU2_MPI::Send( &nLocalLinkedNodes, 1, MPI_UNSIGNED_LONG, 0, 0, MPI_COMM_WORLD); SU2_MPI::Send(Buffer_Send_LinkedNodes, nLocalLinkedNodes, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD); - + SU2_MPI::Send( &nLocalVertex, 1, MPI_UNSIGNED_LONG, 0, 0, MPI_COMM_WORLD); SU2_MPI::Send(Buffer_Send_Coord, nDim * nLocalVertex, MPI_DOUBLE, 0, 1, MPI_COMM_WORLD); - + SU2_MPI::Send( Buffer_Send_GlobalPoint, nLocalVertex, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD); SU2_MPI::Send( Buffer_Send_nLinkedNodes, nLocalVertex, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD); SU2_MPI::Send(Buffer_Send_StartLinkedNodes, nLocalVertex, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD); - } + } #else for (iVertex = 0; iVertex < nDim * nGlobalVertex; iVertex++) Buffer_Receive_Coord[iVertex] = Buffer_Send_Coord[iVertex]; - + for (iVertex = 0; iVertex < nGlobalVertex; iVertex++){ Buffer_Receive_GlobalPoint[iVertex] = Buffer_Send_GlobalPoint[iVertex]; Buffer_Receive_Proc[iVertex] = MASTER_NODE; Buffer_Receive_nLinkedNodes[iVertex] = Buffer_Send_nLinkedNodes[iVertex]; Buffer_Receive_StartLinkedNodes[iVertex] = Buffer_Send_StartLinkedNodes[iVertex]; } - + for (iVertex = 0; iVertex < nGlobalLinkedNodes; iVertex++) Buffer_Receive_LinkedNodes[iVertex] = Buffer_Send_LinkedNodes[iVertex]; -#endif +#endif if (rank == MASTER_NODE){ for (iVertex = 0; iVertex < nGlobalVertex; iVertex++){ count = 0; uptr = &Buffer_Receive_LinkedNodes[ Buffer_Receive_StartLinkedNodes[iVertex] ]; - + for (jVertex = 0; jVertex < Buffer_Receive_nLinkedNodes[iVertex]; jVertex++){ iTmp = uptr[ jVertex ]; for (kVertex = 0; kVertex < nGlobalVertex; kVertex++){ @@ -391,13 +391,13 @@ void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ break; } } - + if( count != (jVertex+1) ){ for (kVertex = jVertex; kVertex < Buffer_Receive_nLinkedNodes[iVertex]-1; kVertex++){ uptr[ kVertex ] = uptr[ kVertex + 1]; } Buffer_Receive_nLinkedNodes[iVertex]--; - jVertex--; + jVertex--; } } } @@ -453,7 +453,7 @@ void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { /*--- On the donor side: find the tag of the boundary sharing the interface. ---*/ const auto markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); - + /*--- On the target side: find the tag of the boundary sharing the interface. ---*/ const auto markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); @@ -487,7 +487,7 @@ void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { /*--- Coordinates of the target point. ---*/ const su2double* Coord_i = target_geometry->node[Point_Target]->GetCoord(); - su2double mindist = 1e20; + su2double mindist = 1e20; long pGlobalPoint = 0; int pProcessor = 0; @@ -502,8 +502,8 @@ void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { if (dist < mindist) { mindist = dist; - pProcessor = iProcessor; - pGlobalPoint = Buffer_Receive_GlobalPoint[idx]; + pProcessor = iProcessor; + pGlobalPoint = Buffer_Receive_GlobalPoint[idx]; } /*--- Test for "exact" match. ---*/ @@ -592,7 +592,7 @@ void CIsoparametric::Set_TransferCoeff(CConfig **config) { /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); - + /*--- On the target side: find the tag of the boundary sharing the interface ---*/ markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); @@ -608,7 +608,7 @@ void CIsoparametric::Set_TransferCoeff(CConfig **config) { nVertexTarget = target_geometry->GetnVertex( markTarget ); else nVertexTarget = 0; - + Buffer_Send_nVertex_Donor = new unsigned long [1]; Buffer_Send_nFace_Donor = new unsigned long [1]; Buffer_Send_nFaceNodes_Donor = new unsigned long [1]; @@ -825,7 +825,7 @@ void CIsoparametric::Set_TransferCoeff(CConfig **config) { storeProc[iDonor] = (int)Buffer_Receive_FaceProc[faceindex+iDonor]; } } - + delete [] X; } } @@ -868,7 +868,7 @@ void CIsoparametric::Set_TransferCoeff(CConfig **config) { } delete [] Coord; delete [] Normal; - + delete [] projected_point; } @@ -876,7 +876,7 @@ void CIsoparametric::Isoparameters(unsigned short nDim, unsigned short nDonor, su2double *X, su2double *xj, su2double *isoparams) { short iDonor,iDim,k; // indices su2double tmp, tmp2; - + su2double *x = new su2double[nDim+1]; su2double *x_tmp = new su2double[nDim+1]; su2double *Q = new su2double[nDonor*nDonor]; @@ -884,12 +884,12 @@ void CIsoparametric::Isoparameters(unsigned short nDim, unsigned short nDonor, su2double *A = new su2double[(nDim+2)*nDonor]; su2double *A2 = NULL; su2double *x2 = new su2double[nDim+1]; - + bool *test = new bool[nDim+1]; bool *testi = new bool[nDim+1]; - + su2double eps = 1E-10; - + short n = nDim+1; if (nDonor>2) { @@ -1050,7 +1050,7 @@ void CIsoparametric::Isoparameters(unsigned short nDim, unsigned short nDonor, isoparams[k] = 1.0; } } - + delete [] x; delete [] x_tmp; delete [] Q; @@ -1058,7 +1058,7 @@ void CIsoparametric::Isoparameters(unsigned short nDim, unsigned short nDonor, delete [] A; delete [] A2; delete [] x2; - + delete [] test; delete [] testi; @@ -1279,9 +1279,9 @@ CSlidingMesh::CSlidingMesh(CGeometry ****geometry_container, CConfig **config, u void CSlidingMesh::Set_TransferCoeff(CConfig **config) { /* --- This routine sets the transfer coefficient for sliding mesh approach --- */ - + /* - * The algorithm is based on Rinaldi et al. "Flux-conserving treatment of non-conformal interfaces + * The algorithm is based on Rinaldi et al. "Flux-conserving treatment of non-conformal interfaces * for finite-volume discritization of conservaation laws" 2015, Comp. Fluids, 120, pp 126-139 */ @@ -1290,19 +1290,19 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { /* --- General variables --- */ bool check; - + unsigned short iDim, nDim; - + unsigned long ii, jj, *uptr; unsigned long vPoint; unsigned long iEdgeVisited, nEdgeVisited, iNodeVisited; unsigned long nAlreadyVisited, nToVisit, StartVisited; - + unsigned long *alreadyVisitedDonor, *ToVisit, *tmpVect; unsigned long *storeProc, *tmp_storeProc; su2double dTMP; - su2double *Coeff_Vect, *tmp_Coeff_Vect; + su2double *Coeff_Vect, *tmp_Coeff_Vect; /* --- Geometrical variables --- */ @@ -1313,7 +1313,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { /* --- Markers Variables --- */ - unsigned short iMarkerInt, nMarkerInt; + unsigned short iMarkerInt, nMarkerInt; unsigned long iVertex, nVertexTarget; @@ -1325,24 +1325,24 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { unsigned long nEdges_target, nNode_target; unsigned long *Target_nLinkedNodes, *Target_LinkedNodes, *Target_StartLinkedNodes, *target_segment; - unsigned long *Target_Proc; + unsigned long *Target_Proc; long *Target_GlobalPoint, *Donor_GlobalPoint; - + su2double *TargetPoint_Coord, *target_iMidEdge_point, *target_jMidEdge_point, **target_element; /* --- Donor variables --- */ - unsigned long donor_StartIndex, donor_forward_point, donor_backward_point, donor_iPoint, donor_OldiPoint; - unsigned long nEdges_donor, nNode_donor, nGlobalVertex_Donor; + unsigned long donor_StartIndex, donor_forward_point, donor_backward_point, donor_iPoint, donor_OldiPoint; + unsigned long nEdges_donor, nNode_donor, nGlobalVertex_Donor; unsigned long nDonorPoints, iDonor; unsigned long *Donor_Vect, *tmp_Donor_Vect; unsigned long *Donor_nLinkedNodes, *Donor_LinkedNodes, *Donor_StartLinkedNodes; unsigned long *Donor_Proc; - + su2double *donor_iMidEdge_point, *donor_jMidEdge_point; su2double **donor_element, *DonorPoint_Coord; - + /* 1 - Variable pre-processing - */ nDim = donor_geometry->GetnDim(); @@ -1356,11 +1356,11 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { tmp_Donor_Vect = NULL; tmp_Coeff_Vect = NULL; tmp_storeProc = NULL; - + Normal = new su2double[nDim]; Direction = new su2double[nDim]; - - + + /* 2 - Find boundary tag between touching grids */ /*--- Number of markers on the FSI interface ---*/ @@ -1389,7 +1389,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { /*--- Target boundary ---*/ ReconstructBoundary(targetZone, markTarget); - + nGlobalVertex_Target = nGlobalVertex; TargetPoint_Coord = Buffer_Receive_Coord; @@ -1398,10 +1398,10 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { Target_StartLinkedNodes = Buffer_Receive_StartLinkedNodes; Target_LinkedNodes = Buffer_Receive_LinkedNodes; Target_Proc = Buffer_Receive_Proc; - + /*--- Donor boundary ---*/ ReconstructBoundary(donorZone, markDonor); - + nGlobalVertex_Donor = nGlobalVertex; DonorPoint_Coord = Buffer_Receive_Coord; @@ -1414,22 +1414,22 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { /*--- Starts building the supermesh layer (2D or 3D) ---*/ /* - For each target node, it first finds the closest donor point * - Then it creates the supermesh in the close proximity of the target point: - * - Starting from the closest donor node, it expands the supermesh by including + * - Starting from the closest donor node, it expands the supermesh by including * donor elements neighboring the initial one, until the overall target area is fully covered. */ if(nDim == 2){ - + target_iMidEdge_point = new su2double[nDim]; target_jMidEdge_point = new su2double[nDim]; donor_iMidEdge_point = new su2double[nDim]; donor_jMidEdge_point = new su2double[nDim]; - + /*--- Starts with supermesh reconstruction ---*/ target_segment = new unsigned long[2]; - + for (iVertex = 0; iVertex < nVertexTarget; iVertex++) { nDonorPoints = 0; @@ -1446,34 +1446,34 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { mindist = 1E6; donor_StartIndex = 0; - + for (donor_iPoint = 0; donor_iPoint < nGlobalVertex_Donor; donor_iPoint++) { - + Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ]; dist = PointsDistance(nDim, Coord_i, Coord_j); if (dist < mindist) { - mindist = dist; + mindist = dist; donor_StartIndex = donor_iPoint; } if (dist == 0.0){ donor_StartIndex = donor_iPoint; break; - } + } } donor_iPoint = donor_StartIndex; donor_OldiPoint = donor_iPoint; - + /*--- Contruct information regarding the target cell ---*/ - + long dPoint = target_geometry->node[target_iPoint]->GetGlobalIndex(); for (jVertexTarget = 0; jVertexTarget < nGlobalVertex_Target; jVertexTarget++) if( dPoint == Target_GlobalPoint[jVertexTarget] ) break; - + if ( Target_nLinkedNodes[jVertexTarget] == 1 ){ target_segment[0] = Target_LinkedNodes[ Target_StartLinkedNodes[jVertexTarget] ]; target_segment[1] = jVertexTarget; @@ -1482,7 +1482,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { target_segment[0] = Target_LinkedNodes[ Target_StartLinkedNodes[jVertexTarget] ]; target_segment[1] = Target_LinkedNodes[ Target_StartLinkedNodes[jVertexTarget] + 1]; } - + dTMP = 0; for(iDim = 0; iDim < nDim; iDim++){ target_iMidEdge_point[iDim] = ( TargetPoint_Coord[ nDim * target_segment[0] + iDim ] + target_geometry->node[ target_iPoint ]->GetCoord(iDim) ) / 2; @@ -1503,7 +1503,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { /*--- Proceeds along the forward direction (depending on which connected boundary node is found first) ---*/ while( !check ){ - + /*--- Proceeds until the value of the intersection area is null ---*/ if ( Donor_nLinkedNodes[donor_iPoint] == 1 ){ @@ -1512,7 +1512,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { } else{ uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_iPoint] ]; - + if( donor_OldiPoint != uptr[0] ){ donor_forward_point = uptr[0]; donor_backward_point = uptr[1]; @@ -1522,12 +1522,12 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { donor_backward_point = uptr[0]; } } - + if(donor_iPoint >= nGlobalVertex_Donor){ check = true; continue; } - + for(iDim = 0; iDim < nDim; iDim++){ donor_iMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_forward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; donor_jMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_backward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; @@ -1539,13 +1539,13 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { check = true; continue; } - + /*--- In case the element intersects the target cell, update the auxiliary communication data structure ---*/ tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ]; tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ]; tmp_storeProc = new unsigned long[ nDonorPoints + 1 ]; - + for( iDonor = 0; iDonor < nDonorPoints; iDonor++){ tmp_Donor_Vect[iDonor] = Donor_Vect[iDonor]; tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor]; @@ -1555,9 +1555,9 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint; tmp_Coeff_Vect[ nDonorPoints ] = LineIntersectionLength / length; tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint]; - - if (Donor_Vect != NULL) delete [] Donor_Vect; - if (Coeff_Vect != NULL) delete [] Coeff_Vect; + + if (Donor_Vect != NULL) delete [] Donor_Vect; + if (Coeff_Vect != NULL) delete [] Coeff_Vect; if (storeProc != NULL) delete [] storeProc; Donor_Vect = tmp_Donor_Vect; @@ -1569,10 +1569,10 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { nDonorPoints++; } - + if ( Donor_nLinkedNodes[donor_StartIndex] == 2 ){ check = false; - + uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_StartIndex] ]; donor_iPoint = uptr[1]; @@ -1592,7 +1592,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { } else{ uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_iPoint] ]; - + if( donor_OldiPoint != uptr[0] ){ donor_forward_point = uptr[0]; donor_backward_point = uptr[1]; @@ -1607,11 +1607,11 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { check = true; continue; } - + for(iDim = 0; iDim < nDim; iDim++){ donor_iMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_forward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; donor_jMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_backward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; - } + } LineIntersectionLength = ComputeLineIntersectionLength(target_iMidEdge_point, target_jMidEdge_point, donor_iMidEdge_point, donor_jMidEdge_point, Direction); @@ -1625,14 +1625,14 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ]; tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ]; tmp_storeProc = new unsigned long[ nDonorPoints + 1 ]; - + for( iDonor = 0; iDonor < nDonorPoints; iDonor++){ tmp_Donor_Vect[iDonor] = Donor_Vect[iDonor]; tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor]; tmp_storeProc[iDonor] = storeProc[iDonor]; } - - tmp_Coeff_Vect[ nDonorPoints ] = LineIntersectionLength / length; + + tmp_Coeff_Vect[ nDonorPoints ] = LineIntersectionLength / length; tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint; tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint]; @@ -1646,52 +1646,52 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { donor_OldiPoint = donor_iPoint; donor_iPoint = donor_forward_point; - + nDonorPoints++; } - + /*--- Set the communication data structure and copy data from the auxiliary vectors ---*/ target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nDonorPoints); target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); - - for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){ + + for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){ target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor, Coeff_Vect[iDonor]); target_geometry->vertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor, Donor_GlobalPoint[Donor_Vect[iDonor]]); target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]); } } - } - + } + delete [] target_segment; - + delete [] target_iMidEdge_point; delete [] target_jMidEdge_point; delete [] donor_iMidEdge_point; delete [] donor_jMidEdge_point; } - else{ + else{ /* --- 3D geometry, creates a superficial super-mesh --- */ - + for (iVertex = 0; iVertex < nVertexTarget; iVertex++) { - + nDonorPoints = 0; /*--- Stores coordinates of the target node ---*/ target_iPoint = target_geometry->vertex[markTarget][iVertex]->GetNode(); - + if (!target_geometry->node[target_iPoint]->GetDomain()) continue; - + Coord_i = target_geometry->node[target_iPoint]->GetCoord(); target_geometry->vertex[markTarget][iVertex]->GetNormal(Normal); /*--- The value of Area computed here includes also portion of boundary belonging to different marker ---*/ Area = 0.0; - for (iDim = 0; iDim < nDim; iDim++) + for (iDim = 0; iDim < nDim; iDim++) Area += Normal[iDim]*Normal[iDim]; Area = sqrt(Area); @@ -1700,23 +1700,23 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { for (iDim = 0; iDim < nDim; iDim++) Coord_i[iDim] = target_geometry->node[target_iPoint]->GetCoord(iDim); - + long dPoint = target_geometry->node[target_iPoint]->GetGlobalIndex(); for (target_iPoint = 0; target_iPoint < nGlobalVertex_Target; target_iPoint++){ if( dPoint == Target_GlobalPoint[target_iPoint] ) break; - } - + } + /*--- Build local surface dual mesh for target element ---*/ - + nEdges_target = Target_nLinkedNodes[target_iPoint]; nNode_target = 2*(nEdges_target + 1); - + target_element = new su2double*[nNode_target]; for (ii = 0; ii < nNode_target; ii++) target_element[ii] = new su2double[nDim]; - + nNode_target = Build_3D_surface_element(Target_LinkedNodes, Target_StartLinkedNodes, Target_nLinkedNodes, TargetPoint_Coord, target_iPoint, target_element); /*--- Brute force to find the closest donor_node ---*/ @@ -1725,29 +1725,29 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { donor_StartIndex = 0; for (donor_iPoint = 0; donor_iPoint < nGlobalVertex_Donor; donor_iPoint++) { - + Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ]; dist = PointsDistance(nDim, Coord_i, Coord_j); if (dist < mindist) { - mindist = dist; + mindist = dist; donor_StartIndex = donor_iPoint; } if (dist == 0.0){ donor_StartIndex = donor_iPoint; break; - } + } } - + donor_iPoint = donor_StartIndex; nEdges_donor = Donor_nLinkedNodes[donor_iPoint]; donor_element = new su2double*[ 2*nEdges_donor + 2 ]; for (ii = 0; ii < 2*nEdges_donor + 2; ii++) - donor_element[ii] = new su2double[nDim]; + donor_element[ii] = new su2double[nDim]; nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, DonorPoint_Coord, donor_iPoint, donor_element); @@ -1782,10 +1782,10 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { StartVisited = 0; Area_old = -1; - - while( Area > Area_old ){ - /* + while( Area > Area_old ){ + + /* * - Starting from the closest donor_point, it expands the supermesh by a countour search pattern. * - The closest donor element becomes the core, at each iteration a new layer of elements around the core is taken into account */ @@ -1798,7 +1798,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { for( iNodeVisited = StartVisited; iNodeVisited < nAlreadyVisited; iNodeVisited++ ){ vPoint = alreadyVisitedDonor[ iNodeVisited ]; - + nEdgeVisited = Donor_nLinkedNodes[vPoint]; for (iEdgeVisited = 0; iEdgeVisited < nEdgeVisited; iEdgeVisited++){ @@ -1811,7 +1811,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { for( jj = 0; jj < nAlreadyVisited; jj++ ){ if( donor_iPoint == alreadyVisitedDonor[jj] ){ - check = 1; + check = 1; break; } } @@ -1819,12 +1819,12 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { if( check == 0 && ToVisit != NULL){ for( jj = 0; jj < nToVisit; jj++ ) if( donor_iPoint == ToVisit[jj] ){ - check = 1; + check = 1; break; - } + } } - if( check == 0 ){ + if( check == 0 ){ /*--- If the node was not already visited, visit it and list it into data structure ---*/ tmpVect = new unsigned long[ nToVisit + 1 ]; @@ -1835,19 +1835,19 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { if( ToVisit != NULL ) delete [] ToVisit; - + ToVisit = tmpVect; tmpVect = NULL; - nToVisit++; + nToVisit++; /*--- Find the value of the intersection area between the current donor element and the target element --- */ nEdges_donor = Donor_nLinkedNodes[donor_iPoint]; - donor_element = new su2double*[ 2*nEdges_donor + 2 ]; + donor_element = new su2double*[ 2*nEdges_donor + 2 ]; for (ii = 0; ii < 2*nEdges_donor + 2; ii++) - donor_element[ii] = new su2double[nDim]; + donor_element[ii] = new su2double[nDim]; nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, DonorPoint_Coord, donor_iPoint, donor_element); @@ -1871,8 +1871,8 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor]; tmp_storeProc[iDonor] = storeProc[iDonor]; } - - tmp_Coeff_Vect[ nDonorPoints ] = tmp_Area; + + tmp_Coeff_Vect[ nDonorPoints ] = tmp_Area; tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint; tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint]; @@ -1884,15 +1884,15 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { Coeff_Vect = tmp_Coeff_Vect; storeProc = tmp_storeProc; - tmp_Coeff_Vect = NULL; + tmp_Coeff_Vect = NULL; tmp_Donor_Vect = NULL; tmp_storeProc = NULL; nDonorPoints++; - + Area += tmp_Area; } - } + } } /*--- Update auxiliary data structure ---*/ @@ -1903,18 +1903,18 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { for( jj = 0; jj < nAlreadyVisited; jj++ ) tmpVect[jj] = alreadyVisitedDonor[jj]; - + for( jj = 0; jj < nToVisit; jj++ ) tmpVect[ nAlreadyVisited + jj ] = ToVisit[jj]; if( alreadyVisitedDonor != NULL ) delete [] alreadyVisitedDonor; - alreadyVisitedDonor = tmpVect; + alreadyVisitedDonor = tmpVect; - nAlreadyVisited += nToVisit; + nAlreadyVisited += nToVisit; - delete [] ToVisit; + delete [] ToVisit; } delete [] alreadyVisitedDonor; @@ -1924,17 +1924,17 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nDonorPoints); target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); - for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){ + for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){ target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor, Coeff_Vect[iDonor]/Area); target_geometry->vertex[markTarget][iVertex]->SetInterpDonorPoint( iDonor, Donor_GlobalPoint[ Donor_Vect[iDonor] ] ); target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]); - //cout <= 0) check++; - return (check == 3); + return (check == 3); } CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, @@ -2914,7 +2914,7 @@ int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector Date: Wed, 18 Mar 2020 14:55:31 +0000 Subject: [PATCH 029/112] split interpolation_structure --- .../interface_interpolation/CInterpolator.hpp | 186 + .../CIsoparametric.hpp | 70 + .../interface_interpolation/CMirror.hpp | 53 + .../CNearestNeighbor.hpp | 51 + .../CRadialBasisFunction.hpp | 159 + .../interface_interpolation/CSlidingMesh.hpp | 117 + Common/include/interpolation_structure.hpp | 498 --- Common/lib/Makefile.am | 7 +- .../interface_interpolation/CInterpolator.cpp | 415 +++ .../CIsoparametric.cpp | 563 +++ .../src/interface_interpolation/CMirror.cpp | 243 ++ .../CNearestNeighbor.cpp | 137 + .../CRadialBasisFunction.cpp | 772 ++++ .../interface_interpolation/CSlidingMesh.cpp | 1255 +++++++ .../src/interface_interpolation/meson.build | 6 + Common/src/interpolation_structure.cpp | 3235 ----------------- Common/src/meson.build | 2 +- SU2_CFD/include/SU2_CFD.hpp | 1 - SU2_CFD/include/drivers/CDriver.hpp | 2 +- SU2_CFD/src/drivers/CDriver.cpp | 8 +- SU2_CFD/src/drivers/CMultizoneDriver.cpp | 4 +- 21 files changed, 4044 insertions(+), 3740 deletions(-) create mode 100644 Common/include/interface_interpolation/CInterpolator.hpp create mode 100644 Common/include/interface_interpolation/CIsoparametric.hpp create mode 100644 Common/include/interface_interpolation/CMirror.hpp create mode 100644 Common/include/interface_interpolation/CNearestNeighbor.hpp create mode 100644 Common/include/interface_interpolation/CRadialBasisFunction.hpp create mode 100644 Common/include/interface_interpolation/CSlidingMesh.hpp delete mode 100644 Common/include/interpolation_structure.hpp create mode 100644 Common/src/interface_interpolation/CInterpolator.cpp create mode 100644 Common/src/interface_interpolation/CIsoparametric.cpp create mode 100644 Common/src/interface_interpolation/CMirror.cpp create mode 100644 Common/src/interface_interpolation/CNearestNeighbor.cpp create mode 100644 Common/src/interface_interpolation/CRadialBasisFunction.cpp create mode 100644 Common/src/interface_interpolation/CSlidingMesh.cpp create mode 100644 Common/src/interface_interpolation/meson.build delete mode 100644 Common/src/interpolation_structure.cpp diff --git a/Common/include/interface_interpolation/CInterpolator.hpp b/Common/include/interface_interpolation/CInterpolator.hpp new file mode 100644 index 000000000000..5144e37ce2bc --- /dev/null +++ b/Common/include/interface_interpolation/CInterpolator.hpp @@ -0,0 +1,186 @@ +/*! + * \file CInterpolator.hpp + * \brief Base class for multiphysics interpolation. + * \author H. Kline + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ +#pragma once + +#include "../../include/datatype_structure.hpp" + +class CConfig; +class CGeometry; + +/*! + * \class CInterpolator + * \brief Main class for defining the interpolator, it requires + * a child class for each particular interpolation method + * \author H. Kline + */ +class CInterpolator { +protected: + const int rank; /*!< \brief MPI Rank. */ + const int size; /*!< \brief MPI Size. */ + const unsigned donorZone; /*!< \brief Index of donor zone. */ + const unsigned targetZone; /*!< \brief Index of target zone. */ + + unsigned long + MaxLocalVertex_Donor, /*!< \brief Maximum vertices per processor*/ + nGlobalFace_Donor, /*!< \brief Number of global donor faces*/ + nGlobalFaceNodes_Donor, /*!< \brief Number of global donor face nodes*/ + MaxFace_Donor, /*!< \brief Maximum faces per processor*/ + MaxFaceNodes_Donor; /*!< \brief Maximum nodes associated with faces per processor*/ + + unsigned long + *Buffer_Receive_nVertex_Donor, /*!< \brief Buffer to store the number of vertices per processor on the Donor domain */ + *Buffer_Receive_nFace_Donor, /*!< \brief Buffer to store the number of faces per processor*/ + *Buffer_Receive_nFaceNodes_Donor, /*!< \brief Buffer to store the number of nodes associated with faces per processor*/ + *Buffer_Send_nVertex_Donor, /*!< \brief Buffer to send number of vertices on the local processor*/ + *Buffer_Send_nFace_Donor, /*!< \brief Buffer to send number of faces on the local processor*/ + *Buffer_Send_nFaceNodes_Donor, /*!< \brief Buffer to send the number of nodes assocated with faces per processor*/ + *Buffer_Send_FaceIndex, /*!< \brief Buffer to send indices pointing to the node indices that define the faces*/ + *Buffer_Receive_FaceIndex, /*!< \brief Buffer to receive indices pointing to the node indices that define the faces*/ + *Buffer_Send_FaceNodes, /*!< \brief Buffer to send indices pointing to the location of node information in other buffers, defining faces*/ + *Buffer_Receive_FaceNodes, /*!< \brief Buffer to receive indices pointing to the location of node information in other buffers, defining faces*/ + *Buffer_Send_FaceProc, /*!< \brief Buffer to send processor which stores the node indicated in Buffer_Receive_FaceNodes*/ + *Buffer_Receive_FaceProc; /*!< \brief Buffer to receive processor which stores the node indicated in Buffer_Receive_FaceNodes*/ + + long *Buffer_Send_GlobalPoint, /*!< \brief Buffer to send global point indices*/ + *Buffer_Receive_GlobalPoint; /*!< \brief Buffer to receive global point indices*/ + + su2double *Buffer_Send_Coord, /*!< \brief Buffer to send coordinate values*/ + *Buffer_Send_Normal, /*!< \brief Buffer to send normal vector values */ + *Buffer_Receive_Coord, /*!< \brief Buffer to receive coordinate values*/ + *Buffer_Receive_Normal; /*!< \brief Buffer to receive normal vector values*/ + + unsigned long *Receive_GlobalPoint, /*!< \brief Buffer to receive Global point indexes*/ + *Buffer_Receive_nLinkedNodes, /*!< \brief Buffer to receive the number of edges connected to each node*/ + *Buffer_Receive_LinkedNodes, /*!< \brief Buffer to receive the list of notes connected to the nodes through an edge*/ + *Buffer_Receive_StartLinkedNodes, /*!< \brief Buffer to receive the index of the Receive_LinkedNodes buffer where corresponding list of linked nodes begins */ + *Buffer_Receive_Proc; /*!< \brief Buffer to receive the thread that owns the node*/ + + unsigned long nGlobalVertex_Target, /*!< \brief Global number of vertex of the target boundary*/ + nLocalVertex_Target, /*!< \brief Number of vertex of the target boundary owned by the thread*/ + nGlobalVertex_Donor, /*!< \brief Global number of vertex of the donor boundary*/ + nLocalVertex_Donor, /*!< \brief Number of vertex of the donor boundary owned by the thread*/ + nGlobalVertex, /*!< \brief Dummy variable to temporarily store the global number of vertex of a boundary*/ + nLocalLinkedNodes; /*!< \brief Dummy variable to temporarily store the number of vertex of a boundary*/ + + CGeometry**** const Geometry; /*! \brief Vector which stores n zones of geometry. */ + CGeometry* const donor_geometry; /*! \brief Donor geometry. */ + CGeometry* const target_geometry; /*! \brief Target geometry. */ + +public: + /*! + * \brief Constructor of the class. + * \param[in] geometry - Geometrical definition of the problem. + * \param[in] config - Definition of the particular problem. + * \param[in] iZone - index of the donor zone + * \param[in] jZone - index of the target zone + */ + CInterpolator(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); + + /*! + * \brief No default construction allowed. + */ + CInterpolator(void) = delete; + + /*! + * \brief Destructor of the class, nothing is deleted, derived classes need to manage the MPI buffers. + */ + virtual ~CInterpolator(void) = default; + + /*! + * \brief Set up transfer matrix defining relation between two meshes + * \note Main method that derived classes must implement. + * \param[in] config - Definition of the particular problem. + */ + virtual void Set_TransferCoeff(CConfig **config) = 0; + +protected: + /*! + * \brief Find the index of the interface marker shared by that zone + * \param[in] config - Definition of the particular problem. + * \param[in] val_marker_interface - Interface tag. + */ + int Find_InterfaceMarker(const CConfig *config, unsigned short val_marker_interface) const; + + /*! + * \brief Check whether an interface should be processed or not, i.e. if it is part of the zones. + * \param[in] val_markDonor - Marker tag from donor zone. + * \param[in] val_markTarget - Marker tag from target zone. + */ + bool CheckInterfaceBoundary(int val_markDonor, int val_markTarget) const; + + /*! + * \brief Recontstruct the boundary connectivity from parallel partitioning and broadcasts it to all threads + * \param[in] val_zone - index of the zone + * \param[in] val_marker - index of the marker + */ + void ReconstructBoundary(unsigned long val_zone, int val_marker); + + /*! + * \brief compute squared distance between 2 points + * \param[in] nDim - number of dimensions + * \param[in] point_i - coordinates of point i + * \param[in] point_j - coordinates of point j + */ + inline su2double PointsSquareDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) const { + su2double d = 0.0; + for(unsigned short iDim = 0; iDim < nDim; iDim++) + d += pow(point_j[iDim] - point_i[iDim], 2); + return d; + } + + /*! + * \brief compute distance between 2 points + * \param[in] nDim - number of dimensions + * \param[in] point_i - coordinates of point i + * \param[in] point_j - coordinates of point j + */ + inline su2double PointsDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) const { + return sqrt(PointsSquareDistance(nDim, point_i, point_j)); + } + + /*! + * \brief Determine array sizes used to collect and send coordinate and global point + * information. + * \param[in] faces - boolean that determines whether or not to set face information as well + * \param[in] markDonor - Index of the boundary on the donor domain. + * \param[in] markTarget - Index of the boundary on the target domain. + * \param[in] nVertexDonor - Number of vertices on the donor boundary. + * \param[in] nDim - number of physical dimensions. + */ + void Determine_ArraySize(bool faces, int markDonor, int markTarget, unsigned long nVertexDonor, unsigned short nDim); + + /*! + * \brief Collect and communicate vertex info: coord, global point, and if faces=true the normal vector + * \param[in] faces - boolean that determines whether or not to set face information as well + * \param[in] markDonor - Index of the boundary on the donor domain. + * \param[in] markTarget - Index of the boundary on the target domain. + * \param[in] nVertexDonor - Number of vertices on the donor boundary. + * \param[in] nDim - number of physical dimensions. + */ + void Collect_VertexInfo(bool faces, int markDonor, int markTarget, unsigned long nVertexDonor, unsigned short nDim); + +}; diff --git a/Common/include/interface_interpolation/CIsoparametric.hpp b/Common/include/interface_interpolation/CIsoparametric.hpp new file mode 100644 index 000000000000..250a7814f15d --- /dev/null +++ b/Common/include/interface_interpolation/CIsoparametric.hpp @@ -0,0 +1,70 @@ +/*! + * \file CIsoparametric.hpp + * \brief Isoparametric interpolation using FE shape functions. + * \author H. Kline + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ +#pragma once + +#include "CInterpolator.hpp" + +/*! + * \brief Isoparametric interpolation. + */ +class CIsoparametric final : public CInterpolator { +public: + /*! + * \brief Constructor of the class. + * \param[in] geometry - Geometrical definition of the problem. + * \param[in] config - Definition of the particular problem. + * \param[in] iZone - index of the donor zone + * \param[in] jZone - index of the target zone + */ + CIsoparametric(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); + + /*! + * \brief Set up transfer matrix defining relation between two meshes + * \param[in] config - Definition of the particular problem. + */ + void Set_TransferCoeff(CConfig **config) override; + +private: + /*! + * \brief Calculate the isoparametric representation of point iVertex in marker iZone_0 by + * nodes of element donor_elem in marker jMarker of zone iZone_1. + * \param[in] iVertex - vertex index of the point being interpolated. + * \param[in] nDim - the dimension of the coordinates. + * \param[in] iZone_1 - zone index of the element to use for interpolation (the DONOR zone) + * \param[in] donor_elem - element index of the element to use for interpolation (or global index of a point in 2D) + * \param[in] nDonorPoints - number of donor points in the element. + * \param[in] xj - point projected onto the plane of the donor element. + * \param[out] isoparams - isoparametric coefficients. Must be allocated to size nNodes ahead of time. (size> nDonors) + * + * \note If the problem is 2D, the 'face' projected onto is actually an edge; the local index + * of the edge is then stored in iFace, and the global index of the node (from which the edge + * is referenced) + */ + void Isoparameters(unsigned short nDim, unsigned short nDonor, const su2double *X, + const su2double *xj, su2double* isoparams) const; + +}; diff --git a/Common/include/interface_interpolation/CMirror.hpp b/Common/include/interface_interpolation/CMirror.hpp new file mode 100644 index 000000000000..c53ebc4bd889 --- /dev/null +++ b/Common/include/interface_interpolation/CMirror.hpp @@ -0,0 +1,53 @@ +/*! + * \file CMirror.hpp + * \brief Mirror interpolation for the conservative (work-wise) approach in FSI problems. + * \author H. Kline + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ +#pragma once + +#include "CInterpolator.hpp" + +/*! + * \brief Mirror interpolation: copy point linking and coefficient values from the opposing mesh. + * \note Assumes that the oppoosing mesh has already run interpolation, otherwise will result in empty/trivial interpolation. + */ +class CMirror final : public CInterpolator { +public: + /*! + * \brief Constructor of the class. + * \note Data is set in geometry[targetZone]. + * \param[in] geometry_container + * \param[in] config - config container + * \param[in] iZone - First zone + * \param[in] jZone - Second zone + */ + CMirror(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); + + /*! + * \brief Set up transfer matrix defining relation between two meshes + * \param[in] config - Definition of the particular problem. + */ + void Set_TransferCoeff(CConfig **config) override; + +}; diff --git a/Common/include/interface_interpolation/CNearestNeighbor.hpp b/Common/include/interface_interpolation/CNearestNeighbor.hpp new file mode 100644 index 000000000000..95dcdfdd3e2f --- /dev/null +++ b/Common/include/interface_interpolation/CNearestNeighbor.hpp @@ -0,0 +1,51 @@ +/*! + * \file CNearestNeighbor.hpp + * \brief Nearest Neighbor interpolation class. + * \author H. Kline + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ +#pragma once + +#include "CInterpolator.hpp" + +/*! + * \brief Nearest Neighbor interpolation. + */ +class CNearestNeighbor final : public CInterpolator { +public: + /*! + * \brief Constructor of the class. + * \param[in] geometry - Geometrical definition of the problem. + * \param[in] config - Definition of the particular problem. + * \param[in] iZone - index of the donor zone + * \param[in] jZone - index of the target zone + */ + CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); + + /*! + * \brief Set up transfer matrix defining relation between two meshes + * \param[in] config - Definition of the particular problem. + */ + void Set_TransferCoeff(CConfig **config) override; + +}; diff --git a/Common/include/interface_interpolation/CRadialBasisFunction.hpp b/Common/include/interface_interpolation/CRadialBasisFunction.hpp new file mode 100644 index 000000000000..18b1b2083644 --- /dev/null +++ b/Common/include/interface_interpolation/CRadialBasisFunction.hpp @@ -0,0 +1,159 @@ +/*! + * \file CRadialBasisFunction.hpp + * \brief Radial basis function interpolation. + * \author Joel Ho, P. Gomes + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ +#pragma once + +#include "CInterpolator.hpp" +#include "../option_structure.hpp" +#include "../toolboxes/C2DContainer.hpp" + +/*! + * \brief Radial basis function interpolation. + */ +class CRadialBasisFunction final : public CInterpolator { +public: + /*! + * \brief Constructor of the class. + * \param[in] geometry - Geometrical definition of the problem. + * \param[in] config - Definition of the particular problem. + * \param[in] iZone - index of the donor zone + * \param[in] jZone - index of the target zone + */ + CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); + + /*! + * \brief Set up transfer matrix defining relation between two meshes + * \param[in] config - Definition of the particular problem. + */ + void Set_TransferCoeff(CConfig **config) override; + + /*! + * \brief Compute the value of a radial basis function, this is static so it can be re-used. + * \param[in] type - of radial basis function + * \param[in] radius - the characteristic dimension + * \param[in] dist - distance + * \return value of the RBF. + */ + static su2double Get_RadialBasisValue(ENUM_RADIALBASIS type, const su2double radius, const su2double dist); + +private: + /*! + * \brief Compute the RBF "generator" matrix with or without polynomial terms. + * \note Multiplying C_inv_trunc by a column vector gives specific coefficients for given "known values", + * conversely, multiplying (on the left) by a row vector of polynomial and RBF values gives generic + * interpolation coefficients for a given target evaluation point. + * \param[in] type - Type of radial basis function. + * \param[in] usePolynomial - Whether to use polynomial terms. + * \param[in] radius - Normalizes point-to-point distance when computing RBF values. + * \param[in] coords - Coordinates of the donor points. + * \param[out] nPolynomial - Num of poly terms, -1 if !usePolynomial, nDim-1 if coords lie on plane, else nDim. + * \param[out] keepPolynomialRow - Size nDim, signals which (if any) iDim was removed from polynomial term. + * \param[out] C_inv_trunc - The generator matrix as described above. + */ + void ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, su2double radius, + const su2activematrix& coords, int& nPolynomial, + vector& keepPolynomialRow, su2passivematrix& C_inv_trunc) const; + + /*! + * \brief If the polynomial term is included in the interpolation, and the points lie on a plane, the matrix + * becomes rank deficient and cannot be inverted. This method detects that condition and corrects it by + * removing a row from P (the polynomial part of the interpolation matrix). + * \param[in] max_diff_tol - Tolerance to detect whether points are on a plane. + * \param[out] keep_row - Marks the dimensions of P kept. + * \param[in,out] P - Polynomial part of the interpolation matrix, one row may be eliminated. + * \return n_polynomial - Size of the polynomial part on exit (in practice nDim or nDim-1). + */ + int CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, su2passivematrix &P) const; + + /*! + * \brief Prunes (by setting to zero) small interpolation coefficients, i.e. + * <= tolerance*max(abs(coeffs)). The vector is re-scaled such that sum(coeffs)==1. + * \param[in] tolerance - Relative pruning tolerance. + * \param[in,out] coeffs - The vector of interpolation coefficients. + * \return Number of non-zero coefficients after pruning. + */ + int PruneSmallCoefficients(passivedouble tolerance, su2passivevector& coeffs) const; + +}; + +/*! + * \brief Helper class used by CRadialBasisFunction to compute the interpolation weights. + * The matrix is symmetric but full storage is used as that gives much better performance + * for some BLAS libraries (notably OpenBLAS). The code should be compiled with LAPACK + * to use optimized matrix inversion and multiplication routines. + */ +class CSymmetricMatrix { +private: + enum DecompositionType { NONE, CHOLESKY, LU }; + + vector val_vec, decomp_vec; + vector perm_vec; + int sz = 0; + bool initialized = false; + DecompositionType decomposed = NONE; + + inline void CheckBounds(int i, int j) const { + assert(initialized && "Matrix not initialized."); + assert(i>=0 && i=0 && j. + */ +#pragma once + +#include "CInterpolator.hpp" + +/*! + * \brief Sliding mesh approach. + */ +class CSlidingMesh final : public CInterpolator { +public: + /*! + * \brief Constructor of the class. + * \param[in] geometry - Geometrical definition of the problem. + * \param[in] config - Definition of the particular problem. + * \param[in] iZone - index of the donor zone + * \param[in] jZone - index of the target zone + */ + CSlidingMesh(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); + + /*! + * \brief Set up transfer matrix defining relation between two meshes + * \param[in] config - Definition of the particular problem. + */ + void Set_TransferCoeff(CConfig **config) override; + +private: + /*! + * \brief For 3-Dimensional grids, build the dual surface element + * \param[in] map - array containing the index of the boundary points connected to the node + * \param[in] startIndex - for each vertex specifies the corresponding index in the global array containing the indexes of all its neighbouring vertexes + * \param[in] nNeighbour - for each vertex specifies the number of its neighbouring vertexes (on the boundary) + * \param[in] coord - array containing the coordinates of all the boundary vertexes + * \param[in] centralNode - label of the vertex around which the dual surface element is built + * \param[in] element - double array where element node coordinates will be stored + */ + int Build_3D_surface_element(unsigned long *map, unsigned long *startIndex, unsigned long* nNeighbor, + su2double *coord, unsigned long centralNode, su2double** element); + + /*! + * \brief For 2-Dimensional grids, compute intersection length of two segments projected along a given direction + * \param[in] A1 - first point of segment A + * \param[in] A2 - second point of segment A + * \param[in] B1 - first point of segment B + * \param[in] B2 - second point of segment B + * \param[in] Direction - along which segments are projected + */ + su2double ComputeLineIntersectionLength(su2double* A1, su2double* A2, su2double* B1, su2double* B2, su2double* Direction); + + /*! + * \brief For 3-Dimensional grids, compute intersection area between two triangle projected on a given plane + * \param[in] A1 - first point of triangle A + * \param[in] A2 - second point of triangle A + * \param[in] A3 - third point of triangle A + * \param[in] B1 - first point of triangle B + * \param[in] B2 - second point of triangle B + * \param[in] B3 - third point of triangle B + * \param[in] Direction - vector normal to projection plane + */ + su2double Compute_Triangle_Intersection(su2double* A1, su2double* A2, su2double* A3, su2double* B1, su2double* B2, su2double* B3, su2double* Direction); + + /*! + * \brief For 3-Dimensional grids, compute intersection area between two triangle projected on a given plane + * P1 from triangle P MUST be inside triangle Q, points order doesn't matter + * \param[in] P1 - first point of triangle A + * \param[in] P2 - second point of triangle A + * \param[in] P3 - third point of triangle A + * \param[in] Q1 - first point of triangle B + * \param[in] Q2 - second point of triangle B + * \param[in] Q3 - third point of triangle B + */ + su2double ComputeIntersectionArea( su2double* P1, su2double* P2, su2double* P3, su2double* Q1, su2double* Q2, su2double* Q3 ); + + /*! + * \brief For 2-Dimensional grids, check whether, and compute, two lines are intersecting + * \param[in] A1 - first defining first line + * \param[in] A2 - second defining first line + * \param[in] B1 - first defining second line + * \param[in] B2 - second defining second line + * \param[in] IntersectionPoint - Container for intersection coordinates + */ + void ComputeLineIntersectionPoint( su2double* A1, su2double* A2, su2double* B1, su2double* B2, su2double* IntersectionPoint ); + + /*! + * \brief For N-Dimensional grids, check whether a point is inside a triangle specified by 3 T points + * \param[in] Point - query point + * \param[in] T1 - first point of triangle T + * \param[in] T2 - second point of triangle T + * \param[in] T3 - third point of triangle T + */ + bool CheckPointInsideTriangle(su2double* Point, su2double* T1, su2double* T2, su2double* T3); + +}; diff --git a/Common/include/interpolation_structure.hpp b/Common/include/interpolation_structure.hpp deleted file mode 100644 index 115e477ef46b..000000000000 --- a/Common/include/interpolation_structure.hpp +++ /dev/null @@ -1,498 +0,0 @@ -/*! - * \file interpolation_structure.hpp - * \brief Headers of classes used for multiphysics interpolation. - * The implementation is in the interpolation_structure.cpp file. - * \author H. Kline - * \version 7.0.2 "Blackbird" - * - * SU2 Project Website: https://su2code.github.io - * - * The SU2 Project is maintained by the SU2 Foundation - * (http://su2foundation.org) - * - * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md) - * - * SU2 is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * SU2 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with SU2. If not, see . - */ - -#pragma once - -#include "../../Common/include/mpi_structure.hpp" - -#include -#include -#include -#include -#include - -#include "CConfig.hpp" -#include "geometry/CGeometry.hpp" - -using namespace std; - - -/*! - * \class CInterpolator - * \brief Main class for defining the interpolator, it requires - * a child class for each particular interpolation method - * \author H. Kline - */ -class CInterpolator { -protected: - const int rank; /*!< \brief MPI Rank. */ - const int size; /*!< \brief MPI Size. */ - const unsigned donorZone; /*!< \brief Index of donor zone. */ - const unsigned targetZone; /*!< \brief Index of target zone. */ - - unsigned long - MaxLocalVertex_Donor, /*!< \brief Maximum vertices per processor*/ - nGlobalFace_Donor, /*!< \brief Number of global donor faces*/ - nGlobalFaceNodes_Donor, /*!< \brief Number of global donor face nodes*/ - MaxFace_Donor, /*!< \brief Maximum faces per processor*/ - MaxFaceNodes_Donor; /*!< \brief Maximum nodes associated with faces per processor*/ - - unsigned long - *Buffer_Receive_nVertex_Donor, /*!< \brief Buffer to store the number of vertices per processor on the Donor domain */ - *Buffer_Receive_nFace_Donor, /*!< \brief Buffer to store the number of faces per processor*/ - *Buffer_Receive_nFaceNodes_Donor, /*!< \brief Buffer to store the number of nodes associated with faces per processor*/ - *Buffer_Send_nVertex_Donor, /*!< \brief Buffer to send number of vertices on the local processor*/ - *Buffer_Send_nFace_Donor, /*!< \brief Buffer to send number of faces on the local processor*/ - *Buffer_Send_nFaceNodes_Donor, /*!< \brief Buffer to send the number of nodes assocated with faces per processor*/ - *Buffer_Send_FaceIndex, /*!< \brief Buffer to send indices pointing to the node indices that define the faces*/ - *Buffer_Receive_FaceIndex, /*!< \brief Buffer to receive indices pointing to the node indices that define the faces*/ - *Buffer_Send_FaceNodes, /*!< \brief Buffer to send indices pointing to the location of node information in other buffers, defining faces*/ - *Buffer_Receive_FaceNodes, /*!< \brief Buffer to receive indices pointing to the location of node information in other buffers, defining faces*/ - *Buffer_Send_FaceProc, /*!< \brief Buffer to send processor which stores the node indicated in Buffer_Receive_FaceNodes*/ - *Buffer_Receive_FaceProc; /*!< \brief Buffer to receive processor which stores the node indicated in Buffer_Receive_FaceNodes*/ - - long *Buffer_Send_GlobalPoint, /*!< \brief Buffer to send global point indices*/ - *Buffer_Receive_GlobalPoint; /*!< \brief Buffer to receive global point indices*/ - - su2double *Buffer_Send_Coord, /*!< \brief Buffer to send coordinate values*/ - *Buffer_Send_Normal, /*!< \brief Buffer to send normal vector values */ - *Buffer_Receive_Coord, /*!< \brief Buffer to receive coordinate values*/ - *Buffer_Receive_Normal; /*!< \brief Buffer to receive normal vector values*/ - - unsigned long *Receive_GlobalPoint, /*!< \brief Buffer to receive Global point indexes*/ - *Buffer_Receive_nLinkedNodes, /*!< \brief Buffer to receive the number of edges connected to each node*/ - *Buffer_Receive_LinkedNodes, /*!< \brief Buffer to receive the list of notes connected to the nodes through an edge*/ - *Buffer_Receive_StartLinkedNodes, /*!< \brief Buffer to receive the index of the Receive_LinkedNodes buffer where corresponding list of linked nodes begins */ - *Buffer_Receive_Proc; /*!< \brief Buffer to receive the thread that owns the node*/ - - unsigned long nGlobalVertex_Target, /*!< \brief Global number of vertex of the target boundary*/ - nLocalVertex_Target, /*!< \brief Number of vertex of the target boundary owned by the thread*/ - nGlobalVertex_Donor, /*!< \brief Global number of vertex of the donor boundary*/ - nLocalVertex_Donor, /*!< \brief Number of vertex of the donor boundary owned by the thread*/ - nGlobalVertex, /*!< \brief Dummy variable to temporarily store the global number of vertex of a boundary*/ - nLocalLinkedNodes; /*!< \brief Dummy variable to temporarily store the number of vertex of a boundary*/ - - CGeometry**** const Geometry; /*! \brief Vector which stores n zones of geometry. */ - CGeometry* const donor_geometry; /*! \brief Vector which stores the donor geometry. */ - CGeometry* const target_geometry; /*! \brief Vector which stores the target geometry. */ - - /*! - * \brief Constructor of the class, protected as it does not make sense to instantiate base class. - * \param[in] geometry - Geometrical definition of the problem. - * \param[in] config - Definition of the particular problem. - * \param[in] iZone - index of the donor zone - * \param[in] jZone - index of the target zone - */ - CInterpolator(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); - -public: - /*! - * \brief No default construction allowed. - */ - CInterpolator(void) = delete; - - /*! - * \brief Destructor of the class, nothing is deleted, derived classes need to manage the MPI buffers. - */ - virtual ~CInterpolator(void) = default; - - /*! - * \brief Set up transfer matrix defining relation between two meshes - * \note Main method that derived classes should implement. - * \param[in] config - Definition of the particular problem. - */ - inline virtual void Set_TransferCoeff(CConfig **config) {} - -protected: - /*! - * \brief Find the index of the interface marker shared by that zone - * \param[in] config - Definition of the particular problem. - * \param[in] val_marker_interface - Interface tag. - */ - int Find_InterfaceMarker(const CConfig *config, unsigned short val_marker_interface) const; - - /*! - * \brief Check whether an interface should be processed or not, i.e. if it is part of the zones. - * \param[in] val_markDonor - Marker tag from donor zone. - * \param[in] val_markTarget - Marker tag from target zone. - */ - bool CheckInterfaceBoundary(int val_markDonor, int val_markTarget) const; - - /*! - * \brief Recontstruct the boundary connectivity from parallel partitioning and broadcasts it to all threads - * \param[in] val_zone - index of the zone - * \param[in] val_marker - index of the marker - */ - void ReconstructBoundary(unsigned long val_zone, int val_marker); - - /*! - * \brief compute squared distance between 2 points - * \param[in] nDim - number of dimensions - * \param[in] point_i - coordinates of point i - * \param[in] point_j - coordinates of point j - */ - inline su2double PointsSquareDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) const { - su2double d = 0.0; - for(unsigned short iDim = 0; iDim < nDim; iDim++) - d += pow(point_j[iDim] - point_i[iDim], 2); - return d; - } - - /*! - * \brief compute distance between 2 points - * \param[in] nDim - number of dimensions - * \param[in] point_i - coordinates of point i - * \param[in] point_j - coordinates of point j - */ - inline su2double PointsDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) const { - return sqrt(PointsSquareDistance(nDim, point_i, point_j)); - } - - /*! - * \brief Determine array sizes used to collect and send coordinate and global point - * information. - * \param[in] faces - boolean that determines whether or not to set face information as well - * \param[in] markDonor - Index of the boundary on the donor domain. - * \param[in] markTarget - Index of the boundary on the target domain. - * \param[in] nVertexDonor - Number of vertices on the donor boundary. - * \param[in] nDim - number of physical dimensions. - */ - void Determine_ArraySize(bool faces, int markDonor, int markTarget, unsigned long nVertexDonor, unsigned short nDim); - - /*! - * \brief Collect and communicate vertex info: coord, global point, and if faces=true the normal vector - * \param[in] faces - boolean that determines whether or not to set face information as well - * \param[in] markDonor - Index of the boundary on the donor domain. - * \param[in] markTarget - Index of the boundary on the target domain. - * \param[in] nVertexDonor - Number of vertices on the donor boundary. - * \param[in] nDim - number of physical dimensions. - */ - void Collect_VertexInfo(bool faces, int markDonor, int markTarget, unsigned long nVertexDonor, unsigned short nDim); - -}; - -/*! - * \brief Nearest Neighbor interpolation - */ -class CNearestNeighbor final : public CInterpolator { -public: - /*! - * \brief Constructor of the class. - * \param[in] geometry - Geometrical definition of the problem. - * \param[in] config - Definition of the particular problem. - * \param[in] iZone - index of the donor zone - * \param[in] jZone - index of the target zone - */ - CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); - - /*! - * \brief Set up transfer matrix defining relation between two meshes - * \param[in] config - Definition of the particular problem. - */ - void Set_TransferCoeff(CConfig **config) override; - -}; - -/*! - * \brief Isoparametric interpolation - */ -class CIsoparametric final : public CInterpolator { -public: - /*! - * \brief Constructor of the class. - * \param[in] geometry - Geometrical definition of the problem. - * \param[in] config - Definition of the particular problem. - * \param[in] iZone - index of the donor zone - * \param[in] jZone - index of the target zone - */ - CIsoparametric(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); - - /*! - * \brief Set up transfer matrix defining relation between two meshes - * \param[in] config - Definition of the particular problem. - */ - void Set_TransferCoeff(CConfig **config) override; - -private: - /*! - * \brief Calculate the isoparametric representation of point iVertex in marker iZone_0 by nodes of element donor_elem in marker jMarker of zone iZone_1. - * \param[in] iVertex - vertex index of the point being interpolated. - * \param[in] nDim - the dimension of the coordinates. - * \param[in] iZone_1 - zone index of the element to use for interpolation (the DONOR zone) - * \param[in] donor_elem - element index of the element to use for interpolation (or global index of a point in 2D) - * \param[in] nDonorPoints - number of donor points in the element. - * \param[in] xj - point projected onto the plane of the donor element. - * \param[out] isoparams - isoparametric coefficients. Must be allocated to size nNodes ahead of time. (size> nDonors) - * - * \note If the problem is 2D, the 'face' projected onto is actually an edge; the local index - * of the edge is then stored in iFace, and the global index of the node (from which the edge - * is referenced) - */ - void Isoparameters(unsigned short nDim, unsigned short nDonor, su2double *X, su2double *xj,su2double* isoparams); - -}; - -/*! - * \brief Mirror interpolation: copy point linking and coefficient values from the opposing mesh - * Assumes that the oppoosing mesh has already run interpolation. (otherwise this will result in empty/trivial interpolation) - */ -class CMirror final : public CInterpolator { -public: - /*! - * \brief Constructor of the class. - * \note Data is set in geometry[targetZone]. - * \param[in] geometry_container - * \param[in] config - config container - * \param[in] iZone - First zone - * \param[in] jZone - Second zone - */ - CMirror(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); - - /*! - * \brief Set up transfer matrix defining relation between two meshes - * \param[in] config - Definition of the particular problem. - */ - void Set_TransferCoeff(CConfig **config) override; - -}; - -/*! - * \brief Sliding mesh approach - */ -class CSlidingMesh final : public CInterpolator { -public: - /*! - * \brief Constructor of the class. - * \param[in] geometry - Geometrical definition of the problem. - * \param[in] config - Definition of the particular problem. - * \param[in] iZone - index of the donor zone - * \param[in] jZone - index of the target zone - */ - CSlidingMesh(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); - - /*! - * \brief Set up transfer matrix defining relation between two meshes - * \param[in] config - Definition of the particular problem. - */ - void Set_TransferCoeff(CConfig **config) override; - -private: - /*! - * \brief For 3-Dimensional grids, build the dual surface element - * \param[in] map - array containing the index of the boundary points connected to the node - * \param[in] startIndex - for each vertex specifies the corresponding index in the global array containing the indexes of all its neighbouring vertexes - * \param[in] nNeighbour - for each vertex specifies the number of its neighbouring vertexes (on the boundary) - * \param[in] coord - array containing the coordinates of all the boundary vertexes - * \param[in] centralNode - label of the vertex around which the dual surface element is built - * \param[in] element - double array where element node coordinates will be stored - */ - int Build_3D_surface_element(unsigned long *map, unsigned long *startIndex, unsigned long* nNeighbor, - su2double *coord, unsigned long centralNode, su2double** element); - - /*! - * \brief For 2-Dimensional grids, compute intersection length of two segments projected along a given direction - * \param[in] A1 - first point of segment A - * \param[in] A2 - second point of segment A - * \param[in] B1 - first point of segment B - * \param[in] B2 - second point of segment B - * \param[in] Direction - along which segments are projected - */ - su2double ComputeLineIntersectionLength(su2double* A1, su2double* A2, su2double* B1, su2double* B2, su2double* Direction); - - /*! - * \brief For 3-Dimensional grids, compute intersection area between two triangle projected on a given plane - * \param[in] A1 - first point of triangle A - * \param[in] A2 - second point of triangle A - * \param[in] A3 - third point of triangle A - * \param[in] B1 - first point of triangle B - * \param[in] B2 - second point of triangle B - * \param[in] B3 - third point of triangle B - * \param[in] Direction - vector normal to projection plane - */ - su2double Compute_Triangle_Intersection(su2double* A1, su2double* A2, su2double* A3, su2double* B1, su2double* B2, su2double* B3, su2double* Direction); - - /*! - * \brief For 3-Dimensional grids, compute intersection area between two triangle projected on a given plane - * P1 from triangle P MUST be inside triangle Q, points order doesn't matter - * \param[in] P1 - first point of triangle A - * \param[in] P2 - second point of triangle A - * \param[in] P3 - third point of triangle A - * \param[in] Q1 - first point of triangle B - * \param[in] Q2 - second point of triangle B - * \param[in] Q3 - third point of triangle B - */ - su2double ComputeIntersectionArea( su2double* P1, su2double* P2, su2double* P3, su2double* Q1, su2double* Q2, su2double* Q3 ); - - /*! - * \brief For 2-Dimensional grids, check whether, and compute, two lines are intersecting - * \param[in] A1 - first defining first line - * \param[in] A2 - second defining first line - * \param[in] B1 - first defining second line - * \param[in] B2 - second defining second line - * \param[in] IntersectionPoint - Container for intersection coordinates - */ - void ComputeLineIntersectionPoint( su2double* A1, su2double* A2, su2double* B1, su2double* B2, su2double* IntersectionPoint ); - - /*! - * \brief For N-Dimensional grids, check whether a point is inside a triangle specified by 3 T points - * \param[in] Point - query point - * \param[in] T1 - first point of triangle T - * \param[in] T2 - second point of triangle T - * \param[in] T3 - third point of triangle T - */ - bool CheckPointInsideTriangle(su2double* Point, su2double* T1, su2double* T2, su2double* T3); - -}; - -/*! - * \brief Radial basis function interpolation - */ -class CRadialBasisFunction final : public CInterpolator { -public: - /*! - * \brief Constructor of the class. - * \param[in] geometry - Geometrical definition of the problem. - * \param[in] config - Definition of the particular problem. - * \param[in] iZone - index of the donor zone - * \param[in] jZone - index of the target zone - */ - CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); - - /*! - * \brief Set up transfer matrix defining relation between two meshes - * \param[in] config - Definition of the particular problem. - */ - void Set_TransferCoeff(CConfig **config) override; - - /*! - * \brief Compute the value of a radial basis function, this is static so it can be re-used. - * \param[in] type - of radial basis function - * \param[in] radius - the characteristic dimension - * \param[in] dist - distance - * \return value of the RBF. - */ - static su2double Get_RadialBasisValue(ENUM_RADIALBASIS type, const su2double radius, const su2double dist); - -private: - /*! - * \brief Compute the RBF "generator" matrix with or without polynomial terms. - * \note Multiplying C_inv_trunc by a column vector gives specific coefficients for given "known values", - * conversely, multiplying (on the left) by a row vector of polynomial and RBF values gives generic - * interpolation coefficients for a given target evaluation point. - * \param[in] type - Type of radial basis function. - * \param[in] usePolynomial - Whether to use polynomial terms. - * \param[in] radius - Normalizes point-to-point distance when computing RBF values. - * \param[in] coords - Coordinates of the donor points. - * \param[out] nPolynomial - Num of poly terms, -1 if !usePolynomial, nDim-1 if coords lie on plane, else nDim. - * \param[out] keepPolynomialRow - Size nDim, signals which (if any) iDim was removed from polynomial term. - * \param[out] C_inv_trunc - The generator matrix as described above. - */ - void ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, su2double radius, - const su2activematrix& coords, int& nPolynomial, - vector& keepPolynomialRow, su2passivematrix& C_inv_trunc) const; - - /*! - * \brief If the polynomial term is included in the interpolation, and the points lie on a plane, the matrix - * becomes rank deficient and cannot be inverted. This method detects that condition and corrects it by - * removing a row from P (the polynomial part of the interpolation matrix). - * \param[in] max_diff_tol - Tolerance to detect whether points are on a plane. - * \param[out] keep_row - Marks the dimensions of P kept. - * \param[in,out] P - Polynomial part of the interpolation matrix, one row may be eliminated. - * \return n_polynomial - Size of the polynomial part on exit (in practice nDim or nDim-1). - */ - int CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, su2passivematrix &P) const; - - /*! - * \brief Prunes (by setting to zero) small interpolation coefficients, i.e. - * <= tolerance*max(abs(coeffs)). The vector is re-scaled such that sum(coeffs)==1. - * \param[in] tolerance - Relative pruning tolerance. - * \param[in,out] coeffs - The vector of interpolation coefficients. - * \return Number of non-zero coefficients after pruning. - */ - int PruneSmallCoefficients(passivedouble tolerance, su2passivevector& coeffs) const; - -}; - -/*! - * \brief Helper class used by CRadialBasisFunction to compute the interpolation weights. - * The matrix is symmetric but full storage is used as that gives much better performance - * for some BLAS libraries (notably OpenBLAS). The code should be compiled with LAPACK - * to use optimized matrix inversion and multiplication routines. - */ -class CSymmetricMatrix{ -private: - enum DecompositionType { NONE, CHOLESKY, LU }; - - vector val_vec, decomp_vec; - vector perm_vec; - int sz = 0; - bool initialized = false; - DecompositionType decomposed = NONE; - - inline void CheckBounds(int i, int j) const { - assert(initialized && "Matrix not initialized."); - assert(i>=0 && i=0 && j. + */ + +#include "../../include/interface_interpolation/CInterpolator.hpp" +#include "../../include/CConfig.hpp" +#include "../../include/geometry/CGeometry.hpp" + + +CInterpolator::CInterpolator(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : + rank(SU2_MPI::GetRank()), + size(SU2_MPI::GetSize()), + donorZone(iZone), + targetZone(jZone), + Geometry(geometry_container), + donor_geometry(geometry_container[iZone][INST_0][MESH_0]), + target_geometry(geometry_container[jZone][INST_0][MESH_0]) { +} + +int CInterpolator::Find_InterfaceMarker(const CConfig *config, unsigned short val_marker_interface) const { + + for (unsigned short iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { + /*--- If the tag GetMarker_All_ZoneInterface(iMarker) equals the interface we are looking for. ---*/ + if (config->GetMarker_All_ZoneInterface(iMarker) == val_marker_interface) return iMarker; + } + return -1; +} + +bool CInterpolator::CheckInterfaceBoundary(int markDonor, int markTarget) const { + + /*--- Determine whether the boundary is not on the rank because of + * the partition or because it is not part of the zone. ---*/ + int donorCheck = -1, targetCheck = -1; + SU2_MPI::Allreduce(&markDonor, &donorCheck, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); + SU2_MPI::Allreduce(&markTarget, &targetCheck, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); + return (donorCheck != -1) && (targetCheck != -1); +} + +void CInterpolator::Determine_ArraySize(bool faces, int markDonor, int markTarget, + unsigned long nVertexDonor, unsigned short nDim) { + + unsigned long nLocalVertex_Donor = 0, nLocalFaceNodes_Donor=0, nLocalFace_Donor=0; + unsigned long iVertex, iPointDonor = 0; + /* Only needed if face data is also collected */ + unsigned long inode; + unsigned long donor_elem, jElem, jPoint; + unsigned short iDonor; + unsigned int nFaces=0, iFace, nNodes=0; + bool face_on_marker = true; + + for (iVertex = 0; iVertex < nVertexDonor; iVertex++) { + iPointDonor = donor_geometry->vertex[markDonor][iVertex]->GetNode(); + + if (!donor_geometry->node[iPointDonor]->GetDomain()) continue; + + nLocalVertex_Donor++; + + if (!faces) continue; + + /*--- On Donor geometry also communicate face info ---*/ + if (nDim==3) { + for (jElem=0; jElemnode[iPointDonor]->GetnElem(); jElem++) { + donor_elem = donor_geometry->node[iPointDonor]->GetElem(jElem); + nFaces = donor_geometry->elem[donor_elem]->GetnFaces(); + for (iFace=0; iFaceelem[donor_elem]->GetnNodesFace(iFace); + for (iDonor=0; iDonorelem[donor_elem]->GetFaces(iFace, iDonor); + jPoint = donor_geometry->elem[donor_elem]->GetNode(inode); + face_on_marker = (face_on_marker && (donor_geometry->node[jPoint]->GetVertex(markDonor) !=-1)); + } + if (face_on_marker ) { + nLocalFace_Donor++; + nLocalFaceNodes_Donor+=nNodes; + } + } + } + } + else { + /*--- in 2D we use the edges ---*/ + nNodes=2; + nFaces = donor_geometry->node[iPointDonor]->GetnPoint(); + for (iFace=0; iFacenode[iPointDonor]->GetEdge(iFace); + jPoint = donor_geometry->edge[inode]->GetNode(iDonor); + face_on_marker = (face_on_marker && (donor_geometry->node[jPoint]->GetVertex(markDonor) !=-1)); + } + if (face_on_marker ) { + nLocalFace_Donor++; + nLocalFaceNodes_Donor+=nNodes; + } + } + } + } + + Buffer_Send_nVertex_Donor[0] = nLocalVertex_Donor; + if (faces) { + Buffer_Send_nFace_Donor[0] = nLocalFace_Donor; + Buffer_Send_nFaceNodes_Donor[0] = nLocalFaceNodes_Donor; + } + + /*--- Send Interface vertex information --*/ + SU2_MPI::Allreduce(&nLocalVertex_Donor, &MaxLocalVertex_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_nVertex_Donor, 1, MPI_UNSIGNED_LONG, + Buffer_Receive_nVertex_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + if (faces) { + SU2_MPI::Allreduce(&nLocalFace_Donor, &nGlobalFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); + SU2_MPI::Allreduce(&nLocalFace_Donor, &MaxFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); + SU2_MPI::Allreduce(&nLocalFaceNodes_Donor, &nGlobalFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); + SU2_MPI::Allreduce(&nLocalFaceNodes_Donor, &MaxFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_nFace_Donor, 1, MPI_UNSIGNED_LONG, + Buffer_Receive_nFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, + Buffer_Receive_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + MaxFace_Donor++; + } +} + +void CInterpolator::Collect_VertexInfo(bool faces, int markDonor, int markTarget, + unsigned long nVertexDonor, unsigned short nDim) { + + unsigned long iVertex, iPointDonor = 0, iVertexDonor, nBuffer_Coord, nBuffer_Point, nLocalVertex_Donor; + unsigned short iDim; + + for (iVertex = 0; iVertex < MaxLocalVertex_Donor; iVertex++) Buffer_Send_GlobalPoint[iVertex] = -1; + + for (iVertex = 0; iVertex < MaxLocalVertex_Donor*nDim; iVertex++) Buffer_Send_Coord[iVertex] = 0.0; + + if(faces) + for (iVertex = 0; iVertex < MaxLocalVertex_Donor*nDim; iVertex++) Buffer_Send_Normal[iVertex] = 0.0; + + /*--- Copy coordinates and point to the auxiliar vector --*/ + nLocalVertex_Donor = 0; + + for (iVertexDonor = 0; iVertexDonor < nVertexDonor; iVertexDonor++) { + iPointDonor = donor_geometry->vertex[markDonor][iVertexDonor]->GetNode(); + if (donor_geometry->node[iPointDonor]->GetDomain()) { + Buffer_Send_GlobalPoint[nLocalVertex_Donor] = donor_geometry->node[iPointDonor]->GetGlobalIndex(); + for (iDim = 0; iDim < nDim; iDim++) + Buffer_Send_Coord[nLocalVertex_Donor*nDim+iDim] = donor_geometry->node[iPointDonor]->GetCoord(iDim); + + if (faces) { + const su2double* Normal = donor_geometry->vertex[markDonor][iVertexDonor]->GetNormal(); + for (iDim = 0; iDim < nDim; iDim++) + Buffer_Send_Normal[nLocalVertex_Donor*nDim+iDim] = Normal[iDim]; + } + nLocalVertex_Donor++; + } + } + nBuffer_Coord = MaxLocalVertex_Donor*nDim; + nBuffer_Point = MaxLocalVertex_Donor; + + SU2_MPI::Allgather(Buffer_Send_Coord, nBuffer_Coord, MPI_DOUBLE, + Buffer_Receive_Coord, nBuffer_Coord, MPI_DOUBLE, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_GlobalPoint, nBuffer_Point, MPI_LONG, + Buffer_Receive_GlobalPoint, nBuffer_Point, MPI_LONG, MPI_COMM_WORLD); + if (faces) { + SU2_MPI::Allgather(Buffer_Send_Normal, nBuffer_Coord, MPI_DOUBLE, + Buffer_Receive_Normal, nBuffer_Coord, MPI_DOUBLE, MPI_COMM_WORLD); + } +} + +void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ + + CGeometry *geom = Geometry[val_zone][INST_0][MESH_0]; + + unsigned long iVertex, jVertex, kVertex; + + unsigned long count, iTmp, *uptr, dPoint, EdgeIndex, jEdge, nEdges, nNodes, nVertex, iDim, nDim, iPoint; + + unsigned long nGlobalLinkedNodes, nLocalVertex, nLocalLinkedNodes; + + nDim = geom->GetnDim(); + + if( val_marker != -1 ) + nVertex = geom->GetnVertex( val_marker ); + else + nVertex = 0; + + + su2double *Buffer_Send_Coord = new su2double [ nVertex * nDim ]; + unsigned long *Buffer_Send_GlobalPoint = new unsigned long [ nVertex ]; + + unsigned long *Buffer_Send_nLinkedNodes = new unsigned long [ nVertex ]; + unsigned long *Buffer_Send_StartLinkedNodes = new unsigned long [ nVertex ]; + unsigned long **Aux_Send_Map = new unsigned long*[ nVertex ]; + +#ifdef HAVE_MPI + int nProcessor = size, iRank; + unsigned long iTmp2, tmp_index, tmp_index_2; +#endif + + /*--- Copy coordinates and point to the auxiliar vector ---*/ + + nGlobalVertex = 0; + nLocalVertex = 0; + nLocalLinkedNodes = 0; + + for (iVertex = 0; iVertex < nVertex; iVertex++) { + + Buffer_Send_nLinkedNodes[iVertex] = 0; + Aux_Send_Map[iVertex] = NULL; + + iPoint = geom->vertex[val_marker][iVertex]->GetNode(); + + if (geom->node[iPoint]->GetDomain()) { + Buffer_Send_GlobalPoint[nLocalVertex] = geom->node[iPoint]->GetGlobalIndex(); + + for (iDim = 0; iDim < nDim; iDim++) + Buffer_Send_Coord[nLocalVertex*nDim+iDim] = geom->node[iPoint]->GetCoord(iDim); + + nNodes = 0; + nEdges = geom->node[iPoint]->GetnPoint(); + + for (jEdge = 0; jEdge < nEdges; jEdge++){ + EdgeIndex = geom->node[iPoint]->GetEdge(jEdge); + + if( iPoint == geom->edge[EdgeIndex]->GetNode(0) ) + dPoint = geom->edge[EdgeIndex]->GetNode(1); + else + dPoint = geom->edge[EdgeIndex]->GetNode(0); + + if ( geom->node[dPoint]->GetVertex(val_marker) != -1 ) + nNodes++; + } + + Buffer_Send_StartLinkedNodes[nLocalVertex] = nLocalLinkedNodes; + Buffer_Send_nLinkedNodes[nLocalVertex] = nNodes; + + nLocalLinkedNodes += nNodes; + + Aux_Send_Map[nLocalVertex] = new unsigned long[ nNodes ]; + nNodes = 0; + + for (jEdge = 0; jEdge < nEdges; jEdge++){ + EdgeIndex = geom->node[iPoint]->GetEdge(jEdge); + + if( iPoint == geom->edge[EdgeIndex]->GetNode(0) ) + dPoint = geom->edge[EdgeIndex]->GetNode(1); + else + dPoint = geom->edge[EdgeIndex]->GetNode(0); + + if ( geom->node[dPoint]->GetVertex(val_marker) != -1 ){ + Aux_Send_Map[nLocalVertex][nNodes] = geom->node[dPoint]->GetGlobalIndex(); + nNodes++; + } + } + nLocalVertex++; + } + } + + unsigned long *Buffer_Send_LinkedNodes = new unsigned long [ nLocalLinkedNodes ]; + + nLocalLinkedNodes = 0; + + for (iVertex = 0; iVertex < nLocalVertex; iVertex++){ + for (jEdge = 0; jEdge < Buffer_Send_nLinkedNodes[iVertex]; jEdge++){ + Buffer_Send_LinkedNodes[nLocalLinkedNodes] = Aux_Send_Map[iVertex][jEdge]; + nLocalLinkedNodes++; + } + } + + for (iVertex = 0; iVertex < nVertex; iVertex++){ + if( Aux_Send_Map[iVertex] != NULL ) + delete [] Aux_Send_Map[iVertex]; + } + delete [] Aux_Send_Map; Aux_Send_Map = NULL; + + /*--- Reconstruct boundary by gathering data from all ranks ---*/ + + SU2_MPI::Allreduce( &nLocalVertex, &nGlobalVertex, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); + SU2_MPI::Allreduce(&nLocalLinkedNodes, &nGlobalLinkedNodes, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); + + Buffer_Receive_Coord = new su2double [ nGlobalVertex * nDim ]; + Buffer_Receive_GlobalPoint = new long[ nGlobalVertex ]; + Buffer_Receive_Proc = new unsigned long[ nGlobalVertex ]; + + Buffer_Receive_nLinkedNodes = new unsigned long[ nGlobalVertex ]; + Buffer_Receive_LinkedNodes = new unsigned long[ nGlobalLinkedNodes ]; + Buffer_Receive_StartLinkedNodes = new unsigned long[ nGlobalVertex ]; + +#ifdef HAVE_MPI + if (rank == MASTER_NODE){ + + for (iVertex = 0; iVertex < nDim*nLocalVertex; iVertex++) + Buffer_Receive_Coord[iVertex] = Buffer_Send_Coord[iVertex]; + + for (iVertex = 0; iVertex < nLocalVertex; iVertex++){ + Buffer_Receive_GlobalPoint[iVertex] = Buffer_Send_GlobalPoint[iVertex]; + Buffer_Receive_Proc[iVertex] = MASTER_NODE; + Buffer_Receive_nLinkedNodes[iVertex] = Buffer_Send_nLinkedNodes[iVertex]; + Buffer_Receive_StartLinkedNodes[iVertex] = Buffer_Send_StartLinkedNodes[iVertex]; + } + + for (iVertex = 0; iVertex < nLocalLinkedNodes; iVertex++) + Buffer_Receive_LinkedNodes[iVertex] = Buffer_Send_LinkedNodes[iVertex]; + + tmp_index = nLocalVertex; + tmp_index_2 = nLocalLinkedNodes; + + for(iRank = 1; iRank < nProcessor; iRank++){ + + SU2_MPI::Recv( &iTmp2, 1, MPI_UNSIGNED_LONG, iRank, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + SU2_MPI::Recv(&Buffer_Receive_LinkedNodes[tmp_index_2], iTmp2, MPI_UNSIGNED_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + + SU2_MPI::Recv( &iTmp, 1, MPI_UNSIGNED_LONG, iRank, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + SU2_MPI::Recv(&Buffer_Receive_Coord[tmp_index*nDim], nDim*iTmp, MPI_DOUBLE, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + + SU2_MPI::Recv( &Buffer_Receive_GlobalPoint[tmp_index], iTmp, MPI_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + SU2_MPI::Recv( &Buffer_Receive_nLinkedNodes[tmp_index], iTmp, MPI_UNSIGNED_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + SU2_MPI::Recv(&Buffer_Receive_StartLinkedNodes[tmp_index], iTmp, MPI_UNSIGNED_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + + for (iVertex = 0; iVertex < iTmp; iVertex++){ + Buffer_Receive_Proc[ tmp_index + iVertex ] = iRank; + Buffer_Receive_StartLinkedNodes[ tmp_index + iVertex ] += tmp_index_2; + } + + tmp_index += iTmp; + tmp_index_2 += iTmp2; + } + } + else{ + SU2_MPI::Send( &nLocalLinkedNodes, 1, MPI_UNSIGNED_LONG, 0, 0, MPI_COMM_WORLD); + SU2_MPI::Send(Buffer_Send_LinkedNodes, nLocalLinkedNodes, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD); + + SU2_MPI::Send( &nLocalVertex, 1, MPI_UNSIGNED_LONG, 0, 0, MPI_COMM_WORLD); + SU2_MPI::Send(Buffer_Send_Coord, nDim * nLocalVertex, MPI_DOUBLE, 0, 1, MPI_COMM_WORLD); + + SU2_MPI::Send( Buffer_Send_GlobalPoint, nLocalVertex, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD); + SU2_MPI::Send( Buffer_Send_nLinkedNodes, nLocalVertex, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD); + SU2_MPI::Send(Buffer_Send_StartLinkedNodes, nLocalVertex, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD); + } +#else + for (iVertex = 0; iVertex < nDim * nGlobalVertex; iVertex++) + Buffer_Receive_Coord[iVertex] = Buffer_Send_Coord[iVertex]; + + for (iVertex = 0; iVertex < nGlobalVertex; iVertex++){ + Buffer_Receive_GlobalPoint[iVertex] = Buffer_Send_GlobalPoint[iVertex]; + Buffer_Receive_Proc[iVertex] = MASTER_NODE; + Buffer_Receive_nLinkedNodes[iVertex] = Buffer_Send_nLinkedNodes[iVertex]; + Buffer_Receive_StartLinkedNodes[iVertex] = Buffer_Send_StartLinkedNodes[iVertex]; + } + + for (iVertex = 0; iVertex < nGlobalLinkedNodes; iVertex++) + Buffer_Receive_LinkedNodes[iVertex] = Buffer_Send_LinkedNodes[iVertex]; +#endif + + if (rank == MASTER_NODE){ + for (iVertex = 0; iVertex < nGlobalVertex; iVertex++){ + count = 0; + uptr = &Buffer_Receive_LinkedNodes[ Buffer_Receive_StartLinkedNodes[iVertex] ]; + + for (jVertex = 0; jVertex < Buffer_Receive_nLinkedNodes[iVertex]; jVertex++){ + iTmp = uptr[ jVertex ]; + for (kVertex = 0; kVertex < nGlobalVertex; kVertex++){ + if( Buffer_Receive_GlobalPoint[kVertex] == long(iTmp) ){ + uptr[ jVertex ] = kVertex; + count++; + break; + } + } + + if( count != (jVertex+1) ){ + for (kVertex = jVertex; kVertex < Buffer_Receive_nLinkedNodes[iVertex]-1; kVertex++){ + uptr[ kVertex ] = uptr[ kVertex + 1]; + } + Buffer_Receive_nLinkedNodes[iVertex]--; + jVertex--; + } + } + } + } + + SU2_MPI::Bcast(Buffer_Receive_GlobalPoint, nGlobalVertex, MPI_LONG, 0, MPI_COMM_WORLD); + SU2_MPI::Bcast(Buffer_Receive_Coord, nGlobalVertex*nDim, MPI_DOUBLE, 0, MPI_COMM_WORLD); + SU2_MPI::Bcast(Buffer_Receive_Proc, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + + SU2_MPI::Bcast(Buffer_Receive_nLinkedNodes, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + SU2_MPI::Bcast(Buffer_Receive_StartLinkedNodes, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + SU2_MPI::Bcast(Buffer_Receive_LinkedNodes, nGlobalLinkedNodes, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); + + delete [] Buffer_Send_Coord; Buffer_Send_Coord = NULL; + delete [] Buffer_Send_GlobalPoint; Buffer_Send_GlobalPoint = NULL; + delete [] Buffer_Send_LinkedNodes; Buffer_Send_LinkedNodes = NULL; + delete [] Buffer_Send_nLinkedNodes; Buffer_Send_nLinkedNodes = NULL; + delete [] Buffer_Send_StartLinkedNodes; Buffer_Send_StartLinkedNodes = NULL; + +} diff --git a/Common/src/interface_interpolation/CIsoparametric.cpp b/Common/src/interface_interpolation/CIsoparametric.cpp new file mode 100644 index 000000000000..0d42834b6a29 --- /dev/null +++ b/Common/src/interface_interpolation/CIsoparametric.cpp @@ -0,0 +1,563 @@ +/*! + * \file CIsoparametric.cpp + * \brief Implementation isoparametric interpolation (using FE shape functions). + * \author H. Kline + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ + +#include "../../include/interface_interpolation/CIsoparametric.hpp" +#include "../../include/CConfig.hpp" +#include "../../include/geometry/CGeometry.hpp" + + +CIsoparametric::CIsoparametric(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { + Set_TransferCoeff(config); +} + +void CIsoparametric::Set_TransferCoeff(CConfig **config) { + + unsigned long iVertex, jVertex; + unsigned long dPoint, inode, jElem, nElem; + unsigned short iDim, iDonor=0, iFace; + + unsigned short nDim = donor_geometry->GetnDim(); + + unsigned short nMarkerInt; + unsigned short iMarkerInt; + + int markDonor=0, markTarget=0; + + long donor_elem=0, temp_donor=0; + unsigned int nNodes=0; + /*--- Restricted to 2-zone for now ---*/ + unsigned int nFaces=1; //For 2D cases, we want to look at edges, not faces, as the 'interface' + bool face_on_marker=true; + + unsigned long nVertexDonor = 0, nVertexTarget= 0; + unsigned long Point_Target = 0; + + unsigned long iVertexDonor, iPointDonor = 0; + int iProcessor; + + unsigned long nLocalFace_Donor = 0, nLocalFaceNodes_Donor=0; + + unsigned long faceindex; + + su2double dist = 0.0, mindist=1E6, *Coord, *Coord_i; + su2double myCoeff[10]; // Maximum # of donor points + su2double *Normal; + su2double *projected_point = new su2double[nDim]; + su2double tmp, tmp2; + su2double storeCoeff[10]; + unsigned long storeGlobal[10]; + int storeProc[10]; + + int nProcessor = size; + Coord = new su2double[nDim]; + Normal = new su2double[nDim]; + + nMarkerInt = (config[donorZone]->GetMarker_n_ZoneInterface())/2; + + /*--- For the number of markers on the interface... ---*/ + for (iMarkerInt=1; iMarkerInt <= nMarkerInt; iMarkerInt++) { + /*--- Procedure: + * -Loop through vertices of the aero grid + * -Find nearest element and allocate enough space in the aero grid donor point info + * -set the transfer coefficient values + */ + + /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ + markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); + + /*--- On the target side: find the tag of the boundary sharing the interface ---*/ + markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); + + /*--- Checks if the zone contains the interface, if not continue to the next step ---*/ + if(!CheckInterfaceBoundary(markDonor, markTarget)) continue; + + if(markDonor != -1) + nVertexDonor = donor_geometry->GetnVertex( markDonor ); + else + nVertexDonor = 0; + + if(markTarget != -1) + nVertexTarget = target_geometry->GetnVertex( markTarget ); + else + nVertexTarget = 0; + + Buffer_Send_nVertex_Donor = new unsigned long [1]; + Buffer_Send_nFace_Donor = new unsigned long [1]; + Buffer_Send_nFaceNodes_Donor = new unsigned long [1]; + + Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; + Buffer_Receive_nFace_Donor = new unsigned long [nProcessor]; + Buffer_Receive_nFaceNodes_Donor = new unsigned long [nProcessor]; + + /* Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor */ + Determine_ArraySize(true, markDonor, markTarget, nVertexDonor, nDim); + + Buffer_Send_Coord = new su2double [MaxLocalVertex_Donor*nDim]; + Buffer_Send_Normal = new su2double [MaxLocalVertex_Donor*nDim]; + Buffer_Send_GlobalPoint = new long [MaxLocalVertex_Donor]; + + Buffer_Receive_Coord = new su2double [nProcessor*MaxLocalVertex_Donor*nDim]; + Buffer_Receive_Normal = new su2double [nProcessor*MaxLocalVertex_Donor*nDim]; + Buffer_Receive_GlobalPoint = new long [nProcessor*MaxLocalVertex_Donor]; + + /*-- Collect coordinates, global points, and normal vectors ---*/ + Collect_VertexInfo(true, markDonor,markTarget,nVertexDonor,nDim); + + Buffer_Send_FaceIndex = new unsigned long[MaxFace_Donor]; + Buffer_Send_FaceNodes = new unsigned long[MaxFaceNodes_Donor]; + Buffer_Send_FaceProc = new unsigned long[MaxFaceNodes_Donor]; + + Buffer_Receive_FaceIndex = new unsigned long[MaxFace_Donor*nProcessor]; + Buffer_Receive_FaceNodes = new unsigned long[MaxFaceNodes_Donor*nProcessor]; + Buffer_Receive_FaceProc = new unsigned long[MaxFaceNodes_Donor*nProcessor]; + + nLocalFace_Donor=0; + nLocalFaceNodes_Donor=0; + + /*--- Collect Face info ---*/ + + for (iVertex = 0; iVertex < MaxFace_Donor; iVertex++) { + Buffer_Send_FaceIndex[iVertex] = 0; + } + for (iVertex=0; iVertexvertex[markDonor][iVertexDonor]->GetNode(); + + if (donor_geometry->node[iPointDonor]->GetDomain()) { + + if (nDim==3) nElem = donor_geometry->node[iPointDonor]->GetnElem(); + else nElem =donor_geometry->node[iPointDonor]->GetnPoint(); + + for (jElem=0; jElem < nElem; jElem++) { + if (nDim==3) { + temp_donor = donor_geometry->node[iPointDonor]->GetElem(jElem); + nFaces = donor_geometry->elem[temp_donor]->GetnFaces(); + for (iFace=0; iFaceelem[temp_donor]->GetnNodesFace(iFace); + for (iDonor=0; iDonorelem[temp_donor]->GetFaces(iFace, iDonor); + dPoint = donor_geometry->elem[temp_donor]->GetNode(inode); + face_on_marker = (face_on_marker && (donor_geometry->node[dPoint]->GetVertex(markDonor) !=-1)); + } + + if (face_on_marker ) { + for (iDonor=0; iDonorelem[temp_donor]->GetFaces(iFace, iDonor); + dPoint = donor_geometry->elem[temp_donor]->GetNode(inode); + // Match node on the face to the correct global index + long jGlobalPoint = donor_geometry->node[dPoint]->GetGlobalIndex(); + for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { + for (jVertex = 0; jVertex < Buffer_Receive_nVertex_Donor[iProcessor]; jVertex++) { + if (jGlobalPoint == Buffer_Receive_GlobalPoint[MaxLocalVertex_Donor*iProcessor+jVertex]) { + Buffer_Send_FaceNodes[nLocalFaceNodes_Donor]=MaxLocalVertex_Donor*iProcessor+jVertex; + Buffer_Send_FaceProc[nLocalFaceNodes_Donor]=iProcessor; + } + } + } + nLocalFaceNodes_Donor++; // Increment total number of face-nodes / processor + } + /* Store the indices */ + Buffer_Send_FaceIndex[nLocalFace_Donor+1] = Buffer_Send_FaceIndex[nLocalFace_Donor]+nNodes; + nLocalFace_Donor++; // Increment number of faces / processor + } + } + } + else { + /*-- Determine whether this face/edge is on the marker --*/ + face_on_marker=true; + for (iDonor=0; iDonornode[iPointDonor]->GetEdge(jElem); + dPoint = donor_geometry->edge[inode]->GetNode(iDonor); + face_on_marker = (face_on_marker && (donor_geometry->node[dPoint]->GetVertex(markDonor) !=-1)); + } + if (face_on_marker ) { + for (iDonor=0; iDonornode[iPointDonor]->GetEdge(jElem); + dPoint = donor_geometry->edge[inode]->GetNode(iDonor); + // Match node on the face to the correct global index + long jGlobalPoint = donor_geometry->node[dPoint]->GetGlobalIndex(); + for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { + for (jVertex = 0; jVertex < Buffer_Receive_nVertex_Donor[iProcessor]; jVertex++) { + if (jGlobalPoint == Buffer_Receive_GlobalPoint[MaxLocalVertex_Donor*iProcessor+jVertex]) { + Buffer_Send_FaceNodes[nLocalFaceNodes_Donor]=MaxLocalVertex_Donor*iProcessor+jVertex; + Buffer_Send_FaceProc[nLocalFaceNodes_Donor]=iProcessor; + } + } + } + nLocalFaceNodes_Donor++; // Increment total number of face-nodes / processor + } + /* Store the indices */ + Buffer_Send_FaceIndex[nLocalFace_Donor+1] = Buffer_Send_FaceIndex[nLocalFace_Donor]+nNodes; + nLocalFace_Donor++; // Increment number of faces / processor + } + } + } + } + } + + //Buffer_Send_FaceIndex[nLocalFace_Donor+1] = MaxFaceNodes_Donor*rank+nLocalFaceNodes_Donor; + + SU2_MPI::Allgather(Buffer_Send_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, + Buffer_Receive_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_FaceProc, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, + Buffer_Receive_FaceProc, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, + Buffer_Receive_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + + /*--- Loop over the vertices on the target Marker ---*/ + for (iVertex = 0; iVertexvertex[markTarget][iVertex]->GetNode(); + + if (!target_geometry->node[Point_Target]->GetDomain()) continue; + + Coord_i = target_geometry->node[Point_Target]->GetCoord(); + /*---Loop over the faces previously communicated/stored ---*/ + for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { + + nFaces = (unsigned int)Buffer_Receive_nFace_Donor[iProcessor]; + + for (iFace = 0; iFace< nFaces; iFace++) { + /*--- ---*/ + + nNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1] - + (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; + + su2double *X = new su2double[nNodes*(nDim+1)]; + faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face + for (iDonor=0; iDonorvertex[markTarget][iVertex]->SetDonorElem(donor_elem); // in 2D is nearest neighbor + target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nNodes); + for (iDonor=0; iDonorvertex[markTarget][iVertex]->GetnDonorPoints(); + target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); + + for (iDonor=0; iDonorvertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor,storeGlobal[iDonor]); + //cout <vertex[markTarget][iVertex]->SetDonorCoeff(iDonor,storeCoeff[iDonor]); + target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]); + } + + } + + delete[] Buffer_Send_nVertex_Donor; + delete[] Buffer_Send_nFace_Donor; + delete[] Buffer_Send_nFaceNodes_Donor; + + delete[] Buffer_Receive_nVertex_Donor; + delete[] Buffer_Receive_nFace_Donor; + delete[] Buffer_Receive_nFaceNodes_Donor; + + delete[] Buffer_Send_Coord; + delete[] Buffer_Send_Normal; + delete[] Buffer_Send_GlobalPoint; + + delete[] Buffer_Receive_Coord; + delete[] Buffer_Receive_Normal; + delete[] Buffer_Receive_GlobalPoint; + + delete[] Buffer_Send_FaceIndex; + delete[] Buffer_Send_FaceNodes; + delete[] Buffer_Send_FaceProc; + + delete[] Buffer_Receive_FaceIndex; + delete[] Buffer_Receive_FaceNodes; + delete[] Buffer_Receive_FaceProc; + } + delete [] Coord; + delete [] Normal; + + delete [] projected_point; +} + +void CIsoparametric::Isoparameters(unsigned short nDim, unsigned short nDonor, + const su2double *X, const su2double *xj, su2double *isoparams) const { + + short iDonor,iDim,k; // indices + su2double tmp, tmp2; + + su2double *x = new su2double[nDim+1]; + su2double *x_tmp = new su2double[nDim+1]; + su2double *Q = new su2double[nDonor*nDonor]; + su2double *R = new su2double[nDonor*nDonor]; + su2double *A = new su2double[(nDim+2)*nDonor]; + su2double *A2 = NULL; + su2double *x2 = new su2double[nDim+1]; + + bool *test = new bool[nDim+1]; + bool *testi = new bool[nDim+1]; + + su2double eps = 1E-10; + + short n = nDim+1; + + if (nDonor>2) { + /*--- Create Matrix A: 1st row all 1's, 2nd row x coordinates, 3rd row y coordinates, etc ---*/ + /*--- Right hand side is [1, \vec{x}']'---*/ + for (iDonor=0; iDonoreps && iDonor=0; iDonor--) { + if (R[iDonor*nDonor+iDonor]>eps) + isoparams[iDonor]=x_tmp[iDonor]/R[iDonor*nDonor+iDonor]; + else + isoparams[iDonor]=0; + for (k=0; k1.0) xi=1.0; + if (xi<-1.0) xi=-1.0; + if (eta>1.0) eta=1.0; + if (eta<-1.0) eta=-1.0; + isoparams[0]=0.25*(1-xi)*(1-eta); + isoparams[1]=0.25*(1+xi)*(1-eta); + isoparams[2]=0.25*(1+xi)*(1+eta); + isoparams[3]=0.25*(1-xi)*(1+eta); + + } + if (nDonor<4) { + tmp = 0.0; // value for normalization + tmp2=0; // check for maximum value, to be used to id nearest neighbor if necessary + k=0; // index for maximum value + for (iDonor=0; iDonor< nDonor; iDonor++) { + if (isoparams[iDonor]>tmp2) { + k=iDonor; + tmp2=isoparams[iDonor]; + } + // [0,1] + if (isoparams[iDonor]<0) isoparams[iDonor]=0; + if (isoparams[iDonor]>1) isoparams[iDonor] = 1; + tmp +=isoparams[iDonor]; + } + if (tmp>0) + for (iDonor=0; iDonor< nDonor; iDonor++) + isoparams[iDonor]=isoparams[iDonor]/tmp; + else { + isoparams[k] = 1.0; + } + } + + delete [] x; + delete [] x_tmp; + delete [] Q; + delete [] R; + delete [] A; + delete [] A2; + delete [] x2; + + delete [] test; + delete [] testi; + +} diff --git a/Common/src/interface_interpolation/CMirror.cpp b/Common/src/interface_interpolation/CMirror.cpp new file mode 100644 index 000000000000..67ed3246c628 --- /dev/null +++ b/Common/src/interface_interpolation/CMirror.cpp @@ -0,0 +1,243 @@ +/*! + * \file CMirror.cpp + * \brief Implementation of mirror interpolation (conservative approach in FSI problems). + * \author H. Kline + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ + +#include "../../include/interface_interpolation/CMirror.hpp" +#include "../../include/CConfig.hpp" +#include "../../include/geometry/CGeometry.hpp" + + +CMirror::CMirror(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { + Set_TransferCoeff(config); +} + +void CMirror::Set_TransferCoeff(CConfig **config) { + + unsigned long iVertex, jVertex; + unsigned long iPoint; + unsigned short iDonor=0, iFace=0, iTarget=0; + + unsigned short nMarkerInt; + unsigned short iMarkerInt; + + int markDonor=0, markTarget=0; + + unsigned int nNodes=0, iNodes=0; + unsigned long nVertexDonor = 0, nVertexTarget= 0; + unsigned long Point_Donor = 0; + unsigned long pGlobalPoint = 0; + int iProcessor; + + unsigned long nLocalFace_Donor = 0, nLocalFaceNodes_Donor=0; + + unsigned long faceindex; + + int nProcessor = size; + + su2double *Buffer_Send_Coeff, *Buffer_Receive_Coeff; + su2double coeff; + + /*--- Number of markers on the interface ---*/ + nMarkerInt = (config[targetZone]->GetMarker_n_ZoneInterface())/2; + + /*--- For the number of markers on the interface... ---*/ + for (iMarkerInt=1; iMarkerInt <= nMarkerInt; iMarkerInt++) { + /*--- Procedure: + * - Loop through vertices of the aero grid + * - Find nearest element and allocate enough space in the aero grid donor point info + * - Set the transfer coefficient values + */ + + /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ + markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); + + /*--- On the target side: find the tag of the boundary sharing the interface ---*/ + markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); + + /*--- Checks if the zone contains the interface, if not continue to the next step ---*/ + if(!CheckInterfaceBoundary(markDonor, markTarget)) continue; + + if(markDonor != -1) + nVertexDonor = donor_geometry->GetnVertex( markDonor ); + else + nVertexDonor = 0; + + if(markTarget != -1) + nVertexTarget = target_geometry->GetnVertex( markTarget ); + else + nVertexTarget = 0; + + /*-- Collect the number of donor nodes: re-use 'Face' containers --*/ + nLocalFace_Donor=0; + nLocalFaceNodes_Donor=0; + for (jVertex = 0; jVertexvertex[markDonor][jVertex]->GetNode(); // Local index of jVertex + + if (donor_geometry->node[Point_Donor]->GetDomain()) { + nNodes = donor_geometry->vertex[markDonor][jVertex]->GetnDonorPoints(); + nLocalFaceNodes_Donor+=nNodes; + nLocalFace_Donor++; + } + } + Buffer_Send_nFace_Donor= new unsigned long [1]; + Buffer_Send_nFaceNodes_Donor= new unsigned long [1]; + + Buffer_Receive_nFace_Donor = new unsigned long [nProcessor]; + Buffer_Receive_nFaceNodes_Donor = new unsigned long [nProcessor]; + + Buffer_Send_nFace_Donor[0] = nLocalFace_Donor; + Buffer_Send_nFaceNodes_Donor[0] = nLocalFaceNodes_Donor; + + /*--- Send Interface vertex information --*/ +#ifdef HAVE_MPI + SU2_MPI::Allreduce(&nLocalFaceNodes_Donor, &MaxFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); + SU2_MPI::Allreduce(&nLocalFace_Donor, &MaxFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_nFace_Donor, 1, MPI_UNSIGNED_LONG, Buffer_Receive_nFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, Buffer_Receive_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + MaxFace_Donor++; +#else + nGlobalFace_Donor = nLocalFace_Donor; + nGlobalFaceNodes_Donor = nLocalFaceNodes_Donor; + MaxFaceNodes_Donor = nLocalFaceNodes_Donor; + MaxFace_Donor = nLocalFace_Donor+1; + Buffer_Receive_nFace_Donor[0] = Buffer_Send_nFace_Donor[0]; + Buffer_Receive_nFaceNodes_Donor[0] = Buffer_Send_nFaceNodes_Donor[0]; +#endif + + /*-- Send donor info --*/ + Buffer_Send_FaceIndex = new unsigned long[MaxFace_Donor]; + Buffer_Send_FaceNodes = new unsigned long[MaxFaceNodes_Donor]; + Buffer_Send_GlobalPoint = new long[MaxFaceNodes_Donor]; + Buffer_Send_Coeff = new su2double[MaxFaceNodes_Donor]; + + Buffer_Receive_FaceIndex= new unsigned long[MaxFace_Donor*nProcessor]; + Buffer_Receive_FaceNodes= new unsigned long[MaxFaceNodes_Donor*nProcessor]; + Buffer_Receive_GlobalPoint = new long[MaxFaceNodes_Donor*nProcessor]; + Buffer_Receive_Coeff = new su2double[MaxFaceNodes_Donor*nProcessor]; + + for (iVertex=0; iVertexvertex[markDonor][jVertex]->GetNode(); // Local index of jVertex + if (donor_geometry->node[Point_Donor]->GetDomain()) { + nNodes = donor_geometry->vertex[markDonor][jVertex]->GetnDonorPoints(); + for (iDonor=0; iDonornode[Point_Donor]->GetGlobalIndex(); + Buffer_Send_GlobalPoint[nLocalFaceNodes_Donor] = + donor_geometry->vertex[markDonor][jVertex]->GetInterpDonorPoint(iDonor); + Buffer_Send_Coeff[nLocalFaceNodes_Donor] = + donor_geometry->vertex[markDonor][jVertex]->GetDonorCoeff(iDonor); + nLocalFaceNodes_Donor++; + } + Buffer_Send_FaceIndex[nLocalFace_Donor+1] =Buffer_Send_FaceIndex[nLocalFace_Donor]+nNodes; + nLocalFace_Donor++; + } + } + + SU2_MPI::Allgather(Buffer_Send_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, + Buffer_Receive_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_GlobalPoint, MaxFaceNodes_Donor, MPI_LONG, + Buffer_Receive_GlobalPoint, MaxFaceNodes_Donor, MPI_LONG, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_Coeff, MaxFaceNodes_Donor, MPI_DOUBLE, + Buffer_Receive_Coeff, MaxFaceNodes_Donor, MPI_DOUBLE, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, + Buffer_Receive_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + + /*--- Loop over the vertices on the target Marker ---*/ + for (iVertex = 0; iVertexvertex[markTarget][iVertex]->GetNode(); + if (target_geometry->node[iPoint]->GetDomain()) { + long Global_Point = target_geometry->node[iPoint]->GetGlobalIndex(); + nNodes = 0; + for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { + for (iFace = 0; iFace < Buffer_Receive_nFace_Donor[iProcessor]; iFace++) { + faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face + iNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1]- (unsigned int)faceindex; + for (iTarget=0; iTargetvertex[markTarget][iVertex]->SetnDonorPoints(nNodes); + target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); + + iDonor = 0; + for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { + for (iFace = 0; iFace < Buffer_Receive_nFace_Donor[iProcessor]; iFace++) { + + faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face + iNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1]- (unsigned int)faceindex; + for (iTarget=0; iTargetvertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor,pGlobalPoint); + target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor,coeff); + target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, iProcessor); + iDonor++; + } + } + } + } + } + } + delete[] Buffer_Send_nFace_Donor; + delete[] Buffer_Send_nFaceNodes_Donor; + + delete[] Buffer_Receive_nFace_Donor; + delete[] Buffer_Receive_nFaceNodes_Donor; + + delete[] Buffer_Send_FaceIndex; + delete[] Buffer_Send_FaceNodes; + delete[] Buffer_Send_GlobalPoint; + delete[] Buffer_Send_Coeff; + + delete[] Buffer_Receive_FaceIndex; + delete[] Buffer_Receive_FaceNodes; + delete[] Buffer_Receive_GlobalPoint; + delete[] Buffer_Receive_Coeff; + + } +} diff --git a/Common/src/interface_interpolation/CNearestNeighbor.cpp b/Common/src/interface_interpolation/CNearestNeighbor.cpp new file mode 100644 index 000000000000..04843729aa97 --- /dev/null +++ b/Common/src/interface_interpolation/CNearestNeighbor.cpp @@ -0,0 +1,137 @@ +/*! + * \file CNearestNeighbor.cpp + * \brief Implementation of nearest neighbor interpolation. + * \author H. Kline + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ + +#include "../../include/interface_interpolation/CNearestNeighbor.hpp" +#include "../../include/CConfig.hpp" +#include "../../include/geometry/CGeometry.hpp" + + +CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { + Set_TransferCoeff(config); +} + +void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { + + /*--- By definition, one donor point per target point. ---*/ + constexpr auto numDonor = 1; + constexpr auto idxDonor = 0; + + const su2double eps = numeric_limits::epsilon(); + + const int nProcessor = size; + const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2; + const auto nDim = donor_geometry->GetnDim(); + + Buffer_Send_nVertex_Donor = new unsigned long [1]; + Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; + + /*--- Cycle over nMarkersInt interface to determine communication pattern. ---*/ + + for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { + + /*--- On the donor side: find the tag of the boundary sharing the interface. ---*/ + const auto markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); + + /*--- On the target side: find the tag of the boundary sharing the interface. ---*/ + const auto markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); + + /*--- Checks if the zone contains the interface, if not continue to the next step. ---*/ + if (!CheckInterfaceBoundary(markDonor, markTarget)) continue; + + unsigned long nVertexDonor = 0, nVertexTarget = 0; + if(markDonor != -1) nVertexDonor = donor_geometry->GetnVertex( markDonor ); + if(markTarget != -1) nVertexTarget = target_geometry->GetnVertex( markTarget ); + + /* Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor. */ + Determine_ArraySize(false, markDonor, markTarget, nVertexDonor, nDim); + + Buffer_Send_Coord = new su2double [ MaxLocalVertex_Donor * nDim ]; + Buffer_Send_GlobalPoint = new long [ MaxLocalVertex_Donor ]; + Buffer_Receive_Coord = new su2double [ nProcessor * MaxLocalVertex_Donor * nDim ]; + Buffer_Receive_GlobalPoint = new long [ nProcessor * MaxLocalVertex_Donor ]; + + /*-- Collect coordinates and global point indices. ---*/ + Collect_VertexInfo( false, markDonor, markTarget, nVertexDonor, nDim ); + + /*--- Compute the closest donor point to each target. ---*/ + SU2_OMP_PARALLEL_(for schedule(dynamic,roundUpDiv(nVertexTarget,2*omp_get_max_threads()))) + for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget++) { + + auto target_vertex = target_geometry->vertex[markTarget][iVertexTarget]; + const auto Point_Target = target_vertex->GetNode(); + + if (!target_geometry->node[Point_Target]->GetDomain()) continue; + + /*--- Coordinates of the target point. ---*/ + const su2double* Coord_i = target_geometry->node[Point_Target]->GetCoord(); + + su2double mindist = 1e20; + long pGlobalPoint = 0; + int pProcessor = 0; + + for (int iProcessor = 0; iProcessor < nProcessor; ++iProcessor) { + for (auto jVertex = 0ul; jVertex < Buffer_Receive_nVertex_Donor[iProcessor]; ++jVertex) { + + const auto idx = iProcessor*MaxLocalVertex_Donor + jVertex; + + const su2double* Coord_j = &Buffer_Receive_Coord[idx*nDim]; + + const auto dist = PointsSquareDistance(nDim, Coord_i, Coord_j); + + if (dist < mindist) { + mindist = dist; + pProcessor = iProcessor; + pGlobalPoint = Buffer_Receive_GlobalPoint[idx]; + } + + /*--- Test for "exact" match. ---*/ + if (dist < eps) break; + } + } + + /*--- Store matching pair. ---*/ + target_vertex->SetnDonorPoints(numDonor); + target_vertex->Allocate_DonorInfo(); + target_vertex->SetInterpDonorPoint(idxDonor, pGlobalPoint); + target_vertex->SetInterpDonorProcessor(idxDonor, pProcessor); + target_vertex->SetDonorCoeff(idxDonor, 1.0); + + } + + delete[] Buffer_Send_Coord; + delete[] Buffer_Send_GlobalPoint; + + delete[] Buffer_Receive_Coord; + delete[] Buffer_Receive_GlobalPoint; + + } + + delete[] Buffer_Send_nVertex_Donor; + delete[] Buffer_Receive_nVertex_Donor; + +} diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp new file mode 100644 index 000000000000..c9ba5b5e9294 --- /dev/null +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -0,0 +1,772 @@ +/*! + * \file CRadialBasisFunction.cpp + * \brief Implementation of RBF interpolation. + * \author Joel Ho, P. Gomes + * \version 7.0.2 "Blackbird" + * + * SU2 Project Website: https://su2code.github.io + * + * The SU2 Project is maintained by the SU2 Foundation + * (http://su2foundation.org) + * + * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md) + * + * SU2 is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * SU2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with SU2. If not, see . + */ + +#include "../../include/interface_interpolation/CRadialBasisFunction.hpp" +#include "../../include/CConfig.hpp" +#include "../../include/geometry/CGeometry.hpp" + + +CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { + Set_TransferCoeff(config); +} + +su2double CRadialBasisFunction::Get_RadialBasisValue(ENUM_RADIALBASIS type, const su2double radius, const su2double dist) +{ + su2double rbf = dist/radius; + + switch (type) { + + case WENDLAND_C2: + if(rbf < 1) rbf = pow(pow((1-rbf),2),2)*(4*rbf+1); // double use of pow(x,2) for optimization + else rbf = 0.0; + break; + + case GAUSSIAN: + rbf = exp(-rbf*rbf); + break; + + case THIN_PLATE_SPLINE: + if(rbf < numeric_limits::min()) rbf = 0.0; + else rbf *= rbf*log(rbf); + break; + + case MULTI_QUADRIC: + case INV_MULTI_QUADRIC: + rbf = sqrt(1.0+rbf*rbf); + if(type == INV_MULTI_QUADRIC) rbf = 1.0/rbf; + break; + } + + return rbf; +} + +void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { + + /*--- RBF options. ---*/ + const auto kindRBF = static_cast(config[donorZone]->GetKindRadialBasisFunction()); + const bool usePolynomial = config[donorZone]->GetRadialBasisFunctionPolynomialOption(); + const su2double paramRBF = config[donorZone]->GetRadialBasisFunctionParameter(); + const su2double pruneTol = config[donorZone]->GetRadialBasisFunctionPruneTol(); + + const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2; + const int nDim = donor_geometry->GetnDim(); + + const int nProcessor = size; + Buffer_Send_nVertex_Donor = new unsigned long [1]; + Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; + + /*--- Process interface patches in parallel, fetch all donor point coordinates, + * then distribute interpolation matrix computation over ranks and threads. + * To avoid repeating calls to Collect_VertexInfo we also save the global + * indices of the donor points and the mpi rank index that owns them. ---*/ + vector DonorCoordinates(nMarkerInt); + vector > DonorGlobalPoint(nMarkerInt); + vector > DonorProcessor(nMarkerInt); + vector AssignedProcessor(nMarkerInt,-1); + vector TotalWork(nProcessor,0); + + for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; ++iMarkerInt) { + + /*--- On the donor side: find the tag of the boundary sharing the interface. ---*/ + const auto mark_donor = Find_InterfaceMarker(config[donorZone], iMarkerInt+1); + + /*--- On the target side: find the tag of the boundary sharing the interface. ---*/ + const auto mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt+1); + + /*--- If the zone does not contain the interface continue to the next pair of markers. ---*/ + if(!CheckInterfaceBoundary(mark_donor,mark_target)) continue; + + unsigned long nVertexDonor = 0; + if(mark_donor != -1) nVertexDonor = donor_geometry->GetnVertex(mark_donor); + + /*--- Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor. ---*/ + Determine_ArraySize(false, mark_donor, mark_target, nVertexDonor, nDim); + + /*--- Compute total number of donor vertices. ---*/ + auto nGlobalVertexDonor = accumulate(Buffer_Receive_nVertex_Donor, + Buffer_Receive_nVertex_Donor+nProcessor, 0ul); + + /*--- Gather coordinates and global point indices. ---*/ + Buffer_Send_Coord = new su2double [ MaxLocalVertex_Donor * nDim ]; + Buffer_Send_GlobalPoint = new long [ MaxLocalVertex_Donor ]; + Buffer_Receive_Coord = new su2double [ nProcessor * MaxLocalVertex_Donor * nDim ]; + Buffer_Receive_GlobalPoint = new long [ nProcessor * MaxLocalVertex_Donor ]; + + Collect_VertexInfo(false, mark_donor, mark_target, nVertexDonor, nDim); + + /*--- Compresses the gathered donor point information to simplify computations. ---*/ + auto& DonorCoord = DonorCoordinates[iMarkerInt]; + auto& DonorPoint = DonorGlobalPoint[iMarkerInt]; + auto& DonorProc = DonorProcessor[iMarkerInt]; + DonorCoord.resize(nGlobalVertexDonor, nDim); + DonorPoint.resize(nGlobalVertexDonor); + DonorProc.resize(nGlobalVertexDonor); + + auto iCount = 0ul; + for (int iProcessor = 0; iProcessor < nProcessor; ++iProcessor) { + auto offset = iProcessor * MaxLocalVertex_Donor; + for (auto iVertex = 0ul; iVertex < Buffer_Receive_nVertex_Donor[iProcessor]; ++iVertex) { + for (int iDim = 0; iDim < nDim; ++iDim) + DonorCoord(iCount,iDim) = Buffer_Receive_Coord[(offset+iVertex)*nDim + iDim]; + DonorPoint[iCount] = Buffer_Receive_GlobalPoint[offset+iVertex]; + DonorProc[iCount] = iProcessor; + ++iCount; + } + } + assert((iCount == nGlobalVertexDonor) && "Global donor point count mismatch."); + + delete[] Buffer_Send_Coord; + delete[] Buffer_Send_GlobalPoint; + delete[] Buffer_Receive_Coord; + delete[] Buffer_Receive_GlobalPoint; + + /*--- Static work scheduling over ranks based on which one has less work currently. ---*/ + int iProcessor = 0; + for (int i = 1; i < nProcessor; ++i) + if (TotalWork[i] < TotalWork[iProcessor]) iProcessor = i; + + TotalWork[iProcessor] += pow(nGlobalVertexDonor,3); // based on matrix inversion. + + AssignedProcessor[iMarkerInt] = iProcessor; + + } + + /*--- Compute the interpolation matrices for each patch of coordinates + * assigned to the rank. Subdivide work further by threads. ---*/ + vector nPolynomialVec(nMarkerInt,-1); + vector > keepPolynomialRowVec(nMarkerInt, vector(nDim,1)); + vector CinvTrucVec(nMarkerInt); + + SU2_OMP_PARALLEL_(for schedule(dynamic,1)) + for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; ++iMarkerInt) { + if (rank == AssignedProcessor[iMarkerInt]) { + ComputeGeneratorMatrix(kindRBF, usePolynomial, paramRBF, + DonorCoordinates[iMarkerInt], nPolynomialVec[iMarkerInt], + keepPolynomialRowVec[iMarkerInt], CinvTrucVec[iMarkerInt]); + } + } + + /*--- Final loop over interface markers to compute the interpolation coefficients. ---*/ + + for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; iMarkerInt++) { + + /*--- Identify the rank that computed the interpolation matrix for this marker. ---*/ + const int iProcessor = AssignedProcessor[iMarkerInt]; + /*--- If no processor was assigned to work, the zone does not contain the interface. ---*/ + if (iProcessor < 0) continue; + + /*--- Setup target information. ---*/ + const int mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt+1); + unsigned long nVertexTarget = 0; + if(mark_target != -1) nVertexTarget = target_geometry->GetnVertex(mark_target); + + /*--- Set references to donor information. ---*/ + auto& DonorCoord = DonorCoordinates[iMarkerInt]; + auto& DonorPoint = DonorGlobalPoint[iMarkerInt]; + auto& DonorProc = DonorProcessor[iMarkerInt]; + + auto& C_inv_trunc = CinvTrucVec[iMarkerInt]; + auto& nPolynomial = nPolynomialVec[iMarkerInt]; + auto& keepPolynomialRow = keepPolynomialRowVec[iMarkerInt]; + + const auto nGlobalVertexDonor = DonorCoord.rows(); + +#ifdef HAVE_MPI + /*--- For simplicity, broadcast small information about the interpolation matrix. ---*/ + SU2_MPI::Bcast(&nPolynomial, 1, MPI_INT, iProcessor, MPI_COMM_WORLD); + SU2_MPI::Bcast(keepPolynomialRow.data(), nDim, MPI_INT, iProcessor, MPI_COMM_WORLD); + + /*--- Send C_inv_trunc only to the ranks that need it (those with target points), + * partial broadcast. MPI wrapper not used due to passive double. ---*/ + vector allNumVertex(nProcessor); + SU2_MPI::Allgather(&nVertexTarget, 1, MPI_UNSIGNED_LONG, + allNumVertex.data(), 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + + if (rank == iProcessor) { + for (int jProcessor = 0; jProcessor < nProcessor; ++jProcessor) + if ((jProcessor != iProcessor) && (allNumVertex[jProcessor] != 0)) + MPI_Send(C_inv_trunc.data(), C_inv_trunc.size(), + MPI_DOUBLE, jProcessor, 0, MPI_COMM_WORLD); + } + else if (nVertexTarget != 0) { + C_inv_trunc.resize(1+nPolynomial+nGlobalVertexDonor, nGlobalVertexDonor); + MPI_Recv(C_inv_trunc.data(), C_inv_trunc.size(), MPI_DOUBLE, + iProcessor, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + } +#endif + + /*--- Compute H (interpolation) matrix, distributing + * target points over the threads in the rank. ---*/ + SU2_OMP_PARALLEL + { + su2passivevector coeff_vec(nGlobalVertexDonor); + + SU2_OMP_FOR_DYN(roundUpDiv(nVertexTarget, 2*omp_get_max_threads())) + for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget++) { + + auto target_vertex = target_geometry->vertex[mark_target][iVertexTarget]; + const auto point_target = target_vertex->GetNode(); + + /*--- If not domain point move to next. ---*/ + if (!target_geometry->node[point_target]->GetDomain()) continue; + + const su2double* coord_i = target_geometry->node[point_target]->GetCoord(); + + /*--- Multiply target vector by C_inv_trunc to obtain the interpolation coefficients. + * The target vector is not stored, we consume its entries immediately to avoid + * strided access to C_inv_trunc (as it is row-major). ---*/ + + /*--- Polynominal part: ---*/ + if (usePolynomial) { + /*--- Constant term. ---*/ + for (auto j = 0ul; j < nGlobalVertexDonor; ++j) + coeff_vec(j) = C_inv_trunc(0,j); + + /*--- Linear terms. ---*/ + for (int iDim = 0, idx = 1; iDim < nDim; ++iDim) { + /*--- Of which one may have been excluded. ---*/ + if (!keepPolynomialRow[iDim]) continue; + for (auto j = 0ul; j < nGlobalVertexDonor; ++j) + coeff_vec(j) += SU2_TYPE::GetValue(coord_i[iDim]) * C_inv_trunc(idx,j); + idx += 1; + } + } + else { + /*--- Initialize vector to zero. ---*/ + for (auto j = 0ul; j < nGlobalVertexDonor; ++j) coeff_vec(j) = 0.0; + } + + /*--- RBF terms: ---*/ + for (auto iVertexDonor = 0ul; iVertexDonor < nGlobalVertexDonor; ++iVertexDonor) { + auto w_ij = SU2_TYPE::GetValue(Get_RadialBasisValue(kindRBF, paramRBF, + PointsDistance(nDim, coord_i, DonorCoord[iVertexDonor]))); + + for (auto j = 0ul; j < nGlobalVertexDonor; ++j) + coeff_vec(j) += w_ij * C_inv_trunc(1+nPolynomial+iVertexDonor, j); + } + + /*--- Prune small coefficients. ---*/ + auto nnz = PruneSmallCoefficients(SU2_TYPE::GetValue(pruneTol), coeff_vec); + + /*--- Allocate and set donor information for this target point. ---*/ + target_vertex->SetnDonorPoints(nnz); + target_vertex->Allocate_DonorInfo(); + + for (unsigned long iVertex = 0, iSet = 0; iVertex < nGlobalVertexDonor; ++iVertex) { + if (fabs(coeff_vec(iVertex)) > 0.0) { + target_vertex->SetInterpDonorProcessor(iSet, DonorProc[iVertex]); + target_vertex->SetInterpDonorPoint(iSet, DonorPoint[iVertex]); + target_vertex->SetDonorCoeff(iSet, coeff_vec(iVertex)); + ++iSet; + } + } + + } // end target vertex loop + } // end SU2_OMP_PARALLEL + + /*--- Delete global data that will no longer be used. ---*/ + DonorCoord.resize(0,0); + vector().swap(DonorPoint); + vector().swap(DonorProc); + C_inv_trunc.resize(0,0); + + } // end loop over interface markers + + delete[] Buffer_Send_nVertex_Donor; + delete[] Buffer_Receive_nVertex_Donor; + +} + +void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, + su2double radius, const su2activematrix& coords, int& nPolynomial, + vector& keepPolynomialRow, su2passivematrix& C_inv_trunc) const { + + const su2double interfaceCoordTol = 1e6 * numeric_limits::epsilon(); + + const auto nVertexDonor = coords.rows(); + const int nDim = coords.cols(); + + /*--- Populate interpolation kernel. ---*/ + CSymmetricMatrix global_M(nVertexDonor); + + for (auto iVertex = 0ul; iVertex < nVertexDonor; ++iVertex) + for (auto jVertex = iVertex; jVertex < nVertexDonor; ++jVertex) + global_M(iVertex, jVertex) = SU2_TYPE::GetValue(Get_RadialBasisValue(type, radius, + PointsDistance(nDim, coords[iVertex], coords[jVertex]))); + + /*--- Invert M matrix (operation is in-place). ---*/ + const bool kernelIsSPD = (type==WENDLAND_C2) || (type==GAUSSIAN) || (type==INV_MULTI_QUADRIC); + global_M.Invert(kernelIsSPD); + + /*--- Compute C_inv_trunc. ---*/ + if (usePolynomial) { + + /*--- Fill P matrix (P for points, with an extra top row of ones). ---*/ + su2passivematrix P(1+nDim, nVertexDonor); + + for (auto iVertex = 0ul; iVertex < nVertexDonor; iVertex++) { + P(0, iVertex) = 1.0; + for (int iDim = 0; iDim < nDim; ++iDim) + P(1+iDim, iVertex) = SU2_TYPE::GetValue(coords(iVertex, iDim)); + } + + /*--- Check if points lie on a plane and remove one coordinate from P if so. ---*/ + nPolynomial = CheckPolynomialTerms(interfaceCoordTol, keepPolynomialRow, P); + + /*--- Compute Mp = (P * M^-1 * P^T)^-1 ---*/ + CSymmetricMatrix Mp(nPolynomial+1); + + su2passivematrix tmp; + global_M.MatMatMult('R', P, tmp); // tmp = P * M^-1 + + for (int i = 0; i <= nPolynomial; ++i) // Mp = tmp * P + for (int j = i; j <= nPolynomial; ++j) { + Mp(i,j) = 0.0; + for (auto k = 0ul; k < nVertexDonor; ++k) Mp(i,j) += tmp(i,k) * P(j,k); + } + Mp.Invert(false); // Mp = Mp^-1 + + /*--- Compute M_p * P * M^-1, the top part of C_inv_trunc. ---*/ + Mp.MatMatMult('L', P, tmp); + su2passivematrix C_inv_top; + global_M.MatMatMult('R', tmp, C_inv_top); + + /*--- Compute tmp = (I - P^T * M_p * P * M^-1), part of the bottom part of + C_inv_trunc. Note that most of the product is known from the top part. ---*/ + tmp.resize(nVertexDonor, nVertexDonor); + + for (auto i = 0ul; i < nVertexDonor; ++i) { + for (auto j = 0ul; j < nVertexDonor; ++j) { + tmp(i,j) = 0.0; + for (int k = 0; k <= nPolynomial; ++k) tmp(i,j) -= P(k,i) * C_inv_top(k,j); + } + tmp(i,i) += 1.0; // identity part + } + + /*--- Compute M^-1 * (I - P^T * M_p * P * M^-1), finalize bottom of C_inv_trunc. ---*/ + global_M.MatMatMult('L', tmp, C_inv_trunc); + + /*--- Merge top and bottom of C_inv_trunc. ---*/ + tmp = move(C_inv_trunc); + C_inv_trunc.resize(1+nPolynomial+nVertexDonor, nVertexDonor); + memcpy(C_inv_trunc[0], C_inv_top.data(), C_inv_top.size()*sizeof(passivedouble)); + memcpy(C_inv_trunc[1+nPolynomial], tmp.data(), tmp.size()*sizeof(passivedouble)); + } + else { + /*--- No polynomial term used in the interpolation, C_inv_trunc = M^-1. ---*/ + + C_inv_trunc.resize(nVertexDonor, nVertexDonor); + for (auto i = 0ul; i < nVertexDonor; ++i) + for (auto j = 0ul; j < nVertexDonor; ++j) + C_inv_trunc(i,j) = global_M(i,j); + + } // end usePolynomial + +} + +int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, + su2passivematrix &P) const { + const int m = P.rows(); + const int n = P.cols(); + + /*--- The first row of P is all ones and we do not care about it for this analysis. ---*/ + const int n_rows = m-1; + keep_row.resize(n_rows); + + /*--- By default assume points are not on a plane (all rows kept). ---*/ + int n_polynomial = n_rows; + for (int i = 0; i < n_rows; ++i) keep_row[i] = 1; + + /*--- Fit a plane through the points in P. ---*/ + + /*--- Compute P times its transpose and invert. ---*/ + CSymmetricMatrix PPT(n_rows); + + for (int i = 0; i < n_rows; ++i) + for (int j = i; j < n_rows; ++j) { + PPT(i,j) = 0.0; + for (int k = 0; k < n; ++k) PPT(i,j) += P(i+1,k) * P(j+1,k); + } + PPT.Invert(true); + + /*--- RHS for the least squares fit (vector of ones times P). ---*/ + vector coeff(n_rows,0.0); + + for (int i = 0; i < n_rows; ++i) + for (int j = 0; j < n; ++j) + coeff[i] += P(i+1,j); + + /*--- Multiply the RHS by the inverse thus obtaining the coefficients. ---*/ + PPT.MatVecMult(coeff.data()); + + /*--- Determine the maximum deviation of the points from the fitted plane. ---*/ + passivedouble max_diff = 0.0; + + for (int j = 0; j < n; ++j) + { + passivedouble sum = 0.0; + for (int i = 0; i < n_rows; ++i) sum += coeff[i] * P(i+1,j); + + /*--- 1.0 is the arbitrary constant we are assuming when fitting + the plane, i.e. the vector of ones used to generate the RHS. ---*/ + max_diff = max(abs(1.0-sum), max_diff); + } + + /*--- If points lie on plane remove row associated with the maximum coefficient. ---*/ + if (max_diff < max_diff_tol) + { + /*--- Find the max coeff and mark the corresponding row for removal. ---*/ + int remove_row = 0; + for (int i = 1; i < n_rows; ++i) + if (abs(coeff[i]) > abs(coeff[remove_row])) + remove_row = i; + + /*--- Mark row as removed and adjust number of polynomial terms. ---*/ + n_polynomial = n_rows-1; + keep_row[remove_row] = 0; + + /*--- Truncated P by shifting rows "up". ---*/ + for (auto i = remove_row+1; i < m-1; ++i) + for (int j = 0; j < n; ++j) + P(i,j) = P(i+1,j); + } + + return n_polynomial; +} + +int CRadialBasisFunction::PruneSmallCoefficients(passivedouble tolerance, + su2passivevector& coeffs) const { + + /*--- Determine the pruning threshold. ---*/ + passivedouble thresh = 0.0; + for (auto i = 0ul; i < coeffs.size(); ++i) + thresh = max(thresh, fabs(coeffs(i))); + thresh *= tolerance; + + /*--- Prune and count non-zeros. ---*/ + int numNonZeros = 0; + passivedouble coeffSum = 0.0; + for (auto i = 0ul; i < coeffs.size(); ++i) { + if (fabs(coeffs(i)) > thresh) { + coeffSum += coeffs(i); + ++numNonZeros; + } + else coeffs(i) = 0.0; + } + + /*--- Correct remaining coefficients, sum must be 1 for conservation. ---*/ + passivedouble correction = 1.0 / coeffSum; + for (auto i = 0ul; i < coeffs.size(); ++i) coeffs(i) *= correction; + + return numNonZeros; +} + +void CSymmetricMatrix::Initialize(int N) +{ + sz = N; + val_vec.resize(sz*sz); + initialized = true; +} + +void CSymmetricMatrix::CholeskyDecompose() +{ +#ifndef HAVE_LAPACK + assert(initialized && "Matrix not initialized."); + + for (int j = 0; j < sz; ++j) { + passivedouble sum = 0.0; + for (int k = 0; k < j; ++k) sum -= pow(Get(j,k), 2); + sum += Get(j,j); + assert(sum > 0.0 && "LLT failed, matrix is not SPD."); + Set(j, j, sqrt(sum)); + + for (int i = j+1; i < sz; ++i) { + passivedouble sum = 0.0; + for (int k = 0; k < j; ++k) sum -= Get(i,k) * Get(j,k); + sum += Get(i,j); + Set(i, j, sum / Get(j,j)); + } + } + decomposed = CHOLESKY; +#endif +} + +void CSymmetricMatrix::LUDecompose() +{ +#ifndef HAVE_LAPACK + assert(initialized && "Matrix not initialized."); + + /*--- Copy matrix values to LU matrix, init permutation vec. ---*/ + decomp_vec.resize(sz*sz); + perm_vec.resize(sz); + for (int i = 0; i < sz; ++i) { + perm_vec[i] = i; + for (int j = i; j < sz; ++j) decomp(j,i) = decomp(i,j) = Get(i,j); + } + + /*--- Decompose LU matrix. ---*/ + for (int j = 0; j < sz-1; ++j) { + /*--- Search for maximum pivot and interchange rows. ---*/ + passivedouble pivot = decomp(j,j); + int pivot_idx = j; + for (int i = j+1; i < sz; ++i) + if (abs(decomp(i,j)) > abs(pivot)) { + pivot = decomp(i,j); + pivot_idx = i; + } + + if (pivot_idx != j) { + swap(perm_vec[j], perm_vec[pivot_idx]); + for (int k = 0; k < sz; ++k) + swap(decomp(j,k), decomp(pivot_idx,k)); + } + + /*--- Perform elimination. ---*/ + for (int k = j+1; k < sz; ++k) decomp(k,j) /= pivot; + + for (int k = j+1; k < sz; ++k) + for (int i = j+1; i < sz; ++i) + decomp(i,k) -= decomp(j,k)*decomp(i,j); + } + + decomposed = LU; +#endif +} + +void CSymmetricMatrix::CalcInv() +{ +#ifndef HAVE_LAPACK + assert(initialized && "Matrix not initialized."); + + /*--- Decompose matrix if not done yet. ---*/ + if (decomposed == NONE) { LUDecompose(); } + + /*--- Compute inverse from decomposed matrices. ---*/ + switch (decomposed) { + + case CHOLESKY: + { + /*--- Initialize inverse matrix. ---*/ + vector inv(sz*sz, 0.0); + + /*--- Compute L inverse. ---*/ + /*--- Solve smaller and smaller systems. ---*/ + for (int j = 0; j < sz; ++j) { + /*--- Forward substitution. ---*/ + inv[IdxSym(j,j)] = 1.0 / Get(j,j); + + for (int i = j+1; i < sz; ++i) { + passivedouble sum = 0.0; + for (int k = j; k < i; ++k) sum -= Get(i,k) * inv[IdxSym(k,j)]; + inv[IdxSym(i,j)] = sum / Get(i,i); + } + } // L inverse in inv + + /*--- Multiply inversed matrices overwrite val_vec. ---*/ + for (int j = 0; j < sz; ++j) + for (int i = j; i < sz; ++i) { + passivedouble sum = 0.0; + for (int k = i; k < sz; ++k) sum += inv[IdxSym(k,i)] * inv[IdxSym(k,j)]; + Set(i, j, sum); + } + + break; + } + + case LU: + { + /*--- Alias val_vec. ---*/ + vector& inv = val_vec; + + /*--- Invert L and U matrices in place. ---*/ + for (int j = 0; j < sz; ++j) { + inv[IdxFull(j,j)] = 1.0 / decomp(j,j); + + for (int i = j+1; i < sz; ++i) { + inv[IdxFull(i,j)] = -decomp(i,j); + inv[IdxFull(j,i)] = -decomp(j,i) * inv[IdxFull(j,j)]; + + for (int k = j+1; k < i; ++k) { + inv[IdxFull(i,j)] -= decomp(i,k) * inv[IdxFull(k,j)]; + inv[IdxFull(j,i)] -= decomp(k,i) * inv[IdxFull(j,k)]; + } + if (j+1 <= i) inv[IdxFull(j,i)] /= decomp(i,i); + } + } + // inverses in val_vec + + /*--- Multiply U_inv by L_inv, overwrite decomp_vec. ---*/ + for (int i = 0; i < sz; ++i) + for (int j = 0; j < sz; ++j) { + decomp(i,j) = 0.0; + for (int k = max(i,j); k < sz; ++k) + decomp(i,j) += inv[IdxFull(i,k)] * ((k==j)? 1.0 : inv[IdxFull(k,j)]); + } + + /*--- Permute multiplied matrix to recover A_inv, overwrite val_vec. ---*/ + for (int j = 0; j < sz; ++j) { + int k = perm_vec[j]; + for (int i = k; i < sz; ++i) Set(i, k, decomp(i,j)); + } + + /*--- Decomposition no longer needed. ---*/ + vector().swap(decomp_vec); + + break; + } + default: assert(false && "Default (LU) decomposition failed."); + } + + decomposed = NONE; +#endif +} + +void CSymmetricMatrix::CalcInv_sytri() +{ +#ifdef HAVE_LAPACK + char uplo = 'L'; + int info; + perm_vec.resize(sz); // ipiv array + + /*--- Query the optimum work size. ---*/ + int query = -1; passivedouble tmp; + dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), &tmp, &query, &info); + query = static_cast(tmp); + decomp_vec.resize(query); // work array + + /*--- Factorize and invert. ---*/ + dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &query, &info); + if (info!=0) SU2_MPI::Error("LDLT factorization failed.", CURRENT_FUNCTION); + dsytri_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &info); + if (info!=0) SU2_MPI::Error("Inversion with LDLT factorization failed.", CURRENT_FUNCTION); + + decomposed = NONE; +#endif +} + +void CSymmetricMatrix::CalcInv_potri() +{ +#ifdef HAVE_LAPACK + char uplo = 'L'; + int info; + + dpotrf_(&uplo, &sz, val_vec.data(), &sz, &info); + if (info!=0) SU2_MPI::Error("LLT factorization failed.", CURRENT_FUNCTION); + dpotri_(&uplo, &sz, val_vec.data(), &sz, &info); + if (info!=0) SU2_MPI::Error("Inversion with LLT factorization failed.", CURRENT_FUNCTION); + + decomposed = NONE; +#endif +} + +void CSymmetricMatrix::Invert(const bool is_spd) +{ +#ifdef HAVE_LAPACK + if(is_spd) CalcInv_potri(); + else CalcInv_sytri(); +#else + if(!is_spd) LUDecompose(); + else CholeskyDecompose(); + CalcInv(); +#endif +} + +void CSymmetricMatrix::MatVecMult(passivedouble *v) const +{ + passivedouble *tmp_res = new passivedouble [sz]; + + for (int i=0; i. + */ + +#include "../../include/interface_interpolation/CSlidingMesh.hpp" +#include "../../include/CConfig.hpp" +#include "../../include/geometry/CGeometry.hpp" + + +CSlidingMesh::CSlidingMesh(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, + unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { + Set_TransferCoeff(config); +} + +void CSlidingMesh::Set_TransferCoeff(CConfig **config) { + + /* --- This routine sets the transfer coefficient for sliding mesh approach --- */ + + /* + * The algorithm is based on Rinaldi et al. "Flux-conserving treatment of non-conformal interfaces + * for finite-volume discritization of conservation laws" 2015, Comp. Fluids, 120, pp 126-139 + */ + + /* 0 - Variable declaration - */ + + /* --- General variables --- */ + + bool check; + + unsigned short iDim, nDim; + + unsigned long ii, jj, *uptr; + unsigned long vPoint; + unsigned long iEdgeVisited, nEdgeVisited, iNodeVisited; + unsigned long nAlreadyVisited, nToVisit, StartVisited; + + unsigned long *alreadyVisitedDonor, *ToVisit, *tmpVect; + unsigned long *storeProc, *tmp_storeProc; + + su2double dTMP; + su2double *Coeff_Vect, *tmp_Coeff_Vect; + + /* --- Geometrical variables --- */ + + su2double *Coord_i, *Coord_j, dist, mindist, *Normal; + su2double Area, Area_old, tmp_Area; + su2double LineIntersectionLength, *Direction, length; + + + /* --- Markers Variables --- */ + + unsigned short iMarkerInt, nMarkerInt; + + unsigned long iVertex, nVertexTarget; + + int markDonor, markTarget; + + /* --- Target variables --- */ + + unsigned long target_iPoint, jVertexTarget; + unsigned long nEdges_target, nNode_target; + + unsigned long *Target_nLinkedNodes, *Target_LinkedNodes, *Target_StartLinkedNodes, *target_segment; + unsigned long *Target_Proc; + long *Target_GlobalPoint, *Donor_GlobalPoint; + + su2double *TargetPoint_Coord, *target_iMidEdge_point, *target_jMidEdge_point, **target_element; + + /* --- Donor variables --- */ + + unsigned long donor_StartIndex, donor_forward_point, donor_backward_point, donor_iPoint, donor_OldiPoint; + unsigned long nEdges_donor, nNode_donor, nGlobalVertex_Donor; + + unsigned long nDonorPoints, iDonor; + unsigned long *Donor_Vect, *tmp_Donor_Vect; + unsigned long *Donor_nLinkedNodes, *Donor_LinkedNodes, *Donor_StartLinkedNodes; + unsigned long *Donor_Proc; + + su2double *donor_iMidEdge_point, *donor_jMidEdge_point; + su2double **donor_element, *DonorPoint_Coord; + + /* 1 - Variable pre-processing - */ + + nDim = donor_geometry->GetnDim(); + + /*--- Setting up auxiliary vectors ---*/ + + Donor_Vect = NULL; + Coeff_Vect = NULL; + storeProc = NULL; + + tmp_Donor_Vect = NULL; + tmp_Coeff_Vect = NULL; + tmp_storeProc = NULL; + + Normal = new su2double[nDim]; + Direction = new su2double[nDim]; + + + /* 2 - Find boundary tag between touching grids */ + + /*--- Number of markers on the FSI interface ---*/ + nMarkerInt = (int)( config[ donorZone ]->GetMarker_n_ZoneInterface() ) / 2; + + /*--- For the number of markers on the interface... ---*/ + for ( iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++ ){ + + /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ + markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); + + /*--- On the target side: find the tag of the boundary sharing the interface ---*/ + markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); + + /*--- Checks if the zone contains the interface, if not continue to the next step ---*/ + if(!CheckInterfaceBoundary(markDonor, markTarget)) continue; + + if(markTarget != -1) + nVertexTarget = target_geometry->GetnVertex( markTarget ); + else + nVertexTarget = 0; + + /* + 3 -Reconstruct the boundaries from parallel partitioning + */ + + /*--- Target boundary ---*/ + ReconstructBoundary(targetZone, markTarget); + + nGlobalVertex_Target = nGlobalVertex; + + TargetPoint_Coord = Buffer_Receive_Coord; + Target_GlobalPoint = Buffer_Receive_GlobalPoint; + Target_nLinkedNodes = Buffer_Receive_nLinkedNodes; + Target_StartLinkedNodes = Buffer_Receive_StartLinkedNodes; + Target_LinkedNodes = Buffer_Receive_LinkedNodes; + Target_Proc = Buffer_Receive_Proc; + + /*--- Donor boundary ---*/ + ReconstructBoundary(donorZone, markDonor); + + nGlobalVertex_Donor = nGlobalVertex; + + DonorPoint_Coord = Buffer_Receive_Coord; + Donor_GlobalPoint = Buffer_Receive_GlobalPoint; + Donor_nLinkedNodes = Buffer_Receive_nLinkedNodes; + Donor_StartLinkedNodes = Buffer_Receive_StartLinkedNodes; + Donor_LinkedNodes = Buffer_Receive_LinkedNodes; + Donor_Proc = Buffer_Receive_Proc; + + /*--- Starts building the supermesh layer (2D or 3D) ---*/ + /* - For each target node, it first finds the closest donor point + * - Then it creates the supermesh in the close proximity of the target point: + * - Starting from the closest donor node, it expands the supermesh by including + * donor elements neighboring the initial one, until the overall target area is fully covered. + */ + + if(nDim == 2){ + + target_iMidEdge_point = new su2double[nDim]; + target_jMidEdge_point = new su2double[nDim]; + + donor_iMidEdge_point = new su2double[nDim]; + donor_jMidEdge_point = new su2double[nDim]; + + /*--- Starts with supermesh reconstruction ---*/ + + target_segment = new unsigned long[2]; + + for (iVertex = 0; iVertex < nVertexTarget; iVertex++) { + + nDonorPoints = 0; + + /*--- Stores coordinates of the target node ---*/ + + target_iPoint = target_geometry->vertex[markTarget][iVertex]->GetNode(); + + if (target_geometry->node[target_iPoint]->GetDomain()){ + + Coord_i = target_geometry->node[target_iPoint]->GetCoord(); + + /*--- Brute force to find the closest donor_node ---*/ + + mindist = 1E6; + donor_StartIndex = 0; + + for (donor_iPoint = 0; donor_iPoint < nGlobalVertex_Donor; donor_iPoint++) { + + Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ]; + + dist = PointsDistance(nDim, Coord_i, Coord_j); + + if (dist < mindist) { + mindist = dist; + donor_StartIndex = donor_iPoint; + } + + if (dist == 0.0){ + donor_StartIndex = donor_iPoint; + break; + } + } + + donor_iPoint = donor_StartIndex; + donor_OldiPoint = donor_iPoint; + + /*--- Contruct information regarding the target cell ---*/ + + long dPoint = target_geometry->node[target_iPoint]->GetGlobalIndex(); + for (jVertexTarget = 0; jVertexTarget < nGlobalVertex_Target; jVertexTarget++) + if( dPoint == Target_GlobalPoint[jVertexTarget] ) + break; + + if ( Target_nLinkedNodes[jVertexTarget] == 1 ){ + target_segment[0] = Target_LinkedNodes[ Target_StartLinkedNodes[jVertexTarget] ]; + target_segment[1] = jVertexTarget; + } + else{ + target_segment[0] = Target_LinkedNodes[ Target_StartLinkedNodes[jVertexTarget] ]; + target_segment[1] = Target_LinkedNodes[ Target_StartLinkedNodes[jVertexTarget] + 1]; + } + + dTMP = 0; + for(iDim = 0; iDim < nDim; iDim++){ + target_iMidEdge_point[iDim] = ( TargetPoint_Coord[ nDim * target_segment[0] + iDim ] + target_geometry->node[ target_iPoint ]->GetCoord(iDim) ) / 2; + target_jMidEdge_point[iDim] = ( TargetPoint_Coord[ nDim * target_segment[1] + iDim ] + target_geometry->node[ target_iPoint ]->GetCoord(iDim) ) / 2; + + Direction[iDim] = target_jMidEdge_point[iDim] - target_iMidEdge_point[iDim]; + dTMP += Direction[iDim] * Direction[iDim]; + } + + dTMP = sqrt(dTMP); + for(iDim = 0; iDim < nDim; iDim++) + Direction[iDim] /= dTMP; + + length = PointsDistance(nDim, target_iMidEdge_point, target_jMidEdge_point); + + check = false; + + /*--- Proceeds along the forward direction (depending on which connected boundary node is found first) ---*/ + + while( !check ){ + + /*--- Proceeds until the value of the intersection area is null ---*/ + + if ( Donor_nLinkedNodes[donor_iPoint] == 1 ){ + donor_forward_point = Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_iPoint] ]; + donor_backward_point = donor_iPoint; + } + else{ + uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_iPoint] ]; + + if( donor_OldiPoint != uptr[0] ){ + donor_forward_point = uptr[0]; + donor_backward_point = uptr[1]; + } + else{ + donor_forward_point = uptr[1]; + donor_backward_point = uptr[0]; + } + } + + if(donor_iPoint >= nGlobalVertex_Donor){ + check = true; + continue; + } + + for(iDim = 0; iDim < nDim; iDim++){ + donor_iMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_forward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; + donor_jMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_backward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; + } + + LineIntersectionLength = ComputeLineIntersectionLength(target_iMidEdge_point, target_jMidEdge_point, donor_iMidEdge_point, donor_jMidEdge_point, Direction); + + if ( LineIntersectionLength == 0.0 ){ + check = true; + continue; + } + + /*--- In case the element intersects the target cell, update the auxiliary communication data structure ---*/ + + tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ]; + tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ]; + tmp_storeProc = new unsigned long[ nDonorPoints + 1 ]; + + for( iDonor = 0; iDonor < nDonorPoints; iDonor++){ + tmp_Donor_Vect[iDonor] = Donor_Vect[iDonor]; + tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor]; + tmp_storeProc[iDonor] = storeProc[iDonor]; + } + + tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint; + tmp_Coeff_Vect[ nDonorPoints ] = LineIntersectionLength / length; + tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint]; + + if (Donor_Vect != NULL) delete [] Donor_Vect; + if (Coeff_Vect != NULL) delete [] Coeff_Vect; + if (storeProc != NULL) delete [] storeProc; + + Donor_Vect = tmp_Donor_Vect; + Coeff_Vect = tmp_Coeff_Vect; + storeProc = tmp_storeProc; + + donor_OldiPoint = donor_iPoint; + donor_iPoint = donor_forward_point; + + nDonorPoints++; + } + + if ( Donor_nLinkedNodes[donor_StartIndex] == 2 ){ + check = false; + + uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_StartIndex] ]; + + donor_iPoint = uptr[1]; + donor_OldiPoint = donor_StartIndex; + } + else + check = true; + + /*--- Proceeds along the backward direction (depending on which connected boundary node is found first) ---*/ + + while( !check ){ + + /*--- Proceeds until the value of the intersection length is null ---*/ + if ( Donor_nLinkedNodes[donor_iPoint] == 1 ){ + donor_forward_point = donor_OldiPoint; + donor_backward_point = donor_iPoint; + } + else{ + uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_iPoint] ]; + + if( donor_OldiPoint != uptr[0] ){ + donor_forward_point = uptr[0]; + donor_backward_point = uptr[1]; + } + else{ + donor_forward_point = uptr[1]; + donor_backward_point = uptr[0]; + } + } + + if(donor_iPoint >= nGlobalVertex_Donor){ + check = true; + continue; + } + + for(iDim = 0; iDim < nDim; iDim++){ + donor_iMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_forward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; + donor_jMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_backward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; + } + + LineIntersectionLength = ComputeLineIntersectionLength(target_iMidEdge_point, target_jMidEdge_point, donor_iMidEdge_point, donor_jMidEdge_point, Direction); + + if ( LineIntersectionLength == 0.0 ){ + check = true; + continue; + } + + /*--- In case the element intersects the target cell, update the auxiliary communication data structure ---*/ + + tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ]; + tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ]; + tmp_storeProc = new unsigned long[ nDonorPoints + 1 ]; + + for( iDonor = 0; iDonor < nDonorPoints; iDonor++){ + tmp_Donor_Vect[iDonor] = Donor_Vect[iDonor]; + tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor]; + tmp_storeProc[iDonor] = storeProc[iDonor]; + } + + tmp_Coeff_Vect[ nDonorPoints ] = LineIntersectionLength / length; + tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint; + tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint]; + + if (Donor_Vect != NULL) delete [] Donor_Vect; + if (Coeff_Vect != NULL) delete [] Coeff_Vect; + if (storeProc != NULL) delete [] storeProc; + + Donor_Vect = tmp_Donor_Vect; + Coeff_Vect = tmp_Coeff_Vect; + storeProc = tmp_storeProc; + + donor_OldiPoint = donor_iPoint; + donor_iPoint = donor_forward_point; + + nDonorPoints++; + } + + /*--- Set the communication data structure and copy data from the auxiliary vectors ---*/ + + target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nDonorPoints); + + target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); + + for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){ + target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor, Coeff_Vect[iDonor]); + target_geometry->vertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor, Donor_GlobalPoint[Donor_Vect[iDonor]]); + target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]); + } + } + } + + delete [] target_segment; + + delete [] target_iMidEdge_point; + delete [] target_jMidEdge_point; + + delete [] donor_iMidEdge_point; + delete [] donor_jMidEdge_point; + } + else{ + /* --- 3D geometry, creates a superficial super-mesh --- */ + + for (iVertex = 0; iVertex < nVertexTarget; iVertex++) { + + nDonorPoints = 0; + + /*--- Stores coordinates of the target node ---*/ + + target_iPoint = target_geometry->vertex[markTarget][iVertex]->GetNode(); + + if (!target_geometry->node[target_iPoint]->GetDomain()) continue; + + Coord_i = target_geometry->node[target_iPoint]->GetCoord(); + + target_geometry->vertex[markTarget][iVertex]->GetNormal(Normal); + + /*--- The value of Area computed here includes also portion of boundary belonging to different marker ---*/ + Area = 0.0; + for (iDim = 0; iDim < nDim; iDim++) + Area += Normal[iDim]*Normal[iDim]; + Area = sqrt(Area); + + for (iDim = 0; iDim < nDim; iDim++) + Normal[iDim] /= Area; + + for (iDim = 0; iDim < nDim; iDim++) + Coord_i[iDim] = target_geometry->node[target_iPoint]->GetCoord(iDim); + + long dPoint = target_geometry->node[target_iPoint]->GetGlobalIndex(); + for (target_iPoint = 0; target_iPoint < nGlobalVertex_Target; target_iPoint++){ + if( dPoint == Target_GlobalPoint[target_iPoint] ) + break; + } + + /*--- Build local surface dual mesh for target element ---*/ + + nEdges_target = Target_nLinkedNodes[target_iPoint]; + + nNode_target = 2*(nEdges_target + 1); + + target_element = new su2double*[nNode_target]; + for (ii = 0; ii < nNode_target; ii++) + target_element[ii] = new su2double[nDim]; + + nNode_target = Build_3D_surface_element(Target_LinkedNodes, Target_StartLinkedNodes, Target_nLinkedNodes, TargetPoint_Coord, target_iPoint, target_element); + + /*--- Brute force to find the closest donor_node ---*/ + + mindist = 1E6; + donor_StartIndex = 0; + + for (donor_iPoint = 0; donor_iPoint < nGlobalVertex_Donor; donor_iPoint++) { + + Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ]; + + dist = PointsDistance(nDim, Coord_i, Coord_j); + + if (dist < mindist) { + mindist = dist; + donor_StartIndex = donor_iPoint; + } + + if (dist == 0.0){ + donor_StartIndex = donor_iPoint; + break; + } + } + + donor_iPoint = donor_StartIndex; + + nEdges_donor = Donor_nLinkedNodes[donor_iPoint]; + + donor_element = new su2double*[ 2*nEdges_donor + 2 ]; + for (ii = 0; ii < 2*nEdges_donor + 2; ii++) + donor_element[ii] = new su2double[nDim]; + + nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, DonorPoint_Coord, donor_iPoint, donor_element); + + Area = 0; + for (ii = 1; ii < nNode_target-1; ii++){ + for (jj = 1; jj < nNode_donor-1; jj++){ + Area += Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], donor_element[0], donor_element[jj], donor_element[jj+1], Normal); + //cout << Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], donor_element[0], donor_element[jj], donor_element[jj+1], Normal) << endl; + } + } + + for (ii = 0; ii < 2*nEdges_donor + 2; ii++) + delete [] donor_element[ii]; + delete [] donor_element; + + nDonorPoints = 1; + + /*--- In case the element intersect the target cell update the auxiliary communication data structure ---*/ + + Coeff_Vect = new su2double[ nDonorPoints ]; + Donor_Vect = new unsigned long[ nDonorPoints ]; + storeProc = new unsigned long[ nDonorPoints ]; + + Coeff_Vect[0] = Area; + Donor_Vect[0] = donor_iPoint; + storeProc[0] = Donor_Proc[donor_iPoint]; + + alreadyVisitedDonor = new unsigned long[1]; + + alreadyVisitedDonor[0] = donor_iPoint; + nAlreadyVisited = 1; + StartVisited = 0; + + Area_old = -1; + + while( Area > Area_old ){ + + /* + * - Starting from the closest donor_point, it expands the supermesh by a countour search pattern. + * - The closest donor element becomes the core, at each iteration a new layer of elements around the core is taken into account + */ + + Area_old = Area; + + ToVisit = NULL; + nToVisit = 0; + + for( iNodeVisited = StartVisited; iNodeVisited < nAlreadyVisited; iNodeVisited++ ){ + + vPoint = alreadyVisitedDonor[ iNodeVisited ]; + + nEdgeVisited = Donor_nLinkedNodes[vPoint]; + + for (iEdgeVisited = 0; iEdgeVisited < nEdgeVisited; iEdgeVisited++){ + + donor_iPoint = Donor_LinkedNodes[ Donor_StartLinkedNodes[vPoint] + iEdgeVisited]; + + /*--- Check if the node to visit is already listed in the data structure to avoid double visits ---*/ + + check = 0; + + for( jj = 0; jj < nAlreadyVisited; jj++ ){ + if( donor_iPoint == alreadyVisitedDonor[jj] ){ + check = 1; + break; + } + } + + if( check == 0 && ToVisit != NULL){ + for( jj = 0; jj < nToVisit; jj++ ) + if( donor_iPoint == ToVisit[jj] ){ + check = 1; + break; + } + } + + if( check == 0 ){ + /*--- If the node was not already visited, visit it and list it into data structure ---*/ + + tmpVect = new unsigned long[ nToVisit + 1 ]; + + for( jj = 0; jj < nToVisit; jj++ ) + tmpVect[jj] = ToVisit[jj]; + tmpVect[nToVisit] = donor_iPoint; + + if( ToVisit != NULL ) + delete [] ToVisit; + + ToVisit = tmpVect; + tmpVect = NULL; + + nToVisit++; + + /*--- Find the value of the intersection area between the current donor element and the target element --- */ + + nEdges_donor = Donor_nLinkedNodes[donor_iPoint]; + + donor_element = new su2double*[ 2*nEdges_donor + 2 ]; + for (ii = 0; ii < 2*nEdges_donor + 2; ii++) + donor_element[ii] = new su2double[nDim]; + + nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, DonorPoint_Coord, donor_iPoint, donor_element); + + tmp_Area = 0; + for (ii = 1; ii < nNode_target-1; ii++) + for (jj = 1; jj < nNode_donor-1; jj++) + tmp_Area += Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], donor_element[0], donor_element[jj], donor_element[jj+1], Normal); + + for (ii = 0; ii < 2*nEdges_donor + 2; ii++) + delete [] donor_element[ii]; + delete [] donor_element; + + /*--- In case the element intersect the target cell update the auxiliary communication data structure ---*/ + + tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ]; + tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ]; + tmp_storeProc = new unsigned long[ nDonorPoints + 1 ]; + + for( iDonor = 0; iDonor < nDonorPoints; iDonor++){ + tmp_Donor_Vect[iDonor] = Donor_Vect[iDonor]; + tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor]; + tmp_storeProc[iDonor] = storeProc[iDonor]; + } + + tmp_Coeff_Vect[ nDonorPoints ] = tmp_Area; + tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint; + tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint]; + + if (Donor_Vect != NULL) {delete [] Donor_Vect; } + if (Coeff_Vect != NULL) {delete [] Coeff_Vect; } + if (storeProc != NULL) {delete [] storeProc; } + + Donor_Vect = tmp_Donor_Vect; + Coeff_Vect = tmp_Coeff_Vect; + storeProc = tmp_storeProc; + + tmp_Coeff_Vect = NULL; + tmp_Donor_Vect = NULL; + tmp_storeProc = NULL; + + nDonorPoints++; + + Area += tmp_Area; + } + } + } + + /*--- Update auxiliary data structure ---*/ + + StartVisited = nAlreadyVisited; + + tmpVect = new unsigned long[ nAlreadyVisited + nToVisit ]; + + for( jj = 0; jj < nAlreadyVisited; jj++ ) + tmpVect[jj] = alreadyVisitedDonor[jj]; + + for( jj = 0; jj < nToVisit; jj++ ) + tmpVect[ nAlreadyVisited + jj ] = ToVisit[jj]; + + if( alreadyVisitedDonor != NULL ) + delete [] alreadyVisitedDonor; + + alreadyVisitedDonor = tmpVect; + + nAlreadyVisited += nToVisit; + + delete [] ToVisit; + } + + delete [] alreadyVisitedDonor; + + /*--- Set the communication data structure and copy data from the auxiliary vectors ---*/ + + target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nDonorPoints); + target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); + + for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){ + target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor, Coeff_Vect[iDonor]/Area); + target_geometry->vertex[markTarget][iVertex]->SetInterpDonorPoint( iDonor, Donor_GlobalPoint[ Donor_Vect[iDonor] ] ); + target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]); + //cout <GetnDim(); + + su2double dotA2, dotB1, dotB2; + + dotA2 = 0; + for(iDim = 0; iDim < nDim; iDim++) + dotA2 += ( A2[iDim] - A1[iDim] ) * Direction[iDim]; + + if( dotA2 >= 0 ){ + dotB1 = 0; + dotB2 = 0; + for(iDim = 0; iDim < nDim; iDim++){ + dotB1 += ( B1[iDim] - A1[iDim] ) * Direction[iDim]; + dotB2 += ( B2[iDim] - A1[iDim] ) * Direction[iDim]; + } + } + else{ + dotA2 *= -1; + + dotB1 = 0; + dotB2 = 0; + for(iDim = 0; iDim < nDim; iDim++){ + dotB1 -= ( B1[iDim] - A1[iDim] ) * Direction[iDim]; + dotB2 -= ( B2[iDim] - A1[iDim] ) * Direction[iDim]; + } + } + + if( dotB1 >= 0 && dotB1 <= dotA2 ){ + if ( dotB2 < 0 ) + return fabs( dotB1 ); + if ( dotB2 > dotA2 ) + return fabs( dotA2 - dotB1 ); + + return fabs( dotB1 - dotB2 ); + } + + if( dotB2 >= 0 && dotB2 <= dotA2 ){ + if ( dotB1 < 0 ) + return fabs(dotB2); + if ( dotB1 > dotA2 ) + return fabs( dotA2 - dotB2 ); + } + + if( ( dotB1 <= 0 && dotA2 <= dotB2 ) || ( dotB2 <= 0 && dotA2 <= dotB1 ) ) + return fabs( dotA2 ); + + return 0.0; +} + +su2double CSlidingMesh::Compute_Triangle_Intersection(su2double* A1, su2double* A2, su2double* A3, su2double* B1, su2double* B2, su2double* B3, su2double* Direction){ + + /* --- This routine is ONLY for 3D grids --- */ + /* --- Projects triangle points onto a plane, specified by its normal "Direction", and calls the ComputeIntersectionArea routine --- */ + + unsigned short iDim; + unsigned short nDim = 3; + + su2double I[3], J[3], K[3]; + su2double a1[3], a2[3], a3[3]; + su2double b1[3], b2[3], b3[3]; + su2double m1, m2; + + /* --- Reference frame is determined by: x = A1A2 y = x ^ ( -Direction ) --- */ + + for(iDim = 0; iDim < 3; iDim++){ + a1[iDim] = 0; + a2[iDim] = 0; + a3[iDim] = 0; + + b1[iDim] = 0; + b2[iDim] = 0; + b3[iDim] = 0; + } + + m1 = 0; + for(iDim = 0; iDim < nDim; iDim++){ + K[iDim] = Direction[iDim]; + + m1 += K[iDim] * K[iDim]; + } + + for(iDim = 0; iDim < nDim; iDim++) + K[iDim] /= sqrt(m1); + + m2 = 0; + for(iDim = 0; iDim < nDim; iDim++) + m2 += (A2[iDim] - A1[iDim]) * K[iDim]; + + m1 = 0; + for(iDim = 0; iDim < nDim; iDim++){ + I[iDim] = (A2[iDim] - A1[iDim]) - m2 * K[iDim]; + m1 += I[iDim] * I[iDim]; + } + + for(iDim = 0; iDim < nDim; iDim++) + I[iDim] /= sqrt(m1); + + // Cross product to find Y + J[0] = K[1]*I[2] - K[2]*I[1]; + J[1] = -(K[0]*I[2] - K[2]*I[0]); + J[2] = K[0]*I[1] - K[1]*I[0]; + + /* --- Project all points on the plane specified by Direction and change their reference frame taking A1 as origin --- */ + + for(iDim = 0; iDim < nDim; iDim++){ + a2[0] += (A2[iDim] - A1[iDim]) * I[iDim]; + a2[1] += (A2[iDim] - A1[iDim]) * J[iDim]; + a2[2] += (A2[iDim] - A1[iDim]) * K[iDim]; + + a3[0] += (A3[iDim] - A1[iDim]) * I[iDim]; + a3[1] += (A3[iDim] - A1[iDim]) * J[iDim]; + a3[2] += (A3[iDim] - A1[iDim]) * K[iDim]; + + b1[0] += (B1[iDim] - A1[iDim]) * I[iDim]; + b1[1] += (B1[iDim] - A1[iDim]) * J[iDim]; + b1[2] += (B1[iDim] - A1[iDim]) * K[iDim]; + + b2[0] += (B2[iDim] - A1[iDim]) * I[iDim]; + b2[1] += (B2[iDim] - A1[iDim]) * J[iDim]; + b2[2] += (B2[iDim] - A1[iDim]) * K[iDim]; + + b3[0] += (B3[iDim] - A1[iDim]) * I[iDim]; + b3[1] += (B3[iDim] - A1[iDim]) * J[iDim]; + b3[2] += (B3[iDim] - A1[iDim]) * K[iDim]; + } + + /*--- Compute intersection area ---*/ + + return ComputeIntersectionArea( a1, a2, a3, b1, b2, b3 ); +} + +su2double CSlidingMesh::ComputeIntersectionArea( su2double* P1, su2double* P2, su2double* P3, su2double* Q1, su2double* Q2, su2double* Q3 ){ + + /* --- This routines computes the area of the polygonal element generated by the superimposition of 2 planar triangle --- */ + /* --- The 2 triangle must lie on the same plane --- */ + + unsigned short iDim, nPoints, i, j, k; + unsigned short nDim, min_theta_index; + + su2double points[16][2], IntersectionPoint[2], theta[6]; + su2double TriangleP[4][2], TriangleQ[4][2]; + su2double Area, det, dot1, dot2, dtmp, min_theta; + + nDim = 2; + nPoints = 0; + + for(iDim = 0; iDim < nDim; iDim++){ + TriangleP[0][iDim] = 0; + TriangleP[1][iDim] = P2[iDim] - P1[iDim]; + TriangleP[2][iDim] = P3[iDim] - P1[iDim]; + TriangleP[3][iDim] = 0; + + TriangleQ[0][iDim] = Q1[iDim] - P1[iDim]; + TriangleQ[1][iDim] = Q2[iDim] - P1[iDim]; + TriangleQ[2][iDim] = Q3[iDim] - P1[iDim]; + TriangleQ[3][iDim] = Q1[iDim] - P1[iDim]; + } + + + for( j = 0; j < 3; j++){ + if( CheckPointInsideTriangle(TriangleP[j], TriangleQ[0], TriangleQ[1], TriangleQ[2]) ){ + + // Then P1 is also inside triangle Q, so store it + for(iDim = 0; iDim < nDim; iDim++) + points[nPoints][iDim] = TriangleP[j][iDim]; + + nPoints++; + } + } + + for( j = 0; j < 3; j++){ + if( CheckPointInsideTriangle(TriangleQ[j], TriangleP[0], TriangleP[1], TriangleP[2]) ){ + + // Then Q1 is also inside triangle P, so store it + for(iDim = 0; iDim < nDim; iDim++) + points[nPoints][iDim] = TriangleQ[j][iDim]; + + nPoints++; + } + } + + + // Compute all edge intersections + + for( j = 0; j < 3; j++){ + for( i = 0; i < 3; i++){ + + det = (TriangleP[j][0] - TriangleP[j+1][0]) * ( TriangleQ[i][1] - TriangleQ[i+1][1] ) - (TriangleP[j][1] - TriangleP[j+1][1]) * (TriangleQ[i][0] - TriangleQ[i+1][0]); + + if ( det != 0.0 ){ + ComputeLineIntersectionPoint( TriangleP[j], TriangleP[j+1], TriangleQ[i], TriangleQ[i+1], IntersectionPoint ); + + dot1 = 0; + dot2 = 0; + for(iDim = 0; iDim < nDim; iDim++){ + dot1 += ( TriangleP[j][iDim] - IntersectionPoint[iDim] ) * ( TriangleP[j+1][iDim] - IntersectionPoint[iDim] ); + dot2 += ( TriangleQ[i][iDim] - IntersectionPoint[iDim] ) * ( TriangleQ[i+1][iDim] - IntersectionPoint[iDim] ); + } + + if( dot1 <= 0 && dot2 <= 0 ){ // It found one intersection + + // Store temporarily the intersection point + + for(iDim = 0; iDim < nDim; iDim++) + points[nPoints][iDim] = IntersectionPoint[iDim]; + + nPoints++; + } + } + } + } + + // Remove double points, if any + + for( i = 0; i < nPoints; i++){ + for( j = i+1; j < nPoints; j++){ + if(points[j][0] == points[i][0] && points[j][1] == points[i][1]){ + for( k = j; k < nPoints-1; k++){ + points[k][0] = points[k+1][0]; + points[k][1] = points[k+1][1]; + } + nPoints--; + j--; + } + } + } + + // Re-order nodes + + for( i = 1; i < nPoints; i++){ // Change again reference frame + for(iDim = 0; iDim < nDim; iDim++) + points[i][iDim] -= points[0][iDim]; + + // Compute polar azimuth for each node but the first + theta[i] = atan2(points[i][1], points[i][0]); + } + + for(iDim = 0; iDim < nDim; iDim++) + points[0][iDim] = 0; + + for( i = 1; i < nPoints; i++){ + + min_theta = theta[i]; + min_theta_index = 0; + + for( j = i + 1; j < nPoints; j++){ + + if( theta[j] < min_theta ){ + min_theta = theta[j]; + min_theta_index = j; + } + } + + if( min_theta_index != 0 ){ + dtmp = theta[i]; + theta[i] = theta[min_theta_index]; + theta[min_theta_index] = dtmp; + + dtmp = points[i][0]; + points[i][0] = points[min_theta_index][0]; + points[min_theta_index][0] = dtmp; + + dtmp = points[i][1]; + points[i][1] = points[min_theta_index][1]; + points[min_theta_index][1] = dtmp; + } + } + + // compute area using cross product rule, points position are referred to the 2-dimensional, local, reference frame centered in points[0] + + Area = 0; + + if (nPoints > 2){ + for( i = 1; i < nPoints-1; i++ ){ + + // Ax*By + Area += ( points[i][0] - points[0][0] ) * ( points[i+1][1] - points[0][1] ); + + // Ay*Bx + Area -= ( points[i][1] - points[0][1] ) * ( points[i+1][0] - points[0][0] ); + } + } + + return fabs(Area)/2; +} + +void CSlidingMesh::ComputeLineIntersectionPoint( su2double* A1, su2double* A2, su2double* B1, su2double* B2, su2double* IntersectionPoint ){ + + /* --- Uses determinant rule to compute the intersection point between 2 straight segments --- */ + /* This works only for lines on a 2D plane, A1, A2 and B1, B2 are respectively the head and the tail points of each segment, + * since they're on a 2D plane they are defined by a 2-elements array containing their coordinates */ + + su2double det; + + det = (A1[0] - A2[0]) * (B1[1] - B2[1]) - (A1[1] - A2[1]) * (B1[0] - B2[0]); + + if ( det != 0.0 ){ // else there is no intersection point + IntersectionPoint[0] = ( ( A1[0]*A2[1] - A1[1]*A2[0] ) * ( B1[0] - B2[0] ) - ( B1[0]*B2[1] - B1[1]*B2[0] ) * ( A1[0] - A2[0] ) ) / det; + IntersectionPoint[1] = ( ( A1[0]*A2[1] - A1[1]*A2[0] ) * ( B1[1] - B2[1] ) - ( B1[0]*B2[1] - B1[1]*B2[0] ) * ( A1[1] - A2[1] ) ) / det; + } +} + +bool CSlidingMesh::CheckPointInsideTriangle(su2double* Point, su2double* T1, su2double* T2, su2double* T3){ + + /* --- Check whether a point "Point" lies inside or outside a triangle defined by 3 points "T1", "T2", "T3" --- */ + /* For each edge it checks on which side the point lies: + * - Computes the unit vector pointing at the internal side of the edge + * - Comutes the vector that connects the point to a point along the edge + * - If the dot product is positive it means that the point is on the internal side of the edge + * - If the check is positive for all the 3 edges, then the point lies within the triangle + */ + + unsigned short iDim, nDim, check; + + su2double vect1[2], vect2[2], r[2]; + su2double dot; + + check = 0; + nDim = 2; + + /* --- Check first edge --- */ + + dot = 0; + for(iDim = 0; iDim < nDim; iDim++){ + vect1[iDim] = T3[iDim] - T1[iDim]; // vec 1 is aligned to the edge + vect2[iDim] = T2[iDim] - T1[iDim]; // vect 2 is the vector connecting one edge point to the third triangle vertex + + r[iDim] = Point[iDim] - T1[iDim]; // Connects point to vertex T1 + + dot += vect2[iDim] * vect2[iDim]; + } + dot = sqrt(dot); + + for(iDim = 0; iDim < nDim; iDim++) + vect2[iDim] /= dot; + + dot = 0; + for(iDim = 0; iDim < nDim; iDim++) + dot += vect1[iDim] * vect2[iDim]; + + for(iDim = 0; iDim < nDim; iDim++) + vect1[iDim] = T3[iDim] - (T1[iDim] + dot * vect2[iDim]); // Computes the inward unit vector + + dot = 0; + for(iDim = 0; iDim < nDim; iDim++) // Checs that the point lies on the internal plane + dot += vect1[iDim] * r[iDim]; + + if (dot >= 0) + check++; + + /* --- Check second edge --- */ + + dot = 0; + for(iDim = 0; iDim < nDim; iDim++){ + vect1[iDim] = T1[iDim] - T2[iDim]; + vect2[iDim] = T3[iDim] - T2[iDim]; + + r[iDim] = Point[iDim] - T2[iDim]; + + dot += vect2[iDim] * vect2[iDim]; + } + dot = sqrt(dot); + + for(iDim = 0; iDim < nDim; iDim++) + vect2[iDim] /= dot; + + dot = 0; + for(iDim = 0; iDim < nDim; iDim++) + dot += vect1[iDim] * vect2[iDim]; + + for(iDim = 0; iDim < nDim; iDim++) + vect1[iDim] = T1[iDim] - (T2[iDim] + dot * vect2[iDim]); + + dot = 0; + for(iDim = 0; iDim < nDim; iDim++) + dot += vect1[iDim] * r[iDim]; + + if (dot >= 0) + check++; + + /* --- Check third edge --- */ + + dot = 0; + for(iDim = 0; iDim < nDim; iDim++){ + vect1[iDim] = T2[iDim] - T3[iDim]; + vect2[iDim] = T1[iDim] - T3[iDim]; + + r[iDim] = Point[iDim] - T3[iDim]; + + dot += vect2[iDim] * vect2[iDim]; + } + dot = sqrt(dot); + + for(iDim = 0; iDim < nDim; iDim++) + vect2[iDim] /= dot; + + dot = 0; + for(iDim = 0; iDim < nDim; iDim++) + dot += vect1[iDim] * vect2[iDim]; + + for(iDim = 0; iDim < nDim; iDim++) + vect1[iDim] = T2[iDim] - (T3[iDim] + dot * vect2[iDim]); + + dot = 0; + for(iDim = 0; iDim < nDim; iDim++) + dot += vect1[iDim] * r[iDim]; + + if (dot >= 0) + check++; + + return (check == 3); +} diff --git a/Common/src/interface_interpolation/meson.build b/Common/src/interface_interpolation/meson.build new file mode 100644 index 000000000000..3e1d1a49b2e4 --- /dev/null +++ b/Common/src/interface_interpolation/meson.build @@ -0,0 +1,6 @@ +common_src += files(['CInterpolator.cpp', + 'CMirror.cpp', + 'CSlidingMesh.cpp', + 'CIsoparametric.cpp', + 'CNearestNeighbor.cpp', + 'CRadialBasisFunction.cpp']) diff --git a/Common/src/interpolation_structure.cpp b/Common/src/interpolation_structure.cpp deleted file mode 100644 index e88bcef4e1f4..000000000000 --- a/Common/src/interpolation_structure.cpp +++ /dev/null @@ -1,3235 +0,0 @@ -/*! - * \file interpolation_structure.cpp - * \brief Main subroutines used by SU2_FSI - * \author H. Kline - * \version 7.0.2 "Blackbird" - * - * SU2 Project Website: https://su2code.github.io - * - * The SU2 Project is maintained by the SU2 Foundation - * (http://su2foundation.org) - * - * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md) - * - * SU2 is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * SU2 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with SU2. If not, see . - */ -#include "../include/interpolation_structure.hpp" - -#if defined(HAVE_MKL) -#include "mkl.h" -#ifndef HAVE_LAPACK -#define HAVE_LAPACK -#endif -#elif defined(HAVE_LAPACK) -/*--- Lapack / Blas routines used in RBF interpolation. ---*/ -extern "C" void dsytrf_(char*, int*, passivedouble*, int*, int*, passivedouble*, int*, int*); -extern "C" void dsytri_(char*, int*, passivedouble*, int*, int*, passivedouble*, int*); -extern "C" void dpotrf_(char*, int*, passivedouble*, int*, int*); -extern "C" void dpotri_(char*, int*, passivedouble*, int*, int*); -extern "C" void dsymm_(char*, char*, int*, int*, passivedouble*, passivedouble*, int*, - passivedouble*, int*, passivedouble*, passivedouble*, int*); -#endif - -CInterpolator::CInterpolator(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : - rank(SU2_MPI::GetRank()), - size(SU2_MPI::GetSize()), - donorZone(iZone), - targetZone(jZone), - Geometry(geometry_container), - donor_geometry(geometry_container[iZone][INST_0][MESH_0]), - target_geometry(geometry_container[jZone][INST_0][MESH_0]) { - - /*--- Initialize transfer coefficients between the zones. ---*/ - Set_TransferCoeff(config); - -} - -void CInterpolator::Determine_ArraySize(bool faces, int markDonor, int markTarget, - unsigned long nVertexDonor, unsigned short nDim) { - - unsigned long nLocalVertex_Donor = 0, nLocalFaceNodes_Donor=0, nLocalFace_Donor=0; - unsigned long iVertex, iPointDonor = 0; - /* Only needed if face data is also collected */ - unsigned long inode; - unsigned long donor_elem, jElem, jPoint; - unsigned short iDonor; - unsigned int nFaces=0, iFace, nNodes=0; - bool face_on_marker = true; - - for (iVertex = 0; iVertex < nVertexDonor; iVertex++) { - iPointDonor = donor_geometry->vertex[markDonor][iVertex]->GetNode(); - - if (!donor_geometry->node[iPointDonor]->GetDomain()) continue; - - nLocalVertex_Donor++; - - if (!faces) continue; - - /*--- On Donor geometry also communicate face info ---*/ - if (nDim==3) { - for (jElem=0; jElemnode[iPointDonor]->GetnElem(); jElem++) { - donor_elem = donor_geometry->node[iPointDonor]->GetElem(jElem); - nFaces = donor_geometry->elem[donor_elem]->GetnFaces(); - for (iFace=0; iFaceelem[donor_elem]->GetnNodesFace(iFace); - for (iDonor=0; iDonorelem[donor_elem]->GetFaces(iFace, iDonor); - jPoint = donor_geometry->elem[donor_elem]->GetNode(inode); - face_on_marker = (face_on_marker && (donor_geometry->node[jPoint]->GetVertex(markDonor) !=-1)); - } - if (face_on_marker ) { - nLocalFace_Donor++; - nLocalFaceNodes_Donor+=nNodes; - } - } - } - } - else { - /*--- in 2D we use the edges ---*/ - nNodes=2; - nFaces = donor_geometry->node[iPointDonor]->GetnPoint(); - for (iFace=0; iFacenode[iPointDonor]->GetEdge(iFace); - jPoint = donor_geometry->edge[inode]->GetNode(iDonor); - face_on_marker = (face_on_marker && (donor_geometry->node[jPoint]->GetVertex(markDonor) !=-1)); - } - if (face_on_marker ) { - nLocalFace_Donor++; - nLocalFaceNodes_Donor+=nNodes; - } - } - } - } - - Buffer_Send_nVertex_Donor[0] = nLocalVertex_Donor; - if (faces) { - Buffer_Send_nFace_Donor[0] = nLocalFace_Donor; - Buffer_Send_nFaceNodes_Donor[0] = nLocalFaceNodes_Donor; - } - - /*--- Send Interface vertex information --*/ - SU2_MPI::Allreduce(&nLocalVertex_Donor, &MaxLocalVertex_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_nVertex_Donor, 1, MPI_UNSIGNED_LONG, - Buffer_Receive_nVertex_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - if (faces) { - SU2_MPI::Allreduce(&nLocalFace_Donor, &nGlobalFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); - SU2_MPI::Allreduce(&nLocalFace_Donor, &MaxFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); - SU2_MPI::Allreduce(&nLocalFaceNodes_Donor, &nGlobalFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); - SU2_MPI::Allreduce(&nLocalFaceNodes_Donor, &MaxFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_nFace_Donor, 1, MPI_UNSIGNED_LONG, - Buffer_Receive_nFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, - Buffer_Receive_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - MaxFace_Donor++; - } -} - -void CInterpolator::Collect_VertexInfo(bool faces, int markDonor, int markTarget, - unsigned long nVertexDonor, unsigned short nDim) { - - unsigned long iVertex, iPointDonor = 0, iVertexDonor, nBuffer_Coord, nBuffer_Point, nLocalVertex_Donor; - unsigned short iDim; - - for (iVertex = 0; iVertex < MaxLocalVertex_Donor; iVertex++) Buffer_Send_GlobalPoint[iVertex] = -1; - - for (iVertex = 0; iVertex < MaxLocalVertex_Donor*nDim; iVertex++) Buffer_Send_Coord[iVertex] = 0.0; - - if(faces) - for (iVertex = 0; iVertex < MaxLocalVertex_Donor*nDim; iVertex++) Buffer_Send_Normal[iVertex] = 0.0; - - /*--- Copy coordinates and point to the auxiliar vector --*/ - nLocalVertex_Donor = 0; - - for (iVertexDonor = 0; iVertexDonor < nVertexDonor; iVertexDonor++) { - iPointDonor = donor_geometry->vertex[markDonor][iVertexDonor]->GetNode(); - if (donor_geometry->node[iPointDonor]->GetDomain()) { - Buffer_Send_GlobalPoint[nLocalVertex_Donor] = donor_geometry->node[iPointDonor]->GetGlobalIndex(); - for (iDim = 0; iDim < nDim; iDim++) - Buffer_Send_Coord[nLocalVertex_Donor*nDim+iDim] = donor_geometry->node[iPointDonor]->GetCoord(iDim); - - if (faces) { - const su2double* Normal = donor_geometry->vertex[markDonor][iVertexDonor]->GetNormal(); - for (iDim = 0; iDim < nDim; iDim++) - Buffer_Send_Normal[nLocalVertex_Donor*nDim+iDim] = Normal[iDim]; - } - nLocalVertex_Donor++; - } - } - nBuffer_Coord = MaxLocalVertex_Donor*nDim; - nBuffer_Point = MaxLocalVertex_Donor; - - SU2_MPI::Allgather(Buffer_Send_Coord, nBuffer_Coord, MPI_DOUBLE, - Buffer_Receive_Coord, nBuffer_Coord, MPI_DOUBLE, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_GlobalPoint, nBuffer_Point, MPI_LONG, - Buffer_Receive_GlobalPoint, nBuffer_Point, MPI_LONG, MPI_COMM_WORLD); - if (faces) { - SU2_MPI::Allgather(Buffer_Send_Normal, nBuffer_Coord, MPI_DOUBLE, - Buffer_Receive_Normal, nBuffer_Coord, MPI_DOUBLE, MPI_COMM_WORLD); - } -} - -int CInterpolator::Find_InterfaceMarker(const CConfig *config, unsigned short val_marker_interface) const { - - for (unsigned short iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { - /*--- If the tag GetMarker_All_ZoneInterface(iMarker) equals the interface we are looking for. ---*/ - if (config->GetMarker_All_ZoneInterface(iMarker) == val_marker_interface) return iMarker; - } - return -1; -} - -void CInterpolator::ReconstructBoundary(unsigned long val_zone, int val_marker){ - - CGeometry *geom = Geometry[val_zone][INST_0][MESH_0]; - - unsigned long iVertex, jVertex, kVertex; - - unsigned long count, iTmp, *uptr, dPoint, EdgeIndex, jEdge, nEdges, nNodes, nVertex, iDim, nDim, iPoint; - - unsigned long nGlobalLinkedNodes, nLocalVertex, nLocalLinkedNodes; - - nDim = geom->GetnDim(); - - if( val_marker != -1 ) - nVertex = geom->GetnVertex( val_marker ); - else - nVertex = 0; - - - su2double *Buffer_Send_Coord = new su2double [ nVertex * nDim ]; - unsigned long *Buffer_Send_GlobalPoint = new unsigned long [ nVertex ]; - - unsigned long *Buffer_Send_nLinkedNodes = new unsigned long [ nVertex ]; - unsigned long *Buffer_Send_StartLinkedNodes = new unsigned long [ nVertex ]; - unsigned long **Aux_Send_Map = new unsigned long*[ nVertex ]; - -#ifdef HAVE_MPI - int nProcessor = size, iRank; - unsigned long iTmp2, tmp_index, tmp_index_2; -#endif - - /*--- Copy coordinates and point to the auxiliar vector ---*/ - - nGlobalVertex = 0; - nLocalVertex = 0; - nLocalLinkedNodes = 0; - - for (iVertex = 0; iVertex < nVertex; iVertex++) { - - Buffer_Send_nLinkedNodes[iVertex] = 0; - Aux_Send_Map[iVertex] = NULL; - - iPoint = geom->vertex[val_marker][iVertex]->GetNode(); - - if (geom->node[iPoint]->GetDomain()) { - Buffer_Send_GlobalPoint[nLocalVertex] = geom->node[iPoint]->GetGlobalIndex(); - - for (iDim = 0; iDim < nDim; iDim++) - Buffer_Send_Coord[nLocalVertex*nDim+iDim] = geom->node[iPoint]->GetCoord(iDim); - - nNodes = 0; - nEdges = geom->node[iPoint]->GetnPoint(); - - for (jEdge = 0; jEdge < nEdges; jEdge++){ - EdgeIndex = geom->node[iPoint]->GetEdge(jEdge); - - if( iPoint == geom->edge[EdgeIndex]->GetNode(0) ) - dPoint = geom->edge[EdgeIndex]->GetNode(1); - else - dPoint = geom->edge[EdgeIndex]->GetNode(0); - - if ( geom->node[dPoint]->GetVertex(val_marker) != -1 ) - nNodes++; - } - - Buffer_Send_StartLinkedNodes[nLocalVertex] = nLocalLinkedNodes; - Buffer_Send_nLinkedNodes[nLocalVertex] = nNodes; - - nLocalLinkedNodes += nNodes; - - Aux_Send_Map[nLocalVertex] = new unsigned long[ nNodes ]; - nNodes = 0; - - for (jEdge = 0; jEdge < nEdges; jEdge++){ - EdgeIndex = geom->node[iPoint]->GetEdge(jEdge); - - if( iPoint == geom->edge[EdgeIndex]->GetNode(0) ) - dPoint = geom->edge[EdgeIndex]->GetNode(1); - else - dPoint = geom->edge[EdgeIndex]->GetNode(0); - - if ( geom->node[dPoint]->GetVertex(val_marker) != -1 ){ - Aux_Send_Map[nLocalVertex][nNodes] = geom->node[dPoint]->GetGlobalIndex(); - nNodes++; - } - } - nLocalVertex++; - } - } - - unsigned long *Buffer_Send_LinkedNodes = new unsigned long [ nLocalLinkedNodes ]; - - nLocalLinkedNodes = 0; - - for (iVertex = 0; iVertex < nLocalVertex; iVertex++){ - for (jEdge = 0; jEdge < Buffer_Send_nLinkedNodes[iVertex]; jEdge++){ - Buffer_Send_LinkedNodes[nLocalLinkedNodes] = Aux_Send_Map[iVertex][jEdge]; - nLocalLinkedNodes++; - } - } - - for (iVertex = 0; iVertex < nVertex; iVertex++){ - if( Aux_Send_Map[iVertex] != NULL ) - delete [] Aux_Send_Map[iVertex]; - } - delete [] Aux_Send_Map; Aux_Send_Map = NULL; - - /*--- Reconstruct boundary by gathering data from all ranks ---*/ - - SU2_MPI::Allreduce( &nLocalVertex, &nGlobalVertex, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); - SU2_MPI::Allreduce(&nLocalLinkedNodes, &nGlobalLinkedNodes, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); - - Buffer_Receive_Coord = new su2double [ nGlobalVertex * nDim ]; - Buffer_Receive_GlobalPoint = new long[ nGlobalVertex ]; - Buffer_Receive_Proc = new unsigned long[ nGlobalVertex ]; - - Buffer_Receive_nLinkedNodes = new unsigned long[ nGlobalVertex ]; - Buffer_Receive_LinkedNodes = new unsigned long[ nGlobalLinkedNodes ]; - Buffer_Receive_StartLinkedNodes = new unsigned long[ nGlobalVertex ]; - -#ifdef HAVE_MPI - if (rank == MASTER_NODE){ - - for (iVertex = 0; iVertex < nDim*nLocalVertex; iVertex++) - Buffer_Receive_Coord[iVertex] = Buffer_Send_Coord[iVertex]; - - for (iVertex = 0; iVertex < nLocalVertex; iVertex++){ - Buffer_Receive_GlobalPoint[iVertex] = Buffer_Send_GlobalPoint[iVertex]; - Buffer_Receive_Proc[iVertex] = MASTER_NODE; - Buffer_Receive_nLinkedNodes[iVertex] = Buffer_Send_nLinkedNodes[iVertex]; - Buffer_Receive_StartLinkedNodes[iVertex] = Buffer_Send_StartLinkedNodes[iVertex]; - } - - for (iVertex = 0; iVertex < nLocalLinkedNodes; iVertex++) - Buffer_Receive_LinkedNodes[iVertex] = Buffer_Send_LinkedNodes[iVertex]; - - tmp_index = nLocalVertex; - tmp_index_2 = nLocalLinkedNodes; - - for(iRank = 1; iRank < nProcessor; iRank++){ - - SU2_MPI::Recv( &iTmp2, 1, MPI_UNSIGNED_LONG, iRank, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - SU2_MPI::Recv(&Buffer_Receive_LinkedNodes[tmp_index_2], iTmp2, MPI_UNSIGNED_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - - SU2_MPI::Recv( &iTmp, 1, MPI_UNSIGNED_LONG, iRank, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - SU2_MPI::Recv(&Buffer_Receive_Coord[tmp_index*nDim], nDim*iTmp, MPI_DOUBLE, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - - SU2_MPI::Recv( &Buffer_Receive_GlobalPoint[tmp_index], iTmp, MPI_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - SU2_MPI::Recv( &Buffer_Receive_nLinkedNodes[tmp_index], iTmp, MPI_UNSIGNED_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - SU2_MPI::Recv(&Buffer_Receive_StartLinkedNodes[tmp_index], iTmp, MPI_UNSIGNED_LONG, iRank, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - - for (iVertex = 0; iVertex < iTmp; iVertex++){ - Buffer_Receive_Proc[ tmp_index + iVertex ] = iRank; - Buffer_Receive_StartLinkedNodes[ tmp_index + iVertex ] += tmp_index_2; - } - - tmp_index += iTmp; - tmp_index_2 += iTmp2; - } - } - else{ - SU2_MPI::Send( &nLocalLinkedNodes, 1, MPI_UNSIGNED_LONG, 0, 0, MPI_COMM_WORLD); - SU2_MPI::Send(Buffer_Send_LinkedNodes, nLocalLinkedNodes, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD); - - SU2_MPI::Send( &nLocalVertex, 1, MPI_UNSIGNED_LONG, 0, 0, MPI_COMM_WORLD); - SU2_MPI::Send(Buffer_Send_Coord, nDim * nLocalVertex, MPI_DOUBLE, 0, 1, MPI_COMM_WORLD); - - SU2_MPI::Send( Buffer_Send_GlobalPoint, nLocalVertex, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD); - SU2_MPI::Send( Buffer_Send_nLinkedNodes, nLocalVertex, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD); - SU2_MPI::Send(Buffer_Send_StartLinkedNodes, nLocalVertex, MPI_UNSIGNED_LONG, 0, 1, MPI_COMM_WORLD); - } -#else - for (iVertex = 0; iVertex < nDim * nGlobalVertex; iVertex++) - Buffer_Receive_Coord[iVertex] = Buffer_Send_Coord[iVertex]; - - for (iVertex = 0; iVertex < nGlobalVertex; iVertex++){ - Buffer_Receive_GlobalPoint[iVertex] = Buffer_Send_GlobalPoint[iVertex]; - Buffer_Receive_Proc[iVertex] = MASTER_NODE; - Buffer_Receive_nLinkedNodes[iVertex] = Buffer_Send_nLinkedNodes[iVertex]; - Buffer_Receive_StartLinkedNodes[iVertex] = Buffer_Send_StartLinkedNodes[iVertex]; - } - - for (iVertex = 0; iVertex < nGlobalLinkedNodes; iVertex++) - Buffer_Receive_LinkedNodes[iVertex] = Buffer_Send_LinkedNodes[iVertex]; -#endif - - if (rank == MASTER_NODE){ - for (iVertex = 0; iVertex < nGlobalVertex; iVertex++){ - count = 0; - uptr = &Buffer_Receive_LinkedNodes[ Buffer_Receive_StartLinkedNodes[iVertex] ]; - - for (jVertex = 0; jVertex < Buffer_Receive_nLinkedNodes[iVertex]; jVertex++){ - iTmp = uptr[ jVertex ]; - for (kVertex = 0; kVertex < nGlobalVertex; kVertex++){ - if( Buffer_Receive_GlobalPoint[kVertex] == long(iTmp) ){ - uptr[ jVertex ] = kVertex; - count++; - break; - } - } - - if( count != (jVertex+1) ){ - for (kVertex = jVertex; kVertex < Buffer_Receive_nLinkedNodes[iVertex]-1; kVertex++){ - uptr[ kVertex ] = uptr[ kVertex + 1]; - } - Buffer_Receive_nLinkedNodes[iVertex]--; - jVertex--; - } - } - } - } - - SU2_MPI::Bcast(Buffer_Receive_GlobalPoint, nGlobalVertex, MPI_LONG, 0, MPI_COMM_WORLD); - SU2_MPI::Bcast(Buffer_Receive_Coord, nGlobalVertex*nDim, MPI_DOUBLE, 0, MPI_COMM_WORLD); - SU2_MPI::Bcast(Buffer_Receive_Proc, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); - - SU2_MPI::Bcast(Buffer_Receive_nLinkedNodes, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); - SU2_MPI::Bcast(Buffer_Receive_StartLinkedNodes, nGlobalVertex, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); - SU2_MPI::Bcast(Buffer_Receive_LinkedNodes, nGlobalLinkedNodes, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); - - delete [] Buffer_Send_Coord; Buffer_Send_Coord = NULL; - delete [] Buffer_Send_GlobalPoint; Buffer_Send_GlobalPoint = NULL; - delete [] Buffer_Send_LinkedNodes; Buffer_Send_LinkedNodes = NULL; - delete [] Buffer_Send_nLinkedNodes; Buffer_Send_nLinkedNodes = NULL; - delete [] Buffer_Send_StartLinkedNodes; Buffer_Send_StartLinkedNodes = NULL; - -} - -bool CInterpolator::CheckInterfaceBoundary(int markDonor, int markTarget) const { - - /*--- Determine whether the boundary is not on the rank because of - * the partition or because it is not part of the zone. ---*/ - int donorCheck = -1, targetCheck = -1; - SU2_MPI::Allreduce(&markDonor, &donorCheck, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); - SU2_MPI::Allreduce(&markTarget, &targetCheck, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); - return (donorCheck != -1) && (targetCheck != -1); -} - -CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, - unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { } - -void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { - - /*--- By definition, one donor point per target point. ---*/ - constexpr auto numDonor = 1; - constexpr auto idxDonor = 0; - - const su2double eps = numeric_limits::epsilon(); - - const int nProcessor = size; - const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2; - const auto nDim = donor_geometry->GetnDim(); - - Buffer_Send_nVertex_Donor = new unsigned long [1]; - Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; - - /*--- Cycle over nMarkersInt interface to determine communication pattern. ---*/ - - for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { - - /*--- On the donor side: find the tag of the boundary sharing the interface. ---*/ - const auto markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); - - /*--- On the target side: find the tag of the boundary sharing the interface. ---*/ - const auto markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); - - /*--- Checks if the zone contains the interface, if not continue to the next step. ---*/ - if (!CheckInterfaceBoundary(markDonor, markTarget)) continue; - - unsigned long nVertexDonor = 0, nVertexTarget = 0; - if(markDonor != -1) nVertexDonor = donor_geometry->GetnVertex( markDonor ); - if(markTarget != -1) nVertexTarget = target_geometry->GetnVertex( markTarget ); - - /* Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor. */ - Determine_ArraySize(false, markDonor, markTarget, nVertexDonor, nDim); - - Buffer_Send_Coord = new su2double [ MaxLocalVertex_Donor * nDim ]; - Buffer_Send_GlobalPoint = new long [ MaxLocalVertex_Donor ]; - Buffer_Receive_Coord = new su2double [ nProcessor * MaxLocalVertex_Donor * nDim ]; - Buffer_Receive_GlobalPoint = new long [ nProcessor * MaxLocalVertex_Donor ]; - - /*-- Collect coordinates and global point indices. ---*/ - Collect_VertexInfo( false, markDonor, markTarget, nVertexDonor, nDim ); - - /*--- Compute the closest donor point to each target. ---*/ - SU2_OMP_PARALLEL_(for schedule(dynamic,roundUpDiv(nVertexTarget,2*omp_get_max_threads()))) - for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget++) { - - auto target_vertex = target_geometry->vertex[markTarget][iVertexTarget]; - const auto Point_Target = target_vertex->GetNode(); - - if (!target_geometry->node[Point_Target]->GetDomain()) continue; - - /*--- Coordinates of the target point. ---*/ - const su2double* Coord_i = target_geometry->node[Point_Target]->GetCoord(); - - su2double mindist = 1e20; - long pGlobalPoint = 0; - int pProcessor = 0; - - for (int iProcessor = 0; iProcessor < nProcessor; ++iProcessor) { - for (auto jVertex = 0ul; jVertex < Buffer_Receive_nVertex_Donor[iProcessor]; ++jVertex) { - - const auto idx = iProcessor*MaxLocalVertex_Donor + jVertex; - - const su2double* Coord_j = &Buffer_Receive_Coord[idx*nDim]; - - const auto dist = PointsSquareDistance(nDim, Coord_i, Coord_j); - - if (dist < mindist) { - mindist = dist; - pProcessor = iProcessor; - pGlobalPoint = Buffer_Receive_GlobalPoint[idx]; - } - - /*--- Test for "exact" match. ---*/ - if (dist < eps) break; - } - } - - /*--- Store matching pair. ---*/ - target_vertex->SetnDonorPoints(numDonor); - target_vertex->Allocate_DonorInfo(); - target_vertex->SetInterpDonorPoint(idxDonor, pGlobalPoint); - target_vertex->SetInterpDonorProcessor(idxDonor, pProcessor); - target_vertex->SetDonorCoeff(idxDonor, 1.0); - - } - - delete[] Buffer_Send_Coord; - delete[] Buffer_Send_GlobalPoint; - - delete[] Buffer_Receive_Coord; - delete[] Buffer_Receive_GlobalPoint; - - } - - delete[] Buffer_Send_nVertex_Donor; - delete[] Buffer_Receive_nVertex_Donor; - -} - - - -CIsoparametric::CIsoparametric(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, - unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { } - -void CIsoparametric::Set_TransferCoeff(CConfig **config) { - - unsigned long iVertex, jVertex; - unsigned long dPoint, inode, jElem, nElem; - unsigned short iDim, iDonor=0, iFace; - - unsigned short nDim = donor_geometry->GetnDim(); - - unsigned short nMarkerInt; - unsigned short iMarkerInt; - - int markDonor=0, markTarget=0; - - long donor_elem=0, temp_donor=0; - unsigned int nNodes=0; - /*--- Restricted to 2-zone for now ---*/ - unsigned int nFaces=1; //For 2D cases, we want to look at edges, not faces, as the 'interface' - bool face_on_marker=true; - - unsigned long nVertexDonor = 0, nVertexTarget= 0; - unsigned long Point_Target = 0; - - unsigned long iVertexDonor, iPointDonor = 0; - int iProcessor; - - unsigned long nLocalFace_Donor = 0, nLocalFaceNodes_Donor=0; - - unsigned long faceindex; - - su2double dist = 0.0, mindist=1E6, *Coord, *Coord_i; - su2double myCoeff[10]; // Maximum # of donor points - su2double *Normal; - su2double *projected_point = new su2double[nDim]; - su2double tmp, tmp2; - su2double storeCoeff[10]; - unsigned long storeGlobal[10]; - int storeProc[10]; - - int nProcessor = size; - Coord = new su2double[nDim]; - Normal = new su2double[nDim]; - - nMarkerInt = (config[donorZone]->GetMarker_n_ZoneInterface())/2; - - /*--- For the number of markers on the interface... ---*/ - for (iMarkerInt=1; iMarkerInt <= nMarkerInt; iMarkerInt++) { - /*--- Procedure: - * -Loop through vertices of the aero grid - * -Find nearest element and allocate enough space in the aero grid donor point info - * -set the transfer coefficient values - */ - - /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ - markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); - - /*--- On the target side: find the tag of the boundary sharing the interface ---*/ - markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); - - /*--- Checks if the zone contains the interface, if not continue to the next step ---*/ - if(!CheckInterfaceBoundary(markDonor, markTarget)) continue; - - if(markDonor != -1) - nVertexDonor = donor_geometry->GetnVertex( markDonor ); - else - nVertexDonor = 0; - - if(markTarget != -1) - nVertexTarget = target_geometry->GetnVertex( markTarget ); - else - nVertexTarget = 0; - - Buffer_Send_nVertex_Donor = new unsigned long [1]; - Buffer_Send_nFace_Donor = new unsigned long [1]; - Buffer_Send_nFaceNodes_Donor = new unsigned long [1]; - - Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; - Buffer_Receive_nFace_Donor = new unsigned long [nProcessor]; - Buffer_Receive_nFaceNodes_Donor = new unsigned long [nProcessor]; - - /* Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor */ - Determine_ArraySize(true, markDonor, markTarget, nVertexDonor, nDim); - - Buffer_Send_Coord = new su2double [MaxLocalVertex_Donor*nDim]; - Buffer_Send_Normal = new su2double [MaxLocalVertex_Donor*nDim]; - Buffer_Send_GlobalPoint = new long [MaxLocalVertex_Donor]; - - Buffer_Receive_Coord = new su2double [nProcessor*MaxLocalVertex_Donor*nDim]; - Buffer_Receive_Normal = new su2double [nProcessor*MaxLocalVertex_Donor*nDim]; - Buffer_Receive_GlobalPoint = new long [nProcessor*MaxLocalVertex_Donor]; - - /*-- Collect coordinates, global points, and normal vectors ---*/ - Collect_VertexInfo(true, markDonor,markTarget,nVertexDonor,nDim); - - Buffer_Send_FaceIndex = new unsigned long[MaxFace_Donor]; - Buffer_Send_FaceNodes = new unsigned long[MaxFaceNodes_Donor]; - Buffer_Send_FaceProc = new unsigned long[MaxFaceNodes_Donor]; - - Buffer_Receive_FaceIndex = new unsigned long[MaxFace_Donor*nProcessor]; - Buffer_Receive_FaceNodes = new unsigned long[MaxFaceNodes_Donor*nProcessor]; - Buffer_Receive_FaceProc = new unsigned long[MaxFaceNodes_Donor*nProcessor]; - - nLocalFace_Donor=0; - nLocalFaceNodes_Donor=0; - - /*--- Collect Face info ---*/ - - for (iVertex = 0; iVertex < MaxFace_Donor; iVertex++) { - Buffer_Send_FaceIndex[iVertex] = 0; - } - for (iVertex=0; iVertexvertex[markDonor][iVertexDonor]->GetNode(); - - if (donor_geometry->node[iPointDonor]->GetDomain()) { - - if (nDim==3) nElem = donor_geometry->node[iPointDonor]->GetnElem(); - else nElem =donor_geometry->node[iPointDonor]->GetnPoint(); - - for (jElem=0; jElem < nElem; jElem++) { - if (nDim==3) { - temp_donor = donor_geometry->node[iPointDonor]->GetElem(jElem); - nFaces = donor_geometry->elem[temp_donor]->GetnFaces(); - for (iFace=0; iFaceelem[temp_donor]->GetnNodesFace(iFace); - for (iDonor=0; iDonorelem[temp_donor]->GetFaces(iFace, iDonor); - dPoint = donor_geometry->elem[temp_donor]->GetNode(inode); - face_on_marker = (face_on_marker && (donor_geometry->node[dPoint]->GetVertex(markDonor) !=-1)); - } - - if (face_on_marker ) { - for (iDonor=0; iDonorelem[temp_donor]->GetFaces(iFace, iDonor); - dPoint = donor_geometry->elem[temp_donor]->GetNode(inode); - // Match node on the face to the correct global index - long jGlobalPoint = donor_geometry->node[dPoint]->GetGlobalIndex(); - for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { - for (jVertex = 0; jVertex < Buffer_Receive_nVertex_Donor[iProcessor]; jVertex++) { - if (jGlobalPoint == Buffer_Receive_GlobalPoint[MaxLocalVertex_Donor*iProcessor+jVertex]) { - Buffer_Send_FaceNodes[nLocalFaceNodes_Donor]=MaxLocalVertex_Donor*iProcessor+jVertex; - Buffer_Send_FaceProc[nLocalFaceNodes_Donor]=iProcessor; - } - } - } - nLocalFaceNodes_Donor++; // Increment total number of face-nodes / processor - } - /* Store the indices */ - Buffer_Send_FaceIndex[nLocalFace_Donor+1] = Buffer_Send_FaceIndex[nLocalFace_Donor]+nNodes; - nLocalFace_Donor++; // Increment number of faces / processor - } - } - } - else { - /*-- Determine whether this face/edge is on the marker --*/ - face_on_marker=true; - for (iDonor=0; iDonornode[iPointDonor]->GetEdge(jElem); - dPoint = donor_geometry->edge[inode]->GetNode(iDonor); - face_on_marker = (face_on_marker && (donor_geometry->node[dPoint]->GetVertex(markDonor) !=-1)); - } - if (face_on_marker ) { - for (iDonor=0; iDonornode[iPointDonor]->GetEdge(jElem); - dPoint = donor_geometry->edge[inode]->GetNode(iDonor); - // Match node on the face to the correct global index - long jGlobalPoint = donor_geometry->node[dPoint]->GetGlobalIndex(); - for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { - for (jVertex = 0; jVertex < Buffer_Receive_nVertex_Donor[iProcessor]; jVertex++) { - if (jGlobalPoint == Buffer_Receive_GlobalPoint[MaxLocalVertex_Donor*iProcessor+jVertex]) { - Buffer_Send_FaceNodes[nLocalFaceNodes_Donor]=MaxLocalVertex_Donor*iProcessor+jVertex; - Buffer_Send_FaceProc[nLocalFaceNodes_Donor]=iProcessor; - } - } - } - nLocalFaceNodes_Donor++; // Increment total number of face-nodes / processor - } - /* Store the indices */ - Buffer_Send_FaceIndex[nLocalFace_Donor+1] = Buffer_Send_FaceIndex[nLocalFace_Donor]+nNodes; - nLocalFace_Donor++; // Increment number of faces / processor - } - } - } - } - } - - //Buffer_Send_FaceIndex[nLocalFace_Donor+1] = MaxFaceNodes_Donor*rank+nLocalFaceNodes_Donor; - - SU2_MPI::Allgather(Buffer_Send_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, - Buffer_Receive_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_FaceProc, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, - Buffer_Receive_FaceProc, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, - Buffer_Receive_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - - /*--- Loop over the vertices on the target Marker ---*/ - for (iVertex = 0; iVertexvertex[markTarget][iVertex]->GetNode(); - - if (!target_geometry->node[Point_Target]->GetDomain()) continue; - - Coord_i = target_geometry->node[Point_Target]->GetCoord(); - /*---Loop over the faces previously communicated/stored ---*/ - for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { - - nFaces = (unsigned int)Buffer_Receive_nFace_Donor[iProcessor]; - - for (iFace = 0; iFace< nFaces; iFace++) { - /*--- ---*/ - - nNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1] - - (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; - - su2double *X = new su2double[nNodes*(nDim+1)]; - faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face - for (iDonor=0; iDonorvertex[markTarget][iVertex]->SetDonorElem(donor_elem); // in 2D is nearest neighbor - target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nNodes); - for (iDonor=0; iDonorvertex[markTarget][iVertex]->GetnDonorPoints(); - target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); - - for (iDonor=0; iDonorvertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor,storeGlobal[iDonor]); - //cout <vertex[markTarget][iVertex]->SetDonorCoeff(iDonor,storeCoeff[iDonor]); - target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]); - } - - } - - delete[] Buffer_Send_nVertex_Donor; - delete[] Buffer_Send_nFace_Donor; - delete[] Buffer_Send_nFaceNodes_Donor; - - delete[] Buffer_Receive_nVertex_Donor; - delete[] Buffer_Receive_nFace_Donor; - delete[] Buffer_Receive_nFaceNodes_Donor; - - delete[] Buffer_Send_Coord; - delete[] Buffer_Send_Normal; - delete[] Buffer_Send_GlobalPoint; - - delete[] Buffer_Receive_Coord; - delete[] Buffer_Receive_Normal; - delete[] Buffer_Receive_GlobalPoint; - - delete[] Buffer_Send_FaceIndex; - delete[] Buffer_Send_FaceNodes; - delete[] Buffer_Send_FaceProc; - - delete[] Buffer_Receive_FaceIndex; - delete[] Buffer_Receive_FaceNodes; - delete[] Buffer_Receive_FaceProc; - } - delete [] Coord; - delete [] Normal; - - delete [] projected_point; -} - -void CIsoparametric::Isoparameters(unsigned short nDim, unsigned short nDonor, - su2double *X, su2double *xj, su2double *isoparams) { - short iDonor,iDim,k; // indices - su2double tmp, tmp2; - - su2double *x = new su2double[nDim+1]; - su2double *x_tmp = new su2double[nDim+1]; - su2double *Q = new su2double[nDonor*nDonor]; - su2double *R = new su2double[nDonor*nDonor]; - su2double *A = new su2double[(nDim+2)*nDonor]; - su2double *A2 = NULL; - su2double *x2 = new su2double[nDim+1]; - - bool *test = new bool[nDim+1]; - bool *testi = new bool[nDim+1]; - - su2double eps = 1E-10; - - short n = nDim+1; - - if (nDonor>2) { - /*--- Create Matrix A: 1st row all 1's, 2nd row x coordinates, 3rd row y coordinates, etc ---*/ - /*--- Right hand side is [1, \vec{x}']'---*/ - for (iDonor=0; iDonoreps && iDonor=0; iDonor--) { - if (R[iDonor*nDonor+iDonor]>eps) - isoparams[iDonor]=x_tmp[iDonor]/R[iDonor*nDonor+iDonor]; - else - isoparams[iDonor]=0; - for (k=0; k1.0) xi=1.0; - if (xi<-1.0) xi=-1.0; - if (eta>1.0) eta=1.0; - if (eta<-1.0) eta=-1.0; - isoparams[0]=0.25*(1-xi)*(1-eta); - isoparams[1]=0.25*(1+xi)*(1-eta); - isoparams[2]=0.25*(1+xi)*(1+eta); - isoparams[3]=0.25*(1-xi)*(1+eta); - - } - if (nDonor<4) { - tmp = 0.0; // value for normalization - tmp2=0; // check for maximum value, to be used to id nearest neighbor if necessary - k=0; // index for maximum value - for (iDonor=0; iDonor< nDonor; iDonor++) { - if (isoparams[iDonor]>tmp2) { - k=iDonor; - tmp2=isoparams[iDonor]; - } - // [0,1] - if (isoparams[iDonor]<0) isoparams[iDonor]=0; - if (isoparams[iDonor]>1) isoparams[iDonor] = 1; - tmp +=isoparams[iDonor]; - } - if (tmp>0) - for (iDonor=0; iDonor< nDonor; iDonor++) - isoparams[iDonor]=isoparams[iDonor]/tmp; - else { - isoparams[k] = 1.0; - } - } - - delete [] x; - delete [] x_tmp; - delete [] Q; - delete [] R; - delete [] A; - delete [] A2; - delete [] x2; - - delete [] test; - delete [] testi; - -} - -CMirror::CMirror(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, - unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { } - -void CMirror::Set_TransferCoeff(CConfig **config) { - unsigned long iVertex, jVertex; - unsigned long iPoint; - unsigned short iDonor=0, iFace=0, iTarget=0; - - unsigned short nMarkerInt; - unsigned short iMarkerInt; - - int markDonor=0, markTarget=0; - - unsigned int nNodes=0, iNodes=0; - unsigned long nVertexDonor = 0, nVertexTarget= 0; - unsigned long Point_Donor = 0; - unsigned long pGlobalPoint = 0; - int iProcessor; - - unsigned long nLocalFace_Donor = 0, nLocalFaceNodes_Donor=0; - - unsigned long faceindex; - - int nProcessor = size; - - su2double *Buffer_Send_Coeff, *Buffer_Receive_Coeff; - su2double coeff; - - /*--- Number of markers on the interface ---*/ - nMarkerInt = (config[targetZone]->GetMarker_n_ZoneInterface())/2; - - /*--- For the number of markers on the interface... ---*/ - for (iMarkerInt=1; iMarkerInt <= nMarkerInt; iMarkerInt++) { - /*--- Procedure: - * - Loop through vertices of the aero grid - * - Find nearest element and allocate enough space in the aero grid donor point info - * - Set the transfer coefficient values - */ - - /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ - markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); - - /*--- On the target side: find the tag of the boundary sharing the interface ---*/ - markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); - - /*--- Checks if the zone contains the interface, if not continue to the next step ---*/ - if(!CheckInterfaceBoundary(markDonor, markTarget)) continue; - - if(markDonor != -1) - nVertexDonor = donor_geometry->GetnVertex( markDonor ); - else - nVertexDonor = 0; - - if(markTarget != -1) - nVertexTarget = target_geometry->GetnVertex( markTarget ); - else - nVertexTarget = 0; - - /*-- Collect the number of donor nodes: re-use 'Face' containers --*/ - nLocalFace_Donor=0; - nLocalFaceNodes_Donor=0; - for (jVertex = 0; jVertexvertex[markDonor][jVertex]->GetNode(); // Local index of jVertex - - if (donor_geometry->node[Point_Donor]->GetDomain()) { - nNodes = donor_geometry->vertex[markDonor][jVertex]->GetnDonorPoints(); - nLocalFaceNodes_Donor+=nNodes; - nLocalFace_Donor++; - } - } - Buffer_Send_nFace_Donor= new unsigned long [1]; - Buffer_Send_nFaceNodes_Donor= new unsigned long [1]; - - Buffer_Receive_nFace_Donor = new unsigned long [nProcessor]; - Buffer_Receive_nFaceNodes_Donor = new unsigned long [nProcessor]; - - Buffer_Send_nFace_Donor[0] = nLocalFace_Donor; - Buffer_Send_nFaceNodes_Donor[0] = nLocalFaceNodes_Donor; - - /*--- Send Interface vertex information --*/ -#ifdef HAVE_MPI - SU2_MPI::Allreduce(&nLocalFaceNodes_Donor, &MaxFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); - SU2_MPI::Allreduce(&nLocalFace_Donor, &MaxFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_nFace_Donor, 1, MPI_UNSIGNED_LONG, Buffer_Receive_nFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, Buffer_Receive_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - MaxFace_Donor++; -#else - nGlobalFace_Donor = nLocalFace_Donor; - nGlobalFaceNodes_Donor = nLocalFaceNodes_Donor; - MaxFaceNodes_Donor = nLocalFaceNodes_Donor; - MaxFace_Donor = nLocalFace_Donor+1; - Buffer_Receive_nFace_Donor[0] = Buffer_Send_nFace_Donor[0]; - Buffer_Receive_nFaceNodes_Donor[0] = Buffer_Send_nFaceNodes_Donor[0]; -#endif - - /*-- Send donor info --*/ - Buffer_Send_FaceIndex = new unsigned long[MaxFace_Donor]; - Buffer_Send_FaceNodes = new unsigned long[MaxFaceNodes_Donor]; - Buffer_Send_GlobalPoint = new long[MaxFaceNodes_Donor]; - Buffer_Send_Coeff = new su2double[MaxFaceNodes_Donor]; - - Buffer_Receive_FaceIndex= new unsigned long[MaxFace_Donor*nProcessor]; - Buffer_Receive_FaceNodes= new unsigned long[MaxFaceNodes_Donor*nProcessor]; - Buffer_Receive_GlobalPoint = new long[MaxFaceNodes_Donor*nProcessor]; - Buffer_Receive_Coeff = new su2double[MaxFaceNodes_Donor*nProcessor]; - - for (iVertex=0; iVertexvertex[markDonor][jVertex]->GetNode(); // Local index of jVertex - if (donor_geometry->node[Point_Donor]->GetDomain()) { - nNodes = donor_geometry->vertex[markDonor][jVertex]->GetnDonorPoints(); - for (iDonor=0; iDonornode[Point_Donor]->GetGlobalIndex(); - Buffer_Send_GlobalPoint[nLocalFaceNodes_Donor] = - donor_geometry->vertex[markDonor][jVertex]->GetInterpDonorPoint(iDonor); - Buffer_Send_Coeff[nLocalFaceNodes_Donor] = - donor_geometry->vertex[markDonor][jVertex]->GetDonorCoeff(iDonor); - nLocalFaceNodes_Donor++; - } - Buffer_Send_FaceIndex[nLocalFace_Donor+1] =Buffer_Send_FaceIndex[nLocalFace_Donor]+nNodes; - nLocalFace_Donor++; - } - } - - SU2_MPI::Allgather(Buffer_Send_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, - Buffer_Receive_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_GlobalPoint, MaxFaceNodes_Donor, MPI_LONG, - Buffer_Receive_GlobalPoint, MaxFaceNodes_Donor, MPI_LONG, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_Coeff, MaxFaceNodes_Donor, MPI_DOUBLE, - Buffer_Receive_Coeff, MaxFaceNodes_Donor, MPI_DOUBLE, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, - Buffer_Receive_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - - /*--- Loop over the vertices on the target Marker ---*/ - for (iVertex = 0; iVertexvertex[markTarget][iVertex]->GetNode(); - if (target_geometry->node[iPoint]->GetDomain()) { - long Global_Point = target_geometry->node[iPoint]->GetGlobalIndex(); - nNodes = 0; - for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { - for (iFace = 0; iFace < Buffer_Receive_nFace_Donor[iProcessor]; iFace++) { - faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face - iNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1]- (unsigned int)faceindex; - for (iTarget=0; iTargetvertex[markTarget][iVertex]->SetnDonorPoints(nNodes); - target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); - - iDonor = 0; - for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { - for (iFace = 0; iFace < Buffer_Receive_nFace_Donor[iProcessor]; iFace++) { - - faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face - iNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1]- (unsigned int)faceindex; - for (iTarget=0; iTargetvertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor,pGlobalPoint); - target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor,coeff); - target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, iProcessor); - iDonor++; - } - } - } - } - } - } - delete[] Buffer_Send_nFace_Donor; - delete[] Buffer_Send_nFaceNodes_Donor; - - delete[] Buffer_Receive_nFace_Donor; - delete[] Buffer_Receive_nFaceNodes_Donor; - - delete[] Buffer_Send_FaceIndex; - delete[] Buffer_Send_FaceNodes; - delete[] Buffer_Send_GlobalPoint; - delete[] Buffer_Send_Coeff; - - delete[] Buffer_Receive_FaceIndex; - delete[] Buffer_Receive_FaceNodes; - delete[] Buffer_Receive_GlobalPoint; - delete[] Buffer_Receive_Coeff; - - } -} - -CSlidingMesh::CSlidingMesh(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, - unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { } - -void CSlidingMesh::Set_TransferCoeff(CConfig **config) { - - /* --- This routine sets the transfer coefficient for sliding mesh approach --- */ - - /* - * The algorithm is based on Rinaldi et al. "Flux-conserving treatment of non-conformal interfaces - * for finite-volume discritization of conservaation laws" 2015, Comp. Fluids, 120, pp 126-139 - */ - - /* 0 - Variable declaration - */ - - /* --- General variables --- */ - - bool check; - - unsigned short iDim, nDim; - - unsigned long ii, jj, *uptr; - unsigned long vPoint; - unsigned long iEdgeVisited, nEdgeVisited, iNodeVisited; - unsigned long nAlreadyVisited, nToVisit, StartVisited; - - unsigned long *alreadyVisitedDonor, *ToVisit, *tmpVect; - unsigned long *storeProc, *tmp_storeProc; - - su2double dTMP; - su2double *Coeff_Vect, *tmp_Coeff_Vect; - - /* --- Geometrical variables --- */ - - su2double *Coord_i, *Coord_j, dist, mindist, *Normal; - su2double Area, Area_old, tmp_Area; - su2double LineIntersectionLength, *Direction, length; - - - /* --- Markers Variables --- */ - - unsigned short iMarkerInt, nMarkerInt; - - unsigned long iVertex, nVertexTarget; - - int markDonor, markTarget; - - /* --- Target variables --- */ - - unsigned long target_iPoint, jVertexTarget; - unsigned long nEdges_target, nNode_target; - - unsigned long *Target_nLinkedNodes, *Target_LinkedNodes, *Target_StartLinkedNodes, *target_segment; - unsigned long *Target_Proc; - long *Target_GlobalPoint, *Donor_GlobalPoint; - - su2double *TargetPoint_Coord, *target_iMidEdge_point, *target_jMidEdge_point, **target_element; - - /* --- Donor variables --- */ - - unsigned long donor_StartIndex, donor_forward_point, donor_backward_point, donor_iPoint, donor_OldiPoint; - unsigned long nEdges_donor, nNode_donor, nGlobalVertex_Donor; - - unsigned long nDonorPoints, iDonor; - unsigned long *Donor_Vect, *tmp_Donor_Vect; - unsigned long *Donor_nLinkedNodes, *Donor_LinkedNodes, *Donor_StartLinkedNodes; - unsigned long *Donor_Proc; - - su2double *donor_iMidEdge_point, *donor_jMidEdge_point; - su2double **donor_element, *DonorPoint_Coord; - - /* 1 - Variable pre-processing - */ - - nDim = donor_geometry->GetnDim(); - - /*--- Setting up auxiliary vectors ---*/ - - Donor_Vect = NULL; - Coeff_Vect = NULL; - storeProc = NULL; - - tmp_Donor_Vect = NULL; - tmp_Coeff_Vect = NULL; - tmp_storeProc = NULL; - - Normal = new su2double[nDim]; - Direction = new su2double[nDim]; - - - /* 2 - Find boundary tag between touching grids */ - - /*--- Number of markers on the FSI interface ---*/ - nMarkerInt = (int)( config[ donorZone ]->GetMarker_n_ZoneInterface() ) / 2; - - /*--- For the number of markers on the interface... ---*/ - for ( iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++ ){ - - /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ - markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); - - /*--- On the target side: find the tag of the boundary sharing the interface ---*/ - markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); - - /*--- Checks if the zone contains the interface, if not continue to the next step ---*/ - if(!CheckInterfaceBoundary(markDonor, markTarget)) continue; - - if(markTarget != -1) - nVertexTarget = target_geometry->GetnVertex( markTarget ); - else - nVertexTarget = 0; - - /* - 3 -Reconstruct the boundaries from parallel partitioning - */ - - /*--- Target boundary ---*/ - ReconstructBoundary(targetZone, markTarget); - - nGlobalVertex_Target = nGlobalVertex; - - TargetPoint_Coord = Buffer_Receive_Coord; - Target_GlobalPoint = Buffer_Receive_GlobalPoint; - Target_nLinkedNodes = Buffer_Receive_nLinkedNodes; - Target_StartLinkedNodes = Buffer_Receive_StartLinkedNodes; - Target_LinkedNodes = Buffer_Receive_LinkedNodes; - Target_Proc = Buffer_Receive_Proc; - - /*--- Donor boundary ---*/ - ReconstructBoundary(donorZone, markDonor); - - nGlobalVertex_Donor = nGlobalVertex; - - DonorPoint_Coord = Buffer_Receive_Coord; - Donor_GlobalPoint = Buffer_Receive_GlobalPoint; - Donor_nLinkedNodes = Buffer_Receive_nLinkedNodes; - Donor_StartLinkedNodes = Buffer_Receive_StartLinkedNodes; - Donor_LinkedNodes = Buffer_Receive_LinkedNodes; - Donor_Proc = Buffer_Receive_Proc; - - /*--- Starts building the supermesh layer (2D or 3D) ---*/ - /* - For each target node, it first finds the closest donor point - * - Then it creates the supermesh in the close proximity of the target point: - * - Starting from the closest donor node, it expands the supermesh by including - * donor elements neighboring the initial one, until the overall target area is fully covered. - */ - - if(nDim == 2){ - - target_iMidEdge_point = new su2double[nDim]; - target_jMidEdge_point = new su2double[nDim]; - - donor_iMidEdge_point = new su2double[nDim]; - donor_jMidEdge_point = new su2double[nDim]; - - /*--- Starts with supermesh reconstruction ---*/ - - target_segment = new unsigned long[2]; - - for (iVertex = 0; iVertex < nVertexTarget; iVertex++) { - - nDonorPoints = 0; - - /*--- Stores coordinates of the target node ---*/ - - target_iPoint = target_geometry->vertex[markTarget][iVertex]->GetNode(); - - if (target_geometry->node[target_iPoint]->GetDomain()){ - - Coord_i = target_geometry->node[target_iPoint]->GetCoord(); - - /*--- Brute force to find the closest donor_node ---*/ - - mindist = 1E6; - donor_StartIndex = 0; - - for (donor_iPoint = 0; donor_iPoint < nGlobalVertex_Donor; donor_iPoint++) { - - Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ]; - - dist = PointsDistance(nDim, Coord_i, Coord_j); - - if (dist < mindist) { - mindist = dist; - donor_StartIndex = donor_iPoint; - } - - if (dist == 0.0){ - donor_StartIndex = donor_iPoint; - break; - } - } - - donor_iPoint = donor_StartIndex; - donor_OldiPoint = donor_iPoint; - - /*--- Contruct information regarding the target cell ---*/ - - long dPoint = target_geometry->node[target_iPoint]->GetGlobalIndex(); - for (jVertexTarget = 0; jVertexTarget < nGlobalVertex_Target; jVertexTarget++) - if( dPoint == Target_GlobalPoint[jVertexTarget] ) - break; - - if ( Target_nLinkedNodes[jVertexTarget] == 1 ){ - target_segment[0] = Target_LinkedNodes[ Target_StartLinkedNodes[jVertexTarget] ]; - target_segment[1] = jVertexTarget; - } - else{ - target_segment[0] = Target_LinkedNodes[ Target_StartLinkedNodes[jVertexTarget] ]; - target_segment[1] = Target_LinkedNodes[ Target_StartLinkedNodes[jVertexTarget] + 1]; - } - - dTMP = 0; - for(iDim = 0; iDim < nDim; iDim++){ - target_iMidEdge_point[iDim] = ( TargetPoint_Coord[ nDim * target_segment[0] + iDim ] + target_geometry->node[ target_iPoint ]->GetCoord(iDim) ) / 2; - target_jMidEdge_point[iDim] = ( TargetPoint_Coord[ nDim * target_segment[1] + iDim ] + target_geometry->node[ target_iPoint ]->GetCoord(iDim) ) / 2; - - Direction[iDim] = target_jMidEdge_point[iDim] - target_iMidEdge_point[iDim]; - dTMP += Direction[iDim] * Direction[iDim]; - } - - dTMP = sqrt(dTMP); - for(iDim = 0; iDim < nDim; iDim++) - Direction[iDim] /= dTMP; - - length = PointsDistance(nDim, target_iMidEdge_point, target_jMidEdge_point); - - check = false; - - /*--- Proceeds along the forward direction (depending on which connected boundary node is found first) ---*/ - - while( !check ){ - - /*--- Proceeds until the value of the intersection area is null ---*/ - - if ( Donor_nLinkedNodes[donor_iPoint] == 1 ){ - donor_forward_point = Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_iPoint] ]; - donor_backward_point = donor_iPoint; - } - else{ - uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_iPoint] ]; - - if( donor_OldiPoint != uptr[0] ){ - donor_forward_point = uptr[0]; - donor_backward_point = uptr[1]; - } - else{ - donor_forward_point = uptr[1]; - donor_backward_point = uptr[0]; - } - } - - if(donor_iPoint >= nGlobalVertex_Donor){ - check = true; - continue; - } - - for(iDim = 0; iDim < nDim; iDim++){ - donor_iMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_forward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; - donor_jMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_backward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; - } - - LineIntersectionLength = ComputeLineIntersectionLength(target_iMidEdge_point, target_jMidEdge_point, donor_iMidEdge_point, donor_jMidEdge_point, Direction); - - if ( LineIntersectionLength == 0.0 ){ - check = true; - continue; - } - - /*--- In case the element intersects the target cell, update the auxiliary communication data structure ---*/ - - tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ]; - tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ]; - tmp_storeProc = new unsigned long[ nDonorPoints + 1 ]; - - for( iDonor = 0; iDonor < nDonorPoints; iDonor++){ - tmp_Donor_Vect[iDonor] = Donor_Vect[iDonor]; - tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor]; - tmp_storeProc[iDonor] = storeProc[iDonor]; - } - - tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint; - tmp_Coeff_Vect[ nDonorPoints ] = LineIntersectionLength / length; - tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint]; - - if (Donor_Vect != NULL) delete [] Donor_Vect; - if (Coeff_Vect != NULL) delete [] Coeff_Vect; - if (storeProc != NULL) delete [] storeProc; - - Donor_Vect = tmp_Donor_Vect; - Coeff_Vect = tmp_Coeff_Vect; - storeProc = tmp_storeProc; - - donor_OldiPoint = donor_iPoint; - donor_iPoint = donor_forward_point; - - nDonorPoints++; - } - - if ( Donor_nLinkedNodes[donor_StartIndex] == 2 ){ - check = false; - - uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_StartIndex] ]; - - donor_iPoint = uptr[1]; - donor_OldiPoint = donor_StartIndex; - } - else - check = true; - - /*--- Proceeds along the backward direction (depending on which connected boundary node is found first) ---*/ - - while( !check ){ - - /*--- Proceeds until the value of the intersection length is null ---*/ - if ( Donor_nLinkedNodes[donor_iPoint] == 1 ){ - donor_forward_point = donor_OldiPoint; - donor_backward_point = donor_iPoint; - } - else{ - uptr = &Donor_LinkedNodes[ Donor_StartLinkedNodes[donor_iPoint] ]; - - if( donor_OldiPoint != uptr[0] ){ - donor_forward_point = uptr[0]; - donor_backward_point = uptr[1]; - } - else{ - donor_forward_point = uptr[1]; - donor_backward_point = uptr[0]; - } - } - - if(donor_iPoint >= nGlobalVertex_Donor){ - check = true; - continue; - } - - for(iDim = 0; iDim < nDim; iDim++){ - donor_iMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_forward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; - donor_jMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_backward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; - } - - LineIntersectionLength = ComputeLineIntersectionLength(target_iMidEdge_point, target_jMidEdge_point, donor_iMidEdge_point, donor_jMidEdge_point, Direction); - - if ( LineIntersectionLength == 0.0 ){ - check = true; - continue; - } - - /*--- In case the element intersects the target cell, update the auxiliary communication data structure ---*/ - - tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ]; - tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ]; - tmp_storeProc = new unsigned long[ nDonorPoints + 1 ]; - - for( iDonor = 0; iDonor < nDonorPoints; iDonor++){ - tmp_Donor_Vect[iDonor] = Donor_Vect[iDonor]; - tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor]; - tmp_storeProc[iDonor] = storeProc[iDonor]; - } - - tmp_Coeff_Vect[ nDonorPoints ] = LineIntersectionLength / length; - tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint; - tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint]; - - if (Donor_Vect != NULL) delete [] Donor_Vect; - if (Coeff_Vect != NULL) delete [] Coeff_Vect; - if (storeProc != NULL) delete [] storeProc; - - Donor_Vect = tmp_Donor_Vect; - Coeff_Vect = tmp_Coeff_Vect; - storeProc = tmp_storeProc; - - donor_OldiPoint = donor_iPoint; - donor_iPoint = donor_forward_point; - - nDonorPoints++; - } - - /*--- Set the communication data structure and copy data from the auxiliary vectors ---*/ - - target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nDonorPoints); - - target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); - - for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){ - target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor, Coeff_Vect[iDonor]); - target_geometry->vertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor, Donor_GlobalPoint[Donor_Vect[iDonor]]); - target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]); - } - } - } - - delete [] target_segment; - - delete [] target_iMidEdge_point; - delete [] target_jMidEdge_point; - - delete [] donor_iMidEdge_point; - delete [] donor_jMidEdge_point; - } - else{ - /* --- 3D geometry, creates a superficial super-mesh --- */ - - for (iVertex = 0; iVertex < nVertexTarget; iVertex++) { - - nDonorPoints = 0; - - /*--- Stores coordinates of the target node ---*/ - - target_iPoint = target_geometry->vertex[markTarget][iVertex]->GetNode(); - - if (!target_geometry->node[target_iPoint]->GetDomain()) continue; - - Coord_i = target_geometry->node[target_iPoint]->GetCoord(); - - target_geometry->vertex[markTarget][iVertex]->GetNormal(Normal); - - /*--- The value of Area computed here includes also portion of boundary belonging to different marker ---*/ - Area = 0.0; - for (iDim = 0; iDim < nDim; iDim++) - Area += Normal[iDim]*Normal[iDim]; - Area = sqrt(Area); - - for (iDim = 0; iDim < nDim; iDim++) - Normal[iDim] /= Area; - - for (iDim = 0; iDim < nDim; iDim++) - Coord_i[iDim] = target_geometry->node[target_iPoint]->GetCoord(iDim); - - long dPoint = target_geometry->node[target_iPoint]->GetGlobalIndex(); - for (target_iPoint = 0; target_iPoint < nGlobalVertex_Target; target_iPoint++){ - if( dPoint == Target_GlobalPoint[target_iPoint] ) - break; - } - - /*--- Build local surface dual mesh for target element ---*/ - - nEdges_target = Target_nLinkedNodes[target_iPoint]; - - nNode_target = 2*(nEdges_target + 1); - - target_element = new su2double*[nNode_target]; - for (ii = 0; ii < nNode_target; ii++) - target_element[ii] = new su2double[nDim]; - - nNode_target = Build_3D_surface_element(Target_LinkedNodes, Target_StartLinkedNodes, Target_nLinkedNodes, TargetPoint_Coord, target_iPoint, target_element); - - /*--- Brute force to find the closest donor_node ---*/ - - mindist = 1E6; - donor_StartIndex = 0; - - for (donor_iPoint = 0; donor_iPoint < nGlobalVertex_Donor; donor_iPoint++) { - - Coord_j = &DonorPoint_Coord[ donor_iPoint * nDim ]; - - dist = PointsDistance(nDim, Coord_i, Coord_j); - - if (dist < mindist) { - mindist = dist; - donor_StartIndex = donor_iPoint; - } - - if (dist == 0.0){ - donor_StartIndex = donor_iPoint; - break; - } - } - - donor_iPoint = donor_StartIndex; - - nEdges_donor = Donor_nLinkedNodes[donor_iPoint]; - - donor_element = new su2double*[ 2*nEdges_donor + 2 ]; - for (ii = 0; ii < 2*nEdges_donor + 2; ii++) - donor_element[ii] = new su2double[nDim]; - - nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, DonorPoint_Coord, donor_iPoint, donor_element); - - Area = 0; - for (ii = 1; ii < nNode_target-1; ii++){ - for (jj = 1; jj < nNode_donor-1; jj++){ - Area += Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], donor_element[0], donor_element[jj], donor_element[jj+1], Normal); - //cout << Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], donor_element[0], donor_element[jj], donor_element[jj+1], Normal) << endl; - } - } - - for (ii = 0; ii < 2*nEdges_donor + 2; ii++) - delete [] donor_element[ii]; - delete [] donor_element; - - nDonorPoints = 1; - - /*--- In case the element intersect the target cell update the auxiliary communication data structure ---*/ - - Coeff_Vect = new su2double[ nDonorPoints ]; - Donor_Vect = new unsigned long[ nDonorPoints ]; - storeProc = new unsigned long[ nDonorPoints ]; - - Coeff_Vect[0] = Area; - Donor_Vect[0] = donor_iPoint; - storeProc[0] = Donor_Proc[donor_iPoint]; - - alreadyVisitedDonor = new unsigned long[1]; - - alreadyVisitedDonor[0] = donor_iPoint; - nAlreadyVisited = 1; - StartVisited = 0; - - Area_old = -1; - - while( Area > Area_old ){ - - /* - * - Starting from the closest donor_point, it expands the supermesh by a countour search pattern. - * - The closest donor element becomes the core, at each iteration a new layer of elements around the core is taken into account - */ - - Area_old = Area; - - ToVisit = NULL; - nToVisit = 0; - - for( iNodeVisited = StartVisited; iNodeVisited < nAlreadyVisited; iNodeVisited++ ){ - - vPoint = alreadyVisitedDonor[ iNodeVisited ]; - - nEdgeVisited = Donor_nLinkedNodes[vPoint]; - - for (iEdgeVisited = 0; iEdgeVisited < nEdgeVisited; iEdgeVisited++){ - - donor_iPoint = Donor_LinkedNodes[ Donor_StartLinkedNodes[vPoint] + iEdgeVisited]; - - /*--- Check if the node to visit is already listed in the data structure to avoid double visits ---*/ - - check = 0; - - for( jj = 0; jj < nAlreadyVisited; jj++ ){ - if( donor_iPoint == alreadyVisitedDonor[jj] ){ - check = 1; - break; - } - } - - if( check == 0 && ToVisit != NULL){ - for( jj = 0; jj < nToVisit; jj++ ) - if( donor_iPoint == ToVisit[jj] ){ - check = 1; - break; - } - } - - if( check == 0 ){ - /*--- If the node was not already visited, visit it and list it into data structure ---*/ - - tmpVect = new unsigned long[ nToVisit + 1 ]; - - for( jj = 0; jj < nToVisit; jj++ ) - tmpVect[jj] = ToVisit[jj]; - tmpVect[nToVisit] = donor_iPoint; - - if( ToVisit != NULL ) - delete [] ToVisit; - - ToVisit = tmpVect; - tmpVect = NULL; - - nToVisit++; - - /*--- Find the value of the intersection area between the current donor element and the target element --- */ - - nEdges_donor = Donor_nLinkedNodes[donor_iPoint]; - - donor_element = new su2double*[ 2*nEdges_donor + 2 ]; - for (ii = 0; ii < 2*nEdges_donor + 2; ii++) - donor_element[ii] = new su2double[nDim]; - - nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, DonorPoint_Coord, donor_iPoint, donor_element); - - tmp_Area = 0; - for (ii = 1; ii < nNode_target-1; ii++) - for (jj = 1; jj < nNode_donor-1; jj++) - tmp_Area += Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], donor_element[0], donor_element[jj], donor_element[jj+1], Normal); - - for (ii = 0; ii < 2*nEdges_donor + 2; ii++) - delete [] donor_element[ii]; - delete [] donor_element; - - /*--- In case the element intersect the target cell update the auxiliary communication data structure ---*/ - - tmp_Coeff_Vect = new su2double[ nDonorPoints + 1 ]; - tmp_Donor_Vect = new unsigned long[ nDonorPoints + 1 ]; - tmp_storeProc = new unsigned long[ nDonorPoints + 1 ]; - - for( iDonor = 0; iDonor < nDonorPoints; iDonor++){ - tmp_Donor_Vect[iDonor] = Donor_Vect[iDonor]; - tmp_Coeff_Vect[iDonor] = Coeff_Vect[iDonor]; - tmp_storeProc[iDonor] = storeProc[iDonor]; - } - - tmp_Coeff_Vect[ nDonorPoints ] = tmp_Area; - tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint; - tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint]; - - if (Donor_Vect != NULL) {delete [] Donor_Vect; } - if (Coeff_Vect != NULL) {delete [] Coeff_Vect; } - if (storeProc != NULL) {delete [] storeProc; } - - Donor_Vect = tmp_Donor_Vect; - Coeff_Vect = tmp_Coeff_Vect; - storeProc = tmp_storeProc; - - tmp_Coeff_Vect = NULL; - tmp_Donor_Vect = NULL; - tmp_storeProc = NULL; - - nDonorPoints++; - - Area += tmp_Area; - } - } - } - - /*--- Update auxiliary data structure ---*/ - - StartVisited = nAlreadyVisited; - - tmpVect = new unsigned long[ nAlreadyVisited + nToVisit ]; - - for( jj = 0; jj < nAlreadyVisited; jj++ ) - tmpVect[jj] = alreadyVisitedDonor[jj]; - - for( jj = 0; jj < nToVisit; jj++ ) - tmpVect[ nAlreadyVisited + jj ] = ToVisit[jj]; - - if( alreadyVisitedDonor != NULL ) - delete [] alreadyVisitedDonor; - - alreadyVisitedDonor = tmpVect; - - nAlreadyVisited += nToVisit; - - delete [] ToVisit; - } - - delete [] alreadyVisitedDonor; - - /*--- Set the communication data structure and copy data from the auxiliary vectors ---*/ - - target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nDonorPoints); - target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); - - for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){ - target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor, Coeff_Vect[iDonor]/Area); - target_geometry->vertex[markTarget][iVertex]->SetInterpDonorPoint( iDonor, Donor_GlobalPoint[ Donor_Vect[iDonor] ] ); - target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]); - //cout <GetnDim(); - - su2double dotA2, dotB1, dotB2; - - dotA2 = 0; - for(iDim = 0; iDim < nDim; iDim++) - dotA2 += ( A2[iDim] - A1[iDim] ) * Direction[iDim]; - - if( dotA2 >= 0 ){ - dotB1 = 0; - dotB2 = 0; - for(iDim = 0; iDim < nDim; iDim++){ - dotB1 += ( B1[iDim] - A1[iDim] ) * Direction[iDim]; - dotB2 += ( B2[iDim] - A1[iDim] ) * Direction[iDim]; - } - } - else{ - dotA2 *= -1; - - dotB1 = 0; - dotB2 = 0; - for(iDim = 0; iDim < nDim; iDim++){ - dotB1 -= ( B1[iDim] - A1[iDim] ) * Direction[iDim]; - dotB2 -= ( B2[iDim] - A1[iDim] ) * Direction[iDim]; - } - } - - if( dotB1 >= 0 && dotB1 <= dotA2 ){ - if ( dotB2 < 0 ) - return fabs( dotB1 ); - if ( dotB2 > dotA2 ) - return fabs( dotA2 - dotB1 ); - - return fabs( dotB1 - dotB2 ); - } - - if( dotB2 >= 0 && dotB2 <= dotA2 ){ - if ( dotB1 < 0 ) - return fabs(dotB2); - if ( dotB1 > dotA2 ) - return fabs( dotA2 - dotB2 ); - } - - if( ( dotB1 <= 0 && dotA2 <= dotB2 ) || ( dotB2 <= 0 && dotA2 <= dotB1 ) ) - return fabs( dotA2 ); - - return 0.0; -} - -su2double CSlidingMesh::Compute_Triangle_Intersection(su2double* A1, su2double* A2, su2double* A3, su2double* B1, su2double* B2, su2double* B3, su2double* Direction){ - - /* --- This routine is ONLY for 3D grids --- */ - /* --- Projects triangle points onto a plane, specified by its normal "Direction", and calls the ComputeIntersectionArea routine --- */ - - unsigned short iDim; - unsigned short nDim = 3; - - su2double I[3], J[3], K[3]; - su2double a1[3], a2[3], a3[3]; - su2double b1[3], b2[3], b3[3]; - su2double m1, m2; - - /* --- Reference frame is determined by: x = A1A2 y = x ^ ( -Direction ) --- */ - - for(iDim = 0; iDim < 3; iDim++){ - a1[iDim] = 0; - a2[iDim] = 0; - a3[iDim] = 0; - - b1[iDim] = 0; - b2[iDim] = 0; - b3[iDim] = 0; - } - - m1 = 0; - for(iDim = 0; iDim < nDim; iDim++){ - K[iDim] = Direction[iDim]; - - m1 += K[iDim] * K[iDim]; - } - - for(iDim = 0; iDim < nDim; iDim++) - K[iDim] /= sqrt(m1); - - m2 = 0; - for(iDim = 0; iDim < nDim; iDim++) - m2 += (A2[iDim] - A1[iDim]) * K[iDim]; - - m1 = 0; - for(iDim = 0; iDim < nDim; iDim++){ - I[iDim] = (A2[iDim] - A1[iDim]) - m2 * K[iDim]; - m1 += I[iDim] * I[iDim]; - } - - for(iDim = 0; iDim < nDim; iDim++) - I[iDim] /= sqrt(m1); - - // Cross product to find Y - J[0] = K[1]*I[2] - K[2]*I[1]; - J[1] = -(K[0]*I[2] - K[2]*I[0]); - J[2] = K[0]*I[1] - K[1]*I[0]; - - /* --- Project all points on the plane specified by Direction and change their reference frame taking A1 as origin --- */ - - for(iDim = 0; iDim < nDim; iDim++){ - a2[0] += (A2[iDim] - A1[iDim]) * I[iDim]; - a2[1] += (A2[iDim] - A1[iDim]) * J[iDim]; - a2[2] += (A2[iDim] - A1[iDim]) * K[iDim]; - - a3[0] += (A3[iDim] - A1[iDim]) * I[iDim]; - a3[1] += (A3[iDim] - A1[iDim]) * J[iDim]; - a3[2] += (A3[iDim] - A1[iDim]) * K[iDim]; - - b1[0] += (B1[iDim] - A1[iDim]) * I[iDim]; - b1[1] += (B1[iDim] - A1[iDim]) * J[iDim]; - b1[2] += (B1[iDim] - A1[iDim]) * K[iDim]; - - b2[0] += (B2[iDim] - A1[iDim]) * I[iDim]; - b2[1] += (B2[iDim] - A1[iDim]) * J[iDim]; - b2[2] += (B2[iDim] - A1[iDim]) * K[iDim]; - - b3[0] += (B3[iDim] - A1[iDim]) * I[iDim]; - b3[1] += (B3[iDim] - A1[iDim]) * J[iDim]; - b3[2] += (B3[iDim] - A1[iDim]) * K[iDim]; - } - - /*--- Compute intersection area ---*/ - - return ComputeIntersectionArea( a1, a2, a3, b1, b2, b3 ); -} - -su2double CSlidingMesh::ComputeIntersectionArea( su2double* P1, su2double* P2, su2double* P3, su2double* Q1, su2double* Q2, su2double* Q3 ){ - - /* --- This routines computes the area of the polygonal element generated by the superimposition of 2 planar triangle --- */ - /* --- The 2 triangle must lie on the same plane --- */ - - unsigned short iDim, nPoints, i, j, k; - unsigned short nDim, min_theta_index; - - su2double points[16][2], IntersectionPoint[2], theta[6]; - su2double TriangleP[4][2], TriangleQ[4][2]; - su2double Area, det, dot1, dot2, dtmp, min_theta; - - nDim = 2; - nPoints = 0; - - for(iDim = 0; iDim < nDim; iDim++){ - TriangleP[0][iDim] = 0; - TriangleP[1][iDim] = P2[iDim] - P1[iDim]; - TriangleP[2][iDim] = P3[iDim] - P1[iDim]; - TriangleP[3][iDim] = 0; - - TriangleQ[0][iDim] = Q1[iDim] - P1[iDim]; - TriangleQ[1][iDim] = Q2[iDim] - P1[iDim]; - TriangleQ[2][iDim] = Q3[iDim] - P1[iDim]; - TriangleQ[3][iDim] = Q1[iDim] - P1[iDim]; - } - - - for( j = 0; j < 3; j++){ - if( CheckPointInsideTriangle(TriangleP[j], TriangleQ[0], TriangleQ[1], TriangleQ[2]) ){ - - // Then P1 is also inside triangle Q, so store it - for(iDim = 0; iDim < nDim; iDim++) - points[nPoints][iDim] = TriangleP[j][iDim]; - - nPoints++; - } - } - - for( j = 0; j < 3; j++){ - if( CheckPointInsideTriangle(TriangleQ[j], TriangleP[0], TriangleP[1], TriangleP[2]) ){ - - // Then Q1 is also inside triangle P, so store it - for(iDim = 0; iDim < nDim; iDim++) - points[nPoints][iDim] = TriangleQ[j][iDim]; - - nPoints++; - } - } - - - // Compute all edge intersections - - for( j = 0; j < 3; j++){ - for( i = 0; i < 3; i++){ - - det = (TriangleP[j][0] - TriangleP[j+1][0]) * ( TriangleQ[i][1] - TriangleQ[i+1][1] ) - (TriangleP[j][1] - TriangleP[j+1][1]) * (TriangleQ[i][0] - TriangleQ[i+1][0]); - - if ( det != 0.0 ){ - ComputeLineIntersectionPoint( TriangleP[j], TriangleP[j+1], TriangleQ[i], TriangleQ[i+1], IntersectionPoint ); - - dot1 = 0; - dot2 = 0; - for(iDim = 0; iDim < nDim; iDim++){ - dot1 += ( TriangleP[j][iDim] - IntersectionPoint[iDim] ) * ( TriangleP[j+1][iDim] - IntersectionPoint[iDim] ); - dot2 += ( TriangleQ[i][iDim] - IntersectionPoint[iDim] ) * ( TriangleQ[i+1][iDim] - IntersectionPoint[iDim] ); - } - - if( dot1 <= 0 && dot2 <= 0 ){ // It found one intersection - - // Store temporarily the intersection point - - for(iDim = 0; iDim < nDim; iDim++) - points[nPoints][iDim] = IntersectionPoint[iDim]; - - nPoints++; - } - } - } - } - - // Remove double points, if any - - for( i = 0; i < nPoints; i++){ - for( j = i+1; j < nPoints; j++){ - if(points[j][0] == points[i][0] && points[j][1] == points[i][1]){ - for( k = j; k < nPoints-1; k++){ - points[k][0] = points[k+1][0]; - points[k][1] = points[k+1][1]; - } - nPoints--; - j--; - } - } - } - - // Re-order nodes - - for( i = 1; i < nPoints; i++){ // Change again reference frame - for(iDim = 0; iDim < nDim; iDim++) - points[i][iDim] -= points[0][iDim]; - - // Compute polar azimuth for each node but the first - theta[i] = atan2(points[i][1], points[i][0]); - } - - for(iDim = 0; iDim < nDim; iDim++) - points[0][iDim] = 0; - - for( i = 1; i < nPoints; i++){ - - min_theta = theta[i]; - min_theta_index = 0; - - for( j = i + 1; j < nPoints; j++){ - - if( theta[j] < min_theta ){ - min_theta = theta[j]; - min_theta_index = j; - } - } - - if( min_theta_index != 0 ){ - dtmp = theta[i]; - theta[i] = theta[min_theta_index]; - theta[min_theta_index] = dtmp; - - dtmp = points[i][0]; - points[i][0] = points[min_theta_index][0]; - points[min_theta_index][0] = dtmp; - - dtmp = points[i][1]; - points[i][1] = points[min_theta_index][1]; - points[min_theta_index][1] = dtmp; - } - } - - // compute area using cross product rule, points position are referred to the 2-dimensional, local, reference frame centered in points[0] - - Area = 0; - - if (nPoints > 2){ - for( i = 1; i < nPoints-1; i++ ){ - - // Ax*By - Area += ( points[i][0] - points[0][0] ) * ( points[i+1][1] - points[0][1] ); - - // Ay*Bx - Area -= ( points[i][1] - points[0][1] ) * ( points[i+1][0] - points[0][0] ); - } - } - - return fabs(Area)/2; -} - -void CSlidingMesh::ComputeLineIntersectionPoint( su2double* A1, su2double* A2, su2double* B1, su2double* B2, su2double* IntersectionPoint ){ - - /* --- Uses determinant rule to compute the intersection point between 2 straight segments --- */ - /* This works only for lines on a 2D plane, A1, A2 and B1, B2 are respectively the head and the tail points of each segment, - * since they're on a 2D plane they are defined by a 2-elements array containing their coordinates */ - - su2double det; - - det = (A1[0] - A2[0]) * (B1[1] - B2[1]) - (A1[1] - A2[1]) * (B1[0] - B2[0]); - - if ( det != 0.0 ){ // else there is no intersection point - IntersectionPoint[0] = ( ( A1[0]*A2[1] - A1[1]*A2[0] ) * ( B1[0] - B2[0] ) - ( B1[0]*B2[1] - B1[1]*B2[0] ) * ( A1[0] - A2[0] ) ) / det; - IntersectionPoint[1] = ( ( A1[0]*A2[1] - A1[1]*A2[0] ) * ( B1[1] - B2[1] ) - ( B1[0]*B2[1] - B1[1]*B2[0] ) * ( A1[1] - A2[1] ) ) / det; - } -} - -bool CSlidingMesh::CheckPointInsideTriangle(su2double* Point, su2double* T1, su2double* T2, su2double* T3){ - - /* --- Check whether a point "Point" lies inside or outside a triangle defined by 3 points "T1", "T2", "T3" --- */ - /* For each edge it checks on which side the point lies: - * - Computes the unit vector pointing at the internal side of the edge - * - Comutes the vector that connects the point to a point along the edge - * - If the dot product is positive it means that the point is on the internal side of the edge - * - If the check is positive for all the 3 edges, then the point lies within the triangle - */ - - unsigned short iDim, nDim, check; - - su2double vect1[2], vect2[2], r[2]; - su2double dot; - - check = 0; - nDim = 2; - - /* --- Check first edge --- */ - - dot = 0; - for(iDim = 0; iDim < nDim; iDim++){ - vect1[iDim] = T3[iDim] - T1[iDim]; // vec 1 is aligned to the edge - vect2[iDim] = T2[iDim] - T1[iDim]; // vect 2 is the vector connecting one edge point to the third triangle vertex - - r[iDim] = Point[iDim] - T1[iDim]; // Connects point to vertex T1 - - dot += vect2[iDim] * vect2[iDim]; - } - dot = sqrt(dot); - - for(iDim = 0; iDim < nDim; iDim++) - vect2[iDim] /= dot; - - dot = 0; - for(iDim = 0; iDim < nDim; iDim++) - dot += vect1[iDim] * vect2[iDim]; - - for(iDim = 0; iDim < nDim; iDim++) - vect1[iDim] = T3[iDim] - (T1[iDim] + dot * vect2[iDim]); // Computes the inward unit vector - - dot = 0; - for(iDim = 0; iDim < nDim; iDim++) // Checs that the point lies on the internal plane - dot += vect1[iDim] * r[iDim]; - - if (dot >= 0) - check++; - - /* --- Check second edge --- */ - - dot = 0; - for(iDim = 0; iDim < nDim; iDim++){ - vect1[iDim] = T1[iDim] - T2[iDim]; - vect2[iDim] = T3[iDim] - T2[iDim]; - - r[iDim] = Point[iDim] - T2[iDim]; - - dot += vect2[iDim] * vect2[iDim]; - } - dot = sqrt(dot); - - for(iDim = 0; iDim < nDim; iDim++) - vect2[iDim] /= dot; - - dot = 0; - for(iDim = 0; iDim < nDim; iDim++) - dot += vect1[iDim] * vect2[iDim]; - - for(iDim = 0; iDim < nDim; iDim++) - vect1[iDim] = T1[iDim] - (T2[iDim] + dot * vect2[iDim]); - - dot = 0; - for(iDim = 0; iDim < nDim; iDim++) - dot += vect1[iDim] * r[iDim]; - - if (dot >= 0) - check++; - - /* --- Check third edge --- */ - - dot = 0; - for(iDim = 0; iDim < nDim; iDim++){ - vect1[iDim] = T2[iDim] - T3[iDim]; - vect2[iDim] = T1[iDim] - T3[iDim]; - - r[iDim] = Point[iDim] - T3[iDim]; - - dot += vect2[iDim] * vect2[iDim]; - } - dot = sqrt(dot); - - for(iDim = 0; iDim < nDim; iDim++) - vect2[iDim] /= dot; - - dot = 0; - for(iDim = 0; iDim < nDim; iDim++) - dot += vect1[iDim] * vect2[iDim]; - - for(iDim = 0; iDim < nDim; iDim++) - vect1[iDim] = T2[iDim] - (T3[iDim] + dot * vect2[iDim]); - - dot = 0; - for(iDim = 0; iDim < nDim; iDim++) - dot += vect1[iDim] * r[iDim]; - - if (dot >= 0) - check++; - - return (check == 3); -} - -CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, - unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { } - -su2double CRadialBasisFunction::Get_RadialBasisValue(ENUM_RADIALBASIS type, const su2double radius, const su2double dist) -{ - su2double rbf = dist/radius; - - switch (type) { - - case WENDLAND_C2: - if(rbf < 1) rbf = pow(pow((1-rbf),2),2)*(4*rbf+1); // double use of pow(x,2) for optimization - else rbf = 0.0; - break; - - case GAUSSIAN: - rbf = exp(-rbf*rbf); - break; - - case THIN_PLATE_SPLINE: - if(rbf < numeric_limits::min()) rbf = 0.0; - else rbf *= rbf*log(rbf); - break; - - case MULTI_QUADRIC: - case INV_MULTI_QUADRIC: - rbf = sqrt(1.0+rbf*rbf); - if(type == INV_MULTI_QUADRIC) rbf = 1.0/rbf; - break; - } - - return rbf; -} - -void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { - - /*--- RBF options. ---*/ - const auto kindRBF = static_cast(config[donorZone]->GetKindRadialBasisFunction()); - const bool usePolynomial = config[donorZone]->GetRadialBasisFunctionPolynomialOption(); - const su2double paramRBF = config[donorZone]->GetRadialBasisFunctionParameter(); - const su2double pruneTol = config[donorZone]->GetRadialBasisFunctionPruneTol(); - - const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2; - const int nDim = donor_geometry->GetnDim(); - - const int nProcessor = size; - Buffer_Send_nVertex_Donor = new unsigned long [1]; - Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; - - /*--- Process interface patches in parallel, fetch all donor point coordinates, - * then distribute interpolation matrix computation over ranks and threads. - * To avoid repeating calls to Collect_VertexInfo we also save the global - * indices of the donor points and the mpi rank index that owns them. ---*/ - vector DonorCoordinates(nMarkerInt); - vector > DonorGlobalPoint(nMarkerInt); - vector > DonorProcessor(nMarkerInt); - vector AssignedProcessor(nMarkerInt,-1); - vector TotalWork(nProcessor,0); - - for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; ++iMarkerInt) { - - /*--- On the donor side: find the tag of the boundary sharing the interface. ---*/ - const auto mark_donor = Find_InterfaceMarker(config[donorZone], iMarkerInt+1); - - /*--- On the target side: find the tag of the boundary sharing the interface. ---*/ - const auto mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt+1); - - /*--- If the zone does not contain the interface continue to the next pair of markers. ---*/ - if(!CheckInterfaceBoundary(mark_donor,mark_target)) continue; - - unsigned long nVertexDonor = 0; - if(mark_donor != -1) nVertexDonor = donor_geometry->GetnVertex(mark_donor); - - /*--- Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor. ---*/ - Determine_ArraySize(false, mark_donor, mark_target, nVertexDonor, nDim); - - /*--- Compute total number of donor vertices. ---*/ - auto nGlobalVertexDonor = accumulate(Buffer_Receive_nVertex_Donor, - Buffer_Receive_nVertex_Donor+nProcessor, 0ul); - - /*--- Gather coordinates and global point indices. ---*/ - Buffer_Send_Coord = new su2double [ MaxLocalVertex_Donor * nDim ]; - Buffer_Send_GlobalPoint = new long [ MaxLocalVertex_Donor ]; - Buffer_Receive_Coord = new su2double [ nProcessor * MaxLocalVertex_Donor * nDim ]; - Buffer_Receive_GlobalPoint = new long [ nProcessor * MaxLocalVertex_Donor ]; - - Collect_VertexInfo(false, mark_donor, mark_target, nVertexDonor, nDim); - - /*--- Compresses the gathered donor point information to simplify computations. ---*/ - auto& DonorCoord = DonorCoordinates[iMarkerInt]; - auto& DonorPoint = DonorGlobalPoint[iMarkerInt]; - auto& DonorProc = DonorProcessor[iMarkerInt]; - DonorCoord.resize(nGlobalVertexDonor, nDim); - DonorPoint.resize(nGlobalVertexDonor); - DonorProc.resize(nGlobalVertexDonor); - - auto iCount = 0ul; - for (int iProcessor = 0; iProcessor < nProcessor; ++iProcessor) { - auto offset = iProcessor * MaxLocalVertex_Donor; - for (auto iVertex = 0ul; iVertex < Buffer_Receive_nVertex_Donor[iProcessor]; ++iVertex) { - for (int iDim = 0; iDim < nDim; ++iDim) - DonorCoord(iCount,iDim) = Buffer_Receive_Coord[(offset+iVertex)*nDim + iDim]; - DonorPoint[iCount] = Buffer_Receive_GlobalPoint[offset+iVertex]; - DonorProc[iCount] = iProcessor; - ++iCount; - } - } - assert((iCount == nGlobalVertexDonor) && "Global donor point count mismatch."); - - delete[] Buffer_Send_Coord; - delete[] Buffer_Send_GlobalPoint; - delete[] Buffer_Receive_Coord; - delete[] Buffer_Receive_GlobalPoint; - - /*--- Static work scheduling over ranks based on which one has less work currently. ---*/ - int iProcessor = 0; - for (int i = 1; i < nProcessor; ++i) - if (TotalWork[i] < TotalWork[iProcessor]) iProcessor = i; - - TotalWork[iProcessor] += pow(nGlobalVertexDonor,3); // based on matrix inversion. - - AssignedProcessor[iMarkerInt] = iProcessor; - - } - - /*--- Compute the interpolation matrices for each patch of coordinates - * assigned to the rank. Subdivide work further by threads. ---*/ - vector nPolynomialVec(nMarkerInt,-1); - vector > keepPolynomialRowVec(nMarkerInt, vector(nDim,1)); - vector CinvTrucVec(nMarkerInt); - - SU2_OMP_PARALLEL_(for schedule(dynamic,1)) - for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; ++iMarkerInt) { - if (rank == AssignedProcessor[iMarkerInt]) { - ComputeGeneratorMatrix(kindRBF, usePolynomial, paramRBF, - DonorCoordinates[iMarkerInt], nPolynomialVec[iMarkerInt], - keepPolynomialRowVec[iMarkerInt], CinvTrucVec[iMarkerInt]); - } - } - - /*--- Final loop over interface markers to compute the interpolation coefficients. ---*/ - - for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; iMarkerInt++) { - - /*--- Identify the rank that computed the interpolation matrix for this marker. ---*/ - const int iProcessor = AssignedProcessor[iMarkerInt]; - /*--- If no processor was assigned to work, the zone does not contain the interface. ---*/ - if (iProcessor < 0) continue; - - /*--- Setup target information. ---*/ - const int mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt+1); - unsigned long nVertexTarget = 0; - if(mark_target != -1) nVertexTarget = target_geometry->GetnVertex(mark_target); - - /*--- Set references to donor information. ---*/ - auto& DonorCoord = DonorCoordinates[iMarkerInt]; - auto& DonorPoint = DonorGlobalPoint[iMarkerInt]; - auto& DonorProc = DonorProcessor[iMarkerInt]; - - auto& C_inv_trunc = CinvTrucVec[iMarkerInt]; - auto& nPolynomial = nPolynomialVec[iMarkerInt]; - auto& keepPolynomialRow = keepPolynomialRowVec[iMarkerInt]; - - const auto nGlobalVertexDonor = DonorCoord.rows(); - -#ifdef HAVE_MPI - /*--- For simplicity, broadcast small information about the interpolation matrix. ---*/ - SU2_MPI::Bcast(&nPolynomial, 1, MPI_INT, iProcessor, MPI_COMM_WORLD); - SU2_MPI::Bcast(keepPolynomialRow.data(), nDim, MPI_INT, iProcessor, MPI_COMM_WORLD); - - /*--- Send C_inv_trunc only to the ranks that need it (those with target points), - * partial broadcast. MPI wrapper not used due to passive double. ---*/ - vector allNumVertex(nProcessor); - SU2_MPI::Allgather(&nVertexTarget, 1, MPI_UNSIGNED_LONG, - allNumVertex.data(), 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - - if (rank == iProcessor) { - for (int jProcessor = 0; jProcessor < nProcessor; ++jProcessor) - if ((jProcessor != iProcessor) && (allNumVertex[jProcessor] != 0)) - MPI_Send(C_inv_trunc.data(), C_inv_trunc.size(), - MPI_DOUBLE, jProcessor, 0, MPI_COMM_WORLD); - } - else if (nVertexTarget != 0) { - C_inv_trunc.resize(1+nPolynomial+nGlobalVertexDonor, nGlobalVertexDonor); - MPI_Recv(C_inv_trunc.data(), C_inv_trunc.size(), MPI_DOUBLE, - iProcessor, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); - } -#endif - - /*--- Compute H (interpolation) matrix, distributing - * target points over the threads in the rank. ---*/ - SU2_OMP_PARALLEL - { - su2passivevector coeff_vec(nGlobalVertexDonor); - - SU2_OMP_FOR_DYN(roundUpDiv(nVertexTarget, 2*omp_get_max_threads())) - for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget++) { - - auto target_vertex = target_geometry->vertex[mark_target][iVertexTarget]; - const auto point_target = target_vertex->GetNode(); - - /*--- If not domain point move to next. ---*/ - if (!target_geometry->node[point_target]->GetDomain()) continue; - - const su2double* coord_i = target_geometry->node[point_target]->GetCoord(); - - /*--- Multiply target vector by C_inv_trunc to obtain the interpolation coefficients. - * The target vector is not stored, we consume its entries immediately to avoid - * strided access to C_inv_trunc (as it is row-major). ---*/ - - /*--- Polynominal part: ---*/ - if (usePolynomial) { - /*--- Constant term. ---*/ - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) - coeff_vec(j) = C_inv_trunc(0,j); - - /*--- Linear terms. ---*/ - for (int iDim = 0, idx = 1; iDim < nDim; ++iDim) { - /*--- Of which one may have been excluded. ---*/ - if (!keepPolynomialRow[iDim]) continue; - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) - coeff_vec(j) += SU2_TYPE::GetValue(coord_i[iDim]) * C_inv_trunc(idx,j); - idx += 1; - } - } - else { - /*--- Initialize vector to zero. ---*/ - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) coeff_vec(j) = 0.0; - } - - /*--- RBF terms: ---*/ - for (auto iVertexDonor = 0ul; iVertexDonor < nGlobalVertexDonor; ++iVertexDonor) { - auto w_ij = SU2_TYPE::GetValue(Get_RadialBasisValue(kindRBF, paramRBF, - PointsDistance(nDim, coord_i, DonorCoord[iVertexDonor]))); - - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) - coeff_vec(j) += w_ij * C_inv_trunc(1+nPolynomial+iVertexDonor, j); - } - - /*--- Prune small coefficients. ---*/ - auto nnz = PruneSmallCoefficients(SU2_TYPE::GetValue(pruneTol), coeff_vec); - - /*--- Allocate and set donor information for this target point. ---*/ - target_vertex->SetnDonorPoints(nnz); - target_vertex->Allocate_DonorInfo(); - - for (unsigned long iVertex = 0, iSet = 0; iVertex < nGlobalVertexDonor; ++iVertex) { - if (fabs(coeff_vec(iVertex)) > 0.0) { - target_vertex->SetInterpDonorProcessor(iSet, DonorProc[iVertex]); - target_vertex->SetInterpDonorPoint(iSet, DonorPoint[iVertex]); - target_vertex->SetDonorCoeff(iSet, coeff_vec(iVertex)); - ++iSet; - } - } - - } // end target vertex loop - } // end SU2_OMP_PARALLEL - - /*--- Delete global data that will no longer be used. ---*/ - DonorCoord.resize(0,0); - vector().swap(DonorPoint); - vector().swap(DonorProc); - C_inv_trunc.resize(0,0); - - } // end loop over interface markers - - delete[] Buffer_Send_nVertex_Donor; - delete[] Buffer_Receive_nVertex_Donor; - -} - -void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, - su2double radius, const su2activematrix& coords, int& nPolynomial, - vector& keepPolynomialRow, su2passivematrix& C_inv_trunc) const { - - const su2double interfaceCoordTol = 1e6 * numeric_limits::epsilon(); - - const auto nVertexDonor = coords.rows(); - const int nDim = coords.cols(); - - /*--- Populate interpolation kernel. ---*/ - CSymmetricMatrix global_M(nVertexDonor); - - for (auto iVertex = 0ul; iVertex < nVertexDonor; ++iVertex) - for (auto jVertex = iVertex; jVertex < nVertexDonor; ++jVertex) - global_M(iVertex, jVertex) = SU2_TYPE::GetValue(Get_RadialBasisValue(type, radius, - PointsDistance(nDim, coords[iVertex], coords[jVertex]))); - - /*--- Invert M matrix (operation is in-place). ---*/ - const bool kernelIsSPD = (type==WENDLAND_C2) || (type==GAUSSIAN) || (type==INV_MULTI_QUADRIC); - global_M.Invert(kernelIsSPD); - - /*--- Compute C_inv_trunc. ---*/ - if (usePolynomial) { - - /*--- Fill P matrix (P for points, with an extra top row of ones). ---*/ - su2passivematrix P(1+nDim, nVertexDonor); - - for (auto iVertex = 0ul; iVertex < nVertexDonor; iVertex++) { - P(0, iVertex) = 1.0; - for (int iDim = 0; iDim < nDim; ++iDim) - P(1+iDim, iVertex) = SU2_TYPE::GetValue(coords(iVertex, iDim)); - } - - /*--- Check if points lie on a plane and remove one coordinate from P if so. ---*/ - nPolynomial = CheckPolynomialTerms(interfaceCoordTol, keepPolynomialRow, P); - - /*--- Compute Mp = (P * M^-1 * P^T)^-1 ---*/ - CSymmetricMatrix Mp(nPolynomial+1); - - su2passivematrix tmp; - global_M.MatMatMult('R', P, tmp); // tmp = P * M^-1 - - for (int i = 0; i <= nPolynomial; ++i) // Mp = tmp * P - for (int j = i; j <= nPolynomial; ++j) { - Mp(i,j) = 0.0; - for (auto k = 0ul; k < nVertexDonor; ++k) Mp(i,j) += tmp(i,k) * P(j,k); - } - Mp.Invert(false); // Mp = Mp^-1 - - /*--- Compute M_p * P * M^-1, the top part of C_inv_trunc. ---*/ - Mp.MatMatMult('L', P, tmp); - su2passivematrix C_inv_top; - global_M.MatMatMult('R', tmp, C_inv_top); - - /*--- Compute tmp = (I - P^T * M_p * P * M^-1), part of the bottom part of - C_inv_trunc. Note that most of the product is known from the top part. ---*/ - tmp.resize(nVertexDonor, nVertexDonor); - - for (auto i = 0ul; i < nVertexDonor; ++i) { - for (auto j = 0ul; j < nVertexDonor; ++j) { - tmp(i,j) = 0.0; - for (int k = 0; k <= nPolynomial; ++k) tmp(i,j) -= P(k,i) * C_inv_top(k,j); - } - tmp(i,i) += 1.0; // identity part - } - - /*--- Compute M^-1 * (I - P^T * M_p * P * M^-1), finalize bottom of C_inv_trunc. ---*/ - global_M.MatMatMult('L', tmp, C_inv_trunc); - - /*--- Merge top and bottom of C_inv_trunc. ---*/ - tmp = move(C_inv_trunc); - C_inv_trunc.resize(1+nPolynomial+nVertexDonor, nVertexDonor); - memcpy(C_inv_trunc[0], C_inv_top.data(), C_inv_top.size()*sizeof(passivedouble)); - memcpy(C_inv_trunc[1+nPolynomial], tmp.data(), tmp.size()*sizeof(passivedouble)); - } - else { - /*--- No polynomial term used in the interpolation, C_inv_trunc = M^-1. ---*/ - - C_inv_trunc.resize(nVertexDonor, nVertexDonor); - for (auto i = 0ul; i < nVertexDonor; ++i) - for (auto j = 0ul; j < nVertexDonor; ++j) - C_inv_trunc(i,j) = global_M(i,j); - - } // end usePolynomial - -} - -int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, - su2passivematrix &P) const { - const int m = P.rows(); - const int n = P.cols(); - - /*--- The first row of P is all ones and we do not care about it for this analysis. ---*/ - const int n_rows = m-1; - keep_row.resize(n_rows); - - /*--- By default assume points are not on a plane (all rows kept). ---*/ - int n_polynomial = n_rows; - for (int i = 0; i < n_rows; ++i) keep_row[i] = 1; - - /*--- Fit a plane through the points in P. ---*/ - - /*--- Compute P times its transpose and invert. ---*/ - CSymmetricMatrix PPT(n_rows); - - for (int i = 0; i < n_rows; ++i) - for (int j = i; j < n_rows; ++j) { - PPT(i,j) = 0.0; - for (int k = 0; k < n; ++k) PPT(i,j) += P(i+1,k) * P(j+1,k); - } - PPT.Invert(true); - - /*--- RHS for the least squares fit (vector of ones times P). ---*/ - vector coeff(n_rows,0.0); - - for (int i = 0; i < n_rows; ++i) - for (int j = 0; j < n; ++j) - coeff[i] += P(i+1,j); - - /*--- Multiply the RHS by the inverse thus obtaining the coefficients. ---*/ - PPT.MatVecMult(coeff.data()); - - /*--- Determine the maximum deviation of the points from the fitted plane. ---*/ - passivedouble max_diff = 0.0; - - for (int j = 0; j < n; ++j) - { - passivedouble sum = 0.0; - for (int i = 0; i < n_rows; ++i) sum += coeff[i] * P(i+1,j); - - /*--- 1.0 is the arbitrary constant we are assuming when fitting - the plane, i.e. the vector of ones used to generate the RHS. ---*/ - max_diff = max(abs(1.0-sum), max_diff); - } - - /*--- If points lie on plane remove row associated with the maximum coefficient. ---*/ - if (max_diff < max_diff_tol) - { - /*--- Find the max coeff and mark the corresponding row for removal. ---*/ - int remove_row = 0; - for (int i = 1; i < n_rows; ++i) - if (abs(coeff[i]) > abs(coeff[remove_row])) - remove_row = i; - - /*--- Mark row as removed and adjust number of polynomial terms. ---*/ - n_polynomial = n_rows-1; - keep_row[remove_row] = 0; - - /*--- Truncated P by shifting rows "up". ---*/ - for (auto i = remove_row+1; i < m-1; ++i) - for (int j = 0; j < n; ++j) - P(i,j) = P(i+1,j); - } - - return n_polynomial; -} - -int CRadialBasisFunction::PruneSmallCoefficients(passivedouble tolerance, - su2passivevector& coeffs) const { - - /*--- Determine the pruning threshold. ---*/ - passivedouble thresh = 0.0; - for (auto i = 0ul; i < coeffs.size(); ++i) - thresh = max(thresh, fabs(coeffs(i))); - thresh *= tolerance; - - /*--- Prune and count non-zeros. ---*/ - int numNonZeros = 0; - passivedouble coeffSum = 0.0; - for (auto i = 0ul; i < coeffs.size(); ++i) { - if (fabs(coeffs(i)) > thresh) { - coeffSum += coeffs(i); - ++numNonZeros; - } - else coeffs(i) = 0.0; - } - - /*--- Correct remaining coefficients, sum must be 1 for conservation. ---*/ - passivedouble correction = 1.0 / coeffSum; - for (auto i = 0ul; i < coeffs.size(); ++i) coeffs(i) *= correction; - - return numNonZeros; -} - -void CSymmetricMatrix::Initialize(int N) -{ - sz = N; - val_vec.resize(sz*sz); - initialized = true; -} - -void CSymmetricMatrix::CholeskyDecompose() -{ -#ifndef HAVE_LAPACK - assert(initialized && "Matrix not initialized."); - - for (int j = 0; j < sz; ++j) { - passivedouble sum = 0.0; - for (int k = 0; k < j; ++k) sum -= pow(Get(j,k), 2); - sum += Get(j,j); - assert(sum > 0.0 && "LLT failed, matrix is not SPD."); - Set(j, j, sqrt(sum)); - - for (int i = j+1; i < sz; ++i) { - passivedouble sum = 0.0; - for (int k = 0; k < j; ++k) sum -= Get(i,k) * Get(j,k); - sum += Get(i,j); - Set(i, j, sum / Get(j,j)); - } - } - decomposed = CHOLESKY; -#endif -} - -void CSymmetricMatrix::LUDecompose() -{ -#ifndef HAVE_LAPACK - assert(initialized && "Matrix not initialized."); - - /*--- Copy matrix values to LU matrix, init permutation vec. ---*/ - decomp_vec.resize(sz*sz); - perm_vec.resize(sz); - for (int i = 0; i < sz; ++i) { - perm_vec[i] = i; - for (int j = i; j < sz; ++j) decomp(j,i) = decomp(i,j) = Get(i,j); - } - - /*--- Decompose LU matrix. ---*/ - for (int j = 0; j < sz-1; ++j) { - /*--- Search for maximum pivot and interchange rows. ---*/ - passivedouble pivot = decomp(j,j); - int pivot_idx = j; - for (int i = j+1; i < sz; ++i) - if (abs(decomp(i,j)) > abs(pivot)) { - pivot = decomp(i,j); - pivot_idx = i; - } - - if (pivot_idx != j) { - swap(perm_vec[j], perm_vec[pivot_idx]); - for (int k = 0; k < sz; ++k) - swap(decomp(j,k), decomp(pivot_idx,k)); - } - - /*--- Perform elimination. ---*/ - for (int k = j+1; k < sz; ++k) decomp(k,j) /= pivot; - - for (int k = j+1; k < sz; ++k) - for (int i = j+1; i < sz; ++i) - decomp(i,k) -= decomp(j,k)*decomp(i,j); - } - - decomposed = LU; -#endif -} - -void CSymmetricMatrix::CalcInv() -{ -#ifndef HAVE_LAPACK - assert(initialized && "Matrix not initialized."); - - /*--- Decompose matrix if not done yet. ---*/ - if (decomposed == NONE) { LUDecompose(); } - - /*--- Compute inverse from decomposed matrices. ---*/ - switch (decomposed) { - - case CHOLESKY: - { - /*--- Initialize inverse matrix. ---*/ - vector inv(sz*sz, 0.0); - - /*--- Compute L inverse. ---*/ - /*--- Solve smaller and smaller systems. ---*/ - for (int j = 0; j < sz; ++j) { - /*--- Forward substitution. ---*/ - inv[IdxSym(j,j)] = 1.0 / Get(j,j); - - for (int i = j+1; i < sz; ++i) { - passivedouble sum = 0.0; - for (int k = j; k < i; ++k) sum -= Get(i,k) * inv[IdxSym(k,j)]; - inv[IdxSym(i,j)] = sum / Get(i,i); - } - } // L inverse in inv - - /*--- Multiply inversed matrices overwrite val_vec. ---*/ - for (int j = 0; j < sz; ++j) - for (int i = j; i < sz; ++i) { - passivedouble sum = 0.0; - for (int k = i; k < sz; ++k) sum += inv[IdxSym(k,i)] * inv[IdxSym(k,j)]; - Set(i, j, sum); - } - - break; - } - - case LU: - { - /*--- Alias val_vec. ---*/ - vector& inv = val_vec; - - /*--- Invert L and U matrices in place. ---*/ - for (int j = 0; j < sz; ++j) { - inv[IdxFull(j,j)] = 1.0 / decomp(j,j); - - for (int i = j+1; i < sz; ++i) { - inv[IdxFull(i,j)] = -decomp(i,j); - inv[IdxFull(j,i)] = -decomp(j,i) * inv[IdxFull(j,j)]; - - for (int k = j+1; k < i; ++k) { - inv[IdxFull(i,j)] -= decomp(i,k) * inv[IdxFull(k,j)]; - inv[IdxFull(j,i)] -= decomp(k,i) * inv[IdxFull(j,k)]; - } - if (j+1 <= i) inv[IdxFull(j,i)] /= decomp(i,i); - } - } - // inverses in val_vec - - /*--- Multiply U_inv by L_inv, overwrite decomp_vec. ---*/ - for (int i = 0; i < sz; ++i) - for (int j = 0; j < sz; ++j) { - decomp(i,j) = 0.0; - for (int k = max(i,j); k < sz; ++k) - decomp(i,j) += inv[IdxFull(i,k)] * ((k==j)? 1.0 : inv[IdxFull(k,j)]); - } - - /*--- Permute multiplied matrix to recover A_inv, overwrite val_vec. ---*/ - for (int j = 0; j < sz; ++j) { - int k = perm_vec[j]; - for (int i = k; i < sz; ++i) Set(i, k, decomp(i,j)); - } - - /*--- Decomposition no longer needed. ---*/ - vector().swap(decomp_vec); - - break; - } - default: assert(false && "Default (LU) decomposition failed."); - } - - decomposed = NONE; -#endif -} - -void CSymmetricMatrix::CalcInv_sytri() -{ -#ifdef HAVE_LAPACK - char uplo = 'L'; - int info; - perm_vec.resize(sz); // ipiv array - - /*--- Query the optimum work size. ---*/ - int query = -1; passivedouble tmp; - dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), &tmp, &query, &info); - query = static_cast(tmp); - decomp_vec.resize(query); // work array - - /*--- Factorize and invert. ---*/ - dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &query, &info); - if (info!=0) SU2_MPI::Error("LDLT factorization failed.", CURRENT_FUNCTION); - dsytri_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &info); - if (info!=0) SU2_MPI::Error("Inversion with LDLT factorization failed.", CURRENT_FUNCTION); - - decomposed = NONE; -#endif -} - -void CSymmetricMatrix::CalcInv_potri() -{ -#ifdef HAVE_LAPACK - char uplo = 'L'; - int info; - - dpotrf_(&uplo, &sz, val_vec.data(), &sz, &info); - if (info!=0) SU2_MPI::Error("LLT factorization failed.", CURRENT_FUNCTION); - dpotri_(&uplo, &sz, val_vec.data(), &sz, &info); - if (info!=0) SU2_MPI::Error("Inversion with LLT factorization failed.", CURRENT_FUNCTION); - - decomposed = NONE; -#endif -} - -void CSymmetricMatrix::Invert(const bool is_spd) -{ -#ifdef HAVE_LAPACK - if(is_spd) CalcInv_potri(); - else CalcInv_sytri(); -#else - if(!is_spd) LUDecompose(); - else CholeskyDecompose(); - CalcInv(); -#endif -} - -void CSymmetricMatrix::MatVecMult(passivedouble *v) const -{ - passivedouble *tmp_res = new passivedouble [sz]; - - for (int i=0; i Date: Wed, 18 Mar 2020 15:34:26 +0000 Subject: [PATCH 030/112] cleanup interface preprocessing --- .../interface_interpolation/CInterpolator.hpp | 48 +- .../interface_interpolation/CInterpolator.cpp | 4 +- .../CIsoparametric.cpp | 34 +- .../CRadialBasisFunction.cpp | 16 + .../interface_interpolation/CSlidingMesh.cpp | 72 +-- SU2_CFD/src/drivers/CDriver.cpp | 411 +++++++----------- SU2_CFD/src/drivers/CMultizoneDriver.cpp | 8 +- 7 files changed, 271 insertions(+), 322 deletions(-) diff --git a/Common/include/interface_interpolation/CInterpolator.hpp b/Common/include/interface_interpolation/CInterpolator.hpp index 5144e37ce2bc..74e4840d5d02 100644 --- a/Common/include/interface_interpolation/CInterpolator.hpp +++ b/Common/include/interface_interpolation/CInterpolator.hpp @@ -45,11 +45,11 @@ class CInterpolator { const unsigned targetZone; /*!< \brief Index of target zone. */ unsigned long - MaxLocalVertex_Donor, /*!< \brief Maximum vertices per processor*/ - nGlobalFace_Donor, /*!< \brief Number of global donor faces*/ - nGlobalFaceNodes_Donor, /*!< \brief Number of global donor face nodes*/ - MaxFace_Donor, /*!< \brief Maximum faces per processor*/ - MaxFaceNodes_Donor; /*!< \brief Maximum nodes associated with faces per processor*/ + MaxLocalVertex_Donor, /*!< \brief Maximum vertices per processor*/ + nGlobalFace_Donor, /*!< \brief Number of global donor faces*/ + nGlobalFaceNodes_Donor, /*!< \brief Number of global donor face nodes*/ + MaxFace_Donor, /*!< \brief Maximum faces per processor*/ + MaxFaceNodes_Donor; /*!< \brief Maximum nodes associated with faces per processor*/ unsigned long *Buffer_Receive_nVertex_Donor, /*!< \brief Buffer to store the number of vertices per processor on the Donor domain */ @@ -68,23 +68,25 @@ class CInterpolator { long *Buffer_Send_GlobalPoint, /*!< \brief Buffer to send global point indices*/ *Buffer_Receive_GlobalPoint; /*!< \brief Buffer to receive global point indices*/ - su2double *Buffer_Send_Coord, /*!< \brief Buffer to send coordinate values*/ - *Buffer_Send_Normal, /*!< \brief Buffer to send normal vector values */ - *Buffer_Receive_Coord, /*!< \brief Buffer to receive coordinate values*/ - *Buffer_Receive_Normal; /*!< \brief Buffer to receive normal vector values*/ + su2double *Buffer_Send_Coord, /*!< \brief Buffer to send coordinate values*/ + *Buffer_Send_Normal, /*!< \brief Buffer to send normal vector values */ + *Buffer_Receive_Coord, /*!< \brief Buffer to receive coordinate values*/ + *Buffer_Receive_Normal; /*!< \brief Buffer to receive normal vector values*/ - unsigned long *Receive_GlobalPoint, /*!< \brief Buffer to receive Global point indexes*/ - *Buffer_Receive_nLinkedNodes, /*!< \brief Buffer to receive the number of edges connected to each node*/ - *Buffer_Receive_LinkedNodes, /*!< \brief Buffer to receive the list of notes connected to the nodes through an edge*/ - *Buffer_Receive_StartLinkedNodes, /*!< \brief Buffer to receive the index of the Receive_LinkedNodes buffer where corresponding list of linked nodes begins */ - *Buffer_Receive_Proc; /*!< \brief Buffer to receive the thread that owns the node*/ + unsigned long + *Receive_GlobalPoint, /*!< \brief Buffer to receive Global point indexes*/ + *Buffer_Receive_nLinkedNodes, /*!< \brief Buffer to receive the number of edges connected to each node*/ + *Buffer_Receive_LinkedNodes, /*!< \brief Buffer to receive the list of notes connected to the nodes through an edge*/ + *Buffer_Receive_StartLinkedNodes, /*!< \brief Buffer to receive the index of the Receive_LinkedNodes buffer where corresponding list of linked nodes begins */ + *Buffer_Receive_Proc; /*!< \brief Buffer to receive the thread that owns the node*/ - unsigned long nGlobalVertex_Target, /*!< \brief Global number of vertex of the target boundary*/ - nLocalVertex_Target, /*!< \brief Number of vertex of the target boundary owned by the thread*/ - nGlobalVertex_Donor, /*!< \brief Global number of vertex of the donor boundary*/ - nLocalVertex_Donor, /*!< \brief Number of vertex of the donor boundary owned by the thread*/ - nGlobalVertex, /*!< \brief Dummy variable to temporarily store the global number of vertex of a boundary*/ - nLocalLinkedNodes; /*!< \brief Dummy variable to temporarily store the number of vertex of a boundary*/ + unsigned long + nGlobalVertex_Target, /*!< \brief Global number of vertex of the target boundary*/ + nLocalVertex_Target, /*!< \brief Number of vertex of the target boundary owned by the thread*/ + nGlobalVertex_Donor, /*!< \brief Global number of vertex of the donor boundary*/ + nLocalVertex_Donor, /*!< \brief Number of vertex of the donor boundary owned by the thread*/ + nGlobalVertex, /*!< \brief Dummy variable to temporarily store the global number of vertex of a boundary*/ + nLocalLinkedNodes; /*!< \brief Dummy variable to temporarily store the number of vertex of a boundary*/ CGeometry**** const Geometry; /*! \brief Vector which stores n zones of geometry. */ CGeometry* const donor_geometry; /*! \brief Donor geometry. */ @@ -117,21 +119,21 @@ class CInterpolator { */ virtual void Set_TransferCoeff(CConfig **config) = 0; -protected: /*! * \brief Find the index of the interface marker shared by that zone * \param[in] config - Definition of the particular problem. * \param[in] val_marker_interface - Interface tag. */ - int Find_InterfaceMarker(const CConfig *config, unsigned short val_marker_interface) const; + static int Find_InterfaceMarker(const CConfig *config, unsigned short val_marker_interface); /*! * \brief Check whether an interface should be processed or not, i.e. if it is part of the zones. * \param[in] val_markDonor - Marker tag from donor zone. * \param[in] val_markTarget - Marker tag from target zone. */ - bool CheckInterfaceBoundary(int val_markDonor, int val_markTarget) const; + static bool CheckInterfaceBoundary(int val_markDonor, int val_markTarget); +protected: /*! * \brief Recontstruct the boundary connectivity from parallel partitioning and broadcasts it to all threads * \param[in] val_zone - index of the zone diff --git a/Common/src/interface_interpolation/CInterpolator.cpp b/Common/src/interface_interpolation/CInterpolator.cpp index 354336c35ade..05d54a74fbdf 100644 --- a/Common/src/interface_interpolation/CInterpolator.cpp +++ b/Common/src/interface_interpolation/CInterpolator.cpp @@ -40,7 +40,7 @@ CInterpolator::CInterpolator(CGeometry ****geometry_container, CConfig **config, target_geometry(geometry_container[jZone][INST_0][MESH_0]) { } -int CInterpolator::Find_InterfaceMarker(const CConfig *config, unsigned short val_marker_interface) const { +int CInterpolator::Find_InterfaceMarker(const CConfig *config, unsigned short val_marker_interface) { for (unsigned short iMarker = 0; iMarker < config->GetnMarker_All(); iMarker++) { /*--- If the tag GetMarker_All_ZoneInterface(iMarker) equals the interface we are looking for. ---*/ @@ -49,7 +49,7 @@ int CInterpolator::Find_InterfaceMarker(const CConfig *config, unsigned short va return -1; } -bool CInterpolator::CheckInterfaceBoundary(int markDonor, int markTarget) const { +bool CInterpolator::CheckInterfaceBoundary(int markDonor, int markTarget) { /*--- Determine whether the boundary is not on the rank because of * the partition or because it is not part of the zone. ---*/ diff --git a/Common/src/interface_interpolation/CIsoparametric.cpp b/Common/src/interface_interpolation/CIsoparametric.cpp index 0d42834b6a29..ef3468b86fb3 100644 --- a/Common/src/interface_interpolation/CIsoparametric.cpp +++ b/Common/src/interface_interpolation/CIsoparametric.cpp @@ -77,6 +77,14 @@ void CIsoparametric::Set_TransferCoeff(CConfig **config) { Coord = new su2double[nDim]; Normal = new su2double[nDim]; + Buffer_Send_nVertex_Donor = new unsigned long [1]; + Buffer_Send_nFace_Donor = new unsigned long [1]; + Buffer_Send_nFaceNodes_Donor = new unsigned long [1]; + + Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; + Buffer_Receive_nFace_Donor = new unsigned long [nProcessor]; + Buffer_Receive_nFaceNodes_Donor = new unsigned long [nProcessor]; + nMarkerInt = (config[donorZone]->GetMarker_n_ZoneInterface())/2; /*--- For the number of markers on the interface... ---*/ @@ -106,14 +114,6 @@ void CIsoparametric::Set_TransferCoeff(CConfig **config) { else nVertexTarget = 0; - Buffer_Send_nVertex_Donor = new unsigned long [1]; - Buffer_Send_nFace_Donor = new unsigned long [1]; - Buffer_Send_nFaceNodes_Donor = new unsigned long [1]; - - Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; - Buffer_Receive_nFace_Donor = new unsigned long [nProcessor]; - Buffer_Receive_nFaceNodes_Donor = new unsigned long [nProcessor]; - /* Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor */ Determine_ArraySize(true, markDonor, markTarget, nVertexDonor, nDim); @@ -332,21 +332,12 @@ void CIsoparametric::Set_TransferCoeff(CConfig **config) { for (iDonor=0; iDonorvertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor,storeGlobal[iDonor]); - //cout <vertex[markTarget][iVertex]->SetDonorCoeff(iDonor,storeCoeff[iDonor]); target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]); } } - delete[] Buffer_Send_nVertex_Donor; - delete[] Buffer_Send_nFace_Donor; - delete[] Buffer_Send_nFaceNodes_Donor; - - delete[] Buffer_Receive_nVertex_Donor; - delete[] Buffer_Receive_nFace_Donor; - delete[] Buffer_Receive_nFaceNodes_Donor; - delete[] Buffer_Send_Coord; delete[] Buffer_Send_Normal; delete[] Buffer_Send_GlobalPoint; @@ -363,6 +354,15 @@ void CIsoparametric::Set_TransferCoeff(CConfig **config) { delete[] Buffer_Receive_FaceNodes; delete[] Buffer_Receive_FaceProc; } + + delete[] Buffer_Send_nVertex_Donor; + delete[] Buffer_Send_nFace_Donor; + delete[] Buffer_Send_nFaceNodes_Donor; + + delete[] Buffer_Receive_nVertex_Donor; + delete[] Buffer_Receive_nFace_Donor; + delete[] Buffer_Receive_nFaceNodes_Donor; + delete [] Coord; delete [] Normal; diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp index c9ba5b5e9294..fd5504ed8c8b 100644 --- a/Common/src/interface_interpolation/CRadialBasisFunction.cpp +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -29,6 +29,22 @@ #include "../../include/CConfig.hpp" #include "../../include/geometry/CGeometry.hpp" +#if defined(HAVE_MKL) +#include "mkl.h" +#ifndef HAVE_LAPACK +#define HAVE_LAPACK +#endif +#elif defined(HAVE_LAPACK) +/*--- Lapack / Blas routines used in RBF interpolation. ---*/ +extern "C" void dsptrf_(char*, int*, passivedouble*, int*, int*); +extern "C" void dsptri_(char*, int*, passivedouble*, int*, passivedouble*, int*); +extern "C" void dsytrf_(char*, int*, passivedouble*, int*, int*, passivedouble*, int*, int*); +extern "C" void dsytri_(char*, int*, passivedouble*, int*, int*, passivedouble*, int*); +extern "C" void dpotrf_(char*, int*, passivedouble*, int*, int*); +extern "C" void dpotri_(char*, int*, passivedouble*, int*, int*); +extern "C" void dsymm_(char*, char*, int*, int*, passivedouble*, passivedouble*, int*, + passivedouble*, int*, passivedouble*, passivedouble*, int*); +#endif CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { diff --git a/Common/src/interface_interpolation/CSlidingMesh.cpp b/Common/src/interface_interpolation/CSlidingMesh.cpp index 41018ddda0be..62084eb55afd 100644 --- a/Common/src/interface_interpolation/CSlidingMesh.cpp +++ b/Common/src/interface_interpolation/CSlidingMesh.cpp @@ -244,8 +244,10 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { dTMP = 0; for(iDim = 0; iDim < nDim; iDim++){ - target_iMidEdge_point[iDim] = ( TargetPoint_Coord[ nDim * target_segment[0] + iDim ] + target_geometry->node[ target_iPoint ]->GetCoord(iDim) ) / 2; - target_jMidEdge_point[iDim] = ( TargetPoint_Coord[ nDim * target_segment[1] + iDim ] + target_geometry->node[ target_iPoint ]->GetCoord(iDim) ) / 2; + target_iMidEdge_point[iDim] = ( TargetPoint_Coord[ nDim * target_segment[0] + iDim ] + + target_geometry->node[ target_iPoint ]->GetCoord(iDim) ) / 2; + target_jMidEdge_point[iDim] = ( TargetPoint_Coord[ nDim * target_segment[1] + iDim ] + + target_geometry->node[ target_iPoint ]->GetCoord(iDim) ) / 2; Direction[iDim] = target_jMidEdge_point[iDim] - target_iMidEdge_point[iDim]; dTMP += Direction[iDim] * Direction[iDim]; @@ -288,11 +290,14 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { } for(iDim = 0; iDim < nDim; iDim++){ - donor_iMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_forward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; - donor_jMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_backward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; + donor_iMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_forward_point * nDim + iDim] + + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; + donor_jMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_backward_point * nDim + iDim] + + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; } - LineIntersectionLength = ComputeLineIntersectionLength(target_iMidEdge_point, target_jMidEdge_point, donor_iMidEdge_point, donor_jMidEdge_point, Direction); + LineIntersectionLength = ComputeLineIntersectionLength(target_iMidEdge_point, target_jMidEdge_point, + donor_iMidEdge_point, donor_jMidEdge_point, Direction); if ( LineIntersectionLength == 0.0 ){ check = true; @@ -368,11 +373,14 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { } for(iDim = 0; iDim < nDim; iDim++){ - donor_iMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_forward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; - donor_jMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_backward_point * nDim + iDim] + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; + donor_iMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_forward_point * nDim + iDim] + + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; + donor_jMidEdge_point[iDim] = ( DonorPoint_Coord[ donor_backward_point * nDim + iDim] + + DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; } - LineIntersectionLength = ComputeLineIntersectionLength(target_iMidEdge_point, target_jMidEdge_point, donor_iMidEdge_point, donor_jMidEdge_point, Direction); + LineIntersectionLength = ComputeLineIntersectionLength(target_iMidEdge_point, target_jMidEdge_point, + donor_iMidEdge_point, donor_jMidEdge_point, Direction); if ( LineIntersectionLength == 0.0 ){ check = true; @@ -476,7 +484,8 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { for (ii = 0; ii < nNode_target; ii++) target_element[ii] = new su2double[nDim]; - nNode_target = Build_3D_surface_element(Target_LinkedNodes, Target_StartLinkedNodes, Target_nLinkedNodes, TargetPoint_Coord, target_iPoint, target_element); + nNode_target = Build_3D_surface_element(Target_LinkedNodes, Target_StartLinkedNodes, Target_nLinkedNodes, + TargetPoint_Coord, target_iPoint, target_element); /*--- Brute force to find the closest donor_node ---*/ @@ -508,13 +517,14 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { for (ii = 0; ii < 2*nEdges_donor + 2; ii++) donor_element[ii] = new su2double[nDim]; - nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, DonorPoint_Coord, donor_iPoint, donor_element); + nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, + DonorPoint_Coord, donor_iPoint, donor_element); Area = 0; for (ii = 1; ii < nNode_target-1; ii++){ for (jj = 1; jj < nNode_donor-1; jj++){ - Area += Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], donor_element[0], donor_element[jj], donor_element[jj+1], Normal); - //cout << Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], donor_element[0], donor_element[jj], donor_element[jj+1], Normal) << endl; + Area += Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], + donor_element[0], donor_element[jj], donor_element[jj+1], Normal); } } @@ -608,12 +618,14 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { for (ii = 0; ii < 2*nEdges_donor + 2; ii++) donor_element[ii] = new su2double[nDim]; - nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, DonorPoint_Coord, donor_iPoint, donor_element); + nNode_donor = Build_3D_surface_element(Donor_LinkedNodes, Donor_StartLinkedNodes, Donor_nLinkedNodes, + DonorPoint_Coord, donor_iPoint, donor_element); tmp_Area = 0; for (ii = 1; ii < nNode_target-1; ii++) for (jj = 1; jj < nNode_donor-1; jj++) - tmp_Area += Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], donor_element[0], donor_element[jj], donor_element[jj+1], Normal); + tmp_Area += Compute_Triangle_Intersection(target_element[0], target_element[ii], target_element[ii+1], + donor_element[0], donor_element[jj], donor_element[jj+1], Normal); for (ii = 0; ii < 2*nEdges_donor + 2; ii++) delete [] donor_element[ii]; @@ -687,7 +699,6 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor, Coeff_Vect[iDonor]/Area); target_geometry->vertex[markTarget][iVertex]->SetInterpDonorPoint( iDonor, Donor_GlobalPoint[ Donor_Vect[iDonor] ] ); target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, storeProc[iDonor]); - //cout < -CDriver::CDriver(char* confFile, - unsigned short val_nZone, - SU2_Comm MPICommunicator, bool dummy_geo):config_file_name(confFile), StartTime(0.0), StopTime(0.0), UsedTime(0.0), - TimeIter(0), nZone(val_nZone), StopCalc(false), fsi(false), fem_solver(false), dry_run(dummy_geo) { +CDriver::CDriver(char* confFile, unsigned short val_nZone, SU2_Comm MPICommunicator, bool dummy_geo) : + config_file_name(confFile), StartTime(0.0), StopTime(0.0), UsedTime(0.0), + TimeIter(0), nZone(val_nZone), StopCalc(false), fsi(false), fem_solver(false), dry_run(dummy_geo) { /*--- Initialize Medipack (must also be here so it is initialized from python) ---*/ #ifdef HAVE_MPI @@ -2532,22 +2531,11 @@ void CDriver::Interface_Preprocessing(CConfig **config, CSolver***** solver, CGe unsigned short donorZone, targetZone; unsigned short nVar, nVarTransfer; - unsigned short nMarkerTarget, iMarkerTarget, nMarkerDonor, iMarkerDonor; - /*--- Initialize some useful booleans ---*/ bool fluid_donor, structural_donor, heat_donor; bool fluid_target, structural_target, heat_target; - bool discrete_adjoint = config[ZONE_0]->GetDiscrete_Adjoint(); - - int markDonor, markTarget, Donor_check, Target_check, iMarkerInt, nMarkerInt; - -#ifdef HAVE_MPI - int *Buffer_Recv_mark = NULL, iRank, nProcessor = size; - - if (rank == MASTER_NODE) - Buffer_Recv_mark = new int[nProcessor]; -#endif + const bool discrete_adjoint = config[ZONE_0]->GetDiscrete_Adjoint(); /*--- Coupling between zones ---*/ // There's a limit here, the interface boundary must connect only 2 zones @@ -2566,278 +2554,209 @@ void CDriver::Interface_Preprocessing(CConfig **config, CSolver***** solver, CGe continue; } - nMarkerInt = (int) ( config[donorZone]->GetMarker_n_ZoneInterface() / 2 ); + const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface() / 2; /*--- Loops on Interface markers to find if the 2 zones are sharing the boundary and to *--- determine donor and target marker tag ---*/ - for (iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { + for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { - markDonor = -1; - markTarget = -1; - - /*--- On the donor side ---*/ - nMarkerDonor = config[donorZone]->GetnMarker_All(); - - for (iMarkerDonor = 0; iMarkerDonor < nMarkerDonor; iMarkerDonor++) { - - /*--- If the tag GetMarker_All_ZoneInterface(iMarker) equals the index we are looping at ---*/ - if ( config[donorZone]->GetMarker_All_ZoneInterface(iMarkerDonor) == iMarkerInt ) { - /*--- We have identified the identifier for the interface marker ---*/ - markDonor = iMarkerDonor; - - break; - } + /* --- Check if zones are actually sharing the interface boundary, if not skip. ---*/ + if(!CInterpolator::CheckInterfaceBoundary(CInterpolator::Find_InterfaceMarker(config[donorZone],iMarkerInt), + CInterpolator::Find_InterfaceMarker(config[targetZone],iMarkerInt))) { + interface_types[donorZone][targetZone] = NO_COMMON_INTERFACE; + continue; } - /*--- On the target side ---*/ - nMarkerTarget = config[targetZone]->GetnMarker_All(); - - for (iMarkerTarget = 0; iMarkerTarget < nMarkerTarget; iMarkerTarget++) { - - /*--- If the tag GetMarker_All_ZoneInterface(iMarker) equals the index we are looping at ---*/ - if ( config[targetZone]->GetMarker_All_ZoneInterface(iMarkerTarget) == iMarkerInt ) { - /*--- We have identified the identifier for the interface marker ---*/ - markTarget = iMarkerTarget; - - break; - } - } - -#ifdef HAVE_MPI + /*--- Set some boolean to properly allocate data structure later ---*/ + fluid_target = false; + structural_target = false; - Donor_check = -1; - Target_check = -1; + fluid_donor = false; + structural_donor = false; - /*--- We gather a vector in MASTER_NODE that determines if the boundary is not on the processor because - * of the partition or because the zone does not include it ---*/ + heat_donor = false; + heat_target = false; - SU2_MPI::Gather(&markDonor , 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); + switch ( config[targetZone]->GetKind_Solver() ) { - if (rank == MASTER_NODE) { - for (iRank = 0; iRank < nProcessor; iRank++) { - if( Buffer_Recv_mark[iRank] != -1 ) { - Donor_check = Buffer_Recv_mark[iRank]; + case EULER : case NAVIER_STOKES: case RANS: + case INC_EULER : case INC_NAVIER_STOKES: case INC_RANS: + case DISC_ADJ_INC_EULER: case DISC_ADJ_INC_NAVIER_STOKES: case DISC_ADJ_INC_RANS: + case DISC_ADJ_EULER: case DISC_ADJ_NAVIER_STOKES: case DISC_ADJ_RANS: + fluid_target = true; break; - } - } - } - SU2_MPI::Bcast(&Donor_check , 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - - SU2_MPI::Gather(&markTarget, 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); + case FEM_ELASTICITY: case DISC_ADJ_FEM: + structural_target = true; + break; - if (rank == MASTER_NODE){ - for (iRank = 0; iRank < nProcessor; iRank++){ - if( Buffer_Recv_mark[iRank] != -1 ){ - Target_check = Buffer_Recv_mark[iRank]; + case HEAT_EQUATION: case DISC_ADJ_HEAT: + heat_target = true; break; - } } - } - - SU2_MPI::Bcast(&Target_check, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); -#else - Donor_check = markDonor; - Target_check = markTarget; -#endif - - /* --- Check if zones are actually sharing the interface boundary, if not skip. ---*/ - if(Target_check == -1 || Donor_check == -1) { - interface_types[donorZone][targetZone] = NO_COMMON_INTERFACE; - continue; - } - - /*--- Set some boolean to properly allocate data structure later ---*/ - fluid_target = false; - structural_target = false; - - fluid_donor = false; - structural_donor = false; + switch ( config[donorZone]->GetKind_Solver() ) { - heat_donor = false; - heat_target = false; + case EULER : case NAVIER_STOKES: case RANS: + case INC_EULER : case INC_NAVIER_STOKES: case INC_RANS: + case DISC_ADJ_INC_EULER: case DISC_ADJ_INC_NAVIER_STOKES: case DISC_ADJ_INC_RANS: + case DISC_ADJ_EULER: case DISC_ADJ_NAVIER_STOKES: case DISC_ADJ_RANS: + fluid_donor = true; + break; - switch ( config[targetZone]->GetKind_Solver() ) { + case FEM_ELASTICITY: case DISC_ADJ_FEM: + structural_donor = true; + break; - case EULER : case NAVIER_STOKES: case RANS: - case INC_EULER : case INC_NAVIER_STOKES: case INC_RANS: - case DISC_ADJ_INC_EULER: case DISC_ADJ_INC_NAVIER_STOKES: case DISC_ADJ_INC_RANS: - case DISC_ADJ_EULER: case DISC_ADJ_NAVIER_STOKES: case DISC_ADJ_RANS: - fluid_target = true; - break; + case HEAT_EQUATION : case DISC_ADJ_HEAT: + heat_donor = true; + break; + } - case FEM_ELASTICITY: case DISC_ADJ_FEM: - structural_target = true; - break; + /*--- Begin the creation of the communication pattern among zones ---*/ - case HEAT_EQUATION: case DISC_ADJ_HEAT: - heat_target = true; - break; - } + /*--- Retrieve the number of conservative variables (for problems not involving structural analysis ---*/ + if (fluid_donor && fluid_target) + nVar = solver[donorZone][INST_0][MESH_0][FLOW_SOL]->GetnVar(); + else + /*--- If at least one of the components is structural ---*/ + nVar = nDim; - switch ( config[donorZone]->GetKind_Solver() ) { + if (rank == MASTER_NODE) cout << "From zone " << donorZone << " to zone " << targetZone << ": "; - case EULER : case NAVIER_STOKES: case RANS: - case INC_EULER : case INC_NAVIER_STOKES: case INC_RANS: - case DISC_ADJ_INC_EULER: case DISC_ADJ_INC_NAVIER_STOKES: case DISC_ADJ_INC_RANS: - case DISC_ADJ_EULER: case DISC_ADJ_NAVIER_STOKES: case DISC_ADJ_RANS: - fluid_donor = true; - break; + /*--- Match Zones ---*/ + if (rank == MASTER_NODE) cout << "Setting coupling "; - case FEM_ELASTICITY: case DISC_ADJ_FEM: - structural_donor = true; - break; + bool conservative_interp = config[donorZone]->GetConservativeInterpolation(); - case HEAT_EQUATION : case DISC_ADJ_HEAT: - heat_donor = true; - break; - } + /*--- Conditions for conservative interpolation are not met, we cannot fallback on the consistent approach + because CFlowTractionInterface relies on the information in config to be correct. ---*/ + if ( conservative_interp && targetZone == 0 && structural_target ) + SU2_MPI::Error("Conservative interpolation assumes the structural model mesh is evaluated second, " + "somehow this has not happened.",CURRENT_FUNCTION); - /*--- Begin the creation of the communication pattern among zones ---*/ + switch (config[donorZone]->GetKindInterpolation()) { - /*--- Retrieve the number of conservative variables (for problems not involving structural analysis ---*/ - if (fluid_donor && fluid_target) - nVar = solver[donorZone][INST_0][MESH_0][FLOW_SOL]->GetnVar(); - else - /*--- If at least one of the components is structural ---*/ - nVar = nDim; - - if (rank == MASTER_NODE) cout << "From zone " << donorZone << " to zone " << targetZone << ": "; + case NEAREST_NEIGHBOR: + if ( conservative_interp && targetZone > 0 && structural_target ) { + interpolation[donorZone][targetZone] = new CMirror(geometry, config, donorZone, targetZone); + if (rank == MASTER_NODE) cout << "using a mirror approach: matching coefficients " + "from opposite mesh." << endl; + } + else { + interpolation[donorZone][targetZone] = new CNearestNeighbor(geometry, config, donorZone, targetZone); + if (rank == MASTER_NODE) cout << "using a nearest-neighbor approach." << endl; + } + break; - /*--- Match Zones ---*/ - if (rank == MASTER_NODE) cout << "Setting coupling "; + case ISOPARAMETRIC: + if ( conservative_interp && targetZone > 0 && structural_target ) { + interpolation[donorZone][targetZone] = new CMirror(geometry, config, donorZone, targetZone); + if (rank == MASTER_NODE) cout << "using a mirror approach: matching coefficients " + "from opposite mesh." << endl; + } + else { + interpolation[donorZone][targetZone] = new CIsoparametric(geometry, config, donorZone, targetZone); + if (rank == MASTER_NODE) cout << "using an isoparametric approach." << endl; + } + break; - bool conservative_interp = config[donorZone]->GetConservativeInterpolation(); + case WEIGHTED_AVERAGE: + interpolation[donorZone][targetZone] = new CSlidingMesh(geometry, config, donorZone, targetZone); + if (rank == MASTER_NODE) cout << "using an sliding mesh approach." << endl; - /*--- Conditions for conservative interpolation are not met, we cannot fallback on the consistent approach - because CFlowTractionInterface relies on the information in config to be correct. ---*/ - if ( conservative_interp && targetZone == 0 && structural_target ) - SU2_MPI::Error("Conservative interpolation assumes the structural model mesh is evaluated second, " - "somehow this has not happened.",CURRENT_FUNCTION); + break; - switch (config[donorZone]->GetKindInterpolation()) { + case RADIAL_BASIS_FUNCTION: + if ( conservative_interp && targetZone > 0 && structural_target ) { + interpolation[donorZone][targetZone] = new CMirror(geometry, config, donorZone, targetZone); + if (rank == MASTER_NODE) cout << "using a mirror approach: matching coefficients " + "from opposite mesh." << endl; + } + else { + interpolation[donorZone][targetZone] = new CRadialBasisFunction(geometry, config, + donorZone, targetZone); + if (rank == MASTER_NODE) cout << "using a radial basis function approach." << endl; + } + break; + } - case NEAREST_NEIGHBOR: - if ( conservative_interp && targetZone > 0 && structural_target ) { - interpolation[donorZone][targetZone] = new CMirror(geometry, config, donorZone, targetZone); - if (rank == MASTER_NODE) cout << "using a mirror approach: matching coefficients " - "from opposite mesh." << endl; - } - else { - interpolation[donorZone][targetZone] = new CNearestNeighbor(geometry, config, donorZone, targetZone); - if (rank == MASTER_NODE) cout << "using a nearest-neighbor approach." << endl; - } - break; + /*--- Initialize the appropriate transfer strategy ---*/ + if (rank == MASTER_NODE) cout << "Transferring "; - case ISOPARAMETRIC: - if ( conservative_interp && targetZone > 0 && structural_target ) { - interpolation[donorZone][targetZone] = new CMirror(geometry, config, donorZone, targetZone); - if (rank == MASTER_NODE) cout << "using a mirror approach: matching coefficients " - "from opposite mesh." << endl; - } - else { - interpolation[donorZone][targetZone] = new CIsoparametric(geometry, config, donorZone, targetZone); - if (rank == MASTER_NODE) cout << "using an isoparametric approach." << endl; + if (fluid_donor && structural_target) { + interface_types[donorZone][targetZone] = FLOW_TRACTION; + nVarTransfer = 2; + if(!discrete_adjoint) { + interface[donorZone][targetZone] = new CFlowTractionInterface(nVar, nVarTransfer, config[donorZone]); + } else { + interface[donorZone][targetZone] = new CDiscAdjFlowTractionInterface(nVar, nVarTransfer, config[donorZone]); } - break; - - case WEIGHTED_AVERAGE: - interpolation[donorZone][targetZone] = new CSlidingMesh(geometry, config, donorZone, targetZone); - if (rank == MASTER_NODE) cout << "using an sliding mesh approach." << endl; - - break; - - case RADIAL_BASIS_FUNCTION: - if ( conservative_interp && targetZone > 0 && structural_target ) { - interpolation[donorZone][targetZone] = new CMirror(geometry, config, donorZone, targetZone); - if (rank == MASTER_NODE) cout << "using a mirror approach: matching coefficients " - "from opposite mesh." << endl; + if (rank == MASTER_NODE) cout << "flow tractions. " << endl; + } + else if (structural_donor && fluid_target) { + /*--- If we are using the new mesh solver, we transfer the total boundary displacements (not incremental) --*/ + if (solver_container[targetZone][INST_0][MESH_0][MESH_SOL] != NULL) { + interface_types[donorZone][targetZone] = BOUNDARY_DISPLACEMENTS; + nVarTransfer = 0; + interface[donorZone][targetZone] = new CDisplacementsInterface(nVar, nVarTransfer, config[donorZone]); + if (rank == MASTER_NODE) cout << "boundary displacements from the structural solver. " << endl; } + /*--- We keep the legacy method temporarily until FSI-adjoint has been adapted ---*/ + /// TODO: LEGACY CLEANUP remove the "else" part and every class and enum referenced there, + /// add a check above to make sure MESH_SOL has been instantiated. else { - interpolation[donorZone][targetZone] = new CRadialBasisFunction(geometry, config, - donorZone, targetZone); - if (rank == MASTER_NODE) cout << "using a radial basis function approach." << endl; + nVarTransfer = 0; + if(!discrete_adjoint) { + interface_types[donorZone][targetZone] = STRUCTURAL_DISPLACEMENTS_LEGACY; + interface[donorZone][targetZone] = new CDisplacementsInterfaceLegacy(nVar, nVarTransfer, config[donorZone]); + } else { + interface_types[donorZone][targetZone] = STRUCTURAL_DISPLACEMENTS_DISC_ADJ; + interface[donorZone][targetZone] = new CDiscAdjDisplacementsInterfaceLegacy(nVar, nVarTransfer, config[donorZone]); + } + if (rank == MASTER_NODE) cout << "structural displacements (legacy). " << endl; } - break; - } - - /*--- Initialize the appropriate transfer strategy ---*/ - if (rank == MASTER_NODE) cout << "Transferring "; - - if (fluid_donor && structural_target) { - interface_types[donorZone][targetZone] = FLOW_TRACTION; - nVarTransfer = 2; - if(!discrete_adjoint) { - interface[donorZone][targetZone] = new CFlowTractionInterface(nVar, nVarTransfer, config[donorZone]); - } else { - interface[donorZone][targetZone] = new CDiscAdjFlowTractionInterface(nVar, nVarTransfer, config[donorZone]); } - if (rank == MASTER_NODE) cout << "flow tractions. " << endl; - } - else if (structural_donor && fluid_target) { - /*--- If we are using the new mesh solver, we transfer the total boundary displacements (not incremental) --*/ - if (solver_container[targetZone][INST_0][MESH_0][MESH_SOL] != NULL) { - interface_types[donorZone][targetZone] = BOUNDARY_DISPLACEMENTS; + else if (fluid_donor && fluid_target) { + interface_types[donorZone][targetZone] = SLIDING_INTERFACE; + nVarTransfer = 0; + nVar = solver[donorZone][INST_0][MESH_0][FLOW_SOL]->GetnPrimVar(); + interface[donorZone][targetZone] = new CSlidingInterface(nVar, nVarTransfer, config[donorZone]); + if (rank == MASTER_NODE) cout << "sliding interface. " << endl; + } + else if (fluid_donor && heat_target) { nVarTransfer = 0; - interface[donorZone][targetZone] = new CDisplacementsInterface(nVar, nVarTransfer, config[donorZone]); - if (rank == MASTER_NODE) cout << "boundary displacements from the structural solver. " << endl; + nVar = 4; + if(config[donorZone]->GetEnergy_Equation() || (config[donorZone]->GetKind_Regime() == COMPRESSIBLE)) + interface_types[donorZone][targetZone] = CONJUGATE_HEAT_FS; + else if (config[donorZone]->GetWeakly_Coupled_Heat()) + interface_types[donorZone][targetZone] = CONJUGATE_HEAT_WEAKLY_FS; + else { } + interface[donorZone][targetZone] = new CConjugateHeatInterface(nVar, nVarTransfer, config[donorZone]); + if (rank == MASTER_NODE) cout << "conjugate heat variables. " << endl; + } + else if (heat_donor && fluid_target) { + nVarTransfer = 0; + nVar = 4; + if(config[targetZone]->GetEnergy_Equation() || (config[targetZone]->GetKind_Regime() == COMPRESSIBLE)) + interface_types[donorZone][targetZone] = CONJUGATE_HEAT_SF; + else if (config[targetZone]->GetWeakly_Coupled_Heat()) + interface_types[donorZone][targetZone] = CONJUGATE_HEAT_WEAKLY_SF; + else { } + interface[donorZone][targetZone] = new CConjugateHeatInterface(nVar, nVarTransfer, config[donorZone]); + if (rank == MASTER_NODE) cout << "conjugate heat variables. " << endl; + } + else if (heat_donor && heat_target) { + SU2_MPI::Error("Conjugate heat transfer between solids not implemented yet.", CURRENT_FUNCTION); } - /*--- We keep the legacy method temporarily until FSI-adjoint has been adapted ---*/ - /// TODO: LEGACY CLEANUP remove the "else" part and every class and enum referenced there, - /// add a check above to make sure MESH_SOL has been instantiated. else { + interface_types[donorZone][targetZone] = CONSERVATIVE_VARIABLES; nVarTransfer = 0; - if(!discrete_adjoint) { - interface_types[donorZone][targetZone] = STRUCTURAL_DISPLACEMENTS_LEGACY; - interface[donorZone][targetZone] = new CDisplacementsInterfaceLegacy(nVar, nVarTransfer, config[donorZone]); - } else { - interface_types[donorZone][targetZone] = STRUCTURAL_DISPLACEMENTS_DISC_ADJ; - interface[donorZone][targetZone] = new CDiscAdjDisplacementsInterfaceLegacy(nVar, nVarTransfer, config[donorZone]); - } - if (rank == MASTER_NODE) cout << "structural displacements (legacy). " << endl; + interface[donorZone][targetZone] = new CConservativeVarsInterface(nVar, nVarTransfer, config[donorZone]); + if (rank == MASTER_NODE) cout << "generic conservative variables. " << endl; } - } - else if (fluid_donor && fluid_target) { - interface_types[donorZone][targetZone] = SLIDING_INTERFACE; - nVarTransfer = 0; - nVar = solver[donorZone][INST_0][MESH_0][FLOW_SOL]->GetnPrimVar(); - interface[donorZone][targetZone] = new CSlidingInterface(nVar, nVarTransfer, config[donorZone]); - if (rank == MASTER_NODE) cout << "sliding interface. " << endl; - } - else if (fluid_donor && heat_target) { - nVarTransfer = 0; - nVar = 4; - if(config[donorZone]->GetEnergy_Equation() || (config[donorZone]->GetKind_Regime() == COMPRESSIBLE)) - interface_types[donorZone][targetZone] = CONJUGATE_HEAT_FS; - else if (config[donorZone]->GetWeakly_Coupled_Heat()) - interface_types[donorZone][targetZone] = CONJUGATE_HEAT_WEAKLY_FS; - else { } - interface[donorZone][targetZone] = new CConjugateHeatInterface(nVar, nVarTransfer, config[donorZone]); - if (rank == MASTER_NODE) cout << "conjugate heat variables. " << endl; - } - else if (heat_donor && fluid_target) { - nVarTransfer = 0; - nVar = 4; - if(config[targetZone]->GetEnergy_Equation() || (config[targetZone]->GetKind_Regime() == COMPRESSIBLE)) - interface_types[donorZone][targetZone] = CONJUGATE_HEAT_SF; - else if (config[targetZone]->GetWeakly_Coupled_Heat()) - interface_types[donorZone][targetZone] = CONJUGATE_HEAT_WEAKLY_SF; - else { } - interface[donorZone][targetZone] = new CConjugateHeatInterface(nVar, nVarTransfer, config[donorZone]); - if (rank == MASTER_NODE) cout << "conjugate heat variables. " << endl; - } - else if (heat_donor && heat_target) { - SU2_MPI::Error("Conjugate heat transfer between solids not implemented yet.", CURRENT_FUNCTION); - } - else { - interface_types[donorZone][targetZone] = CONSERVATIVE_VARIABLES; - nVarTransfer = 0; - interface[donorZone][targetZone] = new CConservativeVarsInterface(nVar, nVarTransfer, config[donorZone]); - if (rank == MASTER_NODE) cout << "generic conservative variables. " << endl; - } - break; + break; } @@ -2855,10 +2774,6 @@ void CDriver::Interface_Preprocessing(CConfig **config, CSolver***** solver, CGe } -#ifdef HAVE_MPI - if (rank == MASTER_NODE) - delete [] Buffer_Recv_mark; -#endif } void CDriver::StaticMesh_Preprocessing(CConfig *config, CGeometry** geometry, CSurfaceMovement* surface_movement){ diff --git a/SU2_CFD/src/drivers/CMultizoneDriver.cpp b/SU2_CFD/src/drivers/CMultizoneDriver.cpp index 2179cb084a11..66db6a2544cc 100644 --- a/SU2_CFD/src/drivers/CMultizoneDriver.cpp +++ b/SU2_CFD/src/drivers/CMultizoneDriver.cpp @@ -29,12 +29,8 @@ #include "../../include/definition_structure.hpp" #include "../../../Common/include/interface_interpolation/CInterpolator.hpp" -CMultizoneDriver::CMultizoneDriver(char* confFile, - unsigned short val_nZone, - SU2_Comm MPICommunicator) : CDriver(confFile, - val_nZone, - MPICommunicator, - false) { +CMultizoneDriver::CMultizoneDriver(char* confFile, unsigned short val_nZone, SU2_Comm MPICommunicator) : + CDriver(confFile, val_nZone, MPICommunicator, false) { /*--- Initialize the counter for TimeIter ---*/ TimeIter = 0; From e1fa6dfbac3298a9f9b3c630de738090e980d139 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 18 Mar 2020 15:56:59 +0000 Subject: [PATCH 031/112] move CSymmetricMatrix to toolboxes --- .../interface_interpolation/CInterpolator.hpp | 4 +- .../CRadialBasisFunction.hpp | 70 +--- Common/include/toolboxes/CSymmetricMatrix.hpp | 90 +++++ Common/lib/Makefile.am | 1 + .../src/interface_interpolation/CMirror.cpp | 102 +++--- .../CRadialBasisFunction.cpp | 308 +--------------- Common/src/toolboxes/CSymmetricMatrix.cpp | 332 ++++++++++++++++++ Common/src/toolboxes/meson.build | 3 +- 8 files changed, 489 insertions(+), 421 deletions(-) create mode 100644 Common/include/toolboxes/CSymmetricMatrix.hpp create mode 100644 Common/src/toolboxes/CSymmetricMatrix.cpp diff --git a/Common/include/interface_interpolation/CInterpolator.hpp b/Common/include/interface_interpolation/CInterpolator.hpp index 74e4840d5d02..487e5131f345 100644 --- a/Common/include/interface_interpolation/CInterpolator.hpp +++ b/Common/include/interface_interpolation/CInterpolator.hpp @@ -147,7 +147,7 @@ class CInterpolator { * \param[in] point_i - coordinates of point i * \param[in] point_j - coordinates of point j */ - inline su2double PointsSquareDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) const { + static inline su2double PointsSquareDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) { su2double d = 0.0; for(unsigned short iDim = 0; iDim < nDim; iDim++) d += pow(point_j[iDim] - point_i[iDim], 2); @@ -160,7 +160,7 @@ class CInterpolator { * \param[in] point_i - coordinates of point i * \param[in] point_j - coordinates of point j */ - inline su2double PointsDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) const { + static inline su2double PointsDistance(unsigned short nDim, const su2double *point_i, const su2double *point_j) { return sqrt(PointsSquareDistance(nDim, point_i, point_j)); } diff --git a/Common/include/interface_interpolation/CRadialBasisFunction.hpp b/Common/include/interface_interpolation/CRadialBasisFunction.hpp index 18b1b2083644..37b75d784940 100644 --- a/Common/include/interface_interpolation/CRadialBasisFunction.hpp +++ b/Common/include/interface_interpolation/CRadialBasisFunction.hpp @@ -59,7 +59,6 @@ class CRadialBasisFunction final : public CInterpolator { */ static su2double Get_RadialBasisValue(ENUM_RADIALBASIS type, const su2double radius, const su2double dist); -private: /*! * \brief Compute the RBF "generator" matrix with or without polynomial terms. * \note Multiplying C_inv_trunc by a column vector gives specific coefficients for given "known values", @@ -73,9 +72,9 @@ class CRadialBasisFunction final : public CInterpolator { * \param[out] keepPolynomialRow - Size nDim, signals which (if any) iDim was removed from polynomial term. * \param[out] C_inv_trunc - The generator matrix as described above. */ - void ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, su2double radius, - const su2activematrix& coords, int& nPolynomial, - vector& keepPolynomialRow, su2passivematrix& C_inv_trunc) const; + static void ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, su2double radius, + const su2activematrix& coords, int& nPolynomial, + vector& keepPolynomialRow, su2passivematrix& C_inv_trunc); /*! * \brief If the polynomial term is included in the interpolation, and the points lie on a plane, the matrix @@ -86,7 +85,7 @@ class CRadialBasisFunction final : public CInterpolator { * \param[in,out] P - Polynomial part of the interpolation matrix, one row may be eliminated. * \return n_polynomial - Size of the polynomial part on exit (in practice nDim or nDim-1). */ - int CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, su2passivematrix &P) const; + static int CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, su2passivematrix &P); /*! * \brief Prunes (by setting to zero) small interpolation coefficients, i.e. @@ -95,65 +94,6 @@ class CRadialBasisFunction final : public CInterpolator { * \param[in,out] coeffs - The vector of interpolation coefficients. * \return Number of non-zero coefficients after pruning. */ - int PruneSmallCoefficients(passivedouble tolerance, su2passivevector& coeffs) const; - -}; - -/*! - * \brief Helper class used by CRadialBasisFunction to compute the interpolation weights. - * The matrix is symmetric but full storage is used as that gives much better performance - * for some BLAS libraries (notably OpenBLAS). The code should be compiled with LAPACK - * to use optimized matrix inversion and multiplication routines. - */ -class CSymmetricMatrix { -private: - enum DecompositionType { NONE, CHOLESKY, LU }; - - vector val_vec, decomp_vec; - vector perm_vec; - int sz = 0; - bool initialized = false; - DecompositionType decomposed = NONE; - - inline void CheckBounds(int i, int j) const { - assert(initialized && "Matrix not initialized."); - assert(i>=0 && i=0 && j. + */ +#pragma once + +#include +#include "C2DContainer.hpp" + +using namespace std; + +/*! + * \brief The matrix is symmetric but full storage is used as that gives much better + * performance for some BLAS libraries (notably OpenBLAS). The code should be compiled + * with LAPACK to use optimized matrix inversion and multiplication routines. + */ +class CSymmetricMatrix { +private: + enum DecompositionType { NONE, CHOLESKY, LU }; + + vector val_vec, decomp_vec; + vector perm_vec; + int sz = 0; + bool initialized = false; + DecompositionType decomposed = NONE; + + inline void CheckBounds(int i, int j) const { + assert(initialized && "Matrix not initialized."); + assert(i>=0 && i=0 && jGetMarker_n_ZoneInterface())/2; @@ -73,7 +79,7 @@ void CMirror::Set_TransferCoeff(CConfig **config) { */ /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ - markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); + markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); /*--- On the target side: find the tag of the boundary sharing the interface ---*/ markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); @@ -103,11 +109,6 @@ void CMirror::Set_TransferCoeff(CConfig **config) { nLocalFace_Donor++; } } - Buffer_Send_nFace_Donor= new unsigned long [1]; - Buffer_Send_nFaceNodes_Donor= new unsigned long [1]; - - Buffer_Receive_nFace_Donor = new unsigned long [nProcessor]; - Buffer_Receive_nFaceNodes_Donor = new unsigned long [nProcessor]; Buffer_Send_nFace_Donor[0] = nLocalFace_Donor; Buffer_Send_nFaceNodes_Donor[0] = nLocalFaceNodes_Donor; @@ -116,8 +117,10 @@ void CMirror::Set_TransferCoeff(CConfig **config) { #ifdef HAVE_MPI SU2_MPI::Allreduce(&nLocalFaceNodes_Donor, &MaxFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); SU2_MPI::Allreduce(&nLocalFace_Donor, &MaxFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_nFace_Donor, 1, MPI_UNSIGNED_LONG, Buffer_Receive_nFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); - SU2_MPI::Allgather(Buffer_Send_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, Buffer_Receive_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_nFace_Donor, 1, MPI_UNSIGNED_LONG, + Buffer_Receive_nFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); + SU2_MPI::Allgather(Buffer_Send_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, + Buffer_Receive_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); MaxFace_Donor++; #else nGlobalFace_Donor = nLocalFace_Donor; @@ -129,15 +132,15 @@ void CMirror::Set_TransferCoeff(CConfig **config) { #endif /*-- Send donor info --*/ - Buffer_Send_FaceIndex = new unsigned long[MaxFace_Donor]; - Buffer_Send_FaceNodes = new unsigned long[MaxFaceNodes_Donor]; + Buffer_Send_FaceIndex = new unsigned long[MaxFace_Donor]; + Buffer_Send_FaceNodes = new unsigned long[MaxFaceNodes_Donor]; Buffer_Send_GlobalPoint = new long[MaxFaceNodes_Donor]; - Buffer_Send_Coeff = new su2double[MaxFaceNodes_Donor]; + Buffer_Send_Coeff = new su2double[MaxFaceNodes_Donor]; - Buffer_Receive_FaceIndex= new unsigned long[MaxFace_Donor*nProcessor]; - Buffer_Receive_FaceNodes= new unsigned long[MaxFaceNodes_Donor*nProcessor]; + Buffer_Receive_FaceIndex = new unsigned long[MaxFace_Donor*nProcessor]; + Buffer_Receive_FaceNodes = new unsigned long[MaxFaceNodes_Donor*nProcessor]; Buffer_Receive_GlobalPoint = new long[MaxFaceNodes_Donor*nProcessor]; - Buffer_Receive_Coeff = new su2double[MaxFaceNodes_Donor*nProcessor]; + Buffer_Receive_Coeff = new su2double[MaxFaceNodes_Donor*nProcessor]; for (iVertex=0; iVertexvertex[markTarget][iVertex]->GetNode(); - if (target_geometry->node[iPoint]->GetDomain()) { - long Global_Point = target_geometry->node[iPoint]->GetGlobalIndex(); - nNodes = 0; - for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { - for (iFace = 0; iFace < Buffer_Receive_nFace_Donor[iProcessor]; iFace++) { - faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face - iNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1]- (unsigned int)faceindex; - for (iTarget=0; iTargetnode[iPoint]->GetDomain()) continue; + + long Global_Point = target_geometry->node[iPoint]->GetGlobalIndex(); + nNodes = 0; + for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { + for (iFace = 0; iFace < Buffer_Receive_nFace_Donor[iProcessor]; iFace++) { + faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face + iNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1]- (unsigned int)faceindex; + for (iTarget=0; iTargetvertex[markTarget][iVertex]->SetnDonorPoints(nNodes); - target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); - - iDonor = 0; - for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { - for (iFace = 0; iFace < Buffer_Receive_nFace_Donor[iProcessor]; iFace++) { - - faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face - iNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1]- (unsigned int)faceindex; - for (iTarget=0; iTargetvertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor,pGlobalPoint); - target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor,coeff); - target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, iProcessor); - iDonor++; - } + target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nNodes); + target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); + + iDonor = 0; + for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { + for (iFace = 0; iFace < Buffer_Receive_nFace_Donor[iProcessor]; iFace++) { + + faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face + iNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1]- (unsigned int)faceindex; + for (iTarget=0; iTargetvertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor,pGlobalPoint); + target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor,coeff); + target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, iProcessor); + iDonor++; } } } } } - delete[] Buffer_Send_nFace_Donor; - delete[] Buffer_Send_nFaceNodes_Donor; - - delete[] Buffer_Receive_nFace_Donor; - delete[] Buffer_Receive_nFaceNodes_Donor; delete[] Buffer_Send_FaceIndex; delete[] Buffer_Send_FaceNodes; @@ -238,6 +236,12 @@ void CMirror::Set_TransferCoeff(CConfig **config) { delete[] Buffer_Receive_FaceNodes; delete[] Buffer_Receive_GlobalPoint; delete[] Buffer_Receive_Coeff; - } + + delete[] Buffer_Send_nFace_Donor; + delete[] Buffer_Send_nFaceNodes_Donor; + + delete[] Buffer_Receive_nFace_Donor; + delete[] Buffer_Receive_nFaceNodes_Donor; + } diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp index fd5504ed8c8b..eabd34671bab 100644 --- a/Common/src/interface_interpolation/CRadialBasisFunction.cpp +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -28,23 +28,8 @@ #include "../../include/interface_interpolation/CRadialBasisFunction.hpp" #include "../../include/CConfig.hpp" #include "../../include/geometry/CGeometry.hpp" +#include "../../include/toolboxes/CSymmetricMatrix.hpp" -#if defined(HAVE_MKL) -#include "mkl.h" -#ifndef HAVE_LAPACK -#define HAVE_LAPACK -#endif -#elif defined(HAVE_LAPACK) -/*--- Lapack / Blas routines used in RBF interpolation. ---*/ -extern "C" void dsptrf_(char*, int*, passivedouble*, int*, int*); -extern "C" void dsptri_(char*, int*, passivedouble*, int*, passivedouble*, int*); -extern "C" void dsytrf_(char*, int*, passivedouble*, int*, int*, passivedouble*, int*, int*); -extern "C" void dsytri_(char*, int*, passivedouble*, int*, int*, passivedouble*, int*); -extern "C" void dpotrf_(char*, int*, passivedouble*, int*, int*); -extern "C" void dpotri_(char*, int*, passivedouble*, int*, int*); -extern "C" void dsymm_(char*, char*, int*, int*, passivedouble*, passivedouble*, int*, - passivedouble*, int*, passivedouble*, passivedouble*, int*); -#endif CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { @@ -320,7 +305,7 @@ void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, su2double radius, const su2activematrix& coords, int& nPolynomial, - vector& keepPolynomialRow, su2passivematrix& C_inv_trunc) const { + vector& keepPolynomialRow, su2passivematrix& C_inv_trunc) { const su2double interfaceCoordTol = 1e6 * numeric_limits::epsilon(); @@ -406,7 +391,7 @@ void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool us } int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, - su2passivematrix &P) const { + su2passivematrix &P) { const int m = P.rows(); const int n = P.cols(); @@ -476,7 +461,7 @@ int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector 0.0 && "LLT failed, matrix is not SPD."); - Set(j, j, sqrt(sum)); - - for (int i = j+1; i < sz; ++i) { - passivedouble sum = 0.0; - for (int k = 0; k < j; ++k) sum -= Get(i,k) * Get(j,k); - sum += Get(i,j); - Set(i, j, sum / Get(j,j)); - } - } - decomposed = CHOLESKY; -#endif -} - -void CSymmetricMatrix::LUDecompose() -{ -#ifndef HAVE_LAPACK - assert(initialized && "Matrix not initialized."); - - /*--- Copy matrix values to LU matrix, init permutation vec. ---*/ - decomp_vec.resize(sz*sz); - perm_vec.resize(sz); - for (int i = 0; i < sz; ++i) { - perm_vec[i] = i; - for (int j = i; j < sz; ++j) decomp(j,i) = decomp(i,j) = Get(i,j); - } - - /*--- Decompose LU matrix. ---*/ - for (int j = 0; j < sz-1; ++j) { - /*--- Search for maximum pivot and interchange rows. ---*/ - passivedouble pivot = decomp(j,j); - int pivot_idx = j; - for (int i = j+1; i < sz; ++i) - if (abs(decomp(i,j)) > abs(pivot)) { - pivot = decomp(i,j); - pivot_idx = i; - } - - if (pivot_idx != j) { - swap(perm_vec[j], perm_vec[pivot_idx]); - for (int k = 0; k < sz; ++k) - swap(decomp(j,k), decomp(pivot_idx,k)); - } - - /*--- Perform elimination. ---*/ - for (int k = j+1; k < sz; ++k) decomp(k,j) /= pivot; - - for (int k = j+1; k < sz; ++k) - for (int i = j+1; i < sz; ++i) - decomp(i,k) -= decomp(j,k)*decomp(i,j); - } - - decomposed = LU; -#endif -} - -void CSymmetricMatrix::CalcInv() -{ -#ifndef HAVE_LAPACK - assert(initialized && "Matrix not initialized."); - - /*--- Decompose matrix if not done yet. ---*/ - if (decomposed == NONE) { LUDecompose(); } - - /*--- Compute inverse from decomposed matrices. ---*/ - switch (decomposed) { - - case CHOLESKY: - { - /*--- Initialize inverse matrix. ---*/ - vector inv(sz*sz, 0.0); - - /*--- Compute L inverse. ---*/ - /*--- Solve smaller and smaller systems. ---*/ - for (int j = 0; j < sz; ++j) { - /*--- Forward substitution. ---*/ - inv[IdxSym(j,j)] = 1.0 / Get(j,j); - - for (int i = j+1; i < sz; ++i) { - passivedouble sum = 0.0; - for (int k = j; k < i; ++k) sum -= Get(i,k) * inv[IdxSym(k,j)]; - inv[IdxSym(i,j)] = sum / Get(i,i); - } - } // L inverse in inv - - /*--- Multiply inversed matrices overwrite val_vec. ---*/ - for (int j = 0; j < sz; ++j) - for (int i = j; i < sz; ++i) { - passivedouble sum = 0.0; - for (int k = i; k < sz; ++k) sum += inv[IdxSym(k,i)] * inv[IdxSym(k,j)]; - Set(i, j, sum); - } - - break; - } - - case LU: - { - /*--- Alias val_vec. ---*/ - vector& inv = val_vec; - - /*--- Invert L and U matrices in place. ---*/ - for (int j = 0; j < sz; ++j) { - inv[IdxFull(j,j)] = 1.0 / decomp(j,j); - - for (int i = j+1; i < sz; ++i) { - inv[IdxFull(i,j)] = -decomp(i,j); - inv[IdxFull(j,i)] = -decomp(j,i) * inv[IdxFull(j,j)]; - - for (int k = j+1; k < i; ++k) { - inv[IdxFull(i,j)] -= decomp(i,k) * inv[IdxFull(k,j)]; - inv[IdxFull(j,i)] -= decomp(k,i) * inv[IdxFull(j,k)]; - } - if (j+1 <= i) inv[IdxFull(j,i)] /= decomp(i,i); - } - } - // inverses in val_vec - - /*--- Multiply U_inv by L_inv, overwrite decomp_vec. ---*/ - for (int i = 0; i < sz; ++i) - for (int j = 0; j < sz; ++j) { - decomp(i,j) = 0.0; - for (int k = max(i,j); k < sz; ++k) - decomp(i,j) += inv[IdxFull(i,k)] * ((k==j)? 1.0 : inv[IdxFull(k,j)]); - } - - /*--- Permute multiplied matrix to recover A_inv, overwrite val_vec. ---*/ - for (int j = 0; j < sz; ++j) { - int k = perm_vec[j]; - for (int i = k; i < sz; ++i) Set(i, k, decomp(i,j)); - } - - /*--- Decomposition no longer needed. ---*/ - vector().swap(decomp_vec); - - break; - } - default: assert(false && "Default (LU) decomposition failed."); - } - - decomposed = NONE; -#endif -} - -void CSymmetricMatrix::CalcInv_sytri() -{ -#ifdef HAVE_LAPACK - char uplo = 'L'; - int info; - perm_vec.resize(sz); // ipiv array - - /*--- Query the optimum work size. ---*/ - int query = -1; passivedouble tmp; - dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), &tmp, &query, &info); - query = static_cast(tmp); - decomp_vec.resize(query); // work array - - /*--- Factorize and invert. ---*/ - dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &query, &info); - if (info!=0) SU2_MPI::Error("LDLT factorization failed.", CURRENT_FUNCTION); - dsytri_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &info); - if (info!=0) SU2_MPI::Error("Inversion with LDLT factorization failed.", CURRENT_FUNCTION); - - decomposed = NONE; -#endif -} - -void CSymmetricMatrix::CalcInv_potri() -{ -#ifdef HAVE_LAPACK - char uplo = 'L'; - int info; - - dpotrf_(&uplo, &sz, val_vec.data(), &sz, &info); - if (info!=0) SU2_MPI::Error("LLT factorization failed.", CURRENT_FUNCTION); - dpotri_(&uplo, &sz, val_vec.data(), &sz, &info); - if (info!=0) SU2_MPI::Error("Inversion with LLT factorization failed.", CURRENT_FUNCTION); - - decomposed = NONE; -#endif -} - -void CSymmetricMatrix::Invert(const bool is_spd) -{ -#ifdef HAVE_LAPACK - if(is_spd) CalcInv_potri(); - else CalcInv_sytri(); -#else - if(!is_spd) LUDecompose(); - else CholeskyDecompose(); - CalcInv(); -#endif -} - -void CSymmetricMatrix::MatVecMult(passivedouble *v) const -{ - passivedouble *tmp_res = new passivedouble [sz]; - - for (int i=0; i. + */ + +#include "../../include/toolboxes/CSymmetricMatrix.hpp" +#include "../../include/mpi_structure.hpp" + +#if defined(HAVE_MKL) +#include "mkl.h" +#ifndef HAVE_LAPACK +#define HAVE_LAPACK +#endif +#elif defined(HAVE_LAPACK) +/*--- Lapack / Blas routines used in RBF interpolation. ---*/ +extern "C" void dsptrf_(char*, int*, passivedouble*, int*, int*); +extern "C" void dsptri_(char*, int*, passivedouble*, int*, passivedouble*, int*); +extern "C" void dsytrf_(char*, int*, passivedouble*, int*, int*, passivedouble*, int*, int*); +extern "C" void dsytri_(char*, int*, passivedouble*, int*, int*, passivedouble*, int*); +extern "C" void dpotrf_(char*, int*, passivedouble*, int*, int*); +extern "C" void dpotri_(char*, int*, passivedouble*, int*, int*); +extern "C" void dsymm_(char*, char*, int*, int*, passivedouble*, passivedouble*, int*, + passivedouble*, int*, passivedouble*, passivedouble*, int*); +#endif + + +void CSymmetricMatrix::Initialize(int N) +{ + sz = N; + val_vec.resize(sz*sz); + initialized = true; +} + +void CSymmetricMatrix::CholeskyDecompose() +{ +#ifndef HAVE_LAPACK + assert(initialized && "Matrix not initialized."); + + for (int j = 0; j < sz; ++j) { + passivedouble sum = 0.0; + for (int k = 0; k < j; ++k) sum -= pow(Get(j,k), 2); + sum += Get(j,j); + assert(sum > 0.0 && "LLT failed, matrix is not SPD."); + Set(j, j, sqrt(sum)); + + for (int i = j+1; i < sz; ++i) { + passivedouble sum = 0.0; + for (int k = 0; k < j; ++k) sum -= Get(i,k) * Get(j,k); + sum += Get(i,j); + Set(i, j, sum / Get(j,j)); + } + } + decomposed = CHOLESKY; +#endif +} + +void CSymmetricMatrix::LUDecompose() +{ +#ifndef HAVE_LAPACK + assert(initialized && "Matrix not initialized."); + + /*--- Copy matrix values to LU matrix, init permutation vec. ---*/ + decomp_vec.resize(sz*sz); + perm_vec.resize(sz); + for (int i = 0; i < sz; ++i) { + perm_vec[i] = i; + for (int j = i; j < sz; ++j) decomp(j,i) = decomp(i,j) = Get(i,j); + } + + /*--- Decompose LU matrix. ---*/ + for (int j = 0; j < sz-1; ++j) { + /*--- Search for maximum pivot and interchange rows. ---*/ + passivedouble pivot = decomp(j,j); + int pivot_idx = j; + for (int i = j+1; i < sz; ++i) + if (abs(decomp(i,j)) > abs(pivot)) { + pivot = decomp(i,j); + pivot_idx = i; + } + + if (pivot_idx != j) { + swap(perm_vec[j], perm_vec[pivot_idx]); + for (int k = 0; k < sz; ++k) + swap(decomp(j,k), decomp(pivot_idx,k)); + } + + /*--- Perform elimination. ---*/ + for (int k = j+1; k < sz; ++k) decomp(k,j) /= pivot; + + for (int k = j+1; k < sz; ++k) + for (int i = j+1; i < sz; ++i) + decomp(i,k) -= decomp(j,k)*decomp(i,j); + } + + decomposed = LU; +#endif +} + +void CSymmetricMatrix::CalcInv() +{ +#ifndef HAVE_LAPACK + assert(initialized && "Matrix not initialized."); + + /*--- Decompose matrix if not done yet. ---*/ + if (decomposed == NONE) { LUDecompose(); } + + /*--- Compute inverse from decomposed matrices. ---*/ + switch (decomposed) { + + case CHOLESKY: + { + /*--- Initialize inverse matrix. ---*/ + vector inv(sz*sz, 0.0); + + /*--- Compute L inverse. ---*/ + /*--- Solve smaller and smaller systems. ---*/ + for (int j = 0; j < sz; ++j) { + /*--- Forward substitution. ---*/ + inv[IdxSym(j,j)] = 1.0 / Get(j,j); + + for (int i = j+1; i < sz; ++i) { + passivedouble sum = 0.0; + for (int k = j; k < i; ++k) sum -= Get(i,k) * inv[IdxSym(k,j)]; + inv[IdxSym(i,j)] = sum / Get(i,i); + } + } // L inverse in inv + + /*--- Multiply inversed matrices overwrite val_vec. ---*/ + for (int j = 0; j < sz; ++j) + for (int i = j; i < sz; ++i) { + passivedouble sum = 0.0; + for (int k = i; k < sz; ++k) sum += inv[IdxSym(k,i)] * inv[IdxSym(k,j)]; + Set(i, j, sum); + } + + break; + } + + case LU: + { + /*--- Alias val_vec. ---*/ + vector& inv = val_vec; + + /*--- Invert L and U matrices in place. ---*/ + for (int j = 0; j < sz; ++j) { + inv[IdxFull(j,j)] = 1.0 / decomp(j,j); + + for (int i = j+1; i < sz; ++i) { + inv[IdxFull(i,j)] = -decomp(i,j); + inv[IdxFull(j,i)] = -decomp(j,i) * inv[IdxFull(j,j)]; + + for (int k = j+1; k < i; ++k) { + inv[IdxFull(i,j)] -= decomp(i,k) * inv[IdxFull(k,j)]; + inv[IdxFull(j,i)] -= decomp(k,i) * inv[IdxFull(j,k)]; + } + if (j+1 <= i) inv[IdxFull(j,i)] /= decomp(i,i); + } + } + // inverses in val_vec + + /*--- Multiply U_inv by L_inv, overwrite decomp_vec. ---*/ + for (int i = 0; i < sz; ++i) + for (int j = 0; j < sz; ++j) { + decomp(i,j) = 0.0; + for (int k = max(i,j); k < sz; ++k) + decomp(i,j) += inv[IdxFull(i,k)] * ((k==j)? 1.0 : inv[IdxFull(k,j)]); + } + + /*--- Permute multiplied matrix to recover A_inv, overwrite val_vec. ---*/ + for (int j = 0; j < sz; ++j) { + int k = perm_vec[j]; + for (int i = k; i < sz; ++i) Set(i, k, decomp(i,j)); + } + + /*--- Decomposition no longer needed. ---*/ + vector().swap(decomp_vec); + + break; + } + default: assert(false && "Default (LU) decomposition failed."); + } + + decomposed = NONE; +#endif +} + +void CSymmetricMatrix::CalcInv_sytri() +{ +#ifdef HAVE_LAPACK + char uplo = 'L'; + int info; + perm_vec.resize(sz); // ipiv array + + /*--- Query the optimum work size. ---*/ + int query = -1; passivedouble tmp; + dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), &tmp, &query, &info); + query = static_cast(tmp); + decomp_vec.resize(query); // work array + + /*--- Factorize and invert. ---*/ + dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &query, &info); + if (info!=0) SU2_MPI::Error("LDLT factorization failed.", CURRENT_FUNCTION); + dsytri_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &info); + if (info!=0) SU2_MPI::Error("Inversion with LDLT factorization failed.", CURRENT_FUNCTION); + + decomposed = NONE; +#endif +} + +void CSymmetricMatrix::CalcInv_potri() +{ +#ifdef HAVE_LAPACK + char uplo = 'L'; + int info; + + dpotrf_(&uplo, &sz, val_vec.data(), &sz, &info); + if (info!=0) SU2_MPI::Error("LLT factorization failed.", CURRENT_FUNCTION); + dpotri_(&uplo, &sz, val_vec.data(), &sz, &info); + if (info!=0) SU2_MPI::Error("Inversion with LLT factorization failed.", CURRENT_FUNCTION); + + decomposed = NONE; +#endif +} + +void CSymmetricMatrix::Invert(const bool is_spd) +{ +#ifdef HAVE_LAPACK + if(is_spd) CalcInv_potri(); + else CalcInv_sytri(); +#else + if(!is_spd) LUDecompose(); + else CholeskyDecompose(); + CalcInv(); +#endif +} + +void CSymmetricMatrix::MatVecMult(passivedouble *v) const +{ + passivedouble *tmp_res = new passivedouble [sz]; + + for (int i=0; i Date: Wed, 18 Mar 2020 16:34:31 +0000 Subject: [PATCH 032/112] cleanup CMirror, make search hybrid parallel --- .../src/interface_interpolation/CMirror.cpp | 178 +++++++----------- SU2_CFD/src/SU2_CFD.cpp | 1 - SU2_CFD/src/drivers/CDriver.cpp | 4 +- 3 files changed, 67 insertions(+), 116 deletions(-) diff --git a/Common/src/interface_interpolation/CMirror.cpp b/Common/src/interface_interpolation/CMirror.cpp index 0106de67f214..49bcc91b93c0 100644 --- a/Common/src/interface_interpolation/CMirror.cpp +++ b/Common/src/interface_interpolation/CMirror.cpp @@ -37,29 +37,7 @@ CMirror::CMirror(CGeometry ****geometry_container, CConfig **config, unsigned i void CMirror::Set_TransferCoeff(CConfig **config) { - unsigned long iVertex, jVertex; - unsigned long iPoint; - unsigned short iDonor=0, iFace=0, iTarget=0; - - unsigned short nMarkerInt; - unsigned short iMarkerInt; - - int markDonor=0, markTarget=0; - - unsigned int nNodes=0, iNodes=0; - unsigned long nVertexDonor = 0, nVertexTarget= 0; - unsigned long Point_Donor = 0; - unsigned long pGlobalPoint = 0; - int iProcessor; - - unsigned long nLocalFace_Donor = 0, nLocalFaceNodes_Donor=0; - - unsigned long faceindex; - - int nProcessor = size; - - su2double *Buffer_Send_Coeff, *Buffer_Receive_Coeff; - su2double coeff; + const int nProcessor = size; Buffer_Send_nFace_Donor= new unsigned long [1]; Buffer_Send_nFaceNodes_Donor= new unsigned long [1]; @@ -68,10 +46,10 @@ void CMirror::Set_TransferCoeff(CConfig **config) { Buffer_Receive_nFaceNodes_Donor = new unsigned long [nProcessor]; /*--- Number of markers on the interface ---*/ - nMarkerInt = (config[targetZone]->GetMarker_n_ZoneInterface())/2; + const auto nMarkerInt = (config[targetZone]->GetMarker_n_ZoneInterface())/2; /*--- For the number of markers on the interface... ---*/ - for (iMarkerInt=1; iMarkerInt <= nMarkerInt; iMarkerInt++) { + for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { /*--- Procedure: * - Loop through vertices of the aero grid * - Find nearest element and allocate enough space in the aero grid donor point info @@ -79,33 +57,27 @@ void CMirror::Set_TransferCoeff(CConfig **config) { */ /*--- On the donor side: find the tag of the boundary sharing the interface ---*/ - markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); + const auto markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt); /*--- On the target side: find the tag of the boundary sharing the interface ---*/ - markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); + const auto markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt); /*--- Checks if the zone contains the interface, if not continue to the next step ---*/ if(!CheckInterfaceBoundary(markDonor, markTarget)) continue; - if(markDonor != -1) - nVertexDonor = donor_geometry->GetnVertex( markDonor ); - else - nVertexDonor = 0; - - if(markTarget != -1) - nVertexTarget = target_geometry->GetnVertex( markTarget ); - else - nVertexTarget = 0; + unsigned long nVertexDonor = 0, nVertexTarget = 0; + if (markDonor != -1) nVertexDonor = donor_geometry->GetnVertex( markDonor ); + if (markTarget != -1) nVertexTarget = target_geometry->GetnVertex( markTarget ); /*-- Collect the number of donor nodes: re-use 'Face' containers --*/ - nLocalFace_Donor=0; - nLocalFaceNodes_Donor=0; - for (jVertex = 0; jVertexvertex[markDonor][jVertex]->GetNode(); // Local index of jVertex + auto nLocalFace_Donor = 0ul; + auto nLocalFaceNodes_Donor = 0ul; + for (auto jVertex = 0ul; jVertexvertex[markDonor][jVertex]->GetNode(); // Local index of jVertex if (donor_geometry->node[Point_Donor]->GetDomain()) { - nNodes = donor_geometry->vertex[markDonor][jVertex]->GetnDonorPoints(); - nLocalFaceNodes_Donor+=nNodes; + auto nNodes = donor_geometry->vertex[markDonor][jVertex]->GetnDonorPoints(); + nLocalFaceNodes_Donor += nNodes; nLocalFace_Donor++; } } @@ -114,7 +86,6 @@ void CMirror::Set_TransferCoeff(CConfig **config) { Buffer_Send_nFaceNodes_Donor[0] = nLocalFaceNodes_Donor; /*--- Send Interface vertex information --*/ -#ifdef HAVE_MPI SU2_MPI::Allreduce(&nLocalFaceNodes_Donor, &MaxFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); SU2_MPI::Allreduce(&nLocalFace_Donor, &MaxFace_Donor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); SU2_MPI::Allgather(Buffer_Send_nFace_Donor, 1, MPI_UNSIGNED_LONG, @@ -122,58 +93,39 @@ void CMirror::Set_TransferCoeff(CConfig **config) { SU2_MPI::Allgather(Buffer_Send_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, Buffer_Receive_nFaceNodes_Donor, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); MaxFace_Donor++; -#else - nGlobalFace_Donor = nLocalFace_Donor; - nGlobalFaceNodes_Donor = nLocalFaceNodes_Donor; - MaxFaceNodes_Donor = nLocalFaceNodes_Donor; - MaxFace_Donor = nLocalFace_Donor+1; - Buffer_Receive_nFace_Donor[0] = Buffer_Send_nFace_Donor[0]; - Buffer_Receive_nFaceNodes_Donor[0] = Buffer_Send_nFaceNodes_Donor[0]; -#endif - - /*-- Send donor info --*/ - Buffer_Send_FaceIndex = new unsigned long[MaxFace_Donor]; - Buffer_Send_FaceNodes = new unsigned long[MaxFaceNodes_Donor]; - Buffer_Send_GlobalPoint = new long[MaxFaceNodes_Donor]; - Buffer_Send_Coeff = new su2double[MaxFaceNodes_Donor]; + + /*-- Send donor info and init to 0 --*/ + Buffer_Send_FaceIndex = new unsigned long[MaxFace_Donor] (); + Buffer_Send_FaceNodes = new unsigned long[MaxFaceNodes_Donor] (); + Buffer_Send_GlobalPoint = new long[MaxFaceNodes_Donor] (); + auto Buffer_Send_Coeff = new su2double[MaxFaceNodes_Donor] (); Buffer_Receive_FaceIndex = new unsigned long[MaxFace_Donor*nProcessor]; Buffer_Receive_FaceNodes = new unsigned long[MaxFaceNodes_Donor*nProcessor]; Buffer_Receive_GlobalPoint = new long[MaxFaceNodes_Donor*nProcessor]; - Buffer_Receive_Coeff = new su2double[MaxFaceNodes_Donor*nProcessor]; - - for (iVertex=0; iVertexvertex[markDonor][jVertex]->GetNode(); // Local index of jVertex - if (donor_geometry->node[Point_Donor]->GetDomain()) { - nNodes = donor_geometry->vertex[markDonor][jVertex]->GetnDonorPoints(); - for (iDonor=0; iDonornode[Point_Donor]->GetGlobalIndex(); - Buffer_Send_GlobalPoint[nLocalFaceNodes_Donor] = - donor_geometry->vertex[markDonor][jVertex]->GetInterpDonorPoint(iDonor); - Buffer_Send_Coeff[nLocalFaceNodes_Donor] = - donor_geometry->vertex[markDonor][jVertex]->GetDonorCoeff(iDonor); - nLocalFaceNodes_Donor++; - } - Buffer_Send_FaceIndex[nLocalFace_Donor+1] =Buffer_Send_FaceIndex[nLocalFace_Donor]+nNodes; - nLocalFace_Donor++; + auto Point_Donor = donor_geometry->vertex[markDonor][jVertex]->GetNode(); // Local index of jVertex + + if (!donor_geometry->node[Point_Donor]->GetDomain()) continue; + + auto nNodes = donor_geometry->vertex[markDonor][jVertex]->GetnDonorPoints(); + for (auto iDonor = 0; iDonor < nNodes; iDonor++) { + Buffer_Send_FaceNodes[nLocalFaceNodes_Donor] = donor_geometry->node[Point_Donor]->GetGlobalIndex(); + Buffer_Send_GlobalPoint[nLocalFaceNodes_Donor] = + donor_geometry->vertex[markDonor][jVertex]->GetInterpDonorPoint(iDonor); + Buffer_Send_Coeff[nLocalFaceNodes_Donor] = + donor_geometry->vertex[markDonor][jVertex]->GetDonorCoeff(iDonor); + nLocalFaceNodes_Donor++; } + Buffer_Send_FaceIndex[nLocalFace_Donor+1] =Buffer_Send_FaceIndex[nLocalFace_Donor]+nNodes; + nLocalFace_Donor++; } SU2_MPI::Allgather(Buffer_Send_FaceNodes, MaxFaceNodes_Donor, MPI_UNSIGNED_LONG, @@ -186,40 +138,42 @@ void CMirror::Set_TransferCoeff(CConfig **config) { Buffer_Receive_FaceIndex, MaxFace_Donor, MPI_UNSIGNED_LONG, MPI_COMM_WORLD); /*--- Loop over the vertices on the target Marker ---*/ - for (iVertex = 0; iVertexvertex[markTarget][iVertex]; + auto iPoint = target_vertex->GetNode(); - iPoint = target_geometry->vertex[markTarget][iVertex]->GetNode(); if (!target_geometry->node[iPoint]->GetDomain()) continue; - long Global_Point = target_geometry->node[iPoint]->GetGlobalIndex(); - nNodes = 0; - for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { - for (iFace = 0; iFace < Buffer_Receive_nFace_Donor[iProcessor]; iFace++) { - faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face - iNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1]- (unsigned int)faceindex; - for (iTarget=0; iTargetnode[iPoint]->GetGlobalIndex(); + + auto nNodes = 0; + for (int iProcessor = 0; iProcessor < nProcessor; iProcessor++) { + for (auto iFace = 0ul; iFace < Buffer_Receive_nFace_Donor[iProcessor]; iFace++) { + auto faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face + auto iNodes = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1] - faceindex; + for (auto iTarget = 0ul; iTargetvertex[markTarget][iVertex]->SetnDonorPoints(nNodes); - target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); - - iDonor = 0; - for (iProcessor = 0; iProcessor < nProcessor; iProcessor++) { - for (iFace = 0; iFace < Buffer_Receive_nFace_Donor[iProcessor]; iFace++) { - - faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face - iNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1]- (unsigned int)faceindex; - for (iTarget=0; iTargetvertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor,pGlobalPoint); - target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor,coeff); - target_geometry->vertex[markTarget][iVertex]->SetInterpDonorProcessor(iDonor, iProcessor); + target_vertex->SetnDonorPoints(nNodes); + target_vertex->Allocate_DonorInfo(); + + for (int iProcessor = 0, iDonor = 0; iProcessor < nProcessor; iProcessor++) { + for (auto iFace = 0ul; iFace < Buffer_Receive_nFace_Donor[iProcessor]; iFace++) { + + auto faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face + auto iNodes = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1] - faceindex; + + for (auto iTarget = 0ul; iTarget < iNodes; iTarget++) { + if (Global_Point == long(Buffer_Receive_GlobalPoint[faceindex+iTarget])) { + auto coeff = Buffer_Receive_Coeff[faceindex+iTarget]; + auto pGlobalPoint = Buffer_Receive_FaceNodes[faceindex+iTarget]; + target_vertex->SetInterpDonorPoint(iDonor,pGlobalPoint); + target_vertex->SetDonorCoeff(iDonor,coeff); + target_vertex->SetInterpDonorProcessor(iDonor, iProcessor); iDonor++; } } diff --git a/SU2_CFD/src/SU2_CFD.cpp b/SU2_CFD/src/SU2_CFD.cpp index 9fac0ec50e99..229aaab1f7e1 100644 --- a/SU2_CFD/src/SU2_CFD.cpp +++ b/SU2_CFD/src/SU2_CFD.cpp @@ -25,7 +25,6 @@ * License along with SU2. If not, see . */ - #include "../include/SU2_CFD.hpp" /* LIBXSMM include files, if supported. */ diff --git a/SU2_CFD/src/drivers/CDriver.cpp b/SU2_CFD/src/drivers/CDriver.cpp index b1b741c731b6..0d17063686a1 100644 --- a/SU2_CFD/src/drivers/CDriver.cpp +++ b/SU2_CFD/src/drivers/CDriver.cpp @@ -3043,11 +3043,9 @@ void CDriver::Turbomachinery_Preprocessing(CConfig** config, CGeometry**** geome } - - - CDriver::~CDriver(void) {} + CFluidDriver::CFluidDriver(char* confFile, unsigned short val_nZone, SU2_Comm MPICommunicator) : CDriver(confFile, val_nZone, MPICommunicator, false) { Max_Iter = config_container[ZONE_0]->GetnInner_Iter(); } From fe8fcc9e6fb4b0a858ab3f490b3555504306d2b1 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 18 Mar 2020 18:11:19 +0000 Subject: [PATCH 033/112] generalize CNearestNeighbor for KNN --- Common/include/CConfig.hpp | 6 ++ .../CNearestNeighbor.hpp | 7 +- .../interface_interpolation/CSlidingMesh.hpp | 9 ++- Common/src/CConfig.cpp | 10 +-- .../CNearestNeighbor.cpp | 72 ++++++++++++------- .../interface_interpolation/CSlidingMesh.cpp | 11 +-- 6 files changed, 71 insertions(+), 44 deletions(-) diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp index 877f073cc08e..d3f028bcf4b0 100644 --- a/Common/include/CConfig.hpp +++ b/Common/include/CConfig.hpp @@ -988,6 +988,7 @@ class CConfig { unsigned short Pred_Order; /*!< \brief Order of the predictor for FSI applications. */ unsigned short Kind_Interpolation; /*!< \brief type of interpolation to use for FSI applications. */ bool ConservativeInterpolation; /*!< \brief Conservative approach for non matching mesh interpolation. */ + unsigned short NumNearestNeighbors; /*!< \brief Number of neighbors used for Nearest Neighbor interpolation. */ unsigned short Kind_RadialBasisFunction; /*!< \brief type of radial basis function to use for radial basis FSI. */ bool RadialBasisFunction_PolynomialOption; /*!< \brief Option of whether to include polynomial terms in Radial Basis Function Interpolation or not. */ su2double RadialBasisFunction_Parameter; /*!< \brief Radial basis function parameter (radius). */ @@ -8793,6 +8794,11 @@ class CConfig { */ su2double GetRadialBasisFunctionPruneTol(void) const { return RadialBasisFunction_PruneTol; } + /*! + * \brief Get the number of donor points to use in Nearest Neighbor interpolation. + */ + unsigned short GetNumNearestNeighbors(void) const { return NumNearestNeighbors; } + /*! * \brief Get the kind of inlet face interpolation function to use. */ diff --git a/Common/include/interface_interpolation/CNearestNeighbor.hpp b/Common/include/interface_interpolation/CNearestNeighbor.hpp index 95dcdfdd3e2f..5c16ed2cdb29 100644 --- a/Common/include/interface_interpolation/CNearestNeighbor.hpp +++ b/Common/include/interface_interpolation/CNearestNeighbor.hpp @@ -29,7 +29,10 @@ #include "CInterpolator.hpp" /*! - * \brief Nearest Neighbor interpolation. + * \brief Nearest Neighbor(s) interpolation. + * \note The closest k neighbors are used for IDW interpolation, the computational + * cost of setting up the interpolation is O(N^2 log(k)), this can be improved + * by using a kd-tree. */ class CNearestNeighbor final : public CInterpolator { public: @@ -43,7 +46,7 @@ class CNearestNeighbor final : public CInterpolator { CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); /*! - * \brief Set up transfer matrix defining relation between two meshes + * \brief Set up transfer matrix defining relation between two meshes. * \param[in] config - Definition of the particular problem. */ void Set_TransferCoeff(CConfig **config) override; diff --git a/Common/include/interface_interpolation/CSlidingMesh.hpp b/Common/include/interface_interpolation/CSlidingMesh.hpp index 43abee84af98..3cbb419d15db 100644 --- a/Common/include/interface_interpolation/CSlidingMesh.hpp +++ b/Common/include/interface_interpolation/CSlidingMesh.hpp @@ -30,6 +30,8 @@ /*! * \brief Sliding mesh approach. + * \note The algorithm is based on Rinaldi et al. "Flux-conserving treatment of non-conformal interfaces + * for finite-volume discritization of conservation laws" 2015, Comp. Fluids, 120, pp 126-139 */ class CSlidingMesh final : public CInterpolator { public: @@ -81,7 +83,8 @@ class CSlidingMesh final : public CInterpolator { * \param[in] B3 - third point of triangle B * \param[in] Direction - vector normal to projection plane */ - su2double Compute_Triangle_Intersection(su2double* A1, su2double* A2, su2double* A3, su2double* B1, su2double* B2, su2double* B3, su2double* Direction); + su2double Compute_Triangle_Intersection(su2double* A1, su2double* A2, su2double* A3, su2double* B1, + su2double* B2, su2double* B3, su2double* Direction); /*! * \brief For 3-Dimensional grids, compute intersection area between two triangle projected on a given plane @@ -93,7 +96,7 @@ class CSlidingMesh final : public CInterpolator { * \param[in] Q2 - second point of triangle B * \param[in] Q3 - third point of triangle B */ - su2double ComputeIntersectionArea( su2double* P1, su2double* P2, su2double* P3, su2double* Q1, su2double* Q2, su2double* Q3 ); + su2double ComputeIntersectionArea(su2double* P1, su2double* P2, su2double* P3, su2double* Q1, su2double* Q2, su2double* Q3); /*! * \brief For 2-Dimensional grids, check whether, and compute, two lines are intersecting @@ -103,7 +106,7 @@ class CSlidingMesh final : public CInterpolator { * \param[in] B2 - second defining second line * \param[in] IntersectionPoint - Container for intersection coordinates */ - void ComputeLineIntersectionPoint( su2double* A1, su2double* A2, su2double* B1, su2double* B2, su2double* IntersectionPoint ); + void ComputeLineIntersectionPoint(su2double* A1, su2double* A2, su2double* B1, su2double* B2, su2double* IntersectionPoint); /*! * \brief For N-Dimensional grids, check whether a point is inside a triangle specified by 3 T points diff --git a/Common/src/CConfig.cpp b/Common/src/CConfig.cpp index 2899aab3328f..4f0328015ace 100644 --- a/Common/src/CConfig.cpp +++ b/Common/src/CConfig.cpp @@ -2457,17 +2457,17 @@ void CConfig::SetConfig_Options() { addBoolOption("WRT_FORCES_BREAKDOWN", Wrt_ForcesBreakdown, false); - - /* DESCRIPTION: Use conservative approach for interpolating between meshes. - * Options: NO, YES \ingroup Config */ - addBoolOption("CONSERVATIVE_INTERPOLATION", ConservativeInterpolation, true); - /*!\par KIND_INTERPOLATION \n * DESCRIPTION: Type of interpolation to use for multi-zone problems. \n OPTIONS: see \link Interpolator_Map \endlink * Sets Kind_Interpolation \ingroup Config */ addEnumOption("KIND_INTERPOLATION", Kind_Interpolation, Interpolator_Map, NEAREST_NEIGHBOR); + /* DESCRIPTION: Use conservative approach for interpolating between meshes. */ + addBoolOption("CONSERVATIVE_INTERPOLATION", ConservativeInterpolation, true); + + addUnsignedShortOption("NUM_NEAREST_NEIGHBORS", NumNearestNeighbors, 1); + /*!\par KIND_INTERPOLATION \n * DESCRIPTION: Type of radial basis function to use for radial basis function interpolation. \n OPTIONS: see \link RadialBasis_Map \endlink * Sets Kind_RadialBasis \ingroup Config diff --git a/Common/src/interface_interpolation/CNearestNeighbor.cpp b/Common/src/interface_interpolation/CNearestNeighbor.cpp index 04843729aa97..7d74a8fb93dc 100644 --- a/Common/src/interface_interpolation/CNearestNeighbor.cpp +++ b/Common/src/interface_interpolation/CNearestNeighbor.cpp @@ -30,6 +30,15 @@ #include "../../include/geometry/CGeometry.hpp" +/*! \brief Helper struct to search sort neighbours according to distance. */ +struct DonorInfo { + su2double dist; + unsigned long pidx; + int proc; + DonorInfo(su2double d = 0.0, unsigned long i = 0, int p = 0) : + dist(d), pidx(i), proc(p) {} +}; + CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { Set_TransferCoeff(config); @@ -37,9 +46,8 @@ CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, CConfig **c void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { - /*--- By definition, one donor point per target point. ---*/ - constexpr auto numDonor = 1; - constexpr auto idxDonor = 0; + /*--- Desired number of donor points. ---*/ + const auto nDonor = config[donorZone]->GetNumNearestNeighbors(); const su2double eps = numeric_limits::epsilon(); @@ -50,6 +58,8 @@ void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { Buffer_Send_nVertex_Donor = new unsigned long [1]; Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; + vector > DonorInfoVec(omp_get_max_threads()); + /*--- Cycle over nMarkersInt interface to determine communication pattern. ---*/ for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { @@ -70,6 +80,9 @@ void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { /* Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor. */ Determine_ArraySize(false, markDonor, markTarget, nVertexDonor, nDim); + const auto nPossibleDonor = accumulate(Buffer_Receive_nVertex_Donor, + Buffer_Receive_nVertex_Donor+nProcessor, 0ul); + Buffer_Send_Coord = new su2double [ MaxLocalVertex_Donor * nDim ]; Buffer_Send_GlobalPoint = new long [ MaxLocalVertex_Donor ]; Buffer_Receive_Coord = new su2double [ nProcessor * MaxLocalVertex_Donor * nDim ]; @@ -78,8 +91,14 @@ void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { /*-- Collect coordinates and global point indices. ---*/ Collect_VertexInfo( false, markDonor, markTarget, nVertexDonor, nDim ); - /*--- Compute the closest donor point to each target. ---*/ - SU2_OMP_PARALLEL_(for schedule(dynamic,roundUpDiv(nVertexTarget,2*omp_get_max_threads()))) + /*--- Find the closest donor points to each target. ---*/ + SU2_OMP_PARALLEL + { + /*--- Working array for this thread. ---*/ + auto& donorInfo = DonorInfoVec[omp_get_thread_num()]; + donorInfo.resize(nPossibleDonor); + + SU2_OMP_FOR_DYN(roundUpDiv(nVertexTarget,2*omp_get_max_threads())) for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget++) { auto target_vertex = target_geometry->vertex[markTarget][iVertexTarget]; @@ -90,38 +109,41 @@ void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { /*--- Coordinates of the target point. ---*/ const su2double* Coord_i = target_geometry->node[Point_Target]->GetCoord(); - su2double mindist = 1e20; - long pGlobalPoint = 0; - int pProcessor = 0; - - for (int iProcessor = 0; iProcessor < nProcessor; ++iProcessor) { + /*--- Compute all distances. ---*/ + for (int iProcessor = 0, iDonor = 0; iProcessor < nProcessor; ++iProcessor) { for (auto jVertex = 0ul; jVertex < Buffer_Receive_nVertex_Donor[iProcessor]; ++jVertex) { const auto idx = iProcessor*MaxLocalVertex_Donor + jVertex; - + const auto pGlobalPoint = Buffer_Receive_GlobalPoint[idx]; const su2double* Coord_j = &Buffer_Receive_Coord[idx*nDim]; + const auto dist2 = PointsSquareDistance(nDim, Coord_i, Coord_j); - const auto dist = PointsSquareDistance(nDim, Coord_i, Coord_j); + donorInfo[iDonor++] = DonorInfo(dist2, pGlobalPoint, iProcessor); + } + } - if (dist < mindist) { - mindist = dist; - pProcessor = iProcessor; - pGlobalPoint = Buffer_Receive_GlobalPoint[idx]; - } + /*--- Find k closest points. ---*/ + partial_sort(donorInfo.begin(), donorInfo.begin()+nDonor, donorInfo.end(), + [](const DonorInfo& a, const DonorInfo& b){return a.dist < b.dist;}); - /*--- Test for "exact" match. ---*/ - if (dist < eps) break; - } + /*--- Compute interpolation numerators and denominator. ---*/ + su2double denom = 0.0; + for (auto iDonor = 0ul; iDonor < nDonor; ++iDonor) { + donorInfo[iDonor].dist = 1.0 / (donorInfo[iDonor].dist + eps); + denom += donorInfo[iDonor].dist; } - /*--- Store matching pair. ---*/ - target_vertex->SetnDonorPoints(numDonor); + /*--- Set interpolation coefficients. ---*/ + target_vertex->SetnDonorPoints(nDonor); target_vertex->Allocate_DonorInfo(); - target_vertex->SetInterpDonorPoint(idxDonor, pGlobalPoint); - target_vertex->SetInterpDonorProcessor(idxDonor, pProcessor); - target_vertex->SetDonorCoeff(idxDonor, 1.0); + for (auto iDonor = 0ul; iDonor < nDonor; ++iDonor) { + target_vertex->SetInterpDonorPoint(iDonor, donorInfo[iDonor].pidx); + target_vertex->SetInterpDonorProcessor(iDonor, donorInfo[iDonor].proc); + target_vertex->SetDonorCoeff(iDonor, donorInfo[iDonor].dist/denom); + } } + } // end SU2_OMP_PARALLEL delete[] Buffer_Send_Coord; delete[] Buffer_Send_GlobalPoint; diff --git a/Common/src/interface_interpolation/CSlidingMesh.cpp b/Common/src/interface_interpolation/CSlidingMesh.cpp index 62084eb55afd..b76804529b90 100644 --- a/Common/src/interface_interpolation/CSlidingMesh.cpp +++ b/Common/src/interface_interpolation/CSlidingMesh.cpp @@ -37,14 +37,7 @@ CSlidingMesh::CSlidingMesh(CGeometry ****geometry_container, CConfig **config, u void CSlidingMesh::Set_TransferCoeff(CConfig **config) { - /* --- This routine sets the transfer coefficient for sliding mesh approach --- */ - - /* - * The algorithm is based on Rinaldi et al. "Flux-conserving treatment of non-conformal interfaces - * for finite-volume discritization of conservation laws" 2015, Comp. Fluids, 120, pp 126-139 - */ - - /* 0 - Variable declaration - */ + /* 0 - Variable declaration */ /* --- General variables --- */ @@ -102,7 +95,7 @@ void CSlidingMesh::Set_TransferCoeff(CConfig **config) { su2double *donor_iMidEdge_point, *donor_jMidEdge_point; su2double **donor_element, *DonorPoint_Coord; - /* 1 - Variable pre-processing - */ + /* 1 - Variable pre-processing */ nDim = donor_geometry->GetnDim(); From dd1cdcedaf10a294d40a9655d29dec566260c958 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 18 Mar 2020 19:16:03 +0000 Subject: [PATCH 034/112] more cleanup, try to recover behavior of testcase --- .../CNearestNeighbor.cpp | 31 ++- SU2_CFD/include/interfaces/CInterface.hpp | 22 +- SU2_CFD/src/interfaces/CInterface.cpp | 210 +++--------------- .../src/interfaces/cfd/CSlidingInterface.cpp | 41 +--- 4 files changed, 70 insertions(+), 234 deletions(-) diff --git a/Common/src/interface_interpolation/CNearestNeighbor.cpp b/Common/src/interface_interpolation/CNearestNeighbor.cpp index 7d74a8fb93dc..aa40c4ea433c 100644 --- a/Common/src/interface_interpolation/CNearestNeighbor.cpp +++ b/Common/src/interface_interpolation/CNearestNeighbor.cpp @@ -47,7 +47,7 @@ CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, CConfig **c void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { /*--- Desired number of donor points. ---*/ - const auto nDonor = config[donorZone]->GetNumNearestNeighbors(); + const auto nDonor = max(config[donorZone]->GetNumNearestNeighbors(), 1); const su2double eps = numeric_limits::epsilon(); @@ -126,21 +126,28 @@ void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { partial_sort(donorInfo.begin(), donorInfo.begin()+nDonor, donorInfo.end(), [](const DonorInfo& a, const DonorInfo& b){return a.dist < b.dist;}); - /*--- Compute interpolation numerators and denominator. ---*/ - su2double denom = 0.0; - for (auto iDonor = 0ul; iDonor < nDonor; ++iDonor) { - donorInfo[iDonor].dist = 1.0 / (donorInfo[iDonor].dist + eps); - denom += donorInfo[iDonor].dist; - } - /*--- Set interpolation coefficients. ---*/ target_vertex->SetnDonorPoints(nDonor); target_vertex->Allocate_DonorInfo(); - for (auto iDonor = 0ul; iDonor < nDonor; ++iDonor) { - target_vertex->SetInterpDonorPoint(iDonor, donorInfo[iDonor].pidx); - target_vertex->SetInterpDonorProcessor(iDonor, donorInfo[iDonor].proc); - target_vertex->SetDonorCoeff(iDonor, donorInfo[iDonor].dist/denom); + if (nDonor > 1) { + /*--- Compute interpolation numerators and denominator. ---*/ + su2double denom = 0.0; + for (auto iDonor = 0ul; iDonor < nDonor; ++iDonor) { + donorInfo[iDonor].dist = 1.0 / (donorInfo[iDonor].dist + eps); + denom += donorInfo[iDonor].dist; + } + + for (auto iDonor = 0ul; iDonor < nDonor; ++iDonor) { + target_vertex->SetInterpDonorPoint(iDonor, donorInfo[iDonor].pidx); + target_vertex->SetInterpDonorProcessor(iDonor, donorInfo[iDonor].proc); + target_vertex->SetDonorCoeff(iDonor, donorInfo[iDonor].dist/denom); + } + } + else { + target_vertex->SetInterpDonorPoint(0, donorInfo[0].pidx); + target_vertex->SetInterpDonorProcessor(0, donorInfo[0].proc); + target_vertex->SetDonorCoeff(0, 1.0); } } } // end SU2_OMP_PARALLEL diff --git a/SU2_CFD/include/interfaces/CInterface.hpp b/SU2_CFD/include/interfaces/CInterface.hpp index 6c3a2c8cab84..ecd5b540a4bd 100644 --- a/SU2_CFD/include/interfaces/CInterface.hpp +++ b/SU2_CFD/include/interfaces/CInterface.hpp @@ -7,7 +7,7 @@ * * SU2 Project Website: https://su2code.github.io * - * The SU2 Project is maintained by the SU2 Foundation + * The SU2 Project is maintained by the SU2 Foundation * (http://su2foundation.org) * * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md) @@ -56,20 +56,20 @@ using namespace std; class CInterface { protected: - int rank, /*!< \brief MPI Rank. */ - size; /*!< \brief MPI Size. */ + const int rank; /*!< \brief MPI Rank. */ + const int size; /*!< \brief MPI Size. */ - su2double *Physical_Constants; - su2double *Donor_Variable; - su2double *Target_Variable; - bool valAggregated; + su2double *Physical_Constants = nullptr; + su2double *Donor_Variable = nullptr; + su2double *Target_Variable = nullptr; + bool valAggregated = false; /*--- Mixing Plane interface variable ---*/ - su2double *SpanValueCoeffTarget; - unsigned short *SpanLevelDonor; - unsigned short nSpanMaxAllZones; + su2double *SpanValueCoeffTarget = nullptr; + unsigned short *SpanLevelDonor = nullptr; + unsigned short nSpanMaxAllZones = 0; - unsigned short nVar; + unsigned short nVar = 0; public: /*! diff --git a/SU2_CFD/src/interfaces/CInterface.cpp b/SU2_CFD/src/interfaces/CInterface.cpp index 33b5ef6ad62a..455c8b8d96b9 100644 --- a/SU2_CFD/src/interfaces/CInterface.cpp +++ b/SU2_CFD/src/interfaces/CInterface.cpp @@ -6,7 +6,7 @@ * * SU2 Project Website: https://su2code.github.io * - * The SU2 Project is maintained by the SU2 Foundation + * The SU2 Project is maintained by the SU2 Foundation * (http://su2foundation.org) * * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md) @@ -26,79 +26,42 @@ */ #include "../../include/interfaces/CInterface.hpp" +#include "../../../Common/include/interface_interpolation/CInterpolator.hpp" -CInterface::CInterface(void) { - - rank = SU2_MPI::GetRank(); - size = SU2_MPI::GetSize(); - - Physical_Constants = NULL; - Donor_Variable = NULL; - Target_Variable = NULL; - SpanLevelDonor = NULL; - SpanValueCoeffTarget = NULL; - - nVar = 0; - +CInterface::CInterface(void) : + rank(SU2_MPI::GetRank()), + size(SU2_MPI::GetSize()) { } -CInterface::CInterface(unsigned short val_nVar, unsigned short val_nConst, CConfig *config) { - - rank = SU2_MPI::GetRank(); - size = SU2_MPI::GetSize(); +CInterface::CInterface(unsigned short val_nVar, unsigned short val_nConst, CConfig *config) : + rank(SU2_MPI::GetRank()), + size(SU2_MPI::GetSize()), + nVar(val_nVar) { - Physical_Constants = NULL; - Donor_Variable = NULL; - Target_Variable = NULL; - - unsigned short iVar; - - Physical_Constants = new su2double[val_nConst]; - Donor_Variable = new su2double[val_nVar]; - Target_Variable = new su2double[val_nVar]; + Physical_Constants = new su2double[val_nConst] (); + Donor_Variable = new su2double[val_nVar] (); + Target_Variable = new su2double[val_nVar] (); /*--- By default, the value is aggregated in the transfer routine ---*/ valAggregated = true; - - nVar = val_nVar; - - for (iVar = 0; iVar < nVar; iVar++) { - Donor_Variable[iVar] = 0.0; - Target_Variable[iVar] = 0.0; - } - - for (iVar = 0; iVar < val_nConst; iVar++) { - Physical_Constants[iVar] = 0.0; - } - - SpanLevelDonor = NULL; - SpanValueCoeffTarget = NULL; - } CInterface::~CInterface(void) { - if (Physical_Constants != NULL) delete [] Physical_Constants; - if (Donor_Variable != NULL) delete [] Donor_Variable; - if (Target_Variable != NULL) delete [] Target_Variable; - - if (SpanValueCoeffTarget != NULL) delete[] SpanValueCoeffTarget; - if (SpanLevelDonor != NULL) delete[] SpanLevelDonor; + delete [] Physical_Constants; + delete [] Donor_Variable; + delete [] Target_Variable; + delete[] SpanValueCoeffTarget; + delete[] SpanLevelDonor; } void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution, CGeometry *donor_geometry, CGeometry *target_geometry, CConfig *donor_config, CConfig *target_config) { - - unsigned short nMarkerInt, nMarkerDonor, nMarkerTarget; // Number of markers on the interface, donor and target side - unsigned short iMarkerInt, iMarkerDonor, iMarkerTarget; // Variables for iteration over markers int Marker_Donor, Marker_Target; - int Target_check, Donor_check; - - unsigned long iVertex; // Variables for iteration over vertices and nodes - + unsigned long iVertex; unsigned short iVar; GetPhysical_Constants(donor_solution, target_solution, donor_geometry, target_geometry, @@ -107,14 +70,8 @@ void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution unsigned long Point_Donor_Global, Donor_Global_Index; unsigned long Point_Donor, Point_Target; -#ifdef HAVE_MPI - int *Buffer_Recv_mark = NULL, iRank; - - if (rank == MASTER_NODE) - Buffer_Recv_mark = new int[size]; -#endif - - unsigned long Buffer_Send_nVertexDonor[1], *Buffer_Recv_nVertexDonor; + unsigned long Buffer_Send_nVertexDonor[1]; + unsigned long *Buffer_Recv_nVertexDonor = new unsigned long[size]; unsigned long iLocalVertex = 0; unsigned long nLocalVertexDonor = 0, nLocalVertexDonorOwned = 0; @@ -125,98 +82,23 @@ void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution unsigned long nBuffer_BcastVariables = 0, nBuffer_BcastIndices = 0; - int nProcessor = 0; + const int nProcessor = size; /*--- Number of markers on the FSI interface ---*/ - nMarkerInt = (donor_config->GetMarker_n_ZoneInterface())/2; - nMarkerTarget = target_config->GetnMarker_All(); - nMarkerDonor = donor_config->GetnMarker_All(); - - nProcessor = size; + const auto nMarkerInt = donor_config->GetMarker_n_ZoneInterface()/2; /*--- Outer loop over the markers on the FSI interface: compute one by one ---*/ /*--- The tags are always an integer greater than 1: loop from 1 to nMarkerFSI ---*/ - for (iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { - - Buffer_Recv_nVertexDonor = NULL; - - Marker_Donor = -1; - Marker_Target = -1; - - /*--- The donor and target markers are tagged with the same index. - *--- This is independent of the MPI domain decomposition. - *--- We need to loop over all markers on both sides and get the number of nodes - *--- that belong to each FSI marker for each processor ---*/ - - /*--- On the donor side ---*/ - - for (iMarkerDonor = 0; iMarkerDonor < nMarkerDonor; iMarkerDonor++) { - /*--- If the tag GetMarker_All_ZoneInterface(iMarkerDonor) equals the index we are looping at ---*/ - if ( donor_config->GetMarker_All_ZoneInterface(iMarkerDonor) == iMarkerInt ) { - /*--- Store the identifier for the structural marker ---*/ - Marker_Donor = iMarkerDonor; - /*--- Exit the for loop: we have found the local index for iMarkerFSI on the FEA side ---*/ - break; - } - } - - /*--- On the target side we only have to identify the marker; - * then we'll loop over it and retrieve from the donor points ---*/ - - for (iMarkerTarget = 0; iMarkerTarget < nMarkerTarget; iMarkerTarget++) { - /*--- If the tag GetMarker_All_ZoneInterface(iMarkerFlow) equals the index we are looping at ---*/ - if ( target_config->GetMarker_All_ZoneInterface(iMarkerTarget) == iMarkerInt ) { - /*--- Store the identifier for the fluid marker ---*/ - Marker_Target = iMarkerTarget; - /*--- Exit the for loop: we have found the local index for iMarkerFSI on the FEA side ---*/ - break; - } - } - -#ifdef HAVE_MPI - - Donor_check = -1; - Target_check = -1; - - /*--- We gather a vector in MASTER_NODE that determines if the boundary is not on the processor - * because of the partition or because the zone does not include it ---*/ - - SU2_MPI::Gather(&Marker_Donor , 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - - if (rank == MASTER_NODE) { - for (iRank = 0; iRank < nProcessor; iRank++) { - if( Buffer_Recv_mark[iRank] != -1 ) { - Donor_check = Buffer_Recv_mark[iRank]; - break; - } - } - } - - SU2_MPI::Bcast(&Donor_check , 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - - SU2_MPI::Gather(&Marker_Target, 1, MPI_INT, Buffer_Recv_mark, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); - - if (rank == MASTER_NODE) { - for (iRank = 0; iRank < nProcessor; iRank++) { - if( Buffer_Recv_mark[iRank] != -1 ) { - Target_check = Buffer_Recv_mark[iRank]; - break; - } - } - } + for (unsigned short iMarkerInt = 1; iMarkerInt <= nMarkerInt; iMarkerInt++) { - SU2_MPI::Bcast(&Target_check, 1, MPI_INT, MASTER_NODE, MPI_COMM_WORLD); + /*--- Check if this interface connects the two zones, if not continue. ---*/ -#else - Donor_check = Marker_Donor; - Target_check = Marker_Target; -#endif + Marker_Donor = CInterpolator::Find_InterfaceMarker(donor_config, iMarkerInt); + Marker_Target = CInterpolator::Find_InterfaceMarker(target_config, iMarkerInt); - if(Target_check == -1 || Donor_check == -1) { - continue; - } + if(!CInterpolator::CheckInterfaceBoundary(Marker_Donor, Marker_Target)) continue; nLocalVertexDonorOwned = 0; nLocalVertexDonor = 0; @@ -233,10 +115,6 @@ void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution Buffer_Send_nVertexDonor[0] = nLocalVertexDonor; // Retrieve total number of vertices on Donor marker - // Allocate memory to receive how many vertices are on each rank on the structural side - if (rank == MASTER_NODE) Buffer_Recv_nVertexDonor = new unsigned long[size]; - -#ifdef HAVE_MPI /*--- We receive MaxLocalVertexDonor as the maximum number of vertices * in one single processor on the donor side---*/ SU2_MPI::Allreduce(&nLocalVertexDonor, &MaxLocalVertexDonor, 1, MPI_UNSIGNED_LONG, MPI_MAX, MPI_COMM_WORLD); @@ -245,13 +123,8 @@ void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution SU2_MPI::Allreduce(&nLocalVertexDonorOwned, &TotalVertexDonor, 1, MPI_UNSIGNED_LONG, MPI_SUM, MPI_COMM_WORLD); /*--- We gather a vector in MASTER_NODE that determines how many elements are there * on each processor on the structural side ---*/ - SU2_MPI::Gather(&Buffer_Send_nVertexDonor, 1, MPI_UNSIGNED_LONG, Buffer_Recv_nVertexDonor, 1, - MPI_UNSIGNED_LONG, MASTER_NODE, MPI_COMM_WORLD); -#else - MaxLocalVertexDonor = nLocalVertexDonor; - TotalVertexDonor = nLocalVertexDonorOwned; - Buffer_Recv_nVertexDonor[0] = Buffer_Send_nVertexDonor[0]; -#endif + SU2_MPI::Gather(&Buffer_Send_nVertexDonor, 1, MPI_UNSIGNED_LONG, + Buffer_Recv_nVertexDonor, 1, MPI_UNSIGNED_LONG, MASTER_NODE, MPI_COMM_WORLD); /*--- We will be gathering the donor information into the master node ---*/ nBuffer_DonorVariables = MaxLocalVertexDonor * nVar; @@ -308,20 +181,12 @@ void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution } -#ifdef HAVE_MPI /*--- Once all the messages have been prepared, we gather them all into the MASTER_NODE ---*/ SU2_MPI::Gather(Buffer_Send_DonorVariables, nBuffer_DonorVariables, MPI_DOUBLE, Buffer_Recv_DonorVariables, nBuffer_DonorVariables, MPI_DOUBLE, MASTER_NODE, MPI_COMM_WORLD); SU2_MPI::Gather(Buffer_Send_DonorIndices, nBuffer_DonorIndices, MPI_LONG, Buffer_Recv_DonorIndices, nBuffer_DonorIndices, MPI_LONG, MASTER_NODE, MPI_COMM_WORLD); -#else - for (unsigned long iVariable = 0; iVariable < nBuffer_DonorVariables; iVariable++) - Buffer_Recv_DonorVariables[iVariable] = Buffer_Send_DonorVariables[iVariable]; - for (unsigned long iVariable = 0; iVariable < nBuffer_DonorIndices; iVariable++) - Buffer_Recv_DonorIndices[iVariable] = Buffer_Send_DonorIndices[iVariable]; -#endif - /*--- Now we pack the information to send it over to the different processors ---*/ if (rank == MASTER_NODE) { @@ -352,10 +217,8 @@ void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution } -#ifdef HAVE_MPI SU2_MPI::Bcast(Buffer_Bcast_Variables, nBuffer_BcastVariables, MPI_DOUBLE, MASTER_NODE, MPI_COMM_WORLD); SU2_MPI::Bcast(Buffer_Bcast_Indices, nBuffer_BcastIndices, MPI_LONG, MASTER_NODE, MPI_COMM_WORLD); -#endif long indexPoint_iVertex; unsigned short iDonorPoint, nDonorPoints; @@ -418,18 +281,11 @@ void CInterface::BroadcastData(CSolver *donor_solution, CSolver *target_solution delete [] Buffer_Bcast_Variables; delete [] Buffer_Bcast_Indices; - if (rank == MASTER_NODE) { - delete [] Buffer_Recv_nVertexDonor; - delete [] Buffer_Recv_DonorVariables; - delete [] Buffer_Recv_DonorIndices; - } - + delete [] Buffer_Recv_DonorVariables; + delete [] Buffer_Recv_DonorIndices; } -#ifdef HAVE_MPI - if (rank == MASTER_NODE && Buffer_Recv_mark != NULL) - delete [] Buffer_Recv_mark; -#endif + delete [] Buffer_Recv_nVertexDonor; } void CInterface::PreprocessAverage(CGeometry *donor_geometry, CGeometry *target_geometry, @@ -449,7 +305,6 @@ void CInterface::PreprocessAverage(CGeometry *donor_geometry, CGeometry *target_ int *BuffMarkerDonor, *BuffDonorFlag; #endif - nMarkerDonor = donor_geometry->GetnMarker(); nMarkerTarget = target_geometry->GetnMarker(); //TODO turbo this approach only works if all the turboamchinery marker @@ -574,6 +429,7 @@ void CInterface::PreprocessAverage(CGeometry *donor_geometry, CGeometry *target_ void CInterface::AllgatherAverage(CSolver *donor_solution, CSolver *target_solution, CGeometry *donor_geometry, CGeometry *target_geometry, CConfig *donor_config, CConfig *target_config, unsigned short iMarkerInt){ + unsigned short nMarkerDonor, nMarkerTarget; // Number of markers on the interface, donor and target side unsigned short iMarkerDonor, iMarkerTarget; // Variables for iteration over markers unsigned short iSpan, nSpanDonor, nSpanTarget; diff --git a/SU2_CFD/src/interfaces/cfd/CSlidingInterface.cpp b/SU2_CFD/src/interfaces/cfd/CSlidingInterface.cpp index 242a5d6bddc7..a8cf13a71dd5 100644 --- a/SU2_CFD/src/interfaces/cfd/CSlidingInterface.cpp +++ b/SU2_CFD/src/interfaces/cfd/CSlidingInterface.cpp @@ -7,7 +7,7 @@ * * SU2 Project Website: https://su2code.github.io * - * The SU2 Project is maintained by the SU2 Foundation + * The SU2 Project is maintained by the SU2 Foundation * (http://su2foundation.org) * * Copyright 2012-2020, SU2 Contributors (cf. AUTHORS.md) @@ -29,52 +29,25 @@ #include "../../../include/interfaces/cfd/CSlidingInterface.hpp" -CSlidingInterface::CSlidingInterface(void) : CInterface() { - -} +CSlidingInterface::CSlidingInterface(void) : CInterface() { } CSlidingInterface::CSlidingInterface(unsigned short val_nVar, unsigned short val_nConst, CConfig *config) : CInterface() { - rank = SU2_MPI::GetRank(); - size = SU2_MPI::GetSize(); - - Physical_Constants = NULL; - Donor_Variable = NULL; - Target_Variable = NULL; - - unsigned short iVar; - - Physical_Constants = new su2double[val_nConst]; - Donor_Variable = new su2double[val_nVar]; - - Target_Variable = new su2double[val_nVar+1]; + Physical_Constants = new su2double[val_nConst] (); + Donor_Variable = new su2double[val_nVar] (); + Target_Variable = new su2double[val_nVar+1] (); valAggregated = false; nVar = val_nVar; - - for (iVar = 0; iVar < nVar; iVar++) { - Donor_Variable[iVar] = 0.0; - Target_Variable[iVar] = 0.0; - } - - for (iVar = 0; iVar < val_nConst; iVar++) { - Physical_Constants[iVar] = 0.0; - } - -} - -CSlidingInterface::~CSlidingInterface(void) { - } +CSlidingInterface::~CSlidingInterface(void) { } void CSlidingInterface::GetPhysical_Constants(CSolver *donor_solution, CSolver *target_solution, CGeometry *donor_geometry, CGeometry *target_geometry, - CConfig *donor_config, CConfig *target_config) { - -} + CConfig *donor_config, CConfig *target_config) { } void CSlidingInterface::GetDonor_Variable(CSolver *donor_solution, CGeometry *donor_geometry, CConfig *donor_config, unsigned long Marker_Donor, From c0504ff15275d77db3ffe28d797515ba9de9f162 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Wed, 18 Mar 2020 19:56:21 +0000 Subject: [PATCH 035/112] fix build issues --- Common/src/interface_interpolation/CNearestNeighbor.cpp | 9 +++++---- Common/src/linear_algebra/CSysMatrix.cpp | 2 +- SU2_CFD/obj/Makefile.am | 1 - 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Common/src/interface_interpolation/CNearestNeighbor.cpp b/Common/src/interface_interpolation/CNearestNeighbor.cpp index aa40c4ea433c..8322cc0cf50c 100644 --- a/Common/src/interface_interpolation/CNearestNeighbor.cpp +++ b/Common/src/interface_interpolation/CNearestNeighbor.cpp @@ -30,13 +30,14 @@ #include "../../include/geometry/CGeometry.hpp" -/*! \brief Helper struct to search sort neighbours according to distance. */ +/*! \brief Helper struct to (partially) sort neighbours according to distance while + * keeping track of the origin of the point (i.e. index and processor). */ struct DonorInfo { su2double dist; - unsigned long pidx; + unsigned pidx; int proc; - DonorInfo(su2double d = 0.0, unsigned long i = 0, int p = 0) : - dist(d), pidx(i), proc(p) {} + DonorInfo(su2double d = 0.0, unsigned i = 0, int p = 0) : + dist(d), pidx(i), proc(p) { } }; CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, diff --git a/Common/src/linear_algebra/CSysMatrix.cpp b/Common/src/linear_algebra/CSysMatrix.cpp index 2a47d4785a60..60a42b635428 100644 --- a/Common/src/linear_algebra/CSysMatrix.cpp +++ b/Common/src/linear_algebra/CSysMatrix.cpp @@ -1383,7 +1383,7 @@ void CSysMatrix::ComputePastixPreconditioner(const CSysVector void CSysMatrix::BuildPastixPreconditioner(CGeometry *geometry, CConfig *config, unsigned short kind_fact, bool transposed) { diff --git a/SU2_CFD/obj/Makefile.am b/SU2_CFD/obj/Makefile.am index 2ed339136e4e..a46d07ef0b79 100644 --- a/SU2_CFD/obj/Makefile.am +++ b/SU2_CFD/obj/Makefile.am @@ -126,7 +126,6 @@ libSU2Core_sources = ../src/definition_structure.cpp \ ../src/output/CMultizoneOutput.cpp \ ../src/output/COutputFactory.cpp \ ../src/output/output_structure_legacy.cpp \ - ../src/output/COutputFactory.cpp \ ../src/python_wrapper_structure.cpp \ ../src/solvers/CAdjEulerSolver.cpp \ ../src/solvers/CAdjNSSolver.cpp \ From a61f3b808da800fc2bd733b13bb6de554c399bc4 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 19 Mar 2020 10:55:54 +0000 Subject: [PATCH 036/112] const use of CConfig in CInterpolator --- Common/include/CConfig.hpp | 30 ++++++++--------- .../interface_interpolation/CInterpolator.hpp | 4 +-- .../CIsoparametric.hpp | 4 +-- .../interface_interpolation/CMirror.hpp | 4 +-- .../CNearestNeighbor.hpp | 4 +-- .../CRadialBasisFunction.hpp | 5 +-- .../interface_interpolation/CSlidingMesh.hpp | 5 +-- .../interface_interpolation/CInterpolator.cpp | 3 +- .../CIsoparametric.cpp | 4 +-- .../src/interface_interpolation/CMirror.cpp | 4 +-- .../CNearestNeighbor.cpp | 33 ++++++++----------- .../CRadialBasisFunction.cpp | 4 +-- .../interface_interpolation/CSlidingMesh.cpp | 4 +-- SU2_CFD/src/interfaces/CInterface.cpp | 23 ++++--------- 14 files changed, 59 insertions(+), 72 deletions(-) diff --git a/Common/include/CConfig.hpp b/Common/include/CConfig.hpp index d3f028bcf4b0..604f83e10f9b 100644 --- a/Common/include/CConfig.hpp +++ b/Common/include/CConfig.hpp @@ -3462,27 +3462,27 @@ class CConfig { unsigned short GetMarker_All_ZoneInterface(unsigned short val_marker) const { return Marker_All_ZoneInterface[val_marker]; } /*! - * \brief Get the MixingPlane interface information for a marker val_marker. - * \param[in] val_marker value of the marker on the grid. - * \return 0 if is not part of the MixingPlane Interface and greater than 1 if it is part. - */ + * \brief Get the MixingPlane interface information for a marker val_marker. + * \param[in] val_marker value of the marker on the grid. + * \return 0 if is not part of the MixingPlane Interface and greater than 1 if it is part. + */ unsigned short GetMarker_All_MixingPlaneInterface(unsigned short val_marker) const { return Marker_All_MixingPlaneInterface[val_marker]; } - /*! - * \brief Get the Turbomachinery information for a marker val_marker. - * \param[in] val_marker value of the marker on the grid. - * \return 0 if is not part of the Turbomachinery and greater than 1 if it is part. - */ + /*! + * \brief Get the Turbomachinery information for a marker val_marker. + * \param[in] val_marker value of the marker on the grid. + * \return 0 if is not part of the Turbomachinery and greater than 1 if it is part. + */ unsigned short GetMarker_All_Turbomachinery(unsigned short val_marker) const { return Marker_All_Turbomachinery[val_marker]; } - /*! - * \brief Get the Turbomachinery flag information for a marker val_marker. - * \param[in] val_marker value of the marker on the grid. - * \return 0 if is not part of the Turbomachinery, flag INFLOW or OUTFLOW if it is part. - */ + /*! + * \brief Get the Turbomachinery flag information for a marker val_marker. + * \param[in] val_marker value of the marker on the grid. + * \return 0 if is not part of the Turbomachinery, flag INFLOW or OUTFLOW if it is part. + */ unsigned short GetMarker_All_TurbomachineryFlag(unsigned short val_marker) const { return Marker_All_TurbomachineryFlag[val_marker]; } - /*! + /*! * \brief Get the number of FSI interface markers val_marker. * \param[in] void. * \return Number of markers belonging to the FSI interface. diff --git a/Common/include/interface_interpolation/CInterpolator.hpp b/Common/include/interface_interpolation/CInterpolator.hpp index 487e5131f345..1bc27bcb3cd5 100644 --- a/Common/include/interface_interpolation/CInterpolator.hpp +++ b/Common/include/interface_interpolation/CInterpolator.hpp @@ -100,7 +100,7 @@ class CInterpolator { * \param[in] iZone - index of the donor zone * \param[in] jZone - index of the target zone */ - CInterpolator(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); + CInterpolator(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone); /*! * \brief No default construction allowed. @@ -117,7 +117,7 @@ class CInterpolator { * \note Main method that derived classes must implement. * \param[in] config - Definition of the particular problem. */ - virtual void Set_TransferCoeff(CConfig **config) = 0; + virtual void Set_TransferCoeff(const CConfig* const* config) = 0; /*! * \brief Find the index of the interface marker shared by that zone diff --git a/Common/include/interface_interpolation/CIsoparametric.hpp b/Common/include/interface_interpolation/CIsoparametric.hpp index 250a7814f15d..1175e3890f2e 100644 --- a/Common/include/interface_interpolation/CIsoparametric.hpp +++ b/Common/include/interface_interpolation/CIsoparametric.hpp @@ -40,13 +40,13 @@ class CIsoparametric final : public CInterpolator { * \param[in] iZone - index of the donor zone * \param[in] jZone - index of the target zone */ - CIsoparametric(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); + CIsoparametric(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone); /*! * \brief Set up transfer matrix defining relation between two meshes * \param[in] config - Definition of the particular problem. */ - void Set_TransferCoeff(CConfig **config) override; + void Set_TransferCoeff(const CConfig* const* config) override; private: /*! diff --git a/Common/include/interface_interpolation/CMirror.hpp b/Common/include/interface_interpolation/CMirror.hpp index c53ebc4bd889..311d54d96a60 100644 --- a/Common/include/interface_interpolation/CMirror.hpp +++ b/Common/include/interface_interpolation/CMirror.hpp @@ -42,12 +42,12 @@ class CMirror final : public CInterpolator { * \param[in] iZone - First zone * \param[in] jZone - Second zone */ - CMirror(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); + CMirror(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone); /*! * \brief Set up transfer matrix defining relation between two meshes * \param[in] config - Definition of the particular problem. */ - void Set_TransferCoeff(CConfig **config) override; + void Set_TransferCoeff(const CConfig* const* config) override; }; diff --git a/Common/include/interface_interpolation/CNearestNeighbor.hpp b/Common/include/interface_interpolation/CNearestNeighbor.hpp index 5c16ed2cdb29..c8efe3998e40 100644 --- a/Common/include/interface_interpolation/CNearestNeighbor.hpp +++ b/Common/include/interface_interpolation/CNearestNeighbor.hpp @@ -43,12 +43,12 @@ class CNearestNeighbor final : public CInterpolator { * \param[in] iZone - index of the donor zone * \param[in] jZone - index of the target zone */ - CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); + CNearestNeighbor(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone); /*! * \brief Set up transfer matrix defining relation between two meshes. * \param[in] config - Definition of the particular problem. */ - void Set_TransferCoeff(CConfig **config) override; + void Set_TransferCoeff(const CConfig* const* config) override; }; diff --git a/Common/include/interface_interpolation/CRadialBasisFunction.hpp b/Common/include/interface_interpolation/CRadialBasisFunction.hpp index 37b75d784940..784ba1b321fd 100644 --- a/Common/include/interface_interpolation/CRadialBasisFunction.hpp +++ b/Common/include/interface_interpolation/CRadialBasisFunction.hpp @@ -42,13 +42,14 @@ class CRadialBasisFunction final : public CInterpolator { * \param[in] iZone - index of the donor zone * \param[in] jZone - index of the target zone */ - CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); + CRadialBasisFunction(CGeometry ****geometry_container, const CConfig* const* config, + unsigned int iZone, unsigned int jZone); /*! * \brief Set up transfer matrix defining relation between two meshes * \param[in] config - Definition of the particular problem. */ - void Set_TransferCoeff(CConfig **config) override; + void Set_TransferCoeff(const CConfig* const* config) override; /*! * \brief Compute the value of a radial basis function, this is static so it can be re-used. diff --git a/Common/include/interface_interpolation/CSlidingMesh.hpp b/Common/include/interface_interpolation/CSlidingMesh.hpp index 3cbb419d15db..e84c4c908a74 100644 --- a/Common/include/interface_interpolation/CSlidingMesh.hpp +++ b/Common/include/interface_interpolation/CSlidingMesh.hpp @@ -42,13 +42,14 @@ class CSlidingMesh final : public CInterpolator { * \param[in] iZone - index of the donor zone * \param[in] jZone - index of the target zone */ - CSlidingMesh(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone); + CSlidingMesh(CGeometry ****geometry_container, const CConfig* const* config, + unsigned int iZone, unsigned int jZone); /*! * \brief Set up transfer matrix defining relation between two meshes * \param[in] config - Definition of the particular problem. */ - void Set_TransferCoeff(CConfig **config) override; + void Set_TransferCoeff(const CConfig* const* config) override; private: /*! diff --git a/Common/src/interface_interpolation/CInterpolator.cpp b/Common/src/interface_interpolation/CInterpolator.cpp index 05d54a74fbdf..2c2c0089bb9e 100644 --- a/Common/src/interface_interpolation/CInterpolator.cpp +++ b/Common/src/interface_interpolation/CInterpolator.cpp @@ -30,7 +30,8 @@ #include "../../include/geometry/CGeometry.hpp" -CInterpolator::CInterpolator(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, unsigned int jZone) : +CInterpolator::CInterpolator(CGeometry ****geometry_container, const CConfig* const* config, + unsigned int iZone, unsigned int jZone) : rank(SU2_MPI::GetRank()), size(SU2_MPI::GetSize()), donorZone(iZone), diff --git a/Common/src/interface_interpolation/CIsoparametric.cpp b/Common/src/interface_interpolation/CIsoparametric.cpp index ef3468b86fb3..4e46e5f94ee1 100644 --- a/Common/src/interface_interpolation/CIsoparametric.cpp +++ b/Common/src/interface_interpolation/CIsoparametric.cpp @@ -30,12 +30,12 @@ #include "../../include/geometry/CGeometry.hpp" -CIsoparametric::CIsoparametric(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, +CIsoparametric::CIsoparametric(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { Set_TransferCoeff(config); } -void CIsoparametric::Set_TransferCoeff(CConfig **config) { +void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { unsigned long iVertex, jVertex; unsigned long dPoint, inode, jElem, nElem; diff --git a/Common/src/interface_interpolation/CMirror.cpp b/Common/src/interface_interpolation/CMirror.cpp index 49bcc91b93c0..d64d0451811d 100644 --- a/Common/src/interface_interpolation/CMirror.cpp +++ b/Common/src/interface_interpolation/CMirror.cpp @@ -30,12 +30,12 @@ #include "../../include/geometry/CGeometry.hpp" -CMirror::CMirror(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, +CMirror::CMirror(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { Set_TransferCoeff(config); } -void CMirror::Set_TransferCoeff(CConfig **config) { +void CMirror::Set_TransferCoeff(const CConfig* const* config) { const int nProcessor = size; diff --git a/Common/src/interface_interpolation/CNearestNeighbor.cpp b/Common/src/interface_interpolation/CNearestNeighbor.cpp index 8322cc0cf50c..9d5536e88fe7 100644 --- a/Common/src/interface_interpolation/CNearestNeighbor.cpp +++ b/Common/src/interface_interpolation/CNearestNeighbor.cpp @@ -40,12 +40,12 @@ struct DonorInfo { dist(d), pidx(i), proc(p) { } }; -CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, +CNearestNeighbor::CNearestNeighbor(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { Set_TransferCoeff(config); } -void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { +void CNearestNeighbor::Set_TransferCoeff(const CConfig* const* config) { /*--- Desired number of donor points. ---*/ const auto nDonor = max(config[donorZone]->GetNumNearestNeighbors(), 1); @@ -127,28 +127,21 @@ void CNearestNeighbor::Set_TransferCoeff(CConfig **config) { partial_sort(donorInfo.begin(), donorInfo.begin()+nDonor, donorInfo.end(), [](const DonorInfo& a, const DonorInfo& b){return a.dist < b.dist;}); + /*--- Compute interpolation numerators and denominator. ---*/ + su2double denom = 0.0; + for (auto iDonor = 0ul; iDonor < nDonor; ++iDonor) { + donorInfo[iDonor].dist = 1.0 / (donorInfo[iDonor].dist + eps); + denom += donorInfo[iDonor].dist; + } + /*--- Set interpolation coefficients. ---*/ target_vertex->SetnDonorPoints(nDonor); target_vertex->Allocate_DonorInfo(); - if (nDonor > 1) { - /*--- Compute interpolation numerators and denominator. ---*/ - su2double denom = 0.0; - for (auto iDonor = 0ul; iDonor < nDonor; ++iDonor) { - donorInfo[iDonor].dist = 1.0 / (donorInfo[iDonor].dist + eps); - denom += donorInfo[iDonor].dist; - } - - for (auto iDonor = 0ul; iDonor < nDonor; ++iDonor) { - target_vertex->SetInterpDonorPoint(iDonor, donorInfo[iDonor].pidx); - target_vertex->SetInterpDonorProcessor(iDonor, donorInfo[iDonor].proc); - target_vertex->SetDonorCoeff(iDonor, donorInfo[iDonor].dist/denom); - } - } - else { - target_vertex->SetInterpDonorPoint(0, donorInfo[0].pidx); - target_vertex->SetInterpDonorProcessor(0, donorInfo[0].proc); - target_vertex->SetDonorCoeff(0, 1.0); + for (auto iDonor = 0ul; iDonor < nDonor; ++iDonor) { + target_vertex->SetInterpDonorPoint(iDonor, donorInfo[iDonor].pidx); + target_vertex->SetInterpDonorProcessor(iDonor, donorInfo[iDonor].proc); + target_vertex->SetDonorCoeff(iDonor, donorInfo[iDonor].dist/denom); } } } // end SU2_OMP_PARALLEL diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp index eabd34671bab..3edf9bace1b2 100644 --- a/Common/src/interface_interpolation/CRadialBasisFunction.cpp +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -31,7 +31,7 @@ #include "../../include/toolboxes/CSymmetricMatrix.hpp" -CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, +CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { Set_TransferCoeff(config); } @@ -66,7 +66,7 @@ su2double CRadialBasisFunction::Get_RadialBasisValue(ENUM_RADIALBASIS type, cons return rbf; } -void CRadialBasisFunction::Set_TransferCoeff(CConfig **config) { +void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { /*--- RBF options. ---*/ const auto kindRBF = static_cast(config[donorZone]->GetKindRadialBasisFunction()); diff --git a/Common/src/interface_interpolation/CSlidingMesh.cpp b/Common/src/interface_interpolation/CSlidingMesh.cpp index b76804529b90..4468b31157e2 100644 --- a/Common/src/interface_interpolation/CSlidingMesh.cpp +++ b/Common/src/interface_interpolation/CSlidingMesh.cpp @@ -30,12 +30,12 @@ #include "../../include/geometry/CGeometry.hpp" -CSlidingMesh::CSlidingMesh(CGeometry ****geometry_container, CConfig **config, unsigned int iZone, +CSlidingMesh::CSlidingMesh(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { Set_TransferCoeff(config); } -void CSlidingMesh::Set_TransferCoeff(CConfig **config) { +void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { /* 0 - Variable declaration */ diff --git a/SU2_CFD/src/interfaces/CInterface.cpp b/SU2_CFD/src/interfaces/CInterface.cpp index 455c8b8d96b9..df0d1ab46490 100644 --- a/SU2_CFD/src/interfaces/CInterface.cpp +++ b/SU2_CFD/src/interfaces/CInterface.cpp @@ -300,11 +300,6 @@ void CInterface::PreprocessAverage(CGeometry *donor_geometry, CGeometry *target_ const su2double *SpanValuesDonor, *SpanValuesTarget; su2double dist, test, dist2, test2; -#ifdef HAVE_MPI - int iSize; - int *BuffMarkerDonor, *BuffDonorFlag; -#endif - nMarkerDonor = donor_geometry->GetnMarker(); nMarkerTarget = target_geometry->GetnMarker(); //TODO turbo this approach only works if all the turboamchinery marker @@ -333,25 +328,23 @@ void CInterface::PreprocessAverage(CGeometry *donor_geometry, CGeometry *target_ } #ifdef HAVE_MPI - BuffMarkerDonor = new int[size]; - BuffDonorFlag = new int[size]; - for (iSize=0; iSize 0.0){ Marker_Donor = BuffMarkerDonor[iSize]; - Donor_Flag = BuffDonorFlag[iSize]; + Donor_Flag = BuffDonorFlag[iSize]; break; } } @@ -623,7 +616,6 @@ void CInterface::AllgatherAverage(CSolver *donor_solution, CSolver *target_solut delete [] BuffAvgKineDonor; delete [] BuffAvgOmegaDonor; delete [] BuffMarkerDonor; - #endif /*--- On the target side we have to identify the marker as well ---*/ @@ -760,4 +752,3 @@ void CInterface::GatherAverageTurboGeoValues(CGeometry *donor_geometry, CGeometr SetAverageTurboGeoValues(donor_geometry, target_geometry, donorZone); } - From 02df871bf41d942aa3d1ca0a448b36d97a9626f9 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 19 Mar 2020 12:12:50 +0000 Subject: [PATCH 037/112] more const correctness --- .../interface_interpolation/CSlidingMesh.hpp | 32 +++-- Common/include/toolboxes/C2DContainer.hpp | 1 + Common/include/toolboxes/CSymmetricMatrix.hpp | 14 ++- .../CRadialBasisFunction.cpp | 7 +- .../interface_interpolation/CSlidingMesh.cpp | 111 +++++++++--------- Common/src/toolboxes/CSymmetricMatrix.cpp | 29 ++--- 6 files changed, 100 insertions(+), 94 deletions(-) diff --git a/Common/include/interface_interpolation/CSlidingMesh.hpp b/Common/include/interface_interpolation/CSlidingMesh.hpp index e84c4c908a74..74ae1d9ae18f 100644 --- a/Common/include/interface_interpolation/CSlidingMesh.hpp +++ b/Common/include/interface_interpolation/CSlidingMesh.hpp @@ -55,24 +55,29 @@ class CSlidingMesh final : public CInterpolator { /*! * \brief For 3-Dimensional grids, build the dual surface element * \param[in] map - array containing the index of the boundary points connected to the node - * \param[in] startIndex - for each vertex specifies the corresponding index in the global array containing the indexes of all its neighbouring vertexes + * \param[in] startIndex - for each vertex specifies the corresponding index in the global + * array containing the indexes of all its neighbouring vertexes * \param[in] nNeighbour - for each vertex specifies the number of its neighbouring vertexes (on the boundary) * \param[in] coord - array containing the coordinates of all the boundary vertexes * \param[in] centralNode - label of the vertex around which the dual surface element is built - * \param[in] element - double array where element node coordinates will be stored + * \param[out] element - double array where element node coordinates will be stored + * \return Number of points included in the element. */ - int Build_3D_surface_element(unsigned long *map, unsigned long *startIndex, unsigned long* nNeighbor, - su2double *coord, unsigned long centralNode, su2double** element); + static int Build_3D_surface_element(const unsigned long *map, const unsigned long *startIndex, + const unsigned long* nNeighbor, const su2double *coord, + unsigned long centralNode, su2double** element); /*! * \brief For 2-Dimensional grids, compute intersection length of two segments projected along a given direction + * \param[in] nDim - Number of dimensions * \param[in] A1 - first point of segment A * \param[in] A2 - second point of segment A * \param[in] B1 - first point of segment B * \param[in] B2 - second point of segment B * \param[in] Direction - along which segments are projected */ - su2double ComputeLineIntersectionLength(su2double* A1, su2double* A2, su2double* B1, su2double* B2, su2double* Direction); + static su2double ComputeLineIntersectionLength(unsigned short nDim, const su2double* A1, const su2double* A2, + const su2double* B1, const su2double* B2, const su2double* Direction); /*! * \brief For 3-Dimensional grids, compute intersection area between two triangle projected on a given plane @@ -84,8 +89,9 @@ class CSlidingMesh final : public CInterpolator { * \param[in] B3 - third point of triangle B * \param[in] Direction - vector normal to projection plane */ - su2double Compute_Triangle_Intersection(su2double* A1, su2double* A2, su2double* A3, su2double* B1, - su2double* B2, su2double* B3, su2double* Direction); + static su2double Compute_Triangle_Intersection(const su2double* A1, const su2double* A2, const su2double* A3, + const su2double* B1, const su2double* B2, const su2double* B3, + const su2double* Direction); /*! * \brief For 3-Dimensional grids, compute intersection area between two triangle projected on a given plane @@ -97,7 +103,8 @@ class CSlidingMesh final : public CInterpolator { * \param[in] Q2 - second point of triangle B * \param[in] Q3 - third point of triangle B */ - su2double ComputeIntersectionArea(su2double* P1, su2double* P2, su2double* P3, su2double* Q1, su2double* Q2, su2double* Q3); + static su2double ComputeIntersectionArea(const su2double* P1, const su2double* P2, const su2double* P3, + const su2double* Q1, const su2double* Q2, const su2double* Q3); /*! * \brief For 2-Dimensional grids, check whether, and compute, two lines are intersecting @@ -105,9 +112,10 @@ class CSlidingMesh final : public CInterpolator { * \param[in] A2 - second defining first line * \param[in] B1 - first defining second line * \param[in] B2 - second defining second line - * \param[in] IntersectionPoint - Container for intersection coordinates + * \param[out] IntersectionPoint - Container for intersection coordinates */ - void ComputeLineIntersectionPoint(su2double* A1, su2double* A2, su2double* B1, su2double* B2, su2double* IntersectionPoint); + static void ComputeLineIntersectionPoint(const su2double* A1, const su2double* A2, const su2double* B1, + const su2double* B2, su2double* IntersectionPoint); /*! * \brief For N-Dimensional grids, check whether a point is inside a triangle specified by 3 T points @@ -116,6 +124,6 @@ class CSlidingMesh final : public CInterpolator { * \param[in] T2 - second point of triangle T * \param[in] T3 - third point of triangle T */ - bool CheckPointInsideTriangle(su2double* Point, su2double* T1, su2double* T2, su2double* T3); - + static bool CheckPointInsideTriangle(const su2double* Point, const su2double* T1, + const su2double* T2, const su2double* T3); }; diff --git a/Common/include/toolboxes/C2DContainer.hpp b/Common/include/toolboxes/C2DContainer.hpp index a5c978f2bff3..93f1314766f6 100644 --- a/Common/include/toolboxes/C2DContainer.hpp +++ b/Common/include/toolboxes/C2DContainer.hpp @@ -371,6 +371,7 @@ class C2DContainer : using Base::size; using Index = Index_t; using Scalar = Scalar_t; + static constexpr StorageType Storage = Store; private: /*! diff --git a/Common/include/toolboxes/CSymmetricMatrix.hpp b/Common/include/toolboxes/CSymmetricMatrix.hpp index 24f057cdcd65..b32613a864ae 100644 --- a/Common/include/toolboxes/CSymmetricMatrix.hpp +++ b/Common/include/toolboxes/CSymmetricMatrix.hpp @@ -81,9 +81,19 @@ class CSymmetricMatrix { inline const passivedouble& operator() (int i, int j) const { return val_vec[IdxSym(i,j)]; } - void MatVecMult(passivedouble *v) const; + template + void MatVecMult(ForwardIt vec_in, ForwardIt vec_out) const + { + for (int i = 0; i < sz; ++i) { + *vec_out = 0.0; + auto vec = vec_in; + for (int k = 0; k < sz; ++k) + *vec_out += *(vec++) * Get(i,k); + ++vec_out; + } + } - void MatMatMult(const char side, su2passivematrix& mat_in, su2passivematrix& mat_out); + void MatMatMult(const char side, const su2passivematrix& mat_in, su2passivematrix& mat_out) const; void Invert(const bool is_spd); diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp index 3edf9bace1b2..e25bdbb688d4 100644 --- a/Common/src/interface_interpolation/CRadialBasisFunction.cpp +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -401,6 +401,7 @@ int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector coeff(n_rows,0.0); + vector rhs(n_rows,0.0), coeff(n_rows); for (int i = 0; i < n_rows; ++i) for (int j = 0; j < n; ++j) - coeff[i] += P(i+1,j); + rhs[i] += P(i+1,j); /*--- Multiply the RHS by the inverse thus obtaining the coefficients. ---*/ - PPT.MatVecMult(coeff.data()); + PPT.MatVecMult(rhs.begin(), coeff.begin()); /*--- Determine the maximum deviation of the points from the fitted plane. ---*/ passivedouble max_diff = 0.0; diff --git a/Common/src/interface_interpolation/CSlidingMesh.cpp b/Common/src/interface_interpolation/CSlidingMesh.cpp index 4468b31157e2..2ac685049af6 100644 --- a/Common/src/interface_interpolation/CSlidingMesh.cpp +++ b/Common/src/interface_interpolation/CSlidingMesh.cpp @@ -43,7 +43,7 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { bool check; - unsigned short iDim, nDim; + unsigned short iDim; unsigned long ii, jj, *uptr; unsigned long vPoint; @@ -97,17 +97,17 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { /* 1 - Variable pre-processing */ - nDim = donor_geometry->GetnDim(); + const unsigned short nDim = donor_geometry->GetnDim(); /*--- Setting up auxiliary vectors ---*/ - Donor_Vect = NULL; - Coeff_Vect = NULL; - storeProc = NULL; + Donor_Vect = nullptr; + Coeff_Vect = nullptr; + storeProc = nullptr; - tmp_Donor_Vect = NULL; - tmp_Coeff_Vect = NULL; - tmp_storeProc = NULL; + tmp_Donor_Vect = nullptr; + tmp_Coeff_Vect = nullptr; + tmp_storeProc = nullptr; Normal = new su2double[nDim]; Direction = new su2double[nDim]; @@ -289,7 +289,7 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; } - LineIntersectionLength = ComputeLineIntersectionLength(target_iMidEdge_point, target_jMidEdge_point, + LineIntersectionLength = ComputeLineIntersectionLength(nDim, target_iMidEdge_point, target_jMidEdge_point, donor_iMidEdge_point, donor_jMidEdge_point, Direction); if ( LineIntersectionLength == 0.0 ){ @@ -313,9 +313,9 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { tmp_Coeff_Vect[ nDonorPoints ] = LineIntersectionLength / length; tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint]; - if (Donor_Vect != NULL) delete [] Donor_Vect; - if (Coeff_Vect != NULL) delete [] Coeff_Vect; - if (storeProc != NULL) delete [] storeProc; + if (Donor_Vect != nullptr) delete [] Donor_Vect; + if (Coeff_Vect != nullptr) delete [] Coeff_Vect; + if (storeProc != nullptr) delete [] storeProc; Donor_Vect = tmp_Donor_Vect; Coeff_Vect = tmp_Coeff_Vect; @@ -372,7 +372,7 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { DonorPoint_Coord[ donor_iPoint * nDim + iDim] ) / 2; } - LineIntersectionLength = ComputeLineIntersectionLength(target_iMidEdge_point, target_jMidEdge_point, + LineIntersectionLength = ComputeLineIntersectionLength(nDim, target_iMidEdge_point, target_jMidEdge_point, donor_iMidEdge_point, donor_jMidEdge_point, Direction); if ( LineIntersectionLength == 0.0 ){ @@ -396,9 +396,9 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint; tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint]; - if (Donor_Vect != NULL) delete [] Donor_Vect; - if (Coeff_Vect != NULL) delete [] Coeff_Vect; - if (storeProc != NULL) delete [] storeProc; + if (Donor_Vect != nullptr) delete [] Donor_Vect; + if (Coeff_Vect != nullptr) delete [] Coeff_Vect; + if (storeProc != nullptr) delete [] storeProc; Donor_Vect = tmp_Donor_Vect; Coeff_Vect = tmp_Coeff_Vect; @@ -554,7 +554,7 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { Area_old = Area; - ToVisit = NULL; + ToVisit = nullptr; nToVisit = 0; for( iNodeVisited = StartVisited; iNodeVisited < nAlreadyVisited; iNodeVisited++ ){ @@ -578,7 +578,7 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { } } - if( check == 0 && ToVisit != NULL){ + if( check == 0 && ToVisit != nullptr){ for( jj = 0; jj < nToVisit; jj++ ) if( donor_iPoint == ToVisit[jj] ){ check = 1; @@ -595,11 +595,11 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { tmpVect[jj] = ToVisit[jj]; tmpVect[nToVisit] = donor_iPoint; - if( ToVisit != NULL ) + if( ToVisit != nullptr ) delete [] ToVisit; ToVisit = tmpVect; - tmpVect = NULL; + tmpVect = nullptr; nToVisit++; @@ -640,17 +640,17 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { tmp_Donor_Vect[ nDonorPoints ] = donor_iPoint; tmp_storeProc[ nDonorPoints ] = Donor_Proc[donor_iPoint]; - if (Donor_Vect != NULL) {delete [] Donor_Vect; } - if (Coeff_Vect != NULL) {delete [] Coeff_Vect; } - if (storeProc != NULL) {delete [] storeProc; } + if (Donor_Vect != nullptr) {delete [] Donor_Vect; } + if (Coeff_Vect != nullptr) {delete [] Coeff_Vect; } + if (storeProc != nullptr) {delete [] storeProc; } Donor_Vect = tmp_Donor_Vect; Coeff_Vect = tmp_Coeff_Vect; storeProc = tmp_storeProc; - tmp_Coeff_Vect = NULL; - tmp_Donor_Vect = NULL; - tmp_storeProc = NULL; + tmp_Coeff_Vect = nullptr; + tmp_Donor_Vect = nullptr; + tmp_storeProc = nullptr; nDonorPoints++; @@ -671,7 +671,7 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { for( jj = 0; jj < nToVisit; jj++ ) tmpVect[ nAlreadyVisited + jj ] = ToVisit[jj]; - if( alreadyVisitedDonor != NULL ) + if( alreadyVisitedDonor != nullptr ) delete [] alreadyVisitedDonor; alreadyVisitedDonor = tmpVect; @@ -698,9 +698,9 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { delete [] target_element[ii]; delete [] target_element; - delete [] Donor_Vect; Donor_Vect = NULL; - delete [] Coeff_Vect; Coeff_Vect = NULL; - delete [] storeProc; storeProc = NULL; + delete [] Donor_Vect; Donor_Vect = nullptr; + delete [] Coeff_Vect; Coeff_Vect = nullptr; + delete [] storeProc; storeProc = nullptr; } } @@ -723,24 +723,25 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { delete [] Normal; delete [] Direction; - if (Donor_Vect != NULL) delete [] Donor_Vect; - if (Coeff_Vect != NULL) delete [] Coeff_Vect; - if (storeProc != NULL) delete [] storeProc; + if (Donor_Vect != nullptr) delete [] Donor_Vect; + if (Coeff_Vect != nullptr) delete [] Coeff_Vect; + if (storeProc != nullptr) delete [] storeProc; } -int CSlidingMesh::Build_3D_surface_element(unsigned long *map, unsigned long *startIndex, unsigned long* nNeighbor, - su2double *coord, unsigned long centralNode, su2double** element){ +int CSlidingMesh::Build_3D_surface_element(const unsigned long *map, const unsigned long *startIndex, + const unsigned long* nNeighbor, const su2double *coord, + unsigned long centralNode, su2double** element) { /*--- Given a node "centralNode", this routines reconstruct the vertex centered * surface element around the node and store it into "element" ---*/ - /*--- Returns the number of points included in the element ---*/ unsigned long iNode, jNode, kNode, iElementNode, iPoint, jPoint, nOuterNodes; - unsigned short nDim = 3, iDim, nTmp; + constexpr unsigned short nDim = 3; + unsigned short iDim, nTmp; int NextNode, **OuterNodesNeighbour, CurrentNode, StartIndex, count; - unsigned long *OuterNodes, *ptr; + const unsigned long *OuterNodes, *ptr; /* --- Store central node as element first point --- */ @@ -841,15 +842,14 @@ int CSlidingMesh::Build_3D_surface_element(unsigned long *map, unsigned long *st } -su2double CSlidingMesh::ComputeLineIntersectionLength(su2double* A1, su2double* A2, su2double* B1, - su2double* B2, su2double* Direction){ +su2double CSlidingMesh::ComputeLineIntersectionLength(unsigned short nDim, const su2double* A1, const su2double* A2, + const su2double* B1, const su2double* B2, const su2double* Direction) { /*--- Given 2 segments, each defined by 2 points, it projects them along a given direction * and it computes the length of the segment resulting from their intersection ---*/ /*--- The algorithm works for both 2D and 3D problems ---*/ unsigned short iDim; - unsigned short nDim = donor_geometry->GetnDim(); su2double dotA2, dotB1, dotB2; @@ -898,14 +898,15 @@ su2double CSlidingMesh::ComputeLineIntersectionLength(su2double* A1, su2double* return 0.0; } -su2double CSlidingMesh::Compute_Triangle_Intersection(su2double* A1, su2double* A2, su2double* A3, - su2double* B1, su2double* B2, su2double* B3, su2double* Direction){ +su2double CSlidingMesh::Compute_Triangle_Intersection(const su2double* A1, const su2double* A2, const su2double* A3, + const su2double* B1, const su2double* B2, const su2double* B3, + const su2double* Direction) { /* --- This routine is ONLY for 3D grids --- */ /* --- Projects triangle points onto a plane, specified by its normal "Direction", and calls the ComputeIntersectionArea routine --- */ unsigned short iDim; - unsigned short nDim = 3; + constexpr unsigned short nDim = 3; su2double I[3], J[3], K[3]; su2double a1[3], a2[3], a3[3]; @@ -981,21 +982,20 @@ su2double CSlidingMesh::Compute_Triangle_Intersection(su2double* A1, su2double* return ComputeIntersectionArea( a1, a2, a3, b1, b2, b3 ); } -su2double CSlidingMesh::ComputeIntersectionArea(su2double* P1, su2double* P2, su2double* P3, - su2double* Q1, su2double* Q2, su2double* Q3 ){ +su2double CSlidingMesh::ComputeIntersectionArea(const su2double* P1, const su2double* P2, const su2double* P3, + const su2double* Q1, const su2double* Q2, const su2double* Q3) { /* --- This routines computes the area of the polygonal element generated by the superimposition of 2 planar triangle --- */ /* --- The 2 triangle must lie on the same plane --- */ - unsigned short iDim, nPoints, i, j, k; - unsigned short nDim, min_theta_index; + unsigned short iDim, nPoints = 0, i, j, k; + unsigned short min_theta_index; su2double points[16][2], IntersectionPoint[2], theta[6]; su2double TriangleP[4][2], TriangleQ[4][2]; su2double Area, det, dot1, dot2, dtmp, min_theta; - nDim = 2; - nPoints = 0; + constexpr unsigned short nDim = 2; for(iDim = 0; iDim < nDim; iDim++){ TriangleP[0][iDim] = 0; @@ -1139,8 +1139,8 @@ su2double CSlidingMesh::ComputeIntersectionArea(su2double* P1, su2double* P2, su return fabs(Area)/2; } -void CSlidingMesh::ComputeLineIntersectionPoint(su2double* A1, su2double* A2, su2double* B1, su2double* B2, - su2double* IntersectionPoint ){ +void CSlidingMesh::ComputeLineIntersectionPoint(const su2double* A1, const su2double* A2, const su2double* B1, + const su2double* B2, su2double* IntersectionPoint ){ /* --- Uses determinant rule to compute the intersection point between 2 straight segments --- */ /* This works only for lines on a 2D plane, A1, A2 and B1, B2 are respectively the head and the tail points of each segment, @@ -1156,7 +1156,7 @@ void CSlidingMesh::ComputeLineIntersectionPoint(su2double* A1, su2double* A2, su } } -bool CSlidingMesh::CheckPointInsideTriangle(su2double* Point, su2double* T1, su2double* T2, su2double* T3){ +bool CSlidingMesh::CheckPointInsideTriangle(const su2double* Point, const su2double* T1, const su2double* T2, const su2double* T3) { /* --- Check whether a point "Point" lies inside or outside a triangle defined by 3 points "T1", "T2", "T3" --- */ /* For each edge it checks on which side the point lies: @@ -1166,13 +1166,12 @@ bool CSlidingMesh::CheckPointInsideTriangle(su2double* Point, su2double* T1, su2 * - If the check is positive for all the 3 edges, then the point lies within the triangle */ - unsigned short iDim, nDim, check; + unsigned short iDim, check = 0; su2double vect1[2], vect2[2], r[2]; su2double dot; - check = 0; - nDim = 2; + constexpr unsigned short nDim = 2; /* --- Check first edge --- */ diff --git a/Common/src/toolboxes/CSymmetricMatrix.cpp b/Common/src/toolboxes/CSymmetricMatrix.cpp index 8e884d4131ea..8ec0a020ebf7 100644 --- a/Common/src/toolboxes/CSymmetricMatrix.cpp +++ b/Common/src/toolboxes/CSymmetricMatrix.cpp @@ -256,25 +256,12 @@ void CSymmetricMatrix::Invert(const bool is_spd) #endif } -void CSymmetricMatrix::MatVecMult(passivedouble *v) const -{ - passivedouble *tmp_res = new passivedouble [sz]; - - for (int i=0; i(val_vec.data()), + &M, const_cast(mat_in.data()), &N, &beta, mat_out.data(), &N); #endif } /*--- Right_side: mat_out = mat_in * this. ---*/ @@ -324,8 +311,8 @@ void CSymmetricMatrix::MatMatMult(const char side, /*--- Left and lower because matrices are in row major order. ---*/ char side = 'L', uplo = 'L'; passivedouble alpha = 1.0, beta = 0.0; - dsymm_(&side, &uplo, &N, &M, &alpha, val_vec.data(), &N, - mat_in.data(), &N, &beta, mat_out.data(), &N); + dsymm_(&side, &uplo, &N, &M, &alpha, const_cast(val_vec.data()), + &N, const_cast(mat_in.data()), &N, &beta, mat_out.data(), &N); #endif } From 759e8a7e03424995ef1bee2c06abcef1b927de7a Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 19 Mar 2020 19:46:35 +0000 Subject: [PATCH 038/112] more blas 3 optimization of RBF interpolation, some cleanup of CVertex --- Common/include/geometry/dual_grid/CVertex.hpp | 48 ++-- .../interface_interpolation/CInterpolator.hpp | 6 +- .../CRadialBasisFunction.hpp | 37 ++- Common/src/geometry/dual_grid/CVertex.cpp | 60 ++--- .../CIsoparametric.cpp | 15 +- .../src/interface_interpolation/CMirror.cpp | 9 +- .../CNearestNeighbor.cpp | 5 +- .../CRadialBasisFunction.cpp | 233 ++++++++++-------- .../interface_interpolation/CSlidingMesh.cpp | 7 +- Common/src/toolboxes/CSymmetricMatrix.cpp | 32 ++- 10 files changed, 225 insertions(+), 227 deletions(-) diff --git a/Common/include/geometry/dual_grid/CVertex.hpp b/Common/include/geometry/dual_grid/CVertex.hpp index 70bc0dd2492c..084e6e0137ca 100644 --- a/Common/include/geometry/dual_grid/CVertex.hpp +++ b/Common/include/geometry/dual_grid/CVertex.hpp @@ -37,26 +37,25 @@ */ class CVertex : public CDualGrid { protected: - unsigned long *Nodes; /*!< \brief Vector to store the global nodes of an element. */ - su2double *Normal; /*!< \brief Normal coordinates of the element and its center of gravity. */ - su2double Aux_Var; /*!< \brief Auxiliar variable defined only on the surface. */ - su2double CartCoord[3]; /*!< \brief Vertex cartesians coordinates. */ - su2double VarCoord[3]; /*!< \brief Used for storing the coordinate variation due to a surface modification. */ - su2double *VarRot; /*!< \brief Used for storing the rotation variation due to a surface modification. */ - long PeriodicPoint[5]; /*!< \brief Store the periodic point of a boundary (iProcessor, iPoint) */ - bool ActDisk_Perimeter; /*!< \brief Identify nodes at the perimeter of the actuator disk */ - short Rotation_Type; /*!< \brief Type of rotation associated with the vertex (MPI and periodic) */ + unsigned long Nodes[1]; /*!< \brief Vector to store the global nodes of an element. */ + su2double Normal[3]; /*!< \brief Normal coordinates of the element and its center of gravity. */ + su2double Aux_Var; /*!< \brief Auxiliar variable defined only on the surface. */ + su2double CartCoord[3]; /*!< \brief Vertex cartesians coordinates. */ + su2double VarCoord[3]; /*!< \brief Used for storing the coordinate variation due to a surface modification. */ + su2double *VarRot; /*!< \brief Used for storing the rotation variation due to a surface modification. */ + long PeriodicPoint[5]; /*!< \brief Store the periodic point of a boundary (iProcessor, iPoint) */ + bool ActDisk_Perimeter; /*!< \brief Identify nodes at the perimeter of the actuator disk */ + short Rotation_Type; /*!< \brief Type of rotation associated with the vertex (MPI and periodic) */ unsigned long Normal_Neighbor; /*!< \brief Index of the closest neighbor. */ - unsigned long *Donor_Points; /*!< \brief indices of donor points for interpolation across zones */ - unsigned long *Donor_Proc; /*!< \brief indices of donor processor for interpolation across zones in parallel */ - unsigned long Donor_Elem; /*!< \brief Store the donor element for interpolation across zones/ */ - unsigned short Donor_Face; /*!<\brief Store the donor face (w/in donor element) for interpolation across zones */ - su2double Basis_Function[3]; /*!< \brief Basis function values for interpolation across zones. */ - su2double *Donor_Coeff; /*!\brief Store a list of coefficients corresponding to the donor points. */ - unsigned short nDonor_Points; /*!\brief Number of points in Donor_Points; at least there will be one donor point (if the mesh is matching)*/ + unsigned long *Donor_Points; /*!< \brief indices of donor points for interpolation across zones */ + unsigned long *Donor_Proc; /*!< \brief indices of donor processor for interpolation across zones in parallel */ + unsigned long Donor_Elem; /*!< \brief Store the donor element for interpolation across zones/ */ + unsigned short Donor_Face; /*!< \brief Store the donor face (w/in donor element) for interpolation across zones */ + su2double Basis_Function[3]; /*!< \brief Basis function values for interpolation across zones. */ + su2double *Donor_Coeff; /*!< \brief Store a list of coefficients corresponding to the donor points. */ + unsigned short nDonor_Points; /*!< \brief Number of points in Donor_Coeff. */ public: - /*! * \brief Constructor of the class. * \param[in] val_point - Node of the vertex. @@ -358,17 +357,6 @@ class CVertex : public CDualGrid { */ inline unsigned long GetNormal_Neighbor(void) const { return Normal_Neighbor; } - /*! - * \brief Increment the number of donor points by 1. - */ - inline void IncrementnDonor(void) {nDonor_Points++;} - - /*! - * \brief Set the value of nDonor_Points - * \param[in] nDonor - the number of donor points - */ - inline void SetnDonorPoints(unsigned short nDonor) {nDonor_Points = nDonor;} - /*! * \brief Return the value of nDonor_Points * \return nDonor - the number of donor points @@ -419,9 +407,9 @@ class CVertex : public CDualGrid { /*! * \brief Allocate memory based on how many donor points need to be stored. - * Uses nDonor_Points + * \param[in] nDonor - the number of donor points */ - void Allocate_DonorInfo(void); + void Allocate_DonorInfo(unsigned short nDonor); /*! * \brief Get the rotation variation diff --git a/Common/include/interface_interpolation/CInterpolator.hpp b/Common/include/interface_interpolation/CInterpolator.hpp index 1bc27bcb3cd5..6da4e028c4f2 100644 --- a/Common/include/interface_interpolation/CInterpolator.hpp +++ b/Common/include/interface_interpolation/CInterpolator.hpp @@ -55,9 +55,9 @@ class CInterpolator { *Buffer_Receive_nVertex_Donor, /*!< \brief Buffer to store the number of vertices per processor on the Donor domain */ *Buffer_Receive_nFace_Donor, /*!< \brief Buffer to store the number of faces per processor*/ *Buffer_Receive_nFaceNodes_Donor, /*!< \brief Buffer to store the number of nodes associated with faces per processor*/ - *Buffer_Send_nVertex_Donor, /*!< \brief Buffer to send number of vertices on the local processor*/ - *Buffer_Send_nFace_Donor, /*!< \brief Buffer to send number of faces on the local processor*/ - *Buffer_Send_nFaceNodes_Donor, /*!< \brief Buffer to send the number of nodes assocated with faces per processor*/ + Buffer_Send_nVertex_Donor[1], /*!< \brief Buffer to send number of vertices on the local processor*/ + Buffer_Send_nFace_Donor[1], /*!< \brief Buffer to send number of faces on the local processor*/ + Buffer_Send_nFaceNodes_Donor[1], /*!< \brief Buffer to send the number of nodes assocated with faces per processor*/ *Buffer_Send_FaceIndex, /*!< \brief Buffer to send indices pointing to the node indices that define the faces*/ *Buffer_Receive_FaceIndex, /*!< \brief Buffer to receive indices pointing to the node indices that define the faces*/ *Buffer_Send_FaceNodes, /*!< \brief Buffer to send indices pointing to the location of node information in other buffers, defining faces*/ diff --git a/Common/include/interface_interpolation/CRadialBasisFunction.hpp b/Common/include/interface_interpolation/CRadialBasisFunction.hpp index 784ba1b321fd..c5f9cb19e856 100644 --- a/Common/include/interface_interpolation/CRadialBasisFunction.hpp +++ b/Common/include/interface_interpolation/CRadialBasisFunction.hpp @@ -35,6 +35,8 @@ */ class CRadialBasisFunction final : public CInterpolator { public: + static_assert(su2passivematrix::Storage == StorageType::RowMajor, + "This class does not work otherwise."); /*! * \brief Constructor of the class. * \param[in] geometry - Geometrical definition of the problem. @@ -89,12 +91,39 @@ class CRadialBasisFunction final : public CInterpolator { static int CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, su2passivematrix &P); /*! - * \brief Prunes (by setting to zero) small interpolation coefficients, i.e. - * <= tolerance*max(abs(coeffs)). The vector is re-scaled such that sum(coeffs)==1. + * \brief Helper function, prunes (by setting to zero) small interpolation coefficients, + * i.e. <= tolerance*max(abs(coeffs)). The vector is re-scaled such that sum(coeffs)==1. * \param[in] tolerance - Relative pruning tolerance. - * \param[in,out] coeffs - The vector of interpolation coefficients. + * \param[in] size - Size of the coefficient vector. + * \param[in,out] coeffs - Iterator to start of vector of interpolation coefficients. * \return Number of non-zero coefficients after pruning. */ - static int PruneSmallCoefficients(passivedouble tolerance, su2passivevector& coeffs); + template + static Int PruneSmallCoefficients(Float tolerance, Int size, ForwardIt coeffs) { + + /*--- Determine the pruning threshold. ---*/ + Float thresh = 0.0; + auto end = coeffs; + for (Int i = 0; i < size; ++i) + thresh = max(thresh, fabs(*(++end))); + thresh *= tolerance; + + /*--- Prune and count non-zeros. ---*/ + Int numNonZeros = 0; + Float coeffSum = 0.0; + for (auto it = coeffs; it != end; ++it) { + if (fabs(*it) > thresh) { // keep + coeffSum += *it; + ++numNonZeros; + } + else { *it = 0.0; } // prune + } + + /*--- Correct remaining coefficients, sum must be 1 for conservation. ---*/ + Float correction = 1.0 / coeffSum; + for (auto it = coeffs; it != end; ++it) *it *= correction; + + return numNonZeros; + } }; diff --git a/Common/src/geometry/dual_grid/CVertex.cpp b/Common/src/geometry/dual_grid/CVertex.cpp index 4cf14bf19f39..a65c22e63460 100644 --- a/Common/src/geometry/dual_grid/CVertex.cpp +++ b/Common/src/geometry/dual_grid/CVertex.cpp @@ -40,52 +40,35 @@ CVertex::CVertex(unsigned long val_point, unsigned short val_nDim) : CDualGrid(v ActDisk_Perimeter = false; - /*--- Pointers initialization ---*/ - - Nodes = NULL; - Normal = NULL; - - /*--- Allocate node, and face normal ---*/ - - Nodes = new unsigned long[1]; - Normal = new su2double [nDim]; - /*--- Initializate the structure ---*/ Nodes[0] = val_point; - for (iDim = 0; iDim < nDim; iDim ++) - Normal[iDim] = 0.0; + + for (iDim = 0; iDim < 3; iDim ++) Normal[iDim] = 0.0; /*--- Set to zero the variation of the coordinates ---*/ - VarCoord[0] = 0.0; - VarCoord[1] = 0.0; - VarCoord[2] = 0.0; + for (iDim = 0; iDim < 3; iDim ++) VarCoord[iDim] = 0.0; - /*--- Set to NULL variation of the rotation ---*/ + /*--- Set to nullptr variation of the rotation ---*/ - VarRot = NULL; + VarRot = nullptr; - /*--- Set to NULL donor arrays for interpolation ---*/ + /*--- Set to nullptr donor arrays for interpolation ---*/ - Donor_Points = NULL; - Donor_Proc = NULL; - Donor_Coeff = NULL; + Donor_Points = nullptr; + Donor_Proc = nullptr; + Donor_Coeff = nullptr; nDonor_Points = 1; } CVertex::~CVertex() { - if (Normal != NULL) delete[] Normal; - if (Nodes != NULL) delete[] Nodes; - - /*--- donor arrays for interpolation ---*/ - - if (VarRot != NULL) delete[] VarRot; - if (Donor_Coeff != NULL) delete[] Donor_Coeff; - if (Donor_Proc != NULL) delete[] Donor_Proc; - if (Donor_Points != NULL) delete[] Donor_Points; + delete[] VarRot; + delete[] Donor_Coeff; + delete[] Donor_Proc; + delete[] Donor_Points; } @@ -131,13 +114,16 @@ void CVertex::SetNodes_Coord(su2double *val_coord_Edge_CG, su2double *val_coord_ } -void CVertex::Allocate_DonorInfo(void){ +void CVertex::Allocate_DonorInfo(unsigned short nDonor) { + + nDonor_Points = nDonor; + + delete [] Donor_Points; + delete [] Donor_Proc; + delete [] Donor_Coeff; - if( Donor_Points != NULL ) delete [] Donor_Points; - if( Donor_Proc != NULL ) delete [] Donor_Proc; - if( Donor_Coeff != NULL ) delete [] Donor_Coeff; + Donor_Points = new unsigned long [nDonor_Points]; + Donor_Proc = new unsigned long [nDonor_Points]; + Donor_Coeff = new su2double [nDonor_Points]; - Donor_Points = new unsigned long[nDonor_Points]; - Donor_Proc = new unsigned long[nDonor_Points]; - Donor_Coeff = new su2double[nDonor_Points]; } diff --git a/Common/src/interface_interpolation/CIsoparametric.cpp b/Common/src/interface_interpolation/CIsoparametric.cpp index 4e46e5f94ee1..d1032f26495b 100644 --- a/Common/src/interface_interpolation/CIsoparametric.cpp +++ b/Common/src/interface_interpolation/CIsoparametric.cpp @@ -77,10 +77,6 @@ void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { Coord = new su2double[nDim]; Normal = new su2double[nDim]; - Buffer_Send_nVertex_Donor = new unsigned long [1]; - Buffer_Send_nFace_Donor = new unsigned long [1]; - Buffer_Send_nFaceNodes_Donor = new unsigned long [1]; - Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; Buffer_Receive_nFace_Donor = new unsigned long [nProcessor]; Buffer_Receive_nFaceNodes_Donor = new unsigned long [nProcessor]; @@ -259,7 +255,7 @@ void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { /*--- ---*/ nNodes = (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace+1] - - (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; + (unsigned int)Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; su2double *X = new su2double[nNodes*(nDim+1)]; faceindex = Buffer_Receive_FaceIndex[iProcessor*MaxFace_Donor+iFace]; // first index of this face @@ -314,7 +310,6 @@ void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { /*--- Store info ---*/ donor_elem = temp_donor; target_geometry->vertex[markTarget][iVertex]->SetDonorElem(donor_elem); // in 2D is nearest neighbor - target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nNodes); for (iDonor=0; iDonorvertex[markTarget][iVertex]->GetnDonorPoints(); - target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); + target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(nNodes); for (iDonor=0; iDonorvertex[markTarget][iVertex]->SetInterpDonorPoint(iDonor,storeGlobal[iDonor]); @@ -355,10 +350,6 @@ void CIsoparametric::Set_TransferCoeff(const CConfig* const* config) { delete[] Buffer_Receive_FaceProc; } - delete[] Buffer_Send_nVertex_Donor; - delete[] Buffer_Send_nFace_Donor; - delete[] Buffer_Send_nFaceNodes_Donor; - delete[] Buffer_Receive_nVertex_Donor; delete[] Buffer_Receive_nFace_Donor; delete[] Buffer_Receive_nFaceNodes_Donor; diff --git a/Common/src/interface_interpolation/CMirror.cpp b/Common/src/interface_interpolation/CMirror.cpp index d64d0451811d..089af9eceec1 100644 --- a/Common/src/interface_interpolation/CMirror.cpp +++ b/Common/src/interface_interpolation/CMirror.cpp @@ -39,9 +39,6 @@ void CMirror::Set_TransferCoeff(const CConfig* const* config) { const int nProcessor = size; - Buffer_Send_nFace_Donor= new unsigned long [1]; - Buffer_Send_nFaceNodes_Donor= new unsigned long [1]; - Buffer_Receive_nFace_Donor = new unsigned long [nProcessor]; Buffer_Receive_nFaceNodes_Donor = new unsigned long [nProcessor]; @@ -158,8 +155,7 @@ void CMirror::Set_TransferCoeff(const CConfig* const* config) { } } - target_vertex->SetnDonorPoints(nNodes); - target_vertex->Allocate_DonorInfo(); + target_vertex->Allocate_DonorInfo(nNodes); for (int iProcessor = 0, iDonor = 0; iProcessor < nProcessor; iProcessor++) { for (auto iFace = 0ul; iFace < Buffer_Receive_nFace_Donor[iProcessor]; iFace++) { @@ -192,9 +188,6 @@ void CMirror::Set_TransferCoeff(const CConfig* const* config) { delete[] Buffer_Receive_Coeff; } - delete[] Buffer_Send_nFace_Donor; - delete[] Buffer_Send_nFaceNodes_Donor; - delete[] Buffer_Receive_nFace_Donor; delete[] Buffer_Receive_nFaceNodes_Donor; diff --git a/Common/src/interface_interpolation/CNearestNeighbor.cpp b/Common/src/interface_interpolation/CNearestNeighbor.cpp index 9d5536e88fe7..3e0a7ee24170 100644 --- a/Common/src/interface_interpolation/CNearestNeighbor.cpp +++ b/Common/src/interface_interpolation/CNearestNeighbor.cpp @@ -56,7 +56,6 @@ void CNearestNeighbor::Set_TransferCoeff(const CConfig* const* config) { const auto nMarkerInt = config[donorZone]->GetMarker_n_ZoneInterface()/2; const auto nDim = donor_geometry->GetnDim(); - Buffer_Send_nVertex_Donor = new unsigned long [1]; Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; vector > DonorInfoVec(omp_get_max_threads()); @@ -135,8 +134,7 @@ void CNearestNeighbor::Set_TransferCoeff(const CConfig* const* config) { } /*--- Set interpolation coefficients. ---*/ - target_vertex->SetnDonorPoints(nDonor); - target_vertex->Allocate_DonorInfo(); + target_vertex->Allocate_DonorInfo(nDonor); for (auto iDonor = 0ul; iDonor < nDonor; ++iDonor) { target_vertex->SetInterpDonorPoint(iDonor, donorInfo[iDonor].pidx); @@ -154,7 +152,6 @@ void CNearestNeighbor::Set_TransferCoeff(const CConfig* const* config) { } - delete[] Buffer_Send_nVertex_Donor; delete[] Buffer_Receive_nVertex_Donor; } diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp index e25bdbb688d4..f87df1a47460 100644 --- a/Common/src/interface_interpolation/CRadialBasisFunction.cpp +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -30,6 +30,18 @@ #include "../../include/geometry/CGeometry.hpp" #include "../../include/toolboxes/CSymmetricMatrix.hpp" +#if defined(HAVE_MKL) +#include "mkl.h" +#ifndef HAVE_LAPACK +#define HAVE_LAPACK +#endif +#elif defined(HAVE_LAPACK) +// dgemm(opA, opB, m, n, k, alpha, A, lda, B, ldb, beta, C, ldc) +extern "C" void dgemm_(const char*, const char*, const int*, const int*, const int*, + const passivedouble*, const passivedouble*, const int*, const passivedouble*, + const int*, const passivedouble*, passivedouble*, const int*); +#endif + CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone) : CInterpolator(geometry_container, config, iZone, jZone) { @@ -78,35 +90,35 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { const int nDim = donor_geometry->GetnDim(); const int nProcessor = size; - Buffer_Send_nVertex_Donor = new unsigned long [1]; Buffer_Receive_nVertex_Donor = new unsigned long [nProcessor]; /*--- Process interface patches in parallel, fetch all donor point coordinates, * then distribute interpolation matrix computation over ranks and threads. * To avoid repeating calls to Collect_VertexInfo we also save the global * indices of the donor points and the mpi rank index that owns them. ---*/ - vector DonorCoordinates(nMarkerInt); - vector > DonorGlobalPoint(nMarkerInt); - vector > DonorProcessor(nMarkerInt); - vector AssignedProcessor(nMarkerInt,-1); - vector TotalWork(nProcessor,0); + + vector donorCoordinates(nMarkerInt); + vector > donorGlobalPoint(nMarkerInt); + vector > donorProcessor(nMarkerInt); + vector assignedProcessor(nMarkerInt,-1); + vector totalWork(nProcessor,0); for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; ++iMarkerInt) { /*--- On the donor side: find the tag of the boundary sharing the interface. ---*/ - const auto mark_donor = Find_InterfaceMarker(config[donorZone], iMarkerInt+1); + const auto markDonor = Find_InterfaceMarker(config[donorZone], iMarkerInt+1); /*--- On the target side: find the tag of the boundary sharing the interface. ---*/ - const auto mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt+1); + const auto markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt+1); /*--- If the zone does not contain the interface continue to the next pair of markers. ---*/ - if(!CheckInterfaceBoundary(mark_donor,mark_target)) continue; + if(!CheckInterfaceBoundary(markDonor,markTarget)) continue; unsigned long nVertexDonor = 0; - if(mark_donor != -1) nVertexDonor = donor_geometry->GetnVertex(mark_donor); + if(markDonor != -1) nVertexDonor = donor_geometry->GetnVertex(markDonor); /*--- Sets MaxLocalVertex_Donor, Buffer_Receive_nVertex_Donor. ---*/ - Determine_ArraySize(false, mark_donor, mark_target, nVertexDonor, nDim); + Determine_ArraySize(false, markDonor, markTarget, nVertexDonor, nDim); /*--- Compute total number of donor vertices. ---*/ auto nGlobalVertexDonor = accumulate(Buffer_Receive_nVertex_Donor, @@ -118,24 +130,24 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { Buffer_Receive_Coord = new su2double [ nProcessor * MaxLocalVertex_Donor * nDim ]; Buffer_Receive_GlobalPoint = new long [ nProcessor * MaxLocalVertex_Donor ]; - Collect_VertexInfo(false, mark_donor, mark_target, nVertexDonor, nDim); + Collect_VertexInfo(false, markDonor, markTarget, nVertexDonor, nDim); /*--- Compresses the gathered donor point information to simplify computations. ---*/ - auto& DonorCoord = DonorCoordinates[iMarkerInt]; - auto& DonorPoint = DonorGlobalPoint[iMarkerInt]; - auto& DonorProc = DonorProcessor[iMarkerInt]; - DonorCoord.resize(nGlobalVertexDonor, nDim); - DonorPoint.resize(nGlobalVertexDonor); - DonorProc.resize(nGlobalVertexDonor); + auto& donorCoord = donorCoordinates[iMarkerInt]; + auto& donorPoint = donorGlobalPoint[iMarkerInt]; + auto& donorProc = donorProcessor[iMarkerInt]; + donorCoord.resize(nGlobalVertexDonor, nDim); + donorPoint.resize(nGlobalVertexDonor); + donorProc.resize(nGlobalVertexDonor); auto iCount = 0ul; for (int iProcessor = 0; iProcessor < nProcessor; ++iProcessor) { auto offset = iProcessor * MaxLocalVertex_Donor; for (auto iVertex = 0ul; iVertex < Buffer_Receive_nVertex_Donor[iProcessor]; ++iVertex) { for (int iDim = 0; iDim < nDim; ++iDim) - DonorCoord(iCount,iDim) = Buffer_Receive_Coord[(offset+iVertex)*nDim + iDim]; - DonorPoint[iCount] = Buffer_Receive_GlobalPoint[offset+iVertex]; - DonorProc[iCount] = iProcessor; + donorCoord(iCount,iDim) = Buffer_Receive_Coord[(offset+iVertex)*nDim + iDim]; + donorPoint[iCount] = Buffer_Receive_GlobalPoint[offset+iVertex]; + donorProc[iCount] = iProcessor; ++iCount; } } @@ -149,13 +161,14 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { /*--- Static work scheduling over ranks based on which one has less work currently. ---*/ int iProcessor = 0; for (int i = 1; i < nProcessor; ++i) - if (TotalWork[i] < TotalWork[iProcessor]) iProcessor = i; + if (totalWork[i] < totalWork[iProcessor]) iProcessor = i; - TotalWork[iProcessor] += pow(nGlobalVertexDonor,3); // based on matrix inversion. + totalWork[iProcessor] += pow(nGlobalVertexDonor,3); // based on matrix inversion. - AssignedProcessor[iMarkerInt] = iProcessor; + assignedProcessor[iMarkerInt] = iProcessor; } + delete[] Buffer_Receive_nVertex_Donor; /*--- Compute the interpolation matrices for each patch of coordinates * assigned to the rank. Subdivide work further by threads. ---*/ @@ -165,9 +178,9 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { SU2_OMP_PARALLEL_(for schedule(dynamic,1)) for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; ++iMarkerInt) { - if (rank == AssignedProcessor[iMarkerInt]) { + if (rank == assignedProcessor[iMarkerInt]) { ComputeGeneratorMatrix(kindRBF, usePolynomial, paramRBF, - DonorCoordinates[iMarkerInt], nPolynomialVec[iMarkerInt], + donorCoordinates[iMarkerInt], nPolynomialVec[iMarkerInt], keepPolynomialRowVec[iMarkerInt], CinvTrucVec[iMarkerInt]); } } @@ -177,25 +190,25 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; iMarkerInt++) { /*--- Identify the rank that computed the interpolation matrix for this marker. ---*/ - const int iProcessor = AssignedProcessor[iMarkerInt]; + const int iProcessor = assignedProcessor[iMarkerInt]; /*--- If no processor was assigned to work, the zone does not contain the interface. ---*/ if (iProcessor < 0) continue; /*--- Setup target information. ---*/ - const int mark_target = Find_InterfaceMarker(config[targetZone], iMarkerInt+1); + const int markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt+1); unsigned long nVertexTarget = 0; - if(mark_target != -1) nVertexTarget = target_geometry->GetnVertex(mark_target); + if(markTarget != -1) nVertexTarget = target_geometry->GetnVertex(markTarget); /*--- Set references to donor information. ---*/ - auto& DonorCoord = DonorCoordinates[iMarkerInt]; - auto& DonorPoint = DonorGlobalPoint[iMarkerInt]; - auto& DonorProc = DonorProcessor[iMarkerInt]; + auto& donorCoord = donorCoordinates[iMarkerInt]; + auto& donorPoint = donorGlobalPoint[iMarkerInt]; + auto& donorProc = donorProcessor[iMarkerInt]; auto& C_inv_trunc = CinvTrucVec[iMarkerInt]; auto& nPolynomial = nPolynomialVec[iMarkerInt]; auto& keepPolynomialRow = keepPolynomialRowVec[iMarkerInt]; - const auto nGlobalVertexDonor = DonorCoord.rows(); + const auto nGlobalVertexDonor = donorCoord.rows(); #ifdef HAVE_MPI /*--- For simplicity, broadcast small information about the interpolation matrix. ---*/ @@ -221,86 +234,119 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { } #endif - /*--- Compute H (interpolation) matrix, distributing - * target points over the threads in the rank. ---*/ + /*--- Compute interpolation matrix (H). This is a large matrix-matrix product with + * the generator matrix (C_inv_trunc) on the right. We avoid instantiation + * of the entire function matrix (A) and of the result (H), but work + * on a slab (set of rows) of A/H to amortize accesses to C_inv_trunc. ---*/ + + /*--- Fetch domain target vertices. ---*/ + + vector targetVertices; targetVertices.reserve(nVertexTarget); + vector targetCoord; targetCoord.reserve(nVertexTarget); + + for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; ++iVertexTarget) { + + auto targetVertex = target_geometry->vertex[markTarget][iVertexTarget]; + auto pointTarget = targetVertex->GetNode(); + + if (target_geometry->node[pointTarget]->GetDomain()) { + targetVertices.push_back(targetVertex); + targetCoord.push_back(target_geometry->node[pointTarget]->GetCoord()); + } + } + nVertexTarget = targetVertices.size(); + + /*--- Distribute target slabs over the threads in the rank for processing. ---*/ + SU2_OMP_PARALLEL - { - su2passivevector coeff_vec(nGlobalVertexDonor); + if (nVertexTarget > 0) { - SU2_OMP_FOR_DYN(roundUpDiv(nVertexTarget, 2*omp_get_max_threads())) - for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget++) { + constexpr unsigned long targetSlabSize = 32; - auto target_vertex = target_geometry->vertex[mark_target][iVertexTarget]; - const auto point_target = target_vertex->GetNode(); + su2passivematrix funcMat(targetSlabSize, 1+nPolynomial+nGlobalVertexDonor); + su2passivematrix interpMat(targetSlabSize, nGlobalVertexDonor); - /*--- If not domain point move to next. ---*/ - if (!target_geometry->node[point_target]->GetDomain()) continue; + SU2_OMP_FOR_DYN(1) + for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget += targetSlabSize) { - const su2double* coord_i = target_geometry->node[point_target]->GetCoord(); + const auto iLastVertex = min(nVertexTarget, iVertexTarget+targetSlabSize); + const auto slabSize = iLastVertex - iVertexTarget; - /*--- Multiply target vector by C_inv_trunc to obtain the interpolation coefficients. - * The target vector is not stored, we consume its entries immediately to avoid - * strided access to C_inv_trunc (as it is row-major). ---*/ + /*--- Prepare matrix of functions A (the targets to donors matrix). ---*/ /*--- Polynominal part: ---*/ if (usePolynomial) { /*--- Constant term. ---*/ - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) - coeff_vec(j) = C_inv_trunc(0,j); + for (auto k = 0ul; k < slabSize; ++k) funcMat(k,0) = 1.0; /*--- Linear terms. ---*/ for (int iDim = 0, idx = 1; iDim < nDim; ++iDim) { /*--- Of which one may have been excluded. ---*/ if (!keepPolynomialRow[iDim]) continue; - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) - coeff_vec(j) += SU2_TYPE::GetValue(coord_i[iDim]) * C_inv_trunc(idx,j); + for (auto k = 0ul; k < slabSize; ++k) + funcMat(k, idx) = SU2_TYPE::GetValue(targetCoord[iVertexTarget+k][iDim]); idx += 1; } } - else { - /*--- Initialize vector to zero. ---*/ - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) coeff_vec(j) = 0.0; - } - /*--- RBF terms: ---*/ for (auto iVertexDonor = 0ul; iVertexDonor < nGlobalVertexDonor; ++iVertexDonor) { - auto w_ij = SU2_TYPE::GetValue(Get_RadialBasisValue(kindRBF, paramRBF, - PointsDistance(nDim, coord_i, DonorCoord[iVertexDonor]))); - - for (auto j = 0ul; j < nGlobalVertexDonor; ++j) - coeff_vec(j) += w_ij * C_inv_trunc(1+nPolynomial+iVertexDonor, j); + for (auto k = 0ul; k < slabSize; ++k) { + auto dist = PointsDistance(nDim, targetCoord[iVertexTarget+k], donorCoord[iVertexDonor]); + auto rbf = Get_RadialBasisValue(kindRBF, paramRBF, dist); + funcMat(k, 1+nPolynomial+iVertexDonor) = SU2_TYPE::GetValue(rbf); + } } - /*--- Prune small coefficients. ---*/ - auto nnz = PruneSmallCoefficients(SU2_TYPE::GetValue(pruneTol), coeff_vec); + /*--- Compute slab of the interpolation matrix. ---*/ +#ifdef HAVE_LAPACK + /*--- interpMat = funcMat * C_inv_trunc, but order of gemm arguments + * is swapped due to row-major storage of su2passivematrix. ---*/ + const char op = 'N'; + const int M = interpMat.cols(), N = slabSize, K = funcMat.cols(); + // lda = C_inv_trunc.cols() = M; ldb = funcMat.cols() = K; ldc = interpMat.cols() = M; + const passivedouble alpha = 1.0, beta = 0.0; + dgemm_(&op, &op, &M, &N, &K, &alpha, C_inv_trunc[0], &M, funcMat[0], &K, &beta, interpMat[0], &M); +#else + /*--- Naive product, loop order considers short-wide + * nature of funcMat and interpMat. ---*/ + interpMat = 0.0; + for (auto k = 0ul; k < funcMat.cols(); ++k) + for (auto i = 0ul; i < slabSize; ++i) + for (auto j = 0ul; j < interpMat.cols(); ++j) + interpMat(i,j) += funcMat(i,k) * C_inv_trunc(k,j); +#endif + /*--- Set interpolation coefficients. ---*/ + + for (auto k = 0ul; k < slabSize; ++k) { + auto targetVertex = targetVertices[iVertexTarget+k]; + + /*--- Prune small coefficients. ---*/ + auto nnz = PruneSmallCoefficients(SU2_TYPE::GetValue(pruneTol), interpMat.cols(), interpMat[k]); - /*--- Allocate and set donor information for this target point. ---*/ - target_vertex->SetnDonorPoints(nnz); - target_vertex->Allocate_DonorInfo(); + /*--- Allocate and set donor information for this target point. ---*/ + targetVertex->Allocate_DonorInfo(nnz); - for (unsigned long iVertex = 0, iSet = 0; iVertex < nGlobalVertexDonor; ++iVertex) { - if (fabs(coeff_vec(iVertex)) > 0.0) { - target_vertex->SetInterpDonorProcessor(iSet, DonorProc[iVertex]); - target_vertex->SetInterpDonorPoint(iSet, DonorPoint[iVertex]); - target_vertex->SetDonorCoeff(iSet, coeff_vec(iVertex)); - ++iSet; + for (unsigned long iVertex = 0, iSet = 0; iVertex < nGlobalVertexDonor; ++iVertex) { + auto coeff = interpMat(k,iVertex); + if (fabs(coeff) > 0.0) { + targetVertex->SetInterpDonorProcessor(iSet, donorProc[iVertex]); + targetVertex->SetInterpDonorPoint(iSet, donorPoint[iVertex]); + targetVertex->SetDonorCoeff(iSet, coeff); + ++iSet; + } } } - } // end target vertex loop } // end SU2_OMP_PARALLEL - /*--- Delete global data that will no longer be used. ---*/ - DonorCoord.resize(0,0); - vector().swap(DonorPoint); - vector().swap(DonorProc); + /*--- Free global data that will no longer be used. ---*/ + donorCoord.resize(0,0); + vector().swap(donorPoint); + vector().swap(donorProc); C_inv_trunc.resize(0,0); } // end loop over interface markers - delete[] Buffer_Send_nVertex_Donor; - delete[] Buffer_Receive_nVertex_Donor; - } void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, @@ -460,30 +506,3 @@ int CRadialBasisFunction::CheckPolynomialTerms(su2double max_diff_tol, vector thresh) { - coeffSum += coeffs(i); - ++numNonZeros; - } - else coeffs(i) = 0.0; - } - - /*--- Correct remaining coefficients, sum must be 1 for conservation. ---*/ - passivedouble correction = 1.0 / coeffSum; - for (auto i = 0ul; i < coeffs.size(); ++i) coeffs(i) *= correction; - - return numNonZeros; -} diff --git a/Common/src/interface_interpolation/CSlidingMesh.cpp b/Common/src/interface_interpolation/CSlidingMesh.cpp index 2ac685049af6..3fe75ad5c033 100644 --- a/Common/src/interface_interpolation/CSlidingMesh.cpp +++ b/Common/src/interface_interpolation/CSlidingMesh.cpp @@ -412,9 +412,7 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { /*--- Set the communication data structure and copy data from the auxiliary vectors ---*/ - target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nDonorPoints); - - target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); + target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(nDonorPoints); for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){ target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor, Coeff_Vect[iDonor]); @@ -685,8 +683,7 @@ void CSlidingMesh::Set_TransferCoeff(const CConfig* const* config) { /*--- Set the communication data structure and copy data from the auxiliary vectors ---*/ - target_geometry->vertex[markTarget][iVertex]->SetnDonorPoints(nDonorPoints); - target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(); + target_geometry->vertex[markTarget][iVertex]->Allocate_DonorInfo(nDonorPoints); for ( iDonor = 0; iDonor < nDonorPoints; iDonor++ ){ target_geometry->vertex[markTarget][iVertex]->SetDonorCoeff(iDonor, Coeff_Vect[iDonor]/Area); diff --git a/Common/src/toolboxes/CSymmetricMatrix.cpp b/Common/src/toolboxes/CSymmetricMatrix.cpp index 8ec0a020ebf7..b8eec72c2111 100644 --- a/Common/src/toolboxes/CSymmetricMatrix.cpp +++ b/Common/src/toolboxes/CSymmetricMatrix.cpp @@ -34,15 +34,13 @@ #define HAVE_LAPACK #endif #elif defined(HAVE_LAPACK) -/*--- Lapack / Blas routines used in RBF interpolation. ---*/ -extern "C" void dsptrf_(char*, int*, passivedouble*, int*, int*); -extern "C" void dsptri_(char*, int*, passivedouble*, int*, passivedouble*, int*); -extern "C" void dsytrf_(char*, int*, passivedouble*, int*, int*, passivedouble*, int*, int*); -extern "C" void dsytri_(char*, int*, passivedouble*, int*, int*, passivedouble*, int*); -extern "C" void dpotrf_(char*, int*, passivedouble*, int*, int*); -extern "C" void dpotri_(char*, int*, passivedouble*, int*, int*); -extern "C" void dsymm_(char*, char*, int*, int*, passivedouble*, passivedouble*, int*, - passivedouble*, int*, passivedouble*, passivedouble*, int*); +/*--- Lapack / Blas routines used in CSymmetricMatrix. ---*/ +extern "C" void dsytrf_(const char*, const int*, passivedouble*, const int*, int*, passivedouble*, const int*, int*); +extern "C" void dsytri_(const char*, const int*, passivedouble*, const int*, const int*, passivedouble*, int*); +extern "C" void dpotrf_(const char*, const int*, passivedouble*, const int*, int*); +extern "C" void dpotri_(const char*, const int*, passivedouble*, const int*, int*); +extern "C" void dsymm_(const char*, const char*, const int*, const int*, const passivedouble*, const passivedouble*, + const int*, const passivedouble*, const int*, const passivedouble*, passivedouble*, const int*); #endif @@ -283,10 +281,10 @@ void CSymmetricMatrix::MatMatMult(const char side, val_vec.data(), M, mat_in.data(), N, beta, mat_out.data(), N); #else // BLAS/LAPACK /*--- Right and lower because matrices are in row major order. ---*/ - char side = 'R', uplo = 'L'; - passivedouble alpha = 1.0, beta = 0.0; - dsymm_(&side, &uplo, &N, &M, &alpha, const_cast(val_vec.data()), - &M, const_cast(mat_in.data()), &N, &beta, mat_out.data(), &N); + const char side = 'R', uplo = 'L'; + const passivedouble alpha = 1.0, beta = 0.0; + dsymm_(&side, &uplo, &N, &M, &alpha, val_vec.data(), &M, + mat_in.data(), &N, &beta, mat_out.data(), &N); #endif } /*--- Right_side: mat_out = mat_in * this. ---*/ @@ -309,10 +307,10 @@ void CSymmetricMatrix::MatMatMult(const char side, val_vec.data(), N, mat_in.data(), N, beta, mat_out.data(), N); #else // BLAS/LAPACK /*--- Left and lower because matrices are in row major order. ---*/ - char side = 'L', uplo = 'L'; - passivedouble alpha = 1.0, beta = 0.0; - dsymm_(&side, &uplo, &N, &M, &alpha, const_cast(val_vec.data()), - &N, const_cast(mat_in.data()), &N, &beta, mat_out.data(), &N); + const char side = 'L', uplo = 'L'; + const passivedouble alpha = 1.0, beta = 0.0; + dsymm_(&side, &uplo, &N, &M, &alpha, val_vec.data(), &N, + mat_in.data(), &N, &beta, mat_out.data(), &N); #endif } From 9ef625a042087cfabfc1ca6dd697bb6683c75bca Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 19 Mar 2020 21:28:18 +0000 Subject: [PATCH 039/112] fix deallocation bug in C2DContainer for systems with weird aligned_alloc --- Common/include/toolboxes/C2DContainer.hpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/Common/include/toolboxes/C2DContainer.hpp b/Common/include/toolboxes/C2DContainer.hpp index 93f1314766f6..4b3dfcad14f1 100644 --- a/Common/include/toolboxes/C2DContainer.hpp +++ b/Common/include/toolboxes/C2DContainer.hpp @@ -113,15 +113,14 @@ class AccessorImpl \ AccessorImpl& operator= (AccessorImpl&& other) noexcept \ { \ - if(m_data!=nullptr) free(m_data); \ + MemoryAllocation::aligned_free(m_data); \ MOVE; m_data=other.m_data; other.m_data=nullptr; \ return *this; \ } \ \ ~AccessorImpl() \ { \ - if(m_data!=nullptr) \ - MemoryAllocation::aligned_free(m_data); \ + MemoryAllocation::aligned_free(m_data); \ } /*! * Shorthand for when specialization has only one more member than m_data. @@ -381,7 +380,7 @@ class C2DContainer : { /*--- fully static, no allocation needed ---*/ if(StaticRows!=DynamicSize && StaticCols!=DynamicSize) - return StaticRows*StaticCols; + return StaticRows*StaticCols; /*--- dynamic row vector, swap size specification ---*/ if(StaticRows==1 && StaticCols==DynamicSize) {cols = rows; rows = 1;} @@ -400,12 +399,10 @@ class C2DContainer : /*--- compare with current dimensions to determine if deallocation is needed, also makes the container safe against self assignment no need to check for 0 size as the allocators handle that ---*/ - if(m_data!=nullptr) - { - if(rows==this->rows() && cols==this->cols()) - return reqSize; - free(m_data); - } + if(rows==this->rows() && cols==this->cols()) + return reqSize; + + MemoryAllocation::aligned_free(m_data); /*--- request actual allocation to base class as it needs specialization ---*/ size_t bytes = reqSize*sizeof(Scalar_t); From faafa22c194f2037ee0276d8339ae6e46bc3f776 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 19 Mar 2020 21:29:11 +0000 Subject: [PATCH 040/112] more CSymmetricMatrix optimization --- Common/include/toolboxes/CSymmetricMatrix.hpp | 43 ++- .../CRadialBasisFunction.cpp | 8 +- Common/src/toolboxes/CSymmetricMatrix.cpp | 246 ++++++++---------- 3 files changed, 131 insertions(+), 166 deletions(-) diff --git a/Common/include/toolboxes/CSymmetricMatrix.hpp b/Common/include/toolboxes/CSymmetricMatrix.hpp index b32613a864ae..853257dbdf54 100644 --- a/Common/include/toolboxes/CSymmetricMatrix.hpp +++ b/Common/include/toolboxes/CSymmetricMatrix.hpp @@ -37,30 +37,15 @@ using namespace std; * with LAPACK to use optimized matrix inversion and multiplication routines. */ class CSymmetricMatrix { + static_assert(su2passivematrix::Storage == StorageType::RowMajor, + "Row major storage is assumed for LAPACK."); private: - enum DecompositionType { NONE, CHOLESKY, LU }; - - vector val_vec, decomp_vec; - vector perm_vec; - int sz = 0; - bool initialized = false; - DecompositionType decomposed = NONE; - - inline void CheckBounds(int i, int j) const { - assert(initialized && "Matrix not initialized."); - assert(i>=0 && i=0 && j& perm) const; // Matrix inversion using LAPACK routines (LDLT and LLT factorization). void CalcInv_sytri(); void CalcInv_potri(); @@ -71,23 +56,23 @@ class CSymmetricMatrix { void Initialize(int N); - inline int GetSize() const { return sz; } + inline int Size() const { return mat.rows(); } - inline passivedouble Get(int i, int j) const { return val_vec[IdxSym(i,j)]; } + inline passivedouble Get(int i, int j) const { return mat(min(i,j),max(i,j)); } - inline void Set(int i, int j, passivedouble val) { val_vec[IdxSym(i,j)] = val; } + inline void Set(int i, int j, passivedouble val) { mat(min(i,j),max(i,j)) = val; } - inline passivedouble& operator() (int i, int j) { return val_vec[IdxSym(i,j)]; } + inline passivedouble& operator() (int i, int j) { return mat(min(i,j),max(i,j)); } - inline const passivedouble& operator() (int i, int j) const { return val_vec[IdxSym(i,j)]; } + inline const passivedouble& operator() (int i, int j) const { return mat(min(i,j),max(i,j)); } template void MatVecMult(ForwardIt vec_in, ForwardIt vec_out) const { - for (int i = 0; i < sz; ++i) { + for (int i = 0; i < Size(); ++i) { *vec_out = 0.0; auto vec = vec_in; - for (int k = 0; k < sz; ++k) + for (int k = 0; k < Size(); ++k) *vec_out += *(vec++) * Get(i,k); ++vec_out; } @@ -95,6 +80,8 @@ class CSymmetricMatrix { void MatMatMult(const char side, const su2passivematrix& mat_in, su2passivematrix& mat_out) const; - void Invert(const bool is_spd); + void Invert(bool is_spd = false); + + su2passivematrix StealData(); }; diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp index f87df1a47460..ff8d7697ed65 100644 --- a/Common/src/interface_interpolation/CRadialBasisFunction.cpp +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -40,6 +40,7 @@ extern "C" void dgemm_(const char*, const char*, const int*, const int*, const int*, const passivedouble*, const passivedouble*, const int*, const passivedouble*, const int*, const passivedouble*, passivedouble*, const int*); +#define DGEMM dgemm_ #endif @@ -305,7 +306,7 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { const int M = interpMat.cols(), N = slabSize, K = funcMat.cols(); // lda = C_inv_trunc.cols() = M; ldb = funcMat.cols() = K; ldc = interpMat.cols() = M; const passivedouble alpha = 1.0, beta = 0.0; - dgemm_(&op, &op, &M, &N, &K, &alpha, C_inv_trunc[0], &M, funcMat[0], &K, &beta, interpMat[0], &M); + DGEMM(&op, &op, &M, &N, &K, &alpha, C_inv_trunc[0], &M, funcMat[0], &K, &beta, interpMat[0], &M); #else /*--- Naive product, loop order considers short-wide * nature of funcMat and interpMat. ---*/ @@ -427,10 +428,7 @@ void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool us else { /*--- No polynomial term used in the interpolation, C_inv_trunc = M^-1. ---*/ - C_inv_trunc.resize(nVertexDonor, nVertexDonor); - for (auto i = 0ul; i < nVertexDonor; ++i) - for (auto j = 0ul; j < nVertexDonor; ++j) - C_inv_trunc(i,j) = global_M(i,j); + C_inv_trunc = global_M.StealData(); } // end usePolynomial diff --git a/Common/src/toolboxes/CSymmetricMatrix.cpp b/Common/src/toolboxes/CSymmetricMatrix.cpp index b8eec72c2111..f5190a8672e0 100644 --- a/Common/src/toolboxes/CSymmetricMatrix.cpp +++ b/Common/src/toolboxes/CSymmetricMatrix.cpp @@ -41,49 +41,44 @@ extern "C" void dpotrf_(const char*, const int*, passivedouble*, const int*, int extern "C" void dpotri_(const char*, const int*, passivedouble*, const int*, int*); extern "C" void dsymm_(const char*, const char*, const int*, const int*, const passivedouble*, const passivedouble*, const int*, const passivedouble*, const int*, const passivedouble*, passivedouble*, const int*); +#define DSYMM dsymm_ #endif - -void CSymmetricMatrix::Initialize(int N) -{ - sz = N; - val_vec.resize(sz*sz); - initialized = true; -} +void CSymmetricMatrix::Initialize(int N) { mat.resize(N,N); } void CSymmetricMatrix::CholeskyDecompose() { #ifndef HAVE_LAPACK - assert(initialized && "Matrix not initialized."); - - for (int j = 0; j < sz; ++j) { + int j; + for (j = 0; j < Size(); ++j) { passivedouble sum = 0.0; for (int k = 0; k < j; ++k) sum -= pow(Get(j,k), 2); sum += Get(j,j); - assert(sum > 0.0 && "LLT failed, matrix is not SPD."); + if (sum < 0.0) break; // not SPD Set(j, j, sqrt(sum)); - for (int i = j+1; i < sz; ++i) { + for (int i = j+1; i < Size(); ++i) { passivedouble sum = 0.0; for (int k = 0; k < j; ++k) sum -= Get(i,k) * Get(j,k); sum += Get(i,j); Set(i, j, sum / Get(j,j)); } } - decomposed = CHOLESKY; + if (j!=Size()) SU2_MPI::Error("LLT factorization failed.", CURRENT_FUNCTION); #endif } -void CSymmetricMatrix::LUDecompose() +void CSymmetricMatrix::LUDecompose(su2activematrix& decomp, vector& perm) const { #ifndef HAVE_LAPACK - assert(initialized && "Matrix not initialized."); + const int sz = Size(); /*--- Copy matrix values to LU matrix, init permutation vec. ---*/ - decomp_vec.resize(sz*sz); - perm_vec.resize(sz); + decomp.resize(sz,sz); + perm.resize(sz); + for (int i = 0; i < sz; ++i) { - perm_vec[i] = i; + perm[i] = i; for (int j = i; j < sz; ++j) decomp(j,i) = decomp(i,j) = Get(i,j); } @@ -99,7 +94,7 @@ void CSymmetricMatrix::LUDecompose() } if (pivot_idx != j) { - swap(perm_vec[j], perm_vec[pivot_idx]); + swap(perm[j], perm[pivot_idx]); for (int k = 0; k < sz; ++k) swap(decomp(j,k), decomp(pivot_idx,k)); } @@ -111,116 +106,104 @@ void CSymmetricMatrix::LUDecompose() for (int i = j+1; i < sz; ++i) decomp(i,k) -= decomp(j,k)*decomp(i,j); } - - decomposed = LU; #endif } -void CSymmetricMatrix::CalcInv() +void CSymmetricMatrix::CalcInv(bool is_spd) { #ifndef HAVE_LAPACK - assert(initialized && "Matrix not initialized."); - - /*--- Decompose matrix if not done yet. ---*/ - if (decomposed == NONE) { LUDecompose(); } + const int sz = Size(); /*--- Compute inverse from decomposed matrices. ---*/ - switch (decomposed) { - - case CHOLESKY: - { - /*--- Initialize inverse matrix. ---*/ - vector inv(sz*sz, 0.0); - - /*--- Compute L inverse. ---*/ - /*--- Solve smaller and smaller systems. ---*/ - for (int j = 0; j < sz; ++j) { - /*--- Forward substitution. ---*/ - inv[IdxSym(j,j)] = 1.0 / Get(j,j); - - for (int i = j+1; i < sz; ++i) { - passivedouble sum = 0.0; - for (int k = j; k < i; ++k) sum -= Get(i,k) * inv[IdxSym(k,j)]; - inv[IdxSym(i,j)] = sum / Get(i,i); - } - } // L inverse in inv - - /*--- Multiply inversed matrices overwrite val_vec. ---*/ - for (int j = 0; j < sz; ++j) - for (int i = j; i < sz; ++i) { - passivedouble sum = 0.0; - for (int k = i; k < sz; ++k) sum += inv[IdxSym(k,i)] * inv[IdxSym(k,j)]; - Set(i, j, sum); - } - - break; - } - - case LU: - { - /*--- Alias val_vec. ---*/ - vector& inv = val_vec; - - /*--- Invert L and U matrices in place. ---*/ - for (int j = 0; j < sz; ++j) { - inv[IdxFull(j,j)] = 1.0 / decomp(j,j); - - for (int i = j+1; i < sz; ++i) { - inv[IdxFull(i,j)] = -decomp(i,j); - inv[IdxFull(j,i)] = -decomp(j,i) * inv[IdxFull(j,j)]; - - for (int k = j+1; k < i; ++k) { - inv[IdxFull(i,j)] -= decomp(i,k) * inv[IdxFull(k,j)]; - inv[IdxFull(j,i)] -= decomp(k,i) * inv[IdxFull(j,k)]; - } - if (j+1 <= i) inv[IdxFull(j,i)] /= decomp(i,i); - } + if (is_spd) + { + CholeskyDecompose(); + + /*--- Initialize inverse matrix. ---*/ + CSymmetricMatrix inv(sz); + + /*--- Compute L inverse. ---*/ + /*--- Solve smaller and smaller systems. ---*/ + for (int j = 0; j < sz; ++j) { + /*--- Forward substitution. ---*/ + inv(j,j) = 1.0 / Get(j,j); + + for (int i = j+1; i < sz; ++i) { + passivedouble sum = 0.0; + for (int k = j; k < i; ++k) sum -= Get(i,k) * inv(k,j); + inv(i,j) = sum / Get(i,i); } - // inverses in val_vec - - /*--- Multiply U_inv by L_inv, overwrite decomp_vec. ---*/ - for (int i = 0; i < sz; ++i) - for (int j = 0; j < sz; ++j) { - decomp(i,j) = 0.0; - for (int k = max(i,j); k < sz; ++k) - decomp(i,j) += inv[IdxFull(i,k)] * ((k==j)? 1.0 : inv[IdxFull(k,j)]); + } // L inverse in inv + + /*--- Multiply inversed matrices overwrite mat. ---*/ + for (int j = 0; j < sz; ++j) + for (int i = j; i < sz; ++i) { + passivedouble sum = 0.0; + for (int k = i; k < sz; ++k) sum += inv(k,i) * inv(k,j); + Set(i, j, sum); + } + } + else + { + su2passivematrix decomp; + vector perm; + LUDecompose(decomp, perm); + + /*--- Alias mat. ---*/ + auto& inv = mat; + + /*--- Invert L and U matrices in place. ---*/ + for (int j = 0; j < sz; ++j) { + inv(j,j) = 1.0 / decomp(j,j); + + for (int i = j+1; i < sz; ++i) { + inv(i,j) = -decomp(i,j); + inv(j,i) = -decomp(j,i) * inv(j,j); + + for (int k = j+1; k < i; ++k) { + inv(i,j) -= decomp(i,k) * inv(k,j); + inv(j,i) -= decomp(k,i) * inv(j,k); } + if (j+1 <= i) inv(j,i) /= decomp(i,i); + } + } + // inverses in mat - /*--- Permute multiplied matrix to recover A_inv, overwrite val_vec. ---*/ + /*--- Multiply U_inv by L_inv, overwrite decomp_vec. ---*/ + for (int i = 0; i < sz; ++i) for (int j = 0; j < sz; ++j) { - int k = perm_vec[j]; - for (int i = k; i < sz; ++i) Set(i, k, decomp(i,j)); + decomp(i,j) = 0.0; + for (int k = max(i,j); k < sz; ++k) + decomp(i,j) += inv(i,k) * ((k==j)? 1.0 : inv(k,j)); } - /*--- Decomposition no longer needed. ---*/ - vector().swap(decomp_vec); - - break; + /*--- Permute multiplied matrix to recover A_inv, overwrite mat. ---*/ + for (int j = 0; j < sz; ++j) { + int k = perm[j]; + for (int i = k; i < sz; ++i) Set(i, k, decomp(i,j)); } - default: assert(false && "Default (LU) decomposition failed."); } - - decomposed = NONE; #endif } void CSymmetricMatrix::CalcInv_sytri() { #ifdef HAVE_LAPACK - char uplo = 'L'; + const char uplo = 'L'; + const int sz = Size(); int info; - perm_vec.resize(sz); // ipiv array + vector ipiv(sz); /*--- Query the optimum work size. ---*/ int query = -1; passivedouble tmp; - dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), &tmp, &query, &info); + dsytrf_(&uplo, &sz, mat.data(), &sz, ipiv.data(), &tmp, &query, &info); query = static_cast(tmp); - decomp_vec.resize(query); // work array + vector work(query); /*--- Factorize and invert. ---*/ - dsytrf_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &query, &info); + dsytrf_(&uplo, &sz, mat.data(), &sz, ipiv.data(), work.data(), &query, &info); if (info!=0) SU2_MPI::Error("LDLT factorization failed.", CURRENT_FUNCTION); - dsytri_(&uplo, &sz, val_vec.data(), &sz, perm_vec.data(), decomp_vec.data(), &info); + dsytri_(&uplo, &sz, mat.data(), &sz, ipiv.data(), work.data(), &info); if (info!=0) SU2_MPI::Error("Inversion with LDLT factorization failed.", CURRENT_FUNCTION); decomposed = NONE; @@ -230,12 +213,13 @@ void CSymmetricMatrix::CalcInv_sytri() void CSymmetricMatrix::CalcInv_potri() { #ifdef HAVE_LAPACK - char uplo = 'L'; + const char uplo = 'L'; + const int sz = Size(); int info; - dpotrf_(&uplo, &sz, val_vec.data(), &sz, &info); + dpotrf_(&uplo, &sz, mat.data(), &sz, &info); if (info!=0) SU2_MPI::Error("LLT factorization failed.", CURRENT_FUNCTION); - dpotri_(&uplo, &sz, val_vec.data(), &sz, &info); + dpotri_(&uplo, &sz, mat.data(), &sz, &info); if (info!=0) SU2_MPI::Error("Inversion with LLT factorization failed.", CURRENT_FUNCTION); decomposed = NONE; @@ -248,9 +232,7 @@ void CSymmetricMatrix::Invert(const bool is_spd) if(is_spd) CalcInv_potri(); else CalcInv_sytri(); #else - if(!is_spd) LUDecompose(); - else CholeskyDecompose(); - CalcInv(); + CalcInv(is_spd); #endif } @@ -258,60 +240,58 @@ void CSymmetricMatrix::MatMatMult(const char side, const su2passivematrix& mat_in, su2passivematrix& mat_out) const { - /*--- Assumes row major storage of in/out matrices for LAPACK. ---*/ - static_assert(su2passivematrix::Storage == StorageType::RowMajor,""); - /*--- Left side: mat_out = this * mat_in. ---*/ if (side == 'L' || side == 'l') { - int M = sz, N = mat_in.cols(); + const int M = Size(), N = mat_in.cols(); assert(M == mat_in.rows()); mat_out.resize(M,N); -#if !defined(HAVE_LAPACK) // Naive product +#ifdef HAVE_LAPACK + /*--- Right and lower because matrices are in row major order. ---*/ + const char side = 'R', uplo = 'L'; + const passivedouble alpha = 1.0, beta = 0.0; + DSYMM(&side, &uplo, &N, &M, &alpha, mat.data(), &M, + mat_in.data(), &N, &beta, mat_out.data(), &N); +#else // Naive product for (int i = 0; i < M; ++i) for (int j = 0; j < N; ++j) { mat_out(i,j) = 0.0; for (int k = 0; k < M; ++k) mat_out(i,j) += Get(i,k) * mat_in(k,j); } -#elif defined(HAVE_MKL) - passivedouble alpha = 1.0, beta = 0.0; - cblas_dsymm(CblasRowMajor, CblasLeft, CblasUpper, M, N, alpha, - val_vec.data(), M, mat_in.data(), N, beta, mat_out.data(), N); -#else // BLAS/LAPACK - /*--- Right and lower because matrices are in row major order. ---*/ - const char side = 'R', uplo = 'L'; - const passivedouble alpha = 1.0, beta = 0.0; - dsymm_(&side, &uplo, &N, &M, &alpha, val_vec.data(), &M, - mat_in.data(), &N, &beta, mat_out.data(), &N); #endif } /*--- Right_side: mat_out = mat_in * this. ---*/ else { - int M = mat_in.rows(), N = sz; + const int M = mat_in.rows(), N = Size(); assert(N == mat_in.cols()); mat_out.resize(M,N); -#if !defined(HAVE_LAPACK) // Naive product +#ifdef HAVE_LAPACK + /*--- Left and lower because matrices are in row major order. ---*/ + const char side = 'L', uplo = 'L'; + const passivedouble alpha = 1.0, beta = 0.0; + DSYMM(&side, &uplo, &N, &M, &alpha, mat.data(), &N, + mat_in.data(), &N, &beta, mat_out.data(), &N); +#else // Naive product for (int i = 0; i < M; ++i) for (int j = 0; j < N; ++j) { mat_out(i,j) = 0.0; for (int k = 0; k < N; ++k) mat_out(i,j) += mat_in(i,k) * Get(k,j); } -#elif defined(HAVE_MKL) - passivedouble alpha = 1.0, beta = 0.0; - cblas_dsymm(CblasRowMajor, CblasRight, CblasUpper, M, N, alpha, - val_vec.data(), N, mat_in.data(), N, beta, mat_out.data(), N); -#else // BLAS/LAPACK - /*--- Left and lower because matrices are in row major order. ---*/ - const char side = 'L', uplo = 'L'; - const passivedouble alpha = 1.0, beta = 0.0; - dsymm_(&side, &uplo, &N, &M, &alpha, val_vec.data(), &N, - mat_in.data(), &N, &beta, mat_out.data(), &N); #endif } +} + +su2passivematrix CSymmetricMatrix::StealData() +{ + /*--- Fill lower triangular part. ---*/ + for (int i = 1; i < Size(); ++i) + for (int j = 0; j < i; ++j) + mat(i,j) = mat(j,i); + return move(mat); } From 72da53230a6dffa15b3ecf4b95d2d1e893324f27 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 19 Mar 2020 22:33:22 +0000 Subject: [PATCH 041/112] fix AD build --- Common/include/toolboxes/CSymmetricMatrix.hpp | 2 +- Common/src/toolboxes/CSymmetricMatrix.cpp | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Common/include/toolboxes/CSymmetricMatrix.hpp b/Common/include/toolboxes/CSymmetricMatrix.hpp index 853257dbdf54..bc15264c7049 100644 --- a/Common/include/toolboxes/CSymmetricMatrix.hpp +++ b/Common/include/toolboxes/CSymmetricMatrix.hpp @@ -45,7 +45,7 @@ class CSymmetricMatrix { // Not optimized dense matrix factorization and inversion for portability. void CalcInv(bool is_spd); void CholeskyDecompose(); - void LUDecompose(su2activematrix& decomp, vector& perm) const; + void LUDecompose(su2passivematrix& decomp, vector& perm) const; // Matrix inversion using LAPACK routines (LDLT and LLT factorization). void CalcInv_sytri(); void CalcInv_potri(); diff --git a/Common/src/toolboxes/CSymmetricMatrix.cpp b/Common/src/toolboxes/CSymmetricMatrix.cpp index f5190a8672e0..5845daac5496 100644 --- a/Common/src/toolboxes/CSymmetricMatrix.cpp +++ b/Common/src/toolboxes/CSymmetricMatrix.cpp @@ -68,7 +68,7 @@ void CSymmetricMatrix::CholeskyDecompose() #endif } -void CSymmetricMatrix::LUDecompose(su2activematrix& decomp, vector& perm) const +void CSymmetricMatrix::LUDecompose(su2passivematrix& decomp, vector& perm) const { #ifndef HAVE_LAPACK const int sz = Size(); @@ -205,8 +205,6 @@ void CSymmetricMatrix::CalcInv_sytri() if (info!=0) SU2_MPI::Error("LDLT factorization failed.", CURRENT_FUNCTION); dsytri_(&uplo, &sz, mat.data(), &sz, ipiv.data(), work.data(), &info); if (info!=0) SU2_MPI::Error("Inversion with LDLT factorization failed.", CURRENT_FUNCTION); - - decomposed = NONE; #endif } @@ -221,8 +219,6 @@ void CSymmetricMatrix::CalcInv_potri() if (info!=0) SU2_MPI::Error("LLT factorization failed.", CURRENT_FUNCTION); dpotri_(&uplo, &sz, mat.data(), &sz, &info); if (info!=0) SU2_MPI::Error("Inversion with LLT factorization failed.", CURRENT_FUNCTION); - - decomposed = NONE; #endif } From 6864175cea2601be8cdfccbcbf6e2dbed003be09 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Thu, 19 Mar 2020 23:29:26 +0000 Subject: [PATCH 042/112] basic interpolation statistics and error checking --- .../interface_interpolation/CInterpolator.hpp | 7 +++- .../CRadialBasisFunction.hpp | 8 +++++ .../CRadialBasisFunction.cpp | 36 +++++++++++++++++++ SU2_CFD/src/drivers/CDriver.cpp | 16 ++++----- 4 files changed, 56 insertions(+), 11 deletions(-) diff --git a/Common/include/interface_interpolation/CInterpolator.hpp b/Common/include/interface_interpolation/CInterpolator.hpp index 6da4e028c4f2..af95221275f0 100644 --- a/Common/include/interface_interpolation/CInterpolator.hpp +++ b/Common/include/interface_interpolation/CInterpolator.hpp @@ -103,7 +103,7 @@ class CInterpolator { CInterpolator(CGeometry ****geometry_container, const CConfig* const* config, unsigned int iZone, unsigned int jZone); /*! - * \brief No default construction allowed. + * \brief No default construction allowed to force zones and geometry to always be set. */ CInterpolator(void) = delete; @@ -119,6 +119,11 @@ class CInterpolator { */ virtual void Set_TransferCoeff(const CConfig* const* config) = 0; + /*! + * \brief Print information about the interpolation. + */ + virtual void PrintStatistics(void) const { } + /*! * \brief Find the index of the interface marker shared by that zone * \param[in] config - Definition of the particular problem. diff --git a/Common/include/interface_interpolation/CRadialBasisFunction.hpp b/Common/include/interface_interpolation/CRadialBasisFunction.hpp index c5f9cb19e856..01c056ac9b84 100644 --- a/Common/include/interface_interpolation/CRadialBasisFunction.hpp +++ b/Common/include/interface_interpolation/CRadialBasisFunction.hpp @@ -34,6 +34,9 @@ * \brief Radial basis function interpolation. */ class CRadialBasisFunction final : public CInterpolator { +private: + unsigned long MinDonors = 0, AvgDonors = 0, MaxDonors = 0; + public: static_assert(su2passivematrix::Storage == StorageType::RowMajor, "This class does not work otherwise."); @@ -53,6 +56,11 @@ class CRadialBasisFunction final : public CInterpolator { */ void Set_TransferCoeff(const CConfig* const* config) override; + /*! + * \brief Print information about the interpolation. + */ + void PrintStatistics(void) const override; + /*! * \brief Compute the value of a radial basis function, this is static so it can be re-used. * \param[in] type - of radial basis function diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp index ff8d7697ed65..8d503ecaca3c 100644 --- a/Common/src/interface_interpolation/CRadialBasisFunction.cpp +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -49,6 +49,12 @@ CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, con Set_TransferCoeff(config); } +void CRadialBasisFunction::PrintStatistics() const { + if (rank == MASTER_NODE) + cout << "Min / avg / max number of RBF donors per target point: " + << MinDonors << " / " << AvgDonors << " / " << MaxDonors << endl; +} + su2double CRadialBasisFunction::Get_RadialBasisValue(ENUM_RADIALBASIS type, const su2double radius, const su2double dist) { su2double rbf = dist/radius; @@ -188,6 +194,9 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { /*--- Final loop over interface markers to compute the interpolation coefficients. ---*/ + unsigned long totalTargetPoints = 0, totalDonorPoints = 0; + MinDonors = 1<<30; MaxDonors = 0; + for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; iMarkerInt++) { /*--- Identify the rank that computed the interpolation matrix for this marker. ---*/ @@ -256,6 +265,7 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { } } nVertexTarget = targetVertices.size(); + totalTargetPoints += nVertexTarget; /*--- Distribute target slabs over the threads in the rank for processing. ---*/ @@ -267,6 +277,8 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { su2passivematrix funcMat(targetSlabSize, 1+nPolynomial+nGlobalVertexDonor); su2passivematrix interpMat(targetSlabSize, nGlobalVertexDonor); + unsigned long minDonors = 1<<30, maxDonors = 0, totalDonors = 0; + SU2_OMP_FOR_DYN(1) for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget += targetSlabSize) { @@ -323,6 +335,9 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { /*--- Prune small coefficients. ---*/ auto nnz = PruneSmallCoefficients(SU2_TYPE::GetValue(pruneTol), interpMat.cols(), interpMat[k]); + totalDonors += nnz; + minDonors = min(minDonors, nnz); + maxDonors = max(maxDonors, nnz); /*--- Allocate and set donor information for this target point. ---*/ targetVertex->Allocate_DonorInfo(nnz); @@ -338,6 +353,13 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { } } } // end target vertex loop + + SU2_OMP_CRITICAL + { + totalDonorPoints += totalDonors; + MinDonors = min(MinDonors, minDonors); + MaxDonors = max(MaxDonors, maxDonors); + } } // end SU2_OMP_PARALLEL /*--- Free global data that will no longer be used. ---*/ @@ -348,6 +370,20 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { } // end loop over interface markers + /*--- Final reduction of interpolation statistics. ---*/ + auto Reduce = [](SU2_MPI::Op op, unsigned long &val) { + auto tmp = val; + SU2_MPI::Reduce(&tmp, &val, 1, MPI_UNSIGNED_LONG, op, MASTER_NODE, MPI_COMM_WORLD); + }; + Reduce(MPI_SUM, totalTargetPoints); + Reduce(MPI_SUM, totalDonorPoints); + Reduce(MPI_MIN, MinDonors); + Reduce(MPI_MAX, MaxDonors); + AvgDonors = passivedouble(totalDonorPoints) / totalTargetPoints; + if (MinDonors == 0) + SU2_MPI::Error("One or more target points have no donors, either:\n" + " - The RBF radius is too small.\n" + " - The pruning tolerance is too aggressive.", CURRENT_FUNCTION); } void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, diff --git a/SU2_CFD/src/drivers/CDriver.cpp b/SU2_CFD/src/drivers/CDriver.cpp index 0d17063686a1..aec0ab14f9d2 100644 --- a/SU2_CFD/src/drivers/CDriver.cpp +++ b/SU2_CFD/src/drivers/CDriver.cpp @@ -100,8 +100,6 @@ CDriver::CDriver(char* confFile, unsigned short val_nZone, SU2_Comm MPICommunica #endif #endif - unsigned short jZone; - SU2_MPI::SetComm(MPICommunicator); rank = SU2_MPI::GetRank(); @@ -149,13 +147,8 @@ CDriver::CDriver(char* confFile, unsigned short val_nZone, SU2_Comm MPICommunica /*--- Allocate transfer and interpolation container --- */ - interface_container[iZone] = new CInterface*[nZone]; - interpolator_container[iZone] = new CInterpolator*[nZone]; - - for (jZone = 0; jZone < nZone; jZone++){ - interface_container[iZone][jZone] = NULL; - interpolator_container[iZone][jZone] = NULL; - } + interface_container[iZone] = new CInterface*[nZone] (); + interpolator_container[iZone] = new CInterpolator*[nZone] (); for (iInst = 0; iInst < nInst[iZone]; iInst++){ @@ -212,7 +205,7 @@ CDriver::CDriver(char* confFile, unsigned short val_nZone, SU2_Comm MPICommunica /*--- Dynamic mesh processing. ---*/ DynamicMesh_Preprocessing(config_container[iZone], geometry_container[iZone][iInst], solver_container[iZone][iInst], - iteration_container[iZone][iInst], grid_movement[iZone][iInst], surface_movement[iZone]); + iteration_container[iZone][iInst], grid_movement[iZone][iInst], surface_movement[iZone]); /*--- Static mesh processing. ---*/ StaticMesh_Preprocessing(config_container[iZone], geometry_container[iZone][iInst], surface_movement[iZone]); @@ -2681,6 +2674,9 @@ void CDriver::Interface_Preprocessing(CConfig **config, CSolver***** solver, CGe break; } + if (interpolation[donorZone][targetZone]) + interpolation[donorZone][targetZone]->PrintStatistics(); + /*--- Initialize the appropriate transfer strategy ---*/ if (rank == MASTER_NODE) cout << "Transferring "; From f48e235a612b90b273cb331e62578bbca0ba5de5 Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Fri, 20 Mar 2020 10:57:32 +0000 Subject: [PATCH 043/112] cleaner computation of RBF polynomial terms --- .../CRadialBasisFunction.hpp | 5 +- .../CRadialBasisFunction.cpp | 99 +++++++++++-------- 2 files changed, 62 insertions(+), 42 deletions(-) diff --git a/Common/include/interface_interpolation/CRadialBasisFunction.hpp b/Common/include/interface_interpolation/CRadialBasisFunction.hpp index 01c056ac9b84..7d75460ea392 100644 --- a/Common/include/interface_interpolation/CRadialBasisFunction.hpp +++ b/Common/include/interface_interpolation/CRadialBasisFunction.hpp @@ -34,12 +34,13 @@ * \brief Radial basis function interpolation. */ class CRadialBasisFunction final : public CInterpolator { + static_assert(su2passivematrix::Storage == StorageType::RowMajor, + "This class relies on row major storage throughout."); private: unsigned long MinDonors = 0, AvgDonors = 0, MaxDonors = 0; + passivedouble Density = 0.0; public: - static_assert(su2passivematrix::Storage == StorageType::RowMajor, - "This class does not work otherwise."); /*! * \brief Constructor of the class. * \param[in] geometry - Geometrical definition of the problem. diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp index 8d503ecaca3c..9bbce09ee271 100644 --- a/Common/src/interface_interpolation/CRadialBasisFunction.cpp +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -50,9 +50,13 @@ CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, con } void CRadialBasisFunction::PrintStatistics() const { - if (rank == MASTER_NODE) - cout << "Min / avg / max number of RBF donors per target point: " - << MinDonors << " / " << AvgDonors << " / " << MaxDonors << endl; + if (rank == MASTER_NODE) { + cout.precision(3); + cout << " Min/avg/max number of RBF donors per target point: " + << MinDonors << "/" << AvgDonors << "/" << MaxDonors + << "\n Interpolation matrix is " << Density << "% dense." << endl; + cout.unsetf(ios::floatfield); + } } su2double CRadialBasisFunction::Get_RadialBasisValue(ENUM_RADIALBASIS type, const su2double radius, const su2double dist) @@ -128,8 +132,8 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { Determine_ArraySize(false, markDonor, markTarget, nVertexDonor, nDim); /*--- Compute total number of donor vertices. ---*/ - auto nGlobalVertexDonor = accumulate(Buffer_Receive_nVertex_Donor, - Buffer_Receive_nVertex_Donor+nProcessor, 0ul); + const auto nGlobalVertexDonor = accumulate(Buffer_Receive_nVertex_Donor, + Buffer_Receive_nVertex_Donor+nProcessor, 0ul); /*--- Gather coordinates and global point indices. ---*/ Buffer_Send_Coord = new su2double [ MaxLocalVertex_Donor * nDim ]; @@ -194,7 +198,8 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { /*--- Final loop over interface markers to compute the interpolation coefficients. ---*/ - unsigned long totalTargetPoints = 0, totalDonorPoints = 0; + /*--- Initialize variables for interpolation statistics. ---*/ + unsigned long totalTargetPoints = 0, totalDonorPoints = 0, denseSize = 0; MinDonors = 1<<30; MaxDonors = 0; for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; iMarkerInt++) { @@ -266,6 +271,7 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { } nVertexTarget = targetVertices.size(); totalTargetPoints += nVertexTarget; + denseSize += nVertexTarget*nGlobalVertexDonor; /*--- Distribute target slabs over the threads in the rank for processing. ---*/ @@ -277,6 +283,7 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { su2passivematrix funcMat(targetSlabSize, 1+nPolynomial+nGlobalVertexDonor); su2passivematrix interpMat(targetSlabSize, nGlobalVertexDonor); + /*--- Thread-local variables for statistics. ---*/ unsigned long minDonors = 1<<30, maxDonors = 0, totalDonors = 0; SU2_OMP_FOR_DYN(1) @@ -370,20 +377,29 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { } // end loop over interface markers - /*--- Final reduction of interpolation statistics. ---*/ + /*--- Final reduction of interpolation statistics and basic sanity checks. ---*/ auto Reduce = [](SU2_MPI::Op op, unsigned long &val) { auto tmp = val; - SU2_MPI::Reduce(&tmp, &val, 1, MPI_UNSIGNED_LONG, op, MASTER_NODE, MPI_COMM_WORLD); + SU2_MPI::Allreduce(&tmp, &val, 1, MPI_UNSIGNED_LONG, op, MPI_COMM_WORLD); }; Reduce(MPI_SUM, totalTargetPoints); Reduce(MPI_SUM, totalDonorPoints); + Reduce(MPI_SUM, denseSize); Reduce(MPI_MIN, MinDonors); Reduce(MPI_MAX, MaxDonors); - AvgDonors = passivedouble(totalDonorPoints) / totalTargetPoints; + + if (totalTargetPoints == 0) + SU2_MPI::Error("Somehow there are no target interpolation points.", CURRENT_FUNCTION); + if (MinDonors == 0) SU2_MPI::Error("One or more target points have no donors, either:\n" + " - The interface surfaces are not in contact.\n" " - The RBF radius is too small.\n" " - The pruning tolerance is too aggressive.", CURRENT_FUNCTION); + + AvgDonors = totalDonorPoints / totalTargetPoints; + Density = totalDonorPoints / (0.01*denseSize); + } void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool usePolynomial, @@ -392,14 +408,14 @@ void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool us const su2double interfaceCoordTol = 1e6 * numeric_limits::epsilon(); - const auto nVertexDonor = coords.rows(); + const int nVertexDonor = coords.rows(); const int nDim = coords.cols(); /*--- Populate interpolation kernel. ---*/ CSymmetricMatrix global_M(nVertexDonor); - for (auto iVertex = 0ul; iVertex < nVertexDonor; ++iVertex) - for (auto jVertex = iVertex; jVertex < nVertexDonor; ++jVertex) + for (int iVertex = 0; iVertex < nVertexDonor; ++iVertex) + for (int jVertex = iVertex; jVertex < nVertexDonor; ++jVertex) global_M(iVertex, jVertex) = SU2_TYPE::GetValue(Get_RadialBasisValue(type, radius, PointsDistance(nDim, coords[iVertex], coords[jVertex]))); @@ -413,7 +429,7 @@ void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool us /*--- Fill P matrix (P for points, with an extra top row of ones). ---*/ su2passivematrix P(1+nDim, nVertexDonor); - for (auto iVertex = 0ul; iVertex < nVertexDonor; iVertex++) { + for (int iVertex = 0; iVertex < nVertexDonor; iVertex++) { P(0, iVertex) = 1.0; for (int iDim = 0; iDim < nDim; ++iDim) P(1+iDim, iVertex) = SU2_TYPE::GetValue(coords(iVertex, iDim)); @@ -422,44 +438,47 @@ void CRadialBasisFunction::ComputeGeneratorMatrix(ENUM_RADIALBASIS type, bool us /*--- Check if points lie on a plane and remove one coordinate from P if so. ---*/ nPolynomial = CheckPolynomialTerms(interfaceCoordTol, keepPolynomialRow, P); - /*--- Compute Mp = (P * M^-1 * P^T)^-1 ---*/ - CSymmetricMatrix Mp(nPolynomial+1); + /*--- Compute Q = P * M^-1 ---*/ + su2passivematrix Q; + global_M.MatMatMult('R', P, Q); - su2passivematrix tmp; - global_M.MatMatMult('R', P, tmp); // tmp = P * M^-1 + /*--- Compute Mp = (Q * P^T)^-1 ---*/ + CSymmetricMatrix Mp(nPolynomial+1); - for (int i = 0; i <= nPolynomial; ++i) // Mp = tmp * P + for (int i = 0; i <= nPolynomial; ++i) for (int j = i; j <= nPolynomial; ++j) { Mp(i,j) = 0.0; - for (auto k = 0ul; k < nVertexDonor; ++k) Mp(i,j) += tmp(i,k) * P(j,k); + for (int k = 0; k < nVertexDonor; ++k) + Mp(i,j) += Q(i,k) * P(j,k); } - Mp.Invert(false); // Mp = Mp^-1 + Mp.Invert(false); - /*--- Compute M_p * P * M^-1, the top part of C_inv_trunc. ---*/ - Mp.MatMatMult('L', P, tmp); + /*--- Compute M_p * Q, the top part of C_inv_trunc. ---*/ su2passivematrix C_inv_top; - global_M.MatMatMult('R', tmp, C_inv_top); + Mp.MatMatMult('L', Q, C_inv_top); - /*--- Compute tmp = (I - P^T * M_p * P * M^-1), part of the bottom part of - C_inv_trunc. Note that most of the product is known from the top part. ---*/ - tmp.resize(nVertexDonor, nVertexDonor); - - for (auto i = 0ul; i < nVertexDonor; ++i) { - for (auto j = 0ul; j < nVertexDonor; ++j) { - tmp(i,j) = 0.0; - for (int k = 0; k <= nPolynomial; ++k) tmp(i,j) -= P(k,i) * C_inv_top(k,j); - } - tmp(i,i) += 1.0; // identity part - } - - /*--- Compute M^-1 * (I - P^T * M_p * P * M^-1), finalize bottom of C_inv_trunc. ---*/ - global_M.MatMatMult('L', tmp, C_inv_trunc); + /*--- Compute M^-1 - Q^T * C_inv_top, the bottom (main) part of C_inv_trunc. ---*/ + su2passivematrix C_inv_bot = global_M.StealData(); +#ifdef HAVE_LAPACK + /*--- Order of gemm arguments swapped due to row-major storage. ---*/ + const char opa = 'N', opb = 'T'; + const int M = nVertexDonor, N = nVertexDonor, K = nPolynomial+1; + // lda = C_inv_top.cols() = M; ldb = Q.cols() = M; ldc = C_inv_bot.cols() = M; + const passivedouble alpha = -1.0, beta = 1.0; + DGEMM(&opa, &opb, &M, &N, &K, &alpha, C_inv_top[0], &M, Q[0], &M, &beta, C_inv_bot[0], &M); +#else // naive product + for (int i = 0; i < nVertexDonor; ++i) + for (int j = 0; j < nVertexDonor; ++j) + for (int k = 0; k <= nPolynomial; ++k) + C_inv_bot(i,j) -= Q(k,i) * C_inv_top(k,j); +#endif - /*--- Merge top and bottom of C_inv_trunc. ---*/ - tmp = move(C_inv_trunc); + /*--- Merge top and bottom of C_inv_trunc. More intrusive memory + * management, or separate handling of top and bottom, would + * avoid these copies (and associated temporary vars). ---*/ C_inv_trunc.resize(1+nPolynomial+nVertexDonor, nVertexDonor); memcpy(C_inv_trunc[0], C_inv_top.data(), C_inv_top.size()*sizeof(passivedouble)); - memcpy(C_inv_trunc[1+nPolynomial], tmp.data(), tmp.size()*sizeof(passivedouble)); + memcpy(C_inv_trunc[1+nPolynomial], C_inv_bot.data(), C_inv_bot.size()*sizeof(passivedouble)); } else { /*--- No polynomial term used in the interpolation, C_inv_trunc = M^-1. ---*/ From 2af4b88f841da8e9e904bc8dc0147725b2161bdb Mon Sep 17 00:00:00 2001 From: Pedro Gomes Date: Fri, 20 Mar 2020 15:59:54 +0000 Subject: [PATCH 044/112] fix bug in pruning of coefficients, more interp stats --- .../CRadialBasisFunction.hpp | 15 ++++---- .../CRadialBasisFunction.cpp | 38 +++++++++++++------ 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/Common/include/interface_interpolation/CRadialBasisFunction.hpp b/Common/include/interface_interpolation/CRadialBasisFunction.hpp index 7d75460ea392..48416ae33053 100644 --- a/Common/include/interface_interpolation/CRadialBasisFunction.hpp +++ b/Common/include/interface_interpolation/CRadialBasisFunction.hpp @@ -38,7 +38,7 @@ class CRadialBasisFunction final : public CInterpolator { "This class relies on row major storage throughout."); private: unsigned long MinDonors = 0, AvgDonors = 0, MaxDonors = 0; - passivedouble Density = 0.0; + passivedouble Density = 0.0, AvgCorrection = 0.0, MaxCorrection = 0.0; public: /*! @@ -99,22 +99,23 @@ class CRadialBasisFunction final : public CInterpolator { */ static int CheckPolynomialTerms(su2double max_diff_tol, vector& keep_row, su2passivematrix &P); +private: /*! * \brief Helper function, prunes (by setting to zero) small interpolation coefficients, * i.e. <= tolerance*max(abs(coeffs)). The vector is re-scaled such that sum(coeffs)==1. * \param[in] tolerance - Relative pruning tolerance. * \param[in] size - Size of the coefficient vector. * \param[in,out] coeffs - Iterator to start of vector of interpolation coefficients. - * \return Number of non-zero coefficients after pruning. + * \return Number of non-zero coefficients after pruning and correction factor. */ - template - static Int PruneSmallCoefficients(Float tolerance, Int size, ForwardIt coeffs) { + template + static pair PruneSmallCoefficients(Float tolerance, Int size, ForwardIt coeffs) { /*--- Determine the pruning threshold. ---*/ Float thresh = 0.0; auto end = coeffs; for (Int i = 0; i < size; ++i) - thresh = max(thresh, fabs(*(++end))); + thresh = max(thresh, fabs(*(end++))); thresh *= tolerance; /*--- Prune and count non-zeros. ---*/ @@ -130,9 +131,9 @@ class CRadialBasisFunction final : public CInterpolator { /*--- Correct remaining coefficients, sum must be 1 for conservation. ---*/ Float correction = 1.0 / coeffSum; - for (auto it = coeffs; it != end; ++it) *it *= correction; + while (coeffs != end) *(coeffs++) *= correction; - return numNonZeros; + return make_pair(numNonZeros, correction); } }; diff --git a/Common/src/interface_interpolation/CRadialBasisFunction.cpp b/Common/src/interface_interpolation/CRadialBasisFunction.cpp index 9bbce09ee271..7938efb4a364 100644 --- a/Common/src/interface_interpolation/CRadialBasisFunction.cpp +++ b/Common/src/interface_interpolation/CRadialBasisFunction.cpp @@ -50,13 +50,16 @@ CRadialBasisFunction::CRadialBasisFunction(CGeometry ****geometry_container, con } void CRadialBasisFunction::PrintStatistics() const { - if (rank == MASTER_NODE) { - cout.precision(3); - cout << " Min/avg/max number of RBF donors per target point: " - << MinDonors << "/" << AvgDonors << "/" << MaxDonors - << "\n Interpolation matrix is " << Density << "% dense." << endl; - cout.unsetf(ios::floatfield); - } + if (rank != MASTER_NODE) return; + cout.precision(3); + cout << " Min/avg/max number of RBF donors per target point: " + << MinDonors << "/" << AvgDonors << "/" << MaxDonors << "\n" + << " Avg/max correction factor after pruning: " << AvgCorrection << "/" << MaxCorrection; + if (MaxCorrection < 1.1 || AvgCorrection < 1.02) cout << " (ok)\n"; + else if (MaxCorrection < 2.0 && AvgCorrection < 1.05) cout << " (warning)\n"; + else cout << " <<< WARNING >>>\n"; + cout << " Interpolation matrix is " << Density << "% dense." << endl; + cout.unsetf(ios::floatfield); } su2double CRadialBasisFunction::Get_RadialBasisValue(ENUM_RADIALBASIS type, const su2double radius, const su2double dist) @@ -200,7 +203,7 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { /*--- Initialize variables for interpolation statistics. ---*/ unsigned long totalTargetPoints = 0, totalDonorPoints = 0, denseSize = 0; - MinDonors = 1<<30; MaxDonors = 0; + MinDonors = 1<<30; MaxDonors = 0; MaxCorrection = 0.0; AvgCorrection = 0.0; for (unsigned short iMarkerInt = 0; iMarkerInt < nMarkerInt; iMarkerInt++) { @@ -212,7 +215,7 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { /*--- Setup target information. ---*/ const int markTarget = Find_InterfaceMarker(config[targetZone], iMarkerInt+1); unsigned long nVertexTarget = 0; - if(markTarget != -1) nVertexTarget = target_geometry->GetnVertex(markTarget); + if (markTarget != -1) nVertexTarget = target_geometry->GetnVertex(markTarget); /*--- Set references to donor information. ---*/ auto& donorCoord = donorCoordinates[iMarkerInt]; @@ -285,6 +288,7 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { /*--- Thread-local variables for statistics. ---*/ unsigned long minDonors = 1<<30, maxDonors = 0, totalDonors = 0; + passivedouble sumCorr = 0.0, maxCorr = 0.0; SU2_OMP_FOR_DYN(1) for (auto iVertexTarget = 0ul; iVertexTarget < nVertexTarget; iVertexTarget += targetSlabSize) { @@ -341,10 +345,14 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { auto targetVertex = targetVertices[iVertexTarget+k]; /*--- Prune small coefficients. ---*/ - auto nnz = PruneSmallCoefficients(SU2_TYPE::GetValue(pruneTol), interpMat.cols(), interpMat[k]); + auto info = PruneSmallCoefficients(SU2_TYPE::GetValue(pruneTol), interpMat.cols(), interpMat[k]); + auto nnz = info.first; totalDonors += nnz; minDonors = min(minDonors, nnz); maxDonors = max(maxDonors, nnz); + auto corr = fabs(info.second-1.0); // far from 1 either way is bad; + sumCorr += corr; + maxCorr = max(maxCorr, corr); /*--- Allocate and set donor information for this target point. ---*/ targetVertex->Allocate_DonorInfo(nnz); @@ -366,6 +374,8 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { totalDonorPoints += totalDonors; MinDonors = min(MinDonors, minDonors); MaxDonors = max(MaxDonors, maxDonors); + AvgCorrection += sumCorr; + MaxCorrection = max(MaxCorrection, maxCorr); } } // end SU2_OMP_PARALLEL @@ -387,7 +397,11 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { Reduce(MPI_SUM, denseSize); Reduce(MPI_MIN, MinDonors); Reduce(MPI_MAX, MaxDonors); - +#ifdef HAVE_MPI + passivedouble tmp1 = AvgCorrection, tmp2 = MaxCorrection; + MPI_Allreduce(&tmp1, &AvgCorrection, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(&tmp2, &MaxCorrection, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); +#endif if (totalTargetPoints == 0) SU2_MPI::Error("Somehow there are no target interpolation points.", CURRENT_FUNCTION); @@ -397,6 +411,8 @@ void CRadialBasisFunction::Set_TransferCoeff(const CConfig* const* config) { " - The RBF radius is too small.\n" " - The pruning tolerance is too aggressive.", CURRENT_FUNCTION); + MaxCorrection += 1.0; // put back the reference "1" + AvgCorrection = AvgCorrection / totalTargetPoints + 1.0; AvgDonors = totalDonorPoints / totalTargetPoints; Density = totalDonorPoints / (0.01*denseSize); From ceb37bce0fec3c275c605d1ef39d35b909df83b5 Mon Sep 17 00:00:00 2001 From: cvencro Date: Sat, 21 Mar 2020 18:49:18 +0000 Subject: [PATCH 045/112] Revert "add dual time derivative" This reverts commit d77f73c68694da40bd660375cc3568137fbadb03. --- .../variables/CDiscAdjMeshBoundVariable.hpp | 11 -- SU2_CFD/include/variables/CMeshVariable.hpp | 15 --- SU2_CFD/src/iteration_structure.cpp | 4 +- SU2_CFD/src/solvers/CDiscAdjMeshSolver.cpp | 110 +----------------- .../variables/CDiscAdjMeshBoundVariable.cpp | 8 -- SU2_CFD/src/variables/CMeshVariable.cpp | 37 ------ 6 files changed, 2 insertions(+), 183 deletions(-) diff --git a/SU2_CFD/include/variables/CDiscAdjMeshBoundVariable.hpp b/SU2_CFD/include/variables/CDiscAdjMeshBoundVariable.hpp index 565458a40f5b..6ed2d4ef8017 100644 --- a/SU2_CFD/include/variables/CDiscAdjMeshBoundVariable.hpp +++ b/SU2_CFD/include/variables/CDiscAdjMeshBoundVariable.hpp @@ -37,9 +37,6 @@ class CDiscAdjMeshBoundVariable final : public CVariable { MatrixType Bound_Disp_Sens; /*!< \brief Store the reference coordinates of the mesh. */ MatrixType Bound_Disp_Direct; /*!< \brief Store the reference boundary displacements of the mesh. */ - MatrixType DualTime_Derivative; - MatrixType DualTime_Derivative_n; - MatrixType Solution_BGS_k; /*!< \brief BGS solution to compute overall convergence. */ CVertexMap VertexMap; /*!< \brief Object that controls accesses to the variables of this class. */ @@ -65,14 +62,6 @@ class CDiscAdjMeshBoundVariable final : public CVariable { */ void AllocateBoundaryVariables(CConfig *config); - inline void SetDual_Time_Derivative(unsigned long iPoint, unsigned long iVar, su2double der) override { DualTime_Derivative(iPoint,iVar) = der; } - - inline void SetDual_Time_Derivative_n(unsigned long iPoint, unsigned long iVar, su2double der) override { DualTime_Derivative_n(iPoint,iVar) = der; } - - inline su2double GetDual_Time_Derivative(unsigned long iPoint, unsigned long iVar) const override { return DualTime_Derivative(iPoint,iVar); } - - inline su2double GetDual_Time_Derivative_n(unsigned long iPoint, unsigned long iVar) const override { return DualTime_Derivative_n(iPoint,iVar); } - /*! * \brief Get the value of the displacement imposed at the boundary. * \return Value of the boundary displacement. diff --git a/SU2_CFD/include/variables/CMeshVariable.hpp b/SU2_CFD/include/variables/CMeshVariable.hpp index 025f9a0c841e..b434b82197e1 100644 --- a/SU2_CFD/include/variables/CMeshVariable.hpp +++ b/SU2_CFD/include/variables/CMeshVariable.hpp @@ -99,19 +99,4 @@ class CMeshVariable : public CVariable { adj_mesh[iDim] = SU2_TYPE::GetDerivative(Mesh_Coord(iPoint,iDim)); } - /*! - * \brief Register the variables in the solution_time_n array as input/output variable. - */ - void RegisterSolution(bool input, bool push_index = true); - - /*! - * \brief Register the variables in the solution_time_n array as input/output variable. - */ - void RegisterSolution_time_n(); - - /*! - * \brief Register the variables in the solution_time_n1 array as input/output variable. - */ - void RegisterSolution_time_n1(); - }; diff --git a/SU2_CFD/src/iteration_structure.cpp b/SU2_CFD/src/iteration_structure.cpp index 539cc57f94c6..b7a118b19a46 100644 --- a/SU2_CFD/src/iteration_structure.cpp +++ b/SU2_CFD/src/iteration_structure.cpp @@ -2262,9 +2262,7 @@ void CDiscAdjFluidIteration::Preprocess(COutput *output, if (config[val_iZone]->AddRadiation()){ solver[val_iZone][val_iInst][MESH_0][ADJRAD_SOL]->Preprocessing(geometry[val_iZone][val_iInst][MESH_0], solver[val_iZone][val_iInst][MESH_0], config[val_iZone] , MESH_0, 0, RUNTIME_ADJRAD_SYS, false); } - if (config[val_iZone]->GetDeform_Mesh()) { - solver[val_iZone][val_iInst][MESH_0][ADJMESH_SOL]->Preprocessing(geometry[val_iZone][val_iInst][MESH_0], solver[val_iZone][val_iInst][MESH_0], config[val_iZone] , MESH_0, 0, RUNTIME_ADJFLOW_SYS, false); - } + } diff --git a/SU2_CFD/src/solvers/CDiscAdjMeshSolver.cpp b/SU2_CFD/src/solvers/CDiscAdjMeshSolver.cpp index 711970ebabeb..ce3108d51699 100644 --- a/SU2_CFD/src/solvers/CDiscAdjMeshSolver.cpp +++ b/SU2_CFD/src/solvers/CDiscAdjMeshSolver.cpp @@ -123,61 +123,19 @@ CDiscAdjMeshSolver::~CDiscAdjMeshSolver(void){ void CDiscAdjMeshSolver::Preprocessing(CGeometry *geometry, CSolver **solver_container, CConfig *config_container, unsigned short iMesh, unsigned short iRKStep, unsigned short RunTime_EqSystem, bool Output){ - bool dual_time = (config_container->GetTime_Marching() == DT_STEPPING_1ST || - config_container->GetTime_Marching() == DT_STEPPING_2ND); - su2double *solution_n, *solution_n1; - unsigned long iPoint; - unsigned short iVar; - if (dual_time) { - for (iPoint = 0; iPointGetnPoint(); iPoint++) { - solution_n = nodes->GetSolution_time_n(iPoint); - solution_n1 = nodes->GetSolution_time_n1(iPoint); - for (iVar = 0; iVar < nVar; iVar++) { - nodes->SetDual_Time_Derivative(iPoint, iVar, solution_n[iVar]+nodes->GetDual_Time_Derivative_n(iPoint, iVar)); - nodes->SetDual_Time_Derivative_n(iPoint,iVar, solution_n1[iVar]); - } - } - } } void CDiscAdjMeshSolver::SetRecording(CGeometry* geometry, CConfig *config){ - bool time_n1_needed = config->GetTime_Marching() == DT_STEPPING_2ND; - bool time_n_needed = (config->GetTime_Marching() == DT_STEPPING_1ST) || time_n1_needed; - bool time_domain = config->GetTime_Domain(); unsigned long iPoint; - unsigned short iDim; - /*--- Reset the solution to the initial (converged) solution ---*/ for (iPoint = 0; iPoint < nPoint; iPoint++) { direct_solver->GetNodes()->SetBound_Disp(iPoint,nodes->GetBoundDisp_Direct(iPoint)); } - if (time_domain) { - // for (iPoint = 0; iPoint < nPoint; iPoint++) { - // for (iDim = 0; iDim < nVar; iDim++) { - // AD::ResetInput(direct_solver->GetNodes()->GetSolution(iPoint)[iDim]); - // } - // } - if (time_n_needed) { - for (iPoint = 0; iPoint < nPoint; iPoint++) { - for (iDim = 0; iDim < nVar; iDim++) { - AD::ResetInput(direct_solver->GetNodes()->GetSolution_time_n(iPoint)[iDim]); - } - } - } - if (time_n1_needed) { - for (iPoint = 0; iPoint < nPoint; iPoint++) { - for (iDim = 0; iDim < nVar; iDim++) { - AD::ResetInput(direct_solver->GetNodes()->GetSolution_time_n1(iPoint)[iDim]); - } - } - } - } - /*--- Set indices to zero ---*/ RegisterVariables(geometry, config, true); @@ -186,20 +144,10 @@ void CDiscAdjMeshSolver::SetRecording(CGeometry* geometry, CConfig *config){ void CDiscAdjMeshSolver::RegisterSolution(CGeometry *geometry, CConfig *config){ - bool time_n1_needed = (config->GetTime_Marching() == DT_STEPPING_2ND); - bool time_n_needed = (config->GetTime_Marching() == DT_STEPPING_1ST) || time_n1_needed; - bool time_domain = config->GetTime_Domain(); - /*--- Register reference mesh coordinates ---*/ bool input = true; direct_solver->GetNodes()->Register_MeshCoord(input); - if (time_domain) { - // direct_solver->GetNodes()->RegisterSolution(input); - if (time_n_needed) - direct_solver->GetNodes()->RegisterSolution_time_n(); - if (time_n1_needed) - direct_solver->GetNodes()->RegisterSolution_time_n1(); - } + } void CDiscAdjMeshSolver::RegisterVariables(CGeometry *geometry, CConfig *config, bool reset){ @@ -212,13 +160,7 @@ void CDiscAdjMeshSolver::RegisterVariables(CGeometry *geometry, CConfig *config, void CDiscAdjMeshSolver::ExtractAdjoint_Solution(CGeometry *geometry, CConfig *config){ - bool time_n1_needed = config->GetTime_Marching() == DT_STEPPING_2ND; - bool time_domain = config->GetTime_Domain(); - bool dual_time = (config->GetTime_Marching() == DT_STEPPING_1ST || - config->GetTime_Marching() == DT_STEPPING_2ND); - unsigned long iPoint; - unsigned short iDim; /*--- Extract the sensitivities of the mesh coordinates ---*/ @@ -234,56 +176,6 @@ void CDiscAdjMeshSolver::ExtractAdjoint_Solution(CGeometry *geometry, CConfig *c } - if (time_domain) { - // for (iPoint = 0; iPoint < nPoint; iPoint++) { - - // /*--- Extract the adjoint solution at time n ---*/ - - // direct_solver->GetNodes()->GetAdjointSolution(iPoint,Solution); - - // /*--- Store the adjoint solution at time n ---*/ - - // nodes->SetSolution(iPoint,Solution); - // } - - if (dual_time) { - for (iPoint = 0; iPoint < nPoint; iPoint++) { - - /*--- Extract the adjoint solution at time n ---*/ - - direct_solver->GetNodes()->GetAdjointSolution_time_n(iPoint,Solution); - - /*--- Store the adjoint solution at time n ---*/ - - nodes->Set_Solution_time_n(iPoint,Solution); - } - } - if (time_n1_needed) { - - for (iPoint = 0; iPoint < nPoint; iPoint++) { - - /*--- Extract the adjoint solution at time n-1 ---*/ - - direct_solver->GetNodes()->GetAdjointSolution_time_n1(iPoint,Solution); - - /*--- Store the adjoint solution at time n-1 ---*/ - - nodes->Set_Solution_time_n1(iPoint,Solution); - } - } - - for (iPoint = 0; iPoint < nPoint; iPoint++) { - for (iDim = 0; iDim < nVar; iDim++) { - Solution[iDim] = nodes->GetSolution(iPoint,iDim); - } - if (dual_time) { - for (iDim = 0; iDim < nVar; iDim++) { - Solution[iDim] += nodes->GetDual_Time_Derivative(iPoint,iDim); - } - } - nodes->SetSolution(iPoint,Solution); - } - } } void CDiscAdjMeshSolver::ExtractAdjoint_Variables(CGeometry *geometry, CConfig *config){ diff --git a/SU2_CFD/src/variables/CDiscAdjMeshBoundVariable.cpp b/SU2_CFD/src/variables/CDiscAdjMeshBoundVariable.cpp index 616af24eb1a3..6680eb7ec018 100644 --- a/SU2_CFD/src/variables/CDiscAdjMeshBoundVariable.cpp +++ b/SU2_CFD/src/variables/CDiscAdjMeshBoundVariable.cpp @@ -35,14 +35,6 @@ CDiscAdjMeshBoundVariable::CDiscAdjMeshBoundVariable(unsigned long npoint, unsig nDim = ndim; VertexMap.Reset(nPoint); - - if (config->GetTime_Domain()) { - DualTime_Derivative.resize(nPoint,nDim) = su2double(0.0); - DualTime_Derivative_n.resize(nPoint,nDim) = su2double(0.0); - - Solution_time_n.resize(nPoint,nDim) = su2double(0.0); - Solution_time_n1.resize(nPoint,nDim) = su2double(0.0); - } } void CDiscAdjMeshBoundVariable::AllocateBoundaryVariables(CConfig *config) { diff --git a/SU2_CFD/src/variables/CMeshVariable.cpp b/SU2_CFD/src/variables/CMeshVariable.cpp index 17e73d3ee584..9c1af06f2db1 100644 --- a/SU2_CFD/src/variables/CMeshVariable.cpp +++ b/SU2_CFD/src/variables/CMeshVariable.cpp @@ -60,40 +60,3 @@ void CMeshVariable::Register_MeshCoord(bool input) { AD::RegisterOutput(Mesh_Coord(iPoint,iDim)); } } - -void CMeshVariable::RegisterSolution(bool input, bool push_index) { - for (unsigned long iPoint = 0; iPoint < nPoint; ++iPoint) { - for (unsigned long iDim = 0; iDim < nDim; iDim++) { - if(input) { - if(push_index) { - AD::RegisterInput(Solution(iPoint,iDim)); - } - else { - AD::RegisterInput(Solution(iPoint,iDim), false); - AD::SetIndex(AD_InputIndex(iPoint,iDim), Solution(iPoint,iDim)); - } - } - else { - AD::RegisterOutput(Solution(iPoint,iDim)); - if(!push_index) - AD::SetIndex(AD_OutputIndex(iPoint,iDim), Solution(iPoint,iDim)); - } - } - } -} - -void CMeshVariable::RegisterSolution_time_n() { - for (unsigned long iPoint = 0; iPoint < nPoint; ++iPoint) { - for(unsigned long iDim=0; iDim Date: Sat, 21 Mar 2020 19:17:33 +0000 Subject: [PATCH 046/112] add regression test results --- TestCases/serial_regression.py | 12 ++++++++++++ TestCases/serial_regression_AD.py | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/TestCases/serial_regression.py b/TestCases/serial_regression.py index 09657a95b35c..4a45ccb1e55b 100644 --- a/TestCases/serial_regression.py +++ b/TestCases/serial_regression.py @@ -933,6 +933,18 @@ def main(): unst_inc_turb_naca0015_sa.unsteady = True test_list.append(unst_inc_turb_naca0015_sa) + # unsteady pitching NACA0012, Euler, Deforming + unst_deforming_naca0012 = TestCase('unst_deforming_naca0012') + unst_deforming_naca0012.cfg_dir = "disc_adj_euler/naca0012_pitching" + unst_deforming_naca0012.cfg_file = "inv_NACA0012_pitching_deform.cfg" + unst_deforming_naca0012.test_iter = 1 + unst_deforming_naca0012.test_vals = [ -6.910285, -7.233256, -7.273257, -6.366622] #last 4 columns + unst_deforming_naca0012.su2_exec = "SU2_CFD" + unst_deforming_naca0012.timeout = 1600 + unst_deforming_naca0012.tol = 0.00001 + unst_deforming_naca0012.unsteady = True + test_list.append(unst_deforming_naca0012) + ###################################### ### NICFD ### ###################################### diff --git a/TestCases/serial_regression_AD.py b/TestCases/serial_regression_AD.py index e90b5d0e973a..49329cbf4898 100644 --- a/TestCases/serial_regression_AD.py +++ b/TestCases/serial_regression_AD.py @@ -205,6 +205,18 @@ def main(): discadj_pitchingNACA0012.unsteady = True test_list.append(discadj_pitchingNACA0012) + # deforming pitching NACA0012 + unst_deforming_naca0012 = TestCase('unst_deforming_naca0012') + unst_deforming_naca0012.cfg_dir = "disc_adj_euler/naca0012_pitching" + unst_deforming_naca0012.cfg_file = "inv_NACA0012_pitching_deform_ad.cfg" + unst_deforming_naca0012.test_iter = 1 + unst_deforming_naca0012.test_vals = [ -3.743594, -3.546465, 1.0917e+03, 3.3439e-06] #last 4 columns + unst_deforming_naca0012.su2_exec = "SU2_CFD_AD" + unst_deforming_naca0012.timeout = 1600 + unst_deforming_naca0012.tol = 0.00001 + unst_deforming_naca0012.unsteady = True + test_list.append(unst_deforming_naca0012) + ################################### ### Structural Adjoint ### ################################### From 92078855350bf578276044d5584f012586543c6f Mon Sep 17 00:00:00 2001 From: cvencro Date: Sat, 21 Mar 2020 22:19:21 +0000 Subject: [PATCH 047/112] update vals to start of time iteration --- .../naca0012_pitching/solution_flow_00000.dat | Bin 0 -> 418990 bytes .../naca0012_pitching/solution_flow_00001.dat | Bin 0 -> 418990 bytes .../naca0012_pitching/solution_flow_00002.dat | Bin 0 -> 418990 bytes .../naca0012_pitching/solution_flow_00003.dat | Bin 0 -> 418990 bytes .../naca0012_pitching/solution_flow_00004.dat | Bin 0 -> 418990 bytes TestCases/serial_regression.py | 4 ++-- TestCases/serial_regression_AD.py | 4 ++-- 7 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 TestCases/disc_adj_euler/naca0012_pitching/solution_flow_00000.dat create mode 100644 TestCases/disc_adj_euler/naca0012_pitching/solution_flow_00001.dat create mode 100644 TestCases/disc_adj_euler/naca0012_pitching/solution_flow_00002.dat create mode 100644 TestCases/disc_adj_euler/naca0012_pitching/solution_flow_00003.dat create mode 100644 TestCases/disc_adj_euler/naca0012_pitching/solution_flow_00004.dat diff --git a/TestCases/disc_adj_euler/naca0012_pitching/solution_flow_00000.dat b/TestCases/disc_adj_euler/naca0012_pitching/solution_flow_00000.dat new file mode 100644 index 0000000000000000000000000000000000000000..824fc41b86814294d564c764a8c99d2a7992237f GIT binary patch literal 418990 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 z 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