incompressibleAdjointSolver.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) 2007-2023 PCOpt/NTUA
9  Copyright (C) 2013-2023 FOSS GP
10  Copyright (C) 2019-2021 OpenCFD Ltd.
11 -------------------------------------------------------------------------------
12 License
13  This file is part of OpenFOAM.
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 
32 #include "wallFvPatch.H"
33 #include "sensitivityTopO.H"
37 
38 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
39 
40 namespace Foam
41 {
42  defineTypeNameAndDebug(incompressibleAdjointSolver, 0);
43  defineRunTimeSelectionTable(incompressibleAdjointSolver, dictionary);
45  (
46  adjointSolver,
47  incompressibleAdjointSolver,
48  adjointSolver
49  );
50 }
51 
52 
53 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
54 
56 (
57  const labelHashSet& sensitivityPatchIDs
58 )
59 {
60  if (hasBCdxdbMult_.bad())
61  {
62  const volVectorField& Ua = getAdjointVars().Ua();
63  hasBCdxdbMult_ = false;
64  for (const label patchI : sensitivityPatchIDs)
65  {
66  const fvPatchVectorField& Uab = Ua.boundaryField()[patchI];
67  if (isA<adjointVectorBoundaryCondition>(Uab))
68  {
70  refCast<adjointVectorBoundaryCondition>
71  (const_cast<fvPatchVectorField&>(Uab));
72  tmp<tensorField> dxdbMult = abc.dxdbMult();
73  if (dxdbMult)
74  {
75  hasBCdxdbMult_ = true;
76  break;
77  }
78  }
79  }
80  }
82 }
83 
84 
85 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
86 
87 Foam::incompressibleAdjointSolver::incompressibleAdjointSolver
88 (
89  fvMesh& mesh,
90  const word& managerType,
91  const dictionary& dict,
92  const word& primalSolverName,
93  const word& solverName
94 )
95 :
96  adjointSolver(mesh, managerType, dict, primalSolverName, solverName),
97  primalVars_
98  (
99  mesh.lookupObjectRef<incompressiblePrimalSolver>(primalSolverName).
100  getIncoVars()
101  ),
102  ATCModel_(nullptr),
103  hasBCdxdbMult_(Switch::INVALID)
104 {}
105 
106 
107 // * * * * * * * * * * * * * * * * * Selectors * * * * * * * * * * * * * * * //
108 
111 (
112  fvMesh& mesh,
113  const word& managerType,
114  const dictionary& dict,
115  const word& primalSolverName,
116  const word& solverName
117 )
118 {
119  const word solverType(dict.get<word>("solver"));
120  auto* ctorPtr = dictionaryConstructorTable(solverType);
121 
122  if (!ctorPtr)
123  {
125  (
126  dict,
127  "incompressibleAdjointSolver",
128  solverType,
129  *dictionaryConstructorTablePtr_
130  ) << exit(FatalIOError);
131  }
132 
133  return
134  autoPtr<incompressibleAdjointSolver>
135  (
136  ctorPtr(mesh, managerType, dict, primalSolverName, solverName)
137  );
138 }
139 
140 
141 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
142 
145 {
146  return primalVars_;
147 }
148 
149 
150 
153 {
154  const incompressibleAdjointVars& adjointVars =
155  refCast<incompressibleAdjointVars>(const_cast<variablesSet&>(vars_()));
156  return adjointVars;
157 }
158 
159 
162 {
163  incompressibleAdjointVars& adjointVars =
164  refCast<incompressibleAdjointVars>(const_cast<variablesSet&>(vars_()));
165  return adjointVars;
166 }
167 
168 
169 
172 {
173  return ATCModel_;
174 }
175 
178 {
179  return getAdjointVars().adjointTurbulence()->includeDistance();
180 }
181 
184 {
185  return sqr(dimLength)/pow3(dimTime);
186 }
187 
188 
190 {
191  return pow3(dimLength/dimTime);
192 }
193 
194 
197 {
198  return getAdjointVars().adjointTurbulence()->distanceSensitivities();
199 }
200 
201 
204 {
205  return getPrimalVars().RASModelVariables()->d();
206 }
207 
210 {
211  return ATCModel_;
212 }
213 
214 
216 {
217  if (vars_)
218  {
219  getAdjointVars().adjointTurbulence()->setChangedPrimalSolution();
220  ATCModel_().updatePrimalBasedQuantities();
221  getAdjointVars().updatePrimalBasedQuantities();
222  }
223 }
224 
225 
227 (
228  volTensorField& gradDxDbMult,
229  const scalar dt
230 )
231 {
232  /*
233  addProfiling
234  (
235  incompressibleAdjointSolver,
236  "incompressibleAdjointSolver::accumulateGradDxDbMultiplier"
237  );
238  */
239  autoPtr<incompressibleAdjoint::adjointRASModel>& adjointRAS
240  (
241  getAdjointVars().adjointTurbulence()
242  );
243 
244  const volScalarField& p = primalVars_.p();
245  const volVectorField& U = primalVars_.U();
246  const volScalarField& pa = getAdjointVars().pa();
247  const volVectorField& Ua = getAdjointVars().Ua();
248 
249  // We only need to modify the boundaryField of gradU locally.
250  // If grad(U) is cached then
251  // a. The .ref() call fails since the tmp is initialised from a
252  // const ref
253  // b. we would be changing grad(U) for all other places in the code
254  // that need it
255  // So, always allocate new memory and avoid registering the new field
256  tmp<volTensorField> tgradU =
257  volTensorField::New("gradULocal", fvc::grad(U));
258  volTensorField& gradU = tgradU.ref();
259  volTensorField::Boundary& gradUbf = gradU.boundaryFieldRef();
260 
261  // Explicitly correct the boundary gradient to get rid of
262  // the tangential component
263  forAll(mesh_.boundary(), patchI)
264  {
265  const fvPatch& patch = mesh_.boundary()[patchI];
266  if (isA<wallFvPatch>(patch))
267  {
268  tmp<vectorField> tnf = mesh_.boundary()[patchI].nf();
269  gradUbf[patchI] = tnf*U.boundaryField()[patchI].snGrad();
270  }
271  }
272 
273  tmp<volScalarField> tnuEff = adjointRAS->nuEff();
274  tmp<volSymmTensorField> stress = tnuEff()*twoSymm(gradU);
275  tmp<volTensorField> tgradUa = fvc::grad(Ua);
276 
277  // Return field
278  auto tflowTerm
279  (
281  (
282  IOobject
283  (
284  "flowTerm",
285  mesh_.time().timeName(),
286  mesh_,
289  ),
290  mesh_,
293  )
294  );
295  volTensorField& flowTerm = tflowTerm.ref();
296  // Term 3
297  flowTerm = - tnuEff*(gradU & twoSymm(tgradUa()));
298  // Term 4
299  flowTerm += fvc::grad(Ua & stress()) - (tgradUa & stress());
300 
301  // Release memory
302  stress.clear();
303 
304  // Compute dxdb multiplier
305  flowTerm +=
306  // Term 1, ATC
307  ATCModel_->getFISensitivityTerm()
308  // Term 2
309  - fvc::grad(p)*Ua;
310 
311  // Term 5
312  flowTerm += pa*tgradU;
313 
314  // Term 6, from the adjoint turbulence model
315  flowTerm += T(adjointRAS->FISensitivityTerm());
316 
317  // Term 7, term from objective functions
318  PtrList<objective>& functions = objectiveManager_.getObjectiveFunctions();
319 
320  for (objective& objI : functions)
321  {
322  if (objI.hasGradDxDbMult())
323  {
324  flowTerm += objI.weight()*objI.gradDxDbMultiplier();
325  }
326  }
327 
328  flowTerm.correctBoundaryConditions();
330  gradDxDbMult += flowTerm.T()*dt;
331  //profiling::writeNow();
332 }
333 
334 
336 (
337  autoPtr<scalarField>& divDxDbMult,
338  const scalar dt
339 )
340 {
341  PtrList<objective>& functions = objectiveManager_.getObjectiveFunctions();
342  for (objective& func : functions)
343  {
344  if (func.hasDivDxDbMult())
345  {
346  divDxDbMult() +=
347  func.weight()*func.divDxDbMultiplier().primitiveField()*dt;
348  }
349  }
350 }
351 
352 
354 (
355  autoPtr<boundaryVectorField>& dSfdbMult,
356  autoPtr<boundaryVectorField>& dnfdbMult,
357  autoPtr<boundaryVectorField>& dxdbDirectMult,
358  autoPtr<pointBoundaryVectorField>& pointDxDbDirectMult,
359  const labelHashSet& sensitivityPatchIDs,
360  const scalar dt
361 )
362 {
363  PtrList<objective>& functions = objectiveManager_.getObjectiveFunctions();
364  for (const label patchI : sensitivityPatchIDs)
365  {
366  const scalarField magSfDt(mesh_.boundary()[patchI].magSf()*dt);
367  for (objective& func : functions)
368  {
369  const scalar wei = func.weight();
370  if (func.hasdSdbMult())
371  {
372  dSfdbMult()[patchI] += wei*func.dSdbMultiplier(patchI)*dt;
373  }
374  if (func.hasdndbMult())
375  {
376  dnfdbMult()[patchI] += wei*func.dndbMultiplier(patchI)*magSfDt;
377  }
378  if (func.hasdxdbDirectMult())
379  {
380  dxdbDirectMult()[patchI] +=
381  wei*func.dxdbDirectMultiplier(patchI)*magSfDt;
382  }
383  }
384  }
385 }
386 
387 
389 (
390  autoPtr<boundaryVectorField>& bcDxDbMult,
391  const labelHashSet& sensitivityPatchIDs,
392  const scalar dt
393 )
394 {
395  if (!hasBCdxdbMult(sensitivityPatchIDs))
396  {
397  return;
398  }
399 
400  // Grab references
401  const volVectorField& Ua = getAdjointVars().Ua();
402  const autoPtr<incompressibleAdjoint::adjointRASModel>& adjointTurbulence =
403  getAdjointVars().adjointTurbulence();
404 
405  // Fields needed to calculate adjoint sensitivities
406  const autoPtr<incompressible::RASModelVariables>&
407  turbVars = primalVars_.RASModelVariables();
408  const singlePhaseTransportModel& lamTransp = primalVars_.laminarTransport();
409  volScalarField nuEff(lamTransp.nu() + turbVars->nut());
410  tmp<volTensorField> tgradUa = fvc::grad(Ua);
411  const volTensorField::Boundary& gradUabf = tgradUa.cref().boundaryField();
412 
413  // Avoid updating the event number to keep consistency with cases caching
414  // gradUa
415  auto& UaBoundary = getAdjointVars().Ua().boundaryFieldRef(false);
416  auto& nuEffBoundary = nuEff.boundaryField();
417 
418  for (const label patchI : sensitivityPatchIDs)
419  {
420  fvPatchVectorField& Uab = UaBoundary[patchI];
421  if (isA<adjointVectorBoundaryCondition>(Uab))
422  {
423  const fvPatch& patch = mesh_.boundary()[patchI];
424  tmp<vectorField> tnf = patch.nf();
425  const scalarField& magSf = patch.magSf();
426 
427  tmp<vectorField> DvDbMult =
428  nuEffBoundary[patchI]*(Uab.snGrad() + (gradUabf[patchI] & tnf))
429  // - (nf*pa.boundaryField()[patchI])
430  + adjointTurbulence().adjointMomentumBCSource()[patchI];
431  bcDxDbMult()[patchI] +=
432  (
433  DvDbMult
434  & refCast<adjointVectorBoundaryCondition>(Uab).dxdbMult()
435  )*magSf*dt;
436  }
437  }
438 }
439 
440 
442 (
443  vectorField& optionsDxDbMult,
444  const scalar dt
445 )
446 {
447  // Terms from fvOptions - missing contributions from turbulence models
448  const incompressibleAdjointVars& av = getAdjointVars();
449  vectorField temp(mesh_.nCells(), Zero);
450  fv::options::New(this->mesh_).postProcessSens
451  (
452  temp, av.UaInst().name(), av.solverName()
453  );
454  optionsDxDbMult += temp*dt;
455  temp = Zero;
456  fv::options::New(this->mesh_).postProcessSens
457  (
458  temp, av.paInst().name(), av.solverName()
459  );
460  optionsDxDbMult += temp*dt;
461 }
462 
463 
465 (
466  scalarField& betaMult,
467  const word& designVariablesName,
468  const scalar dt
469 )
470 {
471  const incompressibleAdjointVars& adjointVars = getAdjointVars();
472  const volVectorField& U = primalVars_.U();
473  const volVectorField& Ua = adjointVars.Ua();
474  const autoPtr<incompressibleAdjoint::adjointRASModel>& adjointTurbulence =
475  adjointVars.adjointTurbulence();
477 
478  // Term from the momentum equations
479  scalarField momSens((U.primitiveField() & Ua.primitiveField())*dt);
481  (betaMult, momSens, fvOptions, U.name(), designVariablesName);
482  if (debug > 2)
483  {
484  volScalarField IvSens
485  (
486  IOobject
487  (
488  "IvSens" + solverName(),
489  mesh_.time().timeName(),
490  mesh_,
493  ),
494  mesh_,
496  );
497  IvSens.primitiveFieldRef() = momSens;
498  IvSens.write();
499  }
500 
501  // Term from the turbulence model.
502  // Includes already the derivative of the interpolation function
503  betaMult +=
504  (adjointTurbulence->topologySensitivities(designVariablesName))*dt;
505 
506  // Terms resulting directly from the objective function
507  PtrList<objective>& functions = objectiveManager_.getObjectiveFunctions();
508  for (objective& objI : functions)
509  {
510  const scalar weight(objI.weight());
511  if (objI.hasdJdb())
512  {
513  betaMult += weight*objI.dJdb()*dt;
514  }
515 
516  if (objI.hasdJdbField())
517  {
518  SubField<scalar> betaSens(objI.dJdbField(), mesh_.nCells(), 0);
519  betaMult += weight*betaSens*dt;
520  }
521  }
522 }
523 
524 
525 // ************************************************************************* //
static const word & zeroGradientType() noexcept
The type name for zeroGradient patch fields.
Definition: fvPatchField.H:221
dictionary dict
tmp< GeometricField< typename outerProduct< vector, Type >::type, fvPatchField, volMesh >> grad(const GeometricField< Type, fvsPatchField, surfaceMesh > &ssf)
Definition: fvcGrad.C:47
bool hasBCdxdbMult(const labelHashSet &sensitivityPatchIDs)
Compute, if necessary, and return the hasBCdxdbMult_ bool.
fvPatchField< vector > fvPatchVectorField
const Internal::FieldType & primitiveField() const noexcept
Return a const-reference to the internal field values.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
bool bad() const noexcept
True if the Switch does not represent a valid enumeration.
Definition: Switch.H:287
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:129
virtual void accumulateDivDxDbMultiplier(autoPtr< scalarField > &divDxDbMult, const scalar dt)
Compute the multiplier for div(dxdb)
virtual const incompressibleAdjointVars & getAdjointVars() const
Access to the incompressible adjoint variables set.
virtual void accumulateGradDxDbMultiplier(volTensorField &gradDxDbMult, const scalar dt)
Compute the multiplier for grad(dxdb)
void postProcessSens(Field< Type > &sensField, const word &fieldName=word::null, const word &designVariablesName=word::null)
Post process sensitivity field related to the fvOption.
GeometricField< tensor, fvPatchField, volMesh > volTensorField
Definition: volFieldsFwd.H:86
dimensionedSymmTensor sqr(const dimensionedVector &dv)
const incompressibleVars & getPrimalVars() const
Access to the incompressible primal variables set.
virtual void updatePrimalBasedQuantities()
Update primal based quantities, e.g. the primal fields in adjoint turbulence models.
A simple wrapper around bool so that it can be read as a word: true/false, on/off, yes/no, any/none. Also accepts 0/1 as a string and shortcuts t/f, y/n.
Definition: Switch.H:77
Base class for adjoint solvers.
Definition: adjointSolver.H:53
GeometricBoundaryField< tensor, fvPatchField, volMesh > Boundary
Type of boundary fields.
Ignore writing from objectRegistry::writeObject()
const dimensionSet dimless
Dimensionless.
virtual bool includeDistance() const
Should the adjoint to the eikonal equation be solved.
T get(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Find and return a T. FatalIOError if not found, or if the number of tokens is incorrect.
Finite-volume options.
Definition: fvOptions.H:51
GeometricField< vector, fvPatchField, volMesh > volVectorField
Definition: volFieldsFwd.H:82
Macros for easy insertion into run-time selection tables.
dimensionedSymmTensor twoSymm(const dimensionedSymmTensor &dt)
fv::options & fvOptions
Class including all adjoint fields for incompressible flows.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
GeometricField< scalar, fvPatchField, volMesh > volScalarField
Definition: volFieldsFwd.H:81
HashSet< label, Hash< label > > labelHashSet
A HashSet of labels, uses label hasher.
Definition: HashSet.H:85
Base class for solution control classes.
Dimension set for the base types, which can be used to implement rigorous dimension checking for alge...
Definition: dimensionSet.H:105
dynamicFvMesh & mesh
A class for handling words, derived from Foam::string.
Definition: word.H:63
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
virtual void accumulateBCSensitivityIntegrand(autoPtr< boundaryVectorField > &bcDxDbMult, const labelHashSet &sensitivityPatchIDs, const scalar dt)
Contributions from boundary functions that inlcude geometric aspects in them and change when the geom...
static autoPtr< incompressibleAdjointSolver > New(fvMesh &mesh, const word &managerType, const dictionary &dict, const word &primalSolverName, const word &solverName)
Return a reference to the selected incompressible adjoint solver.
virtual dimensionSet maDimensions() const
Return the dimensions of the adjoint grid displacement variable.
const volVectorField & Ua() const
Return const reference to velocity.
Switch hasBCdxdbMult_
Auxiliary bool to avoid a potentially expensive part of the sensitivity computation.
static tmp< T > New(Args &&... args)
Construct tmp with forwarding arguments.
Definition: tmp.H:206
const autoPtr< incompressibleAdjoint::adjointRASModel > & adjointTurbulence() const
Return const reference to the adjointRASModel.
Calculation of adjoint based sensitivities for topology optimisation. This returns just the field par...
dimensioned< tensor > dimensionedTensor
Dimensioned tensor obtained from generic dimensioned type.
static tmp< GeometricField< tensor, fvPatchField, volMesh > > New(const word &name, IOobjectOption::registerOption regOpt, const Mesh &mesh, const dimensionSet &dims, const word &patchFieldType=fvPatchField< tensor >::calculatedType())
Return tmp field (NO_READ, NO_WRITE) from name, mesh, dimensions and patch type. [Takes current timeN...
Abstract base class for objective functions. No point in making this runTime selectable since its chi...
Definition: objective.H:55
defineRunTimeSelectionTable(reactionRateFlameArea, dictionary)
int debug
Static debugging option.
defineTypeNameAndDebug(combustionModel, 0)
void T(FieldField< Field, Type > &f1, const FieldField< Field, Type > &f2)
const autoPtr< ATCModel > & getATCModel() const
Access to the ATC model.
static void postProcessSens(scalarField &sens, scalarField &auxSens, fv::options &fvOptions, const word &fieldName, const word &designVariablesName)
Add part of the sensitivities coming from fvOptions.
Base class for creating a set of variables.
Definition: variablesSet.H:43
dimensionedScalar pow3(const dimensionedScalar &ds)
Internal::FieldType & primitiveFieldRef(const bool updateAccessTime=true)
Return a reference to the internal field values.
U
Definition: pEqn.H:72
virtual tmp< scalarField > topologySensitivities(const word &designVarsName) const =0
Term contributing to the computation of topology optimisation sensitivities.
virtual tmp< volScalarField > yWall() const
Return the distance field, to be used in the solution of the adjoint eikonal PDE. ...
const dimensionSet dimLength(0, 1, 0, 0, 0, 0, 0)
Definition: dimensionSets.H:50
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
A list of pointers to objects of type <T>, with allocation/deallocation management of the pointers...
Definition: List.H:55
virtual void topOSensMultiplier(scalarField &betaMult, const word &designVariablesName, const scalar dt)
Compute the multiplier of beta.
Base class for solution control classes.
virtual tmp< volScalarField > adjointEikonalSource()
Return the source the adjoint eikonal equation.
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:78
virtual dimensionSet daDimensions() const
Return the dimensions of the adjoint distance field.
Nothing to be read.
const std::string patch
OpenFOAM patch number as a std::string.
const dimensionSet dimTime(0, 0, 1, 0, 0, 0, 0)
Definition: dimensionSets.H:51
Internal & ref(const bool updateAccessTime=true)
Same as internalFieldRef()
Field< vector > vectorField
Specialisation of Field<T> for vector.
Pointer management similar to std::unique_ptr, with some additional methods and type checking...
Definition: HashPtrTable.H:48
virtual void accumulateGeometryVariationsMultipliers(autoPtr< boundaryVectorField > &dSfdbMult, autoPtr< boundaryVectorField > &dnfdbMult, autoPtr< boundaryVectorField > &dxdbDirectMult, autoPtr< pointBoundaryVectorField > &pointDxDirectDbMult, const labelHashSet &sensitivityPatchIDs, const scalar dt)
Accumulate the multipliers of geometric quantities defined at the boundary, usually through an object...
Base class for primal incompressible solvers.
volScalarField & p
A class for managing temporary objects.
Definition: HashPtrTable.H:50
static options & New(const fvMesh &mesh)
Construct fvOptions and register to database if not present.
Definition: fvOptions.C:96
virtual void accumulateOptionsDxDbMultiplier(vectorField &optionsDxDbMult, const scalar dt)
Contributions from fvOptions that inlcude geometric aspects in them and change when the geometry is d...
Defines the attributes of an object for which implicit objectRegistry management is supported...
Definition: IOobject.H:172
#define FatalIOErrorInLookup(ios, lookupTag, lookupName, lookupTable)
Report an error message using Foam::FatalIOError.
Definition: error.H:635
const Boundary & boundaryField() const noexcept
Return const-reference to the boundary field.
Namespace for OpenFOAM.
addToRunTimeSelectionTable(functionObject, pointHistory, dictionary)
IOerror FatalIOError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL IO ERROR&#39; header text and ...
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127