adjointSpalartAllmaras.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-2023 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 
30 #include "adjointSpalartAllmaras.H"
32 #include "wallDist.H"
33 #include "wallFvPatch.H"
36 #include "coupledFvPatch.H"
37 #include "ATCModel.H"
38 #include "fvOptions.H"
39 #include "sensitivityTopO.H"
40 
41 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
42 
43 namespace Foam
44 {
45 namespace incompressibleAdjoint
46 {
47 namespace adjointRASModels
48 {
49 
50 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
51 
52 defineTypeNameAndDebug(adjointSpalartAllmaras, 0);
54 (
55  adjointRASModel,
56  adjointSpalartAllmaras,
57  dictionary
58 );
59 
60 
61 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
62 
63 // * * * * * * * * * * * * Primal Spalart - Allmaras * * * * * * * * * * * * //
64 
66 {
67  return nuTilda()/nu();
68 }
69 
70 
72 {
73  const volScalarField chi3(pow3(chi));
74  return chi3/(chi3 + pow3(Cv1_));
75 }
76 
77 
79 (
80  const volScalarField& chi,
81  const volScalarField& fv1
82 ) const
83 {
84  return 1.0 - chi/(1.0 + chi*fv1);
85 }
86 
87 
89 (
90  const volScalarField& chi,
91  const volScalarField& fv1
92 ) const
93 {
94  volScalarField Omega(::sqrt(2.0)*mag(skew(gradU_)));
95 
96  return
97  (
98  max
99  (
100  Omega
101  + fv2(chi, fv1)*nuTilda()/sqr(kappa_*y_),
102  Cs_*Omega
103  )
104  );
105 }
106 
107 
109 (
110  const volScalarField& Stilda
111 ) const
112 {
113  auto tr =
115  (
116  min
117  (
119  scalar(10)
120  )
121  );
122  tr.ref().boundaryFieldRef() == Zero;
123 
124  return tr;
125 }
126 
127 
129 (
130  const volScalarField& Stilda
131 ) const
132 {
133  const volScalarField g(r_ + Cw2_*(pow6(r_) - r_));
134 
135  return g*pow((1.0 + pow6(Cw3_))/(pow6(g) + pow6(Cw3_)), 1.0/6.0);
136 }
137 
138 
140 {
141  return
143  ("DnuTildaEff", (nuTilda() + this->nu())/sigmaNut_);
144 }
145 
148 {
149  return primalVars_.RASModelVariables()().TMVar1();
150 }
151 
152 
154 {
156 }
157 
158 
159 // * * * * * * * * * * * Adjoint Spalart - Allmaras * * * * * * * * * * * * //
160 
162 (
163  const volScalarField& chi
164 ) const
165 {
167 
168  return 3.0*pow3(Cv1_)*sqr(chi/(chi3 + pow3(Cv1_)));
169 }
170 
171 
173 (
174  const volScalarField& chi,
175  const volScalarField& fv1,
176  const volScalarField& dFv1dChi
177 ) const
178 {
179  return (chi*chi*dFv1dChi - 1.)/sqr(1. + chi*fv1);
180 }
181 
182 
184 (
185  const volScalarField& Omega,
186  const volScalarField& fv2
187 ) const
188 {
189  volScalarField fieldSwitch
190  (
191  Omega + fv2*nuTilda()/sqr(kappa_*y_) - Cs_*Omega
192  );
193 
194  return pos(fieldSwitch) + neg(fieldSwitch)*Cs_;
195 }
196 
197 
199 (
200  const volScalarField& Omega,
201  const volScalarField& fv2,
202  const volScalarField& dFv2dChi
203 ) const
204 {
205  volScalarField invDenom(1./sqr(kappa_*y_));
206  volScalarField fieldSwitch(Omega + fv2*nuTilda()*invDenom - Cs_*Omega);
207 
208  return pos(fieldSwitch)*(dFv2dChi*nuTilda()*invDenom/nu() + fv2*invDenom);
209 }
210 
211 
213 (
214  const volScalarField& Omega,
215  const volScalarField& fv2
216 ) const
217 {
219  volScalarField fieldSwitch(Omega + aux - Cs_*Omega);
220 
221  return - 2.*pos(fieldSwitch)*aux/y_;
222 }
223 
224 
226 (
227  const volScalarField& Stilda
228 ) const
229 {
230  tmp<volScalarField> tdrdNutilda
231  (
232  1./(max(Stilda, minStilda_)*sqr(kappa_*y_))
233  *(scalar(10) - r_)/(scalar(10) - r_ + SMALL)
234  );
235  tdrdNutilda.ref().boundaryFieldRef() == Zero;
236 
237  return tdrdNutilda;
238 }
239 
240 
242 (
243  const volScalarField& Stilda
244 ) const
245 {
246  tmp<volScalarField> tdrdStilda
247  (
249  *(scalar(10) - r_)/(scalar(10) - r_ + SMALL)
250  );
251  tdrdStilda.ref().boundaryFieldRef() == Zero;
252 
253  return tdrdStilda;
254 }
255 
256 
258 (
259  const volScalarField& Stilda
260 ) const
261 {
262  tmp<volScalarField> tdrdDelta
263  (
264  - 2.*nuTilda()/(max(Stilda, minStilda_)*sqr(kappa_*y_)*y_)
265  *(scalar(10) - r_)/(scalar(10) - r_ + SMALL)
266  );
267  tdrdDelta.ref().boundaryFieldRef() == Zero;
268 
269  return tdrdDelta;
270 }
271 
272 
274 (
275  const volScalarField& Stilda
276 ) const
277 {
278  volScalarField g(r_ + Cw2_*(pow6(r_) - r_));
279 
280  dimensionedScalar pow6Cw3 = pow6(Cw3_);
281  volScalarField pow6g(pow6(g));
282 
283  return
284  pow6Cw3/(pow6g + pow6Cw3)
285  *pow((1.0 + pow6Cw3)/(pow6g + pow6Cw3), 1.0/6.0)
286  *(1.0 + Cw2_*(6.0*pow5(r_) - 1.0));
287 }
288 
289 
291 (
292  const volScalarField& Stilda,
293  const volScalarField& dfwdr,
294  const volScalarField& dStildadNuTilda
295 ) const
296 {
297  volScalarField invDenom(1./sqr(kappa_*y_));
299  return
300  dfwdr*(dr_dNuTilda(Stilda) + dr_dStilda(Stilda)*dStildadNuTilda);
301 }
302 
303 
305 (
306  const volScalarField& Stilda,
307  const volScalarField& dfwdr,
308  const volScalarField& dStildadOmega
309 ) const
310 {
311  return dfwdr*dr_dStilda(Stilda)*dStildadOmega;
312 }
313 
314 
316 (
317  const volScalarField& Stilda,
318  const volScalarField& dfwdr,
319  const volScalarField& dStildadDelta
320 ) const
321 {
322  return dfwdr*(dr_dDelta(Stilda) + dr_dStilda(Stilda)*dStildadDelta);
323 }
324 
325 
327 (
328  const volScalarField& fw,
329  const volScalarField& dfwdNuTilda
330 ) const
331 {
332  return Cw1_*(nuTilda()*dfwdNuTilda + fw)/sqr(y_);
333 }
334 
335 
337 (
338  const volScalarField& dStildadNuTilda
339 ) const
340 {
341  return - Cb1_*dStildadNuTilda;
342 }
343 
344 
346 (
347  const volScalarField& fv1,
348  const volScalarField& dFv1dChi
349 ) const
350 {
351  return dFv1dChi*nuTilda()/nu() + fv1;
352 }
353 
354 
356 {
357  // Store boundary field of the conservative part,
358  // for use in adjoint outlet boundary conditions
359  forAll(mesh_.boundary(), pI)
360  {
361  const fvPatch& patch = mesh_.boundary()[pI];
362  if(!isA<coupledFvPatch>(patch))
363  {
364  tmp<vectorField> tnf = patch.nf();
366  (tnf & momentumSourceMult_.boundaryField()[pI])
367  *nuaTilda().boundaryField()[pI];
368  }
369  }
370 
372 }
373 
374 
376 {
378  {
379  Info<< "Updating primal-based fields of the adjoint turbulence "
380  << "model ..." << endl;
381 
382  // Grab references
383  const volVectorField& U = primalVars_.U();
384 
385  // Update gradient fields
386  gradU_ = mask_*fvc::grad(U, "gradUStilda");
388 
389  const volScalarField Omega(::sqrt(2.0)*mag(skew(gradU_)));
390 
391  // Primal SA fields
392  volScalarField chi(this->chi());
393  volScalarField fv1(this->fv1(chi));
394  volScalarField fv2(this->fv2(chi, fv1));
395  Stilda_ = Stilda(chi, fv1);
396  r_ = r(Stilda_);
397  fw_ = this->fw(Stilda_);
398 
399  // Derivatives of primal fields wrt to nuTilda
400  volScalarField dFv1_dChi(this->dFv1_dChi(chi));
403  (this->dStilda_dNuTilda(Omega, fv2, dFv2_dChi));
407 
408  // Fields to be used in the nuaTilda equation
410  symm(mask_*fvc::grad(U, "adjointProductionU"));
411 
413  nuTilda()
414  *(
417  );
418 
420 
421  // Constant multiplier in the adjoint momentum source term
425 
427  2.*skew(gradU_)
428  /(Omega + dimensionedScalar("SMALL", Omega.dimensions(), SMALL))
429  *(
432  );
433 
434  // Set changedPrimalSolution_ to false to avoid recomputing these
435  // fields unless the primal has changed
436  changedPrimalSolution_ = false;
437  }
438 }
439 
440 
442 {
443  tmp<volScalarField> mask;
445  {
447  }
448  else
449  {
450  mask = tmp<volScalarField>
451  (
452  new volScalarField
453  (
454  IOobject
455  (
456  "unitMask",
457  mesh_.time().timeName(),
458  mesh_,
461  ),
462  mesh_,
463  dimensionedScalar("unit", dimless, scalar(1))
464  )
465  );
466  }
467 
468  return mask;
469 }
470 
471 
472 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
473 
474 adjointSpalartAllmaras::adjointSpalartAllmaras
475 (
476  incompressibleVars& primalVars,
478  objectiveManager& objManager,
479  const word& adjointTurbulenceModelName,
480  const word& modelName
481 )
482 :
484  (
485  modelName,
486  primalVars,
487  adjointVars,
488  objManager,
489  adjointTurbulenceModelName
490  ),
491 
492  sigmaNut_
493  (
494  dimensioned<scalar>::getOrAddToDict
495  (
496  "sigmaNut",
497  this->coeffDict_,
498  0.66666
499  )
500  ),
501  kappa_
502  (
503  dimensioned<scalar>::getOrAddToDict
504  (
505  "kappa",
506  this->coeffDict_,
507  0.41
508  )
509  ),
510 
511  Cb1_
512  (
513  dimensioned<scalar>::getOrAddToDict
514  (
515  "Cb1",
516  this->coeffDict_,
517  0.1355
518  )
519  ),
520  Cb2_
521  (
522  dimensioned<scalar>::getOrAddToDict
523  (
524  "Cb2",
525  this->coeffDict_,
526  0.622
527  )
528  ),
529  Cw1_(Cb1_/sqr(kappa_) + (1.0 + Cb2_)/sigmaNut_),
530  Cw2_
531  (
532  dimensioned<scalar>::getOrAddToDict
533  (
534  "Cw2",
535  this->coeffDict_,
536  0.3
537  )
538  ),
539  Cw3_
540  (
541  dimensioned<scalar>::getOrAddToDict
542  (
543  "Cw3",
544  this->coeffDict_,
545  2.0
546  )
547  ),
548  Cv1_
549  (
550  dimensioned<scalar>::getOrAddToDict
551  (
552  "Cv1",
553  this->coeffDict_,
554  7.1
555  )
556  ),
557  Cs_
558  (
559  dimensioned<scalar>::getOrAddToDict
560  (
561  "Cs",
562  this->coeffDict_,
563  0.3
564  )
565  ),
566 
567  limitAdjointProduction_
568  (
569  coeffDict_.getOrDefault("limitAdjointProduction", true)
570  ),
571 
572  y_(primalVars_.RASModelVariables()().d()),
573 
574  mask_(allocateMask()),
575 
576  symmAdjointProductionU_
577  (
578  IOobject
579  (
580  "symmAdjointProductionU",
581  runTime_.timeName(),
582  mesh_,
583  IOobject::NO_READ,
584  IOobject::NO_WRITE
585  ),
586  mesh_,
588  ),
589 
590  productionDestructionSource_
591  (
592  IOobject
593  (
594  "productionDestructionSource",
595  runTime_.timeName(),
596  mesh_,
597  IOobject::NO_READ,
598  IOobject::NO_WRITE
599  ),
600  mesh_,
602  ),
603 
604  Stilda_
605  (
606  IOobject
607  (
608  "Stilda",
609  runTime_.timeName(),
610  mesh_,
611  IOobject::NO_READ,
612  IOobject::NO_WRITE
613  ),
614  mesh_,
616  ),
617 
618  r_
619  (
620  IOobject
621  (
622  "r",
623  runTime_.timeName(),
624  mesh_,
625  IOobject::NO_READ,
626  IOobject::NO_WRITE
627  ),
628  mesh_,
630  ),
631 
632  fw_
633  (
634  IOobject
635  (
636  "fw",
637  runTime_.timeName(),
638  mesh_,
639  IOobject::NO_READ,
640  IOobject::NO_WRITE
641  ),
642  mesh_,
644  ),
645 
646  Cdnut_
647  (
648  IOobject
649  (
650  "Cdnut",
651  runTime_.timeName(),
652  mesh_,
653  IOobject::NO_READ,
654  IOobject::NO_WRITE
655  ),
656  mesh_,
658  ),
659 
660  momentumSourceMult_
661  (
662  IOobject
663  (
664  "momentumSourceMult",
665  runTime_.timeName(),
666  mesh_,
667  IOobject::NO_READ,
668  IOobject::NO_WRITE
669  ),
670  mesh_,
672  ),
673 
674  gradU_(fvc::grad(primalVars.U())),
675  gradNuTilda_(fvc::grad(nuTilda())),
676  minStilda_("SMALL", Stilda_.dimensions(), SMALL)
677 {
679  adjointTMVariablesBaseNames_[0] = "nuaTilda";
680  // Read nuaTilda field and reset pointer to the first
681  // adjoint turbulence model variable
683  (
685  mesh_,
686  "nuaTilda",
687  adjointVars.solverName(),
688  adjointVars.useSolverNameForFields()
689  );
690 
691  setMeanFields();
692 
693  // Set the includeDistance to true, to allow for the automatic solution
694  // of the adjoint eikonal equation when computing sensitivities
695  includeDistance_ = true;
696 
697  // Update the primal related fields here so that functions computing
698  // sensitivities have the updated fields in case of continuation
700 }
701 
702 
703 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
704 
706 {
707  const volVectorField& Ua = adjointVars_.UaInst();
708  return devReff(Ua);
709 }
710 
711 
713 (
714  const volVectorField& U
715 ) const
716 {
717  return
719  (
720  IOobject
721  (
722  "devRhoReff",
723  runTime_.timeName(),
724  mesh_,
727  ),
729  );
730 }
731 
732 
734 {
735  tmp<volScalarField> tnuEff(nuEff());
736  const volScalarField& nuEff = tnuEff();
737 
738  return
739  (
740  - fvm::laplacian(nuEff, Ua)
741  - fvc::div(nuEff*dev(fvc::grad(Ua)().T()))
742  );
743 }
744 
745 
747 {
749  (adjointSpalartAllmaras, "adjointSpalartAllmaras::addMomentumSource");
750  // cm formulation
751  //return (- nuTilda()*fvc::grad(nuaTilda() - conservativeMomentumSource());
752 
753  // ncm formulation
755 }
756 
757 
759 {
760  volScalarField chi(this->chi());
761  volScalarField fv1(this->fv1(chi));
762  volScalarField dFv1_dChi(this->dFv1_dChi(chi));
763 
764  return dnut_dNuTilda(fv1, dFv1_dChi);
765 }
766 
767 
769 {
770  tmp<scalarField> tdiffCoeff
771  (
772  new scalarField(mesh_.boundary()[patchI].size(), Zero)
773  );
774 
775  scalarField& diffCoeff = tdiffCoeff.ref();
776 
777  diffCoeff =
778  (nuTilda().boundaryField()[patchI] + nu()().boundaryField()[patchI])
780 
781  return tdiffCoeff;
782 }
783 
784 
785 const boundaryVectorField&
787 {
788  // Computed in conservativeMomentumSource
789  return adjMomentumBCSourcePtr_();
790 }
791 
792 
794 {
796 
797  forAll(mesh_.boundary(), patchI)
798  {
799  const fvPatch& patch = mesh_.boundary()[patchI];
800 
801  tmp<vectorField> tnf = patch.nf();
802  if (isA<wallFvPatch>(patch) && patch.size() != 0)
803  {
804  wallShapeSens[patchI] =
805  - nuaTilda().boundaryField()[patchI].snGrad()
806  *diffusionCoeffVar1(patchI)
807  *nuTilda().boundaryField()[patchI].snGrad()*tnf;
808  }
809  }
810 
811  return wallShapeSens;
812 }
813 
814 
816 {
818 
819  forAll(mesh_.boundary(), patchI)
820  {
821  tmp<vectorField> tnf = mesh_.boundary()[patchI].nf();
822 
823  wallFloCoSens[patchI] =
824  nuaTilda().boundaryField()[patchI]
825  *nuTilda().boundaryField()[patchI]*tnf;
826  }
827 
828  return wallFloCoSens;
829 }
830 
831 
833 {
834  const volVectorField& U = primalVars_.U();
835  const volVectorField& Ua = adjointVars_.Ua();
836 
837  // Primal SA fields
838  volScalarField chi(this->chi());
839  volScalarField fv1(this->fv1(chi));
840  volScalarField fv2(this->fv2(chi, fv1));
841  volScalarField Omega(::sqrt(2.0)*mag(gradU_));
842 
843  // Derivatives of primal fields wrt to nuTilda
844  volScalarField dFv1_dChi(this->dFv1_dChi(chi));
849  (this->dfw_dDelta(Stilda_, dfw_dr, dStilda_dDelta));
850 
851  auto tadjointEikonalSource =
853  (
854  "adjointEikonalSource" + type(),
855  (
857  + Cw1_*sqr(nuTilda()/y_)*(dfw_dDelta - 2.*fw_/y_)
858  )*nuaTilda()
859  );
860  volScalarField& adjointEikonalSource = tadjointEikonalSource.ref();
861 
862  // if wall functions are used, add appropriate source terms
864  SAwallFunctionPatchField;
865 
866  const volScalarField::Boundary& nutBoundary = nutRef().boundaryField();
867  const scalarField& V = mesh_.V().field();
868 
869  tmp<volScalarField> tnuEff = nuEff();
870  const volScalarField& nuEff = tnuEff();
871 
872  forAll(nutBoundary, patchi)
873  {
874  const fvPatch& patch = mesh_.boundary()[patchi];
875  if
876  (
877  isA<SAwallFunctionPatchField>(nutBoundary[patchi])
878  && patch.size() != 0
879  )
880  {
881  const scalar kappa_(0.41);
882  const scalar E_(9.8);
883  tmp<vectorField> tnf = patch.nf();
884  const vectorField& nf = tnf.ref();
885  const scalarField& magSf = patch.magSf();
886 
887  const fvPatchVectorField& Up = U.boundaryField()[patchi];
888  const fvPatchVectorField& Uap = Ua.boundaryField()[patchi];
889  const vectorField Uc(Up.patchInternalField());
890  const vectorField Uc_t(Uc - (Uc & nf)*nf);
891 
892  // By convention, tf has the direction of the tangent
893  // PRIMAL velocity at the first cell off the wall
894  const vectorField tf(Uc_t/mag(Uc_t));
895 
896  const scalarField nuw(nuEff.boundaryField()[patchi]);
897  const scalarField nu(this->nu()().boundaryField()[patchi]);
898  const fvPatchScalarField& yC = y()[patchi];
899 
900  const scalarField magGradU(mag(Up.snGrad()));
901 
902  // Note: What happens in separation?? sign change needed
903  const scalarField vtau(sqrt(nuw*magGradU));
904 
905  // Note: mag for positive uPlus
906  const scalarField uPlus(mag(Uc)/vtau);
907 
908  const scalarField yPlus(yC*vtau/nu);
909  const scalarField kUu(min(kappa_*uPlus, scalar(50)));
910  const scalarField auxA
911  ((kappa_/E_)*(exp(kUu) - 1 - kUu - 0.5*kUu*kUu));
912  const scalarField Cwf_d(sqr(vtau)/nu/(yPlus+uPlus*(1 + auxA)));
913 
914  // Tangential components are according to tf
915  autoPtr<boundaryAdjointContribution> boundaryContrPtr
916  (
918  (
919  "objectiveManager" + objectiveManager_.adjointSolverName(),
921  "incompressible",
922  patch
923  )
924  );
925  tmp<vectorField> tsource(boundaryContrPtr->normalVelocitySource());
926 
927  const scalarField rt(tsource() & tf);
928  const scalarField Uap_t(Uap & tf);
929 
930  const labelList& faceCells = patch.faceCells();
931  forAll(faceCells, faceI)
932  {
933  label cellI = faceCells[faceI];
934  adjointEikonalSource[cellI] -=
935  2.*( rt[faceI] + Uap_t[faceI] )
936  *vtau[faceI]*Cwf_d[faceI]*magSf[faceI]
937  /V[cellI]; // Divide with cell volume since the term
938  // will be used as a source term in the
939  // adjoint eikonal equation
940  }
941  }
942  }
943 
944  return tadjointEikonalSource;
945 }
946 
947 
949 {
950  const volVectorField& U = primalVars_.U();
951 
952  tmp<volTensorField> tgradU = fvc::grad(U);
953  const volTensorField& gradU = tgradU.cref();
954  tmp<volVectorField> tgradNuTilda = fvc::grad(nuTilda());
955  volVectorField& gradNuTilda = tgradNuTilda.ref();
956  tmp<volVectorField> tgradNuaTilda = fvc::grad(nuaTilda());
957  volVectorField::Boundary& gradNuaTildab =
958  tgradNuaTilda.ref().boundaryFieldRef();
959 
960  // Explicitly correct the boundary gradient to get rid of
961  // the tangential component
962  forAll(mesh_.boundary(), patchI)
963  {
964  const fvPatch& patch = mesh_.boundary()[patchI];
965  if (isA<wallFvPatch>(patch))
966  {
967  tmp<vectorField> tnf = patch.nf();
968  const vectorField& nf = tnf();
969  // gradU:: can cause problems in zeroGradient patches for U
970  // and zero fixedValue for nuTilda.
971  // S becomes 0 and is used as a denominator in G
972  //gradU.boundaryField()[patchI] =
973  // nf * U_.boundaryField()[patchI].snGrad();
974  gradNuTilda.boundaryFieldRef()[patchI] =
975  nf*nuTilda().boundaryField()[patchI].snGrad();
976  gradNuaTildab[patchI] =
977  nf*nuaTilda().boundaryField()[patchI].snGrad();
978  }
979  }
980 
981  // delta vorticity
982  volScalarField Omega(::sqrt(2.0)*mag(skew(gradU)));
983  volTensorField deltaOmega
984  (
985  (
986  (gradU & gradU)().T() //jk
987  - (gradU & gradU.T()) //symmetric
988  )
989  /(Omega + dimensionedScalar("SMALL", Omega.dimensions(), SMALL))
990  );
991  tgradU.clear();
992 
993  volScalarField chi(this->chi());
994  volScalarField fv1(this->fv1(chi));
995  volScalarField fv2(this->fv2(chi, fv1));
996 
1001 
1002  // Assemply of the return field
1003  auto tFISens
1004  (
1006  (
1007  IOobject
1008  (
1009  type() + "flowTerm",
1010  mesh_.time().timeName(),
1011  mesh_,
1014  ),
1015  mesh_,
1018  )
1019  );
1020  volTensorField& FISens = tFISens.ref();
1021  FISens =
1022  // jk, cm formulation for the TM model convection
1023  - (nuaTilda()*(U*gradNuTilda))
1024  // jk, symmetric in theory
1025  + nuaTilda()*fvc::grad(DnuTildaEff()*gradNuTilda)().T()
1026  // jk
1027  - DnuTildaEff()*(tgradNuaTilda*gradNuTilda)
1028  // symmetric
1029  + 2.*nuaTilda()*Cb2_/sigmaNut_*(gradNuTilda*gradNuTilda)
1030  + (
1033  )
1034  *nuaTilda()*deltaOmega; // jk
1036 
1037  return tFISens;
1038 }
1039 
1040 
1042 (
1043  const word& designVarsName
1044 ) const
1045 {
1046  auto tres(tmp<scalarField>::New(nuTilda().primitiveField().size(), Zero));
1047  scalarField nuTildaSens
1048  (nuTilda().primitiveField()*nuaTilda().primitiveField());
1049 
1052  (
1053  tres.ref(), nuTildaSens, fvOptions, nuTilda().name(), designVarsName
1054  );
1055 
1056  return tres;
1057 }
1058 
1061 {
1063 }
1064 
1065 
1067 {
1068  addProfiling
1069  (adjointSpalartAllmaras, "adjointSpalartAllmaras::correct");
1070  if (!adjointTurbulence_)
1071  {
1072  return;
1073  }
1074 
1076 
1078 
1080  const volVectorField& Ua = adjointVars_.UaInst();
1081 
1083  volScalarField gradUaR
1084  (
1086  );
1087 
1088  dimensionedScalar oneOverSigmaNut = 1./sigmaNut_;
1089 
1090  nuaTilda().storePrevIter();
1091 
1093 
1094  tmp<fvScalarMatrix> nuaTildaEqn
1095  (
1096  fvm::ddt(nuaTilda())
1097  + fvm::div(-phi, nuaTilda())
1099  // Note: Susp
1101  + fvc::laplacian(2.0*Cb2_*oneOverSigmaNut*nuaTilda(), nuTilda())
1102  + gradNua*oneOverSigmaNut
1103  ==
1104  // Always a negative contribution to the lhs. No Sp used!
1106  // Always a positive contribution to the lhs. no need for SuSp
1107  - fvm::Sp(Cw1_*fw_*nuTilda()/sqr(y_), nuaTilda())
1108  - Cdnut_*gradUaR
1109  + fvOptions(nuaTilda())
1110  );
1111 
1112  // Add sources from the objective functions
1113  objectiveManager_.addSource(nuaTildaEqn.ref());
1114 
1115  nuaTildaEqn.ref().relax();
1116  fvOptions.constrain(nuaTildaEqn.ref());
1117  solve(nuaTildaEqn);
1119  nuaTilda().relax();
1120 
1122  {
1123  dimensionedScalar maxDeltaNuaTilda =
1124  max(mag(nuaTilda() - nuaTilda().prevIter())());
1125  dimensionedScalar maxNuaTilda = max(mag(nuaTilda()));
1126  Info<< "Max mag (" << nuaTilda().name() << ") = "
1127  << maxNuaTilda.value() << endl;
1128  Info<< "Max mag (Delta " << nuaTilda().name() << ") = "
1129  << maxDeltaNuaTilda.value()
1130  << endl;
1131  }
1132 }
1133 
1134 
1136 {
1137  if (adjointRASModel::read())
1138  {
1140  kappa_.readIfPresent(this->coeffDict());
1141 
1142  Cb1_.readIfPresent(this->coeffDict());
1143  Cb2_.readIfPresent(this->coeffDict());
1144  Cw1_ = Cb1_/sqr(kappa_) + (1.0 + Cb2_)/sigmaNut_;
1145  Cw2_.readIfPresent(this->coeffDict());
1146  Cw3_.readIfPresent(this->coeffDict());
1147  Cv1_.readIfPresent(this->coeffDict());
1148  Cs_.readIfPresent(this->coeffDict());
1149 
1150  return true;
1151  }
1152  else
1153  {
1154  return false;
1155  }
1156 }
1157 
1158 
1159 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
1160 
1161 } // End namespace adjointRASModels
1162 } // End namespace incompressibleAdjoint
1163 } // End namespace Foam
1164 
1165 // ************************************************************************* //
dictionary coeffDict_
Model coefficients dictionary.
static const word & zeroGradientType() noexcept
The type name for zeroGradient patch fields.
Definition: fvPatchField.H:221
void updatePrimalRelatedFields()
Update the constant primal-related fields.
scalar uPlus
tmp< volScalarField > dfw_dNuTilda(const volScalarField &Stilda, const volScalarField &dfwdr, const volScalarField &dStildadNuTilda) const
virtual const boundaryVectorField & wallShapeSensitivities()
Sensitivity terms for shape optimisation, emerging from the turbulence model differentiation.
const Type & value() const noexcept
Return const reference to value.
tmp< GeometricField< typename outerProduct< vector, Type >::type, fvPatchField, volMesh >> grad(const GeometricField< Type, fvsPatchField, surfaceMesh > &ssf)
Definition: fvcGrad.C:47
bool includeDistance_
Does the turbulence model include distances and should the adjoint to the distance field be computed...
virtual tmp< Field< Type > > snGrad() const
Return patch-normal gradient.
Definition: fvPatchField.C:213
tmp< volScalarField > dP_dNuTilda(const volScalarField &dStildadNuTilda) const
virtual tmp< scalarField > diffusionCoeffVar1(label patchI) const
Diffusion coefficient of the first primal and adjoint turbulence model equation. Needed for some adjo...
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
static autoPtr< boundaryAdjointContribution > New(const word &managerName, const word &adjointSolverName, const word &simulationType, const fvPatch &patch)
Return a reference to the selected turbulence model.
dimensionedTensor skew(const dimensionedTensor &dt)
const word & name() const noexcept
Return the object name.
Definition: IOobjectI.H:195
T & ref() const
Return non-const reference to the contents of a non-null managed pointer.
Definition: tmpI.H:235
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
GeometricField< tensor, fvPatchField, volMesh > volTensorField
Definition: volFieldsFwd.H:86
dimensionedSymmTensor sqr(const dimensionedVector &dv)
tmp< GeometricField< Type, fvPatchField, volMesh > > div(const GeometricField< Type, fvsPatchField, surfaceMesh > &ssf)
Definition: fvcDiv.C:42
bool readIfPresent(const dictionary &dict)
Update the value of dimensioned<Type> if found in the dictionary, lookup in dictionary with the name(...
virtual tmp< volVectorField > adjointMeanFlowSource()
Source terms to the adjoint momentum equation due to the differentiation of the turbulence model...
const volScalarField & nuTilda() const
References to the primal turbulence model variables.
Class for managing objective functions.
virtual bool read()
Read adjointRASProperties dictionary.
tmp< volScalarField > dStilda_dNuTilda(const volScalarField &Omega, const volScalarField &fv2, const volScalarField &dFv2dChi) const
dimensionedScalar sqrt(const dimensionedScalar &ds)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
A finiteVolume patch using a polyPatch and a fvBoundaryMesh.
Definition: fvPatch.H:70
tmp< volScalarField > Stilda(const volScalarField &chi, const volScalarField &fv1) const
tmp< volScalarField > dFv2_dChi(const volScalarField &chi, const volScalarField &fv1, const volScalarField &dFv1dChi) const
const T & cref() const
Return const reference to the object or to the contents of a (non-null) managed pointer.
Definition: tmpI.H:221
Continuous adjoint to the Spalart-Allmaras one-eqn mixing-length model for incompressible flows...
volScalarField mask_
Field for masking (convergence enhancement)
virtual const boundaryVectorField & adjointMomentumBCSource() const
Source for the outlet adjoint momentum BC coming from differentiating the turbulence model...
static void nullifyField(GeometricField< Type, PatchField, GeoMesh > &fieldPtr)
Nullify field and old times, if present.
autoPtr< boundaryVectorField > adjMomentumBCSourcePtr_
Source to the adjoint momentum BC emerging from differentiating the turbulence model.
virtual void correct()=0
Solve the adjoint turbulence equations.
Ignore writing from objectRegistry::writeObject()
const dimensionSet dimless
Dimensionless.
autoPtr< boundaryVectorField > wallShapeSensitivitiesPtr_
Wall sensitivity term for shape optimisation.
Smooth ATC in cells next to a set of patches supplied by type.
Definition: faceCells.H:52
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:360
const DimensionedField< scalar, volMesh > & V() const
Return cell volumes.
Finite-volume options.
Definition: fvOptions.H:51
tmp< volScalarField > dD_dNuTilda(const volScalarField &fw, const volScalarField &dfwdNuTilda) const
tmp< GeometricField< Type, fvPatchField, volMesh > > laplacian(const GeometricField< Type, fvPatchField, volMesh > &vf, const word &name)
Definition: fvcLaplacian.C:40
tmp< volScalarField > r(const volScalarField &Stilda) const
dimensionedScalar neg(const dimensionedScalar &ds)
dimensionedScalar pow5(const dimensionedScalar &ds)
Macros for easy insertion into run-time selection tables.
fv::options & fvOptions
tmp< volScalarField > nu() const
Return the laminar viscosity.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
Switch adjointTurbulence_
Turbulence on/off flag.
GeometricField< scalar, fvPatchField, volMesh > volScalarField
Definition: volFieldsFwd.H:81
autoPtr< boundaryVectorField > wallFloCoSensitivitiesPtr_
Wall sensitivity term for flow control optimisation.
word timeName
Definition: getTimeIndex.H:3
dimensionedScalar pos(const dimensionedScalar &ds)
fileName::Type type(const fileName &name, const bool followLink=true)
Return the file type: DIRECTORY or FILE, normally following symbolic links.
Definition: POSIX.C:799
const surfaceScalarField & phi() const
Return const reference to volume flux.
Base class for solution control classes.
bool changedPrimalSolution_
Has the primal solution changed?
#define addProfiling(Name, Descr)
Define profiling trigger with specified name and description string.
tmp< volScalarField > dStilda_dDelta(const volScalarField &Omega, const volScalarField &fv2) const
tmp< volScalarField > dr_dNuTilda(const volScalarField &Stilda) const
This boundary condition provides a wall function for the turbulent viscosity (i.e. nut) based on velocity (i.e. U). Using Spalding&#39;s law gives a continuous nut profile to the wall.
void setSize(const label n)
Alias for resize()
Definition: List.H:316
tmp< volScalarField > dr_dDelta(const volScalarField &Stilda) const
dimensionedSymmTensor dev(const dimensionedSymmTensor &dt)
dimensionedScalar exp(const dimensionedScalar &ds)
bool useSolverNameForFields() const
Append solver name to fields?
Definition: variablesSet.C:83
A class for handling words, derived from Foam::string.
Definition: word.H:63
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
virtual tmp< volScalarField > nutJacobianTMVar1() const
Jacobian of nut wrt the first turbulence model variable.
Abstract base class for incompressible turbulence models.
label size() const noexcept
The number of entries in the list.
Definition: UPtrListI.H:106
bool printMaxMags() const
Print max mags of solver fields.
tmp< fvMatrix< Type > > ddt(const GeometricField< Type, fvPatchField, volMesh > &vf)
Definition: fvmDdt.C:41
const volVectorField & Ua() const
Return const reference to velocity.
volScalarField & nuaTilda()
Access to the adjoint Spalart - Allmaras field.
static tmp< volScalarField > createLimiter(const fvMesh &mesh, const dictionary &dict)
Return a limiter based on given cells. For use with classes other than ATC which employ the same smoo...
Definition: ATCModel.C:216
static tmp< T > New(Args &&... args)
Construct tmp with forwarding arguments.
Definition: tmp.H:206
zeroField SuSp(const Foam::zero, const GeometricField< Type, fvPatchField, volMesh > &)
A no-op source.
wordList adjointTMVariablesBaseNames_
Base names of the adjoint fields.
dimensionedScalar tr(const dimensionedSphericalTensor &dt)
addToRunTimeSelectionTable(adjointRASModel, adjointkOmegaSST, dictionary)
Calculation of adjoint based sensitivities for topology optimisation. This returns just the field par...
dimensioned< tensor > dimensionedTensor
Dimensioned tensor obtained from generic dimensioned type.
autoPtr< volScalarField > adjointTMVariable1Ptr_
Adjoint turbulence model variable 1.
const word & solverName() const
Return solver name.
Definition: variablesSet.C:77
virtual tmp< volSymmTensorField > devReff() const
Return the effective stress tensor including the laminar stress.
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:26
zeroField Sp(const Foam::zero, const GeometricField< Type, fvPatchField, volMesh > &)
A no-op source.
SolverPerformance< Type > solve(faMatrix< Type > &, const dictionary &solverControls)
Solve returning the solution statistics given convergence tolerance.
const volVectorField & U() const
Return const reference to velocity.
const uniformDimensionedVectorField & g
static word timeName(const scalar t, const int precision=precision_)
Return a time name for the given scalar time value formatted with the given precision.
Definition: Time.C:714
tmp< fvMatrix< Type > > div(const surfaceScalarField &flux, const GeometricField< Type, fvPatchField, volMesh > &vf, const word &name)
Definition: fvmDiv.C:41
virtual tmp< Field< Type > > patchInternalField() const
Return internal field next to patch.
Definition: fvPatchField.C:221
tmp< volScalarField > dStilda_dOmega(const volScalarField &Omega, const volScalarField &fv2) const
virtual void correct()
Solve the adjoint turbulence equations.
const dictionary & coeffDict() const
Const access to the coefficients dictionary.
tmp< volScalarField > dFv1_dChi(const volScalarField &chi) const
void T(FieldField< Field, Type > &f1, const FieldField< Field, Type > &f2)
const solverControl & getSolverControl() const
Return const reference to solverControl.
static void postProcessSens(scalarField &sens, scalarField &auxSens, fv::options &fvOptions, const word &fieldName, const word &designVariablesName)
Add part of the sensitivities coming from fvOptions.
virtual void addSource(fvVectorMatrix &matrix)
Add contribution to adjoint momentum PDEs.
const autoPtr< incompressible::RASModelVariables > & RASModelVariables() const
Return const reference to the turbulence model variables.
tmp< volScalarField > dnut_dNuTilda(const volScalarField &fv1, const volScalarField &dFv1dChi) const
const word & adjointSolverName() const
Return name of the adjointSolver.
Manages the adjoint mean flow fields and their mean values.
const volVectorField & UaInst() const
Return const reference to velocity.
dimensionedScalar pow(const dimensionedScalar &ds, const dimensionedScalar &expt)
dimensionedScalar pow3(const dimensionedScalar &ds)
tmp< volScalarField > dfw_dOmega(const volScalarField &Stilda, const volScalarField &dfwdr, const volScalarField &dStildadOmega) const
virtual tmp< scalarField > topologySensitivities(const word &designVarsName) const
Term contributing to the computation of topology optimisation sensitivities.
U
Definition: pEqn.H:72
void relax(const scalar alpha)
Relax field (for steady-state solution).
const dimensionSet dimLength(0, 1, 0, 0, 0, 0, 0)
Definition: dimensionSets.H:50
dimensionedSymmTensor symm(const dimensionedSymmTensor &dt)
virtual tmp< fvVectorMatrix > divDevReff(volVectorField &U) const
Return the diffusion term for the momentum equation.
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
tmp< fvMatrix< Type > > laplacian(const GeometricField< Type, fvPatchField, volMesh > &vf, const word &name)
Definition: fvmLaplacian.C:41
virtual const boundaryVectorField & wallFloCoSensitivities()
Sensitivity terms for flow control, emerging from the turbulence model differentiation.
scalar yPlus
tmp< volScalarField > fw(const volScalarField &Stilda) const
Nothing to be read.
tmp< volScalarField > fv2(const volScalarField &chi, const volScalarField &fv1) const
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
Boundary & boundaryFieldRef(const bool updateAccessTime=true)
Return a reference to the boundary field.
tmp< volScalarField > dfw_dr(const volScalarField &Stilda) const
void correctBoundaryConditions()
Correct boundary field.
virtual void nullify()
Nullify all adjoint turbulence model fields and their old times.
dimensionedScalar pow6(const dimensionedScalar &ds)
messageStream Info
Information stream (stdout output on master, null elsewhere)
objectiveManager & objectiveManager_
Reference to the objectiveManager.
Internal & ref(const bool updateAccessTime=true)
Same as internalFieldRef()
const fvBoundaryMesh & boundary() const noexcept
Return reference to boundary mesh.
Definition: fvMesh.H:395
tmp< volScalarField > dr_dStilda(const volScalarField &Stilda) const
void storePrevIter() const
Store the field as the previous iteration value.
void clear() const noexcept
If object pointer points to valid object: delete object and set pointer to nullptr.
Definition: tmpI.H:289
Pointer management similar to std::unique_ptr, with some additional methods and type checking...
Definition: HashPtrTable.H:48
virtual tmp< volScalarField > distanceSensitivities()
Sensitivity terms resulting from the differentiation of the distance field. Misses dxdb...
virtual tmp< volScalarField > nuEff() const
Return the effective viscosity.
tmp< volScalarField > fv1(const volScalarField &chi) const
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 tmp< volTensorField > FISensitivityTerm()
Term contributing to the computation of FI-based sensitivities.
tmp< volVectorField > conservativeMomentumSource()
Conservative source term for the adjoint momentum equations.
Defines the attributes of an object for which implicit objectRegistry management is supported...
Definition: IOobject.H:172
const nearWallDist & y() const
Return the near wall distances.
const Boundary & boundaryField() const noexcept
Return const-reference to the boundary field.
tmp< volScalarField > dfw_dDelta(const volScalarField &Stilda, const volScalarField &dfwdr, const volScalarField &dStildadDelta) const
bool limitAdjointProduction_
Whether to limit the adjoint production term to enhance stability.
static void setField(autoPtr< GeometricField< Type, fvPatchField, volMesh >> &fieldPtr, const fvMesh &mesh, const word &baseName, const word &solverName, const bool useSolverNameForFields)
Read vol fields.
Namespace for OpenFOAM.
tmp< GeometricField< typename outerProduct< vector, Type >::type, faPatchField, areaMesh >> grad(const GeometricField< Type, faePatchField, edgeMesh > &ssf)
Definition: facGrad.C:51
const dimensionSet & dimensions() const noexcept
Return dimensions.
SymmTensor< Cmpt > devTwoSymm(const SymmTensor< Cmpt > &st)
Return the deviatoric part of twice the symmetric part of a SymmTensor.
Definition: SymmTensorI.H:491
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127