11
11
12
12
"""
13
13
14
- __version__ = "2.2.5 "
14
+ __version__ = "2.2.6 "
15
15
16
16
import subprocess
17
17
import os
@@ -57,6 +57,7 @@ class DAOPTION(object):
57
57
## - DASimpleFoam: Incompressible steady-state flow solver for Navier-Stokes equations
58
58
## - DASimpleTFoam: Incompressible steady-state flow solver for Navier-Stokes equations with temperature
59
59
## - DAPisoFoam: Incompressible transient flow solver for Navier-Stokes equations
60
+ ## - DAPimpleFoam: Incompressible transient flow solver for Navier-Stokes equations
60
61
## - DARhoSimpleFoam: Compressible steady-state flow solver for Navier-Stokes equations (subsonic)
61
62
## - DARhoSimpleCFoam: Compressible steady-state flow solver for Navier-Stokes equations (transonic)
62
63
## - DATurboFoam: Compressible steady-state flow solver for Navier-Stokes equations (turbomachinery)
@@ -228,6 +229,20 @@ class DAOPTION(object):
228
229
## "addToAdjoint": True,
229
230
## }
230
231
## },
232
+ ## "THRUST": {
233
+ ## "part1": {
234
+ ## "type": "variableVolSum",
235
+ ## "source": "boxToCell",
236
+ ## "min": [-50.0, -50.0, -50.0],
237
+ ## "max": [50.0, 50.0, 50.0],
238
+ ## "varName": "fvSource",
239
+ ## "varType": "vector",
240
+ ## "component": 0,
241
+ ## "isSquare": 0,
242
+ ## "scale": 1.0,
243
+ ## "addToAdjoint": True,
244
+ ## },
245
+ ## },
231
246
## "FI": {
232
247
## "part1": {
233
248
## "type": "stateErrorNorm",
@@ -397,9 +412,10 @@ class DAOPTION(object):
397
412
## This is used only for transonic solvers such as DARhoSimpleCFoam
398
413
transonicPCOption = - 1
399
414
400
- ## Options for hybrid adjoint. Here nTimeInstances is the number of time instances
401
- ## periodicity is the periodicity of flow oscillation
402
- hybridAdjoint = {"active" : False , "nTimeInstances" : - 1 , "periodicity" : - 1.0 }
415
+ ## Options for unsteady adjoint. mode can be hybridAdjoint or timeAccurateAdjoint
416
+ ## Here nTimeInstances is the number of time instances and periodicity is the
417
+ ## periodicity of flow oscillation (hybrid adjoint only)
418
+ unsteadyAdjoint = {"mode" : "None" , "nTimeInstances" : - 1 , "periodicity" : - 1.0 }
403
419
404
420
## At which iteration should we start the averaging of objective functions.
405
421
## This is only used for unsteady solvers
@@ -703,6 +719,12 @@ def __init__(self, comm=None, options=None):
703
719
# initialize the adjoint vector dict
704
720
self .adjVectors = self ._initializeAdjVectors ()
705
721
722
+ # initialize the dRdWOldTPsi vectors
723
+ self ._initializeTimeAccurateAdjointVectors ()
724
+
725
+ # check if the combination of options is valid.
726
+ self ._checkOptions ()
727
+
706
728
Info ("pyDAFoam initialization done!" )
707
729
708
730
return
@@ -714,9 +736,9 @@ def _solverRegistry(self):
714
736
"""
715
737
716
738
self .solverRegistry = {
717
- "Incompressible" : ["DASimpleFoam" , "DASimpleTFoam" , "DAPisoFoam" ],
739
+ "Incompressible" : ["DASimpleFoam" , "DASimpleTFoam" , "DAPisoFoam" , "DAPimpleFoam" ],
718
740
"Compressible" : ["DARhoSimpleFoam" , "DARhoSimpleCFoam" , "DATurboFoam" ],
719
- "Solid" : ["DASolidDisplacementFoam" ],
741
+ "Solid" : ["DASolidDisplacementFoam" , "DALaplacianFoam" , "DAScalarTransportFoam" ],
720
742
}
721
743
722
744
def __call__ (self ):
@@ -831,6 +853,62 @@ def _initializeAdjTotalDeriv(self):
831
853
832
854
return adjTotalDeriv
833
855
856
+ def _initializeTimeAccurateAdjointVectors (self ):
857
+ """
858
+ Initialize the dRdWTPsi vectors for time accurate adjoint.
859
+ Here we need to initialize current time step and two previous
860
+ time steps 0 and 00 for both state and residuals. This is
861
+ because the backward ddt scheme depends on U, U0, and U00
862
+ """
863
+ if self .getOption ("unsteadyAdjoint" )["mode" ] == "timeAccurateAdjoint" :
864
+ objFuncDict = self .getOption ("objFunc" )
865
+ wSize = self .solver .getNLocalAdjointStates ()
866
+ self .dRdW0TPsi = {}
867
+ self .dRdW00TPsi = {}
868
+ self .dR0dW0TPsi = {}
869
+ self .dR0dW00TPsi = {}
870
+ self .dR00dW0TPsi = {}
871
+ self .dR00dW00TPsi = {}
872
+ for objFuncName in objFuncDict :
873
+ if objFuncName in self .objFuncNames4Adj :
874
+ vecA = PETSc .Vec ().create (PETSc .COMM_WORLD )
875
+ vecA .setSizes ((wSize , PETSc .DECIDE ), bsize = 1 )
876
+ vecA .setFromOptions ()
877
+ vecA .zeroEntries ()
878
+ self .dRdW0TPsi [objFuncName ] = vecA
879
+
880
+ vecB = vecA .duplicate ()
881
+ vecB .zeroEntries ()
882
+ self .dRdW00TPsi [objFuncName ] = vecB
883
+
884
+ vecC = vecA .duplicate ()
885
+ vecC .zeroEntries ()
886
+ self .dR0dW0TPsi [objFuncName ] = vecC
887
+
888
+ vecD = vecA .duplicate ()
889
+ vecD .zeroEntries ()
890
+ self .dR0dW00TPsi [objFuncName ] = vecD
891
+
892
+ vecE = vecA .duplicate ()
893
+ vecE .zeroEntries ()
894
+ self .dR00dW0TPsi [objFuncName ] = vecE
895
+
896
+ vecF = vecA .duplicate ()
897
+ vecF .zeroEntries ()
898
+ self .dR00dW00TPsi [objFuncName ] = vecF
899
+
900
+ def zeroTimeAccurateAdjointVectors (self ):
901
+ if self .getOption ("unsteadyAdjoint" )["mode" ] == "timeAccurateAdjoint" :
902
+ objFuncDict = self .getOption ("objFunc" )
903
+ for objFuncName in objFuncDict :
904
+ if objFuncName in self .objFuncNames4Adj :
905
+ self .dRdW0TPsi [objFuncName ].zeroEntries ()
906
+ self .dRdW00TPsi [objFuncName ].zeroEntries ()
907
+ self .dR0dW0TPsi [objFuncName ].zeroEntries ()
908
+ self .dR0dW00TPsi [objFuncName ].zeroEntries ()
909
+ self .dR00dW0TPsi [objFuncName ].zeroEntries ()
910
+ self .dR00dW00TPsi [objFuncName ].zeroEntries ()
911
+
834
912
def _calcObjFuncNames4Adj (self ):
835
913
"""
836
914
Compute the objective function names for which we solve the adjoint equation
@@ -857,6 +935,20 @@ def _calcObjFuncNames4Adj(self):
857
935
raise Error ("addToAdjoint can be either True or False" )
858
936
return objFuncList
859
937
938
+ def _checkOptions (self ):
939
+ """
940
+ Check if the combination of options are valid.
941
+ For example, if timeAccurateAdjoint is active, we have to set
942
+ adjJacobianOption = JacobianFree
943
+ NOTE: we should add all possible checks here!
944
+ """
945
+ # check time accurate adjoint
946
+ if self .getOption ("unsteadyAdjoint" )["mode" ] == "timeAccurateAdjoint" :
947
+ if not self .getOption ("adjJacobianOption" ) == "JacobianFree" :
948
+ raise Error ("timeAccurateAdjoint only supports adjJacobianOption=JacobianFree!" )
949
+
950
+ # check other combinations...
951
+
860
952
def saveMultiPointField (self , indexMP ):
861
953
"""
862
954
Save the state variable vector to self.wVecMPList
@@ -885,18 +977,76 @@ def setMultiPointField(self, indexMP):
885
977
886
978
return
887
979
980
+ def calcPrimalResidualStatistics (self , mode ):
981
+ if self .getOption ("adjJacobianOption" ) == "JacobianFD" :
982
+ self .solver .calcPrimalResidualStatistics (mode .encode ())
983
+ elif self .getOption ("adjJacobianOption" ) == "JacobianFree" :
984
+ self .solverAD .calcPrimalResidualStatistics (mode .encode ())
985
+
888
986
def setTimeInstanceField (self , instanceI ):
889
987
"""
890
988
Set the OpenFOAM state variables based on instance index
891
989
"""
892
990
893
- self .solver .setTimeInstanceField (instanceI )
991
+ if self .getOption ("adjJacobianOption" ) == "JacobianFD" :
992
+ solver = self .solver
993
+ elif self .getOption ("adjJacobianOption" ) == "JacobianFree" :
994
+ solver = self .solverAD
995
+
996
+ solver .setTimeInstanceField (instanceI )
894
997
# NOTE: we need to set the OF field to wVec vector here!
895
998
# this is because we will assign self.wVec to the solveAdjoint function later
896
- self . solver .ofField2StateVec (self .wVec )
999
+ solver .ofField2StateVec (self .wVec )
897
1000
898
1001
return
899
1002
1003
+ def initTimeInstanceMats (self ):
1004
+
1005
+ nLocalAdjointStates = self .solver .getNLocalAdjointStates ()
1006
+ nLocalAdjointBoundaryStates = self .solver .getNLocalAdjointBoundaryStates ()
1007
+ nTimeInstances = - 99999
1008
+ adjMode = self .getOption ("unsteadyAdjoint" )["mode" ]
1009
+ if adjMode == "hybridAdjoint" or adjMode == "timeAccurateAdjoint" :
1010
+ nTimeInstances = self .getOption ("unsteadyAdjoint" )["nTimeInstances" ]
1011
+
1012
+ self .stateMat = PETSc .Mat ().create (PETSc .COMM_WORLD )
1013
+ self .stateMat .setSizes (((nLocalAdjointStates , None ), (None , nTimeInstances )))
1014
+ self .stateMat .setFromOptions ()
1015
+ self .stateMat .setPreallocationNNZ ((nTimeInstances , nTimeInstances ))
1016
+ self .stateMat .setUp ()
1017
+
1018
+ self .stateBCMat = PETSc .Mat ().create (PETSc .COMM_WORLD )
1019
+ self .stateBCMat .setSizes (((nLocalAdjointBoundaryStates , None ), (None , nTimeInstances )))
1020
+ self .stateBCMat .setFromOptions ()
1021
+ self .stateBCMat .setPreallocationNNZ ((nTimeInstances , nTimeInstances ))
1022
+ self .stateBCMat .setUp ()
1023
+
1024
+ self .timeVec = PETSc .Vec ().createSeq (nTimeInstances , bsize = 1 , comm = PETSc .COMM_SELF )
1025
+ self .timeIdxVec = PETSc .Vec ().createSeq (nTimeInstances , bsize = 1 , comm = PETSc .COMM_SELF )
1026
+
1027
+ def initOldTimes (self ):
1028
+ # No need to initialize oldTimes for FD
1029
+ if self .getOption ("adjJacobianOption" ) == "JacobianFree" :
1030
+ self .solverAD .initOldTimes ()
1031
+
1032
+ def setTimeInstanceVar (self , mode ):
1033
+
1034
+ if mode == "list2Mat" :
1035
+ self .solver .setTimeInstanceVar (mode .encode (), self .stateMat , self .stateBCMat , self .timeVec , self .timeIdxVec )
1036
+ elif mode == "mat2List" :
1037
+ if self .getOption ("adjJacobianOption" ) == "JacobianFD" :
1038
+ self .solver .setTimeInstanceVar (
1039
+ mode .encode (), self .stateMat , self .stateBCMat , self .timeVec , self .timeIdxVec
1040
+ )
1041
+ elif self .getOption ("adjJacobianOption" ) == "JacobianFree" :
1042
+ self .solverAD .setTimeInstanceVar (
1043
+ mode .encode (), self .stateMat , self .stateBCMat , self .timeVec , self .timeIdxVec
1044
+ )
1045
+ else :
1046
+ raise Error ("adjJacobianOption can only be either JacobianFD or JacobianFree!" )
1047
+ else :
1048
+ raise Error ("mode can only be either mat2List or list2Mat!" )
1049
+
900
1050
def writeDesignVariable (self , fileName , xDV ):
901
1051
"""
902
1052
Write the design variable history to files in the json format
@@ -1703,12 +1853,26 @@ def solveAdjoint(self):
1703
1853
elif self .getOption ("adjJacobianOption" ) == "JacobianFree" :
1704
1854
self .solverAD .calcdFdWAD (self .xvVec , self .wVec , objFuncName .encode (), dFdW )
1705
1855
1856
+ # if it is time accurate adjoint, add extra terms for dFdW
1857
+ if self .getOption ("unsteadyAdjoint" )["mode" ] == "timeAccurateAdjoint" :
1858
+ # first copy the vectors from previous residual time step level
1859
+ self .dR0dW0TPsi [objFuncName ].copy (self .dR00dW0TPsi [objFuncName ])
1860
+ self .dR0dW00TPsi [objFuncName ].copy (self .dR00dW00TPsi [objFuncName ])
1861
+ self .dRdW0TPsi [objFuncName ].copy (self .dR0dW0TPsi [objFuncName ])
1862
+ self .dRdW00TPsi [objFuncName ].copy (self .dR0dW00TPsi [objFuncName ])
1863
+ dFdW .axpy (- 1.0 , self .dR0dW0TPsi [objFuncName ])
1864
+ dFdW .axpy (- 1.0 , self .dR00dW00TPsi [objFuncName ])
1865
+
1706
1866
# Initialize the adjoint vector psi and solve for it
1707
1867
if self .getOption ("adjJacobianOption" ) == "JacobianFD" :
1708
1868
self .adjointFail = self .solver .solveLinearEqn (ksp , dFdW , self .adjVectors [objFuncName ])
1709
1869
elif self .getOption ("adjJacobianOption" ) == "JacobianFree" :
1710
1870
self .adjointFail = self .solverAD .solveLinearEqn (ksp , dFdW , self .adjVectors [objFuncName ])
1711
1871
1872
+ if self .getOption ("unsteadyAdjoint" )["mode" ] == "timeAccurateAdjoint" :
1873
+ self .solverAD .calcdRdWOldTPsiAD (1 , self .adjVectors [objFuncName ], self .dRdW0TPsi [objFuncName ])
1874
+ self .solverAD .calcdRdWOldTPsiAD (2 , self .adjVectors [objFuncName ], self .dRdW00TPsi [objFuncName ])
1875
+
1712
1876
dFdW .destroy ()
1713
1877
1714
1878
ksp .destroy ()
@@ -2206,6 +2370,10 @@ def _initSolver(self):
2206
2370
if self .getOption ("printDAOptions" ):
2207
2371
self .solver .printAllOptions ()
2208
2372
2373
+ adjMode = self .getOption ("unsteadyAdjoint" )["mode" ]
2374
+ if adjMode == "hybridAdjoint" or adjMode == "timeAccurateAdjoint" :
2375
+ self .initTimeInstanceMats ()
2376
+
2209
2377
self .solverInitialized = 1
2210
2378
2211
2379
return
0 commit comments