Skip to content

Commit f83bde4

Browse files
authored
Efficiency improvements for optimization of linear elasticity problems (#2815)
* allow adjoin-only, fix residual and objective reporting in single and multizone solvers * save main recording for functions that don't need it * update ref
1 parent 154d085 commit f83bde4

9 files changed

Lines changed: 76 additions & 27 deletions

File tree

Common/src/linear_algebra/CPastixWrapper.cpp

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,7 @@ void CPastixWrapper<ScalarType>::Initialize(CGeometry* geometry, const CConfig*
204204
SU2_MPI::Error("Error analyzing matrix: " + std::to_string(rc), CURRENT_FUNCTION);
205205
}
206206

207-
if (mpi_rank == MASTER_NODE && verb > 0)
208-
cout << " +--------------------------------------------------------------------+" << endl;
207+
if (mpi_rank == MASTER_NODE && verb > 0) cout << "+-------------------------------------------------+" << endl;
209208

210209
isinitialized = true;
211210
}
@@ -250,10 +249,8 @@ void CPastixWrapper<ScalarType>::Factorize(CGeometry* geometry, const CConfig* c
250249
/*--- Yes ---*/
251250

252251
if (mpi_rank == MASTER_NODE && verb > 0) {
253-
cout << endl;
254-
cout << " +--------------------------------------------------------------------+" << endl;
255-
cout << " + PaStiX : Parallel Sparse matriX package +" << endl;
256-
cout << " +--------------------------------------------------------------------+" << endl;
252+
cout << "\n+-------------------------------------------------+";
253+
cout << "\n+ PaStiX : Parallel Sparse matriX package +" << endl;
257254
}
258255

259256
const unsigned long szBlk = matrix.nVar * matrix.nVar, nNonZero = values.size();
@@ -297,8 +294,7 @@ void CPastixWrapper<ScalarType>::Factorize(CGeometry* geometry, const CConfig* c
297294
SU2_MPI::Error("Error factorizing matrix: " + std::to_string(rc), CURRENT_FUNCTION);
298295
}
299296

300-
if (mpi_rank == MASTER_NODE && verb > 0)
301-
cout << " +--------------------------------------------------------------------+" << endl << endl;
297+
if (mpi_rank == MASTER_NODE && verb > 0) cout << "+-------------------------------------------------+\n" << endl;
302298

303299
isfactorized = true;
304300
}

