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 
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
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:85
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:116
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:787
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:86
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:70
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:825
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:362
void read()
Read controls and update solver pointers if necessary.
GeometricField< vector, fvPatchField, volMesh > volVectorField
Definition: volFieldsFwd.H:82
labelList faceLabels(nFaceLabels)
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:871
bool includeGradStressTerm_
Include the term containing the grad of the stress at the boundary.
static const word & zeroGradientType() noexcept
The type name for zeroGradient patch fields.
Definition: faPatchField.H:205
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:1073
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:414
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:860
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:81
const dictionary & dict() const
Return the construction dictionary.
Definition: sensitivity.C:50
pointField points(const UList< point > &pts) const
Return the points corresponding to this face.
Definition: faceI.H:80
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.
const polyBoundaryMesh & boundaryMesh() const noexcept
Return boundary mesh.
Definition: polyMesh.H:584
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 entries in the list.
Definition: UPtrListI.H:113
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.
Reading is optional [identical to LAZY_READ].
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:1098
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.
#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:770
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:620
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
List of word.
Definition: fileName.H:58
static word meshSubDir
The mesh sub-directory name (usually "faMesh")
Definition: faMesh.H:703
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:192
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:81
messageStream Info
Information stream (stdout output on master, null elsewhere)
Internal & ref(const bool updateAccessTime=true)
Same as internalFieldRef()
const fvBoundaryMesh & boundary() const noexcept
Return reference to boundary mesh.
Definition: fvMesh.H:397
bool smoothSensitivities_
Smooth sensitivity derivatives based on the computation of the &#39;Sobolev gradient&#39;.
const boundBox & bounds() const noexcept
Return mesh bounding box.
Definition: polyMesh.H:592
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)
static tmp< GeometricField< tensor, fvPatchField, volMesh > > New(const word &name, const Mesh &mesh, const dimensionSet &dims, const word &patchFieldType=fvPatchField< tensor >::calculatedType())
Return tmp field from name, mesh, dimensions and patch type.
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:171
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
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:133
const dimensionSet dimVelocity