MMA.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) 2020-2023 PCOpt/NTUA
9  Copyright (C) 2020-2023 FOSS GP
10 -------------------------------------------------------------------------------
11 License
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 "MMA.H"
31 #include "scalarField.H"
32 
33 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
34 
35 namespace Foam
36 {
37  defineTypeNameAndDebug(MMA, 2);
39  (
40  updateMethod,
41  MMA,
42  dictionary
43  );
45  (
46  constrainedOptimisationMethod,
47  MMA,
49  );
50 }
51 
52 
53 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
54 
56 {
57  const label n = activeDesignVars_.size();
58  if (n != xNew_.size())
59  {
60  x0_.setSize(n, Zero);
61  x00_.setSize(n, Zero);
62  xNew_.setSize(n, Zero);
63  lower_.setSize(n, Zero);
64  upper_.setSize(n, Zero);
65  a_.setSize(n, Zero);
66  b_.setSize(n, Zero);
67  ksi_.setSize(n, Zero);
68  Eta_.setSize(n, Zero);
72  }
73 }
74 
75 
77 {
78  rho_.setSize(cValues_.size() + 1, raa0_);
79 
80  // Store old objective and constraint values for GCMMA
81  oldObjectiveValue_ = objectiveValue_;
82  oldCValues_ = cValues_;
83 
84  if (variableRho_)
85  {
86  const scalarField x(designVars_().getVars(), activeDesignVars_);
87  const scalarField xMin
88  (designVars_().lowerBounds()(), activeDesignVars_);
89  const scalarField xMax
90  (designVars_().upperBounds()(), activeDesignVars_);
91  const scalarField span(xMax - xMin);
92  const scalarField activeObjDerivs
93  (objectiveDerivatives_, activeDesignVars_);
94 
95  // Objective r
96  rho_[0] =
97  maxInitRhoMult_
98  /globalSum(x.size())*globalSum(mag(activeObjDerivs)*span);
99 
100  // Constraints r
101  forAll(constraintDerivatives_, i)
102  {
103  const scalarField activeDerivs
104  (
105  constraintDerivatives_[i], activeDesignVars_
106  );
107  rho_[i + 1] =
108  maxInitRhoMult_
109  /globalSum(x.size())*globalSum(mag(activeDerivs)*span);
110 
111  }
112 
113  // Find a rho value that produces an approximate objective/constraint
114  // combination, evaluated at the new point, that is lower than the
115  // current one
116  if (dynamicRhoInitialisation_)
117  {
118  Info<< "-----------------------------------------" << endl;
119  Info<< "Solving sub problems for initializing rho" << endl;
120  Info<< "-----------------------------------------" << endl;
121  // Backup bounds and initialisation, to be restored after the rho
122  // initialisation phase
123  scalarField lowerBck = lower_;
124  scalarField upperBck = upper_;
125  scalarField aBck = a_;
126  scalarField bBck = b_;
127  scalarField x0Bck = x0_;
128  scalarField x00Bck = x00_;
129 
130  // First, do a peudo-update of the the design variables
131  designVars_().storeDesignVariables();
132  x0_.map(designVars_().getVars(), activeDesignVars_);
133 
134  Info<< "Initial pseudo update of the design variables" << endl;
135  updateBounds();
136  initialize();
137  solveSubproblem();
138 
139  designVars_().update(correction_);
140 
141  // Check if the approximate function is higher than the CURRENT
142  // function and, if not, update the rho value until this happens.
143  // Used as an inexpensive way to obtain a decent rho initialisation
144  label iter(0);
145  while (!converged() && iter++ < 10)
146  {
147  Info<< nl << "Dynamic rho initialisation iteration " << iter
148  << nl << endl;
149  designVars_().resetDesignVariables();
150  updateRho();
151  solveSubproblem();
152  designVars_().update(correction_);
153  }
154 
155  Info<< "-----------------------------------------" << endl;
156  Info<< "Dynamic rho initialisation converged in " << iter
157  << " iterations " << endl;
158  Info<< "-----------------------------------------" << endl;
159 
160  // Restore bounds and design variables
161  lower_ = lowerBck;
162  upper_ = upperBck;
163  a_ = aBck;
164  b_ = bBck;
165  designVars_().resetDesignVariables();
166  x0_ = x0Bck;
167  x00_ = x00Bck;
168  }
169 
170  rho_ *= dynamicRhoMult_;
171 
172  // Bound too small values
173  if (boundRho_)
174  {
175  rho_ = max(rho_, scalar(1.e-6));
176  }
177  DebugInfo
178  << "Computed r values " << rho_ << endl;
179  }
180 }
181 
182 
184 {
185  const scalarField x(designVars_().getVars(), activeDesignVars_);
186  const scalarField xMin(designVars_().lowerBoundsRef(), activeDesignVars_);
187  const scalarField xMax(designVars_().upperBoundsRef(), activeDesignVars_);
188  const scalarField span(xMax - xMin);
189 
190  if (counter_ > 1)
191  {
192  forAll(x, i)
193  {
194  scalar gamma
195  (
196  (x[i] - x0_[i])*(x0_[i] - x00_[i]) < scalar(0)
197  ? asymDecr_ : asymIncr_
198  );
199  lower_[i] = x[i] - gamma*(x0_[i] - lower_[i]);
200  upper_[i] = x[i] + gamma*(upper_[i] - x0_[i]);
201  }
202 
203  lower_ = min(lower_, x - 0.01*(xMax - xMin));
204  lower_ = max(lower_, x - 10.0*(xMax - xMin));
205 
206  upper_ = max(upper_, x + 0.01*(xMax - xMin));
207  upper_ = min(upper_, x + 10.0*(xMax - xMin));
208  }
209  else
210  {
211  lower_ = x - sInit_*span;
212  upper_ = x + sInit_*span;
213  }
214 
215  a_ = max(xMin, lower_ + 0.1*(x - lower_));
216  a_ = max(a_, x - move_*span);
217  b_ = min(xMax, upper_ - 0.1*(upper_ - x));
218  b_ = min(b_, x + move_*span);
219 }
220 
221 
223 {
224  const label m(cValues_.size());
225  // Allocate correct sizes for array depending on the number of constraints
226  if (c_.empty())
227  {
228  alpha_.setSize(m, Zero);
229  c_.setSize(m, coeffsDict(typeName).getOrDefault<scalar>("c", 100));
230  d_.setSize(m, coeffsDict(typeName).getOrDefault<scalar>("d", 1));
231  deltaLamda_.setSize(m, Zero);
232  deltaY_.setSize(m, Zero);
233  deltaS_.setSize(m, Zero);
234  deltaMu_.setSize(m, Zero);
235  }
236 
237  // Scalar quantities
238  eps_ = 1.;
239  z_ = 1.;
240  zeta_ = 1.;
241 
242  // Quantities related to design variables
243  xNew_ = 0.5*(a_ + b_);
244  ksi_ = max(scalar(1), 1./(xNew_ - a_));
245  Eta_ = max(scalar(1), 1./(b_ - xNew_));
246 
247  // Quantities related to constraints
248  y_.setSize(m, scalar(1));
249  lamda_.setSize(m, scalar(1));
250  s_.setSize(m, scalar(1));
251  mu_.setSize(m, Zero);
252  mu_ = max(scalar(1), 0.5*c_);
253 }
254 
255 
257 {
258  // Store old solutions
259  if (counter_ > 0)
260  {
261  x00_ = x0_;
262  }
263  x0_.map(designVars_().getVars(), activeDesignVars_);
264 }
265 
266 
268 (
269  const scalarField& derivs,
270  const scalar r,
271  const scalarField& x
272 )
273 {
274  const scalarField lowerBounds
275  (
276  designVars_().lowerBoundsRef(), activeDesignVars_
277  );
278  const scalarField upperBounds
279  (
280  designVars_().upperBoundsRef(), activeDesignVars_
281  );
282  tmp<scalarField> tres(tmp<scalarField>::New(x.size(), Zero));
283  scalarField& res = tres.ref();
284 
285  res =
286  sqr(upper_ - x)
287  *(
288  1.001*max(derivs, scalar(0))
289  + 0.001*max(-derivs, scalar(0))
290  + r/(upperBounds - lowerBounds)
291  );
292 
293  return tres;
294 }
295 
297 (
298  const scalarField& derivs,
299  const scalar r
300 )
301 {
302  return
303  p(derivs, r, scalarField(designVars_().getVars(), activeDesignVars_));
304 }
305 
306 
308 (
309  const scalarField& derivs,
310  const scalar r,
311  const scalarField& x
312 )
313 {
314  const scalarField lowerBounds
315  (
316  designVars_().lowerBoundsRef(), activeDesignVars_
317  );
318  const scalarField upperBounds
319  (
320  designVars_().upperBoundsRef(), activeDesignVars_
321  );
323  scalarField& res = tres.ref();
324 
325  res =
326  sqr(x - lower_)
327  *(
328  0.001*max(derivs, scalar(0))
329  + 1.001*max(-derivs, scalar(0))
330  + r/(upperBounds - lowerBounds)
331  );
332 
333  return tres;
334 }
335 
337 (
338  const scalarField& derivs,
339  const scalar r
340 )
341 {
342  return
343  q(derivs, r, scalarField(designVars_().getVars(), activeDesignVars_));
344 }
345 
346 
348 (
349  const scalarField& vars
350 )
351 {
352  tmp<scalarField> tres(tmp<scalarField>::New(cValues_.size(), Zero));
353  scalarField& res = tres.ref();
354  forAll(res, i)
355  {
356  const scalarField activeDerivs
357  (
358  constraintDerivatives_[i], activeDesignVars_
359  );
360  tmp<scalarField> pI(p(activeDerivs, rho_[i + 1]));
361  tmp<scalarField> qI(q(activeDerivs, rho_[i + 1]));
362  res[i] = globalSum(pI/(upper_ - vars) + qI/(vars - lower_));
363  }
364 
365  return tres;
366 }
367 
368 
370 {
371  const scalarField vars(designVars_().getVars(), activeDesignVars_);
372  tmp<scalarField> tres( - cValues_ + gConstr(vars));
373 
374  return tres;
375 }
376 
377 
379 {
380  const scalarField activeObjDerivs(objectiveDerivatives_, activeDesignVars_);
381  tmp<scalarField> tres(p(activeObjDerivs, rho_[0]));
382  scalarField& res = tres.ref();
383  forAll(constraintDerivatives_, cI)
384  {
385  const scalarField activeDerivs
386  (
387  constraintDerivatives_[cI], activeDesignVars_
388  );
389  res += lamda_[cI]*p(activeDerivs, rho_[cI + 1]);
390  }
391 
392  return tres;
393 }
394 
395 
397 {
398  const scalarField activeObjDerivs(objectiveDerivatives_, activeDesignVars_);
399  tmp<scalarField> tres(q(activeObjDerivs, rho_[0]));
400  scalarField& res = tres.ref();
401  forAll(constraintDerivatives_, cI)
402  {
403  const scalarField activeDerivs
404  (
405  constraintDerivatives_[cI], activeDesignVars_
406  );
407  res += lamda_[cI]*q(activeDerivs, rho_[cI + 1]);
408  }
409 
410  return tres;
411 }
412 
413 
415 {
416  deltaLamda_ = Zero;
417  deltaZ_ = Zero;
418  deltaX_ = Zero;
419  deltaY_ = Zero;
420  deltaS_ = Zero;
421  deltaZeta_ = Zero;
422  deltaMu_ = Zero;
423  deltaEta_ = Zero;
424  deltaKsi_ = Zero;
425 }
426 
427 
428 Foam::scalar Foam::MMA::fTilda
429 (
430  const scalarField& xInit,
431  const scalarField& x,
432  const scalar f,
433  const scalarField& dfdx,
434  const scalar rho
435 )
436 {
437  return scalar
438  (
439  f
440  + globalSum
441  (
442  p(dfdx, rho, xInit)*(1./(upper_ - x) - 1./(upper_ - xInit))
443  + q(dfdx, rho, xInit)*(1./(x - lower_) - 1./(xInit - lower_))
444  )
445  );
446 }
447 
448 
450 (
451  const word& name,
452  const label size,
453  const scalar value
454 )
455 {
456  tmp<scalarField> tfield(tmp<scalarField>::New(size, value));
457  scalarField& field = tfield.ref();
458  if (found(name))
459  {
460  field = scalarField(name, *this, field.size());
461  }
462  return tfield;
463 }
464 
465 
467 (
469  const word& name,
470  const label size,
471  const scalarField& defaultField
472 )
473 {
474  if (found(name))
475  {
476  field = scalarField(name, *this, size);
477  }
478  else
479  {
480  field = defaultField;
481  }
482 }
483 
484 
486 {
487  // Zero the updates computed in the previous optimisation cycle
488  zeroUpdates();
489 
490  // Easy access to number of design variables and constraints
491  const label n(xNew_.size());
492  const label m(cValues_.size());
493  const scalarField x(designVars_().getVars(), activeDesignVars_);
494 
495  // Fields needed to compute Psi and its derivatives
496  tmp<scalarField> tpL(pLamda());
497  const scalarField& pL = tpL();
498  tmp<scalarField> tqL(qLamda());
499  const scalarField& qL = tqL();
500 
501  // Build parts of the LHS matrices
502  scalarField DYInv(scalar(1.)/(d_ + mu_/y_));
503  scalarField DLamdaY((s_/lamda_) + DYInv);
504  scalarField DxInv
505  (
506  scalar(1.)/
507  (
508  scalar(2)*pL/pow3(upper_ - xNew_)
509  + scalar(2)*qL/pow3(xNew_ - lower_)
510  + ksi_/(xNew_ - a_)
511  + Eta_/(b_ - xNew_)
512  )
513  );
514 
515  // Compute G
517  for (label i = 0; i < m; ++i)
518  {
519  const scalarField activeDerivs
520  (
521  constraintDerivatives_[i], activeDesignVars_
522  );
523  tmp<scalarField> tp = p(activeDerivs, rho_[i + 1]);
524  tmp<scalarField> tq = q(activeDerivs, rho_[i + 1]);
525  scalarField row(tp/sqr(upper_ - xNew_) - tq/sqr(xNew_ - lower_));
526  forAll(row, j)
527  {
528  G[i][j] = row[j];
529  }
530  }
531 
532  // Compute parts of the RHS
533  scalarField deltaXTilda
534  (
535  pL/sqr(upper_ - xNew_) - qL/sqr(xNew_ - lower_)
536  - eps_/(xNew_ - a_)
537  + eps_/(b_ - xNew_)
538  );
539  scalarField deltaYTilda
540  (
541  c_ + d_*y_ - lamda_ - eps_/y_
542  );
543  scalar deltaZTilda = alpha0_ - sum(lamda_*alpha_) - eps_/z_;
544  scalarField deltaLamdaYTilda
545  (
546  // part of deltaLamdaTilda
547  gConstr(xNew_)
548  - alpha_*z_
549  - y_
550  - b()
551  + eps_/lamda_
552  // part of deltaYTilda
553  + DYInv*deltaYTilda
554  );
555 
556 
557  // Solve system for deltaLamda and deltaZ
558  // The deltaZ equation is substituted into
559  // the one of deltaLamda
560  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
561  scalarSquareMatrix lhs(m, m, Zero);
562  scalar zRatio(z_/zeta_);
563  scalarField rhs(m, Zero);
564 
565  // Deal with parts of lhs and rhs that might need global reduction first
566  for (label i = 0; i < m; ++i)
567  {
568  // Prepare LHS
569  for (label j = 0; j < m; ++j)
570  {
571  for (label l = 0; l < n; ++l)
572  {
573  // Part from GDxInvGT
574  lhs[i][j] += DxInv[l]*G[i][l]*G[j][l];
575  }
576  }
577  // Prepare RHS
578  for (label l = 0; l < n; ++l)
579  {
580  // Part from GDxInvDeltaXTilda
581  rhs[i] -= DxInv[l]*G[i][l]*deltaXTilda[l];
582  }
583  }
584  // If design variables are distributed to many processors, reduce
585  if (globalSum_)
586  {
587  reduce(lhs, sumOp<scalarSquareMatrix>());
588  Pstream::listCombineAllGather(rhs, plusEqOp<scalar>());
589  }
590 
591  // Add remaining parts from deltaLamdaYTilda and the deltaZ eqn
592  rhs += deltaLamdaYTilda + alpha_*deltaZTilda*zRatio;
593  for (label i = 0; i < m; ++i)
594  {
595  // Prepare LHS
596  for (label j = 0; j < m; ++j)
597  {
598  // Part from delta z
599  lhs[i][j] += alpha_[i]*alpha_[j]*zRatio;
600  }
601  // Part from DLamdaY
602  lhs[i][i] += DLamdaY[i];
603  }
604  solve(deltaLamda_, lhs, rhs);
605  deltaZ_ = (sum(alpha_*deltaLamda_) - deltaZTilda)*zRatio;
606 
607  // Compute the rest of the corrections using backwards substitution
608 
609  // deltaX
610  // No need for reduction since DxInv is a diagonal matrix
611  deltaX_ = - DxInv*(G.Tmul(deltaLamda_) + deltaXTilda);
612 
613  // deltaY
614  deltaY_ = DYInv*(deltaLamda_ - deltaYTilda);
615 
616  // deltaS
617  deltaS_ = (-s_*deltaLamda_ + eps_)/lamda_ - s_;
618 
619  // deltaZeta
620  deltaZeta_ = -zeta_/z_*deltaZ_ - zeta_ + eps_/z_;
621 
622  // deltaMu
623  deltaMu_ = (-mu_*deltaY_ + eps_)/y_ - mu_;
624 
625  // deltaEta
626  deltaEta_ = (Eta_*deltaX_ + eps_)/(b_ - xNew_) - Eta_;
627 
628  // deltaKsi
629  deltaKsi_ = (-ksi_*deltaX_ + eps_)/(xNew_ - a_) - ksi_;
630 }
631 
632 
633 Foam::scalar Foam::MMA::lineSearch()
634 {
635  const label n(xNew_.size());
636  const label m(cValues_.size());
637  scalar step(1.);
638 
639  // Perform bound checks and adjust step accordingly
640  for (label i = 0; i < n; ++i)
641  {
642  if
643  (
644  xNew_[i] + step*deltaX_[i] - a_[i] - 0.01*(xNew_[i] - a_[i])
645  < scalar(0)
646  )
647  {
648  step = -0.99*(xNew_[i] - a_[i])/deltaX_[i];
649  }
650 
651  if
652  (
653  -xNew_[i] - step*deltaX_[i] + b_[i] - 0.01*(b_[i] - xNew_[i])
654  < scalar(0)
655  )
656  {
657  step = 0.99*(b_[i] - xNew_[i])/deltaX_[i];
658  }
659 
660  adjustStep(step, ksi_[i], deltaKsi_[i]);
661  adjustStep(step, Eta_[i], deltaEta_[i]);
662  }
663 
664  for (label i = 0; i < m; ++i)
665  {
666  adjustStep(step, y_[i], deltaY_[i]);
667  adjustStep(step, lamda_[i], deltaLamda_[i]);
668  adjustStep(step, mu_[i], deltaMu_[i]);
669  adjustStep(step, s_[i], deltaS_[i]);
670  }
671 
672  adjustStep(step, z_, deltaZ_);
673  adjustStep(step, zeta_, deltaZeta_);
674 
675  // Each processor might have computed a different step, if design variables
676  // are distributed. Get the global minimum
677  if (globalSum_)
678  {
679  reduce(step, minOp<scalar>());
680  }
681 
682  if (debug > 1)
683  {
684  Info<< "Step before line search is " << step << endl;
685  }
686 
687  // Old residual
688  scalar normResOld = sqrt(globalSum(magSqr(computeResiduals())));
689  scalar maxRes(GREAT);
690 
691  for (label i = 0; i < maxLineSearchIters_ ; ++i)
692  {
693  // Update the solution with given step
694  updateSolution(step);
695 
696  // Compute new residuals and their max value
697  scalarField resNew(computeResiduals());
698  scalar normResNew = sqrt(globalSum(magSqr(resNew)));
699  maxRes = gMax(mag(resNew));
700 
701  if (normResNew < normResOld)
702  {
703  DebugInfo
704  << "Initial residual = " << normResOld << ", "
705  << "Final residual = " << normResNew << ", "
706  << "No of LineSearch Iterations = " << i + 1
707  << endl;
708  break;
709  }
710  else
711  {
712  // Return solution to previous and reduce step
713  updateSolution(-step);
714  step *= 0.5;
715 
716  // If line search could find an appropriate step, increase eps
717  if (i == maxLineSearchIters_ - 1)
718  {
719  eps_ *= 1.5;
720  Info<< "Line search could not find a step that reduced"
721  << " residuals while satisfying the constraints" << nl
722  << "Increasing eps to " << eps_ << endl;
723  }
724  }
725  }
726 
727  if (debug > 1)
728  {
729  Info<< "Step after line search is " << step << nl << endl;
730  }
731 
732  return maxRes;
733 }
734 
735 
737 (
738  scalar& step,
739  const scalar value,
740  const scalar update
741 )
742 {
743  if (0.99*value + step*update < scalar(0))
744  {
745  step = -0.99*value/update;
746  }
747 }
748 
749 
750 void Foam::MMA::updateSolution(const scalar step)
751 {
752  xNew_ += step*deltaX_;
753  y_ += step*deltaY_;
754  z_ += step*deltaZ_;
755  lamda_ += step*deltaLamda_;
756  ksi_ += step*deltaKsi_;
757  Eta_ += step*deltaEta_;
758  mu_ += step*deltaMu_;
759  zeta_ += step*deltaZeta_;
760  s_ += step*deltaS_;
761 }
762 
763 
765 {
766  const label n(xNew_.size());
767  const label m(cValues_.size());
768  tmp<scalarField> tres(tmp<scalarField>::New(3*n + 4*m + 2, Zero));
769  scalarField& res = tres.ref();
770 
771  label iRes(0);
772 
773  // dLdx
774  scalarField dPsidx
775  (pLamda()/sqr(upper_ - xNew_) - qLamda()/sqr(xNew_ - lower_));
776  for (label i = 0; i < n; ++i)
777  {
778  res[iRes++] = dPsidx[i] - ksi_[i] + Eta_[i];
779  }
780 
781  // dLdy
782  for (label i = 0; i < m; ++i)
783  {
784  res[iRes++] = c_[i] + d_[i]*y_[i] - lamda_[i] - mu_[i];
785  }
786 
787  // dLdz
788  res[iRes++] = alpha0_ - zeta_ - sum(lamda_*alpha_);
789 
790  // Primal feasibility (constraints)
791  scalarField gb(gConstr(xNew_) - b());
792  for (label i = 0; i < m; ++i)
793  {
794  res[iRes++] = gb[i] - alpha_[i]*z_ - y_[i] + s_[i];
795  }
796 
797  // ksi slackness
798  for (label i = 0; i < n; ++i)
799  {
800  res[iRes++] = ksi_[i]*(xNew_[i] - a_[i]) - eps_;
801  }
802 
803  // Eta slackness
804  for (label i = 0; i < n; ++i)
805  {
806  res[iRes++] = Eta_[i]*(b_[i] - xNew_[i]) - eps_;
807  }
808 
809  // y slackness
810  for (label i = 0; i < m; ++i)
811  {
812  res[iRes++] = mu_[i]*y_[i] - eps_;
813  }
814 
815  // z slackness
816  res[iRes++] = z_*zeta_ - eps_;
817 
818  // Constraint slackness
819  for (label i = 0; i < m; ++i)
820  {
821  res[iRes++] = lamda_[i]*s_[i] - eps_;
822  }
823 
824  return tres;
825 }
826 
827 
829 {
830  if (normalise_)
831  {
832  // Compute normalisation factors
833  if
834  (
835  !Jnorm_
836  || (continuousNormalisation_ && counter_ < lastNormalisationStep_)
837  )
838  {
839  scalarField activeSens(objectiveDerivatives_, activeDesignVars_);
840  Jnorm_.reset(new scalar(Foam::sqrt(globalSum(sqr(activeSens)))));
841  Cnorm_.reset(new scalarField(cValues_.size(), Zero));
842  scalarField& Cnorm = Cnorm_.ref();
843  forAll(constraintDerivatives_, cI)
844  {
845  scalarField activeConstrSens
846  (constraintDerivatives_[cI], activeDesignVars_);
847  Cnorm[cI] = Foam::sqrt(globalSum(sqr(activeConstrSens)));
848  }
849  Info<< "Computed normalisation factors " << nl
850  << "J: " << Jnorm_() << nl
851  << "C: " << Cnorm_() << endl;
852  }
853 
854  // Normalise objective values and gradients
855  objectiveValue_ /= Jnorm_() + SMALL;
856  objectiveDerivatives_ /= Jnorm_() + SMALL;
857 
858  // Normalise constraint values and gradients
859  cValues_ *= cw_/(Cnorm_() + SMALL);
860  forAll(constraintDerivatives_, cI)
861  {
862  constraintDerivatives_[cI] *= cw_/(Cnorm_()[cI] + SMALL);
863  }
864  }
865 }
866 
867 
868 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
869 
870 Foam::MMA::MMA
871 (
872  const fvMesh& mesh,
873  const dictionary& dict,
874  autoPtr<designVariables>& designVars,
875  const label nConstraints,
876  const word& type
877 )
878 :
879  constrainedOptimisationMethod(mesh, dict, designVars, nConstraints, type),
880  updateMethod(mesh, dict, designVars, nConstraints, type),
881  x0_(getOrDefaultScalarField("x0", activeDesignVars_.size())),
882  x00_(getOrDefaultScalarField("x00", activeDesignVars_.size())),
883  xNew_(activeDesignVars_.size()),
884  oldObjectiveValue_(getOrDefault<scalar>("J0", Zero)),
885  oldCValues_(getOrDefaultScalarField("C0", nConstraints_)),
886  valsAndApproxs_(2),
887  z_(coeffsDict(type).getOrDefault<scalar>("z", 1.)),
888  alpha0_(coeffsDict(type).getOrDefault<scalar>("alpha0", 1.)),
889  alpha_(0),
890  y_(0),
891  c_(0),
892  d_(0),
893  lower_(xNew_.size(), -GREAT),
894  upper_(xNew_.size(), GREAT),
895  a_(xNew_.size()),
896  b_(xNew_.size()),
897  rho_(0),
898  boundRho_(coeffsDict(type).getOrDefault<bool>("boundRho", false)),
899  correctDVs_(coeffsDict(type).getOrDefault<bool>("correct", true)),
900  lamda_(0),
901  ksi_(xNew_.size()),
902  Eta_(xNew_.size()),
903  mu_(0),
904  zeta_(coeffsDict(type).getOrDefault<scalar>("zeta", 1.)),
905  s_(0),
906  deltaLamda_(0),
907  deltaZ_(Zero),
908  deltaX_(xNew_.size(), Zero),
909  deltaY_(0),
910  deltaS_(0),
911  deltaZeta_(Zero),
912  deltaMu_(0),
913  deltaEta_(xNew_.size(), Zero),
914  deltaKsi_(xNew_.size(), Zero),
915  eps_(1),
916  maxNewtonIters_(coeffsDict(type).getOrDefault<label>("maxIters", 6000)),
917  maxLineSearchIters_
918  (
919  coeffsDict(type).getOrDefault<label>("maxLineSearchIters", 10)
920  ),
921  initializeEverySubproblem_
922  (coeffsDict(type).getOrDefault<bool>("initializeEverySubproblem", false)),
923  asymDecr_(coeffsDict(type).getOrDefault<scalar>("asymptoteDecrease", 0.7)),
924  asymIncr_(coeffsDict(type).getOrDefault<scalar>("asymptoteIncrease", 1.2)),
925  sInit_(coeffsDict(type).getOrDefault<scalar>("sInit", 0.5)),
926  move_(coeffsDict(type).getOrDefault<scalar>("move", 0.5)),
927  raa0_(coeffsDict(type).getOrDefault<scalar>("raa0", 1.e-05)),
928  maxInitRhoMult_(coeffsDict(type).getOrDefault<scalar>("maxInitRhoMult", 0.1)),
929  maxRhoMult_(coeffsDict(type).getOrDefault<scalar>("maxRhoMult", 10)),
930  variableRho_(coeffsDict(type).getOrDefault<bool>("variableRho", false)),
931  dynamicRhoInitialisation_
932  (coeffsDict(type).getOrDefault<bool>("dynamicRhoInitialisation", false)),
933  dynamicRhoMult_
934  (coeffsDict(type).getOrDefault<scalar>("dynamicRhoMult", 0.1)),
935  normalise_(coeffsDict(type).getOrDefault<bool>("normalise", false)),
936  continuousNormalisation_
937  (coeffsDict(type).getOrDefault<bool>("continuousNormalisation", false)),
938  Jnorm_(nullptr),
939  Cnorm_(nullptr),
940  cw_(coeffsDict(type).getOrDefault<scalar>("constraintWeight", 1)),
941  lastNormalisationStep_
942  (
943  coeffsDict(type).getOrDefault<label>("lastNormalisationStep", 20)
944  )
945 {
946  // Check that the design variables bounds have been set
947  if (!designVars().lowerBounds() || !designVars().upperBounds())
948  {
950  << "Lower and upper design variable bounds must be set for MMA"
951  << abort(FatalError);
952  }
953 
954  // Set initial bounds
956  (
957  lower_,
958  "lower",
960  scalarField(designVars().lowerBoundsRef(), activeDesignVars_)
961  );
963  (
964  upper_,
965  "upper",
967  scalarField(designVars().upperBoundsRef(), activeDesignVars_)
968  );
969 }
970 
971 
972 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
973 
975 {
976  if (correctDVs_)
977  {
978  // Update sizes of fields related to the active design variables
979  updateSizes();
980 
981  // Perform normalisation of the objective and constraint values
982  normalise();
983 
984  // Initialize rho values in the p,q functions
985  // These determine how aggressive or conservative the method will be
986  initializeRho();
987 
988  // Update the bounds associated with the design variables
989  updateBounds();
990 
991  // Initiaze variables
992  initialize();
993 
994  // Solve subproblem using a Newton optimiser
995  solveSubproblem();
996 
997  // Store old solutions, bjective and constaint values
998  storeOldValues();
1000  // Increase counter
1001  ++counter_;
1002  }
1003 }
1004 
1005 
1007 {
1008  // Initialize the solution
1009  if (initializeEverySubproblem_)
1010  {
1011  initialize();
1012  }
1013 
1014  scalar resMax(gMax(mag(computeResiduals())));
1015  label iter(0);
1016  do
1017  {
1018  DebugInfo
1019  << "Newton iter " << iter << nl << endl;
1020 
1021  // Decrease eps
1022  if (resMax < 0.9*eps_)
1023  {
1024  eps_ *= 0.1;
1025  }
1026 
1027  // Computes Newton direction for the subproblem
1028  computeNewtonDirection();
1029 
1030  // Perform line search and return max residual of the solution
1031  // satisfying the bound constraints and the residual reduction.
1032  // Upates solution.
1033  resMax = lineSearch();
1034  DebugInfo
1035  << "max residual = " << resMax << ", "
1036  << "eps = " << eps_ << nl << endl;
1037 
1038  mesh_.time().printExecutionTime(Info);
1039  } while (iter++ < maxNewtonIters_ && (eps_ > 1.e-07 || resMax > 0.9*eps_));
1040 
1041  Info<< "Solved the MMA Newton problem in " << iter << " iterations "
1042  << nl << endl;
1043 
1044  // Pass update to correction field
1045  const scalarField& oldVars = designVars_().getVars();
1046  forAll(activeDesignVars_, avI)
1047  {
1048  const label vI(activeDesignVars_[avI]);
1049  correction_[vI] = xNew_[avI] - oldVars[vI];
1050  }
1051 }
1052 
1053 
1055 {
1056  // Return values, including the objective and constraint values and
1057  // their approximations
1058  label m(cValues_.size());
1059  valsAndApproxs_.set(0, new scalarField(m + 1, Zero));
1060  valsAndApproxs_.set(1, new scalarField(m + 1, Zero));
1061  scalarField& vals = valsAndApproxs_[0];
1062  scalarField& approx = valsAndApproxs_[1];
1063 
1064  // Objective value and approximation
1065  const scalarField activeObjDerivs(objectiveDerivatives_, activeDesignVars_);
1066  vals[0] = objectiveValue_;
1067  approx[0] = fTilda(x0_, xNew_, oldObjectiveValue_, activeObjDerivs, rho_[0]);
1068 
1069  // Constraint values and approximations
1070  forAll(constraintDerivatives_, i)
1071  {
1072  const scalarField activeDerivs
1073  (
1074  constraintDerivatives_[i], activeDesignVars_
1075  );
1076  vals[i + 1] = cValues_[i];
1077  approx[i + 1] =
1078  fTilda(x0_, xNew_, oldCValues_[i], activeDerivs, rho_[i + 1]);
1079  }
1080 }
1081 
1082 
1085 {
1086  return valsAndApproxs_;
1087 }
1088 
1089 
1090 void Foam::MMA::updateRho()
1091 {
1092  // Objective/constraint values and approximations
1093  const scalarField& vals = valsAndApproxs_[0];
1094  const scalarField& approx = valsAndApproxs_[1];
1095 
1096  const scalarField xMin(designVars_().lowerBounds()(), activeDesignVars_);
1097  const scalarField xMax(designVars_().upperBounds()(), activeDesignVars_);
1098 
1099  scalar d = globalSum
1100  (
1101  (upper_ - lower_)*sqr(xNew_ - x0_)
1102  /(upper_ - xNew_)/(xNew_ - lower_)/(xMax - xMin)
1103  );
1104 
1105  // Update rho values
1106  forAll(approx, i)
1107  {
1108  const scalar delta = (vals[i] - approx[i])/d;
1109  if (delta > 0)
1110  {
1111  rho_[i] = min(1.1*(rho_[i] + delta), maxRhoMult_*rho_[i]);
1112  }
1113  }
1114  DebugInfo
1115  << "Updated rho values to " << rho_ << endl;
1116 }
1117 
1119 const Foam::scalarField& Foam::MMA::getRho() const
1120 {
1121  return rho_;
1122 }
1123 
1125 void Foam::MMA::setVariableRho(bool varRho)
1126 {
1127  variableRho_ = varRho;
1128 }
1129 
1130 
1131 bool Foam::MMA::converged()
1132 {
1133  updateValuesAndApproximations();
1134  const scalarField& vals = valsAndApproxs_[0];
1135  const scalarField& approx = valsAndApproxs_[1];
1136 
1137  bool isConverged(true);
1138  forAll(vals, i)
1139  {
1140  DebugInfo
1141  << nl << "MMA, objective/constraint " << i << nl
1142  << "Approximation " << approx[i]
1143  << " | old value " << vals[i] << nl << endl;
1144  isConverged = isConverged && (approx[i] - vals[i] > 0.);
1145  }
1146 
1147  return isConverged;
1148 }
1149 
1150 
1151 bool Foam::MMA::writeData(Ostream& os) const
1152 {
1153  x0_.writeEntry("x0", os);
1154  x00_.writeEntry("x00", os);
1155  lower_.writeEntry("lower", os);
1156  upper_.writeEntry("upper", os);
1157  os.writeEntry("J0", oldObjectiveValue_);
1158  oldCValues_.writeEntry("C0", os);
1159 
1160  return updateMethod::writeData(os);
1161 }
1162 
1163 
1164 // ************************************************************************* //
tmp< scalarField > pLamda()
p and q functions, using the dual lamda
Definition: MMA.C:371
scalar delta
scalarField ksi_
Lagrange multipliers for the lower limits constraints.
Definition: MMA.H:186
dictionary dict
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:116
void setVariableRho(bool varRho=true)
Set variable rho.
Definition: MMA.C:1118
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &f1)
void solveSubproblem()
Solve subproblem using a Newton optimiser.
Definition: MMA.C:999
rDeltaTY field()
void computeCorrection()
Compute design variables correction.
Definition: MMA.C:967
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
void setOrDefaultScalarField(scalarField &field, const word &name, const label size, const scalarField &defaultField)
Read in scalarField from self (potentially in binary), or allocate field with the size of the active ...
Definition: MMA.C:460
tmp< scalarField > b()
The rhs of the contraint approximations.
Definition: MMA.C:362
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:129
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:598
tmp< scalarField > qLamda()
Definition: MMA.C:389
scalarField deltaKsi_
Definition: MMA.H:219
const dimensionedScalar G
Newtonian constant of gravitation.
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
dimensionedSymmTensor sqr(const dimensionedVector &dv)
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
void computeNewtonDirection()
Compute direction for the Newton problem.
Definition: MMA.C:478
void updateValuesAndApproximations()
Compute objective/constraint values and their approximations.
Definition: MMA.C:1047
void updateSolution(const scalar step)
Update the current solution using the known direction and the given step length.
Definition: MMA.C:743
scalarField deltaX_
Definition: MMA.H:213
dimensionedScalar sqrt(const dimensionedScalar &ds)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
virtual bool writeData(Ostream &os) const
Write useful quantities to files.
Definition: MMA.C:1144
Ostream & writeEntry(const keyType &key, const T &value)
Write a keyword/value entry.
Definition: Ostream.H:321
tmp< scalarField > p(const scalarField &derivs, const scalar r, const scalarField &x)
p and q functions, used to approximate the objective and contraints
Definition: MMA.C:261
bool converged()
Checks convergence of GCMMA.
Definition: MMA.C:1124
const scalar xMin
Definition: createFields.H:34
Macros for easy insertion into run-time selection tables.
scalarField b_
Upper design variables constraints.
Definition: MMA.H:157
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
scalarField lower_
Lower design variables asymptotes.
Definition: MMA.H:142
fileName::Type type(const fileName &name, const bool followLink=true)
Return the file type: DIRECTORY or FILE, normally following symbolic links.
Definition: POSIX.C:799
tmp< scalarField > q(const scalarField &derivs, const scalar r, const scalarField &x)
Definition: MMA.C:301
const labelList & activeDesignVars_
Map to active design variables.
Definition: updateMethod.H:75
void normalise()
Normalise the objective and constraints if necessary.
Definition: MMA.C:821
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
void setSize(const label n)
Alias for resize()
Definition: List.H:316
dynamicFvMesh & mesh
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition: exprTraits.C:127
tmp< scalarField > getOrDefaultScalarField(const word &name, const label size, const scalar value=Zero)
Read in scalarField from self (potentially in binary), or allocate field with the size of the active ...
Definition: MMA.C:443
void updateRho()
Update rho value if needed.
Definition: MMA.C:1083
const dimensionedScalar b
Wien displacement law constant: default SI units: [m.K].
Definition: createFields.H:27
A class for handling words, derived from Foam::string.
Definition: word.H:63
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
const scalarField & getRho() const
Get rho value if needed.
Definition: MMA.C:1112
tmp< scalarField > gConstr(const scalarField &vars)
g of all constraint functions
Definition: MMA.C:341
static tmp< T > New(Args &&... args)
Construct tmp with forwarding arguments.
Definition: tmp.H:206
void adjustStep(scalar &step, const scalar value, const scalar update)
Adjust step to satisfy cireteria.
Definition: MMA.C:730
static void listCombineAllGather(List< T > &values, const CombineOp &cop, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Same as Pstream::listCombineReduce.
Definition: Pstream.H:304
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:26
scalarField x00_
The twice previous design variables. Used to compute new bounds.
Definition: MMA.H:78
errorManip< error > abort(error &err)
Definition: errorManip.H:139
SolverPerformance< Type > solve(faMatrix< Type > &, const dictionary &solverControls)
Solve returning the solution statistics given convergence tolerance.
#define DebugInfo
Report an information message using Foam::Info.
void map(const UList< Type > &mapF, const labelUList &mapAddressing)
1 to 1 map from the given field
Definition: Field.C:303
int debug
Static debugging option.
Type gMax(const FieldField< Field, Type > &f)
OBJstream os(runTime.globalPath()/outputName)
mesh update()
defineTypeNameAndDebug(combustionModel, 0)
labelList f(nPoints)
scalarField deltaEta_
Definition: MMA.H:218
const scalar xMax
Definition: createFields.H:35
dimensionedScalar pow3(const dimensionedScalar &ds)
scalarField a_
Lower design variables constraints.
Definition: MMA.H:152
tmp< scalarField > computeResiduals()
Compute and return residuals based on the current solution.
Definition: MMA.C:757
scalarField xNew_
The set of design variables being updated during the subproblem.
Definition: MMA.H:86
A list of pointers to objects of type <T>, with allocation/deallocation management of the pointers...
Definition: List.H:55
void updateSizes()
Update sizes of fields related to the active design variables.
Definition: MMA.C:48
scalar lineSearch()
Perform line search and return max residual corresponding to the updated solution.
Definition: MMA.C:626
const PtrList< scalarField > & getValuesAndApproximations() const
Get objective/constraint values and their approximations.
Definition: MMA.C:1077
void reduce(const List< UPstream::commsStruct > &comms, T &value, const BinaryOp &bop, const int tag, const label comm)
Reduce inplace (cf. MPI Allreduce) using specified communication schedule.
void zeroUpdates()
Zero the updates computed in the previous optimisation cycle.
Definition: MMA.C:407
messageStream Info
Information stream (stdout output on master, null elsewhere)
void initializeRho()
Initialize rho constants in the (p, q) functions.
Definition: MMA.C:69
label n
scalarField upper_
Upper design variables asymptotes.
Definition: MMA.H:147
const scalar gamma
Definition: EEqn.H:9
scalarField Eta_
Lagrange multipliers for the upper limits constraints.
Definition: MMA.H:191
volScalarField & p
A class for managing temporary objects.
Definition: HashPtrTable.H:50
void updateBounds()
Update the bounds associated with the design variables.
Definition: MMA.C:176
SquareMatrix< scalar > scalarSquareMatrix
bool found
virtual bool writeData(Ostream &os) const
Write continuation data under uniform.
Definition: updateMethod.C:466
void initialize()
Allocate fields related to constraints.
Definition: MMA.C:215
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
void storeOldValues()
Store old objcective and constraint values.
Definition: MMA.C:249
Namespace for OpenFOAM.
scalar fTilda(const scalarField &xInit, const scalarField &x, const scalar f, const scalarField &dfdx, const scalar rho)
Computation of the approximate function.
Definition: MMA.C:422
scalarField x0_
The previous design variables. Used to compute new bounds.
Definition: MMA.H:73
addToRunTimeSelectionTable(functionObject, pointHistory, dictionary)
RectangularMatrix< scalar > scalarRectangularMatrix
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127