Skip to content

Commit ff09fd9

Browse files
committed
Enabled using objFunc std as convergence criterion.
1 parent 2e1b6a2 commit ff09fd9

File tree

4 files changed

+120
-7
lines changed

4 files changed

+120
-7
lines changed

Diff for: dafoam/pyDAFoam.py

+4
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ def __init__(self):
7474
## of magnitude (default) higher than this tolerance, the primal solution will return fail=True
7575
self.primalMinResTol = 1.0e-8
7676

77+
## The convergence tolerance based on the selected objective function's standard deviation
78+
## The standard deviation is calculated based on the last N (default 200) steps of the objective function series
79+
self.primalObjStdTol = {"active": False, "objFuncName": "None", "steps": 200, "tol": 1e-5, "tolDiff": 1e2}
80+
7781
## The boundary condition for primal solution. The keys should include "variable", "patch",
7882
## and "value". For turbulence variable, one can also set "useWallFunction" [bool].
7983
## Note that setting "primalBC" will overwrite any values defined in the "0" folder.

Diff for: src/adjoint/DASolver/DASolver.C

+98-6
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,22 @@ DASolver::DASolver(
6464
primalMinResTol_ = daOptionPtr_->getOption<scalar>("primalMinResTol");
6565
primalMinIters_ = daOptionPtr_->getOption<label>("primalMinIters");
6666

67+
// check if the objective function std is used in determining the primal convergence
68+
primalObjStdActive_ = daOptionPtr_->getSubDictOption<label>("primalObjStdTol", "active");
69+
if (primalObjStdActive_)
70+
{
71+
// if it is active, read in the tolerance and set a large value for the initial std
72+
primalObjStdTol_ = daOptionPtr_->getSubDictOption<scalar>("primalObjStdTol", "tol");
73+
primalObjStd_ = 1e10;
74+
}
75+
else
76+
{
77+
// if it is not active, set primalObjStdTol_ > primalObjStd_, such that it will
78+
// always pass the condition in DASolver::loop (ignore primalObjStd)
79+
primalObjStdTol_ = 1e-5;
80+
primalObjStd_ = 0.0;
81+
}
82+
6783
Info << "DAOpton initialized " << endl;
6884
}
6985

@@ -116,7 +132,7 @@ label DASolver::loop(Time& runTime)
116132
{
117133
/*
118134
Description:
119-
The loop method to increment the runtime. The reason we implent this is
135+
The loop method to increment the runtime. The reason we implement this is
120136
because the runTime.loop() and simple.loop() give us seg fault...
121137
*/
122138

@@ -135,12 +151,23 @@ label DASolver::loop(Time& runTime)
135151
funcObj.execute();
136152
}
137153

138-
// check exit condition
139-
if (DAUtility::primalMaxInitRes_ < primalMinResTol_ && runTime.timeIndex() > primalMinIters_)
154+
// calculate the objective function standard deviation. It will be used in determining if the primal converges
155+
this->calcObjStd(runTime);
156+
157+
// check exit condition, we need to satisfy both the residual and objFunc std condition
158+
if (DAUtility::primalMaxInitRes_ < primalMinResTol_ && runTime.timeIndex() > primalMinIters_ && primalObjStd_ < primalObjStdTol_)
140159
{
141160
Info << "Time = " << t << endl;
161+
142162
Info << "Minimal residual " << DAUtility::primalMaxInitRes_ << " satisfied the prescribed tolerance " << primalMinResTol_ << endl
143163
<< endl;
164+
165+
if (primalObjStdActive_)
166+
{
167+
Info << "ObjFunc standard deviation " << primalObjStd_ << " satisfied the prescribed tolerance " << primalObjStdTol_ << endl
168+
<< endl;
169+
}
170+
144171
this->printAllObjFuncs();
145172
runTime.writeNow();
146173
prevPrimalSolTime_ = t;
@@ -160,6 +187,59 @@ label DASolver::loop(Time& runTime)
160187
}
161188
}
162189

190+
void DASolver::calcObjStd(Time& runTime)
191+
{
192+
/*
193+
Description:
194+
calculate the objective function's std, this will be used to stop the primal simulation and also
195+
evaluate whether the primal converges
196+
*/
197+
198+
if (!primalObjStdActive_)
199+
{
200+
return;
201+
}
202+
203+
label steps = daOptionPtr_->getSubDictOption<label>("primalObjStdTol", "steps");
204+
205+
if (runTime.timeIndex() == 1)
206+
{
207+
primalObjSeries_.clear();
208+
}
209+
210+
word objFuncNameWanted = daOptionPtr_->getSubDictOption<word>("primalObjStdTol", "objFuncName");
211+
212+
scalar objFunPartSum = 0.0;
213+
forAll(daObjFuncPtrList_, idxI)
214+
{
215+
DAObjFunc& daObjFunc = daObjFuncPtrList_[idxI];
216+
word objFuncName = daObjFunc.getObjFuncName();
217+
if (objFuncName == objFuncNameWanted)
218+
{
219+
objFunPartSum += daObjFunc.getObjFuncValue();
220+
}
221+
}
222+
primalObjSeries_.append(objFunPartSum);
223+
224+
label seriesSize = primalObjSeries_.size();
225+
226+
if (seriesSize >= steps)
227+
{
228+
scalar mean = 0;
229+
for (label i = seriesSize - 1; i >= seriesSize - steps; i--)
230+
{
231+
mean += primalObjSeries_[i];
232+
}
233+
mean /= steps;
234+
primalObjStd_ = 0.0;
235+
for (label i = seriesSize - 1; i >= seriesSize - steps; i--)
236+
{
237+
primalObjStd_ += (primalObjSeries_[i] - mean) * (primalObjSeries_[i] - mean);
238+
}
239+
primalObjStd_ = sqrt(primalObjStd_ / steps);
240+
}
241+
}
242+
163243
void DASolver::calcUnsteadyObjFuncs()
164244
{
165245
/*
@@ -8481,14 +8561,26 @@ label DASolver::checkResidualTol()
84818561
If yes, return 0 else return 1
84828562
*/
84838563

8484-
scalar tol = daOptionPtr_->getOption<scalar>("primalMinResTol");
8564+
// when checking the tolerance, we relax the criteria by tolMax
8565+
84858566
scalar tolMax = daOptionPtr_->getOption<scalar>("primalMinResTolDiff");
8486-
if (DAUtility::primalMaxInitRes_ / tol > tolMax)
8567+
scalar stdTolMax = daOptionPtr_->getSubDictOption<scalar>("primalObjStdTol", "tolDiff");
8568+
if (DAUtility::primalMaxInitRes_ / primalMinResTol_ > tolMax)
84878569
{
84888570
Info << "********************************************" << endl;
84898571
Info << "Primal min residual " << DAUtility::primalMaxInitRes_ << endl
84908572
<< "did not satisfy the prescribed tolerance "
8491-
<< tol << endl;
8573+
<< primalMinResTol_ << endl;
8574+
Info << "Primal solution failed!" << endl;
8575+
Info << "********************************************" << endl;
8576+
return 1;
8577+
}
8578+
else if (primalObjStd_ / primalObjStdTol_ > stdTolMax)
8579+
{
8580+
Info << "********************************************" << endl;
8581+
Info << "Primal objFunc standard deviation " << primalObjStd_ << endl
8582+
<< "did not satisfy the prescribed tolerance "
8583+
<< primalObjStdTol_ << endl;
84928584
Info << "Primal solution failed!" << endl;
84938585
Info << "********************************************" << endl;
84948586
return 1;

Diff for: src/adjoint/DASolver/DASolver.H

+17-1
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,19 @@ protected:
224224
/// step-averaged surfaceScalar states
225225
PtrList<surfaceScalarField> meanSurfaceScalarStates_;
226226

227+
/// whether to use the primal's std as the convergence criterion
228+
label primalObjStdActive_;
229+
230+
/// the step series of the primal objective, used if primalObjStdTol->active is True
231+
scalarList primalObjSeries_;
232+
233+
/// the standard deviation of the primal objective, used if primalObjStdTol->active is True
234+
scalar primalObjStd_;
235+
236+
/// the tolerance of the primal objective std, used if primalObjStdTol->active is True
237+
scalar primalObjStdTol_;
238+
239+
227240
public:
228241
/// Runtime type information
229242
TypeName("DASolver");
@@ -1294,13 +1307,16 @@ public:
12941307
const word fieldType,
12951308
const double timeName);
12961309

1310+
/// get the latest time solution from the case folder.
12971311
scalar getLatestTime()
12981312
{
1299-
// get the latest time solution from the case folder.
13001313
instantList timeDirs = runTimePtr_->findTimes(runTimePtr_->path(), runTimePtr_->constant());
13011314
scalar latestTime = timeDirs.last().value();
13021315
return latestTime;
13031316
}
1317+
1318+
/// calculate the objective function's std
1319+
void calcObjStd(Time& runTime);
13041320
};
13051321

13061322
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

Diff for: tests/runTests_DARhoSimpleFoamUBend.py

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"designSurfaces": ["ubend"],
4343
"primalMinResTol": 1e-5,
4444
"primalMinResTolDiff": 1e5,
45+
"primalObjStdTol": {"active": True, "objFuncName": "PL", "steps": 500, "tol": 1e-4, "tolDiff": 1e2},
4546
"primalBC": {
4647
"U0": {"variable": "U", "patches": ["inlet"], "value": [U0, 0.0, 0.0]},
4748
"p0": {"variable": "p", "patches": ["outlet"], "value": [p0]},

0 commit comments

Comments
 (0)