SU2_CFD/include/drivers/CDiscAdjSinglezoneDriver.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ class CDiscAdjSinglezoneDriver : public CSinglezoneDriver {
5555
COutput *direct_output;
5656
CNumerics ***numerics; /*!< \brief Container vector with all the numerics. */
5757

58+
/*!
59+
* \brief Returns true if the objective function does not depend on the main variables. In which case,
60+
* the adjoint variables are 0 and the sensitivities can be computed just with the secondary recording.
61+
*/
62+
bool TrivialFunction() const {
63+
return config_container[ZONE_0]->GetnObj() == 1 && config_container[ZONE_0]->GetKind_ObjFunc() == VOLUME_FRACTION;
64+
}
65+
5866
/*!
5967
* \brief Record one iteration of a flow iteration in within multiple zones.
6068
* \param[in] kind_recording - Type of recording (full list in ENUM_RECORDING, option_structure.hpp)

SU2_CFD/src/drivers/CDiscAdjMultizoneDriver.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,9 @@ void CDiscAdjMultizoneDriver::SetRecording(RECORDING kind_recording, Kind_Tape t
719719
switch(kind_recording) {
720720
case RECORDING::CLEAR_INDICES: cout << "Clearing the computational graph." << endl; break;
721721
case RECORDING::MESH_COORDS: cout << "Storing computational graph wrt MESH COORDINATES." << endl; break;
722-
case RECORDING::SOLUTION_VARIABLES: cout << "Storing computational graph wrt CONSERVATIVE VARIABLES." << endl; break;
722+
case RECORDING::SOLUTION_VARIABLES:
723+
cout << "Storing computational graph wrt CONSERVATIVE VARIABLES.\n";
724+
cout << "Computing residuals to check the convergence of the direct problem." << endl; break;
723725
case RECORDING::TAG_INIT_SOLVER_VARIABLES: cout << "Simulating recording with tag 1 on conservative variables." << endl; AD::SetTag(1); break;
724726
case RECORDING::TAG_CHECK_SOLVER_VARIABLES: cout << "Checking first recording with tag 2 on conservative variables." << endl; AD::SetTag(2); break;
725727
case RECORDING::TAG_INIT_SOLVER_AND_MESH: cout << "Simulating recording with tag 1 on conservative variables and mesh coordinates." << endl; AD::SetTag(1); break;
@@ -850,7 +852,7 @@ void CDiscAdjMultizoneDriver::SetObjFunction(RECORDING kind_recording) {
850852
solvers[FLOW_SOL]->Momentum_Forces(geometry, config);
851853
solvers[FLOW_SOL]->Friction_Forces(geometry, config);
852854

853-
if(config->GetWeakly_Coupled_Heat()) {
855+
if (config->GetWeakly_Coupled_Heat()) {
854856
solvers[HEAT_SOL]->Heat_Fluxes(geometry, solvers, config);
855857
}
856858

@@ -865,6 +867,9 @@ void CDiscAdjMultizoneDriver::SetObjFunction(RECORDING kind_recording) {
865867
break;
866868

867869
case MAIN_SOLVER::DISC_ADJ_FEM:
870+
if (config->GetWeakly_Coupled_Heat()) {
871+
solvers[HEAT_SOL]->Heat_Fluxes(geometry, solvers, config);
872+
}
868873
solvers[FEA_SOL]->Postprocessing(geometry, config, numerics_container[iZone][INST_0][MESH_0][FEA_SOL], true);
869874
direct_output[iZone]->SetHistoryOutput(geometry, solvers, config);
870875
ObjFunc += solvers[FEA_SOL]->GetTotal_ComboObj();
@@ -883,7 +888,7 @@ void CDiscAdjMultizoneDriver::SetObjFunction(RECORDING kind_recording) {
883888
kind_recording == RECORDING::TAG_CHECK_SOLVER_VARIABLES ||
884889
kind_recording == RECORDING::TAG_INIT_SOLVER_AND_MESH ||
885890
kind_recording == RECORDING::TAG_CHECK_SOLVER_AND_MESH) {
886-
cout << " Objective function : " << ObjFunc << endl;
891+
cout << "Objective function value: " << std::setprecision(driver_config->GetOutput_Precision()) << ObjFunc << endl;
887892
}
888893
}
889894
}

SU2_CFD/src/drivers/CDiscAdjSinglezoneDriver.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,12 @@ void CDiscAdjSinglezoneDriver::Preprocess(unsigned long TimeIter) {
156156
void CDiscAdjSinglezoneDriver::Run() {
157157
SU2_ZONE_SCOPED
158158

159+
/*--- No need to solve anything, the tape for the main recording is empty. ---*/
160+
if (TrivialFunction()) {
161+
SetAllSolutions(ZONE_0, true, [](auto, auto) { return 0; });
162+
return;
163+
}
164+
159165
CQuasiNewtonInvLeastSquares<passivedouble> fixPtCorrector;
160166
if (config->GetnQuasiNewtonSamples() > 1) {
161167
fixPtCorrector.resize(config->GetnQuasiNewtonSamples(),
@@ -273,7 +279,7 @@ void CDiscAdjSinglezoneDriver::SetRecording(RECORDING kind_recording){
273279
case RECORDING::CLEAR_INDICES: cout << "Clearing the computational graph." << endl; break;
274280
case RECORDING::MESH_COORDS: cout << "Storing computational graph wrt MESH COORDINATES." << endl; break;
275281
case RECORDING::SOLUTION_VARIABLES:
276-
cout << "Direct iteration to store the primal computational graph." << endl;
282+
cout << "Direct iteration to store the primal computational graph.\n";
277283
cout << "Computing residuals to check the convergence of the direct problem." << endl; break;
278284
default: break;
279285
}
@@ -309,6 +315,12 @@ void CDiscAdjSinglezoneDriver::SetRecording(RECORDING kind_recording){
309315

310316
SetObjFunction();
311317

318+
if (rank == MASTER_NODE &&
319+
(kind_recording == RECORDING::SOLUTION_VARIABLES || (TrivialFunction() && kind_recording == RECORDING::MESH_COORDS))) {
320+
cout << "\nObjective function value: " << std::setprecision(config_container[ZONE_0]->GetOutput_Precision()) << ObjFunc;
321+
cout << "\n-------------------------------------------------------------------------\n" << endl;
322+
}
323+
312324
if (kind_recording != RECORDING::CLEAR_INDICES && config_container[ZONE_0]->GetWrt_AD_Statistics()) {
313325
AD::PrintStatistics(SU2_MPI::GetComm(), rank == MASTER_NODE);
314326
}
@@ -410,6 +422,16 @@ void CDiscAdjSinglezoneDriver::DirectRun(RECORDING kind_recording){
410422

411423
void CDiscAdjSinglezoneDriver::MainRecording(){
412424
SU2_ZONE_SCOPED
425+
426+
/*--- We know this function only depends on the secondary variables, hence skip the main recording. ---*/
427+
428+
if (TrivialFunction()) {
429+
if (rank == MASTER_NODE) {
430+
cout << "Trivial objective function, skipping the solution of the adjoint equations." << endl;
431+
}
432+
return;
433+
}
434+
413435
/*--- SetRecording stores the computational graph on one iteration of the direct problem. Calling it with
414436
* RECORDING::CLEAR_INDICES as argument ensures that all information from a previous recording is removed. ---*/
415437

SU2_CFD/src/drivers/CDriver.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2886,8 +2886,6 @@ void CDriver::PrintDirectResidual(RECORDING kind_recording) {
28862886

28872887
} // for addVals
28882888

2889-
cout << "\n-------------------------------------------------------------------------\n" << endl;
2890-
28912889
}
28922890

28932891

SU2_CFD/src/output/CElasticityOutput.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,10 @@ void CElasticityOutput::LoadHistoryData(CConfig *config, CGeometry *geometry, CS
107107
/*--- Nonlinear analysis: UTOL, RTOL and DTOL (defined in the Postprocessing function) ---*/
108108

109109
if (linear_analysis){
110-
SetHistoryOutputValue("RMS_DISP_X", log10(fea_solver->GetRes_RMS(0)));
111-
SetHistoryOutputValue("RMS_DISP_Y", log10(fea_solver->GetRes_RMS(1)));
110+
SetHistoryOutputValue("RMS_DISP_X", log10(fea_solver->GetRes_FEM(0)));
111+
SetHistoryOutputValue("RMS_DISP_Y", log10(fea_solver->GetRes_FEM(1)));
112112
if (nDim == 3){
113-
SetHistoryOutputValue("RMS_DISP_Z", log10(fea_solver->GetRes_RMS(2)));
113+
SetHistoryOutputValue("RMS_DISP_Z", log10(fea_solver->GetRes_FEM(2)));
114114
}
115115
} else if (nonlinear_analysis){
116116
SetHistoryOutputValue("RMS_UTOL", log10(fea_solver->GetRes_FEM(0)));

SU2_CFD/src/solvers/CDiscAdjFEASolver.cpp

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,16 +126,31 @@ CDiscAdjFEASolver::~CDiscAdjFEASolver() { delete nodes; }
126126
void CDiscAdjFEASolver::SetRecording(CGeometry* geometry, CConfig *config){
127127
SU2_ZONE_SCOPED
128128

129-
/*--- Reset the solution to the initial (converged) solution ---*/
129+
/*--- Under some conditions, linear elasticity problems converge in one iteration.
130+
* This means that the "clear indices" step of the discrete adjoint solver produces a
131+
* converged solution regardless of the primal solution given to the adjoint solver.
132+
* We can take advantage of this for optimization to skip the primal solver. ---*/
133+
134+
const bool linear = config->GetGeometricConditions() == STRUCT_DEFORMATION::SMALL;
135+
const bool heat = config->GetWeakly_Coupled_Heat();
136+
const bool time_domain = config->GetTime_Domain();
137+
const bool keep_solution = linear && !heat && !time_domain && !std::is_same_v<su2mixedfloat, float>;
138+
139+
/*--- Restore the solution to the initial (converged) solution or reset AD indices. ---*/
130140

131141
for (auto iPoint = 0ul; iPoint < nPoint; iPoint++) {
132-
for (auto iVar = 0u; iVar < nVar; iVar++)
133-
direct_solver->GetNodes()->SetSolution(iPoint, iVar, nodes->GetSolution_Direct(iPoint)[iVar]);
142+
if (keep_solution) {
143+
for (auto iVar = 0u; iVar < nVar; iVar++)
144+
AD::ResetInput(direct_solver->GetNodes()->GetSolution(iPoint)[iVar]);
145+
} else {
146+
for (auto iVar = 0u; iVar < nVar; iVar++)
147+
direct_solver->GetNodes()->SetSolution(iPoint, iVar, nodes->GetSolution_Direct(iPoint)[iVar]);
148+
}
134149
}
135150

136151
/*--- Reset the input for time n ---*/
137152

138-
if (config->GetTime_Domain()) {
153+
if (time_domain) {
139154
for (auto iPoint = 0ul; iPoint < nPoint; iPoint++)
140155
for (auto iVar = 0u; iVar < nVar; iVar++)
141156
AD::ResetInput(direct_solver->GetNodes()->GetSolution_time_n(iPoint)[iVar]);

SU2_CFD/src/solvers/CFEASolver.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1833,14 +1833,13 @@ void CFEASolver::Postprocessing(CGeometry *geometry, CConfig *config, CNumerics
18331833
/*--- RTOL = norm(Residual(k): ABSOLUTE, norm of the residual (T-F) ---*/
18341834
/*--- ETOL = Delta_U(k) * Residual(k): ABSOLUTE, energy norm ---*/
18351835

1836-
SU2_OMP_PARALLEL
1837-
{
1836+
SU2_OMP_PARALLEL {
1837+
18381838
su2double utol = LinSysSol.norm();
18391839
su2double rtol = LinSysRes.norm();
18401840
su2double etol = fabs(LinSysSol.dot(LinSysRes));
18411841

1842-
SU2_OMP_MASTER
1843-
{
1842+
SU2_OMP_MASTER {
18441843
Conv_Check[0] = utol;
18451844
Conv_Check[1] = rtol;
18461845
Conv_Check[2] = etol;
@@ -1872,8 +1871,14 @@ void CFEASolver::Postprocessing(CGeometry *geometry, CConfig *config, CNumerics
18721871
END_SU2_OMP_FOR
18731872

18741873
/*--- "Add" residuals from all threads to global residual variables. ---*/
1875-
ResidualReductions_FromAllThreads(geometry, config, resRMS,resMax,idxMax);
1874+
ResidualReductions_FromAllThreads(geometry, config, resRMS, resMax, idxMax);
18761875

1876+
SU2_OMP_MASTER {
1877+
Conv_Check[0] = Residual_RMS[0];
1878+
Conv_Check[1] = Residual_RMS[1];
1879+
Conv_Check[2] = nDim == 3 ? Residual_RMS[2] : 0;
1880+
}
1881+
END_SU2_OMP_MASTER
18771882
}
18781883
END_SU2_OMP_PARALLEL
18791884

TestCases/fea_topology/grad_ref_node.dat.ref

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6157,7 +6157,7 @@
61576157
-9.57383e-11
61586158
-2.60763e-10
61596159
-2.54078e-10
6160-
-1.57598e-10
6160+
-1.57599e-10
61616161
-8.68931e-12
61626162
-4.26927e-13
61636163
0

0 commit comments

Comments
 (0)