sensitivitySurfaceIncompressible.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-2021 PCOpt/NTUA
9  Copyright (C) 2013-2021 FOSS GP
10  Copyright (C) 2019-2020 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 
33 #include "syncTools.H"
35 #include "faMatrices.H"
36 #include "famSup.H"
37 #include "famLaplacian.H"
38 #include "volSurfaceMapping.H"
41 
42 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
43 
44 namespace Foam
45 {
46 
47 namespace incompressible
48 {
49 
50 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
51 
52 defineTypeNameAndDebug(sensitivitySurface, 1);
54 (
58 );
59 
60 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
61 
63 {
65  {
66  // Grab objective refs
67  PtrList<objective>& functions
69  // Compute sens for all points in parameterized patches.
70  // Interfacing points will be accumulated later
72  (
73  createZeroBoundaryPointFieldPtr<vector>(mesh_)
74  );
76  (
77  createZeroBoundaryPointFieldPtr<vector>(mesh_)
78  );
79  // Geometric (or "direct") sensitivities are better computed directly
80  // on the points
81  for (const label patchI : sensitivityPatchIDs_)
82  {
83  const fvPatch& patch = mesh_.boundary()[patchI];
84  const vectorField nf(patch.nf());
85 
86  // point sens result for patch
87  vectorField& patchdSdb = pointSensdSdb()[patchI];
88  vectorField& patchdndb = pointSensdndb()[patchI];
89 
90  vectorField dSdbMultiplierTot(patch.size(), Zero);
91  vectorField dndbMultiplierTot(patch.size(), Zero);
92  for (auto& fun : functions)
93  {
94  dSdbMultiplierTot += fun.weight()*fun.dSdbMultiplier(patchI);
95  dndbMultiplierTot += fun.weight()*fun.dndbMultiplier(patchI);
96  }
97  // Correspondence of local point addressing to global point
98  // addressing
99  const labelList& meshPoints = patch.patch().meshPoints();
100  // List with mesh faces. Global addressing
101  const faceList& faces = mesh_.faces();
102  // Each local patch point belongs to these local patch faces
103  // (local numbering)
104  const labelListList& patchPointFaces = patch.patch().pointFaces();
105  // index of first face in patch
106  const label patchStartIndex = patch.start();
107  // geometry differentiation engine
108  deltaBoundary dBoundary(mesh_);
109  // Loop over patch points.
110  // Collect contributions from each boundary face this point
111  // belongs to
112  forAll(meshPoints, ppI)
113  {
114  const labelList& pointFaces = patchPointFaces[ppI];
115  for (label localFaceIndex : pointFaces)
116  {
117  label globalFaceIndex = patchStartIndex + localFaceIndex;
118  const face& faceI = faces[globalFaceIndex];
119  // Point coordinates. All indices in global numbering
120  const pointField p(faceI.points(mesh_.points()));
121  tensorField p_d(faceI.size(), Zero);
122  forAll(faceI, facePointI)
123  {
124  if (faceI[facePointI] == meshPoints[ppI])
125  {
126  p_d[facePointI] = tensor::I;
127  }
128  }
129  const tensorField deltaNormals
130  (
131  dBoundary.makeFaceCentresAndAreas_d(p, p_d)
132  );
133 
134  // Element [1] is the variation in the (dimensional) normal
135  const tensor& deltaSf = deltaNormals[1];
136  patchdSdb[ppI] +=
137  dSdbMultiplierTot[localFaceIndex] & deltaSf;
138 
139  // Element [2] is the variation in the unit normal
140  const tensor& deltaNf = deltaNormals[2];
141  patchdndb[ppI] +=
142  dndbMultiplierTot[localFaceIndex] & deltaNf;
143  }
144  }
145  }
146  // Do parallel communications to avoid wrong values at processor
147  // boundaries
148  vectorField dSdbGlobal(mesh_.nPoints(), Zero);
149  vectorField dndbGlobal(mesh_.nPoints(), Zero);
150  for (const label patchI : sensitivityPatchIDs_)
151  {
152  const labelList& meshPoints =
153  mesh_.boundaryMesh()[patchI].meshPoints();
154  forAll(meshPoints, ppI)
155  {
156  const label globaPointI = meshPoints[ppI];
157  dSdbGlobal[globaPointI] += pointSensdSdb()[patchI][ppI];
158  dndbGlobal[globaPointI] += pointSensdndb()[patchI][ppI];
159  }
160  }
161  // Accumulate over processors
163  (
164  mesh_, dSdbGlobal, plusEqOp<vector>(), vector::zero
165  );
167  (
168  mesh_, dndbGlobal, plusEqOp<vector>(), vector::zero
169  );
170  // Transfer back to local fields and map to faces
171  for (const label patchI : sensitivityPatchIDs_)
172  {
173  const fvPatch& patch = mesh_.boundary()[patchI];
174  const labelList& meshPoints = patch.patch().meshPoints();
175  const scalarField& magSf = patch.magSf();
176  pointSensdSdb()[patchI].map(dSdbGlobal, meshPoints);
177  pointSensdndb()[patchI].map(dndbGlobal, meshPoints);
178  // Map dSf/dx and dnf/dx term from points to faces. Ideally, all
179  // sensitivities should be computed at points rather than faces.
180  PrimitivePatchInterpolation<polyPatch> patchInter(patch.patch());
181  vectorField dSdbFace
182  (
183  patchInter.pointToFaceInterpolate(pointSensdSdb()[patchI])
184  );
185  // dSdb already contains the face area. Divide with it to make it
186  // compatible with the rest of the terms
187  dSdbFace /= magSf;
188 
189  tmp<vectorField> tdndbFace =
190  patchInter.pointToFaceInterpolate(pointSensdndb()[patchI]);
191  const vectorField& dndbFace = tdndbFace();
192 
193  // Add to sensitivity fields
194  wallFaceSensVecPtr_()[patchI] += dSdbFace + dndbFace;
195  }
196  }
197 }
198 
199 
201 {
202  word suffix(dict().getOrDefault<word>("suffix", word::null));
203  // Determine suffix for fields holding the sens
205  {
207  (
208  adjointVars_.solverName() + "ESI" + suffix
209  );
210  }
211  else
212  {
214  (
215  adjointVars_.solverName() + "SI" + suffix
216  );
217  }
218 }
219 
220 
222 {
223  // Read in parameters
224  const label iters(dict().getOrDefault<label>("iters", 500));
225  const scalar tolerance(dict().getOrDefault<scalar>("tolerance", 1.e-06));
226  autoPtr<faMesh> aMeshPtr(nullptr);
227 
228  IOobject faceLabels
229  (
230  "faceLabels",
232  (
234  "faceLabels",
236  ),
238  mesh_,
241  );
242 
243  // If the faMesh already exists, read it
244  if (faceLabels.typeHeaderOk<labelIOList>(false))
245  {
246  Info<< "Reading the already constructed faMesh" << endl;
247  aMeshPtr.reset(new faMesh(mesh_));
248  }
249  else
250  {
251  // Dictionary used to construct the faMesh
252  dictionary faMeshDefinition;
253 
254  IOobject faMeshDefinitionDict
255  (
256  "faMeshDefinition",
257  mesh_.time().caseSystem(),
258  mesh_,
261  );
262 
263  // If the faMeshDefinitionDict exists, use it to construct the mesh
264  if (faMeshDefinitionDict.typeHeaderOk<IOdictionary>(false))
265  {
266  Info<< "Reading faMeshDefinition from system " << endl;
267  faMeshDefinition = IOdictionary(faMeshDefinitionDict);
268  }
269  // Otherwise, faMesh is generated from all patches on which we compute
270  // sensitivities
271  else
272  {
273  Info<< "Constructing faMeshDefinition from sensitivity patches"
274  << endl;
275  wordList polyMeshPatches(sensitivityPatchIDs_.size());
276  label i(0);
277  for (const label patchID : sensitivityPatchIDs_)
278  {
279  polyMeshPatches[i++] = mesh_.boundary()[patchID].name();
280  }
281  faMeshDefinition.add<wordList>("polyMeshPatches", polyMeshPatches);
282  (void)faMeshDefinition.subDictOrAdd("boundary");
283  }
284 
285  // Construct faMesh
286  aMeshPtr.reset(new faMesh(mesh_, faMeshDefinition));
287  }
288  faMesh& aMesh = aMeshPtr.ref();
289 
290  // Physical radius of the smoothing, provided either directly or computed
291  // based on the average 'length' of boundary faces
292  const scalar Rphysical
293  (dict().getOrDefault<scalar>("radius", computeRadius(aMesh)));
294  DebugInfo
295  << "Physical radius of the sensitivity smoothing "
296  << Rphysical << nl << endl;
297 
298  // Radius used as the diffusivity in the Helmholtz filter, computed as a
299  // function of the physical radius
300  const dimensionedScalar RpdeSqr
301  (
302  "RpdeSqr", dimArea, sqr(Rphysical/(2.*::sqrt(3.)))
303  );
304 
305  dimensionedScalar one("1", dimless, 1.);
306 
307  // Mapping engine
308  const volSurfaceMapping vsm(aMesh);
309 
310  // Source term in faMatrix needs to be an areaField
311  areaScalarField sens
312  (
313  IOobject
314  (
315  "sens",
316  mesh_.time().timeName(),
317  mesh_,
320  ),
321  aMesh,
324  );
325 
326  // Copy sensitivities to area field
327  sens.primitiveFieldRef() =
328  vsm.mapToSurface<scalar>(wallFaceSensNormalPtr_());
329 
330  // Initialisation of the smoothed sensitivities field based on the original
331  // sensitivities
332  areaScalarField smoothedSens("smoothedSens", sens);
333  for (label iter = 0; iter < iters; ++iter)
334  {
335  Info<< "Sensitivity smoothing iteration " << iter << endl;
336 
337  faScalarMatrix smoothEqn
338  (
339  fam::Sp(one, smoothedSens)
340  - fam::laplacian(RpdeSqr, smoothedSens)
341  ==
342  sens
343  );
344 
345  smoothEqn.relax();
346 
347  const scalar residual(mag(smoothEqn.solve().initialResidual()));
348 
349  DebugInfo
350  << "Max smoothSens " << gMax(mag(smoothedSens)()) << endl;
351 
352  // Print execution time
354 
355  // Check convergence
356  if (residual < tolerance)
357  {
358  Info<< "\n***Reached smoothing equation convergence limit, "
359  "iteration " << iter << "***\n\n";
360  break;
361  }
362  }
363 
364  // Field used to write the smoothed sensitivity field to file
365  volScalarField volSmoothedSens
366  (
367  IOobject
368  (
369  "smoothedSurfaceSens" + surfaceFieldSuffix_,
370  mesh_.time().timeName(),
371  mesh_,
374  ),
375  mesh_,
377  );
379  // Transfer result back to volField and write
380  vsm.mapToVolume(smoothedSens, volSmoothedSens.boundaryFieldRef());
381  volSmoothedSens.write();
382 }
383 
384 
386 {
387  scalar averageArea(gAverage(aMesh.S().field()));
388  const Vector<label>& geometricD = mesh_.geometricD();
389  const boundBox& bounds = mesh_.bounds();
390  forAll(geometricD, iDir)
391  {
392  if (geometricD[iDir] == -1)
393  {
394  averageArea /= bounds.span()[iDir];
395  }
396  }
397  scalar mult = dict().getOrDefault<scalar>("meanRadiusMultiplier", 10);
398 
399  return mult*pow(averageArea, scalar(1)/scalar(mesh_.nGeometricD() - 1));
400 }
401 
402 
403 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
404 
405 sensitivitySurface::sensitivitySurface
406 (
407  const fvMesh& mesh,
408  const dictionary& dict,
410 )
411 :
414  includeSurfaceArea_(false),
415  includePressureTerm_(false),
416  includeGradStressTerm_(false),
417  includeTransposeStresses_(false),
418  useSnGradInTranposeStresses_(false),
419  includeDivTerm_(false),
420  includeDistance_(false),
421  includeMeshMovement_(false),
422  includeObjective_(false),
423  writeGeometricInfo_(false),
424  smoothSensitivities_(false),
425  eikonalSolver_(nullptr),
426  meshMovementSolver_(nullptr),
427 
428  nfOnPatchPtr_(nullptr),
429  SfOnPatchPtr_(nullptr),
430  CfOnPatchPtr_(nullptr)
431 {
432  read();
433  setSuffixName();
434 
435  // Allocate boundary field pointer
436  wallFaceSensVecPtr_.reset(createZeroBoundaryPtr<vector>(mesh_));
437  wallFaceSensNormalPtr_.reset(createZeroBoundaryPtr<scalar>(mesh_));
438  wallFaceSensNormalVecPtr_.reset(createZeroBoundaryPtr<vector>(mesh_));
439 
440  // Allocate fields to contain geometric info
442  {
443  nfOnPatchPtr_.reset
444  (
445  new volVectorField
446  (
447  IOobject
448  (
449  "nfOnPatch",
450  mesh.time().timeName(),
451  mesh,
454  ),
455  mesh,
456  vector::zero
457  )
458  );
459 
460  SfOnPatchPtr_.reset
461  (
462  new volVectorField
463  (
464  IOobject
465  (
466  "SfOnPatch",
467  mesh.time().timeName(),
468  mesh,
471  ),
472  mesh,
473  vector::zero
474  )
475  );
476 
477  CfOnPatchPtr_.reset
478  (
479  new volVectorField
480  (
481  IOobject
482  (
483  "CfOnPatch",
484  mesh.time().timeName(),
485  mesh,
488  ),
489  mesh,
490  vector::zero
491  )
492  );
493  }
494 
495  // Allocate appropriate space for the sensitivity field
497 }
498 
499 
500 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
501 
503 {
505  dict().getOrDefault<bool>("includeSurfaceArea", true);
507  dict().getOrDefault<bool>("includePressure", true);
509  dict().getOrDefault<bool>("includeGradStressTerm", true);
511  dict().getOrDefault<bool>("includeTransposeStresses", true);
513  dict().getOrDefault<bool>("useSnGradInTranposeStresses", false);
514  includeDivTerm_ = dict().getOrDefault<bool>("includeDivTerm", false);
516  dict().getOrDefault<bool>
517  (
518  "includeDistance",
519  adjointVars_.adjointTurbulence().ref().includeDistance()
520  );
522  dict().getOrDefault<bool>("includeMeshMovement", true);
524  dict().getOrDefault<bool>("includeObjectiveContribution", true);
526  dict().getOrDefault<bool>("writeGeometricInfo", false);
528  dict().getOrDefault<bool>("smoothSensitivities", false);
529 
530  // Allocate new solvers if necessary
532  {
533  eikonalSolver_.reset
534  (
536  (
537  mesh_,
538  dict_,
540  adjointVars_,
541  sensitivityPatchIDs_
542  )
543  );
544  }
546  {
547  meshMovementSolver_.reset
548  (
549  new adjointMeshMovementSolver
550  (
551  mesh_,
552  dict_,
553  *this,
554  sensitivityPatchIDs_,
556  )
557  );
558  }
559 }
560 
561 
563 {
565  {
566  if (eikonalSolver_)
567  {
568  eikonalSolver_().readDict(dict);
569  }
570 
572  {
573  meshMovementSolver_().readDict(dict);
574  }
575 
576  return true;
577  }
578 
579  return false;
580 }
581 
582 
584 {
585  label nFaces(0);
586  for (const label patchI : sensitivityPatchIDs_)
587  {
588  nFaces += mesh_.boundary()[patchI].size();
589  }
590  derivatives_.setSize(nFaces);
591 }
592 
593 
594 void sensitivitySurface::accumulateIntegrand(const scalar dt)
595 {
596  // Grab references
597  const volScalarField& p = primalVars_.p();
598  const volVectorField& U = primalVars_.U();
599 
600  const volScalarField& pa = adjointVars_.pa();
601  const volVectorField& Ua = adjointVars_.Ua();
604 
605  // Accumulate source for additional post-processing PDEs, if necessary
606  if (includeDistance_)
607  {
608  eikonalSolver_->accumulateIntegrand(dt);
609  }
610 
612  {
613  meshMovementSolver_->accumulateIntegrand(dt);
614  }
615 
616  // Terms from the adjoint turbulence model
617  const boundaryVectorField& adjointTMsensitivities =
618  adjointTurbulence->wallShapeSensitivities();
619 
620  DebugInfo
621  << " Calculating adjoint sensitivity. " << endl;
622 
623  tmp<volScalarField> tnuEff = adjointTurbulence->nuEff();
624  const volScalarField& nuEff = tnuEff.ref();
625 
626  // Sensitivities do not include locale surface area by default.
627  // The part of the sensitivities that multiplies dxFace/db follows
628 
629  // Deal with the stress part first since it's the most awkward in terms
630  // of memory managment
632  {
633  // Terms corresponding to contributions from converting delta
634  // to thetas are added through the corresponding adjoint
635  // boundary conditions instead of grabbing contributions from
636  // the objective function. Useful to have a unified
637  // formulation for low- and high-re meshes
638 
639  tmp<volVectorField> tgradp = fvc::grad(p);
640  const volVectorField& gradp = tgradp.ref();
641  for (const label patchI : sensitivityPatchIDs_)
642  {
643  const fvPatch& patch = mesh_.boundary()[patchI];
644  tmp<vectorField> tnf = patch.nf();
645  const fvPatchVectorField& Uab = Ua.boundaryField()[patchI];
646  wallFaceSensVecPtr_()[patchI] -=
647  (Uab & tnf)*gradp.boundaryField()[patchI]*dt;
648  }
649  tgradp.clear();
650 
651  // We only need to modify the boundaryField of gradU locally.
652  // If grad(U) is cached then
653  // a. The .ref() call fails since the tmp is initialised from a
654  // const ref
655  // b. we would be changing grad(U) for all other places in the code
656  // that need it
657  // So, always allocate new memory and avoid registering the new field
658  tmp<volTensorField> tgradU =
659  volTensorField::New("gradULocal", fvc::grad(U));
660  volTensorField::Boundary& gradUbf = tgradU.ref().boundaryFieldRef();
661 
662  // Explicitly correct the boundary gradient to get rid of the
663  // tangential component
664  forAll(mesh_.boundary(), patchI)
665  {
666  const fvPatch& patch = mesh_.boundary()[patchI];
667  if (isA<wallFvPatch>(patch))
668  {
669  tmp<vectorField> tnf = mesh_.boundary()[patchI].nf();
670  gradUbf[patchI] = tnf*U.boundaryField()[patchI].snGrad();
671  }
672  }
673 
674  tmp<volSymmTensorField> tstress = nuEff*twoSymm(tgradU);
675  const volSymmTensorField& stress = tstress.cref();
676  autoPtr<volVectorField> ptemp
677  (Foam::createZeroFieldPtr<vector>(mesh_, "temp", sqr(dimVelocity)));
678  volVectorField& temp = ptemp.ref();
679  for (label idir = 0; idir < pTraits<vector>::nComponents; ++idir)
680  {
681  unzipRow(stress, idir, temp);
682  volTensorField gradStressDir(fvc::grad(temp));
683  for (const label patchI : sensitivityPatchIDs_)
684  {
685  const fvPatch& patch = mesh_.boundary()[patchI];
686  tmp<vectorField> tnf = patch.nf();
687  const fvPatchVectorField& Uab = Ua.boundaryField()[patchI];
688  wallFaceSensVecPtr_()[patchI] +=
689  (
690  Uab.component(idir)
691  *(gradStressDir.boundaryField()[patchI] & tnf)
692  )*dt;
693  }
694  }
695  }
696 
697  // Transpose part of the adjoint stresses
698  // Dealt with separately to deallocate gradUa as soon as possible
700  {
701  tmp<volTensorField> tgradUa = fvc::grad(Ua);
702  const volTensorField::Boundary& gradUabf =
703  tgradUa.cref().boundaryField();
704 
705  for (const label patchI : sensitivityPatchIDs_)
706  {
707  const fvPatch& patch = mesh_.boundary()[patchI];
708  tmp<vectorField> tnf = patch.nf();
709  const vectorField& nf = tnf();
710  vectorField gradUaNf
711  (
713  ? (Ua.boundaryField()[patchI].snGrad() & nf)*nf
714  : (gradUabf[patchI] & nf)
715  );
716  wallFaceSensVecPtr_()[patchI] -=
717  nuEff.boundaryField()[patchI]
718  *(gradUaNf & U.boundaryField()[patchI].snGrad())*nf;
719  }
720  }
721 
722  for (const label patchI : sensitivityPatchIDs_)
723  {
724  const fvPatch& patch = mesh_.boundary()[patchI];
725  tmp<vectorField> tnf = patch.nf();
726  const vectorField& nf = tnf();
727 
728  // Includes spurious tangential gradU part. Deprecated
729  /*
730  vectorField stressAndPressureTerm =
731  (
732  - (
733  Ua.boundaryField()[patchI].snGrad()
734  + (gradUa.boundaryField()[patchI] & nf)
735  ) * nuEff.boundaryField()[patchI]
736  + pa.boundaryField()[patchI] *nf
737  ) & gradU.boundaryField()[patchI].T();
738  */
739 
740  // Adjoint stress term
741  vectorField stressTerm
742  (
743  - (
744  Ua.boundaryField()[patchI].snGrad()
745  & U.boundaryField()[patchI].snGrad()
746  )
747  * nuEff.boundaryField()[patchI]
748  * nf
749  );
750 
751  if (includeDivTerm_)
752  {
753  stressTerm +=
754  scalar(1./3.)*nuEff.boundaryField()[patchI]
755  * (
756  ((Ua.boundaryField()[patchI].snGrad() &nf)*nf)
757  & U.boundaryField()[patchI].snGrad()
758  )
759  * nf;
760  }
761 
762  // Adjoint pressure terms
763  vectorField pressureTerm(patch.size(), Zero);
765  {
766  pressureTerm =
767  (
768  (nf*pa.boundaryField()[patchI])
769  & U.boundaryField()[patchI].snGrad()
770  )* nf;
771  }
772 
773  PtrList<objective>& functions
775 
776  // Term from objectives including x directly (e.g. moments)
777  vectorField dxdbMultiplierTot(pressureTerm.size(), Zero);
778  if (includeObjective_)
779  {
780  forAll(functions, funcI)
781  {
782  dxdbMultiplierTot +=
783  functions[funcI].weight()
784  * (
785  functions[funcI].dxdbDirectMultiplier(patchI)
786  );
787  }
788  }
789 
790  // Fill in sensitivity fields
791  wallFaceSensVecPtr_()[patchI] +=
792  (
793  stressTerm
794  + pressureTerm
795  + adjointTMsensitivities[patchI]
796  + dxdbMultiplierTot
797  )*dt;
798  }
799 
800  // Add terms from physics other than the typical incompressible flow eqns
802  (wallFaceSensVecPtr_(), sensitivityPatchIDs_, dt);
804  // Add the sensitivity part corresponding to changes of the normal vector
805  // Computed at points and mapped to faces
807 }
808 
809 
811 {
812  // Update geometric fields for use by external users
814  {
815  for (const label patchI : sensitivityPatchIDs_)
816  {
817  const fvPatch& patch = mesh_.boundary()[patchI];
818  tmp<vectorField> tnf = patch.nf();
819  const vectorField& nf = tnf();
820  const vectorField& Sf = patch.Sf();
821  const vectorField& Cf = patch.Cf();
822 
823  nfOnPatchPtr_().boundaryFieldRef()[patchI] = nf;
824  SfOnPatchPtr_().boundaryFieldRef()[patchI] = Sf;
825  CfOnPatchPtr_().boundaryFieldRef()[patchI] = Cf;
826  }
827  }
828 
829  // Solve extra equations if necessary
830  // Solved using accumulated sources over time
831  autoPtr<boundaryVectorField> distanceSensPtr(nullptr);
832  if (includeDistance_)
833  {
834  eikonalSolver_->solve();
835  distanceSensPtr.reset(createZeroBoundaryPtr<vector>(mesh_));
836  const boundaryVectorField& sens =
837  eikonalSolver_->distanceSensitivities();
838  for (const label patchI : sensitivityPatchIDs_)
839  {
840  distanceSensPtr()[patchI] = sens[patchI];
841  }
842  }
843 
844  autoPtr<boundaryVectorField> meshMovementSensPtr(nullptr);
846  {
847  meshMovementSolver_->solve();
848  meshMovementSensPtr.reset(createZeroBoundaryPtr<vector>(mesh_));
849  const boundaryVectorField& sens =
850  meshMovementSolver_->meshMovementSensitivities();
851  for (const label patchI : sensitivityPatchIDs_)
852  {
853  meshMovementSensPtr()[patchI] = sens[patchI];
854  }
855  }
856 
857 
858  // Project to normal face vector
859  label nPassedFaces(0);
860  for (const label patchI : sensitivityPatchIDs_)
861  {
862  const fvPatch& patch = mesh_.boundary()[patchI];
863  tmp<vectorField> tnf(patch.nf());
864  const vectorField& nf = tnf();
865 
866  // Distance related terms
867  if (includeDistance_)
868  {
869  wallFaceSensVecPtr_()[patchI] += distanceSensPtr()[patchI];
870  }
871 
872  // Mesh movement related terms
874  {
875  wallFaceSensVecPtr_()[patchI] += meshMovementSensPtr()[patchI];
876  }
877 
879  {
880  wallFaceSensVecPtr_()[patchI] *= patch.magSf();
881  }
882 
883  wallFaceSensNormalPtr_()[patchI] = wallFaceSensVecPtr_()[patchI] & nf;
884  wallFaceSensNormalVecPtr_()[patchI] =
885  wallFaceSensNormalPtr_()[patchI] * nf;
886 
887  forAll(patch, fI)
888  {
889  derivatives_[nPassedFaces + fI]
890  = wallFaceSensNormalPtr_()[patchI][fI];
891  }
892  nPassedFaces += patch.size();
893  }
894 
895  // Smooth sensitivities if needed
897  {
899  }
900 }
901 
902 
904 {
905  // Reset terms in post-processing PDEs
906  if (includeDistance_)
907  {
908  eikonalSolver_->reset();
909  }
911  {
912  meshMovementSolver_->reset();
913  }
914  // Reset sensitivity fields
917 }
918 
921 {
922  return eikonalSolver_;
923 }
924 
925 
926 void sensitivitySurface::write(const word& baseName)
927 {
928  setSuffixName();
931 
933  {
934  nfOnPatchPtr_().write();
935  SfOnPatchPtr_().write();
936  CfOnPatchPtr_().write();
937  }
938 }
939 
940 
941 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
942 
943 } // End namespace Foam
944 } // End namespace incompressible
945 
946 // ************************************************************************* //
const fvMesh & mesh_
Definition: sensitivity.H:65
const polyBoundaryMesh & boundaryMesh() const
Return boundary mesh.
Definition: polyMesh.H:584
Finite area mesh (used for 2-D non-Euclidian finite area method) defined using a patch of faces on a ...
Definition: faMesh.H:88
GeometricField< symmTensor, fvPatchField, volMesh > volSymmTensorField
Definition: volFieldsFwd.H:88
dictionary dict
tmp< GeometricField< typename outerProduct< vector, Type >::type, fvPatchField, volMesh >> grad(const GeometricField< Type, fvsPatchField, surfaceMesh > &ssf)
Definition: fvcGrad.C:47
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:118
void setSuffix(const word &suffix)
Set suffix.
virtual bool readDict(const dictionary &dict)
Read dictionary if changed.
Definition: sensitivity.C:56
defineTypeNameAndDebug(adjointEikonalSolver, 0)
bool useSnGradInTranposeStresses_
Use snGrad in the transpose part of the adjoint stresses.
fvPatchField< vector > fvPatchVectorField
virtual void additionalSensitivityMapTerms(boundaryVectorField &sensitivityMap, const labelHashSet &patchIDs, const scalar dt)
Terms to be added to the sensitivity map, depending on the adjoint solver.
const volSurfaceMapping vsm(aMesh)
bool includeObjective_
Include terms directly emerging from the objective function.
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:68
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
word findInstance(const fileName &dir, const word &name=word::null, IOobjectOption::readOption rOpt=IOobjectOption::MUST_READ, const word &stopInstance=word::null) const
Return time instance (location) of dir that contains the file name (eg, used in reading mesh data)...
Definition: Time.C:777
label nPoints() const noexcept
Number of mesh points.
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:120
PtrList< objective > & getObjectiveFunctions()
Return reference to objective functions.
autoPtr< adjointMeshMovementSolver > meshMovementSolver_
virtual void write(const word &baseName=word::null)
Write sensitivity maps.
GeometricField< tensor, fvPatchField, volMesh > volTensorField
Definition: volFieldsFwd.H:89
dimensionedSymmTensor sqr(const dimensionedVector &dv)
volVectorField::Boundary boundaryVectorField
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:49
void addGeometricSens()
Add sensitivities from dSd/db and dnf/db computed at points and mapped to faces.
virtual void clearSensitivities()
Zero sensitivity fields and their constituents.
dimensionedScalar sqrt(const dimensionedScalar &ds)
Tensor< scalar > tensor
Definition: symmTensor.H:57
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:487
A finiteVolume patch using a polyPatch and a fvBoundaryMesh.
Definition: fvPatch.H:68
virtual void write(const word &baseName=word::null)
Write sensitivity fields.
void smoothSensitivities()
Smooth sensitivity derivatives based on the computation of the &#39;Sobolev gradient&#39;.
Differentiation of the mesh data structure.
Definition: deltaBoundary.H:54
void unzipRow(const FieldField< Field, SymmTensor< Cmpt >> &input, const direction idx, FieldField< Field, Vector< Cmpt >> &result)
Extract a symmTensor field field row (x,y,z) == (0,1,2)
Base class for adjoint solvers.
Definition: adjointSolver.H:51
A bounding box defined in terms of min/max extrema points.
Definition: boundBox.H:63
bool includeTransposeStresses_
Include the transpose part of the adjoint stresses.
virtual const fileName & dbDir() const
Override the objectRegistry dbDir for a single-region case.
Definition: polyMesh.C:824
GeometricBoundaryField< tensor, fvPatchField, volMesh > Boundary
Type of boundary fields.
Ignore writing from objectRegistry::writeObject()
const dimensionSet dimless
Dimensionless.
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:361
void read()
Read controls and update solver pointers if necessary.
GeometricField< vector, fvPatchField, volMesh > volVectorField
Definition: volFieldsFwd.H:85
bool includeDivTerm_
Include the term from the deviatoric part of the stresses.
label nGeometricD() const
Return the number of valid geometric dimensions in the mesh.
Definition: polyMesh.C:870
bool includeGradStressTerm_
Include the term containing the grad of the stress at the boundary.
addToRunTimeSelectionTable(adjointSensitivity, sensitivityBezier, dictionary)
Macros for easy insertion into run-time selection tables.
dimensionedSymmTensor twoSymm(const dimensionedSymmTensor &dt)
Base class for incompressibleAdjoint solvers.
virtual const pointField & points() const
Return raw points.
Definition: polyMesh.C:1066
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:413
tmp< faMatrix< Type > > laplacian(const GeometricField< Type, faPatchField, areaMesh > &vf)
Definition: famLaplacian.C:42
fileName caseSystem() const
Return the system name for the case, which is ../system() for parallel runs.
Definition: TimePathsI.H:112
const Vector< label > & geometricD() const
Return the vector of geometric directions in mesh.
Definition: polyMesh.C:859
void reset(T *p=nullptr) noexcept
Delete managed object and set to new given pointer.
Definition: autoPtrI.H:37
GeometricField< scalar, fvPatchField, volMesh > volScalarField
Definition: volFieldsFwd.H:84
const dictionary & dict() const
Return the construction dictionary.
Definition: sensitivity.C:50
static const char *const typeName
Typename for Field.
Definition: Field.H:86
pointField points(const UList< point > &pts) const
Return the points corresponding to this face.
Definition: faceI.H:80
static tmp< GeometricField< tensor, fvPatchField, volMesh > > New(const word &name, const Mesh &mesh, const dimensionSet &ds, const word &patchFieldType=fvPatchField< tensor >::calculatedType())
Return tmp field from name, mesh, dimensions and patch type.
zeroField Sp(const Foam::zero, const GeometricField< Type, faPatchField, areaMesh > &)
A no-op source.
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
void setSize(const label n)
Alias for resize()
Definition: List.H:289
dynamicFvMesh & mesh
const volScalarField & pa() const
Return const reference to pressure.
static const Identity< scalar > I
Definition: Identity.H:100
autoPtr< adjointEikonalSolver > & getAdjointEikonalSolver()
Get adjoint eikonal solver.
A class for handling words, derived from Foam::string.
Definition: word.H:63
Abstract base class for adjoint-based sensitivities in incompressible flows.
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
virtual bool readDict(const dictionary &dict)
Read dict if changed.
bool includeSurfaceArea_
Include surface area in sens computation.
label size() const noexcept
The number of elements in the list.
Definition: UPtrListI.H:99
void computeDerivativesSize()
Compute the number of faces on sensitivityPatchIDs_.
const volVectorField & Ua() const
Return const reference to velocity.
const volScalarField & p() const
Return const reference to pressure.
bool includeMeshMovement_
Include mesh movement variation in sens computation.
static const word null
An empty word.
Definition: word.H:84
virtual void accumulateIntegrand(const scalar dt)
Accumulate sensitivity integrands.
virtual void assembleSensitivities()
Assemble sensitivities.
const autoPtr< incompressibleAdjoint::adjointRASModel > & adjointTurbulence() const
Return const reference to the adjointRASModel.
virtual const faceList & faces() const
Return raw faces.
Definition: polyMesh.C:1091
bool writeGeometricInfo_
Write geometric info for use by external programs.
bool includeDistance_
Include distance variation in sens computation.
const word & solverName() const
Return solver name.
Definition: variablesSet.C:77
static void syncPointList(const polyMesh &mesh, List< T > &pointValues, const CombineOp &cop, const T &nullValue, const TransformOp &top)
Synchronize values on all mesh points.
Calculate the matrix for the laplacian of the field.
bool typeHeaderOk(const bool checkType=true, const bool search=true, const bool verbose=true)
Read header (uses typeFilePath to find file) and check its info.
#define DebugInfo
Report an information message using Foam::Info.
const volVectorField & U() const
Return const reference to velocity.
Calculate the finiteArea matrix for implicit and explicit sources.
static word timeName(const scalar t, const int precision=precision_)
Return time name of given scalar time formatted with the given precision.
Definition: Time.C:760
Type gMax(const FieldField< Field, Type > &f)
void write()
Write sensitivity fields.
Ostream & printExecutionTime(OSstream &os) const
Print the elapsed ExecutionTime (cpu-time), ClockTime.
Definition: TimeIO.C:612
T & ref()
Return reference to the managed object without nullptr checking.
Definition: autoPtr.H:231
const autoPtr< incompressible::RASModelVariables > & RASModelVariables() const
Return const reference to the turbulence model variables.
bool includePressureTerm_
Include the adjoint pressure term in sens computation.
faMatrix< scalar > faScalarMatrix
Definition: faMatrices.H:46
Field< tensor > tensorField
Specialisation of Field<T> for tensor.
dimensionedScalar pow(const dimensionedScalar &ds, const dimensionedScalar &expt)
List< word > wordList
A List of words.
Definition: fileName.H:58
static word meshSubDir
The mesh sub-directory name (usually "faMesh")
Definition: faMesh.H:698
const boundBox & bounds() const
Return mesh bounding box.
Definition: polyMesh.H:592
Internal::FieldType & primitiveFieldRef(const bool updateAccessTime=true)
Return a reference to the internal field values.
U
Definition: pEqn.H:72
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
Type gAverage(const FieldField< Field, Type > &f)
void clearSensitivities()
Zero sensitivity fields and their constituents.
A list of pointers to objects of type <T>, with allocation/deallocation management of the pointers...
Definition: List.H:55
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:79
Nothing to be read.
loopControl iters(runTime, aMesh.solutionDict(), "solution")
Automatically write from objectRegistry::writeObject()
Calculation of adjoint based sensitivities at wall faces.
const std::string patch
OpenFOAM patch number as a std::string.
vector span() const
The bounding box span (from minimum to maximum)
Definition: boundBoxI.H:185
void setSuffixName()
Set suffix name for sensitivity fields.
scalar computeRadius(const faMesh &aMesh)
Compute the physical smoothing radius based on the average boundary face &#39;length&#39;.
void clear()
Clear the PtrList. Delete allocated entries and set size to zero.
Definition: PtrListI.H:90
messageStream Info
Information stream (stdout output on master, null elsewhere)
Internal & ref(const bool updateAccessTime=true)
Same as internalFieldRef()
bool smoothSensitivities_
Smooth sensitivity derivatives based on the computation of the &#39;Sobolev gradient&#39;.
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
dictionary dict_
Definition: sensitivity.H:66
T getOrDefault(const word &keyword, const T &deflt, enum keyType::option matchOpt=keyType::REGEX) const
Find and return a T, or return the given default value. FatalIOError if it is found and the number of...
List< label > labelList
A List of labels.
Definition: List.H:62
volScalarField & p
A class for managing temporary objects.
Definition: HashPtrTable.H:50
faMesh aMesh(mesh)
virtual void clearSensitivities()
Zero sensitivity fields and their constituents.
Defines the attributes of an object for which implicit objectRegistry management is supported...
Definition: IOobject.H:166
Base class supporting shape sensitivity derivatives for incompressible flows.
const Boundary & boundaryField() const noexcept
Return const-reference to the boundary field.
const dimensionSet dimArea(sqr(dimLength))
Definition: dimensionSets.H:57
Namespace for OpenFOAM.
GeometricField< scalar, faPatchField, areaMesh > areaScalarField
Definition: areaFieldsFwd.H:80
const fvBoundaryMesh & boundary() const
Return reference to boundary mesh.
Definition: fvMesh.C:705
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:157
const dimensionSet dimVelocity