@@ -64,6 +64,9 @@ DASolver::DASolver(
64
64
primalMinResTol_ = daOptionPtr_ -> getOption < scalar > ("primalMinResTol" );
65
65
primalMinIters_ = daOptionPtr_ -> getOption < label > ("primalMinIters" );
66
66
67
+ // initialize the objStd variables.
68
+ this -> initObjStd ();
69
+
67
70
Info << "DAOpton initialized " << endl ;
68
71
}
69
72
@@ -116,7 +119,7 @@ label DASolver::loop(Time& runTime)
116
119
{
117
120
/*
118
121
Description:
119
- The loop method to increment the runtime. The reason we implent this is
122
+ The loop method to increment the runtime. The reason we implement this is
120
123
because the runTime.loop() and simple.loop() give us seg fault...
121
124
*/
122
125
@@ -135,12 +138,23 @@ label DASolver::loop(Time& runTime)
135
138
funcObj .execute ();
136
139
}
137
140
138
- // check exit condition
139
- if (DAUtility ::primalMaxInitRes_ < primalMinResTol_ && runTime .timeIndex () > primalMinIters_ )
141
+ // calculate the objective function standard deviation. It will be used in determining if the primal converges
142
+ this -> calcObjStd (runTime );
143
+
144
+ // check exit condition, we need to satisfy both the residual and objFunc std condition
145
+ if (DAUtility ::primalMaxInitRes_ < primalMinResTol_ && runTime .timeIndex () > primalMinIters_ && primalObjStd_ < primalObjStdTol_ )
140
146
{
141
147
Info << "Time = " << t << endl ;
148
+
142
149
Info << "Minimal residual " << DAUtility ::primalMaxInitRes_ << " satisfied the prescribed tolerance " << primalMinResTol_ << endl
143
150
<< endl ;
151
+
152
+ if (primalObjStdActive_ )
153
+ {
154
+ Info << "ObjFunc standard deviation " << primalObjStd_ << " satisfied the prescribed tolerance " << primalObjStdTol_ << endl
155
+ << endl ;
156
+ }
157
+
144
158
this -> printAllObjFuncs ();
145
159
runTime .writeNow ();
146
160
prevPrimalSolTime_ = t ;
@@ -160,6 +174,99 @@ label DASolver::loop(Time& runTime)
160
174
}
161
175
}
162
176
177
+ void DASolver ::initObjStd ()
178
+ {
179
+ /*
180
+ Description:
181
+ Initialize the objStd variables.
182
+ */
183
+
184
+ // check if the objective function std is used in determining the primal convergence
185
+ primalObjStdActive_ = daOptionPtr_ -> getSubDictOption < label > ("primalObjStdTol" , "active" );
186
+ if (primalObjStdActive_ )
187
+ {
188
+ // if it is active, read in the tolerance and set a large value for the initial std
189
+ primalObjStdTol_ = daOptionPtr_ -> getSubDictOption < scalar > ("primalObjStdTol" , "tol" );
190
+ primalObjStd_ = 999.0 ;
191
+
192
+ label steps = daOptionPtr_ -> getSubDictOption < label > ("primalObjStdTol" , "steps" );
193
+ primalObjSeries_ .setSize (steps , 0.0 );
194
+
195
+ word objFuncNameWanted = daOptionPtr_ -> getSubDictOption < word > ("primalObjStdTol" , "objFuncName" );
196
+
197
+ label objFuncNameFound = 0 ;
198
+ const dictionary & objFuncDict = daOptionPtr_ -> getAllOptions ().subDict ("objFunc" );
199
+ forAll (objFuncDict .toc (), idxI )
200
+ {
201
+ word objFuncName = objFuncDict .toc ()[idxI ];
202
+ if (objFuncName == objFuncNameWanted )
203
+ {
204
+ objFuncNameFound = 1 ;
205
+ }
206
+ }
207
+ if (objFuncNameFound == 0 )
208
+ {
209
+ FatalErrorIn ("initObjStd" ) << "objStd->objFuncName not found! "
210
+ << abort (FatalError );
211
+ }
212
+ }
213
+ else
214
+ {
215
+ // if it is not active, set primalObjStdTol_ > primalObjStd_, such that it will
216
+ // always pass the condition in DASolver::loop (ignore primalObjStd)
217
+ primalObjStdTol_ = 1e-5 ;
218
+ primalObjStd_ = 0.0 ;
219
+ }
220
+ }
221
+
222
+ void DASolver ::calcObjStd (Time & runTime )
223
+ {
224
+ /*
225
+ Description:
226
+ calculate the objective function's std, this will be used to stop the primal simulation and also
227
+ evaluate whether the primal converges. We will start calculating the objStd when primalObjSeries_
228
+ is filled at least once, i.e., runTime.timeIndex() >= steps
229
+ */
230
+
231
+ if (!primalObjStdActive_ || runTime .timeIndex () < 1 )
232
+ {
233
+ return ;
234
+ }
235
+
236
+ label steps = daOptionPtr_ -> getSubDictOption < label > ("primalObjStdTol" , "steps" );
237
+
238
+ word objFuncNameWanted = daOptionPtr_ -> getSubDictOption < word > ("primalObjStdTol" , "objFuncName" );
239
+
240
+ scalar objFunPartSum = 0.0 ;
241
+ forAll (daObjFuncPtrList_ , idxI )
242
+ {
243
+ DAObjFunc & daObjFunc = daObjFuncPtrList_ [idxI ];
244
+ word objFuncName = daObjFunc .getObjFuncName ();
245
+ if (objFuncName == objFuncNameWanted )
246
+ {
247
+ objFunPartSum += daObjFunc .getObjFuncValue ();
248
+ }
249
+ }
250
+ label seriesI = (runTime .timeIndex () - 1 ) % steps ;
251
+ primalObjSeries_ [seriesI ] = objFunPartSum ;
252
+
253
+ if (runTime .timeIndex () >= steps )
254
+ {
255
+ scalar mean = 0 ;
256
+ forAll (primalObjSeries_ , idxI )
257
+ {
258
+ mean += primalObjSeries_ [idxI ];
259
+ }
260
+ mean /= steps ;
261
+ primalObjStd_ = 0.0 ;
262
+ forAll (primalObjSeries_ , idxI )
263
+ {
264
+ primalObjStd_ += (primalObjSeries_ [idxI ] - mean ) * (primalObjSeries_ [idxI ] - mean );
265
+ }
266
+ primalObjStd_ = sqrt (primalObjStd_ / steps );
267
+ }
268
+ }
269
+
163
270
void DASolver ::calcUnsteadyObjFuncs ()
164
271
{
165
272
/*
@@ -248,6 +355,14 @@ void DASolver::printAllObjFuncs()
248
355
<< "-" << objFuncPart
249
356
<< "-" << daObjFunc .getObjFuncType ()
250
357
<< ": " << objFuncVal ;
358
+ if (primalObjStdActive_ )
359
+ {
360
+ word objFuncNameWanted = daOptionPtr_ -> getSubDictOption < word > ("primalObjStdTol" , "objFuncName" );
361
+ if (objFuncNameWanted == objFuncName )
362
+ {
363
+ Info << " Std " << primalObjStd_ ;
364
+ }
365
+ }
251
366
if (timeOperator == "average" || timeOperator == "sum" )
252
367
{
253
368
Info << " Unsteady " << timeOperator << " " << unsteadyObjFuncs_ [uKey ];
@@ -8481,14 +8596,26 @@ label DASolver::checkResidualTol()
8481
8596
If yes, return 0 else return 1
8482
8597
*/
8483
8598
8484
- scalar tol = daOptionPtr_ -> getOption < scalar > ("primalMinResTol ");
8599
+ // when checking the tolerance, we relax the criteria by tolMax
8600
+
8485
8601
scalar tolMax = daOptionPtr_ -> getOption < scalar > ("primalMinResTolDiff ");
8486
- if (DAUtility ::primalMaxInitRes_ / tol > tolMax )
8602
+ scalar stdTolMax = daOptionPtr_ -> getSubDictOption < scalar > ("primalObjStdTol ", "tolDiff ");
8603
+ if (DAUtility ::primalMaxInitRes_ / primalMinResTol_ > tolMax )
8487
8604
{
8488
8605
Info << "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * " << endl ;
8489
8606
Info << "Primal min residual " << DAUtility ::primalMaxInitRes_ << endl
8490
8607
<< "did not satisfy the prescribed tolerance "
8491
- << tol << endl ;
8608
+ << primalMinResTol_ << endl ;
8609
+ Info << "Primal solution failed !" << endl ;
8610
+ Info << "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * " << endl ;
8611
+ return 1 ;
8612
+ }
8613
+ else if (primalObjStd_ / primalObjStdTol_ > stdTolMax )
8614
+ {
8615
+ Info << "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * " << endl ;
8616
+ Info << "Primal objFunc standard deviation " << primalObjStd_ << endl
8617
+ << "did not satisfy the prescribed tolerance "
8618
+ << primalObjStdTol_ << endl ;
8492
8619
Info << "Primal solution failed !" << endl ;
8493
8620
Info << "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * " << endl ;
8494
8621
return 1 ;
0 commit comments