DASimpleFoam.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2 
3  DAFoam : Discrete Adjoint with OpenFOAM
4  Version : v3
5 
6  This class is modified from OpenFOAM's source code
7  applications/solvers/incompressible/simpleFoam
8 
9  OpenFOAM: The Open Source CFD Toolbox
10 
11  Copyright (C): 2011-2016 OpenFOAM Foundation
12 
13  OpenFOAM License:
14 
15  OpenFOAM is free software: you can redistribute it and/or modify it
16  under the terms of the GNU General Public License as published by
17  the Free Software Foundation, either version 3 of the License, or
18  (at your option) any later version.
19 
20  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
21  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
22  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23  for more details.
24 
25  You should have received a copy of the GNU General Public License
26  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
27 
28 \*---------------------------------------------------------------------------*/
29 
30 #include "DASimpleFoam.H"
31 
32 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
33 
34 namespace Foam
35 {
36 
37 defineTypeNameAndDebug(DASimpleFoam, 0);
38 addToRunTimeSelectionTable(DASolver, DASimpleFoam, dictionary);
39 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
40 
42  char* argsAll,
43  PyObject* pyOptions)
44  : DASolver(argsAll, pyOptions),
45  simplePtr_(nullptr),
46  pPtr_(nullptr),
47  UPtr_(nullptr),
48  phiPtr_(nullptr),
49  alphaPorosityPtr_(nullptr),
50  laminarTransportPtr_(nullptr),
51  turbulencePtr_(nullptr),
52  daTurbulenceModelPtr_(nullptr),
53  daFvSourcePtr_(nullptr),
54  fvSourcePtr_(nullptr),
55  MRFPtr_(nullptr)
56 {
57  // get fvSolution and fvSchemes info for fixed-point adjoint
58  const fvSolution& myFvSolution = meshPtr_->thisDb().lookupObject<fvSolution>("fvSolution");
59  if (myFvSolution.found("relaxationFactors"))
60  {
61  if (myFvSolution.subDict("relaxationFactors").found("equations"))
62  {
63  if (myFvSolution.subDict("relaxationFactors").subDict("equations").found("U"))
64  {
65  relaxUEqn_ = myFvSolution.subDict("relaxationFactors").subDict("equations").getScalar("U");
66  }
67  }
68  }
69  solverDictU_ = myFvSolution.subDict("solvers").subDict("U");
70  solverDictP_ = myFvSolution.subDict("solvers").subDict("p");
71 }
72 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
73 
75 {
76  /*
77  Description:
78  Initialize variables for DASolver
79  */
80 
81  Info << "Initializing fields for DASimpleFoam" << endl;
82  Time& runTime = runTimePtr_();
83  fvMesh& mesh = meshPtr_();
85 #include "createFieldsSimple.H"
87  // initialize checkMesh
89 
91 
92  this->setDAObjFuncList();
93 
94  // initialize fvSource and compute the source term
95  const dictionary& allOptions = daOptionPtr_->getAllOptions();
96  if (allOptions.subDict("fvSource").toc().size() != 0)
97  {
98  hasFvSource_ = 1;
99  Info << "Initializing DASource" << endl;
100  word sourceName = allOptions.subDict("fvSource").toc()[0];
101  word fvSourceType = allOptions.subDict("fvSource").subDict(sourceName).getWord("type");
103  fvSourceType, mesh, daOptionPtr_(), daModelPtr_(), daIndexPtr_()));
104  }
105 }
106 
108  const Vec xvVec,
109  Vec wVec)
110 {
111  /*
112  Description:
113  Call the primal solver to get converged state variables
114 
115  Input:
116  xvVec: a vector that contains all volume mesh coordinates
117 
118  Output:
119  wVec: state variable vector
120  */
121 
122 #include "createRefsSimple.H"
123 #include "createFvOptions.H"
124 
125  // change the run status
126  daOptionPtr_->setOption<word>("runStatus", "solvePrimal");
127 
128  // call correctNut, this is equivalent to turbulence->validate();
129  daTurbulenceModelPtr_->updateIntermediateVariables();
130 
131  Info << "\nStarting time loop\n"
132  << endl;
133 
134  // deform the mesh based on the xvVec
135  this->pointVec2OFMesh(xvVec);
136 
137  // check mesh quality
138  label meshOK = this->checkMesh();
139 
140  if (!meshOK)
141  {
142  this->writeFailedMesh();
143  return 1;
144  }
145 
146  // if the forwardModeAD is active, we need to set the seed here
147 #include "setForwardADSeeds.H"
148 
149  word divUScheme = "div(phi,U)";
150  if (daOptionPtr_->getSubDictOption<label>("runLowOrderPrimal4PC", "active"))
151  {
152  if (daOptionPtr_->getSubDictOption<label>("runLowOrderPrimal4PC", "isPC"))
153  {
154  Info << "Using low order div scheme for primal solution .... " << endl;
155  divUScheme = "div(pc)";
156  }
157  }
158 
159  primalMinRes_ = 1e10;
160  label printInterval = daOptionPtr_->getOption<label>("printInterval");
161  label printToScreen = 0;
162  while (this->loop(runTime)) // using simple.loop() will have seg fault in parallel
163  {
164 
165  printToScreen = this->isPrintTime(runTime, printInterval);
166 
167  if (printToScreen)
168  {
169  Info << "Time = " << runTime.timeName() << nl << endl;
170  }
171 
172  p.storePrevIter();
173 
174  // --- Pressure-velocity SIMPLE corrector
175  {
176 #include "UEqnSimple.H"
177 #include "pEqnSimple.H"
178  }
179 
180  laminarTransport.correct();
181  daTurbulenceModelPtr_->correct();
182 
183  if (printToScreen)
184  {
185  daTurbulenceModelPtr_->printYPlus();
186 
187  this->printAllObjFuncs();
188 
189  Info << "ExecutionTime = " << runTime.elapsedCpuTime() << " s"
190  << " ClockTime = " << runTime.elapsedClockTime() << " s"
191  << nl << endl;
192  }
193 
194  runTime.write();
195  }
196 
197  this->writeAssociatedFields();
198 
199  this->calcPrimalResidualStatistics("print");
200 
201  // primal converged, assign the OpenFoam fields to the state vec wVec
202  this->ofField2StateVec(wVec);
203 
204  // write the mesh to files
205  mesh.write();
206 
207  Info << "End\n"
208  << endl;
209 
210  return this->checkResidualTol();
211 }
212 
213 // ************ the following are functions for consistent fixed-point adjoint
214 
216  const Vec xvVec,
217  const Vec wVec,
218  Vec dFdW,
219  Vec psi)
220 {
221  // If adjoint converged, then adjConv = 0
222  // Otherwise, adjConv = 1
223  label adjConv = 1;
224 
225 #ifdef CODI_AD_REVERSE
226  /*
227  Description:
228  Solve the adjoint using the fixed-point iteration method
229 
230  xvVec: the volume mesh coordinate vector
231 
232  wVec: the state variable vector
233 
234  dFdW: The dF/dW vector
235 
236  psi: The adjoint solution vector
237  */
238 
239  // Here we keep the values of the previous step psi
240  //VecZeroEntries(psi);
241 
242  // update the state and mesh
243  this->updateOFField(wVec);
244  this->updateOFMesh(xvVec);
245 
246  label printInterval = daOptionPtr_->getOption<label>("printInterval");
247 
248  word adjEqnSolMethod = daOptionPtr_->getOption<word>("adjEqnSolMethod");
249 
250  if (adjEqnSolMethod == "fixedPoint")
251  {
252  Info << "Solving the adjoint using consistent fixed-point iteration method..."
253  << " Execution Time: " << meshPtr_->time().elapsedCpuTime() << " s" << endl;
254  ;
255  label fpMaxIters = daOptionPtr_->getSubDictOption<label>("adjEqnOption", "fpMaxIters");
256  scalar fpRelTol = daOptionPtr_->getSubDictOption<scalar>("adjEqnOption", "fpRelTol");
257  scalar fpMinResTolDiff = daOptionPtr_->getSubDictOption<scalar>("adjEqnOption", "fpMinResTolDiff");
258 
259  const objectRegistry& db = meshPtr_->thisDb();
260  volVectorField& U = const_cast<volVectorField&>(db.lookupObject<volVectorField>("U"));
261  volScalarField& p = const_cast<volScalarField&>(db.lookupObject<volScalarField>("p"));
262  surfaceScalarField& phi = const_cast<surfaceScalarField&>(db.lookupObject<surfaceScalarField>("phi"));
263  volScalarField& nuTilda = const_cast<volScalarField&>(db.lookupObject<volScalarField>("nuTilda"));
264  // nuEff is only used to construct pseudoUEqn
265  volScalarField nuEff = daTurbulenceModelPtr_->nuEff();
266 
267  // We now pass dFdW directly into adjRes components at the beginning of adjoint residual calculation
268  /*
269  // Initialize field cmpts for dFdW as all-zero
270  volVectorField dFdU("dFdU", 0.0 * U);
271  volScalarField dFdP("dFdP", 0.0 * p);
272  surfaceScalarField dFdPhi("dFdPhi", 0.0 * phi);
273  volScalarField dFdNuTilda("dFdNuTilda", 0.0 * nuTilda);
274  */
275 
276  // Initialize primal residuals as all-zero
277  volVectorField URes("URes", 0.0 * U);
278  volScalarField pRes("pRes", 0.0 * p);
279  surfaceScalarField phiRes("phiRes", 0.0 * phi);
280  volScalarField nuTildaRes("nuTildaRes", 0.0 * nuTilda);
281 
282  // We now pass dFdW directly into adjRes components at the beginning of adjoint residual calculation
283  /*
284  // Pass the values in dFdW to its field cmpts
285  this->vec2Fields("vec2Field", dFdW, dFdU, dFdP, dFdPhi, dFdNuTilda);
286  */
287  /*
288  // Flip the sign for now to avoid the strange issue
289  dFdU = -dFdU;
290  dFdP = -dFdP;
291  dFdPhi = -dFdPhi;
292  dFdNuTilda = -dFdNuTilda;
293  */
294 
295  // Initialize all-zero cmpts for the adjoint vector
296  volVectorField UPsi("UPsi", 0.0 * U);
297  volScalarField pPsi("pPsi", 0.0 * p);
298  surfaceScalarField phiPsi("phiPsi", 0.0 * phi);
299  volScalarField nuTildaPsi("nuTildaPsi", 0.0 * nuTilda);
300 
301  // Pass psiVec to cmpts of the adjoint vector:
302  //this->vec2Fields("vec2Field", psi, UPsi, pPsi, phiPsi, nuTildaPsi);
303 
304  /*
305  // Change every element in the adjoint vector to one
306  forAll(UPsi, cellI)
307  {
308  UPsi[cellI][0] = 1.0;
309  UPsi[cellI][1] = 1.0;
310  UPsi[cellI][2] = 1.0;
311  }
312  forAll(pPsi, cellI)
313  {
314  pPsi[cellI] = 1.0;
315  }
316  forAll(phiPsi.primitiveFieldRef(), faceI)
317  {
318  phiPsi.primitiveFieldRef()[faceI] = 1.0;
319  }
320  forAll(phiPsi.boundaryFieldRef(), patchI)
321  {
322  forAll(phiPsi.boundaryFieldRef()[patchI], faceI)
323  {
324  phiPsi.boundaryFieldRef()[patchI][faceI] = 1.0;
325  }
326  }
327  forAll(nuTildaPsi, cellI)
328  {
329  nuTildaPsi[cellI] = 1.0;
330  }
331  */
332 
333  // Initialize all-zero cmpts for the adjoint residual
334  // Note: we tried defining below as alias of pseudo variables, it works but it actually increases mem usage..
335  volVectorField adjURes("adjURes", 0.0 * U);
336  volScalarField adjPRes("adjPRes", 0.0 * p);
337  surfaceScalarField adjPhiRes("adjPhiRes", 0.0 * phi);
338  volScalarField adjNuTildaRes("adjNuTildaRes", 0.0 * nuTilda);
339 
340  // Initialize the initial L2norm for adjURes, adjPRes, adjPhiRes, adjNuTildaRes as all-zero
341  vector initNormAdjURes = vector::zero;
342  scalar initNormAdjPRes = 0.0;
343  scalar initNormAdjPhiRes = 0.0;
344  scalar initNormAdjNuTildaRes = 0.0;
345 
346  // Below use the norm of dFdW as the initial residual norm
347  /*
348  // Pass -dfdw to adjRes:
349  this->vec2Fields("vec2Field", dFdW, adjURes, adjPRes, adjPhiRes, adjNuTildaRes);
350  adjURes = -adjURes;
351  adjPRes = -adjPRes;
352  adjPhiRes = -adjPhiRes;
353  adjNuTildaRes = -adjNuTildaRes;
354 
355  // Initialize the initial L2norm for adjURes, adjPRes, adjPhiRes, adjNuTildaRes
356  vector initNormAdjURes = L2norm(adjURes);
357  //scalar initNormAdjPRes = L2norm(adjPRes);
358  //scalar initNormAdjPhiRes = L2norm(adjPhiRes);
359  //scalar initNormAdjNuTildaRes = L2norm(adjNuTildaRes);
360 
361  // Get the norm of the total adjoint residual
362  scalar initNormTotAdjRes = sqr(initNormAdjURes[0]) + sqr(initNormAdjURes[1]) + sqr(initNormAdjURes[2]) + sqr(L2norm(adjPRes)) + sqr(L2norm(adjPhiRes)) + sqr(L2norm(adjNuTildaRes));
363  initNormTotAdjRes = sqrt(initNormTotAdjRes);
364  */
365 
366 // Construct the pseudoEqns using pseudoEqns.H
367 // Better to make pseudoUEqn, pseudoPEqn, pseudoNuTildaEqn class variables
368 // Also need to move pseudoNuTildaEqn to DASpalartAllmarasFv3 under DATurbulenceModel
369 #include "pseudoEqns.H"
370 
371  // Initialize the duplicated variables for AD
372  volScalarField p1("p1", p);
373  volScalarField p2("p2", p);
374  this->meshPtr_->setFluxRequired(p2.name());
375  surfaceScalarField phi1("phi1", phi);
376  volVectorField U1("U1", U);
377 
378  // Initialize the intermidiate variables for AD
379  volVectorField gradP("gradP", fvc::grad(p));
380  surfaceScalarField pEqnFlux("pEqnFlux", 0.0 * phi);
381  volScalarField divPhi("divPhi", fvc::div(phi));
382  surfaceScalarField fluxU("fluxU", 0.0 * phi);
383 
384  // No need to print the un-normalized primal residuals
385  /*
386  // Print primal residuals before starting the adjoint iterations
387  this->calcLduResiduals(URes, pRes, phiRes);
388  daTurbulenceModelPtr_->calcLduResidualTurb(nuTildaRes);
389  Info << "Residual for simpleFOAM after convergence: " << endl;
390  Info << "L2 norm of URes: " << L2norm(URes) << endl;
391  Info << "L2 norm of pRes: " << L2norm(pRes) << endl;
392  Info << "L2 norm of phiRes: " << L2norm(phiRes) << endl;
393  Info << "L2 norm of nuTildaRes: " << L2norm(nuTildaRes) << endl;
394  */
395 
396  label cnt = 0;
397  while (cnt < fpMaxIters)
398  {
399  if (cnt % printInterval == 0)
400  {
401  Info << "Step = " << cnt << " Execution Time: " << meshPtr_->time().elapsedCpuTime() << " s" << endl;
402  }
403 
404  // ************************************************************************* //
405  // Step-1: Get D^(-T).adjURes, adjPRes, adjPhiRes, adjNuTildaRes, and update phiPsi
406  // ************************************************************************* //
407  // Step-1.1: Calculate adjURes, adjPRes, adjPhiRes, adjNuTildaRes, and update phiPsi
408 
409  // At beginning of iteration, call calcAdjointResidual once to get the adjoint residual
410  this->calcAdjointResidual(URes, pRes, phiRes, nuTildaRes, dFdW, UPsi, pPsi, phiPsi, nuTildaPsi, adjURes, adjPRes, adjPhiRes, adjNuTildaRes, cnt);
411 
412  // Below uses the norm of total adjoint residual to check convergence
413  /*
414  // Calculate L2 norm for all components of adjoint residual, then check if fpRelTol is met
415  vector normAdjURes = L2norm(adjURes);
416  scalar normAdjPRes = L2norm(adjPRes);
417  scalar normAdjPhiRes = L2norm(adjPhiRes);
418  scalar normAdjNuTildaRes = L2norm(adjNuTildaRes);
419 
420  scalar normTotAdjRes = sqr(normAdjURes[0]) + sqr(normAdjURes[1]) + sqr(normAdjURes[2]) + sqr(normAdjPRes) + sqr(normAdjPhiRes) + sqr(normAdjNuTildaRes);
421  normTotAdjRes = sqrt(normTotAdjRes);
422 
423  // Normalize total residual norm:
424  normTotAdjRes /= initNormTotAdjRes;
425 
426  if (cnt % printInterval == 0)
427  {
428  Info << "L2 norm of adjURes: " << normAdjURes[0] << " " << normAdjURes[1] << " " << normAdjURes[2] << endl;
429  Info << "L2 norm of adjPRes: " << normAdjPRes << endl;
430  Info << "L2 norm of adjPhiRes: " << normAdjPhiRes << endl;
431  Info << "L2 norm of adjNuTildaRes: " << normAdjNuTildaRes << endl;
432  }
433 
434  // Check if fpRelTol is met:
435  if (normTotAdjRes < fpRelTol)
436  {
437  Info << "Residual drop of " << fpRelTol << " has been achieved after " << cnt << " steps! Execution Time: " << meshPtr_->time().elapsedCpuTime() << " s" << endl;;
438  Info << "Final L2 norm of adjURes: " << normAdjURes[0] << " " << normAdjURes[1] << " " << normAdjURes[2] << endl;
439  Info << "Final L2 norm of adjPRes: " << normAdjPRes << endl;
440  Info << "Final L2 norm of adjPhiRes: " << normAdjPhiRes << endl;
441  Info << "Final L2 norm of adjNuTildaRes: " << normAdjNuTildaRes << endl;
442  break;
443  }
444  */
445 
446  // Calculate L2 norm for all components of adjoint residual, then check if fpRelTol is met
447  if (cnt >= 1)
448  {
449  vector normAdjURes = L2norm(adjURes);
450  scalar normAdjPRes = L2norm(adjPRes);
451  scalar normAdjPhiRes = L2norm(adjPhiRes);
452  scalar normAdjNuTildaRes = L2norm(adjNuTildaRes);
453 
454  if (cnt == 1)
455  {
456  initNormAdjURes = normAdjURes;
457  initNormAdjPRes = normAdjPRes;
458  initNormAdjPhiRes = normAdjPhiRes;
459  initNormAdjNuTildaRes = normAdjNuTildaRes;
460  }
461 
462  // Normalize residual norms:
463  normAdjURes[0] /= initNormAdjURes[0];
464  normAdjURes[1] /= initNormAdjURes[1];
465  normAdjURes[2] /= initNormAdjURes[2];
466  normAdjPRes /= initNormAdjPRes;
467  normAdjPhiRes /= initNormAdjPhiRes;
468  normAdjNuTildaRes /= initNormAdjNuTildaRes;
469 
470  if (cnt % printInterval == 0)
471  {
472  Info << "Normalized L2 norm of adjURes: " << normAdjURes[0] << " " << normAdjURes[1] << " " << normAdjURes[2] << endl;
473  Info << "Normalized L2 norm of adjPRes: " << normAdjPRes << endl;
474  Info << "Normalized L2 norm of adjPhiRes: " << normAdjPhiRes << endl;
475  Info << "Normalized L2 norm of adjNuTildaRes: " << normAdjNuTildaRes << endl;
476  }
477 
478  // Check if fpRelTol is met:
479  if (normAdjURes[0] < fpRelTol && normAdjURes[1] < fpRelTol && normAdjURes[2] < fpRelTol && normAdjPRes < fpRelTol && normAdjPhiRes < fpRelTol && normAdjNuTildaRes < fpRelTol)
480  {
481  Info << "Residual drop of " << fpRelTol << " has been achieved after " << cnt << " steps! Execution Time: " << meshPtr_->time().elapsedCpuTime() << " s" << endl;
482  Info << "Adjoint is converged and succussful.." << endl;
483  Info << "Normalized L2 norm of adjURes: " << normAdjURes[0] << " " << normAdjURes[1] << " " << normAdjURes[2] << endl;
484  Info << "Normalized L2 norm of adjPRes: " << normAdjPRes << endl;
485  Info << "Normalized L2 norm of adjPhiRes: " << normAdjPhiRes << endl;
486  Info << "Normalized L2 norm of adjNuTildaRes: " << normAdjNuTildaRes << endl;
487  adjConv = 0;
488  break;
489  }
490  }
491 
492  // Update phiPsi
493  phiPsi += adjPhiRes;
494 
495  // ************************************************************************* //
496  // Step-1.2: Get D^(-T) adjURes, overwrite adjURes
497 
498  forAll(adjURes, cellI)
499  {
500  adjURes[cellI] /= UDiag[cellI];
501  }
502 
503  // ************************************************************************* //
504  // Step-2: Get alphaP.(F_p^grad)^T.(D^(-T).adjURes) - alphaP.adjPRes + (F_p^flux.F_p^aug)^T.adjPhiRes
505  // ************************************************************************* //
506  // Step-2.1: Start with (F_p^grad)^T.(D^(-T).adjURes) - adjPRes
507 
508  // Initialize step_2 as -adjPRes
509  //volScalarField step_2("step_2", -adjPRes);
510  // Use alias to save some mem usage
511  volScalarField& step_2 = adjPRes;
512  step_2 = -step_2;
513 
514  // Do (F_p^grad)^T.(D^(-T).adjURes) with ADR
515  // Here, F_p^grad is the operator of fvc::grad(p) * mesh().V() on p
516 
517  if (cnt == 0)
518  {
519  this->globalADTape_.setActive();
520 
521  // Now register p1 as input:
522  forAll(p1, cellI)
523  {
524  this->globalADTape_.registerInput(p1[cellI]);
525  }
526 
527  // Get tape position before calling function(s)
528  gradPStart_ = this->globalADTape_.getPosition();
529 
530  // Correct boundaries to link the intermediate results
531  p1.correctBoundaryConditions();
532  U.correctBoundaryConditions();
533 
534  // Calculate fvc::grad(p) * mesh().V()
535  gradP = fvc::grad(p1);
536  forAll(gradP, cellI)
537  {
538  gradP.primitiveFieldRef()[cellI] *= U.mesh().V()[cellI];
539  }
540 
541  // Get tape position before calling function(s)
542  gradPEnd_ = this->globalADTape_.getPosition();
543 
544  // stop recording
545  this->globalADTape_.setPassive();
546  }
547 
548  // set the AD seed to the output var
549  // Seed with (D^(-T).adjURes), which is the overwritten adjURes.
550  forAll(gradP, cellI)
551  {
552  gradP[cellI][0].setGradient(adjURes[cellI][0].getValue());
553  gradP[cellI][1].setGradient(adjURes[cellI][1].getValue());
554  gradP[cellI][2].setGradient(adjURes[cellI][2].getValue());
555  }
556 
557  // evaluate the tape to compute the derivative of the seeded output wrt all the input
558  this->globalADTape_.evaluate(gradPEnd_, gradPStart_);
559  forAll(p1, cellI)
560  {
561  step_2[cellI] += p1[cellI].getGradient();
562  }
563 
564  // Clear adjoints for future Jacobian calculations
565  this->globalADTape_.clearAdjoints();
566 
567  // ************************************************************************* //
568  // Step-2.2: Get alphaP*step_2.1 = alphaP.((F_p^grad)^T.(D^(-T).adjURes) - adjPRes)
569  scalar alphaP = p.mesh().fieldRelaxationFactor(p.name());
570  step_2 *= alphaP;
571 
572  //Info<<"Show step_2 after *alphaP: "<< step_2 << endl;
573 
574  // ************************************************************************* //
575  // Step-2.3: Get step_2.2 + (F_p^flux.F_p^aug)^T.adjPhiRes = alphaP.((F_p^grad)^T.(D(-T).adjURes) - adjPRes) + (F_p^flux.F_p^aug)^T.adjPhiRes
576  // Here, (F_p^flux.F_p^aug) is the operator pEqn.flux() on p, (F_p^flux.F_p^aug)^T.adjPhiRes is calculated with ADR
577 
578  if (cnt == 0)
579  {
580  // First, get pEqn1, note that we only need the l.h.s. of pEqn1
581  fvScalarMatrix pEqn1(
582  fvm::laplacian(rAU, p2));
583  //Info<<"Show pEqn1.flux(): "<< pEqn1.flux() << endl;
584 
585  // Re-active the tape, no need to initiate it again
586  this->globalADTape_.setActive();
587 
588  // Now register p2 as input:
589  forAll(p2, cellI)
590  {
591  this->globalADTape_.registerInput(p2[cellI]);
592  }
593 
594  // Get tape position before calling function(s)
595  pEqnFluxStart_ = this->globalADTape_.getPosition();
596 
597  // Correct boundaries to link the intermediate results
598  p2.correctBoundaryConditions();
599  U.correctBoundaryConditions();
600 
601  // Calculate pEqn1.flux():
602  pEqnFlux = pEqn1.flux();
603 
604  // Get tape position after calling function(s)
605  pEqnFluxEnd_ = this->globalADTape_.getPosition();
606 
607  // stop recording
608  this->globalADTape_.setPassive();
609  }
610 
611  // set the AD seed to the output var pEqnFlux
612  // Seed with adjPhiRes
613  // Seed internal of pEqnFlux:
614  forAll(pEqnFlux.primitiveFieldRef(), faceI)
615  {
616  pEqnFlux.primitiveFieldRef()[faceI].setGradient(adjPhiRes.primitiveFieldRef()[faceI].getValue());
617  }
618  // Seed boundary of pEqnFlux:
619  forAll(pEqnFlux.boundaryFieldRef(), patchI)
620  {
621  forAll(pEqnFlux.boundaryFieldRef()[patchI], faceI)
622  {
623  pEqnFlux.boundaryFieldRef()[patchI][faceI].setGradient(adjPhiRes.boundaryFieldRef()[patchI][faceI].getValue());
624  }
625  }
626 
627  // evaluate the tape to compute the derivative of the seeded output wrt all the input
628  this->globalADTape_.evaluate(pEqnFluxEnd_, pEqnFluxStart_);
629  forAll(p2, cellI)
630  {
631  step_2[cellI] += p2[cellI].getGradient();
632  }
633 
634  // Clear adjoints for future Jacobian calculations
635  this->globalADTape_.clearAdjoints();
636  // reset the tape for future recording
637  //tape.reset();
638 
639  //Info<<"Show final form of step_2: "<< step_2 << endl;
640 
641  // ************************************************************************* //
642  // Step-3: Solve and get step_3 = A_p^(-T).step_2,
643  // which is A_p^(-T).(alphaP.((F_p^grad)^T.(D(-T).adjURes) - adjPRes) + (F_p^flux.F_p^aug)^T.adjPhiRes)
644  // then update pPsi += step_3
645 
646  // Solve, using the inverse transpose product function
647  //invTranProd_pEqn(rAU, p, step_2, pseudoP);
648  //Info<<"Show pseudoP: "<< pseudoP << endl;
649 
650  // *********************************** //
651  // Below is equivalent to: invTranProd_pEqn(rAU, p, step_2, pseudoP);
652  // Here, pSource is a redundant alias, which is an "input argument" that takes the value of step_2 into solvePseudopEqn.H
653  volScalarField& pSource = step_2;
654 #include "solvePseudoPEqn.H"
655  // *********************************** //
656 
657  // Get step_3 as the alias of pseudoP
658  volScalarField& step_3 = pseudoP;
659  //Info<<"Show step_3: "<< step_3 << endl;
660 
661  // Update pPsi
662  pPsi += step_3;
663  //Info<<"Show pPsi: "<< pPsi << endl;
664 
665  // ************************************************************************* //
666  // Step-4: Get to the point before solve with A_U^(-T)
667  // which is Q^T.{D^(-T).adjURes - D^(-T)[E^T.F^T.M_p^(-T).alphaP.B^T.D^(-T).adjURes - E^T.F^T.M_p^(-T).alphaP.adjPRes + (E^T.F^T.M_p^(-T).G^T - E^T).adjPhiRes]}
668  // ************************************************************************* //
669  // Step-4.1: Get (F_phi^div)^T.step_3 - adjPhiRes = (F_phi^div)^T.(A_p^(-T).(alphaP.((F_p^grad)^T.(D(-T).adjURes) - adjPRes) + (F_p^flux.F_p^aug)^T.adjPhiRes)) - adjPhiRes
670  // (F_phi^div)^T.step_3 is calculated with ADR
671  // Here, (F_phi^div) is the operator fvc::div(phiHbyA) on phiHbyA
672  // We don't want to calculate phiHbyA, so let's use phi instead
673  // So, (F_phi^div) is the operator fvc::div(phi) on phi
674 
675  // Initiate a phi-like temp variable as - adjPhiRes
676  //surfaceScalarField phiTemp = -adjPhiRes;
677  // Use alia to same some mem usage
678  surfaceScalarField& phiTemp = adjPhiRes;
679  phiTemp = -phiTemp;
680 
681  if (cnt == 0)
682  {
683  // Re-active the tape, no need to initiate it again
684  this->globalADTape_.setActive();
685 
686  // Register phi1 as input
687  // Note that both the internal field of phi1 and the boundary fields need to be registered
688  // Register internal of phi1:
689  forAll(phi1.primitiveFieldRef(), faceI)
690  {
691  this->globalADTape_.registerInput(phi1.primitiveFieldRef()[faceI]);
692  }
693  // Register boundary of phi:
694  forAll(phi1.boundaryFieldRef(), patchI)
695  {
696  forAll(phi1.boundaryFieldRef()[patchI], faceI)
697  {
698  this->globalADTape_.registerInput(phi1.boundaryFieldRef()[patchI][faceI]);
699  }
700  }
701 
702  // Get tape position before calling function(s)
703  divPhiStart_ = this->globalADTape_.getPosition();
704 
705  // This is not needed, but whatever... XD
706  // Correct boundaries to link the intermediate results
707  //U.correctBoundaryConditions();
708  //p.correctBoundaryConditions();
709 
710  // Calculate fvc::div(phi), then scale with cell volumes mesh().V()
711  divPhi = fvc::div(phi1);
712  forAll(divPhi, cellI)
713  {
714  divPhi.primitiveFieldRef()[cellI] *= p.mesh().V()[cellI];
715  }
716 
717  // Get tape position after calling function(s)
718  divPhiEnd_ = this->globalADTape_.getPosition();
719 
720  // stop recording
721  this->globalADTape_.setPassive();
722  }
723 
724  // set the AD seed to the output var divPhi
725  // Seed with step_3
726  forAll(divPhi, cellI)
727  {
728  divPhi[cellI].setGradient(step_3[cellI].getValue());
729  }
730 
731  // evaluate the tape to compute the derivative of the seeded output wrt all the input
732  this->globalADTape_.evaluate(divPhiEnd_, divPhiStart_);
733  forAll(phi1.primitiveFieldRef(), faceI)
734  {
735  phiTemp.primitiveFieldRef()[faceI] += phi1.primitiveFieldRef()[faceI].getGradient();
736  }
737  forAll(phi1.boundaryFieldRef(), patchI)
738  {
739  forAll(phi1.boundaryFieldRef()[patchI], faceI)
740  {
741  phiTemp.boundaryFieldRef()[patchI][faceI] += phi1.boundaryFieldRef()[patchI][faceI].getGradient();
742  }
743  }
744 
745  // Clear adjoints for future Jacobian calculations
746  this->globalADTape_.clearAdjoints();
747  // reset the tape for future recording
748  //tape.reset();
749 
750  //Info<<"Show step-4.1: "<< phiTemp << endl;
751 
752  // ************************************************************************* //
753  // Step-4.2: Get (D^(-T).adjURes) - D^(-T).(F_U^flux.F_U^aug)^T.phiTemp
754  // Here, (F_U^flux.F_U^aug) is the operator fvc::flux(HbyA) on HbyA
755  // We don't want to calculate HbyA, so we use U instead
756  // HbyA and U have the same boundary settings, so the operator as a function stays the same
757  // So, (F_U^flux.F_U^aug) is the operator fvc::flux(U) on U
758 
759  // Update UPsi, for the 1st time
760  // We have to do this before creating step_4 as the alias of adjURes
761  forAll(UPsi, cellI)
762  {
763  UPsi[cellI] -= adjURes[cellI]; // Note that the overwritten adjURes is actually D^(-T).adjURes
764  }
765 
766  // Initiate a U-like temporary variable step_4 as D^(-T).adjURes, which is the overwritten adjURes
767  //volVectorField step_4("step_4", adjURes);
768  // Use alias to save some mem usage
769  volVectorField& step_4 = adjURes;
770 
771  if (cnt == 0)
772  {
773  // Re-active the tape, no need to initiate it again
774  this->globalADTape_.setActive();
775 
776  // Register U1 as input, note that U1 has 3 components
777  forAll(U1, cellI)
778  {
779  this->globalADTape_.registerInput(U1[cellI][0]);
780  this->globalADTape_.registerInput(U1[cellI][1]);
781  this->globalADTape_.registerInput(U1[cellI][2]);
782  }
783 
784  // Get tape position before calling function(s)
785  fluxUStart_ = this->globalADTape_.getPosition();
786 
787  // Correct boundaries to link the intermediate results
788  U1.correctBoundaryConditions();
789  p.correctBoundaryConditions();
790 
791  // Calculate fvc::flux(U)
792  fluxU = fvc::flux(U1);
793 
794  // Get tape position after calling function(s)
795  fluxUEnd_ = this->globalADTape_.getPosition();
796 
797  // stop recording
798  this->globalADTape_.setPassive();
799  }
800 
801  // set the AD seed to the output var fluxU
802  // Seed with phiTemp
803  // Seed internal of fluxU:
804  forAll(fluxU.primitiveFieldRef(), faceI)
805  {
806  fluxU.primitiveFieldRef()[faceI].setGradient(phiTemp.primitiveFieldRef()[faceI].getValue());
807  }
808  // Seed boundary of fluxU:
809  forAll(fluxU.boundaryFieldRef(), patchI)
810  {
811  forAll(fluxU.boundaryFieldRef()[patchI], faceI)
812  {
813  fluxU.boundaryFieldRef()[patchI][faceI].setGradient(phiTemp.boundaryFieldRef()[patchI][faceI].getValue());
814  }
815  }
816 
817  // evaluate the tape to compute the derivative of the seeded output wrt all the input
818  this->globalADTape_.evaluate(fluxUEnd_, fluxUStart_);
819  forAll(U1, cellI)
820  {
821  for (label cmpt = 0; cmpt < 3; cmpt++)
822  {
823  step_4[cellI][cmpt] -= U1[cellI][cmpt].getGradient() / UDiag[cellI];
824  }
825  }
826 
827  // Clear adjoints for future Jacobian calculations
828  this->globalADTape_.clearAdjoints();
829  // reset the tape for future recording
830  //tape.reset();
831 
832  //Info<<"Show step_4: "<< step_4 << endl;
833 
834  // ************************************************************************* //
835  // Step-4.3: Get Q^T.step_4, this is the last sub-step of step-4
836  // We use pseudoUEqn, whose upper and lower are already swapped
837  step_4.primitiveFieldRef() = -pseudoUEqn.lduMatrix::H(step_4);
838  //Info<<"Show step_4: "<< step_4 << endl;
839 
840  // ************************************************************************* //
841  // Step-5: Solve and get step_5 = A_U^(-T).step_4
842  // Then update UPsi += (step_5 - D^(-T).adjURes)
843 
844  // Solve, using the inverse transpose product function
845  //invTranProd_UEqn(U, phi, nuEff, p, step_4, pseudoU);
846  //Info<<"Show pseudoU: "<< pseudoU << endl;
847 
848  // *********************************** //
849  // Below is equivalent to: invTranProd_UEqn(U, phi, nuEff, p, step_4, pseudoU);
850  // Here, USource is a redundant alias, which is an "input argument" that takes the value of step_4 into solvePseudoUEqn.H
851  volVectorField& USource = step_4;
852 #include "solvePseudoUEqn.H"
853  // *********************************** //
854 
855  // Get step_5 as the alias of pseudoU
856  volVectorField& step_5 = pseudoU;
857  //Info<<"Show step_5: "<< step_5 << endl;
858 
859  // Update UPsi, for the 2nd time
860  forAll(UPsi, cellI)
861  {
862  UPsi[cellI] += step_5[cellI];
863  }
864  //Info<<"Show UPsi: "<< UPsi << endl;
865 
866  // ************************************************************************* //
867  // Step-6: (SA turbulence) Solve and get pseudoNuTilda = A_nuTilda^(-T).adjNuTildaRes
868  // Then update nuTildaPsi -= pseudoNuTilda
869 
870  // Solve, using the inverse transpose product function
871  //invTranProd_nuTildaEqn(U, phi, nuTilda, y, nu, sigmaNut, kappa, Cb1, Cb2, Cw1, Cw2, Cw3, Cv1, Cv2, adjNuTildaRes, pseudoNuTilda);
872 
873  // *********************************** //
874  // Below is equivalent to: invTranProd_nuTildaEqn(U, phi, nuTilda, y, nu, sigmaNut, kappa, Cb1, Cb2, Cw1, Cw2, Cw3, Cv1, Cv2, adjNuTildaRes, pseudoNuTilda);
875  // Here, nuTildaSource is a redundant alias, which is an "input argument" that takes the value of adjNuTildaRes into solvePseudoUEqn.H
876 
877  volScalarField& nuTildaSource = adjNuTildaRes;
878 
879  daTurbulenceModelPtr_->rhsSolvePseudoNuTildaEqn(nuTildaSource);
880  //#include "solvePseudonuTildaEqn.H"
881  // *********************************** //
882 
883  // Update nuTildaPsi
884  forAll(nuTildaPsi, cellI)
885  {
886  nuTildaPsi[cellI] -= pseudoNuTilda[cellI];
887  }
888 
889  cnt++;
890  }
891 
892  // If fpRelTol not met, check if the relaxed adjoint relTol is met
893  if (adjConv == 1)
894  {
895  Info << "Prescribed adjoint convergence of " << fpRelTol << " has not been met after " << cnt << " steps! Execution Time: " << meshPtr_->time().elapsedCpuTime() << " s" << endl;
896  Info << "Now checking if the relaxed convergence is met..." << endl;
897  this->calcAdjointResidual(URes, pRes, phiRes, nuTildaRes, dFdW, UPsi, pPsi, phiPsi, nuTildaPsi, adjURes, adjPRes, adjPhiRes, adjNuTildaRes, cnt);
898 
899  vector normAdjURes = L2norm(adjURes);
900  scalar normAdjPRes = L2norm(adjPRes);
901  scalar normAdjPhiRes = L2norm(adjPhiRes);
902  scalar normAdjNuTildaRes = L2norm(adjNuTildaRes);
903 
904  // Normalize residual norms:
905  normAdjURes[0] /= initNormAdjURes[0];
906  normAdjURes[1] /= initNormAdjURes[1];
907  normAdjURes[2] /= initNormAdjURes[2];
908  normAdjPRes /= initNormAdjPRes;
909  normAdjPhiRes /= initNormAdjPhiRes;
910  normAdjNuTildaRes /= initNormAdjNuTildaRes;
911 
912  scalar relaxedFpRelTol = fpRelTol * fpMinResTolDiff;
913  if (normAdjURes[0] < relaxedFpRelTol && normAdjURes[1] < relaxedFpRelTol && normAdjURes[2] < relaxedFpRelTol && normAdjPRes < relaxedFpRelTol && normAdjPhiRes < relaxedFpRelTol && normAdjNuTildaRes < relaxedFpRelTol)
914  {
915  Info << "Relaxed residual drop of " << relaxedFpRelTol << " has been achieved! Execution Time: " << meshPtr_->time().elapsedCpuTime() << " s" << endl;
916  Info << "Adjoint is still considered succussful.." << endl;
917  adjConv = 0;
918  }
919  else
920  {
921  Info << "Relaxed residual drop of " << relaxedFpRelTol << " has not been met! Execution Time: " << meshPtr_->time().elapsedCpuTime() << " s" << endl;
922  Info << "Adjoint has failed to converge.." << endl;
923  }
924  Info << "Normalized L2 norm of adjURes: " << normAdjURes[0] << " " << normAdjURes[1] << " " << normAdjURes[2] << endl;
925  Info << "Normalized L2 norm of adjPRes: " << normAdjPRes << endl;
926  Info << "Normalized L2 norm of adjPhiRes: " << normAdjPhiRes << endl;
927  Info << "Normalized L2 norm of adjNuTildaRes: " << normAdjNuTildaRes << endl;
928  }
929 
930  // converged, assign the field Psi to psiVec
931  this->vec2Fields("field2Vec", psi, UPsi, pPsi, phiPsi, nuTildaPsi);
932  }
933  else
934  {
935  FatalErrorIn("adjEqnSolMethod not valid") << exit(FatalError);
936  }
937 
938 #endif
939  return adjConv;
940 }
941 
943  const word mode,
944  Vec cVec,
945  volVectorField& UField,
946  volScalarField& pField,
947  surfaceScalarField& phiField,
948  volScalarField& nuTildaField)
949 {
950 #ifdef CODI_AD_REVERSE
951  PetscScalar* cVecArray;
952  if (mode == "vec2Field")
953  {
954  VecGetArray(cVec, &cVecArray);
955 
956  // U
957  forAll(meshPtr_->cells(), cellI)
958  {
959  for (label comp = 0; comp < 3; comp++)
960  {
961  label adjLocalIdx = daIndexPtr_->getLocalAdjointStateIndex("U", cellI, comp);
962  UField[cellI][comp] = cVecArray[adjLocalIdx];
963  }
964  }
965  // p
966  forAll(meshPtr_->cells(), cellI)
967  {
968  label adjLocalIdx = daIndexPtr_->getLocalAdjointStateIndex("p", cellI);
969  pField[cellI] = cVecArray[adjLocalIdx];
970  }
971  // phi
972  forAll(meshPtr_->faces(), faceI)
973  {
974  label adjLocalIdx = daIndexPtr_->getLocalAdjointStateIndex("phi", faceI);
975 
976  if (faceI < daIndexPtr_->nLocalInternalFaces)
977  {
978  phiField[faceI] = cVecArray[adjLocalIdx];
979  }
980  else
981  {
982  label relIdx = faceI - daIndexPtr_->nLocalInternalFaces;
983  label patchIdx = daIndexPtr_->bFacePatchI[relIdx];
984  label faceIdx = daIndexPtr_->bFaceFaceI[relIdx];
985  phiField.boundaryFieldRef()[patchIdx][faceIdx] = cVecArray[adjLocalIdx];
986  }
987  }
988  // nuTilda
989  forAll(meshPtr_->cells(), cellI)
990  {
991  label adjLocalIdx = daIndexPtr_->getLocalAdjointStateIndex("nuTilda", cellI);
992  nuTildaField[cellI] = cVecArray[adjLocalIdx];
993  }
994 
995  VecRestoreArray(cVec, &cVecArray);
996  }
997  else if (mode == "field2Vec")
998  {
999  VecGetArray(cVec, &cVecArray);
1000 
1001  // U
1002  forAll(meshPtr_->cells(), cellI)
1003  {
1004  for (label comp = 0; comp < 3; comp++)
1005  {
1006  label adjLocalIdx = daIndexPtr_->getLocalAdjointStateIndex("U", cellI, comp);
1007  cVecArray[adjLocalIdx] = UField[cellI][comp].value();
1008  }
1009  }
1010  // p
1011  forAll(meshPtr_->cells(), cellI)
1012  {
1013  label adjLocalIdx = daIndexPtr_->getLocalAdjointStateIndex("p", cellI);
1014  cVecArray[adjLocalIdx] = pField[cellI].value();
1015  }
1016  // phi
1017  forAll(meshPtr_->faces(), faceI)
1018  {
1019  label adjLocalIdx = daIndexPtr_->getLocalAdjointStateIndex("phi", faceI);
1020 
1021  if (faceI < daIndexPtr_->nLocalInternalFaces)
1022  {
1023  cVecArray[adjLocalIdx] = phiField[faceI].value();
1024  }
1025  else
1026  {
1027  label relIdx = faceI - daIndexPtr_->nLocalInternalFaces;
1028  label patchIdx = daIndexPtr_->bFacePatchI[relIdx];
1029  label faceIdx = daIndexPtr_->bFaceFaceI[relIdx];
1030  cVecArray[adjLocalIdx] = phiField.boundaryFieldRef()[patchIdx][faceIdx].value();
1031  }
1032  }
1033  // nuTilda
1034  forAll(meshPtr_->cells(), cellI)
1035  {
1036  label adjLocalIdx = daIndexPtr_->getLocalAdjointStateIndex("nuTilda", cellI);
1037  cVecArray[adjLocalIdx] = nuTildaField[cellI].value();
1038  }
1039 
1040  VecRestoreArray(cVec, &cVecArray);
1041  }
1042  else
1043  {
1044  FatalErrorIn("mode not valid") << exit(FatalError);
1045  }
1046 #endif
1047 }
1048 
1050  const volVectorField& mySource,
1051  volVectorField& pseudoU)
1052 {
1053  /*
1054  Description:
1055  Inverse transpose product, MU^(-T)
1056  Based on inverseProduct_UEqn from simpleFoamPrimal, but swaping upper() and lower()
1057  We won't ADR this function, so we can treat most of the arguments as const
1058  */
1059 
1060 /*
1061  const objectRegistry& db = meshPtr_->thisDb();
1062  const surfaceScalarField& phi = db.lookupObject<surfaceScalarField>("phi");
1063  volScalarField nuEff = daTurbulenceModelPtr_->nuEff();
1064 
1065  // Get the pseudoUEqn,
1066  // the most important thing here is to make sure the l.h.s. matches that of UEqn.
1067  fvVectorMatrix pseudoUEqn(
1068  fvm::div(phi, pseudoU, "div(phi,U)")
1069  - fvm::laplacian(nuEff, pseudoU)
1070  - fvc::div(nuEff * dev2(T(fvc::grad(pseudoU))), "div((nuEff*dev2(T(grad(U)))))"));
1071  pseudoUEqn.relax(relaxUEqn_);
1072 
1073  // Swap upper() and lower()
1074  List<scalar> temp = pseudoUEqn.upper();
1075  pseudoUEqn.upper() = pseudoUEqn.lower();
1076  pseudoUEqn.lower() = temp;
1077 
1078  // Overwrite the r.h.s.
1079  pseudoUEqn.source() = mySource;
1080 
1081  // Make sure that boundary contribution to source is zero,
1082  // Alternatively, we can deduct source by boundary contribution, so that it would cancel out during solve.
1083  forAll(pseudoU.boundaryField(), patchI)
1084  {
1085  const fvPatch& pp = pseudoU.boundaryField()[patchI].patch();
1086  forAll(pp, faceI)
1087  {
1088  label cellI = pp.faceCells()[faceI];
1089  pseudoUEqn.source()[cellI] -= pseudoUEqn.boundaryCoeffs()[patchI][faceI];
1090  }
1091  }
1092 
1093  // Before solve, force xEqn.psi() to be solved into all zero
1094  forAll(pseudoU.primitiveFieldRef(), cellI)
1095  {
1096  pseudoU.primitiveFieldRef()[cellI][0] = 0;
1097  pseudoU.primitiveFieldRef()[cellI][1] = 0;
1098  pseudoU.primitiveFieldRef()[cellI][2] = 0;
1099  }
1100 
1101  pseudoUEqn.solve(solverDictU_);
1102 */
1103 }
1104 
1106  const volScalarField& mySource,
1107  volScalarField& pseudoP)
1108 {
1109  /*
1110  Description:
1111  Inverse transpose product, Mp^(-T)
1112  Based on inverseProduct_pEqn from simpleFoamPrimal, but swaping upper() and lower()
1113  We won't ADR this function, so we can treat most of the arguments as const
1114  */
1115 
1116 /*
1117  const objectRegistry& db = meshPtr_->thisDb();
1118  const volVectorField& U = db.lookupObject<volVectorField>("U");
1119  const surfaceScalarField& phi = db.lookupObject<surfaceScalarField>("phi");
1120  volScalarField nuEff = daTurbulenceModelPtr_->nuEff();
1121 
1122  // Construct UEqn first
1123  fvVectorMatrix UEqn(
1124  fvm::div(phi, U)
1125  - fvm::laplacian(nuEff, U)
1126  - fvc::div(nuEff * dev2(T(fvc::grad(U)))));
1127  // Without this, pRes would be way off.
1128  UEqn.relax();
1129 
1130  // create a scalar field with 1/A, reverse of A() of U
1131  volScalarField rAU(1.0 / UEqn.A());
1132 
1133  // Get the pseudoPEqn,
1134  // the most important thing here is to make sure the l.h.s. matches that of pEqn.
1135  fvScalarMatrix pseudoPEqn(fvm::laplacian(rAU, pseudoP));
1136 
1137  // Swap upper() and lower()
1138  List<scalar> temp = pseudoPEqn.upper();
1139  pseudoPEqn.upper() = pseudoPEqn.lower();
1140  pseudoPEqn.lower() = temp;
1141 
1142  // Overwrite the r.h.s.
1143  pseudoPEqn.source() = mySource;
1144 
1145  // pEqn.setReference(pRefCell, pRefValue);
1146  // Here, pRefCell is a label, and pRefValue is a scalar
1147  // In actual implementation, they need to passed into this function.
1148  pseudoPEqn.setReference(0, 0.0);
1149 
1150  // Make sure that boundary contribution to source is zero,
1151  // Alternatively, we can deduct source by boundary contribution, so that it would cancel out during solve.
1152  forAll(pseudoP.boundaryField(), patchI)
1153  {
1154  const fvPatch& pp = pseudoP.boundaryField()[patchI].patch();
1155  forAll(pp, faceI)
1156  {
1157  label cellI = pp.faceCells()[faceI];
1158  pseudoPEqn.source()[cellI] -= pseudoPEqn.boundaryCoeffs()[patchI][faceI];
1159  }
1160  }
1161 
1162  // Before solve, force xEqn.psi() to be solved into all zero
1163  forAll(pseudoP.primitiveFieldRef(), cellI)
1164  {
1165  pseudoP.primitiveFieldRef()[cellI] = 0;
1166  }
1167 
1168  pseudoPEqn.solve(solverDictP_);
1169 */
1170 }
1171 
1173  volVectorField& URes,
1174  volScalarField& pRes,
1175  surfaceScalarField& phiRes)
1176 {
1177  const objectRegistry& db = meshPtr_->thisDb();
1178  const volVectorField& U = db.lookupObject<volVectorField>("U");
1179  const volScalarField& p = db.lookupObject<volScalarField>("p");
1180  const surfaceScalarField& phi = db.lookupObject<surfaceScalarField>("phi");
1181  volScalarField nuEff = daTurbulenceModelPtr_->nuEff();
1182 
1183  fvVectorMatrix UEqn(
1184  fvm::div(phi, U)
1185  - fvm::laplacian(nuEff, U)
1186  - fvc::div(nuEff * dev2(T(fvc::grad(U))))); //This term is needed in res though...
1187 
1188  List<vector>& USource = UEqn.source();
1189  // Note we cannot use UEqn.D() here, because boundary contribution to diag have 3 components, and they can be different.
1190  // Thus we use UEqn.diag() here, and we correct both source and diag later.
1191  List<scalar>& UDiag = UEqn.diag();
1192 
1193  // Get fvc::grad(p), so that it can be added to r.h.s.
1194  volVectorField gradP(fvc::grad(p));
1195 
1196  // Initiate URes, with no boundary contribution
1197  for (label i = 0; i < U.size(); i++)
1198  {
1199  URes[i] = UDiag[i] * U[i] - USource[i] + U.mesh().V()[i] * gradP[i];
1200  }
1201  URes.primitiveFieldRef() -= UEqn.lduMatrix::H(U);
1202 
1203  // Add boundary contribution to source and diag
1204  forAll(U.boundaryField(), patchI)
1205  {
1206  const fvPatch& pp = U.boundaryField()[patchI].patch();
1207  forAll(pp, faceI)
1208  {
1209  // Both ways of getting cellI work
1210  // Below is the previous way of getting the address
1211  label cellI = pp.faceCells()[faceI];
1212  // Below is using lduAddr().patchAddr(patchi)
1213  //label cellI = UEqn.lduAddr().patchAddr(patchI)[faceI];
1214  for (label cmpt = 0; cmpt < 3; cmpt++)
1215  {
1216  URes[cellI][cmpt] += UEqn.internalCoeffs()[patchI][faceI][cmpt] * U[cellI][cmpt];
1217  }
1218  //Info << "UEqn.internalCoeffs()[" << patchI << "][" << faceI <<"]= " << UEqn.internalCoeffs()[patchI][faceI] <<endl;
1219  URes[cellI] -= UEqn.boundaryCoeffs()[patchI][faceI];
1220  }
1221  }
1222 
1223  // Below is not necessary, but it doesn't hurt
1224  URes.correctBoundaryConditions();
1225 
1226  UEqn.relax(); // Without this, pRes would be way off.
1227 
1228  volScalarField rAU(1.0 / UEqn.A()); // create a scalar field with 1/A, reverse of A() of U
1229  volVectorField HbyA("HbyA", U); // initialize a vector field with U and pass it to HbyA
1230  HbyA = rAU * UEqn.H(); // basically, HbyA = 1/A * H, H_by_A, need to verify source code though...
1231  surfaceScalarField phiHbyA("phiHbyA", fvc::flux(HbyA)); // get the flux of HbyA, phi_H_by_A
1232 
1233  fvScalarMatrix pEqn(
1234  fvm::laplacian(rAU, p) == fvc::div(phiHbyA));
1235 
1236  List<scalar>& pSource = pEqn.source();
1237  List<scalar>& pDiag = pEqn.diag();
1238 
1239  // Initiate pRes, with no boundary contribution
1240  for (label i = 0; i < p.size(); i++)
1241  {
1242  pRes[i] = pDiag[i] * p[i] - pSource[i];
1243  }
1244  pRes.primitiveFieldRef() -= pEqn.lduMatrix::H(p);
1245 
1246  // Boundary correction
1247  forAll(p.boundaryField(), patchI)
1248  {
1249  const fvPatch& pp = p.boundaryField()[patchI].patch();
1250  forAll(pp, faceI)
1251  {
1252  // Both ways of getting cellI work
1253  // Below is the previous way of getting the address
1254  label cellI = pp.faceCells()[faceI];
1255  // Below is using lduAddr().patchAddr(patchi)
1256  //label cellI = pEqn.lduAddr().patchAddr(patchI)[faceI];
1257  //myDiag[cellI] += TEqn.internalCoeffs()[patchI][faceI];
1258  pRes[cellI] += pEqn.internalCoeffs()[patchI][faceI] * p[cellI];
1259  pRes[cellI] -= pEqn.boundaryCoeffs()[patchI][faceI];
1260  }
1261  }
1262 
1263  // Below is not necessary, but it doesn't hurt
1264  pRes.correctBoundaryConditions();
1265 
1266  // Then do phiRes
1267  // Note: DAFoam also uses this formula for phiRes
1268  phiRes = phiHbyA - pEqn.flux() - phi;
1269 }
1270 
1272  volVectorField& URes,
1273  volScalarField& pRes,
1274  surfaceScalarField& phiRes,
1275  volScalarField& nuTildaRes,
1276  Vec dFdW,
1277  volVectorField& UPsi,
1278  volScalarField& pPsi,
1279  surfaceScalarField& phiPsi,
1280  volScalarField& nuTildaPsi,
1281  volVectorField& adjURes,
1282  volScalarField& adjPRes,
1283  surfaceScalarField& adjPhiRes,
1284  volScalarField& adjNuTildaRes,
1285  label& cnt)
1286 {
1287 #ifdef CODI_AD_REVERSE
1288  volVectorField& U = const_cast<volVectorField&>(meshPtr_->thisDb().lookupObject<volVectorField>("U"));
1289  volScalarField& p = const_cast<volScalarField&>(meshPtr_->thisDb().lookupObject<volScalarField>("p"));
1290  volScalarField& nuTilda = const_cast<volScalarField&>(meshPtr_->thisDb().lookupObject<volScalarField>("nuTilda"));
1291  surfaceScalarField& phi = const_cast<surfaceScalarField&>(meshPtr_->thisDb().lookupObject<surfaceScalarField>("phi"));
1292 
1293  // Pass -dfdw to adjRes:
1294  this->vec2Fields("vec2Field", dFdW, adjURes, adjPRes, adjPhiRes, adjNuTildaRes);
1295  adjURes = -adjURes;
1296  adjPRes = -adjPRes;
1297  adjPhiRes = -adjPhiRes;
1298  adjNuTildaRes = -adjNuTildaRes;
1299 
1300  if (cnt == 0)
1301  {
1302  this->globalADTape_.reset();
1303  this->globalADTape_.setActive();
1304 
1305  // register all (3+1) state variables as input
1306  // Start with U, note that U has 3 components
1307  forAll(U, cellI)
1308  {
1309  this->globalADTape_.registerInput(U[cellI][0]);
1310  this->globalADTape_.registerInput(U[cellI][1]);
1311  this->globalADTape_.registerInput(U[cellI][2]);
1312  }
1313  // Now register p as input:
1314  forAll(p, cellI)
1315  {
1316  this->globalADTape_.registerInput(p[cellI]);
1317  }
1318  // Then, register phi as input
1319  // Note that both the internal field of phi and the boundary fields need to be registered
1320  // Register internal of phi:
1321  forAll(phi.primitiveFieldRef(), faceI)
1322  {
1323  this->globalADTape_.registerInput(phi.primitiveFieldRef()[faceI]);
1324  }
1325  // Register boundary of phi:
1326  forAll(phi.boundaryFieldRef(), patchI)
1327  {
1328  forAll(phi.boundaryFieldRef()[patchI], faceI)
1329  {
1330  this->globalADTape_.registerInput(phi.boundaryFieldRef()[patchI][faceI]);
1331  }
1332  }
1333  // And then, register turbulence variable nuTilda as input:
1334  forAll(nuTilda, cellI)
1335  {
1336  this->globalADTape_.registerInput(nuTilda[cellI]);
1337  }
1338 
1339  // Get tape position before calling function(s)
1340  adjResStart_ = this->globalADTape_.getPosition();
1341 
1342  // Correct boundaries to link the intermediate results
1343  U.correctBoundaryConditions();
1344  p.correctBoundaryConditions();
1345  nuTilda.correctBoundaryConditions();
1346 
1347  // Construct nuEff before calling lduCalcAllRes
1348  daTurbulenceModelPtr_->updateIntermediateVariables();
1349 
1350  // Call the residual functions
1351  this->calcLduResiduals(URes, pRes, phiRes);
1352  daTurbulenceModelPtr_->calcLduResidualTurb(nuTildaRes);
1353 
1354  // No registerOutput needed in positional tapes
1355  /*
1356  // register output
1357  forAll(URes, cellI)
1358  {
1359  tape.registerOutput(URes[cellI][0]);
1360  tape.registerOutput(URes[cellI][1]);
1361  tape.registerOutput(URes[cellI][2]);
1362  }
1363  forAll(pRes, cellI)
1364  {
1365  tape.registerOutput(pRes[cellI]);
1366  }
1367  forAll(phiRes.primitiveFieldRef(), faceI)
1368  {
1369  tape.registerOutput(phiRes[faceI]);
1370  }
1371  // Seed boundary of phiRes:
1372  forAll(phiRes.boundaryFieldRef(), patchI)
1373  {
1374  forAll(phiRes.boundaryFieldRef()[patchI], faceI)
1375  {
1376  tape.registerOutput(phiRes.boundaryFieldRef()[patchI][faceI]);
1377  }
1378  }
1379  forAll(nuTildaRes, cellI)
1380  {
1381  tape.registerOutput(nuTildaRes[cellI]);
1382  }
1383  */
1384 
1385  // Get tape position after calling function(s)
1386  adjResEnd_ = this->globalADTape_.getPosition();
1387 
1388  // stop recording
1389  this->globalADTape_.setPassive();
1390  }
1391 
1392  // set the AD seed to the output var
1393  // Start with URes, note that URes has 3 components
1394  forAll(URes, cellI)
1395  {
1396  URes[cellI][0].setGradient(UPsi[cellI][0].getValue());
1397  URes[cellI][1].setGradient(UPsi[cellI][1].getValue());
1398  URes[cellI][2].setGradient(UPsi[cellI][2].getValue());
1399  }
1400  // Now seed pRes:
1401  forAll(pRes, cellI)
1402  {
1403  pRes[cellI].setGradient(pPsi[cellI].getValue());
1404  }
1405  // Then, seed phiRes:
1406  // Seed internal of phiRes:
1407  forAll(phiRes.primitiveFieldRef(), faceI)
1408  {
1409  phiRes.primitiveFieldRef()[faceI].setGradient(phiPsi.primitiveFieldRef()[faceI].getValue());
1410  }
1411  // Seed boundary of phiRes:
1412  forAll(phiRes.boundaryFieldRef(), patchI)
1413  {
1414  forAll(phiRes.boundaryFieldRef()[patchI], faceI)
1415  {
1416  phiRes.boundaryFieldRef()[patchI][faceI].setGradient(phiPsi.boundaryFieldRef()[patchI][faceI].getValue());
1417  }
1418  }
1419  // And then, seed nuTildaRes:
1420  forAll(nuTildaRes, cellI)
1421  {
1422  nuTildaRes[cellI].setGradient(nuTildaPsi[cellI].getValue());
1423  }
1424 
1425  // evaluate the tape to compute the derivative of the seeded output wrt all the input
1426  this->globalADTape_.evaluate(adjResEnd_, adjResStart_);
1427  forAll(U, cellI)
1428  {
1429  adjURes[cellI][0] += U[cellI][0].getGradient();
1430  adjURes[cellI][1] += U[cellI][1].getGradient();
1431  adjURes[cellI][2] += U[cellI][2].getGradient();
1432  }
1433  forAll(p, cellI)
1434  {
1435  adjPRes[cellI] += p[cellI].getGradient();
1436  }
1437  forAll(phi.primitiveFieldRef(), faceI)
1438  {
1439  adjPhiRes.primitiveFieldRef()[faceI] += phi.primitiveFieldRef()[faceI].getGradient();
1440  }
1441  forAll(phi.boundaryFieldRef(), patchI)
1442  {
1443  forAll(phi.boundaryFieldRef()[patchI], faceI)
1444  {
1445  adjPhiRes.boundaryFieldRef()[patchI][faceI] += phi.boundaryFieldRef()[patchI][faceI].getGradient();
1446  }
1447  }
1448  forAll(nuTilda, cellI)
1449  {
1450  adjNuTildaRes[cellI] += nuTilda[cellI].getGradient();
1451  }
1452 
1453  // Clear adjoints for future Jacobian calculations
1454  this->globalADTape_.clearAdjoints();
1455 #endif
1456 }
1457 
1458 // L2 norm of a scalar list (or the primitive field of a volScalarField)
1459 // Scale off cell volumes
1460 scalar DASimpleFoam::L2norm(const volScalarField& v)
1461 {
1462  scalar L2normV = 0.0;
1463 
1464  forAll(v, cellI)
1465  {
1466  L2normV += sqr(v[cellI] / meshPtr_->V()[cellI]);
1467  }
1468  L2normV = sqrt(L2normV);
1469 
1470  return L2normV;
1471 }
1472 
1473 // Scale off cell volumes
1474 vector DASimpleFoam::L2norm(const volVectorField& U)
1475 {
1476  vector L2normU = vector::zero;
1477 
1478  forAll(U, cellI)
1479  {
1480  L2normU[0] += sqr(U[cellI][0] / meshPtr_->V()[cellI]);
1481  L2normU[1] += sqr(U[cellI][1] / meshPtr_->V()[cellI]);
1482  L2normU[2] += sqr(U[cellI][2] / meshPtr_->V()[cellI]);
1483  }
1484  L2normU[0] = sqrt(L2normU[0]);
1485  L2normU[1] = sqrt(L2normU[1]);
1486  L2normU[2] = sqrt(L2normU[2]);
1487 
1488  return L2normU;
1489 }
1490 
1491 // L2 norm of a surfaceScalarField
1492 scalar DASimpleFoam::L2norm(const surfaceScalarField& Phi)
1493 {
1494  scalar L2normPhi = 0.0;
1495 
1496  forAll(Phi.primitiveField(), faceI)
1497  {
1498  L2normPhi += sqr(Phi.primitiveField()[faceI]);
1499  }
1500  forAll(Phi.boundaryField(), patchI)
1501  {
1502  forAll(Phi.boundaryField()[patchI], faceI)
1503  {
1504  L2normPhi += sqr(Phi.boundaryField()[patchI][faceI]);
1505  }
1506  }
1507  L2normPhi = sqrt(L2normPhi);
1508 
1509  return L2normPhi;
1510 }
1511 
1512 // This function is for swapping the upper and lower of a xxEqn
1513 void DASimpleFoam::swap(List<scalar>& a, List<scalar>& b)
1514 {
1515  List<scalar> temp = a;
1516  a = b;
1517  b = temp;
1518 }
1519 
1520 } // End namespace Foam
1521 
1522 // ************************************************************************* //
Foam::DASolver::writeFailedMesh
void writeFailedMesh()
write the failed mesh to disk
Definition: DASolver.C:7955
Foam::DASimpleFoam::DASimpleFoam
DASimpleFoam(char *argsAll, PyObject *pyOptions)
Definition: DASimpleFoam.C:41
Foam::DASimpleFoam::relaxUEqn_
scalar relaxUEqn_
fvSolution parameters for fixed-point adjoint
Definition: DASimpleFoam.H:120
Foam::DASolver::updateOFField
void updateOFField(const Vec wVec)
Update the OpenFOAM field values (including both internal and boundary fields) based on the state vec...
Definition: DASolver.C:4765
Foam::DASimpleFoam::swap
void swap(List< scalar > &a, List< scalar > &b)
Definition: DASimpleFoam.C:1513
allOptions
const dictionary & allOptions
Definition: createRefsRhoSimpleC.H:15
Foam::DASolver::loop
label loop(Time &runTime)
return whether to loop the primal solution, similar to runTime::loop() except we don't do file IO
Definition: DASolver.C:112
U
U
Definition: pEqnPimpleDyM.H:60
Foam::DASolver::daCheckMeshPtr_
autoPtr< DACheckMesh > daCheckMeshPtr_
DACheckMesh object pointer.
Definition: DASolver.H:91
pseudoP
volScalarField pseudoP("pseudoP", p)
forAll
forAll(pseudoP.boundaryField(), patchI)
Definition: solvePseudoPEqn.H:10
Foam::DASolver::daModelPtr_
autoPtr< DAModel > daModelPtr_
DAModel pointer.
Definition: DASolver.H:79
Foam::DASimpleFoam::invTranProdUEqn
void invTranProdUEqn(const volVectorField &mySource, volVectorField &pseudoU)
Inverse transpose product, MU^(-T)
Definition: DASimpleFoam.C:1049
Foam::DACheckMesh
Definition: DACheckMesh.H:29
Foam::DASimpleFoam::runFPAdj
virtual label runFPAdj(const Vec xvVec, const Vec wVec, Vec dFdW, Vec psi)
solve the adjoint equation using the fixed-point iteration method
Definition: DASimpleFoam.C:215
Foam::DASolver::daIndexPtr_
autoPtr< DAIndex > daIndexPtr_
DAIndex pointer.
Definition: DASolver.H:82
setForwardADSeeds.H
Foam::DASimpleFoam::solverDictU_
dictionary solverDictU_
Definition: DASimpleFoam.H:121
Foam::DASimpleFoam::daTurbulenceModelPtr_
autoPtr< DATurbulenceModel > daTurbulenceModelPtr_
DATurbulenceModel pointer.
Definition: DASimpleFoam.H:81
Foam::DASolver
Definition: DASolver.H:49
pseudoU
volVectorField pseudoU("pseudoU", U)
UEqn
fvVectorMatrix & UEqn
Definition: UEqnPimpleDyM.H:13
laminarTransport
singlePhaseTransportModel & laminarTransport
Definition: createRefsPimple.H:9
pseudoNuTilda
volScalarField & pseudoNuTilda
Definition: pseudoEqns.H:34
Foam::DASolver::daOptionPtr_
autoPtr< DAOption > daOptionPtr_
DAOption pointer.
Definition: DASolver.H:76
DASimpleFoam.H
HbyA
HbyA
Definition: pEqnRhoSimpleC.H:12
p
volScalarField & p
Definition: createRefsPimple.H:6
Foam::DASolver::calcPrimalResidualStatistics
void calcPrimalResidualStatistics(const word mode, const label writeRes=0)
calculate the norms of all residuals and print to screen
Definition: DASolver.C:2559
Foam::DASimpleFoam::hasFvSource_
label hasFvSource_
whether to have fvSource term
Definition: DASimpleFoam.H:93
Foam::DASimpleFoam::initSolver
virtual void initSolver()
initialize fields and variables
Definition: DASimpleFoam.C:74
mesh
fvMesh & mesh
Definition: createRefsHeatTransfer.H:4
Foam::DASimpleFoam::solverDictP_
dictionary solverDictP_
Definition: DASimpleFoam.H:122
Foam::DASolver::isPrintTime
label isPrintTime(const Time &runTime, const label printInterval) const
Definition: DASolver.C:7460
phi
surfaceScalarField & phi
Definition: createRefsPimple.H:8
Foam::DASimpleFoam::daFvSourcePtr_
autoPtr< DAFvSource > daFvSourcePtr_
DASource pointer.
Definition: DASimpleFoam.H:84
Foam::DASolver::daLinearEqnPtr_
autoPtr< DALinearEqn > daLinearEqnPtr_
DALinearEqn pointer.
Definition: DASolver.H:94
phiHbyA
phiHbyA
Definition: pEqnRhoSimpleC.H:27
createFieldsSimple.H
Foam::DASolver::updateOFMesh
void updateOFMesh(const Vec xvVec)
Update the OpenFoam mesh point coordinates based on the point vector xvVec.
Definition: DASolver.C:4823
createSimpleControlPython.H
Foam::DAFvSource::New
static autoPtr< DAFvSource > New(const word modelType, const fvMesh &mesh, const DAOption &daOption, const DAModel &daModel, const DAIndex &daIndex)
Definition: DAFvSource.C:47
Foam::DASimpleFoam::L2norm
scalar L2norm(const volScalarField &v)
Definition: DASimpleFoam.C:1460
Foam
Definition: multiFreqScalarFvPatchField.C:144
Foam::DASolver::setDAObjFuncList
void setDAObjFuncList()
initialize DASolver::daObjFuncPtrList_ one needs to call this before calling printAllObjFuncs
Definition: DASolver.C:235
T
volScalarField & T
Definition: createRefsHeatTransfer.H:5
Foam::DASolver::writeAssociatedFields
void writeAssociatedFields()
write associated fields such as relative velocity
Definition: DASolver.C:7478
UDiag
List< scalar > UDiag
Definition: pseudoEqns.H:18
Foam::DASolver::printAllObjFuncs
void printAllObjFuncs()
calculate the values of all objective functions and print them to screen
Definition: DASolver.C:160
pseudoEqns.H
solvePseudoUEqn.H
Foam::DASimpleFoam::calcLduResiduals
void calcLduResiduals(volVectorField &URes, volScalarField &pRes, surfaceScalarField &phiRes)
calculate all the flow residuals except for the turbulence one
Definition: DASimpleFoam.C:1172
Foam::DASolver::primalMinRes_
scalar primalMinRes_
the maximal residual for primal solution
Definition: DASolver.H:139
Foam::DASimpleFoam::invTranProdPEqn
void invTranProdPEqn(const volScalarField &mySource, volScalarField &pseudoP)
Inverse transpose product, Mp^(-T)
Definition: DASimpleFoam.C:1105
Foam::DASolver::runTimePtr_
autoPtr< Time > runTimePtr_
runTime pointer
Definition: DASolver.H:70
Foam::DASolver::meshPtr_
autoPtr< fvMesh > meshPtr_
fvMesh pointer
Definition: DASolver.H:73
solvePseudoPEqn.H
Foam::defineTypeNameAndDebug
defineTypeNameAndDebug(DAFvSource, 0)
Foam::addToRunTimeSelectionTable
addToRunTimeSelectionTable(DAFvSource, DAFvSourceActuatorDisk, dictionary)
Foam::DASolver::ofField2StateVec
void ofField2StateVec(Vec stateVec) const
set the state vector based on the latest fields in OpenFOAM
Definition: DASolver.H:596
rAU
volScalarField rAU(1.0/UEqn.A())
Foam::DASolver::checkMesh
label checkMesh() const
check the mesh quality and return meshOK
Definition: DASolver.H:710
Foam::DASolver::checkResidualTol
label checkResidualTol()
check whether the min residual in primal satisfy the prescribed tolerance
Definition: DASolver.C:7432
Foam::DALinearEqn
Definition: DALinearEqn.H:31
createRefsSimple.H
UEqnSimple.H
Foam::DASimpleFoam::vec2Fields
void vec2Fields(const word mode, Vec cVec, volVectorField &UField, volScalarField &pField, surfaceScalarField &phiField, volScalarField &nuTildaField)
assign coupled petsc vec to fields or vice versa
Definition: DASimpleFoam.C:942
Foam::DASimpleFoam::solvePrimal
virtual label solvePrimal(const Vec xvVec, Vec wVec)
solve the primal equations
Definition: DASimpleFoam.C:107
Foam::DASimpleFoam::calcAdjointResidual
void calcAdjointResidual(volVectorField &URes, volScalarField &pRes, surfaceScalarField &phiRes, volScalarField &nuTildaRes, Vec dFdW, volVectorField &UPsi, volScalarField &pPsi, surfaceScalarField &phiPsi, volScalarField &nuTildaPsi, volVectorField &adjURes, volScalarField &adjPRes, surfaceScalarField &adjPhiRes, volScalarField &adjNuTildaRes, label &cnt)
Definition: DASimpleFoam.C:1271
runTime
Time & runTime
Definition: createRefsHeatTransfer.H:1
Foam::DASolver::pointVec2OFMesh
void pointVec2OFMesh(const Vec xvVec) const
assign the points in fvMesh of OpenFOAM based on the point vector
Definition: DASolver.H:608
pseudoUEqn
fvVectorMatrix pseudoUEqn(fvm::div(phi, pseudoU, divScheme) - fvm::laplacian(nuEff, pseudoU))
createAdjointIncompressible.H
pEqnSimple.H
psi
const volScalarField & psi
Definition: createRefsRhoSimpleC.H:14
dafoam_plot3dtransform.mode
mode
Definition: dafoam_plot3dtransform.py:21