Loading...
Searching...
No Matches
nullSpace.C
Go to the documentation of this file.
1/*---------------------------------------------------------------------------*\
2 ========= |
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4 \\ / O peration |
5 \\ / A nd | www.openfoam.com
6 \\/ M anipulation |
7-------------------------------------------------------------------------------
8 Copyright (C) 2023 PCOpt/NTUA
9 Copyright (C) 2023 FOSS GP
10-------------------------------------------------------------------------------
11License
12 This file is part of OpenFOAM.
13
14 OpenFOAM is free software: you can redistribute it and/or modify it
15 under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26
27\*---------------------------------------------------------------------------*/
28
29#include "nullSpace.H"
31
32// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
33
34namespace Foam
35{
38 (
42 );
44 (
48 );
49}
50
51
52// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
53
55{
56 eps_ = 1;
58
59 const label m = iTildaEps_[0].size();
60 mu_ = scalarField(m, 1);
61 dualMu_ = scalarField(m, 1);
64
65 const label nl = iTildaEps_[1].size();
66 l_ = scalarField(nl, 1);
67 dualL_ = scalarField(nl, 1);
70
71 const label nu = iTildaEps_[2].size();
81 deltaMu_ = Zero;
82 deltaDualMu_ = Zero;
85 deltaU_ = Zero;
87}
88
89
91{
92 label nConstraints =
93 globalSum
94 (
95 iTildaEps_[0].size()
96 + iTildaEps_[1].size()
97 + iTildaEps_[2].size()
98 );
99 if (nConstraints && solveDualProblem_)
100 {
101 scalar resMax(gMax(mag(computeResiduals())));
102 label iter(0);
103 do
104 {
106 << "Dual problem Newton iter " << iter << nl << endl;
107
108 // Decrease eps
109 if (resMax < 0.9*eps_)
110 {
111 eps_ *= 0.1;
112 }
113
114 // Computes Newton direction for the subproblem
115 computeNewtonDirection();
116
117 // Perform line search and return max residual of the solution
118 // satisfying the bound constraints and the residual reduction.
119 // Upates solution.
120 resMax = lineSearch();
122 << "max residual = " << resMax << ", "
123 << "eps = " << eps_ << nl << endl;
124
125 mesh_.time().printExecutionTime(Info);
126 } while
127 (
128 iter++ < maxNewtonIters_
129 && (eps_ > dualTolerance_ || resMax > 0.9*eps_)
130 );
131
132 Info<< "Solved the dual Newton problem in " << iter << " iterations "
133 << nl << endl;
134 Info<< "fluid related Lagrange mults " << mu_ << endl;
135 }
136}
137
138
140{
141 const labelList& iFlow = iTildaEps_[0];
142 const labelList& iLower = iTildaEps_[1];
143 const labelList& iUpper = iTildaEps_[2];
144 const label m = iFlow.size();
145 const label nl = iLower.size();
146 const label nu = iUpper.size();
147 tmp<scalarField> tres(tmp<scalarField>::New(2*(m + nl + nu), Zero));
148 scalarField& res = tres.ref();
149
150 label iRes = 0;
151
152 // dLdMu
153 scalarField dLdx(objectiveDerivatives_, activeDesignVars_);
154 forAll(iFlow, i)
155 {
156 dLdx +=
157 mu_[i]
158 *scalarField(constraintDerivatives_[iFlow[i]], activeDesignVars_);
159 }
160 forAll(iLower, i)
161 {
162 dLdx[iLower[i]] -= l_[i];
163 }
164 forAll(iUpper, i)
165 {
166 dLdx[iUpper[i]] += u_[i];
167 }
168
169 forAll(iFlow, i)
170 {
171 label ic = iFlow[i];
172 scalar gradPart
173 (
174 2*globalSum
175 (
176 dLdx*scalarField(constraintDerivatives_[ic], activeDesignVars_)
177 )
178 );
179 res[iRes++] = gradPart - dualMu_[i];
180 }
181
182 // mu complementarity slackness
183 forAll(iFlow, i)
184 {
185 res[iRes++] = mu_[i]*dualMu_[i] - eps_;
186 }
187
188 // dKdl
189 forAll(iLower, i)
190 {
191 res[iRes++] = - dualL_[i] - 2*dLdx[iLower[i]];
192 }
193
194 // dKdu
195 forAll(iUpper, i)
196 {
197 res[iRes++] = - dualU_[i] + 2*dLdx[iUpper[i]];
198 }
199
200 // l complementarity slackness
201 forAll(iLower, i)
202 {
203 res[iRes++] = l_[i]*dualL_[i] - eps_;
204 }
205
206 // u complementarity slackness
207 forAll(iUpper, i)
208 {
209 res[iRes++] = u_[i]*dualU_[i] - eps_;
210 }
211
212 return tres;
213}
214
215
217{
218 // Zero the updates computed in the previous optimisation cycle
219 zeroUpdates();
220
221 const scalarField res(computeResiduals());
222
223 const labelList& iFlow = iTildaEps_[0];
224 const labelList& iLower = iTildaEps_[1];
225 const labelList& iUpper = iTildaEps_[2];
226 const label m = iFlow.size();
227 const label nl = iLower.size();
228 const label nu = iUpper.size();
229
230 // Update of the flow-related primal and dual Lagrange multipliers
231 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
232 scalarField diagKsi(nl, Zero);
233 scalarField rhsKsi(nl, Zero);
234 scalarField diagEta(nu, Zero);
235 scalarField rhsEta(nu, Zero);
236 if (nl)
237 {
238 diagKsi = scalar(1)/(2 + dualL_/l_);
239 rhsKsi =
240 -(
241 SubField<scalar>(res, nl, 2*m)
242 + SubField<scalar>(res, nl, 2*m + nl + nu)/l_
243 )*diagKsi;
244 }
245 if (nu)
246 {
247 diagEta = scalar(1)/(2 + dualU_/u_);
248 rhsEta =
249 -(
250 SubField<scalar>(res, nu, 2*m + nl)
251 + SubField<scalar>(res, nu, 2*m + 2*nl + nu)/u_
252 )*diagEta;
253 }
254
255 scalarSquareMatrix lhs(m, m, Zero);
256 scalarField rhs(m, Zero);
257 for (label i = 0; i < m; ++i)
258 {
259 const label ic = iFlow[i];
260 scalarField dci(constraintDerivatives_[ic], activeDesignVars_);
261 lhs[i][i] += 2*globalSum(dci*dci) + dualMu_[i]/mu_[i];
262 rhs[i] -= res[i] + res[m + i]/mu_[i];
263
264 scalarField dciKsi(scalarField(dci, iLower));
265 scalarField dciEta(scalarField(dci, iUpper));
266 lhs[i][i] -=
267 4*globalSum(dciKsi*dciKsi*diagKsi)
268 + 4*globalSum(dciEta*dciEta*diagEta);
269 rhs[i] += 2*globalSum(dciKsi*rhsKsi) - 2*globalSum(dciEta*rhsEta);
270 for (label j = i + 1; j < m; ++j)
271 {
272 const label jc = iFlow[j];
273 scalarField dcj(constraintDerivatives_[jc], activeDesignVars_);
274 scalar ij = 2*globalSum(dci*dcj);
275 lhs[i][j] += ij;
276 lhs[j][i] += ij;
277
278 scalarField dciKsi(scalarField(dci, iLower));
279 scalarField dcjKsi(scalarField(dcj, iLower));
280 scalarField dciEta(scalarField(dci, iUpper));
281 scalarField dcjEta(scalarField(dcj, iUpper));
282 scalar ksiContr = 4*globalSum(dciKsi*dcjKsi*diagKsi);
283 scalar etaContr = 4*globalSum(dciEta*dcjEta*diagEta);
284 lhs[i][j] -= ksiContr + etaContr;
285 lhs[j][i] -= ksiContr + etaContr;
286 }
287 }
288
289 // Update flow related Lagrange multipliers
290 if (m)
291 {
292 solve(deltaMu_, lhs, rhs);
293 deltaDualMu_ = -(deltaMu_*dualMu_ + SubField<scalar>(res, m, m))/mu_;
294 }
295
296 // Compute the rest of the corrections using backwards substitution
297 deltaL_ = Zero;
298 deltaU_ = Zero;
299 forAll(iFlow, i)
300 {
301 const label ic = iFlow[i];
302 scalarField dci(constraintDerivatives_[ic], activeDesignVars_);
303 deltaL_ += scalarField(dci, iLower)*deltaMu_[i];
304 deltaU_ -= scalarField(dci, iUpper)*deltaMu_[i];
305 }
306 deltaL_ *= 2*diagKsi;
307 deltaL_ += rhsKsi;
308
309 deltaU_ *= 2*diagEta;
310 deltaU_ += rhsEta;
311
312 if (nl)
313 {
314 deltaDualL_ =
315 - (dualL_*deltaL_ + SubField<scalar>(res, nl, 2*m + nl + nu))
316 /l_;
317 }
318
319 if (nu)
320 {
322 - (dualU_*deltaU_ + SubField<scalar>(res, nu, 2*m + 2*nl + nu))
323 /u_;
324 }
325}
326
327
328Foam::scalar Foam::nullSpace::lineSearch()
329{
330 scalar step(1.);
331
332 // Perform bound checks and adjust step accordingly
333 forAll(iTildaEps_[0], i)
334 {
335 adjustStep(step, mu_[i], deltaMu_[i]);
336 adjustStep(step, dualMu_[i], deltaDualMu_[i]);
337 }
338
339 forAll(iTildaEps_[1], i)
340 {
341 adjustStep(step, l_[i], deltaL_[i]);
342 adjustStep(step, dualL_[i], deltaDualL_[i]);
343 }
344
345 forAll(iTildaEps_[2], i)
346 {
347 adjustStep(step, u_[i], deltaU_[i]);
348 adjustStep(step, dualU_[i], deltaDualU_[i]);
349 }
350
351 // Each processor might have computed a different step, if design variables
352 // are distributed. Get the global minimum
353 if (globalSum_)
354 {
355 reduce(step, minOp<scalar>());
356 }
357
358 if (debug > 1)
359 {
360 Info<< "Step before line search is " << step << endl;
361 }
362
363 // Old residual
364 scalar normResOld = sqrt(globalSum(magSqr(computeResiduals())));
365 scalar maxRes(GREAT);
366
367 for (label i = 0; i < maxLineSearchIters_ ; ++i)
368 {
369 // Update the solution with given step
370 updateSolution(step);
371
372 // Compute new residuals and their max value
373 scalarField resNew(computeResiduals());
374 scalar normResNew = sqrt(globalSum(magSqr(resNew)));
375 maxRes = gMax(mag(resNew));
376
377 if (normResNew < normResOld)
378 {
380 << "Initial residual = " << normResOld << ", "
381 << "Final residual = " << normResNew << ", "
382 << "No of LineSearch Iterations = " << i + 1
383 << endl;
384 break;
385 }
386 else
387 {
388 // Return solution to previous and reduce step
389 updateSolution(-step);
390 step *= 0.5;
391
392 // If line search could find an appropriate step, increase eps
393 if (i == maxLineSearchIters_ - 1)
394 {
395 eps_ *= 1.5;
396 Info<< "Line search could not find a step that reduced"
397 << " residuals while satisfying the constraints" << nl
398 << "Increasing eps to " << eps_ << endl;
399 }
400 }
401 }
402
403 if (debug > 1)
404 {
405 Info<< "Step after line search is " << step << nl << endl;
406 }
407
408 return maxRes;
409}
410
411
413(
414 scalar& step,
415 const scalar value,
416 const scalar update
417)
418{
419 if (0.99*value + step*update < scalar(0))
420 {
421 step = -0.99*value/update;
422 }
423}
424
425
426void Foam::nullSpace::updateSolution(const scalar step)
427{
428 mu_ += step*deltaMu_;
429 dualMu_ += step*deltaDualMu_;
430
431 l_ += step*deltaL_;
433
434 u_ += step*deltaU_;
435 dualU_ += step*deltaDualU_;
436}
437
438
440{
441 updateViolatedIndices(0, cValues_);
442
443 if (includeBoundConstraints_)
444 {
445 scalarField lowerBounds
446 (designVars_->lowerBoundsRef() - designVars_(), activeDesignVars_);
447 updateViolatedIndices(1, lowerBounds);
448
449 scalarField upperBounds
450 (designVars_() - designVars_->upperBoundsRef(), activeDesignVars_);
451 updateViolatedIndices(2, upperBounds);
453
454 statistics(iTilda_, "violated");
455 statistics(iTildaEps_, "violated-up-to-eps");
456}
457
458
460{
461 if (solveDualProblem_)
462 {
463 updateCorrectionIndices(0, mu_, dualMu_);
464 updateCorrectionIndices(1, l_, dualL_);
465 updateCorrectionIndices(2, u_, dualU_);
466
467 statistics(iHat_, "non-tangent,violated");
468 statistics(iRangeSpace_, "to-be-reduced");
469 }
470 else
474 }
475}
476
477
479(
480 const label i,
481 const scalarField& constraints
482)
483{
484 // Set violated constraints
485 labelList& subset = iTilda_[i];
486 subset.setSize(constraints.size(), -1);
487 label iViolated = Zero;
488 forAll(constraints, i)
489 {
490 if (constraints[i] >= 0)
491 {
492 subset[iViolated++] = i;
493 }
494 }
495 subset.setSize(iViolated);
496
497 // Append violated constraints up to epsConstr_
498 DynamicList<label> iTildaEps(subset);
499 forAll(constraints, i)
500 {
501 if (constraints[i] >= -epsConstr_ && constraints[i] < 0)
502 {
503 iTildaEps.push_back(i);
504 }
505 }
506 iTildaEps_[i].transfer(iTildaEps);
507}
508
509
511(
512 const label i,
513 const scalarField& LagrangeMults,
514 const scalarField& dual
515)
516{
517 // Subset with non-zero Lagrange multipliers
518 const labelList& firstAddr = iTildaEps_[i];
519 labelList& subset = iHat_[i];
520 subset.setSize(LagrangeMults.size(), -1);
521 label iViolated(Zero);
522
523 // Loop over all indices included in iTilda
524 forAll(iTilda_[i], j)
525 {
526 // The criterion of adding a contraint index to the subset
527 // is based on whether the Lagrange multiplier is larger than
528 // its dual (since their product should, theorerically be zero). This
529 // avoids comparisons with arbitrary small numbers
530 if (LagrangeMults[j] > dual[j])
531 {
532 subset[iViolated++] = firstAddr[j];
533 }
534 }
535 // Loop over the remaining indices included in iTildaEps_ and append
536 // elements with a positive Lagrange multiplier to iRangeSpace too
537 DynamicList<label> iRangeSpace(iTilda_[i]);
538 for (label j = iTilda_[i].size(); j < iTildaEps_[i].size(); ++j)
539 {
540 if (LagrangeMults[j] > dual[j])
541 {
542 subset[iViolated++] = firstAddr[j];
543 iRangeSpace.push_back(firstAddr[j]);
545 }
546 subset.setSize(iViolated);
547 iRangeSpace_[i].transfer(iRangeSpace);
548}
549
550
552{
553 // Compute the null space part of the update
554 const scalarField activeDerivs(objectiveDerivatives_, activeDesignVars_);
555 scalarField rJ(Av(activeDerivs, iHat_));
556 scalarField lamdaJ(constraintRelatedUpdate(rJ, iHat_));
557 scalarField ksiJ(activeDerivs - ATv(lamdaJ, iHat_));
558
559 // Compute the range space part of the update
560 scalarField g(activeConstraints(iRangeSpace_));
561 scalarField lamdaC(constraintRelatedUpdate(g, iRangeSpace_));
562 scalarField ksiC(ATv(lamdaC, iRangeSpace_));
563
564 if (debug)
565 {
566 scalar magKsiJ = sqrt(globalSum(sqr(ksiJ))) ;
567 scalarField ksiJUnit(ksiJ/(magKsiJ + SMALL));
568 for (const label i : iHat_[0])
569 {
570 scalarField cDerivs
571 (constraintDerivatives_[i], activeDesignVars_);
572 cDerivs /= sqrt(globalSum(sqr(cDerivs))) + SMALL;
573 Info<< "\tNull space update projected to the " << i
574 << "-th flow constraint gradient "
575 << globalSum(ksiJUnit*cDerivs)
576 << endl;
577 }
578
579 scalar sumLowConsts(Zero);
580 scalar sumUppConsts(Zero);
581 for (const label il : iHat_[1])
582 {
583 sumLowConsts += ksiJUnit[il];
584 }
585 for (const label iu : iHat_[2])
586 {
587 sumUppConsts += ksiJUnit[iu];
588 }
589 if (globalSum_)
590 {
591 reduce(sumLowConsts, sumOp<scalar>());
592 reduce(sumUppConsts, sumOp<scalar>());
593 }
594 Info<< "\tSum of projections to the lower bound constraints "
595 << sumLowConsts << endl;
596 Info<< "\tSum of projections to the upper bound constraints "
597 << sumUppConsts << endl;
598 }
599
600 scalar maxKsiJ = gMax(mag(ksiJ));
601 if (adaptiveStep_ && maxKsiJ > VSMALL)
602 {
603 if (counter_ < lastAcceleratedCycle_)
604 {
605 aJ_ = maxDVChange_()/eta_/maxKsiJ;
606 }
607 else if (strictMaxDVChange_)
608 {
609 aJ_ = min(aJ_, maxDVChange_()/eta_/maxKsiJ);
610 }
611
612 /*
613 // Can become unstable close to the optimum
614 aJ_ =
615 targetObjectiveReduction_*objectiveValue_
616 /(eta_*mag(globalSum(activeDerivs*ksiJ)));
617 */
619 << "aJ set to " << aJ_ << endl;
620 }
621
622 scalar maxKsiC = gMax(mag(ksiC));
623 if (adaptiveStep_ && maxKsiC > VSMALL)
624 {
625 aC_ =
626 min
627 (
628 targetConstraintReduction_/eta_,
629 maxDVChange_()/eta_/maxKsiC
630 );
631 }
632
633 if (debug)
634 {
635 Info<< "Mag of ksiJ and ksiC "
636 << Foam::sqrt(globalSum(ksiJ*ksiJ)) << " "
637 << Foam::sqrt(globalSum(ksiC*ksiC))
638 << endl;
639
640 Info<< "Inner product of ksiJ and ksiC " << globalSum(ksiC*ksiJ)
641 << endl;
642 Info<< "max eta*aJ*ksiJ " << gMax(mag(eta_*aJ_*ksiJ)) << endl;
643 Info<< "max eta*aC*ksiC " << gMax(mag(eta_*aC_*ksiC)) << endl;
644 }
645
646 correction_.rmap(-eta_*(aJ_*ksiJ + aC_*ksiC), activeDesignVars_);
647}
648
649
651(
652 const scalarField& v,
653 const labelListList& subsets
654)
655{
656 const labelList& iFlow = subsets[0];
657 const labelList& iLower = subsets[1];
658 const labelList& iUpper = subsets[2];
659 label m = iFlow.size();
660 label nl = iLower.size();
661 label nu = iUpper.size();
662 if (v.size() != activeDesignVars_.size())
663 {
665 << "Input vector size not equal to the active design variables"
666 << exit(FatalError);
667 }
668 auto tmvp = tmp<scalarField>::New(m + nl + nu, Zero);
669 scalarField& mvp = tmvp.ref();
670
671 // Flow-related constraints
672 forAll(iFlow, ic)
673 {
674 scalarField di(constraintDerivatives_[iFlow[ic]], activeDesignVars_);
675 mvp[ic] += globalSum(di*v);
676 }
677
678 // Lower bounds constraints
679 forAll(iLower, il)
680 {
681 mvp[m + il] = -v[iLower[il]];
682 }
683
684 // Flow-upper bounds constraints interaction
685 forAll(iUpper, iu)
686 {
687 mvp[m + nl + iu] = v[iUpper[iu]];
688 }
689
690 return tmvp;
691}
692
693
695(
696 const scalarField& v,
697 const labelListList& subsets
698)
699{
700 const labelList& iFlow = subsets[0];
701 const labelList& iLower = subsets[1];
702 const labelList& iUpper = subsets[2];
703 label m = iFlow.size();
704 label nl = iLower.size();
705 label nu = iUpper.size();
706 if (v.size() != m + nl + nu)
707 {
709 << "Input vector size not equal to the active constraints"
710 << exit(FatalError);
711 }
712 auto tmvp = tmp<scalarField>::New(activeDesignVars_.size(), Zero);
713 scalarField& mvp = tmvp.ref();
714
715 // Flow-related constraints
716 forAll(iFlow, ic)
717 {
718 scalarField di(constraintDerivatives_[iFlow[ic]], activeDesignVars_);
719 mvp += di*v[ic];
720 }
721
722 // Lower bounds constraints
723 forAll(iLower, il)
724 {
725 mvp[iLower[il]] -= v[m + il];
726 }
727
728 // Upper bounds constraints
729 forAll(iUpper, iu)
730 {
731 mvp[iUpper[iu]] += v[m + nl + iu];
732 }
733
734 return tmvp;
735}
736
737
739(
740 const labelListList& subsets
741)
742{
743 const labelList& iFlow = subsets[0];
744 const labelList& iLower = subsets[1];
745 const labelList& iUpper = subsets[2];
746 label m = iFlow.size();
747 label nl = iLower.size();
748 label nu = iUpper.size();
749 auto tcons = tmp<scalarField>::New(m + nl + nu, Zero);
750 scalarField& cons = tcons.ref();
751
752 label ic = 0;
753 for (const label i : iFlow)
754 {
755 cons[ic++] = cValues_[i];
756 }
757
758 const autoPtr<scalarField>& lowerBounds = designVars_->lowerBounds();
759 for (const label i : iLower)
760 {
761 const label iActive = activeDesignVars_[i];
762 cons[ic++] = bcMult_*(lowerBounds()[iActive] - designVars_()[iActive]);
763 }
764
765 const autoPtr<scalarField>& upperBounds = designVars_->upperBounds();
766 for (const label i : iUpper)
767 {
768 const label iActive = activeDesignVars_[i];
769 cons[ic++] = bcMult_*(designVars_()[iActive] - upperBounds()[iActive]);
770 }
771
772 return tcons;
773}
774
775
777(
778 const scalarField& rhs,
779 const labelListList& subsets
780)
781{
782 const labelList& iFlow = subsets[0];
783 const labelList& iLower = subsets[1];
784 const labelList& iUpper = subsets[2];
785 label m = iFlow.size();
786 label nl = iLower.size();
787 label nu = iUpper.size();
788 if (rhs.size() != m + nl + nu)
789 {
791 << "rhs should have the dimensions of the active constraints"
792 << exit(FatalError);
793 }
794 auto tres = tmp<scalarField>::New(rhs.size(), Zero);
795 scalarField& res = tres.ref();
796
797 scalarSquareMatrix lhs(m, m, Zero);
798 scalarField r(m, Zero);
799
800 forAll(iFlow, i)
801 {
802 const label ic = iFlow[i];
803 scalarField dci(constraintDerivatives_[ic], activeDesignVars_);
804
805 // Diagonal part of lhs
806 lhs[i][i] += globalSum(dci*dci) ;
807 scalar lowerContr(Zero);
808 scalar upperContr(Zero);
809 for (const label il : iLower)
810 {
811 lowerContr -= dci[il]*dci[il];
812 }
813 for (const label iu : iUpper)
814 {
815 upperContr -= dci[iu]*dci[iu];
816 }
817 if (globalSum_)
818 {
819 reduce(lowerContr, sumOp<scalar>());
820 reduce(upperContr, sumOp<scalar>());
821 }
822 lhs[i][i] += lowerContr + upperContr;
823
824 // Off diagonal part of lhs
825 for (label j = i + 1; j < m; ++j)
826 {
827 const label jc = iFlow[j];
828 scalarField dcj(constraintDerivatives_[jc], activeDesignVars_);
829 scalar ij = globalSum(dci*dcj);
830 lhs[i][j] += ij;
831 lhs[j][i] += ij;
832
833 lowerContr = Zero;
834 for (const label il : iLower)
835 {
836 lowerContr -= dci[il]*dcj[il];
837 }
838 upperContr = Zero;
839 for (const label iu : iUpper)
840 {
841 upperContr -= dci[iu]*dcj[iu];
842 }
843
844 if (globalSum_)
845 {
846 reduce(lowerContr, sumOp<scalar>());
847 reduce(upperContr, sumOp<scalar>());
848 }
849 lhs[i][j] += lowerContr + upperContr;
850 lhs[j][i] += lowerContr + upperContr;
851 }
852
853 // rhs
854 r[i] = rhs[i];
855 lowerContr = Zero;
856 forAll(iLower, il)
857 {
858 lowerContr += dci[iLower[il]]*rhs[m + il];
859 }
860 upperContr = Zero;
861 forAll(iUpper, iu)
862 {
863 upperContr -= dci[iUpper[iu]]*rhs[m + nl + iu];
864 }
865
866 if (globalSum_)
867 {
868 reduce(lowerContr, sumOp<scalar>());
869 reduce(upperContr, sumOp<scalar>());
870 }
871 r[i] += lowerContr + upperContr;
872 }
873
874 // Compute solution
875 SubField<scalar>(res, nl, m) = SubField<scalar>(rhs, nl, m);
876 SubField<scalar>(res, nu, nl + m) = SubField<scalar>(rhs, nu, nl + m);
877 if (m)
878 {
879 scalarField solF(m, Zero);
880 solve(solF, lhs, r);
881 SubField<scalar>(res, m, 0) = solF;
882
883 forAll(iFlow, i)
884 {
885 scalarField dci(constraintDerivatives_[iFlow[i]], activeDesignVars_);
886 forAll(iLower, il)
887 {
888 res[m + il] += dci[iLower[il]]*solF[i];
889 }
890 forAll(iUpper, iu)
891 {
892 res[m + nl + iu] -= dci[iUpper[iu]]*solF[i];
894 }
895 }
896 return tres;
897}
898
899
901(
902 const labelListList& subset,
903 const word& description
904)
905{
907 << "Number of flow constraints (" << description << ") "
908 << subset[0].size() << nl;
909 if (includeBoundConstraints_)
910 {
912 << "Number of lower bounds constraints (" << description << ") "
913 << globalSum(subset[1].size())
914 << nl;
916 << "Number of upper bounds constraints (" << description << ") "
917 << globalSum(subset[2].size())
918 << nl;
920 DebugInfo<< endl;
921}
922
923
924// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
925
926Foam::nullSpace::nullSpace
927(
928 const fvMesh& mesh,
929 const dictionary& dict,
930 autoPtr<designVariables>& designVars,
931 const label nConstraints,
932 const word& type
933)
934:
935 constrainedOptimisationMethod(mesh, dict, designVars, nConstraints, type),
936 updateMethod(mesh, dict, designVars, nConstraints, type),
937 includeBoundConstraints_
938 (
939 designVars->upperBounds() && designVars->lowerBounds()
940 ),
941 mu_(nConstraints_, Zero),
942 l_(),
943 u_(),
944 solveDualProblem_
945 (coeffsDict(type).getOrDefault<bool>("solveDualProblem", true)),
946 dualMu_(nConstraints_, Zero),
947 dualL_(),
948 dualU_(),
949 iTilda_(3),
950 iTildaEps_(3),
951 iHat_(3),
952 iRangeSpace_(3),
953 deltaMu_(nConstraints_, Zero),
954 deltaL_(),
955 deltaU_(),
956 deltaDualMu_(nConstraints_, Zero),
957 deltaDualL_(),
958 deltaDualU_(),
959 eps_(1),
960 maxNewtonIters_(coeffsDict(type).getOrDefault<label>("maxIters", 6000)),
961 maxLineSearchIters_
962 (
963 coeffsDict(type).getOrDefault<label>("maxLineSearchIters", 10)
964 ),
965 maxCGIters_
966 (coeffsDict(type).getOrDefault<label>("maxCGIters", maxNewtonIters_)),
967 dualTolerance_
968 (coeffsDict(type).getOrDefault<scalar>("dualTolerance", 1.e-12)),
969 epsConstr_
970 (
971 coeffsDict(type).
972 getOrDefault<scalar>("violatedConstraintsThreshold", 1.e-3)
973 ),
974 epsPerturb_(coeffsDict(type). getOrDefault<scalar>("perturbation", 1.e-2)),
975 aJ_(coeffsDict(type).getOrDefault<scalar>("aJ", 1)),
976 aC_(coeffsDict(type).getOrDefault<scalar>("aC", 1)),
977 adaptiveStep_(coeffsDict(type).getOrDefault<bool>("adaptiveStep", true)),
978 lastAcceleratedCycle_
979 (coeffsDict(type).getOrDefault<label>("lastAcceleratedCycle", 20)),
980 maxDVChange_(nullptr),
981 strictMaxDVChange_
982 (coeffsDict(type).getOrDefault<bool>("strictMaxDVChange", false)),
983 targetConstraintReduction_
984 (
985 coeffsDict(type).
986 getOrDefault<scalar>("targetConstraintReduction", 0.5)
987 ),
988 bcMult_(coeffsDict(type).getOrDefault<scalar>("boundConstraintsMult", 0.5))
989{
990 if (coeffsDict(type).found("maxDVChange"))
991 {
993 (new scalar(coeffsDict(type).get<scalar>("maxDVChange")));
994 }
995 else if (designVars_().isMaxInitChangeSet())
996 {
997 maxDVChange_.reset(new scalar(designVars_().getMaxInitChange()()));
998 }
999 else if (adaptiveStep_)
1000 {
1002 << "Either maxInitChange or maxDVChange has to be setup when using "
1003 << "an adaptive step"
1004 << exit(FatalError);
1005 }
1006
1007 // Sanity checks for the bounds of the design variables.
1008 // If all design variables start from either their lower or upper bound
1009 // and there is at least one active flow-related constraint, the systems
1010 // providing the update of the desgin varaibles become singular.
1011 // Issue a warning and change the initial values of the design variables
1012 // by a small ammount, to kick start the optimisation
1013 if (designVars_->lowerBounds() && designVars_->upperBounds())
1014 {
1015 const scalarField& lowerBounds = designVars_->lowerBoundsRef();
1016 const scalarField& upperBounds = designVars_->upperBoundsRef();
1017 scalarField& designVars = designVars_();
1018 boolList isOnLowerBound(designVars.size(), false);
1019 boolList isOnUpperBound(designVars.size(), false);
1020 bool existsNonBoundVar = false;
1021 for (const label activeVarI : designVars_->activeDesignVariables())
1022 {
1023 isOnLowerBound[activeVarI] =
1024 mag(designVars[activeVarI] - lowerBounds[activeVarI]) < SMALL;
1025 isOnUpperBound[activeVarI] =
1026 mag(designVars[activeVarI] - upperBounds[activeVarI]) < SMALL;
1027 existsNonBoundVar =
1028 existsNonBoundVar
1029 || (!isOnLowerBound[activeVarI] && !isOnUpperBound[activeVarI]);
1030 }
1031 if (globalSum_)
1032 {
1033 UPstream::reduceOr(existsNonBoundVar);
1034 }
1035 if (!existsNonBoundVar)
1036 {
1038 << "All design variables lay on their bound values." << nl
1039 << tab << "This will lead to singular matrices in nullSpace"
1040 << nl
1041 << tab << "Perturbing the design variables by "
1042 << epsPerturb_ << "*(upperBound - lowerBounds)"
1043 << nl
1044 << endl;
1045 scalarField update(designVars.size(), Zero);
1046 for (const label activeVarI : designVars_->activeDesignVariables())
1047 {
1048 if (isOnLowerBound[activeVarI])
1049 {
1050 update[activeVarI] =
1052 *(upperBounds[activeVarI] - lowerBounds[activeVarI]);
1053 }
1054 if (isOnUpperBound[activeVarI])
1055 {
1056 update[activeVarI] =
1057 - epsPerturb_
1058 *(upperBounds[activeVarI] - lowerBounds[activeVarI]);
1059 }
1060 }
1061 designVars_->update(update);
1062 }
1063 }
1064
1065 // Read-in aJ in restarts
1067}
1068
1069
1070// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
1071
1073{
1074 // Update sizes of fields related to the active or saturated
1075 // constraints and initialise values
1076 initialise();
1077
1078 // Solve the dual problem using a Newton optimiser
1079 solveDualProblem();
1080
1081 // Update subsets related to the null space and range space updates
1082 updateNullAndRangeSpaceSubsets();
1083
1084 // Compute the update of the design variables
1086
1087 // Increase counter
1088 ++counter_;
1089}
1090
1091
1093{
1094 scalar LagrangePart = objectiveValue_;
1095 const autoPtr<scalarField>& lBounds = designVars_->lowerBounds();
1096 const autoPtr<scalarField>& uBounds = designVars_->upperBounds();
1097 forAll(iTildaEps_[0], i)
1098 {
1099 LagrangePart += mu_[i]*cValues_[iTildaEps_[0][i]];
1100 }
1101 scalar lowerPart = Zero;
1102 forAll(iTildaEps_[1], i)
1103 {
1104 const label iActive = activeDesignVars_[iTildaEps_[1][i]];
1105 lowerPart += l_[i]*(lBounds()[iActive] - designVars_()[iActive]);
1106 }
1107 scalar upperPart = Zero;
1108 forAll(iTildaEps_[2], i)
1109 {
1110 const label iActive = activeDesignVars_[iTildaEps_[2][i]];
1111 upperPart += u_[i]*(designVars_()[iActive] - uBounds()[iActive]);
1112 }
1113 if (globalSum_)
1114 {
1115 reduce(lowerPart, sumOp<scalar>());
1116 reduce(upperPart, sumOp<scalar>());
1117 }
1118 LagrangePart += lowerPart + upperPart;
1119 LagrangePart *= aJ_;
1120
1121 scalarField g(activeConstraints(iTildaEps_));
1122 scalarField invAAT_g(constraintRelatedUpdate(g, iTildaEps_));
1123
1124 scalar constraintPart = Zero;
1125 const label gSize = g.size();
1126 const label flowSize = iTildaEps_[0].size();
1127 forAll(iTildaEps_[0], i)
1128 {
1129 constraintPart += g[i]*invAAT_g[i];
1130 }
1131
1132 scalarField a = SubField<scalar>(g, gSize - flowSize, flowSize);
1133 scalarField b = SubField<scalar>(invAAT_g, gSize - flowSize, flowSize);
1134 constraintPart += globalSum(a*b);
1135 constraintPart *= 0.5*aC_;
1136
1137 return LagrangePart + constraintPart;
1138}
1139
1140
1142{
1143 scalarField LagrangeMults(mu_.size() + l_.size() + u_.size(), Zero);
1144 SubField<scalar>(LagrangeMults, mu_.size(), 0) = mu_;
1145 SubField<scalar>(LagrangeMults, l_.size(), mu_.size()) = l_;
1146 SubField<scalar>(LagrangeMults, u_.size(), mu_.size() + l_.size()) = u_;
1147 scalarField deriv(objectiveDerivatives_, activeDesignVars_);
1148 deriv += ATv(LagrangeMults, iTildaEps_);
1149 deriv *= aJ_;
1150
1151 scalarField g(activeConstraints(iTildaEps_));
1152 scalarField lamdaC(constraintRelatedUpdate(g, iTildaEps_));
1153 scalarField ksiC(ATv(lamdaC, iTildaEps_));
1154
1155 deriv += aC_*ksiC;
1156
1158 derivAll.rmap(deriv, activeDesignVars_);
1159
1160 return globalSum(sqr(derivAll*correction_));
1161}
1162
1163
1165{
1166 os.writeEntry("aJ", aJ_);
1168}
1169
1170
1171// ************************************************************************* //
if(maxValue - minValue< SMALL)
bool found
Macros for easy insertion into run-time selection tables.
#define addToRunTimeSelectionTable(baseType, thisType, argNames)
Add to construction table with typeName as the key.
if(patchID !=-1)
const uniformDimensionedVectorField & g
label size() const noexcept
The number of elements in list.
Definition DLListBase.H:194
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition DynamicList.H:68
void push_back(const T &val)
Copy append an element to the end of this list.
void rmap(const UList< Type > &mapF, const labelUList &mapAddressing)
1 to 1 reverse-map from the given field
Definition Field.C:528
void setSize(label n)
Alias for resize().
Definition List.H:536
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition Ostream.H:59
SubField is a Field obtained as a section of another Field, without its own allocation....
Definition SubField.H:58
void size(const label n)
Older name for setAddressableSize.
Definition UList.H:118
static void reduceOr(bool &value, const int communicator=worldComm)
Logical (or) reduction (MPI_AllReduce).
Pointer management similar to std::unique_ptr, with some additional methods and type checking.
Definition autoPtr.H:65
void reset(T *p=nullptr) noexcept
Delete managed object and set to new given pointer.
Definition autoPtrI.H:37
Abstract base class for optimisation methods supporting constraints. Does not add functionality to up...
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition dictionary.H:133
T get(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Find and return a T. FatalIOError if not found, or if the number of tokens is incorrect.
T getOrDefault(const word &keyword, const T &deflt, enum keyType::option matchOpt=keyType::REGEX) const
Find and return a T, or return the given default value. FatalIOError if it is found and the number of...
bool readIfPresent(const word &keyword, T &val, enum keyType::option matchOpt=keyType::REGEX) const
Find an entry if present, and assign to T val. FatalIOError if it is found and the number of tokens i...
Mesh data needed to do the Finite Volume discretisation.
Definition fvMesh.H:85
Abstract base class for line search methods.
Definition lineSearch.H:54
Update design variables using a null space approach. Can handle inequality and bound constraints.
Definition nullSpace.H:63
scalar lineSearch()
Perform line search and return max residual corresponding to the updated solution.
Definition nullSpace.C:321
scalar targetConstraintReduction_
Target reduction of active constraints (range in [0-1]).
Definition nullSpace.H:246
void updateCorrectionIndices(const label i, const scalarField &LagrangeMults, const scalarField &dual)
Update constraint indices related to the null and range space part of the correction.
Definition nullSpace.C:504
void updateSolution(const scalar step)
Update the current solution using the known direction and the given step length.
Definition nullSpace.C:419
virtual void computeCorrection()
Compute design variables correction.
Definition nullSpace.C:1065
virtual scalar meritFunctionDirectionalDerivative()
Directional derivative of the merit function, in the direction of the correction.
Definition nullSpace.C:1134
void initialise()
Update sizes of fields related to the constraints.
Definition nullSpace.C:47
autoPtr< scalar > maxDVChange_
Max change of the design variables, pertaining to the objective reduction.
Definition nullSpace.H:232
bool adaptiveStep_
Change aJ and aC adaptively.
Definition nullSpace.H:217
scalar dualTolerance_
Tolerance of the dual problem.
Definition nullSpace.H:185
void updateNullAndRangeSpaceSubsets()
Update the constraint subsets.
Definition nullSpace.C:452
scalarField mu_
Lagrange multipliers for flow-reated constraints.
Definition nullSpace.H:76
void correction()
Compute the update of the design variables as a combination of null space and range space parts.
Definition nullSpace.C:544
tmp< scalarField > activeConstraints(const labelListList &subsets)
Collect all constraint values corresponding to the provided indices.
Definition nullSpace.C:732
void adjustStep(scalar &step, const scalar value, const scalar update)
Adjust step to satisfy cireteria.
Definition nullSpace.C:406
scalarField dualL_
Lagrange multipliers of the dual problem for the lower bound constraints.
Definition nullSpace.H:106
scalar bcMult_
Downplay the importance of the bound constraint reduction by this ammount.
Definition nullSpace.H:252
virtual bool writeData(Ostream &os) const
Write useful quantities to files.
Definition nullSpace.C:1157
scalar epsConstr_
Value for considering a constraint as marginally active.
Definition nullSpace.H:196
tmp< scalarField > ATv(const scalarField &v, const labelListList &subsets)
A.T & v.
Definition nullSpace.C:688
bool strictMaxDVChange_
Whether to impose maxDVChange strictly on all optimisation cycles or just up to the lastAcceleratedCy...
Definition nullSpace.H:238
labelListList iHat_
List of constraints that must remain active.
Definition nullSpace.H:137
void computeNewtonDirection()
Compute direction for the Newton problem.
Definition nullSpace.C:209
scalarField dualU_
Lagrange multipliers of the dual problem for the upper bound constraints.
Definition nullSpace.H:112
label lastAcceleratedCycle_
Last cycle on which to reset aJ.
Definition nullSpace.H:222
scalarField deltaMu_
Definition nullSpace.H:150
void solveDualProblem()
Solve the dual problem for computing the Lagrange multiplier using a Newton solver.
Definition nullSpace.C:83
label maxCGIters_
Maxmimum number of CG iterations for obtaining the null space and range space updates.
Definition nullSpace.H:180
labelListList iRangeSpace_
List of constraints the values of which need to be reduced.
Definition nullSpace.H:144
scalar aC_
Multiplier of the range space update.
Definition nullSpace.H:212
void statistics(const labelListList &subset, const word &description)
Print statistics on the number of flow- and bound-related constraints included in the subset.
Definition nullSpace.C:894
void zeroUpdates()
Zero the updates computed in the previous optimisation cycle.
Definition nullSpace.C:72
virtual scalar computeMeritFunction()
Compute the merit function for line search.
Definition nullSpace.C:1085
scalar eps_
Infinitesimal quantity.
Definition nullSpace.H:163
labelListList iTildaEps_
List of saturated or violated constraints (up to epsConstr_).
Definition nullSpace.H:130
scalarField dualMu_
Lagrange multipliers of the dual problem for flow-related constraints.
Definition nullSpace.H:100
tmp< scalarField > constraintRelatedUpdate(const scalarField &rhs, const labelListList &subsets)
Computes the parts of ksiJ and ksiC that require a system solution.
Definition nullSpace.C:770
label maxLineSearchIters_
Maxmimum number of line search iterations for each Newton iteration of the dual problem.
Definition nullSpace.H:174
void updateViolatedIndices(const label i, const scalarField &constraints)
Update violated constraints indices (iTilda and iTildaEps).
Definition nullSpace.C:472
scalarField l_
Lagrange multipliers for the lower bound constraints.
Definition nullSpace.H:81
scalarField deltaU_
Definition nullSpace.H:152
bool includeBoundConstraints_
Are bound constraints included?
Definition nullSpace.H:71
void updateViolatedConstraintsSubsets()
Update the constraint subsets.
Definition nullSpace.C:432
labelListList iTilda_
List of saturated or violated constraints.
Definition nullSpace.H:125
scalarField deltaDualU_
Definition nullSpace.H:155
scalarField deltaDualMu_
Definition nullSpace.H:153
scalar epsPerturb_
Value to perturb the design variables by, if all of them lay on their bounds at the beginning of the ...
Definition nullSpace.H:202
scalar aJ_
Multiplier of the null space update.
Definition nullSpace.H:207
scalarField u_
Lagrange multipliers for the upper bound constraints.
Definition nullSpace.H:86
tmp< scalarField > computeResiduals()
Compute and return residuals based on the current solution.
Definition nullSpace.C:132
scalarField deltaDualL_
Definition nullSpace.H:154
bool solveDualProblem_
Solve the dual problem?
Definition nullSpace.H:94
scalarField deltaL_
Definition nullSpace.H:151
label maxNewtonIters_
Maxmimum number of Newton iterations for the dual problem.
Definition nullSpace.H:168
tmp< scalarField > Av(const scalarField &v, const labelListList &subsets)
A & v.
Definition nullSpace.C:644
A class for managing temporary objects.
Definition tmp.H:75
static tmp< T > New(Args &&... args)
Construct tmp with forwarding arguments.
Definition tmp.H:215
T & ref() const
Return non-const reference to the contents of a non-null managed pointer.
Definition tmpI.H:235
Abstract base class for optimisation methods.
scalarField objectiveDerivatives_
Derivatives of the objective functions.
label nConstraints_
Number of constraints.
scalarField correction_
Design variables correction.
bool globalSum_
Whether to use gSum or sum in the inner products.
const fvMesh & mesh_
dictionary coeffsDict(const word &type) const
Return optional dictionary with parameters specific to each method.
scalar globalSum(const scalarField &field)
Compute either global or local sum, based on globalSum flag.
scalarField cValues_
Constraint values.
scalar objectiveValue_
Objective value.
PtrList< scalarField > constraintDerivatives_
Derivatives of the constraints.
virtual bool writeData(Ostream &os) const
Write continuation data under uniform.
const labelList & activeDesignVars_
Map to active design variables.
label counter_
Optimisation cycle count.
label nConstraints() const
Get the number of constraints.
scalar eta_
Step multiplying the correction.
autoPtr< designVariables > & designVars_
Design variables.
A class for handling words, derived from Foam::string.
Definition word.H:66
#define defineTypeNameAndDebug(Type, DebugSwitch)
Define the typeName and debug information.
Definition className.H:142
mesh update()
dynamicFvMesh & mesh
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition error.H:600
OBJstream os(runTime.globalPath()/outputName)
#define DebugInfo
Report an information message using Foam::Info.
#define WarningInFunction
Report a warning using Foam::Warning.
Namespace for handling debugging switches.
Definition debug.C:45
Namespace for OpenFOAM.
List< labelList > labelListList
List of labelList.
Definition labelList.H:38
List< label > labelList
A List of labels.
Definition List.H:62
dimensionedSymmTensor sqr(const dimensionedVector &dv)
messageStream Info
Information stream (stdout output on master, null elsewhere).
List< T > subset(const BoolListType &select, const UList< T > &input, const bool invert=false)
Extract elements of the input list when select is true.
fileName::Type type(const fileName &name, const bool followLink=true)
Return the file type: DIRECTORY or FILE, normally following symbolic links.
Definition POSIX.C:801
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition Ostream.H:519
dimensionedScalar sqrt(const dimensionedScalar &ds)
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
void reduce(T &value, BinaryOp bop, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Reduce inplace (cf. MPI Allreduce).
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition hashSets.C:26
SquareMatrix< scalar > scalarSquareMatrix
void rhs(fvMatrix< typename Expr::value_type > &m, const Expr &expression)
static constexpr const zero Zero
Global zero (0).
Definition zero.H:127
tmp< fvMatrix< Type > > correction(const fvMatrix< Type > &)
Return the correction form of the given matrix by subtracting the matrix multiplied by the current fi...
error FatalError
Error stream (stdout output on all processes), with additional 'FOAM FATAL ERROR' header text and sta...
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition errorManip.H:125
Type gMax(const FieldField< Field, Type > &f)
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
constexpr char nl
The newline '\n' character (0x0a).
Definition Ostream.H:50
constexpr char tab
The tab '\t' character(0x09).
Definition Ostream.H:49
volScalarField & nu
dictionary dict
CEqn solve()
volScalarField & b
#define forAll(list, i)
Loop across all elements in list.
Definition stdFoam.H:299