pyDAFoam.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 """
4 
5  DAFoam : Discrete Adjoint with OpenFOAM
6  Version : v3
7 
8  Description:
9  The Python interface to DAFoam. It controls the adjoint
10  solvers and external modules for design optimization
11 
12 """
13 
14 __version__ = "3.0.7"
15 
16 import subprocess
17 import os
18 import sys
19 import copy
20 import shutil
21 import numpy as np
22 from mpi4py import MPI
23 from collections import OrderedDict
24 import petsc4py
25 from petsc4py import PETSc
26 
27 petsc4py.init(sys.argv)
28 try:
29  import tensorflow as tf
30 except ImportError:
31  pass
32 
33 
34 class DAOPTION(object):
35  """
36  Define a set of options to use in PYDAFOAM and set their initial values.
37  This class will be used by PYDAFOAM._getDefOptions()
38 
39  NOTE: Give an initial value for a new option, this help PYDAFOAM determine the type of this
40  option. If it is a list, give at least one default value. If it is a dict, you can leave it
41  blank, e.g., {}. Also, use ## to add comments before the new option such that these comments
42  will be picked up by Doxygen. If possible, give examples.
43 
44  NOTE: We group these options into three categories.
45  - The basic options are those options that will be used for EVERY solvers and EVERY cases.
46  - The intermediate options are options that will be used in some of solvers for special
47  situation (e.g., primalVarBounds to prevent solution from divergence).
48  - The advanced options will be used in special situation to improve performance, e.g.,
49  maxResConLv4JacPCMat to reduce memory of preconditioner. Its usage is highly case dependent.
50  It may also include options that have a default value that is rarely changed, except for
51  very special situation.
52  """
53 
54  def __init__(self):
55 
56  # *********************************************************************************************
57  # *************************************** Basic Options ***************************************
58  # *********************************************************************************************
59 
60 
71  self.solverName = "DASimpleFoam"
72 
73 
75  self.primalMinResTol = 1.0e-8
76 
77 
88  self.primalBC = {}
89 
90 
95  self.normalizeStates = {}
96 
97 
278  self.objFunc = {}
279 
280 
301  self.designVar = {}
302 
303 
305  self.designSurfaces = ["ALL_OPENFOAM_WALL_PATCHES"]
306 
307 
312  self.couplingInfo = {
313  "aerostructural": {
314  "active": False,
315  "pRef": 0,
316  "propMovement": False,
317  "couplingSurfaceGroups": {
318  "wingGroup": ["wing", "wing_te"],
319  },
320  },
321  "aerothermal": {
322  "active": False,
323  "couplingSurfaceGroups": {
324  "wallGroup": ["fin_wall"],
325  },
326  },
327  "aeroacoustic": {
328  "active": False,
329  "pRef": 0,
330  "couplingSurfaceGroups": {
331  "blade1Group": ["blade1_ps", "blade1_ss"],
332  "blade2Group": ["blade2"],
333  },
334  },
335  }
336 
337 
338  self.aeroPropulsive = {}
339 
340 
341  self.primalOnly = False
342 
343  # *********************************************************************************************
344  # ****************************** Intermediate Options *****************************************
345  # *********************************************************************************************
346 
347 
392  self.fvSource = {}
393 
394 
395  self.adjEqnSolMethod = "Krylov"
396 
397 
403  "UMax": 1000.0,
404  "UMin": -1000.0,
405  "pMax": 500000.0,
406  "pMin": 20000.0,
407  "p_rghMax": 500000.0,
408  "p_rghMin": 20000.0,
409  "eMax": 500000.0,
410  "eMin": 100000.0,
411  "TMax": 1000.0,
412  "TMin": 100.0,
413  "hMax": 500000.0,
414  "hMin": 100000.0,
415  "DMax": 1e16,
416  "DMin": -1e16,
417  "rhoMax": 5.0,
418  "rhoMin": 0.2,
419  "nuTildaMax": 1e16,
420  "nuTildaMin": 1e-16,
421  "kMax": 1e16,
422  "kMin": 1e-16,
423  "omegaMax": 1e16,
424  "omegaMin": 1e-16,
425  "epsilonMax": 1e16,
426  "epsilonMin": 1e-16,
427  "ReThetatMax": 1e16,
428  "ReThetatMin": 1e-16,
429  "gammaIntMax": 1e16,
430  "gammaIntMin": 1e-16,
431  }
432 
433 
435  self.discipline = "aero"
436 
437 
438  self.multiPoint = False
439 
440 
441  self.nMultiPoints = 1
442 
443 
446  "State": 1.0e-6,
447  "FFD": 1.0e-3,
448  "BC": 1.0e-2,
449  "AOA": 1.0e-3,
450  "ACTP": 1.0e-2,
451  "ACTD": 1.0e-2,
452  "ACTL": 1.0e-2,
453  }
454 
455 
458 
459 
462  self.unsteadyAdjoint = {"mode": "None", "nTimeInstances": -1, "periodicity": -1.0}
463 
464 
467 
468 
474  self.adjPCLag = 10000
475 
476 
483  self.useAD = {"mode": "reverse", "dvName": "None", "seedIndex": -9999}
484 
485 
487  self.rigidBodyMotion = {"mode": "dummy"}
488 
489  # *********************************************************************************************
490  # ************************************ Advance Options ****************************************
491  # *********************************************************************************************
492 
493 
495  self.runStatus = "None"
496 
497 
498  self.printPYDAFOAMOptions = False
499 
500 
501  self.printDAOptions = True
502 
503 
504  self.debug = False
505 
506 
510  self.writeJacobians = ["None"]
511 
512 
514  self.printInterval = 100
515 
516 
518 
519 
521  self.primalMinResTolDiff = 1.0e2
522 
523 
525  self.adjUseColoring = True
526 
527 
530  self.adjEqnOption = {
531  "globalPCIters": 0,
532  "asmOverlap": 1,
533  "localPCIters": 1,
534  "jacMatReOrdering": "rcm",
535  "pcFillLevel": 1,
536  "gmresMaxIters": 1000,
537  "gmresRestart": 1000,
538  "gmresRelTol": 1.0e-6,
539  "gmresAbsTol": 1.0e-14,
540  "gmresTolDiff": 1.0e2,
541  "useNonZeroInitGuess": False,
542  "useMGSO": False,
543  "printInfo": 1,
544  "fpMaxIters": 1000,
545  "fpRelTol": 1e-6,
546  "fpMinResTolDiff": 1.0e2,
547  "fpPCUpwind": False,
548  "dynAdjustTol": True,
549  }
550 
551 
553  "URes",
554  "pRes",
555  "p_rghRes",
556  "nuTildaRes",
557  "phiRes",
558  "TRes",
559  "DRes",
560  "kRes",
561  "omegaRes",
562  "epsilonRes",
563  ]
564 
565 
569  "pRes": 2,
570  "phiRes": 1,
571  "URes": 2,
572  "TRes": 2,
573  "nuTildaRes": 2,
574  "kRes": 2,
575  "epsilonRes": 2,
576  "omegaRes": 2,
577  "p_rghRes": 2,
578  "DRes": 2,
579  }
580 
581 
583  self.jacLowerBounds = {
584  "dRdW": 1.0e-30,
585  "dRdWPC": 1.0e-30,
586  }
587 
588 
590 
591 
595  "method": "scotch",
596  "simpleCoeffs": {"n": [2, 2, 1], "delta": 0.001},
597  "preservePatches": ["None"],
598  "singleProcessorFaceSets": ["None"],
599  }
600 
601 
603  self.adjStateOrdering = "state"
604 
605 
606  self.meshSurfaceFamily = "None"
607 
608 
610  "maxAspectRatio": 1000.0,
611  "maxNonOrth": 70.0,
612  "maxSkewness": 4.0,
613  "maxIncorrectlyOrientedFaces": 0,
614  }
615 
616 
626  self.writeSensMap = ["NONE"]
627 
628 
629  self.writeDeformedFFDs = False
630 
631 
633 
634 
639  self.writeMinorIterations = False
640 
641 
645  self.runLowOrderPrimal4PC = {"active": False}
646 
647 
648  self.wingProp = {
649  "test_propeller_default": {
650  "active": False,
651  "nForceSections": 10,
652  "axis": [1.0, 0.0, 0.0],
653  "actEps": 0.02,
654  "rotDir": "right",
655  "interpScheme": "Poly4Gauss",
656  },
657  }
658 
659 
661  self.primalMinIters = -1
662 
663 
664  self.tensorflow = {
665  "active": False,
666  "modelName": "model",
667  "nInputs": 1,
668  "nOutputs": 1,
669  "batchSize": 1000,
670  }
671 
672 
673 class PYDAFOAM(object):
674 
675  """
676  Main class for pyDAFoam
677 
678  Parameters
679  ----------
680 
681  comm : mpi4py communicator
682  An optional argument to pass in an external communicator.
683 
684  options : dictionary
685  The list of options to use with pyDAFoam.
686 
687  """
688 
689  def __init__(self, comm=None, options=None):
690  """
691  Initialize class members
692  """
693 
694  assert not os.getenv("WM_PROJECT") is None, "$WM_PROJECT not found. Please source OpenFOAM-v1812/etc/bashrc"
695 
696  self.version = __version__
697 
698  Info(" ")
699  Info("-------------------------------------------------------------------------------")
700  Info("| DAFoam v%s |" % self.version)
701  Info("-------------------------------------------------------------------------------")
702  Info(" ")
703 
704  # name
705  self.name = "PYDAFOAM"
706 
707  # initialize options for adjoints
708  self._initializeOptions(options)
709 
710  # initialize comm for parallel communication
711  self._initializeComm(comm)
712 
713  # check if the combination of options is valid.
714  self._checkOptions()
715 
716  # Initialize families
717  self.families = OrderedDict()
718 
719  # Default it to fault, after calling setSurfaceCoordinates, set it to true
720  self._updateGeomInfo = False
721 
722  # Use double data type: 'd'
723  self.dtype = "d"
724 
725  # write all the setup files
726  self._writeOFCaseFiles()
727 
728  # initialize point set name
730 
731  # Remind the user of all the DAFoam options:
732  if self.getOption("printPYDAFOAMOptions"):
733  self._printCurrentOptions()
734 
735  # run decomposePar for parallel runs
736  self.runDecomposePar()
737 
738  # register solver names and set their types
739  self._solverRegistry()
740 
741  # initialize the pySolvers
743  self._initSolver()
744 
745  # initialize the number of primal and adjoint calls
746  self.nSolvePrimals = 1
747  self.nSolveAdjoints = 1
748 
749  # flags for primal and adjoint failure
750  self.primalFail = 0
751  self.adjointFail = 0
752 
753  # if the primalOnly flag is on, init xvVec and wVec and return
754  if self.getOption("primalOnly"):
755  self.xvVec = None
756  self.wVec = None
757  return
758 
759  # initialize mesh information and read grids
760  self._readMeshInfo()
761 
762  # initialize the mesh point vector xvVec
764 
765  # initialize state variable vector self.wVec
766  self._initializeStateVec()
767 
768  # get the reduced point connectivities for the base patches in the mesh
770 
771  # Add a couple of special families.
772  self.allSurfacesGroup = "allSurfaces"
774 
775  self.allWallsGroup = "allWalls"
776  self.addFamilyGroup(self.allWallsGroup, self.wallList)
777 
778  # Set the design surfaces group
779  self.designSurfacesGroup = "designSurfaces"
780  if "ALL_OPENFOAM_WALL_PATCHES" in self.getOption("designSurfaces"):
782  else:
783  self.addFamilyGroup(self.designSurfacesGroup, self.getOption("designSurfaces"))
784 
785  # Set the couplingSurfacesGroup if any of the MDO scenario is active
786  # otherwise, set the couplingSurfacesGroup to designSurfacesGroup
787  # NOTE: the treatment of aeroacoustic is different because it supports more than
788  # one couplingSurfaceGroups. For other scenarios, only one couplingSurfaceGroup
789  # can be defined. TODO. we need to make them consistent in the future..
790  couplingInfo = self.getOption("couplingInfo")
792  if couplingInfo["aerostructural"]["active"]:
793  # we support only one aerostructural surfaceGroup for now
794  self.couplingSurfacesGroup = list(couplingInfo["aerostructural"]["couplingSurfaceGroups"].keys())[0]
795  patchNames = couplingInfo["aerostructural"]["couplingSurfaceGroups"][self.couplingSurfacesGroup]
796  self.addFamilyGroup(self.couplingSurfacesGroup, patchNames)
797  elif couplingInfo["aeroacoustic"]["active"]:
798  for groupName in couplingInfo["aeroacoustic"]["couplingSurfaceGroups"]:
799  self.addFamilyGroup(groupName, couplingInfo["aeroacoustic"]["couplingSurfaceGroups"][groupName])
800  elif couplingInfo["aerothermal"]["active"]:
801  # we support only one aerothermal coupling surfaceGroup for now
802  self.couplingSurfacesGroup = list(couplingInfo["aerothermal"]["couplingSurfaceGroups"].keys())[0]
803  patchNames = couplingInfo["aerothermal"]["couplingSurfaceGroups"][self.couplingSurfacesGroup]
804  self.addFamilyGroup(self.couplingSurfacesGroup, patchNames)
805 
806  # get the surface coordinate of allSurfacesGroup
808 
809  # By Default we don't have an external mesh object or a
810  # geometric manipulation object
811  self.mesh = None
812  self.DVGeo = None
813 
814  # objFuncValuePreIter stores the objective function value from the previous
815  # iteration. When the primal solution fails, the evalFunctions function will read
816  # value from self.objFuncValuePreIter
818 
819  # compute the objective function names for which we solve the adjoint equation
821 
822  # dictionary to save the total derivative vectors
823  # NOTE: this function need to be called after initializing self.objFuncNames4Adj
825 
826  # preconditioner matrix
827  self.dRdWTPC = None
828 
829  # a KSP object which may be used outside of the pyDAFoam class
830  self.ksp = None
831 
832  # the surface geometry/mesh displacement computed by the structural solver
833  # this is used in FSI. Here self.surfGeoDisp is a N by 3 numpy array
834  # that stores the displacement vector for each surface mesh point. The order of
835  # is same as the surface point return by self.getSurfaceCoordinates
836  self.surfGeoDisp = None
837 
838  # initialize the adjoint vector dict
840 
841  # initialize the dRdWOldTPsi vectors
843 
844  if self.getOption("tensorflow")["active"]:
845  TensorFlowHelper.options = self.getOption("tensorflow")
846  TensorFlowHelper.initialize()
847  # pass this helper function to the C++ layer
848  self.solver.initTensorFlowFuncs(TensorFlowHelper.predict, TensorFlowHelper.calcJacVecProd)
849  if self.getOption("useAD")["mode"] in ["forward", "reverse"]:
850  self.solverAD.initTensorFlowFuncs(TensorFlowHelper.predict, TensorFlowHelper.calcJacVecProd)
851 
852  Info("pyDAFoam initialization done!")
853 
854  return
855 
856  def _solverRegistry(self):
857  """
858  Register solver names and set their types. For a new solver, first identify its
859  type and add their names to the following dict
860  """
861 
862  self.solverRegistry = {
863  "Incompressible": ["DASimpleFoam", "DASimpleTFoam", "DAPisoFoam", "DAPimpleFoam", "DAPimpleDyMFoam"],
864  "Compressible": ["DARhoSimpleFoam", "DARhoSimpleCFoam", "DATurboFoam"],
865  "Solid": ["DASolidDisplacementFoam", "DALaplacianFoam", "DAHeatTransferFoam", "DAScalarTransportFoam"],
866  }
867 
868  def __call__(self):
869  """
870  Solve the primal
871  """
872 
873  # update the mesh coordinates if DVGeo is set
874  # add point set and update the mesh based on the DV values
875 
876  if self.DVGeo is not None:
877 
878  # if the point set is not in DVGeo add it first
879  if self.ptSetName not in self.DVGeo.points:
880 
881  xs0 = self.mapVector(self.xs0, self.allSurfacesGroup, self.designSurfacesGroup)
882 
883  self.DVGeo.addPointSet(xs0, self.ptSetName)
884  self.pointsSet = True
885 
886  # set the surface coords xs
887  Info("DVGeo PointSet UpToDate: " + str(self.DVGeo.pointSetUpToDate(self.ptSetName)))
888  if not self.DVGeo.pointSetUpToDate(self.ptSetName):
889  Info("Updating DVGeo PointSet....")
890  xs = self.DVGeo.update(self.ptSetName, config=None)
891 
892  # if we have surface geometry/mesh displacement computed by the structural solver,
893  # add the displace mesh here.
894  if self.surfGeoDisp is not None:
895  xs += self.surfGeoDisp
896 
898  Info("DVGeo PointSet UpToDate: " + str(self.DVGeo.pointSetUpToDate(self.ptSetName)))
899 
900  # warp the mesh to get the new volume coordinates
901  Info("Warping the volume mesh....")
902  self.mesh.warpMesh()
903 
904  xvNew = self.mesh.getSolverGrid()
905  self.xvFlatten2XvVec(xvNew, self.xvVec)
906 
907  # if it is forward AD mode and we are computing the Xv derivatives
908  # call calcFFD2XvSeedVec
909  if self.getOption("useAD")["mode"] == "forward":
910  dvName = self.getOption("useAD")["dvName"]
911  dvType = self.getOption("designVar")[dvName]["designVarType"]
912  if dvType == "FFD":
913  self.calcFFD2XvSeedVec()
914 
915  # update the primal boundary condition right before calling solvePrimal
917 
918  # solve the primal to get new state variables
919  self.solvePrimal()
920 
921  return
922 
923  def _getDefOptions(self):
924  """
925  Setup default options
926 
927  Returns
928  -------
929 
930  defOpts : dict
931  All the DAFoam options.
932  """
933 
934  # initialize the DAOPTION object
935  daOption = DAOPTION()
936 
937  defOpts = {}
938 
939  # assign all the attribute of daOptoin to defOpts
940  for key in vars(daOption):
941  value = getattr(daOption, key)
942  defOpts[key] = [type(value), value]
943 
944  return defOpts
945 
946  def _initializeAdjVectors(self):
947  """
948  Initialize the adjoint vector dict
949 
950  Returns
951  -------
952 
953  adjAdjVectors : dict
954  A dict that contains adjoint vectors, stored in Petsc format
955  """
956 
957  wSize = self.solver.getNLocalAdjointStates()
958 
959  objFuncDict = self.getOption("objFunc")
960 
961  adjVectors = {}
962  for objFuncName in objFuncDict:
963  if objFuncName in self.objFuncNames4Adj:
964  psi = PETSc.Vec().create(PETSc.COMM_WORLD)
965  psi.setSizes((wSize, PETSc.DECIDE), bsize=1)
966  psi.setFromOptions()
967  psi.zeroEntries()
968  adjVectors[objFuncName] = psi
969 
970  return adjVectors
971 
972  def _initializeAdjTotalDeriv(self):
973  """
974  Initialize the adjoint total derivative dict
975  NOTE: this function need to be called after initializing self.objFuncNames4Adj
976 
977  Returns
978  -------
979 
980  adjTotalDeriv : dict
981  An empty dict that contains total derivative of objective function with respect design variables
982  """
983 
984  designVarDict = self.getOption("designVar")
985  objFuncDict = self.getOption("objFunc")
986 
987  adjTotalDeriv = {}
988  for objFuncName in objFuncDict:
989  if objFuncName in self.objFuncNames4Adj:
990  adjTotalDeriv[objFuncName] = {}
991  for designVarName in designVarDict:
992  adjTotalDeriv[objFuncName][designVarName] = None
993 
994  return adjTotalDeriv
995 
996  def _initializeTimeAccurateAdjointVectors(self):
997  """
998  Initialize the dRdWTPsi vectors for time accurate adjoint.
999  Here we need to initialize current time step and two previous
1000  time steps 0 and 00 for both state and residuals. This is
1001  because the backward ddt scheme depends on U, U0, and U00
1002  """
1003  if self.getOption("unsteadyAdjoint")["mode"] == "timeAccurateAdjoint":
1004  objFuncDict = self.getOption("objFunc")
1005  wSize = self.solver.getNLocalAdjointStates()
1006  self.dRdW0TPsi = {}
1007  self.dRdW00TPsi = {}
1008  self.dR0dW0TPsi = {}
1009  self.dR0dW00TPsi = {}
1010  self.dR00dW0TPsi = {}
1011  self.dR00dW00TPsi = {}
1012  for objFuncName in objFuncDict:
1013  if objFuncName in self.objFuncNames4Adj:
1014  vecA = PETSc.Vec().create(PETSc.COMM_WORLD)
1015  vecA.setSizes((wSize, PETSc.DECIDE), bsize=1)
1016  vecA.setFromOptions()
1017  vecA.zeroEntries()
1018  self.dRdW0TPsi[objFuncName] = vecA
1019 
1020  vecB = vecA.duplicate()
1021  vecB.zeroEntries()
1022  self.dRdW00TPsi[objFuncName] = vecB
1023 
1024  vecC = vecA.duplicate()
1025  vecC.zeroEntries()
1026  self.dR0dW0TPsi[objFuncName] = vecC
1027 
1028  vecD = vecA.duplicate()
1029  vecD.zeroEntries()
1030  self.dR0dW00TPsi[objFuncName] = vecD
1031 
1032  vecE = vecA.duplicate()
1033  vecE.zeroEntries()
1034  self.dR00dW0TPsi[objFuncName] = vecE
1035 
1036  vecF = vecA.duplicate()
1037  vecF.zeroEntries()
1038  self.dR00dW00TPsi[objFuncName] = vecF
1039 
1041  if self.getOption("unsteadyAdjoint")["mode"] == "timeAccurateAdjoint":
1042  objFuncDict = self.getOption("objFunc")
1043  for objFuncName in objFuncDict:
1044  if objFuncName in self.objFuncNames4Adj:
1045  self.dRdW0TPsi[objFuncName].zeroEntries()
1046  self.dRdW00TPsi[objFuncName].zeroEntries()
1047  self.dR0dW0TPsi[objFuncName].zeroEntries()
1048  self.dR0dW00TPsi[objFuncName].zeroEntries()
1049  self.dR00dW0TPsi[objFuncName].zeroEntries()
1050  self.dR00dW00TPsi[objFuncName].zeroEntries()
1051 
1052  def _calcObjFuncNames4Adj(self):
1053  """
1054  Compute the objective function names for which we solve the adjoint equation
1055 
1056  Returns
1057  -------
1058 
1059  objFuncNames4Adj : list
1060  A list of objective function names we will solve the adjoint for
1061  """
1062 
1063  objFuncList = []
1064  objFuncDict = self.getOption("objFunc")
1065  for objFuncName in objFuncDict:
1066  objFuncSubDict = objFuncDict[objFuncName]
1067  for objFuncPart in objFuncSubDict:
1068  objFuncSubDictPart = objFuncSubDict[objFuncPart]
1069  if objFuncSubDictPart["addToAdjoint"] is True:
1070  if objFuncName not in objFuncList:
1071  objFuncList.append(objFuncName)
1072  elif objFuncSubDictPart["addToAdjoint"] is False:
1073  pass
1074  else:
1075  raise Error("addToAdjoint can be either True or False")
1076  return objFuncList
1077 
1078  def _checkOptions(self):
1079  """
1080  Check if the combination of options are valid.
1081  NOTE: we should add all possible checks here!
1082  """
1083 
1084  if not self.getOption("useAD")["mode"] in ["fd", "reverse", "forward"]:
1085  raise Error("useAD->mode only supports fd, reverse, or forward!")
1086 
1087  # check time accurate adjoint
1088  if self.getOption("unsteadyAdjoint")["mode"] == "timeAccurateAdjoint":
1089  if not self.getOption("useAD")["mode"] in ["forward", "reverse"]:
1090  raise Error("timeAccurateAdjoint only supports useAD->mode=forward|reverse")
1091 
1092  if "NONE" not in self.getOption("writeSensMap"):
1093  if not self.getOption("useAD")["mode"] in ["reverse"]:
1094  raise Error("writeSensMap is only compatible with useAD->mode=reverse")
1095 
1096  if self.getOption("runLowOrderPrimal4PC")["active"]:
1097  self.setOption("runLowOrderPrimal4PC", {"active": True, "isPC": False})
1098 
1099  if self.getOption("adjEqnSolMethod") == "fixedPoint":
1100  # for the fixed-point adjoint, we should not normalize the states and residuals
1101  if self.comm.rank == 0:
1102  print("Fixed-point adjoint mode detected. Unset normalizeStates and normalizeResiduals...")
1103 
1104  # force the normalize states to be an empty dict
1105  if len(self.getOption("normalizeStates")) > 0:
1106  raise Error("Please do not set any normalizeStates for the fixed-point adjoint!")
1107  # force the normalize residuals to be None; don't normalize any residuals
1108  self.setOption("normalizeResiduals", ["None"])
1109 
1110  if self.getOption("discipline") not in ["aero", "thermal"]:
1111  raise Error("discipline: %s not supported. Options are: aero or thermal" % self.getOption("discipline"))
1112 
1113  nActivated = 0
1114  for coupling in self.getOption("couplingInfo"):
1115  if self.getOption("couplingInfo")[coupling]["active"]:
1116  nActivated += 1
1117  if nActivated > 1:
1118  raise Error("Only one coupling scenario can be active, while %i found" % nActivated)
1119 
1120  nAerothermalSurfaces = len(self.getOption("couplingInfo")["aerothermal"]["couplingSurfaceGroups"].keys())
1121  if nAerothermalSurfaces > 1:
1122  raise Error(
1123  "Only one couplingSurfaceGroups is supported for aerothermal, while %i found" % nAerothermalSurfaces
1124  )
1125 
1126  nAeroStructSurfaces = len(self.getOption("couplingInfo")["aerostructural"]["couplingSurfaceGroups"].keys())
1127  if nAeroStructSurfaces > 1:
1128  raise Error(
1129  "Only one couplingSurfaceGroups is supported for aerostructural, while %i found" % nAeroStructSurfaces
1130  )
1131 
1132  # check other combinations...
1133 
1134  def saveMultiPointField(self, indexMP):
1135  """
1136  Save the state variable vector to self.wVecMPList
1137  """
1138 
1139  Istart, Iend = self.wVec.getOwnershipRange()
1140  for i in range(Istart, Iend):
1141  self.wVecMPList[indexMP][i] = self.wVec[i]
1142 
1143  self.wVecMPList[indexMP].assemblyBegin()
1144  self.wVecMPList[indexMP].assemblyEnd()
1145 
1146  return
1147 
1148  def setMultiPointField(self, indexMP):
1149  """
1150  Set the state variable vector based on self.wVecMPList
1151  """
1152 
1153  Istart, Iend = self.wVec.getOwnershipRange()
1154  for i in range(Istart, Iend):
1155  self.wVec[i] = self.wVecMPList[indexMP][i]
1156 
1157  self.wVec.assemblyBegin()
1158  self.wVec.assemblyEnd()
1159 
1160  return
1161 
1163  if self.getOption("useAD")["mode"] in ["forward", "reverse"]:
1164  self.solverAD.calcPrimalResidualStatistics(mode.encode())
1165  else:
1166  self.solver.calcPrimalResidualStatistics(mode.encode())
1167 
1168  def setTimeInstanceField(self, instanceI):
1169  """
1170  Set the OpenFOAM state variables based on instance index
1171  """
1172 
1173  if self.getOption("useAD")["mode"] in ["forward", "reverse"]:
1174  solver = self.solverAD
1175  else:
1176  solver = self.solver
1177 
1178  solver.setTimeInstanceField(instanceI)
1179  # NOTE: we need to set the OF field to wVec vector here!
1180  # this is because we will assign self.wVec to the solveAdjoint function later
1181  solver.ofField2StateVec(self.wVec)
1182 
1183  return
1184 
1186 
1187  nLocalAdjointStates = self.solver.getNLocalAdjointStates()
1188  nLocalAdjointBoundaryStates = self.solver.getNLocalAdjointBoundaryStates()
1189  nTimeInstances = -99999
1190  adjMode = self.getOption("unsteadyAdjoint")["mode"]
1191  if adjMode == "hybridAdjoint" or adjMode == "timeAccurateAdjoint":
1192  nTimeInstances = self.getOption("unsteadyAdjoint")["nTimeInstances"]
1193 
1194  self.stateMat = PETSc.Mat().create(PETSc.COMM_WORLD)
1195  self.stateMat.setSizes(((nLocalAdjointStates, None), (None, nTimeInstances)))
1196  self.stateMat.setFromOptions()
1197  self.stateMat.setPreallocationNNZ((nTimeInstances, nTimeInstances))
1198  self.stateMat.setUp()
1199 
1200  self.stateBCMat = PETSc.Mat().create(PETSc.COMM_WORLD)
1201  self.stateBCMat.setSizes(((nLocalAdjointBoundaryStates, None), (None, nTimeInstances)))
1202  self.stateBCMat.setFromOptions()
1203  self.stateBCMat.setPreallocationNNZ((nTimeInstances, nTimeInstances))
1204  self.stateBCMat.setUp()
1205 
1206  self.timeVec = PETSc.Vec().createSeq(nTimeInstances, bsize=1, comm=PETSc.COMM_SELF)
1207  self.timeIdxVec = PETSc.Vec().createSeq(nTimeInstances, bsize=1, comm=PETSc.COMM_SELF)
1208 
1209  def setTimeInstanceVar(self, mode):
1210 
1211  if mode == "list2Mat":
1212  self.solver.setTimeInstanceVar(mode.encode(), self.stateMat, self.stateBCMat, self.timeVec, self.timeIdxVec)
1213  elif mode == "mat2List":
1214  if self.getOption("useAD")["mode"] in ["forward", "reverse"]:
1216  mode.encode(), self.stateMat, self.stateBCMat, self.timeVec, self.timeIdxVec
1217  )
1218  else:
1220  mode.encode(), self.stateMat, self.stateBCMat, self.timeVec, self.timeIdxVec
1221  )
1222  else:
1223  raise Error("mode can only be either mat2List or list2Mat!")
1224 
1225  def writeDesignVariable(self, fileName, xDV):
1226  """
1227  Write the design variable history to files in the json format
1228  """
1229  # Write the design variable history to files
1230  if self.comm.rank == 0:
1231  if self.nSolveAdjoints == 1:
1232  f = open(fileName, "w")
1233  else:
1234  f = open(fileName, "a")
1235  # write design variables
1236  f.write('\n"Optimization_Iteration_%03d":\n' % self.nSolveAdjoints)
1237  f.write("{\n")
1238  nDVNames = len(xDV)
1239  dvNameCounter = 0
1240  for dvName in sorted(xDV):
1241  f.write(' "%s": ' % dvName)
1242  try:
1243  nDVs = len(xDV[dvName])
1244  f.write("[ ")
1245  for i in range(nDVs):
1246  if i < nDVs - 1:
1247  f.write("%20.15e, " % xDV[dvName][i])
1248  else:
1249  f.write("%20.15e " % xDV[dvName][i])
1250  f.write("]")
1251  except Exception:
1252  f.write(" %20.15e" % xDV[dvName])
1253  # check whether to add a comma
1254  dvNameCounter = dvNameCounter + 1
1255  if dvNameCounter < nDVNames:
1256  f.write(",\n")
1257  else:
1258  f.write("\n")
1259  f.write("},\n")
1260  f.close()
1261 
1262  def writeDeformedFFDs(self, counter=None):
1263  """
1264  Write the deformed FFDs to the disk during optimization
1265  """
1266 
1267  if self.comm.rank == 0:
1268  print("writeDeformedFFDs is deprecated since v3.0.1!")
1269 
1270  """
1271  if self.getOption("writeDeformedFFDs"):
1272  if counter is None:
1273  self.DVGeo.writeTecplot("deformedFFD.dat", self.nSolveAdjoints)
1274  else:
1275  self.DVGeo.writeTecplot("deformedFFD.dat", counter)
1276  """
1277 
1278  def writeTotalDeriv(self, fileName, sens, evalFuncs):
1279  """
1280  Write the total derivatives history to files in the json format
1281  This will only write total derivative for evalFuncs
1282  """
1283  # Write the sens history to files
1284  if self.comm.rank == 0:
1285  if self.nSolveAdjoints == 2:
1286  f = open(fileName, "w")
1287  else:
1288  f = open(fileName, "a")
1289  # write design variables
1290  f.write('\n"Optimization_Iteration_%03d":\n' % (self.nSolveAdjoints - 1))
1291  f.write("{\n")
1292  nFuncNames = len(evalFuncs)
1293  funcNameCounter = 0
1294  for funcName in sorted(evalFuncs):
1295  f.write(' "%s": \n {\n' % funcName)
1296  nDVNames = len(sens[funcName])
1297  dvNameCounter = 0
1298  for dvName in sorted(sens[funcName]):
1299  f.write(' "%s": ' % dvName)
1300  try:
1301  nDVs = len(sens[funcName][dvName])
1302  f.write("[ ")
1303  for i in range(nDVs):
1304  if i < nDVs - 1:
1305  f.write("%20.15e, " % sens[funcName][dvName][i])
1306  else:
1307  f.write("%20.15e " % sens[funcName][dvName][i])
1308  f.write("]")
1309  except Exception:
1310  f.write(" %20.15e" % sens[funcName][dvName])
1311  # check whether to add a comma
1312  dvNameCounter = dvNameCounter + 1
1313  if dvNameCounter < nDVNames:
1314  f.write(",\n")
1315  else:
1316  f.write("\n")
1317  f.write(" }")
1318  # check whether to add a comma
1319  funcNameCounter = funcNameCounter + 1
1320  if funcNameCounter < nFuncNames:
1321  f.write(",\n")
1322  else:
1323  f.write("\n")
1324  f.write("},\n")
1325  f.close()
1326 
1327  def getTimeInstanceObjFunc(self, instanceI, objFuncName):
1328  """
1329  Return the value of objective function at the given time instance and name
1330  """
1331 
1332  return self.solver.getTimeInstanceObjFunc(instanceI, objFuncName.encode())
1333 
1334  def getForwardADDerivVal(self, objFuncName):
1335  """
1336  Return the derivative value computed by forward mode AD primal solution
1337  """
1338  return self.solverAD.getForwardADDerivVal(objFuncName.encode())
1339 
1340  def evalFunctions(self, funcs, evalFuncs=None, ignoreMissing=False):
1341  """
1342  Evaluate the desired functions given in iterable object,
1343  'evalFuncs' and add them to the dictionary 'funcs'. The keys
1344  in the funcs dictionary will be have an _<ap.name> appended to
1345  them. Additionally, information regarding whether or not the
1346  last analysis with the solvePrimal was successful is
1347  included. This information is included as "funcs['fail']". If
1348  the 'fail' entry already exits in the dictionary the following
1349  operation is performed:
1350 
1351  funcs['fail'] = funcs['fail'] or <did this problem fail>
1352 
1353  In other words, if any one problem fails, the funcs['fail']
1354  entry will be False. This information can then be used
1355  directly in the pyOptSparse.
1356 
1357  Parameters
1358  ----------
1359  funcs : dict
1360  Dictionary into which the functions are saved.
1361 
1362  evalFuncs : iterable object containing strings
1363  If not None, use these functions to evaluate.
1364 
1365  ignoreMissing : bool
1366  Flag to suppress checking for a valid function. Please use
1367  this option with caution.
1368 
1369  Examples
1370  --------
1371  >>> funcs = {}
1372  >>> CFDsolver()
1373  >>> CFDsolver.evalFunctions(funcs, ['CD', 'CL'])
1374  >>> funcs
1375  >>> # Result will look like:
1376  >>> # {'CD':0.501, 'CL':0.02750}
1377  """
1378 
1379  for funcName in evalFuncs:
1380  if self.primalFail:
1381  if len(self.objFuncValuePrevIter) == 0:
1382  raise Error("Primal solution failed for the baseline design!")
1383  else:
1384  # do not call self.solver.getObjFuncValue because they can be nonphysical,
1385  # assign funcs based on self.objFuncValuePrevIter instead
1386  funcs[funcName] = self.objFuncValuePrevIter[funcName]
1387  else:
1388  # call self.solver.getObjFuncValue to get the objFuncValue from
1389  # the DASolver
1390  objFuncValue = self.solver.getObjFuncValue(funcName.encode())
1391  funcs[funcName] = objFuncValue
1392  # assign the objFuncValuePrevIter
1393  self.objFuncValuePrevIter[funcName] = funcs[funcName]
1394 
1395  if self.primalFail:
1396  funcs["fail"] = True
1397  else:
1398  funcs["fail"] = False
1399 
1400  return
1401 
1402  def evalFunctionsSens(self, funcsSens, evalFuncs=None):
1403  """
1404  Evaluate the sensitivity of the desired functions given in
1405  iterable object,'evalFuncs' and add them to the dictionary
1406  'funcSens'.
1407 
1408  Parameters
1409  ----------
1410  funcSens : dict
1411  Dictionary into which the function derivatives are saved.
1412 
1413  evalFuncs : iterable object containing strings
1414  The functions the user wants the derivatives of
1415 
1416  Examples
1417  --------
1418  >>> funcSens = {}
1419  >>> CFDsolver.evalFunctionsSens(funcSens, ['CD', 'CL'])
1420  """
1421 
1422  if self.DVGeo is None:
1423  raise Error("DVGeo not set!")
1424 
1425  dvs = self.DVGeo.getValues()
1426 
1427  for funcName in evalFuncs:
1428  funcsSens[funcName] = {}
1429  for dvName in dvs:
1430  nDVs = len(dvs[dvName])
1431  funcsSens[funcName][dvName] = np.zeros(nDVs, self.dtype)
1432  for i in range(nDVs):
1433  funcsSens[funcName][dvName][i] = self.adjTotalDeriv[funcName][dvName][i]
1434 
1435  if self.adjointFail:
1436  funcsSens["fail"] = True
1437  else:
1438  funcsSens["fail"] = False
1439 
1440  return
1441 
1442  def setDVGeo(self, DVGeo):
1443  """
1444  Set the DVGeometry object that will manipulate 'geometry' in
1445  this object. Note that <SOLVER> does not **strictly** need a
1446  DVGeometry object, but if optimization with geometric
1447  changes is desired, then it is required.
1448  Parameters
1449  ----------
1450  dvGeo : A DVGeometry object.
1451  Object responsible for manipulating the constraints that
1452  this object is responsible for.
1453  Examples
1454  --------
1455  >>> CFDsolver = <SOLVER>(comm=comm, options=CFDoptions)
1456  >>> CFDsolver.setDVGeo(DVGeo)
1457  """
1458 
1459  self.DVGeo = DVGeo
1460 
1461  def addFamilyGroup(self, groupName, families):
1462  """
1463  Add a custom grouping of families called groupName. The groupName
1464  must be distinct from the existing families. All families must
1465  in the 'families' list must be present in the mesh file.
1466  Parameters
1467  ----------
1468  groupName : str
1469  User-supplied custom name for the family groupings
1470  families : list
1471  List of string. Family names to combine into the family group
1472  """
1473 
1474  # Do some error checking
1475  if groupName in self.families:
1476  raise Error(
1477  "The specified groupName '%s' already exists in the mesh file or has already been added." % groupName
1478  )
1479 
1480  # We can actually allow for nested groups. That is, an entry
1481  # in families may already be a group added in a previous call.
1482  indices = []
1483  for fam in families:
1484  if fam not in self.families:
1485  raise Error(
1486  "The specified family '%s' for group '%s', does "
1487  "not exist in the mesh file or has "
1488  "not already been added. The current list of "
1489  "families (original and grouped) is: %s" % (fam, groupName, repr(self.families.keys()))
1490  )
1491 
1492  indices.extend(self.families[fam])
1493 
1494  # It is very important that the list of families is sorted
1495  # because in fortran we always use a binary search to check if
1496  # a famID is in the list.
1497  self.families[groupName] = sorted(np.unique(indices))
1498 
1499  def setMesh(self, mesh):
1500  """
1501  Set the mesh object to the aero_solver to do geometric deformations
1502  Parameters
1503  ----------
1504  mesh : MBMesh or USMesh object
1505  The mesh object for doing the warping
1506  """
1507 
1508  # Store a reference to the mesh
1509  self.mesh = mesh
1510 
1511  # Setup External Warping with volume indices
1512  meshInd = self.getSolverMeshIndices()
1513  self.mesh.setExternalMeshIndices(meshInd)
1514 
1515  # Set the surface the user has supplied:
1516  conn, faceSizes = self.getSurfaceConnectivity(self.allWallsGroup)
1517  pts = self.getSurfaceCoordinates(self.allWallsGroup)
1518  self.mesh.setSurfaceDefinition(pts, conn, faceSizes)
1519 
1520  def setEvalFuncs(self, evalFuncs):
1521  objFuncs = self.getOption("objFunc")
1522  for funcName in objFuncs:
1523  for funcPart in objFuncs[funcName]:
1524  if objFuncs[funcName][funcPart]["addToAdjoint"] is True:
1525  if funcName not in evalFuncs:
1526  evalFuncs.append(funcName)
1527  return
1528 
1529  def getSurfaceConnectivity(self, groupName=None):
1530  """
1531  Return the connectivity of the coordinates at which the forces (or tractions) are
1532  defined. This is the complement of getForces() which returns
1533  the forces at the locations returned in this routine.
1534 
1535  Parameters
1536  ----------
1537  groupName : str
1538  Group identifier to get only forces cooresponding to the
1539  desired group. The group must be a family or a user-supplied
1540  group of families. The default is None which corresponds to
1541  all wall-type surfaces.
1542  """
1543 
1544  if groupName is None:
1545  groupName = self.allWallsGroup
1546 
1547  # loop over the families in this group and populate the connectivity
1548  famInd = self.families[groupName]
1549  conn = []
1550  faceSizes = []
1551 
1552  pointOffset = 0
1553  for Ind in famInd:
1554  # select the face from the basic families
1555  name = self.basicFamilies[Ind]
1556 
1557  # get the size of this
1558  bc = self.boundaries[name]
1559  nPts = len(bc["indicesRed"])
1560 
1561  # get the number of reduced faces associated with this boundary
1562  nFace = len(bc["facesRed"])
1563 
1564  # check that this isn't an empty boundary
1565  if nFace > 0:
1566  # loop over the faces and add them to the connectivity and faceSizes array
1567  for iFace in range(nFace):
1568  face = copy.copy(bc["facesRed"][iFace])
1569  for i in range(len(face)):
1570  face[i] += pointOffset
1571  conn.extend(face)
1572  faceSizes.append(len(face))
1573 
1574  pointOffset += nPts
1575 
1576  return conn, faceSizes
1577 
1578  def getTriangulatedMeshSurface(self, groupName=None, **kwargs):
1579  """
1580  This function returns a trianguled verision of the surface
1581  mesh on all processors. The intent is to use this for doing
1582  constraints in DVConstraints.
1583  Returns
1584  -------
1585  surf : list
1586  List of points and vectors describing the surface. This may
1587  be passed directly to DVConstraint setSurface() function.
1588  """
1589 
1590  if groupName is None:
1591  groupName = self.allWallsGroup
1592 
1593  # Obtain the points and connectivity for the specified
1594  # groupName
1595  pts = self.comm.allgather(self.getSurfaceCoordinates(groupName, **kwargs))
1596  conn, faceSizes = self.getSurfaceConnectivity(groupName)
1597  conn = np.array(conn).flatten()
1598  conn = self.comm.allgather(conn)
1599  faceSizes = self.comm.allgather(faceSizes)
1600 
1601  # Triangle info...point and two vectors
1602  p0 = []
1603  v1 = []
1604  v2 = []
1605 
1606  # loop over the faces
1607  for iProc in range(len(faceSizes)):
1608 
1609  connCounter = 0
1610  for iFace in range(len(faceSizes[iProc])):
1611  # Get the number of nodes on this face
1612  faceSize = faceSizes[iProc][iFace]
1613  faceNodes = conn[iProc][connCounter : connCounter + faceSize]
1614 
1615  # Start by getting the centerpoint
1616  ptSum = [0, 0, 0]
1617  for i in range(faceSize):
1618  # idx = ptCounter+i
1619  idx = faceNodes[i]
1620  ptSum += pts[iProc][idx]
1621 
1622  avgPt = ptSum / faceSize
1623 
1624  # Now go around the face and add a triangle for each adjacent pair
1625  # of points. This assumes an ordered connectivity from the
1626  # meshwarping
1627  for i in range(faceSize):
1628  idx = faceNodes[i]
1629  p0.append(avgPt)
1630  v1.append(pts[iProc][idx] - avgPt)
1631  if i < (faceSize - 1):
1632  idxp1 = faceNodes[i + 1]
1633  v2.append(pts[iProc][idxp1] - avgPt)
1634  else:
1635  # wrap back to the first point for the last element
1636  idx0 = faceNodes[0]
1637  v2.append(pts[iProc][idx0] - avgPt)
1638 
1639  # Now increment the connectivity
1640  connCounter += faceSize
1641 
1642  return [p0, v1, v2]
1643 
1644  def printFamilyList(self):
1645  """
1646  Print a nicely formatted dictionary of the family names
1647  """
1648  Info(self.families)
1649 
1650  def setDesignVars(self, x):
1651  """
1652  Set the internal design variables.
1653  At the moment we don't have any internal DVs to set.
1654  """
1655  pass
1656 
1657  return
1658 
1659  def _initializeOptions(self, options):
1660  """
1661  Initialize the options passed into pyDAFoam
1662 
1663  Parameters
1664  ----------
1665 
1666  options : dictionary
1667  The list of options to use with pyDAFoam.
1668  """
1669 
1670  # If 'options' is None raise an error
1671  if options is None:
1672  raise Error("The 'options' keyword argument must be passed pyDAFoam.")
1673 
1674  # set immutable options that users should not change during the optimization
1676 
1677  # Load all the option information:
1679 
1680  # Set options based on defaultOptions
1681  # we basically overwrite defaultOptions with the given options
1682  # first assign self.defaultOptions to self.options
1683  self.options = OrderedDict()
1684  for key in self.defaultOptions:
1685  if len(self.defaultOptions[key]) != 2:
1686  raise Error(
1687  "key %s has wrong format! \
1688  Example: {'iters' : [int, 1]}"
1689  % key
1690  )
1691  self.options[key] = self.defaultOptions[key]
1692  # now set options to self.options
1693  for key in options:
1694  self._initOption(key, options[key])
1695 
1696  return
1697 
1698  def _initializeComm(self, comm):
1699  """
1700  Initialize MPI COMM and setup parallel flags
1701  """
1702 
1703  # Set the MPI Communicators and associated info
1704  if comm is None:
1705  comm = MPI.COMM_WORLD
1706  self.comm = comm
1707 
1708  # Check whether we are running in parallel
1709  nProc = self.comm.size
1710  self.parallel = False
1711  if nProc > 1:
1712  self.parallel = True
1713 
1714  # Save the rank and number of processors
1715  self.rank = self.comm.rank
1716  self.nProcs = self.comm.size
1717 
1718  # Setup the parallel flag for OpenFOAM executives
1719  self.parallelFlag = ""
1720  if self.parallel:
1721  self.parallelFlag = "-parallel"
1722 
1723  return
1724 
1725  def _writeOFCaseFiles(self):
1726 
1727  return
1728 
1729  def writeFieldSensitivityMap(self, objFuncName, designVarName, solutionTime, fieldType, sensVec):
1730  """
1731  Save the field sensitivity dObjFunc/dDesignVar map to disk.
1732 
1733  Parameters
1734  ----------
1735 
1736  objFuncName : str
1737  Name of the objective function
1738  designVarName : str
1739  Name of the design variable
1740  solutionTime : float
1741  The solution time where the sensitivity will be save
1742  fieldType : str
1743  The type of the field, either scalar or vector
1744  sensVec : petsc vec
1745  The Petsc vector that contains the sensitivity
1746  """
1747 
1748  workingDir = os.getcwd()
1749  if self.parallel:
1750  sensDir = "processor%d/%.8f/" % (self.rank, solutionTime)
1751  else:
1752  sensDir = "%.8f/" % solutionTime
1753 
1754  sensDir = os.path.join(workingDir, sensDir)
1755 
1756  sensList = []
1757  Istart, Iend = sensVec.getOwnershipRange()
1758  for idxI in range(Istart, Iend):
1759  sensList.append(sensVec[idxI])
1760 
1761  # write sens
1762  if not os.path.isfile(os.path.join(sensDir, "sens_%s_%s" % (objFuncName, designVarName))):
1763  fSens = open(os.path.join(sensDir, "sens_%s_%s" % (objFuncName, designVarName)), "w")
1764  if fieldType == "scalar":
1765  self._writeOpenFoamHeader(fSens, "volScalarField", sensDir, "sens_%s_%s" % (objFuncName, designVarName))
1766  fSens.write("dimensions [0 0 0 0 0 0 0];\n")
1767  fSens.write("internalField nonuniform List<scalar>\n")
1768  fSens.write("%d\n" % len(sensList))
1769  fSens.write("(\n")
1770  for i in range(len(sensList)):
1771  fSens.write("%g\n" % sensList[i])
1772  fSens.write(")\n")
1773  fSens.write(";\n")
1774  elif fieldType == "vector":
1775  self._writeOpenFoamHeader(fSens, "volVectorField", sensDir, "sens_%s_%s" % (objFuncName, designVarName))
1776  fSens.write("dimensions [0 0 0 0 0 0 0];\n")
1777  fSens.write("internalField nonuniform List<vector>\n")
1778  fSens.write("%d\n" % len(sensList) / 3)
1779  fSens.write("(\n")
1780  counterI = 0
1781  for i in range(len(sensList) / 3):
1782  fSens.write("(")
1783  for j in range(3):
1784  fSens.write("%g " % sensList[counterI])
1785  counterI = counterI + 1
1786  fSens.write(")\n")
1787  fSens.write(")\n")
1788  fSens.write(";\n")
1789  else:
1790  raise Error("fieldType %s not valid! Options are: scalar or vector" % fieldType)
1791 
1792  fSens.write("boundaryField\n")
1793  fSens.write("{\n")
1794  fSens.write(' "(.*)"\n')
1795  fSens.write(" {\n")
1796  fSens.write(" type zeroGradient;\n")
1797  fSens.write(" }\n")
1798  fSens.write("}\n")
1799  fSens.close()
1800 
1801  def writeSurfaceSensitivityMap(self, objFuncName, designVarName, solutionTime):
1802  """
1803  Save the sensitivity dObjFunc/dXs map to disk. where Xs is the wall surface mesh coordinate
1804 
1805  Parameters
1806  ----------
1807 
1808  objFuncName : str
1809  Name of the objective function
1810  designVarName : str
1811  Name of the design variable
1812  solutionTime : float
1813  The solution time where the sensitivity will be save
1814  """
1815 
1816  dFdXs = self.mesh.getdXs()
1817  dFdXs = self.mapVector(dFdXs, self.allWallsGroup, self.allWallsGroup)
1818 
1819  pts = self.getSurfaceCoordinates(self.allWallsGroup)
1820  conn, faceSizes = self.getSurfaceConnectivity(self.allWallsGroup)
1821  conn = np.array(conn).flatten()
1822 
1823  workingDir = os.getcwd()
1824  if self.parallel:
1825  meshDir = "processor%d/%.11f/polyMesh/" % (self.rank, solutionTime)
1826  sensDir = "processor%d/%.11f/" % (self.rank, solutionTime)
1827  else:
1828  meshDir = "%.11f/polyMesh/" % solutionTime
1829  sensDir = "%.11f/" % solutionTime
1830 
1831  meshDir = os.path.join(workingDir, meshDir)
1832  sensDir = os.path.join(workingDir, sensDir)
1833 
1834  if not os.path.isdir(sensDir):
1835  try:
1836  os.mkdir(sensDir)
1837  except Exception:
1838  raise Error("Can not make a directory at %s" % sensDir)
1839  if not os.path.isdir(meshDir):
1840  try:
1841  os.mkdir(meshDir)
1842  except Exception:
1843  raise Error("Can not make a directory at %s" % meshDir)
1844 
1845  # write points
1846  if not os.path.isfile(os.path.join(meshDir, "points")):
1847  fPoints = open(os.path.join(meshDir, "points"), "w")
1848  self._writeOpenFoamHeader(fPoints, "dictionary", meshDir, "points")
1849  fPoints.write("%d\n" % len(pts))
1850  fPoints.write("(\n")
1851  for i in range(len(pts)):
1852  fPoints.write("(%g %g %g)\n" % (float(pts[i][0]), float(pts[i][1]), float(pts[i][2])))
1853  fPoints.write(")\n")
1854  fPoints.close()
1855 
1856  # write faces
1857  if not os.path.isfile(os.path.join(meshDir, "faces")):
1858  fFaces = open(os.path.join(meshDir, "faces"), "w")
1859  self._writeOpenFoamHeader(fFaces, "dictionary", meshDir, "faces")
1860  counterI = 0
1861  fFaces.write("%d\n" % len(faceSizes))
1862  fFaces.write("(\n")
1863  for i in range(len(faceSizes)):
1864  fFaces.write("%d(" % faceSizes[i])
1865  for j in range(faceSizes[i]):
1866  fFaces.write(" %d " % conn[counterI])
1867  counterI += 1
1868  fFaces.write(")\n")
1869  fFaces.write(")\n")
1870  fFaces.close()
1871 
1872  # write owner
1873  if not os.path.isfile(os.path.join(meshDir, "owner")):
1874  fOwner = open(os.path.join(meshDir, "owner"), "w")
1875  self._writeOpenFoamHeader(fOwner, "dictionary", meshDir, "owner")
1876  fOwner.write("%d\n" % len(faceSizes))
1877  fOwner.write("(\n")
1878  for i in range(len(faceSizes)):
1879  fOwner.write("0\n")
1880  fOwner.write(")\n")
1881  fOwner.close()
1882 
1883  # write neighbour
1884  if not os.path.isfile(os.path.join(meshDir, "neighbour")):
1885  fNeighbour = open(os.path.join(meshDir, "neighbour"), "w")
1886  self._writeOpenFoamHeader(fNeighbour, "dictionary", meshDir, "neighbour")
1887  fNeighbour.write("%d\n" % len(faceSizes))
1888  fNeighbour.write("(\n")
1889  for i in range(len(faceSizes)):
1890  fNeighbour.write("0\n")
1891  fNeighbour.write(")\n")
1892  fNeighbour.close()
1893 
1894  # write boundary
1895  if not os.path.isfile(os.path.join(meshDir, "boundary")):
1896  fBoundary = open(os.path.join(meshDir, "boundary"), "w")
1897  self._writeOpenFoamHeader(fBoundary, "dictionary", meshDir, "boundary")
1898  fBoundary.write("1\n")
1899  fBoundary.write("(\n")
1900  fBoundary.write(" allWalls\n")
1901  fBoundary.write(" {\n")
1902  fBoundary.write(" type wall;\n")
1903  fBoundary.write(" nFaces %d;\n" % len(faceSizes))
1904  fBoundary.write(" startFace 0;\n")
1905  fBoundary.write(" }\n")
1906  fBoundary.write(")\n")
1907  fBoundary.close()
1908 
1909  # write sens
1910  if not os.path.isfile(os.path.join(sensDir, "sens_%s_%s" % (objFuncName, designVarName))):
1911  fSens = open(os.path.join(sensDir, "sens_%s_%s" % (objFuncName, designVarName)), "w")
1912  self._writeOpenFoamHeader(fSens, "volVectorField", sensDir, "sens_%s_%s" % (objFuncName, designVarName))
1913  fSens.write("dimensions [0 0 0 0 0 0 0];\n")
1914  fSens.write("internalField uniform (0 0 0);\n")
1915 
1916  counterI = 0
1917  fSens.write("boundaryField\n")
1918  fSens.write("{\n")
1919  fSens.write(" allWalls\n")
1920  fSens.write(" {\n")
1921  fSens.write(" type wall;\n")
1922  fSens.write(" value nonuniform List<vector>\n")
1923  fSens.write("%d\n" % len(faceSizes))
1924  fSens.write("(\n")
1925  counterI = 0
1926  for i in range(len(faceSizes)):
1927  sensXMean = 0.0
1928  sensYMean = 0.0
1929  sensZMean = 0.0
1930  for j in range(faceSizes[i]):
1931  idxI = conn[counterI]
1932  sensXMean += dFdXs[idxI][0]
1933  sensYMean += dFdXs[idxI][1]
1934  sensZMean += dFdXs[idxI][2]
1935  counterI += 1
1936  sensXMean /= faceSizes[i]
1937  sensYMean /= faceSizes[i]
1938  sensZMean /= faceSizes[i]
1939  fSens.write("(%f %f %f)\n" % (sensXMean, sensYMean, sensZMean))
1940  fSens.write(")\n")
1941  fSens.write(";\n")
1942  fSens.write(" }\n")
1943  fSens.write("}\n")
1944  fSens.close()
1945 
1946  def writePetscVecMat(self, name, vecMat, mode="Binary"):
1947  """
1948  Write Petsc vectors or matrices
1949  """
1950 
1951  Info("Saving %s to disk...." % name)
1952  if mode == "ASCII":
1953  viewer = PETSc.Viewer().createASCII(name + ".dat", mode="w", comm=PETSc.COMM_WORLD)
1954  viewer.pushFormat(1)
1955  viewer(vecMat)
1956  elif mode == "Binary":
1957  viewer = PETSc.Viewer().createBinary(name + ".bin", mode="w", comm=PETSc.COMM_WORLD)
1958  viewer(vecMat)
1959  else:
1960  raise Error("mode not valid! Options are: ASCII or Binary")
1961 
1962  def readPetscVecMat(self, name, vecMat):
1963  """
1964  Read Petsc vectors or matrices
1965  """
1966 
1967  Info("Reading %s from disk...." % name)
1968  viewer = PETSc.Viewer().createBinary(name + ".bin", comm=PETSc.COMM_WORLD)
1969  vecMat.load(viewer)
1970 
1971  def solvePrimal(self):
1972  """
1973  Run primal solver to compute state variables and objectives
1974 
1975  Input:
1976  ------
1977  xvVec: vector that contains all the mesh point coordinates
1978 
1979  Output:
1980  -------
1981  wVec: vector that contains all the state variables
1982 
1983  self.primalFail: if the primal solution fails, assigns 1, otherwise 0
1984  """
1985 
1986  Info("Running Primal Solver %03d" % self.nSolvePrimals)
1987 
1989 
1990  self.primalFail = 0
1991  if self.getOption("useAD")["mode"] == "forward":
1992  self.primalFail = self.solverAD.solvePrimal(self.xvVec, self.wVec)
1993  else:
1994  self.primalFail = self.solver.solvePrimal(self.xvVec, self.wVec)
1995 
1996  if self.getOption("writeMinorIterations"):
1997  self.renameSolution(self.nSolvePrimals)
1998  self.writeDeformedFFDs(self.nSolvePrimals)
1999 
2000  self.nSolvePrimals += 1
2001 
2002  return
2003 
2004  def solveAdjoint(self):
2005  """
2006  Run adjoint solver to compute the adjoint vector psiVec
2007 
2008  Input:
2009  ------
2010  xvVec: vector that contains all the mesh point coordinates
2011 
2012  wVec: vector that contains all the state variables
2013 
2014  Output:
2015  -------
2016  self.adjTotalDeriv: the dict contains all the total derivative vectors
2017 
2018  self.adjointFail: if the adjoint solution fails, assigns 1, otherwise 0
2019  """
2020 
2021  # save the point vector and state vector to disk
2022  """
2023  Info("Saving the xvVec and wVec vectors to disk....")
2024  self.comm.Barrier()
2025  viewerXv = PETSc.Viewer().createBinary("xvVec_%03d.bin" % self.nSolveAdjoints, mode="w", comm=PETSc.COMM_WORLD)
2026  viewerXv(self.xvVec)
2027  viewerW = PETSc.Viewer().createBinary("wVec_%03d.bin" % self.nSolveAdjoints, mode="w", comm=PETSc.COMM_WORLD)
2028  viewerW(self.wVec)
2029  """
2030 
2031  if self.getOption("useAD")["mode"] == "forward":
2032  raise Error("solveAdjoint only supports useAD->mode=reverse|fd")
2033 
2034  if not self.getOption("writeMinorIterations"):
2035  solutionTime, renamed = self.renameSolution(self.nSolveAdjoints)
2036 
2037  Info("Running adjoint Solver %03d" % self.nSolveAdjoints)
2038 
2039  self.setOption("runStatus", "solveAdjoint")
2040  self.updateDAOption()
2041 
2042  if self.getOption("multiPoint"):
2043  self.solver.updateOFField(self.wVec)
2044  if self.getOption("useAD")["mode"] == "reverse":
2045  self.solverAD.updateOFField(self.wVec)
2046 
2047  self.adjointFail = 0
2048 
2049  # calculate dRdWT
2050  if self.getOption("useAD")["mode"] == "fd":
2051  dRdWT = PETSc.Mat().create(PETSc.COMM_WORLD)
2052  self.solver.calcdRdWT(self.xvVec, self.wVec, 0, dRdWT)
2053  elif self.getOption("useAD")["mode"] == "reverse":
2054  self.solverAD.initializedRdWTMatrixFree(self.xvVec, self.wVec)
2055 
2056  # calculate dRdWTPC. If runLowOrderPrimal4PC is true, we compute the PC mat
2057  # before solving the primal, so we will skip it here
2058  if not self.getOption("runLowOrderPrimal4PC")["active"]:
2059  adjPCLag = self.getOption("adjPCLag")
2060  if self.nSolveAdjoints == 1 or (self.nSolveAdjoints - 1) % adjPCLag == 0:
2061  self.dRdWTPC = PETSc.Mat().create(PETSc.COMM_WORLD)
2062  self.solver.calcdRdWT(self.xvVec, self.wVec, 1, self.dRdWTPC)
2063 
2064  # Initialize the KSP object
2065  ksp = PETSc.KSP().create(PETSc.COMM_WORLD)
2066  if self.getOption("useAD")["mode"] == "fd":
2067  self.solver.createMLRKSP(dRdWT, self.dRdWTPC, ksp)
2068  elif self.getOption("useAD")["mode"] == "reverse":
2069  self.solverAD.createMLRKSPMatrixFree(self.dRdWTPC, ksp)
2070 
2071  # loop over all objFunc, calculate dFdW, and solve the adjoint
2072  objFuncDict = self.getOption("objFunc")
2073  wSize = self.solver.getNLocalAdjointStates()
2074  for objFuncName in objFuncDict:
2075  if objFuncName in self.objFuncNames4Adj:
2076  dFdW = PETSc.Vec().create(PETSc.COMM_WORLD)
2077  dFdW.setSizes((wSize, PETSc.DECIDE), bsize=1)
2078  dFdW.setFromOptions()
2079  if self.getOption("useAD")["mode"] == "fd":
2080  self.solver.calcdFdW(self.xvVec, self.wVec, objFuncName.encode(), dFdW)
2081  elif self.getOption("useAD")["mode"] == "reverse":
2082  self.solverAD.calcdFdWAD(self.xvVec, self.wVec, objFuncName.encode(), dFdW)
2083 
2084  # if it is time accurate adjoint, add extra terms for dFdW
2085  if self.getOption("unsteadyAdjoint")["mode"] == "timeAccurateAdjoint":
2086  # first copy the vectors from previous residual time step level
2087  self.dR0dW0TPsi[objFuncName].copy(self.dR00dW0TPsi[objFuncName])
2088  self.dR0dW00TPsi[objFuncName].copy(self.dR00dW00TPsi[objFuncName])
2089  self.dRdW0TPsi[objFuncName].copy(self.dR0dW0TPsi[objFuncName])
2090  self.dRdW00TPsi[objFuncName].copy(self.dR0dW00TPsi[objFuncName])
2091  dFdW.axpy(-1.0, self.dR0dW0TPsi[objFuncName])
2092  dFdW.axpy(-1.0, self.dR00dW00TPsi[objFuncName])
2093 
2094  # Initialize the adjoint vector psi and solve for it
2095  if self.getOption("useAD")["mode"] == "fd":
2096  self.adjointFail = self.solver.solveLinearEqn(ksp, dFdW, self.adjVectors[objFuncName])
2097  elif self.getOption("useAD")["mode"] == "reverse":
2098  self.adjointFail = self.solverAD.solveLinearEqn(ksp, dFdW, self.adjVectors[objFuncName])
2099 
2100  if self.getOption("unsteadyAdjoint")["mode"] == "timeAccurateAdjoint":
2101  self.solverAD.calcdRdWOldTPsiAD(1, self.adjVectors[objFuncName], self.dRdW0TPsi[objFuncName])
2102  self.solverAD.calcdRdWOldTPsiAD(2, self.adjVectors[objFuncName], self.dRdW00TPsi[objFuncName])
2103 
2104  dFdW.destroy()
2105 
2106  ksp.destroy()
2107  if self.getOption("useAD")["mode"] == "fd":
2108  dRdWT.destroy()
2109  elif self.getOption("useAD")["mode"] == "reverse":
2110  self.solverAD.destroydRdWTMatrixFree()
2111  # we destroy dRdWTPC only when we need to recompute it next time
2112  # see the bottom of this function
2113 
2114  # ************ Now compute the total derivatives **********************
2115  Info("Computing total derivatives....")
2116 
2117  designVarDict = self.getOption("designVar")
2118  for designVarName in designVarDict:
2119  Info("Computing total derivatives for %s" % designVarName)
2120 
2121  if designVarDict[designVarName]["designVarType"] == "BC":
2122  if self.getOption("useAD")["mode"] == "fd":
2123  nDVs = 1
2124  # calculate dRdBC
2125  dRdBC = PETSc.Mat().create(PETSc.COMM_WORLD)
2126  self.solver.calcdRdBC(self.xvVec, self.wVec, designVarName.encode(), dRdBC)
2127  # loop over all objectives
2128  for objFuncName in objFuncDict:
2129  if objFuncName in self.objFuncNames4Adj:
2130  # calculate dFdBC
2131  dFdBC = PETSc.Vec().create(PETSc.COMM_WORLD)
2132  dFdBC.setSizes((PETSc.DECIDE, nDVs), bsize=1)
2133  dFdBC.setFromOptions()
2134  self.solver.calcdFdBC(
2135  self.xvVec, self.wVec, objFuncName.encode(), designVarName.encode(), dFdBC
2136  )
2137  # call the total deriv
2138  totalDeriv = PETSc.Vec().create(PETSc.COMM_WORLD)
2139  totalDeriv.setSizes((PETSc.DECIDE, nDVs), bsize=1)
2140  totalDeriv.setFromOptions()
2141  self.calcTotalDeriv(dRdBC, dFdBC, self.adjVectors[objFuncName], totalDeriv)
2142  # assign the total derivative to self.adjTotalDeriv
2143  self.adjTotalDeriv[objFuncName][designVarName] = np.zeros(nDVs, self.dtype)
2144  # we need to convert the parallel vec to seq vec
2145  totalDerivSeq = PETSc.Vec().createSeq(nDVs, bsize=1, comm=PETSc.COMM_SELF)
2146  self.solver.convertMPIVec2SeqVec(totalDeriv, totalDerivSeq)
2147  for i in range(nDVs):
2148  self.adjTotalDeriv[objFuncName][designVarName][i] = totalDerivSeq[i]
2149 
2150  totalDeriv.destroy()
2151  totalDerivSeq.destroy()
2152  dFdBC.destroy()
2153  dRdBC.destroy()
2154  elif self.getOption("useAD")["mode"] == "reverse":
2155  nDVs = 1
2156  # loop over all objectives
2157  for objFuncName in objFuncDict:
2158  if objFuncName in self.objFuncNames4Adj:
2159  # calculate dFdBC
2160  dFdBC = PETSc.Vec().create(PETSc.COMM_WORLD)
2161  dFdBC.setSizes((PETSc.DECIDE, nDVs), bsize=1)
2162  dFdBC.setFromOptions()
2163  self.solverAD.calcdFdBCAD(
2164  self.xvVec, self.wVec, objFuncName.encode(), designVarName.encode(), dFdBC
2165  )
2166  # Calculate dRBCT^Psi
2167  totalDeriv = PETSc.Vec().create(PETSc.COMM_WORLD)
2168  totalDeriv.setSizes((PETSc.DECIDE, nDVs), bsize=1)
2169  totalDeriv.setFromOptions()
2170  self.solverAD.calcdRdBCTPsiAD(
2171  self.xvVec, self.wVec, self.adjVectors[objFuncName], designVarName.encode(), totalDeriv
2172  )
2173  # totalDeriv = dFdBC - dRdBCT*psi
2174  totalDeriv.scale(-1.0)
2175  totalDeriv.axpy(1.0, dFdBC)
2176  # assign the total derivative to self.adjTotalDeriv
2177  self.adjTotalDeriv[objFuncName][designVarName] = np.zeros(nDVs, self.dtype)
2178  # we need to convert the parallel vec to seq vec
2179  totalDerivSeq = PETSc.Vec().createSeq(nDVs, bsize=1, comm=PETSc.COMM_SELF)
2180  self.solver.convertMPIVec2SeqVec(totalDeriv, totalDerivSeq)
2181  for i in range(nDVs):
2182  self.adjTotalDeriv[objFuncName][designVarName][i] = totalDerivSeq[i]
2183 
2184  totalDeriv.destroy()
2185  totalDerivSeq.destroy()
2186  dFdBC.destroy()
2187 
2188  elif designVarDict[designVarName]["designVarType"] == "AOA":
2189  if self.getOption("useAD")["mode"] == "fd":
2190  nDVs = 1
2191  # calculate dRdAOA
2192  dRdAOA = PETSc.Mat().create(PETSc.COMM_WORLD)
2193  self.solver.calcdRdAOA(self.xvVec, self.wVec, designVarName.encode(), dRdAOA)
2194  # loop over all objectives
2195  for objFuncName in objFuncDict:
2196  if objFuncName in self.objFuncNames4Adj:
2197  # calculate dFdAOA
2198  dFdAOA = PETSc.Vec().create(PETSc.COMM_WORLD)
2199  dFdAOA.setSizes((PETSc.DECIDE, nDVs), bsize=1)
2200  dFdAOA.setFromOptions()
2201  self.solver.calcdFdAOA(
2202  self.xvVec, self.wVec, objFuncName.encode(), designVarName.encode(), dFdAOA
2203  )
2204  # call the total deriv
2205  totalDeriv = PETSc.Vec().create(PETSc.COMM_WORLD)
2206  totalDeriv.setSizes((PETSc.DECIDE, nDVs), bsize=1)
2207  totalDeriv.setFromOptions()
2208  self.calcTotalDeriv(dRdAOA, dFdAOA, self.adjVectors[objFuncName], totalDeriv)
2209  # assign the total derivative to self.adjTotalDeriv
2210  self.adjTotalDeriv[objFuncName][designVarName] = np.zeros(nDVs, self.dtype)
2211  # we need to convert the parallel vec to seq vec
2212  totalDerivSeq = PETSc.Vec().createSeq(nDVs, bsize=1, comm=PETSc.COMM_SELF)
2213  self.solver.convertMPIVec2SeqVec(totalDeriv, totalDerivSeq)
2214  for i in range(nDVs):
2215  self.adjTotalDeriv[objFuncName][designVarName][i] = totalDerivSeq[i]
2216 
2217  totalDeriv.destroy()
2218  totalDerivSeq.destroy()
2219  dFdAOA.destroy()
2220  dRdAOA.destroy()
2221  elif self.getOption("useAD")["mode"] == "reverse":
2222  nDVs = 1
2223  # loop over all objectives
2224  for objFuncName in objFuncDict:
2225  if objFuncName in self.objFuncNames4Adj:
2226  # calculate dFdAOA
2227  dFdAOA = PETSc.Vec().create(PETSc.COMM_WORLD)
2228  dFdAOA.setSizes((PETSc.DECIDE, nDVs), bsize=1)
2229  dFdAOA.setFromOptions()
2230  self.calcdFdAOAAnalytical(objFuncName, dFdAOA)
2231  # Calculate dRAOAT^Psi
2232  totalDeriv = PETSc.Vec().create(PETSc.COMM_WORLD)
2233  totalDeriv.setSizes((PETSc.DECIDE, nDVs), bsize=1)
2234  totalDeriv.setFromOptions()
2235  self.solverAD.calcdRdAOATPsiAD(
2236  self.xvVec, self.wVec, self.adjVectors[objFuncName], designVarName.encode(), totalDeriv
2237  )
2238  # totalDeriv = dFdAOA - dRdAOAT*psi
2239  totalDeriv.scale(-1.0)
2240  totalDeriv.axpy(1.0, dFdAOA)
2241  # assign the total derivative to self.adjTotalDeriv
2242  self.adjTotalDeriv[objFuncName][designVarName] = np.zeros(nDVs, self.dtype)
2243  # we need to convert the parallel vec to seq vec
2244  totalDerivSeq = PETSc.Vec().createSeq(nDVs, bsize=1, comm=PETSc.COMM_SELF)
2245  self.solver.convertMPIVec2SeqVec(totalDeriv, totalDerivSeq)
2246  for i in range(nDVs):
2247  self.adjTotalDeriv[objFuncName][designVarName][i] = totalDerivSeq[i]
2248 
2249  totalDeriv.destroy()
2250  totalDerivSeq.destroy()
2251  dFdAOA.destroy()
2252 
2253  elif designVarDict[designVarName]["designVarType"] == "FFD":
2254  if self.getOption("useAD")["mode"] == "fd":
2255  nDVs = self.setdXvdFFDMat(designVarName)
2256  # calculate dRdFFD
2257  dRdFFD = PETSc.Mat().create(PETSc.COMM_WORLD)
2258  self.solver.calcdRdFFD(self.xvVec, self.wVec, designVarName.encode(), dRdFFD)
2259  # loop over all objectives
2260  for objFuncName in objFuncDict:
2261  if objFuncName in self.objFuncNames4Adj:
2262  # calculate dFdFFD
2263  dFdFFD = PETSc.Vec().create(PETSc.COMM_WORLD)
2264  dFdFFD.setSizes((PETSc.DECIDE, nDVs), bsize=1)
2265  dFdFFD.setFromOptions()
2266  self.solver.calcdFdFFD(
2267  self.xvVec, self.wVec, objFuncName.encode(), designVarName.encode(), dFdFFD
2268  )
2269  # call the total deriv
2270  totalDeriv = PETSc.Vec().create(PETSc.COMM_WORLD)
2271  totalDeriv.setSizes((PETSc.DECIDE, nDVs), bsize=1)
2272  totalDeriv.setFromOptions()
2273  self.calcTotalDeriv(dRdFFD, dFdFFD, self.adjVectors[objFuncName], totalDeriv)
2274  # assign the total derivative to self.adjTotalDeriv
2275  self.adjTotalDeriv[objFuncName][designVarName] = np.zeros(nDVs, self.dtype)
2276  # we need to convert the parallel vec to seq vec
2277  totalDerivSeq = PETSc.Vec().createSeq(nDVs, bsize=1, comm=PETSc.COMM_SELF)
2278  self.solver.convertMPIVec2SeqVec(totalDeriv, totalDerivSeq)
2279  for i in range(nDVs):
2280  self.adjTotalDeriv[objFuncName][designVarName][i] = totalDerivSeq[i]
2281  totalDeriv.destroy()
2282  totalDerivSeq.destroy()
2283  dFdFFD.destroy()
2284  dRdFFD.destroy()
2285  elif self.getOption("useAD")["mode"] == "reverse":
2286  try:
2287  nDVs = len(self.DVGeo.getValues()[designVarName])
2288  except Exception:
2289  nDVs = 1
2290  xvSize = len(self.xv) * 3
2291  for objFuncName in objFuncDict:
2292  if objFuncName in self.objFuncNames4Adj:
2293  # Calculate dFdXv
2294  dFdXv = PETSc.Vec().create(PETSc.COMM_WORLD)
2295  dFdXv.setSizes((xvSize, PETSc.DECIDE), bsize=1)
2296  dFdXv.setFromOptions()
2297  self.solverAD.calcdFdXvAD(
2298  self.xvVec, self.wVec, objFuncName.encode(), designVarName.encode(), dFdXv
2299  )
2300 
2301  # Calculate dRXvT^Psi
2302  totalDerivXv = PETSc.Vec().create(PETSc.COMM_WORLD)
2303  totalDerivXv.setSizes((xvSize, PETSc.DECIDE), bsize=1)
2304  totalDerivXv.setFromOptions()
2305  self.solverAD.calcdRdXvTPsiAD(
2306  self.xvVec, self.wVec, self.adjVectors[objFuncName], totalDerivXv
2307  )
2308 
2309  # totalDeriv = dFdXv - dRdXvT*psi
2310  totalDerivXv.scale(-1.0)
2311  totalDerivXv.axpy(1.0, dFdXv)
2312 
2313  # write the matrix
2314  if "dFdXvTotalDeriv" in self.getOption("writeJacobians") or "all" in self.getOption(
2315  "writeJacobians"
2316  ):
2317  self.writePetscVecMat("dFdXvTotalDeriv_%s" % objFuncName, totalDerivXv)
2318  self.writePetscVecMat("dFdXvTotalDeriv_%s" % objFuncName, totalDerivXv, "ASCII")
2319 
2320  if self.DVGeo is not None and self.DVGeo.getNDV() > 0:
2321  dFdFFD = self.mapdXvTodFFD(totalDerivXv)
2322  if designVarName in self.getOption("writeSensMap"):
2323  # we can't save the surface sensitivity time with the primal solution
2324  # because surfaceSensMap needs to have its own mesh (design surface only)
2325  sensSolTime = float(solutionTime) / 1000.0
2326  self.writeSurfaceSensitivityMap(objFuncName, designVarName, sensSolTime)
2327 
2328  # assign the total derivative to self.adjTotalDeriv
2329  self.adjTotalDeriv[objFuncName][designVarName] = np.zeros(nDVs, self.dtype)
2330  for i in range(nDVs):
2331  self.adjTotalDeriv[objFuncName][designVarName][i] = dFdFFD[designVarName][0][i]
2332 
2333  totalDerivXv.destroy()
2334  dFdXv.destroy()
2335 
2336  elif designVarDict[designVarName]["designVarType"] in ["ACTL", "ACTP", "ACTD"]:
2337  if self.getOption("useAD")["mode"] == "fd":
2338  designVarType = designVarDict[designVarName]["designVarType"]
2339  nDVTable = {"ACTP": 9, "ACTD": 10, "ACTL": 11}
2340  nDVs = nDVTable[designVarType]
2341  # calculate dRdACT
2342  dRdACT = PETSc.Mat().create(PETSc.COMM_WORLD)
2343  self.solver.calcdRdACT(
2344  self.xvVec, self.wVec, designVarName.encode(), designVarType.encode(), dRdACT
2345  )
2346  # loop over all objectives
2347  for objFuncName in objFuncDict:
2348  if objFuncName in self.objFuncNames4Adj:
2349  # calculate dFdACT
2350  dFdACT = PETSc.Vec().create(PETSc.COMM_WORLD)
2351  dFdACT.setSizes((PETSc.DECIDE, nDVs), bsize=1)
2352  dFdACT.setFromOptions()
2353  self.solver.calcdFdACT(
2354  self.xvVec,
2355  self.wVec,
2356  objFuncName.encode(),
2357  designVarName.encode(),
2358  designVarType.encode(),
2359  dFdACT,
2360  )
2361  # call the total deriv
2362  totalDeriv = PETSc.Vec().create(PETSc.COMM_WORLD)
2363  totalDeriv.setSizes((PETSc.DECIDE, nDVs), bsize=1)
2364  totalDeriv.setFromOptions()
2365  self.calcTotalDeriv(dRdACT, dFdACT, self.adjVectors[objFuncName], totalDeriv)
2366  # assign the total derivative to self.adjTotalDeriv
2367  self.adjTotalDeriv[objFuncName][designVarName] = np.zeros(nDVs, self.dtype)
2368  # we need to convert the parallel vec to seq vec
2369  totalDerivSeq = PETSc.Vec().createSeq(nDVs, bsize=1, comm=PETSc.COMM_SELF)
2370  self.solver.convertMPIVec2SeqVec(totalDeriv, totalDerivSeq)
2371  for i in range(nDVs):
2372  self.adjTotalDeriv[objFuncName][designVarName][i] = totalDerivSeq[i]
2373  totalDeriv.destroy()
2374  totalDerivSeq.destroy()
2375  dFdACT.destroy()
2376  dRdACT.destroy()
2377  elif self.getOption("useAD")["mode"] == "reverse":
2378  designVarType = designVarDict[designVarName]["designVarType"]
2379  nDVTable = {"ACTP": 9, "ACTD": 10, "ACTL": 11}
2380  nDVs = nDVTable[designVarType]
2381  # loop over all objectives
2382  for objFuncName in objFuncDict:
2383  if objFuncName in self.objFuncNames4Adj:
2384  # calculate dFdACT
2385  dFdACT = PETSc.Vec().create(PETSc.COMM_WORLD)
2386  dFdACT.setSizes((PETSc.DECIDE, nDVs), bsize=1)
2387  dFdACT.setFromOptions()
2388  self.solverAD.calcdFdACTAD(
2389  self.xvVec, self.wVec, objFuncName.encode(), designVarName.encode(), dFdACT
2390  )
2391  # call the total deriv
2392  totalDeriv = PETSc.Vec().create(PETSc.COMM_WORLD)
2393  totalDeriv.setSizes((PETSc.DECIDE, nDVs), bsize=1)
2394  totalDeriv.setFromOptions()
2395  # calculate dRdActT*Psi and save it to totalDeriv
2396  self.solverAD.calcdRdActTPsiAD(
2397  self.xvVec, self.wVec, self.adjVectors[objFuncName], designVarName.encode(), totalDeriv
2398  )
2399 
2400  # totalDeriv = dFdAct - dRdActT*psi
2401  totalDeriv.scale(-1.0)
2402  totalDeriv.axpy(1.0, dFdACT)
2403 
2404  # assign the total derivative to self.adjTotalDeriv
2405  self.adjTotalDeriv[objFuncName][designVarName] = np.zeros(nDVs, self.dtype)
2406  # we need to convert the parallel vec to seq vec
2407  totalDerivSeq = PETSc.Vec().createSeq(nDVs, bsize=1, comm=PETSc.COMM_SELF)
2408  self.solver.convertMPIVec2SeqVec(totalDeriv, totalDerivSeq)
2409  for i in range(nDVs):
2410  self.adjTotalDeriv[objFuncName][designVarName][i] = totalDerivSeq[i]
2411  totalDeriv.destroy()
2412  totalDerivSeq.destroy()
2413 
2414  elif designVarDict[designVarName]["designVarType"] == "Field":
2415  if self.getOption("useAD")["mode"] == "reverse":
2416 
2417  xDV = self.DVGeo.getValues()
2418  nDVs = len(xDV[designVarName])
2419  fieldType = designVarDict[designVarName]["fieldType"]
2420  if fieldType == "scalar":
2421  fieldComp = 1
2422  elif fieldType == "vector":
2423  fieldComp = 3
2424  nLocalCells = self.solver.getNLocalCells()
2425 
2426  # loop over all objectives
2427  for objFuncName in objFuncDict:
2428  if objFuncName in self.objFuncNames4Adj:
2429 
2430  # calculate dFdField
2431  dFdField = PETSc.Vec().create(PETSc.COMM_WORLD)
2432  dFdField.setSizes((fieldComp * nLocalCells, PETSc.DECIDE), bsize=1)
2433  dFdField.setFromOptions()
2434  self.solverAD.calcdFdFieldAD(
2435  self.xvVec, self.wVec, objFuncName.encode(), designVarName.encode(), dFdField
2436  )
2437 
2438  # call the total deriv
2439  totalDeriv = PETSc.Vec().create(PETSc.COMM_WORLD)
2440  totalDeriv.setSizes((fieldComp * nLocalCells, PETSc.DECIDE), bsize=1)
2441  totalDeriv.setFromOptions()
2442  # calculate dRdFieldT*Psi and save it to totalDeriv
2443  self.solverAD.calcdRdFieldTPsiAD(
2444  self.xvVec, self.wVec, self.adjVectors[objFuncName], designVarName.encode(), totalDeriv
2445  )
2446 
2447  # totalDeriv = dFdField - dRdFieldT*psi
2448  totalDeriv.scale(-1.0)
2449  totalDeriv.axpy(1.0, dFdField)
2450 
2451  # write the matrix
2452  if "dFdFieldTotalDeriv" in self.getOption("writeJacobians") or "all" in self.getOption(
2453  "writeJacobians"
2454  ):
2455  self.writePetscVecMat("dFdFieldTotalDeriv_%s" % objFuncName, totalDeriv)
2456  self.writePetscVecMat("dFdFieldTotalDeriv_%s" % objFuncName, totalDeriv, "ASCII")
2457 
2458  # check if we need to save the sensitivity maps
2459  if designVarName in self.getOption("writeSensMap"):
2460  # we will write the field sensitivity with the primal solution because they
2461  # share the same mesh
2463  objFuncName, designVarName, float(solutionTime), fieldType, totalDeriv
2464  )
2465 
2466  # assign the total derivative to self.adjTotalDeriv
2467  self.adjTotalDeriv[objFuncName][designVarName] = np.zeros(nDVs, self.dtype)
2468  # we need to convert the parallel vec to seq vec
2469  totalDerivSeq = PETSc.Vec().createSeq(nDVs, bsize=1, comm=PETSc.COMM_SELF)
2470  self.solver.convertMPIVec2SeqVec(totalDeriv, totalDerivSeq)
2471  for i in range(nDVs):
2472  self.adjTotalDeriv[objFuncName][designVarName][i] = totalDerivSeq[i]
2473  totalDeriv.destroy()
2474  totalDerivSeq.destroy()
2475  dFdField.destroy()
2476  else:
2477  raise Error("For Field design variable type, we only support useAD->mode=reverse")
2478  else:
2479  raise Error("designVarType %s not supported!" % designVarDict[designVarName]["designVarType"])
2480 
2481  self.nSolveAdjoints += 1
2482 
2483  # we destroy dRdWTPC only when we need to recompute it next time
2484  if (self.nSolveAdjoints - 1) % adjPCLag == 0:
2485  self.dRdWTPC.destroy()
2486 
2487  return
2488 
2489  def mapdXvTodFFD(self, totalDerivXv):
2490  """
2491  Map the Xv derivative (volume derivative) to the FFD derivatives (design variables)
2492  Essentially, we first map the Xv (volume) to Xs (surface) using IDWarp, then, we
2493  further map Xs (surface) to FFD using pyGeo
2494 
2495  Input:
2496  ------
2497  totalDerivXv: total derivative dFdXv vector
2498 
2499  Output:
2500  ------
2501  dFdFFD: the mapped total derivative with respect to FFD variables
2502  """
2503 
2504  xvSize = len(self.xv) * 3
2505 
2506  dFdXvTotalArray = np.zeros(xvSize, self.dtype)
2507 
2508  Istart, Iend = totalDerivXv.getOwnershipRange()
2509 
2510  for idxI in range(Istart, Iend):
2511  idxRel = idxI - Istart
2512  dFdXvTotalArray[idxRel] = totalDerivXv[idxI]
2513 
2514  self.mesh.warpDeriv(dFdXvTotalArray)
2515  dFdXs = self.mesh.getdXs()
2516  dFdXs = self.mapVector(dFdXs, self.allWallsGroup, self.designSurfacesGroup)
2517  dFdFFD = self.DVGeo.totalSensitivity(dFdXs, ptSetName=self.ptSetName, comm=self.comm)
2518 
2519  return dFdFFD
2520 
2521  def getForces(self, groupName=None):
2522  """
2523  Return the forces on this processor on the families defined by groupName.
2524  Parameters
2525  ----------
2526  groupName : str
2527  Group identifier to get only forces cooresponding to the
2528  desired group. The group must be a family or a user-supplied
2529  group of families. The default is None which corresponds to
2530  design surfaces.
2531 
2532  Returns
2533  -------
2534  forces : array (N,3)
2535  Forces on this processor. Note that N may be 0, and an
2536  empty array of shape (0, 3) can be returned.
2537  """
2538  Info("Computing surface forces")
2539  # Calculate number of surface points
2540  if groupName is None:
2541  groupName = self.couplingSurfacesGroup
2542  nPts, _ = self._getSurfaceSize(groupName)
2543 
2544  fX = PETSc.Vec().create(comm=PETSc.COMM_WORLD)
2545  fX.setSizes((nPts, PETSc.DECIDE), bsize=1)
2546  fX.setFromOptions()
2547 
2548  fY = PETSc.Vec().create(comm=PETSc.COMM_WORLD)
2549  fY.setSizes((nPts, PETSc.DECIDE), bsize=1)
2550  fY.setFromOptions()
2551 
2552  fZ = PETSc.Vec().create(comm=PETSc.COMM_WORLD)
2553  fZ.setSizes((nPts, PETSc.DECIDE), bsize=1)
2554  fZ.setFromOptions()
2555 
2556  # Compute forces
2557  self.solver.getForces(fX, fY, fZ)
2558 
2559  # Copy data from PETSc vectors
2560  forces = np.zeros((nPts, 3))
2561  forces[:, 0] = np.copy(fX.getArray())
2562  forces[:, 1] = np.copy(fY.getArray())
2563  forces[:, 2] = np.copy(fZ.getArray())
2564 
2565  # Cleanup PETSc vectors
2566  fX.destroy()
2567  fY.destroy()
2568  fZ.destroy()
2569 
2570  # Print total force
2571  fXSum = np.sum(forces[:, 0])
2572  fYSum = np.sum(forces[:, 1])
2573  fZSum = np.sum(forces[:, 2])
2574 
2575  fXTot = self.comm.allreduce(fXSum, op=MPI.SUM)
2576  fYTot = self.comm.allreduce(fYSum, op=MPI.SUM)
2577  fZTot = self.comm.allreduce(fZSum, op=MPI.SUM)
2578 
2579  Info("Total force:")
2580  Info("Fx = %e" % fXTot)
2581  Info("Fy = %e" % fYTot)
2582  Info("Fz = %e" % fZTot)
2583 
2584  # Finally map the vector as required.
2585  return forces
2586 
2587  def getAcousticData(self, groupName=None):
2588  """
2589  Return the acoustic data on this processor.
2590  Parameters
2591  ----------
2592  groupName : str
2593  Group identifier to get only data cooresponding to the
2594  desired group. The group must be a family or a user-supplied
2595  group of families. The default is None which corresponds to
2596  design surfaces.
2597 
2598  Returns
2599  -------
2600  position : array (N,3)
2601  Face positions on this processor. Note that N may be 0, and an
2602  empty array of shape (0, 3) can be returned.
2603  normal : array (N,3)
2604  Face normals on this processor. Note that N may be 0, and an
2605  empty array of shape (0, 3) can be returned.
2606  area : array (N)
2607  Face areas on this processor. Note that N may be 0, and an
2608  empty array of shape (0) can be returned.
2609  forces : array (N,3)
2610  Face forces on this processor. Note that N may be 0, and an
2611  empty array of shape (0, 3) can be returned.
2612  """
2613  Info("Computing surface acoustic data")
2614  # Calculate number of surface cells
2615  if groupName is None:
2616  raise ValueError("Aeroacoustic grouName not set!")
2617  _, nCls = self._getSurfaceSize(groupName)
2618 
2619  x = PETSc.Vec().create(comm=PETSc.COMM_WORLD)
2620  x.setSizes((nCls, PETSc.DECIDE), bsize=1)
2621  x.setFromOptions()
2622 
2623  y = PETSc.Vec().create(comm=PETSc.COMM_WORLD)
2624  y.setSizes((nCls, PETSc.DECIDE), bsize=1)
2625  y.setFromOptions()
2626 
2627  z = PETSc.Vec().create(comm=PETSc.COMM_WORLD)
2628  z.setSizes((nCls, PETSc.DECIDE), bsize=1)
2629  z.setFromOptions()
2630 
2631  nX = PETSc.Vec().create(comm=PETSc.COMM_WORLD)
2632  nX.setSizes((nCls, PETSc.DECIDE), bsize=1)
2633  nX.setFromOptions()
2634 
2635  nY = PETSc.Vec().create(comm=PETSc.COMM_WORLD)
2636  nY.setSizes((nCls, PETSc.DECIDE), bsize=1)
2637  nY.setFromOptions()
2638 
2639  nZ = PETSc.Vec().create(comm=PETSc.COMM_WORLD)
2640  nZ.setSizes((nCls, PETSc.DECIDE), bsize=1)
2641  nZ.setFromOptions()
2642 
2643  a = PETSc.Vec().create(comm=PETSc.COMM_WORLD)
2644  a.setSizes((nCls, PETSc.DECIDE), bsize=1)
2645  a.setFromOptions()
2646 
2647  fX = PETSc.Vec().create(comm=PETSc.COMM_WORLD)
2648  fX.setSizes((nCls, PETSc.DECIDE), bsize=1)
2649  fX.setFromOptions()
2650 
2651  fY = PETSc.Vec().create(comm=PETSc.COMM_WORLD)
2652  fY.setSizes((nCls, PETSc.DECIDE), bsize=1)
2653  fY.setFromOptions()
2654 
2655  fZ = PETSc.Vec().create(comm=PETSc.COMM_WORLD)
2656  fZ.setSizes((nCls, PETSc.DECIDE), bsize=1)
2657  fZ.setFromOptions()
2658 
2659  # Compute forces
2660  self.solver.getAcousticData(x, y, z, nX, nY, nZ, a, fX, fY, fZ, groupName.encode())
2661 
2662  # Copy data from PETSc vectors
2663  positions = np.zeros((nCls, 3))
2664  positions[:, 0] = np.copy(x.getArray())
2665  positions[:, 1] = np.copy(y.getArray())
2666  positions[:, 2] = np.copy(z.getArray())
2667  normals = np.zeros((nCls, 3))
2668  normals[:, 0] = np.copy(nX.getArray())
2669  normals[:, 1] = np.copy(nY.getArray())
2670  normals[:, 2] = np.copy(nZ.getArray())
2671  areas = np.zeros(nCls)
2672  areas[:] = np.copy(a.getArray())
2673  forces = np.zeros((nCls, 3))
2674  forces[:, 0] = np.copy(fX.getArray())
2675  forces[:, 1] = np.copy(fY.getArray())
2676  forces[:, 2] = np.copy(fZ.getArray())
2677 
2678  # Cleanup PETSc vectors
2679  x.destroy()
2680  y.destroy()
2681  z.destroy()
2682  nX.destroy()
2683  nY.destroy()
2684  nZ.destroy()
2685  a.destroy()
2686  fX.destroy()
2687  fY.destroy()
2688  fZ.destroy()
2689 
2690  # Finally map the vector as required.
2691  return positions, normals, areas, forces
2692 
2693  def calcTotalDeriv(self, dRdX, dFdX, psi, totalDeriv):
2694  """
2695  Compute total derivative
2696 
2697  Input:
2698  ------
2699  dRdX, dFdX, and psi
2700 
2701  Output:
2702  ------
2703  totalDeriv = dFdX - [dRdX]^T * psi
2704  """
2705 
2706  dRdX.multTranspose(psi, totalDeriv)
2707  totalDeriv.scale(-1.0)
2708  totalDeriv.axpy(1.0, dFdX)
2709 
2710  def calcdFdAOAAnalytical(self, objFuncName, dFdAOA):
2711  """
2712  This function computes partials derivatives dFdAlpha with alpha being the angle of attack (AOA)
2713  We use the analytical method:
2714  CD = Fx * cos(alpha) + Fy * sin(alpha)
2715  CL = - Fx * sin(alpha) + Fy * cos(alpha)
2716  So:
2717  dCD/dAlpha = - Fx * sin(alpha) + Fy * cos(alpha) = CL
2718  dCL/dAlpha = - Fx * cos(alpha) - Fy * sin(alpha) = - CD
2719  NOTE: we need to convert the unit from radian to degree
2720  """
2721 
2722  objFuncDict = self.getOption("objFunc")
2723 
2724  # find the neededMode of this objective function and also find out if it is a force objective
2725  neededMode = "None"
2726  isForceObj = 0
2727  for objFuncPart in objFuncDict[objFuncName]:
2728  if objFuncDict[objFuncName][objFuncPart]["type"] == "force":
2729  isForceObj = 1
2730  if objFuncDict[objFuncName][objFuncPart]["directionMode"] == "fixedDirection":
2731  raise Error("AOA derivative does not support directionMode=fixedDirection!")
2732  elif objFuncDict[objFuncName][objFuncPart]["directionMode"] == "parallelToFlow":
2733  neededMode = "normalToFlow"
2734  break
2735  elif objFuncDict[objFuncName][objFuncPart]["directionMode"] == "normalToFlow":
2736  neededMode = "parallelToFlow"
2737  break
2738  else:
2739  raise Error("directionMode not valid!")
2740 
2741  # if it is a forceObj, use the analytical approach to calculate dFdAOA, otherwise set it to zero
2742  if isForceObj == 1:
2743  # loop over all objectives again to find the neededMode
2744  # Note that if the neededMode == "parallelToFlow", we need to add a minus sign
2745  for objFuncNameNeeded in objFuncDict:
2746  for objFuncPart in objFuncDict[objFuncNameNeeded]:
2747  if objFuncDict[objFuncNameNeeded][objFuncPart]["type"] == "force":
2748  if objFuncDict[objFuncNameNeeded][objFuncPart]["directionMode"] == neededMode:
2749  val = self.solver.getObjFuncValue(objFuncNameNeeded.encode())
2750  if neededMode == "parallelToFlow":
2751  dFdAOA[0] = -val * np.pi / 180.0
2752  elif neededMode == "normalToFlow":
2753  dFdAOA[0] = val * np.pi / 180.0
2754  dFdAOA.assemblyBegin()
2755  dFdAOA.assemblyEnd()
2756  break
2757  else:
2758  dFdAOA.zeroEntries()
2759 
2760  def _initSolver(self):
2761  """
2762  Initialize the solvers. This needs to be called before calling any runs
2763  """
2764 
2765  if self.solverInitialized == 1:
2766  raise Error("pyDAFoam: self._initSolver has been called! One shouldn't initialize solvers twice!")
2767 
2768  solverName = self.getOption("solverName")
2769  solverArg = solverName + " -python " + self.parallelFlag
2770  if solverName in self.solverRegistry["Incompressible"]:
2771 
2772  from .pyDASolverIncompressible import pyDASolvers
2773 
2774  self.solver = pyDASolvers(solverArg.encode(), self.options)
2775 
2776  if self.getOption("useAD")["mode"] == "forward":
2777 
2778  from .pyDASolverIncompressibleADF import pyDASolvers as pyDASolversAD
2779 
2780  self.solverAD = pyDASolversAD(solverArg.encode(), self.options)
2781 
2782  elif self.getOption("useAD")["mode"] == "reverse":
2783 
2784  from .pyDASolverIncompressibleADR import pyDASolvers as pyDASolversAD
2785 
2786  self.solverAD = pyDASolversAD(solverArg.encode(), self.options)
2787 
2788  elif solverName in self.solverRegistry["Compressible"]:
2789 
2790  from .pyDASolverCompressible import pyDASolvers
2791 
2792  self.solver = pyDASolvers(solverArg.encode(), self.options)
2793 
2794  if self.getOption("useAD")["mode"] == "forward":
2795 
2796  from .pyDASolverCompressibleADF import pyDASolvers as pyDASolversAD
2797 
2798  self.solverAD = pyDASolversAD(solverArg.encode(), self.options)
2799 
2800  elif self.getOption("useAD")["mode"] == "reverse":
2801 
2802  from .pyDASolverCompressibleADR import pyDASolvers as pyDASolversAD
2803 
2804  self.solverAD = pyDASolversAD(solverArg.encode(), self.options)
2805 
2806  elif solverName in self.solverRegistry["Solid"]:
2807 
2808  from .pyDASolverSolid import pyDASolvers
2809 
2810  self.solver = pyDASolvers(solverArg.encode(), self.options)
2811 
2812  if self.getOption("useAD")["mode"] == "forward":
2813 
2814  from .pyDASolverSolidADF import pyDASolvers as pyDASolversAD
2815 
2816  self.solverAD = pyDASolversAD(solverArg.encode(), self.options)
2817 
2818  elif self.getOption("useAD")["mode"] == "reverse":
2819 
2820  from .pyDASolverSolidADR import pyDASolvers as pyDASolversAD
2821 
2822  self.solverAD = pyDASolversAD(solverArg.encode(), self.options)
2823  else:
2824  raise Error("pyDAFoam: %s not registered! Check _solverRegistry(self)." % solverName)
2825 
2826  self.solver.initSolver()
2827 
2828  if self.getOption("useAD")["mode"] in ["forward", "reverse"]:
2829  self.solverAD.initSolver()
2830 
2831  if self.getOption("printDAOptions"):
2832  self.solver.printAllOptions()
2833 
2834  adjMode = self.getOption("unsteadyAdjoint")["mode"]
2835  if adjMode == "hybridAdjoint" or adjMode == "timeAccurateAdjoint":
2836  self.initTimeInstanceMats()
2837 
2838  self.solverInitialized = 1
2839 
2840  return
2841 
2842  def runColoring(self):
2843  """
2844  Run coloring solver
2845  """
2846 
2847  Info("\n")
2848  Info("+--------------------------------------------------------------------------+")
2849  Info("| Running Coloring Solver |")
2850  Info("+--------------------------------------------------------------------------+")
2851 
2852  solverName = self.getOption("solverName")
2853  if solverName in self.solverRegistry["Incompressible"]:
2854 
2855  from .pyColoringIncompressible import pyColoringIncompressible
2856 
2857  solverArg = "ColoringIncompressible -python " + self.parallelFlag
2858  solver = pyColoringIncompressible(solverArg.encode(), self.options)
2859  elif solverName in self.solverRegistry["Compressible"]:
2860 
2861  from .pyColoringCompressible import pyColoringCompressible
2862 
2863  solverArg = "ColoringCompressible -python " + self.parallelFlag
2864  solver = pyColoringCompressible(solverArg.encode(), self.options)
2865  elif solverName in self.solverRegistry["Solid"]:
2866 
2867  from .pyColoringSolid import pyColoringSolid
2868 
2869  solverArg = "ColoringSolid -python " + self.parallelFlag
2870  solver = pyColoringSolid(solverArg.encode(), self.options)
2871  else:
2872  raise Error("pyDAFoam: %s not registered! Check _solverRegistry(self)." % solverName)
2873  solver.run()
2874 
2875  solver = None
2876 
2877  return
2878 
2879  def runDecomposePar(self):
2880  """
2881  Run decomposePar to parallel run
2882  """
2883 
2884  # don't run it if it is a serial case
2885  if self.comm.size == 1:
2886  return
2887 
2888  # write the decomposeParDict file with the correct numberOfSubdomains number
2889  self._writeDecomposeParDict()
2890 
2891  if self.comm.rank == 0:
2892  status = subprocess.call("decomposePar", stdout=sys.stdout, stderr=subprocess.STDOUT, shell=False)
2893  if status != 0:
2894  # raise Error('pyDAFoam: status %d: Unable to run decomposePar'%status)
2895  print("\nUnable to run decomposePar, the domain has been already decomposed?\n", flush=True)
2896  self.comm.Barrier()
2897 
2898  return
2899 
2901  """
2902  Delete the previous primal solution time folder
2903  """
2904 
2905  solTime = self.solver.getPrevPrimalSolTime()
2906 
2907  rootDir = os.getcwd()
2908  if self.parallel:
2909  checkPath = os.path.join(rootDir, "processor%d/%g" % (self.comm.rank, solTime))
2910  else:
2911  checkPath = os.path.join(rootDir, "%g" % solTime)
2912 
2913  if os.path.isdir(checkPath):
2914  try:
2915  shutil.rmtree(checkPath)
2916  except Exception:
2917  raise Error("Can not delete %s" % checkPath)
2918 
2919  Info("Previous solution time %g found and deleted." % solTime)
2920  else:
2921  Info("Previous solution time %g not found and nothing deleted." % solTime)
2922 
2923  return
2924 
2925  def renameSolution(self, solIndex):
2926  """
2927  Rename the primal solution folder to specific format for post-processing. The renamed time has the
2928  format like 0.00000001, 0.00000002, etc. One can load these intermediate shapes and fields and
2929  plot them in paraview.
2930  The way it is implemented is that we sort the solution folder and consider the largest time folder
2931  as the solution folder and rename it
2932 
2933  Parameters
2934  ----------
2935  solIndex: int
2936  The major interation index
2937  """
2938 
2939  allSolutions = []
2940  rootDir = os.getcwd()
2941  if self.parallel:
2942  checkPath = os.path.join(rootDir, "processor%d" % self.comm.rank)
2943  else:
2944  checkPath = rootDir
2945 
2946  folderNames = os.listdir(checkPath)
2947  for folderName in folderNames:
2948  try:
2949  float(folderName)
2950  allSolutions.append(folderName)
2951  except ValueError:
2952  continue
2953  allSolutions.sort(reverse=True)
2954  # choose the latst solution to rename
2955  solutionTime = allSolutions[0]
2956 
2957  if float(solutionTime) < 1e-4:
2958  Info("Solution time %g less than 1e-4, not renamed." % float(solutionTime))
2959  renamed = False
2960  return solutionTime, renamed
2961 
2962  distTime = "%.8f" % (solIndex / 1e8)
2963 
2964  src = os.path.join(checkPath, solutionTime)
2965  dst = os.path.join(checkPath, distTime)
2966 
2967  Info("Moving time %s to %s" % (solutionTime, distTime))
2968 
2969  if os.path.isdir(dst):
2970  raise Error("%s already exists, moving failed!" % dst)
2971  else:
2972  try:
2973  shutil.move(src, dst)
2974  except Exception:
2975  raise Error("Can not move %s to %s" % (src, dst))
2976 
2977  renamed = True
2978  return distTime, renamed
2979 
2981  """
2982  Calculate the FFD2XvSeedVec vector:
2983  Given a FFD seed xDvDot, run pyGeo and IDWarp and propagate the seed to Xv seed xVDot:
2984  xSDot = \\frac{dX_{S}}{dX_{DV}}\\xDvDot
2985  xVDot = \\frac{dX_{V}}{dX_{S}}\\xSDot
2986 
2987  Then, we assign this vector to FFD2XvSeedVec in DASolver
2988  This will be used in forward mode AD runs
2989  """
2990 
2991  if self.DVGeo is None:
2992  raise Error("DVGeo not set!")
2993 
2994  dvName = self.getOption("useAD")["dvName"]
2995  seedIndex = self.getOption("useAD")["seedIndex"]
2996  # create xDVDot vec and initialize it with zeros
2997  xDV = self.DVGeo.getValues()
2998 
2999  # create a copy of xDV and set the seed to 1.0
3000  # the dv and index depends on dvName and seedIndex
3001  xDvDot = {}
3002  for key in list(xDV.keys()):
3003  xDvDot[key] = np.zeros_like(xDV[key], dtype=self.dtype)
3004  xDvDot[dvName][seedIndex] = 1.0
3005 
3006  # get the original surf coords
3007  xSDot0 = np.zeros_like(self.xs0, self.dtype)
3008  xSDot0 = self.mapVector(xSDot0, self.allSurfacesGroup, self.designSurfacesGroup)
3009 
3010  # get xSDot
3011  xSDot = self.DVGeo.totalSensitivityProd(xDvDot, ptSetName=self.ptSetName).reshape(xSDot0.shape)
3012  # get xVDot
3013  xVDot = self.mesh.warpDerivFwd(xSDot)
3014 
3015  seedVec = self.xvVec.duplicate()
3016  seedVec.zeroEntries()
3017  Istart, Iend = seedVec.getOwnershipRange()
3018 
3019  # assign xVDot to seedVec
3020  for idx in range(Istart, Iend):
3021  idxRel = idx - Istart
3022  seedVec[idx] = xVDot[idxRel]
3023 
3024  seedVec.assemblyBegin()
3025  seedVec.assemblyEnd()
3026 
3027  self.solverAD.setFFD2XvSeedVec(seedVec)
3028 
3029  def setdXvdFFDMat(self, designVarName, deltaVPointThreshold=1.0e-16):
3030  """
3031  Perturb each design variable and save the delta volume point coordinates
3032  to a mat, this will be used to calculate dRdFFD and dFdFFD in DAFoam
3033 
3034  Parameters
3035  ----------
3036  deltaVPointThreshold: float
3037  A threshold, any delta volume coordinates smaller than this value will be ignored
3038 
3039  """
3040 
3041  if self.DVGeo is None:
3042  raise Error("DVGeo not set!")
3043 
3044  # Get the FFD size
3045  nDVs = -9999
3046  xDV = self.DVGeo.getValues()
3047  nDVs = len(xDV[designVarName])
3048 
3049  # get the unperturbed point coordinates
3050  oldVolPoints = self.mesh.getSolverGrid()
3051  # get the size of xv, it is the number of points * 3
3052  nXvs = len(oldVolPoints)
3053  # get eps
3054  epsFFD = self.getOption("adjPartDerivFDStep")["FFD"]
3055 
3056  Info("Calculating the dXvdFFD matrix with epsFFD: " + str(epsFFD))
3057 
3058  dXvdFFDMat = PETSc.Mat().create(PETSc.COMM_WORLD)
3059  dXvdFFDMat.setSizes(((nXvs, None), (None, nDVs)))
3060  dXvdFFDMat.setFromOptions()
3061  dXvdFFDMat.setPreallocationNNZ((nDVs, nDVs))
3062  dXvdFFDMat.setUp()
3063  Istart, Iend = dXvdFFDMat.getOwnershipRange()
3064 
3065  # for each DV, perturb epsFFD and save the delta vol point coordinates
3066  for i in range(nDVs):
3067  # perturb
3068  xDV[designVarName][i] += epsFFD
3069  # set the dv to DVGeo
3070  self.DVGeo.setDesignVars(xDV)
3071  # update the vol points according to the new DV values
3072  self.updateVolumePoints()
3073  # get the new vol points
3074  newVolPoints = self.mesh.getSolverGrid()
3075  # assign the delta vol coords to the mat
3076  for idx in range(Istart, Iend):
3077  idxRel = idx - Istart
3078  deltaVal = newVolPoints[idxRel] - oldVolPoints[idxRel]
3079  if abs(deltaVal) > deltaVPointThreshold: # a threshold
3080  dXvdFFDMat[idx, i] = deltaVal
3081  # reset the perturbation of the dv
3082  xDV[designVarName][i] -= epsFFD
3083 
3084  # reset the volume mesh coordinates
3085  self.DVGeo.setDesignVars(xDV)
3086  self.updateVolumePoints()
3087 
3088  # assemble
3089  dXvdFFDMat.assemblyBegin()
3090  dXvdFFDMat.assemblyEnd()
3091 
3092  # viewer = PETSc.Viewer().createASCII("dXvdFFDMat_%s_%s.dat" % (designVarName, self.comm.size), "w")
3093  # viewer(dXvdFFDMat)
3094 
3095  self.solver.setdXvdFFDMat(dXvdFFDMat)
3096 
3097  dXvdFFDMat.destroy()
3098 
3099  return nDVs
3100 
3102  """
3103  Update the vol mesh point coordinates based on the current values of design variables
3104  """
3105 
3106  # update the CFD Coordinates
3107  if self.DVGeo is not None:
3108  if self.ptSetName not in self.DVGeo.points:
3109  xs0 = self.mapVector(self.xs0, self.allSurfacesGroup, self.designSurfacesGroup)
3110  self.DVGeo.addPointSet(xs0, self.ptSetName)
3111  self.pointsSet = True
3112 
3113  # set the surface coords
3114  if not self.DVGeo.pointSetUpToDate(self.ptSetName):
3115  coords = self.DVGeo.update(self.ptSetName, config=None)
3116  self.setSurfaceCoordinates(coords, self.designSurfacesGroup)
3117 
3118  # warp the mesh
3119  self.mesh.warpMesh()
3120 
3121  return
3122 
3123  def _readMeshInfo(self):
3124  """
3125  Initialize mesh information and read mesh information
3126  """
3127 
3128  dirName = os.getcwd()
3129 
3130  self.fileNames, self.xv0, self.faces, self.boundaries, self.owners, self.neighbours = self._readOFGrid(dirName)
3131  self.xv = copy.copy(self.xv0)
3132 
3133  return
3134 
3135  def setSurfaceCoordinates(self, coordinates, groupName=None):
3136  """
3137  Set the updated surface coordinates for a particular group.
3138  Parameters
3139  ----------
3140  coordinates : numpy array
3141  Numpy array of size Nx3, where N is the number of coordinates on this processor.
3142  This array must have the same shape as the array obtained with getSurfaceCoordinates()
3143  groupName : str
3144  Name of family or group of families for which to return coordinates for.
3145  """
3146  if self.mesh is None:
3147  return
3148 
3149  if groupName is None:
3150  groupName = self.allWallsGroup
3151 
3152  self._updateGeomInfo = True
3153  if self.mesh is None:
3154  raise Error("Cannot set new surface coordinate locations without a mesh" "warping object present.")
3155 
3156  # First get the surface coordinates of the meshFamily in case
3157  # the groupName is a subset, those values will remain unchanged.
3158 
3159  meshSurfCoords = self.getSurfaceCoordinates(self.allWallsGroup)
3160  meshSurfCoords = self.mapVector(coordinates, groupName, self.allWallsGroup, meshSurfCoords)
3161 
3162  self.mesh.setSurfaceCoordinates(meshSurfCoords)
3163 
3164  def getSurfaceCoordinates(self, groupName=None):
3165  """
3166  Return the coordinates for the surfaces defined by groupName.
3167 
3168  Parameters
3169  ----------
3170  groupName : str
3171  Group identifier to get only coordinates cooresponding to
3172  the desired group. The group must be a family or a
3173  user-supplied group of families. The default is None which
3174  corresponds to all wall-type surfaces.
3175 
3176  Output
3177  ------
3178  xs: numpy array of size nPoints * 3 for surface points
3179  """
3180 
3181  if groupName is None:
3182  groupName = self.allWallsGroup
3183 
3184  # Get the required size
3185  npts, ncell = self._getSurfaceSize(groupName)
3186  xs = np.zeros((npts, 3), self.dtype)
3187 
3188  # loop over the families in this group and populate the surface
3189  famInd = self.families[groupName]
3190  counter = 0
3191  for Ind in famInd:
3192  name = self.basicFamilies[Ind]
3193  bc = self.boundaries[name]
3194  for ptInd in bc["indicesRed"]:
3195  xs[counter, :] = self.xv[ptInd]
3196  counter += 1
3197 
3198  return xs
3199 
3200  def _getSurfaceSize(self, groupName):
3201  """
3202  Internal routine to return the size of a particular surface. This
3203  does *NOT* set the actual family group
3204  """
3205  if groupName is None:
3206  groupName = self.allSurfacesGroup
3207 
3208  if groupName not in self.families:
3209  raise Error(
3210  "'%s' is not a family in the OpenFoam Case or has not been added"
3211  " as a combination of families" % groupName
3212  )
3213 
3214  # loop over the basic surfaces in the family group and sum up the number of
3215  # faces and nodes
3216 
3217  famInd = self.families[groupName]
3218  nPts = 0
3219  nCells = 0
3220  for Ind in famInd:
3221  name = self.basicFamilies[Ind]
3222  bc = self.boundaries[name]
3223  nCells += len(bc["facesRed"])
3224  nPts += len(bc["indicesRed"])
3225 
3226  return nPts, nCells
3227 
3228  def setPrimalBoundaryConditions(self, printInfo=1, printInfoAD=0):
3229  """
3230  Assign the boundary condition defined in primalBC to the OF fields
3231  """
3232  self.solver.setPrimalBoundaryConditions(printInfo)
3233  if self.getOption("useAD")["mode"] in ["forward", "reverse"]:
3234  self.solverAD.setPrimalBoundaryConditions(printInfoAD)
3235 
3236  def _computeBasicFamilyInfo(self):
3237  """
3238  Loop over the boundary data and compute necessary family
3239  information for the basic patches
3240 
3241  """
3242  # get the list of basic families
3243  self.basicFamilies = sorted(self.boundaries.keys())
3244 
3245  # save and return a list of the wall boundaries
3246  self.wallList = []
3247  counter = 0
3248  # for each boundary, figure out the unique list of volume node indices it uses
3249  for name in self.basicFamilies:
3250  # setup the basic families dictionary
3251  self.families[name] = [counter]
3252  counter += 1
3253 
3254  # Create a handle for this boundary
3255  bc = self.boundaries[name]
3256 
3257  # get the number of faces associated with this boundary
3258  nFace = len(bc["faces"])
3259 
3260  # create the point index list
3261  indices = []
3262 
3263  # check that this isn't an empty boundary
3264  if nFace > 0:
3265  for iFace in bc["faces"]:
3266  # get the node information for the current face
3267  face = self.faces[iFace]
3268  indices.extend(face)
3269 
3270  # Get the unique point entries for this boundary
3271  indices = np.unique(indices)
3272 
3273  # now create the reverse dictionary to connect the reduced set with the original
3274  inverseInd = {}
3275  for i in range(len(indices)):
3276  inverseInd[indices[i]] = i
3277 
3278  # Now loop back over the faces and store the connectivity in terms of the reduces index set
3279  # Here facesRed store the boundary face reduced-point-index
3280  # For example,
3281  # 'indicesRed': [0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80] <- unique point index for this boundary
3282  # 'facesRed': [0, 8, 9, 1], [1, 9, 10, 2] <- Here 0 means the 0th point index (0) in indicesRed, and 8 means the 8th
3283  # point index (64) in incidexRed. So [0 8 9 1] corresponds to the face [0 64 72 8] in the original point index system.
3284  # NOTE: using the reduce face indexing will faciliate the connectivity calls
3285  facesRed = []
3286  for iFace in bc["faces"]:
3287  # get the node information for the current face
3288  face = self.faces[iFace]
3289  nNodes = len(face)
3290  # Generate the reduced connectivity.
3291  faceReduced = []
3292  for j in range(nNodes):
3293  indOrig = face[j]
3294  indRed = inverseInd[indOrig]
3295  faceReduced.append(indRed)
3296  facesRed.append(faceReduced)
3297 
3298  # Check that the length of faces and facesRed are equal
3299  if not (len(bc["faces"]) == len(facesRed)):
3300  raise Error("Connectivity for faces on reduced index set is not the same length as original.")
3301 
3302  # put the reduced faces and index list in the boundary dict
3303  bc["facesRed"] = facesRed
3304  bc["indicesRed"] = list(indices)
3305 
3306  # now check for walls
3307  if bc["type"] == "wall" or bc["type"] == "slip" or bc["type"] == "cyclic":
3308  self.wallList.append(name)
3309 
3310  return
3311 
3312  def getPointSetName(self):
3313  """
3314  Take the apName and return the mangled point set name.
3315  """
3316  return "openFoamCoords"
3317 
3319  """
3320  Get the list of indices to pass to the mesh object for the
3321  volume mesh mapping
3322  """
3323 
3324  # Setup External Warping
3325  nCoords = len(self.xv0.flatten())
3326 
3327  nCoords = self.comm.allgather(nCoords)
3328  offset = 0
3329  for i in range(self.comm.rank):
3330  offset += nCoords[i]
3331 
3332  meshInd = np.arange(nCoords[self.comm.rank]) + offset
3333 
3334  return meshInd
3335 
3336  def mapVector(self, vec1, groupName1, groupName2, vec2=None):
3337  """This is the main workhorse routine of everything that deals with
3338  families in pyDAFoam. The purpose of this routine is to convert a
3339  vector 'vec1' (of size Nx3) that was evaluated with
3340  'groupName1' and expand or contract it (and adjust the
3341  ordering) to produce 'vec2' evaluated on groupName2.
3342 
3343  A little ascii art might help. Consider the following "mesh"
3344  . Family 'fam1' has 9 points, 'fam2' has 10 pts and 'fam3' has
3345  5 points. Consider that we have also also added two
3346  additional groups: 'f12' containing 'fam1' and 'fma2' and a
3347  group 'f23' that contains families 'fam2' and 'fam3'. The vector
3348  we want to map is 'vec1'. It is length 9+10. All the 'x's are
3349  significant values.
3350 
3351  The call: mapVector(vec1, 'f12', 'f23')
3352 
3353  will produce the "returned vec" array, containing the
3354  significant values from 'fam2', where the two groups overlap,
3355  and the new values from 'fam3' set to zero. The values from
3356  fam1 are lost. The returned vec has size 15.
3357 
3358  fam1 fam2 fam3
3359  |---------+----------+------|
3360 
3361  |xxxxxxxxx xxxxxxxxxx| <- vec1
3362  |xxxxxxxxxx 000000| <- returned vec (vec2)
3363 
3364  Parameters
3365  ----------
3366  vec1 : Numpy array
3367  Array of size Nx3 that will be mapped to a different family set.
3368 
3369  groupName1 : str
3370  The family group where the vector vec1 is currently defined
3371 
3372  groupName2 : str
3373  The family group where we want to the vector to mapped into
3374 
3375  vec2 : Numpy array or None
3376  Array containing existing values in the output vector we want to keep.
3377  If this vector is not given, the values will be filled with zeros.
3378 
3379  Returns
3380  -------
3381  vec2 : Numpy array
3382  The input vector mapped to the families defined in groupName2.
3383  """
3384  if groupName1 not in self.families or groupName2 not in self.families:
3385  raise Error(
3386  "'%s' or '%s' is not a family in the mesh file or has not been added"
3387  " as a combination of families" % (groupName1, groupName2)
3388  )
3389 
3390  # Shortcut:
3391  if groupName1 == groupName2:
3392  return vec1
3393 
3394  if vec2 is None:
3395  npts, ncell = self._getSurfaceSize(groupName2)
3396  vec2 = np.zeros((npts, 3), self.dtype)
3397 
3398  famList1 = self.families[groupName1]
3399  famList2 = self.families[groupName2]
3400 
3401  """
3402  This functionality is predicated on the surfaces being traversed in the
3403  same order every time. Loop over the allfamilies list, keeping track of sizes
3404  as we go and if the family is in both famLists, copy the values from vec1 to vec2.
3405 
3406  """
3407 
3408  vec1counter = 0
3409  vec2counter = 0
3410 
3411  for ind in self.families[self.allSurfacesGroup]:
3412  npts, ncell = self._getSurfaceSize(self.basicFamilies[ind])
3413 
3414  if ind in famList1 and ind in famList2:
3415  vec2[vec2counter : npts + vec2counter] = vec1[vec1counter : npts + vec1counter]
3416 
3417  if ind in famList1:
3418  vec1counter += npts
3419 
3420  if ind in famList2:
3421  vec2counter += npts
3422 
3423  return vec2
3424 
3425  def _initializeMeshPointVec(self):
3426  """
3427  Initialize the mesh point vec: xvVec
3428  """
3429 
3430  xvSize = len(self.xv) * 3
3431  self.xvVec = PETSc.Vec().create(comm=PETSc.COMM_WORLD)
3432  self.xvVec.setSizes((xvSize, PETSc.DECIDE), bsize=1)
3433  self.xvVec.setFromOptions()
3434 
3435  self.xv2XvVec(self.xv, self.xvVec)
3436 
3437  # viewer = PETSc.Viewer().createASCII("xvVec", comm=PETSc.COMM_WORLD)
3438  # viewer(self.xvVec)
3439 
3440  return
3441 
3442  def _initializeStateVec(self):
3443  """
3444  Initialize state variable vector
3445  """
3446 
3447  wSize = self.solver.getNLocalAdjointStates()
3448  self.wVec = PETSc.Vec().create(comm=PETSc.COMM_WORLD)
3449  self.wVec.setSizes((wSize, PETSc.DECIDE), bsize=1)
3450  self.wVec.setFromOptions()
3451 
3452  self.solver.ofField2StateVec(self.wVec)
3453 
3454  # viewer = PETSc.Viewer().createASCII("wVec", comm=PETSc.COMM_WORLD)
3455  # viewer(self.wVec)
3456 
3457  # if it is a multipoint case, initialize self.wVecMPList
3458  if self.getOption("multiPoint") is True:
3459  nMultiPoints = self.getOption("nMultiPoints")
3460  self.wVecMPList = [None] * self.getOption("nMultiPoints")
3461  for i in range(nMultiPoints):
3462  self.wVecMPList[i] = PETSc.Vec().create(comm=PETSc.COMM_WORLD)
3463  self.wVecMPList[i].setSizes((wSize, PETSc.DECIDE), bsize=1)
3464  self.wVecMPList[i].setFromOptions()
3465 
3466  return
3467 
3468  def xv2XvVec(self, xv, xvVec):
3469  """
3470  Convert a Nx3 mesh point numpy array to a Petsc xvVec
3471  """
3472 
3473  xSize = len(xv)
3474 
3475  for i in range(xSize):
3476  for j in range(3):
3477  globalIdx = self.solver.getGlobalXvIndex(i, j)
3478  xvVec[globalIdx] = xv[i][j]
3479 
3480  xvVec.assemblyBegin()
3481  xvVec.assemblyEnd()
3482 
3483  return
3484 
3485  def xvFlatten2XvVec(self, xv, xvVec):
3486  """
3487  Convert a 3Nx1 mesh point numpy array to a Petsc xvVec
3488  """
3489 
3490  xSize = len(xv)
3491  xSize = int(xSize // 3)
3492 
3493  counterI = 0
3494  for i in range(xSize):
3495  for j in range(3):
3496  globalIdx = self.solver.getGlobalXvIndex(i, j)
3497  xvVec[globalIdx] = xv[counterI]
3498  counterI += 1
3499 
3500  xvVec.assemblyBegin()
3501  xvVec.assemblyEnd()
3502 
3503  return
3504 
3505  # base case files
3506  def _readOFGrid(self, caseDir):
3507  """
3508  Read in the mesh information we need to run the case using pyofm
3509 
3510  Parameters
3511  ----------
3512  caseDir : str
3513  The directory containing the openFOAM Mesh files
3514  """
3515 
3516  Info("Reading OpenFOAM mesh information...")
3517 
3518  from pyofm import PYOFM
3519 
3520  # Initialize pyOFM
3521  ofm = PYOFM(comm=self.comm)
3522 
3523  # generate the file names
3524  fileNames = ofm.getFileNames(caseDir, comm=self.comm)
3525 
3526  # Read in the volume points
3527  x0 = ofm.readVolumeMeshPoints()
3528 
3529  # Read the face info for the mesh
3530  faces = ofm.readFaceInfo()
3531 
3532  # Read the boundary info
3533  boundaries = ofm.readBoundaryInfo(faces)
3534 
3535  # Read the cell info for the mesh
3536  owners, neighbours = ofm.readCellInfo()
3537 
3538  return fileNames, x0, faces, boundaries, owners, neighbours
3539 
3540  def setOption(self, name, value):
3541  """
3542  Set a value to options.
3543  NOTE: calling this function will only change the values in self.options
3544  It will NOT change values for allOptions_ in DAOption. To make the options changes
3545  from pyDAFoam to DASolvers, call self.updateDAOption()
3546 
3547  Parameters
3548  ----------
3549  name : str
3550  Name of option to set. Not case sensitive
3551  value : varies
3552  Value to set. Type is checked for consistency.
3553 
3554  Examples
3555  --------
3556  If self.options reads:
3557  self.options =
3558  {
3559  'solverName': [str, 'DASimpleFoam'],
3560  'flowEndTime': [float, 1.0]
3561  }
3562  Then, calling self.options('solverName', 'DARhoSimpleFoam') will give:
3563  self.options =
3564  {
3565  'solverName': [str, 'DARhoSimpleFoam'],
3566  'flowEndTime': [float, 1.0]
3567  }
3568 
3569 
3570  NOTE: if 'value' is of dict type, we will set all the subKey values in
3571  'value' dict to self.options, instead of overiding it. This works for
3572  only THREE levels of subDicts
3573 
3574  For example, if self.options reads
3575  self.options =
3576  {
3577  'objFunc': [dict, {
3578  'name': 'CD',
3579  'direction': [1.0, 0.0, 0.0],
3580  'scale': 1.0}]
3581  }
3582 
3583  Then, calling self.setOption('objFunc', {'name': 'CL'}) will give:
3584 
3585  self.options =
3586  {
3587  'objFunc': [dict, {
3588  'name': 'CL',
3589  'direction': [1.0, 0.0, 0.0],
3590  'scale': 1.0}]
3591  }
3592 
3593  INSTEAD OF
3594 
3595  self.options =
3596  {
3597  'objFunc': [dict, {'name': 'CL'}]
3598  }
3599  """
3600 
3601  try:
3602  self.defaultOptions[name]
3603  except KeyError:
3604  Error("Option '%-30s' is not a valid %s option." % (name, self.name))
3605 
3606  # Make sure we are not trying to change an immutable option if
3607  # we are not allowed to.
3608  if name in self.imOptions:
3609  raise Error("Option '%-35s' cannot be modified after the solver " "is created." % name)
3610 
3611  # Now we know the option exists, lets check if the type is ok:
3612  if isinstance(value, self.defaultOptions[name][0]):
3613  # the type matches, now we need to check if the 'value' is of dict type, if yes, we only
3614  # replace the subKey values of 'value', instead of overiding all the subKey values
3615  # NOTE. we only check 3 levels of subKeys
3616  if isinstance(value, dict):
3617  for subKey1 in value:
3618  # check if this subKey is still a dict.
3619  if isinstance(value[subKey1], dict):
3620  for subKey2 in value[subKey1]:
3621  # check if this subKey is still a dict.
3622  if isinstance(value[subKey1][subKey2], dict):
3623  for subKey3 in value[subKey1][subKey2]:
3624  self.options[name][1][subKey1][subKey2][subKey3] = value[subKey1][subKey2][subKey3]
3625  else:
3626  self.options[name][1][subKey1][subKey2] = value[subKey1][subKey2]
3627  else:
3628  # no need to set self.options[name][0] since it has the right type
3629  self.options[name][1][subKey1] = value[subKey1]
3630  else:
3631  # It is not dict, just set
3632  # no need to set self.options[name][0] since it has the right type
3633  self.options[name][1] = value
3634  else:
3635  raise Error(
3636  "Datatype for Option %-35s was not valid \n "
3637  "Expected data type is %-47s \n "
3638  "Received data type is %-47s" % (name, self.defaultOptions[name][0], type(value))
3639  )
3640 
3641  def _initOption(self, name, value):
3642  """
3643  Set a value to options. This function will be used only for initializing the options internally.
3644  Do NOT call this function from the run script!
3645 
3646  Parameters
3647  ----------
3648  name : str
3649  Name of option to set. Not case sensitive
3650  value : varies
3651  Value to set. Type is checked for consistency.
3652  """
3653 
3654  try:
3655  self.defaultOptions[name]
3656  except KeyError:
3657  Error("Option '%-30s' is not a valid %s option." % (name, self.name))
3658 
3659  # Make sure we are not trying to change an immutable option if
3660  # we are not allowed to.
3661  if name in self.imOptions:
3662  raise Error("Option '%-35s' cannot be modified after the solver " "is created." % name)
3663 
3664  # Now we know the option exists, lets check if the type is ok:
3665  if isinstance(value, self.defaultOptions[name][0]):
3666  # the type matches, now we need to check if the 'value' is of dict type, if yes, we only
3667  # replace the subKey values of 'value', instead of overiding all the subKey values
3668  if isinstance(value, dict):
3669  for subKey in value:
3670  # no need to set self.options[name][0] since it has the right type
3671  self.options[name][1][subKey] = value[subKey]
3672  else:
3673  # It is not dict, just set
3674  # no need to set self.options[name][0] since it has the right type
3675  self.options[name][1] = value
3676  else:
3677  raise Error(
3678  "Datatype for Option %-35s was not valid \n "
3679  "Expected data type is %-47s \n "
3680  "Received data type is %-47s" % (name, self.defaultOptions[name][0], type(value))
3681  )
3682 
3683  def setFieldValue4GlobalCellI(self, fieldName, val, globalCellI, compI=0):
3684  """
3685  Set the field value based on the global cellI. This is usually
3686  used if the state variables are design variables, e.g., betaSA
3687  The reason to use global cell index, instead of local one, is
3688  because this index is usually provided by the optimizer. Optimizer
3689  uses global cell index as the design variable
3690 
3691  Parameters
3692  ----------
3693  fieldName : str
3694  Name of the flow field to set, e.g., U, p, nuTilda
3695  val : float
3696  The value to set
3697  globalCellI : int
3698  The global cell index to set the value
3699  compI : int
3700  The component index to set the value (for vectorField only)
3701 
3702  """
3703 
3704  self.solver.setFieldValue4GlobalCellI(fieldName, val, globalCellI, compI)
3705  if self.getOption("useAD")["mode"] in ["forward", "reverse"]:
3706  self.solverAD.setFieldValue4GlobalCellI(fieldName, val, globalCellI, compI)
3707 
3708  def setFieldValue4LocalCellI(self, fieldName, val, localCellI, compI=0):
3709  """
3710  Set the field value based on the local cellI.
3711 
3712  Parameters
3713  ----------
3714  fieldName : str
3715  Name of the flow field to set, e.g., U, p, nuTilda
3716  val : float
3717  The value to set
3718  localCellI : int
3719  The global cell index to set the value
3720  compI : int
3721  The component index to set the value (for vectorField only)
3722 
3723  """
3724 
3725  self.solver.setFieldValue4LocalCellI(fieldName, val, localCellI, compI)
3726  if self.getOption("useAD")["mode"] in ["forward", "reverse"]:
3727  self.solverAD.setFieldValue4LocalCellI(fieldName, val, localCellI, compI)
3728 
3729  def updateBoundaryConditions(self, fieldName, fieldType):
3730  """
3731  Update the boundary condition for a field
3732 
3733  Parameters
3734  ----------
3735  fieldName : str
3736  Name of the flow field to update, e.g., U, p, nuTilda
3737  fieldType : str
3738  Type of the flow field: scalar or vector
3739 
3740  """
3741 
3742  self.solver.updateBoundaryConditions(fieldName, fieldType)
3743  if self.getOption("useAD")["mode"] in ["forward", "reverse"]:
3744  self.solverAD.updateBoundaryConditions(fieldName, fieldType)
3745 
3746  def getOption(self, name):
3747  """
3748  Get a value from options
3749 
3750  Parameters
3751  ----------
3752  name : str
3753  Name of option to get. Not case sensitive
3754 
3755  Returns
3756  -------
3757  value : varies
3758  Return the value of the option.
3759  """
3760 
3761  if name in self.defaultOptions:
3762  return self.options[name][1]
3763  else:
3764  raise Error("%s is not a valid option name." % name)
3765 
3766  def updateDAOption(self):
3767  """
3768  Update the allOptions_ in DAOption based on the latest self.options in
3769  pyDAFoam. This will pass the changes of self.options from pyDAFoam
3770  to DASolvers. NOTE: need to call this function after calling
3771  self.initSolver
3772  """
3773 
3774  if self.solverInitialized == 0:
3775  raise Error("self._initSolver not called!")
3776 
3777  self.solver.updateDAOption(self.options)
3778 
3779  if self.getOption("useAD")["mode"] in ["forward", "reverse"]:
3780  self.solverAD.updateDAOption(self.options)
3781 
3782  if len(self.getOption("fvSource")) > 0:
3784 
3786  """
3787  Synchronize the values in DAOption and actuatorDiskDVs_. We need to synchronize the values
3788  defined in fvSource from DAOption to actuatorDiskDVs_ in the C++ layer
3789  NOTE: we need to call this function whenever we change the actuator design variables
3790  during optimization.
3791  """
3792 
3794 
3795  if self.getOption("useAD")["mode"] in ["forward", "reverse"]:
3797 
3799  """
3800  Get number of local adjoint states
3801  """
3802  return self.solver.getNLocalAdjointStates()
3803 
3804  def getDVsCons(self):
3805  """
3806  Return the list of design variable names
3807  NOTE: constraints are not implemented yet
3808  """
3809  DVNames = []
3810  DVSizes = []
3811  if self.DVGeo is None:
3812  return None
3813  else:
3814  DVs = self.DVGeo.getValues()
3815  for dvName in DVs:
3816  try:
3817  size = len(DVs[dvName])
3818  except Exception:
3819  size = 1
3820  DVNames.append(dvName)
3821  DVSizes.append(size)
3822  return DVNames, DVSizes
3823 
3824  def getStates(self):
3825  """
3826  Return the adjoint state array owns by this processor
3827  """
3828  nLocalStateSize = self.solver.getNLocalAdjointStates()
3829  states = np.zeros(nLocalStateSize, self.dtype)
3830  Istart, Iend = self.wVec.getOwnershipRange()
3831  for i in range(Istart, Iend):
3832  iRel = i - Istart
3833  states[iRel] = self.wVec[i]
3834 
3835  return states
3836 
3837  def getResiduals(self):
3838  """
3839  Return the residual array owns by this processor
3840  """
3841  nLocalStateSize = self.solver.getNLocalAdjointStates()
3842  residuals = np.zeros(nLocalStateSize, self.dtype)
3843  resVec = self.wVec.duplicate()
3844  resVec.zeroEntries()
3845 
3846  self.solver.calcResidualVec(resVec)
3847 
3848  Istart, Iend = resVec.getOwnershipRange()
3849  for i in range(Istart, Iend):
3850  iRel = i - Istart
3851  residuals[iRel] = resVec[i]
3852 
3853  return residuals
3854 
3855  def setStates(self, states):
3856  """
3857  Set the state to the OpenFOAM field
3858  """
3859  Istart, Iend = self.wVec.getOwnershipRange()
3860  for i in range(Istart, Iend):
3861  iRel = i - Istart
3862  self.wVec[i] = states[iRel]
3863 
3864  self.wVec.assemblyBegin()
3865  self.wVec.assemblyEnd()
3866 
3867  self.solver.updateOFField(self.wVec)
3868 
3869  if self.getOption("useAD")["mode"] in ["forward", "reverse"]:
3870  self.solverAD.updateOFField(self.wVec)
3871 
3872  return
3873 
3874  def convertMPIVec2SeqArray(self, mpiVec):
3875  """
3876  Convert a MPI vector to a seq array
3877  """
3878  vecSize = mpiVec.getSize()
3879  seqVec = PETSc.Vec().createSeq(vecSize, bsize=1, comm=PETSc.COMM_SELF)
3880  self.solver.convertMPIVec2SeqVec(mpiVec, seqVec)
3881 
3882  array1 = np.zeros(vecSize, self.dtype)
3883  for i in range(vecSize):
3884  array1[i] = seqVec[i]
3885 
3886  return array1
3887 
3888  def vec2Array(self, vec):
3889  """
3890  Convert a Petsc vector to numpy array
3891  """
3892 
3893  Istart, Iend = vec.getOwnershipRange()
3894  size = Iend - Istart
3895  array1 = np.zeros(size, self.dtype)
3896  for i in range(Istart, Iend):
3897  iRel = i - Istart
3898  array1[iRel] = vec[i]
3899  return array1
3900 
3901  def array2Vec(self, array1):
3902  """
3903  Convert a numpy array to Petsc vector
3904  """
3905  size = len(array1)
3906 
3907  vec = PETSc.Vec().create(PETSc.COMM_WORLD)
3908  vec.setSizes((size, PETSc.DECIDE), bsize=1)
3909  vec.setFromOptions()
3910  vec.zeroEntries()
3911 
3912  Istart, Iend = vec.getOwnershipRange()
3913  for i in range(Istart, Iend):
3914  iRel = i - Istart
3915  vec[i] = array1[iRel]
3916 
3917  vec.assemblyBegin()
3918  vec.assemblyEnd()
3919 
3920  return vec
3921 
3922  def array2VecSeq(self, array1):
3923  """
3924  Convert a numpy array to Petsc vector in serial mode
3925  """
3926  size = len(array1)
3927 
3928  vec = PETSc.Vec().createSeq(size, bsize=1, comm=PETSc.COMM_SELF)
3929  vec.zeroEntries()
3930 
3931  for i in range(size):
3932  vec[i] = array1[i]
3933 
3934  vec.assemblyBegin()
3935  vec.assemblyEnd()
3936 
3937  return vec
3938 
3939  def vec2ArraySeq(self, vec):
3940  """
3941  Convert a Petsc vector to numpy array in serial mode
3942  """
3943 
3944  size = vec.getSize()
3945  array1 = np.zeros(size, self.dtype)
3946  for i in range(size):
3947  array1[i] = vec[i]
3948  return array1
3949 
3950  def _printCurrentOptions(self):
3951  """
3952  Prints a nicely formatted dictionary of all the current solver
3953  options to the stdout on the root processor
3954  """
3955 
3956  Info("+---------------------------------------+")
3957  Info("| All DAFoam Options: |")
3958  Info("+---------------------------------------+")
3959  # Need to assemble a temporary dictionary
3960  tmpDict = {}
3961  for key in self.options:
3962  tmpDict[key] = self.getOption(key)
3963  Info(tmpDict)
3964 
3965  def _getImmutableOptions(self):
3966  """
3967  We define the list of options that *cannot* be changed after the
3968  object is created. pyDAFoam will raise an error if a user tries to
3969  change these. The strings for these options are placed in a set
3970  """
3971 
3972  return ()
3973 
3974  def _writeDecomposeParDict(self):
3975  """
3976  Write system/decomposeParDict
3977  """
3978  if self.comm.rank == 0:
3979  # Open the options file for writing
3980 
3981  workingDirectory = os.getcwd()
3982  sysDir = "system"
3983  varDir = os.path.join(workingDirectory, sysDir)
3984  fileName = "decomposeParDict"
3985  fileLoc = os.path.join(varDir, fileName)
3986  f = open(fileLoc, "w")
3987  # write header
3988  self._writeOpenFoamHeader(f, "dictionary", sysDir, fileName)
3989  # write content
3990  decomDict = self.getOption("decomposeParDict")
3991  n = decomDict["simpleCoeffs"]["n"]
3992  f.write("numberOfSubdomains %d;\n" % self.nProcs)
3993  f.write("\n")
3994  f.write("method %s;\n" % decomDict["method"])
3995  f.write("\n")
3996  f.write("simpleCoeffs \n")
3997  f.write("{ \n")
3998  f.write(" n (%d %d %d);\n" % (n[0], n[1], n[2]))
3999  f.write(" delta %g;\n" % decomDict["simpleCoeffs"]["delta"])
4000  f.write("} \n")
4001  f.write("\n")
4002  f.write("distributed false;\n")
4003  f.write("\n")
4004  f.write("roots();\n")
4005  if len(decomDict["preservePatches"]) == 1 and decomDict["preservePatches"][0] == "None":
4006  pass
4007  else:
4008  f.write("\n")
4009  f.write("preservePatches (")
4010  for pPatch in decomDict["preservePatches"]:
4011  f.write("%s " % pPatch)
4012  f.write(");\n")
4013  if decomDict["singleProcessorFaceSets"][0] != "None":
4014  f.write("singleProcessorFaceSets (")
4015  for pPatch in decomDict["singleProcessorFaceSets"]:
4016  f.write(" (%s -1) " % pPatch)
4017  f.write(");\n")
4018  f.write("\n")
4019  f.write("// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //\n")
4020 
4021  f.close()
4022  self.comm.Barrier()
4023 
4024  def _writeOpenFoamHeader(self, f, className, location, objectName):
4025  """
4026  Write OpenFOAM header file
4027  """
4028 
4029  f.write("/*--------------------------------*- C++ -*---------------------------------*\ \n")
4030  f.write("| ======== | | \n")
4031  f.write("| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | \n")
4032  f.write("| \\ / O peration | Version: v1812 | \n")
4033  f.write("| \\ / A nd | Web: www.OpenFOAM.com | \n")
4034  f.write("| \\/ M anipulation | | \n")
4035  f.write("\*--------------------------------------------------------------------------*/ \n")
4036  f.write("FoamFile\n")
4037  f.write("{\n")
4038  f.write(" version 2.0;\n")
4039  f.write(" format ascii;\n")
4040  f.write(" class %s;\n" % className)
4041  f.write(' location "%s";\n' % location)
4042  f.write(" object %s;\n" % objectName)
4043  f.write("}\n")
4044  f.write("// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //\n")
4045  f.write("\n")
4046 
4047 
4048 class Error(Exception):
4049  """
4050  Format the error message in a box to make it clear this
4051  was a expliclty raised exception.
4052  """
4053 
4054  def __init__(self, message):
4055  msg = "\n+" + "-" * 78 + "+" + "\n" + "| pyDAFoam Error: "
4056  i = 19
4057  for word in message.split():
4058  if len(word) + i + 1 > 78: # Finish line and start new one
4059  msg += " " * (78 - i) + "|\n| " + word + " "
4060  i = 1 + len(word) + 1
4061  else:
4062  msg += word + " "
4063  i += len(word) + 1
4064  msg += " " * (78 - i) + "|\n" + "+" + "-" * 78 + "+" + "\n"
4065  print(msg, flush=True)
4066  Exception.__init__(self)
4067 
4068  return
4069 
4070 
4071 class Info(object):
4072  """
4073  Print information and flush to screen for parallel cases
4074  """
4075 
4076  def __init__(self, message):
4077  if MPI.COMM_WORLD.rank == 0:
4078  print(message, flush=True)
4079  MPI.COMM_WORLD.Barrier()
4080 
4081 
4083  """
4084  TensorFlow helper class.
4085  NOTE: this is a static class because the callback function
4086  does not accept non-static class members (seg fault)
4087  """
4088 
4089  options = {}
4090 
4091  model = None
4092 
4093  @staticmethod
4094  def initialize():
4095  """
4096  Initialize parameters and load models
4097  """
4098  Info("Initializing the TensorFlowHelper")
4099  TensorFlowHelper.modelName = TensorFlowHelper.options["modelName"]
4100  TensorFlowHelper.nInputs = TensorFlowHelper.options["nInputs"]
4101  TensorFlowHelper.nOutputs = TensorFlowHelper.options["nOutputs"]
4102  TensorFlowHelper.batchSize = TensorFlowHelper.options["batchSize"]
4103  TensorFlowHelper.model = tf.keras.models.load_model(TensorFlowHelper.modelName)
4104 
4105  if TensorFlowHelper.nOutputs != 1:
4106  raise Error("current version supports nOutputs=1 only!")
4107 
4108  @staticmethod
4109  def predict(inputs, n, outputs, m):
4110  """
4111  Calculate the outputs based on the inputs using the saved model
4112  """
4113 
4114  inputs_tf = np.reshape(inputs, (-1, TensorFlowHelper.nInputs))
4115  outputs_tf = TensorFlowHelper.model.predict(inputs_tf, verbose=False, batch_size=TensorFlowHelper.batchSize)
4116 
4117  for i in range(m):
4118  outputs[i] = outputs_tf[i, 0]
4119 
4120  @staticmethod
4121  def calcJacVecProd(inputs, inputs_b, n, outputs, outputs_b, m):
4122  """
4123  Calculate the gradients of the outputs wrt the inputs
4124  """
4125 
4126  inputs_tf = np.reshape(inputs, (-1, TensorFlowHelper.nInputs))
4127  inputs_tf_var = tf.Variable(inputs_tf, dtype=tf.float32)
4128 
4129  with tf.GradientTape() as tape:
4130  outputs_tf = TensorFlowHelper.model(inputs_tf_var)
4131 
4132  gradients_tf = tape.gradient(outputs_tf, inputs_tf_var)
4133 
4134  for i in range(gradients_tf.shape[0]):
4135  for j in range(gradients_tf.shape[1]):
4136  idx = i * gradients_tf.shape[1] + j
4137  inputs_b[idx] = gradients_tf.numpy()[i, j] * outputs_b[i]
dafoam.pyDAFoam.DAOPTION.primalBC
primalBC
The boundary condition for primal solution.
Definition: pyDAFoam.py:88
dafoam.pyDAFoam.PYDAFOAM.getAcousticData
def getAcousticData(self, groupName=None)
Definition: pyDAFoam.py:2587
dafoam.pyDAFoam.Info.__init__
def __init__(self, message)
Definition: pyDAFoam.py:4076
dafoam.pyDAFoam.PYDAFOAM.updateBoundaryConditions
def updateBoundaryConditions(self, fieldName, fieldType)
Definition: pyDAFoam.py:3729
dafoam.pyDAFoam.DAOPTION.solverName
solverName
The name of the DASolver to use for primal and adjoint computation.
Definition: pyDAFoam.py:71
dafoam.pyDAFoam.PYDAFOAM.vec2Array
def vec2Array(self, vec)
Definition: pyDAFoam.py:3888
dafoam.pyDAFoam.DAOPTION.discipline
discipline
The discipline name.
Definition: pyDAFoam.py:435
dafoam.pyDAFoam.PYDAFOAM._initializeOptions
def _initializeOptions(self, options)
Definition: pyDAFoam.py:1659
dafoam.pyDAFoam.PYDAFOAM._getImmutableOptions
def _getImmutableOptions(self)
Definition: pyDAFoam.py:3965
dafoam.pyDAFoam.PYDAFOAM.writeTotalDeriv
def writeTotalDeriv(self, fileName, sens, evalFuncs)
Definition: pyDAFoam.py:1278
dafoam.pyDAFoam.DAOPTION.adjPartDerivFDStep
adjPartDerivFDStep
The step size for finite-difference computation of partial derivatives.
Definition: pyDAFoam.py:445
dafoam.pyDAFoam.PYDAFOAM.setPrimalBoundaryConditions
def setPrimalBoundaryConditions(self, printInfo=1, printInfoAD=0)
Definition: pyDAFoam.py:3228
dafoam.pyDAFoam.PYDAFOAM.solver
solver
Definition: pyDAFoam.py:2774
dafoam.pyDAFoam.DAOPTION.unsteadyAdjoint
unsteadyAdjoint
Options for unsteady adjoint.
Definition: pyDAFoam.py:462
dafoam.pyDAFoam.PYDAFOAM.getSurfaceCoordinates
def getSurfaceCoordinates(self, groupName=None)
Definition: pyDAFoam.py:3164
dafoam.pyDAFoam.DAOPTION.runStatus
runStatus
The run status which can be solvePrimal, solveAdjoint, or calcTotalDeriv.
Definition: pyDAFoam.py:495
dafoam.pyDAFoam.PYDAFOAM.getSolverMeshIndices
def getSolverMeshIndices(self)
Definition: pyDAFoam.py:3318
dafoam.pyDAFoam.Info
Definition: pyDAFoam.py:4071
dafoam.pyDAFoam.PYDAFOAM.writeSurfaceSensitivityMap
def writeSurfaceSensitivityMap(self, objFuncName, designVarName, solutionTime)
Definition: pyDAFoam.py:1801
dafoam.pyDAFoam.PYDAFOAM.calcFFD2XvSeedVec
def calcFFD2XvSeedVec(self)
Definition: pyDAFoam.py:2980
dafoam.pyDAFoam.PYDAFOAM.dR0dW0TPsi
dR0dW0TPsi
Definition: pyDAFoam.py:1008
dafoam.pyDAFoam.PYDAFOAM
Definition: pyDAFoam.py:673
dafoam.pyDAFoam.PYDAFOAM._readOFGrid
def _readOFGrid(self, caseDir)
Definition: pyDAFoam.py:3506
dafoam.pyDAFoam.DAOPTION
Definition: pyDAFoam.py:34
dafoam.pyDAFoam.PYDAFOAM.families
families
Definition: pyDAFoam.py:717
dafoam.pyDAFoam.PYDAFOAM.saveMultiPointField
def saveMultiPointField(self, indexMP)
Definition: pyDAFoam.py:1134
dafoam.pyDAFoam.PYDAFOAM.evalFunctions
def evalFunctions(self, funcs, evalFuncs=None, ignoreMissing=False)
Definition: pyDAFoam.py:1340
dafoam.pyDAFoam.PYDAFOAM.setTimeInstanceField
def setTimeInstanceField(self, instanceI)
Definition: pyDAFoam.py:1168
dafoam.pyDAFoam.DAOPTION.runLowOrderPrimal4PC
runLowOrderPrimal4PC
whether to run the primal using the first order div scheme.
Definition: pyDAFoam.py:645
dafoam.pyDAFoam.PYDAFOAM.__init__
def __init__(self, comm=None, options=None)
Definition: pyDAFoam.py:689
dafoam.pyDAFoam.DAOPTION.meshSurfaceFamily
meshSurfaceFamily
Default name for the mesh surface family.
Definition: pyDAFoam.py:606
dafoam.pyDAFoam.DAOPTION.writeMinorIterations
writeMinorIterations
Whether to write the primal solutions for minor iterations (i.e., line search).
Definition: pyDAFoam.py:639
dafoam.pyDAFoam.PYDAFOAM.solverAD
solverAD
Definition: pyDAFoam.py:2780
dafoam.pyDAFoam.PYDAFOAM.parallel
parallel
Definition: pyDAFoam.py:1710
dafoam.pyDAFoam.PYDAFOAM.wVec
wVec
Definition: pyDAFoam.py:756
dafoam.pyDAFoam.DAOPTION.aeroPropulsive
aeroPropulsive
Aero-propulsive options.
Definition: pyDAFoam.py:338
dafoam.pyDAFoam.DAOPTION.primalMinResTolDiff
primalMinResTolDiff
Users can adjust primalMinResTolDiff to tweak how much difference between primalMinResTol and the act...
Definition: pyDAFoam.py:521
dafoam.pyDAFoam.PYDAFOAM.getSurfaceConnectivity
def getSurfaceConnectivity(self, groupName=None)
Definition: pyDAFoam.py:1529
dafoam.pyDAFoam.PYDAFOAM.allSurfacesGroup
allSurfacesGroup
Definition: pyDAFoam.py:772
dafoam.pyDAFoam.PYDAFOAM._updateGeomInfo
_updateGeomInfo
Definition: pyDAFoam.py:720
dafoam.pyDAFoam.DAOPTION.transonicPCOption
transonicPCOption
Which options to use to improve the adjoint equation convergence of transonic conditions This is used...
Definition: pyDAFoam.py:457
dafoam.pyDAFoam.PYDAFOAM.dRdW00TPsi
dRdW00TPsi
Definition: pyDAFoam.py:1007
dafoam.pyDAFoam.PYDAFOAM._printCurrentOptions
def _printCurrentOptions(self)
Definition: pyDAFoam.py:3950
dafoam.pyDAFoam.PYDAFOAM.timeVec
timeVec
Definition: pyDAFoam.py:1206
dafoam.pyDAFoam.PYDAFOAM.dR00dW00TPsi
dR00dW00TPsi
Definition: pyDAFoam.py:1011
dafoam.pyDAFoam.PYDAFOAM.getOption
def getOption(self, name)
Definition: pyDAFoam.py:3746
dafoam.pyDAFoam.TensorFlowHelper.calcJacVecProd
def calcJacVecProd(inputs, inputs_b, n, outputs, outputs_b, m)
Definition: pyDAFoam.py:4121
dafoam.pyDAFoam.PYDAFOAM._writeOpenFoamHeader
def _writeOpenFoamHeader(self, f, className, location, objectName)
Definition: pyDAFoam.py:4024
dafoam.pyDAFoam.PYDAFOAM.ptSetName
ptSetName
Definition: pyDAFoam.py:729
dafoam.pyDAFoam.DAOPTION.normalizeResiduals
normalizeResiduals
Normalization for residuals.
Definition: pyDAFoam.py:552
dafoam.pyDAFoam.PYDAFOAM.mapVector
def mapVector(self, vec1, groupName1, groupName2, vec2=None)
Definition: pyDAFoam.py:3336
dafoam.pyDAFoam.DAOPTION.adjEqnSolMethod
adjEqnSolMethod
The adjoint equation solution method.
Definition: pyDAFoam.py:395
dafoam.pyDAFoam.PYDAFOAM.neighbours
neighbours
Definition: pyDAFoam.py:3130
dafoam.pyDAFoam.DAOPTION.primalOnly
primalOnly
An option to run the primal only; no adjoint or optimization will be run.
Definition: pyDAFoam.py:341
dafoam.pyDAFoam.DAOPTION.maxCorrectBCCalls
maxCorrectBCCalls
The max number of correctBoundaryConditions calls in the updateOFField function.
Definition: pyDAFoam.py:632
dafoam.pyDAFoam.DAOPTION.debug
debug
Whether running the optimization in the debug mode, which prints extra information.
Definition: pyDAFoam.py:504
dafoam.pyDAFoam.PYDAFOAM.designSurfacesGroup
designSurfacesGroup
Definition: pyDAFoam.py:779
dafoam.pyDAFoam.DAOPTION.primalMinIters
primalMinIters
number of minimal primal iterations.
Definition: pyDAFoam.py:661
dafoam.pyDAFoam.DAOPTION.primalVarBounds
primalVarBounds
The variable upper and lower bounds for primal solution.
Definition: pyDAFoam.py:402
dafoam.pyDAFoam.DAOPTION.decomposeParDict
decomposeParDict
decomposeParDict option.
Definition: pyDAFoam.py:594
dafoam.pyDAFoam.PYDAFOAM.adjointFail
adjointFail
Definition: pyDAFoam.py:751
dafoam.pyDAFoam.PYDAFOAM.__call__
def __call__(self)
Definition: pyDAFoam.py:868
dafoam.pyDAFoam.DAOPTION.adjEqnOption
adjEqnOption
The Petsc options for solving the adjoint linear equation.
Definition: pyDAFoam.py:530
dafoam.pyDAFoam.PYDAFOAM.convertMPIVec2SeqArray
def convertMPIVec2SeqArray(self, mpiVec)
Definition: pyDAFoam.py:3874
dafoam.pyDAFoam.PYDAFOAM.deletePrevPrimalSolTime
def deletePrevPrimalSolTime(self)
Definition: pyDAFoam.py:2900
dafoam.pyDAFoam.PYDAFOAM.stateBCMat
stateBCMat
Definition: pyDAFoam.py:1200
dafoam.pyDAFoam.DAOPTION.printIntervalUnsteady
printIntervalUnsteady
The print interval of unsteady primal solvers, e.g., for DAPisoFoam.
Definition: pyDAFoam.py:517
dafoam.pyDAFoam.PYDAFOAM.options
options
Definition: pyDAFoam.py:1683
dafoam.pyDAFoam.PYDAFOAM._initializeTimeAccurateAdjointVectors
def _initializeTimeAccurateAdjointVectors(self)
Definition: pyDAFoam.py:996
dafoam.pyDAFoam.PYDAFOAM._getSurfaceSize
def _getSurfaceSize(self, groupName)
Definition: pyDAFoam.py:3200
dafoam.pyDAFoam.PYDAFOAM.setMesh
def setMesh(self, mesh)
Definition: pyDAFoam.py:1499
dafoam.pyDAFoam.DAOPTION.writeSensMap
writeSensMap
The sensitivity map will be saved to disk during optimization for the given design variable names in ...
Definition: pyDAFoam.py:626
dafoam.pyDAFoam.DAOPTION.designVar
designVar
Design variable information.
Definition: pyDAFoam.py:301
dafoam.pyDAFoam.PYDAFOAM.solverRegistry
solverRegistry
Definition: pyDAFoam.py:862
dafoam.pyDAFoam.PYDAFOAM.version
version
Definition: pyDAFoam.py:696
dafoam.pyDAFoam.Error.__init__
def __init__(self, message)
Definition: pyDAFoam.py:4054
dafoam.pyDAFoam.PYDAFOAM.runColoring
def runColoring(self)
Definition: pyDAFoam.py:2842
dafoam.pyDAFoam.PYDAFOAM.writeDeformedFFDs
def writeDeformedFFDs(self, counter=None)
Definition: pyDAFoam.py:1262
dafoam.pyDAFoam.DAOPTION.normalizeStates
normalizeStates
State normalization for dRdWT computation.
Definition: pyDAFoam.py:95
dafoam.pyDAFoam.PYDAFOAM._initializeAdjTotalDeriv
def _initializeAdjTotalDeriv(self)
Definition: pyDAFoam.py:972
dafoam.pyDAFoam.DAOPTION.tensorflow
tensorflow
tensorflow related functions
Definition: pyDAFoam.py:664
dafoam.pyDAFoam.PYDAFOAM.setDVGeo
def setDVGeo(self, DVGeo)
Definition: pyDAFoam.py:1442
dafoam.pyDAFoam.PYDAFOAM.getNLocalAdjointStates
def getNLocalAdjointStates(self)
Definition: pyDAFoam.py:3798
dafoam.pyDAFoam.PYDAFOAM.imOptions
imOptions
Definition: pyDAFoam.py:1675
dafoam.pyDAFoam.PYDAFOAM.wVecMPList
wVecMPList
Definition: pyDAFoam.py:3460
dafoam.pyDAFoam.DAOPTION.nMultiPoints
nMultiPoints
If multiPoint = True, how many primal configurations for the multipoint optimization.
Definition: pyDAFoam.py:441
dafoam.pyDAFoam.DAOPTION.adjPCLag
adjPCLag
The interval of recomputing the pre-conditioner matrix dRdWTPC for solveAdjoint By default,...
Definition: pyDAFoam.py:474
dafoam.pyDAFoam.DAOPTION.objFuncAvgStart
objFuncAvgStart
At which iteration should we start the averaging of objective functions.
Definition: pyDAFoam.py:466
dafoam.pyDAFoam.PYDAFOAM.getStates
def getStates(self)
Definition: pyDAFoam.py:3824
dafoam.pyDAFoam.PYDAFOAM.runDecomposePar
def runDecomposePar(self)
Definition: pyDAFoam.py:2879
dafoam.pyDAFoam.PYDAFOAM.ksp
ksp
Definition: pyDAFoam.py:830
dafoam.pyDAFoam.DAOPTION.primalMinResTol
primalMinResTol
The convergence tolerance for the primal solver.
Definition: pyDAFoam.py:75
dafoam.pyDAFoam.PYDAFOAM.xs0
xs0
Definition: pyDAFoam.py:807
dafoam.pyDAFoam.DAOPTION.printPYDAFOAMOptions
printPYDAFOAMOptions
Whether to print all options defined in pyDAFoam to screen before optimization.
Definition: pyDAFoam.py:498
dafoam.pyDAFoam.PYDAFOAM._calcObjFuncNames4Adj
def _calcObjFuncNames4Adj(self)
Definition: pyDAFoam.py:1052
dafoam.pyDAFoam.PYDAFOAM.dR0dW00TPsi
dR0dW00TPsi
Definition: pyDAFoam.py:1009
dafoam.pyDAFoam.PYDAFOAM._initializeMeshPointVec
def _initializeMeshPointVec(self)
Definition: pyDAFoam.py:3425
dafoam.pyDAFoam.PYDAFOAM.dRdWTPC
dRdWTPC
Definition: pyDAFoam.py:827
dafoam.pyDAFoam.PYDAFOAM.name
name
Definition: pyDAFoam.py:705
dafoam.pyDAFoam.PYDAFOAM._solverRegistry
def _solverRegistry(self)
Definition: pyDAFoam.py:856
dafoam.pyDAFoam.Error
Definition: pyDAFoam.py:4048
dafoam.pyDAFoam.PYDAFOAM.pointsSet
pointsSet
Definition: pyDAFoam.py:884
dafoam.pyDAFoam.PYDAFOAM.renameSolution
def renameSolution(self, solIndex)
Definition: pyDAFoam.py:2925
dafoam.pyDAFoam.PYDAFOAM.setSurfaceCoordinates
def setSurfaceCoordinates(self, coordinates, groupName=None)
Definition: pyDAFoam.py:3135
dafoam.pyDAFoam.PYDAFOAM.nSolvePrimals
nSolvePrimals
Definition: pyDAFoam.py:746
dafoam.pyDAFoam.DAOPTION.maxTractionBCIters
maxTractionBCIters
The maximal iterations of tractionDisplacement boundary conditions.
Definition: pyDAFoam.py:589
dafoam.pyDAFoam.PYDAFOAM._readMeshInfo
def _readMeshInfo(self)
Definition: pyDAFoam.py:3123
dafoam.pyDAFoam.PYDAFOAM.DVGeo
DVGeo
Definition: pyDAFoam.py:812
dafoam.pyDAFoam.PYDAFOAM.solveAdjoint
def solveAdjoint(self)
Definition: pyDAFoam.py:2004
dafoam.pyDAFoam.PYDAFOAM.setdXvdFFDMat
def setdXvdFFDMat(self, designVarName, deltaVPointThreshold=1.0e-16)
Definition: pyDAFoam.py:3029
dafoam.pyDAFoam.PYDAFOAM.getDVsCons
def getDVsCons(self)
Definition: pyDAFoam.py:3804
dafoam.pyDAFoam.PYDAFOAM.setFieldValue4GlobalCellI
def setFieldValue4GlobalCellI(self, fieldName, val, globalCellI, compI=0)
Definition: pyDAFoam.py:3683
dafoam.pyDAFoam.DAOPTION.printDAOptions
printDAOptions
Whether to print all DAOption defined in the C++ layer to screen before optimization.
Definition: pyDAFoam.py:501
dafoam.pyDAFoam.PYDAFOAM.writePetscVecMat
def writePetscVecMat(self, name, vecMat, mode="Binary")
Definition: pyDAFoam.py:1946
dafoam.pyDAFoam.TensorFlowHelper.predict
def predict(inputs, n, outputs, m)
Definition: pyDAFoam.py:4109
dafoam.pyDAFoam.DAOPTION.adjUseColoring
adjUseColoring
Whether to use graph coloring to accelerate partial derivative computation.
Definition: pyDAFoam.py:525
dafoam.pyDAFoam.PYDAFOAM.xv
xv
Definition: pyDAFoam.py:3131
dafoam.pyDAFoam.PYDAFOAM.solvePrimal
def solvePrimal(self)
Definition: pyDAFoam.py:1971
dafoam.pyDAFoam.PYDAFOAM._initOption
def _initOption(self, name, value)
Definition: pyDAFoam.py:3641
dafoam.pyDAFoam.PYDAFOAM.setTimeInstanceVar
def setTimeInstanceVar(self, mode)
Definition: pyDAFoam.py:1209
dafoam.pyDAFoam.PYDAFOAM.getPointSetName
def getPointSetName(self)
Definition: pyDAFoam.py:3312
dafoam.pyDAFoam.PYDAFOAM.basicFamilies
basicFamilies
Definition: pyDAFoam.py:3243
dafoam.pyDAFoam.PYDAFOAM.updateVolumePoints
def updateVolumePoints(self)
Definition: pyDAFoam.py:3101
dafoam.pyDAFoam.DAOPTION.designSurfaces
designSurfaces
List of patch names for the design surface.
Definition: pyDAFoam.py:305
dafoam.pyDAFoam.PYDAFOAM.adjTotalDeriv
adjTotalDeriv
Definition: pyDAFoam.py:824
dafoam.pyDAFoam.PYDAFOAM.xvFlatten2XvVec
def xvFlatten2XvVec(self, xv, xvVec)
Definition: pyDAFoam.py:3485
dafoam.pyDAFoam.PYDAFOAM.setFieldValue4LocalCellI
def setFieldValue4LocalCellI(self, fieldName, val, localCellI, compI=0)
Definition: pyDAFoam.py:3708
dafoam.pyDAFoam.PYDAFOAM.syncDAOptionToActuatorDVs
def syncDAOptionToActuatorDVs(self)
Definition: pyDAFoam.py:3785
dafoam.pyDAFoam.DAOPTION.objFunc
objFunc
Information on objective function.
Definition: pyDAFoam.py:278
dafoam.pyDAFoam.PYDAFOAM.timeIdxVec
timeIdxVec
Definition: pyDAFoam.py:1207
dafoam.pyDAFoam.PYDAFOAM.dR00dW0TPsi
dR00dW0TPsi
Definition: pyDAFoam.py:1010
dafoam.pyDAFoam.PYDAFOAM.initTimeInstanceMats
def initTimeInstanceMats(self)
Definition: pyDAFoam.py:1185
dafoam.pyDAFoam.PYDAFOAM.calcPrimalResidualStatistics
def calcPrimalResidualStatistics(self, mode)
Definition: pyDAFoam.py:1162
dafoam.pyDAFoam.PYDAFOAM.comm
comm
Definition: pyDAFoam.py:1706
dafoam.pyDAFoam.PYDAFOAM.mapdXvTodFFD
def mapdXvTodFFD(self, totalDerivXv)
Definition: pyDAFoam.py:2489
dafoam.pyDAFoam.PYDAFOAM.calcdFdAOAAnalytical
def calcdFdAOAAnalytical(self, objFuncName, dFdAOA)
Definition: pyDAFoam.py:2710
dafoam.pyDAFoam.DAOPTION.maxResConLv4JacPCMat
maxResConLv4JacPCMat
The maximal connectivity level for the dRdWTPC matrix.
Definition: pyDAFoam.py:568
dafoam.pyDAFoam.PYDAFOAM.stateMat
stateMat
Definition: pyDAFoam.py:1194
dafoam.pyDAFoam.DAOPTION.multiPoint
multiPoint
Whether to perform multipoint optimization.
Definition: pyDAFoam.py:438
dafoam.pyDAFoam.DAOPTION.fvSource
fvSource
Information for the finite volume source term, which will be added in the momentum equation We suppor...
Definition: pyDAFoam.py:392
dafoam.pyDAFoam.PYDAFOAM.adjVectors
adjVectors
Definition: pyDAFoam.py:839
dafoam.pyDAFoam.PYDAFOAM.setMultiPointField
def setMultiPointField(self, indexMP)
Definition: pyDAFoam.py:1148
dafoam.pyDAFoam.PYDAFOAM.defaultOptions
defaultOptions
Definition: pyDAFoam.py:1678
dafoam.pyDAFoam.DAOPTION.checkMeshThreshold
checkMeshThreshold
The threshold for check mesh call.
Definition: pyDAFoam.py:609
dafoam.pyDAFoam.DAOPTION.useAD
useAD
Whether to use AD: Mode options: forward, reverse, or fd.
Definition: pyDAFoam.py:483
dafoam.pyDAFoam.PYDAFOAM.wallList
wallList
Definition: pyDAFoam.py:3246
dafoam.pyDAFoam.DAOPTION.writeDeformedFFDs
writeDeformedFFDs
Whether to write deformed FFDs to the disk during optimization.
Definition: pyDAFoam.py:629
dafoam.pyDAFoam.PYDAFOAM.getTriangulatedMeshSurface
def getTriangulatedMeshSurface(self, groupName=None, **kwargs)
Definition: pyDAFoam.py:1578
dafoam.pyDAFoam.PYDAFOAM._computeBasicFamilyInfo
def _computeBasicFamilyInfo(self)
Definition: pyDAFoam.py:3236
dafoam.pyDAFoam.PYDAFOAM.couplingSurfacesGroup
couplingSurfacesGroup
Definition: pyDAFoam.py:791
dafoam.pyDAFoam.PYDAFOAM._initSolver
def _initSolver(self)
Definition: pyDAFoam.py:2760
dafoam.pyDAFoam.PYDAFOAM.writeDesignVariable
def writeDesignVariable(self, fileName, xDV)
Definition: pyDAFoam.py:1225
dafoam.pyDAFoam.PYDAFOAM.array2VecSeq
def array2VecSeq(self, array1)
Definition: pyDAFoam.py:3922
dafoam.pyDAFoam.PYDAFOAM.setOption
def setOption(self, name, value)
Definition: pyDAFoam.py:3540
dafoam.pyDAFoam.PYDAFOAM._initializeStateVec
def _initializeStateVec(self)
Definition: pyDAFoam.py:3442
dafoam.pyDAFoam.PYDAFOAM._writeOFCaseFiles
def _writeOFCaseFiles(self)
Definition: pyDAFoam.py:1725
dafoam.pyDAFoam.PYDAFOAM.getResiduals
def getResiduals(self)
Definition: pyDAFoam.py:3837
dafoam.pyDAFoam.PYDAFOAM.xv2XvVec
def xv2XvVec(self, xv, xvVec)
Definition: pyDAFoam.py:3468
dafoam.pyDAFoam.DAOPTION.couplingInfo
couplingInfo
MDO coupling information for aerostructural, aerothermal, or aeroacoustic optimization.
Definition: pyDAFoam.py:312
dafoam.pyDAFoam.PYDAFOAM.evalFunctionsSens
def evalFunctionsSens(self, funcsSens, evalFuncs=None)
Definition: pyDAFoam.py:1402
dafoam.pyDAFoam.DAOPTION.adjStateOrdering
adjStateOrdering
The ordering of state variable.
Definition: pyDAFoam.py:603
dafoam.pyDAFoam.PYDAFOAM.getForwardADDerivVal
def getForwardADDerivVal(self, objFuncName)
Definition: pyDAFoam.py:1334
dafoam.pyDAFoam.PYDAFOAM.zeroTimeAccurateAdjointVectors
def zeroTimeAccurateAdjointVectors(self)
Definition: pyDAFoam.py:1040
dafoam.pyDAFoam.PYDAFOAM.getTimeInstanceObjFunc
def getTimeInstanceObjFunc(self, instanceI, objFuncName)
Definition: pyDAFoam.py:1327
dafoam.pyDAFoam.PYDAFOAM.parallelFlag
parallelFlag
Definition: pyDAFoam.py:1719
dafoam.pyDAFoam.DAOPTION.__init__
def __init__(self)
Definition: pyDAFoam.py:54
dafoam.pyDAFoam.PYDAFOAM.dtype
dtype
Definition: pyDAFoam.py:723
dafoam.pyDAFoam.PYDAFOAM._getDefOptions
def _getDefOptions(self)
Definition: pyDAFoam.py:923
dafoam.pyDAFoam.PYDAFOAM.calcTotalDeriv
def calcTotalDeriv(self, dRdX, dFdX, psi, totalDeriv)
Definition: pyDAFoam.py:2693
dafoam.pyDAFoam.PYDAFOAM.nSolveAdjoints
nSolveAdjoints
Definition: pyDAFoam.py:747
dafoam.pyDAFoam.PYDAFOAM.setStates
def setStates(self, states)
Definition: pyDAFoam.py:3855
dafoam.pyDAFoam.PYDAFOAM._checkOptions
def _checkOptions(self)
Definition: pyDAFoam.py:1078
dafoam.pyDAFoam.PYDAFOAM._writeDecomposeParDict
def _writeDecomposeParDict(self)
Definition: pyDAFoam.py:3974
dafoam.pyDAFoam.TensorFlowHelper.initialize
def initialize()
Definition: pyDAFoam.py:4094
dafoam.pyDAFoam.PYDAFOAM.allWallsGroup
allWallsGroup
Definition: pyDAFoam.py:775
dafoam.pyDAFoam.DAOPTION.wingProp
wingProp
Parameters for wing-propeller coupling optimizations.
Definition: pyDAFoam.py:648
dafoam.pyDAFoam.PYDAFOAM.xvVec
xvVec
Definition: pyDAFoam.py:755
dafoam.pyDAFoam.PYDAFOAM.objFuncValuePrevIter
objFuncValuePrevIter
Definition: pyDAFoam.py:817
dafoam.pyDAFoam.DAOPTION.rigidBodyMotion
rigidBodyMotion
Rigid body motion for dynamic mesh This option will be used in DAPimpleDyMFoam to simulate dynamicMes...
Definition: pyDAFoam.py:487
dafoam.pyDAFoam.DAOPTION.writeJacobians
writeJacobians
Whether to write Jacobian matrices to file for debugging Example: writeJacobians = ["dRdWT",...
Definition: pyDAFoam.py:510
dafoam.pyDAFoam.PYDAFOAM.solverInitialized
solverInitialized
Definition: pyDAFoam.py:742
dafoam.pyDAFoam.PYDAFOAM.objFuncNames4Adj
objFuncNames4Adj
Definition: pyDAFoam.py:820
dafoam.pyDAFoam.DAOPTION.printInterval
printInterval
The print interval of primal and adjoint solution, e.g., how frequent to print the primal solution st...
Definition: pyDAFoam.py:514
dafoam.pyDAFoam.PYDAFOAM.addFamilyGroup
def addFamilyGroup(self, groupName, families)
Definition: pyDAFoam.py:1461
dafoam.pyDAFoam.PYDAFOAM.mesh
mesh
Definition: pyDAFoam.py:811
dafoam.pyDAFoam.TensorFlowHelper
Definition: pyDAFoam.py:4082
dafoam.pyDAFoam.PYDAFOAM.setDesignVars
def setDesignVars(self, x)
Definition: pyDAFoam.py:1650
dafoam.pyDAFoam.PYDAFOAM.rank
rank
Definition: pyDAFoam.py:1715
dafoam.pyDAFoam.PYDAFOAM.setEvalFuncs
def setEvalFuncs(self, evalFuncs)
Definition: pyDAFoam.py:1520
dafoam.pyDAFoam.PYDAFOAM._initializeAdjVectors
def _initializeAdjVectors(self)
Definition: pyDAFoam.py:946
dafoam.pyDAFoam.PYDAFOAM.getForces
def getForces(self, groupName=None)
Definition: pyDAFoam.py:2521
dafoam.pyDAFoam.PYDAFOAM.readPetscVecMat
def readPetscVecMat(self, name, vecMat)
Definition: pyDAFoam.py:1962
dafoam.pyDAFoam.PYDAFOAM.array2Vec
def array2Vec(self, array1)
Definition: pyDAFoam.py:3901
dafoam.pyDAFoam.PYDAFOAM.vec2ArraySeq
def vec2ArraySeq(self, vec)
Definition: pyDAFoam.py:3939
dafoam.pyDAFoam.PYDAFOAM.updateDAOption
def updateDAOption(self)
Definition: pyDAFoam.py:3766
dafoam.pyDAFoam.PYDAFOAM.surfGeoDisp
surfGeoDisp
Definition: pyDAFoam.py:836
dafoam.pyDAFoam.PYDAFOAM.writeFieldSensitivityMap
def writeFieldSensitivityMap(self, objFuncName, designVarName, solutionTime, fieldType, sensVec)
Definition: pyDAFoam.py:1729
dafoam.pyDAFoam.PYDAFOAM.nProcs
nProcs
Definition: pyDAFoam.py:1716
dafoam.pyDAFoam.PYDAFOAM.primalFail
primalFail
Definition: pyDAFoam.py:750
dafoam.pyDAFoam.PYDAFOAM.dRdW0TPsi
dRdW0TPsi
Definition: pyDAFoam.py:1006
dafoam.pyDAFoam.DAOPTION.jacLowerBounds
jacLowerBounds
The min bound for Jacobians, any value that is smaller than the bound will be set to 0 Setting a larg...
Definition: pyDAFoam.py:583
dafoam.pyDAFoam.PYDAFOAM._initializeComm
def _initializeComm(self, comm)
Definition: pyDAFoam.py:1698
dafoam.pyDAFoam.PYDAFOAM.printFamilyList
def printFamilyList(self)
Definition: pyDAFoam.py:1